From c4c49284199ad2c5159f585922d08fb025631b6e Mon Sep 17 00:00:00 2001 From: Antonio Laguna Date: Thu, 24 Jan 2013 14:18:04 +0100 Subject: [PATCH] Update to version currently in production --- .idea/.name | 1 + .idea/Asterisk-nodejs-panel.iml | 12 + .idea/encodings.xml | 5 + .idea/libraries/sass_stdlib.xml | 8 + .idea/misc.xml | 351 +++ .idea/modules.xml | 9 + .idea/scopes/scope_settings.xml | 5 + .idea/vcs.xml | 7 + .idea/workspace.xml | 306 ++ README | 8 - agents.sql | 23 - app.js | 279 -- helpers/array.js | 45 +- helpers/executor.js | 29 + helpers/math.js | 11 + helpers/time.js | 77 +- index.html | 295 +- libraries/mysql.js | 58 + models/agent.js | 423 ++- models/client.js | 335 ++ models/queue.js | 179 ++ models/queues.js | 112 - modules/app.js | 429 +++ modules/browser_ban.js | 23 + modules/pbx_bugs_solver.js | 33 + modules/refetcher.js | 19 + modules/reseter.js | 21 + node_modules/socket.io/.gitignore | 10 - node_modules/socket.io/.npmignore | 3 - node_modules/socket.io/History.md | 187 -- node_modules/socket.io/Makefile | 31 - node_modules/socket.io/Readme.md | 343 -- .../socket.io/benchmarks/decode.bench.js | 64 - .../socket.io/benchmarks/encode.bench.js | 90 - node_modules/socket.io/benchmarks/runner.js | 55 - node_modules/socket.io/examples/chat/app.js | 80 - .../socket.io/examples/chat/index.jade | 83 - .../socket.io/examples/chat/package.json | 11 - .../chat/public/stylesheets/mixins.styl | 96 - .../chat/public/stylesheets/style.css | 188 -- .../chat/public/stylesheets/style.styl | 118 - .../socket.io/examples/irc-output/app.js | 74 - .../socket.io/examples/irc-output/index.jade | 28 - .../socket.io/examples/irc-output/irc.js | 164 - .../examples/irc-output/package.json | 10 - .../irc-output/public/stylesheets/style.styl | 69 - node_modules/socket.io/index.js | 8 - node_modules/socket.io/lib/logger.js | 97 - node_modules/socket.io/lib/manager.js | 961 ------ node_modules/socket.io/lib/namespace.js | 355 --- node_modules/socket.io/lib/parser.js | 249 -- node_modules/socket.io/lib/socket.io.js | 136 - node_modules/socket.io/lib/socket.js | 362 --- node_modules/socket.io/lib/static.js | 395 --- node_modules/socket.io/lib/store.js | 98 - node_modules/socket.io/lib/stores/memory.js | 143 - node_modules/socket.io/lib/stores/redis.js | 269 -- node_modules/socket.io/lib/transport.js | 534 ---- .../socket.io/lib/transports/flashsocket.js | 106 - .../socket.io/lib/transports/htmlfile.js | 82 - .../socket.io/lib/transports/http-polling.js | 135 - node_modules/socket.io/lib/transports/http.js | 111 - .../socket.io/lib/transports/index.js | 12 - .../socket.io/lib/transports/jsonp-polling.js | 96 - .../socket.io/lib/transports/websocket.js | 36 - .../lib/transports/websocket/default.js | 358 --- .../lib/transports/websocket/hybi-07-12.js | 617 ---- .../lib/transports/websocket/hybi-16.js | 617 ---- .../lib/transports/websocket/hybi-17.js | 604 ---- .../lib/transports/websocket/index.js | 11 - .../socket.io/lib/transports/xhr-polling.js | 72 - node_modules/socket.io/lib/util.js | 50 - node_modules/socket.io/package.json | 33 - .../support/node-websocket-client/LICENSE | 27 - .../support/node-websocket-client/Makefile | 22 - .../support/node-websocket-client/README.md | 41 - .../examples/client-unix.js | 12 - .../node-websocket-client/examples/client.js | 10 - .../examples/server-unix.js | 13 - .../node-websocket-client/lib/websocket.js | 617 ---- .../node-websocket-client/package.json | 22 - .../node-websocket-client/test/test-basic.js | 68 - .../test/test-client-close.js | 43 - .../test/test-readonly-attrs.js | 43 - .../test/test-ready-state.js | 26 - .../test/test-server-close.js | 41 - .../test/test-unix-send-fd.js | 63 - .../test/test-unix-sockets.js | 46 - node_modules/socket.io/test/common.js | 274 -- node_modules/socket.io/test/fixtures/cert.crt | 21 - node_modules/socket.io/test/fixtures/key.key | 27 - node_modules/socket.io/test/hybi-common.js | 99 - node_modules/socket.io/test/io.test.js | 125 - .../socket.io/test/leaks/socket.leaktest.js | 54 - node_modules/socket.io/test/manager.test.js | 572 ---- node_modules/socket.io/test/namespace.test.js | 286 -- node_modules/socket.io/test/parser.test.js | 356 --- node_modules/socket.io/test/static.test.js | 549 ---- .../socket.io/test/stores.memory.test.js | 190 -- .../socket.io/test/stores.redis.test.js | 240 -- .../test/transports.flashsocket.test.js | 187 -- .../test/transports.htmlfile.test.js | 458 --- .../test/transports.jsonp-polling.test.js | 774 ----- ...nsports.websocket.hybi07-12.parser.test.js | 262 -- ...transports.websocket.hybi16.parser.test.js | 262 -- .../test/transports.websocket.test.js | 1811 ----------- .../test/transports.xhr-polling.test.js | 2759 ----------------- package.json | 27 + public/images/arrow_posts_left.png | Bin 0 -> 722 bytes public/images/arrow_posts_right.png | Bin 0 -> 725 bytes public/images/asc.gif | Bin 0 -> 54 bytes public/images/asc_light.gif | Bin 0 -> 54 bytes public/images/bg_time_head.gif | Bin 0 -> 43 bytes public/images/desc.gif | Bin 0 -> 54 bytes public/images/desc_light.gif | Bin 0 -> 54 bytes public/images/dots_devider_v.gif | Bin 0 -> 43 bytes public/images/glyphicons-halflings-white.png | Bin 0 -> 4352 bytes public/images/glyphicons-halflings.png | Bin 0 -> 4352 bytes public/images/misc/Thumbs.db | Bin 0 -> 11264 bytes public/images/misc/accenture-logo.png | Bin 0 -> 3486 bytes public/images/misc/button-gloss.png | Bin 0 -> 74 bytes public/images/misc/button-overlay.png | Bin 0 -> 104 bytes public/images/misc/carbon_fibre_v2.png | Bin 0 -> 635 bytes public/images/misc/custom-form-sprites.png | Bin 0 -> 620 bytes public/images/misc/input-bg.png | Bin 0 -> 2993 bytes public/images/misc/modal-gloss.png | Bin 0 -> 2843 bytes public/images/misc/table-sorter.png | Bin 0 -> 447 bytes public/images/orbit/bullets.jpg | Bin 0 -> 657 bytes public/images/orbit/left-arrow.png | Bin 0 -> 522 bytes public/images/orbit/loading.gif | Bin 0 -> 2608 bytes public/images/orbit/mask-black.png | Bin 0 -> 526 bytes public/images/orbit/pause-black.png | Bin 0 -> 288 bytes public/images/orbit/right-arrow.png | Bin 0 -> 519 bytes public/images/orbit/rotator-black.png | Bin 0 -> 536 bytes public/images/orbit/timer-black.png | Bin 0 -> 526 bytes public/images/trashcan.png | Bin 0 -> 3277 bytes public/images/widget_grad.png | Bin 0 -> 1638 bytes public/javascripts/client_stat.js | 436 +++ public/javascripts/foundation.js | 13 + public/javascripts/handlebars.js | 1550 +++++++++ public/javascripts/highcharts.js | 244 ++ public/javascripts/jquery-1.8.0.min.js | 2 + public/javascripts/jquery-1.8.1.min.js | 2 + .../jquery-ui-1.8.17.custom.min.js | 31 + .../jquery-ui-1.8.23.custom.min.js | 38 + .../javascripts}/jquery.stopwatch.js | 0 public/javascripts/jquery.tipTip.minified.js | 21 + public/javascripts/script.js | 590 ++++ public/stylesheets/PIE.htc | 96 + public/stylesheets/app.css | 571 ++++ public/stylesheets/foundation.css | 1205 +++++++ public/stylesheets/ie.css | 6 + .../images/ui-bg_flat_30_cccccc_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_50_5c5c5c_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_glass_20_555555_1x400.png | Bin 0 -> 115 bytes .../images/ui-bg_glass_40_0078a3_1x400.png | Bin 0 -> 135 bytes .../images/ui-bg_glass_40_ffc73d_1x400.png | Bin 0 -> 167 bytes .../ui-bg_gloss-wave_25_333333_500x100.png | Bin 0 -> 1874 bytes .../ui-bg_highlight-soft_80_eeeeee_1x100.png | Bin 0 -> 95 bytes .../ui-bg_inset-soft_25_000000_1x100.png | Bin 0 -> 98 bytes .../ui-bg_inset-soft_30_f58400_1x100.png | Bin 0 -> 117 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_4b8e0b_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_a83300_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cccccc_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4369 bytes .../stylesheets/jquery-ui-1.8.23.custom.css | 352 +++ public/stylesheets/normalize.css | 375 +++ public/stylesheets/style.css | 334 ++ public/stylesheets/tipTip.css | 113 + readme.md | 16 + routes/index.js | 522 ++++ server.js | 300 ++ treehouse.txt | 2 - views/index.html | 112 + views/index.jade | 116 + views/layout.jade | 16 + 177 files changed, 9529 insertions(+), 19315 deletions(-) create mode 100644 .idea/.name create mode 100644 .idea/Asterisk-nodejs-panel.iml create mode 100644 .idea/encodings.xml create mode 100644 .idea/libraries/sass_stdlib.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml delete mode 100644 README delete mode 100644 agents.sql delete mode 100644 app.js create mode 100644 helpers/executor.js create mode 100644 helpers/math.js create mode 100644 libraries/mysql.js create mode 100644 models/client.js create mode 100644 models/queue.js delete mode 100644 models/queues.js create mode 100644 modules/app.js create mode 100644 modules/browser_ban.js create mode 100644 modules/pbx_bugs_solver.js create mode 100644 modules/refetcher.js create mode 100644 modules/reseter.js delete mode 100644 node_modules/socket.io/.gitignore delete mode 100644 node_modules/socket.io/.npmignore delete mode 100644 node_modules/socket.io/History.md delete mode 100644 node_modules/socket.io/Makefile delete mode 100644 node_modules/socket.io/Readme.md delete mode 100644 node_modules/socket.io/benchmarks/decode.bench.js delete mode 100644 node_modules/socket.io/benchmarks/encode.bench.js delete mode 100644 node_modules/socket.io/benchmarks/runner.js delete mode 100644 node_modules/socket.io/examples/chat/app.js delete mode 100644 node_modules/socket.io/examples/chat/index.jade delete mode 100644 node_modules/socket.io/examples/chat/package.json delete mode 100644 node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl delete mode 100644 node_modules/socket.io/examples/chat/public/stylesheets/style.css delete mode 100644 node_modules/socket.io/examples/chat/public/stylesheets/style.styl delete mode 100644 node_modules/socket.io/examples/irc-output/app.js delete mode 100644 node_modules/socket.io/examples/irc-output/index.jade delete mode 100644 node_modules/socket.io/examples/irc-output/irc.js delete mode 100644 node_modules/socket.io/examples/irc-output/package.json delete mode 100644 node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl delete mode 100644 node_modules/socket.io/index.js delete mode 100644 node_modules/socket.io/lib/logger.js delete mode 100644 node_modules/socket.io/lib/manager.js delete mode 100644 node_modules/socket.io/lib/namespace.js delete mode 100644 node_modules/socket.io/lib/parser.js delete mode 100644 node_modules/socket.io/lib/socket.io.js delete mode 100644 node_modules/socket.io/lib/socket.js delete mode 100644 node_modules/socket.io/lib/static.js delete mode 100644 node_modules/socket.io/lib/store.js delete mode 100644 node_modules/socket.io/lib/stores/memory.js delete mode 100644 node_modules/socket.io/lib/stores/redis.js delete mode 100644 node_modules/socket.io/lib/transport.js delete mode 100644 node_modules/socket.io/lib/transports/flashsocket.js delete mode 100644 node_modules/socket.io/lib/transports/htmlfile.js delete mode 100644 node_modules/socket.io/lib/transports/http-polling.js delete mode 100644 node_modules/socket.io/lib/transports/http.js delete mode 100644 node_modules/socket.io/lib/transports/index.js delete mode 100644 node_modules/socket.io/lib/transports/jsonp-polling.js delete mode 100644 node_modules/socket.io/lib/transports/websocket.js delete mode 100644 node_modules/socket.io/lib/transports/websocket/default.js delete mode 100644 node_modules/socket.io/lib/transports/websocket/hybi-07-12.js delete mode 100644 node_modules/socket.io/lib/transports/websocket/hybi-16.js delete mode 100644 node_modules/socket.io/lib/transports/websocket/hybi-17.js delete mode 100644 node_modules/socket.io/lib/transports/websocket/index.js delete mode 100644 node_modules/socket.io/lib/transports/xhr-polling.js delete mode 100644 node_modules/socket.io/lib/util.js delete mode 100644 node_modules/socket.io/package.json delete mode 100644 node_modules/socket.io/support/node-websocket-client/LICENSE delete mode 100644 node_modules/socket.io/support/node-websocket-client/Makefile delete mode 100644 node_modules/socket.io/support/node-websocket-client/README.md delete mode 100644 node_modules/socket.io/support/node-websocket-client/examples/client-unix.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/examples/client.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/examples/server-unix.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/lib/websocket.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/package.json delete mode 100644 node_modules/socket.io/support/node-websocket-client/test/test-basic.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/test/test-client-close.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/test/test-server-close.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js delete mode 100644 node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js delete mode 100644 node_modules/socket.io/test/common.js delete mode 100644 node_modules/socket.io/test/fixtures/cert.crt delete mode 100644 node_modules/socket.io/test/fixtures/key.key delete mode 100644 node_modules/socket.io/test/hybi-common.js delete mode 100644 node_modules/socket.io/test/io.test.js delete mode 100644 node_modules/socket.io/test/leaks/socket.leaktest.js delete mode 100644 node_modules/socket.io/test/manager.test.js delete mode 100644 node_modules/socket.io/test/namespace.test.js delete mode 100644 node_modules/socket.io/test/parser.test.js delete mode 100644 node_modules/socket.io/test/static.test.js delete mode 100644 node_modules/socket.io/test/stores.memory.test.js delete mode 100644 node_modules/socket.io/test/stores.redis.test.js delete mode 100644 node_modules/socket.io/test/transports.flashsocket.test.js delete mode 100644 node_modules/socket.io/test/transports.htmlfile.test.js delete mode 100644 node_modules/socket.io/test/transports.jsonp-polling.test.js delete mode 100644 node_modules/socket.io/test/transports.websocket.hybi07-12.parser.test.js delete mode 100644 node_modules/socket.io/test/transports.websocket.hybi16.parser.test.js delete mode 100644 node_modules/socket.io/test/transports.websocket.test.js delete mode 100644 node_modules/socket.io/test/transports.xhr-polling.test.js create mode 100644 package.json create mode 100644 public/images/arrow_posts_left.png create mode 100644 public/images/arrow_posts_right.png create mode 100644 public/images/asc.gif create mode 100644 public/images/asc_light.gif create mode 100644 public/images/bg_time_head.gif create mode 100644 public/images/desc.gif create mode 100644 public/images/desc_light.gif create mode 100644 public/images/dots_devider_v.gif create mode 100644 public/images/glyphicons-halflings-white.png create mode 100644 public/images/glyphicons-halflings.png create mode 100644 public/images/misc/Thumbs.db create mode 100644 public/images/misc/accenture-logo.png create mode 100644 public/images/misc/button-gloss.png create mode 100644 public/images/misc/button-overlay.png create mode 100644 public/images/misc/carbon_fibre_v2.png create mode 100644 public/images/misc/custom-form-sprites.png create mode 100644 public/images/misc/input-bg.png create mode 100644 public/images/misc/modal-gloss.png create mode 100644 public/images/misc/table-sorter.png create mode 100644 public/images/orbit/bullets.jpg create mode 100644 public/images/orbit/left-arrow.png create mode 100644 public/images/orbit/loading.gif create mode 100644 public/images/orbit/mask-black.png create mode 100644 public/images/orbit/pause-black.png create mode 100644 public/images/orbit/right-arrow.png create mode 100644 public/images/orbit/rotator-black.png create mode 100644 public/images/orbit/timer-black.png create mode 100644 public/images/trashcan.png create mode 100644 public/images/widget_grad.png create mode 100644 public/javascripts/client_stat.js create mode 100644 public/javascripts/foundation.js create mode 100644 public/javascripts/handlebars.js create mode 100644 public/javascripts/highcharts.js create mode 100644 public/javascripts/jquery-1.8.0.min.js create mode 100644 public/javascripts/jquery-1.8.1.min.js create mode 100644 public/javascripts/jquery-ui-1.8.17.custom.min.js create mode 100644 public/javascripts/jquery-ui-1.8.23.custom.min.js rename {assets/js => public/javascripts}/jquery.stopwatch.js (100%) create mode 100644 public/javascripts/jquery.tipTip.minified.js create mode 100644 public/javascripts/script.js create mode 100644 public/stylesheets/PIE.htc create mode 100644 public/stylesheets/app.css create mode 100644 public/stylesheets/foundation.css create mode 100644 public/stylesheets/ie.css create mode 100644 public/stylesheets/images/ui-bg_flat_30_cccccc_40x100.png create mode 100644 public/stylesheets/images/ui-bg_flat_50_5c5c5c_40x100.png create mode 100644 public/stylesheets/images/ui-bg_glass_20_555555_1x400.png create mode 100644 public/stylesheets/images/ui-bg_glass_40_0078a3_1x400.png create mode 100644 public/stylesheets/images/ui-bg_glass_40_ffc73d_1x400.png create mode 100644 public/stylesheets/images/ui-bg_gloss-wave_25_333333_500x100.png create mode 100644 public/stylesheets/images/ui-bg_highlight-soft_80_eeeeee_1x100.png create mode 100644 public/stylesheets/images/ui-bg_inset-soft_25_000000_1x100.png create mode 100644 public/stylesheets/images/ui-bg_inset-soft_30_f58400_1x100.png create mode 100644 public/stylesheets/images/ui-icons_222222_256x240.png create mode 100644 public/stylesheets/images/ui-icons_4b8e0b_256x240.png create mode 100644 public/stylesheets/images/ui-icons_a83300_256x240.png create mode 100644 public/stylesheets/images/ui-icons_cccccc_256x240.png create mode 100644 public/stylesheets/images/ui-icons_ffffff_256x240.png create mode 100644 public/stylesheets/jquery-ui-1.8.23.custom.css create mode 100644 public/stylesheets/normalize.css create mode 100644 public/stylesheets/style.css create mode 100644 public/stylesheets/tipTip.css create mode 100644 readme.md create mode 100644 routes/index.js create mode 100644 server.js delete mode 100644 treehouse.txt create mode 100644 views/index.html create mode 100644 views/index.jade create mode 100644 views/layout.jade diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..ed71f40 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Asterisk-nodejs-panel \ No newline at end of file diff --git a/.idea/Asterisk-nodejs-panel.iml b/.idea/Asterisk-nodejs-panel.iml new file mode 100644 index 0000000..33a3f17 --- /dev/null +++ b/.idea/Asterisk-nodejs-panel.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/libraries/sass_stdlib.xml b/.idea/libraries/sass_stdlib.xml new file mode 100644 index 0000000..546bfd1 --- /dev/null +++ b/.idea/libraries/sass_stdlib.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9973029 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jar:file:\C:\Program Files\JetBrains\PhpStorm 5.0.1\lib\webide.jar!\resources\html5-schema\html5.rnc + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..32b4f74 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..c80f219 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..7b15eea --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1359033107654 + 1359033107654 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README b/README deleted file mode 100644 index 062c32a..0000000 --- a/README +++ /dev/null @@ -1,8 +0,0 @@ -JavaScript Operator Panel [JOP] -================================ - -As opposed to FOP [http://www.asternic.org / http://www.fop2.com/], JOP is meant to be a HTML/JS solution to see the current Asterisk status in a Pannel. - -It's built on top of node.js, socket.io and express.js - -*Note*: As our Asterisk build and dialplan are heavily customized, we are using curls from dialplan and routes through express to perform actions but you could connect to the AMI and be able to capture events. If you are interested, you could use some other Node - Asterisk modules \ No newline at end of file diff --git a/agents.sql b/agents.sql deleted file mode 100644 index c4cdb10..0000000 --- a/agents.sql +++ /dev/null @@ -1,23 +0,0 @@ -CREATE TABLE `agentes` ( - `id` int(11) NOT NULL auto_increment, - `movil` tinyint(1) default '1' COMMENT 'Can the user call to mobile phones?', - `nacional` tinyint(1) NOT NULL default '1' COMMENT 'Can the user call to national phones?', - `especiales` tinyint(1) NOT NULL default '1' COMMENT 'Can the user call to special phones?', - `ochocientos` tinyint(1) NOT NULL default '1' COMMENT 'Can the user call to 800 phones?', - `novecientos` tinyint(1) NOT NULL default '1' COMMENT 'Can the user call to 900 phones?', - `informacion` tinyint(1) NOT NULL default '1' COMMENT 'Can the user call to info phones?', - `internacional` tinyint(1) NOT NULL default '1' COMMENT 'Can the user call to international phones?', - `cid` varchar(25) default NULL COMMENT 'Caller ID', - `codAgente` varchar(8) NOT NULL default '00000000'COMMENT 'User to log on the phone', - `clave` varchar(10) NOT NULL COMMENT 'Password to log on the phone', - `nombre` varchar(50) NOT NULL COMMENT 'Name', - `apellido1` varchar(50) NOT NULL COMMENT 'First surname', - `apellido2` varchar(50) default NULL COMMENT 'Second surname', - `email` varchar(100) default NULL, - `usuario` varchar(45) default NULL COMMENT 'User to log in the application', - `grupo` int(11) default '0' COMMENT 'Group in ACL', - PRIMARY KEY (`id`), - KEY `codAgente` (`codAgente`), - KEY `usuario` (`usuario`), - KEY `grupo` (`grupo`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/app.js b/app.js deleted file mode 100644 index 2d808f5..0000000 --- a/app.js +++ /dev/null @@ -1,279 +0,0 @@ -/* Requiring Express */ -var express = require('express'); -var app = express.createServer(); -var timeHelper = require('./helpers/time.js'); -var arrayHelper = require('./helpers/array.js'); -app.listen(8080); -var Agent = require('./models/agent.js'); -var Queue = require('./models/queues.js'); -var Agents = []; // This will hold agents -/* Requiring and setting up MySQL Configuration*/ -var mysql = require('mysql'); -var PBX_DATABASE = ''; -var mysql_client = mysql.createClient({ - host: '', - user: '', - password: '', - database: PBX_DATABASE -}); -var calls = 0; -var refetcher = { - queuesOP : false, - agentsOP : false, - necessary : false, - perform : function (){ - this.queuesOP = false; - this.agentsOP = true; - - queues.refetchQueues(mysql_client,this); - }, - done : function (who, necessary){ - if (who === 'queues') - this.queuesOP = true; - else - this.agentsOP = true; - this.necessary = this.necessary || necessary; - if (this.queuesOP && this.agentsOP && necessary){ - io.sockets.emit('reload', {}); - } - } -}; - -/* Requiring Socket.io */ -var io = require('socket.io').listen(app); -app.use("/assets", express.static(__dirname + '/assets')); -app.redirect('inicio', function(req,res){ - return '/'; -}); -var queues = new Queue(mysql_client, arrayHelper, timeHelper); -/* Routes, the core of the application */ - -/*Index*/ -app.get('/', function (req, res) { - res.sendfile(__dirname + '/index.html'); -}); -/* Reload - * - * The function perform a refetch, currently for queues but should catch agents too. - */ -app.get('/reload',function (req,res){ - refetcher.perform(); - res.send('ok'); -}); -/* - ########################## - #### Agents management #### - ########################## -*/ -/* - * When an agent logs into the phone this function will be called. - * - * @:agentCode : The Agent Code or Extension. - * - * URL Example: /logAgent/1010 - */ -app.get('/logAgent/:agentCode',function (req, res){ - mysql_client.query('SELECT agentes.nombre as nombre,'+ // You should adapt the query to your system - ' agentes.apellido1 as apellido1,'+ - ' agentes.apellido2 as apellido2,'+ - ' agentes.codAgente as codAgente,'+ - ' relcolaext.cola as cola,'+ - ' relcolaext.prioridad as prioridad'+ - ' FROM agentes, relcolaext'+ - ' WHERE relcolaext.codAgente = agentes.codAgente'+ - ' AND agentes.codAgente = "SIP/' + req.params.agentCode + '"', - function (err, results, fields){ - if (err){ - throw err; - } - if (arrayHelper.arrayObjectIndexOf(Agents,req.params.agentCode,'codAgente') === -1){ // It's not already connected - // If we have a result, we push it into the agents array - Agents.push( new Agent( - req.params.agentCode, - results[0].nombre, - results[0].apellido1, - results[0].apellido2, - results, - io - )); - console.log('Agent has logged %s %s [%s]', results[0].nombre, results[0].apellido1, req.params.agentCode); - res.send('ok'); // Avoid unending requests - } - else - res.send('Agent were already added'); - }); -}); -/* - * When an agent unlog it's phone this function will be called. - * - * @:agentCode : The Agent Code or Extension. - * - * URL Example: /unLogAgent/1010 - */ -app.get('/unLogAgent/:agentCode', function (req, res){ - // We have to delete it from our mantained list of logged agents - arrayHelper.deleteFromArrayOfObjects(Agents,req.params.agentCode,'codAgente'); - console.log('Unlogged agent %s', req.params.agentCode); - io.sockets.emit('unLogAgent', req.params.agentCode); - res.send('ok'); -}); -/* - * When an agent enters in administrative time, this function will be called. - * - * @:agentCode : The Agent Code or Extension. - * - * URL Example: /administrative/1010 - */ -app.get('/administrative/:agentCode',function (req,res){ - console.log ('Agent %s entered in administrative time', req.params.agentCode); - var pos_agent = arrayHelper.arrayObjectIndexOf(Agents,req.params.agentCode,'codAgente'); - Agents[pos_agent].changeStatus(3,io,null); - res.send('ok'); -}); -/* - * When an agent put him/herself into unavailable, this function will be called. - * - * @:agentCode : The Agent Code or Extension. - * - * URL Example: /unavailable/1010 - */ -app.get('/unavailable/:agentCode',function (req,res){ - console.log ('Agent %s became unavailable', req.params.agentCode); - var pos_agent = arrayHelper.arrayObjectIndexOf(Agents,req.params.agentCode,'codAgente'); - Agents[pos_agent].changeStatus(2,io,null); - res.send('ok'); -}); -/* - * When an agent disable administrative time or unavailable mode, this function will be called. - * - * @:agentCode : The Agent Code or Extension. - * - * URL Example: /available/1010 - */ -app.get('/available/:agentCode',function (req,res){ - console.log ('Agent %s became available', req.params.agentCode); - var pos_agent = arrayHelper.arrayObjectIndexOf(Agents,req.params.agentCode,'codAgente'); - Agents[pos_agent].changeStatus(1,io,null); - res.send('ok'); -}); -/* - ########################## - #### Calls management #### - ########################## -*/ -/* - * When a call enters in a queue, this function will be called - * - * @:queue : The queue that is receiving the call. - * @:uniqueid : The uniqueid of the call. - * - * URL Example: /call/APPLUSCola/1234567890 - */ -app.get('/call/:queue/:uniqueid',function (req,res){ - console.log ('Incoming call [%s] to queue %s', req.params.uniqueid, req.params.queue); - updatePrimary(1); - queues.dispatchCall({ - uniqueid: req.params.uniqueid, - queue: req.params.queue, - type: 'in', - io: io - }); - res.send('ok'); -}); -/* - * When a call is answered by an agent, this function will be called. - * - * @:queue : The queue that is receiving the call. - * @:uniqueid : The uniqueid of the call. - * @:agentCode : The agent whom is answering the call. - * - * URL Example: /answerCall/APPLUSCola/1234567890/1010 - */ -app.get('/answerCall/:queue/:uniqueid/:agentCode',function (req,res){ - console.log ('Call answered by %s', req.params.agentCode); - var pos_agent = arrayHelper.arrayObjectIndexOf(Agents,req.params.agentCode,'codAgente'); - - queues.dispatchCall({ - uniqueid: req.params.uniqueid, - queue: req.params.queue, - type: 'out', - io: io - }); - Agents[pos_agent].changeStatus(4,io,req.params.queue.replace('Cola','')); - res.send('ok'); -}); -/* - * When an agent performs a call, this function will be called. - * - * @:agentCode : The agent whom is performing the call. - * @:queue : The queue that is receiving the call. - * - * URL Example: /externalCall/1010/APPLUSCola - */ -app.get('/externalCall/:agentCode/:queue',function (req,res){ - console.log ('Outgoing call from %s', req.params.agentCode); - var pos_agent = arrayHelper.arrayObjectIndexOf(Agents,req.params.agentCode,'codAgente'); - Agents[pos_agent].changeStatus(5,io,req.params.queue.replace('Cola','')); - res.send('ok'); -}); -/* - * When a call hangs, this function will be called. - * - * @:type : This indicates where the call were terminated. Could have this values: - * * 'agente' : If it ends in an agent - * * 'cola' : If it ends in a queue - * @:uniqueid : The uniqueid of the call - * @:agentOrQueue : The AgentCode or the Queue Name where the call terminated. - * - * URL Example 1: /hangCall/cola/1234567890/APPLUSCola - * URL Example 2: /hangCall/agente/1234567890/1010 - */ -app.get('/hangCall/:type/:uniqueid/:agentOrQueue',function (req,res){ - console.log ('Call finished at %s', req.params.type); - updatePrimary(-1); - - if (req.params.type === 'agente'){ - var pos_agent = arrayHelper.arrayObjectIndexOf(Agents, req.params.agentOrQueue, 'codAgente'); - Agents[pos_agent].endCall(io); - } else { - queues.dispatchCall({ - queue: req.params.agentOrQueue.replace('Cola',''), - uniqueid: req.params.uniqueid, - type: 'out', - abandoned: true, - io: io - }); - } - res.send('ok'); -}); -/* ### Socket.io Stuff ### */ -io.disable('heartbeats'); -io.sockets.on('connection', function (socket) { - // If someone new comes, it will notified of the current status of the application - console.log('Someone connected to the pannel'); - sendCurrentStatus(io,socket.id); -}); - -/* Functions */ -function calculateAgentTime(agents,timeHelper){ - for (var i = 0, len = agents.length; i < len; i ++){ - agents[i].calculateTimes(timeHelper); - } -} -function sendCurrentStatus (io, socketid){ - var status = {}; - if (Agents.length > 0){ - calculateAgentTime(Agents,timeHelper); - } - queues.currentStatus(); - - status.agents = Agents; - status.queues = queues; - status.currentCalls = calls; - - io.sockets.socket(socketid).emit('currentStatus', status); -} -function updatePrimary (qty){ - calls = calls + qty; - io.sockets.emit('updatePrimary',{calls: calls}); -} \ No newline at end of file diff --git a/helpers/array.js b/helpers/array.js index fdde093..7a292a0 100644 --- a/helpers/array.js +++ b/helpers/array.js @@ -1,16 +1,51 @@ +/** + * This function will delete an element from the array + * + * @param myArray haystack + * @param element needle + */ exports.deleteFromArray = function (myArray, element) { - position = my_array.indexOf(element); + var position = myArray.indexOf(element); myArray.splice(position, 1); }; -// This function will delete from Array of Objects given the property, the needle and... the haystack. You guessed! +/** + * This function will delete from Array of Objects given the property, the needle and... the haystack. You guessed! + * + * @param myArray haystack + * @param searchTerm needle + * @param property property to match + */ exports.deleteFromArrayOfObjects = function (myArray, searchTerm, property) { - position = this.arrayObjectIndexOf(myArray, searchTerm, property); - myArray.splice(position, 1); + var position = this.arrayObjectIndexOf(myArray, searchTerm, property); + if (position !== -1) { + myArray.splice(position, 1); + } }; -// This functions looks for an Object which have the property === searchterm +/** + * This functions looks for an Object which have the property === searchterm + * + * @param myArray haystack + * @param searchTerm needle + * @param property property to match + * @return {Number} position or -1 in case it's not found + */ exports.arrayObjectIndexOf = function(myArray, searchTerm, property) { for(var i = 0, len = myArray.length; i < len; i++) { if (myArray[i][property] === searchTerm) return i; } return -1; +}; +/** + * This function will delete the element to delete from an array of objects + * + * @param to_delete needle + * @param target_array haystack + * @param property property to match + * + * TODO: Params should be rearranged to match the previous orders + */ +exports.deleteSeveralFromArrayOfObjects = function(to_delete, target_array, property){ + for (var j = 0, length = to_delete.length; j < length; j++){ + this.deleteFromArrayOfObjects(target_array, to_delete[j],property); + } }; \ No newline at end of file diff --git a/helpers/executor.js b/helpers/executor.js new file mode 100644 index 0000000..972a7b2 --- /dev/null +++ b/helpers/executor.js @@ -0,0 +1,29 @@ +/** + * This module encapsulates the execution of commands in bash + */ +module.exports = function Executor() { + var sys = require('sys'), + exec = require('child_process').exec; + + /** + * Private function that just logs the output to console + * + * @param error if any + * @param stdout from execution + * @param stderr from execution + */ + function handleOutput ( error , stdout , stderr ) { + console.log( stdout ); + console.log('End of execution'); + } + /** + * This is the only function visible and is in charge of actually, executing the command + * + * @param command to execute + * @param cb callback to call on finish, if no callback is defined @handleOutput will be used instead + */ + this.execute = function ( command , cb) { + console.log('Executing "%s" in bash', command); + exec (command , cb || handleOutput); + }; +}; \ No newline at end of file diff --git a/helpers/math.js b/helpers/math.js new file mode 100644 index 0000000..2119378 --- /dev/null +++ b/helpers/math.js @@ -0,0 +1,11 @@ +/** + * Function to fix a number to some number of decimals + * + * @param number to fix + * @param n of decimals + * @return {Number} + */ +exports.fixedTo = function (number, n) { + var k = Math.pow(10, n+1); + return (Math.round(number * k) / k); +}; \ No newline at end of file diff --git a/helpers/time.js b/helpers/time.js index 306dc18..fb5cd79 100644 --- a/helpers/time.js +++ b/helpers/time.js @@ -1,9 +1,80 @@ +/** + * This function will calculate the difference in milliseconds between now and Date supplied + * + * @param date + * @return {*} if Date is not a Date it will return null, otherwise, { Number } with difference + */ exports.calculateTimeSince = function(date){ - var now = new Date; + var now = new Date(); + var difference; - if (date) + if (date){ difference = now.getTime() - date.getTime(); - else + + if (difference < 0) { difference = 1; } + } + else{ difference = null; + } + return difference; +}; +/** + * Function that checks if date supplied meets the SLA + * + * @param date to check + * @param objective to achieve + * @return {Boolean} + */ +exports.meetSLA = function(date, objective) { + var now = new Date(), + difference = (now - date) / 1000; + + return difference <= objective; +}; +/** + * Function that checks if the difference between two dates is lesser than the objective + * + * @param date_start + * @param date_end + * @param objective + * @return {Boolean} + */ +exports.meetSLABefore = function(date_start, date_end, objective) { + var difference = (date_end - date_start) / 1000; + + return difference <= objective; +}; +/** + * Function that checks if the difference between two dates is greater than the objective + * + * @param date_start + * @param date_end + * @param objective + * @return {Boolean} + */ +exports.meetSLAAfter = function(date_start, date_end, objective) { + var difference = (date_end - date_start) / 1000; + + return difference >= objective; +}; +/** + * This function parses a MySQL datetime string and returns a Date Object + * + * @timestamp has to be in the following format YYYY-MM-DD H:I:S + */ +exports.mysqlTimestampToDate = function(timestamp){ + var regex=/^([0-9]{2,4})-([0-1][0-9])-([0-3][0-9]) (?:([0-2][0-9]):([0-5][0-9]):([0-5][0-9]))?$/; + var parts=timestamp.replace(regex,"$1 $2 $3 $4 $5 $6").split(' '); + return new Date(parts[0],parts[1]-1,parts[2],parts[3],parts[4],parts[5]); +}; +/** + * Function that remove Hour, Minute and Second data from a Date + * + * @param date + */ +exports.setAbsoluteDay = function (date){ + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); }; \ No newline at end of file diff --git a/index.html b/index.html index 960b572..5893a6d 100644 --- a/index.html +++ b/index.html @@ -5,13 +5,17 @@ - - JavaScript Operator Pannel + + + + + Pannel Flaix - - + + + - - - - - - + + + + + -
+
- ; +
-
- - Iconos de los clientes - +
+
-
-
+
+
+
+
+
+
+
+ Occupation +
+
+
+
+ 0% +
+
-
- -
-
-
- OcupaciĆ³n del primario -
-
-
-
- 0% -
+
+
+
+ Awaiting
-
- + +
+
+ 0 +
+
+
+
+
+
+ Talking +
+
+
+
+ 0 +
+
+
+
+
+
+
- + + + + + + + + + + \ No newline at end of file diff --git a/libraries/mysql.js b/libraries/mysql.js new file mode 100644 index 0000000..46aacc3 --- /dev/null +++ b/libraries/mysql.js @@ -0,0 +1,58 @@ +/** + * Module that encapsulates the mysql module + */ +mysql = require('mysql'); + +/** + * Function that reconnects on connection lost by binding a function that fires up on error + * + * @param connection + * @url https://github.com/felixge/node-mysql/issues/239 + */ +function handleDisconnect(connection) { + connection.on('error', function(err) { + if (!err.fatal) { + return; + } + + if (err.code !== 'PROTOCOL_CONNECTION_LOST') { + throw err; + } + + console.log('Re-connecting lost connection: ' + err.stack); + + connection = mysql.createConnection(connection.config); + handleDisconnect(connection); + connection.connect(); + }); +} + +var mysql_conf = { + host: 'HOST_IP', + user: 'HOST_USER', + password: 'HOST_PASS', + database: 'HOST_DB' +}; +var client = mysql.createConnection(mysql_conf); + +handleDisconnect(client); + +module.exports.client = client; +/** + * Function that will perform a Query to the database + * + * @param query to perform + * @param callback to call when data is ready. + */ +module.exports.doQuery = function(query , callback){ + console.log ('Executing MySQL Query >> %s', query); + client.query(query, + function (err, results, fields){ + if (err) + { + throw err; + } + callback(results); + } + ); +}; \ No newline at end of file diff --git a/models/agent.js b/models/agent.js index 00a6859..78056dd 100644 --- a/models/agent.js +++ b/models/agent.js @@ -1,84 +1,161 @@ -util = require('util'); +var arrayHelper = require('../helpers/array.js'); +/** + * AGENTs model + * + * @param data object *array* with information needed to create the agent: + * @codAgente agent code + * @nombre agent name + * @apellido1 first last name + * @apellido2 second last name (if applicable) + * @estado agent status in Asterisk + * @param io socket.io object to broadcast + * @constructor + */ +function Agent(data, io){ + this.statuses = [ + { + id : 0, + name : 'Disconnected', + start_timer : false, + is_call : false + }, + { + id : 1, + name : 'Available', + start_timer : false, + is_call : false + }, + { + id : 2, + name : 'Meeting', + start_timer : true, + is_call : false + }, + { + id : 3, + name : 'Administrative', + start_timer : true, + is_call : false + }, + { + id : 4, + name : 'Incoming', + start_timer : true, + is_call : true + }, + { + id : 5, + name : 'Outgoing', + start_timer : true, + is_call : true + }, + { + id : 6, + name : 'Resting', + start_timer : true, + is_call : false + }, + { + id : 7, + name : 'Glory time', + start_timer : true, + is_call : false + } + ]; -function Agent(codAgente,nombre,apellido1,apellido2, queues, io){ - this.status = 1; - this.prevStatus = 0; - /******************************************************************************** - * INDEX OF STATUS - * - * 1 - Available: Agent is available and can receive calls - * 2 - Unavailable: Agent is unavailable and can't receive calls - * 3 - Administrative time: Agent is in administrative time, can't receive calls - * 4 - Incoming call: Agent is attending an incoming call - * 5 - Outgoing call: Agent is performing an outgoing call - ********************************************************************************/ - this.codAgente = codAgente; - this.nombre = nombre; - this.apellido1 = apellido1; - this.apellido2 = apellido2; + this.prevStatus = {}; + + this.codAgente = data[0].codAgente; + this.nombre = data[0].nombre; + this.apellido1 = data[0].apellido1; + this.apellido2 = data[0].apellido2; this.currentStatusTime = null; this.currentStatusTimeDiff = null; this.currentCallTimeDiff = null; this.currentCallTime = null; this.currentTalkingQueue = null; - this.queues = this.getQueues(queues); + this.queues = this.getQueues(data); io.sockets.emit('logAgent', this); + + this.arrayHelper = arrayHelper; + this.timeHelper = require('../helpers/time.js'); + this.status = (this.arrayHelper.arrayObjectIndexOf(this.statuses, data[0].estado, 'name') !== -1) ? + this.statuses[this.arrayHelper.arrayObjectIndexOf(this.statuses, data[0].estado, 'name')] : + this.statuses[0]; } -/* +/** * This functions calculate current times of the current status and / or the current call - * - * @timeHelper : The timeHelper which has the function that calculate times. */ -Agent.prototype.calculateTimes = function(timerHelper){ - if (this.currentStatusTime != null) - this.currentStatusTimeDiff = timerHelper.calculateTimeSince(this.currentStatusTime); - if (this.currentCallTime != null) - this.currentCallTimeDiff = timerHelper.calculateTimeSince(this.currentCallTime); +Agent.prototype.calculateTimes = function(){ + if (this.currentStatusTime !== null){ + this.currentStatusTimeDiff = this.timeHelper.calculateTimeSince(this.currentStatusTime); + } + + if (this.currentCallTime !== null){ + this.currentCallTimeDiff = this.timeHelper.calculateTimeSince(this.currentCallTime); + } }; -/* +/** * This function change the status of the agent. * - * @status : The new status (integer) should be a value between 1 and 5 - * @io : The socket object in which will send info once the change has been made - * @queue : The current queue where the agent is talking + * @param data object with needed data + * @status : The new status (integer) should be a value between 1 and 5 + * @queue : The current queue where the agent is talking */ -Agent.prototype.changeStatus = function(status, io, queue, substract){ - if (status == 1){ - this.currentStatusTime = null; - this.currentStatusTimeDiff = null; - this.currentCallTime = null; - this.currentCallTimeDiff = null; - this.currentTalkingQueue = null; - } - else { - if (status < 4){// That's it, not talking by phone - this.currentStatusTime = new Date; +Agent.prototype.changeStatus = function(data){ + this.prevStatus = this.status; + + if (this.statuses[data.status] !== undefined){ + if (!this.statuses[data.status].start_timer) + { + this.currentStatusTime = null; + this.currentStatusTimeDiff = null; + this.currentCallTime = null; + this.currentCallTimeDiff = null; this.currentTalkingQueue = null; } - else{ - this.currentCallTime = new Date; - this.prevStatus = this.status; - this.currentTalkingQueue = queue; + else + { + if (!this.statuses[data.status].is_call) + {// That's it, not talking by phone + this.currentStatusTime = new Date; + this.currentTalkingQueue = null; + } + else + { + this.currentCallTime = new Date; + this.currentTalkingQueue = data.queue || ''; + } } - } - this.status = status; - io.sockets.emit('changeEvent', {agent: this.codAgente, status: this.status, queue: this.currentTalkingQueue}); + this.status = this.statuses[data.status]; + data.io.sockets.emit('changeEvent', {agent: this.codAgente, status: this.status.id, queue: this.currentTalkingQueue}); + } }; -/* +/** * This function ends the current call * * @socket : The socket object in which will send info once the change has been made */ Agent.prototype.endCall = function (socket){ - if (this.prevStatus != 1)// Administrative time or unavailable - this.changeStatus(this.prevStatus,socket,null); + if (!this.prevStatus.is_call) + {// Administrative time or unavailable + this.changeStatus({ + status : this.prevStatus.id || this.status.id, + io : socket + }); + } else - this.changeStatus(1,socket,null); + { + this.changeStatus({ + status: 1, + io: socket + }); + } this.currentTalkingQueue = null; }; -/* - * This functions will store the agent's queues in a property. +/** + * This functions will store the agent's queues (and his/her priority) in a property. * * @queues : The queues */ @@ -91,5 +168,239 @@ Agent.prototype.getQueues = function (queues){ }); } return array; -} -module.exports = Agent; \ No newline at end of file +}; +/** + * This function is called whenever the agent starts or stops ringing. Will broadcast by socket + * + * @param data object with needed data + * @action : Start or Stop + */ +Agent.prototype.manageRinging = function(data){ + console.log('Agent [%s] %s ringing.', this.codAgente, data.action === 'start' ? 'started' : 'stopped'); + data.io.sockets.emit('agentRinging', {agent: this.codAgente, action: data.action}); +}; +/** + * Utility functions related to this model. Those are exported. + * + * @type {Object} + */ +var utils = { + /** + * This function is in charge of fetching agents from Database. First, will fetch and store agents. + * Then, will retrieve their statuses and finally will call the callback with the results + * + * @param database to extract data + * @param io to emit by socket + * @param stored_agents currently stored agents (if any) + * @param callback to be called once it finishes + */ + fetchAgents : function(database, io, stored_agents, callback){ + var agents = []; + + utils.getAgents(database, function(results){ + agents = utils.storeAgentsFromDB(io, stored_agents, results); + utils.loadStatusTimes(database,function(data){ + agents = utils.storeStatusTimes(agents, data); + callback.apply(undefined,[agents]); + }); + }); + }, + + /** + * Function that get Agents from the database + * + * @param database + * @param callback + */ + getAgents : function(database, callback){ + var query = 'SELECT agentes.nombre AS nombre, ' + + 'agentes.apellido1 AS apellido1, ' + + 'agentes.apellido2 AS apellido2, ' + + 'agentes.codAgente AS codAgente, ' + + 'agentes.estado AS estado, ' + + 'relcolaext.cola AS cola, ' + + 'relcolaext.prioridad AS prioridad ' + + 'FROM agentes ' + + 'LEFT JOIN relcolaext ON relcolaext.codAgente = agentes.codAgente ' + + 'LEFT JOIN colas ON colas.nombre = relcolaext.cola ' + + 'WHERE agentes.visible_panel = 1 ' + + 'AND agentes.activo = 1 ' + + 'AND colas.panel = 1'; + + database.doQuery(query, callback); + }, + /** + * This function will store all results from getAgents. It will try to keep current agents if there are any + * updating their info (if applicable), inserting new ones and deleting. + * + * This function could be called anytime in the lifecycle of the app. + * + * @see getAgents + * @param io to emit by socket + * @param stored_agents contains the current stored agents + * @param results that comes from the Database resultset + */ + storeAgentsFromDB : function (io, stored_agents, results) { + var agents = stored_agents || []; + + for (var i = 0; i < results.length; i++) + { + var currentPosition = arrayHelper.arrayObjectIndexOf( + agents, + results[i].codAgente, + 'codAgente' + ); + var agent_rows = utils.getAgentRows(results,results[i].codAgente); + + if (currentPosition === -1) + { // It's not already connected + agents.push( new Agent( + agent_rows, + io + )); + console.log('Agent %s %s [%s] was loaded on server startup', + results[i].nombre, results[i].apellido1, results[i].codAgente); + } + else + { + var data = results[i], + agent = agents[currentPosition]; + + agent.codAgente = data.codAgente; + agent.nombre = data.nombre; + agent.apellido1 = data.apellido1; + agent.apellido2 = data.apellido2; + agent.queues = agent.getQueues(agent_rows); + + if (!agent.status.is_call){ + agent.status = (arrayHelper.arrayObjectIndexOf(agent.statuses, data.estado, 'name') !== -1) ? + agent.statuses[arrayHelper.arrayObjectIndexOf(agent.statuses, data.estado, 'name')] : + agent.statuses[0]; + } + } + } + var to_delete = []; + + for (var j = 0, length = agents.length; j < length; j ++) { + var agent_aux = agents[j]; + var current_position = arrayHelper.arrayObjectIndexOf(results, agent_aux.codAgente,'codAgente'); + if (current_position === -1) { + to_delete.push(agent_aux.codAgente); + } + } + arrayHelper.deleteSeveralFromArrayOfObjects(to_delete, agents,'codAgente'); + + return agents; + }, + + /** + * This function will load the last status from all agents and when the change occurred + * + * @param database to query for data + * @param callback to be called + */ + loadStatusTimes : function(database, callback){ + var query = 'SELECT a.* ' + + 'FROM eventos_centralita a ' + + 'LEFT JOIN eventos_centralita b ' + + 'ON b.codAgente = a.codAgente ' + + 'AND b.fechaHora > a.fechaHora ' + + 'WHERE b.idEvent IS NULL GROUP BY a.codAgente'; + + database.doQuery(query, callback); + }, + /** + * This function will update each agent and will set the currenStatusTime property so it matches with the one we + * have in database. + * + * @see loadStatusTimes + * @param agents current agents + * @param results that comes from the database + */ + storeStatusTimes : function(agents, results){ + for (var i = 0, length = results.length; i < length; i++) { + var agent_pos = arrayHelper.arrayObjectIndexOf(agents, results[i].codAgente, 'codAgente'); + + if (agent_pos !== -1 && agents[agent_pos].status.id > 1){ + agents[agent_pos].currentStatusTime = results[i].fechaHora; + } + } + return agents; + }, + /** + * This function will get all rows for some agent + * + * @param array that contains all the rows for all agents + * @param codAgent the agent code we are looking for + * @return {Array} of rows + */ + getAgentRows : function (array, codAgent) { + var agent_rows = []; + + // We get all results from that agent + for (var j= 0; j < array.length; j++) + { + if (array[j].codAgente === codAgent) { + agent_rows.push(array[j]); + } + } + + return agent_rows; + }, + /** + * This function get the status of the agents, this function is the one that's called once a + * client connects to the pannel and retrieve all necessary information that needs to be displayed + * + * @param agents + * @return {Array} + */ + getStatus : function (agents){ + var status = []; + + for (var i = 0, length = agents.length; i < length; i ++){ + agents[i].calculateTimes(); + + status.push({ + codAgente : agents[i].codAgente, + status_id : agents[i].status.id, + nombre : agents[i].nombre, + apellido1 : agents[i].apellido1, + apellido2 : agents[i].apellido2, + currentCallTimeDiff : agents[i].currentCallTimeDiff, + currentStatusTimeDiff : agents[i].currentStatusTimeDiff, + currentTalkingQueue : agents[i].currentTalkingQueue, + queues : agents[i].queues + }); + } + + return status; + }, + /** + * This function is in charge of creating a new Agent + * + * @param results + * @param io + * @return {Agent} + */ + createAgent : function(results, io){ + return new Agent(results,io); + }, + /** + * Get an Agent object from it's agent's code + * + * @param agents + * @param code + * @return {*} + */ + getAgentFromCode : function (agents, code){ + var agent, + agent_position = arrayHelper.arrayObjectIndexOf(agents, code , 'codAgente'); + + if (agent_position !== -1){ agent = agents[agent_position]; } + + return agent; + } +}; + +exports.model = Agent; +exports.utils = utils; \ No newline at end of file diff --git a/models/client.js b/models/client.js new file mode 100644 index 0000000..a62bcd4 --- /dev/null +++ b/models/client.js @@ -0,0 +1,335 @@ +var arrayHelper = require('../helpers/array.js'), + timeHelper = require('../helpers/time.js'); + +function Client ( data , io , database ) { + var self = this; + + this.name = data.name; + this.database = database; + this.perc_abandoned = data.perc_abandoned; + this.perc_answered = data.perc_answered; + this.sec_abandoned = data.sec_abandoned; + this.sec_answered = data.sec_answered; + this.timeHelper = require('../helpers/time.js'); + this.mathHelper = require('../helpers/math.js'); + + this.total_calls = 0; + this.offered_calls = 0; + this.total_abandoned = 0; + this.total_answered = 0; + this.failed_calls = 0; + this.abandoned_after_SLA = 0; + this.answered_before_SLA = 0; + this.total_response_time = 0; + this.total_abandon_time = 0; + + this.stats_by_hour = []; + this.stats_by_hour.length = 23; + + this.io = io; + this.socket = self.io.of('/' + this.name); + + self.socket.on('connection', function(socket){ + console.log('Someone wants to see some %s stats...', self.name); + self.sendStatus(socket.id); + }); +} +Client.prototype.storeCall = function(now, call_date, abandoned){ + var type = abandoned ? 'abandoned' : 'answered', + meet_sla = false; + + now = (!now) ? new Date() : now; + + if (abandoned){ + this.total_abandon_time += parseInt(((now - call_date) /1000).toFixed(0),10); + this.total_abandoned++; + + if (timeHelper.meetSLAAfter(call_date, now, this.sec_abandoned)){ + this.abandoned_after_SLA++; + meet_sla = true; + } + } + else { + this.total_response_time += parseInt(((now - call_date) /1000).toFixed(0),10); + this.total_answered++; + + if (timeHelper.meetSLABefore(call_date, now, this.sec_answered)){ + this.answered_before_SLA++; + meet_sla = true; + } + } + + utils.storeCall(this.stats_by_hour, now, call_date, type, meet_sla); +}; +Client.prototype.getStatus = function(){ + return { + total_calls : this.total_calls, + total_offered_calls : this.offered_calls, + total_abandoned : this.total_abandoned, + total_answered : this.total_answered, + failed_calls : this.failed_calls, + abandoned_after_SLA : this.abandoned_after_SLA, + answered_before_SLA : this.answered_before_SLA, + average_response_time : this.mathHelper.fixedTo((this.total_response_time / this.total_answered),2), + per_hour : this.stats_by_hour + }; +}; +Client.prototype.sendStatus = function (socketid) { + var status = this.getStatus(); + + if (socketid) { + this.socket.socket(socketid).emit('clientStatus', status); + } + else { + this.socket.emit('clientStatus', status) + } +}; +Client.prototype.resetData = function () { + this.total_calls = 0; + this.total_offered_calls = 0; + this.total_abandoned = 0; + this.total_answered = 0; + this.failed_calls = 0; + this.abandoned_after_SLA = 0; + this.answered_before_SLA = 0; + this.average_response_time = 0; + + this.sendStatus(); +}; +Client.prototype.loadStats = function(start_date, end_date, callback){ + var self = this; + var query = 'SELECT ' + + 'llamadas.uniqueid AS unique_id, ' + + 'llamadas.tipo AS type, ' + + 'clientes.nombre AS client, ' + + 'colas.nombre AS queue, ' + + 'IF(ISNULL(llamadas.fechaInicioCola), llamadas.fecha, llamadas.fechaInicioCola) AS start_date, ' + + 'llamadas.fechaAnswered AS answered_date, ' + + 'llamadas.fechaHungup AS hungup_date, ' + + 'llamadas.agente AS agent, ' + + 'IF(ISNULL(fechaAnswered), TIMESTAMPDIFF(SECOND,IF(ISNULL(llamadas.fechaInicioCola), llamadas.fecha, llamadas.fechaInicioCola),fechaHungup),TIMESTAMPDIFF(SECOND,IF(ISNULL(llamadas.fechaInicioCola), llamadas.fecha, llamadas.fechaInicioCola),fechaAnswered)) AS time_in_queue, '+ + 'llamadas.status AS status ' + + 'FROM llamadas ' + + 'LEFT JOIN colas ON colas.id = llamadas.cola ' + + 'LEFT JOIN numeros_cabecera ON numeros_cabecera.id = colas.numero ' + + 'LEFT JOIN clientes ON clientes.idCliente = numeros_cabecera.cliente ' + + 'WHERE llamadas.fecha >= ? ' + + 'AND llamadas.fecha <= ? ' + + 'AND clientes.nombre = ? ' + + 'AND llamadas.tipo = \'Incoming\' ' + + 'ORDER BY llamadas.fecha;'; + + console.log('Executing query >> %s', query); + + this.database.client.query( query , [start_date, end_date, this.name], function(err,results){ + callback(self.returnStats.call(self,results)); + }); +}; +Client.prototype.returnStats = function(results) { + var stats = { + name : this.name, + sec_abandoned : this.sec_abandoned, + sec_answered : this.sec_answered, + perc_abandoned : this.perc_abandoned, + perc_answered : this.perc_answered, + real_time : false, + total_calls : 0, + total_offered_calls : 0, + total_response_time : 0, + average_response_time : 0, + failed_calls : 0, + total_abandoned : 0, + abandoned_after_SLA : 0, + total_answered : 0, + answered_before_SLA : 0 + }; + stats.per_hour = []; + + for (var i = 0, length = results.length; i < length; i++){ + if (results[i].type === 'Incoming'){ + var call_date = results[i].start_date, + answered_date = results[i].answered_date, + hungup_date = results[i].hungup_date, + status = results[i].status, + type = ( + (status.indexOf('bandoned') === -1) && + (status.indexOf('Voicemail') === -1) + ) ? 'answered' : 'abandoned', + meets_sla; + + stats.total_calls++; + + if (status !== 'Abandoned in message' && status !== 'Out of schedule') { + stats.total_offered_calls++; + stats['total_' + type]++; + + if (type === 'abandoned'){ + meets_sla = this.timeHelper.meetSLAAfter( + call_date, + hungup_date, + this['sec_' + type] + ); + if (meets_sla){ + stats.abandoned_after_SLA++; + } + + if (hungup_date === null || hungup_date.getTime() !== hungup_date.getTime()){ + console.log('Error abandoned: '); + console.log(results[i]); + } + else { + utils.storeCall(stats.per_hour, hungup_date, call_date, type, meets_sla); + } + } + else { + var time_in_queue = parseInt(results[i].time_in_queue,10); + if (!isNaN(time_in_queue)){ + stats.total_response_time += time_in_queue; + } + + meets_sla = this.timeHelper.meetSLABefore( + call_date, + answered_date, + this['sec_' + type] + ); + if (meets_sla){ + stats.answered_before_SLA++; + } + + if (answered_date === null || answered_date.getTime() !== answered_date.getTime()){ + console.log('Error answered: '); + console.log(results[i]); + } + else { + utils.storeCall(stats.per_hour, answered_date, call_date, type, meets_sla); + } + } + } + else { + stats.failed_calls++; + } + } + } + stats.average_response_time = this.mathHelper.fixedTo((stats.total_response_time / stats.total_answered),2); + + return stats; +}; + +var utils = { + fetchClients : function(database, io, stored_clients, callback){ + var clients = []; + + utils.getClients(database, function(results) { + clients = utils.storeClientsFromDB(io, stored_clients, database, results); + callback.apply(undefined,[clients]); + }); + }, + /** + * This function will get all clients for stats purpouses + * + * @param callback to be called when it finishes + */ + getClients : function(database, callback) { + var query = 'SELECT nombre AS name, ' + + 'porcAbandoned AS perc_abandoned, ' + + 'secAbandoned AS sec_abandoned, ' + + 'porcAnswered AS perc_answered, ' + + 'secAnswered AS sec_answered ' + + 'FROM clientes'; + database.doQuery(query, callback); + }, + + /** + * This function will store all results from getClients + * + * @see getClients + */ + storeClientsFromDB : function(io, stored_clients, database, results) { + var prefetched = (stored_clients.length !== 0), + clients = stored_clients || []; + + for (var i = 0; i < results.length; i++) { + var result_set = results[i], + current_position = arrayHelper.arrayObjectIndexOf( + clients, + result_set.name, + 'name' + ); + + if (current_position === -1){ + var client = new Client(result_set, io , database); + + clients.push( + client + ); + } + else { + clients[current_position].perc_abandoned = result_set.perc_abandoned; + clients[current_position].perc_answered = result_set.perc_answered; + clients[current_position].sec_abandoned = result_set.sec_abandoned; + clients[current_position].sec_answered = result_set.sec_answered; + } + + console.log('Client %s was loaded on server startup', result_set.name); + } + + // If there was some clients already loaded and his function is called, it means that the panel + // is being refetched so we'll get rid of those which aren't in our result + if (prefetched){ + var to_delete = []; + for (var j= 0, length = clients.length; j < length; j++){ + var position = arrayHelper.arrayObjectIndexOf( + results, + clients[j].name, + 'name' + ); + + if (position === -1) { + to_delete.push(clients[j].name); + } + } + + arrayHelper.deleteSeveralFromArrayOfObjects(to_delete, clients, 'name'); + } + + return clients; + }, + + /** + * This is a helper method that gets a client object by it's name + * + * @param name you are looking for + * @return {Client} the object or undefined + */ + getClientFromName : function (clients, name){ + var client, + client_position = arrayHelper.arrayObjectIndexOf(clients, name , 'name'); + + if (client_position !== -1){ client = clients[client_position]; } + + return client; + }, + + storeCall : function (storage, now, call_date, type, meet_sla) { + var hour = now.getHours(); + + if (storage[hour] === undefined){ + storage[hour] = { + total : 0, + answered : 0, + abandoned : 0, + answered_sla : 0, + abandoned_sla : 0, + answered_time : 0, + abandoned_time : 0 + }; + } + + storage[hour].total++; + storage[hour][type]++; + storage[hour][type + '_time'] += parseInt(((now - call_date) /1000).toFixed(0),10); + if (meet_sla){ storage[hour][type + '_sla']++; } + } +}; +exports.model = Client; +exports.utils = utils; \ No newline at end of file diff --git a/models/queue.js b/models/queue.js new file mode 100644 index 0000000..c04c9e1 --- /dev/null +++ b/models/queue.js @@ -0,0 +1,179 @@ +var arrayHelper = require('../helpers/array.js'), + timeHelper = require('../helpers/time.js'); + +function Queue(data){ + // Stats + this.total_calls = 0; + this.total_abandoned = 0; + this.total_abandoned_before_sla = 0; + this.total_answered = 0; + this.total_answered_before_sla = 0; + + this.name = data.name; + this.color = data.color; + this.client_name = data.client; + this.client_obj = data.client_obj; + + // Initializing and data fullfillment + this.calls = []; + this.last_call_time = null; + this.last_call_time_diff = null; +} + +var utils = { + fetchQueues : function(database, io, stored_queues, clients, getClientFromName, callback){ + var queues = []; + + utils.getQueues(database,function(results){ + queues = utils.storeQueuesFromDB(stored_queues, clients, getClientFromName, results); + callback.apply(undefined,[queues]); + }); + }, + getQueues : function(database, callback){ + var query = "SELECT " + + "colas.nombre AS name, " + + "colas.color AS color, " + + "clientes.nombre AS client " + + "FROM colas " + + "INNER JOIN numeros_cabecera cabecera ON colas.numero = cabecera.id " + + "INNER JOIN clientes ON cabecera.cliente = clientes.idCliente " + + "WHERE colas.panel = 1 " + + "ORDER BY nombreCola ASC"; + + database.doQuery(query, callback); + }, + storeQueuesFromDB : function(stored_queues, clients, getClientFromName, results){ + // This part runs through the current queue list and update as needed. If the queue isn't found on results + // it will be deleted + var queues = stored_queues || [], + to_delete = []; + + for (var i = 0, len = queues.length; i < len; i ++){ + var queuePosition = arrayHelper.arrayObjectIndexOf(results, queues[i].name, 'name'); + + if (queuePosition === -1) { // If we don't find it in the results may be hidden or deleted + to_delete.push(queues[i].name); + } + else{ + if (queues[i].color !== results[queuePosition].color){ + queues[i].color = results[queuePosition].color; + } + } + } + + // Deleting + arrayHelper.deleteSeveralFromArrayOfObjects(to_delete, queues, 'name'); + + // This part will do the other part, adding queues that aren't present. + for (var j = 0, length = results.length; j < length; j++){ + + var selfQueuePosition = arrayHelper.arrayObjectIndexOf( + queues, + results[j].name, + 'name' + ); + + if (selfQueuePosition === -1){//We have to insert + console.log('New queue detected: %s', results[j].name); + + queues.push( + new Queue( + { + name: results[j].name, + color: results[j].color, + client_name: results[j].client, + client_obj : getClientFromName(clients, results[j].client) + } + ) + ); + } + } + + return queues; + }, + + dispatchCall : function(data){ + var queue = data.queue, + client = queue.client_obj; + + if (queue !== undefined){ + var calls_length = queue.calls.length; + + if (data.type === 'in'){ + // Pushing the call to the queue + queue.calls.push({ + uniqueid : data.uniqueid, + date: new Date() + }); + + // If there is only one call in the queue, the last time of a call is right now! + if (queue.calls.length === 1) { + queue.last_call_time = new Date(); + } + } + else + { + // If it's in the queue + var call_position = arrayHelper.arrayObjectIndexOf(queue.calls, data.uniqueid, 'uniqueid'); + + if (call_position !== -1) { + var call = queue.calls[call_position], + now = new Date(); + + // Client stats stuff + client.storeCall(now, call.date, data.abandoned); + client.sendStatus(); + + arrayHelper.deleteFromArrayOfObjects(queue.calls, data.uniqueid, 'uniqueid'); + + if (queue.calls.length === 0) { + queue.last_call_time = null; + } + else { + queue.last_call_time = queue.calls[0].date; + } + } + else { + console.log('Sorry, we couldn\'t find the call [%s] within %s.', data.uniqueid, queue.name); + } + } + //Emiting by socket + data.io.sockets.emit('callInOrOutQueue', { + type: (data.abandoned === false) ? data.type : 'abandoned', + queue: queue.name, + calls: queue.calls.length, + timeSince: timeHelper.calculateTimeSince(queue.last_call_time) + }); + } + }, + getStatus : function (queues){ + var status = []; + + for (var i = 0, length = queues.length; i < length; i++){ + status.push({ + color : queues[i].color, + last_call_time_diff : timeHelper.calculateTimeSince(queues[i].last_call_time), + name : queues[i].name, + num_calls : queues[i].calls.length + }); + } + + return status; + }, + getQueueFromName : function(queues, queue_name){ + var queuePosition = arrayHelper.arrayObjectIndexOf(queues, queue_name, 'name'); + return (queuePosition !== -1) ? queues[queuePosition] : undefined; + }, + resetData : function(queues) { + for (var i = 0, length = queues.length; i < length; i++){ + queues[i].total_calls = 0; + queues[i].total_abandoned = 0; + queues[i].total_abandoned_before_sla = 0; + queues[i].total_answered = 0; + queues[i].total_answered_before_sla = 0; + } + } +}; + +exports.model = Queue; +exports.utils = utils; \ No newline at end of file diff --git a/models/queues.js b/models/queues.js deleted file mode 100644 index b2a0e8e..0000000 --- a/models/queues.js +++ /dev/null @@ -1,112 +0,0 @@ -function Queues (mysql_client, arrayHelper, timeHelper){ - this.queues = []; - this.arrayHelper = arrayHelper; - this.timeHelper = timeHelper; - var self = this; - - mysql_client.query("SELECT colas.nombre as 'nombreCola', colas.color as 'color' FROM colas, clientes WHERE colas.cliente = clientes.idCliente AND idCliente != 0 AND panel = 1", - function (err, results, fields){ - var queues = []; - if (err){ - throw err; - } else { - for (var i=0, len = results.length; i < len; i++){ - queues.push({ - name: results[i].nombreCola.replace('Cola',''), - originalName: results[i].nombreCola, - color: results[i].color, - calls: [], - lastCallTime: null, - lastCallTimeDiff: null - }); - } - self.queues = queues; - } - }); -} - -Queues.prototype.refetchQueues = function (mysql_client, refetcher){ - var self = this; - var update = false; - mysql_client.query("SELECT colas.nombre as 'nombreCola', colas.color as 'color' FROM colas, clientes WHERE colas.cliente = clientes.idCliente AND idCliente != 0 AND panel = 1", - function (err, results, fields){ - // This part runs through the current queue list and update as needed. If the queue isn't found on results - // it will be deleted - for (var i = 0, len = self.queues.length; i < len; i ++){ - var queuePosition = self.arrayHelper.arrayObjectIndexOf(results, self.queues[i].originalName,'nombreCola'); - if (queuePosition === -1){ // If we don't find it in the results may be hidden or deleted - console.log('La cola %s ha sido eliminada',self.queues[i].name); - self.queues.splice(i,1); // So we have to delete it - len--; - update = true; - } - else{ - if (self.queues[i].color !== results[queuePosition].color){ - self.queues[i].color = results[queuePosition].color; - update = true; - } - } - } - // This part will do the other part, adding queues that aren't present. - for (var j = 0, length = results.length; j < length; j++){ - var selfQueuePosition = self.arrayHelper.arrayObjectIndexOf( - self.queues, - results[j].nombreCola, - 'originalName' - ); - if (selfQueuePosition === -1){//We have to insert - console.log('Nueva cola detectada: %s', results[j].nombreCola.replace('Cola','')); - self.queues.push({ - name: results[j].nombreCola.replace('Cola',''), - originalName: results[j].nombreCola, - color: results[j].color, - calls: [], - lastCallTime: null, - lastCallTimeDiff: null - }); - update = true; - } - } - refetcher.done('queues',update); - }); -}; -/* - * When a call is received by a Queue, this function will be called - * @data.queue : This is the Queue name. - * @data.uniqueid : This is the uniqueid of the call - * @data.type : The type of the event. The call could be 'in'serted or 'out' of the queue - * - * @returns nothing! - */ -Queues.prototype.dispatchCall = function(data){ - var queueName = data.queue.replace('Cola',''); - var queuePosition = this.arrayHelper.arrayObjectIndexOf(this.queues,data.queue.replace('Cola',''), 'name'); - - if (data.type === 'in'){ - this.queues[queuePosition].calls.push({ - uniqueid : data.uniqueid, - date: new Date() - }); - if (this.queues[queuePosition].calls.length === 1) - this.queues[queuePosition].lastCallTime = new Date(); - } - else { - this.arrayHelper.deleteFromArrayOfObjects(this.queues[queuePosition].calls,data.uniqueid,'uniqueid'); - if (this.queues[queuePosition].calls.length === 0) - this.queues[queuePosition].lastCallTime = null; - else - this.queues[queuePosition].lastCallTime = this.queues[queuePosition].calls[0].date; - } - data.io.sockets.emit('callInOrOutQueue', { - type: (typeof data.abandoned === "undefined") ? data.type : 'abandoned', - queue: queueName, - calls: this.queues[queuePosition].calls.length, - timeSince: this.timeHelper.calculateTimeSince(this.queues[queuePosition].lastCallTime) - }); -}; -Queues.prototype.currentStatus = function (){ - for (var i = 0, len = this.queues.length; i < len; i++){ - this.queues[i].lastCallTimeDiff = this.timeHelper.calculateTimeSince(this.queues[i].lastCallTime); - } -}; -module.exports = Queues; \ No newline at end of file diff --git a/modules/app.js b/modules/app.js new file mode 100644 index 0000000..cbbf228 --- /dev/null +++ b/modules/app.js @@ -0,0 +1,429 @@ +module.exports = function App(database, io, async){ + var Agent = require('../models/agent.js'), + Client = require('../models/client.js'), + Queue = require('../models/queue.js'), + arrayHelper = require('../helpers/array.js'), + timeHelper = require('../helpers/time.js'), + Refetcher = require('../modules/refetcher.js'), + Reseter = require('../modules/reseter.js'), + Executor = require('../helpers/executor.js'); + + // Initializing data + this.calls = 0; + this.talking = 0; + this.awaiting = 0; + + this.connected_clients = []; + this.agents = []; + this.clients = []; + this.queues = []; + + this.agent_utils = Agent.utils; + this.client_utils = Client.utils; + this.queue_utils = Queue.utils; + + this.database = database; + this.io = io; + this.async = require('async'); + this.refetcher = new Refetcher(this, database, io); + this.reseter = new Reseter(this); + this.executor = new Executor(); + + this.abandoned_status = [ + 'Out of schedule', + 'Abandoned in ring', + 'Abandoned in message', + 'Abandoned in queue', + 'Voicemail' + ]; + + var self = this; + + // Getting data from the database + this.init = function(callback){ + async.parallel([ + self.getAgents, + self.getClients + ], function(){ + // We need Agents and Clients before we can load Queues + self.getQueues(function(){ + callback.apply(self,[null]); + }); + }); + }; + + /** + * This function get all visible agents and their queues and then store them + * + * @param callback to be called when it finishes + */ + this.getAgents = function(callback){ + Agent.utils.fetchAgents( + self.database, + self.io, + self.agents, + function(agents){ + self.agents = agents; + callback.apply(self,[null]); + } + ); + }; + /** + * This function will get all clients for stats purpouses + * + * @param callback to be called when it finishes + */ + this.getClients = function(callback){ + Client.utils.fetchClients( + self.database, + self.io, + self.clients, + function(clients){ + self.clients = clients; + callback.apply(self,[null]); + } + ); + }; + /** + * This function get all queues and then store them + * + * @param callback to be called when it finishes + */ + this.getQueues = function(callback){ + Queue.utils.fetchQueues( + self.database, + self.io, + self.queues, + self.clients, + Client.utils.getClientFromName, + function(queues){ + self.queues = queues; + callback.apply(self,[null]); + } + ); + }; + /** + * This function will update the primary occupation with the one supplied and will inform all sockets about that + */ + this.updatePrimary = function (){ + if (self.calls < 0) { + self.calls = 0; + } + + if (self.awaiting < 0) { + self.awaiting = 0; + } + + if (self.talking < 0) { + self.talking = 0; + } + + io.sockets.emit('updatePrimary',{ + calls: self.calls, + talking: self.talking, + awaiting: self.awaiting + }); + }; + /** + * This is the function that is called once someone connects to the socket. It gets all data from all sources and + * send back to the socket. + * + * @param socketid that just connected + */ + this.sendCurrentStatus = function (socketid, client_ip){ + var status = {}; + + status.agents = Agent.utils.getStatus(self.agents); + status.queues = Queue.utils.getStatus(self.queues); + + status.calls = self.calls; + status.awaiting = self.awaiting; + status.talking = self.talking; + + self.isAdminUser(client_ip, false, function (is_admin) { + status.is_admin = is_admin; + io.sockets.socket(socketid).emit('currentStatus', status); + }); + }; + /** + * This is the function that is called before sending status to a connected client to determine if this + * user has admin role which enables some advanced functions + * + * @param client_ip is the client IP which is get through request's header + * @param enforced this param avoid query madness because this only will be true the first time the client visits + * @param callback which will be called with the result. + */ + this.isAdminUser = function (client_ip, enforced, callback) { + var client_position = arrayHelper.arrayObjectIndexOf(self.connected_clients, client_ip, 'ip'), + client = self.connected_clients[client_position]; + + if (client && client.is_admin){ + callback(true); + } + else { + if (enforced){ + var query = 'SELECT agentes.grupo, agentes.usuario FROM agentes ' + + 'LEFT JOIN acl_rel_grupos_permisos rel ON agentes.grupo = rel.grupo ' + + 'WHERE agentes.usuario = (SELECT usuario FROM eventos_equipos WHERE ip = \'' + + client_ip + + '\' AND fecha >= CURDATE() ORDER BY fecha DESC LIMIT 1) ' + + 'AND rel.permiso = 6'; + + self.database.doQuery(query, function(results){ + callback(results.length !== 0); + }); + } + else { + callback(false); + } + } + }; + /** + * This funcion will get all rows for some agent + * + * @param array that contains all the rows for all agents + * @param codAgent the agent code we are looking for + * @return {Array} of rows + */ + this.getAgentRows = function (array, codAgent) { + var agent_rows = []; + + // We get all results from that agent + for (var j= 0; j < array.length; j++) + { + if (array[j].codAgente === codAgent) + { + agent_rows.push(array[j]); + } + } + + return agent_rows; + }; + /** + * This function will send a call to an agent. + * + * @param data object that contains all needed properties. + * @from The queue you want to transfer the call from + * @to The agent you want to transfer the call to + */ + this.sendCallToAgent = function (data){ + var queue = Queue.utils.getQueueFromName(self.queues, data.from); + + if (queue){ + var agent_position = arrayHelper.arrayObjectIndexOf(self.agents, data.to, 'codAgente'); + if (agent_position !== -1) { + if (queue.calls.length !== 0){ + // If there is any call to be transfered + var call_id = queue.calls[0].uniqueid, + agent_code = data.to, + command = 'ssh root@170.251.100.9 "/usr/local/bin/transferencia-flaix.sh ' + call_id+ + ' '+agent_code+' '+ + queue.name +'"'; + self.executor.execute(command, function( error , stdout , stderr ){ + console.log( stdout ); + console.log('End of execution'); + if (stdout.indexOf('failed') !== -1){ + console.log('Transfering %s failed!', call_id); + } + }); + } + + } + } + }; + /** + * This function force the unlog of an agent by running a script that calls Asterisk and tell it to unlog he/she + * + * @param data object that contains all needed properties. + * @agent agent that will be unlogged + * + * TODO: This is not ideal, the pannel should connect to Asterisk directly + */ + this.forceUnlogAgent = function(data){ + var agent_position = arrayHelper.arrayObjectIndexOf(self.agents, data.agent, 'codAgente'); + + if (agent_position !== -1) { + self.executor.execute( + 'ssh root@170.251.100.9 "/usr/local/bin/deslogeoforzado.sh ' + data.agent + '"' + ); + } + }; + /** + * This function will load the stats for today. This will act in case you restart the app so the stats keep working + */ + this.loadTodayStats = function(){ + console.log('Let\'s start loading today stats'); + var query = 'SELECT ' + + 'llamadas.uniqueid AS unique_id, ' + + 'llamadas.tipo AS type, ' + + 'clientes.nombre AS client, ' + + 'colas.nombre AS queue, ' + + 'IF(ISNULL(llamadas.fechaInicioCola), fecha , fechaInicioCola) AS start_date, ' + + 'llamadas.fechaAnswered AS answered_date, ' + + 'llamadas.fechaHungup AS hungup_date, ' + + 'IF(ISNULL(fechaAnswered), TIMESTAMPDIFF(SECOND,fechaInicioCola,fechaHungup),TIMESTAMPDIFF(SECOND,fechaInicioCola,fechaAnswered)) AS time_in_queue, '+ + 'llamadas.agente AS agent, ' + + 'llamadas.status AS status ' + + 'FROM llamadas ' + + 'LEFT JOIN colas ON colas.id = llamadas.cola ' + + 'LEFT JOIN numeros_cabecera ON numeros_cabecera.id = colas.numero ' + + 'LEFT JOIN clientes ON clientes.idCliente = numeros_cabecera.cliente ' + + 'WHERE DATE(llamadas.fecha) = CURDATE() ' + + 'ORDER BY llamadas.fecha;'; + + console.log ('Executing MySQL Query >> %s', query); + // TODO: It should be used the encapsulated version of the Database instead of this + + self.database.client.query(query, + function (err, results, fields){ + if (err) + { + throw err; + } + if (results.length > 0) + { + for (var i = 0; i < results.length; i++) + { + var client = Client.utils.getClientFromName(self.clients, results[i].client), + call_queue = Queue.utils.getQueueFromName(self.queues,results[i].queue); + + var agent_position = arrayHelper.arrayObjectIndexOf( + self.agents, + results[i].agent, + 'codAgente' + ); + + var call_agent = (agent_position !== -1) ? self.agents[agent_position] : undefined; + + if (client !== undefined){ + var call_date = results[i].start_date, + answered_date = results[i].answered_date, + hungup_date = results[i].hungup_date, + status = results[i].status, + type = (self.abandoned_status.indexOf(status) === -1) ? 'answered' : 'abandoned'; + + if (results[i].type === 'Incoming'){ + client.total_calls++; + + if (status !== 'Abandoned in message' && status !== 'Out of schedule') { + client.offered_calls++; + + if (type === 'abandoned') { + client.storeCall(hungup_date,call_date,true); + } + else { + client.storeCall(answered_date, call_date,false); + } + } + else { + client.failed_calls++; + } + } + + if (type === 'answered' && hungup_date === null && results[i].agent === null){ + if (call_queue) { + console.log('Call %s assigned to the queue %s in startup.', + results[i].unique_id, + call_queue.name + ); + + self.awaiting++; + self.calls++; + call_queue.calls.push({ + uniqueid : results[i].unique_id, + date : call_date + }); + } + else { + console.log('This call - %s - cannot be allocated', + results[i].unique_id + ); + } + } + else { + if (type === 'answered' && hungup_date === null){ + if (call_agent !== undefined) { + console.log('Call %s assigned to the agent %s in startup.', + results[i].unique_id, + call_agent.codAgente + ); + + if (results[i].type === 'Incoming'){ + self.talking++; + } + + self.calls++; + call_agent.changeStatus({ + start_timer : true, + status : (results[i].type == 'Incoming') ? 4 : 5, + io : self.io, + is_call : true, + queue : (call_queue) ? call_queue.name : null + }); + + call_agent.currentCallTime = answered_date; + } + else { + console.log('This call - %s - cannot be allocated', + results[i].unique_id + ); + } + } + } + } + } + } + } + ); + }; + /** + * This will start the reset process... + */ + this.startReset = function (){ + self.reseter.reset(); + }; + /** + * This function will store clients data on connection + * + * @param client_ip the client ip + * @param admin if it's admin or it's not + */ + this.storeConnectedClient = function(client_ip, admin) { + var client_position = arrayHelper.arrayObjectIndexOf(self.connected_clients, client_ip, 'ip'); + if(client_position === -1){ + var query = 'SELECT usuario FROM eventos_equipos WHERE ip = \'' + + client_ip + + '\' AND fecha >= CURDATE() ORDER BY fecha DESC LIMIT 1'; + + self.database.doQuery(query, function(results){ + var name = undefined; + + if (results.length !== 0){ + name = results[0].usuario; + } + + self.connected_clients.push({ + user_name : name, + is_admin : admin, + ip : client_ip + }); + }); + } + else { + self.connected_clients.push({ + user_name : self.connected_clients[client_position].name, + is_admin : admin, + ip : client_ip + }); + self.connected_clients[client_position].is_admin = admin; + } + }; + /** + * This function will delete the client data using it's IP to find it + * + * @param client_ip + */ + this.deleteConnectedClient = function(client_ip) { + arrayHelper.deleteFromArrayOfObjects(self.connected_clients, client_ip,'ip'); + }; +}; \ No newline at end of file diff --git a/modules/browser_ban.js b/modules/browser_ban.js new file mode 100644 index 0000000..9a12004 --- /dev/null +++ b/modules/browser_ban.js @@ -0,0 +1,23 @@ +/** + * This module is a middleware for express that is executed before any request is processed. In the banned list we can + * add any portion of the Agent String. Currently only Internet Explorer is banned. + */ +var banned = [ + 'MSIE' +]; + +var enabled = true; + +module.exports = function(enabled) { + enabled = (enabled === 'on'); + + return function(req, res, next) { + if (req.headers['user-agent'] !== undefined && + req.headers['user-agent'].indexOf(banned) !== -1 && + req.headers['user-agent'].indexOf('Trident/6.0') === -1) { + console.log(req.headers['user-agent']); + res.end('Browser not compatible'); + } + else { next(); } + } +}; \ No newline at end of file diff --git a/modules/pbx_bugs_solver.js b/modules/pbx_bugs_solver.js new file mode 100644 index 0000000..93a2048 --- /dev/null +++ b/modules/pbx_bugs_solver.js @@ -0,0 +1,33 @@ +/** + * This module is a middleware for express that is executed before any request is processed. + * + * Ideally, this shouldn't be needed but as we are hacking the Dialplan so much, I need to ignore some + * requests to avoid unnecessary work and/or errors. + */ +var keywords = [ + 'ignorame' +]; + +var enabled = true; +var keywords_length = keywords.length; + +module.exports = function(enabled) { + enabled = (enabled === 'on'); + /** + * This function, which is the only exported, will avoid any requests that contains a word stored in the + * keywords array. + */ + return function(req, res, next) { + var found = false; + for (i = 0; i < keywords_length; i++){ + if (req.url.indexOf(keywords[i]) !== -1){ + found = true; + break; + } + } + if (found){ + res.end('Ignoring...'); + } + else { next(); } + } +}; \ No newline at end of file diff --git a/modules/refetcher.js b/modules/refetcher.js new file mode 100644 index 0000000..511b86a --- /dev/null +++ b/modules/refetcher.js @@ -0,0 +1,19 @@ +module.exports = function Refetcher(app, mysql_client, io) { + /** + * This is the function that calls all reseters from different modules + * + * @param necessary indicates if the panel should be restarted. + */ + this.perform = function (){ + var self = this; + + console.log('Starting reload'); + + app.init(function(){ + console.log('All done!'); + setTimeout(function(){ + io.sockets.emit('reload', {}); + }, 5000); + }); + }; +}; \ No newline at end of file diff --git a/modules/reseter.js b/modules/reseter.js new file mode 100644 index 0000000..ea8abb2 --- /dev/null +++ b/modules/reseter.js @@ -0,0 +1,21 @@ +/** + * This function Resets stats for all stuff. + * + * @param app that holds the objects /modules/app.js + */ +module.exports = function Reseter(app){ + /** + * This starts all reset processes + */ + this.reset = function(){ + this.resetClients(); + }; + /** + * This resets client's stats. + */ + this.resetClients = function(){ + for (var i = 0, length = app.clients.length; i < length; i++){ + app.clients[i].resetData(); + } + }; +}; \ No newline at end of file diff --git a/node_modules/socket.io/.gitignore b/node_modules/socket.io/.gitignore deleted file mode 100644 index a70d306..0000000 --- a/node_modules/socket.io/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -.DS_Store -lib-cov -*.seed -*.log -*.csv -*.dat -*.out -*.pid -benchmarks/*.png -node_modules diff --git a/node_modules/socket.io/.npmignore b/node_modules/socket.io/.npmignore deleted file mode 100644 index 39e9864..0000000 --- a/node_modules/socket.io/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -support -test -examples diff --git a/node_modules/socket.io/History.md b/node_modules/socket.io/History.md deleted file mode 100644 index 0c3b4d1..0000000 --- a/node_modules/socket.io/History.md +++ /dev/null @@ -1,187 +0,0 @@ - -0.8.7 / 2011-11-05 -================== - - * Fixed memory leaks in closed clients. - * Fixed memory leaks in namespaces. - * Fixed websocket handling for malformed requests from proxies. [einaros] - * Node 0.6 compatibility. [einaros] [3rd-Eden] - * Adapted tests and examples. - -0.8.6 / 2011-10-27 -================== - - * Added JSON decoding on jsonp-polling transport. - * Fixed README example. - * Major speed optimizations [3rd-Eden] [einaros] [visionmedia] - * Added decode/encode benchmarks [visionmedia] - * Added support for black-listing client sent events. - * Fixed logging options, closes #540 [3rd-Eden] - * Added vary header for gzip [3rd-Eden] - * Properly cleaned up async websocket / flashsocket tests, after patching node-websocket-client - * Patched to properly shut down when a finishClose call is made during connection establishment - * Added support for socket.io version on url and far-future Expires [3rd-Eden] [getify] - * Began IE10 compatibility [einaros] [tbranyen] - * Misc WebSocket fixes [einaros] - * Added UTF8 to respone headers for htmlfile [3rd-Eden] - -0.8.5 / 2011-10-07 -================== - - * Added websocket draft HyBi-16 support. [einaros] - * Fixed websocket continuation bugs. [einaros] - * Fixed flashsocket transport name. - * Fixed websocket tests. - * Ensured `parser#decodePayload` doesn't choke. - * Added http referrer verification to manager verifyOrigin. - * Added access control for cross domain xhr handshakes [3rd-Eden] - * Added support for automatic generation of socket.io files [3rd-Eden] - * Added websocket binary support [einaros] - * Added gzip support for socket.io.js [3rd-Eden] - * Expose socket.transport [3rd-Eden] - * Updated client. - -0.8.4 / 2011-09-06 -================== - - * Client build - -0.8.3 / 2011-09-03 -================== - - * Fixed `\n` parsing for non-JSON packets (fixes #479). - * Fixed parsing of certain unicode characters (fixes #451). - * Fixed transport message packet logging. - * Fixed emission of `error` event resulting in an uncaught exception if unhandled (fixes #476). - * Fixed; allow for falsy values as the configuration value of `log level` (fixes #491). - * Fixed repository URI in `package.json`. Fixes #504. - * Added text/plain content-type to handshake responses [einaros] - * Improved single byte writes [einaros] - * Updated socket.io-flashsocket default port from 843 to 10843 [3rd-Eden] - * Updated client. - -0.8.2 / 2011-08-29 -================== - - * Updated client. - -0.8.1 / 2011-08-29 -================== - - * Fixed utf8 bug in send framing in websocket [einaros] - * Fixed typo in docs [Znarkus] - * Fixed bug in send framing for over 64kB of data in websocket [einaros] - * Corrected ping handling in websocket transport [einaros] - -0.8.0 / 2011-08-28 -================== - - * Updated to work with two-level websocket versioning. [einaros] - * Added hybi07 support. [einaros] - * Added hybi10 support. [einaros] - * Added http referrer verification to manager.js verifyOrigin. [einaors] - -0.7.11 / 2011-08-27 -=================== - - * Updated socket.io-client. - -0.7.10 / 2011-08-27 -=================== - - * Updated socket.io-client. - -0.7.9 / 2011-08-12 -================== - - * Updated socket.io-client. - * Make sure we only do garbage collection when the server we receive is actually run. - -0.7.8 / 2011-08-08 -================== - - * Changed; make sure sio#listen passes options to both HTTP server and socket.io manager. - * Added docs for sio#listen. - * Added options parameter support for Manager constructor. - * Added memory leaks tests and test-leaks Makefile task. - * Removed auto npm-linking from make test. - * Make sure that you can disable heartbeats. [3rd-Eden] - * Fixed rooms memory leak [3rd-Eden] - * Send response once we got all POST data, not immediately [Pita] - * Fixed onLeave behavior with missing clientsk [3rd-Eden] - * Prevent duplicate references in rooms. - * Added alias for `to` to `in` and `in` to `to`. - * Fixed roomClients definition. - * Removed dependency on redis for installation without npm [3rd-Eden] - * Expose path and querystring in handshakeData [3rd-Eden] - -0.7.7 / 2011-07-12 -================== - - * Fixed double dispatch handling with emit to closed clients. - * Added test for emitting to closed clients to prevent regression. - * Fixed race condition in redis test. - * Changed Transport#end instrumentation. - * Leveraged $emit instead of emit internally. - * Made tests faster. - * Fixed double disconnect events. - * Fixed disconnect logic - * Simplified remote events handling in Socket. - * Increased testcase timeout. - * Fixed unknown room emitting (GH-291). [3rd-Eden] - * Fixed `address` in handshakeData. [3rd-Eden] - * Removed transports definition in chat example. - * Fixed room cleanup - * Fixed; make sure the client is cleaned up after booting. - * Make sure to mark the client as non-open if the connection is closed. - * Removed unneeded `buffer` declarations. - * Fixed; make sure to clear socket handlers and subscriptions upon transport close. - -0.7.6 / 2011-06-30 -================== - - * Fixed general dispatching when a client has closed. - -0.7.5 / 2011-06-30 -================== - - * Fixed dispatching to clients that are disconnected. - -0.7.4 / 2011-06-30 -================== - - * Fixed; only clear handlers if they were set. [level09] - -0.7.3 / 2011-06-30 -================== - - * Exposed handshake data to clients. - * Refactored dispatcher interface. - * Changed; Moved id generation method into the manager. - * Added sub-namespace authorization. [3rd-Eden] - * Changed; normalized SocketNamespace local eventing [dvv] - * Changed; Use packet.reason or default to 'packet' [3rd-Eden] - * Changed console.error to console.log. - * Fixed; bind both servers at the same time do that the test never times out. - * Added 304 support. - * Removed `Transport#name` for abstract interface. - * Changed; lazily require http and https module only when needed. [3rd-Eden] - -0.7.2 / 2011-06-22 -================== - - * Make sure to write a packet (of type `noop`) when closing a poll. - This solves a problem with cross-domain requests being flagged as aborted and - reconnection being triggered. - * Added `noop` message type. - -0.7.1 / 2011-06-21 -================== - - * Fixed cross-domain XHR. - * Added CORS test to xhr-polling suite. - -0.7.0 / 2010-06-21 -================== - - * http://socket.io/announcement.html diff --git a/node_modules/socket.io/Makefile b/node_modules/socket.io/Makefile deleted file mode 100644 index 832cba8..0000000 --- a/node_modules/socket.io/Makefile +++ /dev/null @@ -1,31 +0,0 @@ - -ALL_TESTS = $(shell find test/ -name '*.test.js') -ALL_BENCH = $(shell find benchmarks -name '*.bench.js') - -run-tests: - @./node_modules/.bin/expresso \ - -t 3000 \ - -I support \ - --serial \ - $(TESTFLAGS) \ - $(TESTS) - -test: - @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests - -test-cov: - @TESTFLAGS=--cov $(MAKE) test - -test-leaks: - @ls test/leaks/* | xargs node --expose_debug_as=debug --expose_gc - -run-bench: - @node $(PROFILEFLAGS) benchmarks/runner.js - -bench: - @$(MAKE) BENCHMARKS="$(ALL_BENCH)" run-bench - -profile: - @PROFILEFLAGS='--prof --trace-opt --trace-bailout --trace-deopt' $(MAKE) bench - -.PHONY: test bench profile diff --git a/node_modules/socket.io/Readme.md b/node_modules/socket.io/Readme.md deleted file mode 100644 index 779ce57..0000000 --- a/node_modules/socket.io/Readme.md +++ /dev/null @@ -1,343 +0,0 @@ -# Socket.IO - -Socket.IO is a Node.JS project that makes WebSockets and realtime possible in -all browsers. It also enhances WebSockets by providing built-in multiplexing, -horizontal scalability, automatic JSON encoding/decoding, and more. - -## How to Install - - npm install socket.io - -## How to use - -First, require `socket.io`: - -```js -var io = require('socket.io'); -``` - -Next, attach it to a HTTP/HTTPS server. If you're using the fantastic `express` -web framework: - -```js -var app = express.createServer() - , io = io.listen(app); - -app.listen(80); - -io.sockets.on('connection', function (socket) { - socket.emit('news', { hello: 'world' }); - socket.on('my other event', function (data) { - console.log(data); - }); -}); -``` - -Finally, load it from the client side code: - -```html - - -``` - -For more thorough examples, look at the `examples/` directory. - -## Short recipes - -### Sending and receiving events. - -Socket.IO allows you to emit and receive custom events. -Besides `connect`, `message` and `disconnect`, you can emit custom events: - -```js -// note, io.listen() will create a http server for you -var io = require('socket.io').listen(80); - -io.sockets.on('connection', function (socket) { - io.sockets.emit('this', { will: 'be received by everyone' }); - - socket.on('private message', function (from, msg) { - console.log('I received a private message by ', from, ' saying ', msg); - }); - - socket.on('disconnect', function () { - io.sockets.emit('user disconnected'); - }); -}); -``` - -### Storing data associated to a client - -Sometimes it's necessary to store data associated with a client that's -necessary for the duration of the session. - -#### Server side - -```js -var io = require('socket.io').listen(80); - -io.sockets.on('connection', function (socket) { - socket.on('set nickname', function (name) { - socket.set('nickname', name, function () { socket.emit('ready'); }); - }); - - socket.on('msg', function () { - socket.get('nickname', function (err, name) { - console.log('Chat message by ', name); - }); - }); -}); -``` - -#### Client side - -```html - -``` - -### Restricting yourself to a namespace - -If you have control over all the messages and events emitted for a particular -application, using the default `/` namespace works. - -If you want to leverage 3rd-party code, or produce code to share with others, -socket.io provides a way of namespacing a `socket`. - -This has the benefit of `multiplexing` a single connection. Instead of -socket.io using two `WebSocket` connections, it'll use one. - -The following example defines a socket that listens on '/chat' and one for -'/news': - -#### Server side - -```js -var io = require('socket.io').listen(80); - -var chat = io - .of('/chat'); - .on('connection', function (socket) { - socket.emit('a message', { that: 'only', '/chat': 'will get' }); - chat.emit('a message', { everyone: 'in', '/chat': 'will get' }); - }); - -var news = io - .of('/news'); - .on('connection', function (socket) { - socket.emit('item', { news: 'item' }); - }); -``` - -#### Client side: - -```html - -``` - -### Sending volatile messages. - -Sometimes certain messages can be dropped. Let's say you have an app that -shows realtime tweets for the keyword `bieber`. - -If a certain client is not ready to receive messages (because of network slowness -or other issues, or because he's connected through long polling and is in the -middle of a request-response cycle), if he doesn't receive ALL the tweets related -to bieber your application won't suffer. - -In that case, you might want to send those messages as volatile messages. - -#### Server side - -```js -var io = require('socket.io').listen(80); - -io.sockets.on('connection', function (socket) { - var tweets = setInterval(function () { - getBieberTweet(function (tweet) { - socket.volatile.emit('bieber tweet', tweet); - }); - }, 100); - - socket.on('disconnect', function () { - clearInterval(tweets); - }); -}); -``` - -#### Client side - -In the client side, messages are received the same way whether they're volatile -or not. - -### Getting acknowledgements - -Sometimes, you might want to get a callback when the client confirmed the message -reception. - -To do this, simply pass a function as the last parameter of `.send` or `.emit`. -What's more, when you use `.emit`, the acknowledgement is done by you, which -means you can also pass data along: - -#### Server side - -```js -var io = require('socket.io').listen(80); - -io.sockets.on('connection', function (socket) { - socket.on('ferret', function (name, fn) { - fn('woot'); - }); -}); -``` - -#### Client side - -```html - -``` - -### Broadcasting messages - -To broadcast, simply add a `broadcast` flag to `emit` and `send` method calls. -Broadcasting means sending a message to everyone else except for the socket -that starts it. - -#### Server side - -```js -var io = require('socket.io').listen(80); - -io.sockets.on('connection', function (socket) { - socket.broadcast.emit('user connected'); - socket.broadcast.json.send({ a: 'message' }); -}); -``` - -### Rooms - -Sometimes you want to put certain sockets in the same room, so that it's easy -to broadcast to all of them together. - -Think of this as built-in channels for sockets. Sockets `join` and `leave` -rooms in each socket. - -#### Server side - -```js -var io = require('socket.io').listen(80); - -io.sockets.on('connection', function (socket) { - socket.join('justin bieber fans'); - socket.broadcast.to('justin bieber fans').emit('new fan'); - io.sockets.in('rammstein fans').emit('new non-fan'); -}); -``` - -### Using it just as a cross-browser WebSocket - -If you just want the WebSocket semantics, you can do that too. -Simply leverage `send` and listen on the `message` event: - -#### Server side - -```js -var io = require('socket.io').listen(80); - -io.sockets.on('connection', function (socket) { - socket.on('message', function () { }); - socket.on('disconnect', function () { }); -}); -``` - -#### Client side - -```html - -``` - -### Changing configuration - -Configuration in socket.io is TJ-style: - -#### Server side - -```js -var io = require('socket.io').listen(80); - -io.configure(function () { - io.set('transports', ['websocket', 'flashsocket', 'xhr-polling']); -}); - -io.configure('development', function () { - io.set('transports', ['websocket', 'xhr-polling']); - io.enable('log'); -}); -``` - -## License - -(The MIT License) - -Copyright (c) 2011 Guillermo Rauch <guillermo@learnboost.com> - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/socket.io/benchmarks/decode.bench.js b/node_modules/socket.io/benchmarks/decode.bench.js deleted file mode 100644 index 4855d80..0000000 --- a/node_modules/socket.io/benchmarks/decode.bench.js +++ /dev/null @@ -1,64 +0,0 @@ - -/** - * Module dependencies. - */ - -var benchmark = require('benchmark') - , colors = require('colors') - , io = require('../') - , parser = io.parser - , suite = new benchmark.Suite('Decode packet'); - -suite.add('string', function () { - parser.decodePacket('4:::"2"'); -}); - -suite.add('event', function () { - parser.decodePacket('5:::{"name":"woot"}'); -}); - -suite.add('event+ack', function () { - parser.decodePacket('5:1+::{"name":"tobi"}'); -}); - -suite.add('event+data', function () { - parser.decodePacket('5:::{"name":"edwald","args":[{"a": "b"},2,"3"]}'); -}); - -suite.add('heartbeat', function () { - parser.decodePacket('2:::'); -}); - -suite.add('error', function () { - parser.decodePacket('7:::2+0'); -}); - -var payload = parser.encodePayload([ - parser.encodePacket({ type: 'message', data: '5', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) -]); - -suite.add('payload', function () { - parser.decodePayload(payload); -}); - -suite.on('cycle', function (bench, details) { - console.log('\n' + suite.name.grey, details.name.white.bold); - console.log([ - details.hz.toFixed(2).cyan + ' ops/sec'.grey - , details.count.toString().white + ' times executed'.grey - , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey - , - ].join(', '.grey)); -}); - -if (!module.parent) { - suite.run(); -} else { - module.exports = suite; -} diff --git a/node_modules/socket.io/benchmarks/encode.bench.js b/node_modules/socket.io/benchmarks/encode.bench.js deleted file mode 100644 index 5037702..0000000 --- a/node_modules/socket.io/benchmarks/encode.bench.js +++ /dev/null @@ -1,90 +0,0 @@ - -/** - * Module dependencies. - */ - -var benchmark = require('benchmark') - , colors = require('colors') - , io = require('../') - , parser = io.parser - , suite = new benchmark.Suite('Encode packet'); - -suite.add('string', function () { - parser.encodePacket({ - type: 'json' - , endpoint: '' - , data: '2' - }); -}); - -suite.add('event', function () { - parser.encodePacket({ - type: 'event' - , name: 'woot' - , endpoint: '' - , args: [] - }); -}); - -suite.add('event+ack', function () { - parser.encodePacket({ - type: 'json' - , id: 1 - , ack: 'data' - , endpoint: '' - , data: { a: 'b' } - }); -}); - -suite.add('event+data', function () { - parser.encodePacket({ - type: 'event' - , name: 'edwald' - , endpoint: '' - , args: [{a: 'b'}, 2, '3'] - }); -}); - -suite.add('heartbeat', function () { - parser.encodePacket({ - type: 'heartbeat' - , endpoint: '' - }) -}); - -suite.add('error', function () { - parser.encodePacket({ - type: 'error' - , reason: 'unauthorized' - , advice: 'reconnect' - , endpoint: '' - }) -}) - -suite.add('payload', function () { - parser.encodePayload([ - parser.encodePacket({ type: 'message', data: '5', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) - ]); -}); - -suite.on('cycle', function (bench, details) { - console.log('\n' + suite.name.grey, details.name.white.bold); - console.log([ - details.hz.toFixed(2).cyan + ' ops/sec'.grey - , details.count.toString().white + ' times executed'.grey - , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey - , - ].join(', '.grey)); -}); - -if (!module.parent) { - suite.run(); -} else { - module.exports = suite; -} diff --git a/node_modules/socket.io/benchmarks/runner.js b/node_modules/socket.io/benchmarks/runner.js deleted file mode 100644 index 81e55ca..0000000 --- a/node_modules/socket.io/benchmarks/runner.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Benchmark runner dependencies - */ - -var colors = require('colors') - , path = require('path'); - -/** - * Find all the benchmarks - */ - -var benchmarks_files = process.env.BENCHMARKS.split(' ') - , all = [].concat(benchmarks_files) - , first = all.shift() - , benchmarks = {}; - -// find the benchmarks and load them all in our obj -benchmarks_files.forEach(function (file) { - benchmarks[file] = require(path.join(__dirname, '..', file)); -}); - -// setup the complete listeners -benchmarks_files.forEach(function (file) { - var benchmark = benchmarks[file] - , next_file = all.shift() - , next = benchmarks[next_file]; - - /** - * Generate a oncomplete function for the tests, either we are done or we - * have more benchmarks to process. - */ - - function complete () { - if (!next) { - console.log( - '\n\nBenchmark completed in'.grey - , (Date.now() - start).toString().green + ' ms'.grey - ); - } else { - console.log('\nStarting benchmark '.grey + next_file.yellow); - next.run(); - } - } - - // attach the listener - benchmark.on('complete', complete); -}); - -/** - * Start the benchmark - */ - -var start = Date.now(); -console.log('Starting benchmark '.grey + first.yellow); -benchmarks[first].run(); diff --git a/node_modules/socket.io/examples/chat/app.js b/node_modules/socket.io/examples/chat/app.js deleted file mode 100644 index bdfad2a..0000000 --- a/node_modules/socket.io/examples/chat/app.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Module dependencies. - */ - -var express = require('express') - , stylus = require('stylus') - , nib = require('nib') - , sio = require('../../lib/socket.io'); - -/** - * App. - */ - -var app = express.createServer(); - -/** - * App configuration. - */ - -app.configure(function () { - app.use(stylus.middleware({ src: __dirname + '/public', compile: compile })); - app.use(express.static(__dirname + '/public')); - app.set('views', __dirname); - app.set('view engine', 'jade'); - - function compile (str, path) { - return stylus(str) - .set('filename', path) - .use(nib()); - }; -}); - -/** - * App routes. - */ - -app.get('/', function (req, res) { - res.render('index', { layout: false }); -}); - -/** - * App listen. - */ - -app.listen(3000, function () { - var addr = app.address(); - console.log(' app listening on http://' + addr.address + ':' + addr.port); -}); - -/** - * Socket.IO server (single process only) - */ - -var io = sio.listen(app) - , nicknames = {}; - -io.sockets.on('connection', function (socket) { - socket.on('user message', function (msg) { - socket.broadcast.emit('user message', socket.nickname, msg); - }); - - socket.on('nickname', function (nick, fn) { - if (nicknames[nick]) { - fn(true); - } else { - fn(false); - nicknames[nick] = socket.nickname = nick; - socket.broadcast.emit('announcement', nick + ' connected'); - io.sockets.emit('nicknames', nicknames); - } - }); - - socket.on('disconnect', function () { - if (!socket.nickname) return; - - delete nicknames[socket.nickname]; - socket.broadcast.emit('announcement', socket.nickname + ' disconnected'); - socket.broadcast.emit('nicknames', nicknames); - }); -}); diff --git a/node_modules/socket.io/examples/chat/index.jade b/node_modules/socket.io/examples/chat/index.jade deleted file mode 100644 index 0633d8b..0000000 --- a/node_modules/socket.io/examples/chat/index.jade +++ /dev/null @@ -1,83 +0,0 @@ -doctype 5 -html - head - link(href='/stylesheets/style.css', rel='stylesheet') - script(src='http://code.jquery.com/jquery-1.6.1.min.js') - script(src='/socket.io/socket.io.js') - script - // socket.io specific code - var socket = io.connect(); - - socket.on('connect', function () { - $('#chat').addClass('connected'); - }); - - socket.on('announcement', function (msg) { - $('#lines').append($('

').append($('').text(msg))); - }); - - socket.on('nicknames', function (nicknames) { - $('#nicknames').empty().append($('Online: ')); - for (var i in nicknames) { - $('#nicknames').append($('').text(nicknames[i])); - } - }); - - socket.on('user message', message); - socket.on('reconnect', function () { - $('#lines').remove(); - message('System', 'Reconnected to the server'); - }); - - socket.on('reconnecting', function () { - message('System', 'Attempting to re-connect to the server'); - }); - - socket.on('error', function (e) { - message('System', e ? e : 'A unknown error occurred'); - }); - - function message (from, msg) { - $('#lines').append($('

').append($('').text(from), msg)); - } - - // dom manipulation - $(function () { - $('#set-nickname').submit(function (ev) { - socket.emit('nickname', $('#nick').val(), function (set) { - if (!set) { - clear(); - return $('#chat').addClass('nickname-set'); - } - $('#nickname-err').css('visibility', 'visible'); - }); - return false; - }); - - $('#send-message').submit(function () { - message('me', $('#message').val()); - socket.emit('user message', $('#message').val()); - clear(); - $('#lines').get(0).scrollTop = 10000000; - return false; - }); - - function clear () { - $('#message').val('').focus(); - }; - }); - body - #chat - #nickname - form.wrap#set-nickname - p Please type in your nickname and press enter. - input#nick - p#nickname-err Nickname already in use - #connecting - .wrap Connecting to socket.io server - #messages - #nicknames - #lines - form#send-message - input#message - button Send diff --git a/node_modules/socket.io/examples/chat/package.json b/node_modules/socket.io/examples/chat/package.json deleted file mode 100644 index 8b3338a..0000000 --- a/node_modules/socket.io/examples/chat/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "chat.io" - , "description": "example chat application with socket.io" - , "version": "0.0.1" - , "dependencies": { - "express": "2.5.0" - , "jade": "0.16.4" - , "stylus": "0.19.0" - , "nib": "0.2.0" - } -} diff --git a/node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl b/node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl deleted file mode 100644 index fb5644c..0000000 --- a/node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl +++ /dev/null @@ -1,96 +0,0 @@ -border-radius(n) - -webkit-border-radius n - -moz-border-radius n - border-radius n - -// replace str with val - -replace(expr, str, val) - expr = clone(expr) - for e, i in expr - if str == e - expr[i] = val - expr - -// normalize gradient point (webkit) - -grad-point(pos) - if length(pos) == 1 - return left pos if pos in (top bottom) - return pos top if pos in (left right) - else if pos[0] in (top bottom) - pos[1] pos[0] - else - pos - -// implicit color stop position - -pos-in-stops(i, stops) - len = length(stops) - if len - 1 == i - 100% - else if i - unit(i / len * 100, '%') - else - 0% - -// normalize color stops -// - (color pos) -> (pos color) -// - (color) -> (implied-pos color) - -normalize-stops(stops) - stops = clone(stops) - for stop, i in stops - if length(stop) == 1 - color = stop[0] - stop[0] = pos-in-stops(i, stops) - stop[1] = color - else if typeof(stop[1]) == 'unit' - pos = stop[1] - stop[1] = stop[0] - stop[0] = pos - stops - -// join color stops with the given translation function - -join-stops(stops, translate) - str = '' - len = length(stops) - for stop, i in stops - str += ', ' if i - pos = stop[0] - color = stop[1] - str += translate(color, pos) - unquote(str) - -// webkit translation function - -webkit-stop(color, pos) - s('color-stop(%d, %s)', pos / 100, color) - -// mozilla translation function - -moz-stop(color, pos) - s('%s %s', color, pos) - -// create a linear gradient with the given start -// position, followed by color stops - -linear-gradient(start, stops...) - error('color stops required') unless length(stops) - prop = current-property[0] - val = current-property[1] - stops = normalize-stops(stops) - - // webkit - end = grad-point(opposite-position(start)) - webkit = s('-webkit-gradient(linear, %s, %s, %s)', grad-point(start), end, join-stops(stops, webkit-stop)) - add-property(prop, replace(val, '__CALL__', webkit)) - - // moz - stops = join-stops(stops, moz-stop) - moz = s('-moz-linear-gradient(%s, %s)', start, stops) - add-property(prop, replace(val, '__CALL__', moz)) - - // literal - s('linear-gradient(%s, %s)', start, stops) diff --git a/node_modules/socket.io/examples/chat/public/stylesheets/style.css b/node_modules/socket.io/examples/chat/public/stylesheets/style.css deleted file mode 100644 index 42cf98f..0000000 --- a/node_modules/socket.io/examples/chat/public/stylesheets/style.css +++ /dev/null @@ -1,188 +0,0 @@ -#chat, -#nickname, -#messages { - width: 600px; -} -#chat { - position: relative; - border: 1px solid #ccc; -} -#nickname, -#connecting { - position: absolute; - height: 410px; - z-index: 100; - left: 0; - top: 0; - background: #fff; - text-align: center; - width: 600px; - font: 15px Georgia; - color: #666; - display: block; -} -#nickname .wrap, -#connecting .wrap { - padding-top: 150px; -} -#nickname input { - border: 1px solid #ccc; - padding: 10px; -} -#nickname input:focus { - border-color: #999; - outline: 0; -} -#nickname #nickname-err { - color: #8b0000; - font-size: 12px; - visibility: hidden; -} -.connected #connecting { - display: none; -} -.nickname-set #nickname { - display: none; -} -#messages { - height: 380px; - background: #eee; -} -#messages em { - text-shadow: 0 1px 0 #fff; - color: #999; -} -#messages p { - padding: 0; - margin: 0; - font: 12px Helvetica, Arial; - padding: 5px 10px; -} -#messages p b { - display: inline-block; - padding-right: 10px; -} -#messages p:nth-child(even) { - background: #fafafa; -} -#messages #nicknames { - background: #ccc; - padding: 2px 4px 4px; - font: 11px Helvetica; -} -#messages #nicknames span { - color: #000; -} -#messages #nicknames b { - display: inline-block; - color: #fff; - background: #999; - padding: 3px 6px; - margin-right: 5px; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - text-shadow: 0 1px 0 #666; -} -#messages #lines { - height: 355px; - overflow: auto; - overflow-x: hidden; - overflow-y: auto; -} -#messages #lines::-webkit-scrollbar { - width: 6px; - height: 6px; -} -#messages #lines::-webkit-scrollbar-button:start:decrement, -#messages #lines ::-webkit-scrollbar-button:end:increment { - display: block; - height: 10px; -} -#messages #lines::-webkit-scrollbar-button:vertical:increment { - background-color: #fff; -} -#messages #lines::-webkit-scrollbar-track-piece { - background-color: #fff; - -webkit-border-radius: 3px; -} -#messages #lines::-webkit-scrollbar-thumb:vertical { - height: 50px; - background-color: #ccc; - -webkit-border-radius: 3px; -} -#messages #lines::-webkit-scrollbar-thumb:horizontal { - width: 50px; - background-color: #fff; - -webkit-border-radius: 3px; -} -#send-message { - background: #fff; - position: relative; -} -#send-message input { - border: none; - height: 30px; - padding: 0 10px; - line-height: 30px; - vertical-align: middle; - width: 580px; -} -#send-message input:focus { - outline: 0; -} -#send-message button { - position: absolute; - top: 5px; - right: 5px; -} -button { - margin: 0; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - display: inline-block; - text-decoration: none; - background: #43a1f7; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #43a1f7), color-stop(1, #377ad0)); - background: -webkit-linear-gradient(top, #43a1f7 0%, #377ad0 100%); - background: -moz-linear-gradient(top, #43a1f7 0%, #377ad0 100%); - background: linear-gradient(top, #43a1f7 0%, #377ad0 100%); - border: 1px solid #2e70c4; - -webkit-border-radius: 16px; - -moz-border-radius: 16px; - border-radius: 16px; - color: #fff; - font-family: "lucida grande", sans-serif; - font-size: 11px; - font-weight: normal; - line-height: 1; - padding: 3px 10px 5px 10px; - text-align: center; - text-shadow: 0 -1px 1px #2d6dc0; -} -button:hover, -button.hover { - background: darker; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #43a1f7), color-stop(1, #2e70c4)); - background: -webkit-linear-gradient(top, #43a1f7 0%, #2e70c4 100%); - background: -moz-linear-gradient(top, #43a1f7 0%, #2e70c4 100%); - background: linear-gradient(top, #43a1f7 0%, #2e70c4 100%); - border: 1px solid #2e70c4; - cursor: pointer; - text-shadow: 0 -1px 1px #2c6bbb; -} -button:active, -button.active { - background: #2e70c4; - border: 1px solid #2e70c4; - border-bottom: 1px solid #2861aa; - text-shadow: 0 -1px 1px #2b67b5; -} -button:focus, -button.focus { - outline: none; - -webkit-box-shadow: 0 1px 0 0 rgba(255,255,255,0.4), 0 0 4px 0 #377ad0; - -moz-box-shadow: 0 1px 0 0 rgba(255,255,255,0.4), 0 0 4px 0 #377ad0; - box-shadow: 0 1px 0 0 rgba(255,255,255,0.4), 0 0 4px 0 #377ad0; -} diff --git a/node_modules/socket.io/examples/chat/public/stylesheets/style.styl b/node_modules/socket.io/examples/chat/public/stylesheets/style.styl deleted file mode 100644 index 6289c94..0000000 --- a/node_modules/socket.io/examples/chat/public/stylesheets/style.styl +++ /dev/null @@ -1,118 +0,0 @@ -@import 'nib' - -#chat, #nickname, #messages - width 600px - -#chat - position relative - border 1px solid #ccc - -#nickname, #connecting - position absolute - height 410px - z-index 100 - left 0 - top 0 - background #fff - text-align center - width 600px - font 15px Georgia - color #666 - display block - .wrap - padding-top 150px - -#nickname - input - border 1px solid #ccc - padding 10px - &:focus - border-color #999 - outline 0 - #nickname-err - color darkred - font-size 12px - visibility hidden - -.connected - #connecting - display none - -.nickname-set - #nickname - display none - -#messages - height 380px - background #eee - em - text-shadow 0 1px 0 #fff - color #999 - p - padding 0 - margin 0 - font 12px Helvetica, Arial - padding 5px 10px - b - display inline-block - padding-right 10px - p:nth-child(even) - background #fafafa - #nicknames - background #ccc - padding 2px 4px 4px - font 11px Helvetica - span - color black - b - display inline-block - color #fff - background #999 - padding 3px 6px - margin-right 5px - border-radius 10px - text-shadow 0 1px 0 #666 - #lines - height 355px - overflow auto - overflow-x hidden - overflow-y auto - &::-webkit-scrollbar - width 6px - height 6px - &::-webkit-scrollbar-button:start:decrement, ::-webkit-scrollbar-button:end:increment - display block - height 10px - &::-webkit-scrollbar-button:vertical:increment - background-color #fff - &::-webkit-scrollbar-track-piece - background-color #fff - -webkit-border-radius 3px - &::-webkit-scrollbar-thumb:vertical - height 50px - background-color #ccc - -webkit-border-radius 3px - &::-webkit-scrollbar-thumb:horizontal - width 50px - background-color #fff - -webkit-border-radius 3px - -#send-message - background #fff - position relative - input - border none - height 30px - padding 0 10px - line-height 30px - vertical-align middle - width 580px - &:focus - outline 0 - button - position absolute - top 5px - right 5px - -button - download-button() diff --git a/node_modules/socket.io/examples/irc-output/app.js b/node_modules/socket.io/examples/irc-output/app.js deleted file mode 100644 index 784886a..0000000 --- a/node_modules/socket.io/examples/irc-output/app.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Module dependencies. - */ - -var express = require('express') - , stylus = require('stylus') - , nib = require('nib') - , sio = require('../../lib/socket.io') - , irc = require('./irc'); - -/** - * App. - */ - -var app = express.createServer(); - -/** - * App configuration. - */ - -app.configure(function () { - app.use(stylus.middleware({ src: __dirname + '/public', compile: compile })) - app.use(express.static(__dirname + '/public')); - app.set('views', __dirname); - app.set('view engine', 'jade'); - - function compile (str, path) { - return stylus(str) - .set('filename', path) - .use(nib()); - }; -}); - -/** - * App routes. - */ - -app.get('/', function (req, res) { - res.render('index', { layout: false }); -}); - -/** - * App listen. - */ - -app.listen(3000, function () { - var addr = app.address(); - console.log(' app listening on http://' + addr.address + ':' + addr.port); -}); - -/** - * Socket.IO server - */ - -var io = sio.listen(app) - -/** - * Connect to IRC. - */ - -var client = new irc.Client('irc.freenode.net', 6667); -client.connect('socketio\\test\\' + String(Math.random()).substr(-3)); -client.on('001', function () { - this.send('JOIN', '#node.js'); -}); -client.on('PART', function (prefix) { - io.sockets.emit('announcement', irc.user(prefix) + ' left the channel'); -}); -client.on('JOIN', function (prefix) { - io.sockets.emit('announcement', irc.user(prefix) + ' joined the channel'); -}); -client.on('PRIVMSG', function (prefix, channel, text) { - io.sockets.emit('irc message', irc.user(prefix), text); -}); diff --git a/node_modules/socket.io/examples/irc-output/index.jade b/node_modules/socket.io/examples/irc-output/index.jade deleted file mode 100644 index 5468632..0000000 --- a/node_modules/socket.io/examples/irc-output/index.jade +++ /dev/null @@ -1,28 +0,0 @@ -doctype 5 -html - head - link(href='/stylesheets/style.css', rel='stylesheet') - script(src='http://code.jquery.com/jquery-1.6.1.min.js') - script(src='/socket.io/socket.io.js') - script - var socket = io.connect(); - - socket.on('connect', function () { - $('#irc').addClass('connected'); - }); - - socket.on('announcement', function (msg) { - $('#messages').append($('

').append($('').text(msg))); - $('#messages').get(0).scrollTop = 10000000; - }); - - socket.on('irc message', function (user, msg) { - $('#messages').append($('

').append($('').text(user), msg)); - $('#messages').get(0).scrollTop = 10000000; - }); - body - h2 Node.JS IRC - #irc - #connecting - .wrap Connecting to socket.io server - #messages diff --git a/node_modules/socket.io/examples/irc-output/irc.js b/node_modules/socket.io/examples/irc-output/irc.js deleted file mode 100644 index cc679d5..0000000 --- a/node_modules/socket.io/examples/irc-output/irc.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * From https://github.com/felixge/nodelog/ - */ - -var sys = require('util'); -var tcp = require('net'); -var irc = exports; - -function bind(fn, scope) { - var bindArgs = Array.prototype.slice.call(arguments); - bindArgs.shift(); - bindArgs.shift(); - - return function() { - var args = Array.prototype.slice.call(arguments); - fn.apply(scope, bindArgs.concat(args)); - }; -} - -function each(set, iterator) { - for (var i = 0; i < set.length; i++) { - var r = iterator(set[i], i); - if (r === false) { - return; - } - } -} - -var Client = irc.Client = function(host, port) { - this.host = host || 'localhost'; - this.port = port || 6667; - - this.connection = null; - this.buffer = ''; - this.encoding = 'utf8'; - this.timeout = 10 * 60 * 60 * 1000; - - this.nick = null; - this.user = null; - this.real = null; -} -sys.inherits(Client, process.EventEmitter); - -Client.prototype.connect = function(nick, user, real) { - var connection = tcp.createConnection(this.port, this.host); - connection.setEncoding(this.encoding); - connection.setTimeout(this.timeout); - connection.addListener('connect', bind(this.onConnect, this)); - connection.addListener('data', bind(this.onReceive, this)); - connection.addListener('end', bind(this.onEof, this)); - connection.addListener('timeout', bind(this.onTimeout, this)); - connection.addListener('close', bind(this.onClose, this)); - - this.nick = nick; - this.user = user || 'guest'; - this.real = real || 'Guest'; - - this.connection = connection; -}; - -Client.prototype.disconnect = function(why) { - if (this.connection.readyState !== 'closed') { - this.connection.close(); - sys.puts('disconnected (reason: '+why+')'); - this.emit('DISCONNECT', why); - } -}; - -Client.prototype.send = function(arg1) { - if (this.connection.readyState !== 'open') { - return this.disconnect('cannot send with readyState: '+this.connection.readyState); - } - - var message = []; - for (var i = 0; i< arguments.length; i++) { - if (arguments[i]) { - message.push(arguments[i]); - } - } - message = message.join(' '); - - sys.puts('> '+message); - message = message + "\r\n"; - this.connection.write(message, this.encoding); -}; - -Client.prototype.parse = function(message) { - var match = message.match(/(?:(:[^\s]+) )?([^\s]+) (.+)/); - var parsed = { - prefix: match[1], - command: match[2] - }; - - var params = match[3].match(/(.*?) ?:(.*)/); - if (params) { - // Params before : - params[1] = (params[1]) - ? params[1].split(' ') - : []; - // Rest after : - params[2] = params[2] - ? [params[2]] - : []; - - params = params[1].concat(params[2]); - } else { - params = match[3].split(' '); - } - - parsed.params = params; - return parsed; -}; - -Client.prototype.onConnect = function() { - this.send('NICK', this.nick); - this.send('USER', this.user, '0', '*', ':'+this.real); -}; - -Client.prototype.onReceive = function(chunk) { - this.buffer = this.buffer + chunk; - - while (this.buffer) { - var offset = this.buffer.indexOf("\r\n"); - if (offset < 0) { - return; - } - - var message = this.buffer.substr(0, offset); - this.buffer = this.buffer.substr(offset + 2); - sys.puts('< '+message); - - message = this.parse(message); - - this.emit.apply(this, [message.command, message.prefix].concat(message.params)); - - if (message !== false) { - this.onMessage(message); - } - } -}; - -Client.prototype.onMessage = function(message) { - switch (message.command) { - case 'PING': - this.send('PONG', ':'+message.params[0]); - break; - } -}; - -Client.prototype.onEof = function() { - this.disconnect('eof'); -}; - -Client.prototype.onTimeout = function() { - this.disconnect('timeout'); -}; - -Client.prototype.onClose = function() { - this.disconnect('close'); -}; - -exports.user = function(prefix) { - return prefix.match(/:([^!]+)!/)[1] -}; diff --git a/node_modules/socket.io/examples/irc-output/package.json b/node_modules/socket.io/examples/irc-output/package.json deleted file mode 100644 index 0987651..0000000 --- a/node_modules/socket.io/examples/irc-output/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "socket.io-irc" - , "version": "0.0.1" - , "dependencies": { - "express": "2.5.0" - , "jade": "0.16.4" - , "stylus": "0.19.0" - , "nib": "0.2.0" - } -} diff --git a/node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl b/node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl deleted file mode 100644 index 2713512..0000000 --- a/node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl +++ /dev/null @@ -1,69 +0,0 @@ -@import 'nib' - -h2 - font bold 18px Helvetica Neue, Arial - -#irc, #messages - width 600px - -#irc - position relative - border 1px solid #ccc - -#connecting - position absolute - height 410px - z-index 100 - left 0 - top 0 - background #fff - text-align center - width 600px - font 15px Georgia - color #666 - display block - .wrap - padding-top 150px - -.connected - #connecting - display none - -#messages - height 380px - background #eee - overflow auto - overflow-x hidden - overflow-y auto - &::-webkit-scrollbar - width 6px - height 6px - &::-webkit-scrollbar-button:start:decrement, ::-webkit-scrollbar-button:end:increment - display block - height 10px - &::-webkit-scrollbar-button:vertical:increment - background-color #fff - &::-webkit-scrollbar-track-piece - background-color #fff - -webkit-border-radius 3px - &::-webkit-scrollbar-thumb:vertical - height 50px - background-color #ccc - -webkit-border-radius 3px - &::-webkit-scrollbar-thumb:horizontal - width 50px - background-color #fff - -webkit-border-radius 3px - em - text-shadow 0 1px 0 #fff - color #999 - p - padding 0 - margin 0 - font 12px Helvetica, Arial - padding 5px 10px - b - display inline-block - padding-right 10px - p:nth-child(even) - background #fafafa diff --git a/node_modules/socket.io/index.js b/node_modules/socket.io/index.js deleted file mode 100644 index cc00c10..0000000 --- a/node_modules/socket.io/index.js +++ /dev/null @@ -1,8 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -module.exports = require('./lib/socket.io'); diff --git a/node_modules/socket.io/lib/logger.js b/node_modules/socket.io/lib/logger.js deleted file mode 100644 index 49d02c9..0000000 --- a/node_modules/socket.io/lib/logger.js +++ /dev/null @@ -1,97 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var util = require('./util') - , toArray = util.toArray; - -/** - * Log levels. - */ - -var levels = [ - 'error' - , 'warn' - , 'info' - , 'debug' -]; - -/** - * Colors for log levels. - */ - -var colors = [ - 31 - , 33 - , 36 - , 90 -]; - -/** - * Pads the nice output to the longest log level. - */ - -function pad (str) { - var max = 0; - - for (var i = 0, l = levels.length; i < l; i++) - max = Math.max(max, levels[i].length); - - if (str.length < max) - return str + new Array(max - str.length + 1).join(' '); - - return str; -}; - -/** - * Logger (console). - * - * @api public - */ - -var Logger = module.exports = function (opts) { - opts = opts || {} - this.colors = false !== opts.colors; - this.level = 3; - this.enabled = true; -}; - -/** - * Log method. - * - * @api public - */ - -Logger.prototype.log = function (type) { - var index = levels.indexOf(type); - - if (index > this.level || !this.enabled) - return this; - - console.log.apply( - console - , [this.colors - ? ' \033[' + colors[index] + 'm' + pad(type) + ' -\033[39m' - : type + ':' - ].concat(toArray(arguments).slice(1)) - ); - - return this; -}; - -/** - * Generate methods. - */ - -levels.forEach(function (name) { - Logger.prototype[name] = function () { - this.log.apply(this, [name].concat(toArray(arguments))); - }; -}); diff --git a/node_modules/socket.io/lib/manager.js b/node_modules/socket.io/lib/manager.js deleted file mode 100644 index 8d637fb..0000000 --- a/node_modules/socket.io/lib/manager.js +++ /dev/null @@ -1,961 +0,0 @@ -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var fs = require('fs') - , url = require('url') - , util = require('./util') - , store = require('./store') - , client = require('socket.io-client') - , transports = require('./transports') - , Logger = require('./logger') - , Socket = require('./socket') - , MemoryStore = require('./stores/memory') - , SocketNamespace = require('./namespace') - , Static = require('./static') - , EventEmitter = process.EventEmitter; - -/** - * Export the constructor. - */ - -exports = module.exports = Manager; - -/** - * Default transports. - */ - -var defaultTransports = exports.defaultTransports = [ - 'websocket' - , 'htmlfile' - , 'xhr-polling' - , 'jsonp-polling' -]; - -/** - * Inherited defaults. - */ - -var parent = module.parent.exports - , protocol = parent.protocol; - -/** - * Manager constructor. - * - * @param {HTTPServer} server - * @param {Object} options, optional - * @api public - */ - -function Manager (server, options) { - this.server = server; - this.namespaces = {}; - this.sockets = this.of(''); - this.settings = { - origins: '*:*' - , log: true - , store: new MemoryStore - , logger: new Logger - , static: new Static(this) - , heartbeats: true - , resource: '/socket.io' - , transports: defaultTransports - , authorization: false - , blacklist: ['disconnect'] - , 'log level': 3 - , 'log colors': true - , 'close timeout': 25 - , 'heartbeat timeout': 15 - , 'heartbeat interval': 20 - , 'polling duration': 20 - , 'flash policy server': true - , 'flash policy port': 10843 - , 'destroy upgrade': true - , 'browser client': true - , 'browser client cache': true - , 'browser client minification': false - , 'browser client etag': false - , 'browser client expires': 315360000 - , 'browser client gzip': false - , 'browser client handler': false - , 'client store expiration': 15 - }; - - for (var i in options) { - this.settings[i] = options[i]; - } - - var self = this; - - // default error handler - server.on('error', function(err) { - self.log.warn('error raised: ' + err); - }); - - this.initStore(); - - this.on('set:store', function() { - self.initStore(); - }); - - // reset listeners - this.oldListeners = server.listeners('request'); - server.removeAllListeners('request'); - - server.on('request', function (req, res) { - self.handleRequest(req, res); - }); - - server.on('upgrade', function (req, socket, head) { - self.handleUpgrade(req, socket, head); - }); - - server.on('close', function () { - clearInterval(self.gc); - }); - - server.once('listening', function () { - self.gc = setInterval(self.garbageCollection.bind(self), 10000); - }); - - for (var i in transports) { - if (transports[i].init) { - transports[i].init(this); - } - } - - this.log.info('socket.io started'); -}; - -Manager.prototype.__proto__ = EventEmitter.prototype - -/** - * Store accessor shortcut. - * - * @api public - */ - -Manager.prototype.__defineGetter__('store', function () { - var store = this.get('store'); - store.manager = this; - return store; -}); - -/** - * Logger accessor. - * - * @api public - */ - -Manager.prototype.__defineGetter__('log', function () { - var logger = this.get('logger'); - - logger.level = this.get('log level') || -1; - logger.colors = this.get('log colors'); - logger.enabled = this.enabled('log'); - - return logger; -}); - -/** - * Static accessor. - * - * @api public - */ - -Manager.prototype.__defineGetter__('static', function () { - return this.get('static'); -}); - -/** - * Get settings. - * - * @api public - */ - -Manager.prototype.get = function (key) { - return this.settings[key]; -}; - -/** - * Set settings - * - * @api public - */ - -Manager.prototype.set = function (key, value) { - if (arguments.length == 1) return this.get(key); - this.settings[key] = value; - this.emit('set:' + key, this.settings[key], key); - return this; -}; - -/** - * Enable a setting - * - * @api public - */ - -Manager.prototype.enable = function (key) { - this.settings[key] = true; - this.emit('set:' + key, this.settings[key], key); - return this; -}; - -/** - * Disable a setting - * - * @api public - */ - -Manager.prototype.disable = function (key) { - this.settings[key] = false; - this.emit('set:' + key, this.settings[key], key); - return this; -}; - -/** - * Checks if a setting is enabled - * - * @api public - */ - -Manager.prototype.enabled = function (key) { - return !!this.settings[key]; -}; - -/** - * Checks if a setting is disabled - * - * @api public - */ - -Manager.prototype.disabled = function (key) { - return !this.settings[key]; -}; - -/** - * Configure callbacks. - * - * @api public - */ - -Manager.prototype.configure = function (env, fn) { - if ('function' == typeof env) { - env.call(this); - } else if (env == process.env.NODE_ENV) { - fn.call(this); - } - - return this; -}; - -/** - * Initializes everything related to the message dispatcher. - * - * @api private - */ - -Manager.prototype.initStore = function () { - this.handshaken = {}; - this.connected = {}; - this.open = {}; - this.closed = {}; - this.rooms = {}; - this.roomClients = {}; - - var self = this; - - this.store.subscribe('handshake', function (id, data) { - self.onHandshake(id, data); - }); - - this.store.subscribe('connect', function (id) { - self.onConnect(id); - }); - - this.store.subscribe('open', function (id) { - self.onOpen(id); - }); - - this.store.subscribe('join', function (id, room) { - self.onJoin(id, room); - }); - - this.store.subscribe('leave', function (id, room) { - self.onLeave(id, room); - }); - - this.store.subscribe('close', function (id) { - self.onClose(id); - }); - - this.store.subscribe('dispatch', function (room, packet, volatile, exceptions) { - self.onDispatch(room, packet, volatile, exceptions); - }); - - this.store.subscribe('disconnect', function (id) { - self.onDisconnect(id); - }); -}; - -/** - * Called when a client handshakes. - * - * @param text - */ - -Manager.prototype.onHandshake = function (id, data) { - this.handshaken[id] = data; -}; - -/** - * Called when a client connects (ie: transport first opens) - * - * @api private - */ - -Manager.prototype.onConnect = function (id) { - this.connected[id] = true; -}; - -/** - * Called when a client opens a request in a different node. - * - * @api private - */ - -Manager.prototype.onOpen = function (id) { - this.open[id] = true; - - // if we were buffering messages for the client, clear them - if (this.closed[id]) { - var self = this; - - this.store.unsubscribe('dispatch:' + id, function () { - delete self.closed[id]; - }); - } - - // clear the current transport - if (this.transports[id]) { - this.transports[id].discard(); - this.transports[id] = null; - } -}; - -/** - * Called when a message is sent to a namespace and/or room. - * - * @api private - */ - -Manager.prototype.onDispatch = function (room, packet, volatile, exceptions) { - if (this.rooms[room]) { - for (var i = 0, l = this.rooms[room].length; i < l; i++) { - var id = this.rooms[room][i]; - - if (!~exceptions.indexOf(id)) { - if (this.transports[id] && this.transports[id].open) { - this.transports[id].onDispatch(packet, volatile); - } else if (!volatile) { - this.onClientDispatch(id, packet); - } - } - } - } -}; - -/** - * Called when a client joins a nsp / room. - * - * @api private - */ - -Manager.prototype.onJoin = function (id, name) { - if (!this.roomClients[id]) { - this.roomClients[id] = {}; - } - - if (!this.rooms[name]) { - this.rooms[name] = []; - } - - if (!~this.rooms[name].indexOf(id)) { - this.rooms[name].push(id); - this.roomClients[id][name] = true; - } -}; - -/** - * Called when a client leaves a nsp / room. - * - * @param private - */ - -Manager.prototype.onLeave = function (id, room) { - if (this.rooms[room]) { - var index = this.rooms[room].indexOf(id); - - if (index >= 0) { - this.rooms[room].splice(index, 1); - } - - if (!this.rooms[room].length) { - delete this.rooms[room]; - } - delete this.roomClients[id][room]; - } -}; - -/** - * Called when a client closes a request in different node. - * - * @api private - */ - -Manager.prototype.onClose = function (id) { - if (this.open[id]) { - delete this.open[id]; - } - - this.closed[id] = []; - - var self = this; - - this.store.subscribe('dispatch:' + id, function (packet, volatile) { - if (!volatile) { - self.onClientDispatch(id, packet); - } - }); -}; - -/** - * Dispatches a message for a closed client. - * - * @api private - */ - -Manager.prototype.onClientDispatch = function (id, packet) { - if (this.closed[id]) { - this.closed[id].push(packet); - } -}; - -/** - * Receives a message for a client. - * - * @api private - */ - -Manager.prototype.onClientMessage = function (id, packet) { - if (this.namespaces[packet.endpoint]) { - this.namespaces[packet.endpoint].handlePacket(id, packet); - } -}; - -/** - * Fired when a client disconnects (not triggered). - * - * @api private - */ - -Manager.prototype.onClientDisconnect = function (id, reason) { - for (var name in this.namespaces) { - this.namespaces[name].handleDisconnect(id, reason, typeof this.roomClients[id][name] !== 'undefined'); - } - - this.onDisconnect(id); -}; - -/** - * Called when a client disconnects. - * - * @param text - */ - -Manager.prototype.onDisconnect = function (id, local) { - delete this.handshaken[id]; - - if (this.open[id]) { - delete this.open[id]; - } - - if (this.connected[id]) { - delete this.connected[id]; - } - - if (this.transports[id]) { - this.transports[id].discard(); - delete this.transports[id]; - } - - if (this.closed[id]) { - delete this.closed[id]; - } - - if (this.roomClients[id]) { - for (var room in this.roomClients[id]) { - this.onLeave(id, room); - } - delete this.roomClients[id] - } - - this.store.destroyClient(id, this.get('client store expiration')); - - this.store.unsubscribe('dispatch:' + id); - - if (local) { - this.store.unsubscribe('message:' + id); - this.store.unsubscribe('disconnect:' + id); - } -}; - -/** - * Handles an HTTP request. - * - * @api private - */ - -Manager.prototype.handleRequest = function (req, res) { - var data = this.checkRequest(req); - - if (!data) { - for (var i = 0, l = this.oldListeners.length; i < l; i++) { - this.oldListeners[i].call(this.server, req, res); - } - - return; - } - - if (data.static || !data.transport && !data.protocol) { - if (data.static && this.enabled('browser client')) { - this.static.write(data.path, req, res); - } else { - res.writeHead(200); - res.end('Welcome to socket.io.'); - - this.log.info('unhandled socket.io url'); - } - - return; - } - - if (data.protocol != protocol) { - res.writeHead(500); - res.end('Protocol version not supported.'); - - this.log.info('client protocol version unsupported'); - } else { - if (data.id) { - this.handleHTTPRequest(data, req, res); - } else { - this.handleHandshake(data, req, res); - } - } -}; - -/** - * Handles an HTTP Upgrade. - * - * @api private - */ - -Manager.prototype.handleUpgrade = function (req, socket, head) { - var data = this.checkRequest(req) - , self = this; - - if (!data) { - if (this.enabled('destroy upgrade')) { - socket.end(); - this.log.debug('destroying non-socket.io upgrade'); - } - - return; - } - - req.head = head; - this.handleClient(data, req); -}; - -/** - * Handles a normal handshaken HTTP request (eg: long-polling) - * - * @api private - */ - -Manager.prototype.handleHTTPRequest = function (data, req, res) { - req.res = res; - this.handleClient(data, req); -}; - -/** - * Intantiantes a new client. - * - * @api private - */ - -Manager.prototype.handleClient = function (data, req) { - var socket = req.socket - , store = this.store - , self = this; - - if (undefined != data.query.disconnect) { - if (this.transports[data.id] && this.transports[data.id].open) { - this.transports[data.id].onForcedDisconnect(); - } else { - this.store.publish('disconnect-force:' + data.id); - } - return; - } - - if (!~this.get('transports').indexOf(data.transport)) { - this.log.warn('unknown transport: "' + data.transport + '"'); - req.connection.end(); - return; - } - - var transport = new transports[data.transport](this, data, req) - , handshaken = this.handshaken[data.id]; - - if (handshaken) { - if (transport.open) { - if (this.closed[data.id] && this.closed[data.id].length) { - transport.payload(this.closed[data.id]); - this.closed[data.id] = []; - } - - this.onOpen(data.id); - this.store.publish('open', data.id); - this.transports[data.id] = transport; - } - - if (!this.connected[data.id]) { - this.onConnect(data.id); - this.store.publish('connect', data.id); - - // flag as used - delete handshaken.issued; - this.onHandshake(data.id, handshaken); - this.store.publish('handshake', data.id, handshaken); - - // initialize the socket for all namespaces - for (var i in this.namespaces) { - var socket = this.namespaces[i].socket(data.id, true); - - // echo back connect packet and fire connection event - if (i === '') { - this.namespaces[i].handlePacket(data.id, { type: 'connect' }); - } - } - - this.store.subscribe('message:' + data.id, function (packet) { - self.onClientMessage(data.id, packet); - }); - - this.store.subscribe('disconnect:' + data.id, function (reason) { - self.onClientDisconnect(data.id, reason); - }); - } - } else { - if (transport.open) { - transport.error('client not handshaken', 'reconnect'); - } - - transport.discard(); - } -}; - -/** - * Generates a session id. - * - * @api private - */ - -Manager.prototype.generateId = function () { - return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString() - + Math.abs(Math.random() * Math.random() * Date.now() | 0).toString(); -}; - -/** - * Handles a handshake request. - * - * @api private - */ - -Manager.prototype.handleHandshake = function (data, req, res) { - var self = this - , origin = req.headers.origin - , headers = { - 'Content-Type': 'text/plain' - }; - - function writeErr (status, message) { - if (data.query.jsonp) { - res.writeHead(200, { 'Content-Type': 'application/javascript' }); - res.end('io.j[' + data.query.jsonp + '](new Error("' + message + '"));'); - } else { - res.writeHead(status, headers); - res.end(message); - } - }; - - function error (err) { - writeErr(500, 'handshake error'); - self.log.warn('handshake error ' + err); - }; - - if (!this.verifyOrigin(req)) { - writeErr(403, 'handshake bad origin'); - return; - } - - var handshakeData = this.handshakeData(data); - - if (origin) { - // https://developer.mozilla.org/En/HTTP_Access_Control - headers['Access-Control-Allow-Origin'] = '*'; - - if (req.headers.cookie) { - headers['Access-Control-Allow-Credentials'] = 'true'; - } - } - - this.authorize(handshakeData, function (err, authorized, newData) { - if (err) return error(err); - - if (authorized) { - var id = self.generateId() - , hs = [ - id - , self.enabled('heartbeats') ? self.get('heartbeat timeout') || '' : '' - , self.get('close timeout') || '' - , self.transports(data).join(',') - ].join(':'); - - if (data.query.jsonp) { - hs = 'io.j[' + data.query.jsonp + '](' + JSON.stringify(hs) + ');'; - res.writeHead(200, { 'Content-Type': 'application/javascript' }); - } else { - res.writeHead(200, headers); - } - - res.end(hs); - - self.onHandshake(id, newData || handshakeData); - self.store.publish('handshake', id, newData || handshakeData); - - self.log.info('handshake authorized', id); - } else { - writeErr(403, 'handshake unauthorized'); - self.log.info('handshake unauthorized'); - } - }) -}; - -/** - * Gets normalized handshake data - * - * @api private - */ - -Manager.prototype.handshakeData = function (data) { - var connection = data.request.connection - , connectionAddress - , date = new Date; - - if (connection.remoteAddress) { - connectionAddress = { - address: connection.remoteAddress - , port: connection.remotePort - }; - } else if (connection.socket && connection.socket.remoteAddress) { - connectionAddress = { - address: connection.socket.remoteAddress - , port: connection.socket.remotePort - }; - } - - return { - headers: data.headers - , address: connectionAddress - , time: date.toString() - , query: data.query - , url: data.request.url - , xdomain: !!data.request.headers.origin - , secure: data.request.connection.secure - , issued: +date - }; -}; - -/** - * Verifies the origin of a request. - * - * @api private - */ - -Manager.prototype.verifyOrigin = function (request) { - var origin = request.headers.origin || request.headers.referer - , origins = this.get('origins'); - - if (origin === 'null') origin = '*'; - - if (origins.indexOf('*:*') !== -1) { - return true; - } - - if (origin) { - try { - var parts = url.parse(origin); - var ok = - ~origins.indexOf(parts.hostname + ':' + parts.port) || - ~origins.indexOf(parts.hostname + ':*') || - ~origins.indexOf('*:' + parts.port); - if (!ok) this.log.warn('illegal origin: ' + origin); - return ok; - } catch (ex) { - this.log.warn('error parsing origin'); - } - } - else { - this.log.warn('origin missing from handshake, yet required by config'); - } - return false; -}; - -/** - * Handles an incoming packet. - * - * @api private - */ - -Manager.prototype.handlePacket = function (sessid, packet) { - this.of(packet.endpoint || '').handlePacket(sessid, packet); -}; - -/** - * Performs authentication. - * - * @param Object client request data - * @api private - */ - -Manager.prototype.authorize = function (data, fn) { - if (this.get('authorization')) { - var self = this; - - this.get('authorization').call(this, data, function (err, authorized) { - self.log.debug('client ' + authorized ? 'authorized' : 'unauthorized'); - fn(err, authorized); - }); - } else { - this.log.debug('client authorized'); - fn(null, true); - } - - return this; -}; - -/** - * Retrieves the transports adviced to the user. - * - * @api private - */ - -Manager.prototype.transports = function (data) { - var transp = this.get('transports') - , ret = []; - - for (var i = 0, l = transp.length; i < l; i++) { - var transport = transp[i]; - - if (transport) { - if (!transport.checkClient || transport.checkClient(data)) { - ret.push(transport); - } - } - } - - return ret; -}; - -/** - * Checks whether a request is a socket.io one. - * - * @return {Object} a client request data object or `false` - * @api private - */ - -var regexp = /^\/([^\/]+)\/?([^\/]+)?\/?([^\/]+)?\/?$/ - -Manager.prototype.checkRequest = function (req) { - var resource = this.get('resource'); - - if (req.url.substr(0, resource.length) == resource) { - var uri = url.parse(req.url.substr(resource.length), true) - , path = uri.pathname || '' - , pieces = path.match(regexp); - - // client request data - var data = { - query: uri.query || {} - , headers: req.headers - , request: req - , path: path - }; - - if (pieces) { - data.protocol = Number(pieces[1]); - data.transport = pieces[2]; - data.id = pieces[3]; - data.static = !!this.static.has(path); - }; - - return data; - } - - return false; -}; - -/** - * Declares a socket namespace - * - * @api public - */ - -Manager.prototype.of = function (nsp) { - if (this.namespaces[nsp]) { - return this.namespaces[nsp]; - } - - return this.namespaces[nsp] = new SocketNamespace(this, nsp); -}; - -/** - * Perform garbage collection on long living objects and properties that cannot - * be removed automatically. - * - * @api private - */ - -Manager.prototype.garbageCollection = function () { - // clean up unused handshakes - var ids = Object.keys(this.handshaken) - , i = ids.length - , now = Date.now() - , handshake; - - while (i--) { - handshake = this.handshaken[ids[i]]; - - if ('issued' in handshake && (now - handshake.issued) >= 3E4) { - this.onDisconnect(ids[i]); - } - } -}; diff --git a/node_modules/socket.io/lib/namespace.js b/node_modules/socket.io/lib/namespace.js deleted file mode 100644 index 6e1e1c9..0000000 --- a/node_modules/socket.io/lib/namespace.js +++ /dev/null @@ -1,355 +0,0 @@ -/** - * Module dependencies. - */ - -var Socket = require('./socket') - , EventEmitter = process.EventEmitter - , parser = require('./parser') - , util = require('./util'); - -/** - * Exports the constructor. - */ - -exports = module.exports = SocketNamespace; - -/** - * Constructor. - * - * @api public. - */ - -function SocketNamespace (mgr, name) { - this.manager = mgr; - this.name = name || ''; - this.sockets = {}; - this.auth = false; - this.setFlags(); -}; - -/** - * Inherits from EventEmitter. - */ - -SocketNamespace.prototype.__proto__ = EventEmitter.prototype; - -/** - * Copies emit since we override it. - * - * @api private - */ - -SocketNamespace.prototype.$emit = EventEmitter.prototype.emit; - -/** - * Retrieves all clients as Socket instances as an array. - * - * @api public - */ - -SocketNamespace.prototype.clients = function (room) { - var room = this.name + (room !== undefined ? - '/' + room : ''); - - if (!this.manager.rooms[room]) { - return []; - } - - return this.manager.rooms[room].map(function (id) { - return this.socket(id); - }, this); -}; - -/** - * Access logger interface. - * - * @api public - */ - -SocketNamespace.prototype.__defineGetter__('log', function () { - return this.manager.log; -}); - -/** - * Access store. - * - * @api public - */ - -SocketNamespace.prototype.__defineGetter__('store', function () { - return this.manager.store; -}); - -/** - * JSON message flag. - * - * @api public - */ - -SocketNamespace.prototype.__defineGetter__('json', function () { - this.flags.json = true; - return this; -}); - -/** - * Volatile message flag. - * - * @api public - */ - -SocketNamespace.prototype.__defineGetter__('volatile', function () { - this.flags.volatile = true; - return this; -}); - -/** - * Overrides the room to relay messages to (flag). - * - * @api public - */ - -SocketNamespace.prototype.in = SocketNamespace.prototype.to = function (room) { - this.flags.endpoint = this.name + (room ? '/' + room : ''); - return this; -}; - -/** - * Adds a session id we should prevent relaying messages to (flag). - * - * @api public - */ - -SocketNamespace.prototype.except = function (id) { - this.flags.exceptions.push(id); - return this; -}; - -/** - * Sets the default flags. - * - * @api private - */ - -SocketNamespace.prototype.setFlags = function () { - this.flags = { - endpoint: this.name - , exceptions: [] - }; - return this; -}; - -/** - * Sends out a packet. - * - * @api private - */ - -SocketNamespace.prototype.packet = function (packet) { - packet.endpoint = this.name; - - var store = this.store - , log = this.log - , volatile = this.flags.volatile - , exceptions = this.flags.exceptions - , packet = parser.encodePacket(packet); - - this.manager.onDispatch(this.flags.endpoint, packet, volatile, exceptions); - this.store.publish('dispatch', this.flags.endpoint, packet, volatile, exceptions); - - this.setFlags(); - - return this; -}; - -/** - * Sends to everyone. - * - * @api public - */ - -SocketNamespace.prototype.send = function (data) { - return this.packet({ - type: this.flags.json ? 'json' : 'message' - , data: data - }); -}; - -/** - * Emits to everyone (override). - * - * @api public - */ - -SocketNamespace.prototype.emit = function (name) { - if (name == 'newListener') { - return this.$emit.apply(this, arguments); - } - - return this.packet({ - type: 'event' - , name: name - , args: util.toArray(arguments).slice(1) - }); -}; - -/** - * Retrieves or creates a write-only socket for a client, unless specified. - * - * @param {Boolean} whether the socket will be readable when initialized - * @api public - */ - -SocketNamespace.prototype.socket = function (sid, readable) { - if (!this.sockets[sid]) { - this.sockets[sid] = new Socket(this.manager, sid, this, readable); - } - - return this.sockets[sid]; -}; - -/** - * Sets authorization for this namespace. - * - * @api public - */ - -SocketNamespace.prototype.authorization = function (fn) { - this.auth = fn; - return this; -}; - -/** - * Called when a socket disconnects entirely. - * - * @api private - */ - -SocketNamespace.prototype.handleDisconnect = function (sid, reason, raiseOnDisconnect) { - if (this.sockets[sid] && this.sockets[sid].readable) { - if (raiseOnDisconnect) this.sockets[sid].onDisconnect(reason); - delete this.sockets[sid]; - } -}; - -/** - * Performs authentication. - * - * @param Object client request data - * @api private - */ - -SocketNamespace.prototype.authorize = function (data, fn) { - if (this.auth) { - var self = this; - - this.auth.call(this, data, function (err, authorized) { - self.log.debug('client ' + - (authorized ? '' : 'un') + 'authorized for ' + self.name); - fn(err, authorized); - }); - } else { - this.log.debug('client authorized for ' + this.name); - fn(null, true); - } - - return this; -}; - -/** - * Handles a packet. - * - * @api private - */ - -SocketNamespace.prototype.handlePacket = function (sessid, packet) { - var socket = this.socket(sessid) - , dataAck = packet.ack == 'data' - , manager = this.manager - , self = this; - - function ack () { - self.log.debug('sending data ack packet'); - socket.packet({ - type: 'ack' - , args: util.toArray(arguments) - , ackId: packet.id - }); - }; - - function error (err) { - self.log.warn('handshake error ' + err + ' for ' + self.name); - socket.packet({ type: 'error', reason: err }); - }; - - function connect () { - self.manager.onJoin(sessid, self.name); - self.store.publish('join', sessid, self.name); - - // packet echo - socket.packet({ type: 'connect' }); - - // emit connection event - self.$emit('connection', socket); - }; - - switch (packet.type) { - case 'connect': - if (packet.endpoint == '') { - connect(); - } else { - var handshakeData = manager.handshaken[sessid]; - - this.authorize(handshakeData, function (err, authorized, newData) { - if (err) return error(err); - - if (authorized) { - manager.onHandshake(sessid, newData || handshakeData); - self.store.publish('handshake', sessid, newData || handshakeData); - connect(); - } else { - error('unauthorized'); - } - }); - } - break; - - case 'ack': - if (socket.acks[packet.ackId]) { - socket.acks[packet.ackId].apply(socket, packet.args); - } else { - this.log.info('unknown ack packet'); - } - break; - - case 'event': - // check if the emitted event is not blacklisted - if (-~manager.get('blacklist').indexOf(packet.name)) { - this.log.debug('ignoring blacklisted event `' + packet.name + '`'); - } else { - var params = [packet.name].concat(packet.args); - - if (dataAck) { - params.push(ack); - } - - socket.$emit.apply(socket, params); - } - break; - - case 'disconnect': - this.manager.onLeave(sessid, this.name); - this.store.publish('leave', sessid, this.name); - - socket.$emit('disconnect', packet.reason || 'packet'); - break; - - case 'json': - case 'message': - var params = ['message', packet.data]; - - if (dataAck) - params.push(ack); - - socket.$emit.apply(socket, params); - }; -}; diff --git a/node_modules/socket.io/lib/parser.js b/node_modules/socket.io/lib/parser.js deleted file mode 100644 index fe0cc37..0000000 --- a/node_modules/socket.io/lib/parser.js +++ /dev/null @@ -1,249 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -/** - * Packet types. - */ - -var packets = exports.packets = { - 'disconnect': 0 - , 'connect': 1 - , 'heartbeat': 2 - , 'message': 3 - , 'json': 4 - , 'event': 5 - , 'ack': 6 - , 'error': 7 - , 'noop': 8 - } - , packetslist = Object.keys(packets); - -/** - * Errors reasons. - */ - -var reasons = exports.reasons = { - 'transport not supported': 0 - , 'client not handshaken': 1 - , 'unauthorized': 2 - } - , reasonslist = Object.keys(reasons); - -/** - * Errors advice. - */ - -var advice = exports.advice = { - 'reconnect': 0 - } - , advicelist = Object.keys(advice); - -/** - * Encodes a packet. - * - * @api private - */ - -exports.encodePacket = function (packet) { - var type = packets[packet.type] - , id = packet.id || '' - , endpoint = packet.endpoint || '' - , ack = packet.ack - , data = null; - - switch (packet.type) { - case 'message': - if (packet.data !== '') - data = packet.data; - break; - - case 'event': - var ev = { name: packet.name }; - - if (packet.args && packet.args.length) { - ev.args = packet.args; - } - - data = JSON.stringify(ev); - break; - - case 'json': - data = JSON.stringify(packet.data); - break; - - case 'ack': - data = packet.ackId - + (packet.args && packet.args.length - ? '+' + JSON.stringify(packet.args) : ''); - break; - - case 'connect': - if (packet.qs) - data = packet.qs; - break; - - case 'error': - var reason = packet.reason ? reasons[packet.reason] : '' - , adv = packet.advice ? advice[packet.advice] : '' - - if (reason !== '' || adv !== '') - data = reason + (adv !== '' ? ('+' + adv) : '') - - break; - } - - // construct packet with required fragments - var encoded = type + ':' + id + (ack == 'data' ? '+' : '') + ':' + endpoint; - - // data fragment is optional - if (data !== null && data !== undefined) - encoded += ':' + data; - - return encoded; -}; - -/** - * Encodes multiple messages (payload). - * - * @param {Array} messages - * @api private - */ - -exports.encodePayload = function (packets) { - var decoded = ''; - - if (packets.length == 1) - return packets[0]; - - for (var i = 0, l = packets.length; i < l; i++) { - var packet = packets[i]; - decoded += '\ufffd' + packet.length + '\ufffd' + packets[i] - } - - return decoded; -}; - -/** - * Decodes a packet - * - * @api private - */ - -var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; - -/** - * Wrap the JSON.parse in a seperate function the crankshaft optimizer will - * only punish this function for the usage for try catch - * - * @api private - */ - -function parse (data) { - try { return JSON.parse(data) } - catch (e) { return false } -} - -exports.decodePacket = function (data) { - var pieces = data.match(regexp); - - if (!pieces) return {}; - - var id = pieces[2] || '' - , data = pieces[5] || '' - , packet = { - type: packetslist[pieces[1]] - , endpoint: pieces[4] || '' - }; - - // whether we need to acknowledge the packet - if (id) { - packet.id = id; - if (pieces[3]) - packet.ack = 'data'; - else - packet.ack = true; - } - - // handle different packet types - switch (packet.type) { - case 'message': - packet.data = data || ''; - break; - - case 'event': - pieces = parse(data); - if (pieces) { - packet.name = pieces.name; - packet.args = pieces.args; - } - - packet.args = packet.args || []; - break; - - case 'json': - packet.data = parse(data); - break; - - case 'connect': - packet.qs = data || ''; - break; - - case 'ack': - pieces = data.match(/^([0-9]+)(\+)?(.*)/); - if (pieces) { - packet.ackId = pieces[1]; - packet.args = []; - - if (pieces[3]) { - packet.args = parse(pieces[3]) || []; - } - } - break; - - case 'error': - pieces = data.split('+'); - packet.reason = reasonslist[pieces[0]] || ''; - packet.advice = advicelist[pieces[1]] || ''; - } - - return packet; -}; - -/** - * Decodes data payload. Detects multiple messages - * - * @return {Array} messages - * @api public - */ - -exports.decodePayload = function (data) { - if (undefined == data || null == data) { - return []; - } - - if (data[0] == '\ufffd') { - var ret = []; - - for (var i = 1, length = ''; i < data.length; i++) { - if (data[i] == '\ufffd') { - ret.push(exports.decodePacket(data.substr(i + 1).substr(0, length))); - i += Number(length) + 1; - length = ''; - } else { - length += data[i]; - } - } - - return ret; - } else { - return [exports.decodePacket(data)]; - } -}; diff --git a/node_modules/socket.io/lib/socket.io.js b/node_modules/socket.io/lib/socket.io.js deleted file mode 100644 index 342de20..0000000 --- a/node_modules/socket.io/lib/socket.io.js +++ /dev/null @@ -1,136 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var client = require('socket.io-client'); - -/** - * Version. - */ - -exports.version = '0.8.7'; - -/** - * Supported protocol version. - */ - -exports.protocol = 1; - -/** - * Client that we serve. - */ - -exports.clientVersion = client.version; - -/** - * Attaches a manager - * - * @param {HTTPServer/Number} a HTTP/S server or a port number to listen on. - * @param {Object} opts to be passed to Manager and/or http server - * @param {Function} callback if a port is supplied - * @api public - */ - -exports.listen = function (server, options, fn) { - if ('function' == typeof options) { - fn = options; - options = {}; - } - - if ('undefined' == typeof server) { - // create a server that listens on port 80 - server = 80; - } - - if ('number' == typeof server) { - // if a port number is passed - var port = server; - - if (options && options.key) - server = require('https').createServer(options); - else - server = require('http').createServer(); - - // default response - server.on('request', function (req, res) { - res.writeHead(200); - res.end('Welcome to socket.io.'); - }); - - server.listen(port, fn); - } - - // otherwise assume a http/s server - return new exports.Manager(server, options); -}; - -/** - * Manager constructor. - * - * @api public - */ - -exports.Manager = require('./manager'); - -/** - * Transport constructor. - * - * @api public - */ - -exports.Transport = require('./transport'); - -/** - * Socket constructor. - * - * @api public - */ - -exports.Socket = require('./socket'); - -/** - * Static constructor. - * - * @api public - */ - -exports.Static = require('./static'); - -/** - * Store constructor. - * - * @api public - */ - -exports.Store = require('./store'); - -/** - * Memory Store constructor. - * - * @api public - */ - -exports.MemoryStore = require('./stores/memory'); - -/** - * Redis Store constructor. - * - * @api public - */ - -exports.RedisStore = require('./stores/redis'); - -/** - * Parser. - * - * @api public - */ - -exports.parser = require('./parser'); diff --git a/node_modules/socket.io/lib/socket.js b/node_modules/socket.io/lib/socket.js deleted file mode 100644 index 38542f4..0000000 --- a/node_modules/socket.io/lib/socket.js +++ /dev/null @@ -1,362 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var parser = require('./parser') - , util = require('./util') - , EventEmitter = process.EventEmitter - -/** - * Export the constructor. - */ - -exports = module.exports = Socket; - -/** - * Default error event listener to prevent uncaught exceptions. - */ - -var defaultError = function () {}; - -/** - * Socket constructor. - * - * @param {Manager} manager instance - * @param {String} session id - * @param {Namespace} namespace the socket belongs to - * @param {Boolean} whether the - * @api public - */ - -function Socket (manager, id, nsp, readable) { - this.id = id; - this.namespace = nsp; - this.manager = manager; - this.disconnected = false; - this.ackPackets = 0; - this.acks = {}; - this.setFlags(); - this.readable = readable; - this.store = this.manager.store.client(this.id); - this.on('error', defaultError); -}; - -/** - * Inherits from EventEmitter. - */ - -Socket.prototype.__proto__ = EventEmitter.prototype; - -/** - * Accessor shortcut for the handshake data - * - * @api private - */ - -Socket.prototype.__defineGetter__('handshake', function () { - return this.manager.handshaken[this.id]; -}); - -/** - * Accessor shortcut for the transport type - * - * @api private - */ - -Socket.prototype.__defineGetter__('transport', function () { - return this.manager.transports[this.id].name; -}); - -/** - * Accessor shortcut for the logger. - * - * @api private - */ - -Socket.prototype.__defineGetter__('log', function () { - return this.manager.log; -}); - -/** - * JSON message flag. - * - * @api public - */ - -Socket.prototype.__defineGetter__('json', function () { - this.flags.json = true; - return this; -}); - -/** - * Volatile message flag. - * - * @api public - */ - -Socket.prototype.__defineGetter__('volatile', function () { - this.flags.volatile = true; - return this; -}); - -/** - * Broadcast message flag. - * - * @api public - */ - -Socket.prototype.__defineGetter__('broadcast', function () { - this.flags.broadcast = true; - return this; -}); - -/** - * Overrides the room to broadcast messages to (flag) - * - * @api public - */ - -Socket.prototype.to = Socket.prototype.in = function (room) { - this.flags.room = room; - return this; -}; - -/** - * Resets flags - * - * @api private - */ - -Socket.prototype.setFlags = function () { - this.flags = { - endpoint: this.namespace.name - , room: '' - }; - return this; -}; - -/** - * Triggered on disconnect - * - * @api private - */ - -Socket.prototype.onDisconnect = function (reason) { - if (!this.disconnected) { - this.$emit('disconnect', reason); - this.disconnected = true; - } -}; - -/** - * Joins a user to a room. - * - * @api public - */ - -Socket.prototype.join = function (name, fn) { - var nsp = this.namespace.name - , name = (nsp + '/') + name; - - this.manager.onJoin(this.id, name); - this.manager.store.publish('join', this.id, name); - - if (fn) { - this.log.warn('Client#join callback is deprecated'); - fn(); - } - - return this; -}; - -/** - * Un-joins a user from a room. - * - * @api public - */ - -Socket.prototype.leave = function (name, fn) { - var nsp = this.namespace.name - , name = (nsp + '/') + name; - - this.manager.onLeave(this.id, name); - this.manager.store.publish('leave', this.id, name); - - if (fn) { - this.log.warn('Client#leave callback is deprecated'); - fn(); - } - - return this; -}; - -/** - * Transmits a packet. - * - * @api private - */ - -Socket.prototype.packet = function (packet) { - if (this.flags.broadcast) { - this.log.debug('broadcasting packet'); - this.namespace.in(this.flags.room).except(this.id).packet(packet); - } else { - packet.endpoint = this.flags.endpoint; - packet = parser.encodePacket(packet); - - this.dispatch(packet, this.flags.volatile); - } - - this.setFlags(); - - return this; -}; - -/** - * Dispatches a packet - * - * @api private - */ - -Socket.prototype.dispatch = function (packet, volatile) { - if (this.manager.transports[this.id] && this.manager.transports[this.id].open) { - this.manager.transports[this.id].onDispatch(packet, volatile); - } else { - if (!volatile) { - this.manager.onClientDispatch(this.id, packet, volatile); - } - - this.manager.store.publish('dispatch:' + this.id, packet, volatile); - } -}; - -/** - * Stores data for the client. - * - * @api public - */ - -Socket.prototype.set = function (key, value, fn) { - this.store.set(key, value, fn); - return this; -}; - -/** - * Retrieves data for the client - * - * @api public - */ - -Socket.prototype.get = function (key, fn) { - this.store.get(key, fn); - return this; -}; - -/** - * Checks data for the client - * - * @api public - */ - -Socket.prototype.has = function (key, fn) { - this.store.has(key, fn); - return this; -}; - -/** - * Deletes data for the client - * - * @api public - */ - -Socket.prototype.del = function (key, fn) { - this.store.del(key, fn); - return this; -}; - -/** - * Kicks client - * - * @api public - */ - -Socket.prototype.disconnect = function () { - if (!this.disconnected) { - this.log.info('booting client'); - - if (this.manager.transports[this.id] && this.manager.transports[this.id].open) { - this.manager.transports[this.id].onForcedDisconnect(); - } else { - this.manager.onClientDisconnect(this.id); - this.manager.store.publish('disconnect:' + this.id); - } - } - - return this; -}; - -/** - * Send a message. - * - * @api public - */ - -Socket.prototype.send = function (data, fn) { - var packet = { - type: this.flags.json ? 'json' : 'message' - , data: data - }; - - if (fn) { - packet.id = ++this.ackPackets; - packet.ack = true; - this.acks[packet.id] = fn; - } - - return this.packet(packet); -}; - -/** - * Original emit function. - * - * @api private - */ - -Socket.prototype.$emit = EventEmitter.prototype.emit; - -/** - * Emit override for custom events. - * - * @api public - */ - -Socket.prototype.emit = function (ev) { - if (ev == 'newListener') { - return this.$emit.apply(this, arguments); - } - - var args = util.toArray(arguments).slice(1) - , lastArg = args[args.length - 1] - , packet = { - type: 'event' - , name: ev - }; - - if ('function' == typeof lastArg) { - packet.id = ++this.ackPackets; - packet.ack = lastArg.length ? 'data' : true; - this.acks[packet.id] = lastArg; - args = args.slice(0, args.length - 1); - } - - packet.args = args; - - return this.packet(packet); -}; diff --git a/node_modules/socket.io/lib/static.js b/node_modules/socket.io/lib/static.js deleted file mode 100644 index e3117ed..0000000 --- a/node_modules/socket.io/lib/static.js +++ /dev/null @@ -1,395 +0,0 @@ - -/*! -* socket.io-node -* Copyright(c) 2011 LearnBoost -* MIT Licensed -*/ - -/** - * Module dependencies. - */ - -var client = require('socket.io-client') - , cp = require('child_process') - , fs = require('fs') - , util = require('./util'); - -/** - * File type details. - * - * @api private - */ - -var mime = { - js: { - type: 'application/javascript' - , encoding: 'utf8' - , gzip: true - } - , swf: { - type: 'application/x-shockwave-flash' - , encoding: 'binary' - , gzip: false - } -}; - -/** - * Regexp for matching custom transport patterns. Users can configure their own - * socket.io bundle based on the url structure. Different transport names are - * concatinated using the `+` char. /socket.io/socket.io+websocket.js should - * create a bundle that only contains support for the websocket. - * - * @api private - */ - -var bundle = /\+((?:\+)?[\w\-]+)*(?:\.v\d+\.\d+\.\d+)?(?:\.js)$/ - , versioning = /\.v\d+\.\d+\.\d+(?:\.js)$/; - -/** - * Export the constructor - */ - -exports = module.exports = Static; - -/** - * Static constructor - * - * @api public - */ - -function Static (manager) { - this.manager = manager; - this.cache = {}; - this.paths = {}; - - this.init(); -} - -/** - * Initialize the Static by adding default file paths. - * - * @api public - */ - -Static.prototype.init = function () { - /** - * Generates a unique id based the supplied transports array - * - * @param {Array} transports The array with transport types - * @api private - */ - function id (transports) { - var id = transports.join('').split('').map(function (char) { - return ('' + char.charCodeAt(0)).split('').pop(); - }).reduce(function (char, id) { - return char +id; - }); - - return client.version + ':' + id; - } - - /** - * Generates a socket.io-client file based on the supplied transports. - * - * @param {Array} transports The array with transport types - * @param {Function} callback Callback for the static.write - * @api private - */ - - function build (transports, callback) { - client.builder(transports, { - minify: self.manager.enabled('browser client minification') - }, function (err, content) { - callback(err, content ? new Buffer(content) : null, id(transports)); - } - ); - } - - var self = this; - - // add our default static files - this.add('/static/flashsocket/WebSocketMain.swf', { - file: client.dist + '/WebSocketMain.swf' - }); - - this.add('/static/flashsocket/WebSocketMainInsecure.swf', { - file: client.dist + '/WebSocketMainInsecure.swf' - }); - - // generates dedicated build based on the available transports - this.add('/socket.io.js', function (path, callback) { - build(self.manager.get('transports'), callback); - }); - - this.add('/socket.io.v', { mime: mime.js }, function (path, callback) { - build(self.manager.get('transports'), callback); - }); - - // allow custom builds based on url paths - this.add('/socket.io+', { mime: mime.js }, function (path, callback) { - var available = self.manager.get('transports') - , matches = path.match(bundle) - , transports = []; - - if (!matches) return callback('No valid transports'); - - // make sure they valid transports - matches[0].split('.')[0].split('+').slice(1).forEach(function (transport) { - if (!!~available.indexOf(transport)) { - transports.push(transport); - } - }); - - if (!transports.length) return callback('No valid transports'); - build(transports, callback); - }); - - // clear cache when transports change - this.manager.on('set:transports', function (key, value) { - delete self.cache['/socket.io.js']; - Object.keys(self.cache).forEach(function (key) { - if (bundle.test(key)) { - delete self.cache[key]; - } - }); - }); -}; - -/** - * Gzip compress buffers. - * - * @param {Buffer} data The buffer that needs gzip compression - * @param {Function} callback - * @api public - */ - -Static.prototype.gzip = function (data, callback) { - var gzip = cp.spawn('gzip', ['-9', '-c', '-f', '-n']) - , encoding = Buffer.isBuffer(data) ? 'binary' : 'utf8' - , buffer = [] - , err; - - gzip.stdout.on('data', function (data) { - buffer.push(data); - }); - - gzip.stderr.on('data', function (data) { - err = data +''; - buffer.length = 0; - }); - - gzip.on('exit', function () { - if (err) return callback(err); - - var size = 0 - , index = 0 - , i = buffer.length - , content; - - while (i--) { - size += buffer[i].length; - } - - content = new Buffer(size); - i = buffer.length; - - buffer.forEach(function (buffer) { - var length = buffer.length; - - buffer.copy(content, index, 0, length); - index += length; - }); - - buffer.length = 0; - callback(null, content); - }); - - gzip.stdin.end(data, encoding); -}; - -/** - * Is the path a static file? - * - * @param {String} path The path that needs to be checked - * @api public - */ - -Static.prototype.has = function (path) { - // fast case - if (this.paths[path]) return this.paths[path]; - - var keys = Object.keys(this.paths) - , i = keys.length; - - while (i--) { - if (-~path.indexOf(keys[i])) return this.paths[keys[i]]; - } - - return false; -}; - -/** - * Add new paths new paths that can be served using the static provider. - * - * @param {String} path The path to respond to - * @param {Options} options Options for writing out the response - * @param {Function} [callback] Optional callback if no options.file is - * supplied this would be called instead. - * @api public - */ - -Static.prototype.add = function (path, options, callback) { - var extension = /(?:\.(\w{1,4}))$/.exec(path); - - if (!callback && typeof options == 'function') { - callback = options; - options = {}; - } - - options.mime = options.mime || (extension ? mime[extension[1]] : false); - - if (callback) options.callback = callback; - if (!(options.file || options.callback) || !options.mime) return false; - - this.paths[path] = options; - - return true; -}; - -/** - * Writes a static response. - * - * @param {String} path The path for the static content - * @param {HTTPRequest} req The request object - * @param {HTTPResponse} res The response object - * @api public - */ - -Static.prototype.write = function (path, req, res) { - /** - * Write a response without throwing errors because can throw error if the - * response is no longer writable etc. - * - * @api private - */ - - function write (status, headers, content, encoding) { - try { - res.writeHead(status, headers || undefined); - - // only write content if it's not a HEAD request and we actually have - // some content to write (304's doesn't have content). - res.end( - req.method !== 'HEAD' && content ? content : '' - , encoding || undefined - ); - } catch (e) {} - } - - /** - * Answers requests depending on the request properties and the reply object. - * - * @param {Object} reply The details and content to reply the response with - * @api private - */ - - function answer (reply) { - var cached = req.headers['if-none-match'] === reply.etag; - if (cached && self.manager.enabled('browser client etag')) { - return write(304); - } - - var accept = req.headers['accept-encoding'] || '' - , gzip = !!~accept.toLowerCase().indexOf('gzip') - , mime = reply.mime - , versioned = reply.versioned - , headers = { - 'Content-Type': mime.type - }; - - // check if we can add a etag - if (self.manager.enabled('browser client etag') && reply.etag && !versioned) { - headers['Etag'] = reply.etag; - } - - // see if we need to set Expire headers because the path is versioned - if (versioned) { - var expires = self.manager.get('browser client expires'); - headers['Cache-Control'] = 'private, x-gzip-ok="", max-age=' + expires; - headers['Date'] = new Date().toUTCString(); - headers['Expires'] = new Date(Date.now() + (expires * 1000)).toUTCString(); - } - - if (gzip && reply.gzip) { - headers['Content-Length'] = reply.gzip.length; - headers['Content-Encoding'] = 'gzip'; - headers['Vary'] = 'Accept-Encoding'; - write(200, headers, reply.gzip.content, mime.encoding); - } else { - headers['Content-Length'] = reply.length; - write(200, headers, reply.content, mime.encoding); - } - - self.manager.log.debug('served static content ' + path); - } - - var self = this - , details; - - // most common case first - if (this.manager.enabled('browser client cache') && this.cache[path]) { - return answer(this.cache[path]); - } else if (this.manager.get('browser client handler')) { - return this.manager.get('browser client handler').call(this, req, res); - } else if ((details = this.has(path))) { - /** - * A small helper function that will let us deal with fs and dynamic files - * - * @param {Object} err Optional error - * @param {Buffer} content The data - * @api private - */ - - function ready (err, content, etag) { - if (err) { - self.manager.log.warn('Unable to serve file. ' + (err.message || err)); - return write(500, null, 'Error serving static ' + path); - } - - // store the result in the cache - var reply = self.cache[path] = { - content: content - , length: content.length - , mime: details.mime - , etag: etag || client.version - , versioned: versioning.test(path) - }; - - // check if gzip is enabled - if (details.mime.gzip && self.manager.enabled('browser client gzip')) { - self.gzip(content, function (err, content) { - if (!err) { - reply.gzip = { - content: content - , length: content.length - } - } - - answer(reply); - }); - } else { - answer(reply); - } - } - - if (details.file) { - fs.readFile(details.file, ready); - } else if(details.callback) { - details.callback.call(this, path, ready); - } else { - write(404, null, 'File handle not found'); - } - } else { - write(404, null, 'File not found'); - } -}; diff --git a/node_modules/socket.io/lib/store.js b/node_modules/socket.io/lib/store.js deleted file mode 100644 index 06c0389..0000000 --- a/node_modules/socket.io/lib/store.js +++ /dev/null @@ -1,98 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Expose the constructor. - */ - -exports = module.exports = Store; - -/** - * Module dependencies. - */ - -var EventEmitter = process.EventEmitter; - -/** - * Store interface - * - * @api public - */ - -function Store (options) { - this.options = options; - this.clients = {}; -}; - -/** - * Inherit from EventEmitter. - */ - -Store.prototype.__proto__ = EventEmitter.prototype; - -/** - * Initializes a client store - * - * @param {String} id - * @api public - */ - -Store.prototype.client = function (id) { - if (!this.clients[id]) { - this.clients[id] = new (this.constructor.Client)(this, id); - } - - return this.clients[id]; -}; - -/** - * Destroys a client - * - * @api {String} sid - * @param {Number} number of seconds to expire client data - * @api private - */ - -Store.prototype.destroyClient = function (id, expiration) { - if (this.clients[id]) { - this.clients[id].destroy(expiration); - delete this.clients[id]; - } - - return this; -}; - -/** - * Destroys the store - * - * @param {Number} number of seconds to expire client data - * @api private - */ - -Store.prototype.destroy = function (clientExpiration) { - var keys = Object.keys(this.clients) - , count = keys.length; - - for (var i = 0, l = count; i < l; i++) { - this.destroyClient(keys[i], clientExpiration); - } - - this.clients = {}; - - return this; -}; - -/** - * Client. - * - * @api public - */ - -Store.Client = function (store, id) { - this.store = store; - this.id = id; -}; diff --git a/node_modules/socket.io/lib/stores/memory.js b/node_modules/socket.io/lib/stores/memory.js deleted file mode 100644 index 8b731a7..0000000 --- a/node_modules/socket.io/lib/stores/memory.js +++ /dev/null @@ -1,143 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var crypto = require('crypto') - , Store = require('../store'); - -/** - * Exports the constructor. - */ - -exports = module.exports = Memory; -Memory.Client = Client; - -/** - * Memory store - * - * @api public - */ - -function Memory (opts) { - Store.call(this, opts); -}; - -/** - * Inherits from Store. - */ - -Memory.prototype.__proto__ = Store.prototype; - -/** - * Publishes a message. - * - * @api private - */ - -Memory.prototype.publish = function () { }; - -/** - * Subscribes to a channel - * - * @api private - */ - -Memory.prototype.subscribe = function () { }; - -/** - * Unsubscribes - * - * @api private - */ - -Memory.prototype.unsubscribe = function () { }; - -/** - * Client constructor - * - * @api private - */ - -function Client () { - Store.Client.apply(this, arguments); - this.data = {}; -}; - -/** - * Inherits from Store.Client - */ - -Client.prototype.__proto__ = Store.Client; - -/** - * Gets a key - * - * @api public - */ - -Client.prototype.get = function (key, fn) { - fn(null, this.data[key] === undefined ? null : this.data[key]); - return this; -}; - -/** - * Sets a key - * - * @api public - */ - -Client.prototype.set = function (key, value, fn) { - this.data[key] = value; - fn && fn(null); - return this; -}; - -/** - * Has a key - * - * @api public - */ - -Client.prototype.has = function (key, fn) { - fn(null, key in this.data); -}; - -/** - * Deletes a key - * - * @api public - */ - -Client.prototype.del = function (key, fn) { - delete this.data[key]; - fn && fn(null); - return this; -}; - -/** - * Destroys the client. - * - * @param {Number} number of seconds to expire data - * @api private - */ - -Client.prototype.destroy = function (expiration) { - if ('number' != typeof expiration) { - this.data = {}; - } else { - var self = this; - - setTimeout(function () { - self.data = {}; - }, expiration * 1000); - } - - return this; -}; diff --git a/node_modules/socket.io/lib/stores/redis.js b/node_modules/socket.io/lib/stores/redis.js deleted file mode 100644 index 8fea235..0000000 --- a/node_modules/socket.io/lib/stores/redis.js +++ /dev/null @@ -1,269 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var crypto = require('crypto') - , Store = require('../store') - , assert = require('assert'); - -/** - * Exports the constructor. - */ - -exports = module.exports = Redis; -Redis.Client = Client; - -/** - * Redis store. - * Options: - * - nodeId (fn) gets an id that uniquely identifies this node - * - redis (fn) redis constructor, defaults to redis - * - redisPub (object) options to pass to the pub redis client - * - redisSub (object) options to pass to the sub redis client - * - redisClient (object) options to pass to the general redis client - * - pack (fn) custom packing, defaults to JSON or msgpack if installed - * - unpack (fn) custom packing, defaults to JSON or msgpack if installed - * - * @api public - */ - -function Redis (opts) { - opts = opts || {}; - - // node id to uniquely identify this node - var nodeId = opts.nodeId || function () { - // by default, we generate a random id - return Math.abs(Math.random() * Math.random() * Date.now() | 0); - }; - - this.nodeId = nodeId(); - - // packing / unpacking mechanism - if (opts.pack) { - this.pack = opts.pack; - this.unpack = opts.unpack; - } else { - try { - var msgpack = require('msgpack'); - this.pack = msgpack.pack; - this.unpack = msgpack.unpack; - } catch (e) { - this.pack = JSON.stringify; - this.unpack = JSON.parse; - } - } - - var redis = opts.redis || require('redis') - , RedisClient = redis.RedisClient; - - // initialize a pubsub client and a regular client - if (opts.redisPub instanceof RedisClient) { - this.pub = opts.redisPub; - } else { - opts.redisPub || (opts.redisPub = {}); - this.pub = redis.createClient(opts.redisPub.port, opts.redisPub.host, opts.redisPub); - } - if (opts.redisSub instanceof RedisClient) { - this.sub = opts.redisSub; - } else { - opts.redisSub || (opts.redisSub = {}); - this.sub = redis.createClient(opts.redisSub.port, opts.redisSub.host, opts.redisSub); - } - if (opts.redisClient instanceof RedisClient) { - this.cmd = opts.redisClient; - } else { - opts.redisClient || (opts.redisClient = {}); - this.cmd = redis.createClient(opts.redisClient.port, opts.redisClient.host, opts.redisClient); - } - - Store.call(this, opts); - - this.sub.setMaxListeners(0); - this.setMaxListeners(0); -}; - -/** - * Inherits from Store. - */ - -Redis.prototype.__proto__ = Store.prototype; - -/** - * Publishes a message. - * - * @api private - */ - -Redis.prototype.publish = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - this.pub.publish(name, this.pack({ nodeId: this.nodeId, args: args })); - this.emit.apply(this, ['publish', name].concat(args)); -}; - -/** - * Subscribes to a channel - * - * @api private - */ - -Redis.prototype.subscribe = function (name, consumer, fn) { - this.sub.subscribe(name); - - if (consumer || fn) { - var self = this; - - self.sub.on('subscribe', function subscribe (ch) { - if (name == ch) { - function message (ch, msg) { - if (name == ch) { - msg = self.unpack(msg); - - // we check that the message consumed wasnt emitted by this node - if (self.nodeId != msg.nodeId) { - consumer.apply(null, msg.args); - } - } - }; - - self.sub.on('message', message); - - self.on('unsubscribe', function unsubscribe (ch) { - if (name == ch) { - self.sub.removeListener('message', message); - self.removeListener('unsubscribe', unsubscribe); - } - }); - - self.sub.removeListener('subscribe', subscribe); - - fn && fn(); - } - }); - } - - this.emit('subscribe', name, consumer, fn); -}; - -/** - * Unsubscribes - * - * @api private - */ - -Redis.prototype.unsubscribe = function (name, fn) { - this.sub.unsubscribe(name); - - if (fn) { - var client = this.sub; - - client.on('unsubscribe', function unsubscribe (ch) { - if (name == ch) { - fn(); - client.removeListener('unsubscribe', unsubscribe); - } - }); - } - - this.emit('unsubscribe', name, fn); -}; - -/** - * Destroys the store - * - * @api public - */ - -Redis.prototype.destroy = function () { - Store.prototype.destroy.call(this); - - this.pub.end(); - this.sub.end(); - this.cmd.end(); -}; - -/** - * Client constructor - * - * @api private - */ - -function Client (store, id) { - Store.Client.call(this, store, id); -}; - -/** - * Inherits from Store.Client - */ - -Client.prototype.__proto__ = Store.Client; - -/** - * Redis hash get - * - * @api private - */ - -Client.prototype.get = function (key, fn) { - this.store.cmd.hget(this.id, key, fn); - return this; -}; - -/** - * Redis hash set - * - * @api private - */ - -Client.prototype.set = function (key, value, fn) { - this.store.cmd.hset(this.id, key, value, fn); - return this; -}; - -/** - * Redis hash del - * - * @api private - */ - -Client.prototype.del = function (key, fn) { - this.store.cmd.hdel(this.id, key, fn); - return this; -}; - -/** - * Redis hash has - * - * @api private - */ - -Client.prototype.has = function (key, fn) { - this.store.cmd.hexists(this.id, key, function (err, has) { - if (err) return fn(err); - fn(null, !!has); - }); - return this; -}; - -/** - * Destroys client - * - * @param {Number} number of seconds to expire data - * @api private - */ - -Client.prototype.destroy = function (expiration) { - if ('number' != typeof expiration) { - this.store.cmd.del(this.id); - } else { - this.store.cmd.expire(this.id, expiration); - } - - return this; -}; diff --git a/node_modules/socket.io/lib/transport.js b/node_modules/socket.io/lib/transport.js deleted file mode 100644 index 61f456f..0000000 --- a/node_modules/socket.io/lib/transport.js +++ /dev/null @@ -1,534 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var parser = require('./parser'); - -/** - * Expose the constructor. - */ - -exports = module.exports = Transport; - -/** - * Transport constructor. - * - * @api public - */ - -function Transport (mng, data, req) { - this.manager = mng; - this.id = data.id; - this.disconnected = false; - this.drained = true; - this.handleRequest(req); -}; - -/** - * Access the logger. - * - * @api public - */ - -Transport.prototype.__defineGetter__('log', function () { - return this.manager.log; -}); - -/** - * Access the store. - * - * @api public - */ - -Transport.prototype.__defineGetter__('store', function () { - return this.manager.store; -}); - -/** - * Handles a request when it's set. - * - * @api private - */ - -Transport.prototype.handleRequest = function (req) { - this.log.debug('setting request', req.method, req.url); - this.req = req; - - if (req.method == 'GET') { - this.socket = req.socket; - this.open = true; - this.drained = true; - this.setHeartbeatInterval(); - - this.setHandlers(); - this.onSocketConnect(); - } -}; - -/** - * Called when a connection is first set. - * - * @api private - */ - -Transport.prototype.onSocketConnect = function () { }; - -/** - * Sets transport handlers - * - * @api private - */ - -Transport.prototype.setHandlers = function () { - var self = this; - - // we need to do this in a pub/sub way since the client can POST the message - // over a different socket (ie: different Transport instance) - this.store.subscribe('heartbeat-clear:' + this.id, function () { - self.onHeartbeatClear(); - }); - - this.store.subscribe('disconnect-force:' + this.id, function () { - self.onForcedDisconnect(); - }); - - this.store.subscribe('dispatch:' + this.id, function (packet, volatile) { - self.onDispatch(packet, volatile); - }); - - this.bound = { - end: this.onSocketEnd.bind(this) - , close: this.onSocketClose.bind(this) - , error: this.onSocketError.bind(this) - , drain: this.onSocketDrain.bind(this) - }; - - this.socket.on('end', this.bound.end); - this.socket.on('close', this.bound.close); - this.socket.on('error', this.bound.error); - this.socket.on('drain', this.bound.drain); - - this.handlersSet = true; -}; - -/** - * Removes transport handlers - * - * @api private - */ - -Transport.prototype.clearHandlers = function () { - if (this.handlersSet) { - this.store.unsubscribe('disconnect-force:' + this.id); - this.store.unsubscribe('heartbeat-clear:' + this.id); - this.store.unsubscribe('dispatch:' + this.id); - - this.socket.removeListener('end', this.bound.end); - this.socket.removeListener('close', this.bound.close); - this.socket.removeListener('error', this.bound.error); - this.socket.removeListener('drain', this.bound.drain); - } -}; - -/** - * Called when the connection dies - * - * @api private - */ - -Transport.prototype.onSocketEnd = function () { - this.end('socket end'); -}; - -/** - * Called when the connection dies - * - * @api private - */ - -Transport.prototype.onSocketClose = function (error) { - this.end(error ? 'socket error' : 'socket close'); -}; - -/** - * Called when the connection has an error. - * - * @api private - */ - -Transport.prototype.onSocketError = function (err) { - if (this.open) { - this.socket.destroy(); - this.onClose(); - } - - this.log.info('socket error ' + err.stack); -}; - -/** - * Called when the connection is drained. - * - * @api private - */ - -Transport.prototype.onSocketDrain = function () { - this.drained = true; -}; - -/** - * Called upon receiving a heartbeat packet. - * - * @api private - */ - -Transport.prototype.onHeartbeatClear = function () { - this.clearHeartbeatTimeout(); - this.setHeartbeatInterval(); -}; - -/** - * Called upon a forced disconnection. - * - * @api private - */ - -Transport.prototype.onForcedDisconnect = function () { - if (!this.disconnected) { - this.log.info('transport end by forced client disconnection'); - if (this.open) { - this.packet({ type: 'disconnect' }); - } - this.end('booted'); - } -}; - -/** - * Dispatches a packet. - * - * @api private - */ - -Transport.prototype.onDispatch = function (packet, volatile) { - if (volatile) { - this.writeVolatile(packet); - } else { - this.write(packet); - } -}; - -/** - * Sets the close timeout. - */ - -Transport.prototype.setCloseTimeout = function () { - if (!this.closeTimeout) { - var self = this; - - this.closeTimeout = setTimeout(function () { - self.log.debug('fired close timeout for client', self.id); - self.closeTimeout = null; - self.end('close timeout'); - }, this.manager.get('close timeout') * 1000); - - this.log.debug('set close timeout for client', this.id); - } -}; - -/** - * Clears the close timeout. - */ - -Transport.prototype.clearCloseTimeout = function () { - if (this.closeTimeout) { - clearTimeout(this.closeTimeout); - this.closeTimeout = null; - - this.log.debug('cleared close timeout for client', this.id); - } -}; - -/** - * Sets the heartbeat timeout - */ - -Transport.prototype.setHeartbeatTimeout = function () { - if (!this.heartbeatTimeout && this.manager.enabled('heartbeats')) { - var self = this; - - this.heartbeatTimeout = setTimeout(function () { - self.log.debug('fired heartbeat timeout for client', self.id); - self.heartbeatTimeout = null; - self.end('heartbeat timeout'); - }, this.manager.get('heartbeat timeout') * 1000); - - this.log.debug('set heartbeat timeout for client', this.id); - } -}; - -/** - * Clears the heartbeat timeout - * - * @param text - */ - -Transport.prototype.clearHeartbeatTimeout = function () { - if (this.heartbeatTimeout && this.manager.enabled('heartbeats')) { - clearTimeout(this.heartbeatTimeout); - this.heartbeatTimeout = null; - this.log.debug('cleared heartbeat timeout for client', this.id); - } -}; - -/** - * Sets the heartbeat interval. To be called when a connection opens and when - * a heartbeat is received. - * - * @api private - */ - -Transport.prototype.setHeartbeatInterval = function () { - if (!this.heartbeatInterval && this.manager.enabled('heartbeats')) { - var self = this; - - this.heartbeatInterval = setTimeout(function () { - self.heartbeat(); - self.heartbeatInterval = null; - }, this.manager.get('heartbeat interval') * 1000); - - this.log.debug('set heartbeat interval for client', this.id); - } -}; - -/** - * Clears all timeouts. - * - * @api private - */ - -Transport.prototype.clearTimeouts = function () { - this.clearCloseTimeout(); - this.clearHeartbeatTimeout(); - this.clearHeartbeatInterval(); -}; - -/** - * Sends a heartbeat - * - * @api private - */ - -Transport.prototype.heartbeat = function () { - if (this.open) { - this.log.debug('emitting heartbeat for client', this.id); - this.packet({ type: 'heartbeat' }); - this.setHeartbeatTimeout(); - } - - return this; -}; - -/** - * Handles a message. - * - * @param {Object} packet object - * @api private - */ - -Transport.prototype.onMessage = function (packet) { - var current = this.manager.transports[this.id]; - - if ('heartbeat' == packet.type) { - this.log.debug('got heartbeat packet'); - - if (current && current.open) { - current.onHeartbeatClear(); - } else { - this.store.publish('heartbeat-clear:' + this.id); - } - } else { - if ('disconnect' == packet.type && packet.endpoint == '') { - this.log.debug('got disconnection packet'); - - if (current) { - current.onForcedDisconnect(); - } else { - this.store.publish('disconnect-force:' + this.id); - } - - return; - } - - if (packet.id && packet.ack != 'data') { - this.log.debug('acknowledging packet automatically'); - - var ack = parser.encodePacket({ - type: 'ack' - , ackId: packet.id - , endpoint: packet.endpoint || '' - }); - - if (current && current.open) { - current.onDispatch(ack); - } else { - this.manager.onClientDispatch(this.id, ack); - this.store.publish('dispatch:' + this.id, ack); - } - } - - // handle packet locally or publish it - if (current) { - this.manager.onClientMessage(this.id, packet); - } else { - this.store.publish('message:' + this.id, packet); - } - } -}; - -/** - * Clears the heartbeat interval - * - * @api private - */ - -Transport.prototype.clearHeartbeatInterval = function () { - if (this.heartbeatInterval && this.manager.enabled('heartbeats')) { - clearTimeout(this.heartbeatInterval); - this.heartbeatInterval = null; - this.log.debug('cleared heartbeat interval for client', this.id); - } -}; - -/** - * Finishes the connection and makes sure client doesn't reopen - * - * @api private - */ - -Transport.prototype.disconnect = function (reason) { - this.packet({ type: 'disconnect' }); - this.end(reason); - - return this; -}; - -/** - * Closes the connection. - * - * @api private - */ - -Transport.prototype.close = function () { - if (this.open) { - this.doClose(); - this.onClose(); - } -}; - -/** - * Called upon a connection close. - * - * @api private - */ - -Transport.prototype.onClose = function () { - if (this.open) { - this.setCloseTimeout(); - this.clearHandlers(); - this.open = false; - this.manager.onClose(this.id); - this.store.publish('close', this.id); - } -}; - -/** - * Cleans up the connection, considers the client disconnected. - * - * @api private - */ - -Transport.prototype.end = function (reason) { - if (!this.disconnected) { - this.log.info('transport end'); - - var local = this.manager.transports[this.id]; - - this.close(); - this.clearTimeouts(); - this.disconnected = true; - - if (local) { - this.manager.onClientDisconnect(this.id, reason, true); - } else { - this.store.publish('disconnect:' + this.id, reason); - } - } -}; - -/** - * Signals that the transport should pause and buffer data. - * - * @api public - */ - -Transport.prototype.discard = function () { - this.log.debug('discarding transport'); - this.discarded = true; - this.clearTimeouts(); - this.clearHandlers(); - - return this; -}; - -/** - * Writes an error packet with the specified reason and advice. - * - * @param {Number} advice - * @param {Number} reason - * @api public - */ - -Transport.prototype.error = function (reason, advice) { - this.packet({ - type: 'error' - , reason: reason - , advice: advice - }); - - this.log.warn(reason, advice ? ('client should ' + advice) : ''); - this.end('error'); -}; - -/** - * Write a packet. - * - * @api public - */ - -Transport.prototype.packet = function (obj) { - return this.write(parser.encodePacket(obj)); -}; - -/** - * Writes a volatile message. - * - * @api private - */ - -Transport.prototype.writeVolatile = function (msg) { - if (this.open) { - if (this.drained) { - this.write(msg); - } else { - this.log.debug('ignoring volatile packet, buffer not drained'); - } - } else { - this.log.debug('ignoring volatile packet, transport not open'); - } -}; diff --git a/node_modules/socket.io/lib/transports/flashsocket.js b/node_modules/socket.io/lib/transports/flashsocket.js deleted file mode 100644 index d79363d..0000000 --- a/node_modules/socket.io/lib/transports/flashsocket.js +++ /dev/null @@ -1,106 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ -var WebSocket = require('./websocket'); - -/** - * Export the constructor. - */ - -exports = module.exports = FlashSocket; - -/** - * The FlashSocket transport is just a proxy - * for WebSocket connections. - * - * @api public - */ - -function FlashSocket (mng, data, req) { - return WebSocket.call(this, mng, data, req); -} - -/** - * Inherits from WebSocket. - */ - -FlashSocket.prototype.__proto__ = WebSocket.prototype; - -/** - * Transport name - * - * @api public - */ - -FlashSocket.prototype.name = 'flashsocket'; - -/** - * Listens for new configuration changes of the Manager - * this way we can enable and disable the flash server. - * - * @param {Manager} Manager instance. - * @api private - */ - - -FlashSocket.init = function (manager) { - var server; - function create () { - server = require('policyfile').createServer({ - log: function(msg){ - manager.log.info(msg.toLowerCase()); - } - }, manager.get('origins')); - - server.on('close', function (e) { - server = null; - }); - - server.listen(manager.get('flash policy port'), manager.server); - - manager.flashPolicyServer = server; - } - - // listen for origin changes, so we can update the server - manager.on('set:origins', function (value, key) { - if (!server) return; - - // update the origins and compile a new response buffer - server.origins = Array.isArray(value) ? value : [value]; - server.compile(); - }); - - // destory the server and create a new server - manager.on('set:flash policy port', function (value, key) { - var transports = manager.get('transports'); - if (~transports.indexOf('flashsocket')) { - if (server) { - if (server.port === value) return; - // destroy the server and rebuild it on a new port - try { - server.close(); - } - catch (e) { /* ignore exception. could e.g. be that the server isn't started yet */ } - } - create(); - } - }); - - // only start the server - manager.on('set:transports', function (value, key){ - if (!server && ~manager.get('transports').indexOf('flashsocket')) { - create(); - } - }); - // check if we need to initialize at start - if (~manager.get('transports').indexOf('flashsocket')){ - create(); - } -}; diff --git a/node_modules/socket.io/lib/transports/htmlfile.js b/node_modules/socket.io/lib/transports/htmlfile.js deleted file mode 100644 index e8709a3..0000000 --- a/node_modules/socket.io/lib/transports/htmlfile.js +++ /dev/null @@ -1,82 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var HTTPTransport = require('./http'); - -/** - * Export the constructor. - */ - -exports = module.exports = HTMLFile; - -/** - * HTMLFile transport constructor. - * - * @api public - */ - -function HTMLFile (mng, data, req) { - HTTPTransport.call(this, mng, data, req); -}; - -/** - * Inherits from Transport. - */ - -HTMLFile.prototype.__proto__ = HTTPTransport.prototype; - -/** - * Transport name - * - * @api public - */ - -HTMLFile.prototype.name = 'htmlfile'; - -/** - * Handles the request. - * - * @api private - */ - -HTMLFile.prototype.handleRequest = function (req) { - HTTPTransport.prototype.handleRequest.call(this, req); - - if (req.method == 'GET') { - req.res.writeHead(200, { - 'Content-Type': 'text/html; charset=UTF-8' - , 'Connection': 'keep-alive' - , 'Transfer-Encoding': 'chunked' - }); - - req.res.write( - '' - + '' - + new Array(174).join(' ') - ); - } -}; - -/** - * Performs the write. - * - * @api private - */ - -HTMLFile.prototype.write = function (data) { - data = ''; - - if (this.response.write(data)) { - this.drained = true; - } - - this.log.debug(this.name + ' writing', data); -}; diff --git a/node_modules/socket.io/lib/transports/http-polling.js b/node_modules/socket.io/lib/transports/http-polling.js deleted file mode 100644 index c71fc9c..0000000 --- a/node_modules/socket.io/lib/transports/http-polling.js +++ /dev/null @@ -1,135 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var HTTPTransport = require('./http'); - -/** - * Exports the constructor. - */ - -exports = module.exports = HTTPPolling; - -/** - * HTTP polling constructor. - * - * @api public. - */ - -function HTTPPolling (mng, data, req) { - HTTPTransport.call(this, mng, data, req); -}; - -/** - * Inherits from HTTPTransport. - * - * @api public. - */ - -HTTPPolling.prototype.__proto__ = HTTPTransport.prototype; - -/** - * Transport name - * - * @api public - */ - -HTTPPolling.prototype.name = 'httppolling'; - -/** - * Removes heartbeat timeouts for polling. - */ - -HTTPPolling.prototype.setHeartbeatInterval = function () { - return this; -}; - -/** - * Handles a request - * - * @api private - */ - -HTTPPolling.prototype.handleRequest = function (req) { - HTTPTransport.prototype.handleRequest.call(this, req); - - if (req.method == 'GET') { - var self = this; - - this.pollTimeout = setTimeout(function () { - self.packet({ type: 'noop' }); - self.log.debug(self.name + ' closed due to exceeded duration'); - }, this.manager.get('polling duration') * 1000); - - this.log.debug('setting poll timeout'); - } -}; - -/** - * Clears polling timeout - * - * @api private - */ - -HTTPPolling.prototype.clearPollTimeout = function () { - if (this.pollTimeout) { - clearTimeout(this.pollTimeout); - this.pollTimeout = null; - this.log.debug('clearing poll timeout'); - } - - return this; -}; - -/** - * Override clear timeouts to clear the poll timeout - * - * @api private - */ - -HTTPPolling.prototype.clearTimeouts = function () { - HTTPTransport.prototype.clearTimeouts.call(this); - - this.clearPollTimeout(); -}; - -/** - * doWrite to clear poll timeout - * - * @api private - */ - -HTTPPolling.prototype.doWrite = function () { - this.clearPollTimeout(); -}; - -/** - * Performs a write. - * - * @api private. - */ - -HTTPPolling.prototype.write = function (data, close) { - this.doWrite(data); - this.response.end(); - this.onClose(); -}; - -/** - * Override end. - * - * @api private - */ - -HTTPPolling.prototype.end = function () { - this.clearPollTimeout(); - return HTTPTransport.prototype.end.call(this); -}; - diff --git a/node_modules/socket.io/lib/transports/http.js b/node_modules/socket.io/lib/transports/http.js deleted file mode 100644 index d5be006..0000000 --- a/node_modules/socket.io/lib/transports/http.js +++ /dev/null @@ -1,111 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var Transport = require('../transport') - , parser = require('../parser') - , qs = require('querystring'); - -/** - * Export the constructor. - */ - -exports = module.exports = HTTPTransport; - -/** - * HTTP interface constructor. For all non-websocket transports. - * - * @api public - */ - -function HTTPTransport (mng, data, req) { - Transport.call(this, mng, data, req); -}; - -/** - * Inherits from Transport. - */ - -HTTPTransport.prototype.__proto__ = Transport.prototype; - -/** - * Handles a request. - * - * @api private - */ - -HTTPTransport.prototype.handleRequest = function (req) { - if (req.method == 'POST') { - var buffer = '' - , res = req.res - , origin = req.headers.origin - , headers = { 'Content-Length': 1 } - , self = this; - - req.on('data', function (data) { - buffer += data; - }); - - req.on('end', function () { - res.writeHead(200, headers); - res.end('1'); - - self.onData(self.postEncoded ? qs.parse(buffer).d : buffer); - }); - - if (origin) { - // https://developer.mozilla.org/En/HTTP_Access_Control - headers['Access-Control-Allow-Origin'] = '*'; - - if (req.headers.cookie) { - headers['Access-Control-Allow-Credentials'] = 'true'; - } - } - } else { - this.response = req.res; - - Transport.prototype.handleRequest.call(this, req); - } -}; - -/** - * Handles data payload. - * - * @api private - */ - -HTTPTransport.prototype.onData = function (data) { - var messages = parser.decodePayload(data); - this.log.debug(this.name + ' received data packet', data); - - for (var i = 0, l = messages.length; i < l; i++) { - this.onMessage(messages[i]); - } -}; - -/** - * Closes the request-response cycle - * - * @api private - */ - -HTTPTransport.prototype.doClose = function () { - this.response.end(); -}; - -/** - * Writes a payload of messages - * - * @api private - */ - -HTTPTransport.prototype.payload = function (msgs) { - this.write(parser.encodePayload(msgs)); -}; diff --git a/node_modules/socket.io/lib/transports/index.js b/node_modules/socket.io/lib/transports/index.js deleted file mode 100644 index b865559..0000000 --- a/node_modules/socket.io/lib/transports/index.js +++ /dev/null @@ -1,12 +0,0 @@ - -/** - * Export transports. - */ - -module.exports = { - websocket: require('./websocket') - , flashsocket: require('./flashsocket') - , htmlfile: require('./htmlfile') - , 'xhr-polling': require('./xhr-polling') - , 'jsonp-polling': require('./jsonp-polling') -}; diff --git a/node_modules/socket.io/lib/transports/jsonp-polling.js b/node_modules/socket.io/lib/transports/jsonp-polling.js deleted file mode 100644 index 83d11b8..0000000 --- a/node_modules/socket.io/lib/transports/jsonp-polling.js +++ /dev/null @@ -1,96 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var HTTPPolling = require('./http-polling'); - -/** - * Export the constructor. - */ - -exports = module.exports = JSONPPolling; - -/** - * JSON-P polling transport. - * - * @api public - */ - -function JSONPPolling (mng, data, req) { - HTTPPolling.call(this, mng, data, req); - - this.head = 'io.j[0]('; - this.foot = ');'; - - if (data.query.i) { - this.head = 'io.j[' + data.query.i + ']('; - } -}; - -/** - * Inherits from Transport. - */ - -JSONPPolling.prototype.__proto__ = HTTPPolling.prototype; - -/** - * Transport name - * - * @api public - */ - -JSONPPolling.prototype.name = 'jsonppolling'; - -/** - * Make sure POST are decoded. - */ - -JSONPPolling.prototype.postEncoded = true; - -/** - * Handles incoming data. - * Due to a bug in \n handling by browsers, we expect a JSONified string. - * - * @api private - */ - -JSONPPolling.prototype.onData = function (data) { - try { - data = JSON.parse(data); - } catch (e) { - this.error('parse', 'reconnect'); - return; - } - - HTTPPolling.prototype.onData.call(this, data); -}; - -/** - * Performs the write. - * - * @api private - */ - -JSONPPolling.prototype.doWrite = function (data) { - HTTPPolling.prototype.doWrite.call(this); - - var data = data === undefined - ? '' : this.head + JSON.stringify(data) + this.foot; - - this.response.writeHead(200, { - 'Content-Type': 'text/javascript; charset=UTF-8' - , 'Content-Length': Buffer.byteLength(data) - , 'Connection': 'Keep-Alive' - , 'X-XSS-Protection': '0' - }); - - this.response.write(data); - this.log.debug(this.name + ' writing', data); -}; diff --git a/node_modules/socket.io/lib/transports/websocket.js b/node_modules/socket.io/lib/transports/websocket.js deleted file mode 100644 index 78a4304..0000000 --- a/node_modules/socket.io/lib/transports/websocket.js +++ /dev/null @@ -1,36 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var protocolVersions = require('./websocket/'); - -/** - * Export the constructor. - */ - -exports = module.exports = WebSocket; - -/** - * HTTP interface constructor. Interface compatible with all transports that - * depend on request-response cycles. - * - * @api public - */ - -function WebSocket (mng, data, req) { - var transport - , version = req.headers['sec-websocket-version']; - if (typeof version !== 'undefined' && typeof protocolVersions[version] !== 'undefined') { - transport = new protocolVersions[version](mng, data, req); - } - else transport = new protocolVersions['default'](mng, data, req); - if (typeof this.name !== 'undefined') transport.name = this.name; - return transport; -}; diff --git a/node_modules/socket.io/lib/transports/websocket/default.js b/node_modules/socket.io/lib/transports/websocket/default.js deleted file mode 100644 index 524e34e..0000000 --- a/node_modules/socket.io/lib/transports/websocket/default.js +++ /dev/null @@ -1,358 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var Transport = require('../../transport') - , EventEmitter = process.EventEmitter - , crypto = require('crypto') - , parser = require('../../parser'); - -/** - * Export the constructor. - */ - -exports = module.exports = WebSocket; - -/** - * HTTP interface constructor. Interface compatible with all transports that - * depend on request-response cycles. - * - * @api public - */ - -function WebSocket (mng, data, req) { - // parser - var self = this; - - this.parser = new Parser(); - this.parser.on('data', function (packet) { - self.log.debug(self.name + ' received data packet', packet); - self.onMessage(parser.decodePacket(packet)); - }); - this.parser.on('close', function () { - self.end(); - }); - this.parser.on('error', function () { - self.end(); - }); - - Transport.call(this, mng, data, req); -}; - -/** - * Inherits from Transport. - */ - -WebSocket.prototype.__proto__ = Transport.prototype; - -/** - * Transport name - * - * @api public - */ - -WebSocket.prototype.name = 'websocket'; - -/** - * Websocket draft version - * - * @api public - */ - -WebSocket.prototype.protocolVersion = 'hixie-76'; - -/** - * Called when the socket connects. - * - * @api private - */ - -WebSocket.prototype.onSocketConnect = function () { - var self = this; - - this.socket.setNoDelay(true); - - this.buffer = true; - this.buffered = []; - - if (this.req.headers.upgrade !== 'WebSocket') { - this.log.warn(this.name + ' connection invalid'); - this.end(); - return; - } - - var origin = this.req.headers.origin - , location = (this.socket.encrypted ? 'wss' : 'ws') - + '://' + this.req.headers.host + this.req.url - , waitingForNonce = false; - - if (this.req.headers['sec-websocket-key1']) { - // If we don't have the nonce yet, wait for it (HAProxy compatibility). - if (! (this.req.head && this.req.head.length >= 8)) { - waitingForNonce = true; - } - - var headers = [ - 'HTTP/1.1 101 WebSocket Protocol Handshake' - , 'Upgrade: WebSocket' - , 'Connection: Upgrade' - , 'Sec-WebSocket-Origin: ' + origin - , 'Sec-WebSocket-Location: ' + location - ]; - - if (this.req.headers['sec-websocket-protocol']){ - headers.push('Sec-WebSocket-Protocol: ' - + this.req.headers['sec-websocket-protocol']); - } - } else { - var headers = [ - 'HTTP/1.1 101 Web Socket Protocol Handshake' - , 'Upgrade: WebSocket' - , 'Connection: Upgrade' - , 'WebSocket-Origin: ' + origin - , 'WebSocket-Location: ' + location - ]; - } - - try { - this.socket.write(headers.concat('', '').join('\r\n')); - this.socket.setTimeout(0); - this.socket.setNoDelay(true); - this.socket.setEncoding('utf8'); - } catch (e) { - this.end(); - return; - } - - if (waitingForNonce) { - this.socket.setEncoding('binary'); - } else if (this.proveReception(headers)) { - self.flush(); - } - - var headBuffer = ''; - - this.socket.on('data', function (data) { - if (waitingForNonce) { - headBuffer += data; - - if (headBuffer.length < 8) { - return; - } - - // Restore the connection to utf8 encoding after receiving the nonce - self.socket.setEncoding('utf8'); - waitingForNonce = false; - - // Stuff the nonce into the location where it's expected to be - self.req.head = headBuffer.substr(0, 8); - headBuffer = ''; - - if (self.proveReception(headers)) { - self.flush(); - } - - return; - } - - self.parser.add(data); - }); -}; - -/** - * Writes to the socket. - * - * @api private - */ - -WebSocket.prototype.write = function (data) { - if (this.open) { - this.drained = false; - - if (this.buffer) { - this.buffered.push(data); - return this; - } - - var length = Buffer.byteLength(data) - , buffer = new Buffer(2 + length); - - buffer.write('\x00', 'binary'); - buffer.write(data, 1, 'utf8'); - buffer.write('\xff', 1 + length, 'binary'); - - try { - if (this.socket.write(buffer)) { - this.drained = true; - } - } catch (e) { - this.end(); - } - - this.log.debug(this.name + ' writing', data); - } -}; - -/** - * Flushes the internal buffer - * - * @api private - */ - -WebSocket.prototype.flush = function () { - this.buffer = false; - - for (var i = 0, l = this.buffered.length; i < l; i++) { - this.write(this.buffered.splice(0, 1)[0]); - } -}; - -/** - * Finishes the handshake. - * - * @api private - */ - -WebSocket.prototype.proveReception = function (headers) { - var self = this - , k1 = this.req.headers['sec-websocket-key1'] - , k2 = this.req.headers['sec-websocket-key2']; - - if (k1 && k2){ - var md5 = crypto.createHash('md5'); - - [k1, k2].forEach(function (k) { - var n = parseInt(k.replace(/[^\d]/g, '')) - , spaces = k.replace(/[^ ]/g, '').length; - - if (spaces === 0 || n % spaces !== 0){ - self.log.warn('Invalid ' + self.name + ' key: "' + k + '".'); - self.end(); - return false; - } - - n /= spaces; - - md5.update(String.fromCharCode( - n >> 24 & 0xFF, - n >> 16 & 0xFF, - n >> 8 & 0xFF, - n & 0xFF)); - }); - - md5.update(this.req.head.toString('binary')); - - try { - this.socket.write(md5.digest('binary'), 'binary'); - } catch (e) { - this.end(); - } - } - - return true; -}; - -/** - * Writes a payload. - * - * @api private - */ - -WebSocket.prototype.payload = function (msgs) { - for (var i = 0, l = msgs.length; i < l; i++) { - this.write(msgs[i]); - } - - return this; -}; - -/** - * Closes the connection. - * - * @api private - */ - -WebSocket.prototype.doClose = function () { - this.socket.end(); -}; - -/** - * WebSocket parser - * - * @api public - */ - -function Parser () { - this.buffer = ''; - this.i = 0; -}; - -/** - * Inherits from EventEmitter. - */ - -Parser.prototype.__proto__ = EventEmitter.prototype; - -/** - * Adds data to the buffer. - * - * @api public - */ - -Parser.prototype.add = function (data) { - this.buffer += data; - this.parse(); -}; - -/** - * Parses the buffer. - * - * @api private - */ - -Parser.prototype.parse = function () { - for (var i = this.i, chr, l = this.buffer.length; i < l; i++){ - chr = this.buffer[i]; - - if (this.buffer.length == 2 && this.buffer[1] == '\u0000') { - this.emit('close'); - this.buffer = ''; - this.i = 0; - return; - } - - if (i === 0){ - if (chr != '\u0000') - this.error('Bad framing. Expected null byte as first frame'); - else - continue; - } - - if (chr == '\ufffd'){ - this.emit('data', this.buffer.substr(1, i - 1)); - this.buffer = this.buffer.substr(i + 1); - this.i = 0; - return this.parse(); - } - } -}; - -/** - * Handles an error - * - * @api private - */ - -Parser.prototype.error = function (reason) { - this.buffer = ''; - this.i = 0; - this.emit('error', reason); - return this; -}; diff --git a/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js b/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js deleted file mode 100644 index ade99dc..0000000 --- a/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js +++ /dev/null @@ -1,617 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var Transport = require('../../transport') - , EventEmitter = process.EventEmitter - , crypto = require('crypto') - , url = require('url') - , parser = require('../../parser') - , util = require('../../util'); - -/** - * Export the constructor. - */ - -exports = module.exports = WebSocket; -exports.Parser = Parser; - -/** - * HTTP interface constructor. Interface compatible with all transports that - * depend on request-response cycles. - * - * @api public - */ - -function WebSocket (mng, data, req) { - // parser - var self = this; - - this.manager = mng; - this.parser = new Parser(); - this.parser.on('data', function (packet) { - self.onMessage(parser.decodePacket(packet)); - }); - this.parser.on('ping', function () { - // version 8 ping => pong - try { - self.socket.write('\u008a\u0000'); - } - catch (e) { - self.end(); - return; - } - }); - this.parser.on('close', function () { - self.end(); - }); - this.parser.on('error', function (reason) { - self.log.warn(self.name + ' parser error: ' + reason); - self.end(); - }); - - Transport.call(this, mng, data, req); -}; - -/** - * Inherits from Transport. - */ - -WebSocket.prototype.__proto__ = Transport.prototype; - -/** - * Transport name - * - * @api public - */ - -WebSocket.prototype.name = 'websocket'; - -/** - * Websocket draft version - * - * @api public - */ - -WebSocket.prototype.protocolVersion = '07-12'; - -/** - * Called when the socket connects. - * - * @api private - */ - -WebSocket.prototype.onSocketConnect = function () { - var self = this; - - if (typeof this.req.headers.upgrade === 'undefined' || - this.req.headers.upgrade.toLowerCase() !== 'websocket') { - this.log.warn(this.name + ' connection invalid'); - this.end(); - return; - } - - var origin = this.req.headers['sec-websocket-origin'] - , location = (this.socket.encrypted ? 'wss' : 'ws') - + '://' + this.req.headers.host + this.req.url; - - if (!this.verifyOrigin(origin)) { - this.log.warn(this.name + ' connection invalid: origin mismatch'); - this.end(); - return; - } - - if (!this.req.headers['sec-websocket-key']) { - this.log.warn(this.name + ' connection invalid: received no key'); - this.end(); - return; - } - - // calc key - var key = this.req.headers['sec-websocket-key']; - var shasum = crypto.createHash('sha1'); - shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); - key = shasum.digest('base64'); - - var headers = [ - 'HTTP/1.1 101 Switching Protocols' - , 'Upgrade: websocket' - , 'Connection: Upgrade' - , 'Sec-WebSocket-Accept: ' + key - ]; - - try { - this.socket.write(headers.concat('', '').join('\r\n')); - this.socket.setTimeout(0); - this.socket.setNoDelay(true); - } catch (e) { - this.end(); - return; - } - - this.socket.on('data', function (data) { - self.parser.add(data); - }); -}; - -/** - * Verifies the origin of a request. - * - * @api private - */ - -WebSocket.prototype.verifyOrigin = function (origin) { - var origins = this.manager.get('origins'); - - if (origin === 'null') origin = '*'; - - if (origins.indexOf('*:*') !== -1) { - return true; - } - - if (origin) { - try { - var parts = url.parse(origin); - var ok = - ~origins.indexOf(parts.hostname + ':' + parts.port) || - ~origins.indexOf(parts.hostname + ':*') || - ~origins.indexOf('*:' + parts.port); - if (!ok) this.log.warn('illegal origin: ' + origin); - return ok; - } catch (ex) { - this.log.warn('error parsing origin'); - } - } - else { - this.log.warn('origin missing from websocket call, yet required by config'); - } - return false; -}; - -/** - * Writes to the socket. - * - * @api private - */ - -WebSocket.prototype.write = function (data) { - if (this.open) { - var buf = this.frame(0x81, data); - try { - this.socket.write(buf, 'binary'); - } - catch (e) { - this.end(); - return; - } - this.log.debug(this.name + ' writing', data); - } -}; - -/** - * Writes a payload. - * - * @api private - */ - -WebSocket.prototype.payload = function (msgs) { - for (var i = 0, l = msgs.length; i < l; i++) { - this.write(msgs[i]); - } - - return this; -}; - -/** - * Frame server-to-client output as a text packet. - * - * @api private - */ - -WebSocket.prototype.frame = function (opcode, str) { - var dataBuffer = new Buffer(str) - , dataLength = dataBuffer.length - , startOffset = 2 - , secondByte = dataLength; - if (dataLength > 65536) { - startOffset = 10; - secondByte = 127; - } - else if (dataLength > 125) { - startOffset = 4; - secondByte = 126; - } - var outputBuffer = new Buffer(dataLength + startOffset); - outputBuffer[0] = opcode; - outputBuffer[1] = secondByte; - dataBuffer.copy(outputBuffer, startOffset); - switch (secondByte) { - case 126: - outputBuffer[2] = dataLength >>> 8; - outputBuffer[3] = dataLength % 256; - break; - case 127: - var l = dataLength; - for (var i = 1; i <= 8; ++i) { - outputBuffer[startOffset - i] = l & 0xff; - l >>>= 8; - } - } - return outputBuffer; -}; - -/** - * Closes the connection. - * - * @api private - */ - -WebSocket.prototype.doClose = function () { - this.socket.end(); -}; - -/** - * WebSocket parser - * - * @api public - */ - -function Parser () { - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0 - }; - this.overflow = null; - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.currentMessage = ''; - - var self = this; - this.opcodeHandlers = { - // text - '1': function(data) { - var finish = function(mask, data) { - self.currentMessage += self.unmask(mask, data); - if (self.state.lastFragment) { - self.emit('data', self.currentMessage); - self.currentMessage = ''; - } - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - if (util.unpack(data.slice(0, 4)) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported'); - return; - } - var lengthBytes = data.slice(4); // note: cap to 32 bit length - expectData(util.unpack(data)); - }); - } - }, - // binary - '2': function(data) { - var finish = function(mask, data) { - if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list - self.currentMessage.push(self.unmask(mask, data, true)); - if (self.state.lastFragment) { - self.emit('binary', self.concatBuffers(self.currentMessage)); - self.currentMessage = ''; - } - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - if (util.unpack(data.slice(0, 4)) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported'); - return; - } - var lengthBytes = data.slice(4); // note: cap to 32 bit length - expectData(util.unpack(data)); - }); - } - }, - // close - '8': function(data) { - self.emit('close'); - self.reset(); - }, - // ping - '9': function(data) { - if (self.state.lastFragment == false) { - self.error('fragmented ping is not supported'); - return; - } - - var finish = function(mask, data) { - self.emit('ping', self.unmask(mask, data)); - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength == 0) { - finish(null, null); - } - else if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - expectData(util.unpack(data)); - }); - } - } - } - - this.expect('Opcode', 2, this.processPacket); -}; - -/** - * Inherits from EventEmitter. - */ - -Parser.prototype.__proto__ = EventEmitter.prototype; - -/** - * Add new data to the parser. - * - * @api public - */ - -Parser.prototype.add = function(data) { - if (this.expectBuffer == null) { - this.addToOverflow(data); - return; - } - var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset); - data.copy(this.expectBuffer, this.expectOffset, 0, toRead); - this.expectOffset += toRead; - if (toRead < data.length) { - // at this point the overflow buffer shouldn't at all exist - this.overflow = new Buffer(data.length - toRead); - data.copy(this.overflow, 0, toRead, toRead + this.overflow.length); - } - if (this.expectOffset == this.expectBuffer.length) { - var bufferForHandler = this.expectBuffer; - this.expectBuffer = null; - this.expectOffset = 0; - this.expectHandler.call(this, bufferForHandler); - } -} - -/** - * Adds a piece of data to the overflow. - * - * @api private - */ - -Parser.prototype.addToOverflow = function(data) { - if (this.overflow == null) this.overflow = data; - else { - var prevOverflow = this.overflow; - this.overflow = new Buffer(this.overflow.length + data.length); - prevOverflow.copy(this.overflow, 0); - data.copy(this.overflow, prevOverflow.length); - } -} - -/** - * Waits for a certain amount of bytes to be available, then fires a callback. - * - * @api private - */ - -Parser.prototype.expect = function(what, length, handler) { - this.expectBuffer = new Buffer(length); - this.expectOffset = 0; - this.expectHandler = handler; - if (this.overflow != null) { - var toOverflow = this.overflow; - this.overflow = null; - this.add(toOverflow); - } -} - -/** - * Start processing a new packet. - * - * @api private - */ - -Parser.prototype.processPacket = function (data) { - if ((data[0] & 0x70) != 0) this.error('reserved fields must be empty'); - this.state.lastFragment = (data[0] & 0x80) == 0x80; - this.state.masked = (data[1] & 0x80) == 0x80; - var opcode = data[0] & 0xf; - if (opcode == 0) { - // continuation frame - this.state.opcode = this.state.activeFragmentedOperation; - if (!(this.state.opcode == 1 || this.state.opcode == 2)) { - this.error('continuation frame cannot follow current opcode') - return; - } - } - else { - this.state.opcode = opcode; - if (this.state.lastFragment === false) { - this.state.activeFragmentedOperation = opcode; - } - } - var handler = this.opcodeHandlers[this.state.opcode]; - if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode); - else handler(data); -} - -/** - * Endprocessing a packet. - * - * @api private - */ - -Parser.prototype.endPacket = function() { - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - if (this.state.lastFragment && this.state.opcode == this.state.activeFragmentedOperation) { - // end current fragmented operation - this.state.activeFragmentedOperation = null; - } - this.state.lastFragment = false; - this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; - this.state.masked = false; - this.expect('Opcode', 2, this.processPacket); -} - -/** - * Reset the parser state. - * - * @api private - */ - -Parser.prototype.reset = function() { - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0 - }; - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.overflow = null; - this.currentMessage = ''; -} - -/** - * Unmask received data. - * - * @api private - */ - -Parser.prototype.unmask = function (mask, buf, binary) { - if (mask != null) { - for (var i = 0, ll = buf.length; i < ll; i++) { - buf[i] ^= mask[i % 4]; - } - } - if (binary) return buf; - return buf != null ? buf.toString('utf8') : ''; -} - -/** - * Concatenates a list of buffers. - * - * @api private - */ - -Parser.prototype.concatBuffers = function(buffers) { - var length = 0; - for (var i = 0, l = buffers.length; i < l; ++i) { - length += buffers[i].length; - } - var mergedBuffer = new Buffer(length); - var offset = 0; - for (var i = 0, l = buffers.length; i < l; ++i) { - buffers[i].copy(mergedBuffer, offset); - offset += buffers[i].length; - } - return mergedBuffer; -} - -/** - * Handles an error - * - * @api private - */ - -Parser.prototype.error = function (reason) { - this.reset(); - this.emit('error', reason); - return this; -}; diff --git a/node_modules/socket.io/lib/transports/websocket/hybi-16.js b/node_modules/socket.io/lib/transports/websocket/hybi-16.js deleted file mode 100644 index 03b0183..0000000 --- a/node_modules/socket.io/lib/transports/websocket/hybi-16.js +++ /dev/null @@ -1,617 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var Transport = require('../../transport') - , EventEmitter = process.EventEmitter - , crypto = require('crypto') - , url = require('url') - , parser = require('../../parser') - , util = require('../../util'); - -/** - * Export the constructor. - */ - -exports = module.exports = WebSocket; -exports.Parser = Parser; - -/** - * HTTP interface constructor. Interface compatible with all transports that - * depend on request-response cycles. - * - * @api public - */ - -function WebSocket (mng, data, req) { - // parser - var self = this; - - this.manager = mng; - this.parser = new Parser(); - this.parser.on('data', function (packet) { - self.onMessage(parser.decodePacket(packet)); - }); - this.parser.on('ping', function () { - // version 8 ping => pong - try { - self.socket.write('\u008a\u0000'); - } - catch (e) { - self.end(); - return; - } - }); - this.parser.on('close', function () { - self.end(); - }); - this.parser.on('error', function (reason) { - self.log.warn(self.name + ' parser error: ' + reason); - self.end(); - }); - - Transport.call(this, mng, data, req); -}; - -/** - * Inherits from Transport. - */ - -WebSocket.prototype.__proto__ = Transport.prototype; - -/** - * Transport name - * - * @api public - */ - -WebSocket.prototype.name = 'websocket'; - -/** - * Websocket draft version - * - * @api public - */ - -WebSocket.prototype.protocolVersion = '16'; - -/** - * Called when the socket connects. - * - * @api private - */ - -WebSocket.prototype.onSocketConnect = function () { - var self = this; - - if (typeof this.req.headers.upgrade === 'undefined' || - this.req.headers.upgrade.toLowerCase() !== 'websocket') { - this.log.warn(this.name + ' connection invalid'); - this.end(); - return; - } - - var origin = this.req.headers['origin'] - , location = (this.socket.encrypted ? 'wss' : 'ws') - + '://' + this.req.headers.host + this.req.url; - - if (!this.verifyOrigin(origin)) { - this.log.warn(this.name + ' connection invalid: origin mismatch'); - this.end(); - return; - } - - if (!this.req.headers['sec-websocket-key']) { - this.log.warn(this.name + ' connection invalid: received no key'); - this.end(); - return; - } - - // calc key - var key = this.req.headers['sec-websocket-key']; - var shasum = crypto.createHash('sha1'); - shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); - key = shasum.digest('base64'); - - var headers = [ - 'HTTP/1.1 101 Switching Protocols' - , 'Upgrade: websocket' - , 'Connection: Upgrade' - , 'Sec-WebSocket-Accept: ' + key - ]; - - try { - this.socket.write(headers.concat('', '').join('\r\n')); - this.socket.setTimeout(0); - this.socket.setNoDelay(true); - } catch (e) { - this.end(); - return; - } - - this.socket.on('data', function (data) { - self.parser.add(data); - }); -}; - -/** - * Verifies the origin of a request. - * - * @api private - */ - -WebSocket.prototype.verifyOrigin = function (origin) { - var origins = this.manager.get('origins'); - - if (origin === 'null') origin = '*'; - - if (origins.indexOf('*:*') !== -1) { - return true; - } - - if (origin) { - try { - var parts = url.parse(origin); - var ok = - ~origins.indexOf(parts.hostname + ':' + parts.port) || - ~origins.indexOf(parts.hostname + ':*') || - ~origins.indexOf('*:' + parts.port); - if (!ok) this.log.warn('illegal origin: ' + origin); - return ok; - } catch (ex) { - this.log.warn('error parsing origin'); - } - } - else { - this.log.warn('origin missing from websocket call, yet required by config'); - } - return false; -}; - -/** - * Writes to the socket. - * - * @api private - */ - -WebSocket.prototype.write = function (data) { - if (this.open) { - var buf = this.frame(0x81, data); - try { - this.socket.write(buf, 'binary'); - } - catch (e) { - this.end(); - return; - } - this.log.debug(this.name + ' writing', data); - } -}; - -/** - * Writes a payload. - * - * @api private - */ - -WebSocket.prototype.payload = function (msgs) { - for (var i = 0, l = msgs.length; i < l; i++) { - this.write(msgs[i]); - } - - return this; -}; - -/** - * Frame server-to-client output as a text packet. - * - * @api private - */ - -WebSocket.prototype.frame = function (opcode, str) { - var dataBuffer = new Buffer(str) - , dataLength = dataBuffer.length - , startOffset = 2 - , secondByte = dataLength; - if (dataLength > 65536) { - startOffset = 10; - secondByte = 127; - } - else if (dataLength > 125) { - startOffset = 4; - secondByte = 126; - } - var outputBuffer = new Buffer(dataLength + startOffset); - outputBuffer[0] = opcode; - outputBuffer[1] = secondByte; - dataBuffer.copy(outputBuffer, startOffset); - switch (secondByte) { - case 126: - outputBuffer[2] = dataLength >>> 8; - outputBuffer[3] = dataLength % 256; - break; - case 127: - var l = dataLength; - for (var i = 1; i <= 8; ++i) { - outputBuffer[startOffset - i] = l & 0xff; - l >>>= 8; - } - } - return outputBuffer; -}; - -/** - * Closes the connection. - * - * @api private - */ - -WebSocket.prototype.doClose = function () { - this.socket.end(); -}; - -/** - * WebSocket parser - * - * @api public - */ - -function Parser () { - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0 - }; - this.overflow = null; - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.currentMessage = ''; - - var self = this; - this.opcodeHandlers = { - // text - '1': function(data) { - var finish = function(mask, data) { - self.currentMessage += self.unmask(mask, data); - if (self.state.lastFragment) { - self.emit('data', self.currentMessage); - self.currentMessage = ''; - } - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - if (util.unpack(data.slice(0, 4)) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported'); - return; - } - var lengthBytes = data.slice(4); // note: cap to 32 bit length - expectData(util.unpack(data)); - }); - } - }, - // binary - '2': function(data) { - var finish = function(mask, data) { - if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list - self.currentMessage.push(self.unmask(mask, data, true)); - if (self.state.lastFragment) { - self.emit('binary', self.concatBuffers(self.currentMessage)); - self.currentMessage = ''; - } - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - if (util.unpack(data.slice(0, 4)) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported'); - return; - } - var lengthBytes = data.slice(4); // note: cap to 32 bit length - expectData(util.unpack(data)); - }); - } - }, - // close - '8': function(data) { - self.emit('close'); - self.reset(); - }, - // ping - '9': function(data) { - if (self.state.lastFragment == false) { - self.error('fragmented ping is not supported'); - return; - } - - var finish = function(mask, data) { - self.emit('ping', self.unmask(mask, data)); - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength == 0) { - finish(null, null); - } - else if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - expectData(util.unpack(data)); - }); - } - } - } - - this.expect('Opcode', 2, this.processPacket); -}; - -/** - * Inherits from EventEmitter. - */ - -Parser.prototype.__proto__ = EventEmitter.prototype; - -/** - * Add new data to the parser. - * - * @api public - */ - -Parser.prototype.add = function(data) { - if (this.expectBuffer == null) { - this.addToOverflow(data); - return; - } - var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset); - data.copy(this.expectBuffer, this.expectOffset, 0, toRead); - this.expectOffset += toRead; - if (toRead < data.length) { - // at this point the overflow buffer shouldn't at all exist - this.overflow = new Buffer(data.length - toRead); - data.copy(this.overflow, 0, toRead, toRead + this.overflow.length); - } - if (this.expectOffset == this.expectBuffer.length) { - var bufferForHandler = this.expectBuffer; - this.expectBuffer = null; - this.expectOffset = 0; - this.expectHandler.call(this, bufferForHandler); - } -} - -/** - * Adds a piece of data to the overflow. - * - * @api private - */ - -Parser.prototype.addToOverflow = function(data) { - if (this.overflow == null) this.overflow = data; - else { - var prevOverflow = this.overflow; - this.overflow = new Buffer(this.overflow.length + data.length); - prevOverflow.copy(this.overflow, 0); - data.copy(this.overflow, prevOverflow.length); - } -} - -/** - * Waits for a certain amount of bytes to be available, then fires a callback. - * - * @api private - */ - -Parser.prototype.expect = function(what, length, handler) { - this.expectBuffer = new Buffer(length); - this.expectOffset = 0; - this.expectHandler = handler; - if (this.overflow != null) { - var toOverflow = this.overflow; - this.overflow = null; - this.add(toOverflow); - } -} - -/** - * Start processing a new packet. - * - * @api private - */ - -Parser.prototype.processPacket = function (data) { - if ((data[0] & 0x70) != 0) this.error('reserved fields must be empty'); - this.state.lastFragment = (data[0] & 0x80) == 0x80; - this.state.masked = (data[1] & 0x80) == 0x80; - var opcode = data[0] & 0xf; - if (opcode == 0) { - // continuation frame - this.state.opcode = this.state.activeFragmentedOperation; - if (!(this.state.opcode == 1 || this.state.opcode == 2)) { - this.error('continuation frame cannot follow current opcode') - return; - } - } - else { - this.state.opcode = opcode; - if (this.state.lastFragment === false) { - this.state.activeFragmentedOperation = opcode; - } - } - var handler = this.opcodeHandlers[this.state.opcode]; - if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode); - else handler(data); -} - -/** - * Endprocessing a packet. - * - * @api private - */ - -Parser.prototype.endPacket = function() { - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - if (this.state.lastFragment && this.state.opcode == this.state.activeFragmentedOperation) { - // end current fragmented operation - this.state.activeFragmentedOperation = null; - } - this.state.lastFragment = false; - this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; - this.state.masked = false; - this.expect('Opcode', 2, this.processPacket); -} - -/** - * Reset the parser state. - * - * @api private - */ - -Parser.prototype.reset = function() { - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0 - }; - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.overflow = null; - this.currentMessage = ''; -} - -/** - * Unmask received data. - * - * @api private - */ - -Parser.prototype.unmask = function (mask, buf, binary) { - if (mask != null) { - for (var i = 0, ll = buf.length; i < ll; i++) { - buf[i] ^= mask[i % 4]; - } - } - if (binary) return buf; - return buf != null ? buf.toString('utf8') : ''; -} - -/** - * Concatenates a list of buffers. - * - * @api private - */ - -Parser.prototype.concatBuffers = function(buffers) { - var length = 0; - for (var i = 0, l = buffers.length; i < l; ++i) { - length += buffers[i].length; - } - var mergedBuffer = new Buffer(length); - var offset = 0; - for (var i = 0, l = buffers.length; i < l; ++i) { - buffers[i].copy(mergedBuffer, offset); - offset += buffers[i].length; - } - return mergedBuffer; -} - -/** - * Handles an error - * - * @api private - */ - -Parser.prototype.error = function (reason) { - this.reset(); - this.emit('error', reason); - return this; -}; diff --git a/node_modules/socket.io/lib/transports/websocket/hybi-17.js b/node_modules/socket.io/lib/transports/websocket/hybi-17.js deleted file mode 100644 index 8865a3d..0000000 --- a/node_modules/socket.io/lib/transports/websocket/hybi-17.js +++ /dev/null @@ -1,604 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var Transport = require('../../transport') - , EventEmitter = process.EventEmitter - , crypto = require('crypto') - , url = require('url') - , parser = require('../../parser') - , util = require('../../util'); - -/** - * Export the constructor. - */ - -exports = module.exports = WebSocket; -exports.Parser = Parser; - -/** - * HTTP interface constructor. Interface compatible with all transports that - * depend on request-response cycles. - * - * @api public - */ - -function WebSocket (mng, data, req) { - // parser - var self = this; - - this.manager = mng; - this.parser = new Parser(); - this.parser.on('data', function (packet) { - self.onMessage(parser.decodePacket(packet)); - }); - this.parser.on('ping', function () { - // version 8 ping => pong - self.socket.write('\u008a\u0000'); - }); - this.parser.on('close', function () { - self.end(); - }); - this.parser.on('error', function (reason) { - self.log.warn(self.name + ' parser error: ' + reason); - self.end(); - }); - - Transport.call(this, mng, data, req); -}; - -/** - * Inherits from Transport. - */ - -WebSocket.prototype.__proto__ = Transport.prototype; - -/** - * Transport name - * - * @api public - */ - -WebSocket.prototype.name = 'websocket'; - -/** - * Websocket draft version - * - * @api public - */ - -WebSocket.prototype.protocolVersion = '16'; - -/** - * Called when the socket connects. - * - * @api private - */ - -WebSocket.prototype.onSocketConnect = function () { - var self = this; - - if (this.req.headers.upgrade !== 'websocket') { - this.log.warn(this.name + ' connection invalid'); - this.end(); - return; - } - - var origin = this.req.headers['origin'] - , location = (this.socket.encrypted ? 'wss' : 'ws') - + '://' + this.req.headers.host + this.req.url; - - if (!this.verifyOrigin(origin)) { - this.log.warn(this.name + ' connection invalid: origin mismatch'); - this.end(); - return; - } - - if (!this.req.headers['sec-websocket-key']) { - this.log.warn(this.name + ' connection invalid: received no key'); - this.end(); - return; - } - - // calc key - var key = this.req.headers['sec-websocket-key']; - var shasum = crypto.createHash('sha1'); - shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); - key = shasum.digest('base64'); - - var headers = [ - 'HTTP/1.1 101 Switching Protocols' - , 'Upgrade: websocket' - , 'Connection: Upgrade' - , 'Sec-WebSocket-Accept: ' + key - ]; - - try { - this.socket.write(headers.concat('', '').join('\r\n')); - this.socket.setTimeout(0); - this.socket.setNoDelay(true); - } catch (e) { - this.end(); - return; - } - - this.socket.on('data', function (data) { - self.parser.add(data); - }); -}; - -/** - * Verifies the origin of a request. - * - * @api private - */ - -WebSocket.prototype.verifyOrigin = function (origin) { - var origins = this.manager.get('origins'); - - if (origin === 'null') origin = '*'; - - if (origins.indexOf('*:*') !== -1) { - return true; - } - - if (origin) { - try { - var parts = url.parse(origin); - var ok = - ~origins.indexOf(parts.hostname + ':' + parts.port) || - ~origins.indexOf(parts.hostname + ':*') || - ~origins.indexOf('*:' + parts.port); - if (!ok) this.log.warn('illegal origin: ' + origin); - return ok; - } catch (ex) { - this.log.warn('error parsing origin'); - } - } - else { - this.log.warn('origin missing from websocket call, yet required by config'); - } - return false; -}; - -/** - * Writes to the socket. - * - * @api private - */ - -WebSocket.prototype.write = function (data) { - if (this.open) { - var buf = this.frame(0x81, data); - this.socket.write(buf, 'binary'); - this.log.debug(this.name + ' writing', data); - } -}; - -/** - * Writes a payload. - * - * @api private - */ - -WebSocket.prototype.payload = function (msgs) { - for (var i = 0, l = msgs.length; i < l; i++) { - this.write(msgs[i]); - } - - return this; -}; - -/** - * Frame server-to-client output as a text packet. - * - * @api private - */ - -WebSocket.prototype.frame = function (opcode, str) { - var dataBuffer = new Buffer(str) - , dataLength = dataBuffer.length - , startOffset = 2 - , secondByte = dataLength; - if (dataLength > 65536) { - startOffset = 10; - secondByte = 127; - } - else if (dataLength > 125) { - startOffset = 4; - secondByte = 126; - } - var outputBuffer = new Buffer(dataLength + startOffset); - outputBuffer[0] = opcode; - outputBuffer[1] = secondByte; - dataBuffer.copy(outputBuffer, startOffset); - switch (secondByte) { - case 126: - outputBuffer[2] = dataLength >>> 8; - outputBuffer[3] = dataLength % 256; - break; - case 127: - var l = dataLength; - for (var i = 1; i <= 8; ++i) { - outputBuffer[startOffset - i] = l & 0xff; - l >>>= 8; - } - } - return outputBuffer; -}; - -/** - * Closes the connection. - * - * @api private - */ - -WebSocket.prototype.doClose = function () { - this.socket.end(); -}; - -/** - * WebSocket parser - * - * @api public - */ - -function Parser () { - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0 - }; - this.overflow = null; - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.currentMessage = ''; - - var self = this; - this.opcodeHandlers = { - // text - '1': function(data) { - var finish = function(mask, data) { - self.currentMessage += self.unmask(mask, data); - if (self.state.lastFragment) { - self.emit('data', self.currentMessage); - self.currentMessage = ''; - } - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - if (util.unpack(data.slice(0, 4)) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported'); - return; - } - var lengthBytes = data.slice(4); // note: cap to 32 bit length - expectData(util.unpack(data)); - }); - } - }, - // binary - '2': function(data) { - var finish = function(mask, data) { - if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list - self.currentMessage.push(self.unmask(mask, data, true)); - if (self.state.lastFragment) { - self.emit('binary', self.concatBuffers(self.currentMessage)); - self.currentMessage = ''; - } - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - if (util.unpack(data.slice(0, 4)) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported'); - return; - } - var lengthBytes = data.slice(4); // note: cap to 32 bit length - expectData(util.unpack(data)); - }); - } - }, - // close - '8': function(data) { - self.emit('close'); - self.reset(); - }, - // ping - '9': function(data) { - if (self.state.lastFragment == false) { - self.error('fragmented ping is not supported'); - return; - } - - var finish = function(mask, data) { - self.emit('ping', self.unmask(mask, data)); - self.endPacket(); - } - - var expectData = function(length) { - if (self.state.masked) { - self.expect('Mask', 4, function(data) { - var mask = data; - self.expect('Data', length, function(data) { - finish(mask, data); - }); - }); - } - else { - self.expect('Data', length, function(data) { - finish(null, data); - }); - } - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength == 0) { - finish(null, null); - } - else if (firstLength < 126) { - expectData(firstLength); - } - else if (firstLength == 126) { - self.expect('Length', 2, function(data) { - expectData(util.unpack(data)); - }); - } - else if (firstLength == 127) { - self.expect('Length', 8, function(data) { - expectData(util.unpack(data)); - }); - } - } - } - - this.expect('Opcode', 2, this.processPacket); -}; - -/** - * Inherits from EventEmitter. - */ - -Parser.prototype.__proto__ = EventEmitter.prototype; - -/** - * Add new data to the parser. - * - * @api public - */ - -Parser.prototype.add = function(data) { - if (this.expectBuffer == null) { - this.addToOverflow(data); - return; - } - var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset); - data.copy(this.expectBuffer, this.expectOffset, 0, toRead); - this.expectOffset += toRead; - if (toRead < data.length) { - // at this point the overflow buffer shouldn't at all exist - this.overflow = new Buffer(data.length - toRead); - data.copy(this.overflow, 0, toRead, toRead + this.overflow.length); - } - if (this.expectOffset == this.expectBuffer.length) { - var bufferForHandler = this.expectBuffer; - this.expectBuffer = null; - this.expectOffset = 0; - this.expectHandler.call(this, bufferForHandler); - } -} - -/** - * Adds a piece of data to the overflow. - * - * @api private - */ - -Parser.prototype.addToOverflow = function(data) { - if (this.overflow == null) this.overflow = data; - else { - var prevOverflow = this.overflow; - this.overflow = new Buffer(this.overflow.length + data.length); - prevOverflow.copy(this.overflow, 0); - data.copy(this.overflow, prevOverflow.length); - } -} - -/** - * Waits for a certain amount of bytes to be available, then fires a callback. - * - * @api private - */ - -Parser.prototype.expect = function(what, length, handler) { - this.expectBuffer = new Buffer(length); - this.expectOffset = 0; - this.expectHandler = handler; - if (this.overflow != null) { - var toOverflow = this.overflow; - this.overflow = null; - this.add(toOverflow); - } -} - -/** - * Start processing a new packet. - * - * @api private - */ - -Parser.prototype.processPacket = function (data) { - if ((data[0] & 0x70) != 0) this.error('reserved fields must be empty'); - this.state.lastFragment = (data[0] & 0x80) == 0x80; - this.state.masked = (data[1] & 0x80) == 0x80; - var opcode = data[0] & 0xf; - if (opcode == 0) { - // continuation frame - this.state.opcode = this.state.activeFragmentedOperation; - if (!(this.state.opcode == 1 || this.state.opcode == 2)) { - this.error('continuation frame cannot follow current opcode') - return; - } - } - else { - this.state.opcode = opcode; - if (this.state.lastFragment === false) { - this.state.activeFragmentedOperation = opcode; - } - } - var handler = this.opcodeHandlers[this.state.opcode]; - if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode); - else handler(data); -} - -/** - * Endprocessing a packet. - * - * @api private - */ - -Parser.prototype.endPacket = function() { - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - if (this.state.lastFragment && this.state.opcode == this.state.activeFragmentedOperation) { - // end current fragmented operation - this.state.activeFragmentedOperation = null; - } - this.state.lastFragment = false; - this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; - this.state.masked = false; - this.expect('Opcode', 2, this.processPacket); -} - -/** - * Reset the parser state. - * - * @api private - */ - -Parser.prototype.reset = function() { - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0 - }; - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.overflow = null; - this.currentMessage = ''; -} - -/** - * Unmask received data. - * - * @api private - */ - -Parser.prototype.unmask = function (mask, buf, binary) { - if (mask != null) { - for (var i = 0, ll = buf.length; i < ll; i++) { - buf[i] ^= mask[i % 4]; - } - } - if (binary) return buf; - return buf != null ? buf.toString('utf8') : ''; -} - -/** - * Concatenates a list of buffers. - * - * @api private - */ - -Parser.prototype.concatBuffers = function(buffers) { - var length = 0; - for (var i = 0, l = buffers.length; i < l; ++i) { - length += buffers[i].length; - } - var mergedBuffer = new Buffer(length); - var offset = 0; - for (var i = 0, l = buffers.length; i < l; ++i) { - buffers[i].copy(mergedBuffer, offset); - offset += buffers[i].length; - } - return mergedBuffer; -} - -/** - * Handles an error - * - * @api private - */ - -Parser.prototype.error = function (reason) { - this.reset(); - this.emit('error', reason); - return this; -}; diff --git a/node_modules/socket.io/lib/transports/websocket/index.js b/node_modules/socket.io/lib/transports/websocket/index.js deleted file mode 100644 index 3a952b7..0000000 --- a/node_modules/socket.io/lib/transports/websocket/index.js +++ /dev/null @@ -1,11 +0,0 @@ - -/** - * Export websocket versions. - */ - -module.exports = { - 7: require('./hybi-07-12'), - 8: require('./hybi-07-12'), - 13: require('./hybi-16'), - default: require('./default') -}; diff --git a/node_modules/socket.io/lib/transports/xhr-polling.js b/node_modules/socket.io/lib/transports/xhr-polling.js deleted file mode 100644 index fee2438..0000000 --- a/node_modules/socket.io/lib/transports/xhr-polling.js +++ /dev/null @@ -1,72 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module requirements. - */ - -var HTTPPolling = require('./http-polling'); - -/** - * Export the constructor. - */ - -exports = module.exports = XHRPolling; - -/** - * Ajax polling transport. - * - * @api public - */ - -function XHRPolling (mng, data, req) { - HTTPPolling.call(this, mng, data, req); -}; - -/** - * Inherits from Transport. - */ - -XHRPolling.prototype.__proto__ = HTTPPolling.prototype; - -/** - * Transport name - * - * @api public - */ - -XHRPolling.prototype.name = 'xhr-polling'; - -/** - * Frames data prior to write. - * - * @api private - */ - -XHRPolling.prototype.doWrite = function (data) { - HTTPPolling.prototype.doWrite.call(this); - - var origin = this.req.headers.origin - , headers = { - 'Content-Type': 'text/plain; charset=UTF-8' - , 'Content-Length': data === undefined ? 0 : Buffer.byteLength(data) - , 'Connection': 'Keep-Alive' - }; - - if (origin) { - // https://developer.mozilla.org/En/HTTP_Access_Control - headers['Access-Control-Allow-Origin'] = '*'; - - if (this.req.headers.cookie) { - headers['Access-Control-Allow-Credentials'] = 'true'; - } - } - - this.response.writeHead(200, headers); - this.response.write(data); - this.log.debug(this.name + ' writing', data); -}; diff --git a/node_modules/socket.io/lib/util.js b/node_modules/socket.io/lib/util.js deleted file mode 100644 index f7d9f2b..0000000 --- a/node_modules/socket.io/lib/util.js +++ /dev/null @@ -1,50 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -/** - * Converts an enumerable to an array. - * - * @api public - */ - -exports.toArray = function (enu) { - var arr = []; - - for (var i = 0, l = enu.length; i < l; i++) - arr.push(enu[i]); - - return arr; -}; - -/** - * Unpacks a buffer to a number. - * - * @api public - */ - -exports.unpack = function (buffer) { - var n = 0; - for (var i = 0; i < buffer.length; ++i) { - n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; - } - return n; -} - -/** - * Left pads a string. - * - * @api public - */ - -exports.padl = function (s,n,c) { - return new Array(1 + n - s.length).join(c) + s; -} - diff --git a/node_modules/socket.io/package.json b/node_modules/socket.io/package.json deleted file mode 100644 index 4f719bc..0000000 --- a/node_modules/socket.io/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "socket.io" - , "version": "0.8.7" - , "description": "Real-time apps made cross-browser & easy with a WebSocket-like API" - , "homepage": "http://socket.io" - , "keywords": ["websocket", "socket", "realtime", "socket.io", "comet", "ajax"] - , "author": "Guillermo Rauch " - , "contributors": [ - { "name": "Guillermo Rauch", "email": "rauchg@gmail.com" } - , { "name": "Arnout Kazemier", "email": "info@3rd-eden.com" } - , { "name": "Vladimir Dronnikov", "email": "dronnikov@gmail.com" } - , { "name": "Einar Otto Stangvik", "email": "einaros@gmail.com" } - ] - , "repository":{ - "type": "git" - , "url": "https://github.com/LearnBoost/socket.io.git" - } - , "dependencies": { - "socket.io-client": "0.8.7" - , "policyfile": "0.0.4" - , "redis": "0.6.7" - } - , "devDependencies": { - "expresso": "0.9.2" - , "should": "0.0.4" - , "assertvanish": "0.0.3-1" - , "benchmark": "0.2.2" - , "microtime": "0.1.3-1" - , "colors": "0.5.1" - } - , "main": "index" - , "engines": { "node": ">= 0.4.0" } -} diff --git a/node_modules/socket.io/support/node-websocket-client/LICENSE b/node_modules/socket.io/support/node-websocket-client/LICENSE deleted file mode 100644 index f3c2eae..0000000 --- a/node_modules/socket.io/support/node-websocket-client/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2010, Peter Griess -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of node-websocket-client nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/node_modules/socket.io/support/node-websocket-client/Makefile b/node_modules/socket.io/support/node-websocket-client/Makefile deleted file mode 100644 index e7c849a..0000000 --- a/node_modules/socket.io/support/node-websocket-client/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# This makefile exists to help run tests. -# -# If TEST_UNIX is a non-empty value, runs tests for UNIX sockets. This -# functionality is not in node-websocket-server at the moment. - -.PHONY: test - -all: test test-unix - -test: - for f in `ls -1 test/test-*.js | grep -v unix` ; do \ - echo $$f ; \ - node $$f ; \ - done - -test-unix: - if [[ -n "$$TEST_UNIX" ]] ; then \ - for f in `ls -1 test/test-*.js | grep unix` ; do \ - echo $$f ; \ - node $$f ; \ - done \ - fi diff --git a/node_modules/socket.io/support/node-websocket-client/README.md b/node_modules/socket.io/support/node-websocket-client/README.md deleted file mode 100644 index 8823a5c..0000000 --- a/node_modules/socket.io/support/node-websocket-client/README.md +++ /dev/null @@ -1,41 +0,0 @@ -A prototype [Web Socket](http://www.whatwg.org/specs/web-socket-protocol/) -client implementation for [node.js](http://nodejs.org). - -Tested with -[miksago/node-websocket-server](http://github.com/miksago/node-websocket-server) -v1.2.00. - -Requires [nodejs](http://nodejs.org) 0.1.98 or later. - -## Installation - -Install this using `npm` as follows - - npm install websocket-client - -... or just dump `lib/websocket.js` in your `$NODE_PATH`. - -## Usage - - var sys = require('sys'); - var WebSocket = require('websocket').WebSocket; - - var ws = new WebSocket('ws://localhost:8000/biff', 'borf'); - ws.addListener('data', function(buf) { - sys.debug('Got data: ' + sys.inspect(buf)); - }); - ws.onmessage = function(m) { - sys.debug('Got message: ' + m); - } - -## API - -This supports the `send()` and `onmessage()` APIs. The `WebSocket` object will -also emit `data` events that are node `Buffer` objects, in case you want to -work with something lower-level than strings. - -## Transports - -Multiple transports are supported, indicated by the scheme provided to the -`WebSocket` constructor. `ws://` is a standard TCP-based Web Socket; -`ws+unix://` allows connection to a UNIX socket at the given path. diff --git a/node_modules/socket.io/support/node-websocket-client/examples/client-unix.js b/node_modules/socket.io/support/node-websocket-client/examples/client-unix.js deleted file mode 100644 index 3bb23ba..0000000 --- a/node_modules/socket.io/support/node-websocket-client/examples/client-unix.js +++ /dev/null @@ -1,12 +0,0 @@ -var sys = require('sys'); -var WebSocket = require('../lib/websocket').WebSocket; - -var ws = new WebSocket('ws+unix://' + process.argv[2], 'boffo'); - -ws.addListener('message', function(d) { - sys.debug('Received message: ' + d.toString('utf8')); -}); - -ws.addListener('open', function() { - ws.send('This is a message', 1); -}); diff --git a/node_modules/socket.io/support/node-websocket-client/examples/client.js b/node_modules/socket.io/support/node-websocket-client/examples/client.js deleted file mode 100644 index 259bf6e..0000000 --- a/node_modules/socket.io/support/node-websocket-client/examples/client.js +++ /dev/null @@ -1,10 +0,0 @@ -var sys = require('sys'); -var WebSocket = require('../lib/websocket').WebSocket; - -var ws = new WebSocket('ws://localhost:8000/biff', 'borf'); -ws.addListener('data', function(buf) { - sys.debug('Got data: ' + sys.inspect(buf)); -}); -ws.onmessage = function(m) { - sys.debug('Got message: ' + m); -} diff --git a/node_modules/socket.io/support/node-websocket-client/examples/server-unix.js b/node_modules/socket.io/support/node-websocket-client/examples/server-unix.js deleted file mode 100644 index 912be0e..0000000 --- a/node_modules/socket.io/support/node-websocket-client/examples/server-unix.js +++ /dev/null @@ -1,13 +0,0 @@ -var sys = require('sys'); -var ws = require('websocket-server/ws'); - -var srv = ws.createServer({ debug : true}); -srv.addListener('connection', function(s) { - sys.debug('Got a connection!'); - - s._req.socket.addListener('fd', function(fd) { - sys.debug('Got an fd: ' + fd); - }); -}); - -srv.listen(process.argv[2]); diff --git a/node_modules/socket.io/support/node-websocket-client/lib/websocket.js b/node_modules/socket.io/support/node-websocket-client/lib/websocket.js deleted file mode 100644 index 4f7f734..0000000 --- a/node_modules/socket.io/support/node-websocket-client/lib/websocket.js +++ /dev/null @@ -1,617 +0,0 @@ -var assert = require('assert'); -var buffer = require('buffer'); -var crypto = require('crypto'); -var events = require('events'); -var http = require('http'); -var net = require('net'); -var urllib = require('url'); -var sys = require('util'); - -var FRAME_NO = 0; -var FRAME_LO = 1; -var FRAME_HI = 2; - -// Values for readyState as per the W3C spec -var CONNECTING = 0; -var OPEN = 1; -var CLOSING = 2; -var CLOSED = 3; - -var debugLevel = parseInt(process.env.NODE_DEBUG, 16); -var debug = (debugLevel & 0x4) ? - function() { sys.error.apply(this, arguments); } : - function() { }; - -// Generate a Sec-WebSocket-* value -var createSecretKey = function() { - // How many spaces will we be inserting? - var numSpaces = 1 + Math.floor(Math.random() * 12); - assert.ok(1 <= numSpaces && numSpaces <= 12); - - // What is the numerical value of our key? - var keyVal = (Math.floor( - Math.random() * (4294967295 / numSpaces) - ) * numSpaces); - - // Our string starts with a string representation of our key - var s = keyVal.toString(); - - // Insert 'numChars' worth of noise in the character ranges - // [0x21, 0x2f] (14 characters) and [0x3a, 0x7e] (68 characters) - var numChars = 1 + Math.floor(Math.random() * 12); - assert.ok(1 <= numChars && numChars <= 12); - - for (var i = 0; i < numChars; i++) { - var pos = Math.floor(Math.random() * s.length + 1); - - var c = Math.floor(Math.random() * (14 + 68)); - c = (c <= 14) ? - String.fromCharCode(c + 0x21) : - String.fromCharCode((c - 14) + 0x3a); - - s = s.substring(0, pos) + c + s.substring(pos, s.length); - } - - // We shoudln't have any spaces in our value until we insert them - assert.equal(s.indexOf(' '), -1); - - // Insert 'numSpaces' worth of spaces - for (var i = 0; i < numSpaces; i++) { - var pos = Math.floor(Math.random() * (s.length - 1)) + 1; - s = s.substring(0, pos) + ' ' + s.substring(pos, s.length); - } - - assert.notEqual(s.charAt(0), ' '); - assert.notEqual(s.charAt(s.length), ' '); - - return s; -}; - -// Generate a challenge sequence -var createChallenge = function() { - var c = ''; - for (var i = 0; i < 8; i++) { - c += String.fromCharCode(Math.floor(Math.random() * 255)); - } - - return c; -}; - -// Get the value of a secret key string -// -// This strips non-digit values and divides the result by the number of -// spaces found. -var secretKeyValue = function(sk) { - var ns = 0; - var v = 0; - - for (var i = 0; i < sk.length; i++) { - var cc = sk.charCodeAt(i); - - if (cc == 0x20) { - ns++; - } else if (0x30 <= cc && cc <= 0x39) { - v = v * 10 + cc - 0x30; - } - } - - return Math.floor(v / ns); -} - -// Get the to-be-hashed value of a secret key string -// -// This takes the result of secretKeyValue() and encodes it in a big-endian -// byte string -var secretKeyHashValue = function(sk) { - var skv = secretKeyValue(sk); - - var hv = ''; - hv += String.fromCharCode((skv >> 24) & 0xff); - hv += String.fromCharCode((skv >> 16) & 0xff); - hv += String.fromCharCode((skv >> 8) & 0xff); - hv += String.fromCharCode((skv >> 0) & 0xff); - - return hv; -}; - -// Compute the secret key signature based on two secret key strings and some -// handshaking data. -var computeSecretKeySignature = function(s1, s2, hs) { - assert.equal(hs.length, 8); - - var hash = crypto.createHash('md5'); - - hash.update(secretKeyHashValue(s1)); - hash.update(secretKeyHashValue(s2)); - hash.update(hs); - - return hash.digest('binary'); -}; - -// Return a hex representation of the given binary string; used for debugging -var str2hex = function(str) { - var hexChars = [ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f' - ]; - - var out = ''; - for (var i = 0; i < str.length; i++) { - var c = str.charCodeAt(i); - out += hexChars[(c & 0xf0) >>> 4]; - out += hexChars[c & 0x0f]; - out += ' '; - } - - return out.trim(); -}; - -// Get the scheme for a URL, undefined if none is found -var getUrlScheme = function(url) { - var i = url.indexOf(':'); - if (i == -1) { - return undefined; - } - - return url.substring(0, i); -}; - -// Set a constant on the given object -var setConstant = function(obj, name, value) { - Object.defineProperty(obj, name, { - get : function() { - return value; - } - }); -}; - -// WebSocket object -// -// This is intended to conform (mostly) to http://dev.w3.org/html5/websockets/ -// -// N.B. Arguments are parsed in the anonymous function at the bottom of the -// constructor. -var WebSocket = function(url, proto, opts) { - events.EventEmitter.call(this); - - // Retain a reference to our object - var self = this; - - // State of our end of the connection - var readyState = CONNECTING; - - // Whether or not the server has sent a close handshake - var serverClosed = false; - - // Our underlying net.Stream instance - var stream = undefined; - - opts = opts || { - origin : 'http://www.example.com' - }; - - // Frame parsing functions - // - // These read data from the given buffer starting at the given offset, - // looking for the end of the current frame. If found, the current frame is - // emitted and the function returns. Only a single frame is processed at a - // time. - // - // The number of bytes read to complete a frame is returned, which the - // caller is to use to advance along its buffer. If 0 is returned, no - // completed frame bytes were found, and the caller should probably enqueue - // the buffer as a continuation of the current message. If a complete frame - // is read, the function is responsible for resting 'frameType'. - - // Framing data - var frameType = FRAME_NO; - var bufs = []; - var bufsBytes = 0; - - // Frame-parsing functions - var frameFuncs = [ - // FRAME_NO - function(buf, off) { - if (buf[off] & 0x80) { - frameType = FRAME_HI; - } else { - frameType = FRAME_LO; - } - - return 1; - }, - - // FRAME_LO - function(buf, off) { - debug('frame_lo(' + sys.inspect(buf) + ', ' + off + ')'); - - // Find the first instance of 0xff, our terminating byte - for (var i = off; i < buf.length && buf[i] != 0xff; i++) - ; - - // We didn't find a terminating byte - if (i >= buf.length) { - return 0; - } - - // We found a terminating byte; collect all bytes into a single buffer - // and emit it - var mb = null; - if (bufs.length == 0) { - mb = buf.slice(off, i); - } else { - mb = new buffer.Buffer(bufsBytes + i); - - var mbOff = 0; - bufs.forEach(function(b) { - b.copy(mb, mbOff, 0, b.length); - mbOff += b.length; - }); - - assert.equal(mbOff, bufsBytes); - - // Don't call Buffer.copy() if we're coping 0 bytes. Rather - // than being a no-op, this will trigger a range violation on - // the destination. - if (i > 0) { - buf.copy(mb, mbOff, off, i); - } - - // We consumed all of the buffers that we'd been saving; clear - // things out - bufs = []; - bufsBytes = 0; - } - - process.nextTick(function() { - var b = mb; - return function() { - var m = b.toString('utf8'); - - self.emit('data', b); - self.emit('message', m); // wss compat - - if (self.onmessage) { - self.onmessage({data: m}); - } - }; - }()); - - frameType = FRAME_NO; - return i - off + 1; - }, - - // FRAME_HI - function(buf, off) { - debug('frame_hi(' + sys.inspect(buf) + ', ' + off + ')'); - - if (buf[off] !== 0) { - throw new Error('High-byte framing not supported.'); - } - - serverClosed = true; - return 1; - } - ]; - - // Handle data coming from our socket - var dataListener = function(buf) { - if (buf.length <= 0 || serverClosed) { - return; - } - - debug('dataListener(' + sys.inspect(buf) + ')'); - - var off = 0; - var consumed = 0; - - do { - if (frameType < 0 || frameFuncs.length <= frameType) { - throw new Error('Unexpected frame type: ' + frameType); - } - - assert.equal(bufs.length === 0, bufsBytes === 0); - assert.ok(off < buf.length); - - consumed = frameFuncs[frameType](buf, off); - off += consumed; - } while (!serverClosed && consumed > 0 && off < buf.length); - - if (serverClosed) { - serverCloseHandler(); - } - - if (consumed == 0) { - bufs.push(buf.slice(off, buf.length)); - bufsBytes += buf.length - off; - } - }; - - // Handle incoming file descriptors - var fdListener = function(fd) { - self.emit('fd', fd); - }; - - // Handle errors from any source (HTTP client, stream, etc) - var errorListener = function(e) { - process.nextTick(function() { - self.emit('wserror', e); - - if (self.onerror) { - self.onerror(e); - } - }); - }; - - // Finish the closing process; destroy the socket and tell the application - // that we've closed. - var finishClose = self.finishClose = function() { - readyState = CLOSED; - if (stream) { - stream.end(); - stream.destroy(); - stream = undefined; - } - - process.nextTick(function() { - self.emit('close'); - if (self.onclose) { - self.onclose(); - } - }); - }; - - // Send a close frame to the server - var sendClose = function() { - assert.equal(OPEN, readyState); - - readyState = CLOSING; - stream.write('\xff\x00', 'binary'); - }; - - // Handle a close packet sent from the server - var serverCloseHandler = function() { - assert.ok(serverClosed); - assert.ok(readyState === OPEN || readyState === CLOSING); - - bufs = []; - bufsBytes = 0; - - // Handle state transitions asynchronously so that we don't change - // readyState before the application has had a chance to process data - // events which are already in the delivery pipeline. For example, a - // 'data' event could be delivered with a readyState of CLOSING if we - // received both frames in the same packet. - process.nextTick(function() { - if (readyState === OPEN) { - sendClose(); - } - - finishClose(); - }); - }; - - // External API - self.close = function(timeout) { - if (readyState === CONNECTING) { - // If we're still in the process of connecting, the server is not - // in a position to understand our close frame. Just nuke the - // connection and call it a day. - finishClose(); - } else if (readyState === OPEN) { - sendClose(); - - if (timeout) { - setTimeout(finishClose, timeout * 1000); - } - } - }; - - self.send = function(str, fd) { - if (readyState != OPEN) { - return; - } - - stream.write('\x00', 'binary'); - stream.write(str, 'utf8', fd); - stream.write('\xff', 'binary'); - }; - - // wss compat - self.write = self.send; - - setConstant(self, 'url', url); - - Object.defineProperty(self, 'readyState', { - get : function() { - return readyState; - } - }); - - // Connect and perform handshaking with the server - (function() { - // Parse constructor arguments - if (!url) { - throw new Error('Url and must be specified.'); - } - - // Secrets used for handshaking - var key1 = createSecretKey(); - var key2 = createSecretKey(); - var challenge = createChallenge(); - - debug( - 'key1=\'' + str2hex(key1) + '\'; ' + - 'key2=\'' + str2hex(key2) + '\'; ' + - 'challenge=\'' + str2hex(challenge) + '\'' - ); - - var httpHeaders = { - 'Connection' : 'Upgrade', - 'Upgrade' : 'WebSocket', - 'Sec-WebSocket-Key1' : key1, - 'Sec-WebSocket-Key2' : key2 - }; - if (opts.origin) { - httpHeaders['Origin'] = opts.origin; - } - if (proto) { - httpHeaders['Sec-WebSocket-Protocol'] = proto; - } - - var httpPath = '/'; - - // Create the HTTP client that we'll use for handshaking. We'll cannabalize - // its socket via the 'upgrade' event and leave it to rot. - // - // N.B. The ws+unix:// scheme makes use of the implementation detail - // that http.Client passes its constructor arguments through, - // un-inspected to net.Stream.connect(). The latter accepts a - // string as its first argument to connect to a UNIX socket. - var opt = {}; - var agent = null; - switch (getUrlScheme(url)) { - case 'ws': - var u = urllib.parse(url); - agent = new http.Agent({ - host: u.hostname, - port: u.port || 80 - }); - opt.agent = agent; - opt.host = u.hostname; - opt.port = u.port || 80; - opt.path = (u.pathname || '/') + (u.search || ''); - opt.headers = httpHeaders; - break; - - case 'ws+unix': - var sockPath = url.substring('ws+unix://'.length, url.length); - var u = urllib.parse(url); - agent = new http.Agent({ - host: 'localhost', - port: sockPath - }); - opt.agent = agent; - opt.host = 'localhost'; - opt.path = sockPath; - opt.headers = httpHeaders; - break; - - default: - throw new Error('Invalid URL scheme \'' + urlScheme + '\' specified.'); - } - - var httpReq = http.request(opt, function() { }); - var upgradeHandler = (function() { - var data = undefined; - - return function(req, s, head) { - req.socket.setNoDelay(true); - stream = s; - - if (readyState == CLOSED) { - stream.end(); - stream.destroy(); - stream = undefined; - return; - } - - stream.on('data', function(d) { - if (d.length <= 0) { - return; - } - - if (!data) { - data = d; - } else { - var data2 = new buffer.Buffer(data.length + d.length); - - data.copy(data2, 0, 0, data.length); - d.copy(data2, data.length, 0, d.length); - - data = data2; - } - - if (data.length >= 16) { - var expected = computeSecretKeySignature(key1, key2, challenge); - var actual = data.slice(0, 16).toString('binary'); - - // Handshaking fails; we're donezo - if (actual != expected) { - debug( - 'expected=\'' + str2hex(expected) + '\'; ' + - 'actual=\'' + str2hex(actual) + '\'' - ); - - process.nextTick(function() { - // N.B. Emit 'wserror' here, as 'error' is a reserved word in the - // EventEmitter world, and gets thrown. - self.emit( - 'wserror', - new Error('Invalid handshake from server:' + - 'expected \'' + str2hex(expected) + '\', ' + - 'actual \'' + str2hex(actual) + '\'' - ) - ); - - if (self.onerror) { - self.onerror(); - } - - finishClose(); - }); - } - - // Un-register our data handler and add the one to be used - // for the normal, non-handshaking case. If we have extra - // data left over, manually fire off the handler on - // whatever remains. - // - // XXX: This is lame. We should only remove the listeners - // that we added. - httpReq.removeAllListeners('upgrade'); - stream.removeAllListeners('data'); - stream.on('data', dataListener); - - readyState = OPEN; - - process.nextTick(function() { - self.emit('open'); - - if (self.onopen) { - self.onopen(); - } - }); - - // Consume any leftover data - if (data.length > 16) { - stream.emit('data', data.slice(16, data.length)); - } - } - }); - stream.on('fd', fdListener); - stream.on('error', errorListener); - stream.on('close', function() { - errorListener(new Error('Stream closed unexpectedly.')); - }); - - stream.emit('data', head); - }; - })(); - agent.on('upgrade', upgradeHandler); // node v0.4 - httpReq.on('upgrade', upgradeHandler); // node v0.5+ - - httpReq.write(challenge, 'binary'); - httpReq.end(); - })(); -}; -sys.inherits(WebSocket, events.EventEmitter); -exports.WebSocket = WebSocket; - -// Add some constants to the WebSocket object -setConstant(WebSocket.prototype, 'CONNECTING', CONNECTING); -setConstant(WebSocket.prototype, 'OPEN', OPEN); -setConstant(WebSocket.prototype, 'CLOSING', CLOSING); -setConstant(WebSocket.prototype, 'CLOSED', CLOSED); - -// vim:ts=4 sw=4 et diff --git a/node_modules/socket.io/support/node-websocket-client/package.json b/node_modules/socket.io/support/node-websocket-client/package.json deleted file mode 100644 index c6e221f..0000000 --- a/node_modules/socket.io/support/node-websocket-client/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name" : "websocket-client", - "version" : "1.0.0", - "description" : "An HTML5 Web Sockets client", - "author" : "Peter Griess ", - "engines" : { - "node" : ">=0.1.98" - }, - "repositories" : [ - { - "type" : "git", - "url" : "http://github.com/pgriess/node-websocket-client.git" - } - ], - "licenses" : [ - { - "type" : "BSD", - "url" : "http://github.com/pgriess/node-websocket-client/blob/master/LICENSE" - } - ], - "main" : "./lib/websocket" -} diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-basic.js b/node_modules/socket.io/support/node-websocket-client/test/test-basic.js deleted file mode 100644 index f010424..0000000 --- a/node_modules/socket.io/support/node-websocket-client/test/test-basic.js +++ /dev/null @@ -1,68 +0,0 @@ -// Verify that we can connect to a WebSocket server, exchange messages, and -// shut down cleanly. - -var assert = require('assert'); -var WebSocket = require('../lib/websocket').WebSocket; -var WebSocketServer = require('websocket-server/ws/server').Server; - -var PORT = 1024 + Math.floor(Math.random() * 4096); -var C_MSG = 'Client test: ' + (Math.random() * 100); -var S_MSG = 'Server test: ' + (Math.random() * 100); - -var serverGotConnection = false; -var clientGotOpen = false; -var clientGotData = false; -var clientGotMessage = false; -var serverGotMessage = false; -var serverGotClose = false; -var clientGotClose = false; - -var wss = new WebSocketServer(); -wss.listen(PORT, 'localhost'); -wss.on('connection', function(c) { - serverGotConnection = true; - - c.on('message', function(m) { - assert.equal(m, C_MSG); - serverGotMessage = true; - - c.close(); - }); - - c.on('close', function() { - serverGotClose = true; - wss.close(); - }); - - c.write(S_MSG); -}); - -var ws = new WebSocket('ws://localhost:' + PORT + '/', 'biff'); -ws.on('open', function() { - clientGotOpen = true; -}); -ws.on('data', function(buf) { - assert.equal(typeof buf, 'object'); - assert.equal(buf.toString('utf8'), S_MSG); - - clientGotData = true; - - ws.send(C_MSG); -}); -ws.onmessage = function(m) { - assert.deepEqual(m, {data : S_MSG}); - clientGotMessage = true; -}; -ws.onclose = function() { - clientGotClose = true; -}; - -process.on('exit', function() { - assert.ok(serverGotConnection); - assert.ok(clientGotOpen); - assert.ok(clientGotData); - assert.ok(clientGotMessage); - assert.ok(serverGotMessage); - assert.ok(serverGotClose); - assert.ok(clientGotClose); -}); diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-client-close.js b/node_modules/socket.io/support/node-websocket-client/test/test-client-close.js deleted file mode 100644 index 76fb81f..0000000 --- a/node_modules/socket.io/support/node-websocket-client/test/test-client-close.js +++ /dev/null @@ -1,43 +0,0 @@ -// Verify that a connection can be closed gracefully from the client. - -var assert = require('assert'); -var WebSocket = require('../lib/websocket').WebSocket; -var WebSocketServer = require('websocket-server/ws/server').Server; - -var PORT = 1024 + Math.floor(Math.random() * 4096); -var C_MSG = 'Client test: ' + (Math.random() * 100); - -var serverGotClientMessage = false; -var clientGotServerClose = false; -var serverGotClientClose = false; - -var wss = new WebSocketServer(); -wss.listen(PORT, 'localhost'); -wss.on('connection', function(c) { - c.on('message', function(m) { - assert.equal(m, C_MSG); - serverGotClientMessage = true; - }); - c.on('close', function() { - serverGotClientClose = true; - wss.close(); - }); -}); - -var ws = new WebSocket('ws://localhost:' + PORT); -ws.onopen = function() { - ws.send(C_MSG); - - // XXX: Add a timeout here - ws.close(5); -}; -ws.onclose = function() { - assert.equal(ws.CLOSED, ws.readyState); - clientGotServerClose = true; -}; - -process.on('exit', function() { - assert.ok(serverGotClientMessage); - assert.ok(clientGotServerClose); - assert.ok(serverGotClientClose); -}); diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js b/node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js deleted file mode 100644 index de896b3..0000000 --- a/node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js +++ /dev/null @@ -1,43 +0,0 @@ -// Verify that some attributes of a WebSocket object are read-only. - -var assert = require('assert'); -var sys = require('sys'); -var WebSocket = require('../lib/websocket').WebSocket; -var WebSocketServer = require('websocket-server/ws/server').Server; - -var PORT = 1024 + Math.floor(Math.random() * 4096); - -var wss = new WebSocketServer(); -wss.listen(PORT, 'localhost'); -wss.on('connection', function(c) { - c.close(); - wss.close(); -}); -var ws = new WebSocket('ws://localhost:' + PORT + '/', 'biff'); -ws.on('open', function() { - assert.equal(ws.CONNECTING, 0); - try { - ws.CONNECTING = 13; - assert.equal( - ws.CONNECTING, 0, - 'Should not have been able to set read-only CONNECTING attribute' - ); - } catch (e) { - assert.equal(e.type, 'no_setter_in_callback'); - } - - assert.equal(ws.OPEN, 1); - assert.equal(ws.CLOSING, 2); - assert.equal(ws.CLOSED, 3); - - assert.equal(ws.url, 'ws://localhost:' + PORT + '/'); - try { - ws.url = 'foobar'; - assert.equal( - ws.url, 'ws://localhost:' + PORT + '/', - 'Should not have been able to set read-only url attribute' - ); - } catch (e) { - assert.equal(e.type, 'no_setter_in_callback'); - } -}); diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js b/node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js deleted file mode 100644 index 8fcbd4c..0000000 --- a/node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js +++ /dev/null @@ -1,26 +0,0 @@ -// Verify that readyState transitions are implemented correctly - -var assert = require('assert'); -var WebSocket = require('../lib/websocket').WebSocket; -var WebSocketServer = require('websocket-server/ws/server').Server; - -var PORT = 1024 + Math.floor(Math.random() * 4096); - -var wss = new WebSocketServer(); -wss.listen(PORT, 'localhost'); -wss.on('connection', function(c) { - c.close(); -}); - -var ws = new WebSocket('ws://localhost:' + PORT); -assert.equal(ws.readyState, ws.CONNECTING); -ws.onopen = function() { - assert.equal(ws.readyState, ws.OPEN); - - ws.close(); - assert.ok(ws.readyState == ws.CLOSING); -}; -ws.onclose = function() { - assert.equal(ws.readyState, ws.CLOSED); - wss.close(); -}; diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-server-close.js b/node_modules/socket.io/support/node-websocket-client/test/test-server-close.js deleted file mode 100644 index a286429..0000000 --- a/node_modules/socket.io/support/node-websocket-client/test/test-server-close.js +++ /dev/null @@ -1,41 +0,0 @@ -// Verify that a connection can be closed gracefully from the server. - -var assert = require('assert'); -var WebSocket = require('../lib/websocket').WebSocket; -var WebSocketServer = require('websocket-server/ws/server').Server; - -var PORT = 1024 + Math.floor(Math.random() * 4096); -var S_MSG = 'Server test: ' + (Math.random() * 100); - -var clientGotServerMessage = false; -var clientGotServerClose = false; -var serverGotClientClose = false; - -var wss = new WebSocketServer(); -wss.listen(PORT, 'localhost'); -wss.on('connection', function(c) { - c.on('close', function() { - serverGotClientClose = true; - wss.close(); - }); - - c.write(S_MSG); - c.close(); -}); - -var ws = new WebSocket('ws://localhost:' + PORT); -ws.onmessage = function(m) { - assert.deepEqual(m, {data: S_MSG}); - - clientGotServerMessage = true; -}; -ws.onclose = function() { - assert.equal(ws.CLOSED, ws.readyState); - clientGotServerClose = true; -}; - -process.on('exit', function() { - assert.ok(clientGotServerMessage); - assert.ok(clientGotServerClose); - assert.ok(serverGotClientClose); -}); diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js b/node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js deleted file mode 100644 index 8f1c28d..0000000 --- a/node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js +++ /dev/null @@ -1,63 +0,0 @@ -// Verify that both sides of the WS connection can both send and receive file -// descriptors. - -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); -var sys = require('sys'); -var WebSocket = require('../lib/websocket').WebSocket; -var WebSocketServer = require('websocket-server/ws/server').Server; - -var PATH = path.join(__dirname, 'sock.' + process.pid); -var C_MSG = 'Client test: ' + (Math.random() * 100); -var S_MSG = 'Server test: ' + (Math.random() * 100); - -var clientReceivedData = false; -var clientReceivedFD = false; -var serverReceivedData = false; -var serverReceivedFD = false; - -var wss = new WebSocketServer(); -wss.on('listening', function() { - var ws = new WebSocket('ws+unix://' + PATH); - ws.on('data', function(d) { - assert.equal(d.toString('utf8'), S_MSG); - - clientReceivedData = true; - - ws.send(C_MSG, 1); - ws.close(); - }); - ws.on('fd', function(fd) { - assert.ok(fd >= 0); - - clientReceivedFD = true; - }); -}); -wss.on('connection', function(c) { - c.write(S_MSG, 0); - c._req.socket.on('fd', function(fd) { - assert.ok(fd >= 0); - - serverReceivedFD = true; - }); - c.on('message', function(d) { - assert.equal(d, C_MSG); - - serverReceivedData = true; - - wss.close(); - }); -}); -wss.listen(PATH); - -process.on('exit', function() { - assert.ok(clientReceivedFD); - assert.ok(clientReceivedData); - assert.ok(serverReceivedFD); - assert.ok(serverReceivedData); - - try { - fs.unlinkSync(PATH); - } catch (e) { } -}); diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js b/node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js deleted file mode 100644 index 5cbf094..0000000 --- a/node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js +++ /dev/null @@ -1,46 +0,0 @@ -// Verify that we can connect to a server over UNIX domain sockets. - -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); -var sys = require('sys'); -var WebSocket = require('../lib/websocket').WebSocket; -var WebSocketServer = require('websocket-server/ws/server').Server; - -var PATH = path.join(__dirname, 'sock.' + process.pid); -var S_MSG = 'Server test: ' + (Math.random() * 100); - -var serverGotConnection = false; -var clientGotOpen = false; -var clientGotData = false; - -var wss = new WebSocketServer(); -wss.on('listening', function() { - var ws = new WebSocket('ws+unix://' + PATH); - ws.on('open', function() { - clientGotOpen = true; - - ws.close(); - }); - ws.on('data', function(d) { - assert.equal(d.toString('utf8'), S_MSG); - clientGotData = true; - }); -}); -wss.on('connection', function(c) { - serverGotConnection = true; - - c.write(S_MSG); - wss.close(); -}); -wss.listen(PATH); - -process.on('exit', function() { - assert.ok(serverGotConnection); - assert.ok(clientGotOpen); - assert.ok(clientGotData); - - try { - fs.unlinkSync(PATH); - } catch(e) { } -}); diff --git a/node_modules/socket.io/test/common.js b/node_modules/socket.io/test/common.js deleted file mode 100644 index 98aa502..0000000 --- a/node_modules/socket.io/test/common.js +++ /dev/null @@ -1,274 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var io = require('socket.io') - , parser = io.parser - , http = require('http') - , https = require('https') - , WebSocket = require('../support/node-websocket-client/lib/websocket').WebSocket; - -/** - * Exports. - */ - -var should = module.exports = require('should'); - -should.HTTPClient = HTTPClient; - -/** - * Client utility. - * - * @api publiC - */ - -function HTTPClient (port) { - this.port = port; - this.agent = new http.Agent({ - host: 'localhost' - , port: port - }); -}; - -/** - * Issue a request - * - * @api private - */ - -HTTPClient.prototype.request = function (path, opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - opts = opts || {}; - opts.agent = this.agent; - opts.host = 'localhost'; - opts.port = this.port; - opts.path = path.replace(/{protocol}/g, io.protocol); - - opts.headers = opts.headers || {}; - opts.headers.Host = 'localhost'; - opts.headers.Connection = 'keep-alive'; - - var req = http.request(opts, function (res) { - if (false === opts.buffer) - return fn && fn(res); - - var buf = ''; - - res.on('data', function (chunk) { - buf += chunk; - }); - - res.on('end', function () { - fn && fn(res, opts.parse ? opts.parse(buf) : buf); - }); - }); - - req.on('error', function (err) { }); - - if (undefined !== opts.data) - req.write(opts.data); - - req.end(); - - return req; -}; - -/** - * Terminates the client and associated connections. - * - * @api public - */ - -HTTPClient.prototype.end = function () { - // node =v0.5 compat - var self = this; - Object.keys(this.agent.sockets).forEach(function (socket) { - for (var i = 0, l = self.agent.sockets[socket].length; i < l; ++i) { - if (self.agent.sockets[socket][i]._handle) { - self.agent.sockets[socket][i]._handle.socket.end(); - } - } - }); -}; - -/** - * Issue a GET request - * - * @api public - */ - -HTTPClient.prototype.get = function (path, opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - opts = opts || {}; - opts.method = 'GET'; - - // override the parser for transport requests - if (/\/(xhr-polling|htmlfile|jsonp-polling)\//.test(path)) { - // parser that might be necessary for transport-specific framing - var transportParse = opts.parse; - opts.parse = function (data) { - if (data === '') return data; - - data = transportParse ? transportParse(data) : data; - return parser.decodePayload(data); - }; - } else { - opts.parse = undefined; - } - - return this.request(path, opts, fn); -}; - -/** - * Issue a POST request - * - * @api private - */ - -HTTPClient.prototype.post = function (path, data, opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - opts = opts || {}; - opts.method = 'POST'; - opts.data = data; - - return this.request(path, opts, fn); -}; - -/** - * Issue a HEAD request - * - * @api private - */ - -HTTPClient.prototype.head = function (path, opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - opts = opts || {}; - opts.method = 'HEAD'; - - return this.request(path, opts, fn); -}; - -/** - * Performs a handshake (GET) request - * - * @api private - */ - -HTTPClient.prototype.handshake = function (opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - return this.get('/socket.io/{protocol}', opts, function (res, data) { - fn && fn.apply(null, data.split(':')); - }); -}; - -/** - * Generates a new client for the given port. - * - * @api private - */ - -client = function (port) { - return new HTTPClient(port); -}; - -/** - * Create a socket.io server. - */ - -create = function (cl) { - var manager = io.listen(cl.port); - manager.set('client store expiration', 0); - return manager; -}; - -/** - * WebSocket socket.io client. - * - * @api private - */ - -function WSClient (port, sid, transport) { - this.sid = sid; - this.port = port; - this.transportName = transport || 'websocket'; - WebSocket.call( - this - , 'ws://localhost:' + port + '/socket.io/' - + io.protocol + '/' + this.transportName + '/' + sid - ); -}; - -/** - * Inherits from WebSocket. - */ - -WSClient.prototype.__proto__ = WebSocket.prototype; - -/** - * Overrides message event emission. - * - * @api private - */ - -WSClient.prototype.emit = function (name) { - var args = arguments; - - if (name == 'message' || name == 'data') { - args[1] = parser.decodePacket(args[1].toString()); - } - - return WebSocket.prototype.emit.apply(this, arguments); -}; - -/** - * Writes a packet - */ - -WSClient.prototype.packet = function (pack) { - this.write(parser.encodePacket(pack)); - return this; -}; - -/** - * Creates a websocket client. - * - * @api public - */ - -websocket = function (cl, sid, transport) { - return new WSClient(cl.port, sid, transport); -}; diff --git a/node_modules/socket.io/test/fixtures/cert.crt b/node_modules/socket.io/test/fixtures/cert.crt deleted file mode 100644 index 5883cd4..0000000 --- a/node_modules/socket.io/test/fixtures/cert.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJAMUSOvlaeyQHMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTAxMTE2MDkzMjQ5WhcNMTMxMTE1MDkzMjQ5WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEVwfPQQp4X -wtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+1FAE0c5o -exPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404WthquTqg -S7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy25IyBK3QJ -c+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWAQsqW+COL -0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABo1AwTjAdBgNVHQ4EFgQUDnV4d6mD -tOnluLoCjkUHTX/n4agwHwYDVR0jBBgwFoAUDnV4d6mDtOnluLoCjkUHTX/n4agw -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAFwV4MQfTo+qMv9JMiyno -IEiqfOz4RgtmBqRnXUffcjS2dhc7/z+FPZnM79Kej8eLHoVfxCyWRHFlzm93vEdv -wxOCrD13EDOi08OOZfxWyIlCa6Bg8cMAKqQzd2OvQOWqlRWBTThBJIhWflU33izX -Qn5GdmYqhfpc+9ZHHGhvXNydtRQkdxVK2dZNzLBvBlLlRmtoClU7xm3A+/5dddeP -AQHEPtyFlUw49VYtZ3ru6KqPms7MKvcRhYLsy9rwSfuuniMlx4d0bDR7TOkw0QQS -A0N8MGQRQpzl4mw4jLzyM5d5QtuGBh2P6hPGa0YQxtI3RPT/p6ENzzBiAKXiSfzo -xw== ------END CERTIFICATE----- diff --git a/node_modules/socket.io/test/fixtures/key.key b/node_modules/socket.io/test/fixtures/key.key deleted file mode 100644 index f31ff3d..0000000 --- a/node_modules/socket.io/test/fixtures/key.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEV -wfPQQp4XwtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+ -1FAE0c5oexPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404 -WthquTqgS7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy2 -5IyBK3QJc+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWA -QsqW+COL0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABAoIBAGe4+9VqZfJN+dsq -8Osyuz01uQ8OmC0sAWTIqUlQgENIyf9rCJsUBlYmwR5BT6Z69XP6QhHdpSK+TiAR -XUz0EqG9HYzcxHIBaACP7j6iRoQ8R4kbbiWKo0z3WqQGIOqFjvD/mKEuQdE5mEYw -eOUCG6BnX1WY2Yr8WKd2AA/tp0/Y4d8z04u9eodMpSTbHTzYMJb5SbBN1vo6FY7q -8zSuO0BMzXlAxUsCwHsk1GQHFr8Oh3zIR7bQGtMBouI+6Lhh7sjFYsfxJboqMTBV -IKaA216M6ggHG7MU1/jeKcMGDmEfqQLQoyWp29rMK6TklUgipME2L3UD7vTyAVzz -xbVOpZkCgYEA8CXW4sZBBrSSrLR5SB+Ubu9qNTggLowOsC/kVKB2WJ4+xooc5HQo -mFhq1v/WxPQoWIxdYsfg2odlL+JclK5Qcy6vXmRSdAQ5lK9gBDKxZSYc3NwAw2HA -zyHCTK+I0n8PBYQ+yGcrxu0WqTGnlLW+Otk4CejO34WlgHwbH9bbY5UCgYEA3ZvT -C4+OoMHXlmICSt29zUrYiL33IWsR3/MaONxTEDuvgkOSXXQOl/8Ebd6Nu+3WbsSN -bjiPC/JyL1YCVmijdvFpl4gjtgvfJifs4G+QHvO6YfsYoVANk4u6g6rUuBIOwNK4 -RwYxwDc0oysp+g7tPxoSgDHReEVKJNzGBe9NGGsCgYEA4O4QP4gCEA3B9BF2J5+s -n9uPVxmiyvZUK6Iv8zP4pThTBBMIzNIf09G9AHPQ7djikU2nioY8jXKTzC3xGTHM -GJZ5m6fLsu7iH+nDvSreDSeNkTBfZqGAvoGYQ8uGE+L+ZuRfCcXYsxIOT5s6o4c3 -Dle2rVFpsuKzCY00urW796ECgYBn3go75+xEwrYGQSer6WR1nTgCV29GVYXKPooy -zmmMOT1Yw80NSkEw0pFD4cTyqVYREsTrPU0mn1sPfrOXxnGfZSVFpcR/Je9QVfQ7 -eW7GYxwfom335aqHVj10SxRqteP+UoWWnHujCPz94VRKZMakBddYCIGSan+G6YdS -7sdmwwKBgBc2qj0wvGXDF2kCLwSGfWoMf8CS1+5fIiUIdT1e/+7MfDdbmLMIFVjF -QKS3zVViXCbrG5SY6wS9hxoc57f6E2A8vcaX6zy2xkZlGHQCpWRtEM5R01OWJQaH -HsHMmQZGUQVoDm1oRkDhrTFK4K3ukc3rAxzeTZ96utOQN8/KJsTv ------END RSA PRIVATE KEY----- diff --git a/node_modules/socket.io/test/hybi-common.js b/node_modules/socket.io/test/hybi-common.js deleted file mode 100644 index 31aad72..0000000 --- a/node_modules/socket.io/test/hybi-common.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Returns a Buffer from a "ff 00 ff"-type hex string. - */ - -getBufferFromHexString = function(byteStr) { - var bytes = byteStr.split(' '); - var buf = new Buffer(bytes.length); - for (var i = 0; i < bytes.length; ++i) { - buf[i] = parseInt(bytes[i], 16); - } - return buf; -} - -/** - * Returns a hex string from a Buffer. - */ - -getHexStringFromBuffer = function(data) { - var s = ''; - for (var i = 0; i < data.length; ++i) { - s += padl(data[i].toString(16), 2, '0') + ' '; - } - return s.trim(); -} - -/** - * Splits a buffer in two parts. - */ - -splitBuffer = function(buffer) { - var b1 = new Buffer(Math.ceil(buffer.length / 2)); - buffer.copy(b1, 0, 0, b1.length); - var b2 = new Buffer(Math.floor(buffer.length / 2)); - buffer.copy(b2, 0, b1.length, b1.length + b2.length); - return [b1, b2]; -} - -/** - * Performs hybi07+ type masking on a hex string or buffer. - */ - -mask = function(buf, maskString) { - if (typeof buf == 'string') buf = new Buffer(buf); - var mask = getBufferFromHexString(maskString || '34 83 a8 68'); - for (var i = 0; i < buf.length; ++i) { - buf[i] ^= mask[i % 4]; - } - return buf; -} - -/** - * Returns a hex string representing the length of a message - */ - -getHybiLengthAsHexString = function(len, masked) { - if (len < 126) { - var buf = new Buffer(1); - buf[0] = (masked ? 0x80 : 0) | len; - } - else if (len < 65536) { - var buf = new Buffer(3); - buf[0] = (masked ? 0x80 : 0) | 126; - getBufferFromHexString(pack(4, len)).copy(buf, 1); - } - else { - var buf = new Buffer(9); - buf[0] = (masked ? 0x80 : 0) | 127; - getBufferFromHexString(pack(16, len)).copy(buf, 1); - } - return getHexStringFromBuffer(buf); -} - -/** - * Unpacks a Buffer into a number. - */ - -unpack = function(buffer) { - var n = 0; - for (var i = 0; i < buffer.length; ++i) { - n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; - } - return n; -} - -/** - * Returns a hex string, representing a specific byte count 'length', from a number. - */ - -pack = function(length, number) { - return padl(number.toString(16), length, '0').replace(/(\d\d)/g, '$1 ').trim(); -} - -/** - * Left pads the string 's' to a total length of 'n' with char 'c'. - */ - -padl = function(s, n, c) { - return new Array(1 + n - s.length).join(c) + s; -} diff --git a/node_modules/socket.io/test/io.test.js b/node_modules/socket.io/test/io.test.js deleted file mode 100644 index 1e8ed7d..0000000 --- a/node_modules/socket.io/test/io.test.js +++ /dev/null @@ -1,125 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , fs = require('fs') - , http = require('http') - , https = require('https') - , should = require('./common') - , ports = 15000; - -/** - * Test. - */ - -module.exports = { - - 'test that protocol version is present': function (done) { - sio.protocol.should.be.a('number'); - done(); - }, - - 'test that default transports are present': function (done) { - sio.Manager.defaultTransports.should.be.an.instanceof(Array); - done(); - }, - - 'test that version is present': function (done) { - sio.version.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/); - done(); - }, - - 'test listening with a port': function (done) { - var cl = client(++ports) - , io = create(cl); - - io.server.should.be.an.instanceof(http.Server); - - cl.get('/', function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('Welcome to socket.io.'); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test listening with a server': function (done) { - var server = http.createServer() - , io = sio.listen(server) - , port = ++ports - , cl = client(port); - - server.listen(port); - - cl.get('/socket.io', function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('Welcome to socket.io.'); - - cl.end(); - server.close(); - done(); - }); - }, - - 'test listening with a https server': function (done) { - var server = https.createServer({ - key: fs.readFileSync(__dirname + '/fixtures/key.key') - , cert: fs.readFileSync(__dirname + '/fixtures/cert.crt') - }, function () { }) - , io = sio.listen(server) - , port = ++ports; - - server.listen(port); - - var req = require('https').get({ - host: 'localhost' - , port: port - , path: '/socket.io' - }, function (res) { - res.statusCode.should.eql(200); - - var buf = ''; - - res.on('data', function (data) { - buf += data; - }); - - res.on('end', function () { - buf.should.eql('Welcome to socket.io.'); - - res.socket.end(); - server.close(); - done(); - }); - }); - }, - - 'test listening with no arguments listens on 80': function (done) { - try { - var io = sio.listen() - , cl = client(80); - - cl.get('/socket.io', function (res) { - res.statusCode.should.eql(200); - - cl.end(); - io.server.close(); - done(); - }); - done(); - } catch (e) { - e.should.match(/EACCES/); - done(); - } - } -}; diff --git a/node_modules/socket.io/test/leaks/socket.leaktest.js b/node_modules/socket.io/test/leaks/socket.leaktest.js deleted file mode 100644 index 8613b3c..0000000 --- a/node_modules/socket.io/test/leaks/socket.leaktest.js +++ /dev/null @@ -1,54 +0,0 @@ -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -require.paths.unshift(__dirname + '/../../lib'); - -var assertvanish = require('assertvanish') - , common = require('../common') - , ports = 15800; - -function resultCallback (leaks, leakedSocket) { - if (leaks) { - console.error('Leak detected'); - process.exit(1); - } else { - console.error('No leaks'); - process.exit(0); - } -}; - -/** - * Test. - */ - -var cl = client(++ports); -var io = create(cl); - -io.sockets.on('connection', function (socket) { - console.log('connected'); - - socket.on('disconnect', function() { - console.log("client gone"); - setTimeout(gc, 1000); - assertvanish(socket, 2000, {silent: true, callback: resultCallback}); - }); -}); - -setTimeout(function() { - cl.handshake(function (sid) { - var ws = websocket(cl, sid); - ws.on('open', function () { - console.log('open!'); - setTimeout(function() { - ws.close(); - }, 500); - }); - }); -}, 100); diff --git a/node_modules/socket.io/test/manager.test.js b/node_modules/socket.io/test/manager.test.js deleted file mode 100644 index 4d4cad3..0000000 --- a/node_modules/socket.io/test/manager.test.js +++ /dev/null @@ -1,572 +0,0 @@ -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , http = require('http') - , should = require('./common') - , ports = 15100; - -/** - * Test. - */ - -module.exports = { - - 'test setting and getting a configuration flag': function (done) { - var port = ++ports - , io = sio.listen(http.createServer()); - - io.set('a', 'b'); - io.get('a').should.eql('b'); - - var port = ++ports - , io = sio.listen(http.createServer()); - - io.configure(function () { - io.set('a', 'b'); - io.enable('tobi'); - }); - - io.get('a').should.eql('b'); - - done(); - }, - - 'test enabling and disabling a configuration flag': function (done) { - var port = ++ports - , io = sio.listen(http.createServer()); - - io.enable('flag'); - io.enabled('flag').should.be.true; - io.disabled('flag').should.be.false; - - io.disable('flag'); - var port = ++ports - , io = sio.listen(http.createServer()); - - io.configure(function () { - io.enable('tobi'); - }); - - io.enabled('tobi').should.be.true; - - done(); - }, - - 'test configuration callbacks with envs': function (done) { - var port = ++ports - , io = sio.listen(http.createServer()); - - process.env.NODE_ENV = 'development'; - - io.configure('production', function () { - io.set('ferret', 'tobi'); - }); - - io.configure('development', function () { - io.set('ferret', 'jane'); - }); - - io.get('ferret').should.eql('jane'); - done(); - }, - - 'test configuration callbacks conserve scope': function (done) { - var port = ++ports - , io = sio.listen(http.createServer()) - , calls = 0; - - process.env.NODE_ENV = 'development'; - - io.configure(function () { - this.should.eql(io); - calls++; - }); - - io.configure('development', function () { - this.should.eql(io); - calls++; - }); - - calls.should.eql(2); - done(); - }, - - 'test configuration update notifications': function (done) { - var port = ++ports - , io = sio.listen(http.createServer()) - , calls = 0; - - io.on('set:foo', function () { - calls++; - }); - - io.set('foo', 'bar'); - io.set('baz', 'bar'); - - calls.should.eql(1); - - io.enable('foo'); - io.disable('foo'); - - calls.should.eql(3); - - done(); - }, - - 'test that normal requests are still served': function (done) { - var server = http.createServer(function (req, res) { - res.writeHead(200); - res.end('woot'); - }); - - var io = sio.listen(server) - , port = ++ports - , cl = client(port); - - server.listen(ports); - - cl.get('/socket.io', function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('Welcome to socket.io.'); - - cl.get('/woot', function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('woot'); - - cl.end(); - server.close(); - done(); - }); - }); - }, - - 'test that you can disable clients': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.disable('browser client'); - }); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('Welcome to socket.io.'); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test handshake': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(200); - data.should.match(/([^:]+):([0-9]+)?:([0-9]+)?:(.+)/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test handshake with unsupported protocol version': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.get('/socket.io/-1/', function (res, data) { - res.statusCode.should.eql(500); - data.should.match(/Protocol version not supported/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test authorization failure in handshake': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - function auth (data, fn) { - fn(null, false); - }; - - io.set('authorization', auth); - }); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(403); - data.should.match(/handshake unauthorized/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test a handshake error': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - function auth (data, fn) { - fn(new Error); - }; - - io.set('authorization', auth); - }); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(500); - data.should.match(/handshake error/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that a referer is accepted for *:* origin': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('origins', '*:*'); - }); - - cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com:82/something' } }, function (res, data) { - res.statusCode.should.eql(200); - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that valid referer is accepted for addr:* origin': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('origins', 'foo.bar.com:*'); - }); - - cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com/something' } }, function (res, data) { - res.statusCode.should.eql(200); - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that erroneous referer is denied for addr:* origin': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('origins', 'foo.bar.com:*'); - }); - - cl.get('/socket.io/{protocol}', { headers: { referer: 'http://baz.bar.com/something' } }, function (res, data) { - res.statusCode.should.eql(403); - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that valid referer port is accepted for addr:port origin': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('origins', 'foo.bar.com:81'); - }); - - cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com:81/something' } }, function (res, data) { - res.statusCode.should.eql(200); - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that erroneous referer port is denied for addr:port origin': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('origins', 'foo.bar.com:81'); - }); - - cl.get('/socket.io/{protocol}', { headers: { referer: 'http://foo.bar.com/something' } }, function (res, data) { - res.statusCode.should.eql(403); - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test handshake cross domain access control': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port) - , headers = { - Origin: 'http://example.org:1337' - , Cookie: 'name=value' - }; - - cl.get('/socket.io/{protocol}/', { headers:headers }, function (res, data) { - res.statusCode.should.eql(200); - res.headers['access-control-allow-origin'].should.eql('*'); - res.headers['access-control-allow-credentials'].should.eql('true'); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test limiting the supported transports for a manager': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('transports', ['tobi', 'jane']); - }); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(200); - data.should.match(/([^:]+):([0-9]+)?:([0-9]+)?:tobi,jane/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test setting a custom close timeout': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('close timeout', 66); - }); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(200); - data.should.match(/([^:]+):([0-9]+)?:66?:(.*)/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test setting a custom heartbeat timeout': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('heartbeat timeout', 33); - }); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(200); - data.should.match(/([^:]+):33:([0-9]+)?:(.*)/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test disabling timeouts': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.configure(function () { - io.set('heartbeat timeout', null); - io.set('close timeout', ''); - }); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(200); - data.should.match(/([^:]+)::?:(.*)/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test disabling heartbeats': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , messages = 0 - , beat = false - , ws; - - io.configure(function () { - io.disable('heartbeats'); - io.set('heartbeat interval', .05); - io.set('heartbeat timeout', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - setTimeout(function () { - socket.disconnect(); - }, io.get('heartbeat timeout') * 1000 + 100); - - socket.on('disconnect', function (reason) { - beat.should.be.false; - ws.finishClose(); - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.get('/socket.io/{protocol}/', function (res, data) { - res.statusCode.should.eql(200); - data.should.match(/([^:]+)::[\.0-9]+:(.*)/); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('message', function (packet) { - if (++messages == 1) { - packet.type.should.eql('connect'); - } else if (packet.type == 'heartbeat'){ - beat = true; - } - }); - }); - }); - }, - - 'no duplicate room members': function (done) { - var port = ++ports - , io = sio.listen(port); - - Object.keys(io.rooms).length.should.equal(0); - - io.onJoin(123, 'foo'); - io.rooms.foo.length.should.equal(1); - - io.onJoin(123, 'foo'); - io.rooms.foo.length.should.equal(1); - - io.onJoin(124, 'foo'); - io.rooms.foo.length.should.equal(2); - - io.onJoin(124, 'foo'); - io.rooms.foo.length.should.equal(2); - - io.onJoin(123, 'bar'); - io.rooms.foo.length.should.equal(2); - io.rooms.bar.length.should.equal(1); - - io.onJoin(123, 'bar'); - io.rooms.foo.length.should.equal(2); - io.rooms.bar.length.should.equal(1); - - io.onJoin(124, 'bar'); - io.rooms.foo.length.should.equal(2); - io.rooms.bar.length.should.equal(2); - - io.onJoin(124, 'bar'); - io.rooms.foo.length.should.equal(2); - io.rooms.bar.length.should.equal(2); - - process.nextTick(function() { - io.server.close(); - done(); - }); - }, - - 'test passing options directly to the Manager through listen': function (done) { - var port = ++ports - , io = sio.listen(port, { resource: '/my resource', custom: 'opt' }); - - io.get('resource').should.equal('/my resource'); - io.get('custom').should.equal('opt'); - process.nextTick(function() { - io.server.close(); - done(); - }); - }, - - 'test disabling the log': function (done) { - var port = ++ports - , io = sio.listen(port, { log: false }) - , _console = console.log - , calls = 0; - - // the logger uses console.log to output data, override it to see if get's - // used - console.log = function () { ++calls }; - - io.log.debug('test'); - io.log.log('testing'); - - console.log = _console; - calls.should.equal(0); - - process.nextTick(function() { - io.server.close(); - done(); - }); - }, - - 'test disabling logging with colors': function (done) { - var port = ++ports - , io = sio.listen(port, { 'log colors': false }) - , _console = console.log - , calls = 0; - - // the logger uses console.log to output data, override it to see if get's - // used - console.log = function (data) { - ++calls; - data.indexOf('\033').should.equal(-1); - }; - - io.log.debug('test'); - io.log.log('testing'); - - console.log = _console; - calls.should.equal(2); - - process.nextTick(function() { - io.server.close(); - done(); - }); - } -}; diff --git a/node_modules/socket.io/test/namespace.test.js b/node_modules/socket.io/test/namespace.test.js deleted file mode 100644 index 7b99a28..0000000 --- a/node_modules/socket.io/test/namespace.test.js +++ /dev/null @@ -1,286 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , should = require('./common') - , ports = 15700; - -/** - * Test. - */ - -module.exports = { - 'namespace pass no authentication': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.of('/a') - .on('connection', function (socket) { - cl.end(); - ws.finishClose(); - io.server.close() - done(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('open', function () { - ws.packet({ - type: 'connect' - , endpoint: '/a' - }); - }) - }); - }, - - 'namespace pass authentication': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.of('/a') - .authorization(function (data, fn) { - fn(null, true); - }) - .on('connection', function (socket) { - cl.end(); - ws.finishClose(); - io.server.close() - done(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('open', function () { - ws.packet({ - type: 'connect' - , endpoint: '/a' - }); - }) - }); - }, - - 'namespace authentication handshake data': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.of('/a') - .authorization(function (data, fn) { - data.foo = 'bar'; - fn(null, true); - }) - .on('connection', function (socket) { - (!!socket.handshake.address.address).should.be.true; - (!!socket.handshake.address.port).should.be.true; - socket.handshake.headers.host.should.equal('localhost'); - socket.handshake.headers.connection.should.equal('keep-alive'); - socket.handshake.time.should.match(/GMT/); - socket.handshake.foo.should.equal('bar'); - - cl.end(); - ws.finishClose(); - io.server.close() - done(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('open', function () { - ws.packet({ - type: 'connect' - , endpoint: '/a' - }); - }) - }); - }, - - 'namespace fail authentication': function (done) { - var cl = client(++ports) - , io = create(cl) - , calls = 0 - , ws; - - io.of('/a') - .authorization(function (data, fn) { - fn(null, false); - }) - .on('connection', function (socket) { - throw new Error('Should not be called'); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('open', function () { - ws.packet({ - type: 'connect' - , endpoint: '/a' - }); - }); - - ws.on('message', function (data) { - if (data.endpoint == '/a') { - data.type.should.eql('error'); - data.reason.should.eql('unauthorized') - - cl.end(); - ws.finishClose(); - io.server.close() - done(); - } - }) - }); - }, - - 'broadcasting sends and emits on a namespace': function (done) { - var cl = client(++ports) - , io = create(cl) - , calls = 0 - , connect = 0 - , message = 0 - , events = 0 - , expected = 5 - , ws1 - , ws2; - - io.of('a') - .on('connection', function (socket){ - if (connect < 2) { - return; - } - socket.broadcast.emit('b', 'test'); - socket.broadcast.json.emit('json', {foo:'bar'}); - socket.broadcast.send('foo'); - }); - - function finish () { - connect.should.equal(2); - message.should.equal(1); - events.should.equal(2); - cl.end(); - ws1.finishClose(); - ws2.finishClose(); - io.server.close(); - done(); - } - - cl.handshake(function (sid) { - ws1 = websocket(cl, sid); - ws1.on('message', function (data) { - if (data.type === 'connect') { - if (connect == 0) { - cl.handshake(function (sid) { - ws2 = websocket(cl, sid); - ws2.on('open', function () { - ws2.packet({ - type: 'connect' - , endpoint: 'a' - }); - }); - }); - } - ++connect; - if (++calls === expected) finish(); - } - - if (data.type === 'message') { - ++message; - if (++calls === expected) finish(); - } - - if (data.type === 'event') { - if (data.name === 'b' || data.name === 'json') ++events; - if (++calls === expected) finish(); - } - }); - ws1.on('open', function() { - ws1.packet({ - type: 'connect' - , endpoint: 'a' - }); - }); - }) - }, - - 'joining rooms inside a namespace': function (done) { - var cl = client(++ports) - , io = create(cl) - , calls = 0 - , ws; - - io.of('/foo').on('connection', function (socket) { - socket.join('foo.bar'); - this.in('foo.bar').emit('baz', 'pewpew'); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - - ws.on('open', function (){ - ws.packet({ - type: 'connect' - , endpoint: '/foo' - }); - }); - - ws.on('message', function (data) { - if (data.type === 'event') { - data.name.should.equal('baz'); - - cl.end(); - ws.finishClose(); - io.server.close(); - done(); - } - }); - }) - }, - - 'ignoring blacklisted events': function (done) { - var cl = client(++ports) - , io = create(cl) - , calls = 0 - , ws; - - io.set('heartbeat interval', 1); - io.set('blacklist', ['foobar']); - - io.sockets.on('connection', function (socket) { - socket.on('foobar', function () { - calls++; - }); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - - ws.on('open', function (){ - ws.packet({ - type: 'event' - , name: 'foobar' - , endpoint: '' - }); - }); - - ws.on('message', function (data) { - if (data.type === 'heartbeat') { - cl.end(); - ws.finishClose(); - io.server.close(); - - calls.should.equal(0); - done(); - } - }); - }); - } -}; diff --git a/node_modules/socket.io/test/parser.test.js b/node_modules/socket.io/test/parser.test.js deleted file mode 100644 index ee51033..0000000 --- a/node_modules/socket.io/test/parser.test.js +++ /dev/null @@ -1,356 +0,0 @@ - -/** - * Test dependencies. - */ - -var parser = require('socket.io').parser - , decode = parser.decode - , should = require('./common'); - -/** - * Test. - */ - -module.exports = { - - 'decoding error packet': function () { - parser.decodePacket('7:::').should.eql({ - type: 'error' - , reason: '' - , advice: '' - , endpoint: '' - }); - }, - - 'decoding error packet with reason': function () { - parser.decodePacket('7:::0').should.eql({ - type: 'error' - , reason: 'transport not supported' - , advice: '' - , endpoint: '' - }); - }, - - 'decoding error packet with reason and advice': function () { - parser.decodePacket('7:::2+0').should.eql({ - type: 'error' - , reason: 'unauthorized' - , advice: 'reconnect' - , endpoint: '' - }); - }, - - 'decoding error packet with endpoint': function () { - parser.decodePacket('7::/woot').should.eql({ - type: 'error' - , reason: '' - , advice: '' - , endpoint: '/woot' - }); - }, - - 'decoding ack packet': function () { - parser.decodePacket('6:::140').should.eql({ - type: 'ack' - , ackId: '140' - , endpoint: '' - , args: [] - }); - }, - - 'decoding ack packet with args': function () { - parser.decodePacket('6:::12+["woot","wa"]').should.eql({ - type: 'ack' - , ackId: '12' - , endpoint: '' - , args: ['woot', 'wa'] - }); - }, - - 'decoding ack packet with bad json': function () { - var thrown = false; - - try { - parser.decodePacket('6:::1+{"++]').should.eql({ - type: 'ack' - , ackId: '1' - , endpoint: '' - , args: [] - }); - } catch (e) { - thrown = true; - } - - thrown.should.be.false; - }, - - 'decoding json packet': function () { - parser.decodePacket('4:::"2"').should.eql({ - type: 'json' - , endpoint: '' - , data: '2' - }); - }, - - 'decoding json packet with message id and ack data': function () { - parser.decodePacket('4:1+::{"a":"b"}').should.eql({ - type: 'json' - , id: 1 - , ack: 'data' - , endpoint: '' - , data: { a: 'b' } - }); - }, - - 'decoding an event packet': function () { - parser.decodePacket('5:::{"name":"woot"}').should.eql({ - type: 'event' - , name: 'woot' - , endpoint: '' - , args: [] - }); - }, - - 'decoding an event packet with message id and ack': function () { - parser.decodePacket('5:1+::{"name":"tobi"}').should.eql({ - type: 'event' - , id: 1 - , ack: 'data' - , endpoint: '' - , name: 'tobi' - , args: [] - }); - }, - - 'decoding an event packet with data': function () { - parser.decodePacket('5:::{"name":"edwald","args":[{"a": "b"},2,"3"]}') - .should.eql({ - type: 'event' - , name: 'edwald' - , endpoint: '' - , args: [{a: 'b'}, 2, '3'] - }); - }, - - 'decoding a message packet': function () { - parser.decodePacket('3:::woot').should.eql({ - type: 'message' - , endpoint: '' - , data: 'woot' - }); - }, - - 'decoding a message packet with id and endpoint': function () { - parser.decodePacket('3:5:/tobi').should.eql({ - type: 'message' - , id: 5 - , ack: true - , endpoint: '/tobi' - , data: '' - }); - }, - - 'decoding a heartbeat packet': function () { - parser.decodePacket('2:::').should.eql({ - type: 'heartbeat' - , endpoint: '' - }); - }, - - 'decoding a connection packet': function () { - parser.decodePacket('1::/tobi').should.eql({ - type: 'connect' - , endpoint: '/tobi' - , qs: '' - }); - }, - - 'decoding a connection packet with query string': function () { - parser.decodePacket('1::/test:?test=1').should.eql({ - type: 'connect' - , endpoint: '/test' - , qs: '?test=1' - }); - }, - - 'decoding a disconnection packet': function () { - parser.decodePacket('0::/woot').should.eql({ - type: 'disconnect' - , endpoint: '/woot' - }); - }, - - 'encoding error packet': function () { - parser.encodePacket({ - type: 'error' - , reason: '' - , advice: '' - , endpoint: '' - }).should.eql('7::'); - }, - - 'encoding error packet with reason': function () { - parser.encodePacket({ - type: 'error' - , reason: 'transport not supported' - , advice: '' - , endpoint: '' - }).should.eql('7:::0'); - }, - - 'encoding error packet with reason and advice': function () { - parser.encodePacket({ - type: 'error' - , reason: 'unauthorized' - , advice: 'reconnect' - , endpoint: '' - }).should.eql('7:::2+0'); - }, - - 'encoding error packet with endpoint': function () { - parser.encodePacket({ - type: 'error' - , reason: '' - , advice: '' - , endpoint: '/woot' - }).should.eql('7::/woot'); - }, - - 'encoding ack packet': function () { - parser.encodePacket({ - type: 'ack' - , ackId: '140' - , endpoint: '' - , args: [] - }).should.eql('6:::140'); - }, - - 'encoding ack packet with args': function () { - parser.encodePacket({ - type: 'ack' - , ackId: '12' - , endpoint: '' - , args: ['woot', 'wa'] - }).should.eql('6:::12+["woot","wa"]'); - }, - - 'encoding json packet': function () { - parser.encodePacket({ - type: 'json' - , endpoint: '' - , data: '2' - }).should.eql('4:::"2"'); - }, - - 'encoding json packet with message id and ack data': function () { - parser.encodePacket({ - type: 'json' - , id: 1 - , ack: 'data' - , endpoint: '' - , data: { a: 'b' } - }).should.eql('4:1+::{"a":"b"}'); - }, - - 'encoding an event packet': function () { - parser.encodePacket({ - type: 'event' - , name: 'woot' - , endpoint: '' - , args: [] - }).should.eql('5:::{"name":"woot"}'); - }, - - 'encoding an event packet with message id and ack': function () { - parser.encodePacket({ - type: 'event' - , id: 1 - , ack: 'data' - , endpoint: '' - , name: 'tobi' - , args: [] - }).should.eql('5:1+::{"name":"tobi"}'); - }, - - 'encoding an event packet with data': function () { - parser.encodePacket({ - type: 'event' - , name: 'edwald' - , endpoint: '' - , args: [{a: 'b'}, 2, '3'] - }).should.eql('5:::{"name":"edwald","args":[{"a":"b"},2,"3"]}'); - }, - - 'encoding a message packet': function () { - parser.encodePacket({ - type: 'message' - , endpoint: '' - , data: 'woot' - }).should.eql('3:::woot'); - }, - - 'encoding a message packet with id and endpoint': function () { - parser.encodePacket({ - type: 'message' - , id: 5 - , ack: true - , endpoint: '/tobi' - , data: '' - }).should.eql('3:5:/tobi'); - }, - - 'encoding a heartbeat packet': function () { - parser.encodePacket({ - type: 'heartbeat' - , endpoint: '' - }).should.eql('2::'); - }, - - 'encoding a connection packet': function () { - parser.encodePacket({ - type: 'connect' - , endpoint: '/tobi' - , qs: '' - }).should.eql('1::/tobi'); - }, - - 'encoding a connection packet with query string': function () { - parser.encodePacket({ - type: 'connect' - , endpoint: '/test' - , qs: '?test=1' - }).should.eql('1::/test:?test=1'); - }, - - 'encoding a disconnection packet': function () { - parser.encodePacket({ - type: 'disconnect' - , endpoint: '/woot' - }).should.eql('0::/woot'); - }, - - 'test decoding a payload': function () { - parser.decodePayload('\ufffd5\ufffd3:::5\ufffd7\ufffd3:::53d' - + '\ufffd3\ufffd0::').should.eql([ - { type: 'message', data: '5', endpoint: '' } - , { type: 'message', data: '53d', endpoint: '' } - , { type: 'disconnect', endpoint: '' } - ]); - }, - - 'test encoding a payload': function () { - parser.encodePayload([ - parser.encodePacket({ type: 'message', data: '5', endpoint: '' }) - , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' }) - ]).should.eql('\ufffd5\ufffd3:::5\ufffd7\ufffd3:::53d') - }, - - 'test decoding newline': function () { - parser.decodePacket('3:::\n').should.eql({ - type: 'message' - , endpoint: '' - , data: '\n' - }); - } - -}; diff --git a/node_modules/socket.io/test/static.test.js b/node_modules/socket.io/test/static.test.js deleted file mode 100644 index 3a45ab7..0000000 --- a/node_modules/socket.io/test/static.test.js +++ /dev/null @@ -1,549 +0,0 @@ - -/*! -* socket.io-node -* Copyright(c) 2011 LearnBoost -* MIT Licensed -*/ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , should = require('./common') - , ports = 15400; - -/** - * Test. - */ - -module.exports = { - - 'test that the default static files are available': function (done) { - var port = ++ports - , io = sio.listen(port); - - (!!io.static.has('/socket.io.js')).should.be.true; - (!!io.static.has('/socket.io.v1.0.0.js')).should.be.true; - (!!io.static.has('/socket.io+xhr-polling.js')).should.be.true; - (!!io.static.has('/socket.io+xhr-polling.v1.0.0.js')).should.be.true; - (!!io.static.has('/static/flashsocket/WebSocketMain.swf')).should.be.true; - (!!io.static.has('/static/flashsocket/WebSocketMainInsecure.swf')).should.be.true; - - process.nextTick(function() { - io.server.close(); - done(); - }); - }, - - 'test that static files are correctly looked up': function (done) { - var port = ++ports - , io = sio.listen(port); - - (!!io.static.has('/socket.io.js')).should.be.true; - (!!io.static.has('/invalidfilehereplease.js')).should.be.false; - - process.nextTick(function() { - io.server.close(); - done(); - }); - }, - - 'test that the client is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - data.should.match(/XMLHttpRequest/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that the custom build client is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.enable('browser client etag'); - - cl.get('/socket.io/socket.io+websocket.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/); - - data.should.match(/XMLHttpRequest/); - data.should.match(/WS\.prototype\.name/); - data.should.not.match(/Flashsocket\.prototype\.name/); - data.should.not.match(/HTMLFile\.prototype\.name/); - data.should.not.match(/JSONPPolling\.prototype\.name/); - data.should.not.match(/XHRPolling\.prototype\.name/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that the client is build with the enabled transports': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.set('transports', ['websocket']); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - - data.should.match(/XMLHttpRequest/); - data.should.match(/WS\.prototype\.name/); - data.should.not.match(/Flashsocket\.prototype\.name/); - data.should.not.match(/HTMLFile\.prototype\.name/); - data.should.not.match(/JSONPPolling\.prototype\.name/); - data.should.not.match(/XHRPolling\.prototype\.name/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that the client cache is cleared when transports change': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.set('transports', ['websocket']); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - - data.should.match(/XMLHttpRequest/); - data.should.match(/WS\.prototype\.name/); - data.should.not.match(/Flashsocket\.prototype\.name/); - data.should.not.match(/HTMLFile\.prototype\.name/); - data.should.not.match(/JSONPPolling\.prototype\.name/); - data.should.not.match(/XHRPolling\.prototype\.name/); - - io.set('transports', ['xhr-polling']); - should.strictEqual(io.static.cache['/socket.io.js'], undefined); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - - data.should.match(/XMLHttpRequest/); - data.should.match(/XHRPolling\.prototype\.name/); - data.should.not.match(/Flashsocket\.prototype\.name/); - data.should.not.match(/HTMLFile\.prototype\.name/); - data.should.not.match(/JSONPPolling\.prototype\.name/); - data.should.not.match(/WS\.prototype\.name/); - - cl.end(); - io.server.close(); - done(); - }); - }); - }, - - 'test that the client etag is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.enable('browser client etag'); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/); - - data.should.match(/XMLHttpRequest/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that the client etag is changed for new transports': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.set('transports', ['websocket']); - io.enable('browser client etag'); - - cl.get('/socket.io/socket.io.js', function (res, data) { - var wsEtag = res.headers.etag; - - io.set('transports', ['xhr-polling']); - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers.etag.should.not.equal(wsEtag); - - cl.end(); - io.server.close(); - done(); - }); - }); - }, - - 'test that the client is served with gzip': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.enable('browser client gzip'); - - cl.get('/socket.io/socket.io.js', { - headers: { - 'accept-encoding': 'deflate, gzip' - } - } - , function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-encoding'].should.eql('gzip'); - res.headers['content-length'].should.match(/([0-9]+)/); - - cl.end(); - io.server.close(); - done(); - } - ); - }, - - 'test that the cached client is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - data.should.match(/XMLHttpRequest/); - var static = io.static; - static.cache['/socket.io.js'].content.should.match(/XMLHttpRequest/); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - data.should.match(/XMLHttpRequest/); - - cl.end(); - io.server.close(); - done(); - }); - }); - }, - - 'test that the client is not cached': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.static.add('/random.js', function (path, callback) { - var random = Math.floor(Date.now() * Math.random()).toString(); - callback(null, new Buffer(random)); - }); - - io.disable('browser client cache'); - - cl.get('/socket.io/random.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - cl.get('/socket.io/random.js', function (res, random) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - data.should.not.equal(random); - - cl.end(); - io.server.close(); - done(); - }); - }); - }, - - 'test that the cached client etag is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.enable('browser client etag'); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/); - - data.should.match(/XMLHttpRequest/); - var static = io.static - , cache = static.cache['/socket.io.js']; - - cache.content.toString().should.match(/XMLHttpRequest/); - Buffer.isBuffer(cache.content).should.be.true; - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - res.headers.etag.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/); - - data.should.match(/XMLHttpRequest/); - - cl.end(); - io.server.close(); - done(); - }); - }); - }, - - 'test that the cached client sends a 304 header': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.enable('browser client etag'); - - cl.get('/socket.io/socket.io.js', function (res, data) { - cl.get('/socket.io/socket.io.js', { - headers: { - 'if-none-match': res.headers.etag - } - }, function (res, data) { - res.statusCode.should.eql(304); - - cl.end(); - io.server.close(); - done(); - } - ); - }); - }, - - 'test that client minification works': function (done) { - // server 1 - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - // server 2 - var port = ++ports - , io2 = sio.listen(port) - , cl2 = client(port); - - io.enable('browser client minification'); - - cl.get('/socket.io/socket.io.js', function (res, data) { - var length = data.length; - - cl.end(); - io.server.close(); - - cl2.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - data.should.match(/XMLHttpRequest/); - data.length.should.be.greaterThan(length); - - cl2.end(); - io2.server.close(); - done(); - }); - }); - }, - - 'test that the WebSocketMain.swf is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.get('/socket.io/static/flashsocket/WebSocketMain.swf', function (res, data) { - res.headers['content-type'].should.eql('application/x-shockwave-flash'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - var static = io.static - , cache = static.cache['/static/flashsocket/WebSocketMain.swf']; - - Buffer.isBuffer(cache.content).should.be.true; - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that the WebSocketMainInsecure.swf is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.get('/socket.io/static/flashsocket/WebSocketMainInsecure.swf', function (res, data) { - res.headers['content-type'].should.eql('application/x-shockwave-flash'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers.etag, undefined); - - var static = io.static - , cache = static.cache['/static/flashsocket/WebSocketMainInsecure.swf']; - - Buffer.isBuffer(cache.content).should.be.true; - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that swf files are not served with gzip': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.enable('browser client gzip'); - - cl.get('/socket.io/static/flashsocket/WebSocketMain.swf', { - headers: { - 'accept-encoding': 'deflate, gzip' - } - } - , function (res, data) { - res.headers['content-type'].should.eql('application/x-shockwave-flash'); - res.headers['content-length'].should.match(/([0-9]+)/); - should.strictEqual(res.headers['content-encoding'], undefined); - - cl.end(); - io.server.close(); - done(); - } - ); - }, - - 'test that you can serve custom clients': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.set('browser client handler', function (req, res) { - res.writeHead(200, { - 'Content-Type': 'application/javascript' - , 'Content-Length': 13 - , 'ETag': '1.0' - }); - res.end('custom_client'); - }); - - cl.get('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.eql(13); - res.headers.etag.should.eql('1.0'); - - data.should.eql('custom_client'); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that HEAD requests work': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.head('/socket.io/socket.io.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - - data.should.eql(''); - - cl.end(); - io.server.close() - done(); - }); - }, - - 'test that a versioned client is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - cl.get('/socket.io/socket.io.v0.8.9.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - res.headers['cache-control'] - .indexOf(io.get('browser client expires')).should.be.above(-1); - - data.should.match(/XMLHttpRequest/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that a custom versioned build client is served': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.set('browser client expires', 1337); - - cl.get('/socket.io/socket.io+websocket.v0.8.10.js', function (res, data) { - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - res.headers['cache-control'] - .indexOf(io.get('browser client expires')).should.be.above(-1); - - data.should.match(/XMLHttpRequest/); - data.should.match(/WS\.prototype\.name/); - data.should.not.match(/Flashsocket\.prototype\.name/); - data.should.not.match(/HTMLFile\.prototype\.name/); - data.should.not.match(/JSONPPolling\.prototype\.name/); - data.should.not.match(/XHRPolling\.prototype\.name/); - - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that etags are ignored for versioned requests': function (done) { - var port = ++ports - , io = sio.listen(port) - , cl = client(port); - - io.enable('browser client etag'); - - cl.get('/socket.io/socket.io.v0.8.9.js', function (res, data) { - should.strictEqual(res.headers.etag, undefined); - res.headers['content-type'].should.eql('application/javascript'); - res.headers['content-length'].should.match(/([0-9]+)/); - res.headers['cache-control'] - .indexOf(io.get('browser client expires')).should.be.above(-1); - - data.should.match(/XMLHttpRequest/); - - cl.end(); - io.server.close(); - done(); - }); - }, -}; diff --git a/node_modules/socket.io/test/stores.memory.test.js b/node_modules/socket.io/test/stores.memory.test.js deleted file mode 100644 index ecf9c96..0000000 --- a/node_modules/socket.io/test/stores.memory.test.js +++ /dev/null @@ -1,190 +0,0 @@ - -/** - * Test dependencies - * - * @api private - */ - -var sio = require('socket.io') - , should = require('should') - , MemoryStore = sio.MemoryStore; - -/** - * Test. - */ - -module.exports = { - - 'test storing data for a client': function (done) { - var store = new MemoryStore - , client = store.client('test'); - - client.id.should.equal('test'); - - client.set('a', 'b', function (err) { - should.strictEqual(err, null); - - client.get('a', function (err, val) { - should.strictEqual(err, null); - val.should.eql('b'); - - client.has('a', function (err, has) { - should.strictEqual(err, null); - has.should.be.true; - - client.has('b', function (err, has) { - should.strictEqual(err, null); - has.should.be.false; - - client.del('a', function (err) { - should.strictEqual(err, null); - - client.has('a', function (err, has) { - should.strictEqual(err, null); - has.should.be.false; - - client.set('b', 'c', function (err) { - should.strictEqual(err, null); - - client.set('c', 'd', function (err) { - should.strictEqual(err, null); - - client.get('b', function (err, val) { - should.strictEqual(err, null); - val.should.equal('c'); - - client.get('c', function (err, val) { - should.strictEqual(err, null); - val.should.equal('d'); - - store.destroy(); - done(); - }); - }); - }); - }); - }); - }); - }); - }); - }); - }); - }, - - 'test cleaning up clients data': function (done) { - var rand1 = Math.abs(Math.random() * Date.now() | 0) - , rand2 = Math.abs(Math.random() * Date.now() | 0); - - var store = new MemoryStore() - , client1 = store.client(rand1) - , client2 = store.client(rand2); - - client1.set('a', 'b', function (err) { - should.strictEqual(err, null); - - client2.set('c', 'd', function (err) { - should.strictEqual(err, null); - - client1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - client2.has('c', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - store.destroy(); - - var newstore = new MemoryStore() - , newclient1 = newstore.client(rand1) - , newclient2 = newstore.client(rand2); - - newclient1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.be.false; - - newclient2.has('c', function (err, val) { - should.strictEqual(err, null); - val.should.be.false; - - newstore.destroy(); - done(); - }); - }); - }); - }); - }); - }); - }, - - 'test cleaning up a particular client': function (done) { - var rand1 = Math.abs(Math.random() * Date.now() | 0) - , rand2 = Math.abs(Math.random() * Date.now() | 0); - - var store = new MemoryStore() - , client1 = store.client(rand1) - , client2 = store.client(rand2); - - client1.set('a', 'b', function (err) { - should.strictEqual(err, null); - - client2.set('c', 'd', function (err) { - should.strictEqual(err, null); - - client1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - client2.has('c', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - store.clients.should.have.property(rand1); - store.clients.should.have.property(rand2); - store.destroyClient(rand1); - - store.clients.should.not.have.property(rand1); - store.clients.should.have.property(rand2); - - client1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.equal(false); - - store.destroy(); - done(); - }); - }); - }); - }); - }); - }, - - 'test destroy expiration': function (done) { - var store = new MemoryStore() - , id = Math.abs(Math.random() * Date.now() | 0) - , client = store.client(id); - - client.set('a', 'b', function (err) { - should.strictEqual(err, null); - store.destroyClient(id, 1); - - setTimeout(function () { - client.get('a', function (err, val) { - should.strictEqual(err, null); - val.should.equal('b'); - }); - }, 500); - - setTimeout(function () { - client.get('a', function (err, val) { - should.strictEqual(err, null); - should.strictEqual(val, null); - - store.destroy(); - done(); - }); - }, 1900); - }); - } - -}; diff --git a/node_modules/socket.io/test/stores.redis.test.js b/node_modules/socket.io/test/stores.redis.test.js deleted file mode 100644 index 69848f3..0000000 --- a/node_modules/socket.io/test/stores.redis.test.js +++ /dev/null @@ -1,240 +0,0 @@ - -/** - * Test dependencies - * - * @api private - */ - -var sio = require('socket.io') - , redis = require('redis') - , should = require('should') - , RedisStore = sio.RedisStore; - -/** - * Test. - */ - -module.exports = { - - 'test publishing doesnt get caught by the own store subscriber': function (done) { - var a = new RedisStore - , b = new RedisStore; - - a.subscribe('woot', function (arg) { - arg.should.equal('bb'); - a.destroy(); - b.destroy(); - done(); - }, function () { - a.publish('woot', 'aa'); - b.publish('woot', 'bb'); - }); - }, - - 'test publishing to multiple subscribers': function (done) { - var a = new RedisStore - , b = new RedisStore - , c = new RedisStore - , subscriptions = 3 - , messages = 2; - - a.subscribe('tobi', function () { - throw new Error('Shouldnt publish to itself'); - }, publish); - - function subscription (arg1, arg2, arg3) { - arg1.should.equal(1); - arg2.should.equal(2); - arg3.should.equal(3); - --messages || finish(); - } - - b.subscribe('tobi', subscription, publish); - c.subscribe('tobi', subscription, publish); - - function publish () { - --subscriptions || a.publish('tobi', 1, 2, 3); - } - - function finish () { - a.destroy(); - b.destroy(); - c.destroy(); - done(); - } - }, - - 'test storing data for a client': function (done) { - var store = new RedisStore - , rand = 'test-' + Date.now() - , client = store.client(rand); - - client.id.should.equal(rand); - - client.set('a', 'b', function (err) { - should.strictEqual(err, null); - - client.get('a', function (err, val) { - should.strictEqual(err, null); - val.should.equal('b'); - - client.has('a', function (err, has) { - should.strictEqual(err, null); - has.should.be.true; - - client.has('b', function (err, has) { - should.strictEqual(err, null); - has.should.be.false; - - client.del('a', function (err) { - should.strictEqual(err, null); - - client.has('a', function (err, has) { - should.strictEqual(err, null); - has.should.be.false; - - client.set('b', 'c', function (err) { - should.strictEqual(err, null); - - client.set('c', 'd', function (err) { - should.strictEqual(err, null); - - client.get('b', function (err, val) { - should.strictEqual(err, null); - val.should.equal('c'); - - client.get('c', function (err, val) { - should.strictEqual(err, null); - val.should.equal('d'); - - store.destroy(); - done(); - }); - }); - }); - }); - }); - }); - }); - }); - }); - }); - }, - - 'test cleaning up clients data': function (done) { - var rand1 = Math.abs(Math.random() * Date.now() | 0) - , rand2 = Math.abs(Math.random() * Date.now() | 0); - - var store = new RedisStore() - , client1 = store.client(rand1) - , client2 = store.client(rand2); - - client1.set('a', 'b', function (err) { - should.strictEqual(err, null); - - client2.set('c', 'd', function (err) { - should.strictEqual(err, null); - - client1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - client2.has('c', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - store.destroy(); - - var newstore = new RedisStore() - , newclient1 = newstore.client(rand1) - , newclient2 = newstore.client(rand2); - - newclient1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.be.false; - - newclient2.has('c', function (err, val) { - should.strictEqual(err, null); - val.should.be.false; - - newstore.destroy(); - done(); - }); - }); - }); - }); - }); - }); - }, - - 'test cleaning up a particular client': function (done) { - var rand1 = Math.abs(Math.random() * Date.now() | 0) - , rand2 = Math.abs(Math.random() * Date.now() | 0); - - var store = new RedisStore() - , client1 = store.client(rand1) - , client2 = store.client(rand2); - - client1.set('a', 'b', function (err) { - should.strictEqual(err, null); - - client2.set('c', 'd', function (err) { - should.strictEqual(err, null); - - client1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - client2.has('c', function (err, val) { - should.strictEqual(err, null); - val.should.be.true; - - store.clients.should.have.property(rand1); - store.clients.should.have.property(rand2); - store.destroyClient(rand1); - - store.clients.should.not.have.property(rand1); - store.clients.should.have.property(rand2); - - client1.has('a', function (err, val) { - should.strictEqual(err, null); - val.should.equal(false); - - store.destroy(); - done(); - }); - }); - }); - }); - }); - }, - - 'test destroy expiration': function (done) { - var store = new RedisStore() - , id = Math.abs(Math.random() * Date.now() | 0) - , client = store.client(id); - - client.set('a', 'b', function (err) { - should.strictEqual(err, null); - store.destroyClient(id, 1); - - setTimeout(function () { - client.get('a', function (err, val) { - should.strictEqual(err, null); - val.should.equal('b'); - }); - }, 500); - - setTimeout(function () { - client.get('a', function (err, val) { - should.strictEqual(err, null); - should.strictEqual(val, null); - - store.destroy(); - done(); - }); - }, 2000); - }); - } - -}; diff --git a/node_modules/socket.io/test/transports.flashsocket.test.js b/node_modules/socket.io/test/transports.flashsocket.test.js deleted file mode 100644 index e857be3..0000000 --- a/node_modules/socket.io/test/transports.flashsocket.test.js +++ /dev/null @@ -1,187 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , net = require('net') - , http = require('http') - , should = require('./common') - , WebSocket = require('../support/node-websocket-client/lib/websocket').WebSocket - , WSClient = require('./transports.websocket.test') - , parser = sio.parser - , ports = 15600; - -/** - * FlashSocket client constructor. - * - * @api private - */ - -function FlashSocket (port, sid) { - this.sid = sid; - this.port = port; - - WebSocket.call( - this - , 'ws://localhost:' + port + '/socket.io/' - + sio.protocol + '/flashsocket/' + sid - ); -}; - -/** - * Inherits from WSClient. - */ - -FlashSocket.prototype.__proto__ = WebSocket.prototype; - -/** - * Creates a TCP connection to a port. - * - * @api public - */ - -function netConnection (port, callback){ - var nclient = net.createConnection(port); - - nclient.on('data', function (data) { - callback.call(nclient, null, data); - }); - - nclient.on('error', function (e){ - callback.call(nclient, e); - }); - - nclient.write('\0'); -} - -/** - * Tests. - */ - -module.exports = { - - 'flashsocket disabled by default': function (done) { - var io = sio.listen(http.createServer()); - io.get('transports').should.not.contain('flashsocket'); - done(); - }, - - 'flash policy port': function (done) { - var io = sio.listen(http.createServer()) - , port = ++ports; - - io.get('flash policy port').should.eql(10843); - io.set('flash policy port', port); - io.get('flash policy port').should.eql(port); - - should.strictEqual(io.flashPolicyServer, undefined); - - netConnection(port, function (err, data){ - err.should.be.an.instanceof(Error); - err.code.should.eql('ECONNREFUSED'); - - this.destroy(); - done(); - }) - }, - - 'start flash policy': function (done) { - var io = sio.listen(http.createServer()) - , port = ++ports; - - io.set('flash policy port', port); - io.set('transports', ['flashsocket']); - - io.flashPolicyServer.should.be.a('object'); - - netConnection(port, function (err, data){ - should.strictEqual(err, null); - - data.toString().should.include.string(''); - - this.destroy(); - io.flashPolicyServer.close(); - done(); - }) - - }, - - 'change running flash server port': function (done) { - var io = sio.listen(http.createServer()) - , port = ++ports - , next = ++ports; - - io.set('flash policy port', port); - io.set('transports', ['flashsocket']); - io.set('flash policy port', next); - io.flashPolicyServer.port.should.eql(next); - - netConnection(port, function (err, data){ - err.should.be.an.instanceof(Error); - err.code.should.eql('ECONNREFUSED'); - - this.destroy(); - - // should work - netConnection(next, function (err, data){ - should.strictEqual(err, null); - - data.toString().should.include.string(''); - - this.destroy(); - io.flashPolicyServer.close(); - done(); - }); - }); - }, - - 'different origins': function(done) { - var io = sio.listen(http.createServer()) - , port = ++ports; - - io.set('flash policy port', port); - io.set('transports', ['flashsocket']); - io.set('origins', 'google.com:80'); - - var server = io.flashPolicyServer; - - server.origins.should.contain('google.com:80'); - server.origins.should.not.contain('*.*'); - - io.set('origins', ['foo.bar:80', 'socket.io:1337']); - server.origins.should.not.contain('google.com:80'); - server.origins.should.contain('foo.bar:80'); - server.origins.should.contain('socket.io:1337'); - server.buffer.toString('utf8').should.include.string('socket.io'); - - io.flashPolicyServer.close(); - done(); - }, - - 'flashsocket identifies as flashsocket': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.set('transports', ['flashsocket']); - io.sockets.on('connection', function (socket) { - socket.manager.transports[socket.id].name.should.equal('flashsocket'); - ws.finishClose(); - cl.end(); - io.flashPolicyServer.close(); - io.server.close(); - done(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid, 'flashsocket'); - }); - } -}; diff --git a/node_modules/socket.io/test/transports.htmlfile.test.js b/node_modules/socket.io/test/transports.htmlfile.test.js deleted file mode 100644 index a9429ab..0000000 --- a/node_modules/socket.io/test/transports.htmlfile.test.js +++ /dev/null @@ -1,458 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , should = require('./common') - , HTTPClient = should.HTTPClient - , parser = sio.parser - , ports = 15300; - -/** - * HTTPClient for htmlfile transport. - */ - -function HTMLFile (port) { - HTTPClient.call(this, port); -}; - -/** - * Inhertis from HTTPClient. - */ - -HTMLFile.prototype.__proto__ = HTTPClient.prototype; - -/** - * Override GET request with streaming parser. - * - * @api public - */ - -var head = '' - , initial = '' - + '' - + new Array(174).join(' ') - -HTMLFile.prototype.data = function (path, opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - opts.buffer = false; - - return this.request(path, opts, function (res) { - var buf = '' - , messages = 0 - , state = 0; - - res.on('data', function (chunk) { - buf += chunk; - - function parse () { - switch (state) { - case 0: - if (buf.indexOf(initial) === 0) { - buf = buf.substr(initial.length); - state = 1; - } else { - break; - } - - case 1: - if (buf.indexOf(head) === 0) { - buf = buf.substr(head.length); - state = 2; - } else { - break; - } - - case 2: - if (buf.indexOf(foot) != -1) { - var data = buf.slice(0, buf.indexOf(foot)) - , obj = JSON.parse(data); - - fn(obj === '' ? obj : parser.decodePayload(obj), ++messages); - - buf = buf.substr(data.length + foot.length); - state = 1; - - parse(); - } - }; - }; - - parse(); - }); - }); -}; - -/** - * Create client for this transport. - * - * @api public - */ - -function client (port) { - return new HTMLFile(port); -}; - -/** - * Tests. - */ - -module.exports = { - - 'test that not responding to a heartbeat drops client': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , beat = false; - - io.configure(function () { - io.set('heartbeat interval', .05); - io.set('heartbeat timeout', .05); - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function (reason) { - beat.should.be.true; - reason.should.eql('heartbeat timeout'); - - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) { - switch (i) { - case 1: - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - msgs[0].endpoint.should.eql(''); - break; - - case 2: - msgs.should.have.length(1); - msgs[0].type.should.eql('heartbeat'); - beat = true; - }; - }); - }); - }, - - 'test that responding to a heartbeat maintains session': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , heartbeats = 0; - - io.configure(function () { - io.set('heartbeat interval', .05); - io.set('heartbeat timeout', .05); - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function (reason) { - heartbeats.should.eql(2); - reason.should.eql('heartbeat timeout'); - - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) { - switch (i) { - case 1: - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - msgs[0].endpoint.should.eql(''); - break; - - default: - msgs.should.have.length(1); - msgs[0].type.should.eql('heartbeat'); - - heartbeats++; - - if (heartbeats == 1) { - cl.post('/socket.io/{protocol}/htmlfile/' + sid, parser.encodePacket({ - type: 'heartbeat' - })); - } - } - }); - }); - }, - - 'test sending undeliverable volatile messages': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , messaged = false - , s; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - messaged.should.be.false; - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function () { }); - - setTimeout(function () { - cl.end(); - - setTimeout(function () { - s.volatile.send('wooooot'); - cl = client(port); - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs) { - if (msgs && msgs.length) - messaged = true; - }); - - setTimeout(function () { - cl.end(); - }, 20); - }, 20); - }, 20); - }); - }, - - 'test sending undeliverable volatile json': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , messaged = false - , s; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - messaged.should.be.false; - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function () { }); - - setTimeout(function () { - cl.end(); - - setTimeout(function () { - s.volatile.json.send(123); - - cl = client(port); - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs) { - if (msgs && msgs.length) - messaged = true; - }); - - setTimeout(function () { - cl.end(); - }, 20); - }, 20); - }, 20); - }); - }, - - 'test sending undeliverable volatile events': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , messaged = false - , s; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - messaged.should.be.false; - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function () { }); - - setTimeout(function () { - cl.end(); - - setTimeout(function () { - s.volatile.emit('tobi'); - - cl = client(port); - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs) { - if (msgs && msgs.length) - messaged = true; - }); - - setTimeout(function () { - cl.end(); - }, 20); - }, 20); - }, 20); - }); - }, - - 'test sending deliverable volatile messages': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.volatile.send('woot'); - - socket.on('disconnect', function () { - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) { - switch (i) { - case 1: - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - msgs[0].endpoint.should.eql(''); - break; - - case 2: - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'message' - , data: 'woot' - , endpoint: '' - }); - cl.end(); - } - }); - }); - }, - - 'test sending deliverable volatile json': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.volatile.json.send(['woot']); - - socket.on('disconnect', function () { - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) { - switch (i) { - case 1: - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - msgs[0].endpoint.should.eql(''); - break; - - case 2: - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'json' - , data: ['woot'] - , endpoint: '' - }); - cl.end(); - } - }); - }); - }, - - 'test sending deliverable volatile events': function (done) { - var port = ++ports - , cl = client(port) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.volatile.emit('aaa'); - - socket.on('disconnect', function () { - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) { - switch (i) { - case 1: - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - msgs[0].endpoint.should.eql(''); - break; - - case 2: - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'event' - , name: 'aaa' - , endpoint: '' - , args: [] - }); - cl.end(); - } - }); - }); - } - -}; diff --git a/node_modules/socket.io/test/transports.jsonp-polling.test.js b/node_modules/socket.io/test/transports.jsonp-polling.test.js deleted file mode 100644 index 617e87e..0000000 --- a/node_modules/socket.io/test/transports.jsonp-polling.test.js +++ /dev/null @@ -1,774 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , should = require('./common') - , qs = require('querystring') - , HTTPClient = should.HTTPClient - , parser = sio.parser - , ports = 15500; - -/** - * HTTPClient for jsonp-polling transport. - */ - -function JSONPPolling (port) { - HTTPClient.call(this, port); -}; - -/** - * Inhertis from HTTPClient. - */ - -JSONPPolling.prototype.__proto__ = HTTPClient.prototype; - -/** - * Performs a json-p (cross domain) handshake - * - * @api public - */ - -JSONPPolling.prototype.handshake = function (opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - var self = this; - - return this.get( - '/socket.io/{protocol}?jsonp=0' - , opts - , function (res, data) { - var head = 'io.j[0](' - , foot = ');'; - - data.substr(0, head.length).should.eql(head); - data.substr(-foot.length).should.eql(foot); - data = data.slice(head.length, data.length - foot.length); - - var parts = JSON.parse(data).split(':'); - - if (opts.ignoreConnect) { - return fn && fn.apply(null, parts); - } - - // expect connect packet right after handshake - self.get( - '/socket.io/{protocol}/jsonp-polling/' + parts[0] - , function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'connect', endpoint: '', qs: '' }); - - fn && fn.apply(null, parts); - } - ); - } - ); -}; - -/** - * Override GET requests. - * - * @api public - */ - -JSONPPolling.prototype.get = function (path, opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - opts = opts || {}; - - opts.parse = function (data) { - var head = 'io.j[0](' - , foot = ');'; - - if (~path.indexOf('?i=1')) { - head = 'io.j[1]('; - } - - data.substr(0, head.length).should.eql(head); - data.substr(-foot.length).should.eql(foot); - - data = data.substr(head.length, data.length - head.length - foot.length); - - return JSON.parse(data); - }; - - return HTTPClient.prototype.get.call(this, path, opts, fn); -}; - -/** - * Issue an encoded POST request - * - * @api private - */ - -JSONPPolling.prototype.post = function (path, data, opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - opts = opts || {}; - opts.method = 'POST'; - opts.data = qs.stringify({ d: data }); - - return this.request(path, opts, fn); -}; - -/** - * Create client for this transport. - * - * @api public - */ - -function client (port) { - return new JSONPPolling(port); -}; - -/** - * Test. - */ - -module.exports = { - - 'test jsonp handshake': function (done) { - var cl = client(++ports) - , io = create(cl); - - io.configure(function () { - io.set('close timeout', .05); - io.set('polling duration', 0); - }); - - function finish () { - cl.end(); - io.server.close(); - done(); - }; - - cl.handshake(function (sid) { - var total = 2; - - cl.get('/socket.io/{protocol}/jsonp-polling/tobi', function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'error' - , reason: 'client not handshaken' - , endpoint: '' - , advice: 'reconnect' - }); - - --total || finish(); - }); - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - --total || finish(); - }); - }); - }, - - 'test the connection event': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.id.should.eql(sid); - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - }); - }); - }, - - 'test the disconnection event after a close timeout': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.id.should.eql(sid); - - socket.on('disconnect', function () { - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - - setTimeout(function () { - cl.end(); - }, 10); - }); - }); - }, - - 'test the disconnection event when the client sends ?disconnect req': function (done) { - var cl = client(++ports) - , io = create(cl) - , disconnected = false - , sid; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - disconnected = true; - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'disconnect', endpoint: '' }); - disconnected.should.be.true; - io.server.close(); - cl.end(); - done(); - }); - - // with the new http bits in node 0.5, there's no guarantee that - // the previous request is actually dispatched (and received) before the following - // reset call is sent. to not waste more time on a workaround, a timeout is added. - setTimeout(function() { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid + '/?disconnect'); - }, 500); - }); - }); - }, - - 'test the disconnection event booting a client': function (done) { - var cl = client(++ports) - , io = create(cl) - , forced = false; - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - io.server.close(); - done(); - }); - - cl.end(); - socket.disconnect(); - forced = true; - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'disconnect', endpoint: '' }); - - forced.should.be.true; - }); - }); - }, - - 'test the disconnection event with client disconnect packet': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.sockets.on('connection', function (client) { - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'disconnect' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - client.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - }); - }); - }, - - 'test sending back data': function (done) { - var cl = client(++ports) - , io = create(cl); - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.send('woot'); - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, packs) { - packs.should.have.length(1); - packs[0].type.should.eql('message'); - packs[0].data.should.eql('woot'); - }); - }); - }, - - 'test sending a batch of messages': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - var messages = 0; - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePayload([ - parser.encodePacket({ type: 'message', data: 'a' }) - , parser.encodePacket({ type: 'message', data: 'b' }) - , parser.encodePacket({ type: 'disconnect' }) - ])) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - socket.on('message', function (data) { - messages++; - - if (messages == 1) - data.should.eql('a'); - - if (messages == 2) - data.should.eql('b'); - }); - - socket.on('disconnect', function () { - messages.should.eql(2); - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - }); - }); - }, - - 'test message buffering between a response and a request': function (done) { - var cl = client(++ports) - , io = create(cl) - , messages = false - , tobi; - - io.configure(function () { - io.set('polling duration', .1); - io.set('close timeout', .2); - }); - - io.sockets.on('connection', function (socket) { - tobi = function () { - socket.send('a'); - socket.send('b'); - socket.send('c'); - }; - - socket.on('disconnect', function () { - messages.should.be.true; - - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - tobi(); - - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - msgs.should.have.length(3); - msgs[0].should.eql({ type: 'message', endpoint: '', data: 'a' }); - msgs[1].should.eql({ type: 'message', endpoint: '', data: 'b' }); - msgs[2].should.eql({ type: 'message', endpoint: '', data: 'c' }); - messages = true; - }); - }) - }); - }, - - 'test connecting to a specific endpoint': function (done) { - var cl = client(++ports) - , io = create(cl) - , connectMessage = false - , sid; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/woot').on('connection', function (socket) { - connectMessage.should.be.true; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, data) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid); - - connectMessage = true; - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'connect', endpoint: '/woot' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - }); - }); - }, - - 'test that connecting doesnt connect to defined endpoints': function (done) { - var cl = client(++ports) - , io = create(cl) - , tobiConnected = false - , mainConnected = false - , sid; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - mainConnected = true; - - socket.on('disconnect', function () { - tobiConnected.should.be.false; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/tobi').on('connection', function () { - tobiConnected = true; - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid); - }); - }, - - 'test disconnecting a specific endpoint': function (done) { - var cl = client(++ports) - , io = create(cl) - , wootDisconnected = false - , mainDisconnected = false - , checked = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (data) { - data.should.eql('ferret'); - mainDisconnected.should.be.false; - wootDisconnected.should.be.true; - checked = true; - }); - - socket.on('disconnect', function () { - mainDisconnected = true; - checked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/woot').on('connection', function (socket) { - socket.on('disconnect', function () { - wootDisconnected = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function () { - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'connect', endpoint: '/woot' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'disconnect', endpoint: '/woot' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'message', data: 'ferret' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - } - ); - }); - }); - }, - - 'test that disconnecting disconnects all endpoints': function (done) { - var cl = client(++ports) - , io = create(cl) - , aDisconnected = false - , bDisconnected = false; - - io.configure(function () { - io.set('polling duration', .2); - io.set('close timeout', .2); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - setTimeout(function () { - aDisconnected.should.be.true; - bDisconnected.should.be.true; - cl.end(); - io.server.close(); - done(); - }, 50); - }); - }); - - io.of('/a').on('connection', function (socket) { - socket.on('disconnect', function (msg) { - aDisconnected = true; - }); - }); - - io.of('/b').on('connection', function (socket) { - socket.on('disconnect', function (msg) { - bDisconnected = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'connect', endpoint: '/a' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'connect', endpoint: '/b' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - }); - }); - }, - - 'test messaging a specific endpoint': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = true - , aMessaged = false - , bMessaged = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql(''); - messaged = true; - }); - - socket.on('disconnect', function () { - messaged.should.be.true; - aMessaged.should.be.true; - bMessaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/a').on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql('a'); - aMessaged = true; - }); - }); - - io.of('/b').on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql('b'); - bMessaged = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/jsonp-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'message', data: '' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'connect', endpoint: '/a' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'message', endpoint: '/a', data: 'a' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'connect', endpoint: '/b' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/jsonp-polling/' + sid - , JSON.stringify(parser.encodePacket({ type: 'message', endpoint: '/b', data: 'b' })) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - }); - }); - } - -}; diff --git a/node_modules/socket.io/test/transports.websocket.hybi07-12.parser.test.js b/node_modules/socket.io/test/transports.websocket.hybi07-12.parser.test.js deleted file mode 100644 index cc00b13..0000000 --- a/node_modules/socket.io/test/transports.websocket.hybi07-12.parser.test.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Test dependencies. - */ - -var assert = require('assert'); -var Parser = require('../lib/transports/websocket/hybi-07-12.js').Parser; -require('./hybi-common'); - -/** - * Tests. - */ - -module.exports = { - 'can parse unmasked text message': function() { - var p = new Parser(); - var packet = '81 05 48 65 6c 6c 6f'; - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal('Hello', data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse close message': function() { - var p = new Parser(); - var packet = '88 00'; - - var gotClose = false; - p.on('close', function(data) { - gotClose = true; - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotClose); - }, - 'can parse masked text message': function() { - var p = new Parser(); - var packet = '81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5'; - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal('5:::{"name":"echo"}', data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a masked text message longer than 125 bytes': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a really long masked text message': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString(); - var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a fragmented masked text message of 300 bytes': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - var msgpiece1 = message.substr(0, 150); - var msgpiece2 = message.substr(150); - var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); - var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet1)); - p.add(getBufferFromHexString(packet2)); - assert.ok(gotData); - }, - 'can parse a ping message': function() { - var p = new Parser(); - var message = 'Hello'; - var packet = '89 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotPing); - }, - 'can parse a ping with no data': function() { - var p = new Parser(); - var packet = '89 00'; - - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotPing); - }, - 'can parse a fragmented masked text message of 300 bytes with a ping in the middle': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - - var msgpiece1 = message.substr(0, 150); - var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); - - var pingMessage = 'Hello'; - var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); - - var msgpiece2 = message.substr(150); - var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - assert.equal(pingMessage, data); - }); - - p.add(getBufferFromHexString(packet1)); - p.add(getBufferFromHexString(pingPacket)); - p.add(getBufferFromHexString(packet2)); - assert.ok(gotData); - assert.ok(gotPing); - }, - 'can parse a fragmented masked text message of 300 bytes with a ping in the middle, which is delievered over sevaral tcp packets': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - - var msgpiece1 = message.substr(0, 150); - var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); - - var pingMessage = 'Hello'; - var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); - - var msgpiece2 = message.substr(150); - var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - assert.equal(pingMessage, data); - }); - - var buffers = []; - buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1))); - buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket))); - buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2))); - for (var i = 0; i < buffers.length; ++i) { - p.add(buffers[i]); - } - assert.ok(gotData); - assert.ok(gotPing); - }, - 'can parse a 100 byte long masked binary message': function() { - var p = new Parser(); - var length = 100; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a 256 byte long masked binary message': function() { - var p = new Parser(); - var length = 256; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a 200kb long masked binary message': function() { - var p = new Parser(); - var length = 200 * 1024; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a 200kb long unmasked binary message': function() { - var p = new Parser(); - var length = 200 * 1024; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, -}; - diff --git a/node_modules/socket.io/test/transports.websocket.hybi16.parser.test.js b/node_modules/socket.io/test/transports.websocket.hybi16.parser.test.js deleted file mode 100644 index e04d06a..0000000 --- a/node_modules/socket.io/test/transports.websocket.hybi16.parser.test.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Test dependencies. - */ - -var assert = require('assert'); -var Parser = require('../lib/transports/websocket/hybi-16.js').Parser; -require('./hybi-common'); - -/** - * Tests. - */ - -module.exports = { - 'can parse unmasked text message': function() { - var p = new Parser(); - var packet = '81 05 48 65 6c 6c 6f'; - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal('Hello', data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse close message': function() { - var p = new Parser(); - var packet = '88 00'; - - var gotClose = false; - p.on('close', function(data) { - gotClose = true; - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotClose); - }, - 'can parse masked text message': function() { - var p = new Parser(); - var packet = '81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5'; - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal('5:::{"name":"echo"}', data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a masked text message longer than 125 bytes': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a really long masked text message': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString(); - var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a fragmented masked text message of 300 bytes': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - var msgpiece1 = message.substr(0, 150); - var msgpiece2 = message.substr(150); - var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); - var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet1)); - p.add(getBufferFromHexString(packet2)); - assert.ok(gotData); - }, - 'can parse a ping message': function() { - var p = new Parser(); - var message = 'Hello'; - var packet = '89 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - assert.equal(message, data); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotPing); - }, - 'can parse a ping with no data': function() { - var p = new Parser(); - var packet = '89 00'; - - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotPing); - }, - 'can parse a fragmented masked text message of 300 bytes with a ping in the middle': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - - var msgpiece1 = message.substr(0, 150); - var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); - - var pingMessage = 'Hello'; - var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); - - var msgpiece2 = message.substr(150); - var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - assert.equal(pingMessage, data); - }); - - p.add(getBufferFromHexString(packet1)); - p.add(getBufferFromHexString(pingPacket)); - p.add(getBufferFromHexString(packet2)); - assert.ok(gotData); - assert.ok(gotPing); - }, - 'can parse a fragmented masked text message of 300 bytes with a ping in the middle, which is delievered over sevaral tcp packets': function() { - var p = new Parser(); - var message = 'A'; - for (var i = 0; i < 300; ++i) message += (i % 5).toString(); - - var msgpiece1 = message.substr(0, 150); - var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); - - var pingMessage = 'Hello'; - var pingPacket = '89 FE ' + pack(4, pingMessage.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); - - var msgpiece2 = message.substr(150); - var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); - - var gotData = false; - p.on('data', function(data) { - gotData = true; - assert.equal(message, data); - }); - var gotPing = false; - p.on('ping', function(data) { - gotPing = true; - assert.equal(pingMessage, data); - }); - - var buffers = []; - buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1))); - buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket))); - buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2))); - for (var i = 0; i < buffers.length; ++i) { - p.add(buffers[i]); - } - assert.ok(gotData); - assert.ok(gotPing); - }, - 'can parse a 100 byte long masked binary message': function() { - var p = new Parser(); - var length = 100; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a 256 byte long masked binary message': function() { - var p = new Parser(); - var length = 256; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a 200kb long masked binary message': function() { - var p = new Parser(); - var length = 200 * 1024; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, - 'can parse a 200kb long unmasked binary message': function() { - var p = new Parser(); - var length = 200 * 1024; - var message = new Buffer(length); - for (var i = 0; i < length; ++i) message[i] = i % 256; - var originalMessage = getHexStringFromBuffer(message); - var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message); - - var gotData = false; - p.on('binary', function(data) { - gotData = true; - assert.equal(originalMessage, getHexStringFromBuffer(data)); - }); - - p.add(getBufferFromHexString(packet)); - assert.ok(gotData); - }, -}; - diff --git a/node_modules/socket.io/test/transports.websocket.test.js b/node_modules/socket.io/test/transports.websocket.test.js deleted file mode 100644 index ff1fe71..0000000 --- a/node_modules/socket.io/test/transports.websocket.test.js +++ /dev/null @@ -1,1811 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , should = require('./common') - , parser = sio.parser - , ports = 15800; - -/** - * Tests. - */ - -module.exports = { - 'websocket identifies as websocket': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.set('transports', ['websocket']); - io.sockets.on('connection', function (socket) { - socket.manager.transports[socket.id].name.should.equal('websocket'); - ws.finishClose(); - cl.end(); - io.server.close(); - done(); - }); - cl.handshake(function (sid) { - ws = websocket(cl, sid); - }); - }, - - 'default websocket draft parser is used for unknown sec-websocket-version': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.set('transports', ['websocket']); - io.sockets.on('connection', function (socket) { - socket.manager.transports[socket.id].protocolVersion.should.equal('hixie-76'); - ws.finishClose(); - cl.end(); - io.server.close(); - done(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - }); - }, - - 'hybi-07-12 websocket draft parser is used for sec-websocket-version: 8': function (done) { - var cl = client(++ports) - , io = create(cl); - - io.set('transports', ['websocket']); - io.sockets.on('connection', function (socket) { - socket.manager.transports[socket.id].protocolVersion.should.equal('07-12'); - cl.end(); - io.server.close(); - done(); - }); - - var headers = { - 'sec-websocket-version': 8, - 'upgrade': 'websocket', - 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==' - } - - cl.get('/socket.io/{protocol}', {}, function (res, data) { - var sid = data.split(':')[0]; - var url = '/socket.io/' + sio.protocol + '/websocket/' + sid; - cl.get(url, {headers: headers}, function (res, data) {}); - }); - }, - - 'hybi-16 websocket draft parser is used for sec-websocket-version: 13': function (done) { - var cl = client(++ports) - , io = create(cl) - - io.set('transports', ['websocket']); - - io.sockets.on('connection', function (socket) { - socket.manager.transports[socket.id].protocolVersion.should.equal('16'); - cl.end(); - io.server.close(); - done(); - }); - - var headers = { - 'sec-websocket-version': 13, - 'upgrade': 'websocket', - 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==' - } - - cl.get('/socket.io/{protocol}', {}, function (res, data) { - var sid = data.split(':')[0]; - var url = '/socket.io/' + sio.protocol + '/websocket/' + sid; - cl.get(url, {headers: headers}, function (res, data) {}); - }); - }, - - 'hybi-07-12 origin filter blocks access for mismatched sec-websocket-origin': function (done) { - var cl = client(++ports) - , io = create(cl) - - io.set('transports', ['websocket']); - io.set('origins', 'foo.bar.com:*'); - - var headers = { - 'sec-websocket-version': 8, - 'upgrade': 'websocket', - 'Sec-WebSocket-Origin': 'http://baz.bar.com', - 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==' - } - - // handshake uses correct origin -- we want to block the actuall websocket call - cl.get('/socket.io/{protocol}', {headers: {origin: 'http://foo.bar.com'}}, function (res, data) { - var sid = data.split(':')[0]; - var url = '/socket.io/' + sio.protocol + '/websocket/' + sid; - cl.get(url, {headers: headers}, function (res, data) {}); - cl.end(); - io.server.close(); - done(); - }); - }, - - 'hybi-16 origin filter blocks access for mismatched sec-websocket-origin': function (done) { - var cl = client(++ports) - , io = create(cl) - - io.set('transports', ['websocket']); - io.set('origins', 'foo.bar.com:*'); - - var headers = { - 'sec-websocket-version': 13, - 'upgrade': 'websocket', - 'origin': 'http://baz.bar.com', - 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==' - } - - // handshake uses correct origin -- we want to block the actuall websocket call - cl.get('/socket.io/{protocol}', {headers: {origin: 'http://foo.bar.com'}}, function (res, data) { - var sid = data.split(':')[0]; - var url = '/socket.io/' + sio.protocol + '/websocket/' + sid; - cl.get(url, {headers: headers}, function (res, data) {}); - cl.end(); - io.server.close(); - done(); - }); - }, - - 'test that not responding to a heartbeat drops client': function (done) { - var cl = client(++ports) - , io = create(cl) - , messages = 0 - , ws; - - io.configure(function () { - io.set('heartbeat interval', .05); - io.set('heartbeat timeout', .05); - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function (reason) { - beat.should.be.true; - reason.should.eql('heartbeat timeout'); - - cl.end(); - ws.finishClose(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('message', function (packet) { - if (++messages == 1) { - packet.type.should.eql('connect'); - } else { - packet.type.should.eql('heartbeat'); - beat = true; - } - }); - }); - }, - - 'test that responding to a heartbeat maintains session': function (done) { - var cl = client(++ports) - , io = create(cl) - , messages = 0 - , heartbeats = 0 - , ws; - - io.configure(function () { - io.set('heartbeat interval', .05); - io.set('heartbeat timeout', .05); - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function (reason) { - heartbeats.should.eql(2); - reason.should.eql('heartbeat timeout'); - - cl.end(); - ws.finishClose(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('message', function (packet) { - if (++messages == 1) { - packet.type.should.eql('connect'); - } else { - packet.type.should.eql('heartbeat'); - heartbeats++; - - if (heartbeats == 1) { - ws.packet({ type: 'heartbeat' }); - } - } - }); - }); - }, - - 'test sending deliverable volatile messages': function (done) { - var cl = client(++ports) - , io = create(cl) - , messages = 0 - , messaged = false; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.volatile.send('tobi'); - - socket.on('disconnect', function () { - messaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - var ws = websocket(cl, sid); - ws.on('message', function (msg) { - if (++messages == 1) { - msg.type.should.eql('connect'); - } else { - msg.should.eql({ - type: 'message' - , data: 'tobi' - , endpoint: '' - }); - messaged = true; - ws.finishClose(); - } - }); - }); - }, - - 'test sending deliverable volatile json': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.volatile.json.send([1, 2, 3]); - - socket.on('disconnect', function () { - messaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - var ws = websocket(cl, sid); - ws.on('message', function (msg) { - if (!ws.connected) { - msg.type.should.eql('connect'); - ws.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: [1, 2, 3] - , endpoint: '' - }); - messaged = true; - ws.finishClose(); - } - }); - }); - }, - - 'test sending deliverable volatile events': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.volatile.emit('tobi'); - - socket.on('disconnect', function () { - messaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - var ws = websocket(cl, sid); - ws.on('message', function (msg) { - if (!ws.connected) { - msg.type.should.eql('connect'); - ws.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'tobi' - , endpoint: '' - , args: [] - }); - messaged = true; - ws.finishClose(); - } - }); - }); - }, - - 'test sending to all clients in a namespace': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , io = create(cl1) - , messages = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections == 2) { - io.sockets.send('yup'); - } - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 2) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'yup' - , endpoint: '' - }); - - messages++; - ws1.finishClose(); - } - }); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'yup' - , endpoint: '' - }); - - messages++; - ws2.finishClose(); - } - }); - }); - }, - - 'test sending json to all clients in a namespace': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , io = create(cl1) - , messages = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections == 2) { - io.sockets.json.send({ a: 'b' }); - } - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 2) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: { a: 'b' } - , endpoint: '' - }); - - messages++; - ws1.finishClose(); - } - }); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: { a: 'b' } - , endpoint: '' - }); - - messages++; - ws2.finishClose(); - } - }); - }); - }, - - 'test emitting to all clients in a namespace': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , io = create(cl1) - , messages = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections == 2) { - io.sockets.emit('tobi', 'rapture'); - } - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 2) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'tobi' - , args: ['rapture'] - , endpoint: '' - }); - - messages++; - ws1.finishClose(); - } - }); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'tobi' - , args: ['rapture'] - , endpoint: '' - }); - - messages++; - ws2.finishClose(); - } - }); - }); - }, - - 'test sending to all clients in a room': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , joins = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections != 3) { - socket.join('woot'); - joins++; - - if (joins == 2) { - setTimeout(function () { - connections.should.eql(3); - io.sockets.in('woot').send('hahaha'); - }, 20); - } - } - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'hahaha' - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws1.finishClose(); - }, 50); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'hahaha' - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws2.finishClose(); - }, 50); - }); - - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'hahaha' - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws3.finishClose(); - }, 50); - }); - }, - - 'test sending json to all clients in a room': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , joins = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections != 3) { - socket.join('woot'); - joins++; - - if (joins == 2) { - setTimeout(function () { - connections.should.eql(3); - io.sockets.in('woot').json.send(123); - }, 20); - } - } - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: 123 - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws1.finishClose(); - }, 50); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: 123 - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws2.finishClose(); - }, 50); - }); - - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: 123 - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws3.finishClose(); - }, 50); - }); - }, - - 'test emitting to all clients in a room': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , joins = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections != 3) { - socket.join('woot'); - joins++; - - if (joins == 2) { - setTimeout(function () { - connections.should.eql(3); - io.sockets.in('woot').emit('locki'); - }, 20); - } - } - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'locki' - , args: [] - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws1.finishClose(); - }, 50); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'locki' - , args: [] - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws2.finishClose(); - }, 50); - }); - - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'locki' - , args: [] - , endpoint: '' - }); - - messages++; - } - }); - - setTimeout(function () { - ws3.finishClose(); - }, 50); - }); - }, - - 'test leaving a room': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , io = create(cl1) - , joins = 0 - , disconnects = 0; - - io.set('close timeout', 0); - - io.sockets.on('connection', function (socket) { - socket.join('foo'); - io.sockets.clients('foo').should.have.length(++joins); - - socket.on('disconnect', function () { - socket.leave('foo'); - socket.leave('foo'); - socket.leave('foo'); - - io.sockets.clients('foo').should.have.length(--joins); - - if (++disconnects == 2) { - io.server.close(); - cl1.end(); - cl2.end(); - done(); - } - }) - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - ws1.finishClose(); - } - }); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - ws2.finishClose(); - } - }); - }); - }, - - 'test message with broadcast flag': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.on('trigger broadcast', function () { - socket.broadcast.send('boom'); - }); - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'boom' - , endpoint: '' - }); - - messages++; - ws1.finishClose(); - } - }); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'boom' - , endpoint: '' - }); - - messages++; - ws2.finishClose(); - } - }); - }); - - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('open', function () { - ws3.packet({ - type: 'event' - , name: 'trigger broadcast' - , endpoint: '' - }); - - setTimeout(function () { - ws3.finishClose(); - }, 50); - }); - - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - throw new Error('we shouldnt get a message here'); - } - }); - }); - }, - - 'test json with broadcast flag': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.on('trigger broadcast', function () { - socket.broadcast.json.send([1, 2, 3]); - }); - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: [1, 2, 3] - , endpoint: '' - }); - - messages++; - ws1.finishClose(); - } - }); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: [1, 2, 3] - , endpoint: '' - }); - - messages++; - ws2.finishClose(); - } - }); - }); - - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('open', function () { - ws3.packet({ - type: 'event' - , name: 'trigger broadcast' - , endpoint: '' - }); - - setTimeout(function () { - ws3.finishClose(); - }, 50); - }); - - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - throw new Error('we shouldnt get a message here'); - } - }); - }); - }, - - 'test event with broadcast flag': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - socket.on('trigger broadcast', function () { - socket.broadcast.emit('hey', 'arnold'); - }); - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(2); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'hey' - , args: ['arnold'] - , endpoint: '' - }); - - messages++; - ws1.finishClose(); - } - }); - }); - - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'hey' - , args: ['arnold'] - , endpoint: '' - }); - - messages++; - ws2.finishClose(); - } - }); - }); - - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('open', function () { - ws3.packet({ - type: 'event' - , name: 'trigger broadcast' - , endpoint: '' - }); - - setTimeout(function () { - ws3.finishClose(); - }, 50); - }); - - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - throw new Error('we shouldnt get a message here'); - } - }); - }); - }, - - 'test message with broadcast flag and to()': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections == 1) { - socket.join('losers'); - } - - socket.on('trigger broadcast', function () { - socket.broadcast.to('losers').send('boom'); - }); - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(1); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'message' - , data: 'boom' - , endpoint: '' - }); - - messages++; - } - }); - - ws1.on('open', function () { - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - throw new Error('This socket shouldnt get a message'); - } - }); - - ws2.on('open', function () { - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('open', function () { - ws3.packet({ - type: 'event' - , name: 'trigger broadcast' - , endpoint: '' - }); - - setTimeout(function () { - ws1.finishClose(); - ws2.finishClose(); - ws3.finishClose(); - }, 50); - }); - - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - throw new Error('we shouldnt get a message here'); - } - }); - }); - }); - }); - }); - }); - }, - - 'test json with broadcast flag and to()': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections == 1) { - socket.join('losers'); - } - - socket.on('trigger broadcast', function () { - socket.broadcast.json.to('losers').send({ hello: 'world' }); - }); - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(1); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'json' - , data: { hello: 'world' } - , endpoint: '' - }); - - messages++; - } - }); - - ws1.on('open', function () { - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - throw new Error('This socket shouldnt get a message'); - } - }); - - ws2.on('open', function () { - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('open', function () { - ws3.packet({ - type: 'event' - , name: 'trigger broadcast' - , endpoint: '' - }); - - setTimeout(function () { - ws1.finishClose(); - ws2.finishClose(); - ws3.finishClose(); - }, 50); - }); - - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - throw new Error('we shouldnt get a message here'); - } - }); - }); - }); - }); - }); - }); - }, - - 'test event with broadcast flag and to()': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , cl3 = client(port) - , io = create(cl1) - , messages = 0 - , connections = 0 - , disconnections = 0; - - io.configure(function () { - io.set('close timeout', 0); - }); - - io.sockets.on('connection', function (socket) { - connections++; - - if (connections == 1) { - socket.join('losers'); - } - - socket.on('trigger broadcast', function () { - socket.broadcast.to('losers').emit('victory'); - }); - - socket.on('disconnect', function () { - disconnections++; - - if (disconnections == 3) { - messages.should.eql(1); - cl1.end(); - cl2.end(); - cl3.end(); - io.server.close(); - done(); - } - }); - }); - - cl1.handshake(function (sid) { - var ws1 = websocket(cl1, sid); - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - } else { - msg.should.eql({ - type: 'event' - , name: 'victory' - , args: [] - , endpoint: '' - }); - - messages++; - } - }); - - ws1.on('open', function () { - cl2.handshake(function (sid) { - var ws2 = websocket(cl2, sid); - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - } else { - throw new Error('This socket shouldnt get a message'); - }; - }); - - ws2.on('open', function () { - cl3.handshake(function (sid) { - var ws3 = websocket(cl3, sid); - ws3.on('open', function () { - ws3.packet({ - type: 'event' - , name: 'trigger broadcast' - , endpoint: '' - }); - - setTimeout(function () { - ws1.finishClose(); - ws2.finishClose(); - ws3.finishClose(); - }, 50); - }); - - ws3.on('message', function (msg) { - if (!ws3.connected) { - msg.type.should.eql('connect'); - ws3.connected = true; - } else { - throw new Error('we shouldnt get a message here'); - } - }); - }); - }); - }); - }); - }); - }, - - 'test accessing handshake data from sockets': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.sockets.on('connection', function (socket) { - (!!socket.handshake.address.address).should.be.true; - (!!socket.handshake.address.port).should.be.true; - socket.handshake.headers.host.should.equal('localhost'); - socket.handshake.headers.connection.should.equal('keep-alive'); - socket.handshake.time.should.match(/GMT/); - - socket.on('disconnect', function () { - setTimeout(function () { - ws.finishClose(); - cl.end(); - io.server.close(); - done(); - }, 10); - }); - - socket.disconnect(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('message', function (msg) { - if (!ws.connected) { - msg.type.should.eql('connect'); - ws.connected = true; - } - }); - }); - }, - - 'test accessing the array of clients': function (done) { - var port = ++ports - , cl1 = client(port) - , cl2 = client(port) - , io = create(cl1) - , total = 2 - , ws1, ws2; - - io.sockets.on('connection', function (socket) { - socket.on('join ferrets', function () { - socket.join('ferrets'); - socket.send('done'); - }); - }); - - function check() { - io.sockets.clients('ferrets').should.have.length(1); - io.sockets.clients('ferrets')[0].should.be.an.instanceof(sio.Socket); - io.sockets.clients('ferrets')[0].id.should.equal(ws1.sid); - io.sockets.clients().should.have.length(2); - io.sockets.clients()[0].should.be.an.instanceof(sio.Socket); - io.sockets.clients()[0].id.should.equal(ws1.sid); - io.sockets.clients()[1].should.be.an.instanceof(sio.Socket); - io.sockets.clients()[1].id.should.equal(ws2.sid); - - ws1.finishClose(); - ws2.finishClose(); - cl1.end(); - cl2.end(); - io.server.close(); - done(); - }; - - cl1.handshake(function (sid) { - ws1 = websocket(cl1, sid); - ws1.sid = sid; - ws1.on('message', function (msg) { - if (!ws1.connected) { - msg.type.should.eql('connect'); - ws1.connected = true; - ws1.packet({ - type: 'event' - , name: 'join ferrets' - , endpoint: '' - }); - } else { - cl2.handshake(function (sid) { - ws2 = websocket(cl2, sid); - ws2.sid = sid; - ws2.on('message', function (msg) { - if (!ws2.connected) { - msg.type.should.eql('connect'); - ws2.connected = true; - check(); - } - }); - }); - } - }); - }); - }, - - 'test accessing handshake data from sockets on disconnect': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - - (!!socket.handshake.address.address).should.be.true; - (!!socket.handshake.address.port).should.be.true; - socket.handshake.headers.host.should.equal('localhost'); - socket.handshake.headers.connection.should.equal('keep-alive'); - socket.handshake.time.should.match(/GMT/); - - setTimeout(function () { - ws.finishClose(); - cl.end(); - io.server.close(); - done(); - }, 10); - }); - - socket.disconnect(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('message', function (msg) { - if (!ws.connected) { - msg.type.should.eql('connect'); - ws.connected = true; - } - }); - }); - }, - - 'test for intentional and unintentional disconnects': function (done) { - var cl = client(++ports) - , io = create(cl) - , calls = 0 - , ws; - - function close () { - cl.end(); - io.server.close(); - ws.finishClose(); - done(); - } - - io.configure(function () { - io.set('heartbeat interval', .05); - io.set('heartbeat timeout', .05); - io.set('close timeout', 0); - }); - - io.of('/foo').on('connection', function (socket) { - socket.on('disconnect', function (reason) { - reason.should.equal('packet'); - - if (++calls == 2) close(); - }); - }); - - io.of('/bar').on('connection', function (socket) { - socket.on('disconnect', function (reason) { - reason.should.equal('socket end'); - - if (++calls == 2) close(); - }); - }); - - cl.handshake(function (sid) { - var messages = 0; - ws = websocket(cl, sid); - ws.on('open', function () { - ws.packet({ - type: 'connect' - , endpoint: '/foo' - }); - ws.packet({ - type: 'connect' - , endpoint: '/bar' - }); - }); - - ws.on('message', function (packet) { - if (packet.type == 'connect') { - if (++messages === 3) { - ws.packet({ type: 'disconnect', endpoint:'/foo' }); - ws.finishClose(); - } - } - }); - }); - }, - - 'test socket clean up': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.sockets.on('connection', function (socket) { - var self = this - , id = socket.id; - - socket.on('disconnect', function () { - setTimeout(function () { - var available = !!self.sockets[id]; - - available.should.be.false; - ws.finishClose(); - cl.end(); - io.server.close(); - done(); - }, 10); - }); - - socket.disconnect(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('message', function (msg) { - if (!ws.connected) { - msg.type.should.eql('connect'); - ws.connected = true; - } - }); - }); - }, - - 'accessing the transport type': function (done) { - var cl = client(++ports) - , io = create(cl) - , ws; - - io.sockets.on('connection', function (socket) { - socket.transport.should.equal('websocket'); - - socket.on('disconnect', function () { - setTimeout(function () { - ws.finishClose(); - cl.end(); - io.server.close(); - done(); - }, 10); - }); - - socket.disconnect(); - }); - - cl.handshake(function (sid) { - ws = websocket(cl, sid); - ws.on('message', function (msg) { - if (!ws.connected) { - msg.type.should.eql('connect'); - ws.connected = true; - } - }); - }); - } - -}; diff --git a/node_modules/socket.io/test/transports.xhr-polling.test.js b/node_modules/socket.io/test/transports.xhr-polling.test.js deleted file mode 100644 index a8c4b02..0000000 --- a/node_modules/socket.io/test/transports.xhr-polling.test.js +++ /dev/null @@ -1,2759 +0,0 @@ - -/*! - * socket.io-node - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Test dependencies. - */ - -var sio = require('socket.io') - , should = require('./common') - , HTTPClient = should.HTTPClient - , parser = sio.parser - , ports = 15200; - -/** - * HTTPClient for xhr-polling transport. - */ - -function XHRPolling (port) { - HTTPClient.call(this, port); -}; - -/** - * Inhertis from HTTPClient. - */ - -XHRPolling.prototype.__proto__ = HTTPClient.prototype; - -/** - * Performs the handshake and expects the connect echo packet. - * - * @api public - */ - -XHRPolling.prototype.handshake = function (opts, fn) { - if ('function' == typeof opts) { - fn = opts; - opts = {}; - } - - var self = this; - - return this.get('/socket.io/{protocol}', opts, function (res, data) { - var parts = data.split(':'); - - if (opts.ignoreConnect) { - return fn && fn.apply(null, parts); - } - - // expect connect packet right after handshake - self.get( - '/socket.io/{protocol}/xhr-polling/' + parts[0] - , function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'connect', endpoint: '', qs: '' }); - - fn && fn.apply(null, parts); - } - ); - }); -}; - -/** - * Create client for this transport. - * - * @api public - */ - -function client (port) { - return new XHRPolling(port); -}; - -/** - * Test. - */ - -module.exports = { - - 'test handshake': function (done) { - var cl = client(++ports) - , io = create(cl); - - io.configure(function () { - io.set('close timeout', .05); - io.set('polling duration', 0); - }); - - function finish () { - cl.end(); - io.server.close(); - done(); - }; - - cl.handshake(function (sid) { - var total = 2; - - cl.get('/socket.io/{protocol}/xhr-polling/tobi', function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'error' - , reason: 'client not handshaken' - , endpoint: '' - , advice: 'reconnect' - }); - - --total || finish(); - }); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - --total || finish(); - }); - }); - }, - - 'test the connection event': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.id.should.eql(sid); - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - }); - }); - }, - - 'test the disconnection event after a close timeout': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.id.should.eql(sid); - - socket.on('disconnect', function () { - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - - setTimeout(function () { - cl.end(); - }, 10); - }); - }); - }, - - 'test the disconnection event when the client sends ?disconnect req': - function (done) { - var cl = client(++ports) - , io = create(cl) - , disconnected = false - , sid; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - disconnected = true; - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'disconnect', endpoint: '' }); - disconnected.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - - // with the new http bits in node 0.5, there's no guarantee that - // the previous request is actually dispatched (and received) before the following - // reset call is sent. to not waste more time on a workaround, a timeout is added. - setTimeout(function() { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid + '/?disconnect'); - }, 500); - }); - }); - }, - - 'test the disconnection event booting a client': function (done) { - var cl = client(++ports) - , io = create(cl) - , forced = false; - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - io.server.close(); - done(); - }); - - cl.end(); - socket.disconnect(); - forced = true; - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'disconnect', endpoint: '' }); - - forced.should.be.true; - }); - }); - }, - - 'test the disconnection event with client disconnect packet': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.sockets.on('connection', function (client) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'disconnect' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - client.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - }); - }); - }, - - 'test sending back data': function (done) { - var cl = client(++ports) - , io = create(cl); - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.send('woot'); - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, packs) { - packs.should.have.length(1); - packs[0].type.should.eql('message'); - packs[0].data.should.eql('woot'); - }); - }); - }, - - 'test sending a batch of messages': function (done) { - var cl = client(++ports) - , io = create(cl) - , sid; - - io.configure(function () { - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - var messages = 0; - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePayload([ - parser.encodePacket({ type: 'message', data: 'a' }) - , parser.encodePacket({ type: 'message', data: 'b' }) - , parser.encodePacket({ type: 'disconnect' }) - ]) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - socket.on('message', function (data) { - messages++; - - if (messages == 1) - data.should.eql('a'); - - if (messages == 2) - data.should.eql('b'); - }); - - socket.on('disconnect', function () { - messages.should.eql(2); - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake({ ignoreConnect: true }, function (sessid) { - sid = sessid; - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].type.should.eql('connect'); - }); - }); - }, - - 'test message buffering between a response and a request': function (done) { - var cl = client(++ports) - , io = create(cl) - , messages = false - , tobi; - - io.configure(function () { - io.set('polling duration', .1); - io.set('close timeout', .2); - }); - - io.sockets.on('connection', function (socket) { - tobi = function () { - socket.send('a'); - socket.send('b'); - socket.send('c'); - }; - - socket.on('disconnect', function () { - messages.should.be.true; - - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - tobi(); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - msgs.should.have.length(3); - msgs[0].should.eql({ type: 'message', endpoint: '', data: 'a' }); - msgs[1].should.eql({ type: 'message', endpoint: '', data: 'b' }); - msgs[2].should.eql({ type: 'message', endpoint: '', data: 'c' }); - messages = true; - }); - }) - }); - }, - - 'test connecting to a specific endpoint': function (done) { - var cl = client(++ports) - , io = create(cl) - , connectMessage = false - , sid; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/woot').on('connection', function (socket) { - connectMessage.should.be.true; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid); - - connectMessage = true; - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/woot' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - }); - }); - }, - - 'test that connecting doesnt connect to defined endpoints': function (done) { - var cl = client(++ports) - , io = create(cl) - , tobiConnected = false - , mainConnected = false - , sid; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - mainConnected = true; - - socket.on('disconnect', function () { - tobiConnected.should.be.false; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/tobi').on('connection', function () { - tobiConnected = true; - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid); - }); - }, - - 'test disconnecting a specific endpoint': function (done) { - var cl = client(++ports) - , io = create(cl) - , wootDisconnected = false - , mainDisconnected = false - , checked = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (data) { - data.should.eql('ferret'); - mainDisconnected.should.be.false; - wootDisconnected.should.be.true; - checked = true; - }); - - socket.on('disconnect', function () { - mainDisconnected = true; - checked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/woot').on('connection', function (socket) { - socket.on('disconnect', function () { - wootDisconnected = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function () { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/woot' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'disconnect', endpoint: '/woot' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'message', data: 'ferret' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - } - ); - }); - }); - }, - - 'test that disconnecting disconnects all endpoints': function (done) { - var cl = client(++ports) - , io = create(cl) - , aDisconnected = false - , bDisconnected = false; - - io.configure(function () { - io.set('polling duration', .2); - io.set('close timeout', .2); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - setTimeout(function () { - aDisconnected.should.be.true; - bDisconnected.should.be.true; - cl.end(); - io.server.close(); - done(); - }, 50); - }); - }); - - io.of('/a').on('connection', function (socket) { - socket.on('disconnect', function (msg) { - aDisconnected = true; - }); - }); - - io.of('/b').on('connection', function (socket) { - socket.on('disconnect', function (msg) { - bDisconnected = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/a' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/b' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - }); - }); - }, - - 'test messaging a specific endpoint': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = true - , aMessaged = false - , bMessaged = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql(''); - messaged = true; - }); - - socket.on('disconnect', function () { - messaged.should.be.true; - aMessaged.should.be.true; - bMessaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/a').on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql('a'); - aMessaged = true; - }); - }); - - io.of('/b').on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql('b'); - bMessaged = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'message', data: '' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/a' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'message', endpoint: '/a', data: 'a' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/b' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'message', endpoint: '/b', data: 'b' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - }); - }); - }, - - 'test sending json from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messages = 0 - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - s.json.send(['a', 'b', 'c']); - s.json.send({ - a: 'b' - , c: 'd' - }); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'json' - , data: ['a', 'b', 'c'] - , endpoint: '' - }); - msgs[1].should.eql({ - type: 'json' - , data: { - a: 'b' - , c: 'd' - } - , endpoint: '' - }); - }); - }) - }); - }, - - 'test sending json to the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messages = 0; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .1); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (msg) { - messages++; - - if (messages == 1) { - msg.should.eql({ tobi: 'rocks' }); - } else if (messages == 2) { - msg.should.eql(5000); - } - }); - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'json' - , data: { tobi: 'rocks' } - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.equal('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'json' - , data: 5000 - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.equal('1'); - } - ); - } - ); - }); - }); - }, - - 'test emitting an event from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - s.emit('tobi is playing'); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'event' - , name: 'tobi is playing' - , endpoint: '' - , args: [] - }); - }); - }); - }); - }, - - 'test emitting an event with data from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - s.emit('edwald', { woot: 'woot' }, [1, 2, 3]); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'event' - , name: 'edwald' - , endpoint: '' - , args: [{ woot: 'woot' }, [1, 2, 3]] - }); - }); - }); - }); - }, - - 'test emitting an event to the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('jane', function (a, b, c) { - messaged = true; - }); - - socket.on('disconnect', function () { - messaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'event' - , name: 'jane' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.equal('1'); - } - ); - }); - }); - }, - - 'test that emitting an error event doesnt throw': function (done) { - var cl = client(++ports) - , io = create(cl) - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'event' - , name: 'error' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.equal('1'); - } - ); - }); - }); - }, - - 'test emitting an event to the server with data': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('woot', function (a, b, c) { - a.should.eql('a'); - b.should.eql(2); - c.should.eql([1, 2]); - - messaged = true; - }); - - socket.on('disconnect', function () { - messaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'event' - , name: 'woot' - , args: ['a', 2, [1, 2]] - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.equal('1'); - } - ); - }); - }); - }, - - 'test sending undeliverable volatile messages': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - s.volatile.send('woooot'); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - }); - }); - }); - }, - - 'test sending undeliverable volatile json': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - s.volatile.json.send('woooot'); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - }); - }); - }); - }, - - 'test sending undeliverable volatile events': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - s.volatile.emit('woooot'); - - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - }); - }); - }); - }, - - 'test sending deliverable volatile messages': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'message' - , data: 'woooot' - , endpoint: '' - }); - }); - - setTimeout(function () { - s.volatile.send('woooot'); - }, 10); - }); - }, - - 'test sending deliverable volatile json': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'json' - , data: 5 - , endpoint: '' - }); - }); - - setTimeout(function () { - s.volatile.json.send(5); - }, 10); - }); - }, - - 'test sending deliverable volatile events': function (done) { - var cl = client(++ports) - , io = create(cl) - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'event' - , name: 'tobi' - , args: [] - , endpoint: '' - }); - }); - - setTimeout(function () { - s.volatile.json.emit('tobi'); - }, 10); - }); - }, - - 'test automatic acknowledgements sent from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , received = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql('woot'); - received = true; - }); - - socket.on('disconnect', function () { - received.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'message' - , data: 'woot' - , id: 1 - , endpoint: '' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'ack' - , ackId: 1 - , endpoint: '' - , args: [] - }); - } - ); - } - ); - }); - - }); - }, - - 'test manual data acknowledgement sent from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , acknowledged = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (data, fn) { - data.should.eql('tobi'); - fn('woot'); - acknowledged = true; - }); - - socket.on('disconnect', function () { - acknowledged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'ack' - , args: ['woot'] - , endpoint: '' - , ackId: '3' - }); - }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'message' - , data: 'tobi' - , ack: 'data' - , id: '3' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - }); - }, - - 'test automatic acknowledgements sent from the client': function (done) { - var cl = client(++ports) - , io = create(cl) - , acknowledged = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.send('aaaa', function () { - acknowledged = true; - }); - - socket.on('disconnect', function () { - acknowledged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'message' - , id: '1' - , data: 'aaaa' - , ack: true - , endpoint: '' - }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'ack' - , ackId: '1' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - }); - }); - }, - - 'test automatic ack with event sent from the client': function (done) { - var cl = client(++ports) - , io = create(cl) - , acked = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.emit('woot', 1, 2, '3', function () { - acked = true; - }); - - socket.on('disconnect', function () { - acked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'event' - , name: 'woot' - , args: [1, 2, '3'] - , id: '1' - , ack: true - , endpoint: '' - }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'ack' - , ackId: '1' - , args: [] - , endpoint: '' - }) - ); - }); - }); - }, - - 'test manual data ack with event sent from the client': function (done) { - var cl = client(++ports) - , io = create(cl) - , acked = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.emit('woot', 1, 2, '3', function (a) { - a.should.eql('1'); - acked = true; - }); - - socket.on('disconnect', function () { - acked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'event' - , name: 'woot' - , args: [1, 2, '3'] - , id: '1' - , ack: 'data' - , endpoint: '' - }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'ack' - , ackId: '1' - , args: ['1'] - , endpoint: '' - }) - ); - }); - }); - }, - - 'test endpoint sending json from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , received = false;; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - received.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/chrislee').on('connection', function (socket) { - socket.json.send([1, 2, { 3: 4 }]); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - msgs[1].should.eql({ - type: 'json' - , data: [1, 2, { 3: 4 }] - , endpoint: '/chrislee' - }); - - received = true; - } - ); - } - ); - }); - }); - }, - - 'test endpoint sending json to the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false - , subMessaged = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (msg) { - messaged = true; - }); - - socket.on('disconnect', function () { - messaged.should.be.false; - subMessaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/a').on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql(['a', 'b', { c: 'd' }]); - subMessaged = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/a' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'json' - , endpoint: '/a' - , data: ['a', 'b', { c: 'd' }] - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - }); - }); - }, - - 'test endpoint emitting an event from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , received = false;; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - received.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/chrislee').on('connection', function (socket) { - socket.emit('tj'); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - msgs[1].should.eql({ - type: 'event' - , name: 'tj' - , args: [] - , endpoint: '/chrislee' - }); - - received = true; - } - ); - } - ); - }); - }); - }, - - 'test endpoint emitting an event with data from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , received = false;; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('disconnect', function () { - received.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/chrislee').on('connection', function (socket) { - socket.emit('tj', 1, 2, 3, 4); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - msgs[1].should.eql({ - type: 'event' - , name: 'tj' - , args: [1, 2, 3, 4] - , endpoint: '/chrislee' - }); - - received = true; - } - ); - } - ); - }); - }); - }, - - 'test endpoint emitting an event to the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false - , subMessaged = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (msg) { - messaged = true; - }); - - socket.on('disconnect', function () { - messaged.should.be.false; - subMessaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/a').on('connection', function (socket) { - socket.on('tj', function () { - subMessaged = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/a' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'event' - , name: 'tj' - , endpoint: '/a' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - }); - }); - }, - - 'test endpoint emitting an event to the server with data': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false - , subMessaged = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.on('message', function (msg) { - messaged = true; - }); - - socket.on('disconnect', function () { - messaged.should.be.false; - subMessaged.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - io.of('/a').on('connection', function (socket) { - socket.on('tj', function (ferret, age) { - ferret.should.eql('tobi'); - age.should.eql(23); - subMessaged = true; - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/a' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'event' - , name: 'tj' - , endpoint: '/a' - , args: ['tobi', 23] - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - }); - }); - }, - - 'test endpoint sending undeliverable volatile messages': function (done) { - var cl = client(++ports) - , io = create(cl) - , empty = false - , s; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/chrislee').on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - empty.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - s.volatile.send('woot'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.length.should.eql(1); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - empty = true; - } - ); - } - ); - }); - }); - }, - - 'test endpoint sending undeliverable volatile json': function (done) { - var cl = client(++ports) - , io = create(cl) - , empty = false - , s; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/chrislee').on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - empty.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - s.volatile.json.send(15); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.length.should.eql(1); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - empty = true; - } - ); - } - ); - }); - }); - }, - - 'test endpoint sending undeliverable volatile events': function (done) { - var cl = client(++ports) - , io = create(cl) - , empty = false - , s; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/chrislee').on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - empty.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - s.volatile.json.emit('woot'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.length.should.eql(1); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - empty = true; - } - ); - } - ); - }); - }); - }, - - 'test endpoint sending deliverable volatile messages': function (done) { - var cl = client(++ports) - , io = create(cl) - , received = false - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.of('/chrislee').on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - received.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'message' - , data: 'edwald' - , endpoint: '/chrislee' - }); - - received = true; - } - ); - - setTimeout(function () { - s.volatile.send('edwald'); - }, 20); - } - ); - } - ); - }); - }); - }, - - 'test endpoint sending deliverable volatile json': function (done) { - var cl = client(++ports) - , io = create(cl) - , received = false - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.of('/chrislee').on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - received.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'json' - , data: 152 - , endpoint: '/chrislee' - }); - - received = true; - } - ); - - setTimeout(function () { - s.volatile.json.send(152); - }, 20); - } - ); - } - ); - }); - }); - }, - - 'test endpoint sending deliverable volatile events': function (done) { - var cl = client(++ports) - , io = create(cl) - , received = false - , s; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.of('/chrislee').on('connection', function (socket) { - s = socket; - - socket.on('disconnect', function () { - received.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/chrislee' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/chrislee' - , qs: '' - }); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - msgs.should.have.length(1); - msgs[0].should.eql({ - type: 'event' - , name: 'woooo' - , args: [[1, 2]] - , endpoint: '/chrislee' - }); - - received = true; - } - ); - - setTimeout(function () { - s.volatile.emit('woooo', [1, 2]); - }, 20); - } - ); - } - ); - }); - }); - }, - - 'test endpoint automatic acks sent from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false - , acked = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/tobi').on('connection', function (socket) { - socket.on('message', function (msg) { - msg.should.eql('woot'); - messaged = true; - }); - - socket.on('disconnect', function () { - acked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/tobi' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'message' - , id: '3' - , data: 'woot' - , endpoint: '/tobi' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/tobi' - , qs: '' - }); - msgs[1].should.eql({ - type: 'ack' - , ackId: '3' - , endpoint: '/tobi' - , args: [] - }); - - acked = true; - } - ); - } - ); - } - ); - }); - }); - }, - - 'test endpoint manual data ack sent from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false - , acked = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/tobi').on('connection', function (socket) { - socket.on('message', function (msg, fn) { - msg.should.eql('woot'); - fn(); - messaged = true; - }); - - socket.on('disconnect', function () { - acked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/tobi' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'message' - , id: '3' - , data: 'woot' - , ack: 'data' - , endpoint: '/tobi' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/tobi' - , qs: '' - }); - msgs[1].should.eql({ - type: 'ack' - , ackId: '3' - , args: [] - , endpoint: '/tobi' - }); - - acked = true; - } - ); - } - ); - } - ); - }); - }); - }, - - 'test endpoint manual data event ack sent from the server': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false - , acked = false; - - io.configure(function () { - io.set('polling duration', 0); - io.set('close timeout', .05); - }); - - io.of('/tobi').on('connection', function (socket) { - socket.on('woot', function (msg, fn) { - msg.should.eql(1); - fn('aaaa'); - messaged = true; - }); - - socket.on('disconnect', function () { - acked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, data) { - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/tobi' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'event' - , id: '3' - , name: 'woot' - , ack: 'data' - , args: [1] - , endpoint: '/tobi' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/tobi' - , qs: '' - }); - msgs[1].should.eql({ - type: 'ack' - , ackId: '3' - , args: ['aaaa'] - , endpoint: '/tobi' - }); - - acked = true; - } - ); - } - ); - } - ); - }); - }); - }, - - 'test endpoint acknowledgements sent from the client': function (done) { - var cl = client(++ports) - , io = create(cl) - , acked = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.of('/woot').on('connection', function (socket) { - socket.send('aaa', function () { - acked = true; - }); - - socket.on('disconnect', function () { - acked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.eql(200); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/woot' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/woot' - , qs: '' - }); - msgs[1].should.eql({ - type: 'message' - , data: 'aaa' - , endpoint: '/woot' - , id: '1' - , ack: true - }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'ack' - , ackId: '1' - , endpoint: '/woot' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - } - ); - }); - }); - }, - - 'test endpoint manual data event acks sent from the client': function (done) { - var cl = client(++ports) - , io = create(cl) - , acked = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.of('/rapture').on('connection', function (socket) { - socket.emit('woot', 'a', function (a, b, c) { - a.should.eql(5); - b.should.eql('hello'); - c.should.eql([1, 2, 3]); - - acked = true; - }); - - socket.on('disconnect', function () { - acked.should.be.true; - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, msgs) { - res.statusCode.should.equal(200); - msgs.should.have.length(1); - msgs[0].should.eql({ type: 'noop', endpoint: '' }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ type: 'connect', endpoint: '/rapture' }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, msgs) { - res.statusCode.should.eql(200); - msgs.should.have.length(2); - msgs[0].should.eql({ - type: 'connect' - , endpoint: '/rapture' - , qs: '' - }); - msgs[1].should.eql({ - type: 'event' - , id: '1' - , name: 'woot' - , args: ['a'] - , ack: 'data' - , endpoint: '/rapture' - }); - - cl.post( - '/socket.io/{protocol}/xhr-polling/' + sid - , parser.encodePacket({ - type: 'ack' - , ackId: '1' - , args: [5, 'hello', [1, 2, 3]] - , endpoint: '/rapture' - }) - , function (res, data) { - res.statusCode.should.eql(200); - data.should.eql('1'); - } - ); - } - ); - } - ); - }); - }); - }, - - 'test CORS': function (done) { - var cl = client(++ports) - , io = create(cl) - , messaged = false; - - io.configure(function () { - io.set('polling duration', .05); - io.set('close timeout', .05); - }); - - io.sockets.on('connection', function (socket) { - socket.send('woot'); - - socket.on('message', function (msg) { - msg.should.equal('woot'); - messaged = true; - }); - - socket.on('disconnect', function () { - cl.end(); - io.server.close(); - done(); - }); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, { - headers: { - Origin: 'http://localhost:3500' - } - }, function (res, packs) { - var headers = res.headers; - - headers['access-control-allow-origin'].should.equal('*'); - should.strictEqual(headers['access-control-allow-credentials'], undefined); - - packs.should.have.length(1); - packs[0].type.should.eql('message'); - packs[0].data.should.eql('woot'); - - cl.post('/socket.io/{protocol}/xhr-polling/' + sid, parser.encodePacket({ - type: 'message' - , data: 'woot' - }), { - headers: { - Origin: 'http://localhost:3500' - , Cookie: 'woot=woot' - } - }, function (res, data) { - var headers = res.headers; - headers['access-control-allow-origin'].should.equal('*'); - headers['access-control-allow-credentials'].should.equal('true'); - - data.should.equal('1'); - }); - }); - }); - }, - - 'test emitting to closed clients': function (done) { - var cl = client(++ports) - , cl2 = client(ports) - , io = create(cl) - , connections = 0; - - io.configure(function () { - io.set('close timeout', .1); - }); - - io.sockets.on('connection', function (socket) { - socket.send('a'); - }); - - cl.handshake(function (sid) { - cl.get('/socket.io/{protocol}/xhr-polling/' + sid, function (res, packs) { - res.statusCode.should.equal(200); - packs.should.have.length(1); - packs[0].should.eql({ type: 'message', endpoint: '', data: 'a' }); - - cl2.handshake(function (sid2) { - cl2.get( - '/socket.io/{protocol}/xhr-polling/' + sid2 - , function (res, packs) { - res.statusCode.should.equal(200); - packs.should.have.length(1); - packs[0].should.eql({ type: 'message', endpoint: '', data: 'a' }); - - io.sockets.emit('woot', 'b'); - - var total = 2; - - cl.get( - '/socket.io/{protocol}/xhr-polling/' + sid - , function (res, packs) { - res.statusCode.should.equal(200); - packs.should.have.length(1); - packs[0].should.eql({ - type: 'event' - , endpoint: '' - , name: 'woot' - , args: ['b'] - }); - - --total || finish(); - } - ); - - cl2.get( - '/socket.io/{protocol}/xhr-polling/' + sid2 - , function (res, packs) { - res.statusCode.should.equal(200); - packs.should.have.length(1); - packs[0].should.eql({ - type: 'event' - , endpoint: '' - , name: 'woot' - , args: ['b'] - }); - - --total || finish(); - } - ); - - function finish () { - cl.end(); - cl2.end(); - io.server.close(); - done(); - }; - } - ); - }); - - }); - }); - } - -}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..c138675 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "Panel Flaix", + "version": "0.5", + "private": true, + "author" : "Antonio Laguna MatĆ­as ", + "description" : "A real-time interface to show calls, agents and queues from Asterisk", + "scripts": { + "start": "NODE_ENV=production forever start server.js", + "test": "NODE_ENV=development node server.js" + }, + "dependencies": { + "async" : "0.1.X", + "cron" : "1.X", + "express": "3.0.X", + "forever" : "0.X.X", + "socket.io" : "0.9.X", + "mysql" : "2.0.X" + }, + "repository": { + "type": "git", + "url": "https://github.com/Belelros/JavaScript-Operator-Panel.git" + }, + "license": "MIT", + "engines": { + "node": ">=0.6" + } +} \ No newline at end of file diff --git a/public/images/arrow_posts_left.png b/public/images/arrow_posts_left.png new file mode 100644 index 0000000000000000000000000000000000000000..eb445c095a8c589d1bb4bca9be4ee82ca2ea7fdb GIT binary patch literal 722 zcmV;@0xkWCP)P000>X1^@s6#OZ}&0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!bV)=(RCwB~mO+RTQ5eS^ch+32t#S5{ z>?IKd2^J-2FA)XOMY_eJr!XQg)I%;pr(iKU3x*&GB|#`bCoe%nNteY%SqLErVQ~+J z(}*qXGCRB5_sN@On`Y*{S@we;_D+t_!0=}MX-9-SOt7O-%O=a5hy!)rWbikCa4yPL}nX} z#zH6*a#36{H#apAH?YDY5R1j!)oNArdc97@O+U~|_y#af^G>Bw=hNx*1^lh5>QS*+ zoG^&sPTai3hjyIcLZNUGb}@(J;c$46%jM)wr?bx>f>FAPB3H~j1VM0?N+ls24o~CX z?RJ%HHY-b#bZDcj7qy%iS|AYM5pCRRwf1ZZOy3nn_RMD6?Y5FgB&5M$pyD`_$;?A8 zE_pm2&W6YYMYgRx`u)BdkH;kp6^^6P=v=*CU-0|=Jfp}KifqulWLZ8;CX>IL&E_tS zQJ_|5dKiw%1Qf%=|B-I#Sg3jE|<$YASR5WxDR7HiCjK*y@!oX}3yNGWL~`dElkjfS7XKAs05x!{Sv{DG^#A|>07*qoM6N<$ Eg5vl<1ONa4 literal 0 HcmV?d00001 diff --git a/public/images/arrow_posts_right.png b/public/images/arrow_posts_right.png new file mode 100644 index 0000000000000000000000000000000000000000..70669b591cc239e7b83c45e8a40ceb32cbaf1e09 GIT binary patch literal 725 zcmV;`0xJE9P)P000>X1^@s6#OZ}&0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!cS%G+RCwB~mQRQhQ5?rdopeR1rkNyI zSQJ6lXj8B)=Pvzhwu_~5Gjyclq5Wb5C&0A zyA*4?{_XdRnHYwddG9U#;DgS4@AtlMe($~CZ)~AZh@-Hhz!_jo(va5(I&*XvRw5;;OCf(yQ>6}e91B}tN_QmKfkRBDA%=rXW^8;=eTNq1&E9xr0C zm<#tNnM}Hu%O%(8bS4B5JV4thavJ_zB`vAn?@!z9_CY)zcX_>Ddo&t#`Tc&r*=!!n z=W{F9A%<&}b|<^t?lhfFfA95rf8PMY=B`$&Cj>#TIuL8vDX?4uS(X>AR%`!XY6u2- z#OeTG=TBMzkH=#x6bi=@iG&-UN2Ad!o6YVFhr^kL+a8L1q7euL0(`Mp6z~_HF=V-1 zZU<~Z%4ry`55SxxQmfTORiM#m{LW-DKPUvywowG*`-U`9O)I5R=~q6V-fxG%e8r699qo4+S zQ*zr$i^;zNRp18jj^%4bZ>vgCJFa|t$;v_oc-ctKe*_o+rH``1@OmBS00000NkvXX Hu0mjfCG$f7 literal 0 HcmV?d00001 diff --git a/public/images/asc.gif b/public/images/asc.gif new file mode 100644 index 0000000000000000000000000000000000000000..74157867f25acbc146704d43399d6c3605ba7724 GIT binary patch literal 54 zcmZ?wbhEHb6lGvxXkcJa);0M5|G(l-7DfgJMg|=QAOOiQF!A=tFW`Q0{?_dDi`go= G4AuZ#-wosd literal 0 HcmV?d00001 diff --git a/public/images/asc_light.gif b/public/images/asc_light.gif new file mode 100644 index 0000000000000000000000000000000000000000..5d8c06b8320397317cde6ea5fe095f993b7a60b9 GIT binary patch literal 54 zcmZ?wbhEHb6lGvxXkcKtbLss5|Nj+#vM{nUFf!;c00Bsbfr+EGoD<#VNP?1QCB1* GgEatI(+xQQ literal 0 HcmV?d00001 diff --git a/public/images/desc_light.gif b/public/images/desc_light.gif new file mode 100644 index 0000000000000000000000000000000000000000..846bfd6a1df4fbef27800de039fec871ed451d09 GIT binary patch literal 54 zcmZ?wbhEHb6lGvxXkcKtbLss5|Nj+#vM{nUFf!;c00Bsbfr+=LpYi0{4Re}9jJhHj G8LR=SP7gf* literal 0 HcmV?d00001 diff --git a/public/images/dots_devider_v.gif b/public/images/dots_devider_v.gif new file mode 100644 index 0000000000000000000000000000000000000000..edf5b71b4c7609cee082d1be072ada064975d752 GIT binary patch literal 43 vcmZ?wbhEHbWMp7sXkcV8Fg9UeU{L(Y!pOkD$e;sc11Tm31|}vKE(U7=gOLQF literal 0 HcmV?d00001 diff --git a/public/images/glyphicons-halflings-white.png b/public/images/glyphicons-halflings-white.png new file mode 100644 index 0000000000000000000000000000000000000000..a20760bfde58d1c92cee95116059fba03c68d689 GIT binary patch literal 4352 zcmd6r_dnEu|G?izMxtxU%uI5!l8nr)ZF&&*%FGe4jtO*5mbhJzhV&et11z&&^B?xH$MZ007{+ZK!Jj01(PQ zJBFS4pH$0DefCd1HM@h*JNkcsi%oOXzj>qsEle$eQ7ApHL(XYdn5Y$Lk_3-J9p9d) zFeVfl3J47_g1XaoDXWsnBp9ZzZ74CI9RN-Nw{>+8A&#rBpZgc9WX2H3Ssv6doZP?t zS!g}lGvW1<9%?dj_G_x}3WUMN(8(x{a6_pd0yiUsf^67GGS50uSB*ORe5x6}qAf1z z@Q;2y4G{Lb?f21p)uTpChN&4q%^blZ2IsusUOhk)pe0yxPD6oHKXWSjv8&2pMdnegiQUtoXt1U0MmWAWu2&>3j$eb^qKNV z_(`JQZP&mXLT@U%-2rPy!7r|*Y1oAdlarltaUyq+yq^|d{B9_>t@Rd#@_KW9w_6P$ z^Dv8(Hi8pDJK{r0Iqq*va$cL=isZh0=1)wIoQ^vYPs$(rBz$+DY z`y}1}`M%-da686`}zw_w>8 z!BcqxVTim*F)-}$segV$ON*!Zl~dhX@Rz^K2Xurh<1-vjImult%O z!-WXvkA_agVuhluW};J;#r>)?^uHS;G?a?j;(z?Y^FTwOA?tzLFvQDf&X8}9s7Wh< znEfd_vPyF_V`?>kR`w_h@+%59oKa;NPVGUo52QjisO-|$cYE(VNmm#+`#T5a;gh|Z z8A0^l3UwQMn0J3xXWL7tY~OxAu=_hGvp@_%SZKA)ec-h-dfwIhS3jGBLL6e6Os;1LR zRDG&3TF`HV*n{&*H!oTSsLq!U5xV5!Yr6I_!*VhmwC3a2BOYfWH13AtVY|n5jv49e zcb0xCCZnt0i$>-S$k9J@-c!8wG#siu(Lgy_r1nfy+}!W9g-ucwp=&Hs1=Vs4i_q;dQL$8~Uq2BVA4o4uY!6}S`xH(Qec+{mJD~qgg@6W8 zipi@Z!ZR+Kr_)u&G);pG$tg$8#KPrsl&N3(m($NAU&9ogH9rVfW<4Mw>^7$&96g<9 zHQzekG9T5SS7DVm7EFY%CjChhfRyap4+d;+^0ng^B)~xKFG^7d2oOo|R8uY&S|X0@ znAGMb^rFQwGPTzsFQ8ZK4S@WO(8`6T+$Yt9{jGMd?jrTeb|_!Un`n9xDZu-fW+_aJ z4Uyy_$)`Ot!~doWUHW`(?F!iYvc5+g-(W9X<-tX*h%6(f;+A(OQ@w{WYSiq&pjKnN z)tSH~5g)03sKk)U+&GyP*?86fusX1ttpH1ng8ruC6UOddM~t>0wvZh}1cW%&7{tT$ zze(TwkA~V|_~nL{6YE#^RUC__Mx26zo*w(EfK2Q@R6xo`VkJKs^Eax`&*O*bw~*ap zyaqA_p(~(POY{H5+NIgewtB{|(%ML_wR8o);^XGTQ|{*J>74v>{_iyU;U*NTN}A%` z`8ltg(&furYlb!j%1ra!KPSiGmJ>f4c!bkAtjb_qmQ+aVB(QohO zRo@%)1krVtMPgkT6&3T*u`XO8pE&-!!u((3qVnraj|gN5aDxvqtrPs*MCZcO3i^Qt zI7$&BFr)50exhv11)82?u`ab0FgUSw;dpbnAtmz4k^&Nx`xMQ$5(JW}ry%)ry+DV> zS)TWjtXz7V6iK5$ghFuPiT>;;fAp)oy%%7grs4UwqU5+Ms96%`wU=YU5W-UGw(6iq z2GhB=Zw49;Yu<#7=soc@tZvYFIVNfkRPsCT&;76cYOONMwv!v*e#(X?l7eB- z&pWvVcaO;IKDg7C8bZ-+Hm`g>n_WC6%BL=CZlc``M{0T;%eYQ4t}V%m20okR=HET) z@)@WU_}tJOqiH7w2K%lpe0P z^FhhCX$ufUPCq4?C1A8ZSrVz=$~!VZ>;=kb8eaI;S1TKb|E9j*muthJe2||9pYYI$ zR@lkEo?K76^_v{llrL+?Swi1koJYJqG_-g!v?$ITb=q4#Rk--)fABD zh4Ibu7+f~5HEzy@7xoP^f$=} z+D3gYZ3W>%>m=U)p#UNOPPd&2cD&; zxb{vXTzpCjcJAOEA_~=RX^_BM+_BYW*T{zzM(3TosvFOmf6Kp0IerP4`MuBgFdrkZ zf9X~m0O$toCckMn8klZDxWKr2%FHNk1VLQE)$!{Hz9{*a@TaZjC7kKsC1dIUx*6AQ zJFZc8p~!CewW(VvE@yaTPFt-6n+dZ@TM582m7=-#9JoDOH#zYPe{)-Lza89t+w#Zd zvQ3k$)Q)mPF)g)_+v$Gqgq~*RwGeBn{vhp!IPgkixW8WY)H`S{&~om!keO$Sum=oY zTatGW#*O^aVU<^!#et91z~$IYa;_C@J7+V)`<1b_lh`8FHOAgc=Az}lf)k%5xTMrv zr6uV%eKaU~wvi7pU)MeB7HK z2D;27Dik%)-q@hK-!I|N(cl`lAF^EIv0C-t$d1qtFnKIkcMW<4b%Lzf3Y+~~qB7`< zj);HTQS0Oex%zA170>?kRVA_m_*O?rZRpS3v{+O+cifN7Eb&>$Z==vGKh1V)C`qGu z_u8y<#N3Wp&$V^@T??GnE&RN^IyXM)r0h(gS3;b2pt0O!eNIt4{;3H~V5Ln7vs>8{ ziqqZL4Nwlvj4CtEv0>;Fw~D>LB_+-ecI)tiR%a!^GI3BawvNQGz4#b|_df&`e||2k;K}WnvU!Dx=0#ue(=U# zK&pYNNf5RQZOveUm+;dQ*FIA0&#`?@z*bBhUgr(n9_FpoHPB2pI8iMpW|sF*D{+75 z-k;nba~m^}=b7P$FAF1)S!oDKtNG-`%h{XQi6=SMH5GZ%8j?ugqt~!K zwvA_m(*=EIssFVW0EZ;o=u#R5gBB$CUL+->U32;2PM2O(drij20XBy|hH+=bu!0*KIKBj%c+ z^{)B`3$NB2yp-IHf02C#Fw!(;S&rR%2Pq(!<`Q=u&+_V4eCe z?!d0m@ndhMu%QZ`ERBCD+uU~%h>+E^Qd;Cz=IlGV(IwUrOz(+1Gkd7O z$HME|^+mAGBc4k(2jEj5$g30r-BUoK@Nn!*Td)5USoe+IZ-x9)#yd)sD}2Z?2{4@) zb|)xsK&pqOpB;+H#gbf^Pto29M<2Y>dU5pAF4p{+j=oBZ$2EXA*xI~AM@g20H7o_x z{2-Kc;SRpcxLXzU)a53ZoX%ndB^i8=>Sf&{i6CYkGSkvLj0<@C-!VKm#iX8dws__S zKp`T~rIAfaogJ!tV(~rs5)ctD#A};YXgPNI`<5=nWQjnIf<=1Pzn2y$C8yUkFKhwM z@%Ah?L`DM^@d<2evu->Oo=SVaiR<1GjYwe^G2)XY`l$Q%4H`|PpFA($N_8=6uOr0s zj+)C5xin zwn`&QQOr<`27|~lU*GNfe)r$+;%v`3=Q$VW;ymZMrG+ssw-7e~0K7L%46Ffwh5XNs z<6`?KHS^P-{ZmgZZ@~?jOs2~JH%~nY@PG5j1zTI#0Amn(L8qe2oETm=+B^jogFL!D zS!ISRHW3ybWQ6o&?2=byQi)JhfBSH9PzL~<0B#!S!^50cUq25lRnLyYPq06zWw>~J z`$KJG?wJet%MCZ1y81U)c?UzG;{mBi?no2aAHvt8L__Xy66K$DAupSD_4^VSeG;vA zGhrY7dmCA}Zg<=d*dvUYvYMo40k!iu>o|-n)q^ld6Q(6yBtUWr1GY<4vK2?uoeS|r zT(a}}&NC3;#Lv8{0Y$f=#j|95fZYUrx?foCUQ)KvUf$-LSb+6D%%)z#|1KO+ZTgw~ zNbE_n|4p~xYoc$edOQF-XOS;%evzdNi3 zk@(r9h#R5FpacG)j3VDRRz>g49u-o5A=@X`M=nQQ@W&MqFu3+}8)vIJyezf?(vDF#3iq72Yg1rU0$uCw``L1fzH6tU=MT zJ)FP#7~BMLoosB<>)Y`BnyxN?%PW`qwa_nrmk;P<^+|3lA$cC z!KnRdI-*8rENgl-h*t3^hviocbR?_BCX&(%?-)#H*`RRAUES@w^(0ey@bvFIq^EE0 zYIYPpa4Xz>{9(cUIq~=IuByDHtJskc@OXkoyhOvqjT$BRxhihe#hq<$(TaV?g(bYx zzk*$b_y4xdrKd-u!#@W)7x%!%FE62JOZu)fTpnAUKW94KXQKo9lR9BoI`nN#BVNL^WLc-2PBnDb`!FkQ6Yw zt8#VMCqN`vOx>8A-pqa3!sg7$vF4w|C29%3h5O_{d+D-|gED!U;S&A}5QU_Uz%?vp zmMBIPvj7qQQG74PJJYIU8KAgcJcJvNO0O6=%8w|@chXvpUX6O34cERMj)m?X)jwit zWYksusgx8zcrOv1Kd4Cm%yUoW#?wfM-ee=?*pXt7dUvyZrhI*Zx3!VQzm2&Dk2i(z zv;J?=_W|Z`2Nb*9*m`XJ^1ixr>GY^eNXXM8UzHKbJ%`E&g=nC-&t%U{b2>k}4 zM^eC8z9@VJ)NO6~zgW94x7psn_*GsP&AXPV>|c7+3V*`GDl?NuNHOr8_5jSBY+FrJ zxxFy&omakmacj-wPLUexLeI~s2^i^7jdiy$lDh;U-ze^bf8Wq&_j48xx9sRj~I0?AI|l`&NRKa0xj_M7{QQP8x>W$llZ# z^2}mA)Bep^+iA@Qw-LK1wT3nbnW#j??18HOX9M~EwO_4MW54*U(nB|yBja(g7FnMC zblZNR)Y{`EcNWNZ9&#=!$@W#;-?`_@7{fb;%BTGaNt!jg%h zP{`+<{G!`T5|=OLq>Z*{Z2O&8zMn16ACVB$Qm``DYk?tjJdb2uC7aci<-`J?E%OU+ zGrN5UtA#%|w#4Z;NP?k$>n!<|SrjF%qnK36 z-X#tb9{hRfZswTsPVZBN8H~75sHKLYIz~6u+pKzy#crwlQTpM#$E~+Abk)TD#sz#v zXX8Go`ZaF>B8Zu%M9U<;>RXE zbfFb@39Y9#&~E%DMKl*GIPjFwcNZ7nuMbVEpA0WbvBjM9QA!sp{YiDoe131&NawG0 z)w7{^`zTTBX*b%&r|n~U@dMgnxo!))g;D+Qg=`Xw5@VHk^{hiH?Dbc#u;gsXHzn0i z2)8o6*&Kl>6tpGG-xYvB-r`9coW<<#c<0|E=wQpY(XerrkkfVOt!t*N?wvbI|9F@&~JQ7q2jXe2H zCW^MvkWX8I-=%fo@BdI{A^py@pAB`shd&A{*amKE*X!a7A2Yu?Z%f;af$36@t#hgGI$UAqZQr>(vfUM3&C0L=d07kpTV z65hXXqa6SYLUvQ%beIm#w8HN~d3!4?$?iB2Owr|ut8l>>rMSqaZB}JGncrpN>H)eX z?`{XC$$(nou>9J>y&RJ_GCHrPS%%Jr+GeZ-p;^lV`1YLmyxKN-u#7+}dnx}N%zgXH z$CV1rQyi4eN)t(4&9Ix9{_jMeW*4;LYis@>9EQ2Es^gfy-VKyn0lc8i{7q3yuQV}F zD6Fom;2?qz@ukzYpge~g8?BAWbC}{;E82F=WrGc0;?er)DQ&9VG84bSn{>9B(k zwM%!e%*jQ~?@0DuS;yYC#^~O_E+}d7VN;GP%ockmCFlj4DNZ%yl_X-Hn$v_=+Er1z z)xF^ugN@xFweaki3bVXB3?uwjsn55RD1&YMi6B+jBAEU6|0Y1ne zLxbyOnkM9BHX2f}bHa<7WG>P_pz=aP(B)D(uo1i&yvId9DaA3GTsK?WdG%g5Q5z-% zUfT;wH`Xu@LDvM>F<4<`LiFUdk7UO)oS&1>Rnv!81;V#S1gZ^;byAIw5fmjY3m)nw z?+@SmlmBCWV>bFM8|-jGB{WLeI3o9DaWo<)11@8`kh*v=cN0DNB+st4sz6R#2I0qi z4c&8ZcAexDoiEyzoZJ((D9)8bG%^Z+MCs@_Q)++#Uvn&7#CI<7^ioFM{2qLTEAfMX z#1kD>oACS6EsTK8F}{R&pahvhyt|}$lX5-EzVP=!*jL*U(=7^7%UUF#`g>m(9)4uh zN+-O*&B&PgYQ520)x+!;$#)PXM`Kgq-o1CQLPsDGuSVi?k7|gIEtmv^WewHMkLAio zl1Us*ZM8T5*j_cED4OCIiNDZ{(dj&{3{g&T+~4Y*L((GimlI~v8Q&*2;zNurHxdEX zDgWY5T-u#~Rw6AH53<&eUOA_3sJa+<`S@61`0Z+&gPPC(dA9xY-3vCHs+QQ8y<*H| zq`~2~B6ACGIIhlq0$V=$vE_&HDcwxCpLD6$_1>ZT*h{SQByL1NMw0+fOj?Wz& zFvJdbQkbJBeJ=wX#hUle7%rUXR$4yPWhM|#t(`DrC+d#^K8*!sRn%{Eee5S%bqSan z?Gaxb6y6;Dw^4Ura3@7~UnV3ahsAZxfc!%uwqZbo@PGj7@>ji1sVn}8fiB(aiz~Jo zTDXK*@oVh~gVo^Iu~o8PQNMj6)RalL?o3^H@pnjZNLWoX&@@;gDJHvX&C-&SZCkAF z?Pux@B3eZQ037cWb&FZMuP+XLz1yG`s8)?SoCs!ygWlxG$PB`Eka2i37Fv)TK{|58 zJti;S=?xo)8?eTei(HD#f`Jq8j>vX~5NRzRU9sf_ z>oxtdr~$>ax+OJ;^X)vsSztp0JYJsoQlX{)JP`NN^%4mv6u3oW-hBTdM2W@5-Fze> z9n9nd!;qg7R6d&M#&&}CPAvA|mF^4XPltG`XZl9!t)5o^flxcEGJRDAZjOjF zQ0Iea%DG$E3bP&!(93|2RCY3l5t3s3J*JOik0=hGeaJ@3@H8tD7CVRqHg&`+R3j0a8@kqB}PI}{$m!yRab zvul5lL(>3*TF>n~)*#hsmwUTtKRAA2Fnk0PENdI!9GrZLu@zyKzs+&m-IKFviqv>& kg1Lm#gqI~e;$iYPkmG5c&N-g{UI@TVLkokN>#mRg2V?7pi2wiq literal 0 HcmV?d00001 diff --git a/public/images/misc/Thumbs.db b/public/images/misc/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..43e63ca0e6628b4b6790251f24e1d1124a5feb68 GIT binary patch literal 11264 zcmeI22|Sfq8^GV|-fQ38Yb|8SMKrmxlx&emWJ{wdYnCk6I%vj{DV3TGHB_dV=as|8A@SV)D+5EapyUgJJa-4gXTBCPvcyF=XLHm?{l8_oacF;bB>03fjcMi z#JjN9k}ihFCg>a(>nr6rxJED%KZdcu1^Rn}PNz>6Vi;V`r#bWl#-Xq5Q)5VgoCu&X zX9G9@K7bS80=NMlfER$_XNL+bzQyK0Tm;1i5VW5$>tD2%w>t0;GXO0IDksxf~!5ECv(+3ZMum0m{G~SOOaWE5I7C0c-&~0Nuw8@=d_UfCI1@_ypJjYy}(vC%_qS0bBvp4q2Z&-~o67 zUch$18}I>q0Y88S_yYkzAP@uu10letKqwFfpnD?Tme5?p0heGB(A(&DFn+T&rfV=9 zOll|8L3#%~8axXN`hi4%A0j)ENFWe7+1OawxjDJHxj4DFcz6Z*d3XhQxw!a+`2~bX zWHOnXPefFhBq~TElh93YcxXc)auA6eBpxmv(u@=R3MRk~bHoXclfhU7aCiY6y&TOW z91(hpIy{wdEYL?*Hg*n9E~rqz4;G5Yvk>s8*HAkGzQ+gxM8QSN3|NJ%oY`aoNNPJ1 zPO!@w=9QAI8~f#`E`c!|oWdfaV&d|P6)1{I>dQ4WwX|2PHZopgvewkh#@245{icr{ zT-~<0dw6Kj{0A7GVT(pc;(7=k#IY3B8Z6`a)FyTaADTgf8x}WS4*vKg? zr`{z$fK+2>Ha*2+W~JHhiv6KiJI0O2!QkNqzyQZ>(&8xC-vFNeGjr@`oHOVD7jtC( zt6{kL`OmQH4{!d@W**Pa-+BN2moKJ&$bY}j{EdZI^IV8OoUm0Nl>bFB^ZO*U{(x5} zW4{2!nA5qJ3{*;l@kVL+WH#&HvswQjncv|jMXrVjJQ(nSh#JM*Gffa({=WX5uKq?K z3O;^4t)K#9+|KA;d9+S&rXEm`U^ehRe(~ag-ma4cY6t>5( zF#q7jm_SWqnR6fYWOQ{5oC)o6jHLxfsBNw@y{mbI&GX_yqakXZH4~-W$T=jqwpF7|e7?*9?z}b=(ty405Xw+b7W#6t9_hU~NaO8jM-1BU(qfOqk)}F#) z{#E3oh~{6*hsa%YtluIxT32b*^^P(f6B-?UWEDm7R#UfXyZzG>pT~`Rv6wIqC(DMV zS-Ia+Qd#0&NBQ7rZZ}OROt193c*$-Zb)79)4%P9C6VFSUpEXG73^yUDM@L-Os?T-7 zwKZ-v?$_w(R=pgWJ=$FLtT=9Yh+VUem>74GU7@xOwP&gSm6vT-7OvWJrU7S6+VC#e zX1bJM#$U(4p615;YkvJaxi2vH*AEtJW!p+FZB4mFDmIFGR;SmOU=jahX@gBBi=K?$ z*u9jI>XX&+6X6--PnFCle|H%!Z@l6;XNZPgVTpmo8w{9|%33ud@<6erZK8XexPsv$ zk)M1O38|ZosTnq|FZK_T9%7WVxc|W?Dj+iE+x3%by=kxDue-tbPLt+`$OY|lI!%H0 z5*e@N4K)KBi)(Sd5Er;H;sX_k4^Wg41Rqt{d-I!$7f=L+A|Pz4g$wM#1;d91HQv?+ z?MyOWz;A3rgB`qWu&d_-a>M^5%MjWz0;j2Vt{^LjAD~pPr+ZjdpNW<_b0!2v&!@i6dCJ7hDH|1v-L{P=l7t z_=9_v_J__%-Y3EOW5(+rdk`qfaDS6$H18=Nhx|A4Q`24lY=gREQ|r(7k+!vP$4-TWDBLac7#L1vkt?*PX=|K`bd1W#S?ihU z^o>cV{rVFY)LwnwxRtv+o*mi#scwm-!=cR7p~WX+Oc(ybz1#4(8y!xBQ^dXtb>|e@ zk82QWilg9iI*&I|Dct4JT7*G(3B1MGzQH*Dh~rR9hCB(z6G=e-rQi%_vaaB1(SJpR zFnq4c)GiV26_3sIzmLJ6%!T#eg8B2)sdM4Ezy7mH`)^%ld&kwgH31~rc&k5;seJi{ zihHIqa#5Y`SY$9xp3HOkrijx~cT@dGc{?kpFU*?jo)$NY9BcL)NW#td|iN$X|t36dMwCZ z%Egm5QSYnLv*4RTA7{S2>;}zt_eQ5e`J8J)`GX1yQq{J+PdDGZ)jM$Qk@qu=PBs5k z-e&9AGx{$V)g{PDA9nKJ(O^KcZL+sG`1JYp14iZcH3mC}G6x^?-mq>+y~3gVd8M6{ zMo`_x{(Big*`E{?)TVDTcOy#&jkX4loEInm+B$ql&G^!jb3C|UucHbV26)Q#%akj` zv&T=l3GA$vED+90w@mjOVX-$%G(Vjs>3=!MCM=FhBcC}T^urcktZ-FWpY5rV!j?hB z_U_e-1Tz=l;;%(7*`DO)5WK=ClP|eRb&Ogg$z#zfTvg%C_f@jh{s8lU;~`lujV|R3 z)<1txQf+5RUi&;UgBF56d^G*$kSHBn9IYbf&9^^6z%Z>e`ebUh$P!69wnZsW=X7M+ ze{xObOe~8`#Z=|@vW95YkJV`?yPc1)BPHtoq^%$~n99Z9+w0wRuGqTrZd&Ebl&?)I z*}B%T?g@%W?-2d+v@5RF*jh^W-nsm}g1(%c3K9t?OW6v~tS^wrk$JLtv)9kge9^qT SdS)|luy63W^F{wZPvAETfIxQu literal 0 HcmV?d00001 diff --git a/public/images/misc/accenture-logo.png b/public/images/misc/accenture-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b85fb097dcc676302897cea0c6ce939cde5ea9e2 GIT binary patch literal 3486 zcmV;P4Po+$P)NN@R%7G-tth+(Sx9Z5`>y4rDl>ERx0*D%?C;?th7?I%xI-1 zEz{J3BBQ04rlu*%3Y3*XsfqYX67}dgpo8ED@)%)eU}pMbf6rpOJNM2VWO$7Cx7MsX z_nve2`R)Dt?%#Xg3`J@;3gc)5a2_xZcn)|!@cibF!`w@X;%`@~1f~La0>1$+3gc+s zkHf?>McKEj#yS#s0w@PsfX%>4;1S>+;8mcmBWkfCu-X?``vN}$J_#HIlvx;lDQd>Y)1_Z!y!12J*KyOPPF)%Lh{I`n0D$Si8s7 zXaL>>7GSdMiv!R9prcQ`2(0#{cY(`+CBVyp=WqEq?WiKPZ)za}GvHnlbG$6oFNwB7IaJOW|=jrt5KEpeLqCx&^4g)U~z*p5IhJn4OF>08`E= z#q@lyYQs3CG8?@y?Il&(^=rk?dZ01z{FVZE>8>$)YeTIE-U8ME^{$_zI$tMe^KA`0 zzcGv>Fxm7TY;V?E2ZX?;WL_GhvlV@prLb5Y#!*FzW=opNCI>fzJyNcIAB&@FMU) z7)N&jtJ4IkMH-7Kx*i7%ww}{sjdN!hM-Qcqqe%<`PG);2K(!^HW=to*d|*x(N6(~n zTWU^Yfv*6c)tIFgFRL)6mEVSO^sF<+ftY^ve%5FA0=Hti5+-Yofv#=C9PyeN#?hR> z^S1&AV`_!TGY1N+2~`7E0~b52uLZuP?@a@SSUk@IevQfKyEFr^5f=g9(EmpO*ICzE zfXl)-TB7kU1IA14e+H%iby~-6z=^=AdhKZexJ4Uk0Wdp^qvr$9Z_Zk1o&sE=#ZSv` zRVh3k4U7P;l5lN7cGBlx%P~&U1AYvq2k98#`@m}FzR|$X1&WFsCVW5Od`v&nFM!)p zG}U*3i*#=qF9U(EVh%e@598?e!1Eie`5vL~CbTq+BM-PqLMU^z|KS4KW=#7_mFAc$ z`Yr>8r+og70GI_vX{{0=zMjX0870{y)WiC}r=n7v>xTi;ffKA}G+XP~O)@=)4;Rwy zbsVC39|2q$#?iv8Ky{Y#DLt(()F~WplG;lK?E*{!>cTj>I8n)B;3vShodsX7Bw*4) zk^rSrnxr9&qbY&sZxt9u1AoGtYROpYe~nCXSM92d#teWP!#MhH;Q3F9monf&X@f*V z)XRKE;%~6beW?~^I;I5kP}%}pX6Y(v^ju)+30xk=(aUV_V^2(esq?)SX*%~gjh2(& z;@scVN^p?teM`!d-*d8mDK zf3twtsL#r+`-V!}&eIwtrf{sI={5uR0k1gE*`y8Gryc$4CT+f>9SxYV-j9>@NkFwx z3g~Z|dl%qP;1<1RfJ1Cx4Rs!RH*kyPbG)WAayMCv(p!T)E}@+5fU_1;fL)k4fngl2 zke0a6V!j#}7sk<3GD#;mzTLyX6+o@DV;IKK)f#80V+Q;%jv~x4s{^cS{{W^1p1(ow z8^Snx2Dly=X5BknAb&iEFos!<|L?WH^_XMylPw^35pQ>B2Pf1ZU(W{C1DFQCH0{%# zz{(0#r2<(krln^E=8)}HS+YJB5N852qS$2fk>!oc(Ar|iT<+6CQ`s7Q`-Vq$H-U>k64;Q4Nw?lMf*r46io46@dyA5dm@G=y=K z7QMFQI;g7Xv%M%ej#is&fWY%>tY`PsIwmq*Bdanng|CeYgsKw@l9hnYb=2V-DU38) zA+XIHz)>lo?4#}gf#(y(QLP-%Ue=`#G|9uAudD`^r|?pb>2ZoI9yZAQR>*&{=~8LQ zv>;%Zvy1zZztpur(V@%d#uUeVPcQPy=nhRUrxED#sMt zv`(PfkiyU2*lYEgT%Nj=YwqUkDtDuxbvXst#G z$bDw-6s@-<#~2BS>Qct3avbRfY2}6j7bVKGfg)f@zJ%;vMvtbr8FqTfruQ*F1<+W) zxZARpR_cuTU4c`iQL2>^=q$CK&9jqBQyjyJ9kBVX>8K3jh>uwP-yl<1Q{erZ@+@9W zO3rPMAjz;(8^88qEAp`ERjuQy0>*95DtF{5jGW}`_9TZ?ufNJ1;BvK7xqO!=+c3!n zX*xRxHb`SNwqcw)@tpA44Rf4o#SRF{rImZI$*W3nC0e7kt!ywiKewgObu3X{=o*EV zHmKHMPLo`r?DaHqAEHcS@Y8LaBsD*^lZ4l>2b)3PinI%%){?F7 zIq&;hQEx%Onh2a`X^L&YEF~bnsYK~rm;(X1N(s-V_$6b*IP%-5X;!9~)Jb6+wWT3( zQ;NDiJ&dEuoglp34XQMEAt}k7!OrbgS?2hqmB4AL;}!%gS5y5PxFqoWWr63{1)iUr z%Joy6^n{kAgxiCF3-rSmt&%iBTK;GvFiIx75K7MO*>or{v5f%TZ9vHbvw*+0IGFwgRafsK~=p6aQcT>zhPuVH_b%JlV;|ePq%`e=H^CFfEKDzqNHN zNpZ#pJD|#9GO%a1;snlCNb1_d!nL8^D=R)s0?RORtIT}v=VCTS$5mUn|TO{aD+)pHaL+B9A= zJyON~v~?`aDunKCeSTUPM+-59<1BCzrl4Dz58YuLJ&Gy1e%-m;9k@}d`tR!hd5i5` z6GN2OdLC0^nSsgV4{*jg2~)C}C;paWYIfxU@L=VbR%6OEGqvy;$7I|FJQ&8&0_C?h zOA8E>xu2_iReMk@aCNl+KLJ=4#?k$n&whGcDNrrP^tC0_1JhEpO3wN|QV>Z&e9%th z_jOD;>~br|n8Ebz%>w=!#?fNQu30iW6zHD?ZpSpVC1$P<@CQYJC0g_fnW%D1`aX?n zfgfVpSVlQ%gKA7I?L?WV4FXoBcG?W!g~0Qh!#G-mDIHv|Ma)>avO@%p^W~)1X~GqH z{uI|%W{t^dX1rBd&23p~FmjH5d+ zMQ%HJ+|7vq>$Q2mypw^oS!*~1)6{6YLc=ACsj^s!qNoJcl}O#)>*R{M3n2R^Hv+#* zNit?Et%ZKt@GPmy+7z=}!S(h;(sENL(ht3>MH1(5|g;v}mJJ zB-cn&xY1@0f$U)Iv}%DolP5`7(u!M=FTt4jqPL`&x2~GcFt)c&C-Zn3(@C*8PyTPV z*1U5bo-zgS=UO5H&tIp+=!}%)aMG%r$a8=IU!K)uufTMJo-H9|tklEvn0A?En2dbZ ziNMm-=an)k=K@Evy)nNlRDF6Tf literal 0 HcmV?d00001 diff --git a/public/images/misc/button-gloss.png b/public/images/misc/button-gloss.png new file mode 100644 index 0000000000000000000000000000000000000000..6f3957702fec30ffa4a33eaf30012e06525ca9c4 GIT binary patch literal 74 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI}!3HGf{@&OHqy#-(978y+C;#~Wf4|&e4iyQ8 XSrPLr`vdv2fszcKu6{1-oD!M0)+jEP)XavQ3ME)ir9VZV`OF_YZR9KJOG)fEM8U|j>qEx3Ijr|+qSJb z0E-auLBI!tLNI5Ub8a(b&=&y$6shMOwr#>zhQ%p2oYRwRCsUw3e3e$^ z&wj3f5&-I26lktJ>KH}ACVd8@nk?oL5+SvbVAsE&C~@#bH#Wd-VmMOqZYmxy67|-0$*#~zeQ?AshB)9L zfgH32{iyGMFT6oRH~!|zg;`xk9x^8NnOz#3t_5>;vknQOv6NtxtL*XsSm{sY#1 V9^AB1&Sn4r002ovPDHLkV1iCm0jK}~ literal 0 HcmV?d00001 diff --git a/public/images/misc/custom-form-sprites.png b/public/images/misc/custom-form-sprites.png new file mode 100644 index 0000000000000000000000000000000000000000..dcca3069fb3b442bdf9bcf1ee0cadf0b512e9ad5 GIT binary patch literal 620 zcmeAS@N?(olHy`uVBq!ia0vp^PC%T&!3HE+3QzrD?~8W@;36h1KquD^bL)eC{&myXK6jcnkU z>v>(bmNlD#4XCDN-ceTGn+ZCDs{}-8v3~UdDOn? z=Y7}x)c&_kM>a>=#d+6dDe0ygef%L8nRidUr@DUc>xewLFZB*Go~Jn!Ggt06X1?g{ z=<$j%QJ`tNn@Qxo+QSjAW^87gs53wIrQPXIkt!WA>wm_qVg@>`fkDY#;A+-ZCEH9t z@49snO&q1F`nFkd$LE-KTxe#h*!%uky zB>omf*`N9>cyZFbB{Tizi0+j?7A2{0NPC}$W5f~l;xN7E{J{!sV&6@@F4;KEcs=7J z@4VFju!7FN*8=JD9T*r{1mFY*n8n(DV(@hJb6Mw<&;$UfBmjK? literal 0 HcmV?d00001 diff --git a/public/images/misc/input-bg.png b/public/images/misc/input-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b153313ea8f43f404412dfc977e7e5db98f78b3e GIT binary patch literal 2993 zcmeAS@N?(olHy`uVBq!ia0y~yVA=!19Bd$wT{+GS4BQo-E{-7;x8B}3$k|{Z;BqnW z&C!3ijSVXd+7p-$eUi&zF8=jq+7q8klY1%|7y^z>eByPGV$}e03P>T0Mt2IxWDK_>n~Y5Z+;gLz z#1{3i;DgavBW5%vapj!hl3WG$s4-wK8-|MyLFE|M#0@KLU^KQuWVF=57Am7fE39&a z(eSztIWY}~qE(^7mjQR(I~;rn%HqR58b>_|D#wtM>jQKC9lsQPzI$=k0h{6sp00i_ I>zopr01D_m0ssI2 literal 0 HcmV?d00001 diff --git a/public/images/misc/modal-gloss.png b/public/images/misc/modal-gloss.png new file mode 100644 index 0000000000000000000000000000000000000000..89aebbde2d119b952ec9ff96618ffd3100245f60 GIT binary patch literal 2843 zcmd5;eKeH$9)BJqiNS0tdC6$k)yi8jUP@{*6|N{shN~z>@;+KcHJPnFHZq0@)38wu zT1|HniqK70W|$dy4_#d}UJ6UIg%a7{Z?3cF%(?w{@1C>&%=tXu*U#tseV*TMQuceg zXsGF_Aqb-3=DN!pK`=%Lf|{$U0E?qV_f`Z^d*!xk=fQ**zr0M^r|+U99E{P^Bf80#L<)U}#YQL^`lS ziy}}`Ua}|voDm3ymM;T36QM}?cM%Gd{}2&!?gsyS zj0LkZILIE7t!|0eXW|P!j^=+#e2FG$!IfEk7?>{|Ce57@FJFkl&)^^9aDnPrNUonF zWDs2$7pGri&M!Wi$+VRV<_%HirNJY48pxyXkl>a1AgMHg;mmb;olGfP&J^a&bRqM=!Rn5 zP*b~L3>-u9a`?nu7(^U9rI##?Fo_B*v&sM<0Ab(cND1NxrVk&7o9s!C8L@DkH?YCJh5S1CQ6PR+5a zP2uNQ+p~=_bF-j{+#BFOm z1{52(A8KnJ>c$U`J;%>P!TUz{%?rKy=mQ}&&5Q&Cf^@EUkprIhyk_r1VSJb6uixPX zd*vc}gtaOS1d55YTII(^_&t}FU)IxISY5omXrd{W9RsO3zL~nRxp`Aw8y*4aJxxNa zxOlEN%}o+2e{Q>}A~2@d1#eB&gsdk*4t*2MD%&6hOV!W-Uq1b?M+Eu3xX5y>JA#A( z{DKNv0Xc^FjH-sbeBRKTGyz34^7dJ)yx<1HcE+=UA5>s2?eMJ$ag}5fml{om^d_YI zWWjFHUV(7#{@E}DRO=BphQWGH@qhK%F4`6>Pb#w0TgT z(OaD?NEAr~iL?m;4F=DYt!>zY*JCFy1BB}S!QYeFRuF?#2Lq?mu0^FVvX7$R+$d%G zFc?hzCn&T(>Wf^#FsoMr#jo#<{3*)dB*3aTVkA85aQd*%QvggVq)3#PPWD{+n>X{AgwfJCo6*-qFIMmq(Qe(9(Rm2 zG@CViP-r?Ug|`Gzd(_q;eSC0I-Zb|OiQ6xv9(RS5E3oO@wqA`zcr6f4vaD;`T!f+w zcsqp+6DV3j{4~x0mdxEE?%*Bltw#aP8=QC8*#U=@a)*DeaU%PuTUYykk|_CQg}0fS z_Mb7JZ>wZueX;Dcsg0q$;7Hi2buP&mAlTXcig8RtWEH^@==h;HIy~%s+h#CX(8&Lx zxb)5#jb%nsf~~UoDnc)j(-u!KQxKYC$vW4#sY4b7$m5JEta&$NH}ZAB06&&O5)a%+469@b^1jwT-K4C!$ugoJT}w1US`=;QmG?vIiAJyq5V8am0B!= zjM~2*4yDILoFn^#N02Jc(oLUr^Q?`Mw;c24le?_`o}0*n+pMT?_4A%~7y0leYn|l& z+(Jq0IoG(MUl3U9g_npD*Kf=@qb`r`4BR;DAkZ z2}KYbl>k1MF>3AhYe80Tx(x{ks?f~n&@u6kNS?2txu`e8~Ll(Z8Zv+0j)!ECq>j5=579-*p0Sd^-T?zmk z!9WmjDF7lJSfND`D1nx8ivj>=Bp4`geHqY^Kl>k|Mc*WJ(A!5jwKz!|NI~3od+y>p H2VM9L>fCXp literal 0 HcmV?d00001 diff --git a/public/images/misc/table-sorter.png b/public/images/misc/table-sorter.png new file mode 100644 index 0000000000000000000000000000000000000000..2beda263681abbc4450123f9bcf20992d4f2b0a8 GIT binary patch literal 447 zcmeAS@N?(olHy`uVBq!ia0vp^NIiIcvV~lx4C+&d%Aa;3<~&=~_*u zT1(@f8)x@C{5$hvjh*@-Qz0dn4gtmH_K?u6m$Ra4-muZ+E&C1Tdqs6?wjU^Yc~iDvTJ_Z&M)|KC zA6Va9N&fb3Fxdg$MI<;^JDy7{0J?q8X zxEc4#qn@iUJU`$oKf&OqN5_#2p9ws=E~_e7&PXkjXYSWjTsY}@o%ZV4^8^&Z`arIB z3TyFEn>;lxU|On>$8$e@Q5LIthF+X6r?4$)+j7o5$az6((iG-PTF$2#8^zLliX5ei zuf+&tc+4wy`VC5UDGKfoZ!!N9{H%FHOpz$D1XEXer(2tz8! zVL(SB04E0vng~!%fPsODgN2QW8K#zromrTLl}(&Ol2KHQlR-#CRZT)r$x+#rOAw|6 zZsh-43_L)MOoGgU4E7Ai-u`EZxH{$P?jWubKPNGXpv$r0Ofvx1#%#3@^2e&FO7Z2n6nAoQGhDl`a;fAEBQ;WGPZ4Yf{)bh((wvvf!%D10= z+({<8mRBX1-0G}oIclJ^Lm{xmt6WyldTHaz{|o|Fvw}N(KAaZ6+M^+$`e5b z)b=}HCw*CYI6o#8t%9c6Q~>zr;O1eWh4YrsjTwU5x9W{1bV8dgrvF zpF#WQ<)3HpdiQgYp0q%8+QaYeQv}Mlnti_T=}}PK&hocQ4u81Wzx-#({^JFwf2ICs zh}qvTEB4x%?JCJr?`AzH=hEGJ-ecRl>DtqlZCQE#>W_0RQ+A!!h`wR+FJ<+9y$AOH GZvp_rbGwrO literal 0 HcmV?d00001 diff --git a/public/images/orbit/left-arrow.png b/public/images/orbit/left-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3f2d62d929662159e348e77965c9e50e2fce9a GIT binary patch literal 522 zcmeAS@N?(olHy`uVBq!ia0vp^en6bU!3HEX=Cb)RFfd;9ba4!+xb^mqwclX}k=8`< z%-|GB%}v{Mj!atpM#1|4Q)HX&1ils}Ed^0t)~Tru(=;@am&~+${%m`RN_g>lyPu}X zr$a+S!=J=6GFm)nVD4jJmnk^F=-$B02jnUrU<7hkMe2yX&D&o6>i56gtxV^QL)$g=d3cmIBQwu6RG9>&)C(Qp0TR8JY!aGd&a2Vv5a56YZj{<2-cn&YV9dq5Bv@O&0bz<}TRg#Gg5^Sj!0B^`iq%;4$j K=d#Wzp$PyAmD(Es literal 0 HcmV?d00001 diff --git a/public/images/orbit/loading.gif b/public/images/orbit/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..969f50597a35944c2b347a1a3c1786915b7f85f6 GIT binary patch literal 2608 zcmdVcYfw{H0tfI*ZgP`cW8OC;H#`CcWRTZVr4ii@g+BXC~aqF_uFn~+VkOj zIA6}0-<Uy#$g#LwFPH-F?NB>NFPX~8)J;h5sYDsHJ-b9 z));53u>nk@{PUZTeQrhvktXKgJMXGSZMWVBhv%$1kkD_l7zsd+>O$rGgwH}zXSOD+ zAN{myeC_X�xnl(rspR>h;YP7ve1?`vn|B#q%T0{*kGusPQ1VElyHoH}5X5vB<-4 zV*k0aF$Kbq1hWQT>WSs-H;GTDsDu?Zm;Y`ws1(tN-x~qnR7keG4}Z!#>%Nv!@ukrc zlJ#fmh;+4KkfYo8;F5jrZ+gIsmI&|2S1+AWRQgU&& zDZ8bGE=^IhI3~=){R6}eAm~qhgh71>1H}I{hzZfYESzyIO3=qA;hp1vYab87k4;_f zo#m2l%(%|Y@)}fE?1F~~AMCda(Wlgp z@Q(+er^uB~mFgeQ$1Lt-FS4vpK6$I5+H_?7?&riow^9ZM_giWVi7hdfo_uiLOYRwg zE9J1|-cfgnfA^EbA;{Nl<0`Mr(2@~VJ-rUUXl&UdzE#2SC7Wzm4TUEI+??1mud3LA&3n8` zN@2ch`WbE;BO*?KWpcl|LU#kavC`8+54G}25#J0yfOK-&;vZfY;IFJ9GsvE1L1gox zgCrWYqT`2mM*PJa6?=5OhdYhx+N5Mq_=)MI$;VbLC9R9KRnHT+G1cO2OGZGs2qU0t z9{;AS+An73R7lGc}37fCwI0)tO(QLc2u;GQL3W4qU`f8x40YL%}Ls?_wNeyt^(5 zSH6{THU6lt;uLTgz3(y{7E#XIu>a7PoHjq26hJ@Ry0s*7>yyFka^2f#*emnag8cF} zA#43e=eKo-;U<MDh?J$blat+ZjnxPkve1Z@-Id zH4&pXH31FFx#ZT4xOznXgZHmPc-4L80tr7mF{6&h84qo(AWnZcs!Gtm7AFjYD(VASzJ5a+^sYr<(Y<17 z-8}^Om6n}P@cBzZ$BrE`O{(p2qWFM!V07yFNA&2>)s(ny@R6hb*Ex3Ep*2@i_?%aR z$79cWx1WjLA*=S&+!xO@h*q4)|Kap|aR?7yX}08VcN_gx0V0dTn?Bh=t_|FEY1={X z&DtVo`!{xHfYN8?%hQzLmj$U=g;Zemo+y}>2Ix?Mt8%H`3|X}87_k9yhlCn7>#%`{ zkqZHjy}KcVUda9!V;2&@Jh~<7xB+~}z_^CV-XrBTj>CB*D33WV zW#&P1<1>cYxp~8uI}1~W_`I>X$?Oq>b?(Ntgn{v)ywju7q;;yGjhBJeng^Y?*KOO5R%vm`A9ZIv91KsJ^Y@9bximKooxR5xV4(uf@x55fTM@eL^?|8ej>d(KaA8C e+Qgee&ab)=ys&@6bg#4GHaRP9)6)NFdHf4M2#AmX literal 0 HcmV?d00001 diff --git a/public/images/orbit/mask-black.png b/public/images/orbit/mask-black.png new file mode 100644 index 0000000000000000000000000000000000000000..02f3fbab28597dfc981bb8a5ba787c6e09630cb8 GIT binary patch literal 526 zcmV+p0`dKcP) zlkLppW%4q~Z1VUn@y)kil9!jfC6rPkaER6*-2-6a2AiL=NTnY-uv&w%rfN|B9CRT_ z9Yhh|;LHIc!4j#CXzY>bh(c|JC2H5PJsB-=#)%&~;wg3=*PK{%5id=XBF7Np&UHlm zO0^LQdIyo4CFhNypooZ}1VoFzf^ho(;m%etmP9QDvzFk_Mlc1Yd}bDX1; zJlW*oj$zI)BFcTo3#vsNoRnABf&y1VoaN^sibG~o9P-YJ)AOVxfZ)Qm+T`d9&7yoc z7ff6Wj$I3mYy>0MYuLW1N*lp1z+Gx8IAM5EoRW1A7rkE0Qt;hHfn7FAm5pG}nJoE6 zPD$Sw*dTlseJmFO*<9*Z0z+}ZeAKIWQs&4BCX>{zvVBP2z zagZWk2Fbon_-}GmSokyuHA_@#o=hGL0~j2ka?zu~3xvVMr@@)V+^5eR%n_@1@55l+ z`14SWw`119h+ktRvPc)9-o+JeLwzjFLwwC_7;Zv(4{mt!r@mj*6XGy_0WId(;lKp# QI{*Lx07*qoM6N<$f;iXnYXATM literal 0 HcmV?d00001 diff --git a/public/images/orbit/pause-black.png b/public/images/orbit/pause-black.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb087545b00210e8f8198829d2ebd120eebcab0 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^8bBPt!3HF=`^^smspp<9jv*QM-d;PntjSU2z{meK zj<$*Yf`y$sTwJzf6)V4E-m=I4;;LMiEnb3x*YBL=xxeh_c8}+;>dlPXX8xS{_>9jf zOJU1N4vkE#Tp}6)4=y)oF6Ar8%fi2(;gd!>+6%7SdWI&cED3cU3=Zt~_1iB6u-{Hk-|3F}%29+wJzL5MFG-LU{2-i{RxHE`XO;+#D~r0b{)U zCQR`(8ZpGvX~qmst05yiy{1g?G%p1MJlz|xIG*-nUl>pSWm6Q-;}b^^&*#Hh49{yh zM+nbv5m5xsbLmwi)htJbnRpX4dQV!!%9bGu$u?ufSc1Hjd-RJevoc4q6?!V6+-=326RsNod}1 ziDN*ilD{96+(-HD~1*W*S785>2#V3p~b+>P=~|e zbN;_l5wyBvUcdnd903kE;D7^;00$g!zyU{q0}eRg2mru^<`=WB&#f6Bk4gXl002ov JPDHLkV1nf4_gm_6NZp+vde& z6y)3b8Qb=zaB2wABs3l0TtYHaO+o-TbqQ6QSvLyT+*$tBTpwt?ifq2dC- zv!r1iS*YO|fUVS&w~{$jU7uxd!;&!Bxyo*v;_x1FQf{Ts@~dT5h8o&*6KTUF)bPtP z%tH-Vmf{RSP#<=g^eLHb z0Y}6QTM;+zxogvQ1z0-ZHhL0>0XSrokDObNx&WLq_FWPaFl7KvVgkZ$SH!c54S2)I zVi6ONy1c3bpDbxCKu) a8t@y-y4ghe)oPjm0000 zlkLppW%4q~Z1VUn@y)kil9!jfC6rPkaER6*-2-6a2AiL=NTnY-uv&w%rfN|B9CRT_ z9Yhh|;LHIc!4j#CXzY>bh(c|JC2H5PJsB-=#)%&~;wg3=*PK{%5id=XBF7Np&UHlm zO0^LQdIyo4CFhNypooZ}1VoFzf^ho(;m%etmP9QDvzFk_Mlc1Yd}bDX1; zJlW*oj$zI)BFcTo3#vsNoRnABf&y1VoaN^sibG~o9P-YJ)AOVxfZ)Qm+T`d9&7yoc z7ff6Wj$I3mYy>0MYuLW1N*lp1z+Gx8IAM5EoRW1A7rkE0Qt;hHfn7FAm5pG}nJoE6 zPD$Sw*dTlseJmFO*<9*Z0z+}ZeAKIWQs&4BCX>{zvVBP2z zagZWk2Fbon_-}GmSokyuHA_@#o=hGL0~j2ka?zu~3xvVMr@@)V+^5eR%n_@1@55l+ z`14SWw`119h+ktRvPc)9-o+JeLwzjFLwwC_7;Zv(4{mt!r@mj*6XGy_0WId(;lKp# QI{*Lx07*qoM6N<$f;iXnYXATM literal 0 HcmV?d00001 diff --git a/public/images/trashcan.png b/public/images/trashcan.png new file mode 100644 index 0000000000000000000000000000000000000000..56e04a9afe78e9bd674f009afb5cf829c574290f GIT binary patch literal 3277 zcmV;;3^MbHP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005_Nkl{a5XXPJJ8yvlW9+<~(RxD#p@Odm_};8(DqvDL4j#KeN0 zKw(Ho>}-_83JRlz$!W>yp^-y&4|z5`vTl|Yj8~XA$t3^G+xh?Bn>TOP(pqz#W^>N= z?+ZvYU|Ci)o%aHxTUBu*8uv{+R+onyYz&qfLQc8;m{eJ(YA?@{guMO^YyDvlprPLC53%mzriERo5z-J(B zh|=lw7m@X+6N61$DW!qqxNb^p(^WE#A zvIw1EjZHkfw$lk**Of5QUl)iHZHhaN^JgV8iTQl~qQSLV?XtmzLgA9*IEeyDqvFbB zGC2-{K>yCi8nq#u|SjgS&0d}%9A9>6NnxqvJb$WQpvdbSczxmwXe82bJ@0^+0 zSh#4O)jZY=0M>%}o~677_;C)K`1wIS*1$_@eSUHM{WXu(ul21i1G%*3kuq9PM77|_T z#sVs@77|g*PGNR4s8fU+HW7bm93GFz4l5htnro>4uWeU1?6R>qv50+3q+>Pl9kkz4 zn=0bJ$B7fM-!Ytv$nWb@UGgzQq~?yv!o_ttcq(M~pvGT5pvMGa63|1SP$t>fOhhWg z+9nW7pqfa9qayp`135Ct>98m8^@3{A8;{6ILtBD?rU|Y=ahSFmhu3&@Zb@w3q83nhICf1%)#BLuA%R#x5?aEN3t9g{;L}xoo{~gCuDP z86tBaV?&lL3T>l3tFVc6(a#m#Ei@jU)Y?u4y^x4UW%zwYh!x-qXT)b)ye$TZ#r13` zhl?{7t+*FIg;|_tuy^YsT$+Gto)9FL5H*wot)Dw=>HpRm=yU7MkV)P-_0S0i;N-{m zIf1U&8+sjBZSb@vG=7)%`2n&pMe=0LQ*!G-s(mwL0;U&N-Ur|dp7uav&!nxl(hkfz zgIz6N1sJKnFVO%Rt9*$9bv=B-EM9-xpEX2_WiSW3RByfjPP^(bfwp_+S7x}rbXHOW zEa-pM^ZIgkOy54V8kFg`?8}lstSW z4v1OSDeRmx6mA-elPmAU(#b2}^j^p&Ks>ZK7buo=#5D=gG-w?2N2ZCarX7ha4wc~V z-xonnQfCOD=AtzS(DA&ZsX$O~S3Jyq`s~e(=OmnRA_rP6$@rGuuFb%X)h-MRfRBFG zwVgx7!ga>T#O)s&mCGG}nt0|@=$V;!07+n88xOrAAv$iB0fKqjuQ#3?ATI6kp8YeX z-)Nll=*lX3AYhoyO-Do}5g&5#F^HyR#^cN6i&gJ3 zYKf*zjK{B#FB&-DsK`5yknAkJLAiA>9b^>ioZ~hqQ}EiG)PbL&=YGf#gQ;4$#6zd5 z`q%l0yY3kQ)_*)7sV=lXCxd8C|5Nq=HAlmnF_N*J_gm%GA2UQ5E2YVHoIzd*w@QSy zafwogm+^j5v;Ld#jZ`7}UfX8TGD*?$BO@z)b(;m%;g$PJDR!qUp1fW=QXI)`f4O(w zRoAJ8_u7})(SurR(Ou*d>It<6D005b1$KOVL?HE5+-3;6w7tApF literal 0 HcmV?d00001 diff --git a/public/javascripts/client_stat.js b/public/javascripts/client_stat.js new file mode 100644 index 0000000..f078798 --- /dev/null +++ b/public/javascripts/client_stat.js @@ -0,0 +1,436 @@ +(function($){ + function pad2(number) { + return (number < 10 ? '0' : '') + number + } + function fixedTo (number, n) { + var k = Math.pow(10, n+1); + return (Math.round(number * k) / k); + } + + function formatSeconds (seconds){ + seconds = Number(seconds); + + var h = Math.floor(seconds / 3600) || 0; + var m = Math.floor(seconds % 3600 / 60) || 0; + var s = Math.floor(seconds % 3600 % 60) || 0; + + return ((m > 0 ? (h > 0 && m < 10 ? "00" : "") + pad2(m) + ":" : "00:") + (s < 10 ? "0" : "") + s); + } + + var Stats = { + inbound_calls : 0, + failed_calls : 0, + offered_calls : 0, + abandoned_calls : 0, + basic_route : '/stats/clients/', + abandoned_sla : 0, + abandoned_perc : 0, + abandoned_sla_perc : 0, + answered_calls : 0, + answered_perc : 0, + answered_sla : 0, + answered_sla_perc : 0, + average_response_time : 0, + colors : Highcharts.getOptions().colors, + categories : ['Abandoned','Answered'], + per_hour : [], + chart : undefined, + + init : function(config){ + this.config = config; + + // Global var! + if (real_time){ + this.connectSocket(); + } + else { + this.receiveData(loaded_stats); + } + this.bindEvents(); + }, + connectSocket : function(){ + this.socket = io.connect(this.config.socket_address); + }, + bindEvents : function(){ + var self = this; + + if (self.socket) { + self.socket.on('clientStatus',self.receiveData); + window.onbeforeunload = function() { + return "Refreshing is unnecesary since data is fetched in real time."; + } + } + self.config.$open_tab.on('click',self.preventEvent); + self.config.$tab.toggle(self.showTab,self.hideTab); + + self.config. $from.datepicker({ + changeMonth: true, + maxDate : 0, + onSelect: function( selectedDate ) { + self.config.$to.datepicker( "option", "minDate", selectedDate ); + } + }); + self.config.$to.datepicker({ + defaultDate: "+1w", + maxDate : 0, + changeMonth: true, + onSelect: function( selectedDate ) { + self.config.$from.datepicker( "option", "maxDate", selectedDate ); + } + }); + + self.config.$datetime.on('click',self.seeStatsForDate); + self.config.$realtime.on('click', self.seeRealtime); + + }, + seeRealtime : function(e){ + Stats.preventEvent(e); + location.href = Stats.basic_route + window.client_name; + }, + seeStatsForDate : function(e){ + Stats.preventEvent(e); + var from = Stats.config.$from.datepicker("getDate"), + to = Stats.config.$to.datepicker("getDate"), + dates = []; + + if (from === null){ + alert('Plase, select the dates correctly in order to get stats from those dates'); + } + else { + if (from) { dates.push(Stats.ISODateString(from)); } + if (to) { dates.push(Stats.ISODateString(to)); } + + location.href = Stats.basic_route + window.client_name + '/' + dates.join('/to/'); + } + }, + preventEvent : function(e){ + e.preventDefault(); + }, + showTab : function(){ + Stats.config.$tab + .stop() + .animate({ + right: "400px" + },500, function(){ + Stats.config.$inner_tab.addClass('expanded'); + }); + Stats.config.$panel + .stop() + .animate({ + width: "400px", + opacity: 0.8 + }, 500, function(){ + Stats.config.$content.fadeIn('slow'); + }); + }, + hideTab : function() { + Stats.config.$content.fadeOut('slow', function() { + Stats.config.$tab + .stop() + .animate({ + right: "0" + },500, function(){ + Stats.config.$inner_tab.removeClass(); + }); + Stats.config.$panel + .stop() + .animate({ + width: "0", + opacity: 0.1 + }, 500); + }); + }, + receiveData : function (data){ + Stats.storeData(data); + Stats.updateWeb(); + Stats.tableHourUpdate(); + if (Stats.chart === undefined){ + Stats.chartInit(); + } + else{ + Stats.chartUpdate(); + } + }, + getChartData : function(){ + var data = [{ + y: parseFloat(Stats.abandoned_perc,10), + color: Stats.colors[1], + drilldown: { + name: 'Abandoned', + categories: ['Abandoned in SLA', 'Abandoned out of SLA'], + data: [ + parseFloat(parseFloat(Stats.abandoned_sla_perc,10).toFixed(1),10), + parseFloat(parseFloat((100 - Stats.abandoned_sla_perc),10).toFixed(1),10) + ], + color: Stats.colors[0] + } + }, { + y: parseFloat(Stats.answered_perc,10), + color: Stats.colors[2], + drilldown: { + name: 'Answered', + categories: ['Answered in SLA', 'Answered out of SLA'], + data: [ + parseFloat(parseFloat(Stats.answered_sla_perc,10).toFixed(1),10), + parseFloat(parseFloat((100 - Stats.answered_sla_perc),10).toFixed(1),10) + ], + color: Stats.colors[2] + } + }]; + + var total_data = []; + var sla_data = []; + for (var i = 0; i < data.length; i++) { + total_data.push({ + name: Stats.categories[i], + y: data[i].y, + color: data[i].color + }); + for (var j = 0; j < data[i].drilldown.data.length; j++) { + var brightness = 0.2 - (j / data[i].drilldown.data.length) / 5 ; + sla_data.push({ + name: data[i].drilldown.categories[j], + y: data[i].drilldown.data[j], + color: Highcharts.Color(data[i].color).brighten(brightness).get() + }); + } + } + return { + total : total_data, + sla : sla_data + }; + }, + chartInit : function(){ + var data = Stats.getChartData(); + + Stats.chart = chart = new Highcharts.Chart({ + chart: { + renderTo: 'graph-calls', + backgroundColor : '#707275', + type: 'pie' + }, + title: { + text: '' + }, + yAxis: { + title: { + text: '' + } + }, + plotOptions: { + pie: { + shadow: false + } + }, + tooltip: { + formatter: function() { + return ''+ this.point.name +': '+ this.y +' %'; + } + }, + series: [{ + name: 'Total', + data: data.total, + size: '60%', + dataLabels: { + formatter: function() { + return this.y > 5 ? this.point.name : null; + }, + color: 'white', + distance: -30 + } + }, { + name: 'Sla', + data: data.sla, + innerSize: '60%', + dataLabels: { + formatter: function() { + // display only if larger than 1 + return this.y > 1 ? ''+ this.point.name +': '+ this.y +'%' : null; + }, + color: 'white' + } + }] + }); + }, + chartUpdate : function(){ + var data = Stats.getChartData(); + + Stats.chart.series[0].setData(data.total,true); + Stats.chart.series[1].setData(data.sla,true); + }, + tableHourUpdate : function(){ + var html = ''; + for (var i = 0, length = this.per_hour.length; i < length; i++){ + var service_level = this.per_hour[i].service_level === 'NaN' ? '-' : this.per_hour[i].service_level; + html += '' + + ''+ this.per_hour[i].hour_range +'' + + ''+ this.per_hour[i].answered +'' + + ''+ this.per_hour[i].abandoned +'' + + ''+ service_level +' %' + + ''+ this.per_hour[i].abandon_rate +' %' + + ''+ this.per_hour[i].answered_time +'' + + ''+ this.per_hour[i].abandoned_time +'' + + ''; + } + Stats.config.$stats_table.html(html); + }, + storeData : function(data){ + this.inbound_calls = data.total_calls; + this.failed_calls = data.failed_calls; + this.offered_calls = data.total_offered_calls; + this.abandoned_calls = data.total_abandoned; + this.answered_calls = data.total_answered; + this.abandoned_sla = data.abandoned_after_SLA; + this.answered_sla = data.answered_before_SLA; + this.average_response_time = data.average_response_time; + this.abandoned_sla_perc = Stats.calculatePercent('abandoned'); + this.answered_sla_perc = Stats.calculatePercent('answered'); + this.parseHourStats(data.per_hour); + }, + parseHourStats : function (per_hour){ + this.per_hour = []; + + if (per_hour) { + for (var i = 0, len = 24; i < len; i++){ + if (per_hour[i]){ + var hour_range = [[pad2(i),'00'].join(':'), [pad2(i+1),'00'].join(':')].join(' - '), + total_calls = per_hour[i].abandoned + per_hour[i].answered, + abandoned_time = formatSeconds(per_hour[i].abandoned_time / per_hour[i].abandoned), + answered_time = formatSeconds(per_hour[i].answered_time / per_hour[i].answered); + + this.per_hour.push({ + hour_range : hour_range, + abandoned : per_hour[i].abandoned, + abandoned_time : abandoned_time, + answered : per_hour[i].answered, + answered_time : answered_time, + service_level : ((per_hour[i].answered_sla * 100) / per_hour[i].answered).toFixed(1), + abandon_rate : ((per_hour[i].abandoned * 100) / total_calls).toFixed(1) + }); + } + } + } + }, + updateWeb : function() { + Stats.config.sla_abandoned.text(parseFloat(Stats.abandoned_sla_perc,10).toFixed(1) + ' %'); + Stats.config.sla_abandoned.removeClass(); + Stats.config.sla_abandoned.addClass('number ' + + Stats.determineColor(Stats.abandoned_sla_perc, Stats.config.perc_abandoned, true) + ); + + Stats.config.sla_answered.text(parseFloat(Stats.answered_sla_perc,10).toFixed(1) + ' %'); + Stats.config.sla_answered.removeClass(); + Stats.config.sla_answered.addClass('number ' + + Stats.determineColor(Stats.answered_sla_perc, Stats.config.perc_answered) + ); + + Stats.config.average_response_time_row + .text(Stats.average_response_time || '0') + .removeClass() + .addClass('number ' + Stats.determineColor(Stats.average_response_time, Stats.config.sec_answered, true)); + + Stats.config.inbound_calls_row.text(Stats.inbound_calls); + Stats.config.failed_calls_row.text(Stats.failed_calls); + Stats.config.kpi_abandoned_row.text(Stats.abandoned_perc || '0' + ' %'); + + Stats.config.offered_calls_row.text(Stats.offered_calls); + + Stats.config.abandoned_calls_row.text(Stats.abandoned_calls); + Stats.config.answered_calls_row.text(Stats.answered_calls); + Stats.config.abandoned_sla_row.text(Stats.abandoned_sla); + Stats.config.answered_sla_row.text(Stats.answered_sla); + }, + logData : function(data){ + console.log(data); + }, + ISODateString : function (d) { + function pad(n){ + return n < 10 ? '0'+ n : n + } + return d.getUTCFullYear()+'-' + + pad(d.getMonth()+1)+'-' + + pad(d.getDate())+'T' + + pad(d.getHours())+':' + + pad(d.getMinutes())+':' + + pad(d.getSeconds())+'Z' + }, + calculatePercent : function(which) { + var total, amount; + + if (which === 'abandoned'){ + total = Stats.offered_calls; + amount = Stats[which + '_sla']; + Stats[which + '_perc'] = ((Stats.abandoned_calls * 100) / Stats.offered_calls).toFixed(1); + } + else { + total = Stats[which + '_calls']; + amount = Stats[which + '_sla']; + Stats[which + '_perc'] = ((total * 100) / Stats.offered_calls).toFixed(1); + } + + + var result = (amount * 100) / total; + return isNaN(result) ? 100 : result.toFixed(1); + }, + determineColor : function (amount, target, lesser) { + var color = ''; + + if (lesser){ + if (amount > target) { + color = 'red' + } + else { + if (amount >= (target -2)){ + color = 'orange'; + } + else { + color = 'green'; + } + } + } + else { + if (amount < target) { + color = 'red' + } + else { + if (amount <= (target -2)){ + color = 'orange'; + } + else { + color = 'green'; + } + } + } + + return color; + } + }; + + Stats.init({ + socket_address : 'http://170.251.100.90:8080/' + client_name, + perc_abandoned : parseInt(perc_abandoned, 10), + perc_answered : parseInt(perc_answered, 10), + sec_answered : parseInt(sec_answered,10), + offered_calls_row : $('td#offered-calls'), + inbound_calls_row : $('td#inbound-calls'), + failed_calls_row : $('td#failed-calls'), + abandoned_calls_row : $('td#abandoned-calls'), + abandoned_sla_row : $('td#abandoned-sla'), + answered_calls_row : $('td#answered-calls'), + answered_sla_row : $('td#answered-sla'), + average_response_time_row : $('td#average-response'), + sla_abandoned : $('td#sla-abandoned'), + sla_answered : $('td#sla-answering'), + kpi_abandoned_row : $('td#kpi-abandoned'), + $content : $(".content").hide(), + $open_tab : $('a#open-tab'), + $tab : $('#tab'), + $inner_tab : $('#inner_tab'), + $panel : $('#panel'), + $from : $('input#from'), + $to : $('input#to'), + $datetime : $('button#datetime'), + $realtime : $('button#realtime'), + $stats_table : $('table#stats-table').find('tbody') + }); +})(jQuery); \ No newline at end of file diff --git a/public/javascripts/foundation.js b/public/javascripts/foundation.js new file mode 100644 index 0000000..3e026e3 --- /dev/null +++ b/public/javascripts/foundation.js @@ -0,0 +1,13 @@ +/* Foundation v2.1.4 http://foundation.zurb.com */ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement){cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close()}d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border"){for(;g=0===c})}function S(a){return !a||!a.parentNode||a.parentNode.nodeType===11}function K(){return !0}function J(){return !1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b])){continue}if(b!=="toJSON"){return !1}}return !0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else{d=b}}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a){return this}if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2]){return f.find(a)}this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return !d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a)){return f.ready(a)}a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0){return}A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete"){return setTimeout(e.ready,1)}if(c.addEventListener){c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1)}else{if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval" in a},isNumeric:function(a){return !isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a)){return !1}try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf")){return !1}}catch(c){return !1}var d;for(d in a){}return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a){return !1}return !0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b){return null}b=e.trim(b);if(a.JSON&&a.JSON.parse){return a.JSON.parse(b)}if(n.test(b.replace(o,"@").replace(p,"]").replace(q,""))){return(new Function("return "+b))()}e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a){if(c.apply(a[f],d)===!1){break}}}else{for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k){for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e){return{}}g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent){for(o in {submit:1,change:1,focusin:1}){n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p}}k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="

",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return !!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b){return}n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function"){e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c)}g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c]){return g.events}k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k]){return}if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1){return !0}}return !1},val:function(a){var c,d,e,g=this[0];if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set" in c)||c.set(this,h,"value")===b){this.value=h}}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get" in c&&(d=c.get(g,"value"))!==b){return d}d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return !b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0){return null}c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn){return f(a)[c](d)}if(typeof a.getAttribute=="undefined"){return f.prop(a,c,d)}i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set" in h&&i&&(g=h.set(a,d,c))!==b){return g}a.setAttribute(c,""+d);return d}if(h&&"get" in h&&i&&(g=h.get(a,c))!==null){return g}g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h]){return}c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j){j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0)}return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1){return}r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode){r.push([m,s]),n=m}n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9){return[]}if(!b||typeof b!="string"){return e}var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b)){if(w.length===2&&o.relative[w[0]]){j=y(w[0]+w[1],d,f)}else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length){b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}}}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length){q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}}else{k=w=[]}}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]"){if(!u){e.push.apply(e,k)}else{if(d&&d.nodeType===1){for(t=0;k[t]!=null;t++){k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t])}}else{for(t=0;k[t]!=null;t++){k[t]&&k[t].nodeType===1&&e.push(j[t])}}}}else{s(k,e)}l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h){for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a){return[]}for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1))}return !1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else{a[2]&&m.error(a[0])}a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not"){if((a.exec(b[3])||"").length>1||/^\w/.test(b[3])){b[3]=m(b[3],null,null,c)}else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return !1}}else{if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0])){return !0}}return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return !!a.firstChild},empty:function(a){return !a.firstChild},has:function(a,b,c){return !!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f){return f(a,c,b,d)}if(e==="contains"){return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0}if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f){return f(a,c,b,d)}}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match){o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q))}var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]"){Array.prototype.push.apply(d,a)}else{if(typeof a.length=="number"){for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++){c[e].nodeType===1&&d.push(c[e])}c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1]){return s(e.getElementsByTagName(b),f)}if(h[2]&&o.find.CLASS&&e.getElementsByClassName){return s(e.getElementsByClassName(h[2]),f)}}if(e.nodeType===9){if(b==="body"&&e.body){return s([e.body],f)}if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode){return s([],f)}if(i.id===h[3]){return s([i],f)}}try{return s(e.querySelectorAll(b),f)}catch(j){}}else{if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p){return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}}catch(r){}finally{l||k.removeAttribute("id")}}}}return a(b,e,f,g)};for(var e in a){m[e]=a[e]}b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a)){try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11){return f}}}catch(g){}}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1){return}o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c){return b.getElementsByClassName(a[1])}},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return !!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return !1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a)){f+=d[0],a=a.replace(o.match.PSEUDO,"")}a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0){for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11){break}}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a){return this[0]&&this[0].parentNode?this.prevAll().length:-1}if(typeof a=="string"){return f.inArray(this[0],f(a))}return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d))){g.nodeType===1&&e.push(g),g=g[c]}return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c]){if(a.nodeType===1&&++e===b){break}}return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling){a.nodeType===1&&a!==b&&c.push(a)}return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a)){return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))})}if(typeof a!="object"&&a!==b){return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))}return f.text(this)},wrapAll:function(a){if(f.isFunction(a)){return this.each(function(b){f(this).wrapAll(a.call(this,b))})}if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1){a=a.firstChild}return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a)){return this.each(function(b){f(this).wrapInner(a.call(this,b))})}return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)})}if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)})}if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++){if(!a||f.filter(a,[d]).length){!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d)}}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild){b.removeChild(b.firstChild)}}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b){return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null}if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g){e[g]&&bk(d[g],e[g])}}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g){bj(d[g],e[g])}}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k){continue}if(typeof k=="string"){if(!_.test(k)){k=b.createTextNode(k)}else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--){o=o.lastChild}if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i){f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}}var r;if(!f.support.appendChecked){if(k[0]&&typeof(r=k.length)=="number"){for(i=0;i=0){return b+"px"}}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter){return}}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return !f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS){return bS.apply(this,arguments)}if(!this.length){return this}var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified")){f.lastModified[k]=y}if(z=v.getResponseHeader("Etag")){f.etag[k]=z}}if(a===304){w="notmodified",o=!0}else{try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}}else{u=w;if(!w||a){w="error",a<0&&(a=0)}}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n)){o[c[1].toLowerCase()]=c[2]}}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2){for(b in a){j[b]=[j[b],a[b]]}}else{b=a[v.status],v.then(b,b)}}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2){return !1}t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers){v.setRequestHeader(u,d.headers[u])}if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return !1}for(u in {success:1,error:1,complete:1}){v[u](d[u])}p=b$(bU,d,c,v);if(!p){w(-1,"No Transport")}else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2){w(-1,z)}else{throw z}}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a)){f.each(a,function(){e(this.name,this.value)})}else{for(var g in a){ca(g,a[g],c,e)}}return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState)){d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")}},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch){ch[a](0,1)}}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return !this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials" in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields){for(j in c.xhrFields){h[j]=c.xhrFields[j]}}c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e){h.setRequestHeader(j,e[j])}}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e){h.readyState!==4&&h.abort()}else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0){return this.animate(cu("show",3),a,b,c)}for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties){i.animatedProperties[b]!==!0&&(g=!1)}if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show){for(b in i.animatedProperties){f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0)}}d=i.complete,d&&(i.complete=!1,d.call(h))}return !1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return !0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using" in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0]){return null}var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static"){a=a.offsetParent}return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e){return null}g=cy(e);return g?"pageXOffset" in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e){return a==null?null:this}if(f.isFunction(a)){return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))})}if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9){return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c])}if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);(function(a){a("a[data-reveal-id]").live("click",function(c){c.preventDefault();var b=a(this).attr("data-reveal-id");a("#"+b).reveal(a(this).data())});a.fn.reveal=function(b){var c={animation:"fadeAndPop",animationSpeed:300,closeOnBackgroundClick:true,dismissModalClass:"close-reveal-modal"};var b=a.extend({},c,b);return this.each(function(){var l=a(this),g=parseInt(l.css("top")),i=l.height()+g,h=false,e=a(".reveal-modal-bg");if(e.length==0){e=a('
').insertAfter(l);e.fadeTo("fast",0.8)}function k(){e.unbind("click.modalEvent");a("."+b.dismissModalClass).unbind("click.modalEvent");if(!h){m();if(b.animation=="fadeAndPop"){l.css({top:a(document).scrollTop()-i,opacity:0,visibility:"visible"});e.fadeIn(b.animationSpeed/2);l.delay(b.animationSpeed/2).animate({top:a(document).scrollTop()+g+"px",opacity:1},b.animationSpeed,j)}if(b.animation=="fade"){l.css({opacity:0,visibility:"visible",top:a(document).scrollTop()+g});e.fadeIn(b.animationSpeed/2);l.delay(b.animationSpeed/2).animate({opacity:1},b.animationSpeed,j)}if(b.animation=="none"){l.css({visibility:"visible",top:a(document).scrollTop()+g});e.css({display:"block"});j()}}l.unbind("reveal:open",k)}l.bind("reveal:open",k);function f(){if(!h){m();if(b.animation=="fadeAndPop"){e.delay(b.animationSpeed).fadeOut(b.animationSpeed);l.animate({top:a(document).scrollTop()-i+"px",opacity:0},b.animationSpeed/2,function(){l.css({top:g,opacity:1,visibility:"hidden"});j()})}if(b.animation=="fade"){e.delay(b.animationSpeed).fadeOut(b.animationSpeed);l.animate({opacity:0},b.animationSpeed,function(){l.css({opacity:1,visibility:"hidden",top:g});j()})}if(b.animation=="none"){l.css({visibility:"hidden",top:g});e.css({display:"none"})}}l.unbind("reveal:close",f)}l.bind("reveal:close",f);l.trigger("reveal:open");var d=a("."+b.dismissModalClass).bind("click.modalEvent",function(){l.trigger("reveal:close")});if(b.closeOnBackgroundClick){e.css({cursor:"pointer"});e.bind("click.modalEvent",function(){l.trigger("reveal:close")})}a("body").keyup(function(n){if(n.which===27){l.trigger("reveal:close")}});function j(){h=false}function m(){h=true}})}})(jQuery);(function(b){var a={defaults:{animation:"horizontal-push",animationSpeed:600,timer:true,advanceSpeed:4000,pauseOnHover:false,startClockOnMouseOut:false,startClockOnMouseOutAfter:1000,directionalNav:true,captions:true,captionAnimation:"fade",captionAnimationSpeed:600,bullets:false,bulletThumbs:false,bulletThumbLocation:"",afterSlideChange:b.noop,fluid:true,centerBullets:true},activeSlide:0,numberSlides:0,orbitWidth:null,orbitHeight:null,locked:null,timerRunning:null,degrees:0,wrapperHTML:'
',timerHTML:'
',captionHTML:'
',directionalNavHTML:'
RightLeft
',bulletHTML:'
    ',init:function(f,e){var c,g=0,d=this;this.clickTimer=b.proxy(this.clickTimer,this);this.addBullet=b.proxy(this.addBullet,this);this.resetAndUnlock=b.proxy(this.resetAndUnlock,this);this.stopClock=b.proxy(this.stopClock,this);this.startTimerAfterMouseLeave=b.proxy(this.startTimerAfterMouseLeave,this);this.clearClockMouseLeaveTimer=b.proxy(this.clearClockMouseLeaveTimer,this);this.rotateTimer=b.proxy(this.rotateTimer,this);this.options=b.extend({},this.defaults,e);if(this.options.timer==="false"){this.options.timer=false}if(this.options.captions==="false"){this.options.captions=false}if(this.options.directionalNav==="false"){this.options.directionalNav=false}this.$element=b(f);this.$wrapper=this.$element.wrap(this.wrapperHTML).parent();this.$slides=this.$element.children("img, a, div");this.$element.bind("orbit.next",function(){d.shift("next")});this.$element.bind("orbit.prev",function(){d.shift("prev")});this.$element.bind("orbit.goto",function(i,h){d.shift(h)});this.$element.bind("orbit.start",function(i,h){d.startClock()});this.$element.bind("orbit.stop",function(i,h){d.stopClock()});c=this.$slides.filter("img");if(c.length===0){this.loaded()}else{c.bind("imageready",function(){g+=1;if(g===c.length){d.loaded()}})}},loaded:function(){this.$element.addClass("orbit").css({width:"1px",height:"1px"});this.setDimentionsFromLargestSlide();this.updateOptionsIfOnlyOneSlide();this.setupFirstSlide();if(this.options.timer){this.setupTimer();this.startClock()}if(this.options.captions){this.setupCaptions()}if(this.options.directionalNav){this.setupDirectionalNav()}if(this.options.bullets){this.setupBulletNav();this.setActiveBullet()}},currentSlide:function(){return this.$slides.eq(this.activeSlide)},setDimentionsFromLargestSlide:function(){var d=this,c;d.$element.add(d.$wrapper).width(this.$slides.first().width());d.$element.add(d.$wrapper).height(this.$slides.first().height());d.orbitWidth=this.$slides.first().width();d.orbitHeight=this.$slides.first().height();c=this.$slides.first().clone();this.$slides.each(function(){var e=b(this),g=e.width(),f=e.height();if(g>d.$element.width()){d.$element.add(d.$wrapper).width(g);d.orbitWidth=d.$element.width()}if(f>d.$element.height()){d.$element.add(d.$wrapper).height(f);d.orbitHeight=d.$element.height();c=b(this).clone()}d.numberSlides+=1});if(this.options.fluid){if(typeof this.options.fluid==="string"){c=b('')}d.$element.prepend(c);c.addClass("fluid-placeholder");d.$element.add(d.$wrapper).css({width:"inherit"});d.$element.add(d.$wrapper).css({height:"inherit"});b(window).bind("resize",function(){d.orbitWidth=d.$element.width();d.orbitHeight=d.$element.height()})}},lock:function(){this.locked=true},unlock:function(){this.locked=false},updateOptionsIfOnlyOneSlide:function(){if(this.$slides.length===1){this.options.directionalNav=false;this.options.timer=false;this.options.bullets=false}},setupFirstSlide:function(){var c=this;this.$slides.first().css({"z-index":3}).fadeIn(function(){c.$slides.css({display:"block"})})},startClock:function(){var c=this;if(!this.options.timer){return false}if(this.$timer.is(":hidden")){this.clock=setInterval(function(){this.$element.trigger("orbit.next")},this.options.advanceSpeed)}else{this.timerRunning=true;this.$pause.removeClass("active");this.clock=setInterval(this.rotateTimer,this.options.advanceSpeed/180)}},rotateTimer:function(){var c="rotate("+this.degrees+"deg)";this.degrees+=2;this.$rotator.css({"-webkit-transform":c,"-moz-transform":c,"-o-transform":c});if(this.degrees>180){this.$rotator.addClass("move");this.$mask.addClass("move")}if(this.degrees>360){this.$rotator.removeClass("move");this.$mask.removeClass("move");this.degrees=0;this.$element.trigger("orbit.next")}},stopClock:function(){if(!this.options.timer){return false}else{this.timerRunning=false;clearInterval(this.clock);this.$pause.addClass("active")}},setupTimer:function(){this.$timer=b(this.timerHTML);this.$wrapper.append(this.$timer);this.$rotator=this.$timer.find(".rotator");this.$mask=this.$timer.find(".mask");this.$pause=this.$timer.find(".pause");this.$timer.click(this.clickTimer);if(this.options.startClockOnMouseOut){this.$wrapper.mouseleave(this.startTimerAfterMouseLeave);this.$wrapper.mouseenter(this.clearClockMouseLeaveTimer)}if(this.options.pauseOnHover){this.$wrapper.mouseenter(this.stopClock)}},startTimerAfterMouseLeave:function(){var c=this;this.outTimer=setTimeout(function(){if(!c.timerRunning){c.startClock()}},this.options.startClockOnMouseOutAfter)},clearClockMouseLeaveTimer:function(){clearTimeout(this.outTimer)},clickTimer:function(){if(!this.timerRunning){this.startClock()}else{this.stopClock()}},setupCaptions:function(){this.$caption=b(this.captionHTML);this.$wrapper.append(this.$caption);this.setCaption()},setCaption:function(){var d=this.currentSlide().attr("data-caption"),c;if(!this.options.captions){return false}if(d){c=b(d).html();this.$caption.attr("id",d).html(c);switch(this.options.captionAnimation){case"none":this.$caption.show();break;case"fade":this.$caption.fadeIn(this.options.captionAnimationSpeed);break;case"slideOpen":this.$caption.slideDown(this.options.captionAnimationSpeed);break}}else{switch(this.options.captionAnimation){case"none":this.$caption.hide();break;case"fade":this.$caption.fadeOut(this.options.captionAnimationSpeed);break;case"slideOpen":this.$caption.slideUp(this.options.captionAnimationSpeed);break}}},setupDirectionalNav:function(){var c=this;this.$wrapper.append(this.directionalNavHTML);this.$wrapper.find(".left").click(function(){c.stopClock();c.$element.trigger("orbit.prev")});this.$wrapper.find(".right").click(function(){c.stopClock();c.$element.trigger("orbit.next")})},setupBulletNav:function(){this.$bullets=b(this.bulletHTML);this.$wrapper.append(this.$bullets);this.$slides.each(this.addBullet);this.$element.addClass("with-bullets");if(this.options.centerBullets){this.$bullets.css("margin-left",-this.$bullets.width()/2)}},addBullet:function(g,e){var d=g+1,h=b("
  • "+(d)+"
  • "),c,f=this;if(this.options.bulletThumbs){c=b(e).attr("data-thumb");if(c){h.addClass("has-thumb").css({background:"url("+this.options.bulletThumbLocation+c+") no-repeat"})}}this.$bullets.append(h);h.data("index",g);h.click(function(){f.stopClock();f.$element.trigger("orbit.goto",[h.data("index")])})},setActiveBullet:function(){if(!this.options.bullets){return false}else{this.$bullets.find("li").removeClass("active").eq(this.activeSlide).addClass("active")}},resetAndUnlock:function(){this.$slides.eq(this.prevActiveSlide).css({"z-index":1});this.unlock();this.options.afterSlideChange.call(this,this.$slides.eq(this.prevActiveSlide),this.$slides.eq(this.activeSlide))},shift:function(d){var c=d;this.prevActiveSlide=this.activeSlide;if(this.prevActiveSlide==c){return false}if(this.$slides.length=="1"){return false}if(!this.locked){this.lock();if(d=="next"){this.activeSlide++;if(this.activeSlide==this.numberSlides){this.activeSlide=0}}else{if(d=="prev"){this.activeSlide--;if(this.activeSlide<0){this.activeSlide=this.numberSlides-1}}else{this.activeSlide=d;if(this.prevActiveSlidethis.activeSlide){c="prev"}}}}this.setActiveBullet();this.$slides.eq(this.prevActiveSlide).css({"z-index":2});if(this.options.animation=="fade"){this.$slides.eq(this.activeSlide).css({opacity:0,"z-index":3}).animate({opacity:1},this.options.animationSpeed,this.resetAndUnlock)}if(this.options.animation=="horizontal-slide"){if(c=="next"){this.$slides.eq(this.activeSlide).css({left:this.orbitWidth,"z-index":3}).animate({left:0},this.options.animationSpeed,this.resetAndUnlock)}if(c=="prev"){this.$slides.eq(this.activeSlide).css({left:-this.orbitWidth,"z-index":3}).animate({left:0},this.options.animationSpeed,this.resetAndUnlock)}}if(this.options.animation=="vertical-slide"){if(c=="prev"){this.$slides.eq(this.activeSlide).css({top:this.orbitHeight,"z-index":3}).animate({top:0},this.options.animationSpeed,this.resetAndUnlock)}if(c=="next"){this.$slides.eq(this.activeSlide).css({top:-this.orbitHeight,"z-index":3}).animate({top:0},this.options.animationSpeed,this.resetAndUnlock)}}if(this.options.animation=="horizontal-push"){if(c=="next"){this.$slides.eq(this.activeSlide).css({left:this.orbitWidth,"z-index":3}).animate({left:0},this.options.animationSpeed,this.resetAndUnlock);this.$slides.eq(this.prevActiveSlide).animate({left:-this.orbitWidth},this.options.animationSpeed)}if(c=="prev"){this.$slides.eq(this.activeSlide).css({left:-this.orbitWidth,"z-index":3}).animate({left:0},this.options.animationSpeed,this.resetAndUnlock);this.$slides.eq(this.prevActiveSlide).animate({left:this.orbitWidth},this.options.animationSpeed)}}if(this.options.animation=="vertical-push"){if(c=="next"){this.$slides.eq(this.activeSlide).css({top:-this.orbitHeight,"z-index":3}).animate({top:0},this.options.animationSpeed,this.resetAndUnlock);this.$slides.eq(this.prevActiveSlide).animate({top:this.orbitHeight},this.options.animationSpeed)}if(c=="prev"){this.$slides.eq(this.activeSlide).css({top:this.orbitHeight,"z-index":3}).animate({top:0},this.options.animationSpeed,this.resetAndUnlock);this.$slides.eq(this.prevActiveSlide).animate({top:-this.orbitHeight},this.options.animationSpeed)}}this.setCaption()}}};b.fn.orbit=function(c){return this.each(function(){var d=b.extend({},a);d.init(this,c)})}})(jQuery); +/*! + * jQuery imageready Plugin + * http://www.zurb.com/playground/ + * + * Copyright 2011, ZURB + * Released under the MIT License + */ +(function(c){var b={};c.event.special.imageready={setup:function(f,e,d){b=f||b},add:function(d){var e=c(this),f;if(this.nodeType===1&&this.tagName.toLowerCase()==="img"&&this.src!==""){if(b.forceLoad){f=e.attr("src");e.attr("src","");a(this,d.handler);e.attr("src",f)}else{if(this.complete||this.readyState===4){d.handler.apply(this,arguments)}else{a(this,d.handler)}}}},teardown:function(d){c(this).unbind(".imageready")}};function a(d,f){var e=c(d);e.bind("load.imageready",function(){f.apply(d,arguments);e.unbind("load.imageready")})}}(jQuery));new function(a){a.fn.placeholder=function(b){b=b||{};var j=b.dataKey||"placeholderValue";var f=b.attr||"placeholder";var h=b.className||"placeholder";var k=b.values||[];var c=b.blockSubmit||false;var e=b.blankSubmit||false;var g=b.onSubmit||false;var i=b.value||"";var d=b.cursor_position||0;return this.filter(":input").each(function(l){a.data(this,j,k[l]||a(this).attr(f))}).each(function(){if(a.trim(a(this).val())===""){a(this).addClass(h).val(a.data(this,j))}}).focus(function(){if(a.trim(a(this).val())===a.data(this,j)){a(this).removeClass(h).val(i)}if(a.fn.setCursorPosition){a(this).setCursorPosition(d)}}).blur(function(){if(a.trim(a(this).val())===i){a(this).addClass(h).val(a.data(this,j))}}).each(function(l,m){if(c){new function(n){a(n.form).submit(function(){return a.trim(a(n).val())!=a.data(n,j)})}(m)}else{if(e){new function(n){a(n.form).submit(function(){if(a.trim(a(n).val())==a.data(n,j)){a(n).removeClass(h).val("")}return true})}(m)}else{if(g){new function(n){a(n.form).submit(g)}(m)}}}})}}(jQuery);jQuery(document).ready(function(b){function a(c){b("form.custom input:"+c).each(function(){var e=b(this).hide(),d=e.next("span.custom."+c);if(d.length===0){d=b('').insertAfter(e)}d.toggleClass("checked",e.is(":checked"))})}a("checkbox");a("radio");b("form.custom select").each(function(){var e=b(this),g=e.next("div.custom.dropdown"),c=e.find("option"),d=0,f;if(g.length===0){g=b('"');c.each(function(){f=b("
  • "+b(this).html()+"
  • ");g.find("ul").append(f)});g.prepend(''+c.first().html()+"");e.after(g);e.hide()}c.each(function(h){if(this.selected){g.find("li").eq(h).addClass("selected");g.find(".current").html(b(this).html())}});g.find("li").each(function(){g.addClass("open");if(b(this).outerWidth()>d){d=b(this).outerWidth()}g.removeClass("open")});g.css("width",d+18+"px");g.find("ul").css("width",d+16+"px")})});(function(b){function a(d){var f=d.prev(),e=f[0];e.checked=((e.checked)?false:true);d.toggleClass("checked");f.trigger("change")}function c(d){var f=d.prev(),e=f[0];b('input:radio[name="'+f.attr("name")+'"]').each(function(){b(this).next().removeClass("checked")});e.checked=((e.checked)?false:true);d.toggleClass("checked");f.trigger("change")}b(document).on("click","form.custom span.custom.checkbox",function(d){d.preventDefault();d.stopPropagation();a(b(this))});b(document).on("click","form.custom span.custom.radio",function(d){d.preventDefault();d.stopPropagation();c(b(this))});b(document).on("click","form.custom label",function(e){var d=b("#"+b(this).attr("for")),g,f;if(d.length!==0){if(d.attr("type")==="checkbox"){e.preventDefault();g=b(this).find("span.custom.checkbox");a(g)}else{if(d.attr("type")==="radio"){e.preventDefault();f=b(this).find("span.custom.radio");c(f)}}}});b(document).on("click","form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector",function(d){var f=b(this),e=f.closest("div.custom.dropdown");d.preventDefault();e.toggleClass("open");if(e.hasClass("open")){b(document).bind("click.customdropdown",function(g){e.removeClass("open");b(document).unbind(".customdropdown")})}else{b(document).unbind(".customdropdown")}});b(document).on("click","form.custom div.custom.dropdown li",function(g){var h=b(this),e=h.closest("div.custom.dropdown"),f=e.prev(),d=0;g.preventDefault();g.stopPropagation();h.closest("ul").find("li").removeClass("selected");h.addClass("selected");e.removeClass("open").find("a.current").html(h.html());h.closest("ul").find("li").each(function(i){if(h[0]==this){d=i}});f[0].selectedIndex=d;f.trigger("change")})})(jQuery); +/*! http://mths.be/placeholder v1.8.5 by @mathias */ +(function(j,i,l){var k="placeholder" in i.createElement("input"),h="placeholder" in i.createElement("textarea");if(k&&h){l.fn.placeholder=function(){return this};l.fn.placeholder.input=l.fn.placeholder.textarea=true}else{l.fn.placeholder=function(){return this.filter((k?"textarea":":input")+"[placeholder]").bind("focus.placeholder",o).bind("blur.placeholder",m).trigger("blur.placeholder").end()};l.fn.placeholder.input=k;l.fn.placeholder.textarea=h;l(function(){l("form").bind("submit.placeholder",function(){var a=l(".placeholder",this).each(o);setTimeout(function(){a.each(m)},10)})});l(j).bind("unload.placeholder",function(){l(".placeholder").val("")})}function n(b){var c={},a=/^jQuery\d+$/;l.each(b.attributes,function(d,e){if(e.specified&&!a.test(e.name)){c[e.name]=e.value}});return c}function o(){var a=l(this);if(a.val()===a.attr("placeholder")&&a.hasClass("placeholder")){if(a.data("placeholder-password")){a.hide().next().show().focus().attr("id",a.removeAttr("id").data("placeholder-id"))}else{a.val("").removeClass("placeholder")}}}function m(){var a,b=l(this),e=b,c=this.id;if(b.val()===""){if(b.is(":password")){if(!b.data("placeholder-textinput")){try{a=b.clone().attr({type:"text"})}catch(d){a=l("").attr(l.extend(n(this),{type:"text"}))}a.removeAttr("name").data("placeholder-password",true).data("placeholder-id",c).bind("focus.placeholder",o);b.data("placeholder-textinput",a).data("placeholder-id",c).before(a)}b=b.removeAttr("id").hide().prev().attr("id",c).show()}b.addClass("placeholder").val(b.attr("placeholder"))}else{b.removeClass("placeholder")}}}(this,document,jQuery)); diff --git a/public/javascripts/handlebars.js b/public/javascripts/handlebars.js new file mode 100644 index 0000000..48fe215 --- /dev/null +++ b/public/javascripts/handlebars.js @@ -0,0 +1,1550 @@ +// lib/handlebars/base.js +var Handlebars = {}; + +Handlebars.VERSION = "1.0.beta.6"; + +Handlebars.helpers = {}; +Handlebars.partials = {}; + +Handlebars.registerHelper = function(name, fn, inverse) { + if(inverse) { fn.not = inverse; } + this.helpers[name] = fn; +}; + +Handlebars.registerPartial = function(name, str) { + this.partials[name] = str; +}; + +Handlebars.registerHelper('helperMissing', function(arg) { + if(arguments.length === 2) { + return undefined; + } else { + throw new Error("Could not find property '" + arg + "'"); + } +}); + +var toString = Object.prototype.toString, functionType = "[object Function]"; + +Handlebars.registerHelper('blockHelperMissing', function(context, options) { + var inverse = options.inverse || function() {}, fn = options.fn; + + + var ret = ""; + var type = toString.call(context); + + if(type === functionType) { context = context.call(this); } + + if(context === true) { + return fn(this); + } else if(context === false || context == null) { + return inverse(this); + } else if(type === "[object Array]") { + if(context.length > 0) { + for(var i=0, j=context.length; i 0) { + for(var i=0, j=context.length; i 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + var errStr = ""; + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + this.terminals_[symbol] + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; +} +};/* Jison generated lexer */ +var lexer = (function(){ + +var lexer = ({EOF:1, +parseError:function parseError(str, hash) { + if (this.yy.parseError) { + this.yy.parseError(str, hash); + } else { + throw new Error(str); + } + }, +setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + return this; + }, +input:function () { + var ch = this._input[0]; + this.yytext+=ch; + this.yyleng++; + this.match+=ch; + this.matched+=ch; + var lines = ch.match(/\n/); + if (lines) this.yylineno++; + this._input = this._input.slice(1); + return ch; + }, +unput:function (ch) { + this._input = ch + this._input; + return this; + }, +more:function () { + this._more = true; + return this; + }, +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + match = this._input.match(this.rules[rules[i]]); + if (match) { + lines = match[0].match(/\n.*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]); + if (token) return token; + else return; + } + } + if (this._input === "") { + return this.EOF; + } else { + this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, +lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, +begin:function begin(condition) { + this.conditionStack.push(condition); + }, +popState:function popState() { + return this.conditionStack.pop(); + }, +_currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }, +topState:function () { + return this.conditionStack[this.conditionStack.length-2]; + }, +pushState:function begin(condition) { + this.begin(condition); + }}); +lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + +var YYSTATE=YY_START +switch($avoiding_name_collisions) { +case 0: + if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); + if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); + if(yy_.yytext) return 14; + +break; +case 1: return 14; +break; +case 2: this.popState(); return 14; +break; +case 3: return 24; +break; +case 4: return 16; +break; +case 5: return 20; +break; +case 6: return 19; +break; +case 7: return 19; +break; +case 8: return 23; +break; +case 9: return 23; +break; +case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; +break; +case 11: return 22; +break; +case 12: return 34; +break; +case 13: return 33; +break; +case 14: return 33; +break; +case 15: return 36; +break; +case 16: /*ignore whitespace*/ +break; +case 17: this.popState(); return 18; +break; +case 18: this.popState(); return 18; +break; +case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28; +break; +case 20: return 30; +break; +case 21: return 30; +break; +case 22: return 29; +break; +case 23: return 33; +break; +case 24: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33; +break; +case 25: return 'INVALID'; +break; +case 26: return 5; +break; +} +}; +lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^[^\x00]{2,}?(?=(\{\{))/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[\/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s\/.])/,/^\[[^\]]*\]/,/^./,/^$/]; +lexer.conditions = {"mu":{"rules":[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"INITIAL":{"rules":[0,1,26],"inclusive":true}};return lexer;})() +parser.lexer = lexer; +return parser; +})(); +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { +exports.parser = handlebars; +exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); } +exports.main = function commonjsMain(args) { + if (!args[1]) + throw new Error('Usage: '+args[0]+' FILE'); + if (typeof process !== 'undefined') { + var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); + } else { + var cwd = require("file").path(require("file").cwd()); + var source = cwd.join(args[1]).read({charset: "utf-8"}); + } + return exports.parser.parse(source); +} +if (typeof module !== 'undefined' && require.main === module) { + exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); +} +}; +; +// lib/handlebars/compiler/base.js +Handlebars.Parser = handlebars; + +Handlebars.parse = function(string) { + Handlebars.Parser.yy = Handlebars.AST; + return Handlebars.Parser.parse(string); +}; + +Handlebars.print = function(ast) { + return new Handlebars.PrintVisitor().accept(ast); +}; + +Handlebars.logger = { + DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, + + // override in the host environment + log: function(level, str) {} +}; + +Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); }; +; +// lib/handlebars/compiler/ast.js +(function() { + + Handlebars.AST = {}; + + Handlebars.AST.ProgramNode = function(statements, inverse) { + this.type = "program"; + this.statements = statements; + if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } + }; + + Handlebars.AST.MustacheNode = function(params, hash, unescaped) { + this.type = "mustache"; + this.id = params[0]; + this.params = params.slice(1); + this.hash = hash; + this.escaped = !unescaped; + }; + + Handlebars.AST.PartialNode = function(id, context) { + this.type = "partial"; + + // TODO: disallow complex IDs + + this.id = id; + this.context = context; + }; + + var verifyMatch = function(open, close) { + if(open.original !== close.original) { + throw new Handlebars.Exception(open.original + " doesn't match " + close.original); + } + }; + + Handlebars.AST.BlockNode = function(mustache, program, close) { + verifyMatch(mustache.id, close); + this.type = "block"; + this.mustache = mustache; + this.program = program; + }; + + Handlebars.AST.InverseNode = function(mustache, program, close) { + verifyMatch(mustache.id, close); + this.type = "inverse"; + this.mustache = mustache; + this.program = program; + }; + + Handlebars.AST.ContentNode = function(string) { + this.type = "content"; + this.string = string; + }; + + Handlebars.AST.HashNode = function(pairs) { + this.type = "hash"; + this.pairs = pairs; + }; + + Handlebars.AST.IdNode = function(parts) { + this.type = "ID"; + this.original = parts.join("."); + + var dig = [], depth = 0; + + for(var i=0,l=parts.length; i": ">", + '"': """, + "'": "'", + "`": "`" + }; + + var badChars = /&(?!\w+;)|[<>"'`]/g; + var possible = /[&<>"'`]/; + + var escapeChar = function(chr) { + return escape[chr] || "&"; + }; + + Handlebars.Utils = { + escapeExpression: function(string) { + // don't escape SafeStrings, since they're already safe + if (string instanceof Handlebars.SafeString) { + return string.toString(); + } else if (string == null || string === false) { + return ""; + } + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + }, + + isEmpty: function(value) { + if (typeof value === "undefined") { + return true; + } else if (value === null) { + return true; + } else if (value === false) { + return true; + } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) { + return true; + } else { + return false; + } + } + }; +})();; +// lib/handlebars/compiler/compiler.js +Handlebars.Compiler = function() {}; +Handlebars.JavaScriptCompiler = function() {}; + +(function(Compiler, JavaScriptCompiler) { + Compiler.OPCODE_MAP = { + appendContent: 1, + getContext: 2, + lookupWithHelpers: 3, + lookup: 4, + append: 5, + invokeMustache: 6, + appendEscaped: 7, + pushString: 8, + truthyOrFallback: 9, + functionOrFallback: 10, + invokeProgram: 11, + invokePartial: 12, + push: 13, + assignToHash: 15, + pushStringParam: 16 + }; + + Compiler.MULTI_PARAM_OPCODES = { + appendContent: 1, + getContext: 1, + lookupWithHelpers: 2, + lookup: 1, + invokeMustache: 3, + pushString: 1, + truthyOrFallback: 1, + functionOrFallback: 1, + invokeProgram: 3, + invokePartial: 1, + push: 1, + assignToHash: 1, + pushStringParam: 1 + }; + + Compiler.DISASSEMBLE_MAP = {}; + + for(var prop in Compiler.OPCODE_MAP) { + var value = Compiler.OPCODE_MAP[prop]; + Compiler.DISASSEMBLE_MAP[value] = prop; + } + + Compiler.multiParamSize = function(code) { + return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]]; + }; + + Compiler.prototype = { + compiler: Compiler, + + disassemble: function() { + var opcodes = this.opcodes, opcode, nextCode; + var out = [], str, name, value; + + for(var i=0, l=opcodes.length; i 0) { + this.source[1] = this.source[1] + ", " + locals.join(", "); + } + + // Generate minimizer alias mappings + if (!this.isChild) { + var aliases = [] + for (var alias in this.context.aliases) { + this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; + } + } + + if (this.source[1]) { + this.source[1] = "var " + this.source[1].substring(2) + ";"; + } + + // Merge children + if (!this.isChild) { + this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; + } + + if (!this.environment.isSimple) { + this.source.push("return buffer;"); + } + + var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; + + for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } + return "stack" + this.stackSlot; + }, + + popStack: function() { + return "stack" + this.stackSlot--; + }, + + topStack: function() { + return "stack" + this.stackSlot; + }, + + quotedString: function(str) { + return '"' + str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + '"'; + } + }; + + var reservedWords = ( + "break else new var" + + " case finally return void" + + " catch for switch while" + + " continue function this with" + + " default if throw" + + " delete in try" + + " do instanceof typeof" + + " abstract enum int short" + + " boolean export interface static" + + " byte extends long super" + + " char final native synchronized" + + " class float package throws" + + " const goto private transient" + + " debugger implements protected volatile" + + " double import public let yield" + ).split(" "); + + var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + + for(var i=0, l=reservedWords.length; i-1?b.split(".")[1].length:0):a=isNaN(b=N(b))?2:b;var b=a,c=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=f<0?"-":"",a=String(B(f=N(+f||0).toFixed(b))),g=a.length>3?a.length%3:0;return e+(g?a.substr(0,g)+d:"")+a.substr(g).replace(/(\d{3})(?=\d)/g, +"$1"+d)+(b?c+N(f-a).toFixed(b).slice(2):"")}function ta(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function fb(a,b,c,d){var e,c=p(c,1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d=F[gb]&&(i.setMilliseconds(0),i.setSeconds(b>=F[Ta]?0:j*V(i.getSeconds()/j)));if(b>=F[Ta])i[zb](b>=F[Ia]?0:j*V(i[hb]()/j));if(b>=F[Ia])i[Ab](b>=F[qa]?0:j*V(i[ib]()/ +j));if(b>=F[qa])i[jb](b>=F[Ja]?1:j*V(i[Ka]()/j));b>=F[Ja]&&(i[Bb](b>=F[ua]?0:j*V(i[Va]()/j)),h=i[Wa]());b>=F[ua]&&(h-=h%j,i[Cb](h));if(b===F[Ua])i[jb](i[Ka]()-i[kb]()+p(d,1));d=1;h=i[Wa]();for(var k=i.getTime(),l=i[Va](),m=i[Ka](),i=g?0:(864E5+i.getTimezoneOffset()*6E4)%864E5;kc&&(c=a[b]);return c}function Ba(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Ma(a){Ya||(Ya=S(ja));a&&Ya.appendChild(a);Ya.innerHTML=""}function Za(a, +b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else M.console&&console.log(c)}function ka(a){return parseFloat(a.toPrecision(14))}function va(a,b){Na=p(a,b.animation)}function Eb(){var a=O.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";Xa=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,p(c,1),p(g,0),p(h,0),p(i,0))).getTime()};hb=b+"Minutes";ib=b+"Hours";kb=b+"Day";Ka=b+"Date";Va=b+"Month";Wa=b+"FullYear";zb=c+"Minutes";Ab=c+"Hours";jb=c+"Date";Bb=c+"Month"; +Cb=c+"FullYear"}function wa(){}function Oa(a,b,c){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;c||this.addLabel()}function lb(a,b){this.axis=a;if(b)this.options=b,this.id=b.id;return this}function Fb(a,b,c,d,e){var f=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.stack=e;this.alignOptions={align:b.align||(f?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(f?"middle":c?"bottom":"top"),y:p(b.y,f?4:c?14:-6),x:p(b.x,f?c?-6:6:0)};this.textAlign=b.textAlign|| +(f?c?"right":"left":"center")}function mb(){this.init.apply(this,arguments)}function nb(a,b){var c=b.borderWidth,d=b.style,e=B(d.padding);this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.label=a.renderer.label("",0,0,b.shape,null,null,b.useHTML,null,"tooltip").attr({padding:e,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).css({padding:0}).hide().add();$||this.label.shadow(b.shadow);this.shared=b.shared}function ob(a,b){var c=$?"": +b.chart.zoomType;this.zoomX=/x/.test(c);this.zoomY=/y/.test(c);this.options=b;this.chart=a;this.init(a,b.tooltip)}function pb(a){this.init(a)}function qb(a,b){var c,d=a.series;a.series=null;c=A(O,a);c.series=a.series=d;var d=c.chart,e=d.margin,e=Z(e)?e:[e,e,e,e];this.optionsMarginTop=p(d.marginTop,e[0]);this.optionsMarginRight=p(d.marginRight,e[1]);this.optionsMarginBottom=p(d.marginBottom,e[2]);this.optionsMarginLeft=p(d.marginLeft,e[3]);this.runChartClick=(e=d.events)&&!!e.click;this.callback=b; +this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;this.init(e)}var x,z=document,M=window,L=Math,s=L.round,V=L.floor,xa=L.ceil,t=L.max,P=L.min,N=L.abs,W=L.cos,aa=L.sin,ya=L.PI,$a=ya*2/360,Ca=navigator.userAgent,Sa=/msie/i.test(Ca)&&!M.opera,Da=z.documentMode===8,Gb=/AppleWebKit/.test(Ca),rb=/Firefox/.test(Ca),ga=!!z.createElementNS&&!!z.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,Ob=rb&&parseInt(Ca.split("Firefox/")[1],10)<4,$=!ga&& +!Sa&&!!z.createElement("canvas").getContext,Pa,ha=z.documentElement.ontouchstart!==x,Hb={},sb=0,Ya,O,ab,Na,tb,F,Ea=function(){},ja="div",T="none",ub="rgba(192,192,192,"+(ga?1.0E-6:0.0020)+")",yb="millisecond",gb="second",Ta="minute",Ia="hour",qa="day",Ua="week",Ja="month",ua="year",Xa,hb,ib,kb,Ka,Va,Wa,zb,Ab,jb,Bb,Cb,ba={};M.Highcharts={};ab=function(a,b,c){if(!u(b)||isNaN(b))return"Invalid date";var a=p(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b),e,f=d[ib](),g=d[kb](),h=d[Ka](),i=d[Va](),j=d[Wa](),k=O.lang, +l=k.weekdays,b={a:l[g].substr(0,3),A:l[g],d:ta(h),e:h,b:k.shortMonths[i],B:k.months[i],m:ta(i+1),y:j.toString().substr(2,2),Y:j,H:ta(f),I:ta(f%12||12),l:f%12||12,M:ta(d[hb]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:ta(d.getSeconds()),L:ta(s(b%1E3),3)};for(e in b)a=a.replace("%"+e,b[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Db.prototype={wrapColor:function(a){if(this.color>=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};F=ma(yb,1,gb,1E3,Ta,6E4,Ia,36E5,qa,864E5, +Ua,6048E5,Ja,2592E6,ua,31556952E3);tb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length{point.key}
    ',pointFormat:'{series.name}: {point.y}
    ', +shadow:!0,shared:$,snap:ha?25:10,style:{color:"#333333",fontSize:"12px",padding:"5px",whiteSpace:"nowrap"}},credits:{enabled:!1,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"10px"}}};var Y=O.plotOptions,X=Y.line;Eb();var ra=function(a){var b=[],c;(function(a){(c=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(a))?b=[B(c[1]),B(c[2]), +B(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))&&(b=[B(c[1],16),B(c[2],16),B(c[3],16),1])})(a);return{get:function(c){return b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a},brighten:function(a){if(Ga(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=B(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},setOpacity:function(a){b[3]=a;return this}}};wa.prototype={init:function(a,b){this.element=b==="span"?S(b): +z.createElementNS("http://www.w3.org/2000/svg",b);this.renderer=a;this.attrSetters={}},animate:function(a,b,c){b=p(b,Na,!0);cb(this);if(b){b=A(b);if(c)b.complete=c;vb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName,i=this.renderer,j,k=this.attrSetters,l=this.shadows,m,o,q=this;na(a)&&u(b)&&(c=a,a={},a[c]=b);if(na(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),q=C(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&(q=parseFloat(q)); +else for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==x&&(d=e);if(c==="d")d&&d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text"){for(e=0;em&&/[ \-]/.test(b.innerText)&&(G(b,{width:m+"px",display:"block",whiteSpace:"normal"}),k=m),m=a.fontMetrics(b.style.fontSize).b,r=o<0&&-k,y=q<0&&-l,fa=o*q<0,r+=q*m*(fa?1-h:h),y-=o*m*(j?fa?h:1-h:1),i&&(r-=k*h*(o<0?-1:1),j&&(y-=l*h*(q<0?-1: +1)),G(b,{textAlign:g})),this.xCorr=r,this.yCorr=y;G(b,{left:e+r+"px",top:f+y+"px"});this.cTT=ca}}else this.alignOnAdd=!0},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.inverted,d=this.rotation,e=[];c&&(a+=this.attr("width"),b+=this.attr("height"));(a||b)&&e.push("translate("+a+","+b+")");c?e.push("rotate(90) scale(-1,1)"):d&&e.push("rotate("+d+" "+(this.x||0)+" "+(this.y||0)+")");e.length&&C(this.element,"transform",e.join(" "))},toFront:function(){var a=this.element; +a.parentNode.appendChild(a);return this},align:function(a,b,c){a?(this.alignOptions=a,this.alignByTranslate=b,c||this.renderer.alignedObjects.push(this)):(a=this.alignOptions,b=this.alignByTranslate);var c=p(c,this.renderer),d=a.align,e=a.verticalAlign,f=(c.x||0)+(a.x||0),g=(c.y||0)+(a.y||0),h={};/^(right|center)$/.test(d)&&(f+=(c.width-(a.width||0))/{right:1,center:2}[d]);h[b?"translateX":"x"]=s(f);/^(bottom|middle)$/.test(e)&&(g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1));h[b?"translateY": +"y"]=s(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(a){var b,c,d=this.rotation;c=this.element;var e=d*$a;if(c.namespaceURI==="http://www.w3.org/2000/svg"||this.renderer.forExport){try{b=c.getBBox?v({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(f){}if(!b||b.width<0)b={width:0,height:0};a=b.width;c=b.height;if(d)b.width=N(c*aa(e))+N(a*W(e)),b.height=N(c*W(e))+N(a*aa(e))}else b=this.htmlGetBBox(a);return b},show:function(){return this.attr({visibility:"visible"})}, +hide:function(){return this.attr({visibility:"hidden"})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=C(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=B(g);if(c.handleZ)for(c=0;cg||!u(g)&&u(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;H(this,"add");return this},safeRemoveChild:function(a){var b= +a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.box,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=null;cb(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=0;f/g,'').replace(/<(i|em)>/g,'').replace(//g,"").split(//g),d=b.childNodes,e=/style="([^"]+)"/,f=/href="([^"]+)"/,g=C(b,"x"),h=a.styles,i=h&&B(h.width),j=h&&h.lineHeight, +k,h=d.length,l=[];h--;)b.removeChild(d[h]);i&&!a.added&&this.box.appendChild(b);c[c.length-1]===""&&c.pop();n(c,function(c,d){var h,fa=0,r,c=c.replace(//g,"|||");h=c.split("|||");n(h,function(c){if(c!==""||h.length===1){var m={},p=z.createElementNS("http://www.w3.org/2000/svg","tspan");e.test(c)&&C(p,"style",c.match(e)[1].replace(/(;| |^)color([ :])/,"$1fill$2"));f.test(c)&&(C(p,"onclick",'location.href="'+c.match(f)[1]+'"'),G(p,{cursor:"pointer"}));c=(c.replace(/<(.|\n)*?>/g, +"")||" ").replace(/</g,"<").replace(/>/g,">");p.appendChild(z.createTextNode(c));fa?m.dx=3:m.x=g;if(!fa){if(d){!ga&&a.renderer.forExport&&G(p,{display:"block"});r=M.getComputedStyle&&B(M.getComputedStyle(k,null).getPropertyValue("line-height"));if(!r||isNaN(r)){var n;if(!(n=j))if(!(n=k.offsetHeight))l[d]=b.getBBox?b.getBBox().height:a.renderer.fontMetrics(b.style.fontSize).h,n=s(l[d]-(l[d-1]||0))||18;r=n}C(p,"dy",r)}k=p}C(p,m);b.appendChild(p);fa++;if(i)for(var c=c.replace(/-/g,"- ").split(" "), +D=[];c.length||D.length;)n=a.getBBox().width,m=n>i,!m||c.length===1?(c=D,D=[],c.length&&(p=z.createElementNS("http://www.w3.org/2000/svg","tspan"),C(p,{dy:j||16,x:g}),b.appendChild(p),n>i&&(i=n))):(p.removeChild(p.firstChild),D.unshift(c.pop())),c.length&&p.appendChild(z.createTextNode(c.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c),i=0,j,k,l,m,o,a={x1:0,y1:0,x2:0,y2:1},e=A(ma("stroke-width",1,"stroke","#999","fill",ma("linearGradient",a,"stops",[[0,"#FFF"], +[1,"#DDD"]]),"r",3,"padding",3,"style",ma("color","black")),e);l=e.style;delete e.style;f=A(e,ma("stroke","#68A","fill",ma("linearGradient",a,"stops",[[0,"#FFF"],[1,"#ACF"]])),f);m=f.style;delete f.style;g=A(e,ma("stroke","#68A","fill",ma("linearGradient",a,"stops",[[0,"#9BD"],[1,"#CDF"]])),g);o=g.style;delete g.style;K(h.element,"mouseenter",function(){h.attr(f).css(m)});K(h.element,"mouseleave",function(){j=[e,f,g][i];k=[l,m,o][i];h.attr(j).css(k)});h.setState=function(a){(i=a)?a===2&&h.attr(g).css(o): +h.attr(e).css(l)};return h.on("click",function(){d.call(h)}).attr(e).css(v({cursor:"default"},l))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=s(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=s(a[2])+b%2/2);return a},path:function(a){var b={fill:T};Fa(a)?b.d=a:Z(a)&&v(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=Z(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(Z(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc", +a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){e=Z(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:T});return e.attr(Z(a)?a:e.crisp(f,a,b,t(c,0),t(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[p(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return u(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:T}; +arguments.length>1&&v(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(s(b),s(c),d,e,f),i=/^url\((.*?)\)$/,j,k;h?(g=this.path(h),v(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&v(g,f)):i.test(a)&&(k=function(a,b){a.attr({width:b[0],height:b[1]});a.alignByTranslate||a.translate(-s(b[0]/ +2),-s(b[1]/2))},j=a.match(i)[1],a=Hb[j],g=this.image(j).attr({x:b,y:c}),a?k(g,a):(g.attr({width:0,height:0}),S("img",{onload:function(){k(g,Hb[j]=[this.width,this.height])},src:j})));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M", +a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-1.0E-6,d=e.innerR,h=e.open,i=W(f),j=aa(f),k=W(g),g=aa(g),e=e.end-f');if(b)c=b===ja||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=S(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);Da&&d.gVis==="hidden"&&G(c,{visibility:"hidden"});d.appendChild(c);this.added=!0;this.alignOnAdd&& +!this.deferUpdateTransform&&this.updateTransform();H(this,"add");return this},toggleChildren:function(a,b){for(var c=a.childNodes,d=c.length;d--;)G(c[d],{visibility:b}),c[d].nodeName==="DIV"&&this.toggleChildren(c[d],b)},updateTransform:wa.prototype.htmlUpdateTransform,attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,l=this.shadows,m,o=this.attrSetters,q=this;na(a)&&u(b)&&(c=a,a={},a[c]=b);if(na(a))c=a,q=c==="strokeWidth"||c==="stroke-width"? +this.strokeweight:this[c];else for(c in a)if(d=a[c],m=!1,e=o[c]&&o[c].call(this,d,c),e!==!1&&d!==null){e!==x&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k||(this.symbolAttr(a),k=!0),m=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;for(m=[];e--;)m[e]=Ga(d[e])?s(d[e]*10)-5:d[e]==="Z"?"x":d[e];d=m.join(" ")||"x";f.path=d;if(l)for(e=l.length;e--;)l[e].path=l[e].cutOff?this.cutOffPath(d,l[e].cutOff):d;m=!0}else if(c==="zIndex"||c==="visibility"){if(Da&&c=== +"visibility"&&h==="DIV")f.gVis=d,this.toggleChildren(f,d),d==="visible"&&(d=null);d&&(g[c]=d);m=!0}else if(c==="width"||c==="height")d=t(0,d),this[c]=d,this.updateClipping?(this[c]=d,this.updateClipping()):g[c]=d,m=!0;else if(c==="x"||c==="y")this[c]=d,g[{x:"left",y:"top"}[c]]=d;else if(c==="class")f.className=d;else if(c==="stroke")d=i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Ga(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]|| +S(i.prepVML([""]),null,null,f))[c]=d||"solid",this.dashstyle=d,m=!0;else if(c==="fill")h==="SPAN"?g.color=d:(f.filled=d!==T?!0:!1,d=i.color(d,f,c,this),c="fillcolor");else if(h==="shape"&&c==="rotation")this[c]=d,f.style.left=-s(aa(d*$a)+1)+"px",f.style.top=s(W(d*$a))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),m=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,m=!0;if(l&&c==="visibility")for(e=l.length;e--;)l[e].style[c]=d;m||(Da?f[c]= +d:C(f,c,d))}return q},clip:function(a){var b=this,c,d=b.element,e=d.parentNode;a?(c=a.members,c.push(b),b.destroyClip=function(){za(c,b)},e&&e.className==="highcharts-tracker"&&!Da&&G(d,{visibility:"hidden"})):b.destroyClip&&b.destroyClip();return b.css(a?a.getCSS(b):{clip:"inherit"})},css:wa.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&Ma(a)},destroy:function(){this.destroyClip&&this.destroyClip();return wa.prototype.destroy.apply(this)},empty:function(){for(var a=this.element.childNodes, +b=a.length,c;b--;)c=a[b],c.parentNode.removeChild(c)},on:function(a,b){this.element["on"+a]=function(){var a=M.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=B(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,l,m,o,q;k&&typeof k.value!=="string"&&(k="x");m=k;if(a){o=p(a.width,3);q=(a.opacity||0.15)/o;for(e=1;e<=3;e++){l=o*2+1-2*e;c&&(m= +this.cutOffPath(k.value,l+0.5));j=[''];h=S(g.prepVML(j),null,{left:B(i.left)+(a.offsetX||1),top:B(i.top)+(a.offsetY||1)});if(c)h.cutOff=l+1;j=[''];S(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}};la=ea(wa,la);var ia={Element:la,isIE8:Ca.indexOf("MSIE 8.0")> +-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(ja);e=d.element;e.style.position="relative";a.appendChild(d.element);this.box=e;this.boxWrapper=d;this.setSize(b,c,!1);if(!z.namespaces.hcv)z.namespaces.add("hcv","urn:schemas-microsoft-com:vml"),z.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement(), +f=Z(a);return v(e,{members:[],left:f?a.x:a,top:f?a.y:b,width:f?a.width:c,height:f?a.height:d,getCSS:function(a){var b=a.inverted,c=this.top,d=this.left,e=d+this.width,f=c+this.height,c={clip:"rect("+s(b?d:c)+"px,"+s(b?f:e)+"px,"+s(b?e:f)+"px,"+s(b?c:d)+"px)"};!b&&Da&&a.element.nodeName!=="IMG"&&v(c,{width:e+"px",height:f+"px"});return c},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e=this,f,g=/^rgba/,h,i,j=T;a&&a.linearGradient?i="gradient": +a&&a.radialGradient&&(i="pattern");if(i){var k,l,m=a.linearGradient||a.radialGradient,o,q,p,r,y,u="",a=a.stops,E,w=[],D=function(){h=[''];S(e.prepVML(h),null,null,b)};o=a[0];E=a[a.length-1];o[0]>0&&a.unshift([0,o[1]]);E[0]<1&&a.push([1,E[1]]);n(a,function(a,b){g.test(a[1])?(f=ra(a[1]),k=f.get("rgb"),l=f.get("a")):(k=a[1],l=1);w.push(a[0]*100+"% "+k);b?(p=l,r=k):(q=l,y=k)});if(c==="fill")if(i=== +"gradient")c=m.x1||m[0]||0,a=m.y1||m[1]||0,o=m.x2||m[2]||0,m=m.y2||m[3]||0,u='angle="'+(90-L.atan((m-a)/(o-c))*180/ya)+'"',D();else{var j=m.r,J=j*2,Q=j*2,s=m.cx,v=m.cy,x=b.radialReference,t,j=function(){x&&(t=d.getBBox(),s+=(x[0]-t.x)/t.width-0.5,v+=(x[1]-t.y)/t.height-0.5,J*=x[2]/t.width,Q*=x[2]/t.height);u='src="'+O.global.VMLRadialGradientURL+'" size="'+J+","+Q+'" origin="0.5,0.5" position="'+s+","+v+'" color2="'+y+'" ';D()};d.added?j():K(d,"add",j);j=r}else j=k}else if(g.test(a)&&b.tagName!== +"IMG")f=ra(a),h=["<",c,' opacity="',f.get("a"),'"/>'],S(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1;j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","1&&f.css({left:b,top:c,width:d,height:e});return f},rect:function(a, +b,c,d,e,f){if(Z(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,t(c,0),t(d,0)))},invertChild:function(a,b){var c=b.style;G(a,{flip:"x",left:B(c.width)-1,top:B(c.height)-1,rotation:-90})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=W(f),d=aa(f),i=W(g),j=aa(g),k=e.innerR,l=0.08/h,m=k&&0.1/k||0;if(g-f===0)return["x"];else 2*ya-g+fj&&(c=!1)):h+k>m&&(h=m-k,d&&h+l0&&b.height>0){f=A({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=E.text(f.text,0,0).attr({align:f.textAlign||f.align,rotation:f.rotation,zIndex:y}).css(f.style).add();b=[q[1],q[4],p(q[6],q[1])];q=[q[2],q[5],p(q[7], +q[2])];c=La(b);k=La(q);g.align(f,!1,{x:c,y:k,width:Aa(b)-c,height:Aa(q)-k});g.show()}else g&&g.hide();return a},destroy:function(){za(this.axis.plotLinesAndBands,this);Ba(this,this.axis)}};Fb.prototype={destroy:function(){Ba(this,this.axis)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options.formatter.call(this);this.label?this.label.attr({text:b,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(b,0,0).css(this.options.style).attr({align:this.textAlign, +rotation:this.options.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.total,0,0,0,1),c=c.translate(0),c=N(g-c),h=d.xAxis[0].translate(this.x)+a,d=d.plotHeight,e={x:e?f?g:g-c:h,y:e?d-h-b:f?d-g-c:d-g,width:e?c:b,height:e?b:c};this.label&&this.label.align(this.alignOptions,null,e).attr({visibility:"visible"})}};mb.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M", +hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:I,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#6D869F",fontWeight:"bold"}}, +type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Y-values"},stackLabels:{enabled:!1,formatter:function(){return this.total},style:I.style}},defaultLeftAxisOptions:{labels:{align:"right",x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{align:"left",x:8,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{align:"center", +x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{align:"center",x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.xOrY=(this.isXAxis=c)?"x":"y";this.opposite=b.opposite;this.side=this.horiz?this.opposite?0:2:this.opposite?1:3;this.setOptions(b);var d=this.options,e=d.type,f=e==="datetime";this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.staggerLines=this.horiz&&d.labels.staggerLines;this.userOptions=b;this.minPixelPadding= +0;this.chart=a;this.reversed=d.reversed;this.categories=d.categories;this.isLog=e==="logarithmic";this.isLinked=u(d.linkedTo);this.isDatetimeAxis=f;this.ticks={};this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.min=this.max=null;var g,d=this.options.events;a.axes.push(this);a[c?"xAxis":"yAxis"].push(this);this.series=[];if(a.inverted&&c&&this.reversed=== +x)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;this.addPlotLine=this.addPlotBand=this.addPlotBandOrLine;for(g in d)K(this,g,d[g]);if(this.isLog)this.val2lin=oa,this.lin2val=da},setOptions:function(a){this.options=A(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],A(O[this.isXAxis?"xAxis":"yAxis"],a))},defaultLabelFormatter:function(){var a= +this.axis,b=this.value,c=this.dateTimeLabelFormat,d=O.lang.numericSymbols,e=d&&d.length,f,g=a.isLog?b:a.tickInterval;if(a.categories)f=b;else if(c)f=ab(c,b);else if(e&&g>=1E3)for(;e--&&f===x;)a=Math.pow(1E3,e+1),g>=a&&d[e]!==null&&(f=Ha(b/a,-1)+d[e]);f===x&&(f=b>=1E3?Ha(b,0):Ha(b,-1));return f},getSeriesExtremes:function(){var a=this,b=a.chart,c=a.stacks,d=[],e=[],f;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;n(a.series,function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var h=g.options, +i,j,k,l,m,o,q,n,r,y=h.threshold,s,E=[],w=0;a.hasVisibleSeries=!0;if(a.isLog&&y<=0)y=h.threshold=null;if(a.isXAxis){if(h=g.xData,h.length)a.dataMin=P(p(a.dataMin,h[0]),La(h)),a.dataMax=t(p(a.dataMax,h[0]),Aa(h))}else{var D,J,Q,v=g.cropped,A=g.xAxis.getExtremes(),B=!!g.modifyValue;i=h.stacking;a.usePercentage=i==="percent";if(i)m=h.stack,l=g.type+p(m,""),o="-"+l,g.stackKey=l,j=d[l]||[],d[l]=j,k=e[o]||[],e[o]=k;if(a.usePercentage)a.dataMin=0,a.dataMax=99;h=g.processedXData;q=g.processedYData;s=q.length; +for(f=0;f=A.min&&(h[f-1]||n)<=A.max))if(n=r.length)for(;n--;)r[n]!==null&&(E[w++]=r[n]);else E[w++]=r;if(!a.usePercentage&&E.length)a.dataMin=P(p(a.dataMin,E[0]),La(E)),a.dataMax=t(p(a.dataMax,E[0]),Aa(E));if(u(y))if(a.dataMin>=y)a.dataMin=y,a.ignoreMinPadding=!0;else if(a.dataMax< +y)a.dataMax=y,a.ignoreMaxPadding=!0}}})},translate:function(a,b,c,d,e,f){var g=this.len,h=1,i=0,j=d?this.oldTransA:this.transA,d=d?this.oldMin:this.min,e=this.options.ordinal||this.isLog&&e;if(!j)j=this.transA;c&&(h*=-1,i=g);this.reversed&&(h*=-1,i-=h*g);b?(this.reversed&&(a=g-a),a=a/j+d,e&&(a=this.lin2val(a))):(e&&(a=this.val2lin(a)),a=h*(a-d)*j+i+h*this.minPixelPadding+(f?j*this.pointRange/2:0));return a},getPlotLinePath:function(a,b,c){var d=this.chart,e=this.left,f=this.top,g,h,i,a=this.translate(a, +null,null,c),j=c&&d.oldChartHeight||d.chartHeight,k=c&&d.oldChartWidth||d.chartWidth,l;g=this.transB;c=h=s(a+g);g=i=s(j-a-g);if(isNaN(a))l=!0;else if(this.horiz){if(g=f,i=j-this.bottom,ce+this.width)l=!0}else if(c=e,h=k-this.right,gf+this.height)l=!0;return l?null:d.renderer.crispLine(["M",c,g,"L",h,i],b||0)},getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4],c[5],c[1],c[2]):d=null;return d},getLinearTickPositions:function(a,b,c){for(var d, +b=ka(V(b/a)*a),c=ka(xa(c/a)*a),e=[];b<=c;){e.push(b);b=ka(b+a);if(b===d)break;d=b}return e},getLogTickPositions:function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=s(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=V(b),h,i,j,k,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];fb&&g.push(k),k>c&&(l=!0),k=j}else if(b=da(b),c=da(c),a=e[d?"minorTickInterval":"tickInterval"], +a=p(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=fb(a,null,L.pow(10,V(L.log(a)/L.LN10))),g=Qa(this.getLinearTickPositions(a,b,c),oa),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g},getMinorTickPositions:function(){var a=this.tickPositions,b=this.minorTickInterval,c=[],d,e;if(this.isLog){e=a.length;for(d=1;d=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===x&&!this.isLog)u(a.min)||u(a.max)?this.minRange=null:(n(this.series,function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===x||h0||!b.ignoreMaxPadding))b.max+=c*j}b.tickInterval=b.min===b.max||b.min===void 0||b.max===void 0?1:h&&!l&&o===b.linkedParent.options.tickPixelInterval?b.linkedParent.tickInterval:p(l,q?1:(b.max-b.min)*o/(b.len||1));g&&!a&&n(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(a);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(!l&&b.tickIntervale&&i.shift(),d.endOnTick?b.max=f:b.max+hb[d]&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this.xOrY,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&!this.categories&& +!this.isLinked&&this.options.alignTicks!==!1){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(ea||a===null?a=c:b=a.min&&b<=a.max)j[b]||(j[b]=new Oa(a,b)),r&&j[b].isNew&&j[b].render(c,!0),j[b].isActive=!0,j[b].render(c)}),o&&n(g,function(b,c){if(c%2===0&&b1||N(b-f.y)>1)?function(){e.move(a,b,c,d)}:null},hide:function(){if(!this.isHidden){var a=this.chart.hoverPoints; +this.label.hide();a&&n(a,function(a){a.setState()});this.chart.hoverPoints=null;this.isHidden=!0}},hideCrosshairs:function(){n(this.crosshairs,function(a){a&&a.hide()})},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted,f=0,g=0,h,a=pa(a);c=a[0].tooltipPos;c||(n(a,function(a){h=a.series.yAxis;f+=a.plotX;g+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&h?h.top-d.plotTop:0)}),f/=a.length,g/=a.length,c=[e?d.plotWidth-g:f,this.shared&&!e&&a.length>1&&b?b.chartY-d.plotTop:e?d.plotHeight-f:g]); +return Qa(c,s)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i=p(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,l;d<7&&(d=e+j+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,l=!0);k=k&&c<=k+b&&(k=c+f+i));k+b>f+h&&(k=t(f,f+h-b-i));return{x:d,y:k}},refresh:function(a,b){function c(){var a=this.points||pa(this),b=a[0].series,c;c=[b.tooltipHeaderFormatter(a[0].key)];n(a,function(a){b=a.series;c.push(b.tooltipFormatter&& +b.tooltipFormatter(a)||a.point.tooltipFormatter(b.tooltipOptions.pointFormat))});c.push(f.footerFormat||"");return c.join("")}var d=this.chart,e=this.label,f=this.options,g,h,i,j={},k,l=[];k=f.formatter||c;var j=d.hoverPoints,m,o=f.crosshairs;i=this.shared;h=this.getAnchor(a,b);g=h[0];h=h[1];i&&(!a.series||!a.series.noSharedTooltip)?(d.hoverPoints=a,j&&n(j,function(a){a.setState()}),n(a,function(a){a.setState("hover");l.push(a.getLabelConfig())}),j={x:a[0].category,y:a[0].y},j.points=l,a=a[0]):j= +a.getLabelConfig();k=k.call(j);j=a.series;i=i||!j.isCartesian||j.tooltipOutsidePlot||d.isInsidePlot(g,h);k===!1||!i?this.hide():(this.isHidden&&e.show(),e.attr({text:k}),m=f.borderColor||a.color||j.color||"#606060",e.attr({stroke:m}),e=(f.positioner||this.getPosition).call(this,e.width,e.height,{plotX:g,plotY:h}),this.move(s(e.x),s(e.y),g+d.plotLeft,h+d.plotTop),this.isHidden=!1);if(o){o=pa(o);for(e=o.length;e--;)if(i=a.series[e?"yAxis":"xAxis"],o[e]&&i)if(i=i.getPlotLinePath(e?p(a.stackY,a.y):a.x, +1),this.crosshairs[e])this.crosshairs[e].attr({d:i,visibility:"visible"});else{j={"stroke-width":o[e].width||1,stroke:o[e].color||"#C0C0C0",zIndex:o[e].zIndex||2};if(o[e].dashStyle)j.dashstyle=o[e].dashStyle;this.crosshairs[e]=d.renderer.path(i).attr(j).add()}}H(d,"tooltipRefresh",{text:k,x:g+d.plotLeft,y:h+d.plotTop,borderColor:m})},tick:function(){this.tooltipTick&&this.tooltipTick()}};ob.prototype={normalizeMouseEvent:function(a){var b,c,d,a=a||M.event;if(!a.target)a.target=a.srcElement;a=Jb(a); +d=a.touches?a.touches.item(0):a;this.chartPosition=b=Rb(this.chart.container);d.pageX===x?(c=a.x,b=a.y):(c=d.pageX-b.left,b=d.pageY-b.top);return v(a,{chartX:s(c),chartY:s(b)})},getMouseCoordinates:function(a){var b={xAxis:[],yAxis:[]},c=this.chart;n(c.axes,function(d){var e=d.isXAxis;b[e?"xAxis":"yAxis"].push({axis:d,value:d.translate(((c.inverted?!e:e)?a.chartX-c.plotLeft:d.top+d.len-a.chartY)-d.minPixelPadding,!0)})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+ +b.plotTop-a.chartY:a.chartX-b.plotLeft},onmousemove:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f=b.hoverPoint,g=b.hoverSeries,h,i,j=b.chartWidth,k=this.getIndex(a);if(d&&this.options.tooltip.shared&&(!g||!g.noSharedTooltip)){e=[];h=c.length;for(i=0;i +j&&e.splice(h,1);if(e.length&&e[0].plotX!==this.hoverX)d.refresh(e,a),this.hoverX=e[0].plotX}if(g&&g.tracker&&(b=g.tooltipPoints[k])&&b!==f)b.onMouseOver()},resetTracker:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,b=e&&e.shared?b.hoverPoints:d;(a=a&&e&&b)&&pa(b)[0].plotX===x&&(a=!1);if(a)e.refresh(b);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&(e.hide(),e.hideCrosshairs());this.hoverX=null}},setDOMEvents:function(){function a(){if(b.selectionMarker){var f={xAxis:[], +yAxis:[]},g=b.selectionMarker.getBBox(),h=g.x-c.plotLeft,l=g.y-c.plotTop,m;e&&(n(c.axes,function(a){if(a.options.zoomEnabled!==!1){var b=a.isXAxis,d=c.inverted?!b:b,e=a.translate(d?h:c.plotHeight-l-g.height,!0,0,0,1),d=a.translate((d?h+g.width:c.plotHeight-l)-2*a.minPixelPadding,!0,0,0,1);!isNaN(e)&&!isNaN(d)&&(f[b?"xAxis":"yAxis"].push({axis:a,min:P(e,d),max:t(e,d)}),m=!0)}}),m&&H(c,"selection",f,function(a){c.zoom(a)}));b.selectionMarker=b.selectionMarker.destroy()}if(c)G(d,{cursor:"auto"}),c.cancelClick= +e,c.mouseIsDown=e=!1;U(z,ha?"touchend":"mouseup",a)}var b=this,c=b.chart,d=c.container,e,f=b.zoomX&&!c.inverted||b.zoomY&&c.inverted,g=b.zoomY&&!c.inverted||b.zoomX&&c.inverted;b.hideTooltipOnMouseMove=function(a){a=Jb(a);b.chartPosition&&c.hoverSeries&&c.hoverSeries.isCartesian&&!c.isInsidePlot(a.pageX-b.chartPosition.left-c.plotLeft,a.pageY-b.chartPosition.top-c.plotTop)&&b.resetTracker()};b.hideTooltipOnMouseLeave=function(){b.resetTracker();b.chartPosition=null};d.onmousedown=function(d){d=b.normalizeMouseEvent(d); +!ha&&d.preventDefault&&d.preventDefault();c.mouseIsDown=!0;c.cancelClick=!1;c.mouseDownX=b.mouseDownX=d.chartX;b.mouseDownY=d.chartY;K(z,ha?"touchend":"mouseup",a)};var h=function(a){if(!a||!(a.touches&&a.touches.length>1)){a=b.normalizeMouseEvent(a);if(!ha)a.returnValue=!1;var d=a.chartX,h=a.chartY,l=!c.isInsidePlot(d-c.plotLeft,h-c.plotTop);ha&&a.type==="touchstart"&&(C(a.target,"isTracker")?c.runTrackerClick||a.preventDefault():!c.runChartClick&&!l&&a.preventDefault());if(l)dc.plotLeft+c.plotWidth&&(d=c.plotLeft+c.plotWidth),hc.plotTop+c.plotHeight&&(h=c.plotTop+c.plotHeight);if(c.mouseIsDown&&a.type!=="touchstart"&&(e=Math.sqrt(Math.pow(b.mouseDownX-d,2)+Math.pow(b.mouseDownY-h,2)),e>10)){var m=c.isInsidePlot(b.mouseDownX-c.plotLeft,b.mouseDownY-c.plotTop);if(c.hasCartesianSeries&&(b.zoomX||b.zoomY)&&m&&!b.selectionMarker)b.selectionMarker=c.renderer.rect(c.plotLeft,c.plotTop,f?1:c.plotWidth,g?1:c.plotHeight,0).attr({fill:b.options.chart.selectionMarkerFill|| +"rgba(69,114,167,0.25)",zIndex:7}).add();if(b.selectionMarker&&f){var o=d-b.mouseDownX;b.selectionMarker.attr({width:N(o),x:(o>0?0:o)+b.mouseDownX})}b.selectionMarker&&g&&(h-=b.mouseDownY,b.selectionMarker.attr({height:N(h),y:(h>0?0:h)+b.mouseDownY}));m&&!b.selectionMarker&&b.options.chart.panning&&c.pan(d)}if(!l)b.onmousemove(a);return l||!c.hasCartesianSeries}};d.onmousemove=h;K(d,"mouseleave",b.hideTooltipOnMouseLeave);K(z,"mousemove",b.hideTooltipOnMouseMove);d.ontouchstart=function(a){if(b.zoomX|| +b.zoomY)d.onmousedown(a);h(a)};d.ontouchmove=h;d.ontouchend=function(){e&&b.resetTracker()};d.onclick=function(a){var d=c.hoverPoint,e,f,a=b.normalizeMouseEvent(a);a.cancelBubble=!0;if(!c.cancelClick)d&&(C(a.target,"isTracker")||C(a.target.parentNode,"isTracker"))?(e=d.plotX,f=d.plotY,v(d,{pageX:b.chartPosition.left+c.plotLeft+(c.inverted?c.plotWidth-f:e),pageY:b.chartPosition.top+c.plotTop+(c.inverted?c.plotHeight-e:f)}),H(d.series,"click",v(a,{point:d})),d.firePointEvent("click",a)):(v(a,b.getMouseCoordinates(a)), +c.isInsidePlot(a.chartX-c.plotLeft,a.chartY-c.plotTop)&&H(c,"click",a))}},destroy:function(){var a=this.chart,b=a.container;if(a.trackerGroup)a.trackerGroup=a.trackerGroup.destroy();U(b,"mouseleave",this.hideTooltipOnMouseLeave);U(z,"mousemove",this.hideTooltipOnMouseMove);b.onclick=b.onmousedown=b.onmousemove=b.ontouchstart=b.ontouchend=b.ontouchmove=null;clearInterval(this.tooltipInterval)},init:function(a,b){if(!a.trackerGroup)a.trackerGroup=a.renderer.g("tracker").attr({zIndex:9}).add();if(b.enabled)a.tooltip= +new nb(a,b),this.tooltipInterval=setInterval(function(){a&&a.tooltip&&a.tooltip.tick()},32);this.setDOMEvents()}};pb.prototype={init:function(a){var b=this,c=b.options=a.options.legend;if(c.enabled){var d=c.itemStyle,e=p(c.padding,8),f=c.itemMarginTop||0;b.baseline=B(d.fontSize)+3+f;b.itemStyle=d;b.itemHiddenStyle=A(d,c.itemHiddenStyle);b.itemMarginTop=f;b.padding=e;b.initialItemX=e;b.initialItemY=e-5;b.maxItemWidth=0;b.chart=a;b.itemHeight=0;b.lastLineHeight=0;b.render();K(b.chart,"endResize",function(){b.positionCheckboxes()})}}, +colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,g=b?a.color:g;d&&d.css({fill:c});e&&e.attr({stroke:g});f&&f.attr({stroke:g,fill:g})},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine", +"legendSymbol","legendGroup"],function(b){a[b]&&a[b].destroy()});b&&Ma(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(){var a=this;n(a.allItems,function(b){var c=b.checkbox,d=a.group.alignAttr;c&&G(c,{left:d.translateX+b.legendItemWidth+c.x-20+"px",top:d.translateY+c.y+3+"px"})})},renderItem:function(a){var r;var b=this,c=b.chart,d=c.renderer,e=b.options,f=e.layout==="horizontal",g=e.symbolWidth,h=e.symbolPadding, +i=b.itemStyle,j=b.itemHiddenStyle,k=b.padding,l=!e.rtl,m=e.width,o=e.itemMarginBottom||0,q=b.itemMarginTop,p=b.initialItemX,n=a.legendItem,y=a.series||a,s=y.options,u=s.showCheckbox;if(!n&&(a.legendGroup=d.g("legend-item").attr({zIndex:1}).add(b.scrollGroup),y.drawLegendSymbol(b,a),a.legendItem=n=d.text(e.labelFormatter.call(a),l?g+h:-h,b.baseline,e.useHTML).css(A(a.visible?i:j)).attr({align:l?"left":"right",zIndex:2}).add(a.legendGroup),a.legendGroup.on("mouseover",function(){a.setState("hover"); +n.css(b.options.itemHoverStyle)}).on("mouseout",function(){n.css(a.visible?i:j);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):H(a,"legendItemClick",b,c)}),b.colorizeItem(a,a.visible),s&&u))a.checkbox=S("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},e.itemCheckboxStyle,c.container),K(a.checkbox,"click",function(b){H(a,"checkboxClick",{checked:b.target.checked},function(){a.select()})}); +d=n.getBBox();r=a.legendItemWidth=e.itemWidth||g+h+d.width+k+(u?20:0),e=r;b.itemHeight=g=d.height;if(f&&b.itemX-p+e>(m||c.chartWidth-2*k-p))b.itemX=p,b.itemY+=q+b.lastLineHeight+o,b.lastLineHeight=0;b.maxItemWidth=t(b.maxItemWidth,e);b.lastItemY=q+b.itemY+o;b.lastLineHeight=t(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=q+g+o,b.lastLineHeight=g);b.offsetWidth=m||t(f?b.itemX-p:e,b.offsetWidth)},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i= +a.box,j=a.options,k=a.padding,l=j.borderWidth,m=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup),a.clipRect=c.clipRect(0,0,9999,b.chartHeight),a.contentGroup.clip(a.clipRect);e=[];n(b.series,function(a){var b=a.options;b.showInLegend&&(e=e.concat(a.legendItems||(b.legendType==="point"?a.data:a)))});Nb(e,function(a,b){return(a.options.legendIndex|| +0)-(b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;n(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight;h=a.handleOverflow(h);if(l||m){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,l||0).attr({stroke:j.borderColor,"stroke-width":l||0,fill:m||T}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight= +h;n(e,function(b){a.positionItem(b)});f&&d.align(v({width:g,height:h},j),!0,b.spacingBox);b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h=this.clipRect,i=e.navigation,j=p(i.animation,!0),k=i.arrowSize||12,l=this.nav;e.layout==="horizontal"&&(f/=2);g&&(f=P(f,g));if(a>f){this.clipHeight=c=f-20;this.pageCount=xa(a/c);this.currentPage=p(this.currentPage, +1);this.fullHeight=a;h.attr({height:c});if(!l)this.nav=l=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,k,k).on("click",function(){b.scroll(-1,j)}).add(l),this.pager=d.text("",15,10).css(i.style).add(l),this.down=d.symbol("triangle-down",0,0,k,k).on("click",function(){b.scroll(1,j)}).add(l);b.scroll(0);a=f}else l&&(h.attr({height:c.chartHeight}),l.hide(),this.scrollGroup.attr({translateY:1}));return a},scroll:function(a,b){var c=this.pageCount,d=this.currentPage+a,e=this.clipHeight, +f=this.options.navigation,g=f.activeColor,f=f.inactiveColor,h=this.pager,i=this.padding;d>c&&(d=c);if(d>0)b!==x&&va(b,this.chart),this.nav.attr({translateX:i,translateY:e+7,visibility:"visible"}),this.up.attr({fill:d===1?f:g}).css({cursor:d===1?"default":"pointer"}),h.attr({text:d+"/"+this.pageCount}),this.down.attr({x:18+this.pager.getBBox().width,fill:d===c?f:g}).css({cursor:d===c?"default":"pointer"}),this.scrollGroup.animate({translateY:-P(e*(d-1),this.fullHeight-e+i)+1}),h.attr({text:d+"/"+c}), +this.currentPage=d}};qb.prototype={initSeries:function(a){var b=this.options.chart,b=new ba[a.type||b.type||b.defaultSeriesType];b.init(this,a);return b},addSeries:function(a,b,c){var d,e=this;a&&(va(c,e),b=p(b,!0),H(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;b&&e.redraw()}));return d},isInsidePlot:function(a,b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&n(this.axes, +function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.tracker,e=this.legend,f=this.isDirtyLegend,g,h=this.isDirtyBox,i=c.length,j=i,k=this.renderer,l=k.isHidden();va(a,this);for(l&&this.cloneRenderTo();j--;)if(a=c[j],a.isDirty&&a.options.stacking){g=!0;break}if(g)for(j=i;j--;)if(a=c[j],a.options.stacking)a.isDirty=!0;n(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1; +if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,n(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();n(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,H(a,"afterSetExtremes",a.getExtremes());if(a.isDirty||h||g)a.redraw(),h=!0})}h&&this.drawChartBox();n(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.resetTracker&&d.resetTracker(!0);k.draw();H(this,"redraw");l&&this.cloneRenderTo(!0)},showLoading:function(a){var b=this.options, +c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=S(ja,{className:"highcharts-loading"},v(d.style,{left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px",zIndex:10,display:T}),this.container),this.loadingSpan=S("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)G(c,{opacity:0,display:""}),vb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options, +b=this.loadingDiv;b&&vb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){G(b,{display:T})}});this.loadingShown=!1},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;dP(e.dataMin,e.min)&&c< +t(e.dataMax,e.max)&&b.setExtremes(f,c,!0,!1,{trigger:"pan"});this.mouseDownX=a;G(this.container,{cursor:"move"})},setTitle:function(a,b){var c=this,d=c.options,e;c.chartTitleOptions=e=A(d.title,a);c.chartSubtitleOptions=d=A(d.subtitle,b);n([["title",a,e],["subtitle",b,d]],function(a){var b=a[0],d=c[b],e=a[1],a=a[2];d&&e&&(c[b]=d=d.destroy());a&&a.text&&!d&&(c[b]=c.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align,"class":"highcharts-"+b,zIndex:a.zIndex||4}).css(a.style).add().align(a,!1,c.spacingBox))})}, +getChartSize:function(){var a=this.options.chart,b=this.renderToClone||this.renderTo;this.containerWidth=bb(b,"width");this.containerHeight=bb(b,"height");this.chartWidth=a.width||this.containerWidth||600;this.chartHeight=a.height||(this.containerHeight>19?this.containerHeight:400)},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Ma(b),delete this.renderToClone):(c&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0), +G(b,{position:"absolute",top:"-9999px",display:"block"}),z.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+sb++;if(na(a))this.renderTo=a=z.getElementById(a);a||Za(13,!0);a.innerHTML="";a.offsetWidth||this.cloneRenderTo();this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=S(ja,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},v({position:"relative",overflow:"hidden", +width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0},b.style),this.renderToClone||a);this.renderer=b.forExport?new sa(a,c,d,!0):new Pa(a,c,d);$&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.options.chart,b=a.spacingTop,c=a.spacingRight,d=a.spacingBottom,a=a.spacingLeft,e,f=this.legend,g=this.optionsMarginTop,h=this.optionsMarginLeft,i=this.optionsMarginRight,j=this.optionsMarginBottom,k=this.chartTitleOptions,l=this.chartSubtitleOptions,m=this.options.legend, +o=p(m.margin,10),q=m.x,s=m.y,r=m.align,y=m.verticalAlign;this.resetMargins();e=this.axisOffset;if((this.title||this.subtitle)&&!u(this.optionsMarginTop))if(l=t(this.title&&!k.floating&&!k.verticalAlign&&k.y||0,this.subtitle&&!l.floating&&!l.verticalAlign&&l.y||0))this.plotTop=t(this.plotTop,l+p(k.margin,15)+b);if(f.display&&!m.floating)if(r==="right"){if(!u(i))this.marginRight=t(this.marginRight,f.legendWidth-q+o+c)}else if(r==="left"){if(!u(h))this.plotLeft=t(this.plotLeft,f.legendWidth+q+o+a)}else if(y=== +"top"){if(!u(g))this.plotTop=t(this.plotTop,f.legendHeight+s+o+b)}else if(y==="bottom"&&!u(j))this.marginBottom=t(this.marginBottom,f.legendHeight-s+o+d);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin);this.hasCartesianSeries&&n(this.axes,function(a){a.getOffset()});u(h)||(this.plotLeft+=e[3]);u(g)||(this.plotTop+=e[0]);u(j)||(this.marginBottom+=e[2]);u(i)||(this.marginRight+=e[1]);this.setChartSize()},initReflow:function(){function a(a){var g= +c.width||bb(d,"width"),h=c.height||bb(d,"height"),a=a?a.target:M;if(g&&h&&(a===M||a===z)){if(g!==b.containerWidth||h!==b.containerHeight)clearTimeout(e),e=setTimeout(function(){b.resize(g,h,!1)},100);b.containerWidth=g;b.containerHeight=h}}var b=this,c=b.options.chart,d=b.renderTo,e;K(M,"resize",a);K(b,"destroy",function(){U(M,"resize",a)})},resize:function(a,b,c){var d=this,e,f,g=d.resetZoomButton,h=d.title,i=d.subtitle,j;d.isResizing+=1;j=function(){d&&H(d,"endResize",null,function(){d.isResizing-= +1})};va(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(u(a))d.chartWidth=e=s(a);if(u(b))d.chartHeight=f=s(b);G(d.container,{width:e+"px",height:f+"px"});d.renderer.setSize(e,f,c);d.plotWidth=e-d.plotLeft-d.marginRight;d.plotHeight=f-d.plotTop-d.marginBottom;d.maxTicks=null;n(d.axes,function(a){a.isDirty=!0;a.setScale()});n(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();a=d.spacingBox;h&&h.align(null,null,a);i&&i.align(null,null,a);g&&g.align(null, +null,d[g.alignTo]);d.redraw(c);d.oldChartHeight=null;H(d,"resize");Na===!1?j():setTimeout(j,Na&&Na.duration||500)},setChartSize:function(){var a=this.inverted,b=this.chartWidth,c=this.chartHeight,d=this.options.chart,e=d.spacingTop,f=d.spacingRight,g=d.spacingBottom,h=d.spacingLeft,i,j,k,l;this.plotLeft=i=s(this.plotLeft);this.plotTop=j=s(this.plotTop);this.plotWidth=k=s(b-i-this.marginRight);this.plotHeight=l=s(c-j-this.marginBottom);this.plotSizeX=a?l:k;this.plotSizeY=a?k:l;this.plotBorderWidth= +a=d.plotBorderWidth||0;this.spacingBox={x:h,y:e,width:b-h-f,height:c-e-g};this.plotBox={x:i,y:j,width:k,height:l};this.clipBox={x:a/2,y:a/2,width:this.plotSizeX-a,height:this.plotSizeY-a};n(this.axes,function(a){a.setAxisSize();a.setAxisTranslation()})},resetMargins:function(){var a=this.options.chart,b=a.spacingRight,c=a.spacingBottom,d=a.spacingLeft;this.plotTop=p(this.optionsMarginTop,a.spacingTop);this.marginRight=p(this.optionsMarginRight,b);this.marginBottom=p(this.optionsMarginBottom,c);this.plotLeft= +p(this.optionsMarginLeft,d);this.axisOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,l=a.plotBackgroundImage,m=a.plotBorderWidth||0,o,p=this.plotLeft,n=this.plotTop,r=this.plotWidth,s=this.plotHeight,u=this.plotBox,t=this.clipRect,w=this.clipBox;o=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null, +null,null,c-o,d-o));else{e={fill:j||T};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(o/2,o/2,c-o,d-o,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f?f.animate(u):this.plotBackground=b.rect(p,n,r,s,0).attr({fill:k}).add().shadow(a.plotShadow);if(l)h?h.animate(u):this.plotBGImage=b.image(l,p,n,r,s).add();t?t.animate({width:w.width,height:w.height}):this.clipRect=b.clipRect(w);if(m)g?g.animate(g.crisp(null,p,n,r,s)):this.plotBorder=b.rect(p,n,r,s,0,m).attr({stroke:a.plotBorderColor, +"stroke-width":m,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular","polar"],function(g){c=ba[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=ba[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,d=d.credits,f;a.setTitle();a.legend=new pb(a);n(b,function(a){a.setScale()});a.getMargins();a.maxTicks= +null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()});if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&n(e.items,function(){var b=v(e.style,this.style),d=B(b.left)+a.plotLeft,f=B(b.top)+a.plotTop+12;delete b.left;delete b.top;c.text(this.html,d,f).attr({zIndex:2}).css(b).add()});if(d.enabled&& +!a.credits)f=d.href,a.credits=c.text(d.text,0,0).on("click",function(){if(f)location.href=f}).attr({align:d.position.align,zIndex:8}).css(d.style).add().align(d.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;if(a!==null){H(a,"destroy");U(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,tracker,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","), +function(b){var c=a[b];c&&(a[b]=c.destroy())});if(d)d.innerHTML="",U(d),f&&Ma(d),d=null;for(e in a)delete a[e];a=a.options=null}},firstRender:function(){var a=this,b=a.options,c=a.callback;if(!ga&&M==M.top&&z.readyState!=="complete"||$&&!M.canvg)$?Kb.push(function(){a.firstRender()},b.global.canvasToolsURL):z.attachEvent("onreadystatechange",function(){z.detachEvent("onreadystatechange",a.firstRender);z.readyState==="complete"&&a.firstRender()});else{a.getContainer();H(a,"init");if(Highcharts.RangeSelector&& +b.rangeSelector.enabled)a.rangeSelector=new Highcharts.RangeSelector(a);a.resetMargins();a.setChartSize();a.propFromSeries();a.getAxes();n(b.series||[],function(b){a.initSeries(b)});if(Highcharts.Scroller&&(b.navigator.enabled||b.scrollbar.enabled))a.scroller=new Highcharts.Scroller(a);a.tracker=new ob(a,b);a.render();a.renderer.draw();c&&c.apply(a,[a]);n(a.callbacks,function(b){b.apply(a,[a])});a.cloneRenderTo(!0);H(a,"load")}},init:function(a){var b=this.options.chart,c;b.reflow!==!1&&K(this,"load", +this.initReflow);if(a)for(c in a)K(this,c,a[c]);this.xAxis=[];this.yAxis=[];this.animation=$?!1:p(b.animation,!0);this.setSize=this.resize;this.pointCount=0;this.counters=new Db;this.firstRender()}};qb.prototype.callbacks=[];var Ra=function(){};Ra.prototype={init:function(a,b,c){var d=a.chart.counters;this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint){b=a.chart.options.colors;if(!this.options)this.options={};this.color=this.options.color=this.color||b[d.color++];d.wrapColor(b.length)}a.chart.pointCount++; +return this},applyOptions:function(a,b){var c=this.series,d=typeof a;this.config=a;if(d==="number"||a===null)this.y=a;else if(typeof a[0]==="number")this.x=a[0],this.y=a[1];else if(d==="object"&&typeof a.length!=="number"){v(this,a);this.options=a;if(a.dataLabels)c._hasPointLabels=!0;if(a.marker)c._hasPointMarkers=!0}else if(typeof a[0]==="string")this.name=a[0],this.y=a[1];if(this.x===x)this.x=b===x?c.autoIncrement():b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--; +if(b&&(this.setState(),za(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)U(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,tracker,dataLabel,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series, +point:this,percentage:this.percentage,total:this.total||this.stackTotal}},select:function(a,b){var c=this,d=c.series.chart,a=p(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=a;c.setState(a&&"select");b||n(d.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=!1,a.setState(""),a.firePointEvent("unselect")})})},onMouseOver:function(){var a=this.series,b=a.chart,c=b.tooltip,d=b.hoverPoint;if(d&&d!==this)d.onMouseOut();this.firePointEvent("mouseOver"); +c&&(!c.shared||a.noSharedTooltip)&&c.refresh(this);this.setState("hover");b.hoverPoint=this},onMouseOut:function(){var a=this.series.chart,b=a.hoverPoints;if(!b||Qb(this,b)===-1)this.firePointEvent("mouseOut"),this.setState(),a.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=a.match(/\{(series|point)\.[a-zA-Z]+\}/g),e=/[{\.}]/,f,g,h,i,j={y:0,open:0,high:0,low:0,close:0,percentage:1,total:1};c.valuePrefix=c.valuePrefix||c.yPrefix;c.valueDecimals=c.valueDecimals|| +c.yDecimals;c.valueSuffix=c.valueSuffix||c.ySuffix;for(i in d)g=d[i],na(g)&&g!==a&&(h=(" "+g).split(e),f={point:this,series:b}[h[1]],h=h[2],f===this&&j.hasOwnProperty(h)?(f=j[h]?h:"value",f=(c[f+"Prefix"]||"")+Ha(this[h],p(c[f+"Decimals"],-1))+(c[f+"Suffix"]||"")):f=f[h],a=a.replace(g,f));return a},update:function(a,b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,i=h.length,j=e.chart,b=p(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);Z(a)&&(e.getAttribs(),f&&f.attr(d.pointAttr[e.state])); +for(g=0;ga+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b= +this.chart.options,c=b.plotOptions,d=c[this.type],e=a.data;a.data=null;c=A(d,c.series,a);c.data=a.data=e;this.tooltipOptions=A(b.tooltip,c.tooltip);d.marker===null&&delete c.marker;return c},getColor:function(){var a=this.options,b=this.chart.options.colors,c=this.chart.counters;this.color=a.color||!a.colorByPoint&&b[c.color++]||"gray";c.wrapColor(b.length)},getSymbol:function(){var a=this.options.marker,b=this.chart,c=b.options.symbols,b=b.counters;this.symbol=a.symbol||c[b.symbol++];if(/^url/.test(this.symbol))a.radius= +0;b.wrapSymbol(c.length)},drawLegendSymbol:function(a){var b=this.options,c=b.marker,d=a.options.symbolWidth,e=this.chart.renderer,f=this.legendGroup,a=a.baseline,g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a-4,"L",d,a-4]).attr(g).add(f)}if(c&&c.enabled)b=c.radius,this.legendSymbol=e.symbol(this.symbol,d/2-b,a-4-b,2*b,2*b).attr(this.pointAttr[""]).add(f)},addPoint:function(a,b,c,d){var e=this.data,f=this.graph,g=this.area,h= +this.chart,i=this.xData,j=this.yData,k=f&&f.shift||0,l=this.options.data,m=this.pointClass.prototype;va(d,h);if(f&&c)f.shift=k+1;if(g){if(c)g.shift=k+1;g.isArea=!0}b=p(b,!0);d={series:this};m.applyOptions.apply(d,[a]);i.push(d.x);j.push(m.toYData?m.toYData.call(d):d.y);l.push(a);c&&(e[0]&&e[0].remove?e[0].remove(!1):(e.shift(),i.shift(),j.shift(),l.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&h.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.initialColor, +f=this.chart,g=null,h=this.xAxis,i,j=this.pointClass.prototype;this.xIncrement=null;this.pointRange=h&&h.categories?1:d.pointRange;if(u(e))f.counters.color=e;var e=[],k=[],l=a?a.length:[],m=(i=this.pointArrayMap)&&i.length;if(l>(d.turboThreshold||1E3)){for(i=0;g===null&&ik||this.forceCrop))if(a=i.getExtremes(),i=a.min,k=a.max,b[d-1]k)b=[],c=[];else if(b[0]k){for(a=0;a=i){e=t(0,a-1);break}for(;ak){f=a+1;break}b=b.slice(e,f);c=c.slice(e, +f);g=!0}for(a=b.length-1;a>0;a--)if(d=b[a]-b[a-1],d>0&&(h===x||d=0&&c<=d;)i[c++]= +g}this.tooltipPoints=i}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat,d=this.xAxis,e=d&&d.options.type==="datetime",f;if(e&&!c)for(f in F)if(F[f]>=d.closestPointRange){c=b.dateTimeLabelFormats[f];break}return b.headerFormat.replace("{point.key}",e?ab(c,a):a).replace("{series.name}",this.name).replace("{series.color}",this.color)},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&H(this,"mouseOver"); +this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&H(this,"mouseOut");c&&!a.stickyTracking&&!c.shared&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!Z(e))e=Y[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h],e=c[h+"m"],a||(c[h]=a=d.clipRect(v(f,{width:0})), +c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX},e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()},e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group,d=this.trackerGroup;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip());d&&d.clip(a.clipRect); +setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,l=this.options.marker,m,o=this.markerGroup;if(l.enabled||this._hasPointMarkers)for(f=b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,k=g.graphic,i=g.marker||{},a=l.enabled&&i.enabled===x||i.enabled,m=c.isInsidePlot(d,e,c.inverted),a&&e!==x&&!isNaN(e))if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=p(i.symbol,this.symbol),j=i.indexOf("url")===0, +k)k.attr({visibility:m?ga?"inherit":"visible":"hidden"}).animate(v({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else if(m&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(o)},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=p(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=Y[a.type].marker?a.options.marker:a.options,c=b.states,d=c.hover,e,f=a.color,g={stroke:f,fill:f},h=a.points|| +[],i=[],j,k=a.pointAttrToOptions,l;a.options.marker?(d.radius=d.radius||b.radius+2,d.lineWidth=d.lineWidth||b.lineWidth+1):d.color=d.color||ra(d.color||f).brighten(d.brightness).get();i[""]=a.convertAttribs(b,g);n(["hover","select"],function(b){i[b]=a.convertAttribs(c[b],i[""])});a.pointAttr=i;for(f=h.length;f--;){g=h[f];if((b=g.options&&g.options.marker||g.options)&&b.enabled===!1)b.radius=0;e=!1;if(g.options)for(l in k)u(b[k[l]])&&(e=!0);if(e){j=[];c=b.states||{};e=c.hover=c.hover||{};if(!a.options.marker)e.color= +ra(e.color||g.options.color).brighten(e.brightness||d.brightness).get();j[""]=a.convertAttribs(b,i[""]);j.hover=a.convertAttribs(c.hover,i.hover,j[""]);j.select=a.convertAttribs(c.select,i.select,j[""])}else j=i;g.pointAttr=j}},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(Ca),d,e,f=a.data||[],g,h,i;H(a,"destroy");U(a);n(["xAxis","yAxis"],function(b){if(i=a[b])za(i.series,a),i.isDirty=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy(); +a.points=null;clearTimeout(a.animationTimeout);n("area,graph,dataLabelsGroup,group,markerGroup,tracker,trackerGroup".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;za(b.series,a);for(h in a)delete a[h]},drawDataLabels:function(){var a=this,b=a.options,c=b.dataLabels;if(c.enabled||a._hasPointLabels){var d,e,f=a.points,g,h,i,j,k=a.chart,l=k.renderer,m=k.inverted,o=a.type,q=b.stacking,t=o==="column"||o==="bar",r=c.verticalAlign=== +null,y=c.y===null,v=l.fontMetrics(c.style.fontSize),E=v.h,w=v.b,D,J;t&&(v={top:w,middle:w-E/2,bottom:-E+w},q?(r&&(c=A(c,{verticalAlign:"middle"})),y&&(c=A(c,{y:v[c.verticalAlign]}))):r?c=A(c,{verticalAlign:"top"}):y&&(c=A(c,{y:v[c.verticalAlign]})));j=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",6);h=c;n(f,function(f){D=f.dataLabel;c=h;(g=f.options)&&g.dataLabels&&(c=A(c,g.dataLabels));if(J=c.enabled){var n=f.barX&&f.barX+f.barW/2||p(f.plotX,-999),r=p(f.plotY,-999),y=c.y=== +null?f.y>=b.threshold?-E+w:w:c.y;d=(m?k.plotWidth-r:n)+c.x;e=s((m?k.plotHeight-n:r)+y)}if(D&&a.isCartesian&&(!k.isInsidePlot(d,e)||!J))f.dataLabel=D.destroy();else if(J){var n=c.align,v;i=c.formatter.call(f.getLabelConfig(),c);o==="column"&&(d+={left:-1,right:1}[n]*f.barW/2||0);!q&&m&&f.y<0&&(n="right",d-=10);c.style.color=p(c.color,c.style.color,a.color,"black");if(D)D.attr({text:i}).animate({x:d,y:e});else if(u(i)){n={align:n,fill:c.backgroundColor,stroke:c.borderColor,"stroke-width":c.borderWidth, +r:c.borderRadius||0,rotation:c.rotation,padding:c.padding,zIndex:1};for(v in n)n[v]===x&&delete n[v];D=f.dataLabel=l[c.rotation?"text":"label"](i,d,e,null,null,null,c.useHTML,!0).attr(n).css(c.style).add(j).shadow(c.shadow)}if(t&&b.stacking&&D)v=f.barX,n=f.barY,r=f.barW,f=f.barH,D.align(c,null,{x:m?k.plotWidth-n-f:v,y:m?k.plotHeight-v-r:n,width:m?f:r,height:m?r:f})}})}},getSegmentPath:function(a){var b=this,c=[];n(a,function(d,e){b.getPointSpline?c.push.apply(c,b.getPointSpline(a,d,e)):(c.push(e? +"L":"M"),e&&b.options.step&&c.push(d.plotX,a[e-1].plotY),c.push(d.plotX,d.plotY))});return c},getGraphPath:function(){var a=this,b=[],c,d=[];n(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this.options,b=this.graph,c=this.group,d=a.lineColor||this.color,e=a.lineWidth,f=a.dashStyle,g=this.getGraphPath();if(b)cb(b),b.animate({d:g});else if(e){b={stroke:d,"stroke-width":e,zIndex:1};if(f)b.dashstyle= +f;this.graph=this.chart.renderer.path(g).attr(b).add(c).shadow(a.shadow)}},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};n(["group","trackerGroup","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;K(c,"resize",a);K(b,"destroy",function(){U(c,"resize",a)});a();b.invertGroups=a},plotGroup:function(a,b,c,d,e){var f=this[a],g=this.chart,h=this.xAxis,i=this.yAxis;f||(this[a]=f=g.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f.translate(h? +h.left:g.plotLeft,i?i.top:g.plotTop);return f},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate,e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group","series",e,f,h);this.markerGroup=this.plotGroup("markerGroup","markers",e,f,h);this.drawDataLabels();d&&this.animate(!0);this.getAttribs();b.inverted=a.inverted;this.drawGraph&&this.drawGraph();this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker(); +a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&&!g&&b.clip(a.clipRect);d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:this.xAxis.left,translateY:this.yAxis.top}));this.translate();this.setTooltipPoints(!0);this.render();b&&H(this,"updatedData")},setState:function(a){var b=this.options, +c=this.graph,d=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,d[a]&&d[a].enabled===!1||(a&&(b=d[a].lineWidth||b+1),c&&!c.dashstyle&&c.attr({"stroke-width":b},a?0:500))},setVisible:function(a,b){var c=this.chart,d=this.legendItem,e=this.group,f=this.tracker,g=this.dataLabelsGroup,h=this.markerGroup,i,j=this.points,k=c.options.chart.ignoreHiddenSeries;i=this.visible;i=(this.visible=a=a===x?!i:a)?"show":"hide";if(e)e[i]();if(h)h[i]();if(f)f[i]();else if(j)for(e=j.length;e--;)if(f=j[e], +f.tracker)f.tracker[i]();if(g)g[i]();d&&c.legend.colorizeItem(this,a);this.isDirty=!0;this.options.stacking&&n(c.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});if(k)c.isDirtyBox=!0;b!==!1&&c.redraw();H(this,i)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===x?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;H(this,a?"select":"unselect")},drawTracker:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c? +a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.renderer,h=f.options.tooltip.snap,i=a.tracker,j=b.cursor,j=j&&{cursor:j},k=a.singlePoints,l=this.isCartesian&&this.plotGroup("trackerGroup",null,"visible",b.zIndex||1,f.trackerGroup),m;if(e&&!c)for(m=e+1;m--;)d[m]==="M"&&d.splice(m+1,0,d[m+1]-h,d[m+2],"L"),(m&&d[m]==="M"||m===e)&&d.splice(m,0,"L",d[m-2]+h,d[m-1]);for(m=0;m=0;d--)da&&i>e?(i=t(a,e),k=2*e-i):ig&&k>e?(k=t(g,e),i=2*e-k):kw?g-w:E-(f<=E?w:0));v(c,{barX:h,barY:i,barW:y,barH:j,pointWidth:r}); +c.shapeType="rect";c.shapeArgs=f=b.renderer.Element.prototype.crisp.call(0,e,h,i,y,j);e%2&&(f.y-=1,f.height+=1);c.trackerArgs=N(j)<3&&A(c.shapeArgs,{height:6,y:i-3})})},getSymbol:Ea,drawLegendSymbol:I.prototype.drawLegendSymbol,drawGraph:Ea,drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d,e;n(a.points,function(f){var g=f.plotY;if(g!==x&&!isNaN(g)&&f.y!==null)d=f.graphic,e=f.shapeArgs,d?(cb(d),d.animate(A(e))):f.graphic=d=c[f.shapeType](e).attr(f.pointAttr[f.selected?"select":""]).add(a.group).shadow(b.shadow, +null,b.stacking&&!b.borderRadius)})},drawTracker:function(){var a=this,b=a.chart,c=b.renderer,d,e,f=+new Date,g=a.options,h=g.cursor,i=h&&{cursor:h},j=a.isCartesian&&a.plotGroup("trackerGroup",null,"visible",g.zIndex||1,b.trackerGroup),k,l,m;n(a.points,function(h){e=h.tracker;d=h.trackerArgs||h.shapeArgs;l=h.plotY;m=!a.isCartesian||l!==x&&!isNaN(l);delete d.strokeWidth;if(h.y!==null&&m)e?e.attr(d):h.tracker=c[h.shapeType](d).attr({isTracker:f,fill:ub,visibility:a.visible?"visible":"hidden"}).on(ha? +"touchstart":"mouseover",function(c){k=c.relatedTarget||c.fromElement;if(b.hoverSeries!==a&&C(k,"isTracker")!==f)a.onMouseOver();h.onMouseOver()}).on("mouseout",function(b){if(!g.stickyTracking&&(k=b.relatedTarget||b.toElement,C(k,"isTracker")!==f))a.onMouseOut()}).css(i).add(h.group||j)})},animate:function(a){var b=this,c=b.points,d=b.options;if(!a)n(c,function(a){var c=a.graphic,a=a.shapeArgs,g=b.yAxis,h=d.threshold;c&&(c.attr({height:0,y:u(h)?g.getThreshold(h):g.translate(g.getExtremes().min,0, +1,0,1)}),c.animate({height:a.height,y:a.y},d.animation))}),b.animate=null},remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});R.prototype.remove.apply(a,arguments)}});ba.column=Ea;Y.bar=A(Y.column,{dataLabels:{align:"left",x:5,y:null,verticalAlign:"middle"}});ia=ea(Ea,{type:"bar",inverted:!0});ba.bar=ia;Y.scatter=A(X,{lineWidth:0,states:{hover:{lineWidth:0}},tooltip:{headerFormat:'{series.name}
    ', +pointFormat:"x: {point.x}
    y: {point.y}
    "}});ia=ea(R,{type:"scatter",sorted:!1,translate:function(){var a=this;R.prototype.translate.apply(a);n(a.points,function(b){b.shapeType="circle";b.shapeArgs={x:b.plotX,y:b.plotY,r:a.chart.options.tooltip.snap}})},drawTracker:function(){for(var a=this,b=a.options.cursor,b=b&&{cursor:b},c=a.points,d=c.length,e;d--;)if(e=c[d].graphic)e.element._i=d;a._hasTracking?a._hasTracking=!0:a.markerGroup.attr({isTracker:!0}).on(ha?"touchstart":"mouseover", +function(b){a.onMouseOver();if(b.target._i!==x)c[b.target._i].onMouseOver()}).on("mouseout",function(){if(!a.options.stickyTracking)a.onMouseOut()}).css(b)}});ba.scatter=ia;Y.pie=A(X,{borderColor:"#FFFFFF",borderWidth:1,center:["50%","50%"],colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name},y:5},legendType:"point",marker:null,size:"75%",showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}}});X={type:"pie",isCartesian:!1,pointClass:ea(Ra, +{init:function(){Ra.prototype.init.apply(this,arguments);var a=this,b;v(a,{visible:a.visible!==!1,name:p(a.name,"Slice")});b=function(){a.slice()};K(a,"select",b);K(a,"unselect",b);return a},setVisible:function(a){var b=this.series.chart,c=this.tracker,d=this.dataLabel,e=this.connector,f=this.shadowGroup,g;g=(this.visible=a=a===x?!this.visible:a)?"show":"hide";this.group[g]();if(c)c[g]();if(d)d[g]();if(e)e[g]();if(f)f[g]();this.legendItem&&b.legend.colorizeItem(this,a)},slice:function(a,b,c){var d= +this.series.chart,e=this.slicedTranslation;va(c,d);p(b,!0);a=this.sliced=u(a)?a:!this.sliced;a={translateX:a?e[0]:d.plotLeft,translateY:a?e[1]:d.plotTop};this.group.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color"},getColor:function(){this.initialColor=this.chart.counters.color},animate:function(){var a=this;n(a.points,function(b){var c=b.graphic,b=b.shapeArgs,d=-ya/2;c&&(c.attr({r:0,start:d,end:d}),c.animate({r:b.r, +start:b.start,end:b.end},a.options.animation))});a.animate=null},setData:function(a,b){R.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();p(b,!0)&&this.chart.redraw()},getCenter:function(){var a=this.options,b=this.chart,c=b.plotWidth,d=b.plotHeight,a=a.center.concat([a.size,a.innerSize||0]),e=P(c,d),f;return Qa(a,function(a,b){return(f=/%$/.test(a))?[c,d,e,e][b]*B(a)/100:a})},translate:function(){this.generatePoints();var a=0,b=-0.25,c=this.options,d=c.slicedOffset,e=d+ +c.borderWidth,f,g=this.chart,h,i,j,k=this.points,l=2*ya,m,o,p,t=c.dataLabels.distance;this.center=f=this.getCenter();this.getX=function(a,b){j=L.asin((a-f[1])/(f[2]/2+t));return f[0]+(b?-1:1)*W(j)*(f[2]/2+t)};n(k,function(b){a+=b.y});n(k,function(c){m=a?c.y/a:0;h=s(b*l*1E3)/1E3;b+=m;i=s(b*l*1E3)/1E3;c.shapeType="arc";c.shapeArgs={x:f[0],y:f[1],r:f[2]/2,innerR:f[3]/2,start:h,end:i};j=(i+h)/2;c.slicedTranslation=Qa([W(j)*d+g.plotLeft,aa(j)*d+g.plotTop],s);o=W(j)*f[2]/2;p=aa(j)*f[2]/2;c.tooltipPos=[f[0]+ +o*0.7,f[1]+p*0.7];c.labelPos=[f[0]+o+W(j)*t,f[1]+p+aa(j)*t,f[0]+o+W(j)*e,f[1]+p+aa(j)*e,f[0]+o,f[1]+p,t<0?"center":j0,q=[[],[]],s,r,t,u,v=2,w;if(d.enabled||this._hasPointLabels){R.prototype.drawDataLabels.apply(this);n(a,function(a){a.dataLabel&&q[a.labelPos[7]0){for(w=m-l-j;w<= +m+l+j;w+=a)x.push(w);t=x.length;if(C>t){h=[].concat(B);h.sort(u);for(w=C;w--;)h[w].rank=w;for(w=C;w--;)B[w].rank>=t&&B.splice(w,1);C=B.length}for(w=0;w0){if(r=A.pop(),z=r.i,r=r.y,s>r&&x[z+1]!==null|| +s=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bR[a]=c,c}function ch(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||cd.test(a)?d(a,e):ch(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ch(a+"["+e+"]",b[e],c,d);else d(a,b)}function cy(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.0",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
    a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
    t
    ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
    ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.shift(),e=p._queueHooks(a,b),f=function(){p.dequeue(a,b)};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),delete e.stop,d.call(a,f,e)),!c.length&&e&&e.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;jq&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bd(a,b,c,d){var e=0,f=b.length;for(;e0?h(g,c,f):[]}function bf(a,c,d,e,f){var g,h,i,j,k,l,m,n,p=0,q=f.length,s=L.POS,t=new RegExp("^"+s.source+"(?!"+r+")","i"),u=function(){var a=1,c=arguments.length-2;for(;ai){m=a.slice(i,g.index),i=n,l=[c],B.test(m)&&(k&&(l=k),k=e);if(h=H.test(m))m=m.slice(0,-5).replace(B,"$&*");g.length>1&&g[0].replace(t,u),k=be(m,g[1],g[2],l,k,h)}}k?(j=j.concat(k),(m=a.slice(i))&&m!==")"?B.test(m)?bd(m,j,d,e):Z(m,c,d,e?e.concat(k):k):o.apply(d,j)):Z(a,c,d,e)}return q===1?d:Z.uniqueSort(d)}function bg(a,b,c){var d,e,f,g=[],i=0,j=D.exec(a),k=!j.pop()&&!j.pop(),l=k&&a.match(C)||[""],m=$.preFilter,n=$.filter,o=!c&&b!==h;for(;(e=l[i])!=null&&k;i++){g.push(d=[]),o&&(e=" "+e);while(e){k=!1;if(j=B.exec(e))e=e.slice(j[0].length),k=d.push({part:j.pop().replace(A," "),captures:j});for(f in n)(j=L[f].exec(e))&&(!m[f]||(j=m[f](j,b,c)))&&(e=e.slice(j.shift().length),k=d.push({part:f,captures:j}));if(!k)break}}return k||Z.error(a),g}function bh(a,b,e){var f=b.dir,g=m++;return a||(a=function(a){return a===e}),b.first?function(b,c){while(b=b[f])if(b.nodeType===1)return a(b,c)&&b}:function(b,e){var h,i=g+"."+d,j=i+"."+c;while(b=b[f])if(b.nodeType===1){if((h=b[q])===j)return b.sizset;if(typeof h=="string"&&h.indexOf(i)===0){if(b.sizset)return b}else{b[q]=j;if(a(b,e))return b.sizset=!0,b;b.sizset=!1}}}}function bi(a,b){return a?function(c,d){var e=b(c,d);return e&&a(e===!0?c:e,d)}:b}function bj(a,b,c){var d,e,f=0;for(;d=a[f];f++)$.relative[d.part]?e=bh(e,$.relative[d.part],b):(d.captures.push(b,c),e=bi(e,$.filter[d.part].apply(null,d.captures)));return e}function bk(a){return function(b,c){var d,e=0;for(;d=a[e];e++)if(d(b,c))return!0;return!1}}var c,d,e,f,g,h=a.document,i=h.documentElement,j="undefined",k=!1,l=!0,m=0,n=[].slice,o=[].push,q=("sizcache"+Math.random()).replace(".",""),r="[\\x20\\t\\r\\n\\f]",s="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",t=s.replace("w","w#"),u="([*^$|!~]?=)",v="\\["+r+"*("+s+")"+r+"*(?:"+u+r+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+t+")|)|)"+r+"*\\]",w=":("+s+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",x=":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",y=r+"*([\\x20\\t\\r\\n\\f>+~])"+r+"*",z="(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|"+v+"|"+w.replace(2,7)+"|[^\\\\(),])+",A=new RegExp("^"+r+"+|((?:^|[^\\\\])(?:\\\\.)*)"+r+"+$","g"),B=new RegExp("^"+y),C=new RegExp(z+"?(?="+r+"*,|$)","g"),D=new RegExp("^(?:(?!,)(?:(?:^|,)"+r+"*"+z+")*?|"+r+"*(.*?))(\\)|$)"),E=new RegExp(z.slice(19,-6)+"\\x20\\t\\r\\n\\f>+~])+|"+y,"g"),F=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,G=/[\x20\t\r\n\f]*[+~]/,H=/:not\($/,I=/h\d/i,J=/input|select|textarea|button/i,K=/\\(?!\\)/g,L={ID:new RegExp("^#("+s+")"),CLASS:new RegExp("^\\.("+s+")"),NAME:new RegExp("^\\[name=['\"]?("+s+")['\"]?\\]"),TAG:new RegExp("^("+s.replace("[-","[-\\*")+")"),ATTR:new RegExp("^"+v),PSEUDO:new RegExp("^"+w),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+r+"*(even|odd|(([+-]|)(\\d*)n|)"+r+"*(?:([+-]|)"+r+"*(\\d+)|))"+r+"*\\)|)","i"),POS:new RegExp(x,"ig"),needsContext:new RegExp("^"+r+"*[>+~]|"+x,"i")},M={},N=[],O={},P=[],Q=function(a){return a.sizzleFilter=!0,a},R=function(a){return function(b){return b.nodeName.toLowerCase()==="input"&&b.type===a}},S=function(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}},T=function(a){var b=!1,c=h.createElement("div");try{b=a(c)}catch(d){}return c=null,b},U=T(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),V=T(function(a){a.id=q+0,a.innerHTML="
    ",i.insertBefore(a,i.firstChild);var b=h.getElementsByName&&h.getElementsByName(q).length===2+h.getElementsByName(q+0).length;return g=!h.getElementById(q),i.removeChild(a),b}),W=T(function(a){return a.appendChild(h.createComment("")),a.getElementsByTagName("*").length===0}),X=T(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==j&&a.firstChild.getAttribute("href")==="#"}),Y=T(function(a){return a.innerHTML="",!a.getElementsByClassName||a.getElementsByClassName("e").length===0?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length!==1)}),Z=function(a,b,c,d){c=c||[],b=b||h;var e,f,g,i,j=b.nodeType;if(j!==1&&j!==9)return[];if(!a||typeof a!="string")return c;g=ba(b);if(!g&&!d)if(e=F.exec(a))if(i=e[1]){if(j===9){f=b.getElementById(i);if(!f||!f.parentNode)return c;if(f.id===i)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(i))&&bb(b,f)&&f.id===i)return c.push(f),c}else{if(e[2])return o.apply(c,n.call(b.getElementsByTagName(a),0)),c;if((i=e[3])&&Y&&b.getElementsByClassName)return o.apply(c,n.call(b.getElementsByClassName(i),0)),c}return bm(a,b,c,d,g)},$=Z.selectors={cacheLength:50,match:L,order:["ID","TAG"],attrHandle:{},createPseudo:Q,find:{ID:g?function(a,b,c){if(typeof b.getElementById!==j&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==j&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==j&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:W?function(a,b){if(typeof b.getElementsByTagName!==j)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(K,""),a[3]=(a[4]||a[5]||"").replace(K,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||Z.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&Z.error(a[0]),a},PSEUDO:function(a){var b,c=a[4];return L.CHILD.test(a[0])?null:(c&&(b=D.exec(c))&&b.pop()&&(a[0]=a[0].slice(0,b[0].length-c.length-1),c=b[0].slice(0,-1)),a.splice(2,3,c||a[3]),a)}},filter:{ID:g?function(a){return a=a.replace(K,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(K,""),function(b){var c=typeof b.getAttributeNode!==j&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(K,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=M[a];return b||(b=M[a]=new RegExp("(^|"+r+")"+a+"("+r+"|$)"),N.push(a),N.length>$.cacheLength&&delete M[N.shift()]),function(a){return b.test(a.className||typeof a.getAttribute!==j&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=Z.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return Z.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=m++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[q]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[q]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e=$.pseudos[a]||$.pseudos[a.toLowerCase()];return e||Z.error("unsupported pseudo: "+a),e.sizzleFilter?e(b,c,d):e}},pseudos:{not:Q(function(a,b,c){var d=bl(a.replace(A,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!$.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:Q(function(a){return function(b){return(b.textContent||b.innerText||bc(b)).indexOf(a)>-1}}),has:Q(function(a){return function(b){return Z(a,b).length>0}}),header:function(a){return I.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:R("radio"),checkbox:R("checkbox"),file:R("file"),password:R("password"),image:R("image"),submit:S("submit"),reset:S("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return J.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e$.cacheLength&&delete O[P.shift()],g};Z.matches=function(a,b){return Z(a,null,null,b)},Z.matchesSelector=function(a,b){return Z(b,null,null,[a]).length>0};var bm=function(a,b,e,f,g){a=a.replace(A,"$1");var h,i,j,k,l,m,p,q,r,s=a.match(C),t=a.match(E),u=b.nodeType;if(L.POS.test(a))return bf(a,b,e,f,s);if(f)h=n.call(f,0);else if(s&&s.length===1){if(t.length>1&&u===9&&!g&&(s=L.ID.exec(t[0]))){b=$.find.ID(s[1],b,g)[0];if(!b)return e;a=a.slice(t.shift().length)}q=(s=G.exec(t[0]))&&!s.index&&b.parentNode||b,r=t.pop(),m=r.split(":not")[0];for(j=0,k=$.order.length;j",a.querySelectorAll("[selected]").length||e.push("\\["+r+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="

    ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+r+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bm=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return o.apply(f,n.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j=d.getAttribute("id"),k=j||q,l=G.test(a)&&d.parentNode||d;j?k=k.replace(c,"\\$&"):d.setAttribute("id",k);try{return o.apply(f,n.call(l.querySelectorAll(a.replace(C,"[id='"+k+"'] $&")),0)),f}catch(i){}finally{j||d.removeAttribute("id")}}return b(a,d,f,g,h)},g&&(T(function(b){a=g.call(b,"div");try{g.call(b,"[test!='']:sizzle"),f.push($.match.PSEUDO)}catch(c){}}),f=new RegExp(f.join("|")),Z.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!ba(b)&&!f.test(c)&&(!e||!e.test(c)))try{var h=g.call(b,c);if(h||a||b.document&&b.document.nodeType!==11)return h}catch(i){}return Z(c,null,null,[b]).length>0})}(),Z.attr=p.attr,p.find=Z,p.expr=Z.selectors,p.expr[":"]=p.expr.pseudos,p.unique=Z.uniqueSort,p.text=Z.getText,p.isXMLDoc=Z.isXML,p.contains=Z.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
    ","
    "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=0,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(g=b===e&&bA;(h=a[s])!=null;s++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{g=g||bk(b),l=l||g.appendChild(b.createElement("div")),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(f=n.length-1;f>=0;--f)p.nodeName(n[f],"tbody")&&!n[f].childNodes.length&&n[f].parentNode.removeChild(n[f])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l=g.lastChild}h.nodeType?t.push(h):t=p.merge(t,h)}l&&(g.removeChild(l),h=l=g=null);if(!p.support.appendChecked)for(s=0;(h=t[s])!=null;s++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(s=0;(h=t[s])!=null;s++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[s+1,0].concat(r)),s+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^margin/,bO=new RegExp("^("+q+")(.*)$","i"),bP=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bQ=new RegExp("^([-+])=("+q+")","i"),bR={},bS={position:"absolute",visibility:"hidden",display:"block"},bT={letterSpacing:0,fontWeight:400,lineHeight:1},bU=["Top","Right","Bottom","Left"],bV=["Webkit","O","Moz","ms"],bW=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return bZ(this,!0)},hide:function(){return bZ(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bW.apply(this,arguments):this.each(function(){(c?a:bY(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bX(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bQ.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bX(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bT&&(f=bT[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(a,b){var c,d,e,f,g=getComputedStyle(a,null),h=a.style;return g&&(c=g[b],c===""&&!p.contains(a.ownerDocument.documentElement,a)&&(c=p.style(a,b)),bP.test(c)&&bN.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=c,c=g.width,h.width=d,h.minWidth=e,h.maxWidth=f)),c}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bP.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0||bH(a,"display")!=="none"?ca(a,b,d):p.swap(a,bS,function(){return ca(a,b,d)})},set:function(a,c,d){return b$(a,c,d?b_(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bP.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bU[d]+b]=e[d]||e[d-2]||e[0];return f}},bN.test(a)||(p.cssHooks[a+b].set=b$)});var cc=/%20/g,cd=/\[\]$/,ce=/\r?\n/g,cf=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,cg=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||cg.test(this.nodeName)||cf.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(ce,"\r\n")}}):{name:b.name,value:c.replace(ce,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ch(d,a[d],c,f);return e.join("&").replace(cc,"+")};var ci,cj,ck=/#.*$/,cl=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cm=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,cn=/^(?:GET|HEAD)$/,co=/^\/\//,cp=/\?/,cq=/)<[^<]*)*<\/script>/gi,cr=/([?&])_=[^&]*/,cs=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,ct=p.fn.load,cu={},cv={},cw=["*/"]+["*"];try{ci=f.href}catch(cx){ci=e.createElement("a"),ci.href="",ci=ci.href}cj=cs.exec(ci.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&ct)return ct.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
    ").append(a.replace(cq,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cA(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cA(a,b),a},ajaxSettings:{url:ci,isLocal:cm.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cw},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cy(cu),ajaxTransport:cy(cv),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cB(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cC(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cl.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(ck,"").replace(co,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=cs.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==cj[1]&&i[2]==cj[2]&&(i[3]||(i[1]==="http:"?80:443))==(cj[3]||(cj[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cz(cu,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!cn.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cp.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cr,"$1_="+z);l.url=A+(A===l.url?(cp.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cw+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cz(cv,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cD=[],cE=/\?/,cF=/(=)\?(?=&|$)|\?\?/,cG=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cD.pop()||p.expando+"_"+cG++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cF.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cF.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cF,"$1"+f):m?c.data=i.replace(cF,"$1"+f):k&&(c.url+=(cE.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cD.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cH,cI=a.ActiveXObject?function(){for(var a in cH)cH[a](0,1)}:!1,cJ=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cK()||cL()}:cK,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cI&&delete cH[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cJ,cI&&(cH||(cH={},p(a).unload(cI)),cH[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cM,cN,cO=/^(?:toggle|show|hide)$/,cP=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cQ=/queueHooks$/,cR=[cX],cS={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cP.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cV,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c$.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c$.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=c_(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/public/javascripts/jquery-1.8.1.min.js b/public/javascripts/jquery-1.8.1.min.js new file mode 100644 index 0000000..ee68f35 --- /dev/null +++ b/public/javascripts/jquery-1.8.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v@1.8.1 jquery.com | jquery.org/license */ +(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.1",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
    a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
    t
    ",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
    ",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0),h[l]&&j.push(k);j.length&&t.push({elem:f,matches:j})}n.length>o&&t.push({elem:this,matches:n.slice(o)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function $(a,b,c,d){c=c||[],b=b||q;var e,f,g,j,k=b.nodeType;if(k!==1&&k!==9)return[];if(!a||typeof a!="string")return c;g=h(b);if(!g&&!d)if(e=L.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&i(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return u.apply(c,t.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&X&&b.getElementsByClassName)return u.apply(c,t.call(b.getElementsByClassName(j),0)),c}return bk(a,b,c,d,g)}function _(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function ba(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bb(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bc(a,b,c,d){var e,g,h,i,j,k,l,m,n,p,r=!c&&b!==q,s=(r?"":"")+a.replace(H,"$1"),u=y[o][s];if(u)return d?0:t.call(u,0);j=a,k=[],m=0,n=f.preFilter,p=f.filter;while(j){if(!e||(g=I.exec(j)))g&&(j=j.slice(g[0].length),h.selector=l),k.push(h=[]),l="",r&&(j=" "+j);e=!1;if(g=J.exec(j))l+=g[0],j=j.slice(g[0].length),e=h.push({part:g.pop().replace(H," "),string:g[0],captures:g});for(i in p)(g=S[i].exec(j))&&(!n[i]||(g=n[i](g,b,c)))&&(l+=g[0],j=j.slice(g[0].length),e=h.push({part:i,string:g.shift(),captures:g}));if(!e)break}return l&&(h.selector=l),d?j.length:j?$.error(a):t.call(y(s,k),0)}function bd(a,b,e,f){var g=b.dir,h=s++;return a||(a=function(a){return a===e}),b.first?function(b){while(b=b[g])if(b.nodeType===1)return a(b)&&b}:f?function(b){while(b=b[g])if(b.nodeType===1&&a(b))return b}:function(b){var e,f=h+"."+c,i=f+"."+d;while(b=b[g])if(b.nodeType===1){if((e=b[o])===i)return b.sizset;if(typeof e=="string"&&e.indexOf(f)===0){if(b.sizset)return b}else{b[o]=i;if(a(b))return b.sizset=!0,b;b.sizset=!1}}}}function be(a,b){return a?function(c){var d=b(c);return d&&a(d===!0?c:d)}:b}function bf(a,b,c){var d,e,g=0;for(;d=a[g];g++)f.relative[d.part]?e=bd(e,f.relative[d.part],b,c):e=be(e,f.filter[d.part].apply(null,d.captures.concat(b,c)));return e}function bg(a){return function(b){var c,d=0;for(;c=a[d];d++)if(c(b))return!0;return!1}}function bh(a,b,c,d){var e=0,f=b.length;for(;e0?i(h,c,g):[]}function bj(a,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q,r,s=0,t=a.length,v=S.POS,w=new RegExp("^"+v.source+"(?!"+A+")","i"),x=function(){var a=1,c=arguments.length-2;for(;al){g+=k.slice(l,n.index),l=p,q=[c],J.test(g)&&(m&&(q=m),m=e);if(r=O.test(g))g=g.slice(0,-5).replace(J,"$&*"),l++;n.length>1&&n[0].replace(w,x),m=bi(g,n[1],n[2],q,m,r)}g=""}}o||(g+=k),o=!1}g?J.test(g)?bh(g,m||[c],d,e):$(g,c,d,e?e.concat(m):m):u.apply(d,m)}return t===1?d:$.uniqueSort(d)}function bk(a,b,e,g,h){a=a.replace(H,"$1");var i,k,l,m,n,o,p,q,r,s,v=bc(a,b,h),w=b.nodeType;if(S.POS.test(a))return bj(v,b,e,g);if(g)i=t.call(g,0);else if(v.length===1){if((o=t.call(v[0],0)).length>2&&(p=o[0]).part==="ID"&&w===9&&!h&&f.relative[o[1].part]){b=f.find.ID(p.captures[0].replace(R,""),b,h)[0];if(!b)return e;a=a.slice(o.shift().string.length)}r=(v=N.exec(o[0].string))&&!v.index&&b.parentNode||b,q="";for(n=o.length-1;n>=0;n--){p=o[n],s=p.part,q=p.string+q;if(f.relative[s])break;if(f.order.test(s)){i=f.find[s](p.captures[0].replace(R,""),r,h);if(i==null)continue;a=a.slice(0,a.length-q.length)+q.replace(S[s],""),a||u.apply(e,t.call(i,0));break}}}if(a){k=j(a,b,h),c=k.dirruns++,i==null&&(i=f.find.TAG("*",N.test(a)&&b.parentNode||b));for(n=0;m=i[n];n++)d=k.runs++,k(m)&&e.push(m)}return e}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=a.document,r=q.documentElement,s=0,t=[].slice,u=[].push,v=function(a,b){return a[o]=b||!0,a},w=function(){var a={},b=[];return v(function(c,d){return b.push(c)>f.cacheLength&&delete a[b.shift()],a[c]=d},a)},x=w(),y=w(),z=w(),A="[\\x20\\t\\r\\n\\f]",B="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",C=B.replace("w","w#"),D="([*^$|!~]?=)",E="\\["+A+"*("+B+")"+A+"*(?:"+D+A+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+C+")|)|)"+A+"*\\]",F=":("+B+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+E+")|[^:]|\\\\.)*|.*))\\)|)",G=":(nth|eq|gt|lt|first|last|even|odd)(?:\\(((?:-\\d)?\\d*)\\)|)(?=[^-]|$)",H=new RegExp("^"+A+"+|((?:^|[^\\\\])(?:\\\\.)*)"+A+"+$","g"),I=new RegExp("^"+A+"*,"+A+"*"),J=new RegExp("^"+A+"*([\\x20\\t\\r\\n\\f>+~])"+A+"*"),K=new RegExp(F),L=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,M=/^:not/,N=/[\x20\t\r\n\f]*[+~]/,O=/:not\($/,P=/h\d/i,Q=/input|select|textarea|button/i,R=/\\(?!\\)/g,S={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),NAME:new RegExp("^\\[name=['\"]?("+B+")['\"]?\\]"),TAG:new RegExp("^("+B.replace("w","w*")+")"),ATTR:new RegExp("^"+E),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+A+"*(even|odd|(([+-]|)(\\d*)n|)"+A+"*(?:([+-]|)"+A+"*(\\d+)|))"+A+"*\\)|)","i"),POS:new RegExp(G,"ig"),needsContext:new RegExp("^"+A+"*[>+~]|"+G,"i")},T=function(a){var b=q.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},U=T(function(a){return a.appendChild(q.createComment("")),!a.getElementsByTagName("*").length}),V=T(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),W=T(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),X=T(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),Y=T(function(a){a.id=o+0,a.innerHTML="
    ",r.insertBefore(a,r.firstChild);var b=q.getElementsByName&&q.getElementsByName(o).length===2+q.getElementsByName(o+0).length;return e=!q.getElementById(o),r.removeChild(a),b});try{t.call(r.childNodes,0)[0].nodeType}catch(Z){t=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}$.matches=function(a,b){return $(a,null,null,b)},$.matchesSelector=function(a,b){return $(b,null,null,[a]).length>0},g=$.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=g(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=g(b);return c},h=$.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},i=$.contains=r.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:r.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},$.attr=function(a,b){var c,d=h(a);return d||(b=b.toLowerCase()),f.attrHandle[b]?f.attrHandle[b](a):W||d?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},f=$.selectors={cacheLength:50,createPseudo:v,match:S,order:new RegExp("ID|TAG"+(Y?"|NAME":"")+(X?"|CLASS":"")),attrHandle:V?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:e?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:U?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(R,""),a[3]=(a[4]||a[5]||"").replace(R,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||$.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&$.error(a[0]),a},PSEUDO:function(a,b,c){var d,e;if(S.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(d=a[4])K.test(d)&&(e=bc(d,b,c,!0))&&(e=d.indexOf(")",d.length-e)-d.length)&&(d=d.slice(0,e),a[0]=a[0].slice(0,e)),a[2]=d;return a.slice(0,3)}},filter:{ID:e?function(a){return a=a.replace(R,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(R,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(R,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=x[o][a];return b||(b=x(a,new RegExp("(^|"+A+")"+a+"("+A+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=$.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return $.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=s++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[o]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[o]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e,g=f.pseudos[a]||f.pseudos[a.toLowerCase()];return g||$.error("unsupported pseudo: "+a),g[o]?g(b,c,d):g.length>1?(e=[a,a,"",b],function(a){return g(a,0,e)}):g}},pseudos:{not:v(function(a,b,c){var d=j(a.replace(H,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!f.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:v(function(a){return function(b){return(b.textContent||b.innerText||g(b)).indexOf(a)>-1}}),has:v(function(a){return function(b){return $(a,b).length>0}}),header:function(a){return P.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:_("radio"),checkbox:_("checkbox"),file:_("file"),password:_("password"),image:_("image"),submit:ba("submit"),reset:ba("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return Q.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e",a.querySelectorAll("[selected]").length||e.push("\\["+A+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="

    ",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+A+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bk=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return u.apply(f,t.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j,k,l,m=d.getAttribute("id"),n=m||o,p=N.test(a)&&d.parentNode||d;m?n=n.replace(c,"\\$&"):d.setAttribute("id",n),j=bc(a,d,h),n="[id='"+n+"']";for(k=0,l=j.length;k0})}(),f.setFilters.nth=f.setFilters.eq,f.filters=f.pseudos,$.attr=p.attr,p.find=$,p.expr=$.selectors,p.expr[":"]=p.expr.pseudos,p.unique=$.uniqueSort,p.text=$.getText,p.isXMLDoc=$.isXML,p.contains=$.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
    ","
    "]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{cj=f.href}catch(cy){cj=e.createElement("a"),cj.href="",cj=cj.href}ck=ct.exec(cj.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
    ").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:cj,isLocal:cn.test(ck[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,ck[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==ck[1]&&i[2]==ck[2]&&(i[3]||(i[1]==="http:"?80:443))==(ck[3]||(ck[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cQ.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/public/javascripts/jquery-ui-1.8.17.custom.min.js b/public/javascripts/jquery-ui-1.8.17.custom.min.js new file mode 100644 index 0000000..cd0517e --- /dev/null +++ b/public/javascripts/jquery-ui-1.8.17.custom.min.js @@ -0,0 +1,31 @@ +/* + * jQuery UI Effects 1.8.17 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/ + */jQuery.effects||function(a,b){function l(b){if(!b||typeof b=="number"||a.fx.speeds[b])return!0;if(typeof b=="string"&&!a.effects[b])return!0;return!1}function k(b,c,d,e){typeof b=="object"&&(e=c,d=null,c=b,b=c.effect),a.isFunction(c)&&(e=c,d=null,c={});if(typeof c=="number"||a.fx.speeds[c])e=d,d=c,c={};a.isFunction(d)&&(e=d,d=null),c=c||{},d=d||c.duration,d=a.fx.off?0:typeof d=="number"?d:d in a.fx.speeds?a.fx.speeds[d]:a.fx.speeds._default,e=e||c.complete;return[b,c,d,e]}function j(a,b){var c={_:0},d;for(d in b)a[d]!=b[d]&&(c[d]=b[d]);return c}function i(b){var c,d;for(c in b)d=b[c],(d==null||a.isFunction(d)||c in g||/scrollbar/.test(c)||!/color/i.test(c)&&isNaN(parseFloat(d)))&&delete b[c];return b}function h(){var a=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,b={},c,d;if(a&&a.length&&a[0]&&a[a[0]]){var e=a.length;while(e--)c=a[e],typeof a[c]=="string"&&(d=c.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()}),b[d]=a[c])}else for(c in a)typeof a[c]=="string"&&(b[c]=a[c]);return b}function d(b,d){var e;do{e=a.curCSS(b,d);if(e!=""&&e!="transparent"||a.nodeName(b,"body"))break;d="backgroundColor"}while(b=b.parentNode);return c(e)}function c(b){var c;if(b&&b.constructor==Array&&b.length==3)return b;if(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)];if(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55];if(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];if(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];if(c=/rgba\(0, 0, 0, 0\)/.exec(b))return e.transparent;return e[a.trim(b).toLowerCase()]}a.effects={},a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,e){a.fx.step[e]=function(a){a.colorInit||(a.start=d(a.elem,e),a.end=c(a.end),a.colorInit=!0),a.elem.style[e]="rgb("+Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2],10),255),0)+")"}});var e={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},f=["add","remove","toggle"],g={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.effects.animateClass=function(b,c,d,e){a.isFunction(d)&&(e=d,d=null);return this.queue(function(){var g=a(this),k=g.attr("style")||" ",l=i(h.call(this)),m,n=g.attr("class");a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),m=i(h.call(this)),g.attr("class",n),g.animate(j(l,m),{queue:!1,duration:c,easing:d,complete:function(){a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),typeof g.attr("style")=="object"?(g.attr("style").cssText="",g.attr("style").cssText=k):g.attr("style",k),e&&e.apply(this,arguments),a.dequeue(this)}})})},a.fn.extend({_addClass:a.fn.addClass,addClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{add:b},c,d,e]):this._addClass(b)},_removeClass:a.fn.removeClass,removeClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{remove:b},c,d,e]):this._removeClass(b)},_toggleClass:a.fn.toggleClass,toggleClass:function(c,d,e,f,g){return typeof d=="boolean"||d===b?e?a.effects.animateClass.apply(this,[d?{add:c}:{remove:c},e,f,g]):this._toggleClass(c,d):a.effects.animateClass.apply(this,[{toggle:c},d,e,f])},switchClass:function(b,c,d,e,f){return a.effects.animateClass.apply(this,[{add:c,remove:b},d,e,f])}}),a.extend(a.effects,{version:"1.8.17",save:function(a,b){for(var c=0;c
    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"}));return d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;if(b.parent().is(".ui-effects-wrapper")){c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus();return c}return b},setTransition:function(b,c,d,e){e=e||{},a.each(c,function(a,c){unit=b.cssUnit(c),unit[0]>0&&(e[c]=unit[0]*d+unit[1])});return e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];if(a.fx.off||!i)return h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)});return i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="show";return this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="hide";return this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);c[1].mode="toggle";return this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])});return d}}),a.easing.jswing=a.easing.swing,a.extend(a.easing,{def:"easeOutQuad",swing:function(b,c,d,e,f){return a.easing[a.easing.def](b,c,d,e,f)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b+c;return-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b+c;return d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b+c;return-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b*b+c;return d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return b==0?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){if(b==0)return c;if(b==e)return c+d;if((b/=e/2)<1)return d/2*Math.pow(2,10*(b-1))+c;return d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){if((b/=e/2)<1)return-d/2*(Math.sqrt(1-b*b)-1)+c;return d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h").outerWidth(1).jquery||a.each(["Width","Height"],function(c,d){function h(b,c,d,f){return a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)}),c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){return c===b?g["inner"+d].call(this):this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){return typeof b!="number"?g["outer"+d].call(this,b):this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:a.expr.createPseudo?a.expr.createPseudo(function(b){return function(c){return!!a.data(c,b)}}):function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.curCSS||(a.curCSS=a.css),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!d||!a.element[0].parentNode)return;for(var e=0;e0?!0:(b[d]=1,e=b[d]>0,b[d]=0,e)},isOverAxis:function(a,b,c){return a>b&&a=9||!!b.button?this._mouseStarted?(this._mouseDrag(b),b.preventDefault()):(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b)),!this._mouseStarted):this._mouseUp(b)},_mouseUp:function(b){return a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b)),!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 +* https://github.com/jquery/jquery-ui +* Includes: jquery.ui.draggable.js +* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ +(function(a,b){a.widget("ui.draggable",a.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},destroy:function(){if(!this.element.data("draggable"))return;return this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy(),this},_mouseCapture:function(b){var c=this.options;return this.helper||c.disabled||a(b.target).is(".ui-resizable-handle")?!1:(this.handle=this._getHandle(b),this.handle?(c.iframeFix&&a(c.iframeFix===!0?"iframe":c.iframeFix).each(function(){a('
    ').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(a(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(b){var c=this.options;return this.helper=this._createHelper(b),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),a.ui.ddmanager&&(a.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,c.cursorAt&&this._adjustOffsetFromHelper(c.cursorAt),c.containment&&this._setContainment(),this._trigger("start",b)===!1?(this._clear(),!1):(this._cacheHelperProportions(),a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this._mouseDrag(b,!0),a.ui.ddmanager&&a.ui.ddmanager.dragStart(this,b),!0)},_mouseDrag:function(b,c){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute");if(!c){var d=this._uiHash();if(this._trigger("drag",b,d)===!1)return this._mouseUp({}),!1;this.position=d.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";return a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),!1},_mouseStop:function(b){var c=!1;a.ui.ddmanager&&!this.options.dropBehaviour&&(c=a.ui.ddmanager.drop(this,b)),this.dropped&&(c=this.dropped,this.dropped=!1);var d=this.element[0],e=!1;while(d&&(d=d.parentNode))d==document&&(e=!0);if(!e&&this.options.helper==="original")return!1;if(this.options.revert=="invalid"&&!c||this.options.revert=="valid"&&c||this.options.revert===!0||a.isFunction(this.options.revert)&&this.options.revert.call(this.element,c)){var f=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){f._trigger("stop",b)!==!1&&f._clear()})}else this._trigger("stop",b)!==!1&&this._clear();return!1},_mouseUp:function(b){return this.options.iframeFix===!0&&a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),a.ui.ddmanager&&a.ui.ddmanager.dragStop(this,b),a.ui.mouse.prototype._mouseUp.call(this,b)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?!0:!1;return a(this.options.handle,this.element).find("*").andSelf().each(function(){this==b.target&&(c=!0)}),c},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b])):c.helper=="clone"?this.element.clone().removeAttr("id"):this.element;return d.parents("body").length||d.appendTo(c.appendTo=="parent"?this.element[0].parentNode:c.appendTo),d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute"),d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[b.containment=="document"?0:a(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b.containment=="document"?0:a(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(b.containment=="document"?0:a(window).scrollLeft())+a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(b.containment=="document"?0:a(window).scrollTop())+(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)&&b.containment.constructor!=Array){var c=a(b.containment),d=c[0];if(!d)return;var e=c.offset(),f=a(d).css("overflow")!="hidden";this.containment=[(parseInt(a(d).css("borderLeftWidth"),10)||0)+(parseInt(a(d).css("paddingLeft"),10)||0),(parseInt(a(d).css("borderTopWidth"),10)||0)+(parseInt(a(d).css("paddingTop"),10)||0),(f?Math.max(d.scrollWidth,d.offsetWidth):d.offsetWidth)-(parseInt(a(d).css("borderLeftWidth"),10)||0)-(parseInt(a(d).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(f?Math.max(d.scrollHeight,d.offsetHeight):d.offsetHeight)-(parseInt(a(d).css("borderTopWidth"),10)||0)-(parseInt(a(d).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=c}else b.containment.constructor==Array&&(this.containment=b.containment)},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName),f=b.pageX,g=b.pageY;if(this.originalPosition){var h;if(this.containment){if(this.relative_container){var i=this.relative_container.offset();h=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]}else h=this.containment;b.pageX-this.offset.click.lefth[2]&&(f=h[2]+this.offset.click.left),b.pageY-this.offset.click.top>h[3]&&(g=h[3]+this.offset.click.top)}if(c.grid){var j=c.grid[1]?this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1]:this.originalPageY;g=h?j-this.offset.click.toph[3]?j-this.offset.click.toph[2]?k-this.offset.click.left=0;k--){var l=d.snapElements[k].left,m=l+d.snapElements[k].width,n=d.snapElements[k].top,o=n+d.snapElements[k].height;if(!(l-f=k&&g<=l||h>=k&&h<=l||gl)&&(e>=i&&e<=j||f>=i&&f<=j||ej);default:return!1}},a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(b,c){var d=a.ui.ddmanager.droppables[b.options.scope]||[],e=c?c.type:null,f=(b.currentItem||b.element).find(":data(droppable)").andSelf();g:for(var h=0;h").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;try{e.id}catch(f){e=document.body}return b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;return b.parent().is(".ui-effects-wrapper")?(c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus(),c):b},setTransition:function(b,c,d,e){return e=e||{},a.each(c,function(a,c){var f=b.cssUnit(c);f[0]>0&&(e[c]=f[0]*d+f[1])}),e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];return a.fx.off||!i?h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)}):i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);return b[1].mode="show",this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);return b[1].mode="hide",this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);return c[1].mode="toggle",this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];return a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])}),d}});var m={};a.each(["Quad","Cubic","Quart","Quint","Expo"],function(a,b){m[b]=function(b){return Math.pow(b,a+2)}}),a.extend(m,{Sine:function(a){return 1-Math.cos(a*Math.PI/2)},Circ:function(a){return 1-Math.sqrt(1-a*a)},Elastic:function(a){return a===0||a===1?a:-Math.pow(2,8*(a-1))*Math.sin(((a-1)*80-7.5)*Math.PI/15)},Back:function(a){return a*a*(3*a-2)},Bounce:function(a){var b,c=4;while(a<((b=Math.pow(2,--c))-1)/11);return 1/Math.pow(4,3-c)-7.5625*Math.pow((b*3-2)/22-a,2)}}),a.each(m,function(b,c){a.easing["easeIn"+b]=c,a.easing["easeOut"+b]=function(a){return 1-c(1-a)},a.easing["easeInOut"+b]=function(a){return a<.5?c(a*2)/2:c(a*-2+2)/-2+1}})}(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 +* https://github.com/jquery/jquery-ui +* Includes: jquery.effects.highlight.js +* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ +(function(a,b){a.effects.highlight=function(b){return this.queue(function(){var c=a(this),d=["backgroundImage","backgroundColor","opacity"],e=a.effects.setMode(c,b.options.mode||"show"),f={backgroundColor:c.css("backgroundColor")};e=="hide"&&(f.opacity=0),a.effects.save(c,d),c.show().css({backgroundImage:"none",backgroundColor:b.options.color||"#ffff99"}).animate(f,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),e=="show"&&!a.support.opacity&&this.style.removeAttribute("filter"),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 +* https://github.com/jquery/jquery-ui +* Includes: jquery.effects.pulsate.js +* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ +(function(a,b){a.effects.pulsate=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"show"),e=(b.options.times||5)*2-1,f=b.duration?b.duration/2:a.fx.speeds._default/2,g=c.is(":visible"),h=0;g||(c.css("opacity",0).show(),h=1),(d=="hide"&&g||d=="show"&&!g)&&e--;for(var i=0;i'))}function bindHover(a){var b="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout",function(a){var c=$(a.target).closest(b);if(!c.length)return;c.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(c){var d=$(c.target).closest(b);if($.datepicker._isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])||!d.length)return;d.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-prev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-next")&&d.addClass("ui-datepicker-next-hover")})}function extendRemove(a,b){$.extend(a,b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}function isArray(a){return a&&($.browser.safari&&typeof a=="object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(\)/))}$.extend($.ui,{datepicker:{version:"1.8.23"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){return extendRemove(this._defaults,a||{}),this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(a,b){var c=a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:b?bindHover($('
    ')):this.dpDiv}},_connectDatepicker:function(a,b){var c=$(a);b.append=$([]),b.trigger=$([]);if(c.hasClass(this.markerClassName))return;this._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.settings.disabled&&this._disableDatepicker(a)},_attachments:function(a,b){var c=this._get(b,"appendText"),d=this._get(b,"isRTL");b.append&&b.append.remove(),c&&(b.append=$(''+c+""),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),b.trigger&&b.trigger.remove();var e=this._get(b,"showOn");(e=="focus"||e=="both")&&a.focus(this._showDatepicker);if(e=="button"||e=="both"){var f=this._get(b,"buttonText"),g=this._get(b,"buttonImage");b.trigger=$(this._get(b,"buttonImageOnly")?$("").addClass(this._triggerClass).attr({src:g,alt:f,title:f}):$('').addClass(this._triggerClass).html(g==""?f:$("").attr({src:g,alt:f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){return $.datepicker._datepickerShowing&&$.datepicker._lastInput==a[0]?$.datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker._lastInput!=a[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepicker(a[0])):$.datepicker._showDatepicker(a[0]),!1})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var d=function(a){var b=0,c=0;for(var d=0;db&&(b=a[d].length,c=d);return c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=$(a);if(c.hasClass(this.markerClassName))return;c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._getDefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.settings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block")},_dialogDatepicker:function(a,b,c,d,e){var f=this._dialogInst;if(!f){this.uuid+=1;var g="dp"+this.uuid;this._dialogInput=$(''),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),f=this._dialogInst=this._newInst(this._dialogInput,!1),f.settings={},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),b=b&&b.constructor==Date?this._formatDate(f,b):b,this._dialogInput.val(b),this._pos=e?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h=document.documentElement.clientWidth,i=document.documentElement.clientHeight,j=document.documentElement.scrollLeft||document.body.scrollLeft,k=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[h/2-100+j,i/2-150+k]}return this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=c,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,f),this},_destroyDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();$.removeData(a,PROP_NAME),d=="input"?(c.append.remove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=="div"||d=="span")&&b.removeClass(this.markerClassName).empty()},_enableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!1,c.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().removeClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b})},_disableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!0,c.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().addClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b}),this._disabledInputs[this._disabledInputs.length]=a},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b-1}},_doKeyUp:function(a){var b=$.datepicker._getInst(a.target);if(b.input.val()!=b.lastVal)try{var c=$.datepicker.parseDate($.datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlternate(b),$.datepicker._updateDatepicker(b))}catch(d){$.datepicker.log(d)}return!0},_showDatepicker:function(a){a=a.target||a,a.nodeName.toLowerCase()!="input"&&(a=$("input",a.parentNode)[0]);if($.datepicker._isDisabledDatepicker(a)||$.datepicker._lastInput==a)return;var b=$.datepicker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=b&&($.datepicker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var c=$.datepicker._get(b,"beforeShow"),d=c?c.apply(a,[a,b]):{};if(d===!1)return;extendRemove(b.settings,d),b.lastVal=null,$.datepicker._lastInput=a,$.datepicker._setDateFromField(b),$.datepicker._inDialog&&(a.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(a),$.datepicker._pos[1]+=a.offsetHeight);var e=!1;$(a).parents().each(function(){return e|=$(this).css("position")=="fixed",!e}),e&&$.browser.opera&&($.datepicker._pos[0]-=document.documentElement.scrollLeft,$.datepicker._pos[1]-=document.documentElement.scrollTop);var f={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,b.dpDiv.empty(),b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b),f=$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=$.datepicker._get(b,"showAnim"),h=$.datepicker._get(b,"duration"),i=function(){var a=b.dpDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=$.datepicker._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._curInst=b}},_updateDatepicker:function(a){var b=this;b.maxRows=4;var c=$.datepicker._getBorders(a.dpDiv);instActive=a,a.dpDiv.empty().append(this._generateHTML(a)),this._attachHandlers(a);var d=a.dpDiv.find("iframe.ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mouseover();var e=this._getNumberOfMonths(a),f=e[1],g=17;a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"em"),a.dpDiv[(e[0]!=1||e[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),a==$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var h=a.yearshtml;setTimeout(function(){h===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=a.dpDiv.outerWidth(),e=a.dpDiv.outerHeight(),f=a.input?a.input.outerWidth():0,g=a.input?a.input.outerHeight():0,h=document.documentElement.clientWidth+(c?0:$(document).scrollLeft()),i=document.documentElement.clientHeight+(c?0:$(document).scrollTop());return b.left-=this._get(a,"isRTL")?d-f:0,b.left-=c&&b.left==a.input.offset().left?$(document).scrollLeft():0,b.top-=c&&b.top==a.input.offset().top+g?$(document).scrollTop():0,b.left-=Math.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=Math.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0),b},_findPos:function(a){var b=this._getInst(a),c=this._get(b,"isRTL");while(a&&(a.type=="hidden"||a.nodeType!=1||$.expr.filters.hidden(a)))a=a[c?"previousSibling":"nextSibling"];var d=$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=this._curInst;if(!b||a&&b!=$.data(a,PROP_NAME))return;if(this._datepickerShowing){var c=this._get(b,"showAnim"),d=this._get(b,"duration"),e=function(){$.datepicker._tidyDialog(b)};$.effects&&$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,e):b.dpDiv[c=="slideDown"?"slideUp":c=="fadeIn"?"fadeOut":"hide"](c?d:null,e),c||e(),this._datepickerShowing=!1;var f=this._get(b,"onClose");f&&f.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(!$.datepicker._curInst)return;var b=$(a.target),c=$.datepicker._getInst(b[0]);(b[0].id!=$.datepicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length==0&&!b.hasClass($.datepicker.markerClassName)&&!b.closest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=c)&&$.datepicker._hideDatepicker()},_adjustDate:function(a,b,c){var d=$(a),e=this._getInst(d[0]);if(this._isDisabledDatepicker(d[0]))return;this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c),this._updateDatepicker(e)},_gotoToday:function(a){var b=$(a),c=this._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=c.currentDay,c.drawMonth=c.selectedMonth=c.currentMonth,c.drawYear=c.selectedYear=c.currentYear;else{var d=new Date;c.selectedDay=d.getDate(),c.drawMonth=c.selectedMonth=d.getMonth(),c.drawYear=c.selectedYear=d.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthYear:function(a,b,c){var d=$(a),e=this._getInst(d[0]);e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=$(a);if($(d).hasClass(this._unselectableClass)||this._isDisabledDatepicker(e[0]))return;var f=this._getInst(e[0]);f.selectedDay=f.currentDay=$("a",d).html(),f.selectedMonth=f.currentMonth=b,f.selectedYear=f.currentYear=c,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))},_clearDate:function(a){var b=$(a),c=this._getInst(b[0]);this._selectDate(b,"")},_selectDate:function(a,b){var c=$(a),d=this._getInst(c[0]);b=b!=null?b:this._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDatepicker(),this._lastInput=d.input[0],typeof d.input[0]!="object"&&d.input.focus(),this._lastInput=null)},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),d=this._getDate(a),e=this.formatDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=new Date(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var d=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=typeof d!="string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=-1,j=-1,k=-1,l=-1,m=!1,n=function(b){var c=s+1-1){j=1,k=l;do{var u=this._getDaysInMonth(i,j-1);if(k<=u)break;j++,k-=u}while(!0)}var t=this._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=i||t.getMonth()+1!=j||t.getDate()!=k)throw"Invalid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,e=(c?c.dayNames:null)||this._defaults.dayNames,f=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,h=function(b){var c=m+112?a.getHours()+2:0),a):null},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),(e!=a.selectedMonth||f!=a.selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_attachHandlers:function(a){var b=this._get(a,"stepMonths"),c="#"+a.id.replace(/\\\\/g,"\\");a.dpDiv.find("[data-handler]").map(function(){var a={prev:function(){window["DP_jQuery_"+dpuuid].datepicker._adjustDate(c,-b,"M")},next:function(){window["DP_jQuery_"+dpuuid].datepicker._adjustDate(c,+b,"M")},hide:function(){window["DP_jQuery_"+dpuuid].datepicker._hideDatepicker()},today:function(){window["DP_jQuery_"+dpuuid].datepicker._gotoToday(c)},selectDay:function(){return window["DP_jQuery_"+dpuuid].datepicker._selectDay(c,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return window["DP_jQuery_"+dpuuid].datepicker._selectMonthYear(c,this,"M"),!1},selectYear:function(){return window["DP_jQuery_"+dpuuid].datepicker._selectMonthYear(c,this,"Y"),!1}};$(this).bind(this.getAttribute("data-event"),a[this.getAttribute("data-handler")])})},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),d=this._get(a,"showButtonPanel"),e=this._get(a,"hideIfNoPrevNext"),f=this._get(a,"navigationAsDateFormat"),g=this._getNumberOfMonths(a),h=this._get(a,"showCurrentAtPos"),i=this._get(a,"stepMonths"),j=g[0]!=1||g[1]!=1,k=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=this._getMinMaxDate(a,"min"),m=this._getMinMaxDate(a,"max"),n=a.drawMonth-h,o=a.drawYear;n<0&&(n+=12,o--);if(m){var p=this._daylightSavingAdjust(new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=l&&pp)n--,n<0&&(n=11,o--)}a.drawMonth=n,a.drawYear=o;var q=this._get(a,"prevText");q=f?this.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=this._canAdjustMonth(a,-1,o,n)?''+q+"":e?"":''+q+"",s=this._get(a,"nextText");s=f?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatConfig(a)):s;var t=this._canAdjustMonth(a,1,o,n)?''+s+"":e?"":''+s+"",u=this._get(a,"currentText"),v=this._get(a,"gotoCurrent")&&a.currentDay?k:b;u=f?this.formatDate(u,v,this._getFormatConfig(a)):u;var w=a.inline?"":'",x=d?'
    '+(c?w:"")+(this._isInRange(a,v)?'":"")+(c?"":w)+"
    ":"",y=parseInt(this._get(a,"firstDay"),10);y=isNaN(y)?0:y;var z=this._get(a,"showWeek"),A=this._get(a,"dayNames"),B=this._get(a,"dayNamesShort"),C=this._get(a,"dayNamesMin"),D=this._get(a,"monthNames"),E=this._get(a,"monthNamesShort"),F=this._get(a,"beforeShowDay"),G=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths"),I=this._get(a,"calculateWeek")||this.iso8601Week,J=this._getDefaultDate(a),K="";for(var L=0;L1)switch(N){case 0:Q+=" ui-datepicker-group-first",P=" ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=" ui-datepicker-group-last",P=" ui-corner-"+(c?"left":"right");break;default:Q+=" ui-datepicker-group-middle",P=""}Q+='">'}Q+='
    '+(/all|left/.test(P)&&L==0?c?t:r:"")+(/all|right/.test(P)&&L==0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+'
    '+"";var R=z?'":"";for(var S=0;S<7;S++){var T=(S+y)%7;R+="=5?' class="ui-datepicker-week-end"':"")+">"+''+C[T]+""}Q+=R+"";var U=this._getDaysInMonth(o,n);o==a.selectedYear&&n==a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,U));var V=(this._getFirstDayOfMonth(o,n)-y+7)%7,W=Math.ceil((V+U)/7),X=j?this.maxRows>W?this.maxRows:W:W;this.maxRows=X;var Y=this._daylightSavingAdjust(new Date(o,n,1-V));for(var Z=0;Z";var _=z?'":"";for(var S=0;S<7;S++){var ba=F?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=Y.getMonth()!=n,bc=bb&&!H||!ba[0]||l&&Ym;_+='",Y.setDate(Y.getDate()+1),Y=this._daylightSavingAdjust(Y)}Q+=_+""}n++,n>11&&(n=0,o++),Q+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(Y)+""+(bb&&!G?" ":bc?''+Y.getDate()+"":''+Y.getDate()+"")+"
    "+(j?"
    "+(g[0]>0&&N==g[1]-1?'
    ':""):""),M+=Q}K+=M}return K+=x+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.inline?'':""),a._keyEvent=!1,K},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=this._get(a,"changeMonth"),j=this._get(a,"changeYear"),k=this._get(a,"showMonthAfterYear"),l='
    ',m="";if(f||!i)m+=''+g[b]+"";else{var n=d&&d.getFullYear()==c,o=e&&e.getFullYear()==c;m+='"}k||(l+=m+(f||!i||!j?" ":""));if(!a.yearshtml){a.yearshtml="";if(f||!j)l+=''+c+"";else{var q=this._get(a,"yearRange").split(":"),r=(new Date).getFullYear(),s=function(a){var b=a.match(/c[+-].*/)?c+parseInt(a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t=s(q[0]),u=Math.max(t,s(q[1]||""));t=d?Math.max(t,d.getFullYear()):t,u=e?Math.min(u,e.getFullYear()):u,a.yearshtml+='",l+=a.yearshtml,a.yearshtml=null}}return l+=this._get(a,"yearSuffix"),k&&(l+=(f||!i||!j?" ":"")+m),l+="
    ",l},_adjustInstDate:function(a,b,c){var d=a.drawYear+(c=="Y"?b:0),e=a.drawMonth+(c=="M"?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+(c=="D"?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),(c=="M"||c=="Y")&&this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&bd?d:e,e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return b==null?[1,1]:typeof b=="number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));return b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth())),this._isInRange(a,f)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!d||b.getTime()<=d.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");return b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10),{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),$.fn.datepicker=function(a){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.initialized=!0);var b=Array.prototype.slice.call(arguments,1);return typeof a!="string"||a!="isDisabled"&&a!="getDate"&&a!="widget"?a=="option"&&arguments.length==2&&typeof arguments[1]=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b)):this.each(function(){typeof a=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)}):$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b))},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.8.23",window["DP_jQuery_"+dpuuid]=$})(jQuery);; \ No newline at end of file diff --git a/assets/js/jquery.stopwatch.js b/public/javascripts/jquery.stopwatch.js similarity index 100% rename from assets/js/jquery.stopwatch.js rename to public/javascripts/jquery.stopwatch.js diff --git a/public/javascripts/jquery.tipTip.minified.js b/public/javascripts/jquery.tipTip.minified.js new file mode 100644 index 0000000..cdf3a89 --- /dev/null +++ b/public/javascripts/jquery.tipTip.minified.js @@ -0,0 +1,21 @@ + /* + * TipTip + * Copyright 2010 Drew Wilson + * www.drewwilson.com + * code.drewwilson.com/entry/tiptip-jquery-plugin + * + * Version 1.3 - Updated: Mar. 23, 2010 + * + * This Plug-In will create a custom tooltip to replace the default + * browser tooltip. It is extremely lightweight and very smart in + * that it detects the edges of the browser window and will make sure + * the tooltip stays within the current window size. As a result the + * tooltip will adjust itself to be displayed above, below, to the left + * or to the right depending on what is necessary to stay within the + * browser window. It is completely customizable as well via CSS. + * + * This TipTip jQuery plug-in is dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ +(function($){$.fn.tipTip=function(options){var defaults={activation:"hover",keepAlive:false,maxWidth:"200px",edgeOffset:3,defaultPosition:"bottom",delay:400,fadeIn:200,fadeOut:200,attribute:"title",content:false,enter:function(){},exit:function(){}};var opts=$.extend(defaults,options);if($("#tiptip_holder").length<=0){var tiptip_holder=$('
    ');var tiptip_content=$('
    ');var tiptip_arrow=$('
    ');$("body").append(tiptip_holder.html(tiptip_content).prepend(tiptip_arrow.html('
    ')))}else{var tiptip_holder=$("#tiptip_holder");var tiptip_content=$("#tiptip_content");var tiptip_arrow=$("#tiptip_arrow")}return this.each(function(){var org_elem=$(this);if(opts.content){var org_title=opts.content}else{var org_title=org_elem.attr(opts.attribute)}if(org_title!=""){if(!opts.content){org_elem.removeAttr(opts.attribute)}var timeout=false;if(opts.activation=="hover"){org_elem.hover(function(){active_tiptip()},function(){if(!opts.keepAlive){deactive_tiptip()}});if(opts.keepAlive){tiptip_holder.hover(function(){},function(){deactive_tiptip()})}}else if(opts.activation=="focus"){org_elem.focus(function(){active_tiptip()}).blur(function(){deactive_tiptip()})}else if(opts.activation=="click"){org_elem.click(function(){active_tiptip();return false}).hover(function(){},function(){if(!opts.keepAlive){deactive_tiptip()}});if(opts.keepAlive){tiptip_holder.hover(function(){},function(){deactive_tiptip()})}}function active_tiptip(){opts.enter.call(this);tiptip_content.html(org_title);tiptip_holder.hide().removeAttr("class").css("margin","0");tiptip_arrow.removeAttr("style");var top=parseInt(org_elem.offset()['top']);var left=parseInt(org_elem.offset()['left']);var org_width=parseInt(org_elem.outerWidth());var org_height=parseInt(org_elem.outerHeight());var tip_w=tiptip_holder.outerWidth();var tip_h=tiptip_holder.outerHeight();var w_compare=Math.round((org_width-tip_w)/2);var h_compare=Math.round((org_height-tip_h)/2);var marg_left=Math.round(left+w_compare);var marg_top=Math.round(top+org_height+opts.edgeOffset);var t_class="";var arrow_top="";var arrow_left=Math.round(tip_w-12)/2;if(opts.defaultPosition=="bottom"){t_class="_bottom"}else if(opts.defaultPosition=="top"){t_class="_top"}else if(opts.defaultPosition=="left"){t_class="_left"}else if(opts.defaultPosition=="right"){t_class="_right"}var right_compare=(w_compare+left)parseInt($(window).width());if((right_compare&&w_compare<0)||(t_class=="_right"&&!left_compare)||(t_class=="_left"&&left<(tip_w+opts.edgeOffset+5))){t_class="_right";arrow_top=Math.round(tip_h-13)/2;arrow_left=-12;marg_left=Math.round(left+org_width+opts.edgeOffset);marg_top=Math.round(top+h_compare)}else if((left_compare&&w_compare<0)||(t_class=="_left"&&!right_compare)){t_class="_left";arrow_top=Math.round(tip_h-13)/2;arrow_left=Math.round(tip_w);marg_left=Math.round(left-(tip_w+opts.edgeOffset+5));marg_top=Math.round(top+h_compare)}var top_compare=(top+org_height+opts.edgeOffset+tip_h+8)>parseInt($(window).height()+$(window).scrollTop());var bottom_compare=((top+org_height)-(opts.edgeOffset+tip_h+8))<0;if(top_compare||(t_class=="_bottom"&&top_compare)||(t_class=="_top"&&!bottom_compare)){if(t_class=="_top"||t_class=="_bottom"){t_class="_top"}else{t_class=t_class+"_top"}arrow_top=tip_h;marg_top=Math.round(top-(tip_h+5+opts.edgeOffset))}else if(bottom_compare|(t_class=="_top"&&bottom_compare)||(t_class=="_bottom"&&!top_compare)){if(t_class=="_top"||t_class=="_bottom"){t_class="_bottom"}else{t_class=t_class+"_bottom"}arrow_top=-12;marg_top=Math.round(top+org_height+opts.edgeOffset)}if(t_class=="_right_top"||t_class=="_left_top"){marg_top=marg_top+5}else if(t_class=="_right_bottom"||t_class=="_left_bottom"){marg_top=marg_top-5}if(t_class=="_left_top"||t_class=="_left_bottom"){marg_left=marg_left+5}tiptip_arrow.css({"margin-left":arrow_left+"px","margin-top":arrow_top+"px"});tiptip_holder.css({"margin-left":marg_left+"px","margin-top":marg_top+"px"}).attr("class","tip"+t_class);if(timeout){clearTimeout(timeout)}timeout=setTimeout(function(){tiptip_holder.stop(true,true).fadeIn(opts.fadeIn)},opts.delay)}function deactive_tiptip(){opts.exit.call(this);if(timeout){clearTimeout(timeout)}tiptip_holder.fadeOut(opts.fadeOut)}}})}})(jQuery); \ No newline at end of file diff --git a/public/javascripts/script.js b/public/javascripts/script.js new file mode 100644 index 0000000..2cd5d46 --- /dev/null +++ b/public/javascripts/script.js @@ -0,0 +1,590 @@ +(function($){ + function pad(str, len, pad) { + str += ' '; + if (str.length < len){ + for (var i = str.length; i < len; i++){ + str += pad; + } + } + str += ' '; + + return str; + } + + var Panel = { + // Configuration and default values + currentCalls : 0, + ISADMIN : false, + SLALLAM : 40000, + SLA_THRESHOLD : 3600000, + visible_queues : [], + queues_hidden : false, + $queuesBox : null, + MAXLLAM : 30, + statuses : [ + { + name : 'Disconnected', + color : 'gris', + start_timer : false, + is_call : false + }, + { + name : 'Available', + color : 'verde', + start_timer : false, + is_call : false + }, + { + name : 'Meeting', + color : 'rojo', + start_timer : true, + is_call : false + }, + { + name : 'Administrative', + color : 'amarillo', + start_timer : true, + is_call : false + }, + { + name : 'Incoming', + color : 'azul', + start_timer : true, + is_call : true + }, + { + name : 'Outgoing', + color : 'azul-claro', + start_timer : true, + is_call : true + }, + { + name : 'Resting', + color : 'naranja', + start_timer : true, + is_call : false + }, + { + name : 'Glory time', + color : 'morado', + start_timer : true, + is_call : false + } + ], + sla_clients : ['Almirall','Applus','Isolux CorsĆ”n','Repsol'], + + init: function (config){ + this.config = config; + + this.setupTemplates(); + this.bindEvents(); + }, + sendSlaNotifications : function(){ + for (var i = 0, length = Panel.sla_clients.length; i < length; i++){ + var client_name = Panel.sla_clients[i]; + $.getJSON('/stats/json/clients/'+ client_name +'/', Panel.createNotification); + } + }, + createNotification : function(results){ + var notification_body = pad('**Answered calls time SLA** : ' + ((results.answered_before_SLA * 100) / results.total_answered).toFixed(1) + '%', 53,'-'); + notification_body += pad(' **Abandoned calls SLA** : ' + ((results.abandoned_after_SLA * 100) / results.total_offered_calls).toFixed(1) + '%', 53,'-'); + notification_body += ' **Average response time** : ' + results.average_response_time + ' seconds'; + + var notification = window.webkitNotifications.createNotification('', + results.client_name + ' - SLAs', + notification_body + ); + //Show the popup + notification.show(); + + setTimeout(function(){ + notification.close(); + },'30000') + }, + bindEvents : function(){ + var self = this; + + this.config.queuesHolder.on({ + mouseenter : self.vanishAgents, + mouseleave : self.unVanishAgents, + click : self.queueClick + }, this.config.queueBoxSelector); + + this.config.clientLogos.on('click', self.clientLogoClick); + }, + setupTemplates: function(){ + Handlebars.registerHelper('length', function(array){ + return array.length; + }); + + this.config.templateAgent = Handlebars.compile(this.config.templateAgent); + this.config.templateQueue = Handlebars.compile(this.config.templateQueue); + this.config.templateQueueAgent = Handlebars.compile(this.config.templateQueueAgent); + this.config.templateQueueAlert = Handlebars.compile(this.config.templateQueueAlert); + }, + vanishAgents : function() { + var id = $(this).data('cola'); + + $(Panel.config.agentBoxSelector).filter(function(){ + return $(this).find('span[data-cola="'+id+'"]').length === 0; + }).addClass('transparent'); + }, + unVanishAgents : function() { + Panel.config.agentsHolder.find('.caja_agente.transparent').removeClass('transparent'); + }, + queueClick : function() { + if (window.webKitNotifications) { window.webkitNotifications.requestPermission(); } + var $this = $(this), + queue = $this.data('cola'); + + Panel.unVanishAgents(); + + if ($this.hasClass('transparent')) + { + $this.removeClass('transparent'); + Panel.visible_queues.push(queue); + } + else + { + if (!Panel.queues_hidden) + { //Es la primera cola que estamos ocultando + Panel.$queuesBox.filter(function(){ + return $(this)[0] !== $this[0]; + }).addClass('transparent'); + + Panel.visible_queues.push(queue); + Panel.queues_hidden = true; + } + else + { + $this.addClass('transparent'); + Panel.visible_queues.splice(Panel.visible_queues.indexOf(queue),1); + } + } + + // If no queue is hidden + if(Panel.visible_queues.length === 0){ + Panel.config.queuesHolder.find('.transparent').removeClass('transparent'); + Panel.queues_hidden = false; + + $(Panel.config.agentBoxSelector + ':hidden').show(); + Panel.config.agentsHolder.find('div.invisible').hide(); + } + else { + $(Panel.config.agentBoxSelector).filter(function(){ + var $this = $(this), + colas = $this.find('span.cola'); + + var filtradas = colas.filter(function(){ + return Panel.visible_queues.indexOf($(this).data('cola')) === -1; + }); + + if (colas.length !== filtradas.length && $this.is(':hidden')) + { + $this.show(); + } + return colas.length === filtradas.length; + }).hide(); + } + localStorage.setItem('visible-queues', Panel.visible_queues.join('-')); + }, + clientLogoClick : function() { + var client = $(this).data('cliente'); + + Panel.$queuesBox.filter(function(){ + return $(this).data('cola').indexOf(client) !== -1; + }).trigger('click'); + }, + addQueues : function(queues){ + queues = $.grep(queues, function(n,i){ + return (($('#cola-'+ i.name)).length === 0); + }); + + $.each(queues, function(key, queue){ + var html = Panel.config.templateQueue(queue); + + $(html).hide().appendTo(Panel.config.queuesHolder).fadeIn('slow'); + + if (queue.lastCallTimeDiff){ + var divQueue = $('#cola-'+queue.name), + divCallingTime = divQueue.find('div.tiempo-llamadas'); + + divCallingTime + .stopwatch({ + timeFormat: 'mm:ss', + startTime: queue.last_call_time_diff + }).stopwatch('start'); + } + }); + Panel.$queuesBox = $(Panel.config.queueBoxSelector); + + if(Panel.ISADMIN){ + Panel.makeQueuesDraggable(); + } + }, + makeQueuesDraggable : function() { + Panel.$queuesBox.draggable({ + opacity: 0.7, + helper: 'clone', + revert: true + }); + }, + showTrashCans : function() { + Panel.config.trashcans = $('img.force-unlog').css('cursor','pointer').show(); + Panel.config.trashcans.on('click', Panel.unlogAgent); + }, + unlogAgent : function(e){ + var agent_box = $(this).closest('div.caja_agente'); + var aux = agent_box.attr('id').split('-'); + var cod_agente = aux[1]; + + if(cod_agente !== undefined){ + if (confirm('ĀæSeguro que quiere deslogar la extensiĆ³n ' + cod_agente + '?')){ + socket.emit('forceUnlog', { + agent : cod_agente + }); + } + } + }, + addAgents : function(agents){ + $.each(agents, function(key, agent){ + if ($('#agente-'+agent.codAgente).length === 0){ + var html = Panel.config.templateAgent({ + codAgente: agent.codAgente, + headingColor: Panel.statuses[agent.status_id].color, + status: Panel.statuses[agent.status_id].name, + visible: (agent.status_id !== 0), + name: [ + agent.nombre, + agent.apellido1, + agent.apellido2 + ].join(' ') + }); + + if (agent.status_id === 0){ + $(html).hide().appendTo(Panel.config.agentsHolder); + } + else { + $(html).hide().appendTo(Panel.config.agentsHolder).fadeIn('slow'); + } + + var callDifference = agent.currentCallTimeDiff, + previousDifference = agent.currentStatusTimeDiff, + agentBox = $('div#agente-'+ agent.codAgente), + queueContainer = agentBox.find('.colas'); + + queueContainer.append( + Panel.config.templateQueueAgent( + Panel.listAgentsQueues(agent.queues, agent.codAgente) + ) + ); + + if (callDifference|| previousDifference){ + var timer = agentBox.find('.tiempos'); + if (callDifference && previousDifference) { //If we got both + timer.stopwatch({timeFormat: 'mm:ss', startTime: callDifference}).stopwatch('start'); + timer.data('tiempoEstado',previousDifference-callDifference); + } + else{ // Just one + if (callDifference){ + timer.stopwatch({timeFormat: 'mm:ss', startTime: callDifference}).stopwatch('start'); + } + else{ + timer.stopwatch({timeFormat: 'mm:ss', startTime: previousDifference}).stopwatch('start'); + } + } + } + if (agent.currentTalkingQueue){ + agentBox.find('.cola').addClass('transparent'); + $('#'+[agent.codAgente,agent.currentTalkingQueue].join('-')).removeClass('transparent'); + } + } + }); + + if (Panel.ISADMIN) { + Panel.makeAgentsDroppable(); + Panel.showTrashCans(); + } + + Panel.toolTipsOnAgentQueues(); + Panel.calculateAgentsAvailablePerQueue(); + }, + makeAgentsDroppable : function() { + $(Panel.config.agentBoxSelector).droppable({ + accept : Panel.targetValidAgentHelper, + activeClass : 'target', + hoverClass: "flash", + drop : Panel.dropQueueOverAgent + }); + }, + toolTipsOnAgentQueues : function() { + $(Panel.config.agentBoxSelector).find('.cola').tipTip({defaultPosition: 'top'}); + }, + targetValidAgentHelper : function () { + var $this = $(this), + header = $this.find('h2'); + + return ($this.hasClass('visible') && + (!header.hasClass('gradiente-azul') && !header.hasClass('gradiente-azul-claro')) + ); + }, + dropQueueOverAgent : function( event, ui ) { + var $queue = $(ui.draggable[0]), + $this = $(this), + queueName = $queue.data('cola'), + agent = $this.attr('id').replace('agente-',''); + + Panel.unVanishAgents(); + + socket.emit('sendCall', { + from : queueName, + to : agent + }); + }, + listAgentsQueues : function(queues, codAgente){ + var returnable = []; + $.each(queues, function (key, queue){ + var style = $('#cola-'+ queue.name).find('header').attr('style'); + returnable.push({ + style : style, + id: [codAgente , queue.name].join('-'), + text: queue.priority, + queue: queue.name + }); + }); + return returnable; + }, + agentRinging : function(data){ + var agentBox = $('#agente-'+ data.agent); + + if (agentBox.length > 0){ + switch (data.action){ + case 'start': + agentBox.addClass('ring'); + break; + case 'stop': + agentBox.removeClass('ring'); + break; + } + } + }, + calculateAgentsAvailablePerQueue : function(){ + Panel.config.queuesHolder.find(Panel.config.queueBoxSelector).each(function(i, queue_box){ + var $this = $(this), + agents = 0, + agents_available = 0, + queue_name = $this.data('cola'); + + var agents_in_queue = Panel.config.agentsHolder.find('span[data-cola="' + queue_name +'"]'); + agents = agents_in_queue.length; + + agents_in_queue.each(function(i,span){ + var header = $(this).parent().parent().parent().find('h2'); + if (header.hasClass('gradiente-verde')){ + agents_available++; + } + else if (header.hasClass('gradiente-gris')){ + agents--; + } + }); + + $this.find('.agentes-cola').text('('+ [agents_available,agents].join('/') +')'); + }); + }, + changeStatus : function(data){ + var agentBox = $('#agente-'+ data.agent), + header = agentBox.find('h2.cabecera'), + timer = agentBox.find('div.tiempos'), + agentQueues = agentBox.find('.cola'), + callTime, previousTime, + status = Panel.statuses[data.status]; + + agentBox.removeClass('ring'); + + if (!data.queue){ + agentQueues.removeClass('transparent'); + } + + if (! status.start_timer) { + // We have to remove timers + if (timer.text()!= '') + timer.stopwatch('destroy').text(''); + } + else { // This status init a new timer + if (!status.is_call){ + if (timer.text() === '') // Starting a new timer + timer.stopwatch({timeFormat: 'mm:ss'}).stopwatch('start'); + else{ + if ((header.hasClass('gradiente-azul-claro') || header.hasClass('gradiente-azul')) && status.name !== 'Glory time'){ //If it's a call + callTime = timer.stopwatch('getTime'); + previousTime = timer.data('tiempoEstado'); + timer.stopwatch('destroy'); + timer.stopwatch({timeFormat: 'mm:ss', startTime: previousTime+callTime}).stopwatch('start'); + } + else { + this.totallyResetTimer(timer); + } + } + } + else { //It's a call + Panel.vanishAgentQueues(data.queue,data.agent); + if (timer.text() === ''){ + timer.stopwatch({timeFormat: 'mm:ss'}).stopwatch('start'); + } + else{ // We have to save the previous time + previousTime = timer.stopwatch('getTime'); + timer.data('tiempoEstado',previousTime); + this.totallyResetTimer(timer); + } + } + } + header.find('small').text(status.name); + header.removeClass().addClass('cabecera gradiente-' + status.color); + if (status.name === 'Disconnected'){ + agentBox.addClass('invisible').hide(); + } + else { + if (!agentBox.is(':visible') && agentQueues.filter(function(i){ + return $(this).data('cola').indexOf(Panel.visible_queues) !== -1; + }).length > 0 ){ + agentBox.removeClass('invisible').fadeIn(); + } + } + Panel.calculateAgentsAvailablePerQueue(); + }, + /** + * This function will vanish all queues of a selected agent and will leave one active. + * + * @param queue - The queue which will remain active + * @param agent - The agent that contains the queue + */ + vanishAgentQueues : function (queue, agent){ + if (agent && queue){ + // ALL THE QUEUES TRANSPARENTS!!1! + $('#agente-'+ agent).find('.cola').addClass('transparent'); + var agent_queue = $('span#'+agent+'-'+queue); + + if (agent_queue.length !== 0) { + agent_queue.removeClass('transparent'); + } + } + }, + totallyResetTimer : function (timer){ + timer.stopwatch('destroy'); + timer.stopwatch({timeFormat: 'mm:ss'}).stopwatch('start'); + }, + totallyDestroyTimer : function (timer){ timer.stopwatch('destroy').text(''); }, + updateCurrentCalls : function (calls){ + + if (calls.calls !== undefined || calls.calls !== null){ + this.currentCalls = calls.calls; + } + var percent = ((this.currentCalls / this.MAXLLAM) * 100).toFixed(0); + this.config.percentHolder.text(percent + ' %'); + this.config.awaitingHolder.text(calls.awaiting); + this.config.talkingHolder.text(calls.talking); + }, + callInOrOutOfQueue : function (data){ + var queueDiv = $('#cola-'+data.queue), + callTimerDiv = queueDiv.find('.tiempo-llamadas'); + + queueDiv.find('.llamadas-cola').text('('+data.calls+')'); + + if (data.calls === 0){ + Panel.totallyDestroyTimer(callTimerDiv); + } + else { + if (callTimerDiv.text() === ''){ // There weren't any calls + callTimerDiv.stopwatch({ + timeFormat: 'mm:ss' + }).stopwatch('start'); + } + else{ + /*callTimerDiv.stopwatch({ + timeFormat: 'mm:ss', + startTime: data.timeSince + }).stopwatch('start');*/ + } + } + }, + loadFilterPrefs : function() { + var aux = localStorage.getItem('visible-queues'); + + if (aux !== null){ + aux = aux.split('-'); + + if (aux.length !== Panel.$queuesBox.length){ + Panel.$queuesBox.filter(function(){ + return $.inArray($(this).data('cola'),aux) !== -1; + }).trigger('click'); + } + } + }, + alertQueue : function(queue){ + if (Panel.ISADMIN && + Panel.visible_queues.length !== Panel.$queuesBox.length && + Panel.visible_queues.indexOf(queue) !== -1){ + + var new_window = window.open('','height=150,width=150,location=no,menubar=no,resizable=no,scrollbars=no,status=no,titlebar=no'); + new_window.document.write(Panel.config.templateQueueAlert({queue:queue})); + new_window.focus(); + } + } + }; + Panel.init({ + templateAgent : $('#template-agente').html(), + templateQueue : $('#template-cola').html(), + templateQueueAgent : $('#template-cola-agente').html(), + templateQueueAlert : $('#template-alert-queue').html(), + agentsHolder : $('#contenedorAgentes'), + agentBoxSelector : 'div.caja_agente', + queuesHolder : $('#widgetHolder'), + queueBoxSelector : 'div.caja-cola', + percentHolder : $('#texto-porcentaje'), + awaitingHolder : $('#texto-awaiting'), + talkingHolder : $('#texto-talking'), + clientLogos : $('img.logo-cliente') + }); + + // Sockets STUFF and function binding + //region Socket.io stuff + var socket = io.connect('SOCKET_IO_HOST'); + + socket.on('currentStatus', function (data){ + console.log(data); + Panel.ISADMIN = data.is_admin; + Panel.addQueues(data.queues); + Panel.addAgents(data.agents); + Panel.updateCurrentCalls(data); + Panel.loadFilterPrefs(); + + if (window.webkitNotifications && Panel.ISADMIN && window.webkitNotifications.checkPermission() === 0){ + Panel.sendSlaNotifications(); + setInterval(Panel.sendSlaNotifications, Panel.SLA_THRESHOLD); + } + }); + socket.on('logAgent', Panel.addAgents); + socket.on('agentRinging',Panel.agentRinging); + socket.on('changeEvent', function (data) { + Panel.changeStatus({ + agent: data.agent, + status: data.status, + queue: data.queue + }); + }); + socket.on('updatePrimary', function (data){ Panel.updateCurrentCalls(data);}); + socket.on('reload', function(){ + window.location.reload(true); + }); + socket.on('simAvailability', function(data){ + var $sim = $('#sim_' + data.sim); + if (data.available){ + $sim.removeClass('ocupada').removeClass('libre').addClass('libre'); + } + else { + $sim.removeClass('libre').removeClass('ocupada').addClass('ocupada'); + } + }); + socket.on('callInOrOutQueue', Panel.callInOrOutOfQueue); + //endregion +})(jQuery); \ No newline at end of file diff --git a/public/stylesheets/PIE.htc b/public/stylesheets/PIE.htc new file mode 100644 index 0000000..00b6383 --- /dev/null +++ b/public/stylesheets/PIE.htc @@ -0,0 +1,96 @@ + + + + + + + + + diff --git a/public/stylesheets/app.css b/public/stylesheets/app.css new file mode 100644 index 0000000..7807005 --- /dev/null +++ b/public/stylesheets/app.css @@ -0,0 +1,571 @@ +/* -------------------------------------------------- + Table of Contents +----------------------------------------------------- +:: Shared Styles +:: Page Name 1 +:: Page Name 2 +*/ + + +/* ----------------------------------------- + Shared Styles +----------------------------------------- */ +body { + background: url("../images/misc/carbon_fibre_v2.png"); +} +#companyBar { + background: #222; + border-bottom: solid 4px #7C1F30; + padding: 15px 20px 13px 20px; + margin-bottom: 20px; +} +.temporizador { + font-size: 40px; +} +.caja_agente{ + position: relative; + background-color: #ffffff; + border: 1px solid #CCC; + border-width: 1px 0; + + margin: 3px; + margin-bottom: 15px; + padding: 0; + z-index: 400; + + border-image: initial; + -webkit-border-image: initial; + -moz-border-image: initial; + border-image: initial; + + width: 28%; + float: left; + border-radius: 5px 5px 0 0; + + -webkit-transition: background-color 1s linear; + -moz-transition: background-color 1s linear; + -o-transition: background-color 1s linear; + -ms-transition: background-color 1s linear; + transition: background-color 1s linear; +} +.caja_agente h2 img { + display: none; + position: absolute; + top: 0; + right: 0; +} + +.target { + background-color: #FFEC8B; +} +.target.transparent { + filter: alpha(opacity=100); + opacity: 1.0; +} + +div.caja-cola, img.logo-cliente { + cursor: pointer; +} +div.caja-cola { + z-index: 500; +} +div.caja-cola, div.caja_agente { + -webkit-transition: opacity .3s ease-in-out; + -moz-transition: opacity .3s ease-in-out; + -o-transition: opacity .3s ease-in-out; + -ms-transition: opacity .3s ease-in-out; + transition: opacity .3s ease-in-out; +} + +.alerta { + background: #FFCCD5; +} + +.gradiente-rojo { + background: #9e253b; /* Old browsers */ + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzllMjUzYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9Ijk5JSIgc3RvcC1jb2xvcj0iIzdjMWYzMCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); + background: -moz-linear-gradient(top, #9e253b 0%, #7c1f30 99%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9e253b), color-stop(99%,#7c1f30)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #9e253b 0%,#7c1f30 99%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #9e253b 0%,#7c1f30 99%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #9e253b 0%,#7c1f30 99%); /* IE10+ */ + background: linear-gradient(top, #9e253b 0%,#7c1f30 99%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9e253b', endColorstr='#7c1f30',GradientType=0 ); /* IE6-8 */ +} + +.gradiente-verde { + background: #3d8336; /* Old browsers */ + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzNkODMzNiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9Ijk5JSIgc3RvcC1jb2xvcj0iIzI3NzQyMyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); + background: -moz-linear-gradient(top, #3d8336 0%, #277423 99%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#3d8336), color-stop(99%,#277423)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #3d8336 0%,#277423 99%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #3d8336 0%,#277423 99%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #3d8336 0%,#277423 99%); /* IE10+ */ + background: linear-gradient(top, #3d8336 0%,#277423 99%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3d8336', endColorstr='#277423',GradientType=0 ); /* IE6-8 */ +} + +.gradiente-amarillo { + background: #ffd65e; /* Old browsers */ + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZDY1ZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZWJmMDQiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background: -moz-linear-gradient(top, #ffd65e 0%, #febf04 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffd65e), color-stop(100%,#febf04)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ffd65e 0%,#febf04 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ffd65e 0%,#febf04 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #ffd65e 0%,#febf04 100%); /* IE10+ */ + background: linear-gradient(top, #ffd65e 0%,#febf04 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffd65e', endColorstr='#febf04',GradientType=0 ); /* IE6-8 */ +} + +.gradiente-gris { + background-color: #E6ECEF; + + background-image: -webkit-linear-gradient(top, #E6ECEF, #D0D6DA); + background-image: -moz-linear-gradient(top, #E6ECEF, #D0D6DA); + background-image: -o-linear-gradient(top, #E6ECEF, #D0D6DA); + background-image: -ms-linear-gradient(top, #E6ECEF, #D0D6DA); + background-image: linear-gradient(top, #E6ECEF, #D0D6DA); +} + +.gradiente-naranja { + background-color: #F98D35; + + background-image: -webkit-linear-gradient(top, #F98D35, #E46F23); + background-image: -moz-linear-gradient(top, #F98D35, #E46F23); + background-image: -o-linear-gradient(top, #F98D35, #E46F23); + background-image: -ms-linear-gradient(top, #F98D35, #E46F23); + background-image: linear-gradient(top, #F98D35, #E46F23); +} +.gradiente-morado { + background: #9b6ca6; + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzliNmNhNiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM4ZDU3OWEiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background: -moz-linear-gradient(top, #9b6ca6 0%, #8d579a 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9b6ca6), color-stop(100%,#8d579a)); + background: -webkit-linear-gradient(top, #9b6ca6 0%,#8d579a 100%); + background: -o-linear-gradient(top, #9b6ca6 0%,#8d579a 100%); + background: -ms-linear-gradient(top, #9b6ca6 0%,#8d579a 100%); + background: linear-gradient(to bottom, #9b6ca6 0%,#8d579a 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9b6ca6', endColorstr='#8d579a',GradientType=0 ); +} + + +.gradiente-amarillo, .gradiente-gris, .gradiente-amarillo small, .gradiente-gris small { + color: #0F3360 !important; + text-shadow: 0 1px 0 #EFEFEF !important; +} + +.gradiente-azul{ + background: #4c5766; /* Old browsers */ + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzRjNTc2NiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9Ijk5JSIgc3RvcC1jb2xvcj0iIzMxMzg0MSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); + background: -moz-linear-gradient(top, #4c5766 0%, #313841 99%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c5766), color-stop(99%,#313841)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4c5766 0%,#313841 99%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4c5766 0%,#313841 99%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #4c5766 0%,#313841 99%); /* IE10+ */ + background: linear-gradient(top, #4c5766 0%,#313841 99%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c5766', endColorstr='#313841',GradientType=0 ); /* IE6-8 */ +} + +.gradiente-azul-claro{ + background: #1c5ea0; /* Old browsers */ + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzFjNWVhMCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9Ijk5JSIgc3RvcC1jb2xvcj0iIzA2NDc5MiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); + background: -moz-linear-gradient(top, #1c5ea0 0%, #064792 99%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#1c5ea0), color-stop(99%,#064792)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #1c5ea0 0%,#064792 99%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #1c5ea0 0%,#064792 99%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #1c5ea0 0%,#064792 99%); /* IE10+ */ + background: linear-gradient(top, #1c5ea0 0%,#064792 99%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1c5ea0', endColorstr='#064792',GradientType=0 ); /* IE6-8 */ +} + +h2.cabecera { + border-radius: 5px 5px 0 0; + font-size: 18px; + font-weight: normal; + line-height: 20px; + background-color: #4C5766; + color: white; + margin: -2px auto -2px; + text-indent: 10px; + text-shadow: 0 1px 0 #0F3360; + -webkit-user-select: none; + overflow: hidden; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + white-space: nowrap; +} +.cabecera small { + display: block; + text-transform: uppercase; + color: #FFF; +} + + +.cuerpo { + margin-top: 1px; + position: relative; + overflow: hidden; +} +.colas { + position: relative; + float: left; + width: 65%; + padding: 5px 5px 0px 5px; +} +.tiempos { + float: left; + width: 25%; + text-align: center; + vertical-align: middle; + padding: 10px 5px 5px 10px; + font-weight: bold; + font-size: 22px; + color: #333; +} +.cola { + width: 30px; + height: 25px; + display:block; + float: left; + border-radius:66px; + -moz-border-radius:66px; + -webkit-border-radius:66px; + -khtml-border-radius:66px; + font-size:22px; + color: #FFFFFF; + text-decoration:none; + text-align:center; + background:#ddd; + padding-top: 4px; + margin-bottom: 4px; + margin-right: 5px; +} +.peque { + width: 12px; + height: 8px; +} +.nombre-cola { + float: left; + overflow:hidden; + white-space:nowrap; + text-overflow:ellipsis; + -o-text-overflow: ellipsis; + white-space: nowrap; +} + +.lista-colas{ + margin-bottom: 8px; + width: 100%; +} +.verde { + background: #277423; +} +.roja { + background: #9e253b; +} +.naranja { + background: #8D579A; +} + +.widget { + width: 90%; + box-shadow: 2px 0 3px #0d0d0d, -2px 0 3px #0d0d0d, 0 3px 3px #0d0d0d, 0 -2px 3px #0d0d0d; + -moz-box-shadow: 2px 0 3px #0d0d0d, -2px 0 3px #0d0d0d, 0 3px 3px #0d0d0d, 0 -2px 3px #0d0d0d; + -webkit-box-shadow: 2px 0 3px #0d0d0d, -2; + background: url(/public/images/widget_grad.png) no-repeat 100% 0; + background-color: #232526; + z-index: 100; + font-family: Arial,Tahoma, Geneva, sans-serif; +} +.widget-status { + height: 70px; + margin-left: 5px; + margin-bottom: 10px; + width: 29%; + float: left; +} +.widget-status > header { + text-align: center; +} +.widget-status .widget-inner { + color: #D3D4D4; + text-shadow: 1px 1px 1px #3F3F3F, -1px -1px 1px #3F3F3F; + padding-top: 5px; + font-size: 25px; + text-align: center; +} + +.alert{ + background-color: #900200; +} + +.mini { + width: 90%; + float: left; + margin: 0 0 10px 5px; +} +.mini > header > section { + font-size: 12px !important; +} +.widget > header { + border-bottom: 1px solid #151617; + border-top: 1px solid #323434; + height: 10px; + overflow: hidden; + padding: 3px 0 11px 5px; + line-height: 100%; +} +.widget > header > section { + float: left; + color: #d1d1d1; + font-size: 15px; + text-transform: uppercase; + font-weight: bold; + padding-top: 3px; + text-shadow: 0 -1px 1px black; + width: 100%; +} +.widget-status > header > section { + font-size: 12px; +} + +.widget > section { + border-top: 1px solid #303334; + border-bottom: 1px solid #151617; + padding: 0 10px; + color: #d1d1d1; + font-size: 12px; +} +.widget-body{ + overflow: hidden; +} +.widget-inner { + padding: 10px 0px 10px 0px; +} +.porcentaje { + margin-top: 5px; + background: url(/public/images/bg_time_head.gif); + border: 0; + height: 30px; + border-image: initial; +} + +.llamadas-cola, .agentes-cola{ + float: left; + font-size: 15px; + font-weight: bold; + padding-right: 5px; +} +.tiempo-llamadas { + float: right; + font-size: 18px; + font-weight: bold; + padding-right: 10px; +} +.transparent { + zoom: 1; + filter: alpha(opacity=20); + opacity: 0.2; +} +.visible { + display: block; +} +.invisible { + display: none; +} +.logos-clientes { + height: 45px; +} +.logos-clientes li { + display: inline-block; + zoom:1; + *display:inline; + margin-left: 10px; +} + +@-webkit-keyframes shake { + 0% { -webkit-transform: translate(2px, 1px) rotate(0deg); } + 10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); } + 20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); } + 30% { -webkit-transform: translate(0px, 2px) rotate(0deg); } + 40% { -webkit-transform: translate(1px, -1px) rotate(1deg); } + 50% { -webkit-transform: translate(-1px, 1px) rotate(-1deg); } + 60% { -webkit-transform: translate(-3px, -2px) rotate(0deg); } + 70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); } + 80% { -webkit-transform: translate(-1px, -2px) rotate(1deg); } + 90% { -webkit-transform: translate(2px, -1px) rotate(0deg); } + 100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); } +} + +@keyframes shake { + 0% { transform: translate(2px, 1px) rotate(0deg); } + 10% { transform: translate(-1px, -2px) rotate(-1deg); } + 20% { transform: translate(-3px, 0px) rotate(1deg); } + 30% { transform: translate(0px, 2px) rotate(0deg); } + 40% { transform: translate(1px, -1px) rotate(1deg); } + 50% { transform: translate(-1px, 1px) rotate(-1deg); } + 60% { transform: translate(-3px, -2px) rotate(0deg); } + 70% { transform: translate(2px, 1px) rotate(-1deg); } + 80% { transform: translate(-1px, -2px) rotate(1deg); } + 90% { transform: translate(2px, -1px) rotate(0deg); } + 100% { transform: translate(1px, -2px) rotate(-1deg); } +} + +.ring { + animation-name: 'shake'; + -webkit-animation-name: 'shake'; + animation-duration: 0.8s; + -webkit-animation-duration: 0.8s; + transform-origin:50% 50%; + -webkit-transform-origin:50% 50%; + animation-iteration-count: infinite; + -webkit-animation-iteration-count: infinite; + animation-timing-function: linear; + -webkit-animation-timing-function: linear; +} + +@-webkit-keyframes flash { + 0%, 50%, 100% {opacity: 1;} + 25%, 75% {opacity: 0;} +} +@-moz-keyframes flash { + 0%, 50%, 100% {opacity: 1;} +25%, 75% {opacity: 0;} + } +@-ms-keyframes flash { + 0%, 50%, 100% {opacity: 1;} + 25%, 75% {opacity: 0;} +} +@-o-keyframes flash { + 0%, 50%, 100% {opacity: 1;} +25%, 75% {opacity: 0;} + } +@keyframes flash { + 0%, 50%, 100% {opacity: 1;} +25%, 75% {opacity: 0;} +} + +.flash { + -webkit-animation-name: flash; + -moz-animation-name: flash; + -ms-animation-name: flash; + -o-animation-name: flash; + animation-name: flash; + + animation-duration: 1s; + -webkit-animation-duration: 1s; + + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} + +.libre { + background: #44813F; +} +.ocupada { + background: #9e253b; +} +#sim_1 { + margin-left: 46px; +} + +.sim { + height: 40px; + margin-bottom: 10px; + border-radius: 65px; +} +/* TelevisiĆ³n */ +@media screen and (device-width: 1920px) { + .tiempo-llamadas{ + font-size: 25px; + } + .widget > header { + height: 15px; + } + .llamadas-cola{ + font-size: 25px; + width: 10%; + padding-right: 5px; + } + .mini > header > section { + font-size: 18px !important; + } + .caja_agente { + width: 19%; + } + .columm, .columns { + margin-left: 0; + } + .row .nine.columns { + width: 78%; + } + .widget-status > header > section { + font-size: 16px; + } + + .sim { + height: 54px; + width: 0; + margin-left: 25px; + } +} +/* iPad */ +@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { + .widget > header { + height: 5px; + } + .widget > header > section { + font-size: 10px; + padding-top: 0; + margin-top: -1px; + } + .llamadas-cola { padding-right: 2px; } + .nombre-cola { width: 60%; } + .tiempo-llamadas { + font-size: 12px; + padding-right: 2px; + } + .llamadas-cola, .nombre-cola { font-size: 12px; } + #ocupacion_primario { height: 65px; } + .porcentaje { height: 25px; } + #texto-porcentaje { font-size: 15px; } + #company-logo { width: 40%; } + #companyBar { padding: 10px 5px 0 15px; } + .logos-clientes{ + float: right; + padding: 0; + margin: 0; + margin-right: -30px; + } + .logos-clientes li { + margin: 0; + padding: 0; + margin-left: -20px; + } + .logos-clientes img { width: 60%; height: 60%; } + h2.cabecera { + font-size: 14px; + line-height: 14px; + text-indent: 4px; + } + .colas { + padding: 3px; + } + .cola { + width: 15px; + height: 10px; + font-size: 12px; + line-height: 5px; + } + .tiempos { + font-size: 14px; + padding: 5px; + height: 10px; + } +} + diff --git a/public/stylesheets/foundation.css b/public/stylesheets/foundation.css new file mode 100644 index 0000000..97494a1 --- /dev/null +++ b/public/stylesheets/foundation.css @@ -0,0 +1,1205 @@ +/* Foundation v2.1.4 http://foundation.zurb.com */ +/* Artfully Masterminded by ZURB */ + +/* -------------------------------------------------- + Table of Contents +----------------------------------------------------- +:: Reset & Standards +:: Links +:: Lists +:: Tables +:: Misc +*/ + + +/* -------------------------------------------------- + :: Global Reset & Standards + -------------------------------------------------- */ + + /* + Eric Meyer's CSS Reset + http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) + */ + + html, body, div, span, applet, object, iframe, + h1, h2, h3, h4, h5, h6, p, blockquote, pre, + a, abbr, acronym, address, big, cite, code, + del, dfn, em, img, ins, kbd, q, s, samp, + small, strike, strong, sub, sup, tt, var, + b, u, i, center, + dl, dt, dd, ol, ul, li, + fieldset, form, label, legend, + table, caption, tbody, tfoot, thead, tr, th, td, + article, aside, canvas, details, embed, + figure, figcaption, footer, header, hgroup, + menu, nav, output, ruby, section, summary, + time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; + } + html { + font-size: 62.5%; + } + /* HTML5 display-role reset for older browsers */ + article, aside, details, figcaption, figure, + footer, header, hgroup, menu, nav, section { + display: block; + } + body { + line-height: 1; + } + ol, ul { + list-style: none; + } + blockquote, q { + quotes: none; + } + blockquote:before, blockquote:after, + q:before, q:after { + content: ''; + content: none; + } + table { + border-collapse: collapse; + border-spacing: 0; + } + + + + body { background: #fff; font-family: "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, "Lucida Grande", sans-serif; font-size: 13px; line-height: 18px; color: #D5D9D8; position: relative; -webkit-font-smoothing: antialiased; } + + + +/* -------------------------------------------------- + :: Links + -------------------------------------------------- */ + a { color: #2a85e8; text-decoration: none; line-height: inherit; } + a:hover { color: #11639d; } + a:focus { color: #cc4714; outline: none; } + p a, p a:visited { line-height: inherit; } + + +/* -------------------------------------------------- + :: Lists + -------------------------------------------------- */ + ul, ol { margin-bottom: 18px; } + ul { list-style: none outside; } + ol { list-style: decimal; } + ol, ul.square, ul.circle, ul.disc { margin-left: 30px; } + ul.square { list-style: square outside; } + ul.circle { list-style: circle outside; } + ul.disc { list-style: disc outside; } + li { margin-bottom: 12px; } + ul.large li { line-height: 21px; } + + +/* -------------------------------------------------- + :: Tables + -------------------------------------------------- */ + table { background: #fff; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; margin: 0 0 18px; border: 1px solid #ddd; } + + table thead, table tfoot { background: #f5f5f5; } + table thead tr th, + table tfoot tr th, + table tbody tr td, + table tr td, + table tfoot tr td { font-size: 12px; line-height: 18px; text-align: left; } + table thead tr th, + table tfoot tr td { padding: 8px 10px 9px; font-size: 14px; font-weight: bold; color: #222; } + table thead tr th:first-child, table tfoot tr td:first-child { border-left: none; } + table thead tr th:last-child, table tfoot tr td:last-child { border-right: none; } + + table tbody tr.even, + table tbody tr.alt { background: #f9f9f9; } + table tbody tr:nth-child(even) { background: #f9f9f9; } + table tbody tr td { color: #333; padding: 9px 10px; vertical-align: top; border: none; } + +/* -------------------------------------------------- + :: Misc +---------------------------------------------------*/ + .left { float: left; } + .right { float: right; } + .hide { display: none; } + .highlight { background: #ff0; } + +/* Arfully Masterminded by ZURB */ + +/* -------------------------------------------------- + :: Typography + -------------------------------------------------- */ + h1, h2, h3, h4, h5, h6 { color: #D5D9D8; font-weight: bold; line-height: 1.25 } + h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; } + h1 { font-size: 46px; font-size: 4.6rem; margin-bottom: 12px;} + h2 { font-size: 35px; font-size: 3.5rem; margin-bottom: 9px; } + h3 { font-size: 28px; font-size: 2.8rem; margin-bottom: 9px; } + h4 { font-size: 21px; font-size: 2.1rem; margin-bottom: 3px; } + h5 { font-size: 18px; font-size: 1.8rem; font-weight: normal; margin-bottom: 3px; } + h6 { font-size: 15px; font-size: 1.5rem; font-weight: normal; } + + .subheader { color: #777; font-weight: 300; margin-bottom: 24px; } + + p { line-height: 17px; margin: 0 0 18px; } + p img { margin: 0; } + p.lead { font-size: 18px; font-size: 1.8rem; line-height: 24px; } + + em, i { font-style: italic; line-height: inherit; } + strong, b { font-weight: bold; line-height: inherit; } + small { font-size: 60%; line-height: inherit; } + + h1 small, h2 small, h3 small, h4 small, h5 small { color: #777; } + + /* Blockquotes */ + blockquote, blockquote p { line-height: 20px; color: #777; } + blockquote { margin: 0 0 18px; padding: 9px 20px 0 19px; border-left: 1px solid #ddd; } + blockquote cite { display: block; font-size: 12px; font-size: 1.2rem; color: #555; } + blockquote cite:before { content: "\2014 \0020"; } + blockquote cite a, blockquote cite a:visited { color: #555; } + + hr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 12px 0 18px; height: 0; } + + abbr, acronym { text-transform: uppercase; font-size: 90%; color: #222; border-bottom: 1px solid #ddd; cursor: help; } + abbr { text-transform: none; } + + /** + * Print styles. + * + * Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ + * Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) + */ + .print-only { display: none !important; } + @media print { + * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; + -ms-filter: none !important; } /* Black prints faster: sanbeiji.com/archives/953 */ + p a, p a:visited { color: #444 !important; text-decoration: underline; } + p a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */ + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + thead { display: table-header-group; } /* css-discuss.incutio.com/wiki/Printing_Tables */ + tr, img { page-break-inside: avoid; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3{ page-break-after: avoid; } + .hide-on-print { display: none !important; } + .print-only { display: block !important; } + } +/* Arfully Masterminded by ZURB */ + +/* -------------------------------------------------- + :: Grid + + This is the mobile-friendly, responsive grid that + lets Foundation work much of its magic. + + -------------------------------------------------- */ + + .container { padding: 0; } + + .row { width: 100%; max-width: 1920px; min-width: 727px; margin: 0 auto; } + /* To fix the grid into a certain size, set max-width to width */ + .row .row { min-width: 0; } + + .column, .columns { margin-left: 4.4%; float: left; min-height: 1px; position: relative; } + .column:first-child, .columns:first-child { margin-left: 0; } + + .row .one.columns { width: 4.3%; } + .row .two.columns { width: 13%; } + .row .three.columns { width: 21.68%; } + .row .four.columns { width: 30.37%; } + .row .five.columns { width: 39.1%; } + .row .six.columns { width: 47.8%; } + .row .seven.columns { width: 56.5%; } + .row .eight.columns { width: 65.2%; } + .row .nine.columns { width: 73.9%; } + .row .ten.columns { width: 82.6%; } + .row .eleven.columns { width: 91.3%; } + .row .twelve.columns { width: 100%; } + + .row .offset-by-one { margin-left: 13.1%; } + .row .offset-by-two { margin-left: 21.8%; } + .row .offset-by-three { margin-left: 30.5%; } + .row .offset-by-four { margin-left: 39.2%; } + .row .offset-by-five { margin-left: 47.9%; } + .row .offset-by-six { margin-left: 56.6%; } + .row .offset-by-seven { margin-left: 65.3%; } + .row .offset-by-eight { margin-left: 74.0%; } + .row .offset-by-nine { margin-left: 82.7%; } + .row .offset-by-ten { margin-left: 91.4%; } + + .row .centered { float: none; margin: 0 auto; } + + .row .offset-by-one:first-child { margin-left: 8.7%; } + .row .offset-by-two:first-child { margin-left: 17.4%; } + .row .offset-by-three:first-child { margin-left: 26.1%; } + .row .offset-by-four:first-child { margin-left: 34.8%; } + .row .offset-by-five:first-child { margin-left: 43.5%; } + .row .offset-by-six:first-child { margin-left: 52.2%; } + .row .offset-by-seven:first-child { margin-left: 60.9%; } + .row .offset-by-eight:first-child { margin-left: 69.6%; } + .row .offset-by-nine:first-child { margin-left: 78.3%; } + .row .offset-by-ten:first-child { margin-left: 87%; } + .row .offset-by-eleven:first-child { margin-left: 95.7%; } + + /* Source Ordering */ + .push-two { left: 17.4% } + .push-three { left: 26.1%; } + .push-four { left: 34.8%; } + .push-five { left: 43.5%; } + .push-six { left: 52.2%; } + .push-seven { left: 60.9%; } + .push-eight { left: 69.6%; } + .push-nine { left: 78.3%; } + .push-ten { left: 87%; } + + .pull-two { right: 17.4% } + .pull-three { right: 26.1%; } + .pull-four { right: 34.8%; } + .pull-five { right: 43.5%; } + .pull-six { right: 52.2%; } + .pull-seven { right: 60.9%; } + .pull-eight { right: 69.6%; } + .pull-nine { right: 78.3%; } + .pull-ten { right: 87%; } + + + + img, object, embed { max-width: 100%; height: auto; } + img { -ms-interpolation-mode: bicubic; } + + /* Nicolas Gallagher's micro clearfix */ + .row:before, .row:after, .clearfix:before, .clearfix:after { content:""; display:table; } + .row:after, .clearfix:after { clear: both; } + .row, .clearfix { zoom: 1; } + + + + +/* -------------------------------------------------- + :: Block grids + + These are 2-up, 3-up, 4-up and 5-up ULs, suited + for repeating blocks of content. Add 'mobile' to + them to switch them just like the layout grid + (one item per line) on phones + + For IE7/8 compatibility block-grid items need to be + the same height. You can optionally uncomment the + lines below to support arbitrary height, but know + that IE7/8 do not support :nth-child. + -------------------------------------------------- */ + + .block-grid { display: block; overflow: hidden; } + .block-grid>li { display: block; height: auto; float: left; } + + .block-grid.two-up { margin-left: -4% } + .block-grid.two-up>li { margin-left: 4%; width: 46%; } +/* .block-grid.two-up>li:nth-child(2n+1) {clear: left;} */ + + .block-grid.three-up { margin-left: -2% } + .block-grid.three-up>li { margin-left: 2%; width: 31.3%; } +/* .block-grid.three-up>li:nth-child(3n+1) {clear: left;} */ + + .block-grid.four-up { margin-left: -2% } + .block-grid.four-up>li { margin-left: 2%; width: 23%; } +/* .block-grid.four-up>li:nth-child(4n+1) {clear: left;} */ + + .block-grid.five-up { margin-left: -1.5% } + .block-grid.five-up>li { margin-left: 1.5%; width: 18.5%; } +/* .block-grid.five-up>li:nth-child(5n+1) {clear: left;} */ + +/* Artfully masterminded by ZURB */ + + + +/* -------------------------------------------------- + Table of Contents +----------------------------------------------------- +:: Buttons +:: Alerts +:: Notices/Alerts +:: Tabs +:: Pagination +:: Lists +:: Panels +:: Nav +:: Video +:: Microformats +*/ + + + + +/* -------------------------------------------------- + Buttons + -------------------------------------------------- */ + + .button { + background: #00a6fc; + display: inline-block; + text-align: center; + padding: 9px 34px 11px; + color: #fff; + text-decoration: none; + font-weight: bold; + line-height: 1; + font-family: "Helvetica Neue", "Helvetica", Arial, Verdana, sans-serif; + position: relative; + cursor: pointer; + border: none; + } + + /* Don't use native buttons on iOS */ + input[type=submit].button { -webkit-appearance: none; } + + .button.nice { + background: #00a6fc url(../images/misc/button-gloss.png) repeat-x 0 -34px; + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.5); + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.5); + text-shadow: 0 -1px 1px rgba(0,0,0,0.28); + background: #00a6fc url(../images/misc/button-gloss.png) repeat-x 0 -34px, -moz-linear-gradient(top, rgba(255,255,255,.4) 0%, transparent 100%); + background: #00a6fc url(../images/misc/button-gloss.png) repeat-x 0 -34px, -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.4)), color-stop(100%,transparent)); + border: 1px solid #0593dc; + -webkit-transition: background-color .15s ease-in-out; + -moz-transition: background-color .15s ease-in-out; + -o-transition: background-color .15s ease-in-out; + } + + .button.radius { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + } + .button.round { + -moz-border-radius: 1000px; + -webkit-border-radius: 1000px; + border-radius: 1000px; + } + + .button.full-width { + width: 100%; + padding-left: 0 !important; + padding-right: 0 !important; + text-align: center; + } + + .button.left-align { + text-align: left; + text-indent: 12px; + } + + /* Sizes ---------- */ + .small.button { font-size: 11px; padding: 8px 20px 10px; width: auto; } + .medium.button { font-size: 13px; width: auto; } + .large.button { font-size: 18px; padding: 11px 48px 13px; width: auto; } + + /* Nice Sizes ---------- */ + .nice.small.button { background-position: 0 -36px; } + .nice.large.button { background-position: 0 -30px; } + + /* Colors ---------- */ + .blue.button { background-color: #00a6fc; } + .red.button { background-color: #e91c21; } + .white.button { background-color: #e9e9e9; color: #333; } + .black.button { background-color: #141414; } + + /* Nice Colors ---------- */ + .nice.blue.button { border: 1px solid #0593dc; } + .nice.red.button { border: 1px solid #b90b0b; } + .nice.white.button { border: 1px solid #cacaca; text-shadow: none !important; } + .nice.black.button { border: 1px solid #000; } + + /* Hovers ---------- */ + .button:hover, .button:focus { background-color: #0192dd; color: #fff; } + .blue.button:hover, .blue.button:focus { background-color: #0192dd; } + .red.button:hover, .red.button:focus { background-color: #d01217; } + .white.button:hover, .white.button:focus { background-color: #dadada; color: #333; } + .black.button:hover, .black.button:focus { background-color: #000; } + + /* Disabled ---------- */ + .button.disabled, .button[disabled] { opacity: 0.6; cursor: default; } + + + +/* -------------------------------------------------- + Alerts + -------------------------------------------------- */ + + div.alert-box { display: block; padding: 6px 7px; font-weight: bold; font-size: 13px; background: #eee; border: 1px solid rgba(0,0,0,0.1); margin-bottom: 12px; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; text-shadow: 0 1px rgba(255,255,255,0.9); position: relative; } + .alert-box.success { background-color: #7fae00; color: #fff; text-shadow: 0 -1px rgba(0,0,0,0.3); } + .alert-box.warning { background-color: #c08c00; color: #fff; text-shadow: 0 -1px rgba(0,0,0,0.3); } + .alert-box.error { background-color: #c00000; color: #fff; text-shadow: 0 -1px rgba(0,0,0,0.3); } + + .alert-box a.close { color: #000; position: absolute; right: 4px; top: 0; font-size: 18px; opacity: 0.2; padding: 4px; } + .alert-box a.close:hover,.alert-box a.close:focus { opacity: 0.4; } + + +/* -------------------------------------------------- + Tabs + -------------------------------------------------- */ + dl.tabs { display: block; margin: 0 0 20px 0; padding: 0; height: 30px; border-bottom: solid 1px #ddd; } + dl.tabs dt { display: block; width: auto; height: 30px; padding: 0 9px 0 20px; line-height: 30px; float: left; color: #999; font-size: 11px; text-transform: uppercase; cursor: default; } + dl.tabs dt:first-child { padding: 0 9px 0 0; } + dl.tabs dd { display: block; width: auto; height: 30px; padding: 0; float: left; } + dl.tabs dd a { display: block; width: auto; height: 29px; padding: 0 9px; line-height: 30px; border: solid 1px #ddd; margin: 0 -1px 0 0; color: #555; background: #eee; } + dl.tabs dd a.active { background: #fff; border-width: 1px 1px 0 1px; height: 30px; } + + .nice.tabs { border-bottom: solid 1px #eee; margin: 0 0 30px 0; height:43px; } + .nice.tabs dd a { padding: 7px 18px 9px; font-size: 15px; font-size: 1.5rem; color: #555555; background: none; border: none; } + .nice.tabs dd a.active { font-weight: bold; color: #333; background: #fff; border-left: 1px solid #eee; border-right: 1px solid #eee; border-top: 3px solid #00a6fc; margin: 0 10px; position: relative; top: -5px; } + .nice.tabs dd:first-child a.active { margin-left: 0; } + + dl.tabs.vertical { height: auto; } + dl.tabs.vertical dt, dl.tabs.vertical dd, dl.nice.tabs.vertical dt, dl.nice.tabs.vertical dd { float: none; height: auto; } + dl.tabs.vertical dd a { display: block; width: auto; height: auto; padding: 15px 20px; line-height: 1; border: solid 0 #ccc; border-width: 1px 1px 0; margin: 0; color: #555; background: #eee; font-size: 15px; font-size: 1.5rem; } + dl.tabs.vertical dd a.active { height: auto; margin: 0; border-width: 1px 0 0; background: #fff; } + + .nice.tabs.vertical { border-bottom: solid 1px #eee; height: auto; } + .nice.tabs.vertical dd a { padding: 15px 20px; border: none; border-left: 1px solid #eee; border-right: 1px solid #eee; border-top: 1px solid #eee; background: #fff; } + .nice.tabs.vertical dd a.active { border: none; background: #00a6fc; color: #fff; margin: 0; position: static; top: 0; height: auto; } + .nice.tabs.vertical dd:first-child a.active { margin: 0; } + + ul.tabs-content { margin: 0; display: block; } + ul.tabs-content>li { display:none; } + ul.tabs-content>li.active { display: block; } + + dl.contained, dl.nice.contained { margin-bottom: 0; } + dl.contained.tabs dd a { padding: 0 14px; } + dl.nice.contained.tabs dd a { padding: 7px 18px 9px; } + + ul.contained.tabs-content { padding: 0; } + ul.contained.tabs-content>li { padding: 20px; border: solid 0 #ddd; border-width: 0 1px 1px 1px; } + ul.nice.contained.tabs-content>li { border-color: #eee; } + +/* -------------------------------------------------- + Pagination + -------------------------------------------------- */ + ul.pagination { display: block; height: 24px; margin-left: -5px; } + ul.pagination li { float: left; display: block; height: 24px; color: #999; font-size: 15px; margin-left: 5px; } + ul.pagination li a { display: block; padding: 6px 7px 4px; color: #555; } + ul.pagination li.current a, ul.pagination li:hover a, ul.pagination li a:focus { border-bottom: solid 2px #00a6fc; color: #141414; } + ul.pagination li.unavailable a { cursor: default; color: #999; } + ul.pagination li.unavailable:hover a, ul.pagination li.unavailable a:focus { border-bottom: none; } + +/* -------------------------------------------------- + Lists + -------------------------------------------------- */ + ul.nice, ol.nice { list-style: none; margin: 0; } + ul.nice li, ol.nice li { padding-left: 13px; position: relative } + ul.nice li span.bullet, ol.nice li span.number { position: absolute; left: 0; top: 0; color: #ccc; } + +/* -------------------------------------------------- + Panels + -------------------------------------------------- */ + div.panel { + padding: 20px 20px 2px 20px; + background: #efefef; + background: -moz-linear-gradient(top, #FFFFFF 0%, #F4F4F4 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#FFFFFF), color-stop(100%,#F4F4F4)); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#F4F4F4',GradientType=0 ); + box-shadow: 0 2px 5px rgba(0,0,0,0.15); + -webkit-box-shadow: 0 2px 5px rgba(0,0,0,0.15); + -moz-box-shadow: 0 2px 5px rgba(0,0,0,0.25); + margin: 0 0 20px 0; + } + +/* -------------------------------------------------- + Nav Bar with Dropdowns + -------------------------------------------------- */ + + .nav-bar { height: 45px; background: #fff; margin-top: 20px; border: 1px solid #ddd; } + .nav-bar>li { float: left; display: block; position: relative; padding: 0; margin: 0; border-right: 1px solid #ddd; line-height: 45px; } + .nav-bar>li>a { position: relative; font-size: 14px; padding: 0 20px; display: block; text-decoration: none; font-size: 15px; font-size: 1.5rem; } + .nav-bar>li>input { margin: 0 16px; } + .nav-bar>li ul { margin-bottom: 0; } + .nav-bar>li li { line-height: 1.3; } + .nav-bar>li.has-flyout>a { padding-right: 36px; } + .nav-bar>li.has-flyout>a:after { content: ""; width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid #2a85e8; display: block; position: absolute; right: 18px; bottom: 20px; } + .nav-bar>li:hover>a { color: #141414; z-index: 2; } + .nav-bar>li:hover>a:after { border-top-color: #141414; } + + .flyout { background: #fff; margin: 0; padding: 20px; border: 1px solid #ddd; position: absolute; top: 45px; left: -1px; width: 400px; z-index: 10; } + .flyout.small { width: 200px; } + .flyout.large { width: 600px; } + .flyout.right { left: auto; right: 0; } + .flyout p:last-child { margin-bottom: 0; } + .nav-bar>li .flyout { display: none; } + .nav-bar>li:hover .flyout { display: block; } + + +/* -------------------------------------------------- + Video + Mad props to http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ + -------------------------------------------------- */ + + .flex-video { + position: relative; + padding-top: 25px; + padding-bottom: 67.5%; + height: 0; + margin-bottom: 16px; + overflow: hidden; + } + + .flex-video.widescreen { padding-bottom: 57.25%; } + .flex-video.vimeo { padding-top: 0; } + + .flex-video iframe, + .flex-video object, + .flex-video embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + + +/* -------------------------------------------------- + Microformats + -------------------------------------------------- */ + + /* hCard */ + ul.vcard { display: inline-block; margin: 0 0 12px 0; border: 1px solid #ddd; padding: 10px; } + ul.vcard li { margin: 0; display: block; } + ul.vcard li.fn { font-weight: bold; font-size: 15px; font-size: 1.5rem; } + + p.vevent span.summary { font-weight: bold; } + p.vevent abbr { cursor: default; text-decoration: none; font-weight: bold; border: none; padding: 0 1px; } + + + + + +/* Artfully masterminded by ZURB + Make sure to include app.js / foundation.js if you are going to use inline label inputs +*/ + + +/* ----------------------------------------- + Standard Forms +----------------------------------------- */ + + form { margin: 0 0 18px; } + form label { display: block; font-size: 13px; line-height: 18px; cursor: pointer; margin-bottom: 9px; } + + input.input-text, textarea { border-right: 1px solid #bbb; border-bottom: 1px solid #bbb; } + input.input-text, textarea, select { display: block; margin-bottom: 9px; } + label + input.input-text, label + textarea, label + select, label + div.dropdown, select + div.dropdown { margin-top: -9px; } + + /* Text input and textarea font and padding */ + input.input-text, textarea { font-size: 13px; padding: 4px 3px 2px; background: #fff; } + input.input-text:focus, textarea:focus { outline: none !important; } + input.input-text.oversize, textarea.oversize { font-size: 18px !important; padding: 4px 5px !important; } + input.input-text:focus, textarea:focus { background: #f9f9f9; } + + /* Inlined Label Style */ + input.placeholder, textarea.placeholder { color: #888; } + + /* Text input and textarea sizes */ + input.input-text, textarea { width: 254px; } + input.small, textarea.small { width: 134px; } + input.medium, textarea.medium { width: 254px; } + input.large, textarea.large { width: 434px; } + + /* Fieldsets */ + form fieldset { padding: 9px 9px 2px 9px; border: solid 1px #ddd; margin: 18px 0; } + + /* Inlined Radio & Checkbox */ + .form-field input[type=radio], div.form-field input[type=checkbox] { display: inline; width:auto; margin-bottom:0; } + + /* Errors */ + .form-field.error input, input.input-text.red { border-color: #C00000; background-color: rgba(255,0,0,0.15); } + .form-field.error label, label.red { color: #C00000; } + .form-field.error small, small.error { margin-top: -6px; display: block; margin-bottom: 9px; font-size: 11px; color: #C00000; width: 260px; } + + .small + small.error { width: 140px; } + .medium + small.error { width: 260px; } + .large + small.error { width: 440px; } + + /* ----------------------------------------- + Nicer Forms + ----------------------------------------- */ + form.nice div.form-field input, form.nice input.input-text, form.nice textarea { border: solid 1px #bbb; border-radius: 2px; -webkit-border-radius: 2px; -moz-border-radius: 2px; } + form.nice div.form-field input, form.nice input.input-text, form.nice textarea { font-size: 13px; padding: 6px 3px 4px; outline: none !important; background: url(../images/misc/input-bg.png) #fff; } + form.nice div.form-field input:focus, form.nice input.input-text:focus, form.nice textarea:focus { background-color: #f9f9f9; } + + form.nice fieldset { border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; } + + form.nice div.form-field input[type=radio], form.nice div.form-field input[type=checkbox] { display: inline; width:auto; margin-bottom:0; } + + form.nice div.form-field.error small, form.nice small.error { padding: 6px 4px; border: solid 0 #C00000; border-width: 0 1px 1px 1px; margin-top: -10px; background: #C00000; color: #fff; font-size: 12px; font-weight: bold; border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; -webkit-border-bottom-left-radius: 2px; -webkit-border-bottom-right-radius: 2px; -moz-border-radius-bottomleft: 2px; -moz-border-radius-bottomright: 2px; } + + form.nice div.form-field.error .small + small, form.nice .small + small.error { width: 132px; } + form.nice div.form-field.error .medium + small, form.nice .medium + small.error { width: 252px; } + form.nice div.form-field.error .large + small, form.nice .large + small.error { width: 432px; } + + /* ----------------------------------------- + Custom Forms + ----------------------------------------- */ + + form.custom span.custom { display: inline-block; width: 14px; height: 14px; position: relative; top: 2px; border: solid 1px #ccc; background: url(../images/misc/custom-form-sprites.png) 0 0 no-repeat; } + form.custom span.custom.radio { border-radius: 7px; -webkit-border-radius: 7px; -moz-border-radius: 7px; } + form.custom span.custom.radio.checked { background-position: 0 -14px; } + form.custom span.custom.checkbox.checked { background-position: 0 -28px; } + + form.custom div.custom.dropdown { position: relative; display: inline-block; width: auto; height: 28px; margin-bottom: 9px; } + form.custom div.custom.dropdown a.current { display: block; width: auto; line-height: 26px; padding: 0 38px 0 6px; border: solid 1px #ddd; color: #141414; } + form.custom div.custom.dropdown a.selector { position: absolute; width: 26px; height: 26px; display: block; background: url(../images/misc/custom-form-sprites.png) -14px 0 no-repeat; right: 0; top: 0; border: solid 1px #ddd; } + form.custom div.custom.dropdown:hover a.selector, + form.custom div.custom.dropdown.open a.selector { background-position: -14px -26px; } + + form.custom div.custom.dropdown ul { position: absolute; width: auto; display: none; margin: 0; left: 0; top: 27px; margin: 0; padding: 0; background: rgba(255,255,255,0.9); border: solid 1px #ddd; z-index: 10; } + form.custom div.custom.dropdown ul li { cursor: pointer; padding: 3px 38px 3px 6px; margin: 0; white-space: nowrap} + form.custom div.custom.dropdown ul li.selected { background: url(../images/misc/custom-form-sprites.png) right -52px no-repeat; } + form.custom div.custom.dropdown ul li:hover { background-color: #2a85e8; color: #fff; } + form.custom div.custom.dropdown ul li.selected:hover { background: url(../images/misc/custom-form-sprites.png) #2a85e8 right -78px no-repeat; } + form.custom div.custom.dropdown ul.show { display: block; } + + form.custom div.custom.dropdown.open ul { display: block; } + + +/* CSS for jQuery Orbit Plugin 1.2.3 + * www.ZURB.com/playground + * Copyright 2010, ZURB + * Free to use under the MIT license. + * http://www.opensource.org/licenses/mit-license.php + + + +/* PUT IN YOUR SLIDER ID AND SIZE TO MAKE LOAD BEAUTIFULLY + ================================================== */ +#caseStudies { + width: 1000px; + height: 210px; + background: #fff url('../images/orbit/loading.gif') no-repeat center center; + overflow: hidden; } +#caseStudies>img, +#caseStudies>div, +#caseStudies>a { display: none; } + + + + +/* CONTAINER + ================================================== */ + +div.orbit-wrapper { + width: 1px; + height: 1px; + position: relative; } + +div.orbit { + width: 1px; + height: 1px; + position: relative; + overflow: hidden } + +div.orbit.with-bullets { + margin-bottom: 40px; +} + +div.orbit>img { + position: absolute; + top: 0; + left: 0; + /* display: none; */ } + +div.orbit>a { + border: none; + position: absolute; + top: 0; + left: 0; + line-height: 0; + display: none; } + +.orbit>div { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; } + +/* Note: If your slider only uses content or anchors, you're going to want to put the width and height declarations on the ".orbit>div" and "div.orbit>a" tags in addition to just the .orbit-wrapper */ + + +/* TIMER + ================================================== */ + +div.timer { + width: 40px; + height: 40px; + overflow: hidden; + position: absolute; + top: 10px; + right: 10px; + opacity: .6; + cursor: pointer; + z-index: 1001; } + +span.rotator { + display: block; + width: 40px; + height: 40px; + position: absolute; + top: 0; + left: -20px; + background: url(../images/orbit/rotator-black.png) no-repeat; + z-index: 3; } + +span.mask { + display: block; + width: 20px; + height: 40px; + position: absolute; + top: 0; + right: 0; + z-index: 2; + overflow: hidden; } + +span.rotator.move { + left: 0 } + +span.mask.move { + width: 40px; + left: 0; + background: url(../images/orbit/timer-black.png) repeat 0 0; } + +span.pause { + display: block; + width: 40px; + height: 40px; + position: absolute; + top: 0; + left: 0; + background: url(../images/orbit/pause-black.png) no-repeat; + z-index: 4; + opacity: 0; } + +span.pause.active { + background: url(../images/orbit/pause-black.png) no-repeat 0 -40px } + +div.timer:hover span.pause, +span.pause.active { + opacity: 1 } + + +/* CAPTIONS + ================================================== */ + +.orbit-caption { + display: none; + font-family: "HelveticaNeue", "Helvetica-Neue", Helvetica, Arial, sans-serif; } + +.orbit-wrapper .orbit-caption { + background: #000; + background: rgba(0,0,0,.6); + z-index: 1000; + color: #fff; + text-align: center; + padding: 7px 0; + font-size: 13px; + position: absolute; + right: 0; + bottom: 0; + width: 100%; } + + +/* DIRECTIONAL NAV + ================================================== */ + +div.slider-nav { + display: block } + +div.slider-nav span { + width: 78px; + height: 100px; + text-indent: -9999px; + position: absolute; + z-index: 1000; + top: 50%; + margin-top: -50px; + cursor: pointer; } + +div.slider-nav span.right { + background: url(../images/orbit/right-arrow.png); + right: 0; } + +div.slider-nav span.left { + background: url(../images/orbit/left-arrow.png); + left: 0; } + +/* BULLET NAV + ================================================== */ + +.orbit-bullets { + position: absolute; + z-index: 1000; + list-style: none; + bottom: -40px; + left: 50%; + margin-left: -50px; + padding: 0; } + +.orbit-bullets li { + float: left; + margin-left: 5px; + cursor: pointer; + color: #999; + text-indent: -9999px; + background: url(../images/orbit/bullets.jpg) no-repeat 4px 0; + width: 13px; + height: 12px; + overflow: hidden; } + +.orbit-bullets li.active { + color: #222; + background-position: -8px 0; } + +.orbit-bullets li.has-thumb { + background: none; + width: 100px; + height: 75px; } + +.orbit-bullets li.active.has-thumb { + background-position: 0 0; + border-top: 2px solid #000; } + +/* FLUID LAYOUT + ================================================== */ +.orbit .fluid-placeholder { + visibility: hidden; + position: static; + display: block; + width: 100%; +} + +.orbit, .orbit-wrapper { width: 100% !important; } + +.orbit-bullets { + position: absolute; + z-index: 1000; + list-style: none; + bottom: -50px; + left: 50%; + margin-left: -50px; + padding: 0; } + +.orbit-bullets li { + float: left; + margin-left: 5px; + cursor: pointer; + color: #999; + text-indent: -9999px; + background: url(../images/orbit/bullets.jpg) no-repeat 4px 0; + width: 13px; + height: 12px; + overflow: hidden; } + +.orbit-bullets li.has-thumb { + background: none; + width: 100px; + height: 75px; } + +.orbit-bullets li.active { + color: #222; + background-position: -8px 0; } + +.orbit-bullets li.active.has-thumb { + background-position: 0 0; + border-top: 2px solid #000; } +/* -------------------------------------------------- + Reveal Modals + -------------------------------------------------- */ + + .reveal-modal-bg { + position: fixed; + height: 100%; + width: 100%; + background: #000; + z-index: 2000; + display: none; + top: 0; + left: 0; + } + + .reveal-modal { + visibility: hidden; + top: 100px; + left: 50%; + margin-left: -300px; + width: 520px; + background: #eee url(../images/misc/modal-gloss.png) no-repeat -200px -80px; + position: absolute; + z-index: 2001; + padding: 30px 40px 34px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + -moz-box-shadow: 0 0 10px rgba(0,0,0,.4); + -webkit-box-shadow: 0 0 10px rgba(0,0,0,.4); + box-shadow: 0 0 10px rgba(0,0,0,.4); + } + + .reveal-modal.small { width: 200px; margin-left: -140px;} + .reveal-modal.medium { width: 400px; margin-left: -240px;} + .reveal-modal.large { width: 600px; margin-left: -340px;} + .reveal-modal.xlarge { width: 800px; margin-left: -440px;} + + .reveal-modal .close-reveal-modal { + font-size: 22px; + line-height: .5; + position: absolute; + top: 8px; + right: 11px; + color: #aaa; + text-shadow: 0 -1px 1px rbga(0,0,0,.6); + font-weight: bold; + cursor: pointer; + } + + .reveal-modal .row { + min-width: 0; + } + + /* Mobile */ + + @media handheld, only screen and (device-width: 768px), (device-width: 800px) { + .reveal-modal-bg { position: absolute; } + + .reveal-modal, + .reveal-modal.small, + .reveal-modal.medium, + .reveal-modal.large, + .reveal-modal.xlarge { width: 60%; top: 30%; left: 15%; margin-left: 0; padding: 5%; height: auto; } + } + + @media handheld, only screen and (max-width: 767px) { + .reveal-modal-bg { position: absolute; } + + .reveal-modal, + .reveal-modal.small, + .reveal-modal.medium, + .reveal-modal.large, + .reveal-modal.xlarge { width: 80%; top: 15%; left: 5%; margin-left: 0; padding: 5%; height: auto; } + } + + + /* + + NOTES + + Close button entity is × + + Example markup + +
    +

    Awesome. I have it.

    +

    Your couch. I it's mine.

    +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ultrices aliquet placerat. Duis pulvinar orci et nisi euismod vitae tempus lorem consectetur. Duis at magna quis turpis mattis venenatis eget id diam.

    + × +
    + + */ +/* -------------------------------------------------- + :: Typography + -------------------------------------------------- */ + + @media handheld, only screen and (max-width: 767px) { + h1 { font-size: 32px; font-size: 3.2rem; line-height: 1.3; } + h2 { font-size: 28px; font-size: 2.8rem; line-height: 1.3; } + h3 { font-size: 21px; font-size: 2.1rem; line-height: 1.3; } + h4 { font-size: 18px; font-size: 1.8rem; line-height: 1.2; } + h5 { font-size: 16px; font-size: 1.6rem; line-height: 1.2; } + h6 { font-size: 15px; font-size: 1.5rem; line-height: 1.2; } + body, p { font-size: 15px; font-size: 1.5rem; line-height: 1.4; } + } + + +/* -------------------------------------------------- + :: Grid + -------------------------------------------------- */ + + /* Tablet screens */ + @media only screen and (device-width: 768px), (device-width: 800px) { + /* Currently unused */ + } + + + /* Mobile */ + @media only screen and (max-width: 767px) { + body { -webkit-text-size-adjust: none; -ms-text-size-adjust: none; width: 100%; min-width: 0; margin-left: 0; margin-right: 0; padding-left: 0; padding-right: 0; } + .container { min-width: 0; margin-left: 0; margin-right: 0; } + .row { width: 100%; min-width: 0; margin-left: 0; margin-right: 0; } + .row .row .column, .row .row .columns { padding: 0; } + .column, .columns { width: auto !important; float: none; margin-left: 0; margin-right: 0; } + .column:last-child, .columns:last-child { margin-right: 0; } + + .offset-by-one, .offset-by-two, .offset-by-three, .offset-by-four, .offset-by-five, .offset-by-six, .offset-by-seven, .offset-by-eight, .offset-by-nine, .offset-by-ten, .offset-by-eleven, .centered { margin-left: 0 !important; } + + .push-two, .push-three, .push-four, .push-five, .push-six, .push-seven, .push-eight, .push-nine, .push-ten { left: auto; } + .pull-two, .pull-three, .pull-four, .pull-five, .pull-six, .pull-seven, .pull-eight, .pull-nine, .pull-ten { right: auto; } + + /* Mobile 4-column Grid */ + .row .phone-one.column:first-child, .row .phone-two.column:first-child, .row .phone-three.column:first-child, .row .phone-four.column:first-child, .row .phone-one.columns:first-child, .row .phone-two.columns:first-child, .row .phone-three.columns:first-child, .row .phone-four.columns:first-child { margin-left: 0; } + + .row .phone-one.column, .row .phone-two.column, .row .phone-three.column, .row .phone-four.column, + .row .phone-one.columns, .row .phone-two.columns, .row .phone-three.columns, .row .phone-four.columns { margin-left: 4.4%; float: left; min-height: 1px; position: relative; padding: 0; } + + .row .phone-one.columns { width: 21.68% !important; } + .row .phone-two.columns { width: 47.8% !important; } + .row .phone-three.columns { width: 73.9% !important; } + .row .phone-four.columns { width: 100% !important; } + + .row .columns.push-one-phone { left: 26.08%; } + .row .columns.push-two-phone { left: 52.2% } + .row .columns.push-three-phone { left: 78.3% } + + .row .columns.pull-one-phone { right: 26.08% } + .row .columns.pull-two-phone { right: 52.2% } + .row .columns.pull-three-phone { right: 78.3%; } + + + } + + +/* -------------------------------------------------- + :: Block Grids + -------------------------------------------------- */ + + @media only screen and (max-width: 767px) { + .block-grid.mobile { margin-left: 0; } + .block-grid.mobile li { float: none; width: 100%; margin-left: 0; } + } + + + +/* -------------------------------------------------- + :: Mobile Visibility Affordances +---------------------------------------------------*/ + + + .show-on-phones { display: none !important; } + .show-on-tablets { display: none !important; } + .show-on-desktops { display: block !important; } + + .hide-on-phones { display: block !important; } + .hide-on-tablets { display: block !important; } + .hide-on-desktops { display: none !important; } + + + @media only screen and (max-device-width: 800px), only screen and (device-width: 1024px) and (device-height: 600px), only screen and (width: 1280px) and (orientation: landscape), only screen and (device-width: 800px) { + .hide-on-phones { display: block !important; } + .hide-on-tablets { display: none !important; } + .hide-on-desktops { display: block !important; } + + .show-on-phones { display: none !important; } + .show-on-tablets { display: block !important; } + .show-on-desktops { display: none !important; } + } + + + @media only screen and (max-width: 767px) { + .hide-on-phones { display: none !important; } + .hide-on-tablets { display: block !important; } + .hide-on-desktops { display: block !important; } + + .show-on-phones { display: block !important; } + .show-on-tablets { display: none !important; } + .show-on-desktops { display: none !important; } + } + + /* only screen and (device-width: 1280px), only screen and (max-device-width: 1280px), /* + /* Keeping this in as a reminder to address support for other tablet devices like the Xoom in the future */ + + /* Specific overrides for elements that require something other than display: block */ + + table.show-on-desktops { display: table !important; } + table.hide-on-phones { display: table !important; } + table.hide-on-tablets { display: table !important; } + + @media only screen and (max-device-width: 800px), only screen and (device-width: 1024px) and (device-height: 600px), only screen and (width: 1280px) and (orientation: landscape), only screen and (device-width: 800px) { + table.hide-on-phones { display: block !important; } + table.hide-on-desktops { display: block !important; } + table.show-on-tablets { display: block !important; } + } + + @media only screen and (max-width: 767px) { + table.hide-on-tablets { display: block !important; } + table.hide-on-desktops { display: block !important; } + table.show-on-phones { display: block !important; } + } + + +/* -------------------------------------------------- + :: Forms +---------------------------------------------------*/ + + + @media only screen and (max-width: 767px) { + div.form-field input, div.form-field input.small, div.form-field input.medium, div.form-field input.large, div.form-field input.oversize, input.input-text, input.input-text.oversize, textarea, + form.nice div.form-field input, form.nice div.form-field input.oversize, form.nice input.input-text, form.nice input.input-text.oversize, form.nice textarea { display: block; width: 96%; padding: 6px 2% 4px; font-size: 18px; } + form.nice div.form-field input, form.nice div.form-field input.oversize, form.nice input.input-text, form.nice input.input-text.oversize, form.nice textarea { -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; } + form.nice div.form-field.error small, form.nice small.error { padding: 6px 2%; display: block; } + form.nice div.form-field.error .small + small, form.nice .small + .error { width: auto; } + form.nice div.form-field.error .medium + small, form.nice .medium + .error { width: auto; } + form.nice div.form-field.error .large + small, form.nice .large + .error { width: auto; } + } + + +/* -------------------------------------------------- + :: UI +---------------------------------------------------*/ + + /* Buttons */ + @media only screen and (max-width: 767px) { + .button { display: block; } + button.button { width: 100%; padding-left: 0; padding-right: 0; } + } + + /* Tabs */ + + @media only screen and (max-width: 767px) { + dl.tabs.mobile, dl.nice.tabs.mobile { width: auto; margin: 20px -20px 40px; height: auto; } + dl.tabs.mobile dt, dl.tabs.mobile dd, dl.nice.tabs.mobile dt, dl.nice.tabs.mobile dd { float: none; height: auto; } + + dl.tabs.mobile dd a { display: block; width: auto; height: auto; padding: 18px 20px; line-height: 1; border: solid 0 #ccc; border-width: 1px 0 0; margin: 0; color: #555; background: #eee; font-size: 15px; font-size: 1.5rem; } + dl.tabs.mobile dd a.active { height: auto; margin: 0; border-width: 1px 0 0; } + + .nice.tabs.mobile { border-bottom: solid 1px #ccc; height: auto; } + .nice.tabs.mobile dd a { padding: 18px 20px; border: none; border-left: none; border-right: none; border-top: 1px solid #ccc; background: #fff; } + .nice.tabs.mobile dd a.active { border: none; background: #00a6fc; color: #fff; margin: 0; position: static; top: 0; height: auto; } + .nice.tabs.mobile dd:first-child a.active { margin: 0; } + + dl.contained.mobile, dl.nice.contained.mobile { margin-bottom: 0; } + dl.contained.tabs.mobile dd a { padding: 18px 20px; } + dl.nice.contained.tabs.mobile dd a { padding: 18px 20px; } + } + + /* Nav Bar */ + + @media only screen and (max-width: 767px) { + .nav-bar { height: auto; } + .nav-bar>li { float: none; display: block; border-right: none; } + .nav-bar>li>a { text-align: left; border-top: 1px solid #ddd; border-right: none; } + .nav-bar>li:first-child>a { border-top: none; } + .nav-bar>li.has-flyout>a:after { content: ""; width: 0; height: 0; border-left: 4px solid transparent;border-right: 4px solid transparent; border-top: 4px solid #2a85e8; display: block; } + .nav-bar>li:hover>a { font-weight: bold; } + .nav-bar>li:hover ul { position: relative; } + + .flyout { position: relative; width: auto; top: auto; margin-right: -2px; border-width: 1px 1px 0px 1px; } + .flyout.right { float: none; right: auto; left: -1px; } + .flyout.small, .flyout.large { width: auto; } + .flyout p:last-child { margin-bottom: 18px; } + } + + /* Nav Bar */ + + @media only screen and (max-device-width: 800px), only screen and (device-width: 1024px) and (device-height: 600px), only screen and (width: 1280px) and (orientation: landscape), only screen and (device-width: 800px), only screen and (max-width: 767px) { + .video { padding-top: 0; } + } diff --git a/public/stylesheets/ie.css b/public/stylesheets/ie.css new file mode 100644 index 0000000..a0b756f --- /dev/null +++ b/public/stylesheets/ie.css @@ -0,0 +1,6 @@ +/* Foundation v2.1.4 http://foundation.zurb.com */ +/* This is for all IE specfific style less than IE9. We hate IE. */ + + div.panel { border: 1px solid #ccc; } + .lt-ie8 .nav-bar li.has-flyout a { padding-right: 20px; } + .lt-ie8 .nav-bar li.has-flyout a:after { border-top: none; } diff --git a/public/stylesheets/images/ui-bg_flat_30_cccccc_40x100.png b/public/stylesheets/images/ui-bg_flat_30_cccccc_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..5473afffbc2662173f5af5c27d966c072de8039b GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FscKIb$B>N1x91EQ4=4yQ7#`Ta z<$H)q$%zYm;;c7~Kd+Iuj%U9o62cnl7#bi-T}u42J&U%yNJ})(84RATelF{r5}E*| CoG&2& literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-bg_flat_50_5c5c5c_40x100.png b/public/stylesheets/images/ui-bg_flat_50_5c5c5c_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..5950a8db9e64e8d00bb28726cb869947abfdc7fc GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FscKIb$B>N1x91EQ4=4yQ7&<)v zy**H+ z`**vX!*n&P7dc50adyfl&Nwm{9INpTEp=t_C|dt|%9X7Z^~WTNlbGI|=7;M1& literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-bg_glass_40_ffc73d_1x400.png b/public/stylesheets/images/ui-bg_glass_40_ffc73d_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..d683a41030777b87fe72db1ec938fc5472bf8fcb GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAGVZ#9R3S1@b&y978O6-(EE2YjO~2d05XT zHvNc;us0XqvxPgETh1}(=)^?xCsZ80R5SD4*(V<(=UrtGT(M@x=@v;jc6s#@%lG1G zb2iSo6(#3&bK@o6>;L+$#xLfUbvbRHKYdf;lnuLjR)5ZSVxA@w`{_x44Wr={XgrV17G0@K0tCwr|_L4FG`cX~&a)0{{Wbe*M`mHv8AS(OX;q*z!_Z@>{1%MGiyu zR|xbXaPnOIdD+Ux{R6(br<1qK{_>6OVJiyw2cO!f{+2bo_c6Uf@ak*A-gUS0ya}i{ z;V|n10Kl4N15Q?43I?7#{{I#OKX|+gVL-*#_gjMA40sU-?0os>#q4m5zYlyNaNy0i zw^Rg@n2PM1EYnI!r8-ve`DR?E&r)g^!dw?^1cUDl{jIC0LqtYJ2-{< z^zg_cP8X>+p|zOkQCSM#xGKcaAOiy-SU^Z1WD^&VjsjU9nusJEZ09-SzK7LC5*abv z3bV0}9Jo{3#yNAHDALv}?}};!m&BKh#+>m@(WkDJjK<89uTGHg@D-UX>-ETjTK)~$ zOr)}HQ5Q=cqfulXw=zX=4D4-ulurld1T(#Bl#sa@S5 zp`alO&km=OxtEWjt&`%U^>H+i7?)HTR1x^ff5x*RW+|o>hxeK_Kj$^Ig8CR zEqR6U6p+fT895w+wE0LvO;>FXYFOy(XDztA-TA3z$n$NTg#FEvs-1liN z_Nd1G7J&@g9g}bvL8^2_2}WvrG*#cC7wK731W2GBz+jOT^+0{`PNdB!ES|*3C0zQ5 zaN3awY3wD=vgoBTW8JhS)k9-u6((jG8W5J=IvOi0csINJs#yO7^pr@JOg^2@yWSr$ z9H89a;5ALfT5P*q-ysOn-(zrTNv~mw&fses8XC-rM*OQ4w5MNgP4KJ~Bd(HLy{%0; z7gd#cTlMZ*$k+Sqt!4!>k+M1O!(%Np{^9g<`lR~Fc_pDPM+vVC=QPeD`(JRRC~er2 zCi|jkNQ@4;LqAmPTU#w?hmZF1R-aHl=em*uW2JB!U9hT&`^2=uJOOET@j`Ok0#d6 z#(L6k=6ErD*U;XUposM4a^d3L zp-T^hYj>Xmwtaag;LpHTG_dVQw4W-o!G86VCAjbROZ*&Db`T>1l~(J<&d;6s33{d9 z`^k6n9Y1mYo9(hMp0$+wN`M2(jWR#7`wh+LARzCn4N56D%KVR4-u*2}bB{fPi=Paw z6gk|PBCK5=Be17ZBA!UgCS^bmQTXUH0T|@vR`&$2%0`k{CKz4&U2QEYpeHEbxx1yMjh$T zgH4%u$59+kAz#93N7_0O1=Cc<4+g{{!VmvO`@jXnG--~=JzU@`t0Wk2^`7?Wk*WGc5TVydoVC|KVrIzB za*zX6duE2%bCs^gjG+nlgyq-B7Rgi2dP)%$N`p>Fzv0(c&);tlsH5~@w3Wa6?;|Hv zNsmEwPY{wtt{bi6qxNY>L|gaRm?0CRoQUiZ%h) zq(G*nxm^ELo_SHJGxFKNZ)BmA%i(Z}p9hp_yZ~Eg@0lwdJ2rk#AT1>$`NrEH|L$Kk CqOPR? literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-bg_highlight-soft_80_eeeeee_1x100.png b/public/stylesheets/images/ui-bg_highlight-soft_80_eeeeee_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..e56eefd612ae74339bd45ae91ddfc2ae2eb7092b GIT binary patch literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^j6j^i!3HGVb)pi0l$xiDV~E7mQ98UlM literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-bg_inset-soft_25_000000_1x100.png b/public/stylesheets/images/ui-bg_inset-soft_25_000000_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..3525eb9ff4c26d2c74003e7e2fdf09cccf7c9d54 GIT binary patch literal 98 zcmeAS@N?(olHy`uVBq!ia0vp^j6j^i!3HGVb)pi0l%}VPV~E7mvV v4~LtZ8=D#sywNe@G+8Wgr}4YKCJTdvGLyysE7#8hH8FU)`njxgN@xNASlk-& literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-bg_inset-soft_30_f58400_1x100.png b/public/stylesheets/images/ui-bg_inset-soft_30_f58400_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..2b6a9f92408ede5e967497f0c917d99e06a2e7f8 GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^j6j^i!3HGVb)pi0l#Qp0V~E7myZwe-3<^BV`X67{ z-Q39+s_r<4W%}b;6Bcgql6WBPGHXN18AjIAtjA9+sZ48Zd&-pi=Idqi7tQ7DO<7N; RDFDr3@O1TaS?83{1OO`oCW`<7 literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-icons_222222_256x240.png b/public/stylesheets/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..b273ff111d219c9b9a8b96d57683d0075fb7871a GIT binary patch literal 4369 zcmd^?`8O2)_s3^phOrG}UnfiUEn8(9QW1?MNkxXVDEpFin2{xWrLx5kBC;k~GmPmYTG^FX}c% zlGE{DS1Q;~I7-6ze&TN@+F-xsI6sd%SwK#*O5K|pDRZqEy< zJg0Nd8F@!OxqElm`~U#piM22@u@8B<moyKE%ct`B(jysxK+1m?G)UyIFs1t0}L zemGR&?jGaM1YQblj?v&@0iXS#fi-VbR9zLEnHLP?xQ|=%Ihrc7^yPWR!tW$yH!zrw z#I2}_!JnT^(qk)VgJr`NGdPtT^dmQIZc%=6nTAyJDXk+^3}wUOilJuwq>s=T_!9V) zr1)DT6VQ2~rgd@!Jlrte3}}m~j}juCS`J4(d-5+e-3@EzzTJNCE2z)w(kJ90z*QE) zBtnV@4mM>jTrZZ*$01SnGov0&=A-JrX5Ge%Pce1Vj}=5YQqBD^W@n4KmFxxpFK`uH zP;(xKV+6VJ2|g+?_Lct7`uElL<&jzGS8Gfva2+=8A@#V+xsAj9|Dkg)vL5yhX@~B= zN2KZSAUD%QH`x>H+@Ou(D1~Pyv#0nc&$!1kI?IO01yw3jD0@80qvc?T*Nr8?-%rC8 z@5$|WY?Hqp`ixmEkzeJTz_`_wsSRi1%Zivd`#+T{Aib6-rf$}M8sz6v zb6ERbr-SniO2wbOv!M4)nb}6UVzoVZEh5kQWh_5x4rYy3c!871NeaM(_p=4(kbS6U#x<*k8Wg^KHs2ttCz<+pBxQ$Z zQMv;kVm5_fF_vH`Mzrq$Y&6u?j6~ftIV0Yg)Nw7JysIN_ z-_n*K_v1c&D}-1{NbBwS2h#m1y0a5RiEcYil+58$8IDh49bPnzE7R8In6P%V{2IZU z7#clr=V4yyrRe@oXNqbqo^^LvlLE?%8XaI&N(Np90-psU}7kqmbWk zZ;YBwJNnNs$~d!mx9oMGyT( znaBoj0d}gpQ^aRr?6nW)$4god*`@Uh2e+YpS@0(Mw{|z|6ko3NbTvDiCu3YO+)egL z>uW(^ahKFj>iJ-JF!^KhKQyPTznJa;xyHYwxJgr16&Wid_9)-%*mEwo{B_|M9t@S1 zf@T@q?b2Qgl!~_(Roe;fdK)y|XG0;ls;ZbT)w-aOVttk#daQcY7$cpY496H*`m@+L zeP#$&yRbBjFWv}B)|5-1v=(66M_;V1SWv6MHnO}}1=vby&9l+gaP?|pXwp0AFDe#L z&MRJ^*qX6wgxhA_`*o=LGZ>G_NTX%AKHPz4bO^R72ZYK}ale3lffDgM8H!Wrw{B7A z{?c_|dh2J*y8b04c37OmqUw;#;G<* z@nz@dV`;7&^$)e!B}cd5tl0{g(Q>5_7H^@bEJi7;fQ4B$NGZerH#Ae1#8WDTH`iB&) zC6Et3BYY#mcJxh&)b2C^{aLq~psFN)Q1SucCaBaBUr%5PYX{~-q{KGEh)*;n;?75k z=hq%i^I}rd;z-#YyI`8-OfMpWz5kgJE3I!3ean6=UZi!BxG7i(YBk? z02HM7wS0)Wni{dWbQMRtd-A)_Az!t>F;IwWf~!*)-Az4}yryNkz&9)w>ElA80Oc`6 zHo#9H!Y3*Qx9n@Jn)!w6G^hb;e_n8zpIyXCN`JFkPc)^Q?2MsLNFhMgrcZI-<#1ne zjH;KFf?4eAT9mQZ}ZfHLGA#d%s;SZK4p0FwZT2S^{ zQ2BG1xJsbK6?yrHTjJi|5C0u=!|r!?*4FL%y%3q#(d+e>b_2I9!*iI!30}42Ia0bq zUf`Z?LGSEvtz8s``Tg5o_CP(FbR0X$FlE0yCnB7suDPmI2=yOg^*2#cY9o`X z;NY-3VBHZjnVcGS){GZ98{e+lq~O$u6pEcgd0CrnIsWffN1MbCZDH<7c^hv+Z0Ucf0{w zSzi^qKuUHD9Dgp0EAGg@@$zr32dQx>N=ws`MESEsmzgT2&L;?MSTo&ky&!-JR3g~1 zPGTt515X)wr+Bx(G9lWd;@Y3^Vl}50Wb&6-Tiy;HPS0drF`rC}qYq22K4)G#AoD0X zYw$E+Bz@Zr^50MAwu@$?%f9$r4WHH?*2|67&FXFhXBrVFGmg)6?h3^-1?t;UzH0*I zNVf9wQLNLnG2@q>6CGm>&y|lC`iCFfYd}9i%+xkl^5oBJ?<;aneCfcHqJh7Yl5uLS z9Fx-(kMdcNyZejXh22N{mCw_rX1O!cOE&3>e(ZH81PR95wQC37En4O{w;{3q9n1t&;p)D%&Z%Nw$gSPa!nz8Slh7=ko2am)XARwOWw zpsz0~K!s{(dM$NB=(A=kkp>T(*yU6<_dwIx>cH4+LWl282hXa6-EUq>R3t?G2623< z*RwTN%-fgBmD{fu*ejNn)1@KG?Sg*8z3hYtkQJQjB6 zQ|x>wA=o$=O)+nLmgTXW3_6diA;b4EY{*i*R%6dO2EMg z@6g?M3rpbnfB@hOdUeb96=~I?OIA3@BWAGmTwiQ{x5Cqq<8c10L!P zd@Qk^BseTX%$Q7^s}5n%HB|)gKx}H$d8Sb$bBnq9-AglT2dGR2(+I;_fL|R4p$odJ zllfb0NqI)7=^z~qAm1V{(PkpxXsQ#4*NH9yYZ`Vf@)?#ueGgtCmGGY|9U#v|hRdg- zQ%0#cGIfXCd{Y)JB~qykO;KPvHu|5Ck&(Hn%DF~cct@}j+87xhs2ew;fLm5#2+mb| z8{9e*YI(u|gt|{x1G+U=DA3y)9s2w7@cvQ($ZJIA)x$e~5_3LKFV~ASci8W}jF&VeJoPDUy(BB>ExJpck;%;!`0AAo zAcHgcnT8%OX&UW_n|%{2B|<6Wp2MMGvd5`T2KKv;ltt_~H+w00x6+SlAD`{K4!9zx z*1?EpQ%Lwiik){3n{-+YNrT;fH_niD_Ng9|58@m8RsKFVF!6pk@qxa{BH-&8tsim0 zdAQ(GyC^9ane7_KW*#^vMIoeQdpJqmPp%%px3GIftbwESu#+vPyI*YTuJ6+4`z{s? zpkv~0x4c_PFH`-tqafw5)>4AuQ78SkZ!$8}INLK;Egr;2tS18hEO5=t;QDmZ-qu?I zG+=DN`nR72Xto{{bJp||`k}-2G;5#xg8E~xgz22)^_Z;=K|4@(E&5J)SY2of=olcw z5)@L)_Ntcm!*5nEy0M9v0`S33;pO4TN;>4(Z+19p_0>u#e-vE zXCU(6gAvu~I7Cw(xd%0e59MNLw^U37ZDbsBrj%eDCexw8a3G`nTcXVNL6{B7Hj@i& zbVB{;ApEtHk76q08DJ48dSxd$C(;$K6=FpU<~l9pVoT9arW^Vu{%Bcn4`eIpkOVC| z$)AKYG_`ypM{0@BUb3^9lqi_c?ONH|4UJMJWDowMVjacycX7}9g={O7swOB+{;+?; zjBo!9?+nd)ie#x5IbFW-zBOo0c4q@9wGVt5;pNt`=-~Zgcw#*`m($6ibxtZ`H=e=} zF#GZ~5$%AUn};8U#tRem0J(JTR}d4vR(dgK2ML~lZsPhayJ2h1%sD4FVst| zKF)+@`iNzLRjg4=K8@**0=5cE>%?FDc({I^+g9USk<8$&^qD~@%W0i4b|yMG*p4`N zh}I!ltTRI8Ex$+@V{02Br%xq#O?UlhO{r8WsaZnZCZq0MK9%AXU%MDLT;3=0A9(BV z9VxxxJd7jo$hw3q;3o?yBLmA=azBUrd9>-<_ANs0n3?-Ic*6&ytb@H~?0E(*d>T5n z-HiH2jsDf6uWhID%#n>SzOqrFCPDfUcu5QPd?<(=w6pv1BE#nsxS{n!UnC9qAha1< z;3cpZ9A-e$+Y)%b;w@!!YRA9p%Kf9IHGGg^{+p`mh;q8i7}&e@V3EQaMsItEMS&=X plT@$;k0WcB_jb;cn%_Idz4HO$QU*abf4}+wi?e96N>fbq{{i|W0@(ln literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-icons_4b8e0b_256x240.png b/public/stylesheets/images/ui-icons_4b8e0b_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..3bdb67be8f85c00d44940330a9a8cb07a73e83a4 GIT binary patch literal 4369 zcmd^?`8O2)_s3^phOrG}UnfiUEn8(9QW1?MNkxXVDEpFin2{xWrLx5kBC;k~GmC-Ajq!3AfU8Dx90^_ zp3}MKjJzYC+`T(&egFXQ#9Ek{*oVAaa!zrZtmlRFnwQPRJXH<%pkK2*eP`pT=lwD7 zifq+4BY_rUTa+U|2#&?i7>PVvD?7R4ZfOLPT{e9G~G!Ls3s8JtQE`jMM9wl2V9&Q+K2DHW0M+uQmEr%nYJ^7cK?uIpU-)=wn71ZZ-=@ar0;3^AY z5+TI{2b(e%t{2PZ^HKF*vu@+Xr&BAc@2BC4 z_vCgww#i=)ea5Vo$glEEVBBg_VPBj!)OO>)f@}#dg6ULOeC>LBHz<;*5Y;YfE0lNx zg{N+4@lO~ozxpF69qV@VOGnc248Iuag4C1T)P^(hWkpP!{h!JekX}m^Q#b2B4f1oT zIjsGz)4}-$rQ*-tSuc%qG>%<4xM#E& zN)7lRK~^2VdiloY4>;#}A!yHOAXEmEi^+eA#05pawGXs>!z)gSoDuI#>bRCq-qjJe zZ)r=A`*EMX6+)~er1kdv1L^)0-PsAEM7JF$O6G8>496$24lkOSR^RTfUuIz%iSfn5b-t!##cs7sQI);gdAvqmn_v|%I9k;fCPl0Z)R1+hNQONJN zH%3jT9sOq*a`LF*MiY=zlSSQZ;{_FL9M07A=In+O!~wR}=bzGEQpk2!Vc0p)qKAH? zOk{(%06W#)DdICQ_S%Q@<0Y+!?9%#$gWJ%)EO->^YZP{<`oB4~9xh zL9-0*c4@B#O2ylYs_g`Ky$zb~v!M`NRaMNFYF*Gsu|7)=JyyMHjFC=HhGUE@{aI|B zJ~ITXU052%7jFb5Ys#fhS_?4kqc7H0EU49B8(Chg0&JzU=Gka#xOz1)H0d4m7ZnRA z=M^tdY|U6T!fmte{W?_r8H~qdq|q{5AMU_2It1I4143n~xL?4&K#BOB48l9_Rdm!(c^C?JU;tF0 zEh@o1y6Qa_>}#AwX{VY+`C^kNkxhgb1P5cB0%xupAXyg9NO=SnXrJUE?rQg{Lcsn+ zAZKctGLfbK_B#^&Nev|0^fB&?DN=ak8|0!np524LD25=s84BP8Vl(3=jflNp{X>e@ z637Ri5xx;&JNl+XYImA|{;XR~P*svYDEWYJ6I5!6uO~2twFC1ZQevB7#3z~(apxn& z^J@>Mc`>PJair{yT`iuan-V+i%|Ho-pA<1?V-k^R2Q<5;Co%XxmL` z018t4T0TTwO^w)Gx{9OSJ^9_|kgwX`7%0Rw!PO~@?xvnfUehvN;2Rc;^l>3kfbtk3 z8{j7p;S&{uTlTe9&HTc38q@%_KQFk<&n{vmrN7y&Cz{etcE->rq!6HL)2F!aa=0%! zM%Bwo!7TQ5t;@a_#Q}sjk{UebWQZ8{cp&HN^$*JfH#8spkhk{R@CVBiPuP@yEhu{} zsQfuhTqV%rioATpEphMfhyRYbVfVW`YwLFXUWm-===J(byMf!5;W^CV1g~2194Xx) zFK|z{pm%n-)-DRe{Qhk(d!QaoI*y%Wn6h7<6A{i*Sob&B^y|Spg!&J$`kN>zwUJ3x zaB$ciu*0FJKg}T ztgnh)ASF8njz5>h6?f#{c=*Yr4W_34$GmVIo8OLWjcZK4a0`+Yv-!*}9 zBwKm;DAsA(nDI-`iH@;`=gP+m{lgFLHK3m$W@?)&dGhDA_Z2xOzI0$p(ZJtH$vCxE zj>+kYNBJzs-TlSx!tSH}%I9fQv)mc!C7X0bKlZv4f&}C3+O-4k7AmVO|KYZ9ydP%(N1^uisV8y;~p`x4qFXD?!_OyN9=w(Od6W; zGrT?G;l2v@Ob5k^8w<9w%Jbjb^|H}PYKo}I~bobd!XrTbzp2Zp~H8lgJ)I3?l&(bDiWf8gE&6b z>)9GB=Iu-6%I((+>=jGP>CzD8c0oWITFZGgM!Q7|JrUYq4#^Y(vuDu-a>OWDa4Y4} z5a_*lW#IL_aVf8L+Ty}c&2VojLEIA-;eQK6Wo?xAuK>i;1VWx3c=!s2;j_*iRHOsb*>6-CgcYP+Ho=L@XLd*j~2ln-;WHg)|cCixksH$K={5rGSD@yB%LI|(NCc8 z1Er8H+QO)~S~K{g?nH|2dB8SKs)BxQ?%G}}o*LV!NG2m*TmR|pWj~g`>)ClJCE#F$ zcj)fBg(dKOKmc$Cy}IRlasngIR>z~kP&WW~9cC951{AKmnZ~ZMsqup6QQf7J0T1;C zK9*Qd5*(HxW=tl|RfjO>nkoW#AU3t>JkuzWxy4-l?xmTv15_r1X@p@dz^{&j&;{Mq z$^0$0q&y?kbdZh)kZ+NfXfqLTG}Q^j>qHlUH4VEK`3y^-z6Y<6O88Hf4v^;}!{t-a zDWg;znYu%6zA1~A5~w?fxO~i8-Ib(^02{c4pXjhDI^2 zXB1LP4dvWuc%PXQ{r!d#6>${rm+M8EJM8yf#!H$Kp8AxwUXm5`7Tu-J$mHeCG>vw|&Ay415}_1w&*9K8+2d3v1N+@a$|820o4u60Tj@u&kI!~q2V9X; z>tMvQDI|O$#m+m2O**ZHq`_{#8)ry6`&5s~2k{O4Du16Fn0P;&_(0!e5%Bel){nU0 zJX~<8U6hoI%yx}qGY_1Tq7YKDJ)ETOCs&W)TiCrK*1%DE*vXdD-7hwE*LUgjeHRM` z&@pkhTi>m#Kc+QIK+2Ybn9-sFVKNHyIgfob4H_77yYh))Rq$7Pw|+aD6&yZ|ki9 z8Zb6s{oBt1G+PgfIcxd}{m@~1nzhe;LH)5;!gS8@ddyabpdBc?7JVl?tS+<#bPSMT z2@0uYdsWN(;Ww)n-PlA-0r+62@bYkEa`k{0s})fJgYZ#5=DmIdEvok7aZJRi{w-|} zkea&6X}ZA3b7&vbDb7)v8CuI(+zzSf3z&P2eOrPNP?D~ zf zn0@)0h;~5F&BG5vOFU!=woW&ZSl~nrs{?1w>nWfW_dnpTd z4qvLDYJ*ft>Sp%M(^_xCZpNBnc66JX}A|ZL9IENM`U>`ph7d<+RQiI}@E8Y)70s zMC*_&))}GlmR}@{v9*nm)29-=rn`Q$rc^4G)GVQHlTr6BpGxtHuU(8AF7Ffh54?5w zj+EYT9>x)PWL-iQ@RNmT?R+|c@=FOmj)5Za6_ z@DkVy4l^L>Z3#SI@s_eVwd3D)<^Ivq8a~J{|4mhOL^<7M4D8){ut;GIqqn`oqCk|x pNh;Wa$C0(mdpqYz&F>xK-uVD=DT5%Jzh8ZT#aXmjr70%*{{Y;&D8m2% literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-icons_a83300_256x240.png b/public/stylesheets/images/ui-icons_a83300_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..95993eab7625fb91819ce8d3d72fd6f71a520eda GIT binary patch literal 4369 zcmd^?`8O2)_s3^p#%>toqJ#RmwV2==ic*rz7lOw=eaq=H~;_ux21)-Jpcgw zdj+hrf&W^f<%Qk9Zpqf#;q2QJEdM*_Xcp}Lw{TRDJ?aub1a^Xj&9?nvwz`aiAM!R@ zjQ=Ya!cfS};)2w*5>Ja*J$tBf&s(}yEXHh;E`&2E$lYY(W^qq~TUiZg+ zDs$A{j0RbbZ;_AuAy^iF69nRHo!r!phm|o%ZrNC>!vOH?d4F#&5iDR!zZRGG3P22m z{BW$s-aRIe1-um2606C}0zUge6Jyb&q_!$VnHLJ;ypLV-J(@1J^5cERBH%1-KR8r? zz^|3l|lWIgN~)(P8x z4^P+SMQo;vZL%lYyF;5Kkc!Vv=1dDro$*Lubd?X23aeG8kq$VdXY0W*pF2xpfWM|2 z&Wp$C*rq@O^ck~8vY^^8k#Vb8m3?u>NXK2^3ZgZlm_xUw@@x0oyTMTe#pn((JK>}o zNE~%TTVT>?`PCmGnK<{OU%FCGX9UEN7o?x$r8S;OC@*GW9sX3Ufb?6VnR?m(X^_8b z?P1+_-A=AINYw!LuEM@I<>ng|N;MAPwa9#%mvI8fISw0?8sAuU|3M!+OX+sW)1QwI zmZ0m`eUNmbJ+QSf+i6q4(i@yiN>&6WzMm~Thv-K>GpSuQ(j@a2u=xdBKglw~^UwuP5!m3Ew+=y8Z}=q0Q51=;bWI>PSK|>&T*6e7TMe=cJ93)z?VY zq_CL5I}e*WU@ETgi^YmU;61P5k7x6FY+{)>4g{B@+@6D$nbX!5@F~#!m0D7y2@>)B z_Qsgmj8g!GA+LZOX)*;lH(S=vHeFD~#AD6OZq9BvLmWBQ`U6rsN{iU;Dvr1WS@zOz zl#6cg6=J4)x0XlNVSpABoKf9r6^O!Mc-%+#>Mcrz!IBHZPk zw7nJrmUK)1q@E9fg^)g`{6kCX`iq%fkXyn_vbzjVWjii96?dD?kr+;lXfO z8EBTl(;?%%K(2gyQLO{-p}#@Xdo~=3t*(y!QlkeNEiqu}rN?Phk25mpDKNCLRRD{v z^Jj)ojVnvz#Nv(M6fOCTDH}niL(Ijx$_4dWOcTqCRe+te_B_yRO&z9i<*jU|wV%p^Jz!mkNCmXmc!Fx1 zoKSv#K91(b+3--CU21F##u~x!HG9OJeB4lA^O^N^&eG3h)J^r|?8?rDX-^}OPBefD zv_mF(#8kiM=J* z0OU$bS0S(r+;un?S($B9g>}_7as=YhDt#jg#L-~ttCUm(D$(={ z59esZ03>5iMXc_vNyk%9Wn9sH}TQur)uY1(#k zI)IFjxK;qsP**3kl&xZEcTaxzJ>;(mFaZj4PjYvOpn9mMQr5K17WhZRqkLV7WS|0? zzy|mUg!@LvVwe4F)Uv*Cp9VET9nK3a3$Tk?OB-zV=!>QHkX+F8NohE!#_TEfyFB(w zu5rz>atMn)uKNM{IHv+&4gcG(EUkl4$ z3#oh#mrxC|yrQ5Gc}v3Q`{BQ1Zq)Pc&f2>DfHyqrI%>V)(r(bURYWdxB+MS$lyk{ z0fBwHb}6?wWlX+`1Ac(J4vb}HG}}ja?=CrU-jq>M#&e+_du`%Tdjc$XZ?>Nc;z=+> zsTinX7KkZMK@-pAuqB-ZH(vhD^&l-iRe5P9ilA_o;7X}x>VAUIMzk{g*9!xwKxMo= z>lB)7Gx(H|e~NDlEE}pbCZY2QCtiDMLAGFtwe{`L;S44Fip5lVJbgrJ^*Qs}0f|@1 zMU%G?E#=o?Snz%(uR|;cRsO~IYQ&7rh<;v-UUp9tILnwYm3f@fwJV&!6l`cu{jLR+ zC)z0}M6=G&#!X&&O?HkqK36$j6cBN6uMzbGPN{3D;w_lh*;nG4{L+aoLjiv$r(o6F zIHzVR9~H15cJ~*1ih7RrtDdJ%&+=quly1_M{MqYI2;oh(>(&l@TD2{(?!)2(JLnY* zjELCIto(GSj(#Dswgr*IG1E)we*Rar_HKb(-S@`6tORfC?;hS#L4DnFJ;xA^Zy+hs z%yACzhx@8LluoiW4+d;)ogc8b>usx>+#Fe(#}~SgR07pXxf!>Y6pAo6buajLRwU7n zpsz0~LWOH-`mOcDsI%rPQHBqk+2vI?_dqi_8o;<5e5c<|C-17H{ck>UbQDD624P~l z&#Ns?+{cf~oj0H#)F+;v+pQ_a;|hl~wpH+Hj`fHYdBJtSoKmTHX78dax+zQRiNN5-jySirbO1$+E%0iX zcFS+0dv3RUqb(Qp@loqrmFi-Jeu5A$QwV8h@Zl>CG`~&WkP;o3!G5FKIDhD3_#gIR zhKdeLx9IOaGba!Aw9bp+hhH9ieY6Pv_M3#CakYm2%1-CHYr2dF{<)9@qOfM1;lVGDYz zQw3XW$@#{}nP6Q_ApasC!FDq6Xu1oC(~T(xYZ-Mf@*9;x{SICslyRS|93js|M=GXc zQpc!R5_N~va#Ic^En1}mO;uz%H3giIm6g4z#iHsp`1(&*1ADYq$k5xGod){~uz@l`$ZL4k)x$e~l5#&qWFSuo?@-uxhR4uu zXB5-QjO5*EIA6+%fdM1Q%6PKR%k^Td9rk-K6Qs=WPkqbUF3Ab`i0x7-vUz!eU%fK` zByg4h)5!BBO|t`jvww1>R5+E?dpJBo@;ueWz5zN`$v38 zJ~pJxK3dvUcDvT8g%`#}RtzoO9!b{lmv4acFYI0sZ{#dH>|)F984#bo>$mjzzN;kz z=#;eEqu^fJ$JDs(Bt$-}vyxxrO03)~b6wqG~>ZG$yH zlYe@oi~ zq~&b`uYcskB0PFRa7F|lD)!&?-%H&I^azohix%7uPy69RQ=D6u!q`-Wr3pk;e%s)p zGQBB44&mI@6%(h+WCK-7bPgcSQ9%X1#|gnkJK~S8h+k;5r;T58nJujpED?$g4qc4UJTx{+VfkJbf`Ahyy@DbTXB z!bwPDbL%Hkl(xj|C41W`$-+s!?v<_GuxJ&0&d@(9)=7MS7yE2L*sfB!dXn56!0~UG z`R%{qo#9z8(QJ)k=gT+Vw*}A1?Q8(0_kk}Xz1=$(9erMmOs z$63%UU-4|PstuCKuQ~Hk&`xn_o$yNl2Q$cb-)ed*niaB@K^cOwoYoy@XQEO<>WicarmhKc`c$I#O!tqr)GFoD+9l*|67t^VQ>oqwb&K&M75$?Bfv+Cd znc5e@%Qym$u1m@RezFiVGck-Mk8{|cN1J}_-y-FYnR}drHGVM8KGEJQy7FKKf;9}Z>*?d*QM$ngCHZfyI|A4LN`2y4L< zdJFD_gc}m|w*;TB_(%`javz_~-_V*Kv@+``{$_yRzf3I&R*8l(j literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-icons_cccccc_256x240.png b/public/stylesheets/images/ui-icons_cccccc_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..9254e05cfb964be56241b5bb4d0a0eb51c02df80 GIT binary patch literal 4369 zcmd^?`8O2)_s3^phOrG}UnfiUEn8(9QW1?MNkxXVDEpFin2{xWrLx5kBC;k~Gmw z<@?HsG!Qg3zaV+-xQ3ldtad!U<6iGz_enGH*2akP_r)o1D&8p^5M)_c8IIj6Wy*7HJo&CBLuo~nj>(63pZzO(Vv^ZuB3 zMYigjkwA;FEy|G}1jpiMj6|NTm7Uyiw=@FDE*nX<>jR!W@9XIyf%$Fd*J5*D0Z0Lm z9}ZQxyT|x5ftNy?V>EbJz-K>bV9gs9RaXUP<^=;e?&Fqxj;6{ieR-a-@HycA1KMKhql8GOmcxwZ?_-(3hMK^^a*(gaFvBH ziIC!fgH4$W*NbKIaY&T?%&13``KbD@S-0`xQ%v3TV+B!;RC7O!+1a9QCA$H@3tR;k z)SSoR7(s4)f{zM}eWgFN{(ZH5d1O}l)f$ruT!)Q&NImXyZsTzOf9TwctcSfr+M)aJ z5otO+$jvm-P4)ykH)x|cO5xeb>?!`qGw$(>&axqLL6yoB${vsMXgL_-bz@2J_tS92 zdvZG-+vKl@K4Vr(EL{WQt@Z+Ea-hxX0}nTSZxnpi^#Kn8Ox8FgIS|hc}KJQ4tm*HO16ui{(O9} z1YN)GjiQt6fGq`Cj+^`zUf?8hk^(T{{cOQGWFP98am}is28A!5%{R#ENv8fCN!j69 zlMEK(2z?|BY=Je$XD9mB-Kkem*(d-j^9j$2#6r$Dz?s)-TCDCGCs z8>6Pvj{Y+YIeFA@qY22V$)awy@q!9A4rgk5b9TcC;s9Ig^G|6nDP+5=Fzg&?(L=vc zCbGd>fSu~@6!94td+o#d@sid!EIX$rx7*cawe6 z`dScJ+$HssdOjE)O#Ybs56vm-FQ$7yuJJD^Zqk%hMaIgAJ<2yb_MFQte_i;62ScT$ zpjifYyR_E=rQ+>H)pmlr-Udzg*-!|ssw(D7wJvC+Sf8bb9;;q8#z?0p!!bsd{wy|5 zpBaMHE-Ve>i#LLjHRaMLtp%9&(HCng7Sw96jVv!#0k%?F^K7&=T)mnYn)D9(i;4x5 z^NJTJwq~pv;kH@#ejTd*48~(J(r6j34|m`h9fEDj0im)~+%I5XphWymhT;_Zty|Q& zzjPg#-ufAHZ1M*Gccw?Kf|8Pnhtb0`!{N`Bqsa37J+>wC$!e z00k+2Egzz;rbcWoUB%Jvp8W1}$XD%e3>4y;;OZ1ccT-O#uW6Ys@C}Pa`nZrNKzR(2 z4e%3)@QI4SE&E!lW`5y14QhbepBG%_XBV-O(%5tj)@9#|;sC-MNev!zGDHk}JdpGC`iJF#8=8-P$Xoku_=Dw%Cv3{U7L>gf zRQ?<$t`cZ*MP5GQmbmx#!+*!zu>0MewRO9GFGS{b^m_fJ-N0?j@EqoFf>$khj+E|@ z7r3We&^tR^YZrxKe*d22agXqCO0l44&kqCv{u)T|(lv`~PK@DvE z{QI_TlCH5z*gR!>LO)k67{^R+vWx24U2^2ODXpwT;6y+6+$5m)_*w4WY&#do9dCeE z)>p+Ykdhq($DhmMiaYXey!@N%L26uz($aJ!QT{B^Wu}U$^9e#5)=c+XF9@Ill?ZmM zlNgHiz*9!vDc&uxOo;ZVxb`Q!Sk0*gnfxWzmbZh4(=%CD%qP?0=);n$&zaW_$UKV9 z8axdcN#AyZ{P)wj?V{P}vM)YY!>6@}^>U+iv$`9>nMTCPjN>z%yF&3yf%>+T@0vh4 zlC8Xa6zeo?%=o3}M8{aebLHcO{^1Ar8qiM=Gquf?Jo)q5`-+?sUpg?QXyEUpWSm+n z$K-UyqkIwHLquru~o(OF)hhz$Y*|X>ZIbswnxRvr~ z2=rdOGVuD|xRlpAZE<0!X1F(%Anpl^@V^D3vbM}qxe|NI;TTiZy7(IM;R69RkA>a& z6gwYE2sREzQ_LHmWqB+ogMk(fMaSFeoDq-!HkFB_nXt5+2ncFuk9BQL1I&oB1zZi) zYW{6_&-Ip1l*OVRA##1ILQS;5R{-K^0wGTiJbVSi@LA^$D$;@J>^G{6@&+%4{b3(s zC~LEHiTv(0b#zxt?YJ0r_~pUZM~mQ(??(n#>&tD%+@nq=Abj5*8R!~Ul1`G~=qFJ4 zfl|m8ZDCYgtr`4LcOpgiJYX9qRY5;DcWti~PmS$VB$E-Zt^f4)vLDOe_3XTq5^ylW zJ9PKm!V-8sAOJXnUfuFNIf0R9tK-pNs2hO04zr620}5B(Ok>yB)Of-3sP59qfQNbm zA4{w!2@cB;GbR(~szVrbO%(w=5S!X`o@o@x++wbN_tMPT0Vc)*I;Fgsbf^*g0 z2Di?HTApwKq3+YwfNsqd3iP%{hyK1iyuVZc@*0tO_3+N0#GFsz>8MjeJ2UJ%L!%hi zGYYAthH`E+ywA*u{(eJ=ia3h*%k?779rk-K<0VZAPkl;TFUbmei|$fqWO8!_zIvqt z$ly$VrlH46nnpX~X5Yk0iBJl;=WuA4>~X4-f&K0yWf42h&0b30t@NYX$7egQ1Fp!a zbui-D6cWCWV&|R1CY@G8(qOmWjWeX3eX7UggZPGimA}soOuQdXe4uZ#2>5zN>qlI0 z9xk}lE=tNpX1m6*nFr2EQ3xs79!^sCldDJYE$m(qYv3q7>}1R7?iZW7>$~*%zKaC| z=$N?ME$>#+%T&MZC`dW1wUl6Z)JgyCn~V%K&i0H|iwE%$>xsZW3tTfZxIUePci@p;cRu|d=ItIwF z1clVHy{hH?@SD|(Zfqi^0DQ1hczHN7xq85h)rzQqLHMX2^IkuK7FB!kI40s$|CY7~ zNX^{_UjN8}L%Med;|+=4RNTMozn8KT;2tb77bUPCmioh+rZBfIiM6f_P34cQ__o1G zWqQp3VL~~pE5?qODf%iiQQ3f42YF@09tQ*$4v_EKUx;t1KCPCBtgqg z@+Tn;O)a0uky_%jm+WjNB?=~VyH>V#L!*=l*@OS6SVyt_UEH&NA=?V2stHPyKkVNy z&jg<#cjros){#ji)dK z%)We0L_478=HZ8-@xnwsKrWs8)x`MB;(Y`Cmu2c-&SH(vN-F(*e`l?c%+l$|y_AJJ zhcDGnwLvN+bu;_sX|1AiePhx@u&%P$hf*xE+O=~D?_(_KGWQ!158YL-y9$*6mmPo;Rp*Dl5lm-mVM2i`h- zM@nxv590_tvMwPD_{l=b$iOm|+|S{D9&P%zeT$GgX6Akl-tfUF>tL@Ld!B&{pN39t zH>3Vhqkr}2Yul+jb7UiouWVGPNsxX7Ueba+9|~dz?d*QM$ng0DZfO0`7fAy?2yMm| zcnRzUhZ&IcwgjH9cuU!w+VStYa{p*)4IgBf|E8)sqMYtB2KH_}SfsFq(c9i(Q6S3U oBo%DI*Kv;w;*%(i9W@e`=?odH?_b literal 0 HcmV?d00001 diff --git a/public/stylesheets/images/ui-icons_ffffff_256x240.png b/public/stylesheets/images/ui-icons_ffffff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..42f8f992c727ddaa617da224a522e463df690387 GIT binary patch literal 4369 zcmd^?`8O2)_s3^p#%>toqJ#RmwV2==ic*rz7lOw=eaq=H~;_ux21)-Jpcgw zdj+hrf&W^f<%Qk9Zpqf#;q3n5{{POY;f!wmTR1An9(4&I0z1LNX50QSTV2M%4|y9c z#{ZQIVJKu~aY5?ZaZP*GIGqGs=e@q6o|EPhZB3CC?@LnORK8O@z{{<0KtSn5?#~OW zy=L;x8T&*%xqElS;s5~Pjk7d2bqIaA)xZbovnZd7eX17WNxx=w`p(8vulwUZ zl{so}MuRNJx5!8S5G;$o2?BApPHt+)!^#*Ww`?rcVE}mcyuY`X2o|uVUyI9o1t11O zemGWR?;aD#0$vJhiPhv~0iXS#iLq!>Qd$` zU{}<|Vb9Md>$4TMbL7C3GP#r;4Wc$}Z;^j;n}yc!E3d;`wry$!JkmJP0%(tIh!!TET8=+{rhUi^60G0t2HJSxXv-*DgC(HrJd8`|Dp3NvL5yg>xAvU zho|fEA~w^-HrW&H-JwkqNX2I-bEXBR&Uhp+y2^)1h1IIlNCzC!v-Mz@&z&VPz+cl1 z=f&f6Y*U~C`ixm4Sy1hl$hg(4%Dy;bq~k7d1<@K&%%NLT`L+A)-QXyKVswX?op90( zB#yeFEih@c{OXU8Oq~1CFI_38GXmns3(`;W(i+bslovCx4u7gvK>DrGOug*?G|1nz z_OR}|ZYS3pq-p?rS7G0qa`TM}r5XqDT4cV>%Qyk#9ES}`jc+Ww|DcbZrF6UG>CeXp zOVIV}K1e#z9@tu#?X)Ri=?zXMB`X3G-_I7FL-Zq`nbfWtX_EO1*!+U6pJW-_k&+vk zMd}THh}{(Ch_wPk(PI4vVB_KT76kGxVytLxpWg}&bHw`a3G#QzxV@ICNax&@hk3<_ zBh`Tq66G{-tCw$V{(y0v7l!tp20~@gdFXjzFbF#bJE7i>T4ux zQdrF3org^wFcnw$#bQMv@SfN3$Fuo7HnB_`2ZGB{ZqGr>%xP;2_!Q{=N-ZhU1c~^5 zdt=OO#wmcpkXJyCG?{{&n=R{Sn=Ytg;<09CH)l7TA&wkt{Q;>RrA2Ia6-QixEPLrU z%0)N$3Nh0?U825&v($Sz}0G_(!v&xSSAzje4{rup+^W@^}ByqOb95$E0sbwK*%#GP}!6`%*Z@L;&C z3^dE&>5%bWAXmP*X1 z_m}Pivs*u7@9i>qA!58fDCwj^M<1P(u^m;urVdlM@>aIf+E3-d9ZW>fc4cS7w5O3sCmKKn z+94A?VyfSBb9{}rEbCIYtXORJBCv__fnZ>?a}edaA%bP$jI?J^q0UKO!mduA8U!3b z0CJ_Js}NWQZoebapVUHP%pPOUm?1<)zd%`hzUM-Y6g1z|@@3G_kio?S0bcbjQuxJd>vU$Uyz(4*peEDSVc-G;O;% z9Y97%Tq}TRsH+oN%2u(oyC=W<9`e@&m;i;jC%L;sP(9RBDQnth3;ZMEQNFH3GEf0c zU<3RF!hNG-vCDooYFS^nPlFnv4(ElI1=vNcr42TF^uq67f{MoN>{f&>xA91r4pz5Zc&@P^i-9||`98v$Si!U@}ouZ88W zg;YL=OQ;4}UQtkpyd~lD{qWy0H|lwJXKmenz#E=*9kt$YX*X!wDk7ITlIUGWnj>a7 z<_GQR752@J)Y(U)ncu(dIit7P}oBq8x$FP85)&Nsw<#rOW z8U_x(1J)Zgm(8tZXU%+(yYcO+Z7#ZszPwa2`ygiMPayX9KondtFMRK!7x`9uWN;(f zfWW?8yOdj;GA3We0YAW92gWipn(d>zcbA+vZ_21BxF?-pfcW` zbqY??6ie(6M)p@6@WQ?Tl7 zoKrKEj|x~2yZehhMLkFRRnOC>XL&L+N;m0B{_OQ9gzzTYb!!Jct=bk?_hIpY9rOwY zMnr69R(?8EN52qR+k!~qnCYc-KmV&*d$&NY?t5cjR)V+ncMor=puTRoo?{5dH;@!* z<~RrV!+ljAN+;Qx2LraY&JWnz^|sYbZjP+Y;|pC#DuHUH+>F~x3PqTkx)=OAE0X9( z(AO6gp~AH^{nq+n)LHYDD8mQN?DDFcd!U&d4PaajzSD1~lXq3p{x=^vItrq3gD^4O z=hYS`?&C-0&KuAV>Jv}T?ba0IafL$~+bZ}p$9lwyyx=-uPN`Hpvv<)Ia>OWHa4+N4 z6zscrW$^XA32EJw^7hYtkRJr{Q8 zQ|*1pp_q6Mno|D6EX!kgSv0h0I3~ef_l%$DTFjL`0y16n%^dGNQn;2V82mqoIi9i{15vu zLq&(BTl9CInUjZlTIa>^!!HlMK3W8Sd_Ow0+E8IT?h$=55$^Z)$WYIuig=O;Lp_1Q z4wOT;XbWQ!>Mh`pdXuSo=KBba;wT!wK`Hf1Ueh04*%D7Kfj*#b~BNfvz zsbf?uiMm5-xhaQ|7Om2OrYbU>ngUM9%F5nU<65IFyu(`yZ;Vb1)=wCd!L2K?c$ezE z4IbS|^?Z>)eEp}ZfjwF)Waw?pPJ?{~*g%;efxO~Nx7dQGLWZ)cPQ*T!((W- zGm2?tM)K}7oG<0Xz<`ltWjxvE<$AH!4*R{A2~uYGr@m!vm*j+e#CE9^*}Oc#uihB| z5;#kMY2^8mrr80%*+02bDx6B{Jsch(d7kQGV7~iGTgFZBu$Pf`tNf`B2{|t7fGhIq zos0xF#l$bfxOtcGDd*MDbdKBaCKxgCEbr8JTNd_1bjWC{Ubgk z9~)9;A1&=FyIt$l!VBXfD~6VCk0fjO%QwLJ7k00RH*%I8cCqF542VzP^;`OU-_?=< zbV}OoQE)HqV`|)X5+WbgSxGWH>t+7-O;(l~Z+FJJ)sygu^+eF01#Suj+pnAcw!s>p z$-xF}c>7t9X6H$^V9hvT5H{jKv+=zzWHA0pgw8e5fZpm9vIphVq3%S4*N3%&jsY^Q zK%sSPuj=?d{ATs0o0y6#0w3%YT^@-_sTuTUwI(Q{;l3KjeAbVk#Wmi%PDxm`zoqQ~ z((<-}*FSP%5gt7uI3t1&75ne{@1^bpdW1;MMGNkSr~UAuDbB4+VQi|x(gdO^zin_) zncfs2hj8xdiiy)@vVkfkItLKvsGtJhrTb0T~tFl4Q3J!flauS==b& z6Bm!g%dDvlCf(St$kVofvH90|9yl-gmvRvcKS&Ye9DdoTK@2m}iSvC{3m%4E0 z@TJD7c1V?!URM7+t?f3)%{X(6JXg~A9TvGQyX6n(^Yt0NX;>vDPcr~mICPooLWA_` z<1A>FuXr|C)dtDr*PQt%Xs5WePWUB&gBj$zZ#BIY%?jDdpbSA-PV0`dGf^oa_Jp}Z zlrGV7oe`#B^+nPIQ`ZDJeJas=ru#=*YL#+n?Go}f33>1GsZ{TTy2bdBihj}mz*mp! zOzn%{WgLM=*CpiuKUs*GnHa{B$2siJqfNi|Z;|rH%stM*8b26kAMCYY&NHwPGtlYn z7UVx_^sgR$Z8x27foS63FCPt|gtcG_ zy#@C|!VQV~TY}G5e57qp?F4jRxqq~@h6^?-cvD>ySwVLl2m7=gERtEn>Fw_@ND%pO oiVC*mbz<%I+0K1Z`+LWvZ$3~$+A!Gm?^hpSc@||}WrmLVKLvuzv;Y7A literal 0 HcmV?d00001 diff --git a/public/stylesheets/jquery-ui-1.8.23.custom.css b/public/stylesheets/jquery-ui-1.8.23.custom.css new file mode 100644 index 0000000..144e828 --- /dev/null +++ b/public/stylesheets/jquery-ui-1.8.23.custom.css @@ -0,0 +1,352 @@ +/*! + * jQuery UI CSS Framework 1.8.23 + * + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; } +.ui-helper-clearfix:after { clear: both; } +.ui-helper-clearfix { zoom: 1; } +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/*! + * jQuery UI CSS Framework 1.8.23 + * + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Segoe%20UI,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=333333&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=25&borderColorHeader=333333&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=000000&bgTextureContent=05_inset_soft.png&bgImgOpacityContent=25&borderColorContent=666666&fcContent=ffffff&iconColorContent=cccccc&bgColorDefault=555555&bgTextureDefault=02_glass.png&bgImgOpacityDefault=20&borderColorDefault=666666&fcDefault=eeeeee&iconColorDefault=cccccc&bgColorHover=0078a3&bgTextureHover=02_glass.png&bgImgOpacityHover=40&borderColorHover=59b4d4&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=f58400&bgTextureActive=05_inset_soft.png&bgImgOpacityActive=30&borderColorActive=ffaf0f&fcActive=ffffff&iconColorActive=222222&bgColorHighlight=eeeeee&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=80&borderColorHighlight=cccccc&fcHighlight=2e7db2&iconColorHighlight=4b8e0b&bgColorError=ffc73d&bgTextureError=02_glass.png&bgImgOpacityError=40&borderColorError=ffb73d&fcError=111111&iconColorError=a83300&bgColorOverlay=5c5c5c&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=50&opacityOverlay=80&bgColorShadow=cccccc&bgTextureShadow=01_flat.png&bgImgOpacityShadow=30&opacityShadow=60&thicknessShadow=7px&offsetTopShadow=-7px&offsetLeftShadow=-7px&cornerRadiusShadow=8px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Segoe UI, Arial, sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Segoe UI, Arial, sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #666666; background: #000000 url(images/ui-bg_inset-soft_25_000000_1x100.png) 50% bottom repeat-x; color: #ffffff; } +.ui-widget-content a { color: #ffffff; } +.ui-widget-header { border: 1px solid #333333; background: #333333 url(images/ui-bg_gloss-wave_25_333333_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } +.ui-widget-header a { color: #ffffff; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #666666; background: #555555 url(images/ui-bg_glass_20_555555_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eeeeee; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #eeeeee; 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 { border: 1px solid #59b4d4; background: #0078a3 url(images/ui-bg_glass_40_0078a3_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #ffffff; } +.ui-state-hover a, .ui-state-hover a:hover { color: #ffffff; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #ffaf0f; background: #f58400 url(images/ui-bg_inset-soft_30_f58400_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #ffffff; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #ffffff; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #cccccc; background: #eeeeee url(images/ui-bg_highlight-soft_80_eeeeee_1x100.png) 50% top repeat-x; color: #2e7db2; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #2e7db2; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #ffb73d; background: #ffc73d url(images/ui-bg_glass_40_ffc73d_1x400.png) 50% 50% repeat-x; color: #111111; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #111111; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #111111; } +.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; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_cccccc_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_cccccc_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_cccccc_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_4b8e0b_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_a83300_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-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: -64px -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: -64px -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: 0 -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-off { background-position: -96px -144px; } +.ui-icon-radio-on { 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 is deprecated, use ui-icon-seek-start instead */ +.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; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; -khtml-border-top-left-radius: 6px; border-top-left-radius: 6px; } +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; -khtml-border-top-right-radius: 6px; border-top-right-radius: 6px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; -khtml-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; -khtml-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; } + +/* Overlays */ +.ui-widget-overlay { background: #5c5c5c url(images/ui-bg_flat_50_5c5c5c_40x100.png) 50% 50% repeat-x; opacity: .80;filter:Alpha(Opacity=80); } +.ui-widget-shadow { margin: -7px 0 0 -7px; padding: 7px; background: #cccccc url(images/ui-bg_flat_30_cccccc_40x100.png) 50% 50% repeat-x; opacity: .60;filter:Alpha(Opacity=60); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*! + * jQuery UI Datepicker 1.8.23 + * + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +} \ No newline at end of file diff --git a/public/stylesheets/normalize.css b/public/stylesheets/normalize.css new file mode 100644 index 0000000..57b5d26 --- /dev/null +++ b/public/stylesheets/normalize.css @@ -0,0 +1,375 @@ +/*! normalize.css v2.0.1 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/* + * Corrects `block` display not defined in IE 8/9. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section, +summary { + display: block; +} + +/* + * Corrects `inline-block` display not defined in IE 8/9. + */ + +audio, +canvas, +video { + display: inline-block; +} + +/* + * Prevents modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/* + * Addresses styling for `hidden` attribute not present in IE 8/9. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/* + * 1. Sets default font family to sans-serif. + * 2. Prevents iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -ms-text-size-adjust: 100%; /* 2 */ +} + +/* + * Removes default margin. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/* + * Addresses `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/* + * Improves readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/* + * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, + * Safari 5, and Chrome. + */ + +h1 { + font-size: 2em; +} + +/* + * Addresses styling not present in IE 8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/* + * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/* + * Addresses styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/* + * Addresses styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + + +/* + * Corrects font family set oddly in Safari 5 and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +/* + * Improves readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* + * Sets consistent quote types. + */ + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +/* + * Addresses inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/* + * Prevents `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/* + * Removes border when inside `a` element in IE 8/9. + */ + +img { + border: 0; +} + +/* + * Corrects overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/* + * Addresses margin not present in IE 8/9 and Safari 5. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/* + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/* + * 1. Corrects color not being inherited in IE 8/9. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/* + * 1. Corrects font family not being inherited in all browsers. + * 2. Corrects font size not being inherited in all browsers. + * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome + */ + +button, +input, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 2 */ + margin: 0; /* 3 */ +} + +/* + * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/* + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Corrects inability to style clickable `input` types in iOS. + * 3. Improves usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/* + * Re-set default cursor for disabled elements. + */ + +button[disabled], +input[disabled] { + cursor: default; +} + +/* + * 1. Addresses box sizing set to `content-box` in IE 8/9. + * 2. Removes excess padding in IE 8/9. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/* + * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/* + * Removes inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* + * Removes inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/* + * 1. Removes default vertical scrollbar in IE 8/9. + * 2. Improves readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/* + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..a9d382f --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,334 @@ +html { + background: #111314; + height: 100%; +} +body { + padding-top: 10px; + height: 0; + min-height: 0; +} +/* Colors */ +.green { color: #77AB13; } +.red { color: #AE432E; } +.orange { color: #B5712E; } + +#dashboard-container { + width: 960px; + margin:0 auto; +} +.widget { + background: #232526 url(/public/images/widget_grad.png) no-repeat 100% 0; + box-shadow: 2px 0 3px #0d0d0d, -2px 0 3px #0d0d0d, 0 3px 3px #0d0d0d, 0 -2px 3px #0d0d0d; + -moz-box-shadow: 2px 0 3px #0d0d0d, -2px 0 3px #0d0d0d, 0 3px 3px #0d0d0d, 0 -2px 3px #0d0d0d; + -webkit-box-shadow: 2px 0 3px #0d0d0d, -2px 0 3px #0d0d0d, 0 3px 3px #0d0d0d, 0 -2px 3px #0d0d0d; + font: 18px Arial; + color: #707275; + text-align: center; +} +.cabecera { + padding: 10px; + height: 70px; + margin-bottom: 2%; +} +.cabecera img { + display: block; + margin: 0 auto; + height: 50%; + margin-bottom: 18px +} +.cabecera h3 { + margin: 5px 0; +} +.widget > header { + border-bottom:1px solid #151617; + border-top:1px solid #323434; + height:17px; + overflow:hidden; + padding:10px 15px 11px; + line-height:100%; +} +.widget > header > section { + float: left; + color: #707275; + font-size: 18px; + text-transform: uppercase; + text-shadow: 0 -1px 1px black; +} +.widget > section { + border-top: 1px solid #303334; + border-bottom: 1px solid #151617; + padding: 0 15px; + color: #707275; + font-size: 12px; +} +.widget-body { + overflow: hidden; +} +.sla .widget-body { + min-height: 215px; +} +.widget-inner { + padding-top: 20px; +} +.stats-hour { + width: 100%; +} +.stats-hour th { + text-align: center; +} + +.tabla { + float: left; + margin-right: 2%; + margin-bottom: 2%; +} +.stats { + width: 30%; +} +.tabla td { + text-align: center; +} +.slas-kpis { + width: 68%; + margin-right: 0; +} +.slas-kpis table { + font-size: 1.2em; +} +.slas-kpis td, .slas-kpis th { + line-height: 44px; +} +.sla { + width: 40%; + float: left; + margin-bottom: 2%; +} +.graph { + width: 100%; + float: left; + margin-bottom: 20px; +} +.graph .widget-body { + padding-bottom: 15px; +} +.graph .widget-inner { + height: 300px; +} + +table { + width: 100%; + margin-bottom: 18px; + color:#CCC; +} +table caption { + color:#FFF; +} +table th, +table td { + padding: 8px; + line-height: 18px; + text-align: left; + vertical-align: top; + border-top: 1px solid #232323; +} +table th { + font-weight: bold; +} +table thead th { + background:#111; + color:#E5E5E5; + vertical-align: bottom; +} +table colgroup + thead tr:first-child th, +table colgroup + thead tr:first-child td, +table thead:first-child tr:first-child th, +table thead:first-child tr:first-child td { + border-top: 0; +} +table tbody + tbody { + border-top: 2px solid #232323; +} +table.table-condensed th, +table.table-condensed td { + padding: 4px 5px; +} +table.table-bordered { + border: 1px solid #232323; + border-left: 0; + border-collapse: separate; + *border-collapse: collapsed; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +table.table-bordered th, +table.table-bordered td { + border-left: 1px solid #232323; +} +table.table-bordered thead:first-child tr:first-child th, +table.table-bordered tbody:first-child tr:first-child th, +table.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} +table.table-bordered thead:first-child tr:first-child th:first-child, +table.table-bordered tbody:first-child tr:first-child td:first-child { + -webkit-border-radius: 4px 0 0 0; + -moz-border-radius: 4px 0 0 0; + border-radius: 4px 0 0 0; +} +table.table-bordered thead:first-child tr:first-child th:last-child, +table.table-bordered tbody:first-child tr:first-child td:last-child { + -webkit-border-radius: 0 4px 0 0; + -moz-border-radius: 0 4px 0 0; + border-radius: 0 4px 0 0; +} +table.table-bordered thead:last-child tr:last-child th:first-child, +table.table-bordered tbody:last-child tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 4px; + -moz-border-radius: 0 0 0 4px; + border-radius: 0 0 0 4px; +} +table.table-bordered thead:last-child tr:last-child th:last-child, +table.table-bordered tbody:last-child tr:last-child td:last-child { + -webkit-border-radius: 0 0 4px 0; + -moz-border-radius: 0 0 4px 0; + border-radius: 0 0 4px 0; +} +table.table-striped tbody tr:nth-child(odd) td, +table.table-striped tbody tr:nth-child(odd) th { + background-color: #393939; +} +table tbody tr td, +table tbody tr th, +table tbody tr:hover td, +table tbody tr:hover th { + background-color: #333; +} +.sla-row { + overflow: hidden; + padding: 15px; +} +.number { + font-size: 40px; +} +.sla .texto { + float: right; + font-size: 25px; + text-transform: uppercase; + padding: 15px 20px 0 0; +} + +/**** TAB FOR SELECTING DATES ****/ +#tab { + width:40px; + height:50px; + position:fixed; + right:0; + top:25px; + display:block; + cursor:pointer; + background-color:#232526; + border-radius: 10px 0 0 10px; + padding: 5px 0 5px 5px; +} +#inner_tab { + border-radius: 5px 0 0 5px; + width: 100%; + height: 100%; + background: #CCC url('/public/images/arrow_posts_left.png') no-repeat center center; +} +#inner_tab:hover { + background-position: 5px 13px; +} +.expanded { + background: #CCC url('/public/images/arrow_posts_right.png') no-repeat center center !important; +} +.expanded:hover { + background-position: 11px 13px !important; +} +#panel { + position:fixed; + right:0; + top:25px; + height:250px; + width:0; + font: 18px Arial; + color: #707275; + text-align: center; + border-radius: 0 0 0 15px; +} +#panel h3 { + margin: 0; + margin-bottom: 15px; + text-transform: uppercase; +} + +#panel .content { + width:320px; + margin: 5px auto; +} + +#panel p{margin:.5em 20px;} +#panel label{display:block;} +#panel input, #panel textarea{ + width:272px; + border:1px solid #111; + background:#282828; + padding:5px 3px; + color:#fff; +} +#panel textarea{ + height:125px; + overflow:auto; +} +#panel p.submit{ + text-align:right; +} +#panel button{ + position: relative; + cursor: pointer; + font: bold 12px/normal 'Segoe UI', Arial, sans-serif; + color: #333; + text-decoration: none; + text-shadow: 1px 1px rgba(255,255,255,0.5); + border: 1px solid rgba(0,0,0,.1); + padding: 5px 10px 6px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + background-color: #91BD09 + background-image: -moz-linear-gradient(rgba(255,255,255,0.2), rgba(0,0,0,0.2)); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255,255,255,0.2)), color-stop(100%, rgba(0,0,0,0.2))); + background-image: -webkit-linear-gradient(rgba(255,255,255,0.2), rgba(0,0,0,0.2)); + background-image: -o-linear-gradient(rgba(255,255,255,0.2), rgba(0,0,0,0.2)); + background-image: linear-gradient(rgba(255,255,255,0.2), rgba(0,0,0,0.2)); + -webkit-box-shadow: inset 0px 1px rgba(255,255,255,0.6), 0px 0px 3px 0px rgba(0,0,0,.2); + -moz-box-shadow: inset 0px 1px rgba(255,255,255,0.6), 0px 0px 3px 0px rgba(0,0,0,.2); + box-shadow: inset 0px 1px rgba(255,255,255,0.6), 0px 0px 4px 0px rgba(0,0,0,.2); + -webkit-user-select: none; + -moz-user-select: none; + margin-right: 10px; +} + +#panel button::-moz-focus-inner { + margin: 0; + padding: 0; + border: 0; +} + +#panel button:hover, #panel button:active { + text-decoration: none; + background-color: #A0CF0C +} + +#panel button:active { + top: 1px; + margin-bottom: 1px; + border-bottom-width: 1px; + background-color: #F9F9F9; + background-image: -moz-linear-gradient(rgba(0,0,0,0.2), rgba(255,255,255,0.2)); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(0,0,0,0.2)), color-stop(100%, rgba(255,255,255,0.2))); + background-image: -webkit-linear-gradient(rgba(0,0,0,0.2), rgba(255,255,255,0.2)); + background-image: -o-linear-gradient(rgba(0,0,0,0.2), rgba(255,255,255,0.2)); + background-image: linear-gradient(rgba(0,0,0,0.2), rgba(255,255,255,0.2)); +} \ No newline at end of file diff --git a/public/stylesheets/tipTip.css b/public/stylesheets/tipTip.css new file mode 100644 index 0000000..4fb95d3 --- /dev/null +++ b/public/stylesheets/tipTip.css @@ -0,0 +1,113 @@ +/* TipTip CSS - Version 1.2 */ + +#tiptip_holder { + display: none; + position: absolute; + top: 0; + left: 0; + z-index: 99999; +} + +#tiptip_holder.tip_top { + padding-bottom: 5px; +} + +#tiptip_holder.tip_bottom { + padding-top: 5px; +} + +#tiptip_holder.tip_right { + padding-left: 5px; +} + +#tiptip_holder.tip_left { + padding-right: 5px; +} + +#tiptip_content { + font-size: 11px; + color: #fff; + text-shadow: 0 0 2px #000; + padding: 4px 8px; + border: 1px solid rgba(255,255,255,0.25); + background-color: rgb(25,25,25); + background-color: rgba(25,25,25,0.92); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(#000)); + border-radius: 3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + box-shadow: 0 0 3px #555; + -webkit-box-shadow: 0 0 3px #555; + -moz-box-shadow: 0 0 3px #555; +} + +#tiptip_arrow, #tiptip_arrow_inner { + position: absolute; + border-color: transparent; + border-style: solid; + border-width: 6px; + height: 0; + width: 0; +} + +#tiptip_holder.tip_top #tiptip_arrow { + border-top-color: #fff; + border-top-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_bottom #tiptip_arrow { + border-bottom-color: #fff; + border-bottom-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_right #tiptip_arrow { + border-right-color: #fff; + border-right-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_left #tiptip_arrow { + border-left-color: #fff; + border-left-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_top #tiptip_arrow_inner { + margin-top: -7px; + margin-left: -6px; + border-top-color: rgb(25,25,25); + border-top-color: rgba(25,25,25,0.92); +} + +#tiptip_holder.tip_bottom #tiptip_arrow_inner { + margin-top: -5px; + margin-left: -6px; + border-bottom-color: rgb(25,25,25); + border-bottom-color: rgba(25,25,25,0.92); +} + +#tiptip_holder.tip_right #tiptip_arrow_inner { + margin-top: -6px; + margin-left: -5px; + border-right-color: rgb(25,25,25); + border-right-color: rgba(25,25,25,0.92); +} + +#tiptip_holder.tip_left #tiptip_arrow_inner { + margin-top: -6px; + margin-left: -7px; + border-left-color: rgb(25,25,25); + border-left-color: rgba(25,25,25,0.92); +} + +/* Webkit Hacks */ +@media screen and (-webkit-min-device-pixel-ratio:0) { + #tiptip_content { + padding: 4px 8px 5px 8px; + background-color: rgba(45,45,45,0.88); + } + #tiptip_holder.tip_bottom #tiptip_arrow_inner { + border-bottom-color: rgba(45,45,45,0.88); + } + #tiptip_holder.tip_top #tiptip_arrow_inner { + border-top-color: rgba(20,20,20,0.92); + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..c76bf2f --- /dev/null +++ b/readme.md @@ -0,0 +1,16 @@ +JavaScript Operator Panel [JOP] +================================ + +As opposed to FOP [http://www.asternic.org / http://www.fop2.com/], JOP is meant to be a HTML/JS solution to see the +current Asterisk status in a Pannel. + +It's built on top of node.js, socket.io and express.js + +It has even live pannels per-client to show stats of calls in the current day or to select from another day. + +*Note*: As our Asterisk build and dialplan are heavily customized, we are using curls from dialplan and routes through +express to perform actions but you could connect to the AMI and be able to capture events. +If you are interested, you could use some other Node - Asterisk modules + +I would love to work with some Asterisk dev/admin to bring this as something you could deploy within your Asterisk basic +installation. \ No newline at end of file diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..fa7e1fd --- /dev/null +++ b/routes/index.js @@ -0,0 +1,522 @@ +module.exports = function Routes (app, database, io){ + var self = this, + arrayHelper = require('../helpers/array.js'), + timeHelper = require('../helpers/time.js'); + + this.index = function(req, res){ + var ip = req.ip; + + if (req.cookies.isadmin !== undefined){ + // Revalidate cookie + res.cookie('isadmin', true, {maxAge:90000}); + app.storeConnectedClient(ip,true); + res.sendfile('./index.html'); + } + else { + app.isAdminUser(ip, true, function(is_admin){ + if (is_admin){ + res.cookie('isadmin', true, {maxAge:90000}); + } + app.storeConnectedClient(ip,is_admin); + res.sendfile('./index.html'); + }); + } + }; + + /** + * Function that will be called when an agent transitions from unavailable to available + */ + this.logAgent = function (req, res){ + var agent_position = arrayHelper.arrayObjectIndexOf(app.agents, req.params.agentCode,'codAgente'); + + // We have it but it's not logged + if (agent_position !== -1) { + if (app.agents[agent_position].status.id === 0) { // It's unlogged + app.agents[agent_position].changeStatus({ + status : 1, //Available + io: io + }); + } + res.send(200); + } + else { // We don't even know about him/her + var query = 'SELECT agentes.nombre as nombre,'+ + ' agentes.apellido1 as apellido1,'+ + ' agentes.apellido2 as apellido2,'+ + ' agentes.codAgente as codAgente,'+ + ' agentes.estado as estado,'+ + ' relcolaext.cola as cola,'+ + ' relcolaext.prioridad as prioridad'+ + ' FROM agentes, relcolaext'+ + ' WHERE relcolaext.codAgente = agentes.codAgente'+ + ' AND agentes.codAgente = "' + req.params.agentCode + '"'; + console.log ('Executing MySQL Query >> %s', query); + + database.client.query(query, + function (err, results, fields){ + if (err){ + res.send(500); + throw err; + } + + if (results[0]){ + if (arrayHelper.arrayObjectIndexOf(app.agents, req.params.agentCode,'codAgente') === -1){ // It's not already connected + // If we have a result, we push it into the agents array + app.agents.push( app.agent_utils.createAgent( + results, + io + )); + console.log('Agent has logged %s %s [%s]', results[0].nombre, results[0].apellido1, req.params.agentCode); + res.send(200); + } + else { + res.send(500,'Agent were already added'); + } + } + else{ + console.log("Someone has tried to log into the system with the extension %s, but it wasn't able to do that. Maybe the extension is not registered or it's not related to any queues.", req.params.agentCode); + } + } + ); + } + }; + /* + * Function that will be called upon agent disconnection + */ + this.unLogAgent = function (req, res) { + var agent_position = arrayHelper.arrayObjectIndexOf(app.agents, req.params.agentCode,'codAgente'); + if (agent_position !== -1){ + app.agents[agent_position].changeStatus({ + status : 0, //Disconnected + io: io + }); + console.log('Unlogged agent [%s]', req.params.agentCode); + } + + res.send(200); + }; + /* + * Function that will be called when an agent's phone start/stop ringing + */ + this.agentRing = function (req, res) { + var agent_position = arrayHelper.arrayObjectIndexOf(app.agents, req.params.agentCode,'codAgente'); + + if (agent_position !== -1){ + app.agents[agent_position].manageRinging({ + action : req.params.action, + io: io + }); + } + + res.send(200); + }; + /** + * Function that change the status of an agent. This function *should* only be called if the new status it's not a call + */ + this.changeStatusNoCall = function (req, res) { + var newStatus = self.determineStatusByReq(req.url), + agentPosition = arrayHelper.arrayObjectIndexOf(app.agents,req.params.agentCode,'codAgente'); + + // Changing the agent status and emiting by socket + if (agentPosition !== -1){ + app.agents[agentPosition].changeStatus({ + status : newStatus.status_code, + io : io + }); + } + + res.send(200); + }; + + this.dispatchCallInQueue = function (req, res){ + var isIncoming = (req.url.indexOf('/call/') !== -1), + type = '', + queue_name = req.params.queue, + queue = app.queue_utils.getQueueFromName(app.queues, queue_name), + unique_id = req.params.uniqueid, + client = (queue !== undefined) ? queue.client_obj : null, + call_position = -1; + + if (queue){ + call_position = arrayHelper.arrayObjectIndexOf(queue.calls, unique_id, 'uniqueid'); + } + + // If the call isn't in the queue yet + if (isIncoming && call_position === -1) { + console.log ('Incoming call [%s] to queue %s at %s', unique_id, queue_name, new Date()); + + // Stats stuff + if (client) { + client.total_calls++; + client.offered_calls++; + client.sendStatus(); + } + + type = 'in'; + } + else + { + console.log ('Call answered by [%s] in %s at %s', req.params.agentCode, queue_name, new Date()); + + var pos_agent = arrayHelper.arrayObjectIndexOf(app.agents,req.params.agentCode,'codAgente'); + + if (pos_agent !== -1){ + app.agents[pos_agent].changeStatus({status:4, io: io, queue: queue_name}); + app.talking = self.calculateTalking(); + app.updatePrimary(); + } + type = 'out'; + } + + // Valid if queue position is found + // Then if is incoming call, will be assured that the call isn't already registered + // or it's not incoming + if (queue !== undefined && ((isIncoming && call_position === -1) || (!isIncoming))){ + app.queue_utils.dispatchCall({ + uniqueid: unique_id, + queue: queue, + type: type, + abandoned : false, + io: io + }); + } + else { + console.log('The call [%s] wasn\'t added to %s because it was already there!', unique_id, queue_name); + } + + res.send(200); + }; + /** + * Function that will be called when an agent is performing a call + */ + this.externalCall = function (req, res){ + console.log ('Outgoing call from %s', req.params.agentCode); + + var pos_agent = arrayHelper.arrayObjectIndexOf(app.agents,req.params.agentCode,'codAgente'); + + if (pos_agent !== -1){ + app.agents[pos_agent].changeStatus({status: 5, io: io}); + } + + res.send(200); + }; + /* + * This function will be called when a call hangs. It may be in 'agente' in 'cola' or in 'message' + */ + this.hangCall = function (req, res){ + console.log ('Call finished at %s', req.params.type); + var queue; + + switch (req.params.type) + { + case 'agente': + var pos_agent = arrayHelper.arrayObjectIndexOf(app.agents, req.params.agentOrQueue, 'codAgente'); + + if (pos_agent !== -1){ + // If it's an incoming call + app.agents[pos_agent].endCall(io); + app.talking = self.calculateTalking(); + app.updatePrimary(); + } + else { + console.log('Call couldn\'t be ended because agent %s, cannot be found',req.params.agentOrQueue); + } + + break; + case 'cola' : + queue = app.queue_utils.getQueueFromName(app.queues, req.params.agentOrQueue); + if (queue !== undefined) { + console.log('Call [%s] is about to be dispatched from %s', req.params.uniqueid, req.params.agentOrQueue); + + app.queue_utils.dispatchCall( + { + queue: queue, + uniqueid: req.params.uniqueid, + type: 'out', + abandoned: true, + io: io + } + ); + } + else { + console.log('Call [%s] couldn\'t be dispatched from %s because queue wasn\'t found', + req.params.uniqueid, req.params.agentOrQueue + ); + } + break; + case 'message' : + queue = app.queue_utils.getQueueFromName(app.queues, req.params.agentOrQueue); + + if (queue !== undefined){ + var client = queue.client_obj; + + if (client){ + client.failed_calls++; + client.total_calls++; + client.sendStatus(); + } + } + break; + } + + res.send(200); + }; + /* + * Whenever a call is transferred this function will be called from server.js + */ + this.transferCall = function (req, res){ + console.log('Transfering [%s] from %s to %s', req.params.uniqueid, req.params.agent_from, req.params.agent_to); + + var pos_agent_from = arrayHelper.arrayObjectIndexOf(app.agents, req.params.agent_from, 'codAgente'), + pos_agent_to = arrayHelper.arrayObjectIndexOf(app.agents, req.params.agent_to, 'codAgente'); + + // If we get the agent whom is transferring, we end the call there + if (pos_agent_from !== -1) { + app.agents[pos_agent_from].endCall(io); + } + else { + console.log('We couldnt end the call at %s because we couldnt find it', req.params.agent_from) + } + + // If we get the agent to whom the call is going to be transferred, we create a new call to him + if (pos_agent_to !== -1) { + app.agents[pos_agent_to].changeStatus({status:4, io: io, queue: req.params.queue_name}); + } + else { + console.log('We couldnt send the call at %s because we couldnt find it', req.params.agent_to) + } + + res.send(200); + }; + /* + * This function is called whenever someone tries to see some stats from a client + */ + this.getClientStats = function ( req, res ) { + var client = app.client_utils.getClientFromName(app.clients, req.params.client_name); + if (client){ + if (req.params.from_date === undefined){ + // Real time request + var client_data = { + name : client.name, + real_time : true, + range_date : false, + total_offered_calls : 0, + total_calls : 0, + total_abandoned : 0, + abandoned_after_SLA : 0, + total_answered : 0, + answered_before_SLA : 0, + average_response_time : 0, + failed_calls : 0, + perc_abandoned : client.perc_abandoned, + perc_answered : client.perc_answered, + per_hour : null, + sec_abandoned : client.sec_abandoned, + sec_answered : client.sec_answered + }; + + if (req.url.indexOf('json') !== -1){ + var status = client.getStatus(); + status.client_name = client.name; + status.per_hour = client.stats_by_hour; + res.json(status); + } + else { + res.render('index', client_data); + } + } + else { + var start_date = new Date(req.params.from_date), + end_date, original_end_date; + + timeHelper.setAbsoluteDay(start_date); + + if (req.params.to_date !== undefined) { + end_date = new Date(req.params.to_date); + original_end_date = new Date(req.params.to_date); + timeHelper.setAbsoluteDay(end_date); + end_date.setDate(end_date.getDate() +1); + } + else { + end_date = new Date(req.params.from_date); + original_end_date = new Date(req.params.from_date); + timeHelper.setAbsoluteDay(end_date); + end_date.setDate(start_date.getDate() + 1); + } + + if ((end_date - start_date) > 0){ + app.async.parallel([function(callback){ + client.loadStats(start_date, end_date, callback); + }], function(data){ + data.start_date = start_date; + data.end_date = original_end_date; + data.range_date = (end_date - start_date !== 86400000); + + res.render('index',data); + }); + } + else { + res.end('Dates are incorrect. IE, Start date is lower than End date'); + } + } + } + else { + res.send(404, 'Sorry, we cannot find that!'); + } + }; + /* + * This function is called to update the current calls and calls in queue in the pannel + */ + this.updateCalls = function (req, res){ + app.calls = parseInt(req.params.total_calls ,10); + app.awaiting = parseInt(req.params.calls_in_queue ,10); + app.updatePrimary(); + + res.send(200); + }; + /* + * This function performs a reload of the data that the pannel has + */ + this.reload = function ( req, res ) { + app.refetcher.perform(true); + res.send(200, 'Done'); + }; + /* + * This functions output by console a debug trace for a queue + */ + this.debugQueue = function (req, res) { + var queue = app.queue_utils.getQueueFromName(app.queues, req.params.queue_name); + + if (queue){ + console.log('Debug trace for [%s]',queue.name); + console.log('------------------------------'); + console.log(queue.calls); + console.log('------------------------------'); + } + + res.send(200, 'Done'); + }; + + this.debugAgent = function(req, res) { + var agent_position = arrayHelper.arrayObjectIndexOf(app.agents, req.params.agent_code, 'codAgente'), + agent = (agent_position !== -1) ? app.agents[agent_position] : undefined; + + if (agent){ + console.log('Debug trace for [%s]', agent.nombre); + console.log('------------------------------'); + console.log(agent); + console.log('------------------------------'); + } + }; + + this.panelStats = function(req,res){ + console.log(app.connected_clients); + res.send(200,'Done'); + }; + + /* + * This function controlls the sim availability that's shown on the Pannel + */ + this.simAvailability = function (req, res) { + io.sockets.emit('simAvailability', + { + sim: req.params.sim_number , + available : req.params.available.toLowerCase() === 'available' + } + ); + res.send(200, 'Done'); + }; + /** + * This function calculates the number of people that it's currently talking + * it just counts the people attending an Incoming call. + * + * @return {Number} of people talking + */ + this.calculateTalking = function(){ + var talking = 0; + for (var i = 0, length = app.agents.length; i < length; i++){ + if (app.agents[i].status.id === 4){ + talking++; + } + } + return talking; + }; + + /** + * This functions determine the status contained in a string. The status code should match with those in + * agent.js + * + * @request : The string to look within + * + * @return an object with the code and the string as it's properties + */ + this.determineStatusByReq = function (request) { + var searches = [ + { + status_code : 1, + string : '/available/' + }, + { + status_code : 2, + string : '/meeting/' + }, + { + status_code : 3, + string : '/administrative/' + }, + { + status_code : 6, + string : '/resting/' + }, + { + status_code : 7, + string : '/glorytime/' + } + ]; + + for (var i = 0; i < searches.length; i++){ + if (request.indexOf(searches[i].string) !== -1) { return searches[i] } + } + + // We should never reach here! + return { + status_code: 0, + string : '' + }; + }; + + this.getAgentStatus = function (req, res) { + var agent_code = req.params.agent_code, + is_json = req.url.indexOf('json') !== -1; + + var result = is_json ? [] : ''; + + if (agent_code !== 'all'){ + var agent = app.agent_utils.getAgentFromCode(app.agents,agent_code); + if (agent) { + if (is_json){ + result.push(agent.status.name); + } + else { + result = agent.status.name; + } + } + } + else { + for (var i = 0, length = app.agents.length; i < length; i++){ + if (is_json){ + result.push({ + codAgente : app.agents[i].codAgente, + status : app.agents[i].status.name + }); + } + else{ + result += [app.agents[i].codAgente, app.agents[i].status.name].join(' ') + '\n'; + } + } + } + + if (is_json){ res.json(result); } + else { res.send(200, result); } + }; +}; \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..dbd012f --- /dev/null +++ b/server.js @@ -0,0 +1,300 @@ +/** + * Module dependencies. + */ +var express = require('express'), + server = express(), + Routes = require('./routes'), + App = require('./modules/app.js'), + browser_ban = require('./modules/browser_ban.js'), + pbx_bugs_solver = require('./modules/pbx_bugs_solver.js'), + async = require('async'), + database = require('./libraries/mysql.js'), + socketio = require('socket.io'), + CronJob = require('cron').CronJob, + mysql = require('mysql'); + +var io = socketio.listen(server.listen(8080)); + +io.configure('production', function(){ + console.log("***** Server in production mode *********"); + io.enable('browser client minification'); // send minified client + io.enable('browser client etag'); // apply etag caching logic based on version number + io.enable('browser client gzip'); // gzip the file + io.set('log level', 1); // reduce logging + io.set('transports', [ // enable all transports (optional if you want flashsocket) + 'websocket', 'flashsocket', 'htmlfile', 'xhr-polling', 'jsonp-polling' + ]); +}); +io.configure('development', function(){ + io.set('transports', ['websocket']); +}); + +// Undefined max listeners! +process.setMaxListeners(0); + +var app = new App(database, io, async), + routes = new Routes(app, database, io); + +// CronJob which restarts stats for all data +var cron_job = new CronJob('00 00 00 * * *', app.startReset,null,true); + +app.init(app.loadTodayStats); + +/* Server configuration */ +server.configure(function(){ + server.use(express.favicon()); + server.use(express.logger('dev')); + server.set('views', __dirname + '/views'); + server.set('view engine', 'jade'); + server.use(express.bodyParser()); + server.use(express.cookieParser()); + server.use(express.methodOverride()); + server.use(express.static(__dirname + '/public')); + server.use("/public", express.static(__dirname + '/public')); + server.use(browser_ban()); + server.use(pbx_bugs_solver()); + server.use(server.router); +}); + +server.configure('development', function(){ + server.use(express.errorHandler()); +}); + +/**** Routes definition *****/ +server.get('/', routes.index); + +//region Agents management +/* + ########################## + ### Agents management ### + ########################## + */ +/** + * When an agent logs into the phone this function will be called. + * @agentCode : The Agent Code or Extension. + * + * URL Example: /logAgent/1010 + */ +server.get('/logAgent/:agentCode', routes.logAgent); + +/** + * When an agent unlog it's phone this function will be called. + * @agentCode : The Agent Code or Extension. + * + * URL Example: /unLogAgent/1010 + */ +server.get('/unLogAgent/:agentCode', routes.unLogAgent); + +/** + * When an agent enters in administrative time, this function will be called. + * + * @agentCode : The Agent Code or Extension. + * + * URL Example: /administrative/1010 + */ +server.get('/administrative/:agentCode', routes.changeStatusNoCall); +/** + * When an agent enters in resting time, this function will be called. + * + * @agentCode : The Agent Code or Extension. + * + * URL Example: /resting/1010 + */ +server.get('/resting/:agentCode', routes.changeStatusNoCall); +/** + * When an agent disable administrative time or unavailable mode, this function will be called. + * + * @agentCode : The Agent Code or Extension. + * + * URL Example: /available/1010 + */ +server.get('/available/:agentCode', routes.changeStatusNoCall); +/** + * When an agent put him/herself into unavailable, this function will be called. + * + * @agentCode : The Agent Code or Extension. + * + * URL Example: /meeting/1010 + */ +server.get('/meeting/:agentCode', routes.changeStatusNoCall); +/** + * When an agent's phone starts or stops ringing, this function will be called + * + * @action : Whether the phone 'start' or 'stop' ringing + * @agentCode : The Agent Code or Extension. + * + * URL Example: /ringing/start/1010 + */ +server.get('/ringing/:action/:agentCode', routes.agentRing); +/** + * When an agent enters glorytime, this function will be called + * + * @agentCode : The Agent Code or Extension. + * + * URL Example: /glorytime/1010 + */ +server.get('/glorytime/:agentCode', routes.changeStatusNoCall); +//endregion +//region Calls management +/* + ########################## + #### Calls management #### + ########################## + */ +/** + * When a call enters in a queue, this function will be called + * + * @queue : The queue that is receiving the call. + * @uniqueid : The uniqueid of the call. + * + * URL Example: /call/APPLUSCola/1234567890 + */ +server.get('/call/:queue/:uniqueid', routes.dispatchCallInQueue); +/** + * When a call is answered by an agent, this function will be called. + * + * @queue : The queue that is receiving the call. + * @uniqueid : The uniqueid of the call. + * @agentCode : The agent whom is answering the call. + * + * URL Example: /answerCall/APPLUSCola/1234567890/1010 + */ +server.get('/answerCall/:queue/:uniqueid/:agentCode', routes.dispatchCallInQueue); +/** + * When an agent performs a call, this function will be called. + * + * @agentCode : The agent whom is performing the call. + * @queue : The queue that is receiving the call. + * + * URL Example: /externalCall/1010/APPLUSCola + */ +server.get('/externalCall/:agentCode/:queue', routes.externalCall); +/** + * When a call hangs, this function will be called. + * + * @type : This indicates where the call were terminated. Could have this values: + * * 'agente' : If it ends in an agent + * * 'cola' : If it ends in a queue + * @uniqueid : The uniqueid of the call + * @agentOrQueue : The AgentCode or the Queue Name where the call terminated. + * + * URL Example 1: /hangCall/cola/1234567890/APPLUSCola + * URL Example 2: /hangCall/agente/1234567890/1010 + */ +server.get('/hangCall/:type/:uniqueid/:agentOrQueue', routes.hangCall); +/** + * When a is tranferred from an agent to another one, this function will be called + * + * @uniqueid : The uniqueid of the call + * @agent_from : The agent whom is transferring the call + * @agent_to : The agent whom is receiving the call + * @queue_name : The queue in which is the call that's going to be transferred + */ +server.get('/transferCall/:uniqueid/from/:agent_from/to/:agent_to/at/:queue_name', routes.transferCall); +/** + * Simple function that receives the total calls and the calls in queue from Asterisk + * + * @total_calls : Total calls in the system to calculate the primary occupation + * @calls_in_queue : Current calls in queue + */ +server.get('/updateCalls/:total_calls/:calls_in_queue', routes.updateCalls); +//endregion +//region Stats management +/** + * This will try to find a client and will redirect to the stats page of the client + * + * @clientName : The name of the client + * + * URL Example: /stats/clients/Applus + */ +server.get('/stats/clients/:client_name', routes.getClientStats); +/** + * This will return the actual stats for the client in JSON format + * + * @clientName : The name of the client + * + * URL Example: /stats/json/clients/Applus + */ +server.get('/stats/json/clients/:client_name/', routes.getClientStats); +/** + * This will try to find a client and will redirect to the stats page of the client using a given day + * + * @clientName : The name of the client + * @from_date : The day which we want o see the stats + * + * URL Example: /stats/clients/Applus + */ +server.get('/stats/clients/:client_name/:from_date', routes.getClientStats); +/** + * This will try to find a client and will redirect to the stats page of the client given a range date + * it WILL validate that from_date is minor than to_date + * + * @clientName : The name of the client + * @from_date + * + * URL Example: /stats/clients/Applus + */ +server.get('/stats/clients/:client_name/:from_date/to/:to_date', routes.getClientStats); +/** + * This function returns the current status of an agent. `all` can be used to fetch all agents statuses. + * + * @agent_code : The extension of the agent to request or, `all` + * + * URL Example: /stats/clients/Applus + */ +server.get('/status/agents/:agent_code', routes.getAgentStatus); +/** + * This function returns the current status of an agent. `all` can be used to fetch all agents statuses in JSON. + * + * @agent_code : The extension of the agent to request or, `all` + * + * URL Example: /stats/clients/Applus + */ +server.get('/status/agents/:agent_code/json', routes.getAgentStatus); +//endregion +//region Debugging functions +/** + * Simple function that logs the status of a queue. Usefull to see what calls are currently enqueued. + */ +server.get('/debug/queue/:queue_name', routes.debugQueue); +/** + * Simple function that logs the status of an agent. + */ +server.get('/debug/agent/:agent_code', routes.debugAgent); +/** + * Simple function that logs some usage info of the pannel + */ +server.get('/debug/stats/', routes.panelStats); +//endregion +//region Utility functions +/** + * This route will reload all data from all sources and will send a signal to all panels so they refresh + */ +server.get('/reload', routes.reload); +/** + * Function that stablish a mobile sim available or busy + */ +server.get('/sim/:sim_number/:available', routes.simAvailability); +//endregion + +// On connection action +io.sockets.on('connection', function (socket) { + // If someone new comes, it will notified of the current status of the application + var endpoint = socket.manager.handshaken[socket.id].address; + console.log('Someone (%s) connected to the pannel', endpoint.address); + + app.sendCurrentStatus(socket.id, endpoint.address); + + // Binding to socket events + socket.on('sendCall', app.sendCallToAgent); + socket.on('forceUnlog', app.forceUnlogAgent); + socket.on('disconnect', function() { + app.deleteConnectedClient(socket.manager.handshaken[socket.id].address.address); + }); +}); + +// Reload all current pannels after 5 seconds +// If the server reload because an error this will tell all connected clients to reload +setTimeout(function(){ + io.sockets.emit('reload', {}); +}, 5000); \ No newline at end of file diff --git a/treehouse.txt b/treehouse.txt deleted file mode 100644 index 214777d..0000000 --- a/treehouse.txt +++ /dev/null @@ -1,2 +0,0 @@ -Antonio Laguna is Treehouse Member at http://teamtreehouse.com/ALaguna -Feb 3, 2012 \ No newline at end of file diff --git a/views/index.html b/views/index.html new file mode 100644 index 0000000..9e11881 --- /dev/null +++ b/views/index.html @@ -0,0 +1,112 @@ + + + + + + + + + Panel Flaix + + + + + + + + + + + + + + + + + +
    +
    +
    + ; +
    +
    + + Iconos de los clientes + +
    +
    +
    + + +
    + + + + + + \ No newline at end of file diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000..d11bbd6 --- /dev/null +++ b/views/index.jade @@ -0,0 +1,116 @@ +extends layout + +block body + #panel.widget + .content + h3 Date selection + p + label(for='from') From + input#from(type='text', name='from', size='30') + p + label(for='to') To + input#to(type='text', name='to', size='30') + button#datetime Stats for date + button#realtime Today's stats + a#open-tab(href='#') + #tab + #inner_tab + +block content + .widget.cabecera + img(src='/images/misc/accenture-logo.png') + if (real_time) + h3 Realtime #{name}'s stats + else + if (range_date) + h3 #{name}'s stats from #{start_date.toDateString()} to #{end_date.toDateString()} + else + h3 #{name}'s stats for #{start_date.toDateString()} + .widget.tabla.stats + header + section Stats area + section.widget-body + .widget-inner + table.table-striped + tbody + tr + th Inbound calls + td#inbound-calls + tr + th Failed calls + td#failed-calls + tr + th Offered calls + td#offered-calls + tr + th Abandoned calls + td#abandoned-calls + tr + th Abandoned after #{sec_abandoned} seconds + td#abandoned-sla + tr + th Answered calls + td#answered-calls + tr + th Answered before #{sec_answered} seconds + td#answered-sla + .widget.tabla.slas-kpis + header + section SLAs & KPIs Area + section.widget-body + .widget-inner + table.table-striped + tbody + tr + th % calls answered in target time SLA (Target => #{perc_answered}%, #{sec_answered} secs) + td#sla-answering + tr + th Average response time SLA (Target < #{sec_answered} sec) + td#average-response + tr + th Abandoned calls SLA (Target <= #{perc_abandoned}%, #{sec_abandoned} secs) + td#sla-abandoned + tr + th Abandoned calls KPI + td#kpi-abandoned.number.orange + .widget.graph.first + header + section Graph area + section.widget-body + #graph-calls.widget-inner + + .widget.tabla.stats-hour + header + section Stats per hour + section.widget-body + .widget-inner + table#stats-table.table-striped + thead + th Hour + th Answered + th Abandoned + th Service Level + th Abandon rate + th Average answer time + th Average abandon time + tbody + + script + var client_name = '#{name}'; + var real_time = #{real_time}; + var range_date = #{range_date}; + var perc_abandoned = '#{perc_abandoned}'; + var sec_answered = '#{sec_answered}'; + var perc_answered = '#{perc_answered}'; + if (!real_time) + var loaded_stats = { + total_calls : #{total_calls}, + total_offered_calls : #{total_offered_calls}, + average_response_time : #{average_response_time}, + failed_calls : #{failed_calls}, + total_abandoned : #{total_abandoned}, + abandoned_after_SLA : #{abandoned_after_SLA}, + total_answered : #{total_answered}, + answered_before_SLA : #{answered_before_SLA}, + per_hour : !{JSON.stringify(per_hour)} + }; \ No newline at end of file diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000..778fb72 --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,16 @@ +doctype 5 +html + head + title Client stats for #{name} + link(rel='stylesheet', href='/stylesheets/normalize.css') + link(rel='stylesheet', href='/stylesheets/style.css') + link(rel='stylesheet', href='/stylesheets/jquery-ui-1.8.23.custom.css') + script(src='/socket.io/socket.io.js') + body + block body + #dashboard-container + block content + script(src='/javascripts/jquery-1.8.1.min.js', type='text/javascript') + script(src='/javascripts/jquery-ui-1.8.23.custom.min.js', type='text/javascript') + script(src='/javascripts/highcharts.js', type='text/javascript') + script(src='/javascripts/client_stat.js', type='text/javascript') \ No newline at end of file