[
  {
    "path": ".gitignore",
    "content": "# vim swp files\n*.swp\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbin/\nbuild/\ndevelop-eggs/\neggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# Twisted plugins cache\ntest_server/twisted/plugins/dropin.cache\n\n# local node_modules\nnode_modules/\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.cache\nnosetests.xml\ncoverage.xml\n\n# Translations\n*.mo\n\n# Mr Developer\n.mr.developer.cfg\n.project\n.pydevproject\n\n# Rope\n.ropeproject\n\n# Django stuff:\n*.log\n*.pot\n\n# Sphinx documentation\ndocs/_build/\n\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"docs\"]\n\tpath = docs\n\turl = git@github.com:amvtek/EventSource.wiki.git\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "module.exports = function(grunt) {\n\n    \"use strict\";\n    \n    grunt.initConfig({\n\n\tpkg: grunt.file.readJSON('package.json'),\n\n\t'string-replace': {\n\t    dist: {\n\t\toptions: {\n\t\t    replacements: [\n\t\t\t{pattern: /{{VERSION}}/g, replacement: '<%= pkg.version %>'}\n\t\t    ]\n\t\t},\n\t\tfiles: {\n\t\t    'dist/eventsource.js': ['javascript/src/eventsource.js'],\n\t\t    'dist/browserify-eventsource.js': ['javascript/src/browserify-eventsource.js']\n\t\t}\n\t    }\n\t},\n\n\tuglify: {\n\t    dist: {\n\t\tfiles: {\n\t\t    'dist/eventsource.min.js': ['dist/eventsource.js']\n\t\t}\n\t    }\n\t},\n\n\t\n    });\n    grunt.loadNpmTasks('grunt-string-replace');\n    grunt.loadNpmTasks('grunt-contrib-uglify');\n    grunt.registerTask('default', ['string-replace', 'uglify']);\n};\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 AmvTek\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "EventSource Polyfill\n====================\n\nProvide polyfill to support EventSource in browser where it is not available.\n\n> -   Used in production\n> -   Tested in Internet Explorer 8 +\n> -   Tested in Android browser 2.1 +\n> -   [Documented][]\n> -   Run the [Browser test suite][]\n\nInstalling\n----------\n\n### from source\n\nDownload suitable project archive (zip or tar.gz) from [release page][]\n\nInclude in your html documents one of the following javascript file:\n\n> -   *dist/eventsource.js*\n> -   *dist/eventsource.min.js* (minified version)\n\n### Using bower package manager\n\nTo install package from **bower registry**, type :\n\n    bower install eventsource-polyfill\n\nInclude in your html documents one of the following javascript file:\n\n> -   *bower\\_components/eventsource-polyfill/dist/eventsource.js*\n> -   *bower\\_components/eventsource-polyfill/dist/eventsource.min.js* (minified version)\n\n### Using npm package manager\n\nTo install package from **npm registry**, type :\n\n    npm install eventsource-polyfill\n\nNote that this package may only be used with in **browser application**.\n\nIf you are using [browserify][] , you just have to require this package in your main module…\n\n``` sourceCode\n// load (Polyfill) EventSource, in case browser does not support it...\nrequire('eventsource-polyfill');\n```\n\nRun the tests now\n-----------------\n\nWith your web browser visit this [test site][Browser test suite]\n\nAllow **sufficient time** ( ~ 5 minutes) for the full Test Suite to run…\n\nProject content\n---------------\n\ndist/  \n    built version of javascript modules\n\njavascript/  \n    Contains polyfill module and related unit tests\n\ntest_server/  \n    python server which generates *easy to test* **event stream**\n\ndocs/  \n    documentation wiki\n\n  [Documented]: https://github.com/amvtek/EventSource/wiki\n  [Browser test suite]: http://testevs.amvtek.com/\n  [release page]: https://github.com/amvtek/EventSource/releases/latest\n  [browserify]: http://browserify.org\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"eventsource-polyfill\",\n  \"homepage\": \"https://github.com/amvtek/EventSource\",\n  \"authors\": [\n    \"amvtek <devel@amvtek.com>\"\n  ],\n  \"description\": \"A polyfill for http://www.w3.org/TR/eventsource/\",\n  \"main\": [\"javascript/src/eventsource.js\", \"eventsource.min.js\", \"README.rst\"],\n  \"keywords\": [\n    \"sse\",\n    \"server sent events\",\n    \"eventsource\",\n    \"event-source\",\n    \"polyfill\"\n  ],\n  \"license\": \"MIT\",\n  \"ignore\": [\n    \"**/.*\",\n    \"javascript\",\n    \"test_server\",\n    \"Gruntfile.js\",\n    \"package.json\",\n    \"node_modules\",\n    \"bower_components\",\n    \"test\",\n    \"tests\",\n    \"docs\"\n  ]\n}\n"
  },
  {
    "path": "dist/browserify-eventsource.js",
    "content": "/*\n   * CommonJS module that exports EventSource polyfill version 0.9.7\n   * This module is intended for browser side use\n   * =====================================================================\n   * THIS IS A POLYFILL MODULE, SO IT HAS SIDE EFFECTS\n   * IT AUTOMATICALLY CHECKS IF window OBJECT DEFINES EventSource\n   * AND ADD THE EXPORTED ONE IN CASE IT IS UNDEFINED\n   * =====================================================================\n   * Supported by sc AmvTek srl\n   * :email: devel@amvtek.com\n */\n\n\nvar PolyfillEventSource = require('./eventsource.js').EventSource;\nmodule.exports = PolyfillEventSource;\n\n// Add EventSource to window if it is missing...\nif (window && !window.EventSource){\n    window.EventSource = PolyfillEventSource;\n    if (console){\n\tconsole.log(\"polyfill-eventsource added missing EventSource to window\");\n    }\n}\n"
  },
  {
    "path": "dist/eventsource.js",
    "content": "/*\n   * EventSource polyfill version 0.9.7\n   * Supported by sc AmvTek srl\n   * :email: devel@amvtek.com\n */\n;(function (global) {\n\n    if (global.EventSource && !global._eventSourceImportPrefix){\n        return;\n    }\n\n    var evsImportName = (global._eventSourceImportPrefix||'')+\"EventSource\";\n\n    var EventSource = function (url, options) {\n\n        if (!url || typeof url != 'string') {\n            throw new SyntaxError('Not enough arguments');\n        }\n\n        this.URL = url;\n        this.setOptions(options);\n        var evs = this;\n        setTimeout(function(){evs.poll()}, 0);\n    };\n\n    EventSource.prototype = {\n\n        CONNECTING: 0,\n\n        OPEN: 1,\n\n        CLOSED: 2,\n\n        defaultOptions: {\n\n            loggingEnabled: false,\n\n            loggingPrefix: \"eventsource\",\n\n            interval: 500, // milliseconds\n\n            bufferSizeLimit: 256*1024, // bytes\n\n            silentTimeout: 300000, // milliseconds\n\n            getArgs:{\n                'evs_buffer_size_limit': 256*1024\n            },\n\n            xhrHeaders:{\n                'Accept': 'text/event-stream',\n                'Cache-Control': 'no-cache',\n                'X-Requested-With': 'XMLHttpRequest'\n            }\n        },\n\n        setOptions: function(options){\n\n            var defaults = this.defaultOptions;\n            var option;\n\n            // set all default options...\n            for (option in defaults){\n\n                if ( defaults.hasOwnProperty(option) ){\n                    this[option] = defaults[option];\n                }\n            }\n\n            // override with what is in options\n            for (option in options){\n\n                if (option in defaults && options.hasOwnProperty(option)){\n                    this[option] = options[option];\n                }\n            }\n\n            // if getArgs option is enabled\n            // ensure evs_buffer_size_limit corresponds to bufferSizeLimit\n            if (this.getArgs && this.bufferSizeLimit) {\n\n                this.getArgs['evs_buffer_size_limit'] = this.bufferSizeLimit;\n            }\n\n            // if console is not available, force loggingEnabled to false\n            if (typeof console === \"undefined\" || typeof console.log === \"undefined\") {\n\n                this.loggingEnabled = false;\n            }\n        },\n\n        log: function(message) {\n\n            if (this.loggingEnabled) {\n\n                console.log(\"[\" + this.loggingPrefix +\"]:\" + message)\n            }\n        },\n\n        poll: function() {\n\n            try {\n\n                if (this.readyState == this.CLOSED) {\n                    return;\n                }\n\n                this.cleanup();\n                this.readyState = this.CONNECTING;\n                this.cursor = 0;\n                this.cache = '';\n                this._xhr = new this.XHR(this);\n                this.resetNoActivityTimer();\n\n            }\n            catch (e) {\n\n                // in an attempt to silence the errors\n                this.log('There were errors inside the pool try-catch');\n                this.dispatchEvent('error', { type: 'error', data: e.message });\n            }\n        },\n\n        pollAgain: function (interval) {\n\n            // schedule poll to be called after interval milliseconds\n            var evs = this;\n            evs.readyState = evs.CONNECTING;\n            evs.dispatchEvent('error', {\n                type: 'error',\n                data: \"Reconnecting \"\n            });\n            this._pollTimer = setTimeout(function(){evs.poll()}, interval||0);\n        },\n\n\n        cleanup: function() {\n\n            this.log('evs cleaning up')\n\n            if (this._pollTimer){\n                clearInterval(this._pollTimer);\n                this._pollTimer = null;\n            }\n\n            if (this._noActivityTimer){\n                clearInterval(this._noActivityTimer);\n                this._noActivityTimer = null;\n            }\n\n            if (this._xhr){\n                this._xhr.abort();\n                this._xhr = null;\n            }\n        },\n\n        resetNoActivityTimer: function(){\n\n            if (this.silentTimeout){\n\n                if (this._noActivityTimer){\n                    clearInterval(this._noActivityTimer);\n                }\n                var evs = this;\n                this._noActivityTimer = setTimeout(\n                        function(){ evs.log('Timeout! silentTImeout:'+evs.silentTimeout); evs.pollAgain(); },\n                        this.silentTimeout\n                        );\n            }\n        },\n\n        close: function () {\n\n            this.readyState = this.CLOSED;\n            this.log('Closing connection. readyState: '+this.readyState);\n            this.cleanup();\n        },\n\n        _onxhrdata: function() {\n\n            var request = this._xhr;\n\n            if (request.isReady() && !request.hasError() ) {\n                // reset the timer, as we have activity\n                this.resetNoActivityTimer();\n\n                // move this EventSource to OPEN state...\n                if (this.readyState == this.CONNECTING) {\n                    this.readyState = this.OPEN;\n                    this.dispatchEvent('open', { type: 'open' });\n                }\n\n                var buffer = request.getBuffer();\n\n                if (buffer.length > this.bufferSizeLimit) {\n                    this.log('buffer.length > this.bufferSizeLimit');\n                    this.pollAgain();\n                }\n\n                if (this.cursor == 0 && buffer.length > 0){\n\n                    // skip byte order mark \\uFEFF character if it starts the stream\n                    if (buffer.substring(0,1) == '\\uFEFF'){\n                        this.cursor = 1;\n                    }\n                }\n\n                var lastMessageIndex = this.lastMessageIndex(buffer);\n                if (lastMessageIndex[0] >= this.cursor){\n\n                    var newcursor = lastMessageIndex[1];\n                    var toparse = buffer.substring(this.cursor, newcursor);\n                    this.parseStream(toparse);\n                    this.cursor = newcursor;\n                }\n\n                // if request is finished, reopen the connection\n                if (request.isDone()) {\n                    this.log('request.isDone(). reopening the connection');\n                    this.pollAgain(this.interval);\n                }\n            }\n            else if (this.readyState !== this.CLOSED) {\n\n                this.log('this.readyState !== this.CLOSED');\n                this.pollAgain(this.interval);\n\n                //MV: Unsure why an error was previously dispatched\n            }\n        },\n\n        parseStream: function(chunk) {\n\n            // normalize line separators (\\r\\n,\\r,\\n) to \\n\n            // remove white spaces that may precede \\n\n            chunk = this.cache + this.normalizeToLF(chunk);\n\n            var events = chunk.split('\\n\\n');\n\n            var i, j, eventType, datas, line, retry;\n\n            for (i=0; i < (events.length - 1); i++) {\n\n                eventType = 'message';\n                datas = [];\n                parts = events[i].split('\\n');\n\n                for (j=0; j < parts.length; j++) {\n\n                    line = this.trimWhiteSpace(parts[j]);\n\n                    if (line.indexOf('event') == 0) {\n\n                        eventType = line.replace(/event:?\\s*/, '');\n                    }\n                    else if (line.indexOf('retry') == 0) {\n\n                        retry = parseInt(line.replace(/retry:?\\s*/, ''));\n                        if(!isNaN(retry)) {\n                            this.interval = retry;\n                        }\n                    }\n                    else if (line.indexOf('data') == 0) {\n\n                        datas.push(line.replace(/data:?\\s*/, ''));\n                    }\n                    else if (line.indexOf('id:') == 0) {\n\n                        this.lastEventId = line.replace(/id:?\\s*/, '');\n                    }\n                    else if (line.indexOf('id') == 0) { // this resets the id\n\n                        this.lastEventId = null;\n                    }\n                }\n\n                if (datas.length) {\n                    // dispatch a new event\n                    var event = new MessageEvent(eventType, datas.join('\\n'), window.location.origin, this.lastEventId);\n                    this.dispatchEvent(eventType, event);\n                }\n            }\n\n            this.cache = events[events.length - 1];\n        },\n\n        dispatchEvent: function (type, event) {\n            var handlers = this['_' + type + 'Handlers'];\n\n            if (handlers) {\n\n                for (var i = 0; i < handlers.length; i++) {\n                    handlers[i].call(this, event);\n                }\n            }\n\n            if (this['on' + type]) {\n                this['on' + type].call(this, event);\n            }\n\n        },\n\n        addEventListener: function (type, handler) {\n            if (!this['_' + type + 'Handlers']) {\n                this['_' + type + 'Handlers'] = [];\n            }\n\n            this['_' + type + 'Handlers'].push(handler);\n        },\n\n        removeEventListener: function (type, handler) {\n            var handlers = this['_' + type + 'Handlers'];\n            if (!handlers) {\n                return;\n            }\n            for (var i = handlers.length - 1; i >= 0; --i) {\n                if (handlers[i] === handler) {\n                    handlers.splice(i, 1);\n                    break;\n                }\n            }\n        },\n\n        _pollTimer: null,\n\n        _noactivityTimer: null,\n\n        _xhr: null,\n\n        lastEventId: null,\n\n        cache: '',\n\n        cursor: 0,\n\n        onerror: null,\n\n        onmessage: null,\n\n        onopen: null,\n\n        readyState: 0,\n\n        // ===================================================================\n        // helpers functions\n        // those are attached to prototype to ease reuse and testing...\n\n        urlWithParams: function (baseURL, params) {\n\n            var encodedArgs = [];\n\n            if (params){\n\n                var key, urlarg;\n                var urlize = encodeURIComponent;\n\n                for (key in params){\n                    if (params.hasOwnProperty(key)) {\n                        urlarg = urlize(key)+'='+urlize(params[key]);\n                        encodedArgs.push(urlarg);\n                    }\n                }\n            }\n\n            if (encodedArgs.length > 0){\n\n                if (baseURL.indexOf('?') == -1)\n                    return baseURL + '?' + encodedArgs.join('&');\n                return baseURL + '&' + encodedArgs.join('&');\n            }\n            return baseURL;\n        },\n\n        lastMessageIndex: function(text) {\n\n            var ln2 =text.lastIndexOf('\\n\\n');\n            var lr2 = text.lastIndexOf('\\r\\r');\n            var lrln2 = text.lastIndexOf('\\r\\n\\r\\n');\n\n            if (lrln2 > Math.max(ln2, lr2)) {\n                return [lrln2, lrln2+4];\n            }\n            return [Math.max(ln2, lr2), Math.max(ln2, lr2) + 2]\n        },\n\n        trimWhiteSpace: function(str) {\n            // to remove whitespaces left and right of string\n\n            var reTrim = /^(\\s|\\u00A0)+|(\\s|\\u00A0)+$/g;\n            return str.replace(reTrim, '');\n        },\n\n        normalizeToLF: function(str) {\n\n            // replace \\r and \\r\\n with \\n\n            return str.replace(/\\r\\n|\\r/g, '\\n');\n        }\n\n    };\n\n    if (!isOldIE()){\n\n        EventSource.isPolyfill = \"XHR\";\n\n        // EventSource will send request using XMLHttpRequest\n        EventSource.prototype.XHR = function(evs) {\n\n            request = new XMLHttpRequest();\n            this._request = request;\n            evs._xhr = this;\n\n            // set handlers\n            request.onreadystatechange = function(){\n                if (request.readyState > 1 && evs.readyState != evs.CLOSED) {\n                    if (request.status == 200 || (request.status>=300 && request.status<400)){\n                        evs._onxhrdata();\n                    }\n                    else {\n                        request._failed = true;\n                        evs.readyState = evs.CLOSED;\n                        evs.dispatchEvent('error', {\n                            type: 'error',\n                            data: \"The server responded with \"+request.status\n                        });\n                        evs.close();\n                    }\n                }\n            };\n\n            request.onprogress = function () {\n            };\n\n            request.open('GET', evs.urlWithParams(evs.URL, evs.getArgs), true);\n\n            var headers = evs.xhrHeaders; // maybe null\n            for (var header in headers) {\n                if (headers.hasOwnProperty(header)){\n                    request.setRequestHeader(header, headers[header]);\n                }\n            }\n            if (evs.lastEventId) {\n                request.setRequestHeader('Last-Event-Id', evs.lastEventId);\n            }\n\n            request.send();\n        };\n\n        EventSource.prototype.XHR.prototype = {\n\n            useXDomainRequest: false,\n\n            _request: null,\n\n            _failed: false, // true if we have had errors...\n\n            isReady: function() {\n\n\n                return this._request.readyState >= 2;\n            },\n\n            isDone: function() {\n\n                return (this._request.readyState == 4);\n            },\n\n            hasError: function() {\n\n                return (this._failed || (this._request.status >= 400));\n            },\n\n            getBuffer: function() {\n\n                var rv = '';\n                try {\n                    rv = this._request.responseText || '';\n                }\n                catch (e){}\n                return rv;\n            },\n\n            abort: function() {\n\n                if ( this._request ) {\n                    this._request.abort();\n                }\n            }\n        };\n    }\n    else {\n\n\tEventSource.isPolyfill = \"IE_8-9\";\n\n        // patch EventSource defaultOptions\n        var defaults = EventSource.prototype.defaultOptions;\n        defaults.xhrHeaders = null; // no headers will be sent\n        defaults.getArgs['evs_preamble'] = 2048 + 8;\n\n        // EventSource will send request using Internet Explorer XDomainRequest\n        EventSource.prototype.XHR = function(evs) {\n\n            request = new XDomainRequest();\n            this._request = request;\n\n            // set handlers\n            request.onprogress = function(){\n                request._ready = true;\n                evs._onxhrdata();\n            };\n\n            request.onload = function(){\n                this._loaded = true;\n                evs._onxhrdata();\n            };\n\n            request.onerror = function(){\n                this._failed = true;\n                evs.readyState = evs.CLOSED;\n                evs.dispatchEvent('error', {\n                    type: 'error',\n                    data: \"XDomainRequest error\"\n                });\n            };\n\n            request.ontimeout = function(){\n                this._failed = true;\n                evs.readyState = evs.CLOSED;\n                evs.dispatchEvent('error', {\n                    type: 'error',\n                    data: \"XDomainRequest timed out\"\n                });\n            };\n\n            // XDomainRequest does not allow setting custom headers\n            // If EventSource has enabled the use of GET arguments\n            // we add parameters to URL so that server can adapt the stream...\n            var reqGetArgs = {};\n            if (evs.getArgs) {\n\n                // copy evs.getArgs in reqGetArgs\n                var defaultArgs = evs.getArgs;\n                    for (var key in defaultArgs) {\n                        if (defaultArgs.hasOwnProperty(key)){\n                            reqGetArgs[key] = defaultArgs[key];\n                        }\n                    }\n                if (evs.lastEventId){\n                    reqGetArgs['evs_last_event_id'] = evs.lastEventId;\n                }\n            }\n            // send the request\n\n            request.open('GET', evs.urlWithParams(evs.URL,reqGetArgs));\n            request.send();\n        };\n\n        EventSource.prototype.XHR.prototype = {\n\n            useXDomainRequest: true,\n\n            _request: null,\n\n            _ready: false, // true when progress events are dispatched\n\n            _loaded: false, // true when request has been loaded\n\n            _failed: false, // true if when request is in error\n\n            isReady: function() {\n\n                return this._request._ready;\n            },\n\n            isDone: function() {\n\n                return this._request._loaded;\n            },\n\n            hasError: function() {\n\n                return this._request._failed;\n            },\n\n            getBuffer: function() {\n\n                var rv = '';\n                try {\n                    rv = this._request.responseText || '';\n                }\n                catch (e){}\n                return rv;\n            },\n\n            abort: function() {\n\n                if ( this._request){\n                    this._request.abort();\n                }\n            }\n        };\n    }\n\n    function MessageEvent(type, data, origin, lastEventId) {\n\n        this.bubbles = false;\n        this.cancelBubble = false;\n        this.cancelable = false;\n        this.data = data || null;\n        this.origin = origin || '';\n        this.lastEventId = lastEventId || '';\n        this.type = type || 'message';\n    }\n\n    function isOldIE () {\n\n        //return true if we are in IE8 or IE9\n        return (window.XDomainRequest && (window.XMLHttpRequest && new XMLHttpRequest().responseType === undefined)) ? true : false;\n    }\n\n    global[evsImportName] = EventSource;\n})(this);\n"
  },
  {
    "path": "javascript/SpecConcurrentRunner.html",
    "content": "<!DOCTYPE HTML>\n<html>\t\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n  <title>Polyfill EventSource Test Suite</title>\n\n  <link rel=\"shortcut icon\" type=\"image/png\" href=\"lib/jasmine-2.0.0/jasmine_favicon.png\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"lib/jasmine-2.0.0/jasmine.css\">\n\n  <script type=\"text/javascript\" src=\"lib/jasmine-2.0.0/jasmine.js\"></script>\n  <script type=\"text/javascript\" src=\"lib/jasmine-2.0.0/jasmine-html.js\"></script>\n  <script type=\"text/javascript\" src=\"lib/jasmine-2.0.0/boot.js\"></script>\n\n  <!-- include source files here... -->\n  <!--<script type=\"text/javascript\" src=\"src/EventSource.js\"></script>-->\n    <script>\n        // We define a prefix prior to importing evs\n        window._eventSourceImportPrefix = 'Test';\n    </script>\n    <script type=\"text/javascript\" src=\"src/eventsource.js\"></script>\n\n  <!-- include spec files here... -->\n    <!--<script type=\"text/javascript\" src=\"spec/EventSourceSpec.js\"></script>-->\n    <script type=\"text/javascript\" src=\"spec/concurrentSpec.js\"></script>\n\n</head>\n\n<body>\n\n<h1> Concurrent polyfill EventSource Test Suite </h1>\n\n<div class=\"test-infos\">\n  <p>\n      Be patient, those tests need a few minutes to complete.\n  </p>\n\n  <script>\n      if (window[\"EventSource\"]) {\n\t  document.write('<p>This browser supports EventSource natively</p>')\n      }\n      else {\n\t  document.write(\"<p>This browser doesn't support EventSource</p>\")\n      }\n  </script>\n</div>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "javascript/SpecRunner.html",
    "content": "<!DOCTYPE HTML>\n<html>\t\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n  <title>Polyfill EventSource Test Suite</title>\n\n  <link rel=\"shortcut icon\" type=\"image/png\" href=\"lib/jasmine-2.0.0/jasmine_favicon.png\">\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"lib/jasmine-2.0.0/jasmine.css\">\n\n  <script type=\"text/javascript\" src=\"lib/jasmine-2.0.0/jasmine.js\"></script>\n  <script type=\"text/javascript\" src=\"lib/jasmine-2.0.0/jasmine-html.js\"></script>\n  <script type=\"text/javascript\" src=\"lib/jasmine-2.0.0/boot.js\"></script>\n\n  <!-- include source files here... -->\n  <!--<script type=\"text/javascript\" src=\"src/EventSource.js\"></script>-->\n    <script>\n        // We define a prefix prior to importing evs\n        window._eventSourceImportPrefix = 'Test';\n    </script>\n    <script type=\"text/javascript\" src=\"src/eventsource.js\"></script>\n\n  <!-- include spec files here... -->\n    <!--<script type=\"text/javascript\" src=\"spec/EventSourceSpec.js\"></script>-->\n    <script type=\"text/javascript\" src=\"spec/eventsourceSpec.js\"></script>\n\n</head>\n\n<body>\n\n<h1> Polyfill EventSource Test Suite </h1>\n\n<div class=\"test-infos\">\n  <p>\n      Be patient, those tests need a few minutes to complete.\n  </p>\n\n  <script>\n      if (window[\"EventSource\"]) {\n\t  document.write('<p>This browser supports EventSource natively</p>')\n      }\n      else {\n\t  document.write(\"<p>This browser doesn't support EventSource</p>\")\n      }\n  </script>\n</div>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "javascript/spec/concurrentSpec.js",
    "content": "\ndescribe('Test concurrent operation of various EventSource against real stream', function(){\n\n    var polyfillEventSource = window['TestEventSource'];\n\n    var nativeEventSource = window['EventSource'];\n\n    var TEST_SOURCE_URLS = [\n\t\"/test/eventsource/4-messages-with-seed-01\",\n\t\"/test/eventsource/6-messages-with-seed-02\",\n\t\"/test/eventsource/8-messages-closeat-4-with-seed-03\",\n\t\"/test/eventsource/16-messages-closeat-5-with-seed-04\",\n    ];\n\n\n    jasmine.DEFAULT_TIMEOUT_INTERVAL = 300000;\n    \n    var WM = function (EventSourceFactory, EventSourceName, sourceUrls){\n\t\n\tvar self = {};\n\t\n\tvar _count = sourceUrls.length;\n\t\n\tself.setdonecb = function(donefunc){\n\n\t    if (_count <= 0 && donefunc ){\n\t\tdonefunc();\n\t    }\n\t    self.donecallback = donefunc;\n\t};\n\n\tself.complete = function(){\n\n\t    --_count;\n\t    if (_count <= 0 && self.donecallback){\n\t\tself.donecallback();\n\t    }\n\t};\n\n\tfunction EVS(srcUrl){\n\n\t    var evs = new EventSourceFactory(srcUrl);\n\t    var ctx = this;\n\n\t    ctx.eventsource = evs;\n\n\t    ctx.msgEvents = [];\n\t    evs.addEventListener('message', function (e) {\n\t\tif (e.data) {ctx.msgEvents.push(e.data);}\n\t    }, false);\n\n\t    ctx.openEvents = [];\n\t    evs.addEventListener('open', function (e) {\n\t\tctx.openEvents.push(e.type);\n\t    }, false);\n\n\t    ctx.errorEvents = [];\n\t    evs.addEventListener('error', function (e) {\n\t\tctx.errorEvents.push(e.type);\n\t    }, false);\n\n\t    ctx.testmetaEvents = [];\n\t    evs.addEventListener('testmeta', function (e) {\n\t\tctx.testmetaEvents = JSON.parse(e.data);\n\t    }, false);\n\n\t    ctx.testendEvents = [];\n\t    evs.addEventListener('testend', function (e) {\n\t\tctx.testendEvents.push(e.type);\n\t\tself.complete();\n\t\tevs.close();\n\t    }, false);\n\t};\n\t\n\t// create one EventSource for each url in sourceUrls\n\tself.sources = [];\n\tfor (var i=0; i < sourceUrls.length; i++){\n\n\t    console.log(\"creating new \"+EventSourceName+\" on \"+sourceUrls[i]);\n\t    self.sources.push(new EVS(sourceUrls[i]));\n\t}\n\n\treturn self;\n    };\n\n\n    describe(\"consume 4 sources concurrently\", function(){\n\n        describe('using polyfill eventsource', function () {\n\n\t    var wm = WM(polyfillEventSource,\"polyfill EventSource\", TEST_SOURCE_URLS);\n\n            beforeEach(function (done) {\n\n\t\tthis.sources = wm.sources;\n\t\twm.setdonecb(done);\n            });\n\t    \n\t    afterEach(function (done){\n\n\t\tconsole.log(\"polyfill EventSource SPEC OVER...\");\n\t\tdone();\n\t    });\n\n\n\t    it(\"sources.length shall be equal to TEST_SOURCE_URLS.length\", function (){\n\n\t\texpect(this.sources.length).toEqual(TEST_SOURCE_URLS.length);\n\t    });\n\n\t    it(\"data in testmeta event should match all incoming messages data\", function () {\n\n\t\tvar result;\n\t\tfor (var i=0; i < this.sources.length; i++){\n\n\t\t    result = this.sources[i];\n\t\t    expect(result.testmetaEvents).toEqual(result.msgEvents);\n\t\t};\n\t    });\n\n\t    it(\"testend event was received\", function () {\n\n\t\tvar result;\n\t\tfor (var i=0; i < this.sources.length; i++){\n\n\t\t    result = this.sources[i];\n\t\t    expect(result.testendEvents.length).toEqual(1);\n\t\t};\n\t    });\n        });\n\n\tif (nativeEventSource){\n\n\t    describe('using native eventsource', function () {\n\n\t\tvar wm = WM(nativeEventSource,\"native EventSource\", TEST_SOURCE_URLS);\n\n\t\tbeforeEach(function (done) {\n\t\t    this.sources = wm.sources;\n\t\t    wm.setdonecb(done);\n\t\t});\n\n\t\tafterEach(function (done){\n\n\t\t    console.log(\"native EventSource SPEC OVER...\");\n\t\t    done();\n\t\t});\n\n\t\tit(\"sources.length shall be equal to TEST_SOURCE_URLS.length\", function (){\n\n\t\t    expect(this.sources.length).toEqual(TEST_SOURCE_URLS.length);\n\t\t});\n\n\t\tit(\"data in testmeta event should match all incoming messages data\", function () {\n\n\t\t    var result;\n\t\t    for (var i=0; i < this.sources.length; i++){\n\n\t\t\tresult = this.sources[i];\n\t\t\texpect(result.testmetaEvents).toEqual(result.msgEvents);\n\t\t    };\n\t\t});\n\n\t\tit(\"testend event was received\", function () {\n\n\t\t    var result;\n\t\t    for (var i=0; i < this.sources.length; i++){\n\n\t\t\tresult = this.sources[i];\n\t\t\texpect(result.testendEvents.length).toEqual(1);\n\t\t    };\n\t\t});\n\t    });\n\t};\n    });\n});\n"
  },
  {
    "path": "javascript/spec/eventsourceSpec.js",
    "content": "/**\n * Created by vasi on 4/8/14.\n */\n\nvar evsName = \"EventSource\",\n    evsImportName = (window._eventSourceImportPrefix || '') + evsName,\n    isEventSourceSupported = (window[\"EventSource\"] != undefined);\n\ndescribe(\"Testing EventSource polyfill with MockupXHR\", function() {\n\n    var previousXHR;\n\n    beforeEach(function() {\n\n        var mockupXHR = function(evs) {\n\n            this.responseText = '';\n            this.evs = evs;\n        };\n\n        mockupXHR.prototype = {\n\n            useXDomaineRequest: false,\n\n            _request: null,\n\n            _failed: false,\n\n            isReady: function() {\n\n                return true;\n            },\n\n            isDone: function() {\n\n                return false;\n            },\n\n            hasError: function() {\n\n                return (this._failed);\n            },\n\n            getBuffer: function() {\n\n                return this.responseText;\n            },\n\n            abort: function() {\n\n                this.responseText = '';\n            },\n\n            sendData: function(str) {\n\n                this.responseText += str;\n                this.evs._onxhrdata();\n            }\n        };\n\n        this.eventSource = window[evsImportName];\n        previousXHR = this.eventSource.prototype.XHR;\n        this.eventSource.prototype.XHR = mockupXHR;\n    });\n\n    afterEach(function () {\n        this.eventSource.prototype.XHR = previousXHR;\n    });\n\n    describe(\"Provide MockupXHR class to ease testing the EventSource class\", function() {\n        var evs;\n\n        beforeEach(function (done) {\n\n            evs = new this.eventSource('http://exampleLoadMockupXHR.com');\n            setTimeout(function () {\n                done();\n            }, 0);\n        });\n\n        afterEach(function (){\n\n            evs.close();\n        });\n\n        it(\"should load mockupXHR instead of native XHR\", function() {\n            // the prefix is set in SpecRunner.html\n\n            expect(evs._xhr.sendData).toBeDefined();\n        });\n    });\n\n    describe(\"Setting up the polyfill EventSource for it to be available alongside browser native EventSource\", function() {\n\n        it(\"When no options object is passed to the EventSource constructor, all option in EventSource.prototype.defaultOptions are set.\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var evs = new this.eventSource('http://example.com');\n            var expected = evs.defaultOptions;  // in this test we expect all options to be the default ones\n\n            var actual = {\n                loggingEnabled: evs.loggingEnabled,\n                loggingPrefix: evs.loggingPrefix,\n                interval: evs.interval,\n                bufferSizeLimit: evs.bufferSizeLimit, // bytes\n                silentTimeout: evs.silentTimeout, // milliseconds\n                getArgs: evs.getArgs,\n                xhrHeaders: evs.xhrHeaders\n            };\n\n            expect(actual).toEqual(expected);\n\n            evs.close();\n        });\n\n        it(\"When options is passed to constructor, all option not defined in defaultOptions are ignored.\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var extraOptions = {\n                extraOption: 1000\n            };\n            var evs = new this.eventSource('http://example.com', extraOptions);\n\n            expect(evs.extraOption).toBeUndefined();\n\n            evs.close();\n        });\n\n        it(\"When options is passed to constructor, option defined in defaultOptions are over written.\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var options = {\n                interval: 1000,\n                bufferSizeLimit: 256*1024 // bytes\n            };\n\n            var evs = new this.eventSource('http://example.com', options);\n            var defaults = evs.defaultOptions;\n\n            var expected = {\n                interval: options.interval,\n                bufferSizeLimit: options.bufferSizeLimit,\n                silentTimeout: defaults.silentTimeout, // milliseconds\n                getArgs: defaults.getArgs,\n                xhrHeaders: defaults.xhrHeaders\n            };\n\n            var actual = {\n                interval: evs.interval,\n                bufferSizeLimit: evs.bufferSizeLimit, // bytes\n                silentTimeout: evs.silentTimeout, // milliseconds\n                getArgs: evs.getArgs,\n                xhrHeaders: evs.xhrHeaders\n            };\n\n            expect(actual).toEqual(expected);\n\n            evs.close();\n        });\n    });\n\n    describe(\"tests for urlWithParams\", function() {\n\n        it(\"When the baseURL already contains arguments, the rest of evs arguments are appended.\", function() {\n\n            var params =  {\n                a: 1,\n                b: 2\n            };\n            var evs = new this.eventSource('http://example.com?ciao=ola');\n            var urlWithParams = evs.urlWithParams;\n            expect(urlWithParams('http://example.com?ciao=ola', params)).toBe('http://example.com?ciao=ola&a=1&b=2');\n        })\n\n        it(\"When called with a null or undefined params, baseUrl is returned.\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var evs = new this.eventSource('http://exampleurlWithParams.com');\n            var urlWithParams = evs.urlWithParams;\n            expect(urlWithParams('http://example.com')).toBe('http://example.com');\n        });\n\n        it(\"When called with a params object that contains no attribute but has a prototype with attributes, baseUrl is returned.\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var Params = function () {};\n            Params.prototype = {\n                'a': 1,\n                'b': 2\n            };\n            var evs = new this.eventSource('http://exampleurlWithParams.com');\n            var urlWithParams = evs.urlWithParams;\n            expect(urlWithParams('http://example.com', new Params())).toBe('http://example.com');\n        });\n\n        it(\"When called with a params that define arguments, those are added to the url\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var params =  {\n                a: 1,\n                b: 2\n            };\n            var evs = new this.eventSource('http://exampleurlWithParams.com');\n            var urlWithParams = evs.urlWithParams;\n            expect(urlWithParams('http://example.com', params)).toBe('http://example.com?a=1&b=2');\n        });\n    });\n\n    describe(\"tests for lastLineIndex\", function() {\n\n        it(\"returns correct lastMessageIndex in case of \\\\n\\\\n\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var evs = new this.eventSource('http://exampleurlWithParams.com');\n            var lastLineIndex = evs.lastMessageIndex;\n            expect(lastLineIndex(\"0123\\r\\r678\\n\\n9\")).toEqual([9, 11]);\n        });\n\n        it(\"returns correct lastLineIndex in case of \\\\r\\\\r\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var evs = new this.eventSource('http://exampleurlWithParams.com');\n            var lastLineIndex = evs.lastMessageIndex;\n            expect(lastLineIndex(\"0123\\n\\n678\\r\\r\")).toEqual([9, 11]);\n        });\n\n        it(\"returns correct lastLineIndex in case of \\\\r\\\\n\\\\r\\\\n\", function() {\n            // the prefix is set in SpecRunner.html\n\n            var evs = new this.eventSource('http://exampleurlWithParams.com');\n            var lastLineIndex = evs.lastMessageIndex;\n            expect(lastLineIndex(\"0123\\n\\n67\\r\\n\\r\\n\")).toEqual([8, 12]);\n        });\n    });\n\n    describe(\"tests for trimWhiteSpace\", function() {\n\n        it(\"should remove whitespace left and right of the string\", function () {\n            // the prefix is set in SpecRunner.html\n\n            var evs = new this.eventSource('http://exampleurlWithParams.com');\n            var trimWhiteSpace = evs.trimWhiteSpace;\n            expect(trimWhiteSpace(\"       text between spaces    \")).toBe('text between spaces');\n        });\n    });\n\n    describe(\"tests for normalizeToLF\", function() {\n\n        it(\"should replace CR and CRLF with LF(\\\\n) inside a string\", function () {\n            // the prefix is set in SpecRunner.html\n\n            var evs = new this.eventSource('http://example.com');\n            var normalizeToLF = evs.normalizeToLF;\n\n            var str = \"LF:\\n. CR:\\r. CRLF:\\r\\n. and again LF:\\n. CR:\\r. CRLF:\\r\\n. Double CR:\\r\\r. Double CRLF:\\r\\n\\r\\n\";\n            var expectedStr = \"LF:\\n. CR:\\n. CRLF:\\n. and again LF:\\n. CR:\\n. CRLF:\\n. Double CR:\\n\\n. Double CRLF:\\n\\n\";\n            var actualStr = normalizeToLF(str);\n\n            expect(actualStr).toBe(expectedStr);\n\n            evs.close();\n        });\n    });\n\n    describe(\"Simulates EventSource stream using MockupXHR\", function() {\n\n        var evs, receivedMessageEvents, receivedOpenEvents, receivedErrorEvents;\n\n        beforeEach(function (done) {\n\n            evs = new this.eventSource('http://exampleSimulatesEventSourceWithMockupXHR.com');\n            receivedMessageEvents = [];\n            receivedOpenEvents = [];\n            receivedErrorEvents = [];\n            evs.addEventListener('message', function(e) {\n                receivedMessageEvents.push(e.data);\n            }, false);\n            evs.addEventListener('open', function(e) {\n                receivedOpenEvents.push(e.type);\n            }, false);\n            evs.addEventListener('error', function(e) {\n                receivedErrorEvents.push(e.type);\n            }, false);\n\n            setTimeout(function () {\n                done();\n            }, 0)\n        });\n\n        afterEach(function (done) {\n            evs.close();\n            done();\n        });\n\n        it(\"check that Mockup XHR is used\", function () {\n\n            expect(evs._xhr.sendData).toBeDefined();\n        });\n\n        it(\"Initial BOM mark is skipped\", function () {\n\n            evs._xhr.sendData('\\ufeffdata: \"First line of data.\"\\r\\r');\n            var expectedEvents = ['\"First line of data.\"'];\n            expect(receivedMessageEvents).toEqual(expectedEvents);\n        });\n\n        it(\"Multiline message are properly reassembled\", function () {\n\n            evs._xhr.sendData('data: \"First line of data.\"\\ndata: \"Second line of data.\"\\r\\r');\n            var expectedEvents = ['\"First line of data.\"\\n\"Second line of data.\"'];\n            expect(receivedMessageEvents).toEqual(expectedEvents);\n        });\n\n        it(\"Chunky transmissions are properly buffered\", function () {\n\n            evs._xhr.sendData('data: \"First line of data.\"\\ndata: \"Second line of data.\"\\n\\ndata: \"First part of broken message.');\n            evs._xhr.sendData('Second part of broken message.\"\\n\\n');\n            var expectedEvents = ['\"First line of data.\"\\n\"Second line of data.\"', '\"First part of broken message.Second part of broken message.\"'];\n            expect(receivedMessageEvents).toEqual(expectedEvents);\n        });\n\n        it(\"id lines are properly processed\", function () {\n\n            var expectedEventId = '1983';\n            evs._xhr.sendData('data: \"First line of data.\"\\ndata: \"Second line of data.\"\\n\\ndata: \"First part of broken message.');\n            evs._xhr.sendData('Second part of broken message.\"\\n\\nid: '+expectedEventId+'\\ndata: \"Message with new id\"\\n\\n');\n            expect(evs.lastEventId).toEqual(expectedEventId);\n        });\n\n        it(\"option bufferSizeLimit is taken into account\", function () {\n\n            evs.bufferSizeLimit = 100;\n            evs._xhr.sendData('data: \"First line of data.\"\\ndata: \"Second line of data.\"\\n\\ndata: \"First part of broken message.');\n            evs._xhr.sendData('Second part of broken message.\"\\n\\nid: 1983\\rdata: \"Message with new id\"\\n\\n');\n            evs._xhr.sendData('data: \"Message to force dispatching the open event\"\\n\\n');\n            var expectedOpenEvents = [\"open\", \"open\"];\n            expect(receivedOpenEvents).toEqual(expectedOpenEvents);\n        });\n\n        describe(\"Manually ticking the Jasmine Clock to produce a sleep until events are fired:\", function () {\n\n            var previous = 0;\n\n            beforeEach(function() {\n\n                previous = evs.silentTimeout;\n                jasmine.clock().install();\n            });\n\n            afterEach(function(done) {\n\n                evs.silentTimeout = previous;\n                jasmine.clock().uninstall();\n\n                done();\n            });\n\n            it(\"option silentTimeout is taken into account\", function () {\n\n                evs.silentTimeout = 1000;\n                evs._xhr.sendData('data: \"First line of data.\"\\ndata: \"Second line of data.\"\\n\\r\\ndata: \"First part of broken message.');\n                evs._xhr.sendData('Second part of broken message.\"\\n\\r\\nid: 1983\\rdata: \"Message with new id\"\\n\\n');\n\n                setTimeout(function() {\n                }, 1100);\n                jasmine.clock().tick(1101);\n\n                evs._xhr.sendData('data: \"Message to force dispatching the open event\"\\n\\n');\n                var expectedOpenEvents = [\"open\", \"open\"];\n                var expectedErrorEvents = [\"error\"];\n\n                expect(receivedOpenEvents).toEqual(expectedOpenEvents);\n                expect(receivedErrorEvents).toEqual(expectedErrorEvents);\n            });\n        });\n    });\n});\n\ndescribe(\"Evaluating EventSource 'time to attach listener' doubt\", function() {\n\n    var evs, previousXHR,\n        receivedMessageEvents = [],\n        receivedOpenEvents = [],\n        receivedErrorEvents = [];\n\n    beforeEach(function (done) {\n\n        var mockupXHR = function (evs) {\n\n            this.responseText = '';\n            this.evs = evs;\n            evs._xhr = this;\n\n            // send data right away to stress the events\n            this.sendData(this.initialData || '');\n        };\n\n        mockupXHR.prototype = {\n\n            initialData: 'data: \"I will fire very quick\"\\n\\n',\n\n            useXDomaineRequest: false,\n\n            _request: null,\n\n            _failed: false,\n\n            isReady: function () {\n\n                return true;\n            },\n\n            isDone: function () {\n\n                return false;\n            },\n\n            hasError: function () {\n\n                return (this._failed);\n            },\n\n            getBuffer: function () {\n\n                return this.responseText;\n            },\n\n            abort: function () {\n\n            },\n\n            sendData: function (str) {\n\n                evs = this.evs;\n                this.responseText += str;\n                evs._onxhrdata();\n            }\n        };\n\n        this.eventSource = window[evsImportName];\n        previousXHR = this.eventSource.prototype.XHR;\n        this.eventSource.prototype.XHR = mockupXHR;\n\n        evs = new this.eventSource('http://exampleTimeToAttachDoubt.com');\n\n        evs.addEventListener('message', function(e) {\n            receivedMessageEvents.push(e.data);\n        }, false);\n        evs.addEventListener('open', function(e) {\n            receivedOpenEvents.push(e.type);\n        }, false);\n        evs.addEventListener('error', function(e) {\n            receivedErrorEvents.push(e.data);\n        }, false);\n        setTimeout(function () {\n            done();\n        }, 0);\n    });\n\n    afterEach(function (done) {\n        evs.close();\n        this.eventSource.prototype.XHR = previousXHR;\n        done();\n    });\n\n    it(\"should catch initial message sent\", function (done) {\n\n        var expectedMessageEvents = ['\"I will fire very quick\"'];\n        expect(receivedMessageEvents).toEqual(expectedMessageEvents);\n        done();\n    })\n});\n\ndescribe('Failed XHR request(invalid url) shall trigger EventSource to close and \"error\" event to be dispatched', function() {\n\n    var evs,\n        receivedMessageEvents = [],\n        receivedOpenEvents = [],\n        receivedErrorEvents = [];\n\n    beforeEach(function (done) {\n\n        this.eventSource = window[evsImportName];\n\n        // sending to wrong url\n        evs = new this.eventSource('http://exampleFailedXHRequest');\n\n        evs.addEventListener('message', function (e) {\n            receivedMessageEvents.push(e.data);\n        }, false);\n        evs.addEventListener('open', function (e) {\n            receivedOpenEvents.push(e.type);\n        }, false);\n        evs.addEventListener('error', function (e) {\n            receivedErrorEvents.push(e.type);\n            done();\n        }, false);\n    });\n\n    afterEach(function (done) {\n\n        evs.close();\n        receivedErrorEvents = [];\n        done();\n    });\n\n    it(\"should send error event\", function (done) {\n\n        expect(receivedErrorEvents.length).toBe(1);\n        done();\n    });\n});\n\ndescribe('Failed XHR request(missing-code 404) shall trigger EventSource to close and \"error\" event to be dispatched', function() {\n\n    var evs,\n        receivedMessageEvents = [],\n        receivedOpenEvents = [],\n        receivedErrorEvents = [];\n\n    beforeEach(function (done) {\n\n        this.eventSource = window[evsImportName];\n\n        // sending to wrong url\n        evs = new this.eventSource('/missing');\n\n        evs.addEventListener('message', function (e) {\n            receivedMessageEvents.push(e.data);\n        }, false);\n        evs.addEventListener('open', function (e) {\n            receivedOpenEvents.push(e.type);\n        }, false);\n        evs.addEventListener('error', function (e) {\n            receivedErrorEvents.push(e.type);\n            done();\n        }, false);\n    });\n\n    afterEach(function () {\n\n        evs.close()\n    });\n\n    it(\"should send error event\", function (done) {\n\n        expect(receivedErrorEvents.length).toBe(1);\n        done();\n    });\n});\n\ndescribe('Tests with twisted server:', function() {\n\n\n    var evs,\n        receivedMessageEvents = [],\n        receivedOpenEvents = [],\n        receivedErrorEvents = [],\n        receivedTestMetaEvents = [],\n        receivedTestEndEvents = [];\n\n    function addEventListeners(evs, done) {\n\n        evs.addEventListener('message', function (e) {\n//            if (e.lastEventId) {console.log(e.lastEventId);}\n//            console.log('message: ' + e.type + \":\" + e.data)\n//            console.log(e)\n            if (e.data) {receivedMessageEvents.push(e.data);}\n        }, false);\n        evs.addEventListener('open', function (e) {\n            receivedOpenEvents.push(e.type);\n        }, false);\n        evs.addEventListener('error', function (e) {\n            receivedErrorEvents.push(e.type);\n        }, false);\n        evs.addEventListener('testmeta', function (e) {\n//            console.log(\"received testmeta:\" + e.type + \"[\"+ evs.id +\"]: \"+ e.data);\n//            console.log(e)\n            receivedTestMetaEvents = JSON.parse(e.data);\n        }, false);\n        evs.addEventListener('testend', function (e) {\n            receivedTestEndEvents.push(e.type);\n//            console.log('received tesend' + e.type + \":\" + e.data + \"end\");\n//            console.log(e)\n            done();\n        }, false);\n    }\n\n    afterEach(function (done) {\n\n        evs.close();\n        receivedErrorEvents = [];\n        receivedMessageEvents = [];\n        receivedTestEndEvents = [];\n        receivedTestMetaEvents = [];\n        receivedOpenEvents = [];\n        done();\n    });\n\n    describe('4-messages-with-seed-01', function() {\n\n        var twistedUrl = '/test/eventsource/4-messages-with-seed-01';\n\n        describe('using polyfill eventsource', function () {\n\n            beforeEach(function (done) {\n\n                var eventSource = window[evsImportName];\n\n                evs = new eventSource(twistedUrl);\n                addEventListeners(evs, done);\n            });\n\n            describe ('after a complete run until testend:', function() {\n\n                it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                });\n\n                it(\"testend event is received\", function () {\n\n                    expect(receivedTestEndEvents.length).toEqual(1);\n                })\n            });\n        });\n\n        if (isEventSourceSupported) {\n\n            describe('using native eventsource', function () {\n\n                beforeEach(function (done) {\n\n                    var eventSource = window[evsName];\n\n                    evs = new eventSource(twistedUrl);\n                    addEventListeners(evs, done);\n                });\n\n                describe ('after a complete run until testend:', function() {\n\n                    it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                    });\n\n                    it(\"testend event is received\", function () {\n\n                        expect(receivedTestEndEvents.length).toEqual(1);\n                    })\n                });\n            })\n        }\n    });\n\n    describe('6-messages-with-seed-02', function() {\n\n        var twistedUrl = '/test/eventsource/6-messages-with-seed-02';\n\n        describe('using polyfill eventsource', function () {\n\n            beforeEach(function (done) {\n\n                var eventSource = window[evsImportName];\n\n                evs = new eventSource(twistedUrl);\n                addEventListeners(evs, done);\n            });\n\n            describe ('after a complete run until testend:', function() {\n\n                it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                });\n\n                it(\"testend event is received\", function () {\n\n                    expect(receivedTestEndEvents.length).toEqual(1);\n                })\n            });\n        });\n\n        if (isEventSourceSupported) {\n\n            describe('using native eventsource', function () {\n\n                beforeEach(function (done) {\n\n                    var eventSource = window[evsName];\n\n                    evs = new eventSource(twistedUrl);\n                    addEventListeners(evs, done);\n                });\n\n                describe ('after a complete run until testend:', function() {\n\n                    it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                    });\n\n                    it(\"testend event is received\", function () {\n\n                        expect(receivedTestEndEvents.length).toEqual(1);\n                    })\n                });\n            })\n        }\n    });\n\n    describe('8-messages-closeat-4-with-seed-03', function() {\n\n        var twistedUrl = '/test/eventsource/8-messages-closeat-4-with-seed-03';\n\n        describe('using polyfill eventsource', function () {\n\n            beforeEach(function (done) {\n\n                var eventSource = window[evsImportName];\n\n                evs = new eventSource(twistedUrl);\n                addEventListeners(evs, done);\n            });\n\n            describe ('after a complete run until testend:', function() {\n\n                it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                });\n\n                it(\"testend event is received\", function () {\n\n                    expect(receivedTestEndEvents.length).toBe(1);\n                });\n\n                it(\"at least 1 error message are received, meaning 1 reconnection\", function () {\n\n                    expect(receivedErrorEvents.length).toBeGreaterThan(0); // we expect at least 1\n                });\n            });\n        });\n\n        if (isEventSourceSupported) {\n\n            describe('using native eventsource', function () {\n\n                beforeEach(function (done) {\n\n                    var eventSource = window[evsName];\n\n                    evs = new eventSource(twistedUrl);\n                    addEventListeners(evs, done);\n                });\n\n                describe ('after a complete run until testend:', function() {\n\n                    it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                    });\n\n                    it(\"testend event is received\", function () {\n\n                        expect(receivedTestEndEvents.length).toBe(1);\n                    });\n\n                    it(\"at least 1 error messages are received, meaning 1 reconnection\", function () {\n\n                        expect(receivedErrorEvents.length).toBeGreaterThan(0);\n                    });\n                });\n            })\n        }\n    });\n\n    describe('16-messages-closeat-5-with-seed-04', function() {\n\n        var twistedUrl = '/test/eventsource/16-messages-closeat-5-with-seed-04';\n\n        describe('using polyfill eventsource', function () {\n\n            beforeEach(function (done) {\n\n                var eventSource = window[evsImportName];\n\n                evs = new eventSource(twistedUrl);\n                addEventListeners(evs, done);\n            });\n\n            describe ('after a complete run until testend:', function() {\n\n                it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                });\n\n                it(\"testend event is received\", function () {\n\n                    expect(receivedTestEndEvents.length).toBe(1);\n                });\n\n                it(\"3 error messages are received, meaning 3 reconnections\", function () {\n\n                    expect(receivedErrorEvents.length).toBeGreaterThan(1); // we expect at least 3\n                });\n            });\n        });\n\n        if (isEventSourceSupported) {\n\n            describe('using native eventsource', function () {\n\n                beforeEach(function (done) {\n\n                    var eventSource = window[evsName];\n\n                    evs = new eventSource(twistedUrl);\n                    addEventListeners(evs, done);\n                });\n\n                describe ('after a complete run until testend:', function() {\n\n                    it(\"data in testmeta event should match all incoming messages data\", function () {\n\n                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);\n                    });\n\n                    it(\"testend event is received\", function () {\n\n                        expect(receivedTestEndEvents.length).toBe(1);\n                    });\n\n                    it(\"3 error messages are received, meaning 3 reconnections\", function () {\n\n                        expect(receivedErrorEvents.length).toBeGreaterThan(1);\n                    });\n                });\n            })\n        }\n    });\n});\n\n"
  },
  {
    "path": "javascript/src/browserify-eventsource.js",
    "content": "/*\n   * CommonJS module that exports EventSource polyfill version {{VERSION}}\n   * This module is intended for browser side use\n   * =====================================================================\n   * THIS IS A POLYFILL MODULE, SO IT HAS SIDE EFFECTS\n   * IT AUTOMATICALLY CHECKS IF window OBJECT DEFINES EventSource\n   * AND ADD THE EXPORTED ONE IN CASE IT IS UNDEFINED\n   * =====================================================================\n   * Supported by sc AmvTek srl\n   * :email: devel@amvtek.com\n */\n\n\nvar PolyfillEventSource = require('./eventsource.js').EventSource;\nmodule.exports = PolyfillEventSource;\n\n// Add EventSource to window if it is missing...\nif (window && !window.EventSource){\n    window.EventSource = PolyfillEventSource;\n    // Don't break IE < 10\n    if (typeof console !== \"undefined\" && typeof console.log !== \"undefined\"){\n        console.log(\"polyfill-eventsource added missing EventSource to window\");\n    }\n}\n"
  },
  {
    "path": "javascript/src/eventsource.js",
    "content": "/*\n   * EventSource polyfill version {{VERSION}}\n   * Supported by sc AmvTek srl\n   * :email: devel@amvtek.com\n */\n;(function (global) {\n\n    if (global.EventSource && !global._eventSourceImportPrefix){\n        return;\n    }\n\n    var evsImportName = (global._eventSourceImportPrefix||'')+\"EventSource\";\n\n    var EventSource = function (url, options) {\n\n        if (!url || typeof url != 'string') {\n            throw new SyntaxError('Not enough arguments');\n        }\n\n        this.URL = url;\n        this.setOptions(options);\n        var evs = this;\n        setTimeout(function(){evs.poll()}, 0);\n    };\n\n    EventSource.prototype = {\n\n        CONNECTING: 0,\n\n        OPEN: 1,\n\n        CLOSED: 2,\n\n        defaultOptions: {\n\n            loggingEnabled: false,\n\n            loggingPrefix: \"eventsource\",\n\n            interval: 500, // milliseconds\n\n            bufferSizeLimit: 256*1024, // bytes\n\n            silentTimeout: 300000, // milliseconds\n\n            getArgs:{\n                'evs_buffer_size_limit': 256*1024\n            },\n\n            xhrHeaders:{\n                'Accept': 'text/event-stream',\n                'Cache-Control': 'no-cache',\n                'X-Requested-With': 'XMLHttpRequest'\n            }\n        },\n\n        setOptions: function(options){\n\n            var defaults = this.defaultOptions;\n            var option;\n\n            // set all default options...\n            for (option in defaults){\n\n                if ( defaults.hasOwnProperty(option) ){\n                    this[option] = defaults[option];\n                }\n            }\n\n            // override with what is in options\n            for (option in options){\n\n                if (option in defaults && options.hasOwnProperty(option)){\n                    this[option] = options[option];\n                }\n            }\n\n            // if getArgs option is enabled\n            // ensure evs_buffer_size_limit corresponds to bufferSizeLimit\n            if (this.getArgs && this.bufferSizeLimit) {\n\n                this.getArgs['evs_buffer_size_limit'] = this.bufferSizeLimit;\n            }\n\n            // if console is not available, force loggingEnabled to false\n            if (typeof console === \"undefined\" || typeof console.log === \"undefined\") {\n\n                this.loggingEnabled = false;\n            }\n        },\n\n        log: function(message) {\n\n            if (this.loggingEnabled) {\n\n                console.log(\"[\" + this.loggingPrefix +\"]:\" + message)\n            }\n        },\n\n        poll: function() {\n\n            try {\n\n                if (this.readyState == this.CLOSED) {\n                    return;\n                }\n\n                this.cleanup();\n                this.readyState = this.CONNECTING;\n                this.cursor = 0;\n                this.cache = '';\n                this._xhr = new this.XHR(this);\n                this.resetNoActivityTimer();\n\n            }\n            catch (e) {\n\n                // in an attempt to silence the errors\n                this.log('There were errors inside the pool try-catch');\n                this.dispatchEvent('error', { type: 'error', data: e.message });\n            }\n        },\n\n        pollAgain: function (interval) {\n\n            // schedule poll to be called after interval milliseconds\n            var evs = this;\n            evs.readyState = evs.CONNECTING;\n            evs.dispatchEvent('error', {\n                type: 'error',\n                data: \"Reconnecting \"\n            });\n            this._pollTimer = setTimeout(function(){evs.poll()}, interval||0);\n        },\n\n\n        cleanup: function() {\n\n            this.log('evs cleaning up')\n\n            if (this._pollTimer){\n                clearInterval(this._pollTimer);\n                this._pollTimer = null;\n            }\n\n            if (this._noActivityTimer){\n                clearInterval(this._noActivityTimer);\n                this._noActivityTimer = null;\n            }\n\n            if (this._xhr){\n                this._xhr.abort();\n                this._xhr = null;\n            }\n        },\n\n        resetNoActivityTimer: function(){\n\n            if (this.silentTimeout){\n\n                if (this._noActivityTimer){\n                    clearInterval(this._noActivityTimer);\n                }\n                var evs = this;\n                this._noActivityTimer = setTimeout(\n                        function(){ evs.log('Timeout! silentTImeout:'+evs.silentTimeout); evs.pollAgain(); },\n                        this.silentTimeout\n                        );\n            }\n        },\n\n        close: function () {\n\n            this.readyState = this.CLOSED;\n            this.log('Closing connection. readyState: '+this.readyState);\n            this.cleanup();\n        },\n\n        _onxhrdata: function() {\n\n            var request = this._xhr;\n\n            if (request.isReady() && !request.hasError() ) {\n                // reset the timer, as we have activity\n                this.resetNoActivityTimer();\n\n                // move this EventSource to OPEN state...\n                if (this.readyState == this.CONNECTING) {\n                    this.readyState = this.OPEN;\n                    this.dispatchEvent('open', { type: 'open' });\n                }\n\n                var buffer = request.getBuffer();\n\n                if (buffer.length > this.bufferSizeLimit) {\n                    this.log('buffer.length > this.bufferSizeLimit');\n                    this.pollAgain();\n                }\n\n                if (this.cursor == 0 && buffer.length > 0){\n\n                    // skip byte order mark \\uFEFF character if it starts the stream\n                    if (buffer.substring(0,1) == '\\uFEFF'){\n                        this.cursor = 1;\n                    }\n                }\n\n                var lastMessageIndex = this.lastMessageIndex(buffer);\n                if (lastMessageIndex[0] >= this.cursor){\n\n                    var newcursor = lastMessageIndex[1];\n                    var toparse = buffer.substring(this.cursor, newcursor);\n                    this.parseStream(toparse);\n                    this.cursor = newcursor;\n                }\n\n                // if request is finished, reopen the connection\n                if (request.isDone()) {\n                    this.log('request.isDone(). reopening the connection');\n                    this.pollAgain(this.interval);\n                }\n            }\n            else if (this.readyState !== this.CLOSED) {\n\n                this.log('this.readyState !== this.CLOSED');\n                this.pollAgain(this.interval);\n\n                //MV: Unsure why an error was previously dispatched\n            }\n        },\n\n        parseStream: function(chunk) {\n\n            // normalize line separators (\\r\\n,\\r,\\n) to \\n\n            // remove white spaces that may precede \\n\n            chunk = this.cache + this.normalizeToLF(chunk);\n\n            var events = chunk.split('\\n\\n');\n\n            var i, j, eventType, datas, line, retry;\n\n            for (i=0; i < (events.length - 1); i++) {\n\n                eventType = 'message';\n                datas = [];\n                parts = events[i].split('\\n');\n\n                for (j=0; j < parts.length; j++) {\n\n                    line = this.trimWhiteSpace(parts[j]);\n\n                    if (line.indexOf('event') == 0) {\n\n                        eventType = line.replace(/event:?\\s*/, '');\n                    }\n                    else if (line.indexOf('retry') == 0) {\n\n                        retry = parseInt(line.replace(/retry:?\\s*/, ''));\n                        if(!isNaN(retry)) {\n                            this.interval = retry;\n                        }\n                    }\n                    else if (line.indexOf('data') == 0) {\n\n                        datas.push(line.replace(/data:?\\s*/, ''));\n                    }\n                    else if (line.indexOf('id:') == 0) {\n\n                        this.lastEventId = line.replace(/id:?\\s*/, '');\n                    }\n                    else if (line.indexOf('id') == 0) { // this resets the id\n\n                        this.lastEventId = null;\n                    }\n                }\n\n                if (datas.length) {\n                    // dispatch a new event\n                    var event = new MessageEvent(eventType, datas.join('\\n'), window.location.origin, this.lastEventId);\n                    this.dispatchEvent(eventType, event);\n                }\n            }\n\n            this.cache = events[events.length - 1];\n        },\n\n        dispatchEvent: function (type, event) {\n            var handlers = this['_' + type + 'Handlers'];\n\n            if (handlers) {\n\n                for (var i = 0; i < handlers.length; i++) {\n                    handlers[i].call(this, event);\n                }\n            }\n\n            if (this['on' + type]) {\n                this['on' + type].call(this, event);\n            }\n\n        },\n\n        addEventListener: function (type, handler) {\n            if (!this['_' + type + 'Handlers']) {\n                this['_' + type + 'Handlers'] = [];\n            }\n\n            this['_' + type + 'Handlers'].push(handler);\n        },\n\n        removeEventListener: function (type, handler) {\n            var handlers = this['_' + type + 'Handlers'];\n            if (!handlers) {\n                return;\n            }\n            for (var i = handlers.length - 1; i >= 0; --i) {\n                if (handlers[i] === handler) {\n                    handlers.splice(i, 1);\n                    break;\n                }\n            }\n        },\n\n        _pollTimer: null,\n\n        _noactivityTimer: null,\n\n        _xhr: null,\n\n        lastEventId: null,\n\n        cache: '',\n\n        cursor: 0,\n\n        onerror: null,\n\n        onmessage: null,\n\n        onopen: null,\n\n        readyState: 0,\n\n        // ===================================================================\n        // helpers functions\n        // those are attached to prototype to ease reuse and testing...\n\n        urlWithParams: function (baseURL, params) {\n\n            var encodedArgs = [];\n\n            if (params){\n\n                var key, urlarg;\n                var urlize = encodeURIComponent;\n\n                for (key in params){\n                    if (params.hasOwnProperty(key)) {\n                        urlarg = urlize(key)+'='+urlize(params[key]);\n                        encodedArgs.push(urlarg);\n                    }\n                }\n            }\n\n            if (encodedArgs.length > 0){\n\n                if (baseURL.indexOf('?') == -1)\n                    return baseURL + '?' + encodedArgs.join('&');\n                return baseURL + '&' + encodedArgs.join('&');\n            }\n            return baseURL;\n        },\n\n        lastMessageIndex: function(text) {\n\n            var ln2 =text.lastIndexOf('\\n\\n');\n            var lr2 = text.lastIndexOf('\\r\\r');\n            var lrln2 = text.lastIndexOf('\\r\\n\\r\\n');\n\n            if (lrln2 > Math.max(ln2, lr2)) {\n                return [lrln2, lrln2+4];\n            }\n            return [Math.max(ln2, lr2), Math.max(ln2, lr2) + 2]\n        },\n\n        trimWhiteSpace: function(str) {\n            // to remove whitespaces left and right of string\n\n            var reTrim = /^(\\s|\\u00A0)+|(\\s|\\u00A0)+$/g;\n            return str.replace(reTrim, '');\n        },\n\n        normalizeToLF: function(str) {\n\n            // replace \\r and \\r\\n with \\n\n            return str.replace(/\\r\\n|\\r/g, '\\n');\n        }\n\n    };\n\n    if (!isOldIE()){\n\n        EventSource.isPolyfill = \"XHR\";\n\n        // EventSource will send request using XMLHttpRequest\n        EventSource.prototype.XHR = function(evs) {\n\n            request = new XMLHttpRequest();\n            this._request = request;\n            evs._xhr = this;\n\n            // set handlers\n            request.onreadystatechange = function(){\n                if (request.readyState > 1 && evs.readyState != evs.CLOSED) {\n                    if (request.status == 200 || (request.status>=300 && request.status<400)){\n                        evs._onxhrdata();\n                    }\n                    else {\n                        request._failed = true;\n                        evs.readyState = evs.CLOSED;\n                        evs.dispatchEvent('error', {\n                            type: 'error',\n                            data: \"The server responded with \"+request.status\n                        });\n                        evs.close();\n                    }\n                }\n            };\n\n            request.onprogress = function () {\n            };\n\n            request.open('GET', evs.urlWithParams(evs.URL, evs.getArgs), true);\n\n            var headers = evs.xhrHeaders; // maybe null\n            for (var header in headers) {\n                if (headers.hasOwnProperty(header)){\n                    request.setRequestHeader(header, headers[header]);\n                }\n            }\n            if (evs.lastEventId) {\n                request.setRequestHeader('Last-Event-Id', evs.lastEventId);\n            }\n\n            request.send();\n        };\n\n        EventSource.prototype.XHR.prototype = {\n\n            useXDomainRequest: false,\n\n            _request: null,\n\n            _failed: false, // true if we have had errors...\n\n            isReady: function() {\n\n\n                return this._request.readyState >= 2;\n            },\n\n            isDone: function() {\n\n                return (this._request.readyState == 4);\n            },\n\n            hasError: function() {\n\n                return (this._failed || (this._request.status >= 400));\n            },\n\n            getBuffer: function() {\n\n                var rv = '';\n                try {\n                    rv = this._request.responseText || '';\n                }\n                catch (e){}\n                return rv;\n            },\n\n            abort: function() {\n\n                if ( this._request ) {\n                    this._request.abort();\n                }\n            }\n        };\n    }\n    else {\n\n\tEventSource.isPolyfill = \"IE_8-9\";\n\n        // patch EventSource defaultOptions\n        var defaults = EventSource.prototype.defaultOptions;\n        defaults.xhrHeaders = null; // no headers will be sent\n        defaults.getArgs['evs_preamble'] = 2048 + 8;\n\n        // EventSource will send request using Internet Explorer XDomainRequest\n        EventSource.prototype.XHR = function(evs) {\n\n            request = new XDomainRequest();\n            this._request = request;\n\n            // set handlers\n            request.onprogress = function(){\n                request._ready = true;\n                evs._onxhrdata();\n            };\n\n            request.onload = function(){\n                this._loaded = true;\n                evs._onxhrdata();\n            };\n\n            request.onerror = function(){\n                this._failed = true;\n                evs.readyState = evs.CLOSED;\n                evs.dispatchEvent('error', {\n                    type: 'error',\n                    data: \"XDomainRequest error\"\n                });\n            };\n\n            request.ontimeout = function(){\n                this._failed = true;\n                evs.readyState = evs.CLOSED;\n                evs.dispatchEvent('error', {\n                    type: 'error',\n                    data: \"XDomainRequest timed out\"\n                });\n            };\n\n            // XDomainRequest does not allow setting custom headers\n            // If EventSource has enabled the use of GET arguments\n            // we add parameters to URL so that server can adapt the stream...\n            var reqGetArgs = {};\n            if (evs.getArgs) {\n\n                // copy evs.getArgs in reqGetArgs\n                var defaultArgs = evs.getArgs;\n                    for (var key in defaultArgs) {\n                        if (defaultArgs.hasOwnProperty(key)){\n                            reqGetArgs[key] = defaultArgs[key];\n                        }\n                    }\n                if (evs.lastEventId){\n                    reqGetArgs['evs_last_event_id'] = evs.lastEventId;\n                }\n            }\n            // send the request\n\n            request.open('GET', evs.urlWithParams(evs.URL,reqGetArgs));\n            request.send();\n        };\n\n        EventSource.prototype.XHR.prototype = {\n\n            useXDomainRequest: true,\n\n            _request: null,\n\n            _ready: false, // true when progress events are dispatched\n\n            _loaded: false, // true when request has been loaded\n\n            _failed: false, // true if when request is in error\n\n            isReady: function() {\n\n                return this._request._ready;\n            },\n\n            isDone: function() {\n\n                return this._request._loaded;\n            },\n\n            hasError: function() {\n\n                return this._request._failed;\n            },\n\n            getBuffer: function() {\n\n                var rv = '';\n                try {\n                    rv = this._request.responseText || '';\n                }\n                catch (e){}\n                return rv;\n            },\n\n            abort: function() {\n\n                if ( this._request){\n                    this._request.abort();\n                }\n            }\n        };\n    }\n\n    function MessageEvent(type, data, origin, lastEventId) {\n\n        this.bubbles = false;\n        this.cancelBubble = false;\n        this.cancelable = false;\n        this.data = data || null;\n        this.origin = origin || '';\n        this.lastEventId = lastEventId || '';\n        this.type = type || 'message';\n    }\n\n    function isOldIE () {\n\n        //return true if we are in IE8 or IE9\n        return (window.XDomainRequest && (window.XMLHttpRequest && new XMLHttpRequest().responseType === undefined)) ? true : false;\n    }\n\n    global[evsImportName] = EventSource;\n})(this);\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"eventsource-polyfill\",\n  \"version\": \"0.9.7\",\n  \"description\": \"A browser polyfill for W3C EventSource (http://www.w3.org/TR/eventsource/)\",\n  \"main\": \"dist/browserify-eventsource.js\",\n  \"directories\": { \"doc\": \"docs\" },\n  \"files\": [\n    \"dist/eventsource.js\",\n    \"dist/browserify-eventsource.js\"\n  ],\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: to run browser tests, visit http://testevs.amvtek.com/\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/amvtek/EventSource.git\"\n  },\n  \"keywords\": [\n    \"sse\",\n    \"server sent events\",\n    \"eventsource\",\n    \"event-source\",\n    \"polyfill\"\n  ],\n  \"author\": \"amvtek <devel@amvtek.com>\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/amvtek/EventSource/issues\"\n  },\n  \"homepage\": \"https://github.com/amvtek/EventSource\",\n  \"devDependencies\": {\n    \"grunt\": \"^0.4.5\",\n    \"grunt-contrib-uglify\": \"^0.6.0\",\n    \"grunt-string-replace\": \"^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "test_server/etc/nginx/evs_tests.conf",
    "content": "upstream event_sources {\n\t\n    # eventsource web server run from command line \n    server 127.0.0.1:7676;\n}\n\nserver{\n\n    listen 80; # this is for HTTP\n    server_name testevs.amvtek.com;\n    root /usr/local/www/testevs.amvtek.com/EventSource/javascript;\n    index SpecRunner.html;\n\n    location = /favicon.ico {\n\n\treturn 404;\n    }\n    \n    location = /robots.txt {\n\n        alias /usr/local/www/robots/deny_all.txt;\n    }\t\n        \n    location = /test/eventsource/4-messages-with-seed-01{\n\n\tchunked_transfer_encoding off;\n\tproxy_http_version 1.1;\n\tproxy_buffering off;\n\n\tproxy_set_header Host $host;\n\tproxy_set_header X-Browser-Addr $remote_addr;\n\tproxy_set_header X-EVS-Test-Num-Message 4;\n\n\n        proxy_pass http://event_sources;\n        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;\n    }\n    \n    location = /test/eventsource/6-messages-with-seed-02{\n\n\tchunked_transfer_encoding off;\n\tproxy_http_version 1.1;\n\tproxy_buffering off;\n\n\tproxy_set_header Host $host;\n\tproxy_set_header X-Browser-Addr $remote_addr;\n\tproxy_set_header X-EVS-Test-Num-Message 6;\n\n\n        proxy_pass http://event_sources;\n        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;\n    }\n    \n    location = /test/eventsource/8-messages-closeat-4-with-seed-03{\n\n\tchunked_transfer_encoding off;\n\tproxy_http_version 1.1;\n\tproxy_buffering off;\n\n\tproxy_set_header Host $host;\n\tproxy_set_header X-Browser-Addr $remote_addr;\n\tproxy_set_header X-EVS-Test-Num-Message 8;\n\tproxy_set_header X-EVS-Test-CloseAt 4;\n\n\n        proxy_pass http://event_sources;\n        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;\n    }\n    \n    location = /test/eventsource/16-messages-closeat-5-with-seed-04{\n\n\tchunked_transfer_encoding off;\n\tproxy_http_version 1.1;\n\tproxy_buffering off;\n\n\tproxy_set_header Host $host;\n\tproxy_set_header X-Browser-Addr $remote_addr;\n\tproxy_set_header X-EVS-Test-Num-Message 16;\n\tproxy_set_header X-EVS-Test-CloseAt 5;\n\n\n        proxy_pass http://event_sources;\n        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;\n    }\n}\n"
  },
  {
    "path": "test_server/etc/supervisor/evs_test_server.conf",
    "content": "[program:test_eventsource]\ncommand=/usr/local/www/testevs.amvtek.com/bin/twistd \n    -n --pidfile= \n    test_eventsource\n    --host=127.0.0.1\n    --port=7676\nuser=eventsource\ndirectory=/usr/local/www/testevs.amvtek.com/EventSource/test_server\nprocess_name=%(program_name)s\nnum_procs=1\n"
  },
  {
    "path": "test_server/evsutils/__init__.py",
    "content": "# from protocol import *\n# from utils import *"
  },
  {
    "path": "test_server/evsutils/log.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\n    shared.log\n    ~~~~~~~~~~\n\n    AmvTek attend to take control of twisted logging\n\n    :copyright: (c) 2012 by sc AmvTek srl\n    :email: devel@amvtek.com\n\"\"\"\n\nimport sys\n\nfrom logging import DEBUG,INFO,WARNING,ERROR,FATAL\n\nfrom zope.interface import implements\nfrom twisted.python import context, log as _log\n\n\nILogContext = _log.ILogContext\n_FileLogObserver = _log.FileLogObserver  # local alias\n\n\nclass LeveledOnlyFileLogObserver(_FileLogObserver):\n    \"\"\"\n    FileLogObserver that filters out non 'leveled' log events...\n    This is our attend to eliminate unwanted twisted log messages...\n    \"\"\"\n\n    def emit(self,eventDict):\n        \"\"\"skip logging if eventDict does not contain 'level' key...\"\"\"\n\n        if eventDict.get('isError') or eventDict.has_key('level'):\n            return _FileLogObserver.emit(self, eventDict)\n\n\ndef setLeveledLogging():\n    \"\"\"\n    monkey patch twisted.python.log.FileLogObserver so that 'unleveled' log\n    events are ignored...\n    \"\"\"\n    _log.FileLogObserver = LeveledOnlyFileLogObserver\n\n\ndef buildContextAwareLogPrefix(prefix):\n    \"\"\"\n    return logPrefix callable that :\n        appends context retrieved 'system' to set prefix\n    \"\"\"\n\n    def logPrefix():\n        \"logPrefix that adjust to current context\"\n\n        logCtx = context.get('system',\"-\")\n        if logCtx is not \"-\":\n            return \"%s,%s\"%(logCtx,prefix)\n        return prefix\n\n    return logPrefix\n\n\nclass LogPublisher(object):\n\n    minLevel = DEBUG\n\n    defaultLevel = INFO\n\n    def _msg(self,*args,**kwargs):\n        \"wraps twisted.log.msg...\"\n\n        _log.msg(*args,**kwargs)\n\n    def _err(self,_stuff=None,_why=None,**kwargs):\n        \"wraps twisted.log.err...\"\n\n        _log.err(_stuff,_why,**kwargs)\n\n    def debug(self,*args,**kwargs):\n        \"bypass twisted log.msg in case DEBUG below minLevel...\"\n\n        if DEBUG >= self.minLevel:\n            kwargs['level'] = DEBUG\n            self._msg(*args,**kwargs)\n\n    def msg(self,*args,**kwargs):\n        \"bypass twisted log.msg in case level below minLevel...\"\n\n        level = kwargs.setdefault('level',self.defaultLevel)\n        if level >= self.minLevel:\n            self._msg(*args,**kwargs)\n\n    log = msg\n\n    def err(self,_stuff=None,_why=None,**kwargs):\n        \"\"\"\n        bypass twisted log.err in case level below minLevel\n        uses ERROR as default for level\n        \"\"\"\n        level = kwargs.setdefault('level',ERROR)\n        if level >= self.minLevel:\n            self._err(_stuff,_why,**kwargs)\n\n    def getLogger(self,logPrefix):\n        \"return Logger instance\"\n\n        logger = Logger()\n        logger.minLevel = self.minLevel\n        logger.defaultLevel = self.defaultLevel\n        if callable(logPrefix):\n            logger.logPrefix = logPrefix\n        else:\n            logger.logPrefix = lambda :logPrefix\n        return logger\n\n\nclass Logger(LogPublisher):\n    \"a Logger which inline 'level aware' debug, msg, err log methods\"\n\n    def logPrefix(self):\n        return \"?\"\n\n    def _msg(self,*args,**kwargs):\n        \"add 'system' into log 'event dict'...\"\n\n        kwargs['system'] = self.logPrefix()\n        _log.msg(*args,**kwargs)\n\n    def _err(self,_stuff=None,_why=None,**kwargs):\n        \"add 'system' into log 'event dict'...\"\n\n        kwargs['system'] = self.logPrefix()\n        _log.err(_stuff,_why,**kwargs)\n\n\ndef setLevel(minLevel,defaultLevel):\n    \"set minimum and default levels for log publishing\"\n\n    minLevel = int(minLevel)\n    defaultLevel = int(defaultLevel)\n\n    # Initializes global publisher\n    thePublisher.minLevel = minLevel\n    thePublisher.defaultLevel = defaultLevel\n\n    # Initializes Logger class, this simplify inlining Logger\n    Logger.minLevel = minLevel\n    Logger.defaultLevel = defaultLevel\n\n# Install global LogPublisher\nthePublisher = LogPublisher()\ndebug = thePublisher.debug\nmsg = thePublisher.msg\nerr = thePublisher.err\ngetLogger = thePublisher.getLogger\n\n# Add globals to ease replacing twisted.python.log with this module\ncallWithContext = _log.callWithContext\ncallWithLogger = _log.callWithLogger\n\nstartConsoleLogging = lambda :_log.startLogging(sys.stdout)\n"
  },
  {
    "path": "test_server/evsutils/protocol.py",
    "content": "#  -*- coding: utf-8 -*-\nimport random as RND\n\ntry:\n    from cStringIO import StringIO\nexcept ImportError:\n    from StringIO import StringIO\n\nfrom zope.interface import implements\nfrom twisted.protocols import basic, policies\nfrom twisted.internet import reactor, protocol, interfaces\nfrom twisted.internet.task import deferLater, cooperate\n\nimport log as customLog\n\nfrom utils import SimpleHTTPRequest, encode_http_response, TestSource, split_by\n\n\nclass EventSourceRequest(SimpleHTTPRequest):\n\n    ALLOWED_METHODS = frozenset([\"GET\"])\n\n    def parse(self, httpMsg):\n\n        super(EventSourceRequest, self).parse(httpMsg)\n\n        if self._error is not None:\n            return\n\n        self.evsArgs = {}\n        \n        # continue parsing to read test parameters\n        try:\n\n            errReason = \"Bad Request\"\n            \n            # local aliases\n            path = self.path\n            reqArgs = self.args\n            reqHeaders = self.headers\n\n            # read seed\n            errReason = \"Invalid seed\"\n            self.evsArgs['seed'] = hash(path.rsplit('/',1)[-1])\n\n            # read message sequence length\n            errReason = \"Can not parse sequence length\"\n            seqlength = reqArgs.get('evs_num_messages', None) or \\\n                        reqHeaders.get('X-EVS-Test-Num-Message'.lower(), None) or None\n            self.evsArgs['length'] = int(seqlength)\n\n            # read closeAt\n            errReason = \"Can not parse closeAt\"\n            closeAt = reqArgs.get('evs_close_at', None) or\\\n                      reqHeaders.get('X-EVS-Test-CloseAt'.lower(), None)\n            self.evsArgs['closeAt'] = int(closeAt) if closeAt else None\n            \n            # read sendPreamble\n            self.evsArgs['sendPreamble'] = bool(reqArgs.get('evs_preamble'))\n\n            # read Last-Event-Id\n            errReason = \"Invalid Last-Event-Id\"\n            lastEventId = reqArgs.get('evs_last_event_id', None) or\\\n                          reqHeaders.get('Last-Event-Id'.lower(), None) or -1\n            self.evsLastId = int(lastEventId)\n\n        except ValueError:\n\n            self.set_error(400, errReason)\n\n        except:\n\n            self.set_error(500, \"Server side error\")\n\n\nclass SimpleHTTPServerProtocol(basic.LineReceiver, policies.TimeoutMixin):\n\n    START_EVENT_STREAM = \\\n            \"HTTP/1.1 200 OK\\r\\n\"\\\n            \"Content-Type: text/event-stream\\r\\n\"\\\n            \"Access-Control-Allow-Origin: *\\r\\n\"\\\n            \"Cache-Control: no-cache\\r\\n\"\\\n            \"Transfert-Encoding: identity\\r\\n\"\\\n            \"Connection: close\\r\\n\\r\\n\"\n\n    MAX_LENGTH_ERROR = encode_http_response(413, 'Request Entity Too Large')\n\n    MAX_REQUEST_TRANSMIT_TIME_ERROR = encode_http_response(408, 'Request Timeout')\n\n    delimiter = \"\\r\\n\\r\\n\"\n\n    request = None\n    \n    def __init__(self, maxLength, timeout):\n\n        self.MAX_LENGTH = maxLength\n        self.MAX_REQUEST_TRANSMIT_TIME = timeout\n\n    def lineLengthExceeded(self, line):\n\n        self.log.msg(\"request too large, disconnecting\")\n        self.sendError(self.MAX_LENGTH_ERROR)\n\n    def connectionMade(self):\n        \n        self.setTimeout(self.MAX_REQUEST_TRANSMIT_TIME)\n        \n        self.log = customLog.getLogger(self.transport.logstr)\n        self.log.msg('connectionMade')\n\n    def timeoutConnection(self):\n\n        self.log.msg(\"request transmission takes too long, timing out\")\n        self.sendError(self.MAX_REQUEST_TRANSMIT_TIME_ERROR)\n\n    def connectionLost(self, reason=None):\n\n        self.log.msg('Connection closed because %s' % reason)\n        if hasattr(self, 'producer'):\n            self.producer.stopProducing()\n\n    def lineReceived(self, line):\n        \"\"\"parse incoming http request, and start streaming...\"\"\"\n\n        self.log.msg(\"received HTTP request, attending to parse it\")\n        self.resetTimeout()\n        \n        self.request = EventSourceRequest(line)\n\n        if self.request._error is not None:\n\n            self.log.msg(\"Got HTTP error %(status)s\" % self.request._error)\n            self.sendError(self.request.error)\n\n        else:\n\n            # send response headers\n            self.startResponse()\n\n            # register EventSource producer\n            self.producer = CooperativePushProducer(self.buildEventStream())\n            self.transport.registerProducer(self.producer, True)\n            d = self.producer.whenDone()\n            d.addCallback(lambda _: self.transport.loseConnection())\n\n    def sendError(self, error):\n        \"\"\"send error response and close connection\"\"\"\n\n        self.transport.write(error)\n        self.transport.loseConnection()\n\n    def startResponse(self):\n        \"\"\"send response that starts EventSource stream...\"\"\"\n\n        self.transport.write(self.START_EVENT_STREAM)\n\n    def buildEventStream(self):\n\n        lastEvtId = self.request.evsLastId\n\n        evtSource = TestSource(messages=self.factory.messages, **self.request.evsArgs)\n        evtSequence = evtSource.visit_from(lastEvtId+1)\n\n        if lastEvtId > -1:\n\n            self.log.msg(\"restart streaming from : %d\" % lastEvtId)\n\n        else:\n\n            self.log.msg(\"new eventsource stream...\")\n\n        restart = lambda: None\n\n        for message in evtSequence:\n\n            # extract start of message...\n            msgstart = message[:message.find(\":\", 0, 8)+12]\n            self.log.msg(\"new event line : %s...\" % msgstart)\n\n            for part in split_by(message, RND.randint(1, 3)):\n                self.transport.write(part)\n                yield deferLater(reactor, RND.uniform(0.05, 0.3), restart)\n\n\nclass CooperativePushProducer(object):\n\n    implements(interfaces.IPushProducer)\n\n    def __init__(self, iterator):\n\n        self.task = cooperate(iterator)\n\n    def getTaskState(self):\n\n        return self.task._completionState\n\n    def whenDone(self):\n\n        return self.task.whenDone()\n\n    def pauseProducing(self):\n\n        self.task.pause()\n\n    def resumeProducing(self):\n\n        self.task.resume()\n\n    def stopProducing(self):\n\n        if self.task._completionState is None:\n\n            self.task.stop()\n\n\nclass SimpleHTTPServerProtocolFactory(protocol.Factory):\n\n    def __init__(self):\n\n        self.MAX_LENGTH = 100000\n        self.MAX_REQUEST_TRANSMIT_TIME = 20000  # seconds\n        self.messages = None\n\n    def buildProtocol(self, addr):\n\n        proto = SimpleHTTPServerProtocol(self.MAX_LENGTH, self.MAX_REQUEST_TRANSMIT_TIME)\n        proto.factory = self\n        return proto\n\nif __name__ == \"__main__\":\n\n    import doctest\n    doctest.testmod()\n"
  },
  {
    "path": "test_server/evsutils/utils.py",
    "content": "#  -*- coding: utf-8 -*-\n\nfrom urlparse import urlparse, parse_qsl\nfrom rfc822 import Message as MimeMessage\nimport random as RND\nimport json\n\ntry:\n    from cStringIO import StringIO\nexcept ImportError:\n    from StringIO import StringIO\n\nimport log as customLog\n\n\ndef encode_http_response(\n        status, reason, version=\"HTTP/1.1\",\n        headers=None, entity=None, **kwargs):\n    \"\"\"return http message encoding response\"\"\"\n\n    buf = []\n\n    # 'dictify' headers\n    headers = dict(headers or [])\n\n    # add status line\n    buf.append(\"%s %i %s\\r\\n\" % (version, status, reason))\n\n    # add entity description in headers\n    if entity:\n        headers[\"Content-Length\"] = len(entity)\n        headers.setdefault(\"Content-Type\", \"text/plain\")\n\n    # render headers\n    for name, value in headers.items():\n        buf.append(\"%s: %s\\r\\n\" % (name.title(), value))\n\n    # add empty line\n    buf.append(\"\\r\\n\")\n\n    if entity:\n        buf.append(entity)\n\n    return \"\".join(buf)\n\n\nclass SimpleHTTPRequest(object):\n    \"\"\"Simple HTTPRequest object to help parsing HTTP message\"\"\"\n\n    ALLOWED_METHODS = frozenset([\n        \"OPTIONS\", \"GET\", \"HEAD\", \"POST\",\n        \"PUT\", \"DELETE\", \"TRACE\", \"CONNECT\"])\n\n    method = None\n    path = None\n    headers = {}\n    version = None\n    args = None\n\n    _error = None\n\n    def __init__(self, httpMsg):\n        \"\"\"\n        >>> reqText = \"GET /path/to/my/eventsource?arg1=1&arg2=2 HTTP/1.1\\\\r\\\\nheader: 3\\\\r\\\\n\\\\r\\\\n\"\n        >>> req = SimpleHTTPRequest(reqText)\n        >>> req.path, req.args, req.method, req.version, req.headers\n        ('/path/to/my/eventsource', {'arg1': '1', 'arg2': '2'}, 'GET', (1, 1), {'header': '3'})\n        \"\"\"\n\n        self.log = customLog.getLogger('Processing HTTP request')\n        try:\n            self.parse(httpMsg)\n        except:\n            self.set_error(400, \"Bad Request\")\n\n    def parse(self, httpMsg):\n        \"\"\"parse and validate http request out of httpMsg\"\"\"\n\n        f = StringIO(httpMsg)\n\n        # parse request line\n        reqline = f.readline()\n        parts = reqline.split()\n\n        if len(parts) == 3:\n\n            method, path, version = parts\n\n        elif len(parts) == 2:\n\n            method, path = parts\n            version = \"HTTP/0.9\"\n\n        else:\n\n            return self.set_error(400, \"Invalid Request line\")\n\n        # validates method\n        method = method.strip().upper()\n        if method not in self.ALLOWED_METHODS:\n            hdrs = {\"Allow\": \", \".join(self.ALLOWED_METHODS)}\n            return self.set_error(405, \"Method Not Allowed\", hdrs)\n        self.method = method\n\n        # validates path\n        self.path = urlparse(path).path\n        self.args = dict(parse_qsl(urlparse(path).query))\n\n        # validates version\n        version = version.strip().upper()\n        if not version.startswith(\"HTTP/\"):\n\n            return self.set_error(400, \"Invalid HTTP version\")\n        majmin = version[5:].split(\".\")\n        try:\n            major, minor = [int(v) for v in majmin]\n        except:\n\n            return self.set_error(400, \"Invalid HTTP version\")\n        self.version = (major, minor)\n\n        # parse headers\n        self.headers = dict(MimeMessage(f))\n        self.log.msg('\\nFound headers: {}'.format(self.headers))\n\n    def set_error(self, status, reason, headers=None, entity=None):\n        \"\"\"helper method allowing to define 'shortcut' response\"\"\"\n\n        status = int(status)\n        self._error = locals()\n\n    def get_error(self):\n        \"\"\"return string encoding error response if any\"\"\"\n\n        if self._error:\n            return encode_http_response(**self._error)\n    error = property(get_error)\n\n    def has_error(self):\n        \"\"\"return True if request is not valid\"\"\"\n\n        return bool(self._error)\n\n\nclass TestSource(object):\n\n    lineSep = ['\\n', '\\r', '\\r\\n']\n\n    messages = [u'one line. one',\n                u'two lines. one\\ntwo lines. two',\n                u'three lines. one\\nthree lines. two\\nthree lines. three',\n                u'four lines. one\\nfour. two\\nfour lines. three\\nfour lines. four',\n                u'spam, ham and eggs',\n                u\"spam, șuncă și ouă\",\n                u'spam\\nham\\neggs',\n                u'Nobody expects the spanish inquisition',\n                u\"Personne ne s'attend à l'Inquisition espagnole\",\n                u'always look on the bright side of bugs',\n                u\"toujours regarder le côté lumineux de bugs\"]\n\n    def __init__(self, seed, length, closeAt=None,\n                 sendPreamble=False, messages=None):\n        \"\"\"\n        test if we can recreate the exact scenario with the same seed\n        >>> source1 = TestSource(2014, 2)\n        >>> source2 = TestSource(2014, 2)\n        >>> source1.sequence == source2.sequence\n        True\n        \"\"\"\n\n        RND.seed(seed)\n        self.sendPreamble = sendPreamble\n        self.length = int(length)\n        self.closeAt = closeAt\n        self.messages = [unicode(mess) for mess in messages or self.messages]\n        self.chosenLnSep = RND.choice(self.lineSep)\n\n        # self.chosenEvtSep = RND.choice(self.lineSep).rjust(RND.randint(2, 10))\n        self.chosenEvtSep = self.chosenLnSep  # RND.choice(self.lineSep)\n        self.sequence = [\"Message %02i%s\" %\n                         (n, RND.choice(self.messages))\n                         for n in xrange(self.length)]\n        self.encoder = EventSourceEncoder(self.chosenLnSep, self.chosenEvtSep)\n\n    def visit_from(self, fromId=0):\n        \"\"\"\n        generator function, let us iterate sequence from identifier\n\n        helper lambda to simulate event encoding\n        >>> ev = lambda mylist, sep: [\"%s%s\" % (el, sep) for el in mylist]\n\n        start from fromId=0 to closeAt=1\n        >>> source = TestSource(2014, 10, 1)  # closeAt is 1\n        >>> source.sequence = [1, 2, 3]\n\n        >>> actual = list(source.visit_from())\n        >>> sep = source.chosenLnSep + source.chosenEvtSep\n        >>> expected = ev(['event: testmeta\\\\ndata: [1, 2, 3]', 'data: 1\\\\nid: 1'], sep)\n        >>> actual == expected\n        True\n\n        start from fromId=7 to closeAt=4\n        >>> source = TestSource(2014, 10, 4)  # closeAt is 4\n        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\n        >>> actual = list(source.visit_from(7))\n        >>> sep = source.chosenLnSep + source.chosenEvtSep\n        >>> expected = ev(['data: 7\\\\nid: 7', 'data: 8\\\\nid: 8'], sep)\n        >>> actual == expected\n        True\n\n        start from fromId=7 to closeAt=10\n        >>> source = TestSource(2014, 10)  # to the end\n        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\n        >>> actual = list(source.visit_from(7))\n        >>> sep = source.chosenLnSep + source.chosenEvtSep\n        >>> expected = ev(['data: 7\\\\nid: 7', 'data: 8\\\\nid: 8', 'data: 9\\\\nid: 9', 'data: 10\\\\nid: 10', 'event: testend\\\\ndata: This is the end'], sep)\n        >>> actual == expected\n        True\n\n        start from fromId=5 to closeAt=4\n        >>> source = TestSource(2014, 8, 4)  # to the end\n        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8]\n\n        >>> actual = list(source.visit_from(5))\n        >>> sep = source.chosenLnSep + source.chosenEvtSep\n        >>> expected = ev(['data: 5\\\\nid: 5', 'data: 6\\\\nid: 6', 'data: 7\\\\nid: 7',  'data: 8\\\\nid: 8', 'event: testend\\\\ndata: This is the end'], sep)\n        >>> actual == expected\n        True\n\n        tart from fromId=5 to closeAt=4\n        >>> source = TestSource(2014, 16, 4)  # to the end\n        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]\n\n        >>> actual = list(source.visit_from(5))\n        >>> sep = source.chosenLnSep + source.chosenEvtSep\n        >>> expected = ev(['data: 5\\\\nid: 5', 'data: 6\\\\nid: 6', 'data: 7\\\\nid: 7', 'data: 8\\\\nid: 8'], sep)\n        >>> actual == expected\n        True\n        \"\"\"\n\n        fromId = int(fromId)\n        encoder = self.encoder\n        sequence = self.sequence\n        closeAt = self.closeAt\n        encode = encoder.encode_event\n        sendPreamble = self.sendPreamble\n\n        events = [encode(message, None, index+1) for index, message in enumerate(sequence)]\n        events.insert(0, encode(json.dumps(sequence), 'testmeta', 0))\n        events.append(encode(\"This is the end\", 'testend'))\n\n        if sendPreamble:\n            yield encoder.encode_preamble()\n\n        seqEnd = fromId - fromId % closeAt + closeAt + 1 if closeAt else len(events)+1\n        seqEnd = seqEnd if seqEnd < len(events)-1 else len(events)\n\n        for event in events[fromId:seqEnd]:\n            yield event\n\n\nclass EventSourceEncoder(object):\n\n    preamble = \"mypreamble\"\n\n    def __init__(self, linesep='\\n', eventsep='\\n'):\n        self.linesep = linesep\n        self.eventsep = eventsep\n\n    def encode_preamble(self, size=2056):\n        \"\"\"\n        return preamble comment aiming at resetting IE 8 9 XDomainRequest\n\n        >>> EventSourceEncoder().encode_preamble(15)\n        ':mypreamble    \\\\n'\n        \"\"\"\n\n        return (u\"%s%s\" % ((\":%s\" % self.preamble).ljust(size), self.linesep)).encode('utf-8')\n\n    def encode_comment(self, comment):\n        \"\"\"\n        return comment line\n\n        >>> EventSourceEncoder().encode_comment('spam, ham and eggs')\n        u': spam, ham and eggs\\\\n'\n        \"\"\"\n\n        return u\": %s%s\" % (comment, self.linesep)\n\n    def encode_mark(self, evtId):\n        \"\"\"\n        return line encoding event id\n\n        >>> EventSourceEncoder().encode_mark(2014)\n        u'id: 2014\\\\n'\n        \"\"\"\n\n        return u\"id: %s%s\" % (evtId, self.linesep)\n\n    def encode_name(self, evtName):\n        \"\"\"\n        return line encoding event name\n\n        >>> EventSourceEncoder().encode_name('myevent')\n        u'event: myevent\\\\n'\n        \"\"\"\n        return u'event: %s%s' % (evtName, self.linesep)\n\n    def encode_data(self, datas):\n        ur\"\"\"\n        return lines encoding event data\n\n        >>> datas = 'first data\\nsecond data\\nthird data'\n        >>> EventSourceEncoder().encode_data(datas)\n        u'data: first data\\ndata: second data\\ndata: third data\\n'\n\n        >>> datas = u\"toujours regarder le côté lumineux de bugs\"\n        >>> EventSourceEncoder().encode_data(datas)\n        u'data: toujours regarder le c\\xf4t\\xe9 lumineux de bugs\\n'\n        \"\"\"\n\n        return \"data: %s%s\" % ((\"%sdata: \" % self.linesep).join(unicode(datas).splitlines()), self.linesep)\n\n    def encode_event(self, datas, evtName=None, evtId=None):\n        \"\"\"\n        return block encoding event\n\n        >>> datas = 'first data\\\\nsecond data'\n        >>> EventSourceEncoder().encode_event(datas, 'myevent', 2014)\n        'event: myevent\\\\ndata: first data\\\\ndata: second data\\\\nid: 2014\\\\n\\\\n'\n\n        >>> datas = 'only one line of data'\n        >>> EventSourceEncoder().encode_event(datas)\n        'data: only one line of data\\\\n\\\\n'\n        \"\"\"\n\n        return (u\"%s%s%s%s\" % (\n            self.encode_name(evtName) if evtName else '',\n            self.encode_data(datas),\n            self.encode_mark(evtId) if evtId else '',\n            self.eventsep\n        )).encode('utf-8')\n\n\ndef split_by(msg, n):\n    \"\"\"\n    return msg divided in nchunk if msg size allow so...\n\n    >>> split_by('123456789', 3)\n    ['123', '456', '789']\n\n    >>> split_by('123456789', 2)\n    ['1234', '56789']\n\n    >>> split_by('12345', 2)\n    ['12', '345']\n\n    >>> len(split_by('123456789', 4))\n    4\n\n    Generate all randomly(msg, msg length and pieces)\n    and check if the final length matches\n    >>> import random,string\n    >>> pieces = random.randint(1, 20)\n    >>> len(split_by(''.join(random.choice(string.ascii_uppercase) for i in range(random.randint(1, 100))), pieces)) == pieces\n    True\n    \"\"\"\n\n    return [(msg[len(msg)*i//n:len(msg)*(i+1)//n]) for i in range(n)]\n\n\nif __name__ == \"__main__\":\n\n    import doctest\n    doctest.testmod()\n"
  },
  {
    "path": "test_server/requirements.txt",
    "content": "Twisted==19.7.0\nargparse==1.2.1\ndistribute==0.6.24\nwsgiref==0.1.2\nzope.interface==4.1.1\n"
  },
  {
    "path": "test_server/twisted/plugins/test_eventsource.py",
    "content": "#  -*- coding: utf-8 -*-\n\nfrom zope.interface import implements\n\nfrom twisted.application.service import IServiceMaker\nfrom twisted.application import internet\nfrom twisted.plugin import IPlugin\nfrom twisted.python import usage\n\nfrom evsutils.protocol import SimpleHTTPServerProtocolFactory\n\n\nclass Options(usage.Options):\n\n    optParameters = [\n        ['host', 'h', '0.0.0.0', \"host\"],\n        ['port', 'p', 7676, \"port\"]\n    ]\n\n\nclass TestEventsourceServiceMaker(object):\n\n    implements(IServiceMaker, IPlugin)\n    tapname = \"test_eventsource\"\n    description = \"test eventsource\"\n    options = Options\n\n    def makeService(self, options):\n\n        return internet.TCPServer(int(options[\"port\"]), SimpleHTTPServerProtocolFactory())\n\nserviceMaker = TestEventsourceServiceMaker()"
  }
]