├── .gitignore
├── html
├── css
│ ├── images
│ │ ├── ui-icons_444444_256x240.png
│ │ ├── ui-icons_555555_256x240.png
│ │ └── ui-icons_777777_256x240.png
│ ├── common.css
│ └── jquery-ui.css
├── chat.html
├── canvas.html
├── sendfile.html
├── videocall.html
├── js
│ ├── chat.js
│ ├── canvas.js
│ ├── sendfile.js
│ └── videocall.js
└── lib
│ └── peer.min.js
├── README.md
├── server.js
├── key
├── server.crt
└── server.key
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/html/css/images/ui-icons_444444_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yjmyzz/peerjs-sample/HEAD/html/css/images/ui-icons_444444_256x240.png
--------------------------------------------------------------------------------
/html/css/images/ui-icons_555555_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yjmyzz/peerjs-sample/HEAD/html/css/images/ui-icons_555555_256x240.png
--------------------------------------------------------------------------------
/html/css/images/ui-icons_777777_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yjmyzz/peerjs-sample/HEAD/html/css/images/ui-icons_777777_256x240.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # peerjs-sample
2 |
3 | ---
4 |
5 | ### 本机启用/调试方法
6 |
7 | node server.js
8 |
9 | 如果提示缺少包,可通过 cnpm(或npm) install serve-index peer 安装
10 |
11 | 启用成功后,浏览https://localhost:81/ 即可
12 |
13 |
--------------------------------------------------------------------------------
/html/css/common.css:
--------------------------------------------------------------------------------
1 | .w60 {
2 | width: 60px;
3 | }
4 |
5 | .w120 {
6 | width: 120px;
7 | }
8 |
9 | .align_right {
10 | text-align: right;
11 | background-color: beige;
12 | line-height: 150%;
13 | padding: 3px
14 | }
15 |
16 | .align_left {
17 | text-align: left;
18 | line-height: 150%;
19 | padding: 3px
20 | }
21 |
22 | .chatMessageBox {
23 | height: 200px;
24 | overflow-y: scroll;
25 | vertical-align: top
26 | }
27 |
28 | video{
29 | width:320px;
30 | height:240px
31 | }
32 |
33 | #container {
34 | position: relative;
35 | }
36 |
37 | #demoCanvas {
38 | border: 1px solid #000;
39 | }
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var PeerServer = require('peer').PeerServer;
3 |
4 | var options = {
5 | key: fs.readFileSync('key/server.key'),
6 | cert: fs.readFileSync('key/server.crt')
7 | }
8 |
9 | var server = PeerServer({
10 | port: 9000,
11 | ssl: options,
12 | path:"/"
13 | });
14 |
15 | /** 构建html页 */
16 | var https = require('https');
17 | var serveIndex = require('serve-index');
18 | var express = require("express");
19 | var htmlApp = express();
20 | htmlApp.use(serveIndex('./html'));
21 | htmlApp.use(express.static("./html"))
22 | var httpsServer = https.createServer(options, htmlApp);
23 | httpsServer.listen(81, "0.0.0.0");
24 |
25 |
--------------------------------------------------------------------------------
/key/server.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICVTCCAb4CCQCCDZ6FebPIqjANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJV
3 | UzENMAsGA1UECAwETWFyczETMBEGA1UEBwwKaVRyYW5zd2FycDETMBEGA1UECgwK
4 | aVRyYW5zd2FycDETMBEGA1UECwwKaVRyYW5zd2FycDESMBAGA1UEAwwJMTI3LjAu
5 | MC4xMB4XDTE4MDcxMDAzMTYzN1oXDTI4MDcwNzAzMTYzN1owbzELMAkGA1UEBhMC
6 | VVMxDTALBgNVBAgMBE1hcnMxEzARBgNVBAcMCmlUcmFuc3dhcnAxEzARBgNVBAoM
7 | CmlUcmFuc3dhcnAxEzARBgNVBAsMCmlUcmFuc3dhcnAxEjAQBgNVBAMMCTEyNy4w
8 | LjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1I6AQ6eNez85kcKjwy3g
9 | /vcnXtw+EbP4Ab37fLhIIWG+XzmEBAqnCYjM3nmlDIGEfNylGReo9mD2OHg46a1D
10 | wjd3pxTMit41pCTCiu8S9A2UJfbhSzQrfs+IZcNye4KR9/FzNEW6KoKQ0uc6X33E
11 | 0xe41hbRMQoKB3WmxvyN8PcCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAd7GNDtKWA
12 | 0OpSCzMu0pbmss9Erh4/RC8D+wK4+TXgPDKyZ6hX4FYPvk+ryMvwxJf0o4jjx5cx
13 | Yew7UjaKHlGXq+CNVRFYlltsbvO3oQTNkajuyYGWzMSuNxNsT3apOxH7SIu3qao8
14 | COSwj5FxZ2JU7O+SBVFZoJrFXEa+KJMQzQ==
15 | -----END CERTIFICATE-----
16 |
--------------------------------------------------------------------------------
/key/server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQDUjoBDp417PzmRwqPDLeD+9yde3D4Rs/gBvft8uEghYb5fOYQE
3 | CqcJiMzeeaUMgYR83KUZF6j2YPY4eDjprUPCN3enFMyK3jWkJMKK7xL0DZQl9uFL
4 | NCt+z4hlw3J7gpH38XM0RboqgpDS5zpffcTTF7jWFtExCgoHdabG/I3w9wIDAQAB
5 | AoGAWsy1BjGhQrDzisy24D3NC53Q97jl2vIiU7wwnkqqpXf3tv3+4ysZx/zkZ3VX
6 | iEwbqKso69srlnQ9OkpBJbGaa6lZe+z7BGzv2eJr+hKjjVjR122eDjAtXw+Tmt6c
7 | iBUG9+ITC1GdhXLEgTXtYuPq8hbDhoAVI007E+5JuQoO8kECQQD3aR6zGfR7EOmn
8 | byGkNMMPY/FoH894BpB4l7gIlNXH3pxBqukrEwnmVXSfak2PAkeLFO+bgCc6baIZ
9 | +R4iOLn/AkEA2++dIndbY7nmGs7Q//sM7MkFMiFQ4h1nN38V9AEHaUonxlwo+Nks
10 | PTAaVd78YIh6yBlfexm0Fxi1mEVQApqZCQJAFUPqyJglhGJqwuJxcMy8K1l6yWla
11 | isV9q2/W+J3aViiTI63OBs7HHg4gTQd1DSK0BYdSJPp55LLBqRvZdDWN/wJBAMQa
12 | M46exAL4p55xl9MWwyCB4LshD6B9vSGzlBx7qmMMNsjcNcAkzBhGwsScTYW5S1kN
13 | nqABfB037/s0mjGoLRkCQF18j4MovyFaj8VAqY8YUmf86Ez9JmL9kHNA8yEoSjuI
14 | xsRa6y5Nza5y83Mojt4W+PfS386riJ7txqrPdezyag4=
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/html/chat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | peerjs-chat
6 |
7 |
8 |
9 |
10 |
11 |
12 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/html/canvas.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | peerjs-canvas
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/html/sendfile.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | peerjs-send file
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/html/videocall.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | peerjs-video call
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
48 |
49 |
50 |
is calling you, do you accept?
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/html/js/chat.js:
--------------------------------------------------------------------------------
1 | var txtSelfId = document.querySelector("input#txtSelfId");
2 | var txtTargetId = document.querySelector("input#txtTargetId");
3 | var txtMsg = document.querySelector("input#txtMsg");
4 | var tdBox = document.querySelector("td#tdBox");
5 | var btnRegister = document.querySelector("button#btnRegister");
6 | var btnSend = document.querySelector("button#btnSend");
7 |
8 | let peer = null;
9 | let conn = null;
10 |
11 | //peer连接时,id不允许有中文,所以转换成hashcode数字
12 | hashCode = function (str) {
13 | var hash = 0;
14 | if (str.length == 0) return hash;
15 | for (i = 0; i < str.length; i++) {
16 | char = str.charCodeAt(i);
17 | hash = ((hash << 5) - hash) + char;
18 | hash = hash & hash;
19 | }
20 | return hash;
21 | }
22 |
23 | sendMessage = function (message) {
24 | conn.send(JSON.stringify(message));
25 | console.log(message);
26 | tdBox.innerHTML = tdBox.innerHTML += "" + message.from + " : " + message.body + "
";
27 | }
28 |
29 | window.onload = function () {
30 |
31 | //peerserver的连接选项(debug:3表示打开调试,将在浏览器的console输出详细日志)
32 | let connOption = { host: 'localhost', port: 9000, path: '/', debug: 3 };
33 |
34 | //register处理
35 | btnRegister.onclick = function () {
36 | if (!peer) {
37 | if (txtSelfId.value.length == 0) {
38 | alert("please input your name");
39 | txtSelfId.focus();
40 | return;
41 | }
42 | //创建peer实例
43 | peer = new Peer(hashCode(txtSelfId.value), connOption);
44 |
45 | //register成功的回调
46 | peer.on('open', function (id) {
47 | tdBox.innerHTML = tdBox.innerHTML += "system : register success " + id + "
";
48 | });
49 |
50 | peer.on('connection', (conn) => {
51 | //收到对方消息的回调
52 | conn.on('data', (data) => {
53 | var msg = JSON.parse(data);
54 | tdBox.innerHTML = tdBox.innerHTML += "" + msg.from + " : " + msg.body + "
";
55 | if (txtTargetId.value.length == 0) {
56 | txtTargetId.value = msg.from;
57 | }
58 | });
59 | });
60 | }
61 | }
62 |
63 | //发送消息处理
64 | btnSend.onclick = function () {
65 | //消息体
66 | var message = { "from": txtSelfId.value, "to": txtTargetId.value, "body": txtMsg.value };
67 | if (!conn) {
68 | if (txtTargetId.value.length == 0) {
69 | alert("please input target name");
70 | txtTargetId.focus();
71 | return;
72 | }
73 | if (txtMsg.value.length == 0) {
74 | alert("please input message");
75 | txtMsg.focus();
76 | return;
77 | }
78 |
79 | //创建到对方的连接
80 | conn = peer.connect(hashCode(txtTargetId.value));
81 | conn.on('open', () => {
82 | //首次发送消息
83 | sendMessage(message);
84 | });
85 | }
86 |
87 | //发送消息
88 | if (conn.open) {
89 | sendMessage(message);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/html/js/canvas.js:
--------------------------------------------------------------------------------
1 | let txtSelfId = document.querySelector("input#txtSelfId");
2 | let txtTargetId = document.querySelector("input#txtTargetId");
3 | let btnRegister = document.querySelector("button#btnRegister");
4 | let btnShare = document.querySelector("button#btnShare");
5 | let demoCanvas = document.querySelector("canvas#demoCanvas");
6 | let remoteVideo = document.querySelector("video#remoteVideo");
7 |
8 |
9 | let peer = null;
10 | let localConn = null;
11 | let localStream = null;
12 | let context = null;
13 | let started = false;
14 | let buffer = [];
15 |
16 | hashCode = function (str) {
17 | let hash = 0;
18 | if (str.length == 0) return hash;
19 | for (i = 0; i < str.length; i++) {
20 | char = str.charCodeAt(i);
21 | hash = ((hash << 5) - hash) + char;
22 | hash = hash & hash;
23 | }
24 | return hash;
25 | }
26 |
27 | function sendData(from, to, data) {
28 | if (from.length == 0 || to.length == 0 || data.length == 0) {
29 | return;
30 | }
31 | let message = { "from": from, "to": to, "data": data };
32 | if (!localConn) {
33 | localConn = peer.connect(hashCode(to));
34 | localConn.on('open', () => {
35 | localConn.send(JSON.stringify(message));
36 | console.log(message);
37 | });
38 | }
39 | if (localConn.open) {
40 | localConn.send(JSON.stringify(message));
41 | console.log(message);
42 | }
43 | }
44 |
45 | window.onload = function () {
46 | if (!navigator.mediaDevices ||
47 | !navigator.mediaDevices.getUserMedia) {
48 | console.log('webrtc is not supported!');
49 | alert("webrtc is not supported!");
50 | return;
51 | }
52 |
53 | let connOption = { host: 'localhost', port: 9000, path: '/', debug: 3 };
54 |
55 | context = demoCanvas.getContext('2d');
56 |
57 | //canvas鼠标按下的处理
58 | demoCanvas.onmousedown = function (e) {
59 | e.preventDefault();
60 | context.strokeStyle = '#00f';
61 | context.beginPath();
62 | started = true;
63 | buffer.push({ "x": e.offsetX, "y": e.offsetY });
64 | }
65 |
66 | //canvas鼠标移动的处理
67 | demoCanvas.onmousemove = function (e) {
68 | if (started) {
69 | context.lineTo(e.offsetX, e.offsetY);
70 | context.stroke();
71 | buffer.push({ "x": e.offsetX, "y": e.offsetY });
72 | }
73 | }
74 |
75 | //canvas鼠标抬起的处理
76 | demoCanvas.onmouseup = function (e) {
77 | if (started) {
78 | started = false;
79 | //鼠标抬起时,发送坐标数据
80 | sendData(txtSelfId.value, txtTargetId.value, buffer);
81 | buffer = [];
82 | }
83 | }
84 |
85 | //register按钮处理
86 | btnRegister.onclick = function () {
87 | if (!peer) {
88 | if (txtSelfId.value.length == 0) {
89 | alert("please input your name");
90 | txtSelfId.focus();
91 | return;
92 | }
93 | peer = new Peer(hashCode(txtSelfId.value), connOption);
94 | peer.on('open', function (id) {
95 | console.log("register success. " + id);
96 | });
97 | peer.on('connection', (conn) => {
98 | conn.on('data', (data) => {
99 | let msg = JSON.parse(data);
100 | console.log(msg);
101 | txtTargetId.value = msg.from;
102 | //还原canvas
103 | context.strokeStyle = '#f00';
104 | context.beginPath();
105 | context.moveTo(msg.data[0].x, msg.data[0].y);
106 | for (const pos in msg.data) {
107 | context.lineTo(msg.data[pos].x, msg.data[pos].y);
108 | }
109 | context.stroke();
110 | });
111 | });
112 | }
113 | }
114 |
115 | //share按钮处理
116 | btnShare.onclick = function () {
117 | if (txtTargetId.value.length == 0) {
118 | alert("please input target name");
119 | txtTargetId.focus();
120 | return;
121 | }
122 | }
123 |
124 | start();
125 | }
126 |
127 |
--------------------------------------------------------------------------------
/html/js/sendfile.js:
--------------------------------------------------------------------------------
1 | var txtSelfId = document.querySelector("input#txtSelfId");
2 | var txtTargetId = document.querySelector("input#txtTargetId");
3 | var btnRegister = document.querySelector("button#btnRegister");
4 | var btnCall = document.querySelector("button#btnCall");
5 | var inputFile = document.querySelector("input#inputFile");
6 | var img = document.querySelector("img#demoImage")
7 | var lblStatus = document.querySelector("label#lblStatus");
8 |
9 | let peer = null;
10 | let localConn = null;
11 | let localStream = null;
12 |
13 | hashCode = function (str) {
14 | var hash = 0;
15 | if (str.length == 0) return hash;
16 | for (i = 0; i < str.length; i++) {
17 | char = str.charCodeAt(i);
18 | hash = ((hash << 5) - hash) + char;
19 | hash = hash & hash;
20 | }
21 | return hash;
22 | }
23 |
24 | //base64编码
25 | const encode = input => {
26 | const keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
27 | let output = ''
28 | let chr1, chr2, chr3, enc1, enc2, enc3, enc4
29 | let i = 0
30 | while (i < input.length) {
31 | chr1 = input[i++]
32 | chr2 = i < input.length ? input[i++] : Number.NaN
33 | chr3 = i < input.length ? input[i++] : Number.NaN
34 | enc1 = chr1 >> 2
35 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4)
36 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6)
37 | enc4 = chr3 & 63
38 | if (isNaN(chr2)) {
39 | enc3 = enc4 = 64
40 | } else if (isNaN(chr3)) {
41 | enc4 = 64
42 | }
43 | output +=
44 | keyStr.charAt(enc1) +
45 | keyStr.charAt(enc2) +
46 | keyStr.charAt(enc3) +
47 | keyStr.charAt(enc4)
48 | }
49 | return output
50 | }
51 |
52 | function sendFile(from, to, blob, fileName, fileType) {
53 | var message = { "from": from, "to": to, "file": blob, "filename": fileName, "filetype": fileType };
54 | if (!localConn) {
55 | localConn = peer.connect(hashCode(to));
56 | localConn.on('open', () => {
57 | localConn.send(message);
58 | console.log('onopen sendfile');
59 | });
60 | }
61 | localConn.send(message);
62 | console.log('send file');
63 | }
64 |
65 | window.onload = function () {
66 | if (!navigator.mediaDevices ||
67 | !navigator.mediaDevices.getUserMedia) {
68 | console.log('webrtc is not supported!');
69 | alert("webrtc is not supported!");
70 | return;
71 | }
72 |
73 | let connOption = { host: 'localhost', port: 9000, path: '/', debug: 3 };
74 |
75 | btnRegister.onclick = function () {
76 | if (!peer) {
77 | if (txtSelfId.value.length == 0) {
78 | alert("please input your name");
79 | txtSelfId.focus();
80 | return;
81 | }
82 | peer = new Peer(hashCode(txtSelfId.value), connOption);
83 | peer.on('open', function (id) {
84 | console.log("register success. " + id);
85 | lblStatus.innerHTML = "scoket open"
86 | });
87 |
88 | peer.on('connection', (conn) => {
89 | conn.on('data', (data) => {
90 | console.log("receive remote data");
91 | lblStatus.innerHTML = "receive data from " + data.from;
92 | txtTargetId.value = data.from
93 | if (data.filetype.includes('image')) {
94 | lblStatus.innerHTML = data.filename + "(" + data.filetype + ") from:" + data.from
95 | const bytes = new Uint8Array(data.file)
96 | //用base64编码,还原图片
97 | img.src = 'data:image/png;base64,' + encode(bytes)
98 | }
99 | });
100 | });
101 | }
102 | }
103 |
104 | //文件变化时,触发sendFile
105 | inputFile.onchange = function (event) {
106 | if (txtTargetId.value.length == 0) {
107 | alert("please input target name");
108 | txtTargetId.focus();
109 | return;
110 | }
111 | const file = event.target.files[0]
112 | //构造图片对应的blob对象
113 | const blob = new Blob(event.target.files, { type: file.type });
114 | img.src = window.URL.createObjectURL(file);
115 | sendFile(txtSelfId.value, txtTargetId.value, blob, file.name, file.type);
116 | }
117 | }
118 |
119 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "peerjs-samples",
3 | "version": "1.0.0",
4 | "description": "node server.js cnpm install serve-index cnpm install peer",
5 | "main": "server.js",
6 | "dependencies": {
7 | "_accepts@1.3.7@accepts": "^1.3.7",
8 | "_array-flatten@1.1.1@array-flatten": "^1.1.1",
9 | "_batch@0.6.1@batch": "^0.6.1",
10 | "_body-parser@1.19.0@body-parser": "^1.19.0",
11 | "_async-limiter@1.0.1@async-limiter": "^1.0.1",
12 | "_bytes@3.1.0@bytes": "^3.1.0",
13 | "_content-type@1.0.4@content-type": "^1.0.4",
14 | "_cookie-signature@1.0.6@cookie-signature": "^1.0.6",
15 | "_content-disposition@0.5.3@content-disposition": "^0.5.3",
16 | "_cookie@0.4.0@cookie": "^0.4.0",
17 | "_cors@2.8.5@cors": "^2.8.5",
18 | "_debug@2.6.9@debug": "^2.6.9",
19 | "_depd@1.1.2@depd": "^1.1.2",
20 | "_destroy@1.0.4@destroy": "^1.0.4",
21 | "_ee-first@1.1.1@ee-first": "^1.1.1",
22 | "_escape-html@1.0.3@escape-html": "^1.0.3",
23 | "_etag@1.8.1@etag": "^1.8.1",
24 | "_express@4.17.1@express": "^4.17.1",
25 | "_encodeurl@1.0.2@encodeurl": "^1.0.2",
26 | "_finalhandler@1.1.2@finalhandler": "^1.1.2",
27 | "_forwarded@0.1.2@forwarded": "^0.1.2",
28 | "_fresh@0.5.2@fresh": "^0.5.2",
29 | "_http-errors@1.6.3@http-errors": "^1.6.3",
30 | "_http-errors@1.7.2@http-errors": "^1.7.2",
31 | "_inherits@2.0.3@inherits": "^2.0.3",
32 | "_http-errors@1.7.3@http-errors": "^1.7.3",
33 | "_inherits@2.0.4@inherits": "^2.0.4",
34 | "_ipaddr.js@1.9.0@ipaddr.js": "^1.9.0",
35 | "_media-typer@0.3.0@media-typer": "^0.3.0",
36 | "_merge-descriptors@1.0.1@merge-descriptors": "^1.0.1",
37 | "_methods@1.1.2@methods": "^1.1.2",
38 | "_mime-db@1.40.0@mime-db": "^1.40.0",
39 | "_iconv-lite@0.4.24@iconv-lite": "^0.4.24",
40 | "_mime-types@2.1.24@mime-types": "^2.1.24",
41 | "_mime@1.6.0@mime": "^1.6.0",
42 | "_minimist@0.0.10@minimist": "^0.0.10",
43 | "_ms@2.0.0@ms": "^2.0.0",
44 | "_ms@2.1.1@ms": "^2.1.1",
45 | "_negotiator@0.6.2@negotiator": "^0.6.2",
46 | "_object-assign@4.1.1@object-assign": "^4.1.1",
47 | "_on-finished@2.3.0@on-finished": "^2.3.0",
48 | "_parseurl@1.3.3@parseurl": "^1.3.3",
49 | "_optimist@0.6.1@optimist": "^0.6.1",
50 | "_peer@0.2.10@peer": "^0.2.10",
51 | "_path-to-regexp@0.1.7@path-to-regexp": "^0.1.7",
52 | "_proxy-addr@2.0.5@proxy-addr": "^2.0.5",
53 | "_qs@6.7.0@qs": "^6.7.0",
54 | "_range-parser@1.2.1@range-parser": "^1.2.1",
55 | "_raw-body@2.4.0@raw-body": "^2.4.0",
56 | "_safe-buffer@5.1.2@safe-buffer": "^5.1.2",
57 | "_safer-buffer@2.1.2@safer-buffer": "^2.1.2",
58 | "_send@0.17.1@send": "^0.17.1",
59 | "_serve-index@1.9.1@serve-index": "^1.9.1",
60 | "_serve-static@1.14.1@serve-static": "^1.14.1",
61 | "_setprototypeof@1.1.0@setprototypeof": "^1.1.0",
62 | "_setprototypeof@1.1.1@setprototypeof": "^1.1.1",
63 | "_statuses@1.5.0@statuses": "^1.5.0",
64 | "_toidentifier@1.0.0@toidentifier": "^1.0.0",
65 | "_type-is@1.6.18@type-is": "^1.6.18",
66 | "_unpipe@1.0.0@unpipe": "^1.0.0",
67 | "_utils-merge@1.0.1@utils-merge": "^1.0.1",
68 | "_vary@1.1.2@vary": "^1.1.2",
69 | "_wordwrap@0.0.3@wordwrap": "^0.0.3",
70 | "_ws@6.0.0@ws": "^6.0.0",
71 | "accepts": "^1.3.7",
72 | "array-flatten": "^1.1.1",
73 | "async-limiter": "^1.0.1",
74 | "body-parser": "^1.19.0",
75 | "batch": "^0.6.1",
76 | "bytes": "^3.1.0",
77 | "content-disposition": "^0.5.3",
78 | "cookie": "^0.4.0",
79 | "content-type": "^1.0.4",
80 | "cookie-signature": "^1.0.6",
81 | "cors": "^2.8.5",
82 | "depd": "^1.1.2",
83 | "debug": "^2.6.9",
84 | "destroy": "^1.0.4",
85 | "ee-first": "^1.1.1",
86 | "encodeurl": "^1.0.2",
87 | "escape-html": "^1.0.3",
88 | "etag": "^1.8.1",
89 | "express": "^4.17.1",
90 | "finalhandler": "^1.1.2",
91 | "fresh": "^0.5.2",
92 | "http-errors": "^1.7.3",
93 | "iconv-lite": "^0.4.24",
94 | "forwarded": "^0.1.2",
95 | "inherits": "^2.0.4",
96 | "ipaddr.js": "^1.9.0",
97 | "media-typer": "^0.3.0",
98 | "merge-descriptors": "^1.0.1",
99 | "mime": "^1.6.0",
100 | "methods": "^1.1.2",
101 | "mime-db": "^1.40.0",
102 | "mime-types": "^2.1.24",
103 | "minimist": "^1.2.5",
104 | "ms": "^2.1.1",
105 | "negotiator": "^0.6.2",
106 | "object-assign": "^4.1.1",
107 | "optimist": "^0.6.1",
108 | "on-finished": "^2.3.0",
109 | "path-to-regexp": "^0.1.7",
110 | "peer": "^0.2.10",
111 | "parseurl": "^1.3.3",
112 | "proxy-addr": "^2.0.5",
113 | "qs": "^6.7.0",
114 | "range-parser": "^1.2.1",
115 | "raw-body": "^2.4.0",
116 | "safe-buffer": "^5.1.2",
117 | "safer-buffer": "^2.1.2",
118 | "send": "^0.17.1",
119 | "serve-index": "^1.9.1",
120 | "serve-static": "^1.14.1",
121 | "setprototypeof": "^1.1.1",
122 | "statuses": "^1.5.0",
123 | "toidentifier": "^1.0.0",
124 | "type-is": "^1.6.18",
125 | "unpipe": "^1.0.0",
126 | "utils-merge": "^1.0.1",
127 | "vary": "^1.1.2",
128 | "wordwrap": "^0.0.3",
129 | "ws": "^6.0.0"
130 | },
131 | "devDependencies": {},
132 | "scripts": {
133 | "test": "echo \"Error: no test specified\" && exit 1",
134 | "start": "node server.js"
135 | },
136 | "repository": {
137 | "type": "git",
138 | "url": "git@git.dev.sh.ctripcorp.com:junmingyang/peerjs-samples.git"
139 | },
140 | "author": "",
141 | "license": "ISC"
142 | }
143 |
--------------------------------------------------------------------------------
/html/js/videocall.js:
--------------------------------------------------------------------------------
1 | var txtSelfId = document.querySelector("input#txtSelfId");
2 | var txtTargetId = document.querySelector("input#txtTargetId");
3 | var btnRegister = document.querySelector("button#btnRegister");
4 | var btnCall = document.querySelector("button#btnCall");
5 | var localVideo = document.querySelector("video#localVideo");
6 | var remoteVideo = document.querySelector("video#remoteVideo");
7 | var lblFrom = document.querySelector("label#lblFrom");
8 | var videoSelect = document.querySelector("select#videoSelect")
9 |
10 | let peer = null;
11 | let localConn = null;
12 | let localStream = null;
13 |
14 | hashCode = function (str) {
15 | var hash = 0;
16 | if (str.length == 0) return hash;
17 | for (i = 0; i < str.length; i++) {
18 | char = str.charCodeAt(i);
19 | hash = ((hash << 5) - hash) + char;
20 | hash = hash & hash;
21 | }
22 | return hash;
23 | }
24 |
25 | function gotStream(stream) {
26 | console.log('received local stream');
27 | localStream = stream;
28 | localVideo.srcObject = localStream;
29 | }
30 |
31 | function sendMessage(from, to, action) {
32 | var message = { "from": from, "to": to, "action": action };
33 | if (!localConn) {
34 | localConn = peer.connect(hashCode(to));
35 | localConn.on('open', () => {
36 | localConn.send(JSON.stringify(message));
37 | console.log(message);
38 | });
39 | }
40 | if (localConn.open){
41 | localConn.send(JSON.stringify(message));
42 | console.log(message);
43 | }
44 | }
45 |
46 | function handleError(error) {
47 | console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
48 | }
49 |
50 | //绑定摄像头列表到下拉框
51 | function gotDevices(deviceInfos) {
52 | if (deviceInfos===undefined){
53 | return
54 | }
55 | for (let i = 0; i !== deviceInfos.length; ++i) {
56 | const deviceInfo = deviceInfos[i];
57 | const option = document.createElement('option');
58 | option.value = deviceInfo.deviceId;
59 | if (deviceInfo.kind === 'videoinput') {
60 | option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
61 | videoSelect.appendChild(option);
62 | }
63 | }
64 | }
65 |
66 | //开启本地摄像头
67 | function start() {
68 | if (localStream) {
69 | localStream.getTracks().forEach(track => {
70 | track.stop();
71 | });
72 | }
73 |
74 | const videoSource = videoSelect.value;
75 | const constraints = {
76 | audio: false,
77 | video: { width: 320, deviceId: videoSource ? { exact: videoSource } : undefined }
78 | };
79 |
80 | navigator.mediaDevices
81 | .getUserMedia(constraints)
82 | .then(gotStream)
83 | .then(gotDevices)
84 | .catch(handleError);
85 | }
86 |
87 | window.onload = function () {
88 | if (!navigator.mediaDevices ||
89 | !navigator.mediaDevices.getUserMedia) {
90 | console.log('webrtc is not supported!');
91 | alert("webrtc is not supported!");
92 | return;
93 | }
94 |
95 | //获取摄像头列表
96 | navigator.mediaDevices.enumerateDevices()
97 | .then(gotDevices)
98 | .catch(handleError);
99 |
100 | $("#dialog-confirm").hide();
101 |
102 | //连接到peerjs服务器的选项
103 | let connOption = { host: 'localhost', port: 9000, path: '/', debug: 3 };
104 |
105 | //register处理
106 | btnRegister.onclick = function () {
107 | if (!peer) {
108 | if (txtSelfId.value.length == 0) {
109 | alert("please input your name");
110 | txtSelfId.focus();
111 | return;
112 | }
113 | peer = new Peer(hashCode(txtSelfId.value), connOption);
114 | peer.on('open', function (id) {
115 | console.log("register success. " + id);
116 | });
117 | peer.on('call', function (call) {
118 | call.answer(localStream);
119 | });
120 | peer.on('connection', (conn) => {
121 | conn.on('data', (data) => {
122 | var msg = JSON.parse(data);
123 | console.log(msg);
124 | //收到视频邀请时,弹出询问对话框
125 | if (msg.action === "call") {
126 | lblFrom.innerText = msg.from;
127 | txtTargetId.value = msg.from;
128 | $("#dialog-confirm").dialog({
129 | resizable: false,
130 | height: "auto",
131 | width: 400,
132 | modal: true,
133 | buttons: {
134 | "Accept": function () {
135 | $(this).dialog("close");
136 | sendMessage(msg.to, msg.from, "accept");
137 | },
138 | Cancel: function () {
139 | $(this).dialog("close");
140 | }
141 | }
142 | });
143 | }
144 |
145 | //接受视频通话邀请
146 | if (msg.action === "accept") {
147 | console.log("accept call => " + JSON.stringify(msg));
148 | var call = peer.call(hashCode(msg.from), localStream);
149 | call.on('stream', function (stream) {
150 | console.log('received remote stream');
151 | remoteVideo.srcObject = stream;
152 | sendMessage(msg.to, msg.from, "accept-ok");
153 | });
154 | }
155 |
156 | //接受视频通话邀请后,通知另一端
157 | if (msg.action === "accept-ok") {
158 | console.log("accept-ok call => " + JSON.stringify(msg));
159 | var call = peer.call(hashCode(msg.from), localStream);
160 | call.on('stream', function (stream) {
161 | console.log('received remote stream');
162 | remoteVideo.srcObject = stream;
163 | });
164 | }
165 | });
166 | });
167 | }
168 | }
169 |
170 | btnCall.onclick = function () {
171 | if (txtTargetId.value.length == 0) {
172 | alert("please input target name");
173 | txtTargetId.focus();
174 | return;
175 | }
176 | sendMessage(txtSelfId.value, txtTargetId.value, "call");
177 | }
178 |
179 | videoSelect.onchange = start;
180 |
181 | start();
182 | }
183 |
184 |
--------------------------------------------------------------------------------
/html/lib/peer.min.js:
--------------------------------------------------------------------------------
1 | !function o(s,a,u){function c(t,e){if(!a[t]){if(!s[t]){var i="function"==typeof require&&require;if(!e&&i)return i(t,!0);if(p)return p(t,!0);var n=new Error("Cannot find module '"+t+"'");throw n.code="MODULE_NOT_FOUND",n}var r=a[t]={exports:{}};s[t][0].call(r.exports,function(e){return c(s[t][1][e]||e)},r,r.exports,o,s,a,u)}return a[t].exports}for(var p="function"==typeof require&&require,e=0;ea.util.chunkedMTU)return void this._sendChunks(n);a.util.supports.sctp?a.util.supports.binaryBlob?this._bufferedSend(n):a.util.blobToArrayBuffer(n,function(e){i._bufferedSend(e)}):a.util.blobToBinaryString(n,function(e){i._bufferedSend(e)})}else this._bufferedSend(e)}else this.emit("error",new Error("Connection is not open. You should listen for the `open` event before sending messages."))},s.prototype._bufferedSend=function(e){!this._buffering&&this._trySend(e)||(this._buffer.push(e),this.bufferSize=this._buffer.length)},s.prototype._trySend=function(e){try{this._dc.send(e)}catch(e){this._buffering=!0;var t=this;return setTimeout(function(){t._buffering=!1,t._tryBuffer()},100),!1}return!0},s.prototype._tryBuffer=function(){if(0!==this._buffer.length){var e=this._buffer[0];this._trySend(e)&&(this._buffer.shift(),this.bufferSize=this._buffer.length,this._tryBuffer())}},s.prototype._sendChunks=function(e){for(var t=a.util.chunk(e),i=0,n=t.length;i>23&255)-127;return(0==e>>31?1:-1)*(8388607&e|8388608)*Math.pow(2,t-23)},s.prototype.unpack_double=function(){var e=this.unpack_uint32(),t=this.unpack_uint32(),i=(e>>20&2047)-1023;return(0==e>>31?1:-1)*((1048575&e|1048576)*Math.pow(2,i-20)+t*Math.pow(2,i-52))},s.prototype.read=function(e){var t=this.index;if(t+e<=this.length)return this.dataView.subarray(t,t+e);throw new Error("BinaryPackFailure: read index out of range")},a.prototype.getBuffer=function(){return this.bufferBuilder.getBuffer()},a.prototype.pack=function(e){var t=typeof e;if("string"==t)this.pack_string(e);else if("number"==t)Math.floor(e)===e?this.pack_integer(e):this.pack_double(e);else if("boolean"==t)!0===e?this.bufferBuilder.append(195):!1===e&&this.bufferBuilder.append(194);else if("undefined"==t)this.bufferBuilder.append(192);else{if("object"!=t)throw new Error('Type "'+t+'" not yet supported');if(null===e)this.bufferBuilder.append(192);else{var i=e.constructor;if(i==Array)this.pack_array(e);else if(i==Blob||i==File)this.pack_bin(e);else if(i==ArrayBuffer)r.useArrayBufferView?this.pack_bin(new Uint8Array(e)):this.pack_bin(e);else if("BYTES_PER_ELEMENT"in e)r.useArrayBufferView?this.pack_bin(new Uint8Array(e.buffer)):this.pack_bin(e.buffer);else if(i==Object)this.pack_object(e);else if(i==Date)this.pack_string(e.toString());else{if("function"!=typeof e.toBinaryPack)throw new Error('Type "'+i.toString()+'" not yet supported');this.bufferBuilder.append(e.toBinaryPack())}}}this.bufferBuilder.flush()},a.prototype.pack_bin=function(e){var t=e.length||e.byteLength||e.size;if(t<=15)this.pack_uint8(160+t);else if(t<=65535)this.bufferBuilder.append(218),this.pack_uint16(t);else{if(!(t<=4294967295))throw new Error("Invalid length");this.bufferBuilder.append(219),this.pack_uint32(t)}this.bufferBuilder.append(e)},a.prototype.pack_string=function(e){var t,i=600<(t=e).length?new Blob([t]).size:t.replace(/[^\u0000-\u007F]/g,u).length;if(i<=15)this.pack_uint8(176+i);else if(i<=65535)this.bufferBuilder.append(216),this.pack_uint16(i);else{if(!(i<=4294967295))throw new Error("Invalid length");this.bufferBuilder.append(217),this.pack_uint32(i)}this.bufferBuilder.append(e)},a.prototype.pack_array=function(e){var t=e.length;if(t<=15)this.pack_uint8(144+t);else if(t<=65535)this.bufferBuilder.append(220),this.pack_uint16(t);else{if(!(t<=4294967295))throw new Error("Invalid length");this.bufferBuilder.append(221),this.pack_uint32(t)}for(var i=0;i>8),this.bufferBuilder.append(255&e)},a.prototype.pack_uint32=function(e){var t=4294967295&e;this.bufferBuilder.append((4278190080&t)>>>24),this.bufferBuilder.append((16711680&t)>>>16),this.bufferBuilder.append((65280&t)>>>8),this.bufferBuilder.append(255&t)},a.prototype.pack_uint64=function(e){var t=e/Math.pow(2,32),i=e%Math.pow(2,32);this.bufferBuilder.append((4278190080&t)>>>24),this.bufferBuilder.append((16711680&t)>>>16),this.bufferBuilder.append((65280&t)>>>8),this.bufferBuilder.append(255&t),this.bufferBuilder.append((4278190080&i)>>>24),this.bufferBuilder.append((16711680&i)>>>16),this.bufferBuilder.append((65280&i)>>>8),this.bufferBuilder.append(255&i)},a.prototype.pack_int8=function(e){this.bufferBuilder.append(255&e)},a.prototype.pack_int16=function(e){this.bufferBuilder.append((65280&e)>>8),this.bufferBuilder.append(255&e)},a.prototype.pack_int32=function(e){this.bufferBuilder.append(e>>>24&255),this.bufferBuilder.append((16711680&e)>>>16),this.bufferBuilder.append((65280&e)>>>8),this.bufferBuilder.append(255&e)},a.prototype.pack_int64=function(e){var t=Math.floor(e/Math.pow(2,32)),i=e%Math.pow(2,32);this.bufferBuilder.append((4278190080&t)>>>24),this.bufferBuilder.append((16711680&t)>>>16),this.bufferBuilder.append((65280&t)>>>8),this.bufferBuilder.append(255&t),this.bufferBuilder.append((4278190080&i)>>>24),this.bufferBuilder.append((16711680&i)>>>16),this.bufferBuilder.append((65280&i)>>>8),this.bufferBuilder.append(255&i)}},{"./bufferbuilder":11}],11:[function(e,t,i){var n={};n.useBlobBuilder=function(){try{return new Blob([]),!1}catch(e){return!0}}(),n.useArrayBufferView=!n.useBlobBuilder&&function(){try{return 0===new Blob([new Uint8Array([])]).size}catch(e){return!0}}(),t.exports.binaryFeatures=n;var r=t.exports.BlobBuilder;function o(){this._pieces=[],this._parts=[]}"undefined"!=typeof window&&(r=t.exports.BlobBuilder=window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder||window.BlobBuilder),o.prototype.append=function(e){"number"==typeof e?this._pieces.push(e):(this.flush(),this._parts.push(e))},o.prototype.flush=function(){if(0=t.chunks.length?(c.log("Time: ",new Date-t.timer),delete this._outgoing[i]):this._processAcks()}break;case"chunk":if(!(t=n)){if(!0===this._received[i])break;t={ack:["ack",i,0],chunks:[]},this._incoming[i]=t}var a=e[2],u=e[3];t.chunks[a]=new Uint8Array(u),a===t.ack[2]&&this._calculateNextAck(i),this._ack(i);break;default:this._handleSend(e)}},n.prototype._chunk=function(e){for(var t=[],i=e.size,n=0;n=i.length&&n.push(["end",e,i.length]),n._multiple=!0,this._handleSend(n)},n.prototype._complete=function(e){c.log("Completed called for",e);var t=this,i=this._incoming[e].chunks,n=new Blob(i);c.blobToArrayBuffer(n,function(e){t.onmessage(c.unpack(e))}),delete this._incoming[e]},n.higherBandwidthSDP=function(e){var t=navigator.appVersion.match(/Chrome\/(.*?) /);if(t&&(t=parseInt(t[1].split(".").shift()))<31){var i=e.split("b=AS:30");if(1 .ui-controlgroup-item {
243 | float: left;
244 | margin-left: 0;
245 | margin-right: 0;
246 | }
247 | .ui-controlgroup > .ui-controlgroup-item:focus,
248 | .ui-controlgroup > .ui-controlgroup-item.ui-visual-focus {
249 | z-index: 9999;
250 | }
251 | .ui-controlgroup-vertical > .ui-controlgroup-item {
252 | display: block;
253 | float: none;
254 | width: 100%;
255 | margin-top: 0;
256 | margin-bottom: 0;
257 | text-align: left;
258 | }
259 | .ui-controlgroup-vertical .ui-controlgroup-item {
260 | box-sizing: border-box;
261 | }
262 | .ui-controlgroup .ui-controlgroup-label {
263 | padding: .4em 1em;
264 | }
265 | .ui-controlgroup .ui-controlgroup-label span {
266 | font-size: 80%;
267 | }
268 | .ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item {
269 | border-left: none;
270 | }
271 | .ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item {
272 | border-top: none;
273 | }
274 | .ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content {
275 | border-right: none;
276 | }
277 | .ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content {
278 | border-bottom: none;
279 | }
280 |
281 | /* Spinner specific style fixes */
282 | .ui-controlgroup-vertical .ui-spinner-input {
283 |
284 | /* Support: IE8 only, Android < 4.4 only */
285 | width: 75%;
286 | width: calc( 100% - 2.4em );
287 | }
288 | .ui-controlgroup-vertical .ui-spinner .ui-spinner-up {
289 | border-top-style: solid;
290 | }
291 |
292 | .ui-checkboxradio-label .ui-icon-background {
293 | box-shadow: inset 1px 1px 1px #ccc;
294 | border-radius: .12em;
295 | border: none;
296 | }
297 | .ui-checkboxradio-radio-label .ui-icon-background {
298 | width: 16px;
299 | height: 16px;
300 | border-radius: 1em;
301 | overflow: visible;
302 | border: none;
303 | }
304 | .ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,
305 | .ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon {
306 | background-image: none;
307 | width: 8px;
308 | height: 8px;
309 | border-width: 4px;
310 | border-style: solid;
311 | }
312 | .ui-checkboxradio-disabled {
313 | pointer-events: none;
314 | }
315 | .ui-datepicker {
316 | width: 17em;
317 | padding: .2em .2em 0;
318 | display: none;
319 | }
320 | .ui-datepicker .ui-datepicker-header {
321 | position: relative;
322 | padding: .2em 0;
323 | }
324 | .ui-datepicker .ui-datepicker-prev,
325 | .ui-datepicker .ui-datepicker-next {
326 | position: absolute;
327 | top: 2px;
328 | width: 1.8em;
329 | height: 1.8em;
330 | }
331 | .ui-datepicker .ui-datepicker-prev-hover,
332 | .ui-datepicker .ui-datepicker-next-hover {
333 | top: 1px;
334 | }
335 | .ui-datepicker .ui-datepicker-prev {
336 | left: 2px;
337 | }
338 | .ui-datepicker .ui-datepicker-next {
339 | right: 2px;
340 | }
341 | .ui-datepicker .ui-datepicker-prev-hover {
342 | left: 1px;
343 | }
344 | .ui-datepicker .ui-datepicker-next-hover {
345 | right: 1px;
346 | }
347 | .ui-datepicker .ui-datepicker-prev span,
348 | .ui-datepicker .ui-datepicker-next span {
349 | display: block;
350 | position: absolute;
351 | left: 50%;
352 | margin-left: -8px;
353 | top: 50%;
354 | margin-top: -8px;
355 | }
356 | .ui-datepicker .ui-datepicker-title {
357 | margin: 0 2.3em;
358 | line-height: 1.8em;
359 | text-align: center;
360 | }
361 | .ui-datepicker .ui-datepicker-title select {
362 | font-size: 1em;
363 | margin: 1px 0;
364 | }
365 | .ui-datepicker select.ui-datepicker-month,
366 | .ui-datepicker select.ui-datepicker-year {
367 | width: 45%;
368 | }
369 | .ui-datepicker table {
370 | width: 100%;
371 | font-size: .9em;
372 | border-collapse: collapse;
373 | margin: 0 0 .4em;
374 | }
375 | .ui-datepicker th {
376 | padding: .7em .3em;
377 | text-align: center;
378 | font-weight: bold;
379 | border: 0;
380 | }
381 | .ui-datepicker td {
382 | border: 0;
383 | padding: 1px;
384 | }
385 | .ui-datepicker td span,
386 | .ui-datepicker td a {
387 | display: block;
388 | padding: .2em;
389 | text-align: right;
390 | text-decoration: none;
391 | }
392 | .ui-datepicker .ui-datepicker-buttonpane {
393 | background-image: none;
394 | margin: .7em 0 0 0;
395 | padding: 0 .2em;
396 | border-left: 0;
397 | border-right: 0;
398 | border-bottom: 0;
399 | }
400 | .ui-datepicker .ui-datepicker-buttonpane button {
401 | float: right;
402 | margin: .5em .2em .4em;
403 | cursor: pointer;
404 | padding: .2em .6em .3em .6em;
405 | width: auto;
406 | overflow: visible;
407 | }
408 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
409 | float: left;
410 | }
411 |
412 | /* with multiple calendars */
413 | .ui-datepicker.ui-datepicker-multi {
414 | width: auto;
415 | }
416 | .ui-datepicker-multi .ui-datepicker-group {
417 | float: left;
418 | }
419 | .ui-datepicker-multi .ui-datepicker-group table {
420 | width: 95%;
421 | margin: 0 auto .4em;
422 | }
423 | .ui-datepicker-multi-2 .ui-datepicker-group {
424 | width: 50%;
425 | }
426 | .ui-datepicker-multi-3 .ui-datepicker-group {
427 | width: 33.3%;
428 | }
429 | .ui-datepicker-multi-4 .ui-datepicker-group {
430 | width: 25%;
431 | }
432 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
433 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
434 | border-left-width: 0;
435 | }
436 | .ui-datepicker-multi .ui-datepicker-buttonpane {
437 | clear: left;
438 | }
439 | .ui-datepicker-row-break {
440 | clear: both;
441 | width: 100%;
442 | font-size: 0;
443 | }
444 |
445 | /* RTL support */
446 | .ui-datepicker-rtl {
447 | direction: rtl;
448 | }
449 | .ui-datepicker-rtl .ui-datepicker-prev {
450 | right: 2px;
451 | left: auto;
452 | }
453 | .ui-datepicker-rtl .ui-datepicker-next {
454 | left: 2px;
455 | right: auto;
456 | }
457 | .ui-datepicker-rtl .ui-datepicker-prev:hover {
458 | right: 1px;
459 | left: auto;
460 | }
461 | .ui-datepicker-rtl .ui-datepicker-next:hover {
462 | left: 1px;
463 | right: auto;
464 | }
465 | .ui-datepicker-rtl .ui-datepicker-buttonpane {
466 | clear: right;
467 | }
468 | .ui-datepicker-rtl .ui-datepicker-buttonpane button {
469 | float: left;
470 | }
471 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
472 | .ui-datepicker-rtl .ui-datepicker-group {
473 | float: right;
474 | }
475 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
476 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
477 | border-right-width: 0;
478 | border-left-width: 1px;
479 | }
480 |
481 | /* Icons */
482 | .ui-datepicker .ui-icon {
483 | display: block;
484 | text-indent: -99999px;
485 | overflow: hidden;
486 | background-repeat: no-repeat;
487 | left: .5em;
488 | top: .3em;
489 | }
490 | .ui-dialog {
491 | position: absolute;
492 | top: 0;
493 | left: 0;
494 | padding: .2em;
495 | outline: 0;
496 | }
497 | .ui-dialog .ui-dialog-titlebar {
498 | padding: .4em 1em;
499 | position: relative;
500 | }
501 | .ui-dialog .ui-dialog-title {
502 | float: left;
503 | margin: .1em 0;
504 | white-space: nowrap;
505 | width: 90%;
506 | overflow: hidden;
507 | text-overflow: ellipsis;
508 | }
509 | .ui-dialog .ui-dialog-titlebar-close {
510 | position: absolute;
511 | right: .3em;
512 | top: 50%;
513 | width: 20px;
514 | margin: -10px 0 0 0;
515 | padding: 1px;
516 | height: 20px;
517 | }
518 | .ui-dialog .ui-dialog-content {
519 | position: relative;
520 | border: 0;
521 | padding: .5em 1em;
522 | background: none;
523 | overflow: auto;
524 | }
525 | .ui-dialog .ui-dialog-buttonpane {
526 | text-align: left;
527 | border-width: 1px 0 0 0;
528 | background-image: none;
529 | margin-top: .5em;
530 | padding: .3em 1em .5em .4em;
531 | }
532 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
533 | float: right;
534 | }
535 | .ui-dialog .ui-dialog-buttonpane button {
536 | margin: .5em .4em .5em 0;
537 | cursor: pointer;
538 | }
539 | .ui-dialog .ui-resizable-n {
540 | height: 2px;
541 | top: 0;
542 | }
543 | .ui-dialog .ui-resizable-e {
544 | width: 2px;
545 | right: 0;
546 | }
547 | .ui-dialog .ui-resizable-s {
548 | height: 2px;
549 | bottom: 0;
550 | }
551 | .ui-dialog .ui-resizable-w {
552 | width: 2px;
553 | left: 0;
554 | }
555 | .ui-dialog .ui-resizable-se,
556 | .ui-dialog .ui-resizable-sw,
557 | .ui-dialog .ui-resizable-ne,
558 | .ui-dialog .ui-resizable-nw {
559 | width: 7px;
560 | height: 7px;
561 | }
562 | .ui-dialog .ui-resizable-se {
563 | right: 0;
564 | bottom: 0;
565 | }
566 | .ui-dialog .ui-resizable-sw {
567 | left: 0;
568 | bottom: 0;
569 | }
570 | .ui-dialog .ui-resizable-ne {
571 | right: 0;
572 | top: 0;
573 | }
574 | .ui-dialog .ui-resizable-nw {
575 | left: 0;
576 | top: 0;
577 | }
578 | .ui-draggable .ui-dialog-titlebar {
579 | cursor: move;
580 | }
581 | .ui-draggable-handle {
582 | -ms-touch-action: none;
583 | touch-action: none;
584 | }
585 | .ui-resizable {
586 | position: relative;
587 | }
588 | .ui-resizable-handle {
589 | position: absolute;
590 | font-size: 0.1px;
591 | display: block;
592 | -ms-touch-action: none;
593 | touch-action: none;
594 | }
595 | .ui-resizable-disabled .ui-resizable-handle,
596 | .ui-resizable-autohide .ui-resizable-handle {
597 | display: none;
598 | }
599 | .ui-resizable-n {
600 | cursor: n-resize;
601 | height: 7px;
602 | width: 100%;
603 | top: -5px;
604 | left: 0;
605 | }
606 | .ui-resizable-s {
607 | cursor: s-resize;
608 | height: 7px;
609 | width: 100%;
610 | bottom: -5px;
611 | left: 0;
612 | }
613 | .ui-resizable-e {
614 | cursor: e-resize;
615 | width: 7px;
616 | right: -5px;
617 | top: 0;
618 | height: 100%;
619 | }
620 | .ui-resizable-w {
621 | cursor: w-resize;
622 | width: 7px;
623 | left: -5px;
624 | top: 0;
625 | height: 100%;
626 | }
627 | .ui-resizable-se {
628 | cursor: se-resize;
629 | width: 12px;
630 | height: 12px;
631 | right: 1px;
632 | bottom: 1px;
633 | }
634 | .ui-resizable-sw {
635 | cursor: sw-resize;
636 | width: 9px;
637 | height: 9px;
638 | left: -5px;
639 | bottom: -5px;
640 | }
641 | .ui-resizable-nw {
642 | cursor: nw-resize;
643 | width: 9px;
644 | height: 9px;
645 | left: -5px;
646 | top: -5px;
647 | }
648 | .ui-resizable-ne {
649 | cursor: ne-resize;
650 | width: 9px;
651 | height: 9px;
652 | right: -5px;
653 | top: -5px;
654 | }
655 | .ui-progressbar {
656 | height: 2em;
657 | text-align: left;
658 | overflow: hidden;
659 | }
660 | .ui-progressbar .ui-progressbar-value {
661 | margin: -1px;
662 | height: 100%;
663 | }
664 | .ui-progressbar .ui-progressbar-overlay {
665 | background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
666 | height: 100%;
667 | filter: alpha(opacity=25); /* support: IE8 */
668 | opacity: 0.25;
669 | }
670 | .ui-progressbar-indeterminate .ui-progressbar-value {
671 | background-image: none;
672 | }
673 | .ui-selectable {
674 | -ms-touch-action: none;
675 | touch-action: none;
676 | }
677 | .ui-selectable-helper {
678 | position: absolute;
679 | z-index: 100;
680 | border: 1px dotted black;
681 | }
682 | .ui-selectmenu-menu {
683 | padding: 0;
684 | margin: 0;
685 | position: absolute;
686 | top: 0;
687 | left: 0;
688 | display: none;
689 | }
690 | .ui-selectmenu-menu .ui-menu {
691 | overflow: auto;
692 | overflow-x: hidden;
693 | padding-bottom: 1px;
694 | }
695 | .ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
696 | font-size: 1em;
697 | font-weight: bold;
698 | line-height: 1.5;
699 | padding: 2px 0.4em;
700 | margin: 0.5em 0 0 0;
701 | height: auto;
702 | border: 0;
703 | }
704 | .ui-selectmenu-open {
705 | display: block;
706 | }
707 | .ui-selectmenu-text {
708 | display: block;
709 | margin-right: 20px;
710 | overflow: hidden;
711 | text-overflow: ellipsis;
712 | }
713 | .ui-selectmenu-button.ui-button {
714 | text-align: left;
715 | white-space: nowrap;
716 | width: 14em;
717 | }
718 | .ui-selectmenu-icon.ui-icon {
719 | float: right;
720 | margin-top: 0;
721 | }
722 | .ui-slider {
723 | position: relative;
724 | text-align: left;
725 | }
726 | .ui-slider .ui-slider-handle {
727 | position: absolute;
728 | z-index: 2;
729 | width: 1.2em;
730 | height: 1.2em;
731 | cursor: default;
732 | -ms-touch-action: none;
733 | touch-action: none;
734 | }
735 | .ui-slider .ui-slider-range {
736 | position: absolute;
737 | z-index: 1;
738 | font-size: .7em;
739 | display: block;
740 | border: 0;
741 | background-position: 0 0;
742 | }
743 |
744 | /* support: IE8 - See #6727 */
745 | .ui-slider.ui-state-disabled .ui-slider-handle,
746 | .ui-slider.ui-state-disabled .ui-slider-range {
747 | filter: inherit;
748 | }
749 |
750 | .ui-slider-horizontal {
751 | height: .8em;
752 | }
753 | .ui-slider-horizontal .ui-slider-handle {
754 | top: -.3em;
755 | margin-left: -.6em;
756 | }
757 | .ui-slider-horizontal .ui-slider-range {
758 | top: 0;
759 | height: 100%;
760 | }
761 | .ui-slider-horizontal .ui-slider-range-min {
762 | left: 0;
763 | }
764 | .ui-slider-horizontal .ui-slider-range-max {
765 | right: 0;
766 | }
767 |
768 | .ui-slider-vertical {
769 | width: .8em;
770 | height: 100px;
771 | }
772 | .ui-slider-vertical .ui-slider-handle {
773 | left: -.3em;
774 | margin-left: 0;
775 | margin-bottom: -.6em;
776 | }
777 | .ui-slider-vertical .ui-slider-range {
778 | left: 0;
779 | width: 100%;
780 | }
781 | .ui-slider-vertical .ui-slider-range-min {
782 | bottom: 0;
783 | }
784 | .ui-slider-vertical .ui-slider-range-max {
785 | top: 0;
786 | }
787 | .ui-sortable-handle {
788 | -ms-touch-action: none;
789 | touch-action: none;
790 | }
791 | .ui-spinner {
792 | position: relative;
793 | display: inline-block;
794 | overflow: hidden;
795 | padding: 0;
796 | vertical-align: middle;
797 | }
798 | .ui-spinner-input {
799 | border: none;
800 | background: none;
801 | color: inherit;
802 | padding: .222em 0;
803 | margin: .2em 0;
804 | vertical-align: middle;
805 | margin-left: .4em;
806 | margin-right: 2em;
807 | }
808 | .ui-spinner-button {
809 | width: 1.6em;
810 | height: 50%;
811 | font-size: .5em;
812 | padding: 0;
813 | margin: 0;
814 | text-align: center;
815 | position: absolute;
816 | cursor: default;
817 | display: block;
818 | overflow: hidden;
819 | right: 0;
820 | }
821 | /* more specificity required here to override default borders */
822 | .ui-spinner a.ui-spinner-button {
823 | border-top-style: none;
824 | border-bottom-style: none;
825 | border-right-style: none;
826 | }
827 | .ui-spinner-up {
828 | top: 0;
829 | }
830 | .ui-spinner-down {
831 | bottom: 0;
832 | }
833 | .ui-tabs {
834 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
835 | padding: .2em;
836 | }
837 | .ui-tabs .ui-tabs-nav {
838 | margin: 0;
839 | padding: .2em .2em 0;
840 | }
841 | .ui-tabs .ui-tabs-nav li {
842 | list-style: none;
843 | float: left;
844 | position: relative;
845 | top: 0;
846 | margin: 1px .2em 0 0;
847 | border-bottom-width: 0;
848 | padding: 0;
849 | white-space: nowrap;
850 | }
851 | .ui-tabs .ui-tabs-nav .ui-tabs-anchor {
852 | float: left;
853 | padding: .5em 1em;
854 | text-decoration: none;
855 | }
856 | .ui-tabs .ui-tabs-nav li.ui-tabs-active {
857 | margin-bottom: -1px;
858 | padding-bottom: 1px;
859 | }
860 | .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
861 | .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
862 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
863 | cursor: text;
864 | }
865 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
866 | cursor: pointer;
867 | }
868 | .ui-tabs .ui-tabs-panel {
869 | display: block;
870 | border-width: 0;
871 | padding: 1em 1.4em;
872 | background: none;
873 | }
874 | .ui-tooltip {
875 | padding: 8px;
876 | position: absolute;
877 | z-index: 9999;
878 | max-width: 300px;
879 | }
880 | body .ui-tooltip {
881 | border-width: 2px;
882 | }
883 | /* Component containers
884 | ----------------------------------*/
885 | .ui-widget {
886 | font-family: Arial,Helvetica,sans-serif;
887 | font-size: 1em;
888 | }
889 | .ui-widget .ui-widget {
890 | font-size: 1em;
891 | }
892 | .ui-widget input,
893 | .ui-widget select,
894 | .ui-widget textarea,
895 | .ui-widget button {
896 | font-family: Arial,Helvetica,sans-serif;
897 | font-size: 1em;
898 | }
899 | .ui-widget.ui-widget-content {
900 | border: 1px solid #c5c5c5;
901 | }
902 | .ui-widget-content {
903 | border: 1px solid #dddddd;
904 | background: #ffffff;
905 | color: #333333;
906 | }
907 | .ui-widget-content a {
908 | color: #333333;
909 | }
910 | .ui-widget-header {
911 | border: 1px solid #dddddd;
912 | background: #e9e9e9;
913 | color: #333333;
914 | font-weight: bold;
915 | }
916 | .ui-widget-header a {
917 | color: #333333;
918 | }
919 |
920 | /* Interaction states
921 | ----------------------------------*/
922 | .ui-state-default,
923 | .ui-widget-content .ui-state-default,
924 | .ui-widget-header .ui-state-default,
925 | .ui-button,
926 |
927 | /* We use html here because we need a greater specificity to make sure disabled
928 | works properly when clicked or hovered */
929 | html .ui-button.ui-state-disabled:hover,
930 | html .ui-button.ui-state-disabled:active {
931 | border: 1px solid #c5c5c5;
932 | background: #f6f6f6;
933 | font-weight: normal;
934 | color: #454545;
935 | }
936 | .ui-state-default a,
937 | .ui-state-default a:link,
938 | .ui-state-default a:visited,
939 | a.ui-button,
940 | a:link.ui-button,
941 | a:visited.ui-button,
942 | .ui-button {
943 | color: #454545;
944 | text-decoration: none;
945 | }
946 | .ui-state-hover,
947 | .ui-widget-content .ui-state-hover,
948 | .ui-widget-header .ui-state-hover,
949 | .ui-state-focus,
950 | .ui-widget-content .ui-state-focus,
951 | .ui-widget-header .ui-state-focus,
952 | .ui-button:hover,
953 | .ui-button:focus {
954 | border: 1px solid #cccccc;
955 | background: #ededed;
956 | font-weight: normal;
957 | color: #2b2b2b;
958 | }
959 | .ui-state-hover a,
960 | .ui-state-hover a:hover,
961 | .ui-state-hover a:link,
962 | .ui-state-hover a:visited,
963 | .ui-state-focus a,
964 | .ui-state-focus a:hover,
965 | .ui-state-focus a:link,
966 | .ui-state-focus a:visited,
967 | a.ui-button:hover,
968 | a.ui-button:focus {
969 | color: #2b2b2b;
970 | text-decoration: none;
971 | }
972 |
973 | .ui-visual-focus {
974 | box-shadow: 0 0 3px 1px rgb(94, 158, 214);
975 | }
976 | .ui-state-active,
977 | .ui-widget-content .ui-state-active,
978 | .ui-widget-header .ui-state-active,
979 | a.ui-button:active,
980 | .ui-button:active,
981 | .ui-button.ui-state-active:hover {
982 | border: 1px solid #003eff;
983 | background: #007fff;
984 | font-weight: normal;
985 | color: #ffffff;
986 | }
987 | .ui-icon-background,
988 | .ui-state-active .ui-icon-background {
989 | border: #003eff;
990 | background-color: #ffffff;
991 | }
992 | .ui-state-active a,
993 | .ui-state-active a:link,
994 | .ui-state-active a:visited {
995 | color: #ffffff;
996 | text-decoration: none;
997 | }
998 |
999 | /* Interaction Cues
1000 | ----------------------------------*/
1001 | .ui-state-highlight,
1002 | .ui-widget-content .ui-state-highlight,
1003 | .ui-widget-header .ui-state-highlight {
1004 | border: 1px solid #dad55e;
1005 | background: #fffa90;
1006 | color: #777620;
1007 | }
1008 | .ui-state-checked {
1009 | border: 1px solid #dad55e;
1010 | background: #fffa90;
1011 | }
1012 | .ui-state-highlight a,
1013 | .ui-widget-content .ui-state-highlight a,
1014 | .ui-widget-header .ui-state-highlight a {
1015 | color: #777620;
1016 | }
1017 | .ui-state-error,
1018 | .ui-widget-content .ui-state-error,
1019 | .ui-widget-header .ui-state-error {
1020 | border: 1px solid #f1a899;
1021 | background: #fddfdf;
1022 | color: #5f3f3f;
1023 | }
1024 | .ui-state-error a,
1025 | .ui-widget-content .ui-state-error a,
1026 | .ui-widget-header .ui-state-error a {
1027 | color: #5f3f3f;
1028 | }
1029 | .ui-state-error-text,
1030 | .ui-widget-content .ui-state-error-text,
1031 | .ui-widget-header .ui-state-error-text {
1032 | color: #5f3f3f;
1033 | }
1034 | .ui-priority-primary,
1035 | .ui-widget-content .ui-priority-primary,
1036 | .ui-widget-header .ui-priority-primary {
1037 | font-weight: bold;
1038 | }
1039 | .ui-priority-secondary,
1040 | .ui-widget-content .ui-priority-secondary,
1041 | .ui-widget-header .ui-priority-secondary {
1042 | opacity: .7;
1043 | filter:Alpha(Opacity=70); /* support: IE8 */
1044 | font-weight: normal;
1045 | }
1046 | .ui-state-disabled,
1047 | .ui-widget-content .ui-state-disabled,
1048 | .ui-widget-header .ui-state-disabled {
1049 | opacity: .35;
1050 | filter:Alpha(Opacity=35); /* support: IE8 */
1051 | background-image: none;
1052 | }
1053 | .ui-state-disabled .ui-icon {
1054 | filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
1055 | }
1056 |
1057 | /* Icons
1058 | ----------------------------------*/
1059 |
1060 | /* states and images */
1061 | .ui-icon {
1062 | width: 16px;
1063 | height: 16px;
1064 | }
1065 | .ui-icon,
1066 | .ui-widget-content .ui-icon {
1067 | background-image: url("images/ui-icons_444444_256x240.png");
1068 | }
1069 | .ui-widget-header .ui-icon {
1070 | background-image: url("images/ui-icons_444444_256x240.png");
1071 | }
1072 | .ui-state-hover .ui-icon,
1073 | .ui-state-focus .ui-icon,
1074 | .ui-button:hover .ui-icon,
1075 | .ui-button:focus .ui-icon {
1076 | background-image: url("images/ui-icons_555555_256x240.png");
1077 | }
1078 | .ui-state-active .ui-icon,
1079 | .ui-button:active .ui-icon {
1080 | background-image: url("images/ui-icons_ffffff_256x240.png");
1081 | }
1082 | .ui-state-highlight .ui-icon,
1083 | .ui-button .ui-state-highlight.ui-icon {
1084 | background-image: url("images/ui-icons_777620_256x240.png");
1085 | }
1086 | .ui-state-error .ui-icon,
1087 | .ui-state-error-text .ui-icon {
1088 | background-image: url("images/ui-icons_cc0000_256x240.png");
1089 | }
1090 | .ui-button .ui-icon {
1091 | background-image: url("images/ui-icons_777777_256x240.png");
1092 | }
1093 |
1094 | /* positioning */
1095 | .ui-icon-blank { background-position: 16px 16px; }
1096 | .ui-icon-caret-1-n { background-position: 0 0; }
1097 | .ui-icon-caret-1-ne { background-position: -16px 0; }
1098 | .ui-icon-caret-1-e { background-position: -32px 0; }
1099 | .ui-icon-caret-1-se { background-position: -48px 0; }
1100 | .ui-icon-caret-1-s { background-position: -65px 0; }
1101 | .ui-icon-caret-1-sw { background-position: -80px 0; }
1102 | .ui-icon-caret-1-w { background-position: -96px 0; }
1103 | .ui-icon-caret-1-nw { background-position: -112px 0; }
1104 | .ui-icon-caret-2-n-s { background-position: -128px 0; }
1105 | .ui-icon-caret-2-e-w { background-position: -144px 0; }
1106 | .ui-icon-triangle-1-n { background-position: 0 -16px; }
1107 | .ui-icon-triangle-1-ne { background-position: -16px -16px; }
1108 | .ui-icon-triangle-1-e { background-position: -32px -16px; }
1109 | .ui-icon-triangle-1-se { background-position: -48px -16px; }
1110 | .ui-icon-triangle-1-s { background-position: -65px -16px; }
1111 | .ui-icon-triangle-1-sw { background-position: -80px -16px; }
1112 | .ui-icon-triangle-1-w { background-position: -96px -16px; }
1113 | .ui-icon-triangle-1-nw { background-position: -112px -16px; }
1114 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
1115 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
1116 | .ui-icon-arrow-1-n { background-position: 0 -32px; }
1117 | .ui-icon-arrow-1-ne { background-position: -16px -32px; }
1118 | .ui-icon-arrow-1-e { background-position: -32px -32px; }
1119 | .ui-icon-arrow-1-se { background-position: -48px -32px; }
1120 | .ui-icon-arrow-1-s { background-position: -65px -32px; }
1121 | .ui-icon-arrow-1-sw { background-position: -80px -32px; }
1122 | .ui-icon-arrow-1-w { background-position: -96px -32px; }
1123 | .ui-icon-arrow-1-nw { background-position: -112px -32px; }
1124 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
1125 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
1126 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
1127 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
1128 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
1129 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
1130 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
1131 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
1132 | .ui-icon-arrowthick-1-n { background-position: 1px -48px; }
1133 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
1134 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
1135 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
1136 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
1137 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
1138 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
1139 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
1140 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
1141 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
1142 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
1143 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
1144 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
1145 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
1146 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
1147 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
1148 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
1149 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
1150 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
1151 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
1152 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
1153 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
1154 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
1155 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
1156 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
1157 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
1158 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
1159 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
1160 | .ui-icon-arrow-4 { background-position: 0 -80px; }
1161 | .ui-icon-arrow-4-diag { background-position: -16px -80px; }
1162 | .ui-icon-extlink { background-position: -32px -80px; }
1163 | .ui-icon-newwin { background-position: -48px -80px; }
1164 | .ui-icon-refresh { background-position: -64px -80px; }
1165 | .ui-icon-shuffle { background-position: -80px -80px; }
1166 | .ui-icon-transfer-e-w { background-position: -96px -80px; }
1167 | .ui-icon-transferthick-e-w { background-position: -112px -80px; }
1168 | .ui-icon-folder-collapsed { background-position: 0 -96px; }
1169 | .ui-icon-folder-open { background-position: -16px -96px; }
1170 | .ui-icon-document { background-position: -32px -96px; }
1171 | .ui-icon-document-b { background-position: -48px -96px; }
1172 | .ui-icon-note { background-position: -64px -96px; }
1173 | .ui-icon-mail-closed { background-position: -80px -96px; }
1174 | .ui-icon-mail-open { background-position: -96px -96px; }
1175 | .ui-icon-suitcase { background-position: -112px -96px; }
1176 | .ui-icon-comment { background-position: -128px -96px; }
1177 | .ui-icon-person { background-position: -144px -96px; }
1178 | .ui-icon-print { background-position: -160px -96px; }
1179 | .ui-icon-trash { background-position: -176px -96px; }
1180 | .ui-icon-locked { background-position: -192px -96px; }
1181 | .ui-icon-unlocked { background-position: -208px -96px; }
1182 | .ui-icon-bookmark { background-position: -224px -96px; }
1183 | .ui-icon-tag { background-position: -240px -96px; }
1184 | .ui-icon-home { background-position: 0 -112px; }
1185 | .ui-icon-flag { background-position: -16px -112px; }
1186 | .ui-icon-calendar { background-position: -32px -112px; }
1187 | .ui-icon-cart { background-position: -48px -112px; }
1188 | .ui-icon-pencil { background-position: -64px -112px; }
1189 | .ui-icon-clock { background-position: -80px -112px; }
1190 | .ui-icon-disk { background-position: -96px -112px; }
1191 | .ui-icon-calculator { background-position: -112px -112px; }
1192 | .ui-icon-zoomin { background-position: -128px -112px; }
1193 | .ui-icon-zoomout { background-position: -144px -112px; }
1194 | .ui-icon-search { background-position: -160px -112px; }
1195 | .ui-icon-wrench { background-position: -176px -112px; }
1196 | .ui-icon-gear { background-position: -192px -112px; }
1197 | .ui-icon-heart { background-position: -208px -112px; }
1198 | .ui-icon-star { background-position: -224px -112px; }
1199 | .ui-icon-link { background-position: -240px -112px; }
1200 | .ui-icon-cancel { background-position: 0 -128px; }
1201 | .ui-icon-plus { background-position: -16px -128px; }
1202 | .ui-icon-plusthick { background-position: -32px -128px; }
1203 | .ui-icon-minus { background-position: -48px -128px; }
1204 | .ui-icon-minusthick { background-position: -64px -128px; }
1205 | .ui-icon-close { background-position: -80px -128px; }
1206 | .ui-icon-closethick { background-position: -96px -128px; }
1207 | .ui-icon-key { background-position: -112px -128px; }
1208 | .ui-icon-lightbulb { background-position: -128px -128px; }
1209 | .ui-icon-scissors { background-position: -144px -128px; }
1210 | .ui-icon-clipboard { background-position: -160px -128px; }
1211 | .ui-icon-copy { background-position: -176px -128px; }
1212 | .ui-icon-contact { background-position: -192px -128px; }
1213 | .ui-icon-image { background-position: -208px -128px; }
1214 | .ui-icon-video { background-position: -224px -128px; }
1215 | .ui-icon-script { background-position: -240px -128px; }
1216 | .ui-icon-alert { background-position: 0 -144px; }
1217 | .ui-icon-info { background-position: -16px -144px; }
1218 | .ui-icon-notice { background-position: -32px -144px; }
1219 | .ui-icon-help { background-position: -48px -144px; }
1220 | .ui-icon-check { background-position: -64px -144px; }
1221 | .ui-icon-bullet { background-position: -80px -144px; }
1222 | .ui-icon-radio-on { background-position: -96px -144px; }
1223 | .ui-icon-radio-off { background-position: -112px -144px; }
1224 | .ui-icon-pin-w { background-position: -128px -144px; }
1225 | .ui-icon-pin-s { background-position: -144px -144px; }
1226 | .ui-icon-play { background-position: 0 -160px; }
1227 | .ui-icon-pause { background-position: -16px -160px; }
1228 | .ui-icon-seek-next { background-position: -32px -160px; }
1229 | .ui-icon-seek-prev { background-position: -48px -160px; }
1230 | .ui-icon-seek-end { background-position: -64px -160px; }
1231 | .ui-icon-seek-start { background-position: -80px -160px; }
1232 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
1233 | .ui-icon-seek-first { background-position: -80px -160px; }
1234 | .ui-icon-stop { background-position: -96px -160px; }
1235 | .ui-icon-eject { background-position: -112px -160px; }
1236 | .ui-icon-volume-off { background-position: -128px -160px; }
1237 | .ui-icon-volume-on { background-position: -144px -160px; }
1238 | .ui-icon-power { background-position: 0 -176px; }
1239 | .ui-icon-signal-diag { background-position: -16px -176px; }
1240 | .ui-icon-signal { background-position: -32px -176px; }
1241 | .ui-icon-battery-0 { background-position: -48px -176px; }
1242 | .ui-icon-battery-1 { background-position: -64px -176px; }
1243 | .ui-icon-battery-2 { background-position: -80px -176px; }
1244 | .ui-icon-battery-3 { background-position: -96px -176px; }
1245 | .ui-icon-circle-plus { background-position: 0 -192px; }
1246 | .ui-icon-circle-minus { background-position: -16px -192px; }
1247 | .ui-icon-circle-close { background-position: -32px -192px; }
1248 | .ui-icon-circle-triangle-e { background-position: -48px -192px; }
1249 | .ui-icon-circle-triangle-s { background-position: -64px -192px; }
1250 | .ui-icon-circle-triangle-w { background-position: -80px -192px; }
1251 | .ui-icon-circle-triangle-n { background-position: -96px -192px; }
1252 | .ui-icon-circle-arrow-e { background-position: -112px -192px; }
1253 | .ui-icon-circle-arrow-s { background-position: -128px -192px; }
1254 | .ui-icon-circle-arrow-w { background-position: -144px -192px; }
1255 | .ui-icon-circle-arrow-n { background-position: -160px -192px; }
1256 | .ui-icon-circle-zoomin { background-position: -176px -192px; }
1257 | .ui-icon-circle-zoomout { background-position: -192px -192px; }
1258 | .ui-icon-circle-check { background-position: -208px -192px; }
1259 | .ui-icon-circlesmall-plus { background-position: 0 -208px; }
1260 | .ui-icon-circlesmall-minus { background-position: -16px -208px; }
1261 | .ui-icon-circlesmall-close { background-position: -32px -208px; }
1262 | .ui-icon-squaresmall-plus { background-position: -48px -208px; }
1263 | .ui-icon-squaresmall-minus { background-position: -64px -208px; }
1264 | .ui-icon-squaresmall-close { background-position: -80px -208px; }
1265 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
1266 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
1267 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
1268 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
1269 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
1270 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
1271 |
1272 |
1273 | /* Misc visuals
1274 | ----------------------------------*/
1275 |
1276 | /* Corner radius */
1277 | .ui-corner-all,
1278 | .ui-corner-top,
1279 | .ui-corner-left,
1280 | .ui-corner-tl {
1281 | border-top-left-radius: 3px;
1282 | }
1283 | .ui-corner-all,
1284 | .ui-corner-top,
1285 | .ui-corner-right,
1286 | .ui-corner-tr {
1287 | border-top-right-radius: 3px;
1288 | }
1289 | .ui-corner-all,
1290 | .ui-corner-bottom,
1291 | .ui-corner-left,
1292 | .ui-corner-bl {
1293 | border-bottom-left-radius: 3px;
1294 | }
1295 | .ui-corner-all,
1296 | .ui-corner-bottom,
1297 | .ui-corner-right,
1298 | .ui-corner-br {
1299 | border-bottom-right-radius: 3px;
1300 | }
1301 |
1302 | /* Overlays */
1303 | .ui-widget-overlay {
1304 | background: #aaaaaa;
1305 | opacity: .3;
1306 | filter: Alpha(Opacity=30); /* support: IE8 */
1307 | }
1308 | .ui-widget-shadow {
1309 | -webkit-box-shadow: 0px 0px 5px #666666;
1310 | box-shadow: 0px 0px 5px #666666;
1311 | }
1312 |
--------------------------------------------------------------------------------