├── .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 | 13 | 14 | 19 | 26 | 27 | 28 | 30 | 31 |
15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 |
29 |
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 | 17 | 18 | 23 | 28 | 29 | 30 | 36 | 37 |
19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 |
31 |
32 | 33 | 34 |
35 |
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 | 17 | 18 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
33 | 34 |
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 | 17 | 18 | 23 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 39 | 42 | 43 | 44 | 45 | 46 | 47 |
19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 |
31 | 32 |
37 | 38 | 40 | 41 |
local videoremote video
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 | --------------------------------------------------------------------------------