├── twitter.js ├── public ├── geometry.js ├── DINWeb.eot ├── DINWeb.woff ├── favicon.ico ├── DINWeb-Light.eot ├── DINWeb-Light.woff ├── index.css ├── geometry.css ├── index.js ├── color.js ├── getElementsByClassName.polyfill.js ├── colormap.css ├── gmaps.js └── socket.io.js ├── Procfile ├── .gitignore ├── README.md ├── idsecret.js ├── hidden.js ├── package.json ├── views ├── color.ejs ├── index.ejs ├── geometry.ejs └── map.ejs ├── colors.js ├── app.js └── instagram.js /twitter.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/geometry.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node app.js 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.swp 3 | -------------------------------------------------------------------------------- /public/DINWeb.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/colormap/master/public/DINWeb.eot -------------------------------------------------------------------------------- /public/DINWeb.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/colormap/master/public/DINWeb.woff -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/colormap/master/public/favicon.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ColorMap 2 | 3 | ColorMap is the geographical colorspace of your instagram posts. 4 | -------------------------------------------------------------------------------- /public/DINWeb-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/colormap/master/public/DINWeb-Light.eot -------------------------------------------------------------------------------- /public/DINWeb-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatever/colormap/master/public/DINWeb-Light.woff -------------------------------------------------------------------------------- /idsecret.js: -------------------------------------------------------------------------------- 1 | // API APPLICATION ID/SECRET/URL 2 | exports.id = '1b7f74c7514b4bfea1ba2401ad94267b'; 3 | exports.secret = '11d26b5980504e2f85a8b4f8bc691ba3'; 4 | exports.url = 'ping'; 5 | -------------------------------------------------------------------------------- /public/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", "Helvetica", sans-serif; 3 | font-size: 20pt; 4 | color: white; 5 | letter-spacing: .3em; 6 | background-color: black; 7 | } 8 | 9 | #text { 10 | width: 320px; 11 | margin: 1em auto; 12 | text-align: center; 13 | } 14 | -------------------------------------------------------------------------------- /hidden.js: -------------------------------------------------------------------------------- 1 | function sampleN (list, n) { 2 | if (n === undefined || n < 0) 3 | n = 1; 4 | if (!list.length) 5 | return undefined; 6 | var results = []; 7 | for (var k = 0; k < n; k++) 8 | results.push(list[Math.floor(Math.random() * list.length)]); 9 | return results; 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "color", 3 | "author": "Matt Owen", 4 | "version": "0.0.1-12", 5 | "description": "Displays website where colors change across all devices simultaneously", 6 | "main": "./app.js", 7 | "dependencies": { 8 | "express": "3.0.x", 9 | "ejs": "0.8.x", 10 | "socket.io": "0.9.x" 11 | }, 12 | "engines": { 13 | "node": "0.8.x", 14 | "npm": "1.1.x" 15 | }, 16 | "scripts": { 17 | "start": "app.js" 18 | }, 19 | "subdomain": "color", 20 | "domains": [] 21 | } -------------------------------------------------------------------------------- /public/geometry.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: DINWeb; 3 | src: url("/public/DINWeb-Light.eot"); 4 | font-weight: 400; 5 | } 6 | @font-face { 7 | font-family: DINWeb; 8 | src: url("/public/DINWeb-Light.woff") format("woff"); 9 | font-weight: 400; 10 | } 11 | @font-face { 12 | font-family: DINWeb; 13 | src: url("/public/DINWeb.eot"); 14 | font-weight: 600; 15 | } 16 | @font-face { 17 | font-family: DINWeb; 18 | src: url("/public/DINWeb.woff") format("woff"); 19 | font-weight: 600; 20 | } 21 | body { 22 | font-family: DINWeb; 23 | font-size: 16pt; 24 | font-weight: 400; 25 | } 26 | -------------------------------------------------------------------------------- /public/index.js: -------------------------------------------------------------------------------- 1 | // var socket = io.connect('http://color.jit.su/'); 2 | var socket = io.connect('http://localhost:5000/'); 3 | 4 | socket.on('connect', function () { 5 | console.log("Connected!"); 6 | }); 7 | 8 | socket.on('message', function (data) { 9 | console.log("Message pinged = " + data); 10 | // $("body").animate({ backgroundColor : data }, 50); 11 | $("#text").html(data) 12 | .stop(false, false) 13 | .animate({ color : data }, 50); 14 | 15 | $(".color-ui-bg:after, .color-ui-bg").stop(false, false) 16 | .animate({ backgroundColor : data, borderColor : data }); 17 | }); 18 | -------------------------------------------------------------------------------- /views/color.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | COLOR 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 |
<%= lastColor %>
23 | 24 | 25 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | COLOR 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 |
<%= lastColor %>
23 | 24 | 25 | -------------------------------------------------------------------------------- /public/color.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Color Client Library 3 | * Implements generic handlers for using the color api. 4 | * @author Matt Owen 5 | * @since 2013-06-09 6 | */ 7 | 8 | console = console || { log : function () {} }; 9 | 10 | // Connect to color server 11 | var socket = io.connect('http://color.jit.su:80'); 12 | 13 | // Display message saying you are connected to the color server 14 | socket.on('connect', function () { 15 | console.log("[color] connected"); 16 | }); 17 | 18 | // Display message saying you are disconnected to the color server 19 | socket.on('disconnect', function () { 20 | console.log("[color] disconnected"); 21 | }); 22 | 23 | // Received message from server. Adjust colors accordingly. 24 | socket.on('message', function (data) { 25 | console.log("[color] received: " + data); 26 | 27 | $(".color-ui-bg").stop(false, false).animate({ backgroundColor : data, borderColor : data }); 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /public/getElementsByClassName.polyfill.js: -------------------------------------------------------------------------------- 1 | // Add a getElementsByClassName function if the browser doesn't have one 2 | // Limitation: only works with one class name 3 | // Copyright: Eike Send http://eike.se/nd 4 | // License: MIT License 5 | 6 | if (!document.getElementsByClassName) { 7 | document.getElementsByClassName = function(search) { 8 | var d = document, elements, pattern, i, results = []; 9 | if (d.querySelectorAll) { // IE8 10 | return d.querySelectorAll("." + search); 11 | } 12 | if (d.evaluate) { // IE6, IE7 13 | pattern = ".//*[contains(concat(' ', @class, ' '), ' " + search + " ')]"; 14 | elements = d.evaluate(pattern, d, null, 0, null); 15 | while ((i = elements.iterateNext())) { 16 | results.push(i); 17 | } 18 | } else { 19 | elements = d.getElementsByTagName("*"); 20 | pattern = new RegExp("(^|\\s)" + search + "(\\s|$)"); 21 | for (i = 0; i < elements.length; i++) { 22 | if ( pattern.test(elements[i].className) ) { 23 | results.push(elements[i]); 24 | } 25 | } 26 | } 27 | return results; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /colors.js: -------------------------------------------------------------------------------- 1 | // Colors 2 | var colors = [ 3 | "#F11E65", "#E0F11E", "#1EF18B", 4 | "#1EF1B8", "#F15A1E", "#461EF1", 5 | "#1E9AF1", "#8B1EF1", "#1ED1F1" 6 | ]; 7 | 8 | // Initialize last color 9 | lastColor = random(); 10 | 11 | /** 12 | * Return a random color 13 | * @return character specifying a color in hexdecimal 14 | */ 15 | function random () { 16 | return colors[Math.floor(Math.random() * colors.length)]; 17 | } 18 | 19 | /** 20 | * Get the next color, setting it as the new last color 21 | * @return string specifying the next color 22 | */ 23 | function getNext () { 24 | var i = 0; 25 | // Sample until we get a new color... or i gets too large 26 | do { 27 | color = random(); 28 | } while (lastColor === color && ++i < 100); 29 | // Store sampled color 30 | lastColor = color; 31 | // Return 32 | return lastColor; 33 | } 34 | 35 | /** 36 | * Return the last color 37 | * @return string specifying the last color that was returned 38 | */ 39 | function last () { 40 | return lastColor; 41 | } 42 | 43 | /* 44 | * next : get next color 45 | * random : get a *random* 46 | * last : get the last color 47 | */ 48 | exports.next = getNext; 49 | exports.random = random; 50 | exports.last = last; 51 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP + WebSocket for ColorMap 3 | * Implements color protocol 4 | * @author Matt Owen 5 | * @since 2013-06-08 6 | */ 7 | 8 | // Frameworks 9 | var express = require("express"), 10 | app = express(), 11 | http = require("http"), 12 | server = http.createServer(app).listen(process.env.PORT), 13 | io = require("socket.io").listen(server) 14 | ; 15 | 16 | // Custom libs 17 | var colors = require("./colors"); 18 | 19 | // Public 20 | // app.use(express.bodyParser()); 21 | app.use('/public', express.static(__dirname + '/public')); 22 | app.use(express.favicon(__dirname + '/public/favicon.ico', { maxAge: 2592000000 })) 23 | 24 | // Base page 25 | app.get('/', function (req, resp) { 26 | console.log(lastColor); 27 | resp.render('map.ejs', { color: lastColor, lastColor : lastColor }); 28 | }); 29 | 30 | // Show new color 31 | app.get('/color', function (req, resp) { 32 | resp.render('index.ejs', { lastColor : lastColor }); 33 | }); 34 | 35 | // Display the map 36 | app.get('/map', function () { 37 | }); 38 | 39 | app.get('/geometry', function (req, resp) { 40 | resp.render('geometry.ejs', { key : "xxx", secret : "yyy" }); 41 | }); 42 | 43 | app.get('/geometry', function (req, resp) { 44 | resp.render('client', { key : "xxx", secret : "yyy" }); 45 | }); 46 | 47 | // Send a new color every 2 seconds... 48 | setInterval(function () { 49 | io.sockets.send(colors.next()); 50 | }, 2000); 51 | -------------------------------------------------------------------------------- /instagram.js: -------------------------------------------------------------------------------- 1 | var // I want to dance 'till dawn 2 | https = require('https'), 3 | idsecret = require('./idsecret'); 4 | 5 | var lastid = 0; 6 | 7 | /* 8 | * Get tagged media 9 | */ 10 | function getmedia (tag, callback) { 11 | 12 | var o = { 13 | host: 'api.instagram.com', 14 | port: 443, 15 | path: '/v1/tags/' + tag + '/media/recent?client_id=' + idsecret.id, 16 | method: 'GET' 17 | }; 18 | 19 | var data = '', results = []; 20 | 21 | var req = https.request(o, function(res) { 22 | // Get a chunk of data 23 | res.on('data', function(d) { 24 | data += d; 25 | }); 26 | 27 | // Data has stopped coming in 28 | res.on('end', function () { 29 | var o = {}; 30 | 31 | try { 32 | o = JSON.parse(data); 33 | } 34 | catch (ex) { 35 | o = {}; 36 | results = []; 37 | callback.call(undefined, results); 38 | return undefined; 39 | }; 40 | 41 | var max_id = o.pagination.next_max_tag_id, 42 | min_id = o.pagination.min_tag_id, next_url; 43 | 44 | for (var k=0; k < o.data.length; k++) { 45 | var res = { 46 | images : o.data[k].images, 47 | id : o.data[k].id, 48 | filter : o.data[k].filter 49 | }; 50 | 51 | results.push(res); 52 | } 53 | 54 | callback.call(undefined, results); 55 | }); 56 | }); 57 | 58 | req.on('error', function (e) { 59 | console.error(e); 60 | }); 61 | 62 | req.end(); 63 | } 64 | 65 | // EXPORTS 66 | exports.getmedia = getmedia; 67 | -------------------------------------------------------------------------------- /views/geometry.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | geometry.xxx 5 | 6 | 7 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 65 | 66 |
67 | My brain says, I'm a searing pain. 68 |
69 | 70 |
71 | You'll be a walking disaster. 72 |
73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /public/colormap.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100% 3 | } 4 | 5 | body { 6 | height: 100%; 7 | margin: 0; 8 | padding: 0; 9 | font-family: verdana, helvetica, arial, sans-serif; 10 | background-color: rgb(33, 33, 33); 11 | } 12 | 13 | #map-canvas { 14 | height: 100%; 15 | } 16 | 17 | /* Header elements */ 18 | 19 | .logo { 20 | font-family: 'Archivo Narrow', sans-serif; 21 | font-size: 2em; 22 | color: rgba(255,255,255,1); 23 | position: fixed; 24 | z-index: 999; 25 | margin: 0px; 26 | padding: 10px; 27 | } 28 | 29 | /* Options */ 30 | 31 | #options{ 32 | display: block; 33 | position: absolute; 34 | bottom: 300px; 35 | left: 0px; 36 | } 37 | 38 | .panel { 39 | position: absolute; 40 | top: 80px; 41 | left: 0; 42 | display: none; 43 | border:none; 44 | width: 500px; 45 | height: auto; 46 | padding: 30px 30px 30px 130px; 47 | opacity: .80; 48 | } 49 | 50 | .panel:after { 51 | content: ""; 52 | position: absolute; 53 | right: -20px; 54 | top:0px; 55 | border-top: 47px solid transparent; 56 | border-bottom: 47px solid transparent; 57 | border-left-width: 20px; 58 | border-left-style: solid; 59 | } 60 | 61 | a.trigger{ 62 | position: absolute; 63 | text-decoration: none; 64 | top: 80px; left: 0; 65 | font-size: 14px; 66 | letter-spacing:-1px; 67 | color:#fff; 68 | padding: 20px 10px 20px 5px ; 69 | font-weight: 700; 70 | border-width: 1px; 71 | border-style: solid; 72 | -moz-border-radius-topright: 5px; 73 | -webkit-border-top-right-radius: 5px; 74 | -moz-border-radius-bottomright: 5px; 75 | -webkit-border-bottom-right-radius: 5px; 76 | -moz-border-radius-bottomleft: 0px; 77 | -webkit-border-bottom-left-radius: 0px; 78 | display: block; 79 | } 80 | 81 | 82 | 83 | a.trigger:hover { 84 | position: absolute; 85 | text-decoration: none; 86 | top: 80px; left: 0; 87 | font-size: 14px; 88 | letter-spacing:-1px; 89 | font-family: verdana, helvetica, arial, sans-serif; 90 | color:#fff; 91 | padding: 20px 10px 20px 5px ; 92 | font-weight: 700; 93 | border-width: 1px; 94 | border-style: solid; 95 | -moz-border-radius-topright: 5px; 96 | -webkit-border-top-right-radius: 5px; 97 | -moz-border-radius-bottomright: 5px; 98 | -webkit-border-bottom-right-radius: 5px; 99 | -moz-border-radius-bottomleft: 0px; 100 | -webkit-border-bottom-left-radius: 0px; 101 | display: block; 102 | } 103 | 104 | ul { 105 | padding: 0; 106 | margin: 0; 107 | list-style-type: none; 108 | } 109 | 110 | ul li { 111 | display: inline; 112 | padding: 0; 113 | margin: 0; 114 | list-style-type: none; 115 | text-decoration: none; 116 | } 117 | 118 | a.li{ 119 | font-family:helvetica, sans-serif; 120 | font-size: 2em; 121 | padding: 10px; 122 | color: white; 123 | text-decoration: none; !important; 124 | } 125 | 126 | a.li:hover, a.li:active, #footer { 127 | position: fixed; 128 | bottom: 0px; 129 | width: 100%; 130 | height: 80px; 131 | background:rgba(0,0,0,0.8); 132 | } 133 | -------------------------------------------------------------------------------- /views/map.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ColorMap | Color fucking rules 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 36 | 88 | 89 | 90 |

ColorMap

91 |
92 | 93 |
94 |
95 | 96 | 97 |
98 | 99 | 100 |
101 |
102 | 107 |
108 |


109 |
110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /public/gmaps.js: -------------------------------------------------------------------------------- 1 | function initialize() { 2 | // Define basic map options 3 | var mapOptions = { center : new google.maps.LatLng(42.379851,-71.05957), zoom: 8, disableDefaultUI : true, mapTypeId: google.maps.MapTypeId.ROADMAP }; 4 | 5 | // Create map from canvas 6 | var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions); 7 | 8 | // Long list of complex styles 9 | var styles = [ 10 | { 11 | featureType: 'water', 12 | elementType: 'all', 13 | stylers: [ 14 | { hue: '#000000' }, 15 | { saturation: -100 }, 16 | { lightness: -100 }, 17 | { visibility: 'on' } 18 | ] 19 | }, 20 | { 21 | featureType: 'landscape', 22 | elementType: 'all', 23 | stylers: [ 24 | { hue: '#5D5E5E' }, 25 | { saturation: -98 }, 26 | { lightness: -59 }, 27 | { visibility: 'on' } 28 | ] 29 | }, 30 | { 31 | featureType: 'landscape.man_made', 32 | elementType: 'all', 33 | stylers: [ 34 | { hue: '#000000' }, 35 | { saturation: -100 }, 36 | { lightness: -100 }, 37 | { visibility: 'off' } 38 | ] 39 | }, 40 | { 41 | featureType: 'landscape.natural', 42 | elementType: 'all', 43 | stylers: [ 44 | { hue: '#5D5E5E' }, 45 | { saturation: -96 }, 46 | { lightness: -61 }, 47 | { visibility: 'on' } 48 | ] 49 | }, 50 | { 51 | featureType: 'road', 52 | elementType: 'all', 53 | stylers: [ 54 | { hue: '#000000' }, 55 | { saturation: -100 }, 56 | { lightness: -100 }, 57 | { visibility: 'off' } 58 | ] 59 | }, 60 | { 61 | featureType: 'road.highway', 62 | elementType: 'all', 63 | stylers: [ 64 | { hue: '#000000' }, 65 | { saturation: -100 }, 66 | { lightness: -100 }, 67 | { visibility: 'off' } 68 | ] 69 | }, 70 | { 71 | featureType: 'road.arterial', 72 | elementType: 'all', 73 | stylers: [ 74 | { hue: '#000000' }, 75 | { saturation: -100 }, 76 | { lightness: -100 }, 77 | { visibility: 'off' } 78 | ] 79 | }, 80 | { 81 | featureType: 'road.local', 82 | elementType: 'all', 83 | stylers: [ 84 | { hue: '#000000' }, 85 | { saturation: -100 }, 86 | { lightness: -100 }, 87 | { visibility: 'off' } 88 | ] 89 | }, 90 | { 91 | featureType: 'poi', 92 | elementType: 'all', 93 | stylers: [ 94 | { hue: '#000000' }, 95 | { saturation: -100 }, 96 | { lightness: -100 }, 97 | { visibility: 'off' } 98 | ] 99 | }, 100 | { 101 | featureType: 'poi.park', 102 | elementType: 'all', 103 | stylers: [ 104 | { hue: '#000000' }, 105 | { saturation: -100 }, 106 | { lightness: -100 }, 107 | { visibility: 'off' } 108 | ] 109 | }, 110 | { 111 | featureType: 'poi.business', 112 | elementType: 'all', 113 | stylers: [ 114 | { hue: '#000000' }, 115 | { saturation: -100 }, 116 | { lightness: -100 }, 117 | { visibility: 'off' } 118 | ] 119 | }, 120 | { 121 | featureType: 'poi.attraction', 122 | elementType: 'all', 123 | stylers: [ 124 | { hue: '#000000' }, 125 | { saturation: -100 }, 126 | { lightness: -100 }, 127 | { visibility: 'off' } 128 | ] 129 | }, 130 | { 131 | featureType: 'poi.medical', 132 | elementType: 'all', 133 | stylers: [ 134 | { hue: '#000000' }, 135 | { saturation: -100 }, 136 | { lightness: -100 }, 137 | { visibility: 'off' } 138 | ] 139 | }, 140 | { 141 | featureType: 'poi.school', 142 | elementType: 'all', 143 | stylers: [ 144 | { hue: '#000000' }, 145 | { saturation: -100 }, 146 | { lightness: -100 }, 147 | { visibility: 'off' } 148 | ] 149 | }, 150 | { 151 | featureType: 'poi.government', 152 | elementType: 'all', 153 | stylers: [ 154 | { hue: '#000000' }, 155 | { saturation: -100 }, 156 | { lightness: -100 }, 157 | { visibility: 'off' } 158 | ] 159 | }, 160 | { 161 | featureType: 'poi.place_of_worship', 162 | elementType: 'all', 163 | stylers: [ 164 | { hue: '#000000' }, 165 | { saturation: -100 }, 166 | { lightness: -100 }, 167 | { visibility: 'off' } 168 | ] 169 | }, 170 | { 171 | featureType: 'administrative', 172 | elementType: 'all', 173 | stylers: [ 174 | { hue: '#000000' }, 175 | { saturation: 0 }, 176 | { lightness: -100 }, 177 | { visibility: 'off' } 178 | ] 179 | }, 180 | { 181 | featureType: 'administrative.country', 182 | elementType: 'all', 183 | stylers: [ 184 | { hue: '#000000' }, 185 | { saturation: 0 }, 186 | { lightness: -100 }, 187 | { visibility: 'off' } 188 | ] 189 | }, 190 | { 191 | featureType: 'administrative.land_parcel', 192 | elementType: 'all', 193 | stylers: [ 194 | { hue: '#000000' }, 195 | { saturation: 0 }, 196 | { lightness: -100 }, 197 | { visibility: 'off' } 198 | ] 199 | }, 200 | { 201 | featureType: 'administrative.locality', 202 | elementType: 'all', 203 | stylers: [ 204 | { hue: '#000000' }, 205 | { saturation: 0 }, 206 | { lightness: 0 }, 207 | { visibility: 'off' } 208 | ] 209 | }, 210 | { 211 | featureType: 'administrative.neighborhood', 212 | elementType: 'all', 213 | stylers: [ 214 | { hue: '#000000' }, 215 | { saturation: 0 }, 216 | { lightness: -100 }, 217 | { visibility: 'off' } 218 | ] 219 | }, 220 | { 221 | featureType: 'administrative.province', 222 | elementType: 'all', 223 | stylers: [ 224 | { hue: '#000000' }, 225 | { saturation: 0 }, 226 | { lightness: -100 }, 227 | { visibility: 'off' } 228 | ] 229 | }, 230 | { 231 | featureType: 'transit', 232 | elementType: 'all', 233 | stylers: [ 234 | { hue: '#000000' }, 235 | { saturation: 0 }, 236 | { lightness: -100 }, 237 | { visibility: 'off' } 238 | ] 239 | } 240 | ]; 241 | 242 | map.setOptions({ styles : styles }); 243 | } 244 | google.maps.event.addDomListener(window, 'load', initialize); 245 | -------------------------------------------------------------------------------- /public/socket.io.js: -------------------------------------------------------------------------------- 1 | /*! Socket.IO.js build:0.8.7, development. Copyright(c) 2011 LearnBoost MIT Licensed */ 2 | 3 | /** 4 | * socket.io 5 | * Copyright(c) 2011 LearnBoost 6 | * MIT Licensed 7 | */ 8 | 9 | (function (exports, global) { 10 | 11 | /** 12 | * IO namespace. 13 | * 14 | * @namespace 15 | */ 16 | 17 | var io = exports; 18 | 19 | /** 20 | * Socket.IO version 21 | * 22 | * @api public 23 | */ 24 | 25 | io.version = '0.8.7'; 26 | 27 | /** 28 | * Protocol implemented. 29 | * 30 | * @api public 31 | */ 32 | 33 | io.protocol = 1; 34 | 35 | /** 36 | * Available transports, these will be populated with the available transports 37 | * 38 | * @api public 39 | */ 40 | 41 | io.transports = []; 42 | 43 | /** 44 | * Keep track of jsonp callbacks. 45 | * 46 | * @api private 47 | */ 48 | 49 | io.j = []; 50 | 51 | /** 52 | * Keep track of our io.Sockets 53 | * 54 | * @api private 55 | */ 56 | io.sockets = {}; 57 | 58 | 59 | /** 60 | * Manages connections to hosts. 61 | * 62 | * @param {String} uri 63 | * @Param {Boolean} force creation of new socket (defaults to false) 64 | * @api public 65 | */ 66 | 67 | io.connect = function (host, details) { 68 | var uri = io.util.parseUri(host) 69 | , uuri 70 | , socket; 71 | 72 | if (global && global.location) { 73 | uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); 74 | uri.host = uri.host || (global.document 75 | ? global.document.domain : global.location.hostname); 76 | uri.port = uri.port || global.location.port; 77 | } 78 | 79 | uuri = io.util.uniqueUri(uri); 80 | 81 | var options = { 82 | host: uri.host 83 | , secure: 'https' == uri.protocol 84 | , port: uri.port || ('https' == uri.protocol ? 443 : 80) 85 | , query: uri.query || '' 86 | }; 87 | 88 | io.util.merge(options, details); 89 | 90 | if (options['force new connection'] || !io.sockets[uuri]) { 91 | socket = new io.Socket(options); 92 | } 93 | 94 | if (!options['force new connection'] && socket) { 95 | io.sockets[uuri] = socket; 96 | } 97 | 98 | socket = socket || io.sockets[uuri]; 99 | 100 | // if path is different from '' or / 101 | return socket.of(uri.path.length > 1 ? uri.path : ''); 102 | }; 103 | 104 | })('object' === typeof module ? module.exports : (this.io = {}), this); 105 | 106 | /** 107 | * socket.io 108 | * Copyright(c) 2011 LearnBoost 109 | * MIT Licensed 110 | */ 111 | 112 | (function (exports, global) { 113 | 114 | /** 115 | * Utilities namespace. 116 | * 117 | * @namespace 118 | */ 119 | 120 | var util = exports.util = {}; 121 | 122 | /** 123 | * Parses an URI 124 | * 125 | * @author Steven Levithan (MIT license) 126 | * @api public 127 | */ 128 | 129 | var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; 130 | 131 | var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 132 | 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 133 | 'anchor']; 134 | 135 | util.parseUri = function (str) { 136 | var m = re.exec(str || '') 137 | , uri = {} 138 | , i = 14; 139 | 140 | while (i--) { 141 | uri[parts[i]] = m[i] || ''; 142 | } 143 | 144 | return uri; 145 | }; 146 | 147 | /** 148 | * Produces a unique url that identifies a Socket.IO connection. 149 | * 150 | * @param {Object} uri 151 | * @api public 152 | */ 153 | 154 | util.uniqueUri = function (uri) { 155 | var protocol = uri.protocol 156 | , host = uri.host 157 | , port = uri.port; 158 | 159 | if ('document' in global) { 160 | host = host || document.domain; 161 | port = port || (protocol == 'https' 162 | && document.location.protocol !== 'https:' ? 443 : document.location.port); 163 | } else { 164 | host = host || 'localhost'; 165 | 166 | if (!port && protocol == 'https') { 167 | port = 443; 168 | } 169 | } 170 | 171 | return (protocol || 'http') + '://' + host + ':' + (port || 80); 172 | }; 173 | 174 | /** 175 | * Mergest 2 query strings in to once unique query string 176 | * 177 | * @param {String} base 178 | * @param {String} addition 179 | * @api public 180 | */ 181 | 182 | util.query = function (base, addition) { 183 | var query = util.chunkQuery(base || '') 184 | , components = []; 185 | 186 | util.merge(query, util.chunkQuery(addition || '')); 187 | for (var part in query) { 188 | if (query.hasOwnProperty(part)) { 189 | components.push(part + '=' + query[part]); 190 | } 191 | } 192 | 193 | return components.length ? '?' + components.join('&') : ''; 194 | }; 195 | 196 | /** 197 | * Transforms a querystring in to an object 198 | * 199 | * @param {String} qs 200 | * @api public 201 | */ 202 | 203 | util.chunkQuery = function (qs) { 204 | var query = {} 205 | , params = qs.split('&') 206 | , i = 0 207 | , l = params.length 208 | , kv; 209 | 210 | for (; i < l; ++i) { 211 | kv = params[i].split('='); 212 | if (kv[0]) { 213 | query[kv[0]] = decodeURIComponent(kv[1]); 214 | } 215 | } 216 | 217 | return query; 218 | }; 219 | 220 | /** 221 | * Executes the given function when the page is loaded. 222 | * 223 | * io.util.load(function () { console.log('page loaded'); }); 224 | * 225 | * @param {Function} fn 226 | * @api public 227 | */ 228 | 229 | var pageLoaded = false; 230 | 231 | util.load = function (fn) { 232 | if ('document' in global && document.readyState === 'complete' || pageLoaded) { 233 | return fn(); 234 | } 235 | 236 | util.on(global, 'load', fn, false); 237 | }; 238 | 239 | /** 240 | * Adds an event. 241 | * 242 | * @api private 243 | */ 244 | 245 | util.on = function (element, event, fn, capture) { 246 | if (element.attachEvent) { 247 | element.attachEvent('on' + event, fn); 248 | } else if (element.addEventListener) { 249 | element.addEventListener(event, fn, capture); 250 | } 251 | }; 252 | 253 | /** 254 | * Generates the correct `XMLHttpRequest` for regular and cross domain requests. 255 | * 256 | * @param {Boolean} [xdomain] Create a request that can be used cross domain. 257 | * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest. 258 | * @api private 259 | */ 260 | 261 | util.request = function (xdomain) { 262 | 263 | if (xdomain && 'undefined' != typeof XDomainRequest) { 264 | return new XDomainRequest(); 265 | } 266 | 267 | if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { 268 | return new XMLHttpRequest(); 269 | } 270 | 271 | if (!xdomain) { 272 | try { 273 | return new ActiveXObject('Microsoft.XMLHTTP'); 274 | } catch(e) { } 275 | } 276 | 277 | return null; 278 | }; 279 | 280 | /** 281 | * XHR based transport constructor. 282 | * 283 | * @constructor 284 | * @api public 285 | */ 286 | 287 | /** 288 | * Change the internal pageLoaded value. 289 | */ 290 | 291 | if ('undefined' != typeof window) { 292 | util.load(function () { 293 | pageLoaded = true; 294 | }); 295 | } 296 | 297 | /** 298 | * Defers a function to ensure a spinner is not displayed by the browser 299 | * 300 | * @param {Function} fn 301 | * @api public 302 | */ 303 | 304 | util.defer = function (fn) { 305 | if (!util.ua.webkit || 'undefined' != typeof importScripts) { 306 | return fn(); 307 | } 308 | 309 | util.load(function () { 310 | setTimeout(fn, 100); 311 | }); 312 | }; 313 | 314 | /** 315 | * Merges two objects. 316 | * 317 | * @api public 318 | */ 319 | 320 | util.merge = function merge (target, additional, deep, lastseen) { 321 | var seen = lastseen || [] 322 | , depth = typeof deep == 'undefined' ? 2 : deep 323 | , prop; 324 | 325 | for (prop in additional) { 326 | if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) { 327 | if (typeof target[prop] !== 'object' || !depth) { 328 | target[prop] = additional[prop]; 329 | seen.push(additional[prop]); 330 | } else { 331 | util.merge(target[prop], additional[prop], depth - 1, seen); 332 | } 333 | } 334 | } 335 | 336 | return target; 337 | }; 338 | 339 | /** 340 | * Merges prototypes from objects 341 | * 342 | * @api public 343 | */ 344 | 345 | util.mixin = function (ctor, ctor2) { 346 | util.merge(ctor.prototype, ctor2.prototype); 347 | }; 348 | 349 | /** 350 | * Shortcut for prototypical and static inheritance. 351 | * 352 | * @api private 353 | */ 354 | 355 | util.inherit = function (ctor, ctor2) { 356 | function f() {}; 357 | f.prototype = ctor2.prototype; 358 | ctor.prototype = new f; 359 | }; 360 | 361 | /** 362 | * Checks if the given object is an Array. 363 | * 364 | * io.util.isArray([]); // true 365 | * io.util.isArray({}); // false 366 | * 367 | * @param Object obj 368 | * @api public 369 | */ 370 | 371 | util.isArray = Array.isArray || function (obj) { 372 | return Object.prototype.toString.call(obj) === '[object Array]'; 373 | }; 374 | 375 | /** 376 | * Intersects values of two arrays into a third 377 | * 378 | * @api public 379 | */ 380 | 381 | util.intersect = function (arr, arr2) { 382 | var ret = [] 383 | , longest = arr.length > arr2.length ? arr : arr2 384 | , shortest = arr.length > arr2.length ? arr2 : arr; 385 | 386 | for (var i = 0, l = shortest.length; i < l; i++) { 387 | if (~util.indexOf(longest, shortest[i])) 388 | ret.push(shortest[i]); 389 | } 390 | 391 | return ret; 392 | } 393 | 394 | /** 395 | * Array indexOf compatibility. 396 | * 397 | * @see bit.ly/a5Dxa2 398 | * @api public 399 | */ 400 | 401 | util.indexOf = function (arr, o, i) { 402 | if (Array.prototype.indexOf) { 403 | return Array.prototype.indexOf.call(arr, o, i); 404 | } 405 | 406 | for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0; 407 | i < j && arr[i] !== o; i++) {} 408 | 409 | return j <= i ? -1 : i; 410 | }; 411 | 412 | /** 413 | * Converts enumerables to array. 414 | * 415 | * @api public 416 | */ 417 | 418 | util.toArray = function (enu) { 419 | var arr = []; 420 | 421 | for (var i = 0, l = enu.length; i < l; i++) 422 | arr.push(enu[i]); 423 | 424 | return arr; 425 | }; 426 | 427 | /** 428 | * UA / engines detection namespace. 429 | * 430 | * @namespace 431 | */ 432 | 433 | util.ua = {}; 434 | 435 | /** 436 | * Whether the UA supports CORS for XHR. 437 | * 438 | * @api public 439 | */ 440 | 441 | util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { 442 | try { 443 | var a = new XMLHttpRequest(); 444 | } catch (e) { 445 | return false; 446 | } 447 | 448 | return a.withCredentials != undefined; 449 | })(); 450 | 451 | /** 452 | * Detect webkit. 453 | * 454 | * @api public 455 | */ 456 | 457 | util.ua.webkit = 'undefined' != typeof navigator 458 | && /webkit/i.test(navigator.userAgent); 459 | 460 | })('undefined' != typeof io ? io : module.exports, this); 461 | 462 | /** 463 | * socket.io 464 | * Copyright(c) 2011 LearnBoost 465 | * MIT Licensed 466 | */ 467 | 468 | (function (exports, io) { 469 | 470 | /** 471 | * Expose constructor. 472 | */ 473 | 474 | exports.EventEmitter = EventEmitter; 475 | 476 | /** 477 | * Event emitter constructor. 478 | * 479 | * @api public. 480 | */ 481 | 482 | function EventEmitter () {}; 483 | 484 | /** 485 | * Adds a listener 486 | * 487 | * @api public 488 | */ 489 | 490 | EventEmitter.prototype.on = function (name, fn) { 491 | if (!this.$events) { 492 | this.$events = {}; 493 | } 494 | 495 | if (!this.$events[name]) { 496 | this.$events[name] = fn; 497 | } else if (io.util.isArray(this.$events[name])) { 498 | this.$events[name].push(fn); 499 | } else { 500 | this.$events[name] = [this.$events[name], fn]; 501 | } 502 | 503 | return this; 504 | }; 505 | 506 | EventEmitter.prototype.addListener = EventEmitter.prototype.on; 507 | 508 | /** 509 | * Adds a volatile listener. 510 | * 511 | * @api public 512 | */ 513 | 514 | EventEmitter.prototype.once = function (name, fn) { 515 | var self = this; 516 | 517 | function on () { 518 | self.removeListener(name, on); 519 | fn.apply(this, arguments); 520 | }; 521 | 522 | on.listener = fn; 523 | this.on(name, on); 524 | 525 | return this; 526 | }; 527 | 528 | /** 529 | * Removes a listener. 530 | * 531 | * @api public 532 | */ 533 | 534 | EventEmitter.prototype.removeListener = function (name, fn) { 535 | if (this.$events && this.$events[name]) { 536 | var list = this.$events[name]; 537 | 538 | if (io.util.isArray(list)) { 539 | var pos = -1; 540 | 541 | for (var i = 0, l = list.length; i < l; i++) { 542 | if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { 543 | pos = i; 544 | break; 545 | } 546 | } 547 | 548 | if (pos < 0) { 549 | return this; 550 | } 551 | 552 | list.splice(pos, 1); 553 | 554 | if (!list.length) { 555 | delete this.$events[name]; 556 | } 557 | } else if (list === fn || (list.listener && list.listener === fn)) { 558 | delete this.$events[name]; 559 | } 560 | } 561 | 562 | return this; 563 | }; 564 | 565 | /** 566 | * Removes all listeners for an event. 567 | * 568 | * @api public 569 | */ 570 | 571 | EventEmitter.prototype.removeAllListeners = function (name) { 572 | // TODO: enable this when node 0.5 is stable 573 | //if (name === undefined) { 574 | //this.$events = {}; 575 | //return this; 576 | //} 577 | 578 | if (this.$events && this.$events[name]) { 579 | this.$events[name] = null; 580 | } 581 | 582 | return this; 583 | }; 584 | 585 | /** 586 | * Gets all listeners for a certain event. 587 | * 588 | * @api publci 589 | */ 590 | 591 | EventEmitter.prototype.listeners = function (name) { 592 | if (!this.$events) { 593 | this.$events = {}; 594 | } 595 | 596 | if (!this.$events[name]) { 597 | this.$events[name] = []; 598 | } 599 | 600 | if (!io.util.isArray(this.$events[name])) { 601 | this.$events[name] = [this.$events[name]]; 602 | } 603 | 604 | return this.$events[name]; 605 | }; 606 | 607 | /** 608 | * Emits an event. 609 | * 610 | * @api public 611 | */ 612 | 613 | EventEmitter.prototype.emit = function (name) { 614 | if (!this.$events) { 615 | return false; 616 | } 617 | 618 | var handler = this.$events[name]; 619 | 620 | if (!handler) { 621 | return false; 622 | } 623 | 624 | var args = Array.prototype.slice.call(arguments, 1); 625 | 626 | if ('function' == typeof handler) { 627 | handler.apply(this, args); 628 | } else if (io.util.isArray(handler)) { 629 | var listeners = handler.slice(); 630 | 631 | for (var i = 0, l = listeners.length; i < l; i++) { 632 | listeners[i].apply(this, args); 633 | } 634 | } else { 635 | return false; 636 | } 637 | 638 | return true; 639 | }; 640 | 641 | })( 642 | 'undefined' != typeof io ? io : module.exports 643 | , 'undefined' != typeof io ? io : module.parent.exports 644 | ); 645 | 646 | /** 647 | * socket.io 648 | * Copyright(c) 2011 LearnBoost 649 | * MIT Licensed 650 | */ 651 | 652 | /** 653 | * Based on JSON2 (http://www.JSON.org/js.html). 654 | */ 655 | 656 | (function (exports, nativeJSON) { 657 | "use strict"; 658 | 659 | // use native JSON if it's available 660 | if (nativeJSON && nativeJSON.parse){ 661 | return exports.JSON = { 662 | parse: nativeJSON.parse 663 | , stringify: nativeJSON.stringify 664 | } 665 | } 666 | 667 | var JSON = exports.JSON = {}; 668 | 669 | function f(n) { 670 | // Format integers to have at least two digits. 671 | return n < 10 ? '0' + n : n; 672 | } 673 | 674 | function date(d, key) { 675 | return isFinite(d.valueOf()) ? 676 | d.getUTCFullYear() + '-' + 677 | f(d.getUTCMonth() + 1) + '-' + 678 | f(d.getUTCDate()) + 'T' + 679 | f(d.getUTCHours()) + ':' + 680 | f(d.getUTCMinutes()) + ':' + 681 | f(d.getUTCSeconds()) + 'Z' : null; 682 | }; 683 | 684 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 685 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 686 | gap, 687 | indent, 688 | meta = { // table of character substitutions 689 | '\b': '\\b', 690 | '\t': '\\t', 691 | '\n': '\\n', 692 | '\f': '\\f', 693 | '\r': '\\r', 694 | '"' : '\\"', 695 | '\\': '\\\\' 696 | }, 697 | rep; 698 | 699 | 700 | function quote(string) { 701 | 702 | // If the string contains no control characters, no quote characters, and no 703 | // backslash characters, then we can safely slap some quotes around it. 704 | // Otherwise we must also replace the offending characters with safe escape 705 | // sequences. 706 | 707 | escapable.lastIndex = 0; 708 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) { 709 | var c = meta[a]; 710 | return typeof c === 'string' ? c : 711 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 712 | }) + '"' : '"' + string + '"'; 713 | } 714 | 715 | 716 | function str(key, holder) { 717 | 718 | // Produce a string from holder[key]. 719 | 720 | var i, // The loop counter. 721 | k, // The member key. 722 | v, // The member value. 723 | length, 724 | mind = gap, 725 | partial, 726 | value = holder[key]; 727 | 728 | // If the value has a toJSON method, call it to obtain a replacement value. 729 | 730 | if (value instanceof Date) { 731 | value = date(key); 732 | } 733 | 734 | // If we were called with a replacer function, then call the replacer to 735 | // obtain a replacement value. 736 | 737 | if (typeof rep === 'function') { 738 | value = rep.call(holder, key, value); 739 | } 740 | 741 | // What happens next depends on the value's type. 742 | 743 | switch (typeof value) { 744 | case 'string': 745 | return quote(value); 746 | 747 | case 'number': 748 | 749 | // JSON numbers must be finite. Encode non-finite numbers as null. 750 | 751 | return isFinite(value) ? String(value) : 'null'; 752 | 753 | case 'boolean': 754 | case 'null': 755 | 756 | // If the value is a boolean or null, convert it to a string. Note: 757 | // typeof null does not produce 'null'. The case is included here in 758 | // the remote chance that this gets fixed someday. 759 | 760 | return String(value); 761 | 762 | // If the type is 'object', we might be dealing with an object or an array or 763 | // null. 764 | 765 | case 'object': 766 | 767 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 768 | // so watch out for that case. 769 | 770 | if (!value) { 771 | return 'null'; 772 | } 773 | 774 | // Make an array to hold the partial results of stringifying this object value. 775 | 776 | gap += indent; 777 | partial = []; 778 | 779 | // Is the value an array? 780 | 781 | if (Object.prototype.toString.apply(value) === '[object Array]') { 782 | 783 | // The value is an array. Stringify every element. Use null as a placeholder 784 | // for non-JSON values. 785 | 786 | length = value.length; 787 | for (i = 0; i < length; i += 1) { 788 | partial[i] = str(i, value) || 'null'; 789 | } 790 | 791 | // Join all of the elements together, separated with commas, and wrap them in 792 | // brackets. 793 | 794 | v = partial.length === 0 ? '[]' : gap ? 795 | '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : 796 | '[' + partial.join(',') + ']'; 797 | gap = mind; 798 | return v; 799 | } 800 | 801 | // If the replacer is an array, use it to select the members to be stringified. 802 | 803 | if (rep && typeof rep === 'object') { 804 | length = rep.length; 805 | for (i = 0; i < length; i += 1) { 806 | if (typeof rep[i] === 'string') { 807 | k = rep[i]; 808 | v = str(k, value); 809 | if (v) { 810 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 811 | } 812 | } 813 | } 814 | } else { 815 | 816 | // Otherwise, iterate through all of the keys in the object. 817 | 818 | for (k in value) { 819 | if (Object.prototype.hasOwnProperty.call(value, k)) { 820 | v = str(k, value); 821 | if (v) { 822 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 823 | } 824 | } 825 | } 826 | } 827 | 828 | // Join all of the member texts together, separated with commas, 829 | // and wrap them in braces. 830 | 831 | v = partial.length === 0 ? '{}' : gap ? 832 | '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : 833 | '{' + partial.join(',') + '}'; 834 | gap = mind; 835 | return v; 836 | } 837 | } 838 | 839 | // If the JSON object does not yet have a stringify method, give it one. 840 | 841 | JSON.stringify = function (value, replacer, space) { 842 | 843 | // The stringify method takes a value and an optional replacer, and an optional 844 | // space parameter, and returns a JSON text. The replacer can be a function 845 | // that can replace values, or an array of strings that will select the keys. 846 | // A default replacer method can be provided. Use of the space parameter can 847 | // produce text that is more easily readable. 848 | 849 | var i; 850 | gap = ''; 851 | indent = ''; 852 | 853 | // If the space parameter is a number, make an indent string containing that 854 | // many spaces. 855 | 856 | if (typeof space === 'number') { 857 | for (i = 0; i < space; i += 1) { 858 | indent += ' '; 859 | } 860 | 861 | // If the space parameter is a string, it will be used as the indent string. 862 | 863 | } else if (typeof space === 'string') { 864 | indent = space; 865 | } 866 | 867 | // If there is a replacer, it must be a function or an array. 868 | // Otherwise, throw an error. 869 | 870 | rep = replacer; 871 | if (replacer && typeof replacer !== 'function' && 872 | (typeof replacer !== 'object' || 873 | typeof replacer.length !== 'number')) { 874 | throw new Error('JSON.stringify'); 875 | } 876 | 877 | // Make a fake root object containing our value under the key of ''. 878 | // Return the result of stringifying the value. 879 | 880 | return str('', {'': value}); 881 | }; 882 | 883 | // If the JSON object does not yet have a parse method, give it one. 884 | 885 | JSON.parse = function (text, reviver) { 886 | // The parse method takes a text and an optional reviver function, and returns 887 | // a JavaScript value if the text is a valid JSON text. 888 | 889 | var j; 890 | 891 | function walk(holder, key) { 892 | 893 | // The walk method is used to recursively walk the resulting structure so 894 | // that modifications can be made. 895 | 896 | var k, v, value = holder[key]; 897 | if (value && typeof value === 'object') { 898 | for (k in value) { 899 | if (Object.prototype.hasOwnProperty.call(value, k)) { 900 | v = walk(value, k); 901 | if (v !== undefined) { 902 | value[k] = v; 903 | } else { 904 | delete value[k]; 905 | } 906 | } 907 | } 908 | } 909 | return reviver.call(holder, key, value); 910 | } 911 | 912 | 913 | // Parsing happens in four stages. In the first stage, we replace certain 914 | // Unicode characters with escape sequences. JavaScript handles many characters 915 | // incorrectly, either silently deleting them, or treating them as line endings. 916 | 917 | text = String(text); 918 | cx.lastIndex = 0; 919 | if (cx.test(text)) { 920 | text = text.replace(cx, function (a) { 921 | return '\\u' + 922 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 923 | }); 924 | } 925 | 926 | // In the second stage, we run the text against regular expressions that look 927 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 928 | // because they can cause invocation, and '=' because it can cause mutation. 929 | // But just to be safe, we want to reject all unexpected forms. 930 | 931 | // We split the second stage into 4 regexp operations in order to work around 932 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 933 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 934 | // replace all simple value tokens with ']' characters. Third, we delete all 935 | // open brackets that follow a colon or comma or that begin the text. Finally, 936 | // we look to see that the remaining characters are only whitespace or ']' or 937 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 938 | 939 | if (/^[\],:{}\s]*$/ 940 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 941 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 942 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 943 | 944 | // In the third stage we use the eval function to compile the text into a 945 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 946 | // in JavaScript: it can begin a block or an object literal. We wrap the text 947 | // in parens to eliminate the ambiguity. 948 | 949 | j = eval('(' + text + ')'); 950 | 951 | // In the optional fourth stage, we recursively walk the new structure, passing 952 | // each name/value pair to a reviver function for possible transformation. 953 | 954 | return typeof reviver === 'function' ? 955 | walk({'': j}, '') : j; 956 | } 957 | 958 | // If the text is not JSON parseable, then a SyntaxError is thrown. 959 | 960 | throw new SyntaxError('JSON.parse'); 961 | }; 962 | 963 | })( 964 | 'undefined' != typeof io ? io : module.exports 965 | , typeof JSON !== 'undefined' ? JSON : undefined 966 | ); 967 | 968 | /** 969 | * socket.io 970 | * Copyright(c) 2011 LearnBoost 971 | * MIT Licensed 972 | */ 973 | 974 | (function (exports, io) { 975 | 976 | /** 977 | * Parser namespace. 978 | * 979 | * @namespace 980 | */ 981 | 982 | var parser = exports.parser = {}; 983 | 984 | /** 985 | * Packet types. 986 | */ 987 | 988 | var packets = parser.packets = [ 989 | 'disconnect' 990 | , 'connect' 991 | , 'heartbeat' 992 | , 'message' 993 | , 'json' 994 | , 'event' 995 | , 'ack' 996 | , 'error' 997 | , 'noop' 998 | ]; 999 | 1000 | /** 1001 | * Errors reasons. 1002 | */ 1003 | 1004 | var reasons = parser.reasons = [ 1005 | 'transport not supported' 1006 | , 'client not handshaken' 1007 | , 'unauthorized' 1008 | ]; 1009 | 1010 | /** 1011 | * Errors advice. 1012 | */ 1013 | 1014 | var advice = parser.advice = [ 1015 | 'reconnect' 1016 | ]; 1017 | 1018 | /** 1019 | * Shortcuts. 1020 | */ 1021 | 1022 | var JSON = io.JSON 1023 | , indexOf = io.util.indexOf; 1024 | 1025 | /** 1026 | * Encodes a packet. 1027 | * 1028 | * @api private 1029 | */ 1030 | 1031 | parser.encodePacket = function (packet) { 1032 | var type = indexOf(packets, packet.type) 1033 | , id = packet.id || '' 1034 | , endpoint = packet.endpoint || '' 1035 | , ack = packet.ack 1036 | , data = null; 1037 | 1038 | switch (packet.type) { 1039 | case 'error': 1040 | var reason = packet.reason ? indexOf(reasons, packet.reason) : '' 1041 | , adv = packet.advice ? indexOf(advice, packet.advice) : ''; 1042 | 1043 | if (reason !== '' || adv !== '') 1044 | data = reason + (adv !== '' ? ('+' + adv) : ''); 1045 | 1046 | break; 1047 | 1048 | case 'message': 1049 | if (packet.data !== '') 1050 | data = packet.data; 1051 | break; 1052 | 1053 | case 'event': 1054 | var ev = { name: packet.name }; 1055 | 1056 | if (packet.args && packet.args.length) { 1057 | ev.args = packet.args; 1058 | } 1059 | 1060 | data = JSON.stringify(ev); 1061 | break; 1062 | 1063 | case 'json': 1064 | data = JSON.stringify(packet.data); 1065 | break; 1066 | 1067 | case 'connect': 1068 | if (packet.qs) 1069 | data = packet.qs; 1070 | break; 1071 | 1072 | case 'ack': 1073 | data = packet.ackId 1074 | + (packet.args && packet.args.length 1075 | ? '+' + JSON.stringify(packet.args) : ''); 1076 | break; 1077 | } 1078 | 1079 | // construct packet with required fragments 1080 | var encoded = [ 1081 | type 1082 | , id + (ack == 'data' ? '+' : '') 1083 | , endpoint 1084 | ]; 1085 | 1086 | // data fragment is optional 1087 | if (data !== null && data !== undefined) 1088 | encoded.push(data); 1089 | 1090 | return encoded.join(':'); 1091 | }; 1092 | 1093 | /** 1094 | * Encodes multiple messages (payload). 1095 | * 1096 | * @param {Array} messages 1097 | * @api private 1098 | */ 1099 | 1100 | parser.encodePayload = function (packets) { 1101 | var decoded = ''; 1102 | 1103 | if (packets.length == 1) 1104 | return packets[0]; 1105 | 1106 | for (var i = 0, l = packets.length; i < l; i++) { 1107 | var packet = packets[i]; 1108 | decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]; 1109 | } 1110 | 1111 | return decoded; 1112 | }; 1113 | 1114 | /** 1115 | * Decodes a packet 1116 | * 1117 | * @api private 1118 | */ 1119 | 1120 | var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; 1121 | 1122 | parser.decodePacket = function (data) { 1123 | var pieces = data.match(regexp); 1124 | 1125 | if (!pieces) return {}; 1126 | 1127 | var id = pieces[2] || '' 1128 | , data = pieces[5] || '' 1129 | , packet = { 1130 | type: packets[pieces[1]] 1131 | , endpoint: pieces[4] || '' 1132 | }; 1133 | 1134 | // whether we need to acknowledge the packet 1135 | if (id) { 1136 | packet.id = id; 1137 | if (pieces[3]) 1138 | packet.ack = 'data'; 1139 | else 1140 | packet.ack = true; 1141 | } 1142 | 1143 | // handle different packet types 1144 | switch (packet.type) { 1145 | case 'error': 1146 | var pieces = data.split('+'); 1147 | packet.reason = reasons[pieces[0]] || ''; 1148 | packet.advice = advice[pieces[1]] || ''; 1149 | break; 1150 | 1151 | case 'message': 1152 | packet.data = data || ''; 1153 | break; 1154 | 1155 | case 'event': 1156 | try { 1157 | var opts = JSON.parse(data); 1158 | packet.name = opts.name; 1159 | packet.args = opts.args; 1160 | } catch (e) { } 1161 | 1162 | packet.args = packet.args || []; 1163 | break; 1164 | 1165 | case 'json': 1166 | try { 1167 | packet.data = JSON.parse(data); 1168 | } catch (e) { } 1169 | break; 1170 | 1171 | case 'connect': 1172 | packet.qs = data || ''; 1173 | break; 1174 | 1175 | case 'ack': 1176 | var pieces = data.match(/^([0-9]+)(\+)?(.*)/); 1177 | if (pieces) { 1178 | packet.ackId = pieces[1]; 1179 | packet.args = []; 1180 | 1181 | if (pieces[3]) { 1182 | try { 1183 | packet.args = pieces[3] ? JSON.parse(pieces[3]) : []; 1184 | } catch (e) { } 1185 | } 1186 | } 1187 | break; 1188 | 1189 | case 'disconnect': 1190 | case 'heartbeat': 1191 | break; 1192 | }; 1193 | 1194 | return packet; 1195 | }; 1196 | 1197 | /** 1198 | * Decodes data payload. Detects multiple messages 1199 | * 1200 | * @return {Array} messages 1201 | * @api public 1202 | */ 1203 | 1204 | parser.decodePayload = function (data) { 1205 | // IE doesn't like data[i] for unicode chars, charAt works fine 1206 | if (data.charAt(0) == '\ufffd') { 1207 | var ret = []; 1208 | 1209 | for (var i = 1, length = ''; i < data.length; i++) { 1210 | if (data.charAt(i) == '\ufffd') { 1211 | ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length))); 1212 | i += Number(length) + 1; 1213 | length = ''; 1214 | } else { 1215 | length += data.charAt(i); 1216 | } 1217 | } 1218 | 1219 | return ret; 1220 | } else { 1221 | return [parser.decodePacket(data)]; 1222 | } 1223 | }; 1224 | 1225 | })( 1226 | 'undefined' != typeof io ? io : module.exports 1227 | , 'undefined' != typeof io ? io : module.parent.exports 1228 | ); 1229 | /** 1230 | * socket.io 1231 | * Copyright(c) 2011 LearnBoost 1232 | * MIT Licensed 1233 | */ 1234 | 1235 | (function (exports, io) { 1236 | 1237 | /** 1238 | * Expose constructor. 1239 | */ 1240 | 1241 | exports.Transport = Transport; 1242 | 1243 | /** 1244 | * This is the transport template for all supported transport methods. 1245 | * 1246 | * @constructor 1247 | * @api public 1248 | */ 1249 | 1250 | function Transport (socket, sessid) { 1251 | this.socket = socket; 1252 | this.sessid = sessid; 1253 | }; 1254 | 1255 | /** 1256 | * Apply EventEmitter mixin. 1257 | */ 1258 | 1259 | io.util.mixin(Transport, io.EventEmitter); 1260 | 1261 | /** 1262 | * Handles the response from the server. When a new response is received 1263 | * it will automatically update the timeout, decode the message and 1264 | * forwards the response to the onMessage function for further processing. 1265 | * 1266 | * @param {String} data Response from the server. 1267 | * @api private 1268 | */ 1269 | 1270 | Transport.prototype.onData = function (data) { 1271 | this.clearCloseTimeout(); 1272 | 1273 | // If the connection in currently open (or in a reopening state) reset the close 1274 | // timeout since we have just received data. This check is necessary so 1275 | // that we don't reset the timeout on an explicitly disconnected connection. 1276 | if (this.connected || this.connecting || this.reconnecting) { 1277 | this.setCloseTimeout(); 1278 | } 1279 | 1280 | if (data !== '') { 1281 | // todo: we should only do decodePayload for xhr transports 1282 | var msgs = io.parser.decodePayload(data); 1283 | 1284 | if (msgs && msgs.length) { 1285 | for (var i = 0, l = msgs.length; i < l; i++) { 1286 | this.onPacket(msgs[i]); 1287 | } 1288 | } 1289 | } 1290 | 1291 | return this; 1292 | }; 1293 | 1294 | /** 1295 | * Handles packets. 1296 | * 1297 | * @api private 1298 | */ 1299 | 1300 | Transport.prototype.onPacket = function (packet) { 1301 | if (packet.type == 'heartbeat') { 1302 | return this.onHeartbeat(); 1303 | } 1304 | 1305 | if (packet.type == 'connect' && packet.endpoint == '') { 1306 | this.onConnect(); 1307 | } 1308 | 1309 | this.socket.onPacket(packet); 1310 | 1311 | return this; 1312 | }; 1313 | 1314 | /** 1315 | * Sets close timeout 1316 | * 1317 | * @api private 1318 | */ 1319 | 1320 | Transport.prototype.setCloseTimeout = function () { 1321 | if (!this.closeTimeout) { 1322 | var self = this; 1323 | 1324 | this.closeTimeout = setTimeout(function () { 1325 | self.onDisconnect(); 1326 | }, this.socket.closeTimeout); 1327 | } 1328 | }; 1329 | 1330 | /** 1331 | * Called when transport disconnects. 1332 | * 1333 | * @api private 1334 | */ 1335 | 1336 | Transport.prototype.onDisconnect = function () { 1337 | if (this.close && this.open) this.close(); 1338 | this.clearTimeouts(); 1339 | this.socket.onDisconnect(); 1340 | return this; 1341 | }; 1342 | 1343 | /** 1344 | * Called when transport connects 1345 | * 1346 | * @api private 1347 | */ 1348 | 1349 | Transport.prototype.onConnect = function () { 1350 | this.socket.onConnect(); 1351 | return this; 1352 | } 1353 | 1354 | /** 1355 | * Clears close timeout 1356 | * 1357 | * @api private 1358 | */ 1359 | 1360 | Transport.prototype.clearCloseTimeout = function () { 1361 | if (this.closeTimeout) { 1362 | clearTimeout(this.closeTimeout); 1363 | this.closeTimeout = null; 1364 | } 1365 | }; 1366 | 1367 | /** 1368 | * Clear timeouts 1369 | * 1370 | * @api private 1371 | */ 1372 | 1373 | Transport.prototype.clearTimeouts = function () { 1374 | this.clearCloseTimeout(); 1375 | 1376 | if (this.reopenTimeout) { 1377 | clearTimeout(this.reopenTimeout); 1378 | } 1379 | }; 1380 | 1381 | /** 1382 | * Sends a packet 1383 | * 1384 | * @param {Object} packet object. 1385 | * @api private 1386 | */ 1387 | 1388 | Transport.prototype.packet = function (packet) { 1389 | this.send(io.parser.encodePacket(packet)); 1390 | }; 1391 | 1392 | /** 1393 | * Send the received heartbeat message back to server. So the server 1394 | * knows we are still connected. 1395 | * 1396 | * @param {String} heartbeat Heartbeat response from the server. 1397 | * @api private 1398 | */ 1399 | 1400 | Transport.prototype.onHeartbeat = function (heartbeat) { 1401 | this.packet({ type: 'heartbeat' }); 1402 | }; 1403 | 1404 | /** 1405 | * Called when the transport opens. 1406 | * 1407 | * @api private 1408 | */ 1409 | 1410 | Transport.prototype.onOpen = function () { 1411 | this.open = true; 1412 | this.clearCloseTimeout(); 1413 | this.socket.onOpen(); 1414 | }; 1415 | 1416 | /** 1417 | * Notifies the base when the connection with the Socket.IO server 1418 | * has been disconnected. 1419 | * 1420 | * @api private 1421 | */ 1422 | 1423 | Transport.prototype.onClose = function () { 1424 | var self = this; 1425 | 1426 | /* FIXME: reopen delay causing a infinit loop 1427 | this.reopenTimeout = setTimeout(function () { 1428 | self.open(); 1429 | }, this.socket.options['reopen delay']);*/ 1430 | 1431 | this.open = false; 1432 | this.socket.onClose(); 1433 | this.onDisconnect(); 1434 | }; 1435 | 1436 | /** 1437 | * Generates a connection url based on the Socket.IO URL Protocol. 1438 | * See for more details. 1439 | * 1440 | * @returns {String} Connection url 1441 | * @api private 1442 | */ 1443 | 1444 | Transport.prototype.prepareUrl = function () { 1445 | var options = this.socket.options; 1446 | 1447 | return this.scheme() + '://' 1448 | + options.host + ':' + options.port + '/' 1449 | + options.resource + '/' + io.protocol 1450 | + '/' + this.name + '/' + this.sessid; 1451 | }; 1452 | 1453 | /** 1454 | * Checks if the transport is ready to start a connection. 1455 | * 1456 | * @param {Socket} socket The socket instance that needs a transport 1457 | * @param {Function} fn The callback 1458 | * @api private 1459 | */ 1460 | 1461 | Transport.prototype.ready = function (socket, fn) { 1462 | fn.call(this); 1463 | }; 1464 | })( 1465 | 'undefined' != typeof io ? io : module.exports 1466 | , 'undefined' != typeof io ? io : module.parent.exports 1467 | ); 1468 | 1469 | /** 1470 | * socket.io 1471 | * Copyright(c) 2011 LearnBoost 1472 | * MIT Licensed 1473 | */ 1474 | 1475 | (function (exports, io, global) { 1476 | 1477 | /** 1478 | * Expose constructor. 1479 | */ 1480 | 1481 | exports.Socket = Socket; 1482 | 1483 | /** 1484 | * Create a new `Socket.IO client` which can establish a persistent 1485 | * connection with a Socket.IO enabled server. 1486 | * 1487 | * @api public 1488 | */ 1489 | 1490 | function Socket (options) { 1491 | this.options = { 1492 | port: 80 1493 | , secure: false 1494 | , document: 'document' in global ? document : false 1495 | , resource: 'socket.io' 1496 | , transports: io.transports 1497 | , 'connect timeout': 10000 1498 | , 'try multiple transports': true 1499 | , 'reconnect': true 1500 | , 'reconnection delay': 500 1501 | , 'reconnection limit': Infinity 1502 | , 'reopen delay': 3000 1503 | , 'max reconnection attempts': 10 1504 | , 'sync disconnect on unload': true 1505 | , 'auto connect': true 1506 | , 'flash policy port': 10843 1507 | }; 1508 | 1509 | io.util.merge(this.options, options); 1510 | 1511 | this.connected = false; 1512 | this.open = false; 1513 | this.connecting = false; 1514 | this.reconnecting = false; 1515 | this.namespaces = {}; 1516 | this.buffer = []; 1517 | this.doBuffer = false; 1518 | 1519 | if (this.options['sync disconnect on unload'] && 1520 | (!this.isXDomain() || io.util.ua.hasCORS)) { 1521 | var self = this; 1522 | 1523 | io.util.on(global, 'beforeunload', function () { 1524 | self.disconnectSync(); 1525 | }, false); 1526 | } 1527 | 1528 | if (this.options['auto connect']) { 1529 | this.connect(); 1530 | } 1531 | }; 1532 | 1533 | /** 1534 | * Apply EventEmitter mixin. 1535 | */ 1536 | 1537 | io.util.mixin(Socket, io.EventEmitter); 1538 | 1539 | /** 1540 | * Returns a namespace listener/emitter for this socket 1541 | * 1542 | * @api public 1543 | */ 1544 | 1545 | Socket.prototype.of = function (name) { 1546 | if (!this.namespaces[name]) { 1547 | this.namespaces[name] = new io.SocketNamespace(this, name); 1548 | 1549 | if (name !== '') { 1550 | this.namespaces[name].packet({ type: 'connect' }); 1551 | } 1552 | } 1553 | 1554 | return this.namespaces[name]; 1555 | }; 1556 | 1557 | /** 1558 | * Emits the given event to the Socket and all namespaces 1559 | * 1560 | * @api private 1561 | */ 1562 | 1563 | Socket.prototype.publish = function () { 1564 | this.emit.apply(this, arguments); 1565 | 1566 | var nsp; 1567 | 1568 | for (var i in this.namespaces) { 1569 | if (this.namespaces.hasOwnProperty(i)) { 1570 | nsp = this.of(i); 1571 | nsp.$emit.apply(nsp, arguments); 1572 | } 1573 | } 1574 | }; 1575 | 1576 | /** 1577 | * Performs the handshake 1578 | * 1579 | * @api private 1580 | */ 1581 | 1582 | function empty () { }; 1583 | 1584 | Socket.prototype.handshake = function (fn) { 1585 | var self = this 1586 | , options = this.options; 1587 | 1588 | function complete (data) { 1589 | if (data instanceof Error) { 1590 | self.onError(data.message); 1591 | } else { 1592 | fn.apply(null, data.split(':')); 1593 | } 1594 | }; 1595 | 1596 | var url = [ 1597 | 'http' + (options.secure ? 's' : '') + ':/' 1598 | , options.host + ':' + options.port 1599 | , options.resource 1600 | , io.protocol 1601 | , io.util.query(this.options.query, 't=' + +new Date) 1602 | ].join('/'); 1603 | 1604 | if (this.isXDomain() && !io.util.ua.hasCORS) { 1605 | var insertAt = document.getElementsByTagName('script')[0] 1606 | , script = document.createElement('script'); 1607 | 1608 | script.src = url + '&jsonp=' + io.j.length; 1609 | insertAt.parentNode.insertBefore(script, insertAt); 1610 | 1611 | io.j.push(function (data) { 1612 | complete(data); 1613 | script.parentNode.removeChild(script); 1614 | }); 1615 | } else { 1616 | var xhr = io.util.request(); 1617 | 1618 | xhr.open('GET', url, true); 1619 | xhr.onreadystatechange = function () { 1620 | if (xhr.readyState == 4) { 1621 | xhr.onreadystatechange = empty; 1622 | 1623 | if (xhr.status == 200) { 1624 | complete(xhr.responseText); 1625 | } else { 1626 | !self.reconnecting && self.onError(xhr.responseText); 1627 | } 1628 | } 1629 | }; 1630 | xhr.send(null); 1631 | } 1632 | }; 1633 | 1634 | /** 1635 | * Find an available transport based on the options supplied in the constructor. 1636 | * 1637 | * @api private 1638 | */ 1639 | 1640 | Socket.prototype.getTransport = function (override) { 1641 | var transports = override || this.transports, match; 1642 | 1643 | for (var i = 0, transport; transport = transports[i]; i++) { 1644 | if (io.Transport[transport] 1645 | && io.Transport[transport].check(this) 1646 | && (!this.isXDomain() || io.Transport[transport].xdomainCheck())) { 1647 | return new io.Transport[transport](this, this.sessionid); 1648 | } 1649 | } 1650 | 1651 | return null; 1652 | }; 1653 | 1654 | /** 1655 | * Connects to the server. 1656 | * 1657 | * @param {Function} [fn] Callback. 1658 | * @returns {io.Socket} 1659 | * @api public 1660 | */ 1661 | 1662 | Socket.prototype.connect = function (fn) { 1663 | if (this.connecting) { 1664 | return this; 1665 | } 1666 | 1667 | var self = this; 1668 | 1669 | this.handshake(function (sid, heartbeat, close, transports) { 1670 | self.sessionid = sid; 1671 | self.closeTimeout = close * 1000; 1672 | self.heartbeatTimeout = heartbeat * 1000; 1673 | self.transports = io.util.intersect( 1674 | transports.split(',') 1675 | , self.options.transports 1676 | ); 1677 | 1678 | function connect (transports){ 1679 | if (self.transport) self.transport.clearTimeouts(); 1680 | 1681 | self.transport = self.getTransport(transports); 1682 | if (!self.transport) return self.publish('connect_failed'); 1683 | 1684 | // once the transport is ready 1685 | self.transport.ready(self, function () { 1686 | self.connecting = true; 1687 | self.publish('connecting', self.transport.name); 1688 | self.transport.open(); 1689 | 1690 | if (self.options['connect timeout']) { 1691 | self.connectTimeoutTimer = setTimeout(function () { 1692 | if (!self.connected) { 1693 | self.connecting = false; 1694 | 1695 | if (self.options['try multiple transports']) { 1696 | if (!self.remainingTransports) { 1697 | self.remainingTransports = self.transports.slice(0); 1698 | } 1699 | 1700 | var remaining = self.remainingTransports; 1701 | 1702 | while (remaining.length > 0 && remaining.splice(0,1)[0] != 1703 | self.transport.name) {} 1704 | 1705 | if (remaining.length){ 1706 | connect(remaining); 1707 | } else { 1708 | self.publish('connect_failed'); 1709 | } 1710 | } 1711 | } 1712 | }, self.options['connect timeout']); 1713 | } 1714 | }); 1715 | } 1716 | 1717 | connect(); 1718 | 1719 | self.once('connect', function (){ 1720 | clearTimeout(self.connectTimeoutTimer); 1721 | 1722 | fn && typeof fn == 'function' && fn(); 1723 | }); 1724 | }); 1725 | 1726 | return this; 1727 | }; 1728 | 1729 | /** 1730 | * Sends a message. 1731 | * 1732 | * @param {Object} data packet. 1733 | * @returns {io.Socket} 1734 | * @api public 1735 | */ 1736 | 1737 | Socket.prototype.packet = function (data) { 1738 | if (this.connected && !this.doBuffer) { 1739 | this.transport.packet(data); 1740 | } else { 1741 | this.buffer.push(data); 1742 | } 1743 | 1744 | return this; 1745 | }; 1746 | 1747 | /** 1748 | * Sets buffer state 1749 | * 1750 | * @api private 1751 | */ 1752 | 1753 | Socket.prototype.setBuffer = function (v) { 1754 | this.doBuffer = v; 1755 | 1756 | if (!v && this.connected && this.buffer.length) { 1757 | this.transport.payload(this.buffer); 1758 | this.buffer = []; 1759 | } 1760 | }; 1761 | 1762 | /** 1763 | * Disconnect the established connect. 1764 | * 1765 | * @returns {io.Socket} 1766 | * @api public 1767 | */ 1768 | 1769 | Socket.prototype.disconnect = function () { 1770 | if (this.connected) { 1771 | if (this.open) { 1772 | this.of('').packet({ type: 'disconnect' }); 1773 | } 1774 | 1775 | // handle disconnection immediately 1776 | this.onDisconnect('booted'); 1777 | } 1778 | 1779 | return this; 1780 | }; 1781 | 1782 | /** 1783 | * Disconnects the socket with a sync XHR. 1784 | * 1785 | * @api private 1786 | */ 1787 | 1788 | Socket.prototype.disconnectSync = function () { 1789 | // ensure disconnection 1790 | var xhr = io.util.request() 1791 | , uri = this.resource + '/' + io.protocol + '/' + this.sessionid; 1792 | 1793 | xhr.open('GET', uri, true); 1794 | 1795 | // handle disconnection immediately 1796 | this.onDisconnect('booted'); 1797 | }; 1798 | 1799 | /** 1800 | * Check if we need to use cross domain enabled transports. Cross domain would 1801 | * be a different port or different domain name. 1802 | * 1803 | * @returns {Boolean} 1804 | * @api private 1805 | */ 1806 | 1807 | Socket.prototype.isXDomain = function () { 1808 | 1809 | var port = global.location.port || 1810 | ('https:' == global.location.protocol ? 443 : 80); 1811 | 1812 | return this.options.host !== global.location.hostname 1813 | || this.options.port != port; 1814 | }; 1815 | 1816 | /** 1817 | * Called upon handshake. 1818 | * 1819 | * @api private 1820 | */ 1821 | 1822 | Socket.prototype.onConnect = function () { 1823 | if (!this.connected) { 1824 | this.connected = true; 1825 | this.connecting = false; 1826 | if (!this.doBuffer) { 1827 | // make sure to flush the buffer 1828 | this.setBuffer(false); 1829 | } 1830 | this.emit('connect'); 1831 | } 1832 | }; 1833 | 1834 | /** 1835 | * Called when the transport opens 1836 | * 1837 | * @api private 1838 | */ 1839 | 1840 | Socket.prototype.onOpen = function () { 1841 | this.open = true; 1842 | }; 1843 | 1844 | /** 1845 | * Called when the transport closes. 1846 | * 1847 | * @api private 1848 | */ 1849 | 1850 | Socket.prototype.onClose = function () { 1851 | this.open = false; 1852 | }; 1853 | 1854 | /** 1855 | * Called when the transport first opens a connection 1856 | * 1857 | * @param text 1858 | */ 1859 | 1860 | Socket.prototype.onPacket = function (packet) { 1861 | this.of(packet.endpoint).onPacket(packet); 1862 | }; 1863 | 1864 | /** 1865 | * Handles an error. 1866 | * 1867 | * @api private 1868 | */ 1869 | 1870 | Socket.prototype.onError = function (err) { 1871 | if (err && err.advice) { 1872 | if (err.advice === 'reconnect' && this.connected) { 1873 | this.disconnect(); 1874 | this.reconnect(); 1875 | } 1876 | } 1877 | 1878 | this.publish('error', err && err.reason ? err.reason : err); 1879 | }; 1880 | 1881 | /** 1882 | * Called when the transport disconnects. 1883 | * 1884 | * @api private 1885 | */ 1886 | 1887 | Socket.prototype.onDisconnect = function (reason) { 1888 | var wasConnected = this.connected; 1889 | 1890 | this.connected = false; 1891 | this.connecting = false; 1892 | this.open = false; 1893 | 1894 | if (wasConnected) { 1895 | this.transport.close(); 1896 | this.transport.clearTimeouts(); 1897 | this.publish('disconnect', reason); 1898 | 1899 | if ('booted' != reason && this.options.reconnect && !this.reconnecting) { 1900 | this.reconnect(); 1901 | } 1902 | } 1903 | }; 1904 | 1905 | /** 1906 | * Called upon reconnection. 1907 | * 1908 | * @api private 1909 | */ 1910 | 1911 | Socket.prototype.reconnect = function () { 1912 | this.reconnecting = true; 1913 | this.reconnectionAttempts = 0; 1914 | this.reconnectionDelay = this.options['reconnection delay']; 1915 | 1916 | var self = this 1917 | , maxAttempts = this.options['max reconnection attempts'] 1918 | , tryMultiple = this.options['try multiple transports'] 1919 | , limit = this.options['reconnection limit']; 1920 | 1921 | function reset () { 1922 | if (self.connected) { 1923 | for (var i in self.namespaces) { 1924 | if (self.namespaces.hasOwnProperty(i) && '' !== i) { 1925 | self.namespaces[i].packet({ type: 'connect' }); 1926 | } 1927 | } 1928 | self.publish('reconnect', self.transport.name, self.reconnectionAttempts); 1929 | } 1930 | 1931 | self.removeListener('connect_failed', maybeReconnect); 1932 | self.removeListener('connect', maybeReconnect); 1933 | 1934 | self.reconnecting = false; 1935 | 1936 | delete self.reconnectionAttempts; 1937 | delete self.reconnectionDelay; 1938 | delete self.reconnectionTimer; 1939 | delete self.redoTransports; 1940 | 1941 | self.options['try multiple transports'] = tryMultiple; 1942 | }; 1943 | 1944 | function maybeReconnect () { 1945 | if (!self.reconnecting) { 1946 | return; 1947 | } 1948 | 1949 | if (self.connected) { 1950 | return reset(); 1951 | }; 1952 | 1953 | if (self.connecting && self.reconnecting) { 1954 | return self.reconnectionTimer = setTimeout(maybeReconnect, 1000); 1955 | } 1956 | 1957 | if (self.reconnectionAttempts++ >= maxAttempts) { 1958 | if (!self.redoTransports) { 1959 | self.on('connect_failed', maybeReconnect); 1960 | self.options['try multiple transports'] = true; 1961 | self.transport = self.getTransport(); 1962 | self.redoTransports = true; 1963 | self.connect(); 1964 | } else { 1965 | self.publish('reconnect_failed'); 1966 | reset(); 1967 | } 1968 | } else { 1969 | if (self.reconnectionDelay < limit) { 1970 | self.reconnectionDelay *= 2; // exponential back off 1971 | } 1972 | 1973 | self.connect(); 1974 | self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts); 1975 | self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay); 1976 | } 1977 | }; 1978 | 1979 | this.options['try multiple transports'] = false; 1980 | this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay); 1981 | 1982 | this.on('connect', maybeReconnect); 1983 | }; 1984 | 1985 | })( 1986 | 'undefined' != typeof io ? io : module.exports 1987 | , 'undefined' != typeof io ? io : module.parent.exports 1988 | , this 1989 | ); 1990 | /** 1991 | * socket.io 1992 | * Copyright(c) 2011 LearnBoost 1993 | * MIT Licensed 1994 | */ 1995 | 1996 | (function (exports, io) { 1997 | 1998 | /** 1999 | * Expose constructor. 2000 | */ 2001 | 2002 | exports.SocketNamespace = SocketNamespace; 2003 | 2004 | /** 2005 | * Socket namespace constructor. 2006 | * 2007 | * @constructor 2008 | * @api public 2009 | */ 2010 | 2011 | function SocketNamespace (socket, name) { 2012 | this.socket = socket; 2013 | this.name = name || ''; 2014 | this.flags = {}; 2015 | this.json = new Flag(this, 'json'); 2016 | this.ackPackets = 0; 2017 | this.acks = {}; 2018 | }; 2019 | 2020 | /** 2021 | * Apply EventEmitter mixin. 2022 | */ 2023 | 2024 | io.util.mixin(SocketNamespace, io.EventEmitter); 2025 | 2026 | /** 2027 | * Copies emit since we override it 2028 | * 2029 | * @api private 2030 | */ 2031 | 2032 | SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit; 2033 | 2034 | /** 2035 | * Creates a new namespace, by proxying the request to the socket. This 2036 | * allows us to use the synax as we do on the server. 2037 | * 2038 | * @api public 2039 | */ 2040 | 2041 | SocketNamespace.prototype.of = function () { 2042 | return this.socket.of.apply(this.socket, arguments); 2043 | }; 2044 | 2045 | /** 2046 | * Sends a packet. 2047 | * 2048 | * @api private 2049 | */ 2050 | 2051 | SocketNamespace.prototype.packet = function (packet) { 2052 | packet.endpoint = this.name; 2053 | this.socket.packet(packet); 2054 | this.flags = {}; 2055 | return this; 2056 | }; 2057 | 2058 | /** 2059 | * Sends a message 2060 | * 2061 | * @api public 2062 | */ 2063 | 2064 | SocketNamespace.prototype.send = function (data, fn) { 2065 | var packet = { 2066 | type: this.flags.json ? 'json' : 'message' 2067 | , data: data 2068 | }; 2069 | 2070 | if ('function' == typeof fn) { 2071 | packet.id = ++this.ackPackets; 2072 | packet.ack = true; 2073 | this.acks[packet.id] = fn; 2074 | } 2075 | 2076 | return this.packet(packet); 2077 | }; 2078 | 2079 | /** 2080 | * Emits an event 2081 | * 2082 | * @api public 2083 | */ 2084 | 2085 | SocketNamespace.prototype.emit = function (name) { 2086 | var args = Array.prototype.slice.call(arguments, 1) 2087 | , lastArg = args[args.length - 1] 2088 | , packet = { 2089 | type: 'event' 2090 | , name: name 2091 | }; 2092 | 2093 | if ('function' == typeof lastArg) { 2094 | packet.id = ++this.ackPackets; 2095 | packet.ack = 'data'; 2096 | this.acks[packet.id] = lastArg; 2097 | args = args.slice(0, args.length - 1); 2098 | } 2099 | 2100 | packet.args = args; 2101 | 2102 | return this.packet(packet); 2103 | }; 2104 | 2105 | /** 2106 | * Disconnects the namespace 2107 | * 2108 | * @api private 2109 | */ 2110 | 2111 | SocketNamespace.prototype.disconnect = function () { 2112 | if (this.name === '') { 2113 | this.socket.disconnect(); 2114 | } else { 2115 | this.packet({ type: 'disconnect' }); 2116 | this.$emit('disconnect'); 2117 | } 2118 | 2119 | return this; 2120 | }; 2121 | 2122 | /** 2123 | * Handles a packet 2124 | * 2125 | * @api private 2126 | */ 2127 | 2128 | SocketNamespace.prototype.onPacket = function (packet) { 2129 | var self = this; 2130 | 2131 | function ack () { 2132 | self.packet({ 2133 | type: 'ack' 2134 | , args: io.util.toArray(arguments) 2135 | , ackId: packet.id 2136 | }); 2137 | }; 2138 | 2139 | switch (packet.type) { 2140 | case 'connect': 2141 | this.$emit('connect'); 2142 | break; 2143 | 2144 | case 'disconnect': 2145 | if (this.name === '') { 2146 | this.socket.onDisconnect(packet.reason || 'booted'); 2147 | } else { 2148 | this.$emit('disconnect', packet.reason); 2149 | } 2150 | break; 2151 | 2152 | case 'message': 2153 | case 'json': 2154 | var params = ['message', packet.data]; 2155 | 2156 | if (packet.ack == 'data') { 2157 | params.push(ack); 2158 | } else if (packet.ack) { 2159 | this.packet({ type: 'ack', ackId: packet.id }); 2160 | } 2161 | 2162 | this.$emit.apply(this, params); 2163 | break; 2164 | 2165 | case 'event': 2166 | var params = [packet.name].concat(packet.args); 2167 | 2168 | if (packet.ack == 'data') 2169 | params.push(ack); 2170 | 2171 | this.$emit.apply(this, params); 2172 | break; 2173 | 2174 | case 'ack': 2175 | if (this.acks[packet.ackId]) { 2176 | this.acks[packet.ackId].apply(this, packet.args); 2177 | delete this.acks[packet.ackId]; 2178 | } 2179 | break; 2180 | 2181 | case 'error': 2182 | if (packet.advice){ 2183 | this.socket.onError(packet); 2184 | } else { 2185 | if (packet.reason == 'unauthorized') { 2186 | this.$emit('connect_failed', packet.reason); 2187 | } else { 2188 | this.$emit('error', packet.reason); 2189 | } 2190 | } 2191 | break; 2192 | } 2193 | }; 2194 | 2195 | /** 2196 | * Flag interface. 2197 | * 2198 | * @api private 2199 | */ 2200 | 2201 | function Flag (nsp, name) { 2202 | this.namespace = nsp; 2203 | this.name = name; 2204 | }; 2205 | 2206 | /** 2207 | * Send a message 2208 | * 2209 | * @api public 2210 | */ 2211 | 2212 | Flag.prototype.send = function () { 2213 | this.namespace.flags[this.name] = true; 2214 | this.namespace.send.apply(this.namespace, arguments); 2215 | }; 2216 | 2217 | /** 2218 | * Emit an event 2219 | * 2220 | * @api public 2221 | */ 2222 | 2223 | Flag.prototype.emit = function () { 2224 | this.namespace.flags[this.name] = true; 2225 | this.namespace.emit.apply(this.namespace, arguments); 2226 | }; 2227 | 2228 | })( 2229 | 'undefined' != typeof io ? io : module.exports 2230 | , 'undefined' != typeof io ? io : module.parent.exports 2231 | ); 2232 | 2233 | /** 2234 | * socket.io 2235 | * Copyright(c) 2011 LearnBoost 2236 | * MIT Licensed 2237 | */ 2238 | 2239 | (function (exports, io, global) { 2240 | 2241 | /** 2242 | * Expose constructor. 2243 | */ 2244 | 2245 | exports.websocket = WS; 2246 | 2247 | /** 2248 | * The WebSocket transport uses the HTML5 WebSocket API to establish an 2249 | * persistent connection with the Socket.IO server. This transport will also 2250 | * be inherited by the FlashSocket fallback as it provides a API compatible 2251 | * polyfill for the WebSockets. 2252 | * 2253 | * @constructor 2254 | * @extends {io.Transport} 2255 | * @api public 2256 | */ 2257 | 2258 | function WS (socket) { 2259 | io.Transport.apply(this, arguments); 2260 | }; 2261 | 2262 | /** 2263 | * Inherits from Transport. 2264 | */ 2265 | 2266 | io.util.inherit(WS, io.Transport); 2267 | 2268 | /** 2269 | * Transport name 2270 | * 2271 | * @api public 2272 | */ 2273 | 2274 | WS.prototype.name = 'websocket'; 2275 | 2276 | /** 2277 | * Initializes a new `WebSocket` connection with the Socket.IO server. We attach 2278 | * all the appropriate listeners to handle the responses from the server. 2279 | * 2280 | * @returns {Transport} 2281 | * @api public 2282 | */ 2283 | 2284 | WS.prototype.open = function () { 2285 | var query = io.util.query(this.socket.options.query) 2286 | , self = this 2287 | , Socket 2288 | 2289 | 2290 | if (!Socket) { 2291 | Socket = global.MozWebSocket || global.WebSocket; 2292 | } 2293 | 2294 | this.websocket = new Socket(this.prepareUrl() + query); 2295 | 2296 | this.websocket.onopen = function () { 2297 | self.onOpen(); 2298 | self.socket.setBuffer(false); 2299 | }; 2300 | this.websocket.onmessage = function (ev) { 2301 | self.onData(ev.data); 2302 | }; 2303 | this.websocket.onclose = function () { 2304 | self.onClose(); 2305 | self.socket.setBuffer(true); 2306 | }; 2307 | this.websocket.onerror = function (e) { 2308 | self.onError(e); 2309 | }; 2310 | 2311 | return this; 2312 | }; 2313 | 2314 | /** 2315 | * Send a message to the Socket.IO server. The message will automatically be 2316 | * encoded in the correct message format. 2317 | * 2318 | * @returns {Transport} 2319 | * @api public 2320 | */ 2321 | 2322 | WS.prototype.send = function (data) { 2323 | this.websocket.send(data); 2324 | return this; 2325 | }; 2326 | 2327 | /** 2328 | * Payload 2329 | * 2330 | * @api private 2331 | */ 2332 | 2333 | WS.prototype.payload = function (arr) { 2334 | for (var i = 0, l = arr.length; i < l; i++) { 2335 | this.packet(arr[i]); 2336 | } 2337 | return this; 2338 | }; 2339 | 2340 | /** 2341 | * Disconnect the established `WebSocket` connection. 2342 | * 2343 | * @returns {Transport} 2344 | * @api public 2345 | */ 2346 | 2347 | WS.prototype.close = function () { 2348 | this.websocket.close(); 2349 | return this; 2350 | }; 2351 | 2352 | /** 2353 | * Handle the errors that `WebSocket` might be giving when we 2354 | * are attempting to connect or send messages. 2355 | * 2356 | * @param {Error} e The error. 2357 | * @api private 2358 | */ 2359 | 2360 | WS.prototype.onError = function (e) { 2361 | this.socket.onError(e); 2362 | }; 2363 | 2364 | /** 2365 | * Returns the appropriate scheme for the URI generation. 2366 | * 2367 | * @api private 2368 | */ 2369 | WS.prototype.scheme = function () { 2370 | return this.socket.options.secure ? 'wss' : 'ws'; 2371 | }; 2372 | 2373 | /** 2374 | * Checks if the browser has support for native `WebSockets` and that 2375 | * it's not the polyfill created for the FlashSocket transport. 2376 | * 2377 | * @return {Boolean} 2378 | * @api public 2379 | */ 2380 | 2381 | WS.check = function () { 2382 | return ('WebSocket' in global && !('__addTask' in WebSocket)) 2383 | || 'MozWebSocket' in global; 2384 | }; 2385 | 2386 | /** 2387 | * Check if the `WebSocket` transport support cross domain communications. 2388 | * 2389 | * @returns {Boolean} 2390 | * @api public 2391 | */ 2392 | 2393 | WS.xdomainCheck = function () { 2394 | return true; 2395 | }; 2396 | 2397 | /** 2398 | * Add the transport to your public io.transports array. 2399 | * 2400 | * @api private 2401 | */ 2402 | 2403 | io.transports.push('websocket'); 2404 | 2405 | })( 2406 | 'undefined' != typeof io ? io.Transport : module.exports 2407 | , 'undefined' != typeof io ? io : module.parent.exports 2408 | , this 2409 | ); 2410 | 2411 | /** 2412 | * socket.io 2413 | * Copyright(c) 2011 LearnBoost 2414 | * MIT Licensed 2415 | */ 2416 | 2417 | (function (exports, io, global) { 2418 | 2419 | /** 2420 | * Expose constructor. 2421 | * 2422 | * @api public 2423 | */ 2424 | 2425 | exports.XHR = XHR; 2426 | 2427 | /** 2428 | * XHR constructor 2429 | * 2430 | * @costructor 2431 | * @api public 2432 | */ 2433 | 2434 | function XHR (socket) { 2435 | if (!socket) return; 2436 | 2437 | io.Transport.apply(this, arguments); 2438 | this.sendBuffer = []; 2439 | }; 2440 | 2441 | /** 2442 | * Inherits from Transport. 2443 | */ 2444 | 2445 | io.util.inherit(XHR, io.Transport); 2446 | 2447 | /** 2448 | * Establish a connection 2449 | * 2450 | * @returns {Transport} 2451 | * @api public 2452 | */ 2453 | 2454 | XHR.prototype.open = function () { 2455 | this.socket.setBuffer(false); 2456 | this.onOpen(); 2457 | this.get(); 2458 | 2459 | // we need to make sure the request succeeds since we have no indication 2460 | // whether the request opened or not until it succeeded. 2461 | this.setCloseTimeout(); 2462 | 2463 | return this; 2464 | }; 2465 | 2466 | /** 2467 | * Check if we need to send data to the Socket.IO server, if we have data in our 2468 | * buffer we encode it and forward it to the `post` method. 2469 | * 2470 | * @api private 2471 | */ 2472 | 2473 | XHR.prototype.payload = function (payload) { 2474 | var msgs = []; 2475 | 2476 | for (var i = 0, l = payload.length; i < l; i++) { 2477 | msgs.push(io.parser.encodePacket(payload[i])); 2478 | } 2479 | 2480 | this.send(io.parser.encodePayload(msgs)); 2481 | }; 2482 | 2483 | /** 2484 | * Send data to the Socket.IO server. 2485 | * 2486 | * @param data The message 2487 | * @returns {Transport} 2488 | * @api public 2489 | */ 2490 | 2491 | XHR.prototype.send = function (data) { 2492 | this.post(data); 2493 | return this; 2494 | }; 2495 | 2496 | /** 2497 | * Posts a encoded message to the Socket.IO server. 2498 | * 2499 | * @param {String} data A encoded message. 2500 | * @api private 2501 | */ 2502 | 2503 | function empty () { }; 2504 | 2505 | XHR.prototype.post = function (data) { 2506 | var self = this; 2507 | this.socket.setBuffer(true); 2508 | 2509 | function stateChange () { 2510 | if (this.readyState == 4) { 2511 | this.onreadystatechange = empty; 2512 | self.posting = false; 2513 | 2514 | if (this.status == 200){ 2515 | self.socket.setBuffer(false); 2516 | } else { 2517 | self.onClose(); 2518 | } 2519 | } 2520 | } 2521 | 2522 | function onload () { 2523 | this.onload = empty; 2524 | self.socket.setBuffer(false); 2525 | }; 2526 | 2527 | this.sendXHR = this.request('POST'); 2528 | 2529 | if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) { 2530 | this.sendXHR.onload = this.sendXHR.onerror = onload; 2531 | } else { 2532 | this.sendXHR.onreadystatechange = stateChange; 2533 | } 2534 | 2535 | this.sendXHR.send(data); 2536 | }; 2537 | 2538 | /** 2539 | * Disconnects the established `XHR` connection. 2540 | * 2541 | * @returns {Transport} 2542 | * @api public 2543 | */ 2544 | 2545 | XHR.prototype.close = function () { 2546 | this.onClose(); 2547 | return this; 2548 | }; 2549 | 2550 | /** 2551 | * Generates a configured XHR request 2552 | * 2553 | * @param {String} url The url that needs to be requested. 2554 | * @param {String} method The method the request should use. 2555 | * @returns {XMLHttpRequest} 2556 | * @api private 2557 | */ 2558 | 2559 | XHR.prototype.request = function (method) { 2560 | var req = io.util.request(this.socket.isXDomain()) 2561 | , query = io.util.query(this.socket.options.query, 't=' + +new Date); 2562 | 2563 | req.open(method || 'GET', this.prepareUrl() + query, true); 2564 | 2565 | if (method == 'POST') { 2566 | try { 2567 | if (req.setRequestHeader) { 2568 | req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); 2569 | } else { 2570 | // XDomainRequest 2571 | req.contentType = 'text/plain'; 2572 | } 2573 | } catch (e) {} 2574 | } 2575 | 2576 | return req; 2577 | }; 2578 | 2579 | /** 2580 | * Returns the scheme to use for the transport URLs. 2581 | * 2582 | * @api private 2583 | */ 2584 | 2585 | XHR.prototype.scheme = function () { 2586 | return this.socket.options.secure ? 'https' : 'http'; 2587 | }; 2588 | 2589 | /** 2590 | * Check if the XHR transports are supported 2591 | * 2592 | * @param {Boolean} xdomain Check if we support cross domain requests. 2593 | * @returns {Boolean} 2594 | * @api public 2595 | */ 2596 | 2597 | XHR.check = function (socket, xdomain) { 2598 | try { 2599 | if (io.util.request(xdomain)) { 2600 | return true; 2601 | } 2602 | } catch(e) {} 2603 | 2604 | return false; 2605 | }; 2606 | 2607 | /** 2608 | * Check if the XHR transport supports corss domain requests. 2609 | * 2610 | * @returns {Boolean} 2611 | * @api public 2612 | */ 2613 | 2614 | XHR.xdomainCheck = function () { 2615 | return XHR.check(null, true); 2616 | }; 2617 | 2618 | })( 2619 | 'undefined' != typeof io ? io.Transport : module.exports 2620 | , 'undefined' != typeof io ? io : module.parent.exports 2621 | , this 2622 | ); 2623 | 2624 | /** 2625 | * socket.io 2626 | * Copyright(c) 2011 LearnBoost 2627 | * MIT Licensed 2628 | */ 2629 | 2630 | (function (exports, io) { 2631 | 2632 | /** 2633 | * Expose constructor. 2634 | */ 2635 | 2636 | exports.htmlfile = HTMLFile; 2637 | 2638 | /** 2639 | * The HTMLFile transport creates a `forever iframe` based transport 2640 | * for Internet Explorer. Regular forever iframe implementations will 2641 | * continuously trigger the browsers buzy indicators. If the forever iframe 2642 | * is created inside a `htmlfile` these indicators will not be trigged. 2643 | * 2644 | * @constructor 2645 | * @extends {io.Transport.XHR} 2646 | * @api public 2647 | */ 2648 | 2649 | function HTMLFile (socket) { 2650 | io.Transport.XHR.apply(this, arguments); 2651 | }; 2652 | 2653 | /** 2654 | * Inherits from XHR transport. 2655 | */ 2656 | 2657 | io.util.inherit(HTMLFile, io.Transport.XHR); 2658 | 2659 | /** 2660 | * Transport name 2661 | * 2662 | * @api public 2663 | */ 2664 | 2665 | HTMLFile.prototype.name = 'htmlfile'; 2666 | 2667 | /** 2668 | * Creates a new ActiveX `htmlfile` with a forever loading iframe 2669 | * that can be used to listen to messages. Inside the generated 2670 | * `htmlfile` a reference will be made to the HTMLFile transport. 2671 | * 2672 | * @api private 2673 | */ 2674 | 2675 | HTMLFile.prototype.get = function () { 2676 | this.doc = new ActiveXObject('htmlfile'); 2677 | this.doc.open(); 2678 | this.doc.write(''); 2679 | this.doc.close(); 2680 | this.doc.parentWindow.s = this; 2681 | 2682 | var iframeC = this.doc.createElement('div'); 2683 | iframeC.className = 'socketio'; 2684 | 2685 | this.doc.body.appendChild(iframeC); 2686 | this.iframe = this.doc.createElement('iframe'); 2687 | 2688 | iframeC.appendChild(this.iframe); 2689 | 2690 | var self = this 2691 | , query = io.util.query(this.socket.options.query, 't='+ +new Date); 2692 | 2693 | this.iframe.src = this.prepareUrl() + query; 2694 | 2695 | io.util.on(window, 'unload', function () { 2696 | self.destroy(); 2697 | }); 2698 | }; 2699 | 2700 | /** 2701 | * The Socket.IO server will write script tags inside the forever 2702 | * iframe, this function will be used as callback for the incoming 2703 | * information. 2704 | * 2705 | * @param {String} data The message 2706 | * @param {document} doc Reference to the context 2707 | * @api private 2708 | */ 2709 | 2710 | HTMLFile.prototype._ = function (data, doc) { 2711 | this.onData(data); 2712 | try { 2713 | var script = doc.getElementsByTagName('script')[0]; 2714 | script.parentNode.removeChild(script); 2715 | } catch (e) { } 2716 | }; 2717 | 2718 | /** 2719 | * Destroy the established connection, iframe and `htmlfile`. 2720 | * And calls the `CollectGarbage` function of Internet Explorer 2721 | * to release the memory. 2722 | * 2723 | * @api private 2724 | */ 2725 | 2726 | HTMLFile.prototype.destroy = function () { 2727 | if (this.iframe){ 2728 | try { 2729 | this.iframe.src = 'about:blank'; 2730 | } catch(e){} 2731 | 2732 | this.doc = null; 2733 | this.iframe.parentNode.removeChild(this.iframe); 2734 | this.iframe = null; 2735 | 2736 | CollectGarbage(); 2737 | } 2738 | }; 2739 | 2740 | /** 2741 | * Disconnects the established connection. 2742 | * 2743 | * @returns {Transport} Chaining. 2744 | * @api public 2745 | */ 2746 | 2747 | HTMLFile.prototype.close = function () { 2748 | this.destroy(); 2749 | return io.Transport.XHR.prototype.close.call(this); 2750 | }; 2751 | 2752 | /** 2753 | * Checks if the browser supports this transport. The browser 2754 | * must have an `ActiveXObject` implementation. 2755 | * 2756 | * @return {Boolean} 2757 | * @api public 2758 | */ 2759 | 2760 | HTMLFile.check = function () { 2761 | if ('ActiveXObject' in window){ 2762 | try { 2763 | var a = new ActiveXObject('htmlfile'); 2764 | return a && io.Transport.XHR.check(); 2765 | } catch(e){} 2766 | } 2767 | return false; 2768 | }; 2769 | 2770 | /** 2771 | * Check if cross domain requests are supported. 2772 | * 2773 | * @returns {Boolean} 2774 | * @api public 2775 | */ 2776 | 2777 | HTMLFile.xdomainCheck = function () { 2778 | // we can probably do handling for sub-domains, we should 2779 | // test that it's cross domain but a subdomain here 2780 | return false; 2781 | }; 2782 | 2783 | /** 2784 | * Add the transport to your public io.transports array. 2785 | * 2786 | * @api private 2787 | */ 2788 | 2789 | io.transports.push('htmlfile'); 2790 | 2791 | })( 2792 | 'undefined' != typeof io ? io.Transport : module.exports 2793 | , 'undefined' != typeof io ? io : module.parent.exports 2794 | ); 2795 | 2796 | /** 2797 | * socket.io 2798 | * Copyright(c) 2011 LearnBoost 2799 | * MIT Licensed 2800 | */ 2801 | 2802 | (function (exports, io, global) { 2803 | 2804 | /** 2805 | * Expose constructor. 2806 | */ 2807 | 2808 | exports['xhr-polling'] = XHRPolling; 2809 | 2810 | /** 2811 | * The XHR-polling transport uses long polling XHR requests to create a 2812 | * "persistent" connection with the server. 2813 | * 2814 | * @constructor 2815 | * @api public 2816 | */ 2817 | 2818 | function XHRPolling () { 2819 | io.Transport.XHR.apply(this, arguments); 2820 | }; 2821 | 2822 | /** 2823 | * Inherits from XHR transport. 2824 | */ 2825 | 2826 | io.util.inherit(XHRPolling, io.Transport.XHR); 2827 | 2828 | /** 2829 | * Merge the properties from XHR transport 2830 | */ 2831 | 2832 | io.util.merge(XHRPolling, io.Transport.XHR); 2833 | 2834 | /** 2835 | * Transport name 2836 | * 2837 | * @api public 2838 | */ 2839 | 2840 | XHRPolling.prototype.name = 'xhr-polling'; 2841 | 2842 | /** 2843 | * Establish a connection, for iPhone and Android this will be done once the page 2844 | * is loaded. 2845 | * 2846 | * @returns {Transport} Chaining. 2847 | * @api public 2848 | */ 2849 | 2850 | XHRPolling.prototype.open = function () { 2851 | var self = this; 2852 | 2853 | io.Transport.XHR.prototype.open.call(self); 2854 | return false; 2855 | }; 2856 | 2857 | /** 2858 | * Starts a XHR request to wait for incoming messages. 2859 | * 2860 | * @api private 2861 | */ 2862 | 2863 | function empty () {}; 2864 | 2865 | XHRPolling.prototype.get = function () { 2866 | if (!this.open) return; 2867 | 2868 | var self = this; 2869 | 2870 | function stateChange () { 2871 | if (this.readyState == 4) { 2872 | this.onreadystatechange = empty; 2873 | 2874 | if (this.status == 200) { 2875 | self.onData(this.responseText); 2876 | self.get(); 2877 | } else { 2878 | self.onClose(); 2879 | } 2880 | } 2881 | }; 2882 | 2883 | function onload () { 2884 | this.onload = empty; 2885 | self.onData(this.responseText); 2886 | self.get(); 2887 | }; 2888 | 2889 | this.xhr = this.request(); 2890 | 2891 | if (global.XDomainRequest && this.xhr instanceof XDomainRequest) { 2892 | this.xhr.onload = this.xhr.onerror = onload; 2893 | } else { 2894 | this.xhr.onreadystatechange = stateChange; 2895 | } 2896 | 2897 | this.xhr.send(null); 2898 | }; 2899 | 2900 | /** 2901 | * Handle the unclean close behavior. 2902 | * 2903 | * @api private 2904 | */ 2905 | 2906 | XHRPolling.prototype.onClose = function () { 2907 | io.Transport.XHR.prototype.onClose.call(this); 2908 | 2909 | if (this.xhr) { 2910 | this.xhr.onreadystatechange = this.xhr.onload = empty; 2911 | try { 2912 | this.xhr.abort(); 2913 | } catch(e){} 2914 | this.xhr = null; 2915 | } 2916 | }; 2917 | 2918 | /** 2919 | * Webkit based browsers show a infinit spinner when you start a XHR request 2920 | * before the browsers onload event is called so we need to defer opening of 2921 | * the transport until the onload event is called. Wrapping the cb in our 2922 | * defer method solve this. 2923 | * 2924 | * @param {Socket} socket The socket instance that needs a transport 2925 | * @param {Function} fn The callback 2926 | * @api private 2927 | */ 2928 | 2929 | XHRPolling.prototype.ready = function (socket, fn) { 2930 | var self = this; 2931 | 2932 | io.util.defer(function () { 2933 | fn.call(self); 2934 | }); 2935 | }; 2936 | 2937 | /** 2938 | * Add the transport to your public io.transports array. 2939 | * 2940 | * @api private 2941 | */ 2942 | 2943 | io.transports.push('xhr-polling'); 2944 | 2945 | })( 2946 | 'undefined' != typeof io ? io.Transport : module.exports 2947 | , 'undefined' != typeof io ? io : module.parent.exports 2948 | , this 2949 | ); 2950 | 2951 | /** 2952 | * socket.io 2953 | * Copyright(c) 2011 LearnBoost 2954 | * MIT Licensed 2955 | */ 2956 | 2957 | (function (exports, io, global) { 2958 | /** 2959 | * There is a way to hide the loading indicator in Firefox. If you create and 2960 | * remove a iframe it will stop showing the current loading indicator. 2961 | * Unfortunately we can't feature detect that and UA sniffing is evil. 2962 | * 2963 | * @api private 2964 | */ 2965 | 2966 | var indicator = global.document && "MozAppearance" in 2967 | global.document.documentElement.style; 2968 | 2969 | /** 2970 | * Expose constructor. 2971 | */ 2972 | 2973 | exports['jsonp-polling'] = JSONPPolling; 2974 | 2975 | /** 2976 | * The JSONP transport creates an persistent connection by dynamically 2977 | * inserting a script tag in the page. This script tag will receive the 2978 | * information of the Socket.IO server. When new information is received 2979 | * it creates a new script tag for the new data stream. 2980 | * 2981 | * @constructor 2982 | * @extends {io.Transport.xhr-polling} 2983 | * @api public 2984 | */ 2985 | 2986 | function JSONPPolling (socket) { 2987 | io.Transport['xhr-polling'].apply(this, arguments); 2988 | 2989 | this.index = io.j.length; 2990 | 2991 | var self = this; 2992 | 2993 | io.j.push(function (msg) { 2994 | self._(msg); 2995 | }); 2996 | }; 2997 | 2998 | /** 2999 | * Inherits from XHR polling transport. 3000 | */ 3001 | 3002 | io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); 3003 | 3004 | /** 3005 | * Transport name 3006 | * 3007 | * @api public 3008 | */ 3009 | 3010 | JSONPPolling.prototype.name = 'jsonp-polling'; 3011 | 3012 | /** 3013 | * Posts a encoded message to the Socket.IO server using an iframe. 3014 | * The iframe is used because script tags can create POST based requests. 3015 | * The iframe is positioned outside of the view so the user does not 3016 | * notice it's existence. 3017 | * 3018 | * @param {String} data A encoded message. 3019 | * @api private 3020 | */ 3021 | 3022 | JSONPPolling.prototype.post = function (data) { 3023 | var self = this 3024 | , query = io.util.query( 3025 | this.socket.options.query 3026 | , 't='+ (+new Date) + '&i=' + this.index 3027 | ); 3028 | 3029 | if (!this.form) { 3030 | var form = document.createElement('form') 3031 | , area = document.createElement('textarea') 3032 | , id = this.iframeId = 'socketio_iframe_' + this.index 3033 | , iframe; 3034 | 3035 | form.className = 'socketio'; 3036 | form.style.position = 'absolute'; 3037 | form.style.top = '-1000px'; 3038 | form.style.left = '-1000px'; 3039 | form.target = id; 3040 | form.method = 'POST'; 3041 | form.setAttribute('accept-charset', 'utf-8'); 3042 | area.name = 'd'; 3043 | form.appendChild(area); 3044 | document.body.appendChild(form); 3045 | 3046 | this.form = form; 3047 | this.area = area; 3048 | } 3049 | 3050 | this.form.action = this.prepareUrl() + query; 3051 | 3052 | function complete () { 3053 | initIframe(); 3054 | self.socket.setBuffer(false); 3055 | }; 3056 | 3057 | function initIframe () { 3058 | if (self.iframe) { 3059 | self.form.removeChild(self.iframe); 3060 | } 3061 | 3062 | try { 3063 | // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) 3064 | iframe = document.createElement('