This is a supporter for getblackboard.com; mainly for static API services. Let's see if this evolves into something else or not.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1034 lines
117 KiB

// socket.io-1.2.1
// (from http://socket.io/download/)
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.io=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(_dereq_,module,exports){module.exports=_dereq_("./lib/")},{"./lib/":2}],2:[function(_dereq_,module,exports){var url=_dereq_("./url");var parser=_dereq_("socket.io-parser");var Manager=_dereq_("./manager");var debug=_dereq_("debug")("socket.io-client");module.exports=exports=lookup;var cache=exports.managers={};function lookup(uri,opts){if(typeof uri=="object"){opts=uri;uri=undefined}opts=opts||{};var parsed=url(uri);var source=parsed.source;var id=parsed.id;var io;if(opts.forceNew||opts["force new connection"]||false===opts.multiplex){debug("ignoring socket cache for %s",source);io=Manager(source,opts)}else{if(!cache[id]){debug("new io instance for %s",source);cache[id]=Manager(source,opts)}io=cache[id]}return io.socket(parsed.path)}exports.protocol=parser.protocol;exports.connect=lookup;exports.Manager=_dereq_("./manager");exports.Socket=_dereq_("./socket")},{"./manager":3,"./socket":5,"./url":6,debug:9,"socket.io-parser":43}],3:[function(_dereq_,module,exports){var url=_dereq_("./url");var eio=_dereq_("engine.io-client");var Socket=_dereq_("./socket");var Emitter=_dereq_("component-emitter");var parser=_dereq_("socket.io-parser");var on=_dereq_("./on");var bind=_dereq_("component-bind");var object=_dereq_("object-component");var debug=_dereq_("debug")("socket.io-client:manager");var indexOf=_dereq_("indexof");module.exports=Manager;function Manager(uri,opts){if(!(this instanceof Manager))return new Manager(uri,opts);if(uri&&"object"==typeof uri){opts=uri;uri=undefined}opts=opts||{};opts.path=opts.path||"/socket.io";this.nsps={};this.subs=[];this.opts=opts;this.reconnection(opts.reconnection!==false);this.reconnectionAttempts(opts.reconnectionAttempts||Infinity);this.reconnectionDelay(opts.reconnectionDelay||1e3);this.reconnectionDelayMax(opts.reconnectionDelayMax||5e3);this.timeout(null==opts.timeout?2e4:opts.timeout);this.readyState="closed";this.uri=uri;this.connected=[];this.attempts=0;this.encoding=false;this.packetBuffer=[];this.encoder=new parser.Encoder;this.decoder=new parser.Decoder;this.autoConnect=opts.autoConnect!==false;if(this.autoConnect)this.open()}Manager.prototype.emitAll=function(){this.emit.apply(this,arguments);for(var nsp in this.nsps){this.nsps[nsp].emit.apply(this.nsps[nsp],arguments)}};Emitter(Manager.prototype);Manager.prototype.reconnection=function(v){if(!arguments.length)return this._reconnection;this._reconnection=!!v;return this};Manager.prototype.reconnectionAttempts=function(v){if(!arguments.length)return this._reconnectionAttempts;this._reconnectionAttempts=v;return this};Manager.prototype.reconnectionDelay=function(v){if(!arguments.length)return this._reconnectionDelay;this._reconnectionDelay=v;return this};Manager.prototype.reconnectionDelayMax=function(v){if(!arguments.length)return this._reconnectionDelayMax;this._reconnectionDelayMax=v;return this};Manager.prototype.timeout=function(v){if(!arguments.length)return this._timeout;this._timeout=v;return this};Manager.prototype.maybeReconnectOnOpen=function(){if(!this.openReconnect&&!this.reconnecting&&this._reconnection&&this.attempts===0){this.openReconnect=true;this.reconnect()}};Manager.prototype.open=Manager.prototype.connect=function(fn){debug("readyState %s",this.readyState);if(~this.readyState.indexOf("open"))return this;debug("opening %s",this.uri);this.engine=eio(this.uri,this.opts);var socket=this.
}catch(e){self.onError("jsonp polling iframe removal error",e)}}try{var html='<iframe src="javascript:0" name="'+self.iframeId+'">';iframe=document.createElement(html)}catch(e){iframe=document.createElement("iframe");iframe.name=self.iframeId;iframe.src="javascript:0"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,"\\\n");this.area.value=data.replace(rNewline,"\\n");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState=="complete"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling":17,"component-inherit":20}],16:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest");var Polling=_dereq_("./polling");var Emitter=_dereq_("component-emitter");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling-xhr");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!=="string"&&data!==undefined;var req=this.request({method:"POST",data:data,isBinary:isBinary});var self=this;req.on("success",fn);req.on("error",function(err){self.onError("xhr post error",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug("xhr poll");var req=this.request();var self=this;req.on("data",function(data){self.onData(data)});req.on("error",function(err){self.onError("xhr poll error",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||"GET";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var xhr=this.xhr=new XMLHttpRequest({agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR});var self=this;try{debug("xhr open %s: %s",this.method,this.uri);xhr.open(this.method,this.uri,this.async);if(this.supportsBinary){xhr.responseType="arraybuffer"}if("POST"==this.method){try{if(this.isBinary){xhr.setRequestHeader("Content-type","application/octet-stream")}else{xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")}}catch(e){}}if("withCredentials"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug("xhr data %s",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit("success");this.cleanup()};Request.prototype.onData=function(data){this.emit("data",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit("error",err);this.cleanup()};Request.prototype.cleanup=function(){if("undefined"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}try{this.xhr.abort()}catch(e){}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{v
var parts=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];module.exports=function parseuri(str){var src=str,b=str.indexOf("["),e=str.indexOf("]");if(b!=-1&&e!=-1){str=str.substring(0,b)+str.substring(b,e).replace(/:/g,";")+str.substring(e,str.length)}var m=re.exec(str||""),uri={},i=14;while(i--){uri[parts[i]]=m[i]||""}if(b!=-1&&e!=-1){uri.source=src;uri.host=uri.host.substring(1,uri.host.length-1).replace(/;/g,":");uri.authority=uri.authority.replace("[","").replace("]","").replace(/;/g,":");uri.ipv6uri=true}return uri}},{}],34:[function(_dereq_,module,exports){var global=function(){return this}();var WebSocket=global.WebSocket||global.MozWebSocket;module.exports=WebSocket?ws:null;function ws(uri,protocols,opts){var instance;if(protocols){instance=new WebSocket(uri,protocols)}else{instance=new WebSocket(uri)}return instance}if(WebSocket)ws.prototype=WebSocket.prototype},{}],35:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_("isarray");module.exports=hasBinary;function hasBinary(data){function _hasBinary(obj){if(!obj)return false;if(global.Buffer&&global.Buffer.isBuffer(obj)||global.ArrayBuffer&&obj instanceof ArrayBuffer||global.Blob&&obj instanceof Blob||global.File&&obj instanceof File){return true}if(isArray(obj)){for(var i=0;i<obj.length;i++){if(_hasBinary(obj[i])){return true}}}else if(obj&&"object"==typeof obj){if(obj.toJSON){obj=obj.toJSON()}for(var key in obj){if(obj.hasOwnProperty(key)&&_hasBinary(obj[key])){return true}}}return false}return _hasBinary(data)}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{isarray:36}],36:[function(_dereq_,module,exports){module.exports=Array.isArray||function(arr){return Object.prototype.toString.call(arr)=="[object Array]"}},{}],37:[function(_dereq_,module,exports){var global=_dereq_("global");try{module.exports="XMLHttpRequest"in global&&"withCredentials"in new global.XMLHttpRequest}catch(err){module.exports=false}},{global:38}],38:[function(_dereq_,module,exports){module.exports=function(){return this}()},{}],39:[function(_dereq_,module,exports){var indexOf=[].indexOf;module.exports=function(arr,obj){if(indexOf)return arr.indexOf(obj);for(var i=0;i<arr.length;++i){if(arr[i]===obj)return i}return-1}},{}],40:[function(_dereq_,module,exports){var has=Object.prototype.hasOwnProperty;exports.keys=Object.keys||function(obj){var keys=[];for(var key in obj){if(has.call(obj,key)){keys.push(key)}}return keys};exports.values=function(obj){var vals=[];for(var key in obj){if(has.call(obj,key)){vals.push(obj[key])}}return vals};exports.merge=function(a,b){for(var key in b){if(has.call(b,key)){a[key]=b[key]}}return a};exports.length=function(obj){return exports.keys(obj).length};exports.isEmpty=function(obj){return 0==exports.length(obj)}},{}],41:[function(_dereq_,module,exports){var re=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;var parts=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];module.exports=function parseuri(str){var m=re.exec(str||""),uri={},i=14;while(i--){uri[parts[i]]=m[i]||""}return uri}},{}],42:[function(_dereq_,module,exports){(function(global){var isArray=_dereq_("isarray");var isBuf=_dereq_("./is-buffer");exports.deconstructPacket=function(packet){var buffers=[];var packetData=packet.data;function _deconstructPacket(data){if(!data)return data;if(isBuf(data)){var placeholder={_placeholder:true,num:buffers.length};buffers.push(data);return placeholder}else if(isArray(data)){var newData=new Array(data.length);for(var i=0;i<data.length;i++){newData[i]=_deconstructPacket(data[i])}return newData}else if("object"==typeof data&&!(data instanceof Date)){var newData={};for(var key in data){newData[key]=_deconstructPacket(data[key])}return newData}return data}var pack=pac
;
/**
* sails.io.js
* ------------------------------------------------------------------------
* JavaScript Client (SDK) for communicating with Sails.
*
* Note that this script is completely optional, but it is handy if you're
* using WebSockets from the browser to talk to your Sails server.
*
* For tips and documentation, visit:
* http://sailsjs.org/#!documentation/reference/BrowserSDK/BrowserSDK.html
* ------------------------------------------------------------------------
*
* This file allows you to send and receive socket.io messages to & from Sails
* by simulating a REST client interface on top of socket.io. It models its API
* after the $.ajax pattern from jQuery you might already be familiar with.
*
* So if you're switching from using AJAX to sockets, instead of:
* `$.post( url, [data], [cb] )`
*
* You would use:
* `socket.post( url, [data], [cb] )`
*/
(function() {
// Save the URL that this script was fetched from for use below.
// (skip this if this SDK is being used outside of the DOM, i.e. in a Node process)
var urlThisScriptWasFetchedFrom = (function() {
if (
typeof window !== 'object' ||
typeof window.document !== 'object' ||
typeof window.document.getElementsByTagName !== 'function'
) {
return '';
}
// Return the URL of the last script loaded (i.e. this one)
// (this must run before nextTick; see http://stackoverflow.com/a/2976714/486547)
var allScriptsCurrentlyInDOM = window.document.getElementsByTagName('script');
var thisScript = allScriptsCurrentlyInDOM[allScriptsCurrentlyInDOM.length - 1];
return thisScript.src;
})();
// Constants
var CONNECTION_METADATA_PARAMS = {
version: '__sails_io_sdk_version',
platform: '__sails_io_sdk_platform',
language: '__sails_io_sdk_language'
};
// Current version of this SDK (sailsDK?!?!) and other metadata
// that will be sent along w/ the initial connection request.
var SDK_INFO = {
version: '0.11.0', // TODO: pull this automatically from package.json during build.
platform: typeof module === 'undefined' ? 'browser' : 'node',
language: 'javascript'
};
SDK_INFO.versionString =
CONNECTION_METADATA_PARAMS.version + '=' + SDK_INFO.version + '&' +
CONNECTION_METADATA_PARAMS.platform + '=' + SDK_INFO.platform + '&' +
CONNECTION_METADATA_PARAMS.language + '=' + SDK_INFO.language;
// In case you're wrapping the socket.io client to prevent pollution of the
// global namespace, you can pass in your own `io` to replace the global one.
// But we still grab access to the global one if it's available here:
var _io = (typeof io !== 'undefined') ? io : null;
/**
* Augment the `io` object passed in with methods for talking and listening
* to one or more Sails backend(s). Automatically connects a socket and
* exposes it on `io.socket`. If a socket tries to make requests before it
* is connected, the sails.io.js client will queue it up.
*
* @param {SocketIO} io
*/
function SailsIOClient(io) {
// Prefer the passed-in `io` instance, but also use the global one if we've got it.
if (!io) {
io = _io;
}
// If the socket.io client is not available, none of this will work.
if (!io) throw new Error('`sails.io.js` requires a socket.io client, but `io` was not passed in.');
//////////////////////////////////////////////////////////////
///// ///////////////////////////
///// PRIVATE METHODS/CONSTRUCTORS ///////////////////////////
///// ///////////////////////////
//////////////////////////////////////////////////////////////
/**
* A little logger for this library to use internally.
* Basically just a wrapper around `console.log` with
* support for feature-detection.
*
* @api private
* @factory
*/
function LoggerFactory(options) {
options = options || {
prefix: true
};
// If `console.log` is not accessible, `log` is a noop.
if (
typeof console !== 'object' ||
typeof console.log !== 'function' ||
typeof console.log.bind !== 'function'
) {
return function noop() {};
}
return function log() {
var args = Array.prototype.slice.call(arguments);
// All logs are disabled when `io.sails.environment = 'production'`.
if (io.sails.environment === 'production') return;
// Add prefix to log messages (unless disabled)
var PREFIX = '';
if (options.prefix) {
args.unshift(PREFIX);
}
// Call wrapped logger
console.log
.bind(console)
.apply(this, args);
};
}
// Create a private logger instance
var consolog = LoggerFactory();
consolog.noPrefix = LoggerFactory({
prefix: false
});
/**
* What is the `requestQueue`?
*
* The request queue is used to simplify app-level connection logic--
* i.e. so you don't have to wait for the socket to be connected
* to start trying to synchronize data.
*
* @api private
* @param {SailsSocket} socket
*/
function runRequestQueue (socket) {
var queue = socket.requestQueue;
if (!queue) return;
for (var i in queue) {
// Double-check that `queue[i]` will not
// inadvertently discover extra properties attached to the Object
// and/or Array prototype by other libraries/frameworks/tools.
// (e.g. Ember does this. See https://github.com/balderdashy/sails.io.js/pull/5)
var isSafeToDereference = ({}).hasOwnProperty.call(queue, i);
if (isSafeToDereference) {
// Emit the request.
_emitFrom(socket, queue[i]);
}
}
// Now empty the queue to remove it as a source of additional complexity.
queue = null;
}
/**
* Send a JSONP request.
*
* @param {Object} opts [optional]
* @param {Function} cb
* @return {XMLHttpRequest}
*/
function jsonp(opts, cb) {
opts = opts || {};
if (typeof window === 'undefined') {
// TODO: refactor node usage to live in here
return cb();
}
var scriptEl = document.createElement('script');
window._sailsIoJSConnect = function(response) {
scriptEl.parentNode.removeChild(scriptEl);
cb(response);
};
scriptEl.src = opts.url;
document.getElementsByTagName('head')[0].appendChild(scriptEl);
}
/**
* The JWR (JSON WebSocket Response) received from a Sails server.
*
* @api public
* @param {Object} responseCtx
* => :body
* => :statusCode
* => :headers
*
* @constructor
*/
function JWR(responseCtx) {
this.body = responseCtx.body || {};
this.headers = responseCtx.headers || {};
this.statusCode = responseCtx.statusCode || 200;
if (this.statusCode < 200 || this.statusCode >= 400) {
this.error = this.body || this.statusCode;
}
}
JWR.prototype.toString = function() {
return '[ResponseFromSails]' + ' -- ' +
'Status: ' + this.statusCode + ' -- ' +
'Headers: ' + this.headers + ' -- ' +
'Body: ' + this.body;
};
JWR.prototype.toPOJO = function() {
return {
body: this.body,
headers: this.headers,
statusCode: this.statusCode
};
};
JWR.prototype.pipe = function() {
// TODO: look at substack's stuff
return new Error('Client-side streaming support not implemented yet.');
};
/**
* @api private
* @param {SailsSocket} socket [description]
* @param {Object} requestCtx [description]
*/
function _emitFrom(socket, requestCtx) {
if (!socket._raw) {
throw new Error('Failed to emit from socket- raw SIO socket is missing.');
}
// Since callback is embedded in requestCtx,
// retrieve it and delete the key before continuing.
var cb = requestCtx.cb;
delete requestCtx.cb;
// Name of the appropriate socket.io listener on the server
// ( === the request method or "verb", e.g. 'get', 'post', 'put', etc. )
var sailsEndpoint = requestCtx.method;
socket._raw.emit(sailsEndpoint, requestCtx, function serverResponded(responseCtx) {
// Send back (emulatedHTTPBody, jsonWebSocketResponse)
if (cb) {
cb(responseCtx.body, new JWR(responseCtx));
}
});
}
//////////////////////////////////////////////////////////////
///// </PRIVATE METHODS/CONSTRUCTORS> ////////////////////////
//////////////////////////////////////////////////////////////
// Version note:
//
// `io.SocketNamespace.prototype` doesn't exist in sio 1.0.
//
// Rather than adding methods to the prototype for the Socket instance that is returned
// when the browser connects with `io.connect()`, we create our own constructor, `SailsSocket`.
// This makes our solution more future-proof and helps us work better w/ the Socket.io team
// when changes are rolled out in the future. To get a `SailsSocket`, you can run:
// ```
// io.sails.connect();
// ```
/**
* SailsSocket
*
* A wrapper for an underlying Socket instance that communicates directly
* to the Socket.io server running inside of Sails.
*
* If no `socket` option is provied, SailsSocket will function as a mock. It will queue socket
* requests and event handler bindings, replaying them when the raw underlying socket actually
* connects. This is handy when we don't necessarily have the valid configuration to know
* WHICH SERVER to talk to yet, etc. It is also used by `io.socket` for your convenience.
*
* @constructor
*/
function SailsSocket (opts){
var self = this;
opts = opts||{};
// Absorb opts
self.useCORSRouteToGetCookie = opts.useCORSRouteToGetCookie;
self.url = opts.url;
self.multiplex = opts.multiplex;
self.transports = opts.transports;
// Set up "eventQueue" to hold event handlers which have not been set on the actual raw socket yet.
self.eventQueue = {};
// Listen for special `parseError` event sent from sockets hook on the backend
// if an error occurs but a valid callback was not received from the client
// (i.e. so the server had no other way to send back the error information)
self.on('sails:parseError', function (err){
consolog('Sails encountered an error parsing a socket message sent from this client, and did not have access to a callback function to respond with.');
consolog('Error details:',err);
});
// TODO:
// Listen for a special private message on any connected that allows the server
// to set the environment (giving us 100% certainty that we guessed right)
// However, note that the `console.log`s called before and after connection
// are still forced to rely on our existing heuristics (to disable, tack #production
// onto the URL used to fetch this file.)
}
/**
* Start connecting this socket.
*
* @api private
*/
SailsSocket.prototype._connect = function (){
var self = this;
// Apply `io.sails` config as defaults
// (now that at least one tick has elapsed)
self.useCORSRouteToGetCookie = self.useCORSRouteToGetCookie||io.sails.useCORSRouteToGetCookie;
self.url = self.url||io.sails.url;
self.transports = self.transports || io.sails.transports;
// Ensure URL has no trailing slash
self.url = self.url ? self.url.replace(/(\/)$/, '') : undefined;
// Mix the current SDK version into the query string in
// the connection request to the server:
if (typeof self.query !== 'string') self.query = SDK_INFO.versionString;
else self.query += '&' + SDK_INFO.versionString;
// Determine whether this is a cross-origin socket by examining the
// hostname and port on the `window.location` object.
var isXOrigin = (function (){
// If `window` doesn't exist (i.e. being used from node.js), then it's
// always "cross-domain".
if (typeof window === 'undefined' || typeof window.location === 'undefined') {
return false;
}
// If `self.url` (aka "target") is falsy, then we don't need to worry about it.
if (typeof self.url !== 'string') { return false; }
// Get information about the "target" (`self.url`)
var targetProtocol = (function (){
try {
targetProtocol = self.url.match(/^([a-z]+:\/\/)/i)[1].toLowerCase();
}
catch (e) {}
targetProtocol = targetProtocol || 'http://';
return targetProtocol;
})();
var isTargetSSL = !!self.url.match('^https');
var targetPort = (function (){
try {
return self.url.match(/^[a-z]+:\/\/[^:]*:([0-9]*)/i)[1];
}
catch (e){}
return isTargetSSL ? '443' : '80';
})();
var targetAfterProtocol = self.url.replace(/^([a-z]+:\/\/)/i, '');
// If target protocol is different than the actual protocol,
// then we'll consider this cross-origin.
if (targetProtocol.replace(/[:\/]/g, '') !== window.location.protocol.replace(/[:\/]/g,'')) {
return true;
}
// If target hostname is different than actual hostname, we'll consider this cross-origin.
var hasSameHostname = targetAfterProtocol.search(window.location.hostname) !== 0;
if (!hasSameHostname) {
return true;
}
// If no actual port is explicitly set on the `window.location` object,
// we'll assume either 80 or 443.
var isLocationSSL = window.location.protocol.match(/https/i);
var locationPort = (window.location.port+'') || (isLocationSSL ? '443' : '80');
// Finally, if ports don't match, we'll consider this cross-origin.
if (targetPort !== locationPort) {
return true;
}
// Otherwise, it's the same origin.
return false;
})();
// Prepare to start connecting the socket
(function selfInvoking (cb){
// If this is an attempt at a cross-origin or cross-port
// socket connection, send a JSONP request first to ensure
// that a valid cookie is available. This can be disabled
// by setting `io.sails.useCORSRouteToGetCookie` to false.
//
// Otherwise, skip the stuff below.
if (!(self.useCORSRouteToGetCookie && isXOrigin)) {
return cb();
}
// Figure out the x-origin CORS route
// (Sails provides a default)
var xOriginCookieURL = self.url;
if (typeof self.useCORSRouteToGetCookie === 'string') {
xOriginCookieURL += self.useCORSRouteToGetCookie;
}
else {
xOriginCookieURL += '/__getcookie';
}
// Make the AJAX request (CORS)
if (typeof window !== 'undefined') {
jsonp({
url: xOriginCookieURL,
method: 'GET'
}, cb);
return;
}
// If there's no `window` object, we must be running in Node.js
// so just require the request module and send the HTTP request that
// way.
var mikealsReq = require('request');
mikealsReq.get(xOriginCookieURL, function(err, httpResponse, body) {
if (err) {
consolog(
'Failed to connect socket (failed to get cookie)',
'Error:', err
);
return;
}
cb();
});
})(function goAheadAndActuallyConnect() {
// Now that we're ready to connect, create a raw underlying Socket
// using Socket.io and save it as `_raw` (this will start it connecting)
self._raw = io(self.url, self);
// Replay event bindings from the eager socket
self.replay();
/**
* 'connect' event is triggered when the socket establishes a connection
* successfully.
*/
self.on('connect', function socketConnected() {
consolog.noPrefix(
'\n' +
'\n' +
// ' |> ' + '\n' +
// ' \\___/ '+
// '\n'+
' |> Now connected to Sails.' + '\n' +
'\\___/ For help, see: http://bit.ly/1DmTvgK' + '\n' +
' (using '+io.sails.sdk.platform+' SDK @v'+io.sails.sdk.version+')'+ '\n' +
'\n'+
'\n'+
// '\n'+
''
// ' ⚓ (development mode)'
// 'e.g. to send a GET request to Sails via WebSockets, run:'+ '\n' +
// '`io.socket.get("/foo", function serverRespondedWith (body, jwr) { console.log(body); })`'+ '\n' +
);
});
self.on('disconnect', function() {
self.connectionLostTimestamp = (new Date()).getTime();
consolog('====================================');
consolog('Socket was disconnected from Sails.');
consolog('Usually, this is due to one of the following reasons:' + '\n' +
' -> the server ' + (self.url ? self.url + ' ' : '') + 'was taken down' + '\n' +
' -> your browser lost internet connectivity');
consolog('====================================');
});
self.on('reconnecting', function(numAttempts) {
consolog(
'\n'+
' Socket is trying to reconnect to Sails...\n'+
'_-|>_- (attempt #' + numAttempts + ')'+'\n'+
'\n'
);
});
self.on('reconnect', function(transport, numAttempts) {
var msSinceConnectionLost = ((new Date()).getTime() - self.connectionLostTimestamp);
var numSecsOffline = (msSinceConnectionLost / 1000);
consolog(
'\n'+
' |> Socket reconnected successfully after'+'\n'+
'\\___/ being offline for ~' + numSecsOffline + ' seconds.'+'\n'+
'\n'
);
});
// 'error' event is triggered if connection can not be established.
// (usually because of a failed authorization, which is in turn
// usually due to a missing or invalid cookie)
self.on('error', function failedToConnect(err) {
// TODO:
// handle failed connections due to failed authorization
// in a smarter way (probably can listen for a different event)
// A bug in Socket.io 0.9.x causes `connect_failed`
// and `reconnect_failed` not to fire.
// Check out the discussion in github issues for details:
// https://github.com/LearnBoost/socket.io/issues/652
// io.socket.on('connect_failed', function () {
// consolog('io.socket emitted `connect_failed`');
// });
// io.socket.on('reconnect_failed', function () {
// consolog('io.socket emitted `reconnect_failed`');
// });
consolog(
'Failed to connect socket (probably due to failed authorization on server)',
'Error:', err
);
});
});
};
/**
* Disconnect the underlying socket.
*
* @api public
*/
SailsSocket.prototype.disconnect = function (){
if (!this._raw) {
throw new Error('Cannot disconnect- socket is already disconnected');
}
return this._raw.disconnect();
};
/**
* isConnected
*
* @api private
* @return {Boolean} whether the socket is connected and able to
* communicate w/ the server.
*/
SailsSocket.prototype.isConnected = function () {
if (!this._raw) {
return false;
}
return !!this._raw.connected;
};
/**
* [replay description]
* @return {[type]} [description]
*/
SailsSocket.prototype.replay = function (){
var self = this;
// Pass events and a reference to the request queue
// off to the self._raw for consumption
for (var evName in self.eventQueue) {
for (var i in self.eventQueue[evName]) {
self._raw.on(evName, self.eventQueue[evName][i]);
}
}
// Bind a one-time function to run the request queue
// when the self._raw connects.
if ( !self.isConnected() ) {
var alreadyRanRequestQueue = false;
self._raw.on('connect', function whenRawSocketConnects() {
if (alreadyRanRequestQueue) return;
runRequestQueue(self);
alreadyRanRequestQueue = true;
});
}
// Or run it immediately if self._raw is already connected
else {
runRequestQueue(self);
}
return self;
};
/**
* Chainable method to bind an event to the socket.
*
* @param {String} evName [event name]
* @param {Function} fn [event handler function]
* @return {SailsSocket}
*/
SailsSocket.prototype.on = function (evName, fn){
// Bind the event to the raw underlying socket if possible.
if (this._raw) {
this._raw.on(evName, fn);
return this;
}
// Otherwise queue the event binding.
if (!this.eventQueue[evName]) {
this.eventQueue[evName] = [fn];
}
else {
this.eventQueue[evName].push(fn);
}
return this;
};
/**
* Chainable method to unbind an event from the socket.
*
* @param {String} evName [event name]
* @param {Function} fn [event handler function]
* @return {SailsSocket}
*/
SailsSocket.prototype.off = function (evName, fn){
// Bind the event to the raw underlying socket if possible.
if (this._raw) {
this._raw.off(evName, fn);
return this;
}
// Otherwise queue the event binding.
if (this.eventQueue[evName] && this.eventQueue[evName].indexOf(fn) > -1) {
this.eventQueue[evName].splice(this.eventQueue[evName].indexOf(fn), 1);
}
return this;
};
/**
* Chainable method to unbind all events from the socket.
*
* @return {SailsSocket}
*/
SailsSocket.prototype.removeAllListeners = function (){
// Bind the event to the raw underlying socket if possible.
if (this._raw) {
this._raw.removeAllListeners();
return this;
}
// Otherwise queue the event binding.
this.eventQueue = {};
return this;
};
/**
* Simulate a GET request to sails
* e.g.
* `socket.get('/user/3', Stats.populate)`
*
* @api public
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
SailsSocket.prototype.get = function(url, data, cb) {
// `data` is optional
if (typeof data === 'function') {
cb = data;
data = {};
}
return this.request({
method: 'get',
params: data,
url: url
}, cb);
};
/**
* Simulate a POST request to sails
* e.g.
* `socket.post('/event', newMeeting, $spinner.hide)`
*
* @api public
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
SailsSocket.prototype.post = function(url, data, cb) {
// `data` is optional
if (typeof data === 'function') {
cb = data;
data = {};
}
return this.request({
method: 'post',
data: data,
url: url
}, cb);
};
/**
* Simulate a PUT request to sails
* e.g.
* `socket.post('/event/3', changedFields, $spinner.hide)`
*
* @api public
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
SailsSocket.prototype.put = function(url, data, cb) {
// `data` is optional
if (typeof data === 'function') {
cb = data;
data = {};
}
return this.request({
method: 'put',
params: data,
url: url
}, cb);
};
/**
* Simulate a DELETE request to sails
* e.g.
* `socket.delete('/event', $spinner.hide)`
*
* @api public
* @param {String} url :: destination URL
* @param {Object} params :: parameters to send with the request [optional]
* @param {Function} cb :: callback function to call when finished [optional]
*/
SailsSocket.prototype['delete'] = function(url, data, cb) {
// `data` is optional
if (typeof data === 'function') {
cb = data;
data = {};
}
return this.request({
method: 'delete',
params: data,
url: url
}, cb);
};
/**
* Simulate an HTTP request to sails
* e.g.
* ```
* socket.request({
* url:'/user',
* params: {},
* method: 'POST',
* headers: {}
* }, function (responseBody, JWR) {
* // ...
* });
* ```
*
* @api public
* @option {String} url :: destination URL
* @option {Object} params :: parameters to send with the request [optional]
* @option {Object} headers:: headers to send with the request [optional]
* @option {Function} cb :: callback function to call when finished [optional]
* @option {String} method :: HTTP request method [optional]
*/
SailsSocket.prototype.request = function(options, cb) {
var usage =
'Usage:\n'+
'socket.request( options, [fnToCallWhenComplete] )\n\n'+
'options.url :: e.g. "/foo/bar"'+'\n'+
'options.method :: e.g. "get", "post", "put", or "delete", etc.'+'\n'+
'options.params :: e.g. { emailAddress: "mike@sailsjs.org" }'+'\n'+
'options.headers :: e.g. { "x-my-custom-header": "some string" }';
// Old usage:
// var usage = 'Usage:\n socket.'+(options.method||'request')+'('+
// ' destinationURL, [dataToSend], [fnToCallWhenComplete] )';
// Validate options and callback
if (typeof options !== 'object' || typeof options.url !== 'string') {
throw new Error('Invalid or missing URL!\n' + usage);
}
if (options.method && typeof options.method !== 'string') {
throw new Error('Invalid `method` provided (should be a string like "post" or "put")\n' + usage);
}
if (options.headers && typeof options.headers !== 'object') {
throw new Error('Invalid `headers` provided (should be an object with string values)\n' + usage);
}
if (options.params && typeof options.params !== 'object') {
throw new Error('Invalid `params` provided (should be an object with string values)\n' + usage);
}
if (cb && typeof cb !== 'function') {
throw new Error('Invalid callback function!\n' + usage);
}
// Build a simulated request object
// (and sanitize/marshal options along the way)
var requestCtx = {
method: options.method.toLowerCase() || 'get',
headers: options.headers || {},
data: options.params || options.data || {},
// Remove trailing slashes and spaces to make packets smaller.
url: options.url.replace(/^(.+)\/*\s*$/, '$1'),
cb: cb
};
// If this socket is not connected yet, queue up this request
// instead of sending it.
// (so it can be replayed when the socket comes online.)
if ( ! this.isConnected() ) {
// If no queue array exists for this socket yet, create it.
this.requestQueue = this.requestQueue || [];
this.requestQueue.push(requestCtx);
return;
}
// Otherwise, our socket is ok!
// Send the request.
_emitFrom(this, requestCtx);
};
/**
* Socket.prototype._request
*
* Simulate HTTP over Socket.io.
*
* @api private
* @param {[type]} options [description]
* @param {Function} cb [description]
*/
SailsSocket.prototype._request = function(options, cb) {
throw new Error('`_request()` was a private API deprecated as of v0.11 of the sails.io.js client. Use `.request()` instead.');
};
// Set a `sails` object that may be used for configuration before the
// first socket connects (i.e. to prevent auto-connect)
io.sails = {
// Whether to automatically connect a socket and save it as `io.socket`.
autoConnect: true,
// The route (path) to hit to get a x-origin (CORS) cookie
// (or true to use the default: '/__getcookie')
useCORSRouteToGetCookie: true,
// The environment we're running in.
// (logs are not displayed when this is set to 'production')
//
// Defaults to development unless this script was fetched from a URL
// that ends in `*.min.js` or '#production' (may also be manually overridden.)
//
environment: urlThisScriptWasFetchedFrom.match(/(\#production|\.min\.js)/g) ? 'production' : 'development',
// The version of this sails.io.js client SDK
sdk: SDK_INFO,
// Transports to use when communicating with the server, in the order they will be tried
transports: ['polling', 'websocket']
};
/**
* Add `io.sails.connect` function as a wrapper for the built-in `io()` aka `io.connect()`
* method, returning a SailsSocket. This special function respects the configured io.sails
* connection URL, as well as sending other identifying information (most importantly, the
* current version of this SDK).
*
* @param {String} url [optional]
* @param {Object} opts [optional]
* @return {Socket}
*/
io.sails.connect = function(url, opts) {
opts = opts || {};
// If explicit connection url is specified, save it to options
opts.url = url || opts.url || undefined;
// Instantiate and return a new SailsSocket- and try to connect immediately.
var socket = new SailsSocket(opts);
socket._connect();
return socket;
};
// io.socket
//
// The eager instance of Socket which will automatically try to connect
// using the host that this js file was served from.
//
// This can be disabled or configured by setting properties on `io.sails.*` within the
// first cycle of the event loop.
//
// Build `io.socket` so it exists
// (this does not start the connection process)
io.socket = new SailsSocket();
// In the mean time, this eager socket will be queue events bound by the user
// before the first cycle of the event loop (using `.on()`), which will later
// be rebound on the raw underlying socket.
// If configured to do so, start auto-connecting after the first cycle of the event loop
// has completed (to allow time for this behavior to be configured/disabled
// by specifying properties on `io.sails`)
setTimeout(function() {
// If autoConnect is disabled, delete the eager socket (io.socket) and bail out.
if (!io.sails.autoConnect) {
delete io.socket;
return;
}
// consolog('Eagerly auto-connecting socket to Sails... (requests will be queued in the mean-time)');
io.socket._connect();
}, 0); // </setTimeout>
// Return the `io` object.
return io;
}
// Add CommonJS support to allow this client SDK to be used from Node.js.
if (typeof module === 'object' && typeof module.exports !== 'undefined') {
module.exports = SailsIOClient;
return SailsIOClient;
}
// Otherwise, try to instantiate the client:
// In case you're wrapping the socket.io client to prevent pollution of the
// global namespace, you can replace the global `io` with your own `io` here:
return SailsIOClient();
})();