| 'use strict'; | 
|   | 
| var Stream      = require('stream').Stream, | 
|     util        = require('util'), | 
|     driver      = require('websocket-driver'), | 
|     Headers     = require('websocket-driver/lib/websocket/driver/headers'), | 
|     API         = require('./websocket/api'), | 
|     EventTarget = require('./websocket/api/event_target'), | 
|     Event       = require('./websocket/api/event'); | 
|   | 
| var EventSource = function(request, response, options) { | 
|   this.writable = true; | 
|   options = options || {}; | 
|   | 
|   this._stream = response.socket; | 
|   this._ping   = options.ping  || this.DEFAULT_PING; | 
|   this._retry  = options.retry || this.DEFAULT_RETRY; | 
|   | 
|   var scheme       = driver.isSecureRequest(request) ? 'https:' : 'http:'; | 
|   this.url         = scheme + '//' + request.headers.host + request.url; | 
|   this.lastEventId = request.headers['last-event-id'] || ''; | 
|   this.readyState  = API.CONNECTING; | 
|   | 
|   var headers = new Headers(), | 
|       self    = this; | 
|   | 
|   if (options.headers) { | 
|     for (var key in options.headers) headers.set(key, options.headers[key]); | 
|   } | 
|   | 
|   if (!this._stream || !this._stream.writable) return; | 
|   process.nextTick(function() { self._open() }); | 
|   | 
|   this._stream.setTimeout(0); | 
|   this._stream.setNoDelay(true); | 
|   | 
|   var handshake = 'HTTP/1.1 200 OK\r\n' + | 
|                   'Content-Type: text/event-stream\r\n' + | 
|                   'Cache-Control: no-cache, no-store\r\n' + | 
|                   'Connection: close\r\n' + | 
|                   headers.toString() + | 
|                   '\r\n' + | 
|                   'retry: ' + Math.floor(this._retry * 1000) + '\r\n\r\n'; | 
|   | 
|   this._write(handshake); | 
|   | 
|   this._stream.on('drain', function() { self.emit('drain') }); | 
|   | 
|   if (this._ping) | 
|     this._pingTimer = setInterval(function() { self.ping() }, this._ping * 1000); | 
|   | 
|   ['error', 'end'].forEach(function(event) { | 
|     self._stream.on(event, function() { self.close() }); | 
|   }); | 
| }; | 
| util.inherits(EventSource, Stream); | 
|   | 
| EventSource.isEventSource = function(request) { | 
|   if (request.method !== 'GET') return false; | 
|   var accept = (request.headers.accept || '').split(/\s*,\s*/); | 
|   return accept.indexOf('text/event-stream') >= 0; | 
| }; | 
|   | 
| var instance = { | 
|   DEFAULT_PING:   10, | 
|   DEFAULT_RETRY:  5, | 
|   | 
|   _write: function(chunk) { | 
|     if (!this.writable) return false; | 
|     try { | 
|       return this._stream.write(chunk, 'utf8'); | 
|     } catch (e) { | 
|       return false; | 
|     } | 
|   }, | 
|   | 
|   _open: function() { | 
|     if (this.readyState !== API.CONNECTING) return; | 
|   | 
|     this.readyState = API.OPEN; | 
|   | 
|     var event = new Event('open'); | 
|     event.initEvent('open', false, false); | 
|     this.dispatchEvent(event); | 
|   }, | 
|   | 
|   write: function(message) { | 
|     return this.send(message); | 
|   }, | 
|   | 
|   end: function(message) { | 
|     if (message !== undefined) this.write(message); | 
|     this.close(); | 
|   }, | 
|   | 
|   send: function(message, options) { | 
|     if (this.readyState > API.OPEN) return false; | 
|   | 
|     message = String(message).replace(/(\r\n|\r|\n)/g, '$1data: '); | 
|     options = options || {}; | 
|   | 
|     var frame = ''; | 
|     if (options.event) frame += 'event: ' + options.event + '\r\n'; | 
|     if (options.id)    frame += 'id: '    + options.id    + '\r\n'; | 
|     frame += 'data: ' + message + '\r\n\r\n'; | 
|   | 
|     return this._write(frame); | 
|   }, | 
|   | 
|   ping: function() { | 
|     return this._write(':\r\n\r\n'); | 
|   }, | 
|   | 
|   close: function() { | 
|     if (this.readyState > API.OPEN) return false; | 
|   | 
|     this.readyState = API.CLOSED; | 
|     this.writable = false; | 
|     if (this._pingTimer) clearInterval(this._pingTimer); | 
|     if (this._stream) this._stream.end(); | 
|   | 
|     var event = new Event('close'); | 
|     event.initEvent('close', false, false); | 
|     this.dispatchEvent(event); | 
|   | 
|     return true; | 
|   } | 
| }; | 
|   | 
| for (var method in instance) EventSource.prototype[method] = instance[method]; | 
| for (var key in EventTarget) EventSource.prototype[key] = EventTarget[key]; | 
|   | 
| module.exports = EventSource; |