├── README.md
├── .gitignore
├── .gitattributes
├── public
├── imgs
│ ├── back.png
│ └── back2.png
├── js
│ ├── index.js
│ ├── libs
│ │ ├── deparam.js
│ │ ├── mustache.js
│ │ └── jquery-3.3.1.min.js
│ └── chat.js
├── index.html
├── chat.html
└── css
│ └── styles.css
├── server
├── utils
│ ├── validation.js
│ ├── rooms.js
│ ├── validation.test.js
│ ├── giphy.js
│ ├── message.js
│ ├── message.test.js
│ ├── users.js
│ └── users.test.js
└── server.js
├── playground
└── time.js
└── package.json
/README.md:
--------------------------------------------------------------------------------
1 | # node-course-2-chat-app
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | playground/
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/public/imgs/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ashleshk/node-course-2-chat-app/master/public/imgs/back.png
--------------------------------------------------------------------------------
/public/imgs/back2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ashleshk/node-course-2-chat-app/master/public/imgs/back2.png
--------------------------------------------------------------------------------
/server/utils/validation.js:
--------------------------------------------------------------------------------
1 | var isRealString = (str) => {
2 | return typeof str === 'string' && str.trim().length > 0;
3 | };
4 |
5 | module.exports = {isRealString};
6 |
--------------------------------------------------------------------------------
/server/utils/rooms.js:
--------------------------------------------------------------------------------
1 | class Rooms {
2 | constructor() {
3 | this.rooms = [];
4 | }
5 |
6 | addRoom(name) {
7 | this.rooms.push(name);
8 | }
9 |
10 | removeRoom(name) {
11 | this.rooms = this.rooms.filter((room) => room != name);
12 | }
13 |
14 | getRooms() {
15 | return this.rooms;
16 | }
17 | }
18 |
19 | module.exports = {Rooms};
20 |
--------------------------------------------------------------------------------
/public/js/index.js:
--------------------------------------------------------------------------------
1 | let socket = io();
2 |
3 | socket.on('connect', () => {
4 | console.log('Index connected');
5 |
6 | socket.on('loadRooms', function(rooms) {
7 |
8 | rooms.forEach(function(room) {
9 | $('#room-select').append($('').text(room.toUpperCase()).attr('value', `${room}`));
10 | });
11 | });
12 | });
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/playground/time.js:
--------------------------------------------------------------------------------
1 | var moment = require('moment');
2 |
3 | // Jan 1st 1970 00:00:10 am
4 |
5 | // var date = new Date();
6 | // var months = ['Jan', 'Feb']
7 | // console.log(date.getMonth());
8 |
9 | var date = moment();
10 | date.add(100, 'year').subtract(9, 'months');
11 | console.log(date.format('MMM Do, YYYY'));
12 |
13 | // 10:35 am
14 | // 6:01 am
15 |
16 | var someTimestamp = moment().valueOf();
17 | console.log(someTimestamp)
18 |
19 | var createdAt = 1234;
20 | var date = moment(createdAt);
21 | console.log(date.format('h:mm a'))
22 |
--------------------------------------------------------------------------------
/server/utils/validation.test.js:
--------------------------------------------------------------------------------
1 | const expect = require('expect');
2 |
3 | const {isRealString} = require('./validation');
4 |
5 | describe('isRealString', () => {
6 | it('should reject non-string values', () => {
7 | var res = isRealString(98);
8 | expect(res).toBe(false);
9 | });
10 |
11 | it('should reject string with only spaces', () => {
12 | var res = isRealString(' ');
13 | expect(res).toBe(false);
14 | });
15 |
16 | it('should allow string with non-space characters', () => {
17 | var res = isRealString('D');
18 | expect(res).toBe(true);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/server/utils/giphy.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 |
3 |
4 | let getGif = (searchString) => {
5 |
6 | let giphyUrl = `https://api.giphy.com/v1/gifs/translate?api_key=WFYShEMluFwHlUXBJ7Kk7jhGbspo1CC7&s=${searchString}`;
7 |
8 | return axios.get(giphyUrl)
9 | .then((response) => {
10 | let gifUrl = response.data.data.images.original.url;
11 | console.log('Success: ', response.data.data.images.fixed_width.url);
12 | return gifUrl;
13 | })
14 | .catch((errObj) => {
15 | console.log('Error: ', errObj.message);
16 | });
17 | };
18 |
19 | module.exports = {getGif};
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/server/utils/message.js:
--------------------------------------------------------------------------------
1 | var moment = require('moment');
2 |
3 | var generateMessage = (from, text) => {
4 | return {
5 | from,
6 | text,
7 | createdAt: moment().valueOf()
8 | };
9 | };
10 |
11 | let generateGif = (from, url) => {
12 | return {
13 | from,
14 | url,
15 | createdAt: moment().valueOf()
16 | }
17 | };
18 |
19 | // var generateLocationMessage = (from, latitude, longitude) => {
20 | // return {
21 | // from,
22 | // url: `https://www.google.com/maps?q=${latitude},${longitude}`,
23 | // createdAt: moment().valueOf()
24 | // };
25 | // };
26 |
27 |
28 | module.exports = {generateMessage, generateGif};
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-app",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node server/server.js",
8 | "test": "mocha server/**/*.test.js",
9 | "test-watch": "nodemon --exec 'npm test'"
10 | },
11 | "engines": {
12 | "node": "6.2.2"
13 | },
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "axios": "^0.18.0",
18 | "express": "^4.16.4",
19 | "moment": "^2.22.2",
20 | "socket.io": "^1.4.8"
21 | },
22 | "devDependencies": {
23 | "expect": "^1.20.2",
24 | "mocha": "^3.0.2",
25 | "nodemon": "^1.18.6"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/public/js/libs/deparam.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jQuery.deparam - The oposite of jQuery param. Creates an object of query string parameters.
3 | *
4 | * Credits for the idea and Regex:
5 | * http://stevenbenner.com/2010/03/javascript-regex-trick-parse-a-query-string-into-an-object/
6 | */
7 | (function($){
8 | $.deparam = $.deparam || function(uri){
9 | if(uri === undefined){
10 | uri = window.location.search;
11 | }
12 | var queryString = {};
13 | uri.replace(
14 | new RegExp(
15 | "([^?=&]+)(=([^]*))?", "g"),
16 | function($0, $1, $2, $3) {
17 | queryString[$1] = decodeURIComponent($3.replace(/\+/g, '%20'));
18 | }
19 | );
20 | return queryString;
21 | };
22 | })(jQuery);
23 |
--------------------------------------------------------------------------------
/server/utils/message.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('expect');
2 |
3 | var {generateMessage, generateLocationMessage} = require('./message');
4 |
5 | describe('generateMessage', () => {
6 | it('should generate correct message object', () => {
7 | var from = 'Jen';
8 | var text = 'Some message';
9 | var message = generateMessage(from, text);
10 |
11 | expect(message.createdAt).toBeA('number');
12 | expect(message).toInclude({from, text});
13 | });
14 | });
15 |
16 | describe('generateGif', () => {
17 | it('should generate the correct message object', () => {
18 | let from = 'Test';
19 | let url = 'https://someUrl.com';
20 | let message = generateGif(from, url);
21 |
22 | expect(message).toInclude({from, url});
23 | expect(message.createdAt).toBeA('number');
24 | })
25 | });
26 |
27 | // describe('generateLocationMessage', () => {
28 | // it('should generate correct location object', () => {
29 | // var from = 'Deb';
30 | // var latitude = 15;
31 | // var longitude = 19;
32 | // var url = 'https://www.google.com/maps?q=15,19';
33 | // var message = generateLocationMessage(from, latitude, longitude);
34 |
35 | // expect(message.createdAt).toBeA('number');
36 | // expect(message).toInclude({from, url});
37 | // });
38 | // });
39 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Chatty | Join
7 |
8 |
9 |
10 |
11 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/server/utils/users.js:
--------------------------------------------------------------------------------
1 | [{
2 | id: '/#12poiajdspfoif',
3 | name: 'Andrew',
4 | room: 'The Office Fans'
5 | }]
6 |
7 | // addUser(id, name, room)
8 | // removeUser(id)
9 | // getUser(id)
10 | // getUserList(room)
11 |
12 | class Users {
13 | constructor () {
14 | this.users = [];
15 | }
16 | addUser (id, name, room) {
17 | var user = {id, name, room};
18 | this.users.push(user);
19 | return user;
20 | }
21 | removeUser (id) {
22 | var user = this.getUser(id);
23 |
24 | if (user) {
25 | this.users = this.users.filter((user) => user.id !== id);
26 | }
27 |
28 | return user;
29 | }
30 | getUser (id) {
31 | return this.users.filter((user) => user.id === id)[0]
32 | }
33 | getUserList (room) {
34 | var users = this.users.filter((user) => user.room === room);
35 | var namesArray = users.map((user) => user.name);
36 |
37 | return namesArray;
38 | }
39 | }
40 |
41 | module.exports = {Users};
42 |
43 | // class Person {
44 | // constructor (name, age) {
45 | // this.name = name;
46 | // this.age = age;
47 | // }
48 | // getUserDescription () {
49 | // return `${this.name} is ${this.age} year(s) old.`;
50 | // }
51 | // }
52 | //
53 | // var me = new Person('Andrew', 25);
54 | // var description = me.getUserDescription();
55 | // console.log(description);
56 |
--------------------------------------------------------------------------------
/public/chat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Chat | ChatApp
9 |
10 |
11 |
12 |
13 |
14 |
21 |
31 |
32 |
43 |
44 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/server/utils/users.test.js:
--------------------------------------------------------------------------------
1 | const expect = require('expect');
2 |
3 | const {Users} = require('./users');
4 |
5 | describe('Users', () => {
6 | var users;
7 |
8 | beforeEach(() => {
9 | users = new Users();
10 | users.users = [{
11 | id: '1',
12 | name: 'Mike',
13 | room: 'Node Course'
14 | }, {
15 | id: '2',
16 | name: 'Jen',
17 | room: 'React Course'
18 | }, {
19 | id: '3',
20 | name: 'Julie',
21 | room: 'Node Course'
22 | }];
23 | });
24 |
25 | it('should add new user', () => {
26 | var users = new Users();
27 | var user = {
28 | id: '123',
29 | name: 'Andrew',
30 | room: 'The Office Fans'
31 | };
32 | var resUser = users.addUser(user.id, user.name, user.room);
33 |
34 | expect(users.users).toEqual([user]);
35 | });
36 |
37 | it('should remove a user', () => {
38 | var userId = '1';
39 | var user = users.removeUser(userId);
40 |
41 | expect(user.id).toBe(userId);
42 | expect(users.users.length).toBe(2);
43 | });
44 |
45 | it('should not remove user', () => {
46 | var userId = '99';
47 | var user = users.removeUser(userId);
48 |
49 | expect(user).toNotExist();
50 | expect(users.users.length).toBe(3);
51 | });
52 |
53 | it('should find user', () => {
54 | var userId = '2';
55 | var user = users.getUser(userId);
56 |
57 | expect(user.id).toBe(userId);
58 | });
59 |
60 | it('should not find user', () => {
61 | var userId = '99';
62 | var user = users.getUser(userId);
63 |
64 | expect(user).toNotExist();
65 | });
66 |
67 | it('should return names for node course', () => {
68 | var userList = users.getUserList('Node Course');
69 |
70 | expect(userList).toEqual(['Mike', 'Julie']);
71 | });
72 |
73 | it('should return names for react course', () => {
74 | var userList = users.getUserList('React Course');
75 |
76 | expect(userList).toEqual(['Jen']);
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/public/js/chat.js:
--------------------------------------------------------------------------------
1 | let socket = io();
2 |
3 | function scrollToBottom() {
4 | //Selectors
5 | let messages = $('#messages');
6 | let newMessage = messages.children('li:last-child');
7 |
8 | //Heights
9 | //prop is a crossbrowser way of performing document selection
10 | let clientHeight = messages.prop('clientHeight');
11 | let scrollTop = messages.prop('scrollTop');
12 | let scrollHeight = messages.prop('scrollHeight');
13 | let newMessageHeight = newMessage.innerHeight();
14 | let lastMessageHeight = newMessage.prev().innerHeight();
15 |
16 | if(clientHeight + scrollTop + newMessageHeight + lastMessageHeight>= scrollHeight) {
17 | //scrollTop is a jQuery method for setting scrollTop value
18 | messages.scrollTop(scrollHeight);
19 | }
20 | };
21 |
22 | //using pre-es6 function for cross browser compatibility
23 | socket.on('connect', function() {
24 | //returns an object of the query params
25 | let params = $.deparam(window.location.search);
26 | socket.emit('join', params, function(err) {
27 | if(err) {
28 | alert(err);
29 | //redirects back to root page
30 | window.location.href = '/';
31 | } else {
32 | console.log('No error');
33 | }
34 | });
35 | });
36 |
37 | socket.on('disconnect', function() {
38 | console.log('Disconnected from server');
39 | });
40 |
41 | socket.on('updateUserList', function(users) {
42 | let ul = $('');
43 |
44 | users.forEach(function(user) {
45 | ul.append($('').text(user));
46 |
47 | $('#users').html(ul);
48 | });
49 | });
50 |
51 | //custom event, receives message object from emitted event in server.js
52 | socket.on('newMessage', function(message) {
53 | //html method returns the markup inside message-template
54 | let formattedTime = moment(message.createdAt).format('h:mm a');
55 | let template = $('#message-template').html();
56 | let html = Mustache.render(template, {
57 | text: message.text,
58 | from: message.from,
59 | createdAt: formattedTime
60 | });
61 |
62 | $('#messages').append(html);
63 | scrollToBottom();
64 | });
65 |
66 | socket.on('newGifMessage', function(message) {
67 |
68 | let formattedTime = moment(message.createdAt).format('h:mm a');
69 | let template = $('#gif-message-template').html();
70 | let html = Mustache.render(template, {
71 | url: message.url,
72 | from: message.from,
73 | createdAt: formattedTime
74 | });
75 |
76 | $('#messages').append(html);
77 | scrollToBottom();
78 | });
79 |
80 | $('#message-form').on('submit', function(e) {
81 | //prevents page from refreshing on submit
82 | e.preventDefault();
83 |
84 | let messageTextbox = $('[name=message]')
85 |
86 | if(!e.originalEvent) {
87 | socket.emit('createGifMessage', {
88 | query: messageTextbox.val()
89 | }, function() {
90 | messageTextbox.val('');
91 | });
92 | } else {
93 | socket.emit('createMessage', {
94 | text: messageTextbox.val()
95 | }, function() {
96 | messageTextbox.val('');
97 | });
98 | }
99 | });
100 |
101 | $('#search-giphy').click(function() {
102 | $('#message-form').submit()
103 | });
104 |
105 |
106 |
107 | // let locationButton = $('#send-location');
108 | // locationButton.on('click', function() {
109 | // //make sure browser has access to geolcation api
110 | // if(!navigator.geolocation) {
111 | // return alert('Geolocation not supported by your browser');
112 | // }
113 |
114 | // locationButton.attr('disabled', 'disabled').text('Sending Location...');
115 |
116 | // navigator.geolocation.getCurrentPosition(function(position) {
117 | // locationButton.removeAttr('disabled').text('Send Location');
118 |
119 | // socket.emit('createLocationMessage', {
120 | // latitude: position.coords.latitude,
121 | // longitude: position.coords.longitude
122 | // });
123 | // }, function() {
124 | // alert('Unable to fetch location');
125 | // locationButton.removeAttr('disabled').text('Send Location');
126 | // });
127 | // });
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const http = require('http');
3 | const express = require('express');
4 | const socketIO = require('socket.io');
5 |
6 | const {generateMessage, generateLocation, generateGif} = require('./utils/message');
7 | const {isRealString} = require('./utils/validation');
8 | const {Users} = require('./utils/users');
9 | const {Rooms} = require('./utils/rooms');
10 | const {getGif} = require('./utils/giphy');
11 |
12 |
13 | const publicPath = path.join(__dirname, '../public');
14 | const port = process.env.PORT || 3000;
15 | var app = express();
16 | var server = http.createServer(app);
17 | var io = socketIO(server);
18 | var users = new Users();
19 | let rooms = new Rooms();
20 |
21 |
22 | app.use(express.static(publicPath));
23 |
24 | io.on('connection', (socket) => {
25 | socket.emit('loadRooms', rooms.getRooms());
26 |
27 | socket.on('join', (params, callback) => {
28 | let roomSelect = params.room_select.toLowerCase();
29 | let roomInput = params.room_input.toLowerCase();
30 | let room = roomSelect || roomInput;
31 | if (!isRealString(params.name) || !isRealString(room)) {
32 | return callback('Name and room name are required');
33 | }
34 |
35 | if(isRealString(roomSelect) && isRealString(roomInput)) {
36 | return callback('Either select a room or create one');
37 | }
38 |
39 | let roomCheck = () => {
40 | //add room if it isn't already in the array
41 | if(!rooms.getRooms().includes(room)) {
42 | rooms.addRoom(room);
43 | }
44 | console.log('Rooms:', rooms.getRooms());
45 | }
46 |
47 | let joinRoom = () => {
48 | //socket.join takes a string
49 | socket.join(room);
50 | //remove user from any previous rooms before adding to new one
51 | users.removeUser(socket.id);
52 | users.addUser(socket.id, params.name, room);
53 |
54 | roomCheck();
55 |
56 | //only emits to specific room
57 | io.to(room).emit('updateUserList', users.getUserList(room));
58 | socket.emit('newMessage', generateMessage('Admin', `WELCOME TO THE ${room.toUpperCase()} ROOM`));
59 | socket.broadcast.to(room).emit('newMessage', generateMessage('Admin', `User ${params.name} has joined.`));
60 | };
61 |
62 | //check to see if user name already exists in room
63 | if(users.getUserList(room).includes(params.name)) {
64 | //prevent duplicate rooms getting added to select at index.html
65 | roomCheck();
66 | return callback('Name already exists, please choose another');
67 | } else {
68 | joinRoom();
69 | }
70 |
71 | callback();
72 | });
73 |
74 | socket.on('createMessage', (message, callback) => {
75 | var user = users.getUser(socket.id);
76 |
77 | if (user && isRealString(message.text)) {
78 | io.to(user.room).emit('newMessage', generateMessage(user.name, message.text));
79 | }
80 |
81 | callback();
82 | });
83 |
84 | socket.on('createGifMessage', (message, callback) => {
85 | let user = users.getUser(socket.id);
86 |
87 | if(user && isRealString(message.query)) {
88 | getGif(message.query)
89 | .then((response) => {
90 | let url = response;
91 | io.to(user.room).emit('newGifMessage', generateGif(user.name, url));
92 | })
93 | .catch((err) => {
94 | console.log(err);
95 | })
96 | }
97 | callback();
98 | });
99 | // socket.on('createLocationMessage', (coords) => {
100 | // var user = users.getUser(socket.id);
101 |
102 | // if (user) {
103 | // io.to(user.room).emit('newLocationMessage', generateLocationMessage(user.name, coords.latitude, coords.longitude));
104 | // }
105 | // });
106 |
107 | socket.on('disconnect', () => {
108 | var user = users.removeUser(socket.id);
109 | let roomKeys = Object.keys(socket.adapter.rooms);
110 |
111 | rooms.getRooms().map((room) => {
112 | if(!roomKeys.includes(room)) {
113 | rooms.removeRoom(room);
114 | console.log('After remove: ', rooms.getRooms());
115 | }
116 | });
117 |
118 | if (user) {
119 | io.to(user.room).emit('updateUserList', users.getUserList(user.room));
120 | io.to(user.room).emit('newMessage', generateMessage('Admin', `${user.name} has left.`));
121 | }
122 | });
123 | });
124 |
125 | server.listen(port, () => {
126 | console.log(`Server is up on ${port}`);
127 | });
128 |
--------------------------------------------------------------------------------
/public/css/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | margin: 0;
4 | padding: 0;
5 | font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
6 | font-weight: 300;
7 | font-size: .95rem;
8 | }
9 |
10 | ul, li {
11 | list-style-position: inside;
12 | }
13 |
14 | h3 {
15 | font-weight: 600;
16 | text-align: center;
17 | font-size: 1.5rem;
18 | }
19 |
20 | button {
21 | border: none;
22 | background: #265f82;
23 | color: white;
24 | cursor: pointer;
25 | padding: 10px;
26 | transition: background .3s ease;
27 | }
28 |
29 | button:hover {
30 | border: none;
31 | background: #1F4C69;
32 | color: white;
33 | padding: 10px;
34 | }
35 |
36 | button:disabled {
37 | cursor: default;
38 | background: #698ea5;
39 | }
40 |
41 | .centered-form {
42 | display: flex;
43 | align-items: center;
44 | height: 100vh;
45 | width: 100vw;
46 | justify-content: center;
47 | background: -moz-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* ff3.6+ */
48 | background: -webkit-gradient(linear, left top, right bottom, color-stop(0%, rgba(49,84,129,1)), color-stop(100%, rgba(39,107,130,1))); /* safari4+,chrome */
49 | background: -webkit-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* safari5.1+,chrome10+ */
50 | background: -o-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* opera 11.10+ */
51 | background: -ms-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* ie10+ */
52 | background: linear-gradient(325deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* w3c */
53 | }
54 |
55 | .centered-form__form {
56 | background: rgba(250, 250, 250, 0.9);
57 | border: 1px solid #e1e1e1;
58 | border-radius: 5px;
59 | padding: 0px 20px;
60 | margin: 20px;
61 | width: 230px;
62 | }
63 |
64 | .form-field {
65 | margin: 20px 0;
66 | }
67 |
68 | .form-field > * {
69 | width: 100%;
70 | }
71 |
72 | .form-field label {
73 | display: block;
74 | margin-bottom: 7px;
75 | }
76 |
77 | .form-field input, .form-field select {
78 | border: 1px solid #e1e1e1;
79 | padding: 10px;
80 | }
81 |
82 | .chat {
83 | display: flex;
84 | }
85 |
86 | .chat__sidebar {
87 | overflow-y: scroll;
88 | width: 260px;
89 | height: 100vh;
90 | background: -moz-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* ff3.6+ */
91 | background: -webkit-gradient(linear, left top, right bottom, color-stop(0%, rgba(49,84,129,1)), color-stop(100%, rgba(39,107,130,1))); /* safari4+,chrome */
92 | background: -webkit-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* safari5.1+,chrome10+ */
93 | background: -o-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* opera 11.10+ */
94 | background: -ms-linear-gradient(125deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* ie10+ */
95 | background: linear-gradient(325deg, rgba(39,107,130,1) 0%, rgba(49,84,129,1) 100%); /* w3c */
96 | }
97 |
98 | .chat__sidebar h3 {
99 | color: #e6eaee;
100 | margin: 10px 20px;
101 | text-align: left;
102 | }
103 |
104 | .chat__sidebar ul {
105 | list-style-type: none;
106 | }
107 |
108 | .chat__sidebar li {
109 | background: #e6eaee;
110 | border: 1px solid #e1e1e1;
111 | border-radius: 5px;
112 | padding: 10px;
113 | margin: 10px;
114 | }
115 |
116 | .chat__main {
117 | display: flex;
118 | flex-direction: column;
119 | height: 100vh;
120 | width: 100%;
121 | }
122 |
123 | .chat__messages {
124 | flex-grow: 1;
125 | overflow-y: scroll;
126 | -webkit-overflow-scrolling: touch;
127 | }
128 |
129 | .chat__footer {
130 | background: #e6eaee;
131 | display: flex;
132 | padding: 10px;
133 | /*height: 60px;*/
134 | flex-shrink: 0;
135 | }
136 |
137 | .chat__footer form {
138 | flex-grow: 1;
139 | display: flex;
140 | }
141 |
142 | .chat__footer form * {
143 | margin-right: 10px;
144 | }
145 |
146 | .chat__footer input {
147 | border: none;
148 | padding: 10px;
149 | flex-grow: 1;
150 | }
151 |
152 | .chat__messages {
153 | list-style-type: none;
154 | padding: 10px;
155 | }
156 |
157 | .message {
158 | padding: 10px;
159 | }
160 |
161 | .message__title {
162 | display: flex;
163 | margin-bottom: 5px;
164 | }
165 |
166 | .message__title h4 {
167 | font-weight: 600;
168 | margin-right: 10px;
169 | }
170 |
171 | .message__title span {
172 | color: #999;
173 | }
174 |
175 | .sidebar__icon {
176 | display: flex;
177 | justify-content: center;
178 | }
179 |
180 | #back-arrow {
181 | margin-top: 15px;
182 | }
183 |
184 | @media (max-width: 600px) {
185 | * {
186 | font-size: 1rem;
187 | }
188 |
189 | .chat__sidebar {
190 | display: none;
191 | }
192 |
193 | .chat__footer {
194 | flex-direction: column;
195 | }
196 |
197 | .chat__footer form {
198 | margin-bottom: 10px;
199 | }
200 |
201 | .chat__footer button {
202 | margin-right: 0px;
203 | }
204 |
205 | .gif_message {
206 | max-width: 300px;
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/public/js/libs/mustache.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * mustache.js - Logic-less {{mustache}} templates with JavaScript
3 | * http://github.com/janl/mustache.js
4 | */
5 |
6 | /*global define: false Mustache: true*/
7 |
8 | (function defineMustache (global, factory) {
9 | if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') {
10 | factory(exports); // CommonJS
11 | } else if (typeof define === 'function' && define.amd) {
12 | define(['exports'], factory); // AMD
13 | } else {
14 | global.Mustache = {};
15 | factory(global.Mustache); // script, wsh, asp
16 | }
17 | }(this, function mustacheFactory (mustache) {
18 |
19 | var objectToString = Object.prototype.toString;
20 | var isArray = Array.isArray || function isArrayPolyfill (object) {
21 | return objectToString.call(object) === '[object Array]';
22 | };
23 |
24 | function isFunction (object) {
25 | return typeof object === 'function';
26 | }
27 |
28 | /**
29 | * More correct typeof string handling array
30 | * which normally returns typeof 'object'
31 | */
32 | function typeStr (obj) {
33 | return isArray(obj) ? 'array' : typeof obj;
34 | }
35 |
36 | function escapeRegExp (string) {
37 | return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
38 | }
39 |
40 | /**
41 | * Null safe way of checking whether or not an object,
42 | * including its prototype, has a given property
43 | */
44 | function hasProperty (obj, propName) {
45 | return obj != null && typeof obj === 'object' && (propName in obj);
46 | }
47 |
48 | // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
49 | // See https://github.com/janl/mustache.js/issues/189
50 | var regExpTest = RegExp.prototype.test;
51 | function testRegExp (re, string) {
52 | return regExpTest.call(re, string);
53 | }
54 |
55 | var nonSpaceRe = /\S/;
56 | function isWhitespace (string) {
57 | return !testRegExp(nonSpaceRe, string);
58 | }
59 |
60 | var entityMap = {
61 | '&': '&',
62 | '<': '<',
63 | '>': '>',
64 | '"': '"',
65 | "'": ''',
66 | '/': '/',
67 | '`': '`',
68 | '=': '='
69 | };
70 |
71 | function escapeHtml (string) {
72 | return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) {
73 | return entityMap[s];
74 | });
75 | }
76 |
77 | var whiteRe = /\s*/;
78 | var spaceRe = /\s+/;
79 | var equalsRe = /\s*=/;
80 | var curlyRe = /\s*\}/;
81 | var tagRe = /#|\^|\/|>|\{|&|=|!/;
82 |
83 | /**
84 | * Breaks up the given `template` string into a tree of tokens. If the `tags`
85 | * argument is given here it must be an array with two string values: the
86 | * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
87 | * course, the default is to use mustaches (i.e. mustache.tags).
88 | *
89 | * A token is an array with at least 4 elements. The first element is the
90 | * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
91 | * did not contain a symbol (i.e. {{myValue}}) this element is "name". For
92 | * all text that appears outside a symbol this element is "text".
93 | *
94 | * The second element of a token is its "value". For mustache tags this is
95 | * whatever else was inside the tag besides the opening symbol. For text tokens
96 | * this is the text itself.
97 | *
98 | * The third and fourth elements of the token are the start and end indices,
99 | * respectively, of the token in the original template.
100 | *
101 | * Tokens that are the root node of a subtree contain two more elements: 1) an
102 | * array of tokens in the subtree and 2) the index in the original template at
103 | * which the closing tag for that section begins.
104 | */
105 | function parseTemplate (template, tags) {
106 | if (!template)
107 | return [];
108 |
109 | var sections = []; // Stack to hold section tokens
110 | var tokens = []; // Buffer to hold the tokens
111 | var spaces = []; // Indices of whitespace tokens on the current line
112 | var hasTag = false; // Is there a {{tag}} on the current line?
113 | var nonSpace = false; // Is there a non-space char on the current line?
114 |
115 | // Strips all whitespace tokens array for the current line
116 | // if there was a {{#tag}} on it and otherwise only space.
117 | function stripSpace () {
118 | if (hasTag && !nonSpace) {
119 | while (spaces.length)
120 | delete tokens[spaces.pop()];
121 | } else {
122 | spaces = [];
123 | }
124 |
125 | hasTag = false;
126 | nonSpace = false;
127 | }
128 |
129 | var openingTagRe, closingTagRe, closingCurlyRe;
130 | function compileTags (tagsToCompile) {
131 | if (typeof tagsToCompile === 'string')
132 | tagsToCompile = tagsToCompile.split(spaceRe, 2);
133 |
134 | if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)
135 | throw new Error('Invalid tags: ' + tagsToCompile);
136 |
137 | openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
138 | closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
139 | closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
140 | }
141 |
142 | compileTags(tags || mustache.tags);
143 |
144 | var scanner = new Scanner(template);
145 |
146 | var start, type, value, chr, token, openSection;
147 | while (!scanner.eos()) {
148 | start = scanner.pos;
149 |
150 | // Match any text between tags.
151 | value = scanner.scanUntil(openingTagRe);
152 |
153 | if (value) {
154 | for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
155 | chr = value.charAt(i);
156 |
157 | if (isWhitespace(chr)) {
158 | spaces.push(tokens.length);
159 | } else {
160 | nonSpace = true;
161 | }
162 |
163 | tokens.push([ 'text', chr, start, start + 1 ]);
164 | start += 1;
165 |
166 | // Check for whitespace on the current line.
167 | if (chr === '\n')
168 | stripSpace();
169 | }
170 | }
171 |
172 | // Match the opening tag.
173 | if (!scanner.scan(openingTagRe))
174 | break;
175 |
176 | hasTag = true;
177 |
178 | // Get the tag type.
179 | type = scanner.scan(tagRe) || 'name';
180 | scanner.scan(whiteRe);
181 |
182 | // Get the tag value.
183 | if (type === '=') {
184 | value = scanner.scanUntil(equalsRe);
185 | scanner.scan(equalsRe);
186 | scanner.scanUntil(closingTagRe);
187 | } else if (type === '{') {
188 | value = scanner.scanUntil(closingCurlyRe);
189 | scanner.scan(curlyRe);
190 | scanner.scanUntil(closingTagRe);
191 | type = '&';
192 | } else {
193 | value = scanner.scanUntil(closingTagRe);
194 | }
195 |
196 | // Match the closing tag.
197 | if (!scanner.scan(closingTagRe))
198 | throw new Error('Unclosed tag at ' + scanner.pos);
199 |
200 | token = [ type, value, start, scanner.pos ];
201 | tokens.push(token);
202 |
203 | if (type === '#' || type === '^') {
204 | sections.push(token);
205 | } else if (type === '/') {
206 | // Check section nesting.
207 | openSection = sections.pop();
208 |
209 | if (!openSection)
210 | throw new Error('Unopened section "' + value + '" at ' + start);
211 |
212 | if (openSection[1] !== value)
213 | throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
214 | } else if (type === 'name' || type === '{' || type === '&') {
215 | nonSpace = true;
216 | } else if (type === '=') {
217 | // Set the tags for the next time around.
218 | compileTags(value);
219 | }
220 | }
221 |
222 | // Make sure there are no open sections when we're done.
223 | openSection = sections.pop();
224 |
225 | if (openSection)
226 | throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
227 |
228 | return nestTokens(squashTokens(tokens));
229 | }
230 |
231 | /**
232 | * Combines the values of consecutive text tokens in the given `tokens` array
233 | * to a single token.
234 | */
235 | function squashTokens (tokens) {
236 | var squashedTokens = [];
237 |
238 | var token, lastToken;
239 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
240 | token = tokens[i];
241 |
242 | if (token) {
243 | if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
244 | lastToken[1] += token[1];
245 | lastToken[3] = token[3];
246 | } else {
247 | squashedTokens.push(token);
248 | lastToken = token;
249 | }
250 | }
251 | }
252 |
253 | return squashedTokens;
254 | }
255 |
256 | /**
257 | * Forms the given array of `tokens` into a nested tree structure where
258 | * tokens that represent a section have two additional items: 1) an array of
259 | * all tokens that appear in that section and 2) the index in the original
260 | * template that represents the end of that section.
261 | */
262 | function nestTokens (tokens) {
263 | var nestedTokens = [];
264 | var collector = nestedTokens;
265 | var sections = [];
266 |
267 | var token, section;
268 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
269 | token = tokens[i];
270 |
271 | switch (token[0]) {
272 | case '#':
273 | case '^':
274 | collector.push(token);
275 | sections.push(token);
276 | collector = token[4] = [];
277 | break;
278 | case '/':
279 | section = sections.pop();
280 | section[5] = token[2];
281 | collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
282 | break;
283 | default:
284 | collector.push(token);
285 | }
286 | }
287 |
288 | return nestedTokens;
289 | }
290 |
291 | /**
292 | * A simple string scanner that is used by the template parser to find
293 | * tokens in template strings.
294 | */
295 | function Scanner (string) {
296 | this.string = string;
297 | this.tail = string;
298 | this.pos = 0;
299 | }
300 |
301 | /**
302 | * Returns `true` if the tail is empty (end of string).
303 | */
304 | Scanner.prototype.eos = function eos () {
305 | return this.tail === '';
306 | };
307 |
308 | /**
309 | * Tries to match the given regular expression at the current position.
310 | * Returns the matched text if it can match, the empty string otherwise.
311 | */
312 | Scanner.prototype.scan = function scan (re) {
313 | var match = this.tail.match(re);
314 |
315 | if (!match || match.index !== 0)
316 | return '';
317 |
318 | var string = match[0];
319 |
320 | this.tail = this.tail.substring(string.length);
321 | this.pos += string.length;
322 |
323 | return string;
324 | };
325 |
326 | /**
327 | * Skips all text until the given regular expression can be matched. Returns
328 | * the skipped string, which is the entire tail if no match can be made.
329 | */
330 | Scanner.prototype.scanUntil = function scanUntil (re) {
331 | var index = this.tail.search(re), match;
332 |
333 | switch (index) {
334 | case -1:
335 | match = this.tail;
336 | this.tail = '';
337 | break;
338 | case 0:
339 | match = '';
340 | break;
341 | default:
342 | match = this.tail.substring(0, index);
343 | this.tail = this.tail.substring(index);
344 | }
345 |
346 | this.pos += match.length;
347 |
348 | return match;
349 | };
350 |
351 | /**
352 | * Represents a rendering context by wrapping a view object and
353 | * maintaining a reference to the parent context.
354 | */
355 | function Context (view, parentContext) {
356 | this.view = view;
357 | this.cache = { '.': this.view };
358 | this.parent = parentContext;
359 | }
360 |
361 | /**
362 | * Creates a new context using the given view with this context
363 | * as the parent.
364 | */
365 | Context.prototype.push = function push (view) {
366 | return new Context(view, this);
367 | };
368 |
369 | /**
370 | * Returns the value of the given name in this context, traversing
371 | * up the context hierarchy if the value is absent in this context's view.
372 | */
373 | Context.prototype.lookup = function lookup (name) {
374 | var cache = this.cache;
375 |
376 | var value;
377 | if (cache.hasOwnProperty(name)) {
378 | value = cache[name];
379 | } else {
380 | var context = this, names, index, lookupHit = false;
381 |
382 | while (context) {
383 | if (name.indexOf('.') > 0) {
384 | value = context.view;
385 | names = name.split('.');
386 | index = 0;
387 |
388 | /**
389 | * Using the dot notion path in `name`, we descend through the
390 | * nested objects.
391 | *
392 | * To be certain that the lookup has been successful, we have to
393 | * check if the last object in the path actually has the property
394 | * we are looking for. We store the result in `lookupHit`.
395 | *
396 | * This is specially necessary for when the value has been set to
397 | * `undefined` and we want to avoid looking up parent contexts.
398 | **/
399 | while (value != null && index < names.length) {
400 | if (index === names.length - 1)
401 | lookupHit = hasProperty(value, names[index]);
402 |
403 | value = value[names[index++]];
404 | }
405 | } else {
406 | value = context.view[name];
407 | lookupHit = hasProperty(context.view, name);
408 | }
409 |
410 | if (lookupHit)
411 | break;
412 |
413 | context = context.parent;
414 | }
415 |
416 | cache[name] = value;
417 | }
418 |
419 | if (isFunction(value))
420 | value = value.call(this.view);
421 |
422 | return value;
423 | };
424 |
425 | /**
426 | * A Writer knows how to take a stream of tokens and render them to a
427 | * string, given a context. It also maintains a cache of templates to
428 | * avoid the need to parse the same template twice.
429 | */
430 | function Writer () {
431 | this.cache = {};
432 | }
433 |
434 | /**
435 | * Clears all cached templates in this writer.
436 | */
437 | Writer.prototype.clearCache = function clearCache () {
438 | this.cache = {};
439 | };
440 |
441 | /**
442 | * Parses and caches the given `template` and returns the array of tokens
443 | * that is generated from the parse.
444 | */
445 | Writer.prototype.parse = function parse (template, tags) {
446 | var cache = this.cache;
447 | var tokens = cache[template];
448 |
449 | if (tokens == null)
450 | tokens = cache[template] = parseTemplate(template, tags);
451 |
452 | return tokens;
453 | };
454 |
455 | /**
456 | * High-level method that is used to render the given `template` with
457 | * the given `view`.
458 | *
459 | * The optional `partials` argument may be an object that contains the
460 | * names and templates of partials that are used in the template. It may
461 | * also be a function that is used to load partial templates on the fly
462 | * that takes a single argument: the name of the partial.
463 | */
464 | Writer.prototype.render = function render (template, view, partials) {
465 | var tokens = this.parse(template);
466 | var context = (view instanceof Context) ? view : new Context(view);
467 | return this.renderTokens(tokens, context, partials, template);
468 | };
469 |
470 | /**
471 | * Low-level method that renders the given array of `tokens` using
472 | * the given `context` and `partials`.
473 | *
474 | * Note: The `originalTemplate` is only ever used to extract the portion
475 | * of the original template that was contained in a higher-order section.
476 | * If the template doesn't use higher-order sections, this argument may
477 | * be omitted.
478 | */
479 | Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) {
480 | var buffer = '';
481 |
482 | var token, symbol, value;
483 | for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
484 | value = undefined;
485 | token = tokens[i];
486 | symbol = token[0];
487 |
488 | if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);
489 | else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);
490 | else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);
491 | else if (symbol === '&') value = this.unescapedValue(token, context);
492 | else if (symbol === 'name') value = this.escapedValue(token, context);
493 | else if (symbol === 'text') value = this.rawValue(token);
494 |
495 | if (value !== undefined)
496 | buffer += value;
497 | }
498 |
499 | return buffer;
500 | };
501 |
502 | Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {
503 | var self = this;
504 | var buffer = '';
505 | var value = context.lookup(token[1]);
506 |
507 | // This function is used to render an arbitrary template
508 | // in the current context by higher-order sections.
509 | function subRender (template) {
510 | return self.render(template, context, partials);
511 | }
512 |
513 | if (!value) return;
514 |
515 | if (isArray(value)) {
516 | for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
517 | buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
518 | }
519 | } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
520 | buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
521 | } else if (isFunction(value)) {
522 | if (typeof originalTemplate !== 'string')
523 | throw new Error('Cannot use higher-order sections without the original template');
524 |
525 | // Extract the portion of the original template that the section contains.
526 | value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
527 |
528 | if (value != null)
529 | buffer += value;
530 | } else {
531 | buffer += this.renderTokens(token[4], context, partials, originalTemplate);
532 | }
533 | return buffer;
534 | };
535 |
536 | Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {
537 | var value = context.lookup(token[1]);
538 |
539 | // Use JavaScript's definition of falsy. Include empty arrays.
540 | // See https://github.com/janl/mustache.js/issues/186
541 | if (!value || (isArray(value) && value.length === 0))
542 | return this.renderTokens(token[4], context, partials, originalTemplate);
543 | };
544 |
545 | Writer.prototype.renderPartial = function renderPartial (token, context, partials) {
546 | if (!partials) return;
547 |
548 | var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
549 | if (value != null)
550 | return this.renderTokens(this.parse(value), context, partials, value);
551 | };
552 |
553 | Writer.prototype.unescapedValue = function unescapedValue (token, context) {
554 | var value = context.lookup(token[1]);
555 | if (value != null)
556 | return value;
557 | };
558 |
559 | Writer.prototype.escapedValue = function escapedValue (token, context) {
560 | var value = context.lookup(token[1]);
561 | if (value != null)
562 | return mustache.escape(value);
563 | };
564 |
565 | Writer.prototype.rawValue = function rawValue (token) {
566 | return token[1];
567 | };
568 |
569 | mustache.name = 'mustache.js';
570 | mustache.version = '2.2.1';
571 | mustache.tags = [ '{{', '}}' ];
572 |
573 | // All high-level mustache.* functions use this writer.
574 | var defaultWriter = new Writer();
575 |
576 | /**
577 | * Clears all cached templates in the default writer.
578 | */
579 | mustache.clearCache = function clearCache () {
580 | return defaultWriter.clearCache();
581 | };
582 |
583 | /**
584 | * Parses and caches the given template in the default writer and returns the
585 | * array of tokens it contains. Doing this ahead of time avoids the need to
586 | * parse templates on the fly as they are rendered.
587 | */
588 | mustache.parse = function parse (template, tags) {
589 | return defaultWriter.parse(template, tags);
590 | };
591 |
592 | /**
593 | * Renders the `template` with the given `view` and `partials` using the
594 | * default writer.
595 | */
596 | mustache.render = function render (template, view, partials) {
597 | if (typeof template !== 'string') {
598 | throw new TypeError('Invalid template! Template should be a "string" ' +
599 | 'but "' + typeStr(template) + '" was given as the first ' +
600 | 'argument for mustache#render(template, view, partials)');
601 | }
602 |
603 | return defaultWriter.render(template, view, partials);
604 | };
605 |
606 | // This is here for backwards compatibility with 0.4.x.,
607 | /*eslint-disable */ // eslint wants camel cased function name
608 | mustache.to_html = function to_html (template, view, partials, send) {
609 | /*eslint-enable*/
610 |
611 | var result = mustache.render(template, view, partials);
612 |
613 | if (isFunction(send)) {
614 | send(result);
615 | } else {
616 | return result;
617 | }
618 | };
619 |
620 | // Export the escaping function so that the user may override it.
621 | // See https://github.com/janl/mustache.js/issues/244
622 | mustache.escape = escapeHtml;
623 |
624 | // Export these mainly for testing, but also for advanced usage.
625 | mustache.Scanner = Scanner;
626 | mustache.Context = Context;
627 | mustache.Writer = Writer;
628 |
629 | }));
630 |
--------------------------------------------------------------------------------
/public/js/libs/jquery-3.3.1.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */
2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=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=/