diff --git a/.travis.yml b/.travis.yml index c99ba3cc..b5f0fe32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,9 +11,13 @@ env: - "ARTIFACTS_S3_BUCKET=travis-web-production" - secure: "RFuCOppyjWHC4XWKtQlgS4zO4B6KVxytdX8+G5jRY3XM+OEGte8VDD88gZLM\nKDpkqMFDbNJAVTsh1kMANCTct2ONi30RTxuJWLtRyK7RE5zCcaGbAkTNZgXo\nOR5OWLEPJZbNfbh17H6J7izTy6yiLR+CsVP1wMgeVusP0eoDhCA=" - secure: "duqMXPALumXB3e2j/kM2uCaCGwgZsRrU0GCDY+3Zk6a+PK+s0mE9BftcXdxm\n6u87ld2PvCBO0inpe5YeS9LOZsT+OFS4jj+GGTsRI6rmGz+kok0N+ATLTdcj\nu15zhLhUUlhoKW0DZURrDv/iTiC/FKvJ0u5Rft0XbjfTY+0go/M=" + - secure: "sW8X/OxQQnCfSWfo5uho8os/4eV8Y3QWOx5LdhqcmOxb4KLlQkndRNNqz+DF\nKynSgnSpty2uvol6611J9RDPEVn6cZw7EFQj/EO7NRVQC8uD+8LtT/2C2J8L\nc/bf7sQqpAkDq9MOEub/A6R4jIk043lGQ4IyWAQmGlETMQ2Q/K0=" + - secure: "XCGOrMQHDR59KARYeN2GaAkDQKwq2nW9okEFMaKEH1AuUFFcU4hHAsunNeXe\nwSHu4Dpo/CioI2EKQpxL8nwMUPsx3HaY/2W8ef1YPvhU1GuSyiPDW0va8sV7\nBmZf1b26ktrzqaYDxyJjwe1EKrzfZx0LPV0MNU6wSWdHILthPY8=" matrix: - - "TEST_SUITE=spec" - - "TEST_SUITE=ember" + - "TEST_SUITE=ruby" + - "TEST_SUITE=phantomjs" + - "TEST_SUITE=saucelabs BROWSER='firefox:19:Windows 2012'" + - "TEST_SUITE=saucelabs BROWSER='chrome::Windows 2008'" script: "script/ci" after_script: diff --git a/assets/scripts/app/app.coffee b/assets/scripts/app/app.coffee index c296f5eb..fe42c9f2 100644 --- a/assets/scripts/app/app.coffee +++ b/assets/scripts/app/app.coffee @@ -10,7 +10,7 @@ unless window.TravisApplication @store.loadMany(Travis.Sponsor, Travis.SPONSORS) @slider = new Travis.Slider() - @pusher = new Travis.Pusher(Travis.config.pusher_key) + @pusher = new Travis.Pusher(Travis.config.pusher_key) if Travis.config.pusher_key @tailing = new Travis.Tailing() @set('auth', Travis.Auth.create(app: this, endpoint: Travis.config.api_endpoint)) diff --git a/assets/scripts/app/controllers/sidebar.coffee b/assets/scripts/app/controllers/sidebar.coffee index 42bfd638..ae1d7750 100644 --- a/assets/scripts/app/controllers/sidebar.coffee +++ b/assets/scripts/app/controllers/sidebar.coffee @@ -33,11 +33,11 @@ Travis.reopen for worker in content.toArray() host = worker.get('host') unless groups[host] - groups[host] = Em.ArrayProxy.create(Em.SortableMixin, + groups[host] = Em.ArrayProxy.extend(Em.SortableMixin, content: [], sortProperties: ['nameForSort'] - ) - groups[host].addObject(worker) + ).create() + groups[host].pushObject(worker) $.values(groups) ).property('length') diff --git a/assets/scripts/app/models/build.coffee b/assets/scripts/app/models/build.coffee index 03b20781..ca3bb292 100644 --- a/assets/scripts/app/models/build.coffee +++ b/assets/scripts/app/models/build.coffee @@ -1,18 +1,21 @@ require 'travis/model' @Travis.Build = Travis.Model.extend Travis.DurationCalculations, - eventType: DS.attr('string') - repoId: DS.attr('number') - commitId: DS.attr('number') + eventType: DS.attr('string') + repoId: DS.attr('number') + commitId: DS.attr('number') - state: DS.attr('string') - number: DS.attr('number') - branch: DS.attr('string') - message: DS.attr('string') - _duration: DS.attr('number') - _config: DS.attr('object') - startedAt: DS.attr('string') - finishedAt: DS.attr('string') + state: DS.attr('string') + number: DS.attr('number') + branch: DS.attr('string') + message: DS.attr('string') + _duration: DS.attr('number') + _config: DS.attr('object') + startedAt: DS.attr('string') + finishedAt: DS.attr('string') + pullRequest: DS.attr('boolean') + pullRequestTitle: DS.attr('string') + pullRequestNumber: DS.attr('number') repo: DS.belongsTo('Travis.Repo') commit: DS.belongsTo('Travis.Commit') diff --git a/assets/scripts/app/models/extensions.coffee b/assets/scripts/app/models/extensions.coffee index 1579300c..214bedf3 100644 --- a/assets/scripts/app/models/extensions.coffee +++ b/assets/scripts/app/models/extensions.coffee @@ -7,5 +7,6 @@ Travis.DurationCalculations = Ember.Mixin.create ).property('_duration', 'finishedAt', 'startedAt') updateTimes: -> - @notifyPropertyChange '_duration' - @notifyPropertyChange 'finished_at' + if @get('stateManager.currentState.path') != 'rootState.loaded.reloading' + @notifyPropertyChange '_duration' + @notifyPropertyChange 'finished_at' diff --git a/assets/scripts/app/models/job.coffee b/assets/scripts/app/models/job.coffee index 98334b06..e54a717c 100644 --- a/assets/scripts/app/models/job.coffee +++ b/assets/scripts/app/models/job.coffee @@ -87,12 +87,14 @@ require 'travis/model' subscribe: -> return if @get('subscribed') @set('subscribed', true) - Travis.pusher.subscribe "job-#{@get('id')}" + if Travis.pusher + Travis.pusher.subscribe "job-#{@get('id')}" unsubscribe: -> return unless @get('subscribed') @set('subscribed', false) - Travis.pusher.unsubscribe "job-#{@get('id')}" + if Travis.pusher + Travis.pusher.unsubscribe "job-#{@get('id')}" onStateChange: (-> if @get('state') == 'finished' && Travis.pusher diff --git a/assets/scripts/app/routes.coffee b/assets/scripts/app/routes.coffee index ac4dc99c..0e65c243 100644 --- a/assets/scripts/app/routes.coffee +++ b/assets/scripts/app/routes.coffee @@ -1,6 +1,25 @@ require 'travis/location' require 'travis/line_number_parser' +Travis.DontSetupModelForControllerMixin = Ember.Mixin.create + # I've override setup to *not* set controller's model + # this can be remove when this patch will be merged https://github.com/emberjs/ember.js/pull/2044 + # this will allow us to override setting up model for a controller + setup: (context) -> + @redirected = false + @_checkingRedirect = true + + @redirect(context) + + @_checkingRedirect = false + if @redirected + return false + + controller = @controllerFor(@routeName, context) + + @setupController(controller, context) + @renderTemplate(controller, context); + Ember.Router.reopen location: (if testMode? then Ember.NoneLocation.create() else Travis.Location.create()) @@ -75,7 +94,7 @@ Travis.ApplicationRoute = Ember.Route.extend Travis.LineNumberParser, this.controllerFor('repo').set('lineNumber', @fetchLineNumber()) -Travis.IndexCurrentRoute = Ember.Route.extend +Travis.IndexCurrentRoute = Ember.Route.extend Travis.DontSetupModelForControllerMixin, renderTemplate: -> @render 'repo' @render 'build', outlet: 'pane', into: 'repo' @@ -83,7 +102,7 @@ Travis.IndexCurrentRoute = Ember.Route.extend setupController: -> @container.lookup('controller:repo').activate('index') -Travis.AbstractBuildsRoute = Ember.Route.extend +Travis.AbstractBuildsRoute = Ember.Route.extend Travis.DontSetupModelForControllerMixin, renderTemplate: -> @render 'builds', outlet: 'pane', into: 'repo' @@ -94,7 +113,7 @@ Travis.BuildsRoute = Travis.AbstractBuildsRoute.extend(contentType: 'builds') Travis.PullRequestsRoute = Travis.AbstractBuildsRoute.extend(contentType: 'pull_requests') Travis.BranchesRoute = Travis.AbstractBuildsRoute.extend(contentType: 'branches') -Travis.BuildRoute = Ember.Route.extend +Travis.BuildRoute = Ember.Route.extend Travis.DontSetupModelForControllerMixin, renderTemplate: -> @render 'build', outlet: 'pane', into: 'repo' @@ -110,7 +129,7 @@ Travis.BuildRoute = Ember.Route.extend repo.set('build', model) repo.activate('build') -Travis.JobRoute = Ember.Route.extend +Travis.JobRoute = Ember.Route.extend Travis.DontSetupModelForControllerMixin, renderTemplate: -> @render 'job', outlet: 'pane', into: 'repo' @@ -126,14 +145,14 @@ Travis.JobRoute = Ember.Route.extend repo.set('job', model) repo.activate('job') -Travis.RepoIndexRoute = Ember.Route.extend +Travis.RepoIndexRoute = Ember.Route.extend Travis.DontSetupModelForControllerMixin, setupController: (controller, model) -> @container.lookup('controller:repo').activate('current') renderTemplate: -> @render 'build', outlet: 'pane', into: 'repo' -Travis.RepoRoute = Ember.Route.extend +Travis.RepoRoute = Ember.Route.extend Travis.DontSetupModelForControllerMixin, renderTemplate: -> @render 'repo' diff --git a/assets/scripts/app/templates/builds/show.hbs b/assets/scripts/app/templates/builds/show.hbs index c51ec673..64ab8d8d 100644 --- a/assets/scripts/app/templates/builds/show.hbs +++ b/assets/scripts/app/templates/builds/show.hbs @@ -25,9 +25,14 @@
{{t builds.commit}}
{{formatCommit build.commit}}
- {{#if commit.compareUrl}} -
{{t builds.compare}}
-
{{pathFrom build.commit.compareUrl}}
+ {{#if build.pullRequest}} +
{{t builds.pullRequest}}
+
#{{build.pullRequestNumber}} {{build.pullRequestTitle}}
+ {{else}} + {{#if commit.compareUrl}} +
{{t builds.compare}}
+
{{pathFrom build.commit.compareUrl}}
+ {{/if}} {{/if}} {{#if commit.authorName}}
{{t builds.author}}
diff --git a/assets/scripts/app/views/log.coffee b/assets/scripts/app/views/log.coffee index 122b89fd..68686b11 100644 --- a/assets/scripts/app/views/log.coffee +++ b/assets/scripts/app/views/log.coffee @@ -11,9 +11,9 @@ Travis.reopen didInsertElement: -> job = @get('job') - if job && !job.get('isFinished') + if job job.get('log').fetch() - job.subscribe() + job.subscribe() if !job.get('isFinished') willDestroyElement: -> job = @get('job') diff --git a/assets/scripts/spec/unit/chunk_buffer_spec.coffee b/assets/scripts/spec/unit/chunk_buffer_spec.coffee index a07a5d1d..8286af2e 100644 --- a/assets/scripts/spec/unit/chunk_buffer_spec.coffee +++ b/assets/scripts/spec/unit/chunk_buffer_spec.coffee @@ -27,7 +27,7 @@ describe 'Travis.ChunkBuffer', -> expect(buffer.get('length')).toEqual(1) - waits 40 + waits 100 runs -> expect(buffer.get('length')).toEqual(2) expect(buffer.toArray()).toEqual(['foo', 'baz']) diff --git a/assets/scripts/vendor/pusher.js b/assets/scripts/vendor/pusher.js index c0d6ea5a..39c055dc 100644 --- a/assets/scripts/vendor/pusher.js +++ b/assets/scripts/vendor/pusher.js @@ -1,48 +1,100 @@ /*! - * Pusher JavaScript Library v1.12.6 + * Pusher JavaScript Library v2.0.0 * http://pusherapp.com/ * - * Copyright 2011, Pusher + * Copyright 2013, Pusher * Released under the MIT licence. */ -(function(){if(Function.prototype.scopedTo===void 0)Function.prototype.scopedTo=function(b,a){var e=this;return function(){return e.apply(b,Array.prototype.slice.call(a||[]).concat(Array.prototype.slice.call(arguments)))}};var c=function(b,a){this.options=a||{};this.key=b;this.channels=new c.Channels;this.global_emitter=new c.EventsDispatcher;var e=this;this.checkAppKey();this.connection=new c.Connection(this.key,this.options);this.connection.bind("connected",function(){e.subscribeAll()}).bind("message", -function(a){var b=a.event.indexOf("pusher_internal:")===0;if(a.channel){var c;(c=e.channel(a.channel))&&c.emit(a.event,a.data)}b||e.global_emitter.emit(a.event,a.data)}).bind("disconnected",function(){e.channels.disconnect()}).bind("error",function(a){c.warn("Error",a)});c.instances.push(this);c.isReady&&e.connect()};c.instances=[];c.prototype={channel:function(b){return this.channels.find(b)},connect:function(){this.connection.connect()},disconnect:function(){this.connection.disconnect()},bind:function(b, -a){this.global_emitter.bind(b,a);return this},bind_all:function(b){this.global_emitter.bind_all(b);return this},subscribeAll:function(){for(var b in this.channels.channels)this.channels.channels.hasOwnProperty(b)&&this.subscribe(b)},subscribe:function(b){var a=this,e=this.channels.add(b,this);this.connection.state==="connected"&&e.authorize(this.connection.socket_id,this.options,function(c,f){c?e.emit("pusher:subscription_error",f):a.send_event("pusher:subscribe",{channel:b,auth:f.auth,channel_data:f.channel_data})}); -return e},unsubscribe:function(b){this.channels.remove(b);this.connection.state==="connected"&&this.send_event("pusher:unsubscribe",{channel:b})},send_event:function(b,a,e){return this.connection.send_event(b,a,e)},checkAppKey:function(){this.key||c.warn("Warning","You must pass your app key when you instantiate Pusher.")}};c.Util={extend:function a(e,c){for(var f in c)e[f]=c[f]&&c[f].constructor&&c[f].constructor===Object?a(e[f]||{},c[f]):c[f];return e},stringify:function(){for(var a=["Pusher"], -e=0;e "+b),d.emit("state_change",{previous:e,current:b}),d.emit(b,c))}var d=this;a.EventsDispatcher.call(this);this.ping=!0;this.options=a.Util.extend({encrypted:!1},s);this.netInfo=new a.NetInfo;this.netInfo.bind("online",function(){d._machine.is("waiting")&&(d._machine.transition("connecting"), -h("connecting"))});this.netInfo.bind("offline",function(){if(d._machine.is("connected"))d.socket.onclose=void 0,d.socket.onmessage=void 0,d.socket.onerror=void 0,d.socket.onopen=void 0,d.socket.close(),d.socket=void 0,d._machine.transition("waiting")});this._machine=new a.Machine("initialized",e,{initializedPre:function(){d.compulsorySecure=d.options.encrypted;d.key=b;d.socket=null;d.socket_id=null;d.state="initialized"},waitingPre:function(){d.netInfo.isOnLine()?(d.failedAttempts<2?h("connecting"): -(h("unavailable"),d.connectionWait=1E4),d.connectionWait>0&&d.emit("connecting_in",q()),d._waitingTimer=setTimeout(function(){d._machine.transition("connecting")},q())):h("unavailable")},waitingExit:function(){clearTimeout(d._waitingTimer)},connectingPre:function(){if(d.netInfo.isOnLine()===!1)d._machine.transition("waiting"),h("unavailable");else{var b="/app/"+d.key+"?protocol=5&client=js&version="+a.VERSION+"&flash="+(a.TransportType==="flash"?"true":"false");if(a.TransportType==="sockjs"){a.debug("Connecting to sockjs", -a.sockjs);var c=v(d.connectionSecure);d.ping=!1;d.socket=new SockJS(c);d.socket.onopen=function(){d.socket.send(JSON.stringify({path:b}));d._machine.transition("open")}}else c=u(d.connectionSecure)+b,a.debug("Connecting",c),d.socket=new a.Transport(c),d.socket.onopen=function(){d._machine.transition("open")};d.socket.onclose=n;d.socket.onerror=o;d._connectingTimer=setTimeout(m,d.openTimeout)}},connectingExit:function(){clearTimeout(d._connectingTimer);d.socket.onopen=void 0},connectingToWaiting:function(){k()}, -connectingToImpermanentlyClosing:function(){k()},openPre:function(){d.socket.onmessage=w;d.socket.onerror=o;d.socket.onclose=n;d._openTimer=setTimeout(m,d.connectedTimeout)},openExit:function(){clearTimeout(d._openTimer);d.socket.onmessage=void 0},openToWaiting:function(){k()},openToImpermanentlyClosing:function(){k()},connectedPre:function(a){d.socket_id=a;d.socket.onmessage=x;d.socket.onerror=o;d.socket.onclose=n;c(d);d.connectedAt=(new Date).getTime();p()},connectedPost:function(){h("connected")}, -connectedExit:function(){d._activityTimer&&clearTimeout(d._activityTimer);h("disconnected")},impermanentlyClosingPost:function(){if(d.socket)d.socket.onclose=n,d.socket.close()},permanentlyClosingPost:function(){d.socket?(d.socket.onclose=function(){c(d);d._machine.transition("permanentlyClosed")},d.socket.close()):(c(d),d._machine.transition("permanentlyClosed"))},failedPre:function(){h("failed");a.debug("WebSockets are not available in this browser.")},permanentlyClosedPost:function(){h("disconnected")}})} -var a=this.Pusher,e={initialized:["waiting","failed"],waiting:["connecting","permanentlyClosed"],connecting:["open","permanentlyClosing","impermanentlyClosing","waiting"],open:["connected","permanentlyClosing","impermanentlyClosing","waiting"],connected:["permanentlyClosing","waiting"],impermanentlyClosing:["waiting","permanentlyClosing"],permanentlyClosing:["permanentlyClosed"],permanentlyClosed:["waiting","failed"],failed:["permanentlyClosed"]},g=2E3,f=2E3,j=1E4,t=1E4;b.prototype.connect=function(){!this._machine.is("failed")&& -!a.Transport?this._machine.transition("failed"):this._machine.is("initialized")?(c(this),this._machine.transition("waiting")):this._machine.is("waiting")&&this.netInfo.isOnLine()===!0?this._machine.transition("connecting"):this._machine.is("permanentlyClosed")&&(c(this),this._machine.transition("waiting"))};b.prototype.send=function(a){if(this._machine.is("connected")){var b=this;setTimeout(function(){b.socket.send(a)},0);return!0}else return!1};b.prototype.send_event=function(b,c,e){b={event:b,data:c}; -e&&(b.channel=e);a.debug("Event sent",b);return this.send(JSON.stringify(b))};b.prototype.disconnect=function(){this._machine.is("permanentlyClosed")||(this._machine.is("waiting")||this._machine.is("failed")?this._machine.transition("permanentlyClosed"):this._machine.transition("permanentlyClosing"))};a.Util.extend(b.prototype,a.EventsDispatcher.prototype);this.Pusher.Connection=b}).call(this); -(function(){Pusher.Channels=function(){this.channels={}};Pusher.Channels.prototype={add:function(b,a){var c=this.find(b);c||(c=Pusher.Channel.factory(b,a),this.channels[b]=c);return c},find:function(b){return this.channels[b]},remove:function(b){delete this.channels[b]},disconnect:function(){for(var b in this.channels)this.channels[b].disconnect()}};Pusher.Channel=function(b,a){var c=this;Pusher.EventsDispatcher.call(this,function(a){Pusher.debug("No callbacks on "+b+" for "+a)});this.pusher=a;this.name= -b;this.subscribed=!1;this.bind("pusher_internal:subscription_succeeded",function(a){c.onSubscriptionSucceeded(a)})};Pusher.Channel.prototype={init:function(){},disconnect:function(){this.subscribed=!1;this.emit("pusher_internal:disconnected")},onSubscriptionSucceeded:function(){this.subscribed=!0;this.emit("pusher:subscription_succeeded")},authorize:function(b,a,c){return c(!1,{})},trigger:function(b,a){return this.pusher.send_event(b,a,this.name)}};Pusher.Util.extend(Pusher.Channel.prototype,Pusher.EventsDispatcher.prototype); -Pusher.Channel.PrivateChannel={authorize:function(b,a,c){var g=this;return(new Pusher.Channel.Authorizer(this,Pusher.channel_auth_transport,a)).authorize(b,function(a,b){a||g.emit("pusher_internal:authorized",b);c(a,b)})}};Pusher.Channel.PresenceChannel={init:function(){this.members=new c(this)},onSubscriptionSucceeded:function(){this.subscribed=!0}};var c=function(b){var a=this,c=null,g=function(){a._members_map={};a.count=0;c=a.me=null};g();var f=function(f){a._members_map=f.presence.hash;a.count= -f.presence.count;a.me=a.get(c.user_id);b.emit("pusher:subscription_succeeded",a)};b.bind("pusher_internal:authorized",function(a){c=JSON.parse(a.channel_data);b.bind("pusher_internal:subscription_succeeded",f)});b.bind("pusher_internal:member_added",function(c){a.get(c.user_id)===null&&a.count++;a._members_map[c.user_id]=c.user_info;b.emit("pusher:member_added",a.get(c.user_id))});b.bind("pusher_internal:member_removed",function(c){var e=a.get(c.user_id);e&&(delete a._members_map[c.user_id],a.count--, -b.emit("pusher:member_removed",e))});b.bind("pusher_internal:disconnected",function(){g();b.unbind("pusher_internal:subscription_succeeded",f)})};c.prototype={each:function(b){for(var a in this._members_map)b(this.get(a))},get:function(b){return this._members_map.hasOwnProperty(b)?{id:b,info:this._members_map[b]}:null}};Pusher.Channel.factory=function(b,a){var c=new Pusher.Channel(b,a);b.indexOf("private-")===0?Pusher.Util.extend(c,Pusher.Channel.PrivateChannel):b.indexOf("presence-")===0&&(Pusher.Util.extend(c, -Pusher.Channel.PrivateChannel),Pusher.Util.extend(c,Pusher.Channel.PresenceChannel));c.init();return c}}).call(this); -(function(){Pusher.Channel.Authorizer=function(c,b,a){this.channel=c;this.type=b;this.authOptions=(a||{}).auth||{}};Pusher.Channel.Authorizer.prototype={composeQuery:function(c){var c="&socket_id="+encodeURIComponent(c)+"&channel_name="+encodeURIComponent(this.channel.name),b;for(b in this.authOptions.params)c+="&"+encodeURIComponent(b)+"="+encodeURIComponent(this.authOptions.params[b]);return c},authorize:function(c,b){return Pusher.authorizers[this.type].call(this,c,b)}};Pusher.auth_callbacks={}; -Pusher.authorizers={ajax:function(c,b){var a;a=Pusher.XHR?new Pusher.XHR:window.XMLHttpRequest?new window.XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");a.open("POST",Pusher.channel_auth_endpoint,!0);a.setRequestHeader("Content-Type","application/x-www-form-urlencoded");for(var e in this.authOptions.headers)a.setRequestHeader(e,this.authOptions.headers[e]);a.onreadystatechange=function(){if(a.readyState==4)if(a.status==200){var c,e=!1;try{c=JSON.parse(a.responseText),e=!0}catch(j){b(!0,"JSON returned from webapp was invalid, yet status code was 200. Data was: "+ -a.responseText)}e&&b(!1,c)}else Pusher.warn("Couldn't get auth info from your webapp",a.status),b(!0,a.status)};a.send(this.composeQuery(c));return a},jsonp:function(c,b){this.authOptions.headers!==void 0&&Pusher.warn("Warn","To send headers with the auth request, you must use AJAX, rather than JSONP.");var a=document.createElement("script");Pusher.auth_callbacks[this.channel.name]=function(a){b(!1,a)};a.src=Pusher.channel_auth_endpoint+"?callback="+encodeURIComponent("Pusher.auth_callbacks['"+this.channel.name+ -"']")+this.composeQuery(c);var e=document.getElementsByTagName("head")[0]||document.documentElement;e.insertBefore(a,e.firstChild)}}}).call(this); -var _require=function(){function c(a,b){document.addEventListener?a.addEventListener("load",b,!1):a.attachEvent("onreadystatechange",function(){(a.readyState=="loaded"||a.readyState=="complete")&&b()})}function b(a,b){var g=document.getElementsByTagName("head")[0],f=document.createElement("script");f.setAttribute("src",a);f.setAttribute("type","text/javascript");f.setAttribute("async",!0);c(f,function(){b()});g.appendChild(f)}return function(a,c){for(var g=0,f=0;f0?_require(b,a):a()})(); +(function(){function b(a,h){var e=this;this.options=h||{};this.key=a;this.channels=new b.Channels;this.global_emitter=new b.EventsDispatcher;this.sessionID=Math.floor(Math.random()*1E9);c(this.key);this.connection=new b.ConnectionManager(this.key,b.Util.extend({getStrategy:function(a){return b.StrategyBuilder.build(b.getDefaultStrategy(),b.Util.extend({},e.options,a))},getTimeline:function(){return new b.Timeline(e.key,e.sessionID,{features:b.Util.getClientFeatures(),params:e.options.timelineParams|| +{},limit:25,level:b.Timeline.INFO,version:b.VERSION})},getTimelineSender:function(a,d){return e.options.disableStats?null:new b.TimelineSender(a,{encrypted:e.isEncrypted()||!!d.encrypted,host:b.stats_host,path:"/timeline"})},activityTimeout:b.activity_timeout,pongTimeout:b.pong_timeout,unavailableTimeout:b.unavailable_timeout},this.options,{encrypted:this.isEncrypted()}));this.connection.bind("connected",function(){e.subscribeAll()});this.connection.bind("message",function(a){var d=a.event.indexOf("pusher_internal:")=== +0;if(a.channel){var b=e.channel(a.channel);b&&b.emit(a.event,a.data)}d||e.global_emitter.emit(a.event,a.data)});this.connection.bind("disconnected",function(){e.channels.disconnect()});this.connection.bind("error",function(a){b.warn("Error",a)});b.instances.push(this);b.isReady&&e.connect()}function c(a){(a===null||a===void 0)&&b.warn("Warning","You must pass your app key when you instantiate Pusher.")}var a=b.prototype;b.instances=[];b.isReady=!1;b.debug=function(){b.log&&b.log(b.Util.stringify.apply(this, +arguments))};b.warn=function(){window.console&&window.console.warn?window.console.warn(b.Util.stringify.apply(this,arguments)):b.log&&b.log(b.Util.stringify.apply(this,arguments))};b.ready=function(){b.isReady=!0;for(var a=0,c=b.instances.length;a0)for(c=0;c1||a(this.getPath(d),function(){for(var a=0;a>>6)+b(128|d&63):b(224|d>>>12&15)+b(128|d>>>6&63)+b(128|d&63)},d=function(a){var d=[0,2,1][a.length%3],a=a.charCodeAt(0)<<16|(a.length>1?a.charCodeAt(1):0)<<8|(a.length>2?a.charCodeAt(2):0);return["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(a>>>18),"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(a>>> +12&63),d>=2?"=":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(a>>>6&63),d>=1?"=":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(a&63)].join("")},h=window.btoa||function(a){return a.replace(/[\s\S]{1,3}/g,d)};Pusher.Base64={encode:function(d){return h(d.replace(/[^\x00-\x7F]/g,a))}}}).call(this); +(function(){function b(a){this.options=a}function c(a){return Pusher.Util.mapObject(a,function(a){typeof a==="object"&&(a=JSON.stringify(a));return encodeURIComponent(Pusher.Base64.encode(a.toString()))})}b.send=function(a,b){var c=new Pusher.JSONPRequest({url:a.url,receiver:a.receiverName,tagPrefix:a.tagPrefix}),f=a.receiver.register(function(a,d){c.cleanup();b(a,d)});return c.send(f,a.data,function(b){var c=a.receiver.unregister(f);c&&c(b)})};var a=b.prototype;a.send=function(a,b,e){if(this.script)return!1; +var f=this.options.tagPrefix||"_pusher_jsonp_",b=Pusher.Util.extend({},b,{receiver:this.options.receiver}),b=Pusher.Util.map(Pusher.Util.flatten(c(Pusher.Util.filterObject(b,function(a){return a!==void 0}))),Pusher.Util.method("join","=")).join("&");this.script=document.createElement("script");this.script.id=f+a;this.script.src=this.options.url+"/"+a+"?"+b;this.script.type="text/javascript";this.script.charset="UTF-8";this.script.onerror=this.script.onload=e;if(this.script.async===void 0&&document.attachEvent&& +/opera/i.test(navigator.userAgent))f=this.options.receiver||"Pusher.JSONP.receive",this.errorScript=document.createElement("script"),this.errorScript.text=f+"("+a+", true);",this.script.async=this.errorScript.async=!1;var g=this;this.script.onreadystatechange=function(){g.script&&/loaded|complete/.test(g.script.readyState)&&e(!0)};a=document.getElementsByTagName("head")[0];a.insertBefore(this.script,a.firstChild);this.errorScript&&a.insertBefore(this.errorScript,this.script.nextSibling);return!0}; +a.cleanup=function(){if(this.script&&this.script.parentNode)this.script.parentNode.removeChild(this.script),this.script=null;if(this.errorScript&&this.errorScript.parentNode)this.errorScript.parentNode.removeChild(this.errorScript),this.errorScript=null};Pusher.JSONPRequest=b}).call(this); +(function(){function b(){this.lastId=0;this.callbacks={}}var c=b.prototype;c.register=function(a){this.lastId++;var b=this.lastId;this.callbacks[b]=a;return b};c.unregister=function(a){if(this.callbacks[a]){var b=this.callbacks[a];delete this.callbacks[a];return b}else return null};c.receive=function(a,b,c){(a=this.unregister(a))&&a(b,c)};Pusher.JSONPReceiver=b;Pusher.JSONP=new b}).call(this); +(function(){function b(a,b,c){this.key=a;this.session=b;this.events=[];this.options=c||{};this.uniqueID=this.sent=0}var c=b.prototype;b.ERROR=3;b.INFO=6;b.DEBUG=7;c.log=function(a,b){if(this.options.level===void 0||a<=this.options.level)this.events.push(Pusher.Util.extend({},b,{timestamp:Pusher.Util.now(),level:a})),this.options.limit&&this.events.length>this.options.limit&&this.events.shift()};c.error=function(a){this.log(b.ERROR,a)};c.info=function(a){this.log(b.INFO,a)};c.debug=function(a){this.log(b.DEBUG, +a)};c.isEmpty=function(){return this.events.length===0};c.send=function(a,b){var c=this,e={};this.sent===0&&(e=Pusher.Util.extend({key:this.key,features:this.options.features,version:this.options.version},this.options.params||{}));e.session=this.session;e.timeline=this.events;e=Pusher.Util.filterObject(e,function(a){return a!==void 0});this.events=[];a(e,function(a,e){a||c.sent++;b(a,e)});return!0};c.generateUniqueID=function(){this.uniqueID++;return this.uniqueID};Pusher.Timeline=b}).call(this); +(function(){function b(a,b){this.timeline=a;this.options=b||{}}var c=b.prototype;c.send=function(a){if(!this.timeline.isEmpty()){var b=this.options,c="http"+(this.isEncrypted()?"s":"")+"://";this.timeline.send(function(a,f){return Pusher.JSONPRequest.send({data:a,url:c+b.host+b.path,receiver:Pusher.JSONP},f)},a)}};c.isEncrypted=function(){return!!this.options.encrypted};Pusher.TimelineSender=b}).call(this); +(function(){function b(a){this.strategies=a}function c(a,b,c){var h=Pusher.Util.map(a,function(a,d,h,e){return a.connect(b,c(d,e))});return{abort:function(){Pusher.Util.apply(h,d)},forceMinPriority:function(a){Pusher.Util.apply(h,function(b){b.forceMinPriority(a)})}}}function a(a){return Pusher.Util.all(a,function(a){return Boolean(a.error)})}function d(a){if(!a.error&&!a.aborted)a.abort(),a.aborted=!0}var h=b.prototype;h.isSupported=function(){return Pusher.Util.any(this.strategies,Pusher.Util.method("isSupported"))}; +h.connect=function(b,d){return c(this.strategies,b,function(b,c){return function(h,e){(c[b].error=h)?a(c)&&d(!0):(Pusher.Util.apply(c,function(a){a.forceMinPriority(e.priority)}),d(null,e))}})};Pusher.BestConnectedEverStrategy=b}).call(this); +(function(){function b(a,b,c){this.strategy=a;this.transports=b;this.ttl=c.ttl||18E5;this.timeline=c.timeline}function c(){var a=Pusher.Util.getLocalStorage();return a&&a.pusherTransport?JSON.parse(a.pusherTransport):null}var a=b.prototype;a.isSupported=function(){return this.strategy.isSupported()};a.connect=function(a,b){var e=c(),f=[this.strategy];if(e&&e.timestamp+this.ttl>=Pusher.Util.now()){var g=this.transports[e.transport];g&&(this.timeline.info({cached:!0,transport:e.transport}),f.push(new Pusher.SequentialStrategy([g], +{timeout:e.latency*2,failFast:!0})))}var i=Pusher.Util.now(),j=f.pop().connect(a,function k(c,e){if(c){var g=Pusher.Util.getLocalStorage();g&&delete g.pusherTransport;f.length>0?(i=Pusher.Util.now(),j=f.pop().connect(a,k)):b(c)}else{var g=Pusher.Util.now()-i,o=e.name,n=Pusher.Util.getLocalStorage();if(n)n.pusherTransport=JSON.stringify({timestamp:Pusher.Util.now(),transport:o,latency:g});b(null,e)}});return{abort:function(){j.abort()},forceMinPriority:function(b){a=b;j&&j.forceMinPriority(b)}}};Pusher.CachedStrategy= +b}).call(this);(function(){function b(a,b){this.strategy=a;this.options={delay:b.delay}}var c=b.prototype;c.isSupported=function(){return this.strategy.isSupported()};c.connect=function(a,b){var c=this.strategy,e,f=new Pusher.Timer(this.options.delay,function(){e=c.connect(a,b)});return{abort:function(){f.ensureAborted();e&&e.abort()},forceMinPriority:function(b){a=b;e&&e.forceMinPriority(b)}}};Pusher.DelayedStrategy=b}).call(this); +(function(){function b(a){this.strategy=a}var c=b.prototype;c.isSupported=function(){return this.strategy.isSupported()};c.connect=function(a,b){var c=this.strategy.connect(a,function(a,f){f&&c.abort();b(a,f)});return c};Pusher.FirstConnectedStrategy=b}).call(this); +(function(){function b(a,b,c){this.test=a;this.trueBranch=b;this.falseBranch=c}var c=b.prototype;c.isSupported=function(){return(this.test()?this.trueBranch:this.falseBranch).isSupported()};c.connect=function(a,b){return(this.test()?this.trueBranch:this.falseBranch).connect(a,b)};Pusher.IfStrategy=b}).call(this); +(function(){function b(a,b){this.strategies=a;this.loop=Boolean(b.loop);this.failFast=Boolean(b.failFast);this.timeout=b.timeout;this.timeoutLimit=b.timeoutLimit}var c=b.prototype;c.isSupported=function(){return Pusher.Util.any(this.strategies,Pusher.Util.method("isSupported"))};c.connect=function(a,b){var c=this,e=this.strategies,f=0,g=this.timeout,i=null,j=function(l,k){k?b(null,k):(f+=1,c.loop&&(f%=e.length),f0&&(f=new Pusher.Timer(c.timeout,function(){g.abort();e(!0)}));return{abort:function(){f&&f.ensureAborted();g.abort()},forceMinPriority:function(a){g.forceMinPriority(a)}}}; +Pusher.SequentialStrategy=b}).call(this); +(function(){function b(a,b,c,f){this.name=a;this.priority=b;this.transport=c;this.options=f||{}}function c(a,b){new Pusher.Timer(0,function(){b(a)});return{abort:function(){},forceMinPriority:function(){}}}var a=b.prototype;a.isSupported=function(){return this.transport.isSupported({disableFlash:!!this.options.disableFlash})};a.connect=function(a,b){if(this.transport.isSupported()){if(this.priority0};c.reportDeath=function(){this.livesLeft-=1};Pusher.TransportManager=b}).call(this); +(function(){function b(a){return function(b){return[a.apply(this,arguments),b]}}function c(a,b){if(a.length===0)return[[],b];var e=d(a[0],b),h=c(a.slice(1),e[1]);return[[e[0]].concat(h[0]),h[1]]}function a(a,b){if(typeof a[0]==="string"&&a[0].charAt(0)===":"){var e=b[a[0].slice(1)];if(a.length>1){if(typeof e!=="function")throw"Calling non-function "+a[0];var h=[Pusher.Util.extend({},b)].concat(Pusher.Util.map(a.slice(1),function(a){return d(a,Pusher.Util.extend({},b))[0]}));return e.apply(this,h)}else return[e, +b]}else return c(a,b)}function d(b,c){if(typeof b==="string"){var d;if(typeof b==="string"&&b.charAt(0)===":"){d=c[b.slice(1)];if(d===void 0)throw"Undefined symbol "+b;d=[d,c]}else d=[b,c];return d}else if(typeof b==="object"&&b instanceof Array&&b.length>0)return a(b,c);return[b,c]}var h={ws:Pusher.WSTransport,flash:Pusher.FlashTransport,sockjs:Pusher.SockJSTransport},e={def:function(a,b,c){if(a[b]!==void 0)throw"Redefining symbol "+b;a[b]=c;return[void 0,a]},def_transport:function(a,b,c,d,e,k){var m= +h[c];if(!m)throw new Pusher.Errors.UnsupportedTransport(c);c=Pusher.Util.extend({},{key:a.key,encrypted:a.encrypted,timeline:a.timeline,disableFlash:a.disableFlash},e);k&&(m=k.getAssistant(m));d=new Pusher.TransportStrategy(b,d,m,c);k=a.def(a,b,d)[1];k.transports=a.transports||{};k.transports[b]=d;return[void 0,k]},transport_manager:b(function(a,b){return new Pusher.TransportManager(b)}),sequential:b(function(a,b){var c=Array.prototype.slice.call(arguments,2);return new Pusher.SequentialStrategy(c, +b)}),cached:b(function(a,b,c){return new Pusher.CachedStrategy(c,a.transports,{ttl:b,timeline:a.timeline})}),first_connected:b(function(a,b){return new Pusher.FirstConnectedStrategy(b)}),best_connected_ever:b(function(){var a=Array.prototype.slice.call(arguments,1);return new Pusher.BestConnectedEverStrategy(a)}),delayed:b(function(a,b,c){return new Pusher.DelayedStrategy(c,{delay:b})}),"if":b(function(a,b,c,d){return new Pusher.IfStrategy(b,c,d)}),is_supported:b(function(a,b){return function(){return b.isSupported()}})}; +Pusher.StrategyBuilder={build:function(a,b){var c=Pusher.Util.extend({},e,b);return d(a,c)[1].strategy}}}).call(this); +(function(){function b(a){Pusher.EventsDispatcher.call(this);this.transport=a;this.bindListeners()}var c=b.prototype;Pusher.Util.extend(c,Pusher.EventsDispatcher.prototype);c.supportsPing=function(){return this.transport.supportsPing()};c.send=function(a){return this.transport.send(a)};c.send_event=function(a,b,c){a={event:a,data:b};if(c)a.channel=c;Pusher.debug("Event sent",a);return this.send(JSON.stringify(a))};c.close=function(){this.transport.close()};c.bindListeners=function(){var a=this,b= +function(f){f=a.parseMessage(f);if(f!==void 0)f.event==="pusher:connection_established"?(a.id=f.data.socket_id,a.transport.unbind("message",b),a.transport.bind("message",c),a.transport.bind("ping_request",e),a.emit("connected",a.id)):f.event==="pusher:error"&&(a.handleCloseCode(f.data.code,f.data.message),a.transport.close())},c=function(b){b=a.parseMessage(b);if(b!==void 0){Pusher.debug("Event recd",b);switch(b.event){case "pusher:error":a.emit("error",{type:"PusherError",data:b.data});break;case "pusher:ping":a.emit("ping"); +break;case "pusher:pong":a.emit("pong")}a.emit("message",b)}},e=function(){a.emit("ping_request")},f=function(b){a.emit("error",{type:"WebSocketError",error:b})},g=function(i){i&&i.code&&a.handleCloseCode(i.code,i.reason);a.transport.unbind("message",b);a.transport.unbind("message",c);a.transport.unbind("ping_request",e);a.transport.unbind("error",f);a.transport.unbind("closed",g);a.transport=null;a.emit("closed")};this.transport.bind("message",b);this.transport.bind("error",f);this.transport.bind("closed", +g)};c.parseMessage=function(a){try{var b=JSON.parse(a.data);if(typeof b.data==="string")try{b.data=JSON.parse(b.data)}catch(c){if(!(c instanceof SyntaxError))throw c;}return b}catch(e){this.emit("error",{type:"MessageParseError",error:e,data:a.data})}};c.handleCloseCode=function(a,b){this.emit("error",{type:"PusherError",data:{code:a,message:b}});a<4E3?a>=1002&&a<=1004&&this.emit("backoff"):a===4E3?this.emit("ssl_only"):a<4100?this.emit("refused"):a<4200?this.emit("backoff"):a<4300?this.emit("retry"): +this.emit("refused")};Pusher.ProtocolWrapper=b}).call(this); +(function(){function b(a,b){Pusher.EventsDispatcher.call(this);this.key=a;this.options=b||{};this.state="initialized";this.connection=null;this.encrypted=!!b.encrypted;this.timeline=this.options.getTimeline();var c=this;Pusher.Network.bind("online",function(){c.state==="unavailable"&&c.connect()});Pusher.Network.bind("offline",function(){c.shouldRetry()&&(c.disconnect(),c.updateState("unavailable"))});var e=function(){c.timelineSender&&c.timelineSender.send(function(){})};this.bind("connected",e); +setInterval(e,6E4)}var c=b.prototype;Pusher.Util.extend(c,Pusher.EventsDispatcher.prototype);c.connect=function(){if(!this.connection&&this.state!=="connecting"){var a=this.options.getStrategy({key:this.key,timeline:this.timeline,encrypted:this.encrypted});if(a.isSupported())if(Pusher.Network.isOnline()===!1)this.updateState("unavailable");else{this.updateState("connecting");this.timelineSender=this.options.getTimelineSender(this.timeline,{encrypted:this.encrypted},this);var b=this,c=function(e,f){e? +b.runner=a.connect(0,c):(b.runner.abort(),b.setConnection(b.wrapTransport(f)))};this.runner=a.connect(0,c);this.setUnavailableTimer()}else this.updateState("failed")}};c.send=function(a){return this.connection?this.connection.send(a):!1};c.send_event=function(a,b,c){return this.connection?this.connection.send_event(a,b,c):!1};c.disconnect=function(){this.runner&&this.runner.abort();this.clearRetryTimer();this.clearUnavailableTimer();this.stopActivityCheck();this.updateState("disconnected");if(this.connection)this.connection.close(), +this.connection=null};c.retryIn=function(a){var b=this;this.retryTimer=setTimeout(function(){if(b.retryTimer!==null)b.retryTimer=null,b.disconnect(),b.connect()},a||0)};c.clearRetryTimer=function(){if(this.retryTimer)clearTimeout(this.retryTimer),this.retryTimer=null};c.setUnavailableTimer=function(){var a=this;this.unavailableTimer=setTimeout(function(){if(a.unavailableTimer)a.updateState("unavailable"),a.unavailableTimer=null},this.options.unavailableTimeout)};c.clearUnavailableTimer=function(){if(this.unavailableTimer)clearTimeout(this.unavailableTimer), +this.unavailableTimer=null};c.resetActivityCheck=function(){this.stopActivityCheck();if(!this.connection.supportsPing()){var a=this;this.activityTimer=setTimeout(function(){a.send_event("pusher:ping",{});a.activityTimer=setTimeout(function(){a.connection.close()},a.options.pongTimeout)},this.options.activityTimeout)}};c.stopActivityCheck=function(){if(this.activityTimer)clearTimeout(this.activityTimer),this.activityTimer=null};c.setConnection=function(a){this.connection=a;var b=this,c=function(a){b.clearUnavailableTimer(); +b.socket_id=a;b.updateState("connected");b.resetActivityCheck()},e=function(a){b.resetActivityCheck();b.emit("message",a)},f=function(){b.send_event("pusher:pong",{})},g=function(){b.send_event("pusher:ping",{})},i=function(a){b.emit("error",{type:"WebSocketError",error:a})},j=function(){a.unbind("connected",c);a.unbind("message",e);a.unbind("ping",f);a.unbind("ping_request",g);a.unbind("error",i);a.unbind("closed",j);b.connection=null;b.shouldRetry()&&b.retryIn(1E3)};a.bind("connected",c);a.bind("message", +e);a.bind("ping",f);a.bind("ping_request",g);a.bind("error",i);a.bind("closed",j);a.bind("ssl_only",function(){b.encrypted=!0;b.retryIn(0)});a.bind("refused",function(){b.disconnect()});a.bind("backoff",function(){b.retryIn(1E3)});a.bind("retry",function(){b.retryIn(0)});this.resetActivityCheck()};c.updateState=function(a,b){var c=this.state;this.state=a;c!==a&&(Pusher.debug("State changed",c+" -> "+a),this.emit("state_change",{previous:c,current:a}),this.emit(a,b))};c.shouldRetry=function(){return this.state=== +"connecting"||this.state==="connected"};c.wrapTransport=function(a){return new Pusher.ProtocolWrapper(a)};Pusher.ConnectionManager=b}).call(this); +(function(){function b(){Pusher.EventsDispatcher.call(this);var b=this;window.addEventListener!==void 0&&(window.addEventListener("online",function(){b.emit("online")},!1),window.addEventListener("offline",function(){b.emit("offline")},!1))}Pusher.Util.extend(b.prototype,Pusher.EventsDispatcher.prototype);b.prototype.isOnline=function(){return window.navigator.onLine===void 0?!0:window.navigator.onLine};Pusher.NetInfo=b;Pusher.Network=new b}).call(this); +(function(){Pusher.Channels=function(){this.channels={}};Pusher.Channels.prototype={add:function(b,a){var d=this.find(b);d||(d=Pusher.Channel.factory(b,a),this.channels[b]=d);return d},find:function(b){return this.channels[b]},remove:function(b){delete this.channels[b]},disconnect:function(){for(var b in this.channels)this.channels[b].disconnect()}};Pusher.Channel=function(b,a){var d=this;Pusher.EventsDispatcher.call(this,function(a){Pusher.debug("No callbacks on "+b+" for "+a)});this.pusher=a;this.name= +b;this.subscribed=!1;this.bind("pusher_internal:subscription_succeeded",function(a){d.onSubscriptionSucceeded(a)})};Pusher.Channel.prototype={init:function(){},disconnect:function(){this.subscribed=!1;this.emit("pusher_internal:disconnected")},onSubscriptionSucceeded:function(){this.subscribed=!0;this.emit("pusher:subscription_succeeded")},authorize:function(b,a,d){return d(!1,{})},trigger:function(b,a){return this.pusher.send_event(b,a,this.name)}};Pusher.Util.extend(Pusher.Channel.prototype,Pusher.EventsDispatcher.prototype); +Pusher.Channel.PrivateChannel={authorize:function(b,a,d){var h=this;return(new Pusher.Channel.Authorizer(this,Pusher.channel_auth_transport,a)).authorize(b,function(a,b){a||h.emit("pusher_internal:authorized",b);d(a,b)})}};Pusher.Channel.PresenceChannel={init:function(){this.members=new b(this)},onSubscriptionSucceeded:function(){this.subscribed=!0}};var b=function(b){var a=this,d=null,h=function(){a._members_map={};a.count=0;d=a.me=null};h();var e=function(f){a._members_map=f.presence.hash;a.count= +f.presence.count;a.me=a.get(d.user_id);b.emit("pusher:subscription_succeeded",a)};b.bind("pusher_internal:authorized",function(a){d=JSON.parse(a.channel_data);b.bind("pusher_internal:subscription_succeeded",e)});b.bind("pusher_internal:member_added",function(d){a.get(d.user_id)===null&&a.count++;a._members_map[d.user_id]=d.user_info;b.emit("pusher:member_added",a.get(d.user_id))});b.bind("pusher_internal:member_removed",function(d){var e=a.get(d.user_id);e&&(delete a._members_map[d.user_id],a.count--, +b.emit("pusher:member_removed",e))});b.bind("pusher_internal:disconnected",function(){h();b.unbind("pusher_internal:subscription_succeeded",e)})};b.prototype={each:function(b){for(var a in this._members_map)b(this.get(a))},get:function(b){return this._members_map.hasOwnProperty(b)?{id:b,info:this._members_map[b]}:null}};Pusher.Channel.factory=function(b,a){var d=new Pusher.Channel(b,a);b.indexOf("private-")===0?Pusher.Util.extend(d,Pusher.Channel.PrivateChannel):b.indexOf("presence-")===0&&(Pusher.Util.extend(d, +Pusher.Channel.PrivateChannel),Pusher.Util.extend(d,Pusher.Channel.PresenceChannel));d.init();return d}}).call(this); +(function(){Pusher.Channel.Authorizer=function(b,c,a){this.channel=b;this.type=c;this.authOptions=(a||{}).auth||{}};Pusher.Channel.Authorizer.prototype={composeQuery:function(b){var b="&socket_id="+encodeURIComponent(b)+"&channel_name="+encodeURIComponent(this.channel.name),c;for(c in this.authOptions.params)b+="&"+encodeURIComponent(c)+"="+encodeURIComponent(this.authOptions.params[c]);return b},authorize:function(b,c){return Pusher.authorizers[this.type].call(this,b,c)}};Pusher.auth_callbacks={}; +Pusher.authorizers={ajax:function(b,c){var a;a=Pusher.XHR?new Pusher.XHR:window.XMLHttpRequest?new window.XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");a.open("POST",Pusher.channel_auth_endpoint,!0);a.setRequestHeader("Content-Type","application/x-www-form-urlencoded");for(var d in this.authOptions.headers)a.setRequestHeader(d,this.authOptions.headers[d]);a.onreadystatechange=function(){if(a.readyState==4)if(a.status==200){var b,d=!1;try{b=JSON.parse(a.responseText),d=!0}catch(f){c(!0,"JSON returned from webapp was invalid, yet status code was 200. Data was: "+ +a.responseText)}d&&c(!1,b)}else Pusher.warn("Couldn't get auth info from your webapp",a.status),c(!0,a.status)};a.send(this.composeQuery(b));return a},jsonp:function(b,c){this.authOptions.headers!==void 0&&Pusher.warn("Warn","To send headers with the auth request, you must use AJAX, rather than JSONP.");var a=document.createElement("script");Pusher.auth_callbacks[this.channel.name]=function(a){c(!1,a)};a.src=Pusher.channel_auth_endpoint+"?callback="+encodeURIComponent("Pusher.auth_callbacks['"+this.channel.name+ +"']")+this.composeQuery(b);var d=document.getElementsByTagName("head")[0]||document.documentElement;d.insertBefore(a,d.firstChild)}}}).call(this); diff --git a/locales/de.yml b/locales/de.yml index cef61521..6766e5a4 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -20,6 +20,7 @@ de: sponsored_by: Dieser Testablauf lief auf einem Arbeiter-Rechner gesponsert von name: pr: + pull_request: started_at: Läuft state: Status datetime: diff --git a/locales/en.yml b/locales/en.yml index 26473318..de37e234 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -20,6 +20,7 @@ en: sponsored_by: This test series was run on a worker box sponsored by name: Build pr: PR + pull_request: Pull Request started_at: Started state: State datetime: diff --git a/locales/es.yml b/locales/es.yml index 5ff32073..c004b639 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -20,6 +20,7 @@ es: sponsored_by: Esta serie de tests han sido ejecutados en una caja de Proceso patrocinada por name: Build pr: + pull_request: started_at: Iniciado state: datetime: diff --git a/locales/fr.yml b/locales/fr.yml index e1e403d0..ea556788 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -20,6 +20,7 @@ fr: sponsored_by: Cette série de tests a été exécutée sur une machine sponsorisée par name: Version pr: + pull_request: started_at: Commencé state: datetime: diff --git a/locales/ja.yml b/locales/ja.yml index 4cf27327..5b70bcd7 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -20,6 +20,7 @@ ja: sponsored_by: このテストは以下のスポンサーの協力で行いました。 name: ビルド pr: + pull_request: started_at: 開始時刻 state: datetime: diff --git a/locales/nb.yml b/locales/nb.yml index 5532535e..c8ce62af 100644 --- a/locales/nb.yml +++ b/locales/nb.yml @@ -20,6 +20,7 @@ nb: sponsored_by: Denne testen ble kjørt på en maskin sponset av name: Jobb pr: + pull_request: started_at: Startet state: datetime: diff --git a/locales/nl.yml b/locales/nl.yml index 1a213643..b6a50d96 100644 --- a/locales/nl.yml +++ b/locales/nl.yml @@ -20,6 +20,7 @@ nl: sponsored_by: Deze tests zijn gedraaid op een machine gesponsord door name: Bouw pr: + pull_request: started_at: Gestart state: datetime: diff --git a/locales/pl.yml b/locales/pl.yml index 0037df7c..fd4ad095 100644 --- a/locales/pl.yml +++ b/locales/pl.yml @@ -20,6 +20,7 @@ pl: sponsored_by: Te testy zostały uruchomione na maszynie sponsorowanej przez name: Build pr: + pull_request: started_at: Rozpoczęto state: status datetime: diff --git a/locales/pt-BR.yml b/locales/pt-BR.yml index 1ab63836..93e34f24 100644 --- a/locales/pt-BR.yml +++ b/locales/pt-BR.yml @@ -20,6 +20,7 @@ pt-BR: sponsored_by: Esta série de testes foi executada em uma caixa de processos patrocinada por name: Build pr: + pull_request: started_at: Iniciou em state: datetime: diff --git a/locales/ru.yml b/locales/ru.yml index a3752aaa..b647458e 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -20,6 +20,7 @@ ru: sponsored_by: Эта серия тестов была запущена на машине, спонсируемой name: Билд pr: PR + pull_request: started_at: Начало state: состояние datetime: diff --git a/public/spec.html b/public/spec.html index 6d1ef344..0b9dbe8d 100644 --- a/public/spec.html +++ b/public/spec.html @@ -60,9 +60,9 @@ // if(key.match(/_spec$/)) // minispade.require(key); - var console_reporter = new jasmine.ConsoleReporter(); + var consoleReporter = new jasmine.ConsoleReporter(); jasmine.getEnv().addReporter(new jasmine.HtmlReporter()); - jasmine.getEnv().addReporter(console_reporter); + jasmine.getEnv().addReporter(consoleReporter); jasmine.getEnv().execute(); diff --git a/run_jasmine.coffee b/run_jasmine.coffee index 3c276aea..05706719 100755 --- a/run_jasmine.coffee +++ b/run_jasmine.coffee @@ -9,7 +9,7 @@ class PhantomJasmineRunner @tries = 0 @max_tries = 10 - get_status: -> @page.evaluate(-> console_reporter.status) + get_status: -> @page.evaluate(-> consoleReporter.status) terminate: -> switch @get_status() diff --git a/script/ci b/script/ci index 866b79cf..6ec161ac 100755 --- a/script/ci +++ b/script/ci @@ -1,9 +1,15 @@ #!/bin/bash -if [ "$TEST_SUITE" == "spec" ]; then +if [ "$TEST_SUITE" == "ruby" ]; then bundle exec rspec spec -elif [ "$TEST_SUITE" == "ember" ]; then +elif [ "$TEST_SUITE" == "phantomjs" ]; then bundle exec rackup -s puma -p 5000 -D sleep 3 ./run_jasmine.coffee http://localhost:5000/spec.html +elif [ "$TEST_SUITE" == "saucelabs" ]; then + bundle exec rackup -s puma -p 5000 -D + sleep 3 + curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash + gem install selenium-webdriver + ruby script/saucelabs.rb fi diff --git a/script/saucelabs.rb b/script/saucelabs.rb new file mode 100644 index 00000000..9acaf068 --- /dev/null +++ b/script/saucelabs.rb @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'selenium-webdriver' + +browser = ENV['BROWSER'].split(':') + +caps = Selenium::WebDriver::Remote::Capabilities.send browser[0] +caps.version = browser[1] +caps.platform = browser[2] +caps['tunnel-identifier'] = ENV['TRAVIS_JOB_NUMBER'] +caps['name'] = "Travis ##{ENV['TRAVIS_JOB_NUMBER']}" + +driver = Selenium::WebDriver.for( + :remote, + :url => "http://#{ENV['SAUCE_USERNAME']}:#{ENV['SAUCE_ACCESS_KEY']}@localhost:4445/wd/hub", + :desired_capabilities => caps) + +driver.navigate.to "http://localhost:5000/spec.html" +begin + status = driver.execute_script('return consoleReporter.status;') + sleep 1 +end while status == 'running' + +driver.quit + +raise 'tests failed' unless status == 'success'