├── public
├── css
│ ├── index.css
│ ├── index.min.css
│ ├── watcher.css
│ ├── watcher.min.css
│ ├── broadcast.min.css
│ └── broadcast.css
├── index
│ └── css
│ │ ├── index.min.css
│ │ └── watcher.min.css
├── user
│ └── css
│ │ └── broadcast.min.css
├── my_ng_templates
│ ├── my_ng_index.htm
│ ├── my_ng_broadcast.htm
│ ├── my_ng_broadcast.html
│ ├── my_ng_index.html
│ ├── my_ng_watcher.htm
│ └── my_ng_watcher.html
├── js
│ ├── es5-shim
│ │ ├── tests
│ │ │ ├── spec
│ │ │ │ ├── helpers-jasmine.js
│ │ │ │ ├── s-regexp.js
│ │ │ │ ├── s-error.js
│ │ │ │ ├── s-number.js
│ │ │ │ ├── s-global.js
│ │ │ │ └── s-function.js
│ │ │ ├── lib
│ │ │ │ ├── jasmine_favicon.png
│ │ │ │ ├── jasmine.css
│ │ │ │ └── jasmine-html.js
│ │ │ ├── helpers
│ │ │ │ └── h-matchers.js
│ │ │ ├── native.html
│ │ │ ├── index.min.html
│ │ │ └── index.html
│ │ ├── shims.json
│ │ ├── component.json
│ │ ├── bower.json
│ │ ├── LICENSE
│ │ ├── CONTRIBUTORS.md
│ │ ├── Makefile
│ │ ├── package.json
│ │ ├── es5-sham.min.js
│ │ ├── README.md
│ │ └── es5-sham.map
│ ├── jquery-ui-1.12.0
│ │ ├── images
│ │ │ ├── ui-icons_444444_256x240.png
│ │ │ ├── ui-icons_555555_256x240.png
│ │ │ ├── ui-icons_777620_256x240.png
│ │ │ ├── ui-icons_777777_256x240.png
│ │ │ ├── ui-icons_cc0000_256x240.png
│ │ │ └── ui-icons_ffffff_256x240.png
│ │ ├── LICENSE.txt
│ │ └── package.json
│ ├── index.min.js
│ ├── json3
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── lib
│ │ │ └── json3.min.js
│ │ └── README.md
│ ├── index.js
│ ├── html5shiv.min.js
│ ├── watcher.min.js
│ ├── respond.min.js
│ ├── broadcast.min.js
│ ├── watcher.js
│ ├── rtcClient.min.js
│ ├── broadcast.js
│ ├── fingerprint.js
│ └── adapter.min.js
├── imgs
│ └── icons
│ │ ├── favicon.ico
│ │ ├── apple-icon.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon-96x96.png
│ │ ├── ms-icon-144x144.png
│ │ ├── ms-icon-150x150.png
│ │ ├── ms-icon-310x310.png
│ │ ├── ms-icon-70x70.png
│ │ ├── apple-icon-57x57.png
│ │ ├── apple-icon-60x60.png
│ │ ├── apple-icon-72x72.png
│ │ ├── apple-icon-76x76.png
│ │ ├── android-icon-144x144.png
│ │ ├── android-icon-192x192.png
│ │ ├── android-icon-36x36.png
│ │ ├── android-icon-48x48.png
│ │ ├── android-icon-72x72.png
│ │ ├── android-icon-96x96.png
│ │ ├── apple-icon-114x114.png
│ │ ├── apple-icon-120x120.png
│ │ ├── apple-icon-144x144.png
│ │ ├── apple-icon-152x152.png
│ │ ├── apple-icon-180x180.png
│ │ ├── apple-icon-precomposed.png
│ │ ├── browserconfig.xml
│ │ └── manifest.json
└── jquery-ui
│ └── jquery-ui.theme.min.css
├── .gitignore
├── run.sh
├── my_mongodb
├── my_supervisord.conf
├── backup.sh
├── my_docker_mongo.sh
├── run.sh
├── config.sh
├── mongodb-keyfile
└── Dockerfile
├── server_mobile_test.js
├── views
├── index.jade
├── watcher.jade
├── broadcast.jade
└── base.jade
├── my_binaryjs
└── binaryjsServer.js
├── package.json
├── routes
└── routes.js
├── my_nginx
└── nginx.conf.template
├── Gruntfile.js
├── server.js
├── my_modules
├── streams.js
└── socketHandler.js
└── README.md
/public/css/index.css:
--------------------------------------------------------------------------------
1 | /**/
--------------------------------------------------------------------------------
/public/css/index.min.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/css/watcher.css:
--------------------------------------------------------------------------------
1 | /**/
--------------------------------------------------------------------------------
/public/css/watcher.min.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/index/css/index.min.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/index/css/watcher.min.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/user/css/broadcast.min.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/my_ng_templates/my_ng_index.htm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/my_ng_templates/my_ng_broadcast.htm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/my_ng_templates/my_ng_broadcast.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/my_ng_templates/my_ng_index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/my_ng_templates/my_ng_watcher.htm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/my_ng_templates/my_ng_watcher.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | my_binaryjs/videos/
3 | backup/
4 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/spec/helpers-jasmine.js:
--------------------------------------------------------------------------------
1 | /* globals require */
2 | require('../helpers/h-matchers');
3 |
--------------------------------------------------------------------------------
/public/imgs/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/favicon.ico
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon.png
--------------------------------------------------------------------------------
/public/imgs/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/imgs/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/imgs/icons/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/favicon-96x96.png
--------------------------------------------------------------------------------
/public/imgs/icons/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/ms-icon-144x144.png
--------------------------------------------------------------------------------
/public/imgs/icons/ms-icon-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/ms-icon-150x150.png
--------------------------------------------------------------------------------
/public/imgs/icons/ms-icon-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/ms-icon-310x310.png
--------------------------------------------------------------------------------
/public/imgs/icons/ms-icon-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/ms-icon-70x70.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-57x57.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-60x60.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-72x72.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-76x76.png
--------------------------------------------------------------------------------
/public/imgs/icons/android-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/android-icon-144x144.png
--------------------------------------------------------------------------------
/public/imgs/icons/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/android-icon-192x192.png
--------------------------------------------------------------------------------
/public/imgs/icons/android-icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/android-icon-36x36.png
--------------------------------------------------------------------------------
/public/imgs/icons/android-icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/android-icon-48x48.png
--------------------------------------------------------------------------------
/public/imgs/icons/android-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/android-icon-72x72.png
--------------------------------------------------------------------------------
/public/imgs/icons/android-icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/android-icon-96x96.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-114x114.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-120x120.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-144x144.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-152x152.png
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-180x180.png
--------------------------------------------------------------------------------
/public/js/es5-shim/shims.json:
--------------------------------------------------------------------------------
1 | {
2 | "Object": {
3 | "prototype": {},
4 | "keys": "object-keys"
5 | }
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/public/imgs/icons/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/imgs/icons/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/lib/jasmine_favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/js/es5-shim/tests/lib/jasmine_favicon.png
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #./bin/bash
2 |
3 | # start node server
4 | forever start .
5 |
6 | # start binaryjs server
7 | forever start ./my_binaryjs/binaryjsServer.js
8 |
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/images/ui-icons_444444_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/js/jquery-ui-1.12.0/images/ui-icons_444444_256x240.png
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/images/ui-icons_555555_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/js/jquery-ui-1.12.0/images/ui-icons_555555_256x240.png
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/images/ui-icons_777620_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/js/jquery-ui-1.12.0/images/ui-icons_777620_256x240.png
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/images/ui-icons_777777_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/js/jquery-ui-1.12.0/images/ui-icons_777777_256x240.png
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/images/ui-icons_cc0000_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/js/jquery-ui-1.12.0/images/ui-icons_cc0000_256x240.png
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/images/ui-icons_ffffff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gogistics/prjWebRTCDemo/HEAD/public/js/jquery-ui-1.12.0/images/ui-icons_ffffff_256x240.png
--------------------------------------------------------------------------------
/my_mongodb/my_supervisord.conf:
--------------------------------------------------------------------------------
1 | [program:my_supervisord]
2 | command=/bin/bash /my_scripts/backup.sh
3 | autostart=true
4 | autorestart=true
5 | stderr_logfile=/var/log/long.err.log
6 | stdout_logfile=/var/log/long.out.log
--------------------------------------------------------------------------------
/public/css/broadcast.min.css:
--------------------------------------------------------------------------------
1 | .video_container,body,html{height:100%}.is_recording{-webkit-animation:shadow_alert 3s inifite;-moz-animation:shadow_alert 3s inifite;animation:shadow_alert 3s inifite}.is_not_recording{box-shadow:0 0 0}
--------------------------------------------------------------------------------
/public/imgs/icons/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 | #ffffff
--------------------------------------------------------------------------------
/public/css/broadcast.css:
--------------------------------------------------------------------------------
1 | /**/
2 | html,body{
3 | height: 100%;
4 | }
5 | .is_recording{
6 | -webkit-animation: shadow_alert 3s inifite;
7 | -moz-animation: shadow_alert 3s inifite;
8 | animation: shadow_alert 3s inifite;
9 | }
10 | .is_not_recording{
11 | box-shadow: 0px 0px 0px;
12 | }
13 | .video_container{
14 | height: 100%;
15 | }
16 |
--------------------------------------------------------------------------------
/my_mongodb/backup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # backup mongodb
3 | while true
4 | do
5 | # set timestamp
6 | timestamp=$(date +"%s")
7 | backup_file_name="mongodump-$timestamp"
8 | # backup mongodb; username: test_user; password: standalonetestuser
9 | mongodump --db webrtc --username webrtc_user --password standalonewebrtcuser --out /data/backup/$backup_file_name
10 | sleep 12h
11 | done
--------------------------------------------------------------------------------
/server_mobile_test.js:
--------------------------------------------------------------------------------
1 | var server = require('http').createServer();
2 | var io = require('socket.io')(server);
3 |
4 | io.sockets.on('connection', function (socket) {
5 | console.log('socket connected');
6 |
7 | socket.on('disconnect', function () {
8 | console.log('socket disconnected');
9 | });
10 |
11 | socket.emit('text', 'wow. such event. very real time.');
12 | });
13 |
14 | server.listen(3000);
15 |
--------------------------------------------------------------------------------
/public/js/es5-shim/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "es5-shim",
3 | "repo": "es-shims/es5-shim",
4 | "description": "ECMAScript 5 compatibility shims for legacy JavaScript engines",
5 | "version": "v4.5.1",
6 | "keywords": [
7 | "shim",
8 | "es5",
9 | "es5 shim",
10 | "javascript",
11 | "ecmascript",
12 | "polyfill"
13 | ],
14 | "license": "MIT",
15 | "main": "es5-shim.js",
16 | "scripts": [
17 | "es5-shim.js"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/views/index.jade:
--------------------------------------------------------------------------------
1 | extends base
2 |
3 | block css_block
4 | // required css files
5 | link(href='/public/css/index.min.css' type='text/css' rel='stylesheet')
6 |
7 | block content
8 | div(ng-controller="indexCtrl as ctrl")
9 | div(class="container text-center")
10 | h3(class="text-center") Hello WebRTC
11 |
12 | block js_block
13 | // for IE 11 or browser is not IE
14 | script(type="text/javascript" src="/public/js/index.min.js")
15 |
16 | // for lt IE 9
17 |
20 |
--------------------------------------------------------------------------------
/public/js/index.min.js:
--------------------------------------------------------------------------------
1 | /*! webrtc 20-10-2016 */!function($){function initApp(){window.indexApp=window.indexApp||angular.module("indexApp",[],function($locationProvider,$interpolateProvider){$locationProvider.html5Mode(!0),$interpolateProvider.startSymbol("[["),$interpolateProvider.endSymbol("]]")}),window.indexApp.value("APP_VALUES",{EMAIL:"gogistics@gogistics-tw.com"}),window.indexApp.config(function(){}),window.indexApp.run(function(){}),window.indexApp.service("dataProvider",function($http,APP_VALUES){this.getStreams=function(arg_url,arg_headers,arg_data){}}),window.indexApp.controller("indexCtrl",["$scope","dataProvider",function($scope,dataProvider){alert("Index Ctrl")}])}angular.element(document).ready(function(){angular.bootstrap(document.body,["indexApp"])}),initApp()}(jQuery);
--------------------------------------------------------------------------------
/my_mongodb/my_docker_mongo.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | ### BEGIN INIT INFO
4 | # Required-Start: $remote_fs $syslog
5 | # Required-Stop: $remote_fs $syslog
6 | # Default-Start: 2 3 4 5
7 | # Default-Stop: 0 1 6
8 | # Short-Description: Simple script to start a mongo container
9 | ### END INIT INFO
10 |
11 | # If you want a command to always run, put it here
12 |
13 | # Carry out specific functions when asked to by the system
14 | case "$1" in
15 | start)
16 | echo "Starting standalone mongo container"
17 | # run application you want to start
18 | docker restart mongo_standalone
19 | ;;
20 | stop)
21 | echo "Stopping standalone mongo container"
22 | # kill application you want to stop
23 | killall mongod
24 | ;;
25 | *)
26 | echo "Usage: /etc/init.d/my_docker_mongo {start|stop}"
27 | exit 1
28 | ;;
29 | esac
30 |
31 | exit 0
--------------------------------------------------------------------------------
/my_mongodb/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # check if image exist; if not exist, create new image
3 | app_image='alantai/mongo_standalone'
4 | inspect_result=$(docker inspect $app_image)
5 | if [ "[]" == "$inspect_result" ]; then
6 | echo "image does not exist"
7 | docker build -t ${app_image} .
8 | fi
9 |
10 | # run main container
11 | app_container='mongo_standalone'
12 | inspect_result=$(docker inspect $app_container)
13 | if [ "[]" == "$inspect_result" ]; then
14 | echo "container does not exist and a new one will be created..."
15 | docker run --name ${app_container} -p 27025:27027 -d ${app_image}
16 | else
17 | echo "this container already exists!"
18 | fi
19 |
20 | # run config.sh
21 | docker exec -d ${app_container} bash ./config.sh
22 |
23 | # backup
24 | docker exec -d ${app_container} bash ./backup.sh
25 |
26 | # check logs
27 | docker logs ${app_container}
--------------------------------------------------------------------------------
/public/imgs/icons/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "App",
3 | "icons": [
4 | {
5 | "src": "\/android-icon-36x36.png",
6 | "sizes": "36x36",
7 | "type": "image\/png",
8 | "density": "0.75"
9 | },
10 | {
11 | "src": "\/android-icon-48x48.png",
12 | "sizes": "48x48",
13 | "type": "image\/png",
14 | "density": "1.0"
15 | },
16 | {
17 | "src": "\/android-icon-72x72.png",
18 | "sizes": "72x72",
19 | "type": "image\/png",
20 | "density": "1.5"
21 | },
22 | {
23 | "src": "\/android-icon-96x96.png",
24 | "sizes": "96x96",
25 | "type": "image\/png",
26 | "density": "2.0"
27 | },
28 | {
29 | "src": "\/android-icon-144x144.png",
30 | "sizes": "144x144",
31 | "type": "image\/png",
32 | "density": "3.0"
33 | },
34 | {
35 | "src": "\/android-icon-192x192.png",
36 | "sizes": "192x192",
37 | "type": "image\/png",
38 | "density": "4.0"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/my_mongodb/config.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Program:
3 | # configure MongoDB
4 | # History:
5 | # 10/12/2016
6 | echo "use admin" >> authentication.js
7 | echo "db.createUser({user:'siteUserAdmin',pwd:'standaloneadmin',roles:[{role:'userAdminAnyDatabase',db:'admin'}]})" >> authentication.js
8 | echo "db.auth('siteUserAdmin', 'standaloneadmin')" >> authentication.js
9 | echo "db.createUser({user:'siteRootAdmin',pwd:'standaloneadmin',roles:[{role:'root',db:'admin'}]})" >> authentication.js
10 | echo "db.auth('siteRootAdmin', 'standaloneadmin')" >> authentication.js
11 | echo "use webrtc" >> authentication.js
12 | echo "db.createUser({user:'webrtc_user',pwd:'standalonewebrtcuser',roles:[{role:'readWrite',db:'webrtc'}]})" >> authentication.js
13 | echo "db.auth('webrtc_user', 'standalonewebrtcuser')" >> authentication.js
14 |
15 | # run script
16 | mongo < authentication.js
17 |
18 | # restart supervisord
19 | /etc/init.d/supervisor restart
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/helpers/h-matchers.js:
--------------------------------------------------------------------------------
1 | /* global beforeEach, expect */
2 |
3 | var has = Object.prototype.hasOwnProperty;
4 | var getKeys = function (o) {
5 | 'use strict';
6 |
7 | var key, a = [];
8 | for (key in o) {
9 | if (has.call(o, key)) {
10 | a.push(key);
11 | }
12 | }
13 | return a;
14 | };
15 |
16 | beforeEach(function () {
17 | 'use strict';
18 |
19 | this.addMatchers({
20 | toExactlyMatch: function (expected) {
21 | var a1, a2, l, i, key;
22 | var actual = this.actual;
23 |
24 | a1 = getKeys(actual);
25 | a2 = getKeys(expected);
26 |
27 | l = a1.length;
28 | if (l !== a2.length) {
29 | return false;
30 | }
31 | for (i = 0; i < l; i++) {
32 | key = a1[i];
33 | expect(key).toEqual(a2[i]);
34 | expect(actual[key]).toEqual(expected[key]);
35 | }
36 |
37 | return true;
38 | }
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/my_mongodb/mongodb-keyfile:
--------------------------------------------------------------------------------
1 | 73UWhV5i8CehXbeKTl+H0Q8gTg2M+fyPi8VI6EKmeGOuTvzV15wqabUTjmcwb3G7
2 | Cmvg0c6Ba004uy7r8sXYYDHL15nHjl4n4PIHSTrjaXACjCfjiEZefEWQ8/ycf/qC
3 | TRxHWT9SVdwQ+D+DmeBy0ZyV2tH5BD2Haj+qn16QjaWUch/NbisZiVc/pY3biKB9
4 | ZE+1tqYfEhmuyFoM8ECiNiHauS5P+mSTuLwaXCr+VIfPLxAmftn4/rVEuYeD7JAf
5 | Wzb4szun1MvJ8FwsT5jHUk5hK4ff0ryA6dsCMS+9YcGqEp+WUwUuJIGhHzZPIKNE
6 | PgfgNUYk+4Ci5VElOF78d1JkrXGzUmCe9B9f1ztgYs5DDKefABSbyXQEjeU3N3HD
7 | IfKlXn7F/z0AKMFF0t/XV1H5l3DqP9QNsrVFienx6yMY5sllsmpxaYi+q89nCx+j
8 | wBvleXjXjPC1atbB2Zmtxah8dqFALm+qELubqw5y4rBUL4cST+3oM224L/Pyn1EH
9 | FC/7rXW3TKOutKbMO3HqE2u1K19wYSuUQy2+K7wC05IZxz1PBD6b/RGahkInjgeO
10 | zVP45w57cCzeAJL1140g7JlR2rbryFQ+8exAVbGxM5DV4Qr00H5XlAkR96g7SUG7
11 | 0H1Q8LfOsiYxC8wzOSbd0TKqYIX3qRSslea18XiKh9cWrfaFCoSBe7CRR0kXl1Ci
12 | rESGDGB7/hjQsNWDwAAExsVLhteDIP8RfZvbo36tcA99/Rr1sEW2uT4yPEkhUgh1
13 | LK9dEM51GU4F6GkzHlbNQgsLV+z1+6g+HTH9UOZR1kk8gFDmpLuHH8B97at1NmTM
14 | KzyEH2OROBwoL6/uzwu7MWc0MlE3rgZImgepfy7uB32RCnY+OYdHTf3GC9qdcAGG
15 | EJONUX5GJVpqIsHGYkWecs2NMyoSQIW0BsH18vXQh/fez0x55FZcFqSTKPrNmfHN
16 | IUuKm3IzAsw4KOLAfc4vawg6kVw2
17 |
--------------------------------------------------------------------------------
/my_binaryjs/binaryjsServer.js:
--------------------------------------------------------------------------------
1 | var binaryServer = require('binaryjs').BinaryServer,
2 | server = binaryServer({port: 8888, chunkSize: 81920}),
3 | fs = require('fs'),
4 | exec = require('exec');
5 |
6 | server.on('connection', function(client) {
7 | //
8 | var file_writer = null;
9 | client.on('stream', function(stream, meta){
10 | // console.log(meta);
11 | var timestamp = new Date().getTime();
12 | file_writer = fs.createWriteStream( __dirname + '/videos/' + timestamp + '.webm');
13 | stream.pipe(file_writer);
14 |
15 | console.log(server.clients);
16 | console.log(stream._socket._socket.ondata);
17 |
18 | stream.on('data', function(data){
19 | console.log(data);
20 | });
21 |
22 | // file_writer
23 | stream.on('end', function() {
24 | file_writer.end();
25 | });
26 | });
27 | //
28 | client.on('close', function(){
29 | if(file_writer !== null){
30 | // end file_writer
31 | file_writer.end();
32 | }
33 | });
34 | });
35 |
36 | server.on('error', function(err){
37 | //
38 | console.log(err);
39 | });
40 |
--------------------------------------------------------------------------------
/public/js/json3/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-2014 Kit Cambridge.
2 | http://kitcambridge.be/
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | this software and associated documentation files (the "Software"), to deal in
6 | the Software without restriction, including without limitation the rights to
7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | of the Software, and to permit persons to whom the Software is furnished to do
9 | so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
--------------------------------------------------------------------------------
/public/js/es5-shim/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "es5-shim",
3 | "main": "es5-shim.js",
4 | "repository": {
5 | "type": "git",
6 | "url": "git://github.com/es-shims/es5-shim"
7 | },
8 | "homepage": "https://github.com/es-shims/es5-shim",
9 | "authors": [
10 | "Kris Kowal (http://github.com/kriskowal/)",
11 | "Sami Samhuri (http://samhuri.net/)",
12 | "Florian Schäfer (http://github.com/fschaefer)",
13 | "Irakli Gozalishvili (http://jeditoolkit.com)",
14 | "Kit Cambridge (http://kitcambridge.github.com)",
15 | "Jordan Harband (https://github.com/ljharb/)"
16 | ],
17 | "description": "ECMAScript 5 compatibility shims for legacy JavaScript engines",
18 | "keywords": [
19 | "shim",
20 | "es5",
21 | "es5 shim",
22 | "javascript",
23 | "ecmascript",
24 | "polyfill"
25 | ],
26 | "license": "MIT",
27 | "ignore": [
28 | "**/.*",
29 | "node_modules",
30 | "bower_components",
31 | "tests"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/public/js/es5-shim/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (C) 2009-2016 Kristopher Michael Kowal and contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/public/js/index.js:
--------------------------------------------------------------------------------
1 | /**/
2 | (function($){
3 | angular.element(document).ready(function(){
4 | // bootstrap App
5 | angular.bootstrap(document.body, ['indexApp']);
6 | });
7 |
8 | // init App
9 | initApp();
10 |
11 | function initApp(){
12 |
13 | // set module
14 | window.indexApp = window.indexApp || angular.module('indexApp', [], function($locationProvider, $interpolateProvider){
15 | $locationProvider.html5Mode(true);
16 | $interpolateProvider.startSymbol('[[');
17 | $interpolateProvider.endSymbol(']]');
18 | });
19 |
20 | // global values
21 | window.indexApp.value('APP_VALUES', {
22 | EMAIL: 'gogistics@gogistics-tw.com'
23 | });
24 |
25 | window.indexApp.config(function(){
26 | //
27 | });
28 |
29 | window.indexApp.run(function(){
30 | //
31 | });
32 |
33 | window.indexApp.service('dataProvider', function($http, APP_VALUES){
34 | this.getStreams = function(arg_url, arg_headers, arg_data){
35 | //
36 | }
37 | });
38 |
39 | window.indexApp.controller('indexCtrl', ['$scope', 'dataProvider', function($scope, dataProvider){
40 | var ctrl = this;
41 | alert('Index Ctrl');
42 | }]);
43 | }
44 | })(jQuery);
45 |
--------------------------------------------------------------------------------
/my_mongodb/Dockerfile:
--------------------------------------------------------------------------------
1 | # my mongodb standalone
2 | FROM mongo
3 |
4 | # File Author / Maintainer
5 | MAINTAINER Alan Tai
6 |
7 | # run commands
8 | RUN apt-get update && \
9 | DEBIAN_FRONTEND=noninteractive apt-get install -yq apt-utils git build-essential supervisor && \
10 | apt-get update && \
11 | apt-get clean
12 |
13 | # set env; may not necessary, it's up to you
14 | ENV backup_user=webrtc_user user_pwd=standalonewebrtcuser
15 |
16 | # create volume; /data/db for replica set /data/arb for arbiter
17 | VOLUME /data/db
18 |
19 | # add files to working dir; remember to have backup.sh and config.sh executable
20 | COPY ./backup.sh ./config.sh /my_scripts/
21 | COPY ./mongodb-keyfile /opt/keyfile/
22 | COPY ./my_supervisord.conf /etc/supervisor/conf.d/
23 |
24 | # directory
25 | ADD ./data/backup /data/backup
26 |
27 | # Install app dependencies
28 | RUN chmod 600 /opt/keyfile/mongodb-keyfile && \
29 | chown 999 /opt/keyfile/mongodb-keyfile && \
30 | chmod +x /my_scripts/backup.sh
31 |
32 | # set work dir
33 | WORKDIR /my_scripts
34 |
35 | # expose ports
36 | EXPOSE 27017
37 |
38 | # standalone --dbpath /data/db
39 | CMD mongod --dbpath /data/db --smallfiles --keyFile /opt/keyfile/mongodb-keyfile
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/spec/s-regexp.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect */
2 |
3 | describe('RegExp', function () {
4 | 'use strict';
5 |
6 | describe('#toString()', function () {
7 | describe('literals', function () {
8 | it('should return correct flags and in correct order', function () {
9 | expect(String(/pattern/)).toBe('/pattern/');
10 | expect(String(/pattern/i)).toBe('/pattern/i');
11 | expect(String(/pattern/mi)).toBe('/pattern/im');
12 | expect(String(/pattern/im)).toBe('/pattern/im');
13 | expect(String(/pattern/mgi)).toBe('/pattern/gim');
14 | });
15 | });
16 |
17 | describe('objects', function () {
18 | it('should return correct flags and in correct order', function () {
19 | expect(String(new RegExp('pattern'))).toBe('/pattern/');
20 | expect(String(new RegExp('pattern', 'i'))).toBe('/pattern/i');
21 | expect(String(new RegExp('pattern', 'mi'))).toBe('/pattern/im');
22 | expect(String(new RegExp('pattern', 'im'))).toBe('/pattern/im');
23 | expect(String(new RegExp('pattern', 'mgi'))).toBe('/pattern/gim');
24 | });
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/views/watcher.jade:
--------------------------------------------------------------------------------
1 | extends base
2 |
3 | block css_block
4 | // required css files
5 | link(href='/public/css/watcher.min.css' type='text/css' rel='stylesheet')
6 |
7 | block content
8 | div(ng-controller="watcherCtrl as ctrl")
9 | div(class="container text-center")
10 | div(class="col-xs-12")
11 | div(class="col-xs-12")
12 | h3 Remote Broadcasters List
13 | input(type="button" class="btn btn-default" value="Refresh" ng-click="ctrl.loadData()")
14 | // list and videos container
15 | br
16 | div(class="col-sm-3 col-xs-12")
17 | table
18 | thead
19 | tr
20 | th Stream
21 | th Watch
22 | tbody
23 | tr(ng-repeat="stream in ctrl.broadcastStreams track by $index")
24 | td
25 | span [[stream.name]]
26 | td
27 | a(href="#" ng-class="{ 'selected': stream.isPlaying }" ng-click="ctrl.view(stream)") [[ stream.isPlaying ? 'Cancel' : 'View' ]]
28 |
29 | div(class="col-sm-9 col-xs-12")
30 | div(id="remoteVideosContainer")
31 |
32 | block js_block
33 | // for IE 11 or browser is not IE
34 | script(type="text/javascript" src="/public/js/watcher.min.js")
35 |
36 | // for lt IE 9
37 |
40 |
--------------------------------------------------------------------------------
/public/js/es5-shim/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 |
2 | - kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License
3 | - tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal
4 | Project)
5 | - dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA
6 | - fschaefer Florian Schäfer Copyright (C) 2010 MIT License
7 | - Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License
8 | - kitcambridge Kit Cambridge Copyright (C) 2011 MIT License
9 | - kossnocorp Sasha Koss XXX TODO License or CLA
10 | - bryanforbes Bryan Forbes XXX TODO License or CLA
11 | - killdream Quildreen Motta Copyright (C) 2011 MIT Licence
12 | - michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD
13 | License
14 | - sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License
15 | - bbqsrc Brendan Molloy (C) 2011 Creative Commons Zero (public domain)
16 | - iwyg XXX TODO License or CLA
17 | - DomenicDenicola Domenic Denicola Copyright (C) 2011 MIT License
18 | - xavierm02 Montillet Xavier Copyright (C) 2011 MIT License
19 | - Raynos Jake Verbaten Copyright (C) 2011 MIT Licence
20 | - samsonjs Sami Samhuri Copyright (C) 2010 MIT License
21 | - rwldrn Rick Waldron Copyright (C) 2011 MIT License
22 | - lexer Alexey Zakharov XXX TODO License or CLA
23 | - 280 North Inc. (Now Motorola LLC, a subsidiary of Google Inc.)
24 | Copyright (C) 2009 MIT License
25 | - Steven Levithan Copyright (C) 2012 MIT License
26 | - Jordan Harband (C) 2013 MIT License
27 |
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webrtc",
3 | "version": "1.0.0",
4 | "description": "WebRTC Tutorial",
5 | "main": "server.js",
6 | "scripts": {
7 | "test": "./scripts/test"
8 | },
9 | "keywords": [
10 | "WebRTC",
11 | "Docker",
12 | "MongoDB",
13 | "Nginx",
14 | "Node.js",
15 | "Express.js",
16 | "Socket.io",
17 | "Angular.js",
18 | "Bootstrap"
19 | ],
20 | "author": "Alan Tai",
21 | "license": "MIT",
22 | "dependencies": {
23 | "angular": "^1.5.8",
24 | "binaryjs": "^0.2.1",
25 | "body-parser": "^1.15.2",
26 | "bootstrap": "^3.3.7",
27 | "connect-redis": "^3.1.0",
28 | "cookie-parser": "^1.4.3",
29 | "errorhandler": "^1.4.3",
30 | "exec": "^0.2.1",
31 | "express": "^4.14.0",
32 | "express-session": "^1.14.0",
33 | "fingerprintjs": "^0.5.3",
34 | "grunt": "^1.0.1",
35 | "grunt-cli": "^1.2.0",
36 | "grunt-contrib-cssmin": "^1.0.1",
37 | "grunt-contrib-htmlmin": "^2.0.0",
38 | "grunt-contrib-jshint": "^1.0.0",
39 | "grunt-contrib-uglify": "^2.0.0",
40 | "jade": "^1.11.0",
41 | "jquery": "^1.12.0",
42 | "jquery-ui": "^1.12.0",
43 | "method-override": "^2.3.6",
44 | "mongodb": "^2.2.8",
45 | "morgan": "^1.7.0",
46 | "redis": "^2.6.2",
47 | "serve-favicon": "^2.3.0",
48 | "socket.io": "^1.4.8",
49 | "socket.io-client": "^1.4.8",
50 | "socket.io-redis": "^1.1.1"
51 | },
52 | "devDependencies": {
53 | "mocha": "^3.0.2",
54 | "supertest": "^2.0.0"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/views/broadcast.jade:
--------------------------------------------------------------------------------
1 | extends base
2 |
3 | block css_block
4 | // required css files
5 | link(href='/public/css/broadcast.min.css' type='text/css' rel='stylesheet')
6 |
7 | block content
8 | div(ng-controller="broadcastCtrl as ctrl" ng-init="ctrl.init('#{user_type}')" class="video_container")
9 | div(class="container text-center")
10 | br
11 | input(class="btn btn-secondary" type="button" ng-click="ctrl.toggleCam()" value="[[ ctrl.cameraIsOn ? 'Stop' : 'Start' ]]")
12 | hr
13 | div(ng-show="ctrl.cameraIsOn")
14 | div(class="btn-group")
15 | input(type="button" class="btn btn-default" value="Start Recording" ng-click="ctrl.startRecording()" ng-disabled="!!ctrl.isRecording")
16 | input(type="button" class="btn btn-warning" value="Stop Recording" ng-click="ctrl.stopRecording()" ng-disabled="!ctrl.isRecording")
17 | br
18 | br
19 | div(class="col-lg-12 col-sm-12")
20 | video(id="localVideo" class="col-lg-9 col-sm-12 col-xs-12" ng-class="ctrl.isRecording ? 'is_recording' : 'is_not_recording' " muted="muted")
21 | div(class="col-lg-3 col-sm-12 col-xs-12")
22 | h4 Watchers
23 | span(ng-repeat="watcher in ctrl.watchers track by $index")
24 | span Watcher ID: [[watcher.watcher_id]]
25 |
26 | block js_block
27 | // for IE 11 or browser is not IE
28 | script(type="text/javascript" src="/public/js/broadcast.min.js")
29 |
30 | // for lt IE 9
31 |
34 |
--------------------------------------------------------------------------------
/routes/routes.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app, streams) {
2 |
3 | // GET index
4 | var index = function(req, res) {
5 | var params = { title: 'WebRTC Demo' };
6 | res.render('index.jade', params);
7 | };
8 |
9 | // GET watcher
10 | var watcher = function(req, res){
11 | var params = { ttile: 'Watcher', user_type: 'watcher'};
12 | res.render('watcher.jade', params);
13 | }
14 |
15 | // GET broadcast
16 | var broadcast = function(req, res){
17 | var params = { ttile: 'Broadcast', user_type: 'broadcast'};
18 | res.render('broadcast.jade', params);
19 | }
20 |
21 | // GET streams as JSON
22 | var getStreams = function(req, res){
23 | var user_type = req.body.user_type || 'broadcast';
24 | streams.getStreams(user_type, function(err, docs){
25 | if(!err){
26 | res.status(200).json({broadcastStreams: docs});
27 | }else{
28 | res.status(500).json([]);
29 | }
30 | });
31 | };
32 |
33 | var getWatchers = function(req, res){
34 | var broadcast_info = req.body;
35 | streams.getWatchers(broadcast_info['id'], function(err, docs){
36 | if(!err){
37 | res.status(200).json(docs);
38 | }else{
39 | res.status(500).json([]);
40 | }
41 | });
42 | }
43 |
44 | // set GET for login
45 | app.get('/', index);
46 |
47 | // watcher
48 | app.get('/watcher', watcher);
49 |
50 | // broadcast
51 | app.get('/broadcast', broadcast);
52 |
53 | // get stream info. in JSON
54 | app.post('/streams', getStreams);
55 |
56 | // get watchers
57 | app.post('/get-watchers', getWatchers);
58 | }
59 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/native.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Jasmine Spec Runner
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/index.min.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Jasmine Spec Runner
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/spec/s-error.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, xit, expect */
2 |
3 | describe('Error', function () {
4 | 'use strict';
5 |
6 | var supportsDescriptors = Object.defineProperty && (function () {
7 | try {
8 | var obj = {};
9 | Object.defineProperty(obj, 'x', { enumerable: false, value: obj });
10 | for (var _ in obj) { return false; }
11 | return obj.x === obj;
12 | } catch (e) { /* this is ES3 */
13 | return false;
14 | }
15 | }());
16 | var ifSupportsDescriptorsIt = supportsDescriptors ? it : xit;
17 |
18 | describe('#toString()', function () {
19 | it('stringifies a newed error properly', function () {
20 | var msg = 'test';
21 | var error = new RangeError(msg);
22 | expect(error.name).toBe('RangeError');
23 | expect(error.message).toBe(msg);
24 | expect(String(error)).toBe(error.name + ': ' + msg);
25 | });
26 |
27 | it('stringifies a thrown error properly', function () {
28 | var msg = 'test';
29 | var error;
30 | try {
31 | throw new RangeError(msg);
32 | } catch (e) {
33 | error = e;
34 | }
35 | expect(error.name).toBe('RangeError');
36 | expect(error.message).toBe(msg);
37 | expect(String(error)).toBe(error.name + ': ' + msg);
38 | });
39 | });
40 |
41 | describe('enumerability of prototype properties', function () {
42 | ifSupportsDescriptorsIt('#message', function () {
43 | expect(Object.prototype.propertyIsEnumerable.call(Error.prototype, 'message')).toBe(false);
44 | });
45 |
46 | ifSupportsDescriptorsIt('#name', function () {
47 | expect(Object.prototype.propertyIsEnumerable.call(Error.prototype, 'name')).toBe(false);
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Jasmine Spec Runner
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright jQuery Foundation and other contributors, https://jquery.org/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals. For exact contribution history, see the revision history
5 | available at https://github.com/jquery/jquery-ui
6 |
7 | The following license applies to all parts of this software except as
8 | documented below:
9 |
10 | ====
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining
13 | a copy of this software and associated documentation files (the
14 | "Software"), to deal in the Software without restriction, including
15 | without limitation the rights to use, copy, modify, merge, publish,
16 | distribute, sublicense, and/or sell copies of the Software, and to
17 | permit persons to whom the Software is furnished to do so, subject to
18 | the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be
21 | included in all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 |
31 | ====
32 |
33 | Copyright and related rights for sample code are waived via CC0. Sample
34 | code is defined as all source code contained within the demos directory.
35 |
36 | CC0: http://creativecommons.org/publicdomain/zero/1.0/
37 |
38 | ====
39 |
40 | All files located in the node_modules and external directories are
41 | externally maintained libraries used by this software which have their
42 | own licenses; we recommend you read them, as their terms may differ from
43 | the terms above.
44 |
--------------------------------------------------------------------------------
/public/js/jquery-ui-1.12.0/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-ui",
3 | "title": "jQuery UI",
4 | "description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.",
5 | "version": "1.12.0",
6 | "homepage": "http://jqueryui.com",
7 | "author": {
8 | "name": "jQuery Foundation and other contributors",
9 | "url": "https://github.com/jquery/jquery-ui/blob/1.12.0/AUTHORS.txt"
10 | },
11 | "main": "ui/widget.js",
12 | "maintainers": [
13 | {
14 | "name": "Scott González",
15 | "email": "scott.gonzalez@gmail.com",
16 | "url": "http://scottgonzalez.com"
17 | },
18 | {
19 | "name": "Jörn Zaefferer",
20 | "email": "joern.zaefferer@gmail.com",
21 | "url": "http://bassistance.de"
22 | },
23 | {
24 | "name": "Mike Sherov",
25 | "email": "mike.sherov@gmail.com",
26 | "url": "http://mike.sherov.com"
27 | },
28 | {
29 | "name": "TJ VanToll",
30 | "email": "tj.vantoll@gmail.com",
31 | "url": "http://tjvantoll.com"
32 | },
33 | {
34 | "name": "Felix Nagel",
35 | "email": "info@felixnagel.com",
36 | "url": "http://www.felixnagel.com"
37 | },
38 | {
39 | "name": "Alex Schmitz",
40 | "email": "arschmitz@gmail.com",
41 | "url": "https://github.com/arschmitz"
42 | }
43 | ],
44 | "repository": {
45 | "type": "git",
46 | "url": "git://github.com/jquery/jquery-ui.git"
47 | },
48 | "bugs": "https://bugs.jqueryui.com/",
49 | "license": "MIT",
50 | "scripts": {
51 | "test": "grunt"
52 | },
53 | "dependencies": {},
54 | "devDependencies": {
55 | "commitplease": "2.3.0",
56 | "grunt": "0.4.5",
57 | "grunt-bowercopy": "1.2.4",
58 | "grunt-cli": "0.1.13",
59 | "grunt-compare-size": "0.4.0",
60 | "grunt-contrib-concat": "0.5.1",
61 | "grunt-contrib-csslint": "0.5.0",
62 | "grunt-contrib-jshint": "0.12.0",
63 | "grunt-contrib-qunit": "1.0.1",
64 | "grunt-contrib-requirejs": "0.4.4",
65 | "grunt-contrib-uglify": "0.11.1",
66 | "grunt-git-authors": "3.1.0",
67 | "grunt-html": "6.0.0",
68 | "grunt-jscs": "2.1.0",
69 | "load-grunt-tasks": "3.4.0",
70 | "rimraf": "2.5.1",
71 | "testswarm": "1.1.0"
72 | },
73 | "keywords": []
74 | }
75 |
--------------------------------------------------------------------------------
/public/js/html5shiv.min.js:
--------------------------------------------------------------------------------
1 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g){return b.createElement(a)}c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache){b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()}a.createElement=function(c){return !e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x";c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden" in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g){return a.createDocumentFragment()}for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d 21', function () {
19 | expect(function () { return (0.9).toPrecision(0); }).toThrow();
20 | // Firefox allows values up to 100
21 | expect(function () { return (0.9).toPrecision(101); }).toThrow();
22 | });
23 |
24 | it('works as expected', function () {
25 | expect((0.00008).toPrecision(3)).toBe('0.0000800');
26 | expect((1.255).toPrecision(2)).toBe('1.3');
27 | expect((1843654265.0774949).toPrecision(13)).toBe('1843654265.077');
28 | expect(NaN.toPrecision(1)).toBe('NaN');
29 | });
30 |
31 | it('works with an undefined precision', function () {
32 | var num = 123.456;
33 | expect(num.toPrecision()).toBe(String(num));
34 | expect(num.toPrecision(undefined)).toBe(String(num));
35 | });
36 | });
37 |
38 | describe('constants', function () {
39 | it('should have MAX_VALUE', function () {
40 | expect(Number.MAX_VALUE).toBe(1.7976931348623157e308);
41 | });
42 |
43 | it('should have MIN_VALUE', function () {
44 | expect(Number.MIN_VALUE).toBe(5e-324);
45 | });
46 |
47 | it('should have NaN', function () {
48 | expect(Number.NaN).not.toBe(Number.NaN);
49 | });
50 |
51 | it('should have POSITIVE_INFINITY', function () {
52 | expect(Number.POSITIVE_INFINITY).toBe(Infinity);
53 | });
54 |
55 | it('should have NEGATIVE_INFINITY', function () {
56 | expect(Number.NEGATIVE_INFINITY).toBe(-Infinity);
57 | });
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/my_nginx/nginx.conf.template:
--------------------------------------------------------------------------------
1 | # configuration file for Nginx
2 | worker_processes 3;
3 | events { worker_connections 1024; }
4 |
5 | # http config.
6 | http {
7 | # buffer setting
8 | client_body_buffer_size 10K;
9 | client_header_buffer_size 1k;
10 | large_client_header_buffers 2 1k;
11 |
12 | # timeout setting
13 | client_body_timeout 16;
14 | client_header_timeout 16;
15 | keepalive_timeout 12;
16 | send_timeout 12;
17 |
18 | # gzip setting
19 | gzip on;
20 | gzip_http_version 1.1;
21 | gzip_vary on;
22 | gzip_comp_level 6;
23 | gzip_min_length 100;
24 | gzip_proxied any;
25 | gzip_types text/plain text/css application/json application/javascript application/x-javascript text/javascript text/xml application/xml application/rss+xml application/atom+xml application/rdf+xml;
26 | gzip_buffers 16 8k;
27 |
28 | # upstream config.
29 | upstream node-app {
30 | least_conn;
31 | server 127.0.0.1:8000 weight=10 max_fails=3 fail_timeout=20s;
32 | server 127.0.0.1:8001 weight=10 max_fails=3 fail_timeout=20s;
33 | server 127.0.0.1:8002 weight=10 max_fails=3 fail_timeout=20s;
34 |
35 | sticky learn create=$upstream_cookie_sessionid
36 | lookup=$cookie_sessionid
37 | zone=client_sessions:1m
38 | timeout=1h;
39 | }
40 |
41 | # server config.
42 | server {
43 | listen 80;
44 | location / {
45 | proxy_pass http://node-app;
46 | proxy_http_version 1.1;
47 | proxy_set_header Upgrade $http_upgrade;
48 | proxy_set_header Connection 'upgrade';
49 | proxy_set_header Host $host;
50 | proxy_cache_bypass $http_upgrade;
51 | expires modified +12h;
52 | }
53 | }
54 |
55 | # for https; not used in this tutorial
56 | server {
57 | listen 443 default_server ssl;
58 | server_name webrtc.gogistics-tw.com;
59 | return 444;
60 |
61 | ssl on;
62 | ssl_certificate /etc/ssl/gogistics/gogistics_combined.crt;
63 | ssl_certificate_key /etc/ssl/gogistics/gogistics.key;
64 | add_header Strict-Transport-Security "max-age=31536000";
65 |
66 | location / {
67 | proxy_pass http://node-app;
68 | proxy_http_version 1.1;
69 | proxy_set_header Upgrade $http_upgrade;
70 | proxy_set_header Connection 'upgrade';
71 | proxy_set_header Host $host;
72 | proxy_cache_bypass $http_upgrade;
73 | expires modified +12h;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /* Grunt Tasks */
2 | module.exports = function(grunt){
3 | grunt.initConfig({
4 | // basic setting
5 | pkg: grunt.file.readJSON('package.json'),
6 |
7 | // check JS code
8 | jshint: {
9 | files: ['Gruntfile.js', 'public/js/*.js'],
10 | options: {
11 | globals: {
12 | jQuery: true,
13 | },
14 | },
15 | },
16 |
17 | // minify css files
18 | cssmin:{
19 | combine: {
20 | files: {
21 | 'public/css/index.min.css': ['public/css/index.css'],
22 | 'public/css/watcher.min.css': ['public/css/watcher.css'],
23 | 'public/css/broadcast.min.css': ['public/css/broadcast.css']
24 | },
25 | },
26 | },
27 |
28 | // minify js
29 | uglify:{
30 | options: {
31 | banner: '\/\*\! \<\%\= pkg.name \%\> \<\%\= grunt.template.today\(\"dd-mm-yyyy\"\) \%\> \*\/',
32 | report: 'min',
33 | mangle: false
34 | },
35 | combine: {
36 | files: {
37 | 'public/js/adapter.min.js': ['public/js/adapter.js'],
38 | 'public/js/rtcClient.min.js': ['public/js/rtcClient.js'],
39 | 'public/js/rtcRecorder.min.js': ['public/js/rtcRecorder.js'],
40 | 'public/js/index.min.js': ['public/js/index.js'],
41 | 'public/js/watcher.min.js': ['public/js/watcher.js'],
42 | 'public/js/broadcast.min.js': ['public/js/broadcast.js']
43 | },
44 | },
45 | },
46 |
47 | // minify html
48 | htmlmin: {
49 | dist: {
50 | options: {
51 | removeComments: true,
52 | collapseWhitespace: true
53 | },
54 | files: {
55 | 'public/my_ng_templates/my_ng_index.html': 'public/my_ng_templates/my_ng_index.htm',
56 | 'public/my_ng_templates/my_ng_watcher.html': 'public/my_ng_templates/my_ng_watcher.htm',
57 | 'public/my_ng_templates/my_ng_broadcast.html': 'public/my_ng_templates/my_ng_broadcast.htm'
58 | }
59 | }
60 | }
61 | });
62 |
63 | // load the plugin
64 | grunt.loadNpmTasks('grunt-contrib-jshint');
65 | grunt.loadNpmTasks('grunt-contrib-cssmin');
66 | grunt.loadNpmTasks('grunt-contrib-uglify');
67 | grunt.loadNpmTasks('grunt-contrib-htmlmin');
68 |
69 | // register tasks
70 | grunt.registerTask('default', ['jshint', 'cssmin', 'uglify', 'htmlmin']);
71 | };
--------------------------------------------------------------------------------
/public/js/watcher.min.js:
--------------------------------------------------------------------------------
1 | /*! webrtc 20-10-2016 */!function($){function initApp(){window.watcherApp=window.watcherApp||angular.module("watcherApp",[],function($locationProvider,$interpolateProvider){$locationProvider.html5Mode(!0),$interpolateProvider.startSymbol("[["),$interpolateProvider.endSymbol("]]")}),window.watcherApp.value("APP_VALUES",{EMAIL:"gogistics@gogistics-tw.com",BINARY_STREAM:null,FINGERPRINT:null}),window.watcherApp.config(function(){}),window.watcherApp.run(function(){}),window.watcherApp.service("dataProvider",function($http,APP_VALUES){this.getStreams=function(arg_url,arg_headers,arg_data){return $http({url:arg_url,method:"POST",data:arg_data,headers:arg_headers})}}),window.watcherApp.factory("client",function(){return new PeerManager("watcher")}),window.watcherApp.factory("fingerprintManager",function(){return new Fingerprint}),window.watcherApp.controller("watcherCtrl",["$scope","$window","APP_VALUES","dataProvider","client","fingerprintManager",function($scope,$window,APP_VALUES,dataProvider,client,fingerprintManager){APP_VALUES.FINGERPRINT=fingerprintManager.get(),window.localPeer||(window.localPeer=client.localPeerInit()),console.log("<--- Local Peer --->"),console.log(window.localPeer);var ctrl=this;ctrl.broadcastStreams=[],ctrl.remoteStreamsDB=client.getRemoteStreamsDB(),ctrl.getStreamById=function(arg_id){for(var currentIndex,currentElem,minIndex=0,maxIndex=ctrl.broadcastStreams.length-1;minIndex<=maxIndex;){if(currentIndex=(minIndex+maxIndex)/2|0,currentElem=ctrl.broadcastStreams[currentIndex],currentElem.idarg_id))return ctrl.broadcastStreams[currentIndex];maxIndex=currentIndex-1}return null}},ctrl.loadData=function(){var customHeaders={current_cookie:$window.document.cookie,"Content-Type":"application/json"};dataProvider.getStreams("/streams",customHeaders,{user_type:"broadcast"}).success(function(data,status,headers,config){console.log("<---getStreams--->"),console.log(data);for(var latestStreams=data.broadcastStreams,ith=0,max=latestStreams.length;ith"),console.log(data)})},client.addExternalMechanism("load_data",ctrl.loadData),ctrl.loadData(),ctrl.view=function(arg_stream){if(arg_stream.isPlaying)console.log("remove remote stream..."),client.removeStream(arg_stream.id),arg_stream.isPlaying=!arg_stream.isPlaying;else{client.peerInit(arg_stream.id);arg_stream.isPlaying=!arg_stream.isPlaying}}}])}angular.element(document).ready(function(){angular.bootstrap(document.body,["watcherApp"])}),initApp()}(jQuery);
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /**/
2 | /* main.js hosts the app */
3 | const express = require('express'),
4 | session = require('express-session'),
5 | cookieParser = require('cookie-parser'),
6 | path = require('path'),
7 | streams = require('./my_modules/streams.js')(),
8 | favicon = require('serve-favicon'),
9 | logger = require('morgan'),
10 | methodOverride = require('method-override'),
11 | bodyParser = require('body-parser'),
12 | errorHandler = require('errorhandler'),
13 | app = express(),
14 | cache_time = 1 * 86400000,
15 | redis = require("redis"),
16 | redisStore = require('connect-redis')(session),
17 | redis_session_client = redis.createClient();
18 |
19 | // start app
20 | initApp();
21 |
22 | /* init the app server */
23 | function initApp(){
24 | app.set('port', process.env.PORT || 8000);
25 | app.set('views', path.join(__dirname, 'views'));
26 | app.set('views engine', 'jade');
27 | app.use(favicon(__dirname + '/public/imgs/icons/favicon.ico'));
28 | app.use(logger('dev'));
29 | app.use(bodyParser.json());
30 | app.use(bodyParser.urlencoded({ extended: true }));
31 | app.use(methodOverride());
32 |
33 | // set session & cookie; here we use Redis to handle session management
34 | app.use(cookieParser('C1J43I8UDHSBDC4N6SYE83W726SG4GD84JD9S7HA'));
35 | app.use(session({
36 | secret: 'C1J43I8UDHSBDC4N6SYE83W726SG4GD84JD9S7HA',
37 | store: new redisStore({
38 | host: 'localhost',
39 | port: 6378,
40 | pass: 'CJDC934FGHSHD7ZM23R98UV100SDZNP09',
41 | redis_session_client: redis_session_client,
42 | ttl : 3600000
43 | }),
44 | resave: false,
45 | saveUninitialized: true,
46 | cookie: { secure: true, maxAge: 600000, httpOnly: true }
47 | }));
48 |
49 | // set static paths
50 | app.use('/public', express.static( path.join(__dirname, '/public'), { maxAge: cache_time}));
51 | app.use('/videos', express.static( path.join(__dirname, '/my_binaryjs/videos'), { maxAge: cache_time}));
52 |
53 | // dev. only
54 | if('development' === app.get('env')){
55 | app.use(errorHandler());
56 | }
57 |
58 | // set routers
59 | var myRouter = require('./routes/routes.js');
60 | myRouter(app, streams); // add app and streams
61 |
62 | // set port for server to listen
63 | var appServer = app.listen(app.get('port'), function(){
64 | console.log('Express server listening on port: ' + app.get('port'));
65 | });
66 |
67 | // set socket handlers
68 | var io = require('socket.io').listen(appServer),
69 | redis_socket_client = require('redis').createClient,
70 | adapter = require('socket.io-redis'),
71 | pub = redis_socket_client(6378, 'localhost', { auth_pass: 'CJDC934FGHSHD7ZM23R98UV100SDZNP09' }),
72 | sub = redis_socket_client(6378, 'localhost', { return_buffers: true, auth_pass: 'CJDC934FGHSHD7ZM23R98UV100SDZNP09' });
73 |
74 | io.adapter(adapter({ pubClient: pub, subClient: sub }));
75 | require('./my_modules/socketHandler.js')(io, streams); // pass socket.io and stream into stream handler
76 | }
77 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/lib/jasmine.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
3 | }
4 |
5 |
6 | .jasmine_reporter a:visited, .jasmine_reporter a {
7 | color: #303;
8 | }
9 |
10 | .jasmine_reporter a:hover, .jasmine_reporter a:active {
11 | color: blue;
12 | }
13 |
14 | .run_spec {
15 | float:right;
16 | padding-right: 5px;
17 | font-size: .8em;
18 | text-decoration: none;
19 | }
20 |
21 | .jasmine_reporter {
22 | margin: 0 5px;
23 | }
24 |
25 | .banner {
26 | color: #303;
27 | background-color: #fef;
28 | padding: 5px;
29 | }
30 |
31 | .logo {
32 | float: left;
33 | font-size: 1.1em;
34 | padding-left: 5px;
35 | }
36 |
37 | .logo .version {
38 | font-size: .6em;
39 | padding-left: 1em;
40 | }
41 |
42 | .runner.running {
43 | background-color: yellow;
44 | }
45 |
46 |
47 | .options {
48 | text-align: right;
49 | font-size: .8em;
50 | }
51 |
52 |
53 |
54 |
55 | .suite {
56 | border: 1px outset gray;
57 | margin: 5px 0;
58 | padding-left: 1em;
59 | }
60 |
61 | .suite .suite {
62 | margin: 5px;
63 | }
64 |
65 | .suite.passed {
66 | background-color: #dfd;
67 | }
68 |
69 | .suite.failed {
70 | background-color: #fdd;
71 | }
72 |
73 | .spec {
74 | margin: 5px;
75 | padding-left: 1em;
76 | clear: both;
77 | }
78 |
79 | .spec.failed, .spec.passed, .spec.skipped {
80 | padding-bottom: 5px;
81 | border: 1px solid gray;
82 | }
83 |
84 | .spec.failed {
85 | background-color: #fbb;
86 | border-color: red;
87 | }
88 |
89 | .spec.passed {
90 | background-color: #bfb;
91 | border-color: green;
92 | }
93 |
94 | .spec.skipped {
95 | background-color: #bbb;
96 | }
97 |
98 | .messages {
99 | border-left: 1px dashed gray;
100 | padding-left: 1em;
101 | padding-right: 1em;
102 | }
103 |
104 | .passed {
105 | background-color: #cfc;
106 | display: none;
107 | }
108 |
109 | .failed {
110 | background-color: #fbb;
111 | }
112 |
113 | .skipped {
114 | color: #777;
115 | background-color: #eee;
116 | display: none;
117 | }
118 |
119 |
120 | /*.resultMessage {*/
121 | /*white-space: pre;*/
122 | /*}*/
123 |
124 | .resultMessage span.result {
125 | display: block;
126 | line-height: 2em;
127 | color: black;
128 | }
129 |
130 | .resultMessage .mismatch {
131 | color: black;
132 | }
133 |
134 | .stackTrace {
135 | white-space: pre;
136 | font-size: .8em;
137 | margin-left: 10px;
138 | max-height: 5em;
139 | overflow: auto;
140 | border: 1px inset red;
141 | padding: 1em;
142 | background: #eef;
143 | }
144 |
145 | .finished-at {
146 | padding-left: 1em;
147 | font-size: .6em;
148 | }
149 |
150 | .show-passed .passed,
151 | .show-skipped .skipped {
152 | display: block;
153 | }
154 |
155 |
156 | #jasmine_content {
157 | position:fixed;
158 | right: 100%;
159 | }
160 |
161 | .runner {
162 | border: 1px solid gray;
163 | display: block;
164 | margin: 5px 0;
165 | padding: 2px 0 2px 10px;
166 | }
167 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/spec/s-global.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, xit, expect */
2 |
3 | describe('global methods', function () {
4 | 'use strict';
5 |
6 | var foo = function foo() {};
7 | var functionsHaveNames = foo.name === 'foo';
8 | var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
9 |
10 | var is = function (x, y) {
11 | if (x === 0 && y === 0) {
12 | return 1 / x === 1 / y;
13 | }
14 | return x === y;
15 | };
16 |
17 | describe('parseInt', function () {
18 | /* eslint-disable radix */
19 |
20 | ifFunctionsHaveNamesIt('has the right name', function () {
21 | expect(parseInt.name).toBe('parseInt');
22 | });
23 |
24 | it('accepts a radix', function () {
25 | for (var i = 2; i <= 36; ++i) {
26 | expect(parseInt('10', i)).toBe(i);
27 | }
28 | });
29 |
30 | it('defaults the radix to 10 when the number does not start with 0x or 0X', function () {
31 | [
32 | '01',
33 | '08',
34 | '10',
35 | '42'
36 | ].forEach(function (str) {
37 | expect(parseInt(str)).toBe(parseInt(str, 10));
38 | });
39 | });
40 |
41 | it('defaults the radix to 16 when the number starts with 0x or 0X', function () {
42 | expect(parseInt('0x16')).toBe(parseInt('0x16', 16));
43 | expect(parseInt('0X16')).toBe(parseInt('0X16', 16));
44 | });
45 |
46 | it('ignores leading whitespace', function () {
47 | expect(parseInt(' 0x16')).toBe(parseInt('0x16', 16));
48 | expect(parseInt(' 42')).toBe(parseInt('42', 10));
49 | expect(parseInt(' 08')).toBe(parseInt('08', 10));
50 |
51 | var ws = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' +
52 | '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028' +
53 | '\u2029\uFEFF';
54 | expect(parseInt(ws + '08')).toBe(parseInt('08', 10));
55 | expect(parseInt(ws + '0x16')).toBe(parseInt('0x16', 16));
56 | });
57 |
58 | it('defaults the radix properly when not a true number', function () {
59 | var fakeZero = { valueOf: function () { return 0; } };
60 | expect(parseInt('08', fakeZero)).toBe(parseInt('08', 10));
61 | expect(parseInt('0x16', fakeZero)).toBe(parseInt('0x16', 16));
62 | });
63 |
64 | it('allows sign-prefixed hex values', function () {
65 | expect(parseInt('-0xF')).toBe(-15);
66 | expect(parseInt('-0xF', 16)).toBe(-15);
67 | expect(parseInt('+0xF')).toBe(15);
68 | expect(parseInt('+0xF', 16)).toBe(15);
69 | });
70 | /* eslint-enable radix */
71 | });
72 |
73 | describe('parseFloat()', function () {
74 | it('works with zeroes', function () {
75 | expect(is(parseFloat('0'), 0) ? '+0' : '-0').toBe('+0');
76 | expect(is(parseFloat(' 0'), 0) ? '+0' : '-0').toBe('+0');
77 | expect(is(parseFloat('+0'), 0) ? '+0' : '-0').toBe('+0');
78 | expect(is(parseFloat(' +0'), 0) ? '+0' : '-0').toBe('+0');
79 | expect(is(parseFloat('-0'), -0) ? '-0' : '+0').toBe('-0');
80 | expect(is(parseFloat(' -0'), -0) ? '-0' : '+0').toBe('-0');
81 | });
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/public/js/json3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "_args": [
3 | [
4 | "json3@*",
5 | "/Users/alantai/Desktop/Fund364/prjLendUp-Beta-1.0/my_nodejs_app"
6 | ]
7 | ],
8 | "_from": "json3@*",
9 | "_id": "json3@3.3.2",
10 | "_inCache": true,
11 | "_installable": true,
12 | "_location": "/json3",
13 | "_npmUser": {
14 | "email": "github@kitcambridge.be",
15 | "name": "kitcambridge"
16 | },
17 | "_npmVersion": "1.4.3",
18 | "_phantomChildren": {},
19 | "_requested": {
20 | "name": "json3",
21 | "raw": "json3@*",
22 | "rawSpec": "*",
23 | "scope": null,
24 | "spec": "*",
25 | "type": "range"
26 | },
27 | "_requiredBy": [
28 | "/"
29 | ],
30 | "_resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
31 | "_shasum": "3c0434743df93e2f5c42aee7b19bcb483575f4e1",
32 | "_shrinkwrap": null,
33 | "_spec": "json3@*",
34 | "_where": "/Users/alantai/Desktop/Fund364/prjLendUp-Beta-1.0/my_nodejs_app",
35 | "author": {
36 | "email": "github@kitcambridge.be",
37 | "name": "Kit Cambridge",
38 | "url": "http://kitcambridge.be/"
39 | },
40 | "bugs": {
41 | "url": "https://github.com/bestiejs/json3/issues"
42 | },
43 | "contributors": [
44 | {
45 | "name": "Mangled Deutz",
46 | "email": "olivier@webitup.fr",
47 | "url": "http://tech.roxee.tv/"
48 | },
49 | {
50 | "name": "Øyvind Sean Kinsey",
51 | "email": "oyvind@kinsey.no",
52 | "url": "http://fb.me/ok"
53 | },
54 | {
55 | "name": "Oskar Schöldström",
56 | "email": "public@oxy.fi",
57 | "url": "http://oxy.fi/"
58 | },
59 | {
60 | "name": "Kiryl Yermakou",
61 | "email": "rma4ok@gmail.com",
62 | "url": "https://github.com/rma4ok"
63 | }
64 | ],
65 | "dependencies": {},
66 | "description": "A modern JSON implementation compatible with nearly all JavaScript platforms.",
67 | "devDependencies": {},
68 | "directories": {},
69 | "dist": {
70 | "shasum": "3c0434743df93e2f5c42aee7b19bcb483575f4e1",
71 | "tarball": "http://registry.npmjs.org/json3/-/json3-3.3.2.tgz"
72 | },
73 | "files": [
74 | "LICENSE",
75 | "README.md",
76 | "lib/json3.js",
77 | "lib/json3.min.js"
78 | ],
79 | "homepage": "http://bestiejs.github.io/json3",
80 | "jam": {
81 | "includes": [
82 | "LICENSE",
83 | "README.md",
84 | "lib/json3.js",
85 | "lib/json3.min.js"
86 | ],
87 | "main": "./lib/json3.js"
88 | },
89 | "keywords": [
90 | "ecma",
91 | "es5",
92 | "json",
93 | "lexer",
94 | "parser",
95 | "spec",
96 | "stringify"
97 | ],
98 | "licenses": [
99 | {
100 | "type": "MIT",
101 | "url": "http://kit.mit-license.org/"
102 | }
103 | ],
104 | "main": "./lib/json3",
105 | "maintainers": [
106 | {
107 | "name": "kitcambridge",
108 | "email": "kitcambridge@me.com"
109 | },
110 | {
111 | "name": "d10",
112 | "email": "demoneaux@gmail.com"
113 | }
114 | ],
115 | "name": "json3",
116 | "optionalDependencies": {},
117 | "readme": "ERROR: No README data found!",
118 | "repository": {
119 | "type": "git",
120 | "url": "git://github.com/bestiejs/json3.git"
121 | },
122 | "scripts": {
123 | "test": "node test/test_*.js"
124 | },
125 | "version": "3.3.2",
126 | "volo": {
127 | "ignore": [
128 | ".*",
129 | "benchmark",
130 | "bower.json",
131 | "build.js",
132 | "component.json",
133 | "index.html",
134 | "page",
135 | "test",
136 | "vendor"
137 | ],
138 | "type": "directory"
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/public/js/es5-shim/Makefile:
--------------------------------------------------------------------------------
1 | # Since we rely on paths relative to the makefile location, abort if make isn't being run from there.
2 | $(if $(findstring /,$(MAKEFILE_LIST)),$(error Please only invoke this makefile from the directory it resides in))
3 |
4 | # The files that need updating when incrementing the version number.
5 | VERSIONED_FILES := *.js *.json *.map README*
6 |
7 |
8 | # Add the local npm packages' bin folder to the PATH, so that `make` can find them, when invoked directly.
9 | # Note that rather than using `$(npm bin)` the 'node_modules/.bin' path component is hard-coded, so that invocation works even from an environment
10 | # where npm is (temporarily) unavailable due to having deactivated an nvm instance loaded into the calling shell in order to avoid interference with tests.
11 | export PATH := $(shell printf '%s' "$$PWD/node_modules/.bin:$$PATH")
12 | UTILS := semver
13 | # Make sure that all required utilities can be located.
14 | UTIL_CHECK := $(or $(shell PATH="$(PATH)" which $(UTILS) >/dev/null && echo 'ok'),$(error Did you forget to run `npm install` after cloning the repo? At least one of the required supporting utilities not found: $(UTILS)))
15 |
16 | # Default target (by virtue of being the first non '.'-prefixed in the file).
17 | .PHONY: _no-target-specified
18 | _no-target-specified:
19 | $(error Please specify the target to make - `make list` shows targets. Alternatively, use `npm test` to run the default tests; `npm run` shows all tests)
20 |
21 | # Lists all targets defined in this makefile.
22 | .PHONY: list
23 | list:
24 | @$(MAKE) -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | command grep -v -e '^[^[:alnum:]]' -e '^$@$$command ' | sort
25 |
26 | # All-tests target: invokes the specified test suites for ALL shells defined in $(SHELLS).
27 | .PHONY: test
28 | test:
29 | @npm test
30 |
31 | .PHONY: _ensure-tag
32 | _ensure-tag:
33 | ifndef TAG
34 | $(error Please invoke with `make TAG= release`, where is either an increment specifier (patch, minor, major, prepatch, preminor, premajor, prerelease), or an explicit major.minor.patch version number)
35 | endif
36 |
37 | CHANGELOG_ERROR = $(error No CHANGES specified)
38 | .PHONY: _ensure-changelog
39 | _ensure-changelog:
40 | @ (git status -sb --porcelain | command grep -E '^( M|[MA] ) CHANGES' > /dev/null) || (echo no CHANGES specified && exit 2)
41 |
42 | # Ensures that the git workspace is clean.
43 | .PHONY: _ensure-clean
44 | _ensure-clean:
45 | @[ -z "$$((git status --porcelain --untracked-files=no || echo err) | command grep -v 'CHANGES')" ] || { echo "Workspace is not clean; please commit changes first." >&2; exit 2; }
46 |
47 | # Makes a release; invoke with `make TAG= release`.
48 | .PHONY: release
49 | release: _ensure-tag _ensure-changelog _ensure-clean
50 | @old_ver=`git describe --abbrev=0 --tags --match 'v[0-9]*.[0-9]*.[0-9]*'` || { echo "Failed to determine current version." >&2; exit 1; }; old_ver=$${old_ver#v}; \
51 | new_ver=`echo "$(TAG)" | sed 's/^v//'`; new_ver=$${new_ver:-patch}; \
52 | if printf "$$new_ver" | command grep -q '^[0-9]'; then \
53 | semver "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be major.minor.patch' >&2; exit 2; }; \
54 | semver -r "> $$old_ver" "$$new_ver" >/dev/null || { echo 'Invalid version number specified: $(TAG) - must be HIGHER than current one.' >&2; exit 2; } \
55 | else \
56 | new_ver=`semver -i "$$new_ver" "$$old_ver"` || { echo 'Invalid version-increment specifier: $(TAG)' >&2; exit 2; } \
57 | fi; \
58 | printf "=== Bumping version **$$old_ver** to **$$new_ver** before committing and tagging:\n=== TYPE 'proceed' TO PROCEED, anything else to abort: " && read response && [ "$$response" = 'proceed' ] || { echo 'Aborted.' >&2; exit 2; }; \
59 | npm run minify; \
60 | replace "$$old_ver" "$$new_ver" -- $(VERSIONED_FILES) && \
61 | replace "blob/master" "blob/v$$new_ver" -- *.min.js && \
62 | git commit -m "v$$new_ver" $(VERSIONED_FILES) CHANGES && \
63 | git tag -a -m "v$$new_ver" "v$$new_ver"
64 |
--------------------------------------------------------------------------------
/my_modules/streams.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | /*
3 | * MongoDB Config.
4 | * In this tutorial, mongodb npm module is used to handle the communication between the server and database
5 | */
6 | var MongoClient = require('mongodb').MongoClient,
7 | assert = require('assert'),
8 | webRTCCollection;
9 |
10 | var url = 'mongodb://webrtc_user:HappyWebRTC@45.79.106.150:27025/webrtc';
11 | // Use connect method to connect to the Server
12 | MongoClient.connect(url, function(err, db) {
13 | assert.equal(null, err);
14 | console.log("Connected correctly to the server");
15 | socketCollection = db.collection('socket');
16 | });
17 |
18 | /*
19 | * Stream classes
20 | */
21 | // root class
22 | var Stream = function(arg_id, arg_name, arg_user_type, arg_user_ip, arg_time_log_in) {
23 | this.name = arg_name;
24 | this.id = arg_id;
25 | this.user_type = arg_user_type; // will be used for filtering users
26 | this.user_ip = arg_user_ip; // for tracking user ip
27 | this.time_log_in = arg_time_log_in; // will be used for queue
28 | }
29 |
30 | // admin Stream inherit Stream
31 | var WebRTCStream = function(arg_id, arg_name, arg_user_type, arg_user_ip, arg_time_log_id){
32 | // super parent class
33 | Stream.call(this, arg_id, arg_name, arg_user_type, arg_user_ip, arg_time_log_id);
34 | this.servedWatchers = []; // optional
35 | }
36 | WebRTCStream.prototype = Object.create(Stream.prototype);
37 | WebRTCStream.prototype.constructor = WebRTCStream;
38 |
39 | // returned obj
40 | return {
41 | // add new stream
42 | addStream : function(arg_id, arg_name, arg_user_type, arg_user_ip, callback) {
43 | if(arg_user_type === 'watcher' || arg_user_type === 'broadcast'){
44 | var time_log_in = new Date(),
45 | stream = new WebRTCStream(arg_id, arg_name, arg_user_type, arg_user_ip, time_log_in);
46 | console.log(stream);
47 |
48 | // insert if not exist
49 | socketCollection.findAndModify(
50 | { id: stream.id },
51 | [['id', 1]],
52 | { $setOnInsert: stream },
53 | { new: true, upsert: true },
54 | function(err, doc){
55 | callback(err, doc);
56 | });
57 | }else{
58 | callback('invalid user type', {});
59 | }
60 | },
61 |
62 | // remove stream
63 | removeStream : function(arg_id, callback) {
64 | // delete doc.
65 | socketCollection.deleteOne({ id : arg_id }, function(err, result) {
66 | callback(err, result);
67 | });
68 | },
69 |
70 | // update socket infor
71 | addWatcher : function(arg_local_id, arg_remote_id, arg_user_type, callback) {
72 | socketCollection.updateOne({id: arg_local_id}, {$addToSet: {servedWatchers: {watcher_id: arg_remote_id, _id: -1} }}, function(err, result){
73 | callback(err, result);
74 | });
75 | },
76 |
77 | // remove Watcher from broadcast doc
78 | removeWatcher : function(arg_local_id, arg_remote_id, arg_user_type, callback){
79 | console.log('<--- remove watcher from broadcast doc. --->');
80 | socketCollection.update({id: arg_local_id}, {$pull: {servedWatchers: {watcher_id: arg_remote_id} }}, function(err, result){
81 | callback(err, result);
82 | });
83 | },
84 |
85 | getWatchers : function(arg_broadcast_id, callback){
86 | console.log('<--- get watchers --->');
87 | socketCollection.findOne({id: arg_broadcast_id}, {servedWatchers: 1}, function(err, result){
88 | callback(err, result);
89 | });
90 | },
91 |
92 | // get stream list; may be unnecessary for using cloud db
93 | getStreams : function(arg_user_type, callback) {
94 | if(arg_user_type === 'watcher' || arg_user_type === 'broadcast'){
95 | // get sorted docs
96 | socketCollection.find({user_type: arg_user_type}).sort({id: 1}).toArray(function(err, docs){
97 | callback(err, docs);
98 | });
99 | }else{
100 | callback('invalid user type', []);
101 | };
102 | }
103 | }
104 | };
105 |
--------------------------------------------------------------------------------
/public/js/respond.min.js:
--------------------------------------------------------------------------------
1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
3 | * */
4 |
5 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b
3 |
4 | html(lang="en")
5 |
6 | head
7 | meta(http-equiv="Content-Type" content="text/html; charset=utf-8")
8 | meta(http-equiv="X-UA-Compatible" content="IE=11,chrome,firefox,safari")
9 | meta(name="apple-mobile-web-app-capable")
10 | meta(name="viewport", content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0")
11 | meta(name='description', content='swebrtc demo')
12 | meta(name='authors', content='Alan Tai')
13 | meta(name='copyright', content='Copyright (c) 2016 Gogistics')
14 | meta(name="msapplication-TileImage" content="/public/imgs/icons/ms-icon-144x144.png")
15 | link(rel="manifest" href="/public/imgs/icons/manifest.json")
16 |
17 | // set icons
18 | link(rel="apple-touch-icon" sizes="57x57" href="/public/imgs/icons/apple-icon-57x57.png")
19 | link(rel="apple-touch-icon" sizes="60x60" href="/public/imgs/icons/apple-icon-60x60.png")
20 | link(rel="apple-touch-icon" sizes="72x72" href="/public/imgs/icons/apple-icon-72x72.png")
21 | link(rel="apple-touch-icon" sizes="76x76" href="/public/imgs/icons/apple-icon-76x76.png")
22 | link(rel="apple-touch-icon" sizes="114x114" href="/public/imgs/icons/apple-icon-114x114.png")
23 | link(rel="apple-touch-icon" sizes="120x120" href="/public/imgs/icons/apple-icon-120x120.png")
24 | link(rel="apple-touch-icon" sizes="144x144" href="/public/imgs/icons/apple-icon-144x144.png")
25 | link(rel="apple-touch-icon" sizes="152x152" href="/public/imgs/icons/apple-icon-152x152.png")
26 | link(rel="apple-touch-icon" sizes="180x180" href="/public/imgs/icons/apple-icon-180x180.png")
27 | link(rel="icon" type="image/png" sizes="192x192" href="/public/imgs/icons/android-icon-192x192.png")
28 | link(rel="icon" type="image/png" sizes="32x32" href="/public/imgs/icons/favicon-32x32.png")
29 | link(rel="icon" type="image/png" sizes="96x96" href="/public/imgs/icons/favicon-96x96.png")
30 | link(rel="icon" type="image/png" sizes="16x16" href="/public/imgs/icons/favicon-16x16.png")
31 |
32 | // css files and block
33 | link(href='/public/css/jquery-ui.css' type='text/css' rel='stylesheet')
34 | link(href='/public/css/bootstrap.css' type='text/css' rel='stylesheet')
35 |
36 | block css_block
37 |
38 | title #{title}
39 | body(id="page-top")
40 | // alert for lt IE 9
41 |
44 |
45 | // main content
46 | block content
47 |
48 | // js includsion block
49 | // for IE 11 or browser is not IE
50 | script(type="text/javascript" src="/public/js/jquery.js")
51 | script(type="text/javascript" src="/public/js/jquery-ui-1.12.0/jquery-ui.js")
52 | script(type="text/javascript" src="/public/js/bootstrap.js")
53 | script(type="text/javascript" src="/public/js/fingerprint.js")
54 | script(type="text/javascript" src="/public/js/socket.io.js")
55 | script(type="text/javascript" src="/public/js/binary.js")
56 | script(type="text/javascript" src="/public/js/adapter.js")
57 | script(type="text/javascript" src="/public/js/rtcClient.js")
58 | script(type="text/javascript" src="/public/js/rtcRecorder.js")
59 | script(type="text/javascript" src="/public/js/angular.js")
60 |
61 | // for lt IE 9
62 |
79 | block js_block
80 |
--------------------------------------------------------------------------------
/my_modules/socketHandler.js:
--------------------------------------------------------------------------------
1 | module.exports = function(io, streams) {
2 | // socket.io connection callback when a new user visit the website
3 | io.on('connection', function(client) {
4 | console.log('-- ' + client.id + ' joined --');
5 | client.emit('id', client.id); // notify all users the id of new visitor
6 |
7 | // receiver of message; socket.io message callback when new message come in
8 | client.on('message', function (details) {
9 | var otherClient = io.sockets.connected[details.to];
10 | if (!otherClient) return false;
11 | delete details.to;
12 | details.from = client.id; // replace the id of "from" with the sender's id
13 | otherClient.emit('message', details); // notify all users
14 | });
15 |
16 | // receiver of readyToStream notification; the web server and others get notified when a new user is ready for rtc service
17 | client.on('readyToStream', function(options) {
18 | console.log('-- ' + client.id + ' is ready to stream --');
19 | var user_ip = client.request.connection.remoteAddress || 'NA';
20 |
21 | // add new info of socket stream to MongoDB when new visitor is ready
22 | streams.addStream(client.id, options.name, options.user_type, user_ip, function(err, doc){
23 | if(err) console.log(err);
24 | });
25 |
26 | // send notification to all users when new stream coming; notify user-self & need to notify other users
27 | // client.emit('streamNotification', 'stream_on')
28 | notifyUsersWithUpdatedStreamsInfo('stream_on', client.id);
29 | });
30 |
31 | // receiver of update; update the doc of MongoDB
32 | client.on('addWatcher', function(options) {
33 | var localId = options['localId'], remoteId = options['remoteId'], userType = options['userType'];
34 | streams.addWatcher(localId, remoteId, userType, function(err, result){
35 | if(err){
36 | console.log('<--- error of adding watcher --->');
37 | console.log(err);
38 | }else{
39 | updateWatcherList('add_watcher', localId);
40 | }
41 | });
42 | });
43 |
44 | // remove stream ID from broadcast doc
45 | client.on('removeWatcher', function(options){
46 | var localId = options['localId'], remoteId = options['remoteId'], userType = options['userType'];
47 | streams.removeWatcher(localId, remoteId, userType, function(err, result){
48 | if(err){
49 | console.log('<--- error of removing watcher --->');
50 | console.log(err);
51 | }else{
52 | updateWatcherList('remove_watcher', localId);
53 | }
54 | });
55 | });
56 |
57 | // receiver of service notification; notify others with news from the users
58 | client.on('serviceNotification', function(arg_details){
59 | var client_to = io.sockets.connected[arg_details.to];
60 | if(!client_to) return false;
61 | arg_details['from'] = client.id;
62 | console.log(arg_details);
63 | client_to.emit('serviceNotification', arg_details);
64 | });
65 |
66 | // disconnect and leave receiver; "disconnect" and "leave" function the same way in this tutorial
67 | client.on('disconnect', leave);
68 | client.on('leave', leave);
69 |
70 | function leave(arg_info) {
71 | console.log('-- ' + client.id + ' left --');
72 | streams.removeStream(client.id, function(err, result){
73 | if(err) console.log(result);
74 | });
75 |
76 | // send notification to all users when stream leaves
77 | // client.emit('streamNotification', 'stream_off');
78 | notifyUsersWithUpdatedStreamsInfo('stream_off', client.id);
79 | }
80 |
81 | // notification to update stream list and video conatiner
82 | function notifyUsersWithUpdatedStreamsInfo(arg_notification_key, arg_client_id_from){
83 | var clients = io.sockets.connected;
84 | if(!clients || clients.length === 0){
85 | return false;
86 | }
87 | console.log('<--- update stream info. --->');
88 | for(var key in clients){
89 | console.log('socket-key: ' + key);
90 | clients[key].emit('streamNotification', { notification_key: arg_notification_key, client_id_from: arg_client_id_from });
91 | }
92 | }
93 |
94 | function updateWatcherList(arg_notification_key, arg_broadcast_id){
95 | var clients = io.sockets.connected, broadcast;
96 | if ( !clients || clients.length === 0 || !clients[arg_broadcast_id] ) {
97 | return false;
98 | }else{
99 | console.log('<--- update watcher list --->');
100 | console.log('broadcast-key: ' + arg_broadcast_id);
101 | clients[arg_broadcast_id].emit('watcherNotification', {notification_key: arg_notification_key});
102 | }
103 | }
104 | });
105 | };
106 |
--------------------------------------------------------------------------------
/public/js/es5-shim/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "_args": [
3 | [
4 | "es5-shim@*",
5 | "/Users/alantai/Desktop/Fund364/prjLendUp-Beta-1.0/my_nodejs_app"
6 | ]
7 | ],
8 | "_from": "es5-shim@*",
9 | "_id": "es5-shim@4.5.2",
10 | "_inCache": true,
11 | "_installable": true,
12 | "_location": "/es5-shim",
13 | "_nodeVersion": "5.5.0",
14 | "_npmUser": {
15 | "email": "ljharb@gmail.com",
16 | "name": "ljharb"
17 | },
18 | "_npmVersion": "3.3.12",
19 | "_phantomChildren": {},
20 | "_requested": {
21 | "name": "es5-shim",
22 | "raw": "es5-shim@*",
23 | "rawSpec": "*",
24 | "scope": null,
25 | "spec": "*",
26 | "type": "range"
27 | },
28 | "_requiredBy": [
29 | "/"
30 | ],
31 | "_resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.2.tgz",
32 | "_shasum": "b20684dfd99b83e0a40ff927f20dfec53fa20c23",
33 | "_shrinkwrap": null,
34 | "_spec": "es5-shim@*",
35 | "_where": "/Users/alantai/Desktop/Fund364/prjLendUp-Beta-1.0/my_nodejs_app",
36 | "bugs": {
37 | "url": "http://github.com/es-shims/es5-shim/issues"
38 | },
39 | "contributors": [
40 | {
41 | "name": "Kris Kowal",
42 | "email": "kris@cixar.com",
43 | "url": "http://github.com/kriskowal/"
44 | },
45 | {
46 | "name": "Sami Samhuri",
47 | "email": "sami.samhuri@gmail.com",
48 | "url": "http://samhuri.net/"
49 | },
50 | {
51 | "name": "Florian Schäfer",
52 | "email": "florian.schaefer@gmail.com",
53 | "url": "http://github.com/fschaefer"
54 | },
55 | {
56 | "name": "Irakli Gozalishvili",
57 | "email": "rfobic@gmail.com",
58 | "url": "http://jeditoolkit.com"
59 | },
60 | {
61 | "name": "Kit Cambridge",
62 | "email": "kitcambridge@gmail.com",
63 | "url": "http://kitcambridge.github.com"
64 | },
65 | {
66 | "name": "Jordan Harband",
67 | "email": "ljharb@gmail.com",
68 | "url": "https://github.com/ljharb/"
69 | }
70 | ],
71 | "dependencies": {},
72 | "description": "ECMAScript 5 compatibility shims for legacy JavaScript engines",
73 | "devDependencies": {
74 | "@ljharb/eslint-config": "^1.6.1",
75 | "eslint": "^1.10.3",
76 | "jasmine-node": "^1.14.5",
77 | "jscs": "^2.9.0",
78 | "parallelshell": "^2.0.0",
79 | "replace": "^0.3.0",
80 | "semver": "^5.1.0",
81 | "uglify-js": "^2.6.1"
82 | },
83 | "directories": {},
84 | "dist": {
85 | "shasum": "b20684dfd99b83e0a40ff927f20dfec53fa20c23",
86 | "tarball": "http://registry.npmjs.org/es5-shim/-/es5-shim-4.5.2.tgz"
87 | },
88 | "engines": {
89 | "node": ">=0.4.0"
90 | },
91 | "gitHead": "f134b8886f1a0ea800bbc5c18e34c67ae16737c4",
92 | "homepage": "http://github.com/es-shims/es5-shim/",
93 | "keywords": [
94 | "ecmascript",
95 | "es5",
96 | "es5 shim",
97 | "javascript",
98 | "polyfill",
99 | "shim"
100 | ],
101 | "license": "MIT",
102 | "main": "es5-shim.js",
103 | "maintainers": [
104 | {
105 | "name": "kriskowal",
106 | "email": "kris.kowal@cixar.com"
107 | },
108 | {
109 | "name": "gozala",
110 | "email": "rfobic@gmail.com"
111 | },
112 | {
113 | "name": "ljharb",
114 | "email": "ljharb@gmail.com"
115 | }
116 | ],
117 | "name": "es5-shim",
118 | "optionalDependencies": {},
119 | "readme": "ERROR: No README data found!",
120 | "repository": {
121 | "type": "git",
122 | "url": "git+ssh://git@github.com/es-shims/es5-shim.git"
123 | },
124 | "scripts": {
125 | "eslint": "eslint tests/helpers/*.js tests/spec/*.js es5-shim.js es5-sham.js",
126 | "jscs": "jscs tests/helpers/*.js tests/spec/*.js es5-shim.js es5-sham.js",
127 | "lint": "parallelshell 'npm run jscs' 'npm run eslint'",
128 | "minify": "parallelshell 'npm run minify-shim' 'npm run minify-sham'",
129 | "minify-sham": "uglifyjs es5-sham.js --keep-fnames --comments --source-map=es5-sham.map -m -b ascii_only=true,beautify=false > es5-sham.min.js",
130 | "minify-shim": "uglifyjs es5-shim.js --keep-fnames --comments --source-map=es5-shim.map -m -b ascii_only=true,beautify=false > es5-shim.min.js",
131 | "test": "npm run lint && npm run tests-only",
132 | "test-native": "jasmine-node --matchall tests/spec/",
133 | "tests-only": "jasmine-node --matchall ./ tests/spec/"
134 | },
135 | "testling": {
136 | "browsers": [
137 | "android-browser/4.2",
138 | "chrome/25.0..latest",
139 | "chrome/4.0..10.0",
140 | "chrome/canary",
141 | "firefox/18.0..latest",
142 | "firefox/3.0..6.0",
143 | "firefox/nightly",
144 | "iexplore/6.0..latest",
145 | "ipad/6.0..latest",
146 | "iphone/6.0..latest",
147 | "opera/10.0..latest",
148 | "opera/next",
149 | "safari/4.0..latest"
150 | ]
151 | },
152 | "version": "4.5.2"
153 | }
154 |
--------------------------------------------------------------------------------
/public/js/watcher.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WebRTC with Angular.js
3 | *
4 | * @authoer: Alan Tai
5 | * @description: implement WebRTC with Angular.js
6 | *
7 | */
8 | (function($){
9 | angular.element(document).ready(function(){
10 | // bootstrap App
11 | angular.bootstrap(document.body, ['watcherApp']);
12 | });
13 |
14 | // init App
15 | initApp();
16 |
17 | function initApp(){
18 | // set module
19 | window.watcherApp = window.watcherApp || angular.module('watcherApp', [], function($locationProvider, $interpolateProvider){
20 | $locationProvider.html5Mode(true); // set html5 mode
21 |
22 | // change interploate to avoid conflicts when template engines use {{...}}
23 | $interpolateProvider.startSymbol('[[');
24 | $interpolateProvider.endSymbol(']]');
25 | });
26 |
27 | // global values
28 | window.watcherApp.value('APP_VALUES', {
29 | EMAIL: 'gogistics@gogistics-tw.com',
30 | BINARY_STREAM: null,
31 | FINGERPRINT: null
32 | });
33 |
34 | window.watcherApp.config(function(){
35 | // $compileProvider.debugInfoEnabled(false); // for production
36 |
37 | // routing config.
38 | });
39 |
40 | window.watcherApp.run(function(){
41 | // run
42 | });
43 |
44 | // services of serving $http
45 | window.watcherApp.service('dataProvider', function($http, APP_VALUES){
46 | this.getStreams = function(arg_url, arg_headers, arg_data){
47 | return $http({
48 | url: arg_url,
49 | method: 'POST',
50 | data: arg_data,
51 | headers: arg_headers
52 | });
53 | }
54 | });
55 |
56 | // PeerManager factory as client of handling peer information (PeerManager is from rtcClient.js)
57 | window.watcherApp.factory('client', function(){
58 | return new PeerManager('watcher');
59 | });
60 |
61 | window.watcherApp.factory('fingerprintManager', function(){
62 | return new Fingerprint();
63 | });
64 |
65 | // watcher controller which handle everything inside watcher scope
66 | window.watcherApp.controller('watcherCtrl', ['$scope', '$window', 'APP_VALUES', 'dataProvider', 'client', 'fingerprintManager', function($scope, $window, APP_VALUES, dataProvider, client, fingerprintManager){
67 | // fingerprint
68 | APP_VALUES.FINGERPRINT = fingerprintManager.get();
69 |
70 | // init local rtc-peer
71 | if(!window.localPeer) window.localPeer = client.localPeerInit();
72 | console.log('<--- Local Peer --->');
73 | console.log(window.localPeer);
74 |
75 | // set variables and functions of ctrl
76 | var ctrl = this;
77 | ctrl.broadcastStreams = [];
78 | ctrl.remoteStreamsDB = client.getRemoteStreamsDB();
79 |
80 | ctrl.getStreamById = function(arg_id){
81 | // use binary sort to search stream by id since the return is sorted from MongoDB
82 | var minIndex = 0,
83 | maxIndex = ctrl.broadcastStreams.length - 1,
84 | currentIndex,
85 | currentElem;
86 |
87 | while(minIndex <= maxIndex){
88 | currentIndex = (minIndex + maxIndex) / 2 | 0;
89 | currentElem = ctrl.broadcastStreams[currentIndex];
90 |
91 | if(currentElem['id'] < arg_id){
92 | minIndex = currentIndex + 1;
93 | }else if(currentElem['id'] > arg_id){
94 | maxIndex = currentIndex - 1;
95 | }else{
96 | return ctrl.broadcastStreams[currentIndex];
97 | }
98 | return null; // no match
99 | }
100 | }
101 |
102 | ctrl.loadData = function(){
103 | // load latest streams
104 | var customHeaders = { 'current_cookie': $window.document.cookie,
105 | 'Content-Type': 'application/json'};
106 | dataProvider.getStreams('/streams', customHeaders, {user_type: 'broadcast'})
107 | .success(function(data, status, headers, config){
108 | console.log('<---getStreams--->');
109 | console.log(data);
110 | var latestStreams = data.broadcastStreams;
111 | for(var ith = 0, max = latestStreams.length; ith < max; ith++){
112 | var stream = ctrl.getStreamById(latestStreams[ith]['id']);
113 | latestStreams[ith].isPlaying = (!!stream) ? stream.isPlaying : false;
114 | }
115 | ctrl.broadcastStreams = latestStreams;
116 | }).error(function(data, status, headers, config){
117 | console.log('<---error of getStreams--->');
118 | console.log(data);
119 | });
120 | }
121 | client.addExternalMechanism('load_data', ctrl.loadData);
122 | ctrl.loadData();
123 |
124 | ctrl.view = function(arg_stream){
125 | if(!arg_stream.isPlaying){
126 | // get peer by stream id
127 | var remotePeer = client.peerInit(arg_stream['id']);
128 | arg_stream.isPlaying = !arg_stream.isPlaying;
129 | }else{
130 | console.log('remove remote stream...');
131 | client.removeStream(arg_stream['id']);
132 | arg_stream.isPlaying = !arg_stream.isPlaying;
133 | }
134 | };
135 | }]);
136 | }
137 | })(jQuery);
138 |
--------------------------------------------------------------------------------
/public/js/es5-shim/es5-sham.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * https://github.com/es-shims/es5-shim
3 | * @license es5-shim Copyright 2009-2015 by contributors, MIT License
4 | * see https://github.com/es-shims/es5-shim/blob/v4.5.1/LICENSE
5 | */
6 | (function(e,t){"use strict";if(typeof define==="function"&&define.amd){define(t)}else if(typeof exports==="object"){module.exports=t()}else{e.returnExports=t()}})(this,function(){var e=Function.call;var t=Object.prototype;var r=e.bind(t.hasOwnProperty);var n=e.bind(t.propertyIsEnumerable);var o=e.bind(t.toString);var i;var c;var f;var a;var l=r(t,"__defineGetter__");if(l){i=e.bind(t.__defineGetter__);c=e.bind(t.__defineSetter__);f=e.bind(t.__lookupGetter__);a=e.bind(t.__lookupSetter__)}if(!Object.getPrototypeOf){Object.getPrototypeOf=function getPrototypeOf(e){var r=e.__proto__;if(r||r===null){return r}else if(o(e.constructor)==="[object Function]"){return e.constructor.prototype}else if(e instanceof Object){return t}else{return null}}}var u=function doesGetOwnPropertyDescriptorWork(e){try{e.sentinel=0;return Object.getOwnPropertyDescriptor(e,"sentinel").value===0}catch(t){return false}};if(Object.defineProperty){var p=u({});var s=typeof document==="undefined"||u(document.createElement("div"));if(!s||!p){var b=Object.getOwnPropertyDescriptor}}if(!Object.getOwnPropertyDescriptor||b){var O="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function getOwnPropertyDescriptor(e,o){if(typeof e!=="object"&&typeof e!=="function"||e===null){throw new TypeError(O+e)}if(b){try{return b.call(Object,e,o)}catch(i){}}var c;if(!r(e,o)){return c}c={enumerable:n(e,o),configurable:true};if(l){var u=e.__proto__;var p=e!==t;if(p){e.__proto__=t}var s=f(e,o);var y=a(e,o);if(p){e.__proto__=u}if(s||y){if(s){c.get=s}if(y){c.set=y}return c}}c.value=e[o];c.writable=true;return c}}if(!Object.getOwnPropertyNames){Object.getOwnPropertyNames=function getOwnPropertyNames(e){return Object.keys(e)}}if(!Object.create){var y;var d=!({__proto__:null}instanceof Object);var j=function shouldUseActiveX(){if(!document.domain){return false}try{return!!new ActiveXObject("htmlfile")}catch(e){return false}};var v=function getEmptyViaActiveX(){var e;var t;t=new ActiveXObject("htmlfile");t.write("");t.close();e=t.parentWindow.Object.prototype;t=null;return e};var _=function getEmptyViaIFrame(){var e=document.createElement("iframe");var t=document.body||document.documentElement;var r;e.style.display="none";t.appendChild(e);e.src="javascript:";r=e.contentWindow.Object.prototype;t.removeChild(e);e=null;return r};if(d||typeof document==="undefined"){y=function(){return{__proto__:null}}}else{y=function(){var e=j()?v():_();delete e.constructor;delete e.hasOwnProperty;delete e.propertyIsEnumerable;delete e.isPrototypeOf;delete e.toLocaleString;delete e.toString;delete e.valueOf;var t=function Empty(){};t.prototype=e;y=function(){return new t};return new t}}Object.create=function create(e,t){var r;var n=function Type(){};if(e===null){r=y()}else{if(typeof e!=="object"&&typeof e!=="function"){throw new TypeError("Object prototype may only be an Object or null")}n.prototype=e;r=new n;r.__proto__=e}if(t!==void 0){Object.defineProperties(r,t)}return r}}var w=function doesDefinePropertyWork(e){try{Object.defineProperty(e,"sentinel",{});return"sentinel"in e}catch(t){return false}};if(Object.defineProperty){var m=w({});var P=typeof document==="undefined"||w(document.createElement("div"));if(!m||!P){var E=Object.defineProperty,h=Object.defineProperties}}if(!Object.defineProperty||E){var g="Property description must be an object: ";var z="Object.defineProperty called on non-object: ";var T="getters & setters can not be defined on this javascript engine";Object.defineProperty=function defineProperty(e,r,n){if(typeof e!=="object"&&typeof e!=="function"||e===null){throw new TypeError(z+e)}if(typeof n!=="object"&&typeof n!=="function"||n===null){throw new TypeError(g+n)}if(E){try{return E.call(Object,e,r,n)}catch(o){}}if("value"in n){if(l&&(f(e,r)||a(e,r))){var u=e.__proto__;e.__proto__=t;delete e[r];e[r]=n.value;e.__proto__=u}else{e[r]=n.value}}else{if(!l&&("get"in n||"set"in n)){throw new TypeError(T)}if("get"in n){i(e,r,n.get)}if("set"in n){c(e,r,n.set)}}return e}}if(!Object.defineProperties||h){Object.defineProperties=function defineProperties(e,t){if(h){try{return h.call(Object,e,t)}catch(r){}}Object.keys(t).forEach(function(r){if(r!=="__proto__"){Object.defineProperty(e,r,t[r])}});return e}}if(!Object.seal){Object.seal=function seal(e){if(Object(e)!==e){throw new TypeError("Object.seal can only be called on Objects.")}return e}}if(!Object.freeze){Object.freeze=function freeze(e){if(Object(e)!==e){throw new TypeError("Object.freeze can only be called on Objects.")}return e}}try{Object.freeze(function(){})}catch(x){Object.freeze=function(e){return function freeze(t){if(typeof t==="function"){return t}else{return e(t)}}}(Object.freeze)}if(!Object.preventExtensions){Object.preventExtensions=function preventExtensions(e){if(Object(e)!==e){throw new TypeError("Object.preventExtensions can only be called on Objects.")}return e}}if(!Object.isSealed){Object.isSealed=function isSealed(e){if(Object(e)!==e){throw new TypeError("Object.isSealed can only be called on Objects.")}return false}}if(!Object.isFrozen){Object.isFrozen=function isFrozen(e){if(Object(e)!==e){throw new TypeError("Object.isFrozen can only be called on Objects.")}return false}}if(!Object.isExtensible){Object.isExtensible=function isExtensible(e){if(Object(e)!==e){throw new TypeError("Object.isExtensible can only be called on Objects.")}var t="";while(r(e,t)){t+="?"}e[t]=true;var n=r(e,t);delete e[t];return n}}});
7 | //# sourceMappingURL=es5-sham.map
8 |
--------------------------------------------------------------------------------
/public/js/es5-shim/README.md:
--------------------------------------------------------------------------------
1 | #es5-shim [![Version Badge][npm-version-svg]][npm-url]
2 |
3 | [![npm badge][npm-badge-png]][npm-url]
4 |
5 | [![Build Status][travis-svg]][travis-url]
6 | [![dependency status][deps-svg]][deps-url]
7 | [![dev dependency status][dev-deps-svg]][dev-deps-url]
8 |
9 | `es5-shim.js` and `es5-shim.min.js` monkey-patch a JavaScript context to
10 | contain all EcmaScript 5 methods that can be faithfully emulated with a
11 | legacy JavaScript engine.
12 |
13 | `es5-sham.js` and `es5-sham.min.js` monkey-patch other ES5 methods as
14 | closely as possible. For these methods, as closely as possible to ES5
15 | is not very close. Many of these shams are intended only to allow code
16 | to be written to ES5 without causing run-time errors in older engines.
17 | In many cases, this means that these shams cause many ES5 methods to
18 | silently fail. Decide carefully whether this is what you want.
19 | **Note:** `es5-sham.js` requires `es5-shim.js` to be able to work properly.
20 |
21 |
22 | ## Tests
23 |
24 | The tests are written with the Jasmine BDD test framework.
25 | To run the tests, navigate to /tests/ , or,
26 | simply `npm install` and `npm test`.
27 |
28 | ## Shims
29 |
30 | ### Complete tests ###
31 |
32 | * Array.prototype.every
33 | * Array.prototype.filter
34 | * Array.prototype.forEach
35 | * Array.prototype.indexOf
36 | * Array.prototype.lastIndexOf
37 | * Array.prototype.map
38 | * Array.prototype.slice
39 | * Array.prototype.some
40 | * Array.prototype.sort
41 | * Array.prototype.reduce
42 | * Array.prototype.reduceRight
43 | * Array.prototype.push
44 | * Array.prototype.join
45 | * Array.isArray
46 | * Date.now
47 | * Date.prototype.toJSON
48 | * Function.prototype.bind
49 | * :warning: Caveat: the bound function has a prototype property.
50 | * :warning: Caveat: bound functions do not try too hard to keep you
51 | from manipulating their ``arguments`` and ``caller`` properties.
52 | * :warning: Caveat: bound functions don't have checks in ``call`` and
53 | ``apply`` to avoid executing as a constructor.
54 | * Number.prototype.toFixed
55 | * Number.prototype.toPrecision
56 | * Object.keys
57 | * String.prototype.split
58 | * String.prototype.trim
59 | * String.prototype.lastIndexOf
60 | * String.prototype.replace
61 | * Firefox (through v29) natively handles capturing groups incorrectly.
62 | * Date.parse (for ISO parsing)
63 | * Date.prototype.toISOString
64 | * parseInt
65 | * parseFloat
66 | * Error.prototype.toString
67 | * Error.prototype.name
68 | * Error.prototype.message
69 | * RegExp.prototype.toString
70 |
71 | ## Shams
72 |
73 | * :warning: Object.create
74 |
75 | For the case of simply "begetting" an object that inherits
76 | prototypically from another, this should work fine across legacy
77 | engines.
78 |
79 | :warning: The second argument is passed to Object.defineProperties
80 | which will probably fail either silently or with extreme prejudice.
81 |
82 | * :warning: Object.getPrototypeOf
83 |
84 | This will return "undefined" in some cases. It uses `__proto__` if
85 | it's available. Failing that, it uses constructor.prototype, which
86 | depends on the constructor property of the object's prototype having
87 | not been replaced. If your object was created like this, it won't
88 | work:
89 |
90 | function Foo() {
91 | }
92 | Foo.prototype = {};
93 |
94 | Because the prototype reassignment destroys the constructor
95 | property.
96 |
97 | This will work for all objects that were created using
98 | `Object.create` implemented with this library.
99 |
100 | * :warning: Object.getOwnPropertyNames
101 |
102 | This method uses Object.keys, so it will not be accurate on legacy
103 | engines.
104 |
105 | * Object.isSealed
106 |
107 | Returns "false" in all legacy engines for all objects, which is
108 | conveniently guaranteed to be accurate.
109 |
110 | * Object.isFrozen
111 |
112 | Returns "false" in all legacy engines for all objects, which is
113 | conveniently guaranteed to be accurate.
114 |
115 | * Object.isExtensible
116 |
117 | Works like a charm, by trying very hard to extend the object then
118 | redacting the extension.
119 |
120 | ### May fail
121 |
122 | * :warning: Object.getOwnPropertyDescriptor
123 |
124 | The behavior of this shim does not conform to ES5. It should
125 | probably not be used at this time, until its behavior has been
126 | reviewed and been confirmed to be useful in legacy engines.
127 |
128 | * :warning: Object.defineProperty
129 |
130 | In the worst of circumstances, IE 8 provides a version of this
131 | method that only works on DOM objects. This sham will not be
132 | installed. The given version of `defineProperty` will throw an
133 | exception if used on non-DOM objects.
134 |
135 | In slightly better circumstances, this method will silently fail to
136 | set "writable", "enumerable", and "configurable" properties.
137 |
138 | Providing a getter or setter with "get" or "set" on a descriptor
139 | will silently fail on engines that lack "__defineGetter__" and
140 | "__defineSetter__", which include all versions of IE.
141 |
142 | https://github.com/es-shims/es5-shim/issues#issue/5
143 |
144 | * :warning: Object.defineProperties
145 |
146 | This uses the Object.defineProperty shim.
147 |
148 | * Object.seal
149 |
150 | Silently fails on all legacy engines. This should be
151 | fine unless you are depending on the safety and security
152 | provisions of this method, which you cannot possibly
153 | obtain in legacy engines.
154 |
155 | * Object.freeze
156 |
157 | Silently fails on all legacy engines. This should be
158 | fine unless you are depending on the safety and security
159 | provisions of this method, which you cannot possibly
160 | obtain in legacy engines.
161 |
162 | * Object.preventExtensions
163 |
164 | Silently fails on all legacy engines. This should be
165 | fine unless you are depending on the safety and security
166 | provisions of this method, which you cannot possibly
167 | obtain in legacy engines.
168 |
169 | [npm-url]: https://npmjs.org/package/es5-shim
170 | [npm-version-svg]: http://versionbadg.es/es-shims/es5-shim.svg
171 | [travis-svg]: https://travis-ci.org/es-shims/es5-shim.svg
172 | [travis-url]: https://travis-ci.org/es-shims/es5-shim
173 | [deps-svg]: https://david-dm.org/es-shims/es5-shim.svg
174 | [deps-url]: https://david-dm.org/es-shims/es5-shim
175 | [dev-deps-svg]: https://david-dm.org/es-shims/es5-shim/dev-status.svg
176 | [dev-deps-url]: https://david-dm.org/es-shims/es5-shim#info=devDependencies
177 | [npm-badge-png]: https://nodei.co/npm/es5-shim.png?downloads=true&stars=true
178 |
--------------------------------------------------------------------------------
/public/js/es5-shim/es5-sham.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["es5-sham.js"],"names":["root","factory","define","amd","exports","module","returnExports","this","call","Function","prototypeOfObject","Object","prototype","owns","bind","hasOwnProperty","isEnumerable","propertyIsEnumerable","toStr","toString","defineGetter","defineSetter","lookupGetter","lookupSetter","supportsAccessors","__defineGetter__","__defineSetter__","__lookupGetter__","__lookupSetter__","getPrototypeOf","object","proto","__proto__","constructor","doesGetOwnPropertyDescriptorWork","sentinel","getOwnPropertyDescriptor","value","exception","defineProperty","getOwnPropertyDescriptorWorksOnObject","getOwnPropertyDescriptorWorksOnDom","document","createElement","getOwnPropertyDescriptorFallback","ERR_NON_OBJECT","property","TypeError","descriptor","enumerable","configurable","notPrototypeOfObject","getter","setter","get","set","writable","getOwnPropertyNames","keys","create","createEmpty","supportsProto","shouldUseActiveX","domain","ActiveXObject","getEmptyViaActiveX","empty","xDoc","write","close","parentWindow","getEmptyViaIFrame","iframe","parent","body","documentElement","style","display","appendChild","src","contentWindow","removeChild","isPrototypeOf","toLocaleString","valueOf","Empty","properties","Type","defineProperties","doesDefinePropertyWork","definePropertyWorksOnObject","definePropertyWorksOnDom","definePropertyFallback","definePropertiesFallback","ERR_NON_OBJECT_DESCRIPTOR","ERR_NON_OBJECT_TARGET","ERR_ACCESSORS_NOT_SUPPORTED","forEach","seal","freeze","freezeObject","preventExtensions","isSealed","isFrozen","isExtensible","name","returnValue"],"mappings":";;;;;CAaC,SAAUA,EAAMC,GACb,YAGA,UAAWC,UAAW,YAAcA,OAAOC,IAAK,CAE5CD,OAAOD,OACJ,UAAWG,WAAY,SAAU,CAIpCC,OAAOD,QAAUH,QACd,CAEHD,EAAKM,cAAgBL,OAE3BM,KAAM,WAER,GAAIC,GAAOC,SAASD,IACpB,IAAIE,GAAoBC,OAAOC,SAC/B,IAAIC,GAAOL,EAAKM,KAAKJ,EAAkBK,eACvC,IAAIC,GAAeR,EAAKM,KAAKJ,EAAkBO,qBAC/C,IAAIC,GAAQV,EAAKM,KAAKJ,EAAkBS,SAGxC,IAAIC,EACJ,IAAIC,EACJ,IAAIC,EACJ,IAAIC,EACJ,IAAIC,GAAoBX,EAAKH,EAAmB,mBAChD,IAAIc,EAAmB,CAEnBJ,EAAeZ,EAAKM,KAAKJ,EAAkBe,iBAC3CJ,GAAeb,EAAKM,KAAKJ,EAAkBgB,iBAC3CJ,GAAed,EAAKM,KAAKJ,EAAkBiB,iBAC3CJ,GAAef,EAAKM,KAAKJ,EAAkBkB,kBAM/C,IAAKjB,OAAOkB,eAAgB,CAQxBlB,OAAOkB,eAAiB,QAASA,gBAAeC,GAE5C,GAAIC,GAAQD,EAAOE,SAEnB,IAAID,GAASA,IAAU,KAAM,CACzB,MAAOA,OACJ,IAAIb,EAAMY,EAAOG,eAAiB,oBAAqB,CAC1D,MAAOH,GAAOG,YAAYrB,cACvB,IAAIkB,YAAkBnB,QAAQ,CACnC,MAAOD,OACF,CAKL,MAAO,QAQjB,GAAIwB,GAAmC,QAASA,kCAAiCJ,GAC7E,IACIA,EAAOK,SAAW,CAClB,OAAOxB,QAAOyB,yBAAyBN,EAAQ,YAAYO,QAAU,EACvE,MAAOC,GACL,MAAO,QAKf,IAAI3B,OAAO4B,eAAgB,CACvB,GAAIC,GAAwCN,KAC5C,IAAIO,SAA4CC,YAAa,aAC7DR,EAAiCQ,SAASC,cAAc,OACxD,KAAKF,IAAuCD,EAAuC,CAC/E,GAAII,GAAmCjC,OAAOyB,0BAItD,IAAKzB,OAAOyB,0BAA4BQ,EAAkC,CACtE,GAAIC,GAAiB,0DAGrBlC,QAAOyB,yBAA2B,QAASA,0BAAyBN,EAAQgB,GACxE,SAAYhB,KAAW,gBAAmBA,KAAW,YAAeA,IAAW,KAAM,CACjF,KAAM,IAAIiB,WAAUF,EAAiBf,GAKzC,GAAIc,EAAkC,CAClC,IACI,MAAOA,GAAiCpC,KAAKG,OAAQmB,EAAQgB,GAC/D,MAAOR,KAKb,GAAIU,EAGJ,KAAKnC,EAAKiB,EAAQgB,GAAW,CACzB,MAAOE,GAKXA,GACIC,WAAYjC,EAAac,EAAQgB,GACjCI,aAAc,KAKlB,IAAI1B,EAAmB,CAMnB,GAAIZ,GAAYkB,EAAOE,SACvB,IAAImB,GAAuBrB,IAAWpB,CAItC,IAAIyC,EAAsB,CACtBrB,EAAOE,UAAYtB,EAGvB,GAAI0C,GAAS9B,EAAaQ,EAAQgB,EAClC,IAAIO,GAAS9B,EAAaO,EAAQgB,EAElC,IAAIK,EAAsB,CAEtBrB,EAAOE,UAAYpB,EAGvB,GAAIwC,GAAUC,EAAQ,CAClB,GAAID,EAAQ,CACRJ,EAAWM,IAAMF,EAErB,GAAIC,EAAQ,CACRL,EAAWO,IAAMF,EAIrB,MAAOL,IAMfA,EAAWX,MAAQP,EAAOgB,EAC1BE,GAAWQ,SAAW,IACtB,OAAOR,IAOf,IAAKrC,OAAO8C,oBAAqB,CAC7B9C,OAAO8C,oBAAsB,QAASA,qBAAoB3B,GACtD,MAAOnB,QAAO+C,KAAK5B,IAM3B,IAAKnB,OAAOgD,OAAQ,CAGhB,GAAIC,EACJ,IAAIC,MAAoB7B,UAAW,eAAkBrB,QAUrD,IAAImD,GAAmB,QAASA,oBAE5B,IAAKpB,SAASqB,OAAQ,CAClB,MAAO,OAGX,IACI,QAAS,GAAIC,eAAc,YAC7B,MAAO1B,GACL,MAAO,QAOf,IAAI2B,GAAqB,QAASA,sBAC9B,GAAIC,EACJ,IAAIC,EAEJA,GAAO,GAAIH,eAAc,WAEzBG,GAAKC,MAAM,oBACXD,GAAKE,OAELH,GAAQC,EAAKG,aAAa3D,OAAOC,SACjCuD,GAAO,IAEP,OAAOD,GAMX,IAAIK,GAAoB,QAASA,qBAC7B,GAAIC,GAAS9B,SAASC,cAAc,SACpC,IAAI8B,GAAS/B,SAASgC,MAAQhC,SAASiC,eACvC,IAAIT,EAEJM,GAAOI,MAAMC,QAAU,MACvBJ,GAAOK,YAAYN,EAEnBA,GAAOO,IAAM,aAGbb,GAAQM,EAAOQ,cAAcrE,OAAOC,SACpC6D,GAAOQ,YAAYT,EACnBA,GAAS,IAET,OAAON,GAIX,IAAIL,SAAwBnB,YAAa,YAAa,CAClDkB,EAAc,WACV,OAAS5B,UAAW,WAErB,CAMH4B,EAAc,WAGV,GAAIM,GAAQJ,IAAqBG,IAAuBM,UAEjDL,GAAMjC,kBACNiC,GAAMnD,qBACNmD,GAAMjD,2BACNiD,GAAMgB,oBACNhB,GAAMiB,qBACNjB,GAAM/C,eACN+C,GAAMkB,OAEb,IAAIC,GAAQ,QAASA,UACrBA,GAAMzE,UAAYsD,CAElBN,GAAc,WACV,MAAO,IAAIyB,GAEf,OAAO,IAAIA,IAInB1E,OAAOgD,OAAS,QAASA,QAAO/C,EAAW0E,GAEvC,GAAIxD,EACJ,IAAIyD,GAAO,QAASA,SAEpB,IAAI3E,IAAc,KAAM,CACpBkB,EAAS8B,QACN,CACH,SAAWhD,KAAc,gBAAmBA,KAAc,WAAY,CAMlE,KAAM,IAAImC,WAAU,kDAExBwC,EAAK3E,UAAYA,CACjBkB,GAAS,GAAIyD,EAMbzD,GAAOE,UAAYpB,EAIvB,GAAI0E,QAAoB,GAAG,CACvB3E,OAAO6E,iBAAiB1D,EAAQwD,GAGpC,MAAOxD,IAgBf,GAAI2D,GAAyB,QAASA,wBAAuB3D,GACzD,IACInB,OAAO4B,eAAeT,EAAQ,cAC9B,OAAO,YAAcA,GACvB,MAAOQ,GACL,MAAO,QAMf,IAAI3B,OAAO4B,eAAgB,CACvB,GAAImD,GAA8BD,KAClC,IAAIE,SAAkCjD,YAAa,aAC/C+C,EAAuB/C,SAASC,cAAc,OAClD,KAAK+C,IAAgCC,EAA0B,CAC3D,GAAIC,GAAyBjF,OAAO4B,eAChCsD,EAA2BlF,OAAO6E,kBAI9C,IAAK7E,OAAO4B,gBAAkBqD,EAAwB,CAClD,GAAIE,GAA4B,0CAChC,IAAIC,GAAwB,8CAC5B,IAAIC,GAA8B,gEAElCrF,QAAO4B,eAAiB,QAASA,gBAAeT,EAAQgB,EAAUE,GAC9D,SAAYlB,KAAW,gBAAmBA,KAAW,YAAeA,IAAW,KAAM,CACjF,KAAM,IAAIiB,WAAUgD,EAAwBjE,GAEhD,SAAYkB,KAAe,gBAAmBA,KAAe,YAAeA,IAAe,KAAM,CAC7F,KAAM,IAAID,WAAU+C,EAA4B9C,GAIpD,GAAI4C,EAAwB,CACxB,IACI,MAAOA,GAAuBpF,KAAKG,OAAQmB,EAAQgB,EAAUE,GAC/D,MAAOV,KAMb,GAAI,SAAWU,GAAY,CAevB,GAAIxB,IAAsBF,EAAaQ,EAAQgB,IAAavB,EAAaO,EAAQgB,IAAY,CAMzF,GAAIlC,GAAYkB,EAAOE,SACvBF,GAAOE,UAAYtB,QAGZoB,GAAOgB,EACdhB,GAAOgB,GAAYE,EAAWX,KAE9BP,GAAOE,UAAYpB,MAEhB,CACHkB,EAAOgB,GAAYE,EAAWX,WAE/B,CACH,IAAKb,IAAuB,OAASwB,IAAgB,OAASA,IAAc,CACxE,KAAM,IAAID,WAAUiD,GAGxB,GAAI,OAAShD,GAAY,CACrB5B,EAAaU,EAAQgB,EAAUE,EAAWM,KAE9C,GAAI,OAASN,GAAY,CACrB3B,EAAaS,EAAQgB,EAAUE,EAAWO,MAGlD,MAAOzB,IAMf,IAAKnB,OAAO6E,kBAAoBK,EAA0B,CACtDlF,OAAO6E,iBAAmB,QAASA,kBAAiB1D,EAAQwD,GAExD,GAAIO,EAA0B,CAC1B,IACI,MAAOA,GAAyBrF,KAAKG,OAAQmB,EAAQwD,GACvD,MAAOhD,KAKb3B,OAAO+C,KAAK4B,GAAYW,QAAQ,SAAUnD,GACtC,GAAIA,IAAa,YAAa,CAC1BnC,OAAO4B,eAAeT,EAAQgB,EAAUwC,EAAWxC,MAG3D,OAAOhB,IAMf,IAAKnB,OAAOuF,KAAM,CACdvF,OAAOuF,KAAO,QAASA,MAAKpE,GACxB,GAAInB,OAAOmB,KAAYA,EAAQ,CAC3B,KAAM,IAAIiB,WAAU,8CAKxB,MAAOjB,IAMf,IAAKnB,OAAOwF,OAAQ,CAChBxF,OAAOwF,OAAS,QAASA,QAAOrE,GAC5B,GAAInB,OAAOmB,KAAYA,EAAQ,CAC3B,KAAM,IAAIiB,WAAU,gDAKxB,MAAOjB,IAKf,IACInB,OAAOwF,OAAO,cAChB,MAAO7D,GACL3B,OAAOwF,OAAU,SAAUC,GACvB,MAAO,SAASD,QAAOrE,GACnB,SAAWA,KAAW,WAAY,CAC9B,MAAOA,OACJ,CACH,MAAOsE,GAAatE,MAG9BnB,OAAOwF,QAKb,IAAKxF,OAAO0F,kBAAmB,CAC3B1F,OAAO0F,kBAAoB,QAASA,mBAAkBvE,GAClD,GAAInB,OAAOmB,KAAYA,EAAQ,CAC3B,KAAM,IAAIiB,WAAU,2DAKxB,MAAOjB,IAMf,IAAKnB,OAAO2F,SAAU,CAClB3F,OAAO2F,SAAW,QAASA,UAASxE,GAChC,GAAInB,OAAOmB,KAAYA,EAAQ,CAC3B,KAAM,IAAIiB,WAAU,kDAExB,MAAO,QAMf,IAAKpC,OAAO4F,SAAU,CAClB5F,OAAO4F,SAAW,QAASA,UAASzE,GAChC,GAAInB,OAAOmB,KAAYA,EAAQ,CAC3B,KAAM,IAAIiB,WAAU,kDAExB,MAAO,QAMf,IAAKpC,OAAO6F,aAAc,CACtB7F,OAAO6F,aAAe,QAASA,cAAa1E,GAExC,GAAInB,OAAOmB,KAAYA,EAAQ,CAC3B,KAAM,IAAIiB,WAAU,sDAGxB,GAAI0D,GAAO,EACX,OAAO5F,EAAKiB,EAAQ2E,GAAO,CACvBA,GAAQ,IAEZ3E,EAAO2E,GAAQ,IACf,IAAIC,GAAc7F,EAAKiB,EAAQ2E,SACxB3E,GAAO2E,EACd,OAAOC"}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebRTC Tutorial
2 |
3 | [Demo](https://youtu.be/PkFrmtpGK8w)
4 |
5 | How WebRTC works? (by Minko Gechev)
6 |
7 | 
8 |
9 | Prerequisites of this tutorial:
10 |
11 | Web server, database, socket handler, WebRTC adapter, etc. In this demo, Node.js and Express.js are used to build the web server, nginx is used as the proxy server handling load balancing, standalone MongoDB is used as the database to store socket information, Redis is used to handle session information, socket.io is used to handle socket channel, official adapter.js is the WebRTC adapter, Angular.js is used to construct the front-end MVC architecture, and so on.
12 |
13 | Technical Terms of WebRTC:
14 |
15 | [Network Address Translation (NAT)](https://en.wikipedia.org/wiki/Network_address_translation)
16 |
17 | [Session Traversal Utilities for NAT (STUN)](https://tools.ietf.org/html/rfc5389)
18 |
19 | [Traversal Using Relays around NAT (TURN): Relay Extensions to STUN](https://tools.ietf.org/html/rfc5766)
20 |
21 | [Interactive Connectivity Establishment (ICE): A Protocol for NAT Traversal for Offer/Answer Protocols](https://tools.ietf.org/html/rfc5245)
22 |
23 |
24 | Demo 1. Broadcasting with recording mechanism (Chrome/Firefox)
25 |
26 | 1-1. **Initiate a Node.js project:**
27 |
28 | See package.json as the reference of required modules
29 |
30 | 1-2. **Back-end Development:**
31 |
32 | Get streams.js and socketHandler.js ready, before creating a server.js; streams handles the stream information and conmmunication with the database which is MongoDB in our case, and socketHandler.js handles the communication between streams.js and socket.io
33 |
34 | 1-3. **Front-end Development:**
35 |
36 | Start to construct the front-end architecture built with angular.js, socket.io-client, WebRTC adapter.js, and rtcClient.js; angular.js is used to construct front-end MVC architecture, socket.io-client is used to handle the socket comminucation between front-end and back-end, adapter.js is an interface to handle RTC Peer, rtcClient.js is used to create a PeerManager to handle the communication between WebRTC adapter and front-end application which is built with angular.js
37 |
38 | 1-4. **Put all together and start to test**
39 |
40 | http(s)://\/broadcast is for broadcasting real-time video streams
41 |
42 | http(s)://\/watcher is for watching the broadcasted video streams
43 |
44 | Flows:
45 |
46 | * General (users visit the web site)
47 |
48 | Browser --(1. Visit Website Broadcast/Watcher)--> Web Server --(2. Handle Socket Information)--> Socket Handler --(3. Send Notification)--> Browser
49 |
50 | * Broadcast
51 |
52 | ng-controller --(1. Configure Media Stream)--> adapter --(2. Set Local Stream)--> rtcClient --(3. Set Camera Stream)--> camera --(Notify All Scope to Update Message on Views)--> mg-controller
53 |
54 |
55 | * Watcher
56 |
57 | ng-controller --(1. Add Remote Peer)--> rtcClient --(2. Return Remote Peer)--> ng-controller
58 |
59 | 1. Add Remote Peer
60 |
61 | Initiate a new Peer with peerConnectionConfig and peerConnectionConstraints. If success, send an notification to others via socket that a new ICE candidate is available
62 |
63 | Send **init** message to notify others via socket.io
64 |
65 | Remote broadcast site receives **init** notification and then send **offer** back to watcher site
66 |
67 | Watcher site receives **offer**, set session description, and then send **answer** back to broadcast site
68 |
69 | Broadcast site receives **answer** and set session description
70 |
71 | ===
72 |
73 | SocketIO with Redis
74 |
75 | [socket.io-redis](https://github.com/socketio/socket.io-redis)
76 |
77 | [Using multiple nodes of SocketIO](http://socket.io/docs/using-multiple-nodes/)
78 |
79 | ===
80 |
81 | NOTE:
82 |
83 | 1. BinaryJS and rtcRecorder.js are for real-time recoding. Help yourself, if you get interested.
84 |
85 | 2. The configuration template of Nginx and the setting of binaryjs server are stored respectively in my_nginx and my_binaryjs folders
86 |
87 | Ref.
88 |
89 | [PPT and Description for Broadcasting](https://drive.google.com/open?id=0BzeAAvM5Ha9sclY5SzJjTGMwQkk)
90 |
91 | [WebRTC Infrastructure](http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/)
92 |
93 | [Understanding WebRTC Media Connections: ICE, STUN and TURN](http://www.avaya.com/blogs/archives/2014/08/understanding-webrtc-media-connections-ice-stun-and-turn.html)
94 |
95 | [WebRTC beyond one-to-one communication](https://webrtchacks.com/webrtc-beyond-one-one/)
96 |
97 | [W3C WebRTC](https://w3c.github.io/webrtc-pc/)
98 |
99 | [Symmetric NAT and It’s Problems](http://www.think-like-a-computer.com/2011/09/19/symmetric-nat/)
100 |
101 | [Multi-User Video Conference with WebRTC](http://blog.mgechev.com/2014/12/26/multi-user-video-conference-webrtc-angularjs-yeoman/)
102 |
103 | [WebRTC adapter.js](https://github.com/sarandogou/webrtc/blob/master/samples/web/js/adapter.js)
104 |
105 | [WebRTC-everywhere](https://github.com/sarandogou/webrtc-everywhere)
106 |
107 | [Temasys](https://temasys.atlassian.net/wiki/display/TWPP/How+to+integrate+the+Temasys+WebRTC+Plugin+into+your+website)
108 |
109 | [Kurento Introduction (for Java developers)](https://webrtchacks.com/kurento/)
110 |
111 | [Kurento (for Java developers)](https://www.kurento.org/)
112 |
113 | [BinaryJS API](https://github.com/binaryjs/binaryjs/blob/master/doc/api.md)
114 |
115 | [MediaRecorder](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder)
116 |
117 | [DataChannel for Beginner](https://www.webrtc-experiment.com/docs/rtc-datachannel-for-beginners.html)
118 |
119 | [MediaStreamer](https://github.com/muaz-khan/WebRTC-Experiment/blob/master/Pre-recorded-Media-Streaming/MediaStreamer.js)
120 |
121 | [A mobile-first WebRTC client framework for building native apps](https://github.com/EricssonResearch/openwebrtc)
122 |
123 | [A React Native wrapper for the Twilio mobile SDK](https://github.com/rogchap/react-native-twilio)
124 |
125 | [A WebRTC module for React Native](https://github.com/oney/react-native-webrtc)
126 |
127 | Others:
128 |
129 | [Video Processing Experiments with OpenCV and WebRTC (WebRTC could be used to do something similar to the project of Commma.ai)](https://github.com/concord-consortium/video-processing-experiments)
130 |
131 | [Fingerprint](https://github.com/Valve/fingerprintjs)
132 |
133 | Talks:
134 |
135 | [WebRTC for Beginners](https://www.youtube.com/watch?v=RvJuMJUSw8U)
136 |
137 | [Google: What's next for WebRTC?](https://www.youtube.com/watch?v=hl3_dJxKpYo)
138 |
139 | [WebRTC How it Works and How it Breaks](https://www.youtube.com/watch?v=3TbVi9aB09k)
140 |
141 | [Swatting NATs with TURN and WebRTC](https://www.youtube.com/watch?v=JrxSDIJv5xs)
142 |
143 | [WebRTC Everywhere](https://www.youtube.com/watch?v=HJjZtXVeAuw)
144 |
145 | [The Future of ORTC with WebRTC](https://www.youtube.com/watch?v=nQ_NgkpLyjw)
146 |
147 | [Recordings WebRTC Meetup Barcelona 2016](https://www.youtube.com/watch?v=vXia75-cay0)
148 |
149 | Companies:
150 |
151 | [TokBox](https://tokbox.com/)
152 |
153 | [Appear.in](https://appear.in/)
154 |
155 | [Temasys](https://temasys.com.sg/)
156 |
157 | Issues:
158 |
159 | [Can't create RTCIceCandidate](https://github.com/sarandogou/webrtc-everywhere/issues/43)
160 |
161 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/lib/jasmine-html.js:
--------------------------------------------------------------------------------
1 | jasmine.TrivialReporter = function(doc) {
2 | this.document = doc || document;
3 | this.suiteDivs = {};
4 | this.logRunningSpecs = false;
5 | };
6 |
7 | jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
8 | var el = document.createElement(type);
9 |
10 | for (var i = 2; i < arguments.length; i++) {
11 | var child = arguments[i];
12 |
13 | if (typeof child === 'string') {
14 | el.appendChild(document.createTextNode(child));
15 | } else {
16 | if (child) { el.appendChild(child); }
17 | }
18 | }
19 |
20 | for (var attr in attrs) {
21 | if (attr == "className") {
22 | el[attr] = attrs[attr];
23 | } else {
24 | el.setAttribute(attr, attrs[attr]);
25 | }
26 | }
27 |
28 | return el;
29 | };
30 |
31 | jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
32 | var showPassed, showSkipped;
33 |
34 | this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
35 | this.createDom('div', { className: 'banner' },
36 | this.createDom('div', { className: 'logo' },
37 | this.createDom('span', { className: 'title' }, "Jasmine"),
38 | this.createDom('span', { className: 'version' }, runner.env.versionString())),
39 | this.createDom('div', { className: 'options' },
40 | "Show ",
41 | showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
42 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
43 | showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
44 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
45 | )
46 | ),
47 |
48 | this.runnerDiv = this.createDom('div', { className: 'runner running' },
49 | this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
50 | this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
51 | this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
52 | );
53 |
54 | this.document.body.appendChild(this.outerDiv);
55 |
56 | var suites = runner.suites();
57 | for (var i = 0; i < suites.length; i++) {
58 | var suite = suites[i];
59 | var suiteDiv = this.createDom('div', { className: 'suite' },
60 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
61 | this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
62 | this.suiteDivs[suite.id] = suiteDiv;
63 | var parentDiv = this.outerDiv;
64 | if (suite.parentSuite) {
65 | parentDiv = this.suiteDivs[suite.parentSuite.id];
66 | }
67 | parentDiv.appendChild(suiteDiv);
68 | }
69 |
70 | this.startedAt = new Date();
71 |
72 | var self = this;
73 | showPassed.onclick = function(evt) {
74 | if (showPassed.checked) {
75 | self.outerDiv.className += ' show-passed';
76 | } else {
77 | self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
78 | }
79 | };
80 |
81 | showSkipped.onclick = function(evt) {
82 | if (showSkipped.checked) {
83 | self.outerDiv.className += ' show-skipped';
84 | } else {
85 | self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
86 | }
87 | };
88 | };
89 |
90 | jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
91 | var results = runner.results();
92 | var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
93 | this.runnerDiv.setAttribute("class", className);
94 | //do it twice for IE
95 | this.runnerDiv.setAttribute("className", className);
96 | var specs = runner.specs();
97 | var specCount = 0;
98 | for (var i = 0; i < specs.length; i++) {
99 | if (this.specFilter(specs[i])) {
100 | specCount++;
101 | }
102 | }
103 | var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
104 | message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
105 | this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
106 |
107 | this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
108 | };
109 |
110 | jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
111 | var results = suite.results();
112 | var status = results.passed() ? 'passed' : 'failed';
113 | if (results.totalCount === 0) { // todo: change this to check results.skipped
114 | status = 'skipped';
115 | }
116 | this.suiteDivs[suite.id].className += " " + status;
117 | };
118 |
119 | jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
120 | if (this.logRunningSpecs) {
121 | this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
122 | }
123 | };
124 |
125 | jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
126 | var results = spec.results();
127 | var status = results.passed() ? 'passed' : 'failed';
128 | if (results.skipped) {
129 | status = 'skipped';
130 | }
131 | var specDiv = this.createDom('div', { className: 'spec ' + status },
132 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
133 | this.createDom('a', {
134 | className: 'description',
135 | href: '?spec=' + encodeURIComponent(spec.getFullName()),
136 | title: spec.getFullName()
137 | }, spec.description));
138 |
139 |
140 | var resultItems = results.getItems();
141 | var messagesDiv = this.createDom('div', { className: 'messages' });
142 | for (var i = 0; i < resultItems.length; i++) {
143 | var result = resultItems[i];
144 |
145 | if (result.type == 'log') {
146 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
147 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
148 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
149 |
150 | if (result.trace.stack) {
151 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
152 | }
153 | }
154 | }
155 |
156 | if (messagesDiv.childNodes.length > 0) {
157 | specDiv.appendChild(messagesDiv);
158 | }
159 |
160 | this.suiteDivs[spec.suite.id].appendChild(specDiv);
161 | };
162 |
163 | jasmine.TrivialReporter.prototype.log = function() {
164 | var console = jasmine.getGlobal().console;
165 | if (console && console.log) {
166 | if (console.log.apply) {
167 | console.log.apply(console, arguments);
168 | } else {
169 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
170 | }
171 | }
172 | };
173 |
174 | jasmine.TrivialReporter.prototype.getLocation = function() {
175 | return this.document.location;
176 | };
177 |
178 | jasmine.TrivialReporter.prototype.specFilter = function(spec) {
179 | var paramMap = {};
180 | var params = this.getLocation().search.substring(1).split('&');
181 | for (var i = 0; i < params.length; i++) {
182 | var p = params[i].split('=');
183 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
184 | }
185 |
186 | if (!paramMap.spec) {
187 | return true;
188 | }
189 | return spec.getFullName().indexOf(paramMap.spec) === 0;
190 | };
191 |
--------------------------------------------------------------------------------
/public/js/es5-shim/tests/spec/s-function.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, expect, beforeEach */
2 |
3 | describe('Function', function () {
4 | 'use strict';
5 |
6 | describe('#apply()', function () {
7 | it('works with arraylike objects', function () {
8 | var arrayLike = { length: 4, 0: 1, 2: 4, 3: true };
9 | var expectedArray = [1, undefined, 4, true];
10 | var actualArray = (function () {
11 | return Array.prototype.slice.apply(arguments);
12 | }.apply(null, arrayLike));
13 | expect(actualArray).toEqual(expectedArray);
14 | });
15 | });
16 |
17 | describe('#bind()', function () {
18 | var actual;
19 |
20 | var testSubject = {
21 | push: function (o) {
22 | this.a.push(o);
23 | }
24 | };
25 |
26 | var func = function func() {
27 | Array.prototype.forEach.call(arguments, function (a) {
28 | this.push(a);
29 | }, this);
30 | return this;
31 | };
32 |
33 | beforeEach(function () {
34 | actual = [];
35 | testSubject.a = [];
36 | });
37 |
38 | it('binds properly without a context', function () {
39 | var context;
40 | testSubject.func = function () {
41 | context = this;
42 | }.bind();
43 | testSubject.func();
44 | expect(context).toBe(function () { return this; }.call());
45 | });
46 | it('binds properly without a context, and still supplies bound arguments', function () {
47 | var a, context;
48 | testSubject.func = function () {
49 | a = Array.prototype.slice.call(arguments);
50 | context = this;
51 | }.bind(undefined, 1, 2, 3);
52 | testSubject.func(1, 2, 3);
53 | expect(a).toEqual([1, 2, 3, 1, 2, 3]);
54 | expect(context).toBe(function () { return this; }.call());
55 | });
56 | it('binds a context properly', function () {
57 | testSubject.func = func.bind(actual);
58 | testSubject.func(1, 2, 3);
59 | expect(actual).toEqual([1, 2, 3]);
60 | expect(testSubject.a).toEqual([]);
61 | });
62 | it('binds a context and supplies bound arguments', function () {
63 | testSubject.func = func.bind(actual, 1, 2, 3);
64 | testSubject.func(4, 5, 6);
65 | expect(actual).toEqual([1, 2, 3, 4, 5, 6]);
66 | expect(testSubject.a).toEqual([]);
67 | });
68 |
69 | it('returns properly without binding a context', function () {
70 | testSubject.func = function () {
71 | return this;
72 | }.bind();
73 | var context = testSubject.func();
74 | expect(context).toBe(function () { return this; }.call());
75 | });
76 | it('returns properly without binding a context, and still supplies bound arguments', function () {
77 | var context;
78 | testSubject.func = function () {
79 | context = this;
80 | return Array.prototype.slice.call(arguments);
81 | }.bind(undefined, 1, 2, 3);
82 | actual = testSubject.func(1, 2, 3);
83 | expect(context).toBe(function () { return this; }.call());
84 | expect(actual).toEqual([1, 2, 3, 1, 2, 3]);
85 | });
86 | it('returns properly while binding a context properly', function () {
87 | var ret;
88 | testSubject.func = func.bind(actual);
89 | ret = testSubject.func(1, 2, 3);
90 | expect(ret).toBe(actual);
91 | expect(ret).not.toBe(testSubject);
92 | });
93 | it('returns properly while binding a context and supplies bound arguments', function () {
94 | var ret;
95 | testSubject.func = func.bind(actual, 1, 2, 3);
96 | ret = testSubject.func(4, 5, 6);
97 | expect(ret).toBe(actual);
98 | expect(ret).not.toBe(testSubject);
99 | });
100 | it('has the new instance\'s context as a constructor', function () {
101 | var actualContext;
102 | var expectedContext = { foo: 'bar' };
103 | testSubject.Func = function () {
104 | actualContext = this;
105 | }.bind(expectedContext);
106 | var result = new testSubject.Func();
107 | expect(result).toBeTruthy();
108 | expect(actualContext).not.toBe(expectedContext);
109 | });
110 | it('passes the correct arguments as a constructor', function () {
111 | var expected = { name: 'Correct' };
112 | testSubject.Func = function (arg) {
113 | expect(this.hasOwnProperty('name')).toBe(false);
114 | return arg;
115 | }.bind({ name: 'Incorrect' });
116 | var ret = new testSubject.Func(expected);
117 | expect(ret).toBe(expected);
118 | });
119 | it('returns the return value of the bound function when called as a constructor', function () {
120 | var oracle = [1, 2, 3];
121 | var Subject = function () {
122 | expect(this).not.toBe(oracle);
123 | return oracle;
124 | }.bind(null);
125 | var result = new Subject();
126 | expect(result).toBe(oracle);
127 | });
128 |
129 | it('returns the correct value if constructor returns primitive', function () {
130 | var Subject = function (oracle) {
131 | expect(this).not.toBe(oracle);
132 | return oracle;
133 | }.bind(null);
134 |
135 | var primitives = ['asdf', null, true, 1];
136 | for (var i = 0; i < primitives.length; ++i) {
137 | expect(new Subject(primitives[i])).not.toBe(primitives[i]);
138 | }
139 |
140 | var objects = [[1, 2, 3], {}, function () {}];
141 | for (var j = 0; j < objects.length; ++j) {
142 | expect(new Subject(objects[j])).toBe(objects[j]);
143 | }
144 | });
145 | it('returns the value that instance of original "class" when called as a constructor', function () {
146 | var ClassA = function (x) {
147 | this.name = x || 'A';
148 | };
149 | var ClassB = ClassA.bind(null, 'B');
150 |
151 | var result = new ClassB();
152 | expect(result instanceof ClassA).toBe(true);
153 | expect(result instanceof ClassB).toBe(true);
154 | });
155 | it('sets a correct length without thisArg', function () {
156 | var Subject = function (a, b, c) { return a + b + c; }.bind();
157 | expect(Subject.length).toBe(3);
158 | });
159 | it('sets a correct length with thisArg', function () {
160 | var Subject = function (a, b, c) { return a + b + c + this.d; }.bind({ d: 1 });
161 | expect(Subject.length).toBe(3);
162 | });
163 | it('sets a correct length with thisArg and first argument', function () {
164 | var Subject = function (a, b, c) { return a + b + c + this.d; }.bind({ d: 1 }, 1);
165 | expect(Subject.length).toBe(2);
166 | });
167 | it('sets a correct length without thisArg and first argument', function () {
168 | var Subject = function (a, b, c) { return a + b + c; }.bind(undefined, 1);
169 | expect(Subject.length).toBe(2);
170 | });
171 | it('sets a correct length without thisArg and too many argument', function () {
172 | var Subject = function (a, b, c) { return a + b + c; }.bind(undefined, 1, 2, 3, 4);
173 | expect(Subject.length).toBe(0);
174 | });
175 | });
176 | });
177 |
--------------------------------------------------------------------------------
/public/js/json3/lib/json3.min.js:
--------------------------------------------------------------------------------
1 | /*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */
2 | (function(){function N(p,r){function q(a){if(q[a]!==w)return q[a];var c;if("bug-string-char-index"==a)c="a"!="a"[0];else if("json"==a)c=q("json-stringify")&&q("json-parse");else{var e;if("json-stringify"==a){c=r.stringify;var b="function"==typeof c&&s;if(b){(e=function(){return 1}).toJSON=e;try{b="0"===c(0)&&"0"===c(new t)&&'""'==c(new A)&&c(u)===w&&c(w)===w&&c()===w&&"1"===c(e)&&"[1]"==c([e])&&"[null]"==c([w])&&"null"==c(null)&&"[null,null,null]"==c([w,u,null])&&'{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'==
3 | c({a:[e,!0,!1,null,"\x00\b\n\f\r\t"]})&&"1"===c(null,e)&&"[\n 1,\n 2\n]"==c([1,2],null,1)&&'"-271821-04-20T00:00:00.000Z"'==c(new C(-864E13))&&'"+275760-09-13T00:00:00.000Z"'==c(new C(864E13))&&'"-000001-01-01T00:00:00.000Z"'==c(new C(-621987552E5))&&'"1969-12-31T23:59:59.999Z"'==c(new C(-1))}catch(f){b=!1}}c=b}if("json-parse"==a){c=r.parse;if("function"==typeof c)try{if(0===c("0")&&!c(!1)){e=c('{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}');var n=5==e.a.length&&1===e.a[0];if(n){try{n=!c('"\t"')}catch(d){}if(n)try{n=
4 | 1!==c("01")}catch(g){}if(n)try{n=1!==c("1.")}catch(m){}}}}catch(X){n=!1}c=n}}return q[a]=!!c}p||(p=k.Object());r||(r=k.Object());var t=p.Number||k.Number,A=p.String||k.String,H=p.Object||k.Object,C=p.Date||k.Date,G=p.SyntaxError||k.SyntaxError,K=p.TypeError||k.TypeError,L=p.Math||k.Math,I=p.JSON||k.JSON;"object"==typeof I&&I&&(r.stringify=I.stringify,r.parse=I.parse);var H=H.prototype,u=H.toString,v,B,w,s=new C(-0xc782b5b800cec);try{s=-109252==s.getUTCFullYear()&&0===s.getUTCMonth()&&1===s.getUTCDate()&&
5 | 10==s.getUTCHours()&&37==s.getUTCMinutes()&&6==s.getUTCSeconds()&&708==s.getUTCMilliseconds()}catch(Q){}if(!q("json")){var D=q("bug-string-char-index");if(!s)var x=L.floor,M=[0,31,59,90,120,151,181,212,243,273,304,334],E=function(a,c){return M[c]+365*(a-1970)+x((a-1969+(c=+(1d){c+="\\u00"+y(2,d.toString(16));break}c+=f?n[b]:a.charAt(b)}}return c+'"'},O=function(a,c,b,h,f,n,d){var g,m,k,l,p,r,s,t,q;try{g=c[a]}catch(z){}if("object"==typeof g&&g)if(m=u.call(g),"[object Date]"!=m||v.call(g,
9 | "toJSON"))"function"==typeof g.toJSON&&("[object Number]"!=m&&"[object String]"!=m&&"[object Array]"!=m||v.call(g,"toJSON"))&&(g=g.toJSON(a));else if(g>-1/0&&g<1/0){if(E){l=x(g/864E5);for(m=x(l/365.2425)+1970-1;E(m+1,0)<=l;m++);for(k=x((l-E(m,0))/30.42);E(m,k+1)<=l;k++);l=1+l-E(m,k);p=(g%864E5+864E5)%864E5;r=x(p/36E5)%24;s=x(p/6E4)%60;t=x(p/1E3)%60;p%=1E3}else m=g.getUTCFullYear(),k=g.getUTCMonth(),l=g.getUTCDate(),r=g.getUTCHours(),s=g.getUTCMinutes(),t=g.getUTCSeconds(),p=g.getUTCMilliseconds();
10 | g=(0>=m||1E4<=m?(0>m?"-":"+")+y(6,0>m?-m:m):y(4,m))+"-"+y(2,k+1)+"-"+y(2,l)+"T"+y(2,r)+":"+y(2,s)+":"+y(2,t)+"."+y(3,p)+"Z"}else g=null;b&&(g=b.call(c,a,g));if(null===g)return"null";m=u.call(g);if("[object Boolean]"==m)return""+g;if("[object Number]"==m)return g>-1/0&&g<1/0?""+g:"null";if("[object String]"==m)return R(""+g);if("object"==typeof g){for(a=d.length;a--;)if(d[a]===g)throw K();d.push(g);q=[];c=n;n+=f;if("[object Array]"==m){k=0;for(a=g.length;k=b.length?b:b.slice(0,10));return O("",(l={},l[""]=a,l),f,n,h,"",[])}}if(!q("json-parse")){var V=A.fromCharCode,W={92:"\\",34:'"',47:"/",98:"\b",116:"\t",110:"\n",102:"\f",114:"\r"},b,J,l=function(){b=J=null;throw G();},z=function(){for(var a=J,c=a.length,e,h,f,k,d;bd)l();else if(92==d)switch(d=a.charCodeAt(++b),d){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:e+=W[d];b++;break;case 117:h=++b;for(f=b+4;b=d||97<=d&&102>=d||65<=d&&70>=d||l();e+=V("0x"+a.slice(h,b));break;default:l()}else{if(34==d)break;d=a.charCodeAt(b);for(h=b;32<=d&&92!=d&&34!=d;)d=a.charCodeAt(++b);e+=a.slice(h,b)}if(34==a.charCodeAt(b))return b++,e;l();default:h=
14 | b;45==d&&(k=!0,d=a.charCodeAt(++b));if(48<=d&&57>=d){for(48==d&&(d=a.charCodeAt(b+1),48<=d&&57>=d)&&l();b=d);b++);if(46==a.charCodeAt(b)){for(f=++b;f=d);f++);f==b&&l();b=f}d=a.charCodeAt(b);if(101==d||69==d){d=a.charCodeAt(++b);43!=d&&45!=d||b++;for(f=b;f=d);f++);f==b&&l();b=f}return+a.slice(h,b)}k&&l();if("true"==a.slice(b,b+4))return b+=4,!0;if("false"==a.slice(b,b+5))return b+=5,!1;if("null"==a.slice(b,
15 | b+4))return b+=4,null;l()}return"$"},P=function(a){var c,b;"$"==a&&l();if("string"==typeof a){if("@"==(D?a.charAt(0):a[0]))return a.slice(1);if("["==a){for(c=[];;b||(b=!0)){a=z();if("]"==a)break;b&&(","==a?(a=z(),"]"==a&&l()):l());","==a&&l();c.push(P(a))}return c}if("{"==a){for(c={};;b||(b=!0)){a=z();if("}"==a)break;b&&(","==a?(a=z(),"}"==a&&l()):l());","!=a&&"string"==typeof a&&"@"==(D?a.charAt(0):a[0])&&":"==z()||l();c[a.slice(1)]=P(z())}return c}l()}return a},T=function(a,b,e){e=S(a,b,e);e===
16 | w?delete a[b]:a[b]=e},S=function(a,b,e){var h=a[b],f;if("object"==typeof h&&h)if("[object Array]"==u.call(h))for(f=h.length;f--;)T(h,f,e);else B(h,function(a){T(h,a,e)});return e.call(a,b,h)};r.parse=function(a,c){var e,h;b=0;J=""+a;e=P(z());"$"!=z()&&l();b=J=null;return c&&"[object Function]"==u.call(c)?S((h={},h[""]=e,h),"",c):e}}}r.runInContext=N;return r}var K=typeof define==="function"&&define.amd,F={"function":!0,object:!0},G=F[typeof exports]&&exports&&!exports.nodeType&&exports,k=F[typeof window]&&
17 | window||this,t=G&&F[typeof module]&&module&&!module.nodeType&&"object"==typeof global&&global;!t||t.global!==t&&t.window!==t&&t.self!==t||(k=t);if(G&&!K)N(k,G);else{var L=k.JSON,Q=k.JSON3,M=!1,A=N(k,k.JSON3={noConflict:function(){M||(M=!0,k.JSON=L,k.JSON3=Q,L=Q=null);return A}});k.JSON={parse:A.parse,stringify:A.stringify}}K&&define(function(){return A})}).call(this);
18 |
--------------------------------------------------------------------------------
/public/js/rtcClient.min.js:
--------------------------------------------------------------------------------
1 | /*! webrtc 20-10-2016 */"use strict";var PeerManager=function(arg_user_type){function initLocalPeer(){var localPeer=new LocalPeer(config.peerConnectionConfig,config.peerConnectionConstraints),dataChannelOptions={maxRetransmitTime:5e3};return sendChannel=localPeer.pc.createDataChannel("myChannel",dataChannelOptions),sendChannel.binaryType="arraybuffer",sendChannel.onerror=function(error){console.log("Data Channel Error:",error)},sendChannel.onmessage=function(event){console.log("Got Data Channel Message:",event.data)},sendChannel.onopen=function(){console.log("The Data Channel is open..."),dataChannel.send("Hello World!")},sendChannel.onclose=function(){console.log("The Data Channel is Closed")},localPeer}function addPeer(remoteId,arg_usertype){var peer=new RemotePeer(config.peerConnectionConfig,config.peerConnectionConstraints,remoteId,arg_usertype);return peer.pc.onicecandidate=function(event){event.candidate&&send("candidate",remoteId,{label:event.candidate.sdpMLineIndex,id:event.candidate.sdpMid,candidate:event.candidate.candidate})},peer.pc.ontrack=function(){attachMediaStream(peer.remoteVideoEl,event.stream),remoteVideosContainer.appendChild(peer.remoteVideosDiv),remoteStreamsDB[peer.remoteVideoEl.id]=event.stream},peer.pc.onremovestream=function(event){try{remoteVideosContainer.hasChildNodes()&&remoteVideosContainer.contains(peer.remoteVideosDiv)&&remoteVideosContainer.removeChild(peer.remoteVideosDiv)}catch(err){console.log(err)}},peer.pc.oniceconnectionstatechange=function(event){switch((event.srcElement||event.target).iceConnectionState){case"disconnected":try{remoteVideosContainer&&remoteVideosContainer.hasChildNodes()&&remoteVideosContainer.contains(peer.remoteVideosDiv)&&remoteVideosContainer.removeChild(peer.remoteVideosDiv)}catch(err){console.log(err)}}},peer.pc.removeStream=function(){peer.pc.getSenders().forEach(function(sender){console.log("<---sender at removeStream--->"),console.log(sender);try{peer.pc.removeTrack(sender),remoteVideosContainer&&remoteVideosContainer.hasChildNodes()&&remoteVideosContainer.contains(peer.remoteVideosDiv)&&remoteVideosContainer.removeChild(peer.remoteVideosDiv)}catch(err){console.log(err)}})},peer.pc.ondatachannel=function(event){trace("Receive channel Callback"),receiveChannel=event.channel,receiveChannel.binaryType="arraybuffer",receiveChannel.onmessage=function(event){console.log(event.data)},receiveChannel.onopen=function(){console.log("The Data Channel is open")},receiveChannel.onclose=function(){console.log("The Data Channel is closed")},receiveChannel.onerror=function(err){console.log("Data Channel Error: ",err)}},peerDatabase[remoteId]=peer,peer}function answer(remoteId){var pc=peerDatabase[remoteId].pc;pc.createAnswer(function(sessionDescription){pc.setLocalDescription(sessionDescription),send("answer",remoteId,sessionDescription)},error,setSdpConstraints())}function offer(remoteId){var pc=peerDatabase[remoteId].pc;pc.createOffer(function(sessionDescription){pc.setLocalDescription(sessionDescription),send("offer",remoteId,sessionDescription)},error,setSdpConstraints())}function addWatcher(remoteId){console.log("<--- addWatcher broadcast info. --->"),console.log(remoteId),socket.emit("addWatcher",{localId:localId,remoteId:remoteId,userType:userType})}function removeWatcherIdFromDB(remoteId){console.log("<--- remove watcher from broadcast doc. --->"),console.log(remoteId),socket.emit("removeWatcher",{localId:localId,remoteId:remoteId,userType:userType})}function setSdpConstraints(){return navigator.mozGetUserMedia?{offerToReceiveAudio:1,offerToReceiveVideo:1}:{optional:[],mandatory:{OfferToReceiveAudio:!0,OfferToReceiveVideo:!0}}}function handleMessage(message){var type=message.type,from=message.from,pc=(peerDatabase[from]||addPeer(from,userType)).pc;switch(console.log("<---handleMessage--->"),console.log("received "+type+" from "+from),console.log(message),console.log("<---end of handleMessage--->"),type){case"init":toggleLocalStream(pc),offer(from),addWatcher(from);break;case"remove":removeStream(pc),removeWatcherIdFromDB(from),offer(from);break;case"offer":message.payload&&pc.setRemoteDescription(new RTCSessionDescription(message.payload)).then(function(){console.log("successfully receive offer")}).catch(error),answer(from);break;case"answer":message.payload&&pc.setRemoteDescription(new RTCSessionDescription(message.payload)).then(function(){console.log("successfully receive offer")}).catch(error);break;case"candidate":pc.remoteDescription&&pc.addIceCandidate(new RTCIceCandidate({sdpMLineIndex:message.payload.label,sdpMid:message.payload.id,candidate:message.payload.candidate})).then(function(){}).catch(error)}}function send(type,to,payload){console.log("<---send--->"),console.log("sending "+type+" to "+to),console.log(payload),console.log("<---end of send--->"),socket.emit("message",{to:to,type:type,payload:payload})}function toggleLocalStream(pc){console.log("<---toggleLocalStream--->"),console.log(pc.getLocalStreams),localStream&&(pc.getLocalStreams().length?pc.removeStream():pc.addStream(localStream))}function removeStream(pc){console.log("<---removeStream--->"),localStream&&pc.getLocalStreams().length&&(console.log(pc.getLocalStreams()),pc.removeStream())}function error(err){console.log(err)}var localId,localStream,userType=arg_user_type,config={peerConnectionConfig:{iceServers:[{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"},{urls:"stun:stun3.l.google.com:19302"},{urls:"stun:stun4.l.google.com:19302"},{urls:"stun:stun.anyfirewall.com:3478"},{urls:"stun:stun.l.google.com:19302"},{urls:"turn:turn.bistri.com:80",credential:"homeo",username:"homeo"},{urls:"turn:turn.anyfirewall.com:443?transport=tcp",credential:"webrtc",username:"webrtc"},{urls:"stun:stun.services.mozilla.com"}]},peerConnectionConstraints:{optional:[{DtlsSrtpKeyAgreement:!0}]}},peerDatabase={},remoteStreamsDB={},remoteVideosContainer=document.getElementById("remoteVideosContainer"),socket=io(),externalMechanisms={},sendChannel=null,receiveChannel=null;return socket.on("message",handleMessage),socket.on("id",function(id){localId=id,console.log("<--- Local ID: ",localId," --->")}),socket.on("streamNotification",function(res){if(console.log("<--- stream notification --->"),console.log(res),externalMechanisms.hasOwnProperty("load_data")){if("stream_off"===res.notification_key){var remote_id=res.client_id_from,peer=peerDatabase[remote_id];try{remoteVideosContainer&&remoteVideosContainer.hasChildNodes()&&remoteVideosContainer.contains(peer.remoteVideosDiv)&&remoteVideosContainer.removeChild(peer.remoteVideosDiv)}catch(err){console.log(err)}}externalMechanisms.load_data(),console.log("streamNotification: update stream list...")}}),socket.on("watcherNotification",function(res){console.log("<--- watcher notification --->"),console.log(res),externalMechanisms.hasOwnProperty("load_watchers")&&(console.log("watcherNotification: update watcher list..."),externalMechanisms.load_watchers())}),{getId:function(){return localId},setLocalStream:function(stream){if(!stream)for(var id in peerDatabase){var pc=peerDatabase[id].pc;pc.getLocalStreams().length&&(pc.removeStream(localStream),offer(id))}localStream=stream},toggleLocalStream:function(remoteId){var peer=peerDatabase[remoteId]||addPeer(remoteId,userType);toggleLocalStream(peer.pc)},localPeerInit:function(){var localPeer=initLocalPeer();return localPeer},peerInit:function(remoteId){var peer=peerDatabase[remoteId]||addPeer(remoteId,userType);return send("init",remoteId,null),peer},peerRenegociate:function(remoteId){offer(remoteId)},send:function(type,payload){socket.emit(type,payload)},removeStream:function(remoteId){peerDatabase[remoteId];send("remove",remoteId,null)},addExternalMechanism:function(arg_mechanism_name,arg_mechanism){externalMechanisms[arg_mechanism_name]=arg_mechanism},getRemoteStreamsDB:function(){return remoteStreamsDB}}},Peer=function(pcConfig,pcConstraints){this.pc=new RTCPeerConnection(pcConfig,pcConstraints)},LocalPeer=function(pcConfig,pcConstraints){Peer.call(this,pcConfig,pcConstraints)},RemotePeer=function(pcConfig,pcConstraints,arg_remote_id,arg_usertype){Peer.call(this,pcConfig,pcConstraints),this.remoteVideoEl=document.createElement("video"),this.remoteVideoEl.controls=!0,this.remoteVideoEl.autoplay=!0,this.remoteVideoEl.muted=!0,this.remoteVideoEl.id=arg_remote_id,this.remoteVideosDiv=document.createElement("div"),this.remoteVideosDiv.className="remoteVideosDiv",this.remoteVideosDiv.id=arg_remote_id,this.remoteVideosDiv.appendChild(document.createElement("hr")),this.remoteVideosDiv.appendChild(this.remoteVideoEl),this.remoteVideosDiv.appendChild(document.createElement("br")),"broadcast"===arg_usertype&&(this.startRecordingBtn=document.createElement("input"),this.stopRecordingBtn=document.createElement("input"),this.startRecordingBtn.setAttribute("type","button"),this.stopRecordingBtn.setAttribute("type","button"),this.startRecordingBtn.className="col-sm-6 col-xs-12 btn btn-default",this.stopRecordingBtn.className="col-sm-6 col-xs-12 btn btn-default",this.startRecordingBtn.setAttribute("value","Start Recording"),this.stopRecordingBtn.setAttribute("value","Stop Recording"),this.stopRecordingBtn.setAttribute("disabled",!0),this.remoteVideosDiv.appendChild(this.startRecordingBtn),this.remoteVideosDiv.appendChild(this.stopRecordingBtn))};
--------------------------------------------------------------------------------
/public/js/broadcast.js:
--------------------------------------------------------------------------------
1 | /* bootstrap front-end MVC architecture with Angular.js */
2 | (function($){
3 | angular.element(document).ready(function(){
4 | // bootstrap App
5 | angular.bootstrap(document.body, ['broadcastApp']);
6 | });
7 |
8 | // init App
9 | initApp();
10 |
11 | function initApp(){
12 | // set module
13 | window.broadcastApp = window.broadcastApp || angular.module('broadcastApp', [], function($locationProvider, $interpolateProvider){
14 | $locationProvider.html5Mode(true); // set html5 mode
15 |
16 | // change interploate to avoid conflicts when template engines use {{...}}
17 | $interpolateProvider.startSymbol('[[');
18 | $interpolateProvider.endSymbol(']]');
19 | });
20 |
21 | // global values
22 | window.broadcastApp.value('APP_VALUES', {
23 | EMAIL: 'gogistics@gogistics-tw.com',
24 | MEDIA_CONFIG: {audio: true,
25 | video: { mandatory: {
26 | minWidth: 1280,
27 | minHeight: 720,
28 | maxWidth: 1280,
29 | maxHeight: 720,
30 | frameRate: { min: 35, ideal: 50, max: 60 }
31 | }
32 | }
33 | },
34 | BINARY_STREAM: null,
35 | FINGERPRINT: null
36 | });
37 |
38 | window.broadcastApp.config(function(){
39 | // routing config
40 | });
41 |
42 | window.broadcastApp.run(function(){
43 | // run
44 | });
45 |
46 | window.broadcastApp.service('dataProvider', function($http, APP_VALUES){
47 | this.getStreams = function(arg_url, arg_headers, arg_data){
48 | return $http({
49 | url: arg_url,
50 | method: 'POST',
51 | data: arg_data,
52 | headers: arg_headers
53 | });
54 | }
55 | });
56 |
57 | window.broadcastApp.factory('client', function(){
58 | return new PeerManager('broadcast');
59 | });
60 |
61 | window.broadcastApp.factory('binaryjsClient', function(){
62 | return new BinaryClient('ws://45.79.106.150:8888');
63 | });
64 |
65 | window.broadcastApp.factory('fingerprintManager', function(){
66 | return new Fingerprint();
67 | });
68 |
69 | window.broadcastApp.factory('camera', ['$window', '$rootScope', 'client', 'APP_VALUES', function($window, $rootScope, client, APP_VALUES){
70 | var camera = {};
71 | camera.preview = $window.document.getElementById('localVideo');
72 | camera.isOn = false;
73 | camera.start = function(){
74 | return requestUserMedia(APP_VALUES.MEDIA_CONFIG)
75 | .then(function(stream){
76 | // onSuccess
77 | attachMediaStream(camera.preview, stream);
78 | client.setLocalStream(stream);
79 | camera.stream = stream;
80 | camera.isOn = true;
81 | $rootScope.$broadcast('cameraIsOn',true);
82 | console.log('OnSuccess...');
83 | }, function(err){
84 | console.log('OnError...');
85 | }).catch(Error('Failed to get access to local media'));
86 | }
87 | camera.stop = function(){
88 | return new Promise(function(resolve, reject){
89 | try{
90 | // MediaStream.stop() is deprecated and will soon be removed. Use MediaStreamTrack.stop() instead.
91 | camera.stream.stop();
92 | camera.preview.src = '';
93 | resolve();
94 | }catch(err){
95 | reject(err);
96 | }
97 | }).then(function(result){
98 | camera.isOn = false;
99 | $rootScope.$broadcast('cameraIsOn',false);
100 | });
101 | };
102 | return camera;
103 | }]);
104 |
105 | window.broadcastApp.controller('broadcastCtrl', ['$scope', '$window', 'APP_VALUES', 'dataProvider', 'client', 'binaryjsClient', 'camera', 'fingerprintManager', function($scope, $window, APP_VALUES, dataProvider, client, binaryjsClient, camera, fingerprintManager){
106 | // fingerprint
107 | APP_VALUES.FINGERPRINT = fingerprintManager.get();
108 |
109 | // init local rtc-peer
110 | // if(!window.localPeer) window.localPeer = client.localPeerInit();
111 | // console.log(window.localPeer);
112 |
113 | // global notification when camera is on
114 | $scope.$on('cameraIsOn', function(event, data){
115 | console.log(data);
116 | $scope.$apply(function(){
117 | ctrl.cameraIsOn = data;
118 | });
119 | });
120 |
121 | // set variables and functions of ctrl
122 | var ctrl = this;
123 | ctrl.link = '';
124 | ctrl.cameraIsOn = false;
125 | ctrl.userType = null;
126 | ctrl.watchers = [];
127 |
128 | ctrl.init = function(arg_user_type){
129 | ctrl.userType = arg_user_type;
130 | }
131 |
132 | ctrl.toggleCam = function(){
133 | if(ctrl.cameraIsOn){
134 | // stop recording if it's on
135 | ctrl.stopRecording();
136 |
137 | // stop camera
138 | camera.stop().then(function(result){
139 | // send notification via socket
140 | client.send('leave', {name: ctrl['name'], user_type: 'broadcast'});
141 | client.setLocalStream(null);
142 | $window.location.reload();
143 | }).catch(function(err){
144 | console.log(err);
145 | });
146 | } else {
147 | console.log('start camera...');
148 | camera.start().then(function(result){
149 | // send notification via socket
150 | ctrl.link = $window.location.host + '/' + client.getId();
151 | ctrl.name = client.getId();
152 | client.send('readyToStream', {name: ctrl['name'], user_type: ctrl['userType']});
153 |
154 | // open binaryjsStream
155 | binaryjsClient.on('open', function(stream) {
156 | console.log(stream);
157 | });
158 | });
159 | }
160 | }
161 |
162 | ctrl.getWatchers = function(){
163 | var customHeaders = { 'current_cookie': $window.document.cookie,
164 | 'Content-Type': 'application/json'};
165 | dataProvider.getStreams('/get-watchers', customHeaders, {id: client.getId()})
166 | .success(function(data, status, headers, config){
167 | console.log(data);
168 | ctrl.watchers = data['servedWatchers'];
169 | })
170 | .error(function(data, status, headers, config){
171 | console.log(data);
172 | });
173 | }
174 | client.addExternalMechanism('load_watchers', ctrl.getWatchers);
175 |
176 | ctrl.isRecording = false;
177 | ctrl.startRecording = function(){
178 | if(!ctrl.isRecording){
179 | ctrl.isRecording = !ctrl.isRecording;
180 |
181 | // for the sake of this example let's put the stream in the window
182 | var from = 'broadcast-' + client.getId();
183 | APP_VALUES.BINARY_STREAM = binaryjsClient.createStream({from: from});
184 |
185 | // receive data
186 | APP_VALUES.BINARY_STREAM.on('data', function(data){
187 | console.log(data);
188 | });
189 |
190 | // rtcRecorder setting
191 | ctrl.startTimestamp = new Date().getTime();
192 | ctrl.rtcRecorder = RecordRTC( camera.stream,
193 | {bufferSize: 16384, type: 'video', frameInterval: 20}, function(arg_data){
194 | // convert stream to unit 16 array to pipe data via binary stream to cloud server
195 | var arrayBuffer, uint16Array;
196 | var fileReader = new FileReader();
197 | fileReader.onload = function() {
198 | arrayBuffer = this.result;
199 | uint16Array = new Uint16Array(arrayBuffer, 0, (arrayBuffer.length - 1));
200 | if( !!APP_VALUES.BINARY_STREAM && !!uint16Array){
201 | APP_VALUES.BINARY_STREAM.write(uint16Array);
202 | console.log(uint16Array);
203 | }else{
204 | console.log(arrayBuffer);
205 | }
206 | };
207 | fileReader.readAsArrayBuffer(arg_data);
208 | });
209 | ctrl.rtcRecorder.startRecording();
210 | }
211 | }
212 |
213 | ctrl.stopRecording = function(){
214 | if(ctrl.isRecording){
215 | ctrl.isRecording = !ctrl.isRecording; // switch btn status
216 | APP_VALUES.BINARY_STREAM.end(); // end writing stream
217 |
218 | ctrl.stopTimestamp = new Date().getTime();
219 | var fileName = ctrl.name + '-' + ctrl.startTimestamp + '_' + ctrl.stopTimestamp; // set temporary file name
220 | ctrl.rtcRecorder.stopRecording();
221 | ctrl.rtcRecorder.save(fileName); // optional
222 | };
223 | }
224 | }]);
225 | }
226 | })(jQuery);
227 |
--------------------------------------------------------------------------------
/public/js/json3/README.md:
--------------------------------------------------------------------------------
1 | # JSON 3 #
2 |
3 | 
4 |
5 | [](http://travis-ci.org/bestiejs/json3)
6 |
7 | **JSON 3** is a modern JSON implementation compatible with a variety of JavaScript platforms, including Internet Explorer 6, Opera 7, Safari 2, and Netscape 6. The current version is **3.3.2**.
8 |
9 | - [Development Version](http://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.js) *(43 KB; uncompressed with comments)*
10 | - [Production Version](http://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js) *(3.5 KB; compressed and `gzip`-ped)*
11 |
12 | Special thanks to [cdnjs](http://cdnjs.com/libraries/json3/) and [jsDelivr](http://www.jsdelivr.com/#!json3) for hosting CDN copies of JSON 3.
13 |
14 | [JSON](http://json.org/) is a language-independent data interchange format based on a loose subset of the JavaScript grammar. Originally popularized by [Douglas Crockford](http://www.crockford.com/), the format was standardized in the [fifth edition](http://es5.github.com/) of the ECMAScript specification. The 5.1 edition, ratified in June 2011, incorporates several modifications to the grammar pertaining to the serialization of dates.
15 |
16 | JSON 3 exposes two functions: `stringify()` for [serializing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify) a JavaScript value to JSON, and `parse()` for [producing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/parse) a JavaScript value from a JSON source string. It is a **drop-in replacement** for [JSON 2](http://json.org/js). The functions behave exactly as described in the ECMAScript spec, **except** for the date serialization discrepancy noted below.
17 |
18 | The JSON 3 parser does **not** use `eval` or regular expressions. This provides security and performance benefits in obsolete and mobile environments, where the margin is particularly significant. The complete [benchmark suite](http://jsperf.com/json3) is available on [jsPerf](http://jsperf.com/).
19 |
20 | The project is [hosted on GitHub](http://git.io/json3), along with the [unit tests](http://bestiejs.github.io/json3/test/test_browser.html). It is part of the [BestieJS](https://github.com/bestiejs) family, a collection of best-in-class JavaScript libraries that promote cross-platform support, specification precedents, unit testing, and plenty of documentation.
21 |
22 | # Changes from JSON 2 #
23 |
24 | JSON 3...
25 |
26 | * Correctly serializes primitive wrapper objects.
27 | * Throws a `TypeError` when serializing cyclic structures (JSON 2 recurses until the call stack overflows).
28 | * Utilizes **feature tests** to detect broken or incomplete *native* JSON implementations (JSON 2 only checks for the presence of the native functions). The tests are only executed once at runtime, so there is no additional performance cost when parsing or serializing values.
29 |
30 | **As of v3.2.3**, JSON 3 is compatible with [Prototype](http://prototypejs.org) 1.6.1 and older.
31 |
32 | In contrast to JSON 2, JSON 3 **does not**...
33 |
34 | * Add `toJSON()` methods to the `Boolean`, `Number`, and `String` prototypes. These are not part of any standard, and are made redundant by the design of the `stringify()` implementation.
35 | * Add `toJSON()` or `toISOString()` methods to `Date.prototype`. See the note about date serialization below.
36 |
37 | ## Date Serialization
38 |
39 | **JSON 3 deviates from the specification in one important way**: it does not define `Date#toISOString()` or `Date#toJSON()`. This preserves CommonJS compatibility and avoids polluting native prototypes. Instead, date serialization is performed internally by the `stringify()` implementation: if a date object does not define a custom `toJSON()` method, it is serialized as a [simplified ISO 8601 date-time string](http://es5.github.com/#x15.9.1.15).
40 |
41 | **Several native `Date#toJSON()` implementations produce date time strings that do *not* conform to the grammar outlined in the spec**. For instance, all versions of Safari 4, as well as JSON 2, fail to serialize extended years correctly. Furthermore, JSON 2 and older implementations omit the milliseconds from the date-time string (optional in ES 5, but required in 5.1). Finally, in all versions of Safari 4 and 5, serializing an invalid date will produce the string `"Invalid Date"`, rather than `null`. Because these environments exhibit other serialization bugs, however, JSON 3 will override the native `stringify()` implementation.
42 |
43 | Portions of the date serialization code are adapted from the [`date-shim`](https://github.com/Yaffle/date-shim) project.
44 |
45 | # Usage #
46 |
47 | ## Web Browsers
48 |
49 |
50 |
61 |
62 | **When used in a web browser**, JSON 3 exposes an additional `JSON3` object containing the `noConflict()` and `runInContext()` functions, as well as aliases to the `stringify()` and `parse()` functions.
63 |
64 | ### `noConflict` and `runInContext`
65 |
66 | * `JSON3.noConflict()` restores the original value of the global `JSON` object and returns a reference to the `JSON3` object.
67 | * `JSON3.runInContext([context, exports])` initializes JSON 3 using the given `context` object (e.g., `window`, `global`, etc.), or the global object if omitted. If an `exports` object is specified, the `stringify()`, `parse()`, and `runInContext()` functions will be attached to it instead of a new object.
68 |
69 | ### Asynchronous Module Loaders
70 |
71 | JSON 3 is defined as an [anonymous module](https://github.com/amdjs/amdjs-api/wiki/AMD#define-function-) for compatibility with [RequireJS](http://requirejs.org/), [`curl.js`](https://github.com/cujojs/curl), and other asynchronous module loaders.
72 |
73 |
74 |
84 |
85 | To avoid issues with third-party scripts, **JSON 3 is exported to the global scope even when used with a module loader**. If this behavior is undesired, `JSON3.noConflict()` can be used to restore the global `JSON` object to its original value.
86 |
87 | ## CommonJS Environments
88 |
89 | var JSON3 = require("./path/to/json3");
90 | JSON3.parse("[1, 2, 3]");
91 | // => [1, 2, 3]
92 |
93 | ## JavaScript Engines
94 |
95 | load("path/to/json3.js");
96 | JSON.stringify({"Hello": 123, "Good-bye": 456}, ["Hello"], "\t");
97 | // => '{\n\t"Hello": 123\n}'
98 |
99 | # Compatibility #
100 |
101 | JSON 3 has been **tested** with the following web browsers, CommonJS environments, and JavaScript engines.
102 |
103 | ## Web Browsers
104 |
105 | - Windows [Internet Explorer](http://www.microsoft.com/windows/internet-explorer), version 6.0 and higher
106 | - Mozilla [Firefox](http://www.mozilla.com/firefox), version 1.0 and higher
107 | - Apple [Safari](http://www.apple.com/safari), version 2.0 and higher
108 | - [Opera](http://www.opera.com) 7.02 and higher
109 | - [Mozilla](http://sillydog.org/narchive/gecko.php) 1.0, [Netscape](http://sillydog.org/narchive/) 6.2.3, and [SeaMonkey](http://www.seamonkey-project.org/) 1.0 and higher
110 |
111 | ## CommonJS Environments
112 |
113 | - [Node](http://nodejs.org/) 0.2.6 and higher
114 | - [RingoJS](http://ringojs.org/) 0.4 and higher
115 | - [Narwhal](http://narwhaljs.org/) 0.3.2 and higher
116 |
117 | ## JavaScript Engines
118 |
119 | - Mozilla [Rhino](http://www.mozilla.org/rhino) 1.5R5 and higher
120 | - WebKit [JSC](https://trac.webkit.org/wiki/JSC)
121 | - Google [V8](http://code.google.com/p/v8)
122 |
123 | ## Known Incompatibilities
124 |
125 | * Attempting to serialize the `arguments` object may produce inconsistent results across environments due to specification version differences. As a workaround, please convert the `arguments` object to an array first: `JSON.stringify([].slice.call(arguments, 0))`.
126 |
127 | ## Required Native Methods
128 |
129 | JSON 3 assumes that the following methods exist and function as described in the ECMAScript specification:
130 |
131 | - The `Number`, `String`, `Array`, `Object`, `Date`, `SyntaxError`, and `TypeError` constructors.
132 | - `String.fromCharCode`
133 | - `Object#toString`
134 | - `Function#call`
135 | - `Math.floor`
136 | - `Number#toString`
137 | - `Date#valueOf`
138 | - `String.prototype`: `indexOf`, `charCodeAt`, `charAt`, `slice`.
139 | - `Array.prototype`: `push`, `pop`, `join`.
140 |
141 | # Contribute #
142 |
143 | Check out a working copy of the JSON 3 source code with [Git](http://git-scm.com/):
144 |
145 | $ git clone git://github.com/bestiejs/json3.git
146 | $ cd json3
147 |
148 | If you'd like to contribute a feature or bug fix, you can [fork](http://help.github.com/fork-a-repo/) JSON 3, commit your changes, and [send a pull request](http://help.github.com/send-pull-requests/). Please make sure to update the unit tests in the `test` directory as well.
149 |
150 | Alternatively, you can use the [GitHub issue tracker](https://github.com/bestiejs/json3/issues) to submit bug reports, feature requests, and questions, or send tweets to [@kitcambridge](http://twitter.com/kitcambridge).
151 |
152 | JSON 3 is released under the [MIT License](http://kit.mit-license.org/).
153 |
--------------------------------------------------------------------------------
/public/js/fingerprint.js:
--------------------------------------------------------------------------------
1 | /*
2 | * fingerprintJS 0.5.3 - Fast browser fingerprint library
3 | * https://github.com/Valve/fingerprintjs
4 | * Copyright (c) 2013 Valentin Vasilyev (valentin.vasilyev@outlook.com)
5 | * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6 | */
7 |
8 | ;(function (name, context, definition) {
9 | if (typeof module !== 'undefined' && module.exports) { module.exports = definition(); }
10 | else if (typeof define === 'function' && define.amd) { define(definition); }
11 | else { context[name] = definition(); }
12 | })('Fingerprint', this, function () {
13 | 'use strict';
14 |
15 | var Fingerprint = function (options) {
16 | var nativeForEach, nativeMap;
17 | nativeForEach = Array.prototype.forEach;
18 | nativeMap = Array.prototype.map;
19 |
20 | this.each = function (obj, iterator, context) {
21 | if (obj === null) {
22 | return;
23 | }
24 | if (nativeForEach && obj.forEach === nativeForEach) {
25 | obj.forEach(iterator, context);
26 | } else if (obj.length === +obj.length) {
27 | for (var i = 0, l = obj.length; i < l; i++) {
28 | if (iterator.call(context, obj[i], i, obj) === {}) return;
29 | }
30 | } else {
31 | for (var key in obj) {
32 | if (obj.hasOwnProperty(key)) {
33 | if (iterator.call(context, obj[key], key, obj) === {}) return;
34 | }
35 | }
36 | }
37 | };
38 |
39 | this.map = function(obj, iterator, context) {
40 | var results = [];
41 | // Not using strict equality so that this acts as a
42 | // shortcut to checking for `null` and `undefined`.
43 | if (obj == null) return results;
44 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
45 | this.each(obj, function(value, index, list) {
46 | results[results.length] = iterator.call(context, value, index, list);
47 | });
48 | return results;
49 | };
50 |
51 | if (typeof options == 'object'){
52 | this.hasher = options.hasher;
53 | this.screen_resolution = options.screen_resolution;
54 | this.canvas = options.canvas;
55 | this.ie_activex = options.ie_activex;
56 | } else if(typeof options == 'function'){
57 | this.hasher = options;
58 | }
59 | };
60 |
61 | Fingerprint.prototype = {
62 | get: function(){
63 | var keys = [];
64 | keys.push(navigator.userAgent);
65 | keys.push(navigator.language);
66 | keys.push(screen.colorDepth);
67 | if (this.screen_resolution) {
68 | var resolution = this.getScreenResolution();
69 | if (typeof resolution !== 'undefined'){ // headless browsers, such as phantomjs
70 | keys.push(this.getScreenResolution().join('x'));
71 | }
72 | }
73 | keys.push(new Date().getTimezoneOffset());
74 | keys.push(this.hasSessionStorage());
75 | keys.push(this.hasLocalStorage());
76 | keys.push(!!window.indexedDB);
77 | //body might not be defined at this point or removed programmatically
78 | if(document.body){
79 | keys.push(typeof(document.body.addBehavior));
80 | } else {
81 | keys.push(typeof undefined);
82 | }
83 | keys.push(typeof(window.openDatabase));
84 | keys.push(navigator.cpuClass);
85 | keys.push(navigator.platform);
86 | keys.push(navigator.doNotTrack);
87 | keys.push(this.getPluginsString());
88 | if(this.canvas && this.isCanvasSupported()){
89 | keys.push(this.getCanvasFingerprint());
90 | }
91 | if(this.hasher){
92 | return this.hasher(keys.join('###'), 31);
93 | } else {
94 | return this.murmurhash3_32_gc(keys.join('###'), 31);
95 | }
96 | },
97 |
98 | /**
99 | * JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
100 | *
101 | * @author Gary Court
102 | * @see http://github.com/garycourt/murmurhash-js
103 | * @author Austin Appleby
104 | * @see http://sites.google.com/site/murmurhash/
105 | *
106 | * @param {string} key ASCII only
107 | * @param {number} seed Positive integer only
108 | * @return {number} 32-bit positive integer hash
109 | */
110 |
111 | murmurhash3_32_gc: function(key, seed) {
112 | var remainder, bytes, h1, h1b, c1, c2, k1, i;
113 |
114 | remainder = key.length & 3; // key.length % 4
115 | bytes = key.length - remainder;
116 | h1 = seed;
117 | c1 = 0xcc9e2d51;
118 | c2 = 0x1b873593;
119 | i = 0;
120 |
121 | while (i < bytes) {
122 | k1 =
123 | ((key.charCodeAt(i) & 0xff)) |
124 | ((key.charCodeAt(++i) & 0xff) << 8) |
125 | ((key.charCodeAt(++i) & 0xff) << 16) |
126 | ((key.charCodeAt(++i) & 0xff) << 24);
127 | ++i;
128 |
129 | k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
130 | k1 = (k1 << 15) | (k1 >>> 17);
131 | k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
132 |
133 | h1 ^= k1;
134 | h1 = (h1 << 13) | (h1 >>> 19);
135 | h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
136 | h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
137 | }
138 |
139 | k1 = 0;
140 |
141 | switch (remainder) {
142 | case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
143 | case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
144 | case 1: k1 ^= (key.charCodeAt(i) & 0xff);
145 |
146 | k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
147 | k1 = (k1 << 15) | (k1 >>> 17);
148 | k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
149 | h1 ^= k1;
150 | }
151 |
152 | h1 ^= key.length;
153 |
154 | h1 ^= h1 >>> 16;
155 | h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
156 | h1 ^= h1 >>> 13;
157 | h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
158 | h1 ^= h1 >>> 16;
159 |
160 | return h1 >>> 0;
161 | },
162 |
163 | // https://bugzilla.mozilla.org/show_bug.cgi?id=781447
164 | hasLocalStorage: function () {
165 | try{
166 | return !!window.localStorage;
167 | } catch(e) {
168 | return true; // SecurityError when referencing it means it exists
169 | }
170 | },
171 |
172 | hasSessionStorage: function () {
173 | try{
174 | return !!window.sessionStorage;
175 | } catch(e) {
176 | return true; // SecurityError when referencing it means it exists
177 | }
178 | },
179 |
180 | isCanvasSupported: function () {
181 | var elem = document.createElement('canvas');
182 | return !!(elem.getContext && elem.getContext('2d'));
183 | },
184 |
185 | isIE: function () {
186 | if(navigator.appName === 'Microsoft Internet Explorer') {
187 | return true;
188 | } else if(navigator.appName === 'Netscape' && /Trident/.test(navigator.userAgent)){// IE 11
189 | return true;
190 | }
191 | return false;
192 | },
193 |
194 | getPluginsString: function () {
195 | if(this.isIE() && this.ie_activex){
196 | return this.getIEPluginsString();
197 | } else {
198 | return this.getRegularPluginsString();
199 | }
200 | },
201 |
202 | getRegularPluginsString: function () {
203 | return this.map(navigator.plugins, function (p) {
204 | var mimeTypes = this.map(p, function(mt){
205 | return [mt.type, mt.suffixes].join('~');
206 | }).join(',');
207 | return [p.name, p.description, mimeTypes].join('::');
208 | }, this).join(';');
209 | },
210 |
211 | getIEPluginsString: function () {
212 | if(window.ActiveXObject){
213 | var names = ['ShockwaveFlash.ShockwaveFlash',//flash plugin
214 | 'AcroPDF.PDF', // Adobe PDF reader 7+
215 | 'PDF.PdfCtrl', // Adobe PDF reader 6 and earlier, brrr
216 | 'QuickTime.QuickTime', // QuickTime
217 | // 5 versions of real players
218 | 'rmocx.RealPlayer G2 Control',
219 | 'rmocx.RealPlayer G2 Control.1',
220 | 'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)',
221 | 'RealVideo.RealVideo(tm) ActiveX Control (32-bit)',
222 | 'RealPlayer',
223 | 'SWCtl.SWCtl', // ShockWave player
224 | 'WMPlayer.OCX', // Windows media player
225 | 'AgControl.AgControl', // Silverlight
226 | 'Skype.Detection'];
227 |
228 | // starting to detect plugins in IE
229 | return this.map(names, function(name){
230 | try{
231 | new ActiveXObject(name);
232 | return name;
233 | } catch(e){
234 | return null;
235 | }
236 | }).join(';');
237 | } else {
238 | return ""; // behavior prior version 0.5.0, not breaking backwards compat.
239 | }
240 | },
241 |
242 | getScreenResolution: function () {
243 | return [screen.height, screen.width];
244 | },
245 |
246 | getCanvasFingerprint: function () {
247 | var canvas = document.createElement('canvas');
248 | var ctx = canvas.getContext('2d');
249 | // https://www.browserleaks.com/canvas#how-does-it-work
250 | var txt = 'http://valve.github.io';
251 | ctx.textBaseline = "top";
252 | ctx.font = "14px 'Arial'";
253 | ctx.textBaseline = "alphabetic";
254 | ctx.fillStyle = "#f60";
255 | ctx.fillRect(125,1,62,20);
256 | ctx.fillStyle = "#069";
257 | ctx.fillText(txt, 2, 15);
258 | ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
259 | ctx.fillText(txt, 4, 17);
260 | return canvas.toDataURL();
261 | }
262 | };
263 |
264 |
265 | return Fingerprint;
266 |
267 | });
268 |
--------------------------------------------------------------------------------
/public/js/adapter.min.js:
--------------------------------------------------------------------------------
1 | /*! webrtc 20-10-2016 */function trace(text){"\n"==text[text.length-1]&&(text=text.substring(0,text.length-1)),console.log((performance.now()/1e3).toFixed(3)+": "+text)}function maybeFixConfiguration(pcConfig){if(pcConfig)for(var i=0;i=34)iceServers={urls:urls,credential:password,username:username};else for(i=0;i0&&pluginObj.videoHeight>0)if("undefined"!=typeof pluginObj.getScreenShot){var bmpBase64=pluginObj.getScreenShot();if(bmpBase64){var image=new Image;image.onload=function(){context.drawImage(image,0,0,width,height)},image.src="data:image/png;base64,"+bmpBase64}}else{var imageData=context.createImageData(pluginObj.videoWidth,pluginObj.videoHeight);imageData&&(pluginObj.fillImageData(imageData),context.putImageData(imageData,x,y))}},MediaStreamTrack={};var getSourcesDelayed;MediaStreamTrack.getSources=function(gotSources){"complete"!==document.readyState?(console.log("readyState = "+document.readyState+", delaying getSources..."),getSourcesDelayed||(getSourcesDelayed=!0,attachEventListener(document,"readystatechange",function(){getSourcesDelayed&&"complete"==document.readyState&&(getSourcesDelayed=!1,getPlugin().getSources(gotSources))}))):getPlugin().getSources(gotSources)},RTCPeerConnection=function(configuration,constraints){return getPlugin().createPeerConnection(configuration,constraints)},RTCIceCandidate=function(RTCIceCandidateInit){return getPlugin().createIceCandidate(RTCIceCandidateInit)},RTCSessionDescription=function(RTCSessionDescriptionInit){return getPlugin().createSessionDescription(RTCSessionDescriptionInit)}}
--------------------------------------------------------------------------------
/public/jquery-ui/jquery-ui.theme.min.css:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.12.0 - 2016-07-08
2 | * http://jqueryui.com
3 | * Copyright jQuery Foundation and other contributors; Licensed MIT */
4 |
5 | .ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon,.ui-state-default .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.003;filter:Alpha(Opacity=.3)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666}
--------------------------------------------------------------------------------