Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Pull up to 2012 #2

Merged
merged 13 commits into from
This page is out of date. Refresh to see the latest.
View
18 Gemfile
@@ -1,24 +1,25 @@
source "http://rubygems.org"
-gem "rake"
+gem "rails", "3.2.8"
-gem "rails", "3.1.0.rc4"
+group :assets do
+ gem "sass-rails"
+ gem "coffee-script"
+ gem "uglifier"
+end
-gem "sass-rails", "~> 3.1.0.rc"
-gem "haml"
gem "haml-rails"
-gem "coffee-script"
-gem "uglifier"
-
gem "jquery-rails"
gem "configatron"
-
gem "pusher"
+gem "rails_emoji"
group :development do
gem "sqlite3"
+ gem "eventmachine"
+ gem "twitter-stream"
gem "yajl-ruby"
gem "net-irc"
gem "eventmachine"
@@ -26,5 +27,4 @@ end
group :production do
gem "pg"
- gem "therubyracer-heroku"
end
View
186 Gemfile.lock
@@ -1,125 +1,124 @@
GEM
remote: http://rubygems.org/
specs:
- actionmailer (3.1.0.rc4)
- actionpack (= 3.1.0.rc4)
- mail (~> 2.3.0)
- actionpack (3.1.0.rc4)
- activemodel (= 3.1.0.rc4)
- activesupport (= 3.1.0.rc4)
+ actionmailer (3.2.8)
+ actionpack (= 3.2.8)
+ mail (~> 2.4.4)
+ actionpack (3.2.8)
+ activemodel (= 3.2.8)
+ activesupport (= 3.2.8)
builder (~> 3.0.0)
erubis (~> 2.7.0)
- i18n (~> 0.6)
- rack (~> 1.3.0)
- rack-cache (~> 1.0.1)
- rack-mount (~> 0.8.1)
- rack-test (~> 0.6.0)
- sprockets (~> 2.0.0.beta.10)
- tzinfo (~> 0.3.27)
- activemodel (3.1.0.rc4)
- activesupport (= 3.1.0.rc4)
- bcrypt-ruby (~> 2.1.4)
+ journey (~> 1.0.4)
+ rack (~> 1.4.0)
+ rack-cache (~> 1.2)
+ rack-test (~> 0.6.1)
+ sprockets (~> 2.1.3)
+ activemodel (3.2.8)
+ activesupport (= 3.2.8)
builder (~> 3.0.0)
+ activerecord (3.2.8)
+ activemodel (= 3.2.8)
+ activesupport (= 3.2.8)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activeresource (3.2.8)
+ activemodel (= 3.2.8)
+ activesupport (= 3.2.8)
+ activesupport (3.2.8)
i18n (~> 0.6)
- activerecord (3.1.0.rc4)
- activemodel (= 3.1.0.rc4)
- activesupport (= 3.1.0.rc4)
- arel (~> 2.1.1)
- tzinfo (~> 0.3.27)
- activeresource (3.1.0.rc4)
- activemodel (= 3.1.0.rc4)
- activesupport (= 3.1.0.rc4)
- activesupport (3.1.0.rc4)
multi_json (~> 1.0)
- arel (2.1.3)
- bcrypt-ruby (2.1.4)
- builder (3.0.0)
+ arel (3.0.2)
+ builder (3.0.2)
coffee-script (2.2.0)
coffee-script-source
execjs
- coffee-script-source (1.1.1)
- configatron (2.8.0)
+ coffee-script-source (1.3.3)
+ configatron (2.9.1)
yamler (>= 0.1.0)
- crack (0.1.8)
erubis (2.7.0)
eventmachine (0.12.10)
- execjs (1.2.0)
+ execjs (1.4.0)
multi_json (~> 1.0)
- haml (3.1.2)
- haml-rails (0.3.4)
- actionpack (~> 3.0)
- activesupport (~> 3.0)
- haml (~> 3.0)
- railties (~> 3.0)
- hike (1.1.0)
- i18n (0.6.0)
- jquery-rails (1.0.12)
- railties (~> 3.0)
+ haml (3.1.7)
+ haml-rails (0.3.5)
+ actionpack (>= 3.1, < 4.1)
+ activesupport (>= 3.1, < 4.1)
+ haml (~> 3.1)
+ railties (>= 3.1, < 4.1)
+ hike (1.2.1)
+ http_parser.rb (0.5.1)
+ i18n (0.6.1)
+ journey (1.0.4)
+ jquery-rails (2.1.2)
+ railties (>= 3.1.0, < 5.0)
thor (~> 0.14)
- mail (2.3.0)
+ json (1.7.5)
+ mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
- mime-types (1.16)
- multi_json (1.0.3)
+ mime-types (1.19)
+ multi_json (1.3.6)
net-irc (0.0.9)
- pg (0.11.0)
- polyglot (0.3.1)
- pusher (0.8.1)
- crack (~> 0.1.0)
+ pg (0.14.1)
+ polyglot (0.3.3)
+ pusher (0.9.4)
multi_json (~> 1.0)
- ruby-hmac (~> 0.4.0)
signature (~> 0.1.2)
- rack (1.3.0)
- rack-cache (1.0.2)
+ rack (1.4.1)
+ rack-cache (1.2)
rack (>= 0.4)
- rack-mount (0.8.1)
- rack (>= 1.0.0)
rack-ssl (1.3.2)
rack
- rack-test (0.6.0)
+ rack-test (0.6.1)
rack (>= 1.0)
- rails (3.1.0.rc4)
- actionmailer (= 3.1.0.rc4)
- actionpack (= 3.1.0.rc4)
- activerecord (= 3.1.0.rc4)
- activeresource (= 3.1.0.rc4)
- activesupport (= 3.1.0.rc4)
+ rails (3.2.8)
+ actionmailer (= 3.2.8)
+ actionpack (= 3.2.8)
+ activerecord (= 3.2.8)
+ activeresource (= 3.2.8)
+ activesupport (= 3.2.8)
bundler (~> 1.0)
- railties (= 3.1.0.rc4)
- railties (3.1.0.rc4)
- actionpack (= 3.1.0.rc4)
- activesupport (= 3.1.0.rc4)
+ railties (= 3.2.8)
+ rails_emoji (1.6.1)
+ rails (>= 3.1.0)
+ railties (3.2.8)
+ actionpack (= 3.2.8)
+ activesupport (= 3.2.8)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
- thor (~> 0.14.6)
- rake (0.9.2)
- rdoc (3.8)
- ruby-hmac (0.4.0)
- sass (3.1.3)
- sass-rails (3.1.0.rc.3)
- actionpack (~> 3.1.0.rc1)
- railties (~> 3.1.0.rc1)
- sass (>= 3.1.3)
- sprockets (>= 2.0.0.beta.9)
- signature (0.1.2)
- ruby-hmac
- sprockets (2.0.0.beta.10)
- hike (~> 1.0)
+ thor (>= 0.14.6, < 2.0)
+ rake (0.9.2.2)
+ rdoc (3.12)
+ json (~> 1.4)
+ sass (3.2.1)
+ sass-rails (3.2.5)
+ railties (~> 3.2.0)
+ sass (>= 3.1.10)
+ tilt (~> 1.3)
+ signature (0.1.4)
+ simple_oauth (0.1.9)
+ sprockets (2.1.3)
+ hike (~> 1.2)
rack (~> 1.0)
- tilt (!= 1.3.0, ~> 1.1)
- sqlite3 (1.3.3)
- therubyracer-heroku (0.8.1.pre3)
- thor (0.14.6)
- tilt (1.3.2)
- treetop (1.4.9)
+ tilt (~> 1.1, != 1.3.0)
+ sqlite3 (1.3.6)
+ thor (0.16.0)
+ tilt (1.3.3)
+ treetop (1.4.10)
+ polyglot
polyglot (>= 0.3.1)
- tzinfo (0.3.29)
- uglifier (1.0.0)
+ twitter-stream (0.1.14)
+ eventmachine (>= 0.12.8)
+ http_parser.rb (~> 0.5.1)
+ simple_oauth (~> 0.1.4)
+ tzinfo (0.3.33)
+ uglifier (1.3.0)
execjs (>= 0.3.0)
- multi_json (>= 1.0.2)
- yajl-ruby (0.8.2)
+ multi_json (~> 1.0, >= 1.0.2)
+ yajl-ruby (1.1.0)
yamler (0.1.0)
PLATFORMS
@@ -129,16 +128,15 @@ DEPENDENCIES
coffee-script
configatron
eventmachine
- haml
haml-rails
jquery-rails
net-irc
pg
pusher
- rails (= 3.1.0.rc4)
- rake
- sass-rails (~> 3.1.0.rc)
+ rails (= 3.2.8)
+ rails_emoji
+ sass-rails
sqlite3
- therubyracer-heroku
+ twitter-stream
uglifier
yajl-ruby
View
10 app/assets/javascripts/application.js
@@ -1,9 +1,5 @@
-// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
-// be included in the compiled file accessible from http://example.com/assets/application.js
-// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// the compiled file.
-//
//= require jquery
//= require jquery_ujs
-//= require_tree .
+//= require pusher.min
+//= require notices
+//= require screens
View
34 app/assets/javascripts/pusher.min.js
@@ -1,34 +0,0 @@
-/*!
- * Pusher JavaScript Library v1.8.5
- * http://pusherapp.com/
- *
- * Copyright 2011, Pusher
- * Released under the MIT licence.
- */
-
-if(typeof Function.prototype.scopedTo=="undefined")Function.prototype.scopedTo=function(a,b){var c=this;return function(){return c.apply(a,Array.prototype.slice.call(b||[]).concat(Array.prototype.slice.call(arguments)))}};
-var Pusher=function(a,b){this.options=b||{};this.path="/app/"+a+"?client=js&version="+Pusher.VERSION;this.key=a;this.channels=new Pusher.Channels;this.global_channel=new Pusher.Channel("pusher_global_channel");this.global_channel.global=true;this.connected=this.secure=false;this.retry_counter=0;this.encrypted=this.options.encrypted?true:false;Pusher.isReady&&this.connect();Pusher.instances.push(this);this.bind("pusher:connection_established",function(c){this.connected=true;this.retry_counter=0;this.socket_id=
-c.socket_id;this.subscribeAll()}.scopedTo(this));this.bind("pusher:connection_disconnected",function(){for(var c in this.channels.channels)this.channels.channels[c].disconnect()}.scopedTo(this));this.bind("pusher:error",function(c){Pusher.debug("ERROR",c.message)})};Pusher.instances=[];
-Pusher.prototype={channel:function(a){return this.channels.find(a)},connect:function(){var a=this.encrypted||this.secure?"wss://"+Pusher.host+":"+Pusher.wss_port+this.path:"ws://"+Pusher.host+":"+Pusher.ws_port+this.path;Pusher.allow_reconnect=true;Pusher.debug("Connecting",a);var b=this;if(window.WebSocket){var c=new WebSocket(a),d=Pusher.connection_timeout+b.retry_counter*1E3,e=window.setTimeout(function(){Pusher.debug("Connection timeout after",d+"ms");c.close()},d);c.onmessage=function(){b.onmessage.apply(b,
-arguments)};c.onclose=function(){window.clearTimeout(e);b.onclose.apply(b,arguments)};c.onopen=function(){window.clearTimeout(e);b.onopen.apply(b,arguments)};this.connection=c}else{this.connection={};setTimeout(function(){b.send_local_event("pusher:connection_failed",{})},0)}},toggle_secure:function(){if(this.secure==false){this.secure=true;Pusher.debug("Switching to wss:// connection")}else{this.secure=false;Pusher.debug("Switching to ws:// connection")}},disconnect:function(){Pusher.debug("Disconnecting");
-Pusher.allow_reconnect=false;this.retry_counter=0;this.connection.close()},bind:function(a,b){this.global_channel.bind(a,b);return this},bind_all:function(a){this.global_channel.bind_all(a);return this},subscribeAll:function(){for(var a in this.channels.channels)this.channels.channels.hasOwnProperty(a)&&this.subscribe(a)},subscribe:function(a){var b=this.channels.add(a,this);this.connected&&b.authorize(this,function(c){this.send_event("pusher:subscribe",{channel:a,auth:c.auth,channel_data:c.channel_data})}.scopedTo(this));
-return b},unsubscribe:function(a){this.channels.remove(a);this.connected&&this.send_event("pusher:unsubscribe",{channel:a})},send_event:function(a,b,c){Pusher.debug("Event sent (channel,event,data)",c,a,b);a={event:a,data:b};if(c)a.channel=c;this.connection.send(JSON.stringify(a));return this},send_local_event:function(a,b,c){b=Pusher.data_decorator(a,b);if(c)(c=this.channel(c))&&c.dispatch_with_all(a,b);else Pusher.debug("Event recd (event,data)",a,b);this.global_channel.dispatch_with_all(a,b)},
-onmessage:function(a){a=JSON.parse(a.data);if(!(a.socket_id&&a.socket_id==this.socket_id)){if(typeof a.data=="string")a.data=Pusher.parser(a.data);this.send_local_event(a.event,a.data,a.channel)}},reconnect:function(){var a=this;setTimeout(function(){a.connect()},0)},retry_connect:function(){this.encrypted||this.toggle_secure();var a=Math.min(this.retry_counter*1E3,1E4);Pusher.debug("Retrying connection in "+a+"ms");var b=this;setTimeout(function(){b.connect()},a);this.retry_counter+=1},onclose:function(){this.global_channel.dispatch("close",
-null);Pusher.debug("Socket closed");if(this.connected){this.send_local_event("pusher:connection_disconnected",{});if(Pusher.allow_reconnect){Pusher.debug("Connection broken, trying to reconnect");this.reconnect()}}else{this.send_local_event("pusher:connection_failed",{});this.retry_connect()}this.connected=false},onopen:function(){this.global_channel.dispatch("open",null)}};
-Pusher.Util={extend:function a(b,c){for(var d in c)b[d]=c[d]&&c[d].constructor&&c[d].constructor===Object?a(b[d]||{},c[d]):c[d];return b}};Pusher.debug=function(){if(Pusher.log){for(var a=["Pusher"],b=0;b<arguments.length;b++)typeof arguments[b]==="string"?a.push(arguments[b]):a.push(JSON.stringify(arguments[b]));Pusher.log(a.join(" : "))}};Pusher.VERSION="1.8.5";Pusher.host="ws.pusherapp.com";Pusher.ws_port=80;Pusher.wss_port=443;Pusher.channel_auth_endpoint="/pusher/auth";
-Pusher.connection_timeout=5E3;Pusher.cdn_http="http://js.pusherapp.com/";Pusher.cdn_https="https://d3ds63zw57jt09.cloudfront.net/";Pusher.data_decorator=function(a,b){return b};Pusher.allow_reconnect=true;Pusher.channel_auth_transport="ajax";Pusher.parser=function(a){try{return JSON.parse(a)}catch(b){Pusher.debug("Data attribute not valid JSON - you may wish to implement your own Pusher.parser");return a}};Pusher.isReady=false;
-Pusher.ready=function(){Pusher.isReady=true;for(var a=0;a<Pusher.instances.length;a++)Pusher.instances[a].connected||Pusher.instances[a].connect()};Pusher.Channels=function(){this.channels={}};Pusher.Channels.prototype={add:function(a,b){var c=this.find(a);if(c)return c;else{c=Pusher.Channel.factory(a,b);return this.channels[a]=c}},find:function(a){return this.channels[a]},remove:function(a){delete this.channels[a]}};
-Pusher.Channel=function(a,b){this.pusher=b;this.name=a;this.callbacks={};this.global_callbacks=[];this.subscribed=false};
-Pusher.Channel.prototype={init:function(){},disconnect:function(){},acknowledge_subscription:function(){this.subscribed=true},bind:function(a,b){this.callbacks[a]=this.callbacks[a]||[];this.callbacks[a].push(b);return this},bind_all:function(a){this.global_callbacks.push(a);return this},trigger:function(a,b){this.pusher.send_event(a,b,this.name);return this},dispatch_with_all:function(a,b){this.name!="pusher_global_channel"&&Pusher.debug("Event recd (channel,event,data)",this.name,a,b);this.dispatch(a,
-b);this.dispatch_global_callbacks(a,b)},dispatch:function(a,b){var c=this.callbacks[a];if(c)for(var d=0;d<c.length;d++)c[d](b);else this.global||Pusher.debug("No callbacks for "+a)},dispatch_global_callbacks:function(a,b){for(var c=0;c<this.global_callbacks.length;c++)this.global_callbacks[c](a,b)},is_private:function(){return false},is_presence:function(){return false},authorize:function(a,b){b({})}};Pusher.auth_callbacks={};
-Pusher.authorizers={ajax:function(a,b){var c=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");c.open("POST",Pusher.channel_auth_endpoint,true);c.setRequestHeader("Content-Type","application/x-www-form-urlencoded");c.onreadystatechange=function(){if(c.readyState==4)if(c.status==200){var d=Pusher.parser(c.responseText);b(d)}else Pusher.debug("Couldn't get auth info from your webapp",status)};c.send("socket_id="+encodeURIComponent(a.socket_id)+"&channel_name="+encodeURIComponent(this.name))},
-jsonp:function(a,b){var c="socket_id="+encodeURIComponent(a.socket_id)+"&channel_name="+encodeURIComponent(this.name),d=document.createElement("script");Pusher.auth_callbacks[this.name]=b;d.src=Pusher.channel_auth_endpoint+"?callback="+encodeURIComponent("Pusher.auth_callbacks['"+this.name+"']")+"&"+c;c=document.getElementsByTagName("head")[0]||document.documentElement;c.insertBefore(d,c.firstChild)}};
-Pusher.Channel.PrivateChannel={is_private:function(){return true},authorize:function(a,b){Pusher.authorizers[Pusher.channel_auth_transport].scopedTo(this)(a,b)}};
-Pusher.Channel.PresenceChannel={init:function(){this.bind("pusher_internal:subscription_succeeded",function(a){this.acknowledge_subscription(a);this.dispatch_with_all("pusher:subscription_succeeded",this.members)}.scopedTo(this));this.bind("pusher_internal:member_added",function(a){this.dispatch_with_all("pusher:member_added",this.members.add(a.user_id,a.user_info))}.scopedTo(this));this.bind("pusher_internal:member_removed",function(a){(a=this.members.remove(a.user_id))&&this.dispatch_with_all("pusher:member_removed",
-a)}.scopedTo(this))},disconnect:function(){this.members.clear()},acknowledge_subscription:function(a){this.members._members_map=a.presence.hash;this.members.count=a.presence.count;this.subscribed=true},is_presence:function(){return true},members:{_members_map:{},count:0,each:function(a){for(var b in this._members_map)a({id:b,info:this._members_map[b]})},add:function(a,b){this._members_map[a]=b;this.count++;return this.get(a)},remove:function(a){var b=this.get(a);if(b){delete this._members_map[a];
-this.count--}return b},get:function(a){var b=this._members_map[a];return b?{id:a,info:b}:null},clear:function(){this._members_map={};this.count=0}}};Pusher.Channel.factory=function(a,b){var c=new Pusher.Channel(a,b);if(a.indexOf(Pusher.Channel.private_prefix)===0)Pusher.Util.extend(c,Pusher.Channel.PrivateChannel);else if(a.indexOf(Pusher.Channel.presence_prefix)===0){Pusher.Util.extend(c,Pusher.Channel.PrivateChannel);Pusher.Util.extend(c,Pusher.Channel.PresenceChannel)}c.init();return c};
-Pusher.Channel.private_prefix="private-";Pusher.Channel.presence_prefix="presence-";
-var _require=function(){var a;a=document.addEventListener?function(b,c){b.addEventListener("load",c,false)}:function(b,c){b.attachEvent("onreadystatechange",function(){if(b.readyState=="loaded"||b.readyState=="complete")c()})};return function(b,c){function d(j,i){i=i||function(){};var k=document.getElementsByTagName("head")[0],g=document.createElement("script");g.setAttribute("src",j);g.setAttribute("type","text/javascript");g.setAttribute("async",true);a(g,function(){var l=i;e++;h==e&&setTimeout(l,
-0)});k.appendChild(g)}for(var e=0,h=b.length,f=0;f<h;f++)d(b[f],c)}}();
-(function(){var a=(document.location.protocol=="http:"?Pusher.cdn_http:Pusher.cdn_https)+Pusher.VERSION,b=[];window.JSON==undefined&&b.push(a+"/json2.min.js");if(window.WebSocket==undefined){window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=true;b.push(a+"/flashfallback.min.js")}var c=function(){return window.WebSocket==undefined?function(){if(window.WebSocket){window.WEB_SOCKET_SWF_LOCATION=a+"/WebSocketMain.swf";WebSocket.__addTask(function(){Pusher.ready()});WebSocket.__initialize()}else Pusher.debug("Could not connect: WebSocket is not available natively or via Flash")}:
-function(){Pusher.ready()}}(),d=function(h){var f=function(){document.body?h():setTimeout(f,0)};f()},e=function(){d(c)};b.length>0?_require(b,e):e()})();
View
6 app/assets/javascripts/screens.js
@@ -42,16 +42,12 @@ var handlers = {
var channel = data.channel;
var message = data.message;
var usec = data.usec;
- if (prev_usec = usec) {
- return;
- }
- prev.usec = usec;
var div = $("<div/>").addClass("irc")
.append($("<p/>")
.append($("<span/>").addClass("screen_name")
.text(nick))
- .append($('<span/>').text(message)));
+ .append($('<span/>').html(message)));
prepend(div);
}
View
6 app/assets/stylesheets/screens.css.scss
@@ -93,6 +93,12 @@
padding: 0 5px;
}
+.irc .emoji {
+ vertical-align: middle;
+ width: 40px;
+ height: 40px;
+}
+
th {
font-weight: normal;
}
View
2  app/models/assignment.rb
@@ -4,4 +4,6 @@ class Assignment < ActiveRecord::Base
belongs_to :screen
belongs_to :channel
+
+ attr_accessible :screen, :channel # NOTE is it OK?
end
View
2  app/models/channel.rb
@@ -4,4 +4,6 @@ class Channel < ActiveRecord::Base
has_many :assignments
has_many :screens, :through => :assignments
+
+ attr_accessible :group, :name # NOTE is it OK?
end
View
2  app/models/notice.rb
@@ -3,5 +3,7 @@ class Notice < ActiveRecord::Base
validates :status, :presence => true
validates :requested_by, :presence => true
+ attr_accessible :message, :requested_by # NOTE is it OK?
+
scope :published, where("status = 'published'")
end
View
2  app/models/screen.rb
@@ -4,6 +4,8 @@ class Screen < ActiveRecord::Base
validates :name, :presence => true
validates :description, :presence => true
+ attr_accessible :name, :description # NOTE is it OK?
+
has_many :assignments
has_many :channels, :through => :assignments
View
4 app/views/layouts/application.html.haml
@@ -2,9 +2,9 @@
%html
%head
%title
- = ["KaigiSubscreen", @title].compact.join(" - ")
+ = [@title, "KaigiSubscreen"].compact.join(" - ")
= stylesheet_link_tag "application"
- = javascript_include_tag "pusher.min", "application"
+ = javascript_include_tag "application"
:javascript
var channels = [];
= csrf_meta_tags
View
33 config/application.rb
@@ -1,15 +1,13 @@
require File.expand_path('../boot', __FILE__)
-# Pick the frameworks you want:
- require "active_record/railtie"
-require "action_controller/railtie"
-require "action_mailer/railtie"
-require "active_resource/railtie"
-# require "rails/test_unit/railtie"
+require 'rails/all'
-# If you have a Gemfile, require the gems listed there, including any gems
-# you've limited to :test, :development, or :production.
-Bundler.require(:default, Rails.env) if defined?(Bundler)
+if defined?(Bundler)
+ # If you precompile assets before deploying to production, use this line
+ Bundler.require(*Rails.groups(:assets => %w(development test)))
+ # If you want your assets lazily compiled in production, use this line
+ # Bundler.require(:default, :assets, Rails.env)
+end
module Subscreen
class Application < Rails::Application
@@ -41,7 +39,24 @@ class Application < Rails::Application
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
+ # Enable escaping HTML in JSON.
+ config.active_support.escape_html_entities_in_json = true
+
+ # Use SQL instead of Active Record's schema dumper when creating the database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types
+ # config.active_record.schema_format = :sql
+
+ # Enforce whitelist mode for mass assignment.
+ # This will create an empty whitelist of attributes available for mass-assignment for all models
+ # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
+ # parameters by using an attr_accessible or attr_protected declaration.
+ config.active_record.whitelist_attributes = true
+
# Enable the asset pipeline
config.assets.enabled = true
+
+ # Version of your assets, change this if you want to expire all your assets
+ config.assets.version = '1.0'
end
end
View
20 config/config.yml.sample
@@ -3,17 +3,25 @@ pusher:
key: key
secret: secret
twitter:
- auth1:
- username: username
- password: password
- auth2:
- username: username
- password: password
+ oauth:
+ consumer_key: your_consumer_key
+ consumer_secret: your_consumer_secret
+ access_key: your_access_key
+ access_secret: your_access_secret
ignore:
- screen_name_1
- screen_name_2
irc:
+ server: irc.rubykaigi.org
+ port: 6667
nick: nick
user: user
real: real
pass: pass
+ channels:
+ - rubykaigi
+ - kaigi1
+ - kaigi2
+ - kaigi1-m17n
+ - kaigi2-m17n
+ - rubykaigi-staff
View
12 config/environments/development.rb
@@ -2,7 +2,7 @@
# Settings specified here will take precedence over those in config/application.rb
# In the development environment your application's code is reloaded on
- # every request. This slows down response time but is perfect for development
+ # every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
@@ -22,6 +22,16 @@
# Only use best-standards-support built into browsers
config.action_dispatch.best_standards_support = :builtin
+ # Raise exception on mass assignment protection for Active Record models
+ config.active_record.mass_assignment_sanitizer = :strict
+
+ # Log the query plan for queries taking more than this (works
+ # with SQLite, MySQL, and PostgreSQL)
+ config.active_record.auto_explain_threshold_in_seconds = 0.5
+
# Do not compress assets
config.assets.compress = false
+
+ # Expands the lines which load the assets
+ config.assets.debug = true
end
View
23 config/environments/production.rb
@@ -14,12 +14,18 @@
# Compress JavaScripts and CSS
config.assets.compress = true
- # Specify the default JavaScript compressor
- config.assets.js_compressor = :uglifier
+ # Don't fallback to assets pipeline if a precompiled asset is missed
+ config.assets.compile = false
+
+ # Generate digests for assets URLs
+ config.assets.digest = true
+
+ # Defaults to nil and saved in location specified by config.assets.prefix
+ # config.assets.manifest = YOUR_PATH
# Specifies the header that your server uses for sending files
- # (comment out if your front-end server doesn't support this)
- config.action_dispatch.x_sendfile_header = "X-Sendfile" # Use 'X-Accel-Redirect' for nginx
+ # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
@@ -27,8 +33,11 @@
# See everything in the log (default is :info)
# config.log_level = :debug
+ # Prepend all log lines with the following tags
+ # config.log_tags = [ :subdomain, :uuid ]
+
# Use a different logger for distributed setups
- # config.logger = SyslogLogger.new
+ # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production
# config.cache_store = :mem_cache_store
@@ -51,4 +60,8 @@
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
+
+ # Log the query plan for queries taking more than this (works
+ # with SQLite, MySQL, and PostgreSQL)
+ # config.active_record.auto_explain_threshold_in_seconds = 0.5
end
View
10 config/environments/test.rb
@@ -2,9 +2,9 @@
# Settings specified here will take precedence over those in config/application.rb
# The test environment is used exclusively to run your application's
- # test suite. You never need to work with it otherwise. Remember that
+ # test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
- # and recreated between test runs. Don't rely on the data there!
+ # and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Configure static asset server for tests with Cache-Control for performance
@@ -29,10 +29,8 @@
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
- # Use SQL instead of Active Record's schema dumper when creating the test database.
- # This is necessary if your schema can't be completely dumped by the schema dumper,
- # like if you have constraints or database-specific column types
- # config.active_record.schema_format = :sql
+ # Raise exception on mass assignment protection for Active Record models
+ config.active_record.mass_assignment_sanitizer = :strict
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
View
5 config/initializers/inflections.rb
@@ -8,3 +8,8 @@
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
+#
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections do |inflect|
+# inflect.acronym 'RESTful'
+# end
View
2  config/initializers/secret_token.rb
@@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
-Subscreen::Application.config.secret_token = 'b329fb406ab46afef16c57741b3e158cb904cfc2fe78de3ede962ea7a3c5ecef8fbce62b099b56d027bb3297aa47c0aff2c8f1eaf67b193eb3d748256f88aefb'
+Subscreen::Application.config.secret_token = '43cf7bbb9054792bd6406dd229fac683bf853a9114e7a68748e9365643f266df2346aba29f9014c4a6198e13d04cd1f94dc7d7027a2c904725b8672220576514'
View
8 config/initializers/wrap_parameters.rb
@@ -4,9 +4,11 @@
# is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
-ActionController::Base.wrap_parameters format: [:json]
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
# Disable root element in JSON by default.
-if defined?(ActiveRecord)
- ActiveRecord::Base.include_root_in_json = false
+ActiveSupport.on_load(:active_record) do
+ self.include_root_in_json = false
end
View
17 db/schema.rb
@@ -1,3 +1,4 @@
+# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
@@ -15,30 +16,30 @@
create_table "assignments", :force => true do |t|
t.integer "screen_id", :null => false
t.integer "channel_id", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
create_table "channels", :force => true do |t|
t.string "group", :null => false
t.string "name", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
create_table "notices", :force => true do |t|
t.text "message", :null => false
t.string "status", :default => "draft", :null => false
t.string "requested_by", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
create_table "screens", :force => true do |t|
t.string "name", :null => false
t.string "description", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
end
View
38 db/seeds.rb
@@ -1,29 +1,19 @@
# -*- coding: utf-8 -*-
-tw_rubykaigi = Channel.create(:group => :tweet, :name => "rubykaigi")
-tw_kaigi1 = Channel.create(:group => :tweet, :name => "kaigi1")
-tw_kaigi2 = Channel.create(:group => :tweet, :name => "kaigi2")
+tw_sprk2012 = Channel.create(:group => :tweet, :name => "sprk2012")
-irc_rk = Channel.create(:group => :irc, :name => "rubykaigi")
-irc_rk1 = Channel.create(:group => :irc, :name => "kaigi1")
-irc_rk1_m17n = Channel.create(:group => :irc, :name => "kaigi1-m17n")
-irc_rk2 = Channel.create(:group => :irc, :name => "kaigi2")
-irc_rk2_m17n = Channel.create(:group => :irc, :name => "kaigi2-m17n")
+irc_kaigi = Channel.create(:group => :irc, :name => "kaigi")
+irc_kaigi1 = Channel.create(:group => :irc, :name => "kaigi1")
+irc_kaigi1_m17n = Channel.create(:group => :irc, :name => "kaigi1-m17n")
+irc_kaigi2 = Channel.create(:group => :irc, :name => "kaigi2")
+irc_kaigi2_m17n = Channel.create(:group => :irc, :name => "kaigi2-m17n")
-main_left = Screen.create(:name => "main-left", :description => "大ホール左スクリーン")
-main_right = Screen.create(:name => "main-right", :description => "大ホール右スクリーン")
-sub_left = Screen.create(:name => "sub-left", :description => "小ホール左スクリーン")
-sub_right = Screen.create(:name => "sub-right", :description => "小ホール右スクリーン")
-foyer = Screen.create(:name => "foyer", :description => "ホワイエ")
+screen1 = Screen.create(:name => "screen1", :description => "スクリーン1")
+screen2 = Screen.create(:name => "screen2", :description => "スクリーン2")
+screen3 = Screen.create(:name => "screen0", :description => "スクリーン3")
-main_left.channels = [tw_rubykaigi, tw_kaigi1, irc_rk1]
-main_right.channels = [irc_rk1_m17n]
-sub_left.channels = [tw_rubykaigi, tw_kaigi2, irc_rk2]
-sub_right.channels = [irc_rk2_m17n]
-foyer.channels = [tw_rubykaigi, tw_kaigi1, tw_kaigi2]
+screen1.channels = [irc_kaigi1_m17n]
+screen2.channels = [irc_kaigi2_m17n]
+screen3.channels = [irc_kaigi, tw_sprk2012]
-Notice.create({ :message => "日本Ruby会議2011がはじまりました。最後のRuby会議を大いに楽しみましょう。",
- :requested_by => "june29" })
-Notice.create({ :message => "会場内の数ヶ所に、給電所を用意しています。譲り合ってご利用ください。",
- :requested_by => "june29" })
-Notice.create({ :message => "「喫茶自由」には、参加者向けの飲み物があります。どうぞご利用ください。",
- :requested_by => "june29" })
+Notice.create({ :message => "Welcome to SPRK2012!", :requested_by => "darashi" })
+Notice.create({ :message => "ようこそ札幌Ruby会議2012へ", :requested_by => "darashi" })
View
10 lib/passers/irc-passer.rb
@@ -1,4 +1,6 @@
require_relative "passer"
+require "bundler"
+Bundler.require
require "net/irc"
class Client < Net::IRC::Client
@@ -19,8 +21,9 @@ def on_rpl_welcome(m)
def on_privmsg(m)
channel, message = *m
nick = m.prefix.nick.to_s
+ html = RailsEmoji.render(CGI.escapeHTML(message), :class => :emoji)
- @passers[channel.sub(/^#/,"").downcase].pass(:nick => nick, :message => message, :usec => Time.now.usec)
+ @passers[channel.sub(/^#/,"").downcase].pass(:nick => nick, :message => html, :usec => Time.now.usec)
end
end
@@ -30,13 +33,12 @@ def initialize(channel)
end
end
-channels = %w(rubykaigi kaigi1 kaigi2 kaigi1-m17n kaigi2-m17n rubykaigi-staff)
-client = Client.new("irc.rubykaigi.org", 6667,
+client = Client.new(configatron.irc.server, configatron.irc.port,
{
:user => configatron.irc.user,
:nick => configatron.irc.nick,
:real => configatron.irc.real,
:pass => configatron.irc.pass,
- :channels => channels
+ :channels => configatron.irc.channels
})
client.start
View
2  lib/passers/notice-passer.rb
@@ -25,5 +25,5 @@ def start(interval = 10)
end
end
-notice = NoticePasser.new("http://rk11subscreen.herokuapp.com/notices/sample.json")
+notice = NoticePasser.new("http://sprk2012subscreen.herokuapp.com/notices/sample.json")
notice.start
View
76 lib/passers/twitter-search-passer.rb
@@ -1,18 +1,17 @@
require_relative "passer"
require "uri"
+require "time"
+require "eventmachine"
+require "twitter/json_stream"
require "yajl"
-require "yajl/http_stream"
-class TwitterSearchPasser < Passer
- def initialize(auth, query, exclude = nil)
- setting = configatron.twitter.to_hash
- username = setting[auth.to_sym][:username]
- password = setting[auth.to_sym][:password]
+class TwitterSearchPasser < Passer
+ def initialize(query, exclude = nil)
@query = query
@exclude = exclude
+ @oauth = configatron.twitter.oauth.to_hash
@ignore = configatron.twitter.ignore || []
- @auth = "#{username}:#{password}"
super(:stream, "tweet-#{query}")
end
@@ -22,33 +21,58 @@ def start
current = Time.now.strftime("%Y%m%d%H")
logfile = open(File.join(File.dirname(__FILE__), "..", "..", "db", "tweets", "#{@query}.#{current}"), "a+")
- uri = URI.parse("http://#{@auth}@stream.twitter.com/1/statuses/filter.json?track=#{URI.encode(@query)}")
- Yajl::HttpStream.get(uri) do |tweet|
- next if @ignore.include? tweet["user"]["screen_name"]
- next if @exclude && tweet["text"].include?(@exclude)
+ EventMachine::run {
+ EventMachine::defer {
+ begin
+ stream = Twitter::JSONStream.connect({
+ :path => "/1/statuses/filter.json?track=#{URI.encode(@query)}",
+ :ssl => true,
+ :oauth => @oauth
+ })
+
+ stream.each_item { |item|
+ tweet = Yajl::Parser.parse(item)
+
+ next if @ignore.include? tweet["user"]["screen_name"]
+ next if @exclude && tweet["text"].include?(@exclude)
+
+ puts "[%s] @%s: %s" % [Time.parse(tweet["created_at"]).strftime("%H:%M:%S"), tweet["user"]["screen_name"], tweet["text"]]
+ pass(tweet)
+
+ current = Time.now.strftime("%Y%m%d%H")
+ unless current == prev
+ logfile.close
+ logfile = open(File.join(File.dirname(__FILE__), "..", "..", "db", "tweets", "#{@query}.#{current}"), "a+")
+ end
- p tweet
- pass(tweet)
+ logfile.puts tweet
- current = Time.now.strftime("%Y%m%d%H")
- unless current == prev
- logfile.close
- logfile = open(File.join(File.dirname(__FILE__), "..", "..", "db", "tweets", "#{@query}.#{current}"), "a+")
- end
+ prev = current
+ }
- logfile.puts tweet
+ stream.on_error { |message|
+ puts message
+ }
- prev = current
- end
+ trap('TERM') {
+ stream.stop
+ EventMachine.stop if EventMachine.reactor_running?
+ }
+ rescue => e
+ puts e
+ exit
+ end
+ }
+ }
logfile.close
end
end
-auth = ARGV[0]
-query = ARGV[1] || "rubykaigi"
-exclude = ARGV[2]
-p query, exclude
+query = ARGV[0] || "sprk2012"
+exclude = ARGV[1]
+puts "Query : #{query}"
+puts "Exclude : #{exclude}"
-twitter = TwitterSearchPasser.new(auth, query, exclude)
+twitter = TwitterSearchPasser.new(query, exclude)
twitter.start
View
47 vendor/assets/javascripts/pusher.min.js
@@ -0,0 +1,47 @@
+/*!
+ * Pusher JavaScript Library v1.12.2
+ * http://pusherapp.com/
+ *
+ * Copyright 2011, Pusher
+ * Released under the MIT licence.
+ */
+
+(function(){if(Function.prototype.scopedTo===void 0)Function.prototype.scopedTo=function(a,b){var e=this;return function(){return e.apply(a,Array.prototype.slice.call(b||[]).concat(Array.prototype.slice.call(arguments)))}};var c=function(a,b){this.options=b||{};this.key=a;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(b){var a=b.event.indexOf("pusher_internal:")===0;if(b.channel){var c;(c=e.channel(b.channel))&&c.emit(b.event,b.data)}a||e.global_emitter.emit(b.event,b.data)}).bind("disconnected",function(){e.channels.disconnect()}).bind("error",function(b){c.warn("Error",b)});c.instances.push(this);c.isReady&&e.connect()};c.instances=[];c.prototype={channel:function(a){return this.channels.find(a)},connect:function(){this.connection.connect()},disconnect:function(){this.connection.disconnect()},bind:function(a,
+b){this.global_emitter.bind(a,b);return this},bind_all:function(a){this.global_emitter.bind_all(a);return this},subscribeAll:function(){for(channelName in this.channels.channels)this.channels.channels.hasOwnProperty(channelName)&&this.subscribe(channelName)},subscribe:function(a){var b=this,e=this.channels.add(a,this);this.connection.state==="connected"&&e.authorize(this.connection.socket_id,this.options,function(c,f){c?e.emit("pusher:subscription_error",f):b.send_event("pusher:subscribe",{channel:a,
+auth:f.auth,channel_data:f.channel_data})});return e},unsubscribe:function(a){this.channels.remove(a);this.connection.state==="connected"&&this.send_event("pusher:unsubscribe",{channel:a})},send_event:function(a,b,e){return this.connection.send_event(a,b,e)},checkAppKey:function(){(this.key===null||this.key===void 0)&&c.warn("Warning","You must pass your app key when you instantiate Pusher.")}};c.Util={extend:function b(e,c){for(var f in c)e[f]=c[f]&&c[f].constructor&&c[f].constructor===Object?b(e[f]||
+{},c[f]):c[f];return e},stringify:function(){for(var b=["Pusher"],e=0;e<arguments.length;e++)typeof arguments[e]==="string"?b.push(arguments[e]):window.JSON==void 0?b.push(arguments[e].toString()):b.push(JSON.stringify(arguments[e]));return b.join(" : ")},arrayIndexOf:function(b,e){var c=Array.prototype.indexOf;if(b==null)return-1;if(c&&b.indexOf===c)return b.indexOf(e);for(i=0,l=b.length;i<l;i++)if(b[i]===e)return i;return-1}};c.debug=function(){c.log&&c.log(c.Util.stringify.apply(this,arguments))};
+c.warn=function(){window.console&&window.console.warn?window.console.warn(c.Util.stringify.apply(this,arguments)):c.log&&c.log(c.Util.stringify.apply(this,arguments))};c.VERSION="1.12.2";c.host="ws.pusherapp.com";c.ws_port=80;c.wss_port=443;c.channel_auth_endpoint="/pusher/auth";c.cdn_http="http://js.pusher.com/";c.cdn_https="https://d3dy5gmtp8yhk7.cloudfront.net/";c.dependency_suffix=".min";c.channel_auth_transport="ajax";c.activity_timeout=12E4;c.pong_timeout=3E4;c.isReady=!1;c.ready=function(){c.isReady=
+!0;for(var b=0,e=c.instances.length;b<e;b++)c.instances[b].connect()};this.Pusher=c}).call(this);
+(function(){function c(){this._callbacks={}}function a(b){this.callbacks=new c;this.global_callbacks=[];this.failThrough=b}c.prototype.get=function(b){return this._callbacks[this._prefix(b)]};c.prototype.add=function(b,a){var c=this._prefix(b);this._callbacks[c]=this._callbacks[c]||[];this._callbacks[c].push(a)};c.prototype.remove=function(b,a){if(this.get(b)){var c=Pusher.Util.arrayIndexOf(this.get(b),a);this._callbacks[this._prefix(b)].splice(c,1)}};c.prototype._prefix=function(b){return"_"+b};
+a.prototype.bind=function(b,a){this.callbacks.add(b,a);return this};a.prototype.unbind=function(b,a){this.callbacks.remove(b,a);return this};a.prototype.emit=function(b,a){for(var c=0;c<this.global_callbacks.length;c++)this.global_callbacks[c](b,a);var f=this.callbacks.get(b);if(f)for(c=0;c<f.length;c++)f[c](a);else this.failThrough&&this.failThrough(b,a);return this};a.prototype.bind_all=function(b){this.global_callbacks.push(b);return this};this.Pusher.EventsDispatcher=a}).call(this);
+(function(){function c(b,a,c){if(a[b]!==void 0)a[b](c)}function a(a,c,f){b.EventsDispatcher.call(this);this.state=void 0;this.errors=[];this.stateActions=f;this.transitions=c;this.transition(a)}var b=this.Pusher;a.prototype.transition=function(a,g){var f=this.state,h=this.stateActions;if(f&&b.Util.arrayIndexOf(this.transitions[f],a)==-1)throw this.emit("invalid_transition_attempt",{oldState:f,newState:a}),Error("Invalid transition ["+f+" to "+a+"]");c(f+"Exit",h,g);c(f+"To"+(a.substr(0,1).toUpperCase()+
+a.substr(1)),h,g);c(a+"Pre",h,g);this.state=a;this.emit("state_change",{oldState:f,newState:a});c(a+"Post",h,g)};a.prototype.is=function(b){return this.state===b};a.prototype.isNot=function(b){return this.state!==b};b.Util.extend(a.prototype,b.EventsDispatcher.prototype);this.Pusher.Machine=a}).call(this);
+(function(){var c=function(){var a=this;Pusher.EventsDispatcher.call(this);window.addEventListener!==void 0&&(window.addEventListener("online",function(){a.emit("online",null)},!1),window.addEventListener("offline",function(){a.emit("offline",null)},!1))};c.prototype.isOnLine=function(){return window.navigator.onLine===void 0?!0:window.navigator.onLine};Pusher.Util.extend(c.prototype,Pusher.EventsDispatcher.prototype);this.Pusher.NetInfo=c}).call(this);
+(function(){function c(a){a.connectionWait=0;a.openTimeout=b.TransportType==="flash"?5E3:2E3;a.connectedTimeout=2E3;a.connectionSecure=a.compulsorySecure;a.connectionAttempts=0}function a(a,r){function k(){d.connectionWait<s&&(d.connectionWait+=g);d.openTimeout<t&&(d.openTimeout+=f);d.connectedTimeout<u&&(d.connectedTimeout+=h);if(d.compulsorySecure!==!0)d.connectionSecure=!d.connectionSecure;d.connectionAttempts++}function m(){d._machine.transition("impermanentlyClosing")}function p(){d._activityTimer&&
+clearTimeout(d._activityTimer);d._activityTimer=setTimeout(function(){d.send_event("pusher:ping",{});d._activityTimer=setTimeout(function(){d.socket.close()},d.options.pong_timeout||b.pong_timeout)},d.options.activity_timeout||b.activity_timeout)}function v(){var b=d.connectionWait;if(b===0&&d.connectedAt){var a=(new Date).getTime()-d.connectedAt;a<1E3&&(b=1E3-a)}return b}function w(){d._machine.transition("open")}function x(b){b=q(b);if(b!==void 0)if(b.event==="pusher:connection_established")d._machine.transition("connected",
+b.data.socket_id);else if(b.event==="pusher:error"){var a=b.data.code;d.emit("error",{type:"PusherError",data:{code:a,message:b.data.message}});a===4E3?(d.compulsorySecure=!0,d.connectionSecure=!0,d.options.encrypted=!0,m()):a<4100?d._machine.transition("permanentlyClosing"):a<4200?(d.connectionWait=1E3,d._machine.transition("waiting")):a<4300?m():d._machine.transition("permanentlyClosing")}}function y(a){p();a=q(a);if(a!==void 0){b.debug("Event recd",a);switch(a.event){case "pusher:error":d.emit("error",
+{type:"PusherError",data:a.data});break;case "pusher:ping":d.send_event("pusher:pong",{})}d.emit("message",a)}}function q(b){try{var a=JSON.parse(b.data);if(typeof a.data==="string")try{a.data=JSON.parse(a.data)}catch(c){if(!(c instanceof SyntaxError))throw c;}return a}catch(e){d.emit("error",{type:"MessageParseError",error:e,data:b.data})}}function n(){d._machine.transition("waiting")}function o(b){d.emit("error",{type:"WebSocketError",error:b})}function j(a,c){var e=d.state;d.state=a;e!==a&&(b.debug("State changed",
+e+" -> "+a),d.emit("state_change",{previous:e,current:a}),d.emit(a,c))}var d=this;b.EventsDispatcher.call(this);this.options=b.Util.extend({encrypted:!1},r);this.netInfo=new b.NetInfo;this.netInfo.bind("online",function(){d._machine.is("waiting")&&(d._machine.transition("connecting"),j("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 b.Machine("initialized",e,{initializedPre:function(){d.compulsorySecure=d.options.encrypted;d.key=a;d.socket=null;d.socket_id=null;d.state="initialized"},waitingPre:function(){d.connectionWait>0&&d.emit("connecting_in",d.connectionWait);d.netInfo.isOnLine()&&d.connectionAttempts<=4?j("connecting"):j("unavailable");if(d.netInfo.isOnLine())d._waitingTimer=setTimeout(function(){d._machine.transition("connecting")},v())},waitingExit:function(){clearTimeout(d._waitingTimer)},
+connectingPre:function(){if(d.netInfo.isOnLine()===!1)d._machine.transition("waiting"),j("unavailable");else{var a;a=b.ws_port;var c="ws://";if(d.connectionSecure||document.location.protocol==="https:")a=b.wss_port,c="wss://";a=c+b.host+":"+a+"/app/"+d.key+"?protocol=5&client=js&version="+b.VERSION+"&flash="+(b.TransportType==="flash"?"true":"false");b.debug("Connecting",a);d.socket=new b.Transport(a);d.socket.onopen=w;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=x;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=y;d.socket.onerror=
+o;d.socket.onclose=n;c(d);d.connectedAt=(new Date).getTime();p()},connectedPost:function(){j("connected")},connectedExit:function(){d._activityTimer&&clearTimeout(d._activityTimer);j("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(){j("failed");
+b.debug("WebSockets are not available in this browser.")},permanentlyClosedPost:function(){j("disconnected")}})}var b=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,h=2E3,s=5*g,t=5*f,u=5*h;a.prototype.connect=function(){!this._machine.is("failed")&&!b.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"))};a.prototype.send=function(a){if(this._machine.is("connected")){var b=
+this;setTimeout(function(){b.socket.send(a)},0);return!0}else return!1};a.prototype.send_event=function(a,c,e){a={event:a,data:c};e&&(a.channel=e);b.debug("Event sent",a);return this.send(JSON.stringify(a))};a.prototype.disconnect=function(){this._machine.is("permanentlyClosed")||(this._machine.is("waiting")||this._machine.is("failed")?this._machine.transition("permanentlyClosed"):this._machine.transition("permanentlyClosing"))};b.Util.extend(a.prototype,b.EventsDispatcher.prototype);this.Pusher.Connection=
+a}).call(this);
+(function(){Pusher.Channels=function(){this.channels={}};Pusher.Channels.prototype={add:function(a,b){var c=this.find(a);c||(c=Pusher.Channel.factory(a,b),this.channels[a]=c);return c},find:function(a){return this.channels[a]},remove:function(a){delete this.channels[a]},disconnect:function(){for(var a in this.channels)this.channels[a].disconnect()}};Pusher.Channel=function(a,b){var c=this;Pusher.EventsDispatcher.call(this,function(b){Pusher.debug("No callbacks on "+a+" for "+b)});this.pusher=b;this.name=
+a;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(a,b,c){return c(!1,{})},trigger:function(a,b){return this.pusher.send_event(a,b,this.name)}};Pusher.Util.extend(Pusher.Channel.prototype,Pusher.EventsDispatcher.prototype);
+Pusher.Channel.PrivateChannel={authorize:function(a,b,c){var g=this;return(new Pusher.Channel.Authorizer(this,Pusher.channel_auth_transport,b)).authorize(a,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(a){var b=this,c=function(){this._members_map={};this.count=0;this.me=null};c.call(this);a.bind("pusher_internal:authorized",function(c){var e=
+JSON.parse(c.channel_data);a.bind("pusher_internal:subscription_succeeded",function(c){b._members_map=c.presence.hash;b.count=c.presence.count;b.me=b.get(e.user_id);a.emit("pusher:subscription_succeeded",b)})});a.bind("pusher_internal:member_added",function(c){b.get(c.user_id)===null&&b.count++;b._members_map[c.user_id]=c.user_info;a.emit("pusher:member_added",b.get(c.user_id))});a.bind("pusher_internal:member_removed",function(c){var e=b.get(c.user_id);e&&(delete b._members_map[c.user_id],b.count--,
+a.emit("pusher:member_removed",e))});a.bind("pusher_internal:disconnected",function(){c.call(b)})};c.prototype={each:function(a){for(var b in this._members_map)a(this.get(b))},get:function(a){return this._members_map.hasOwnProperty(a)?{id:a,info:this._members_map[a]}:null}};Pusher.Channel.factory=function(a,b){var c=new Pusher.Channel(a,b);a.indexOf("private-")===0?Pusher.Util.extend(c,Pusher.Channel.PrivateChannel):a.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,a,b){this.channel=c;this.type=a;this.authOptions=(b||{}).auth||{}};Pusher.Channel.Authorizer.prototype={composeQuery:function(c){var c="&socket_id="+encodeURIComponent(c)+"&channel_name="+encodeURIComponent(this.channel.name),a;for(a in this.authOptions.params)c+="&"+encodeURIComponent(a)+"="+encodeURIComponent(this.authOptions.params[a]);return c},authorize:function(c,a){return Pusher.authorizers[this.type].call(this,c,a)}};Pusher.auth_callbacks={};
+Pusher.authorizers={ajax:function(c,a){var b;b=Pusher.XHR?new Pusher.XHR:window.XMLHttpRequest?new window.XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");b.open("POST",Pusher.channel_auth_endpoint,!0);b.setRequestHeader("Content-Type","application/x-www-form-urlencoded");for(var e in this.authOptions.headers)b.setRequestHeader(e,this.authOptions.headers[e]);b.onreadystatechange=function(){if(b.readyState==4)if(b.status==200){var c,e=!1;try{c=JSON.parse(b.responseText),e=!0}catch(h){a(!0,"JSON returned from webapp was invalid, yet status code was 200. Data was: "+
+b.responseText)}e&&a(!1,c)}else Pusher.warn("Couldn't get auth info from your webapp",b.status),a(!0,b.status)};b.send(this.composeQuery(c));return b},jsonp:function(c,a){this.authOptions.headers!==void 0&&Pusher.warn("Warn","To send headers with the auth request, you must use AJAX, rather than JSONP.");var b=document.createElement("script");Pusher.auth_callbacks[this.channel.name]=function(b){a(!1,b)};b.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(b,e.firstChild)}}}).call(this);
+var _require=function(){function c(a,c){document.addEventListener?a.addEventListener("load",c,!1):a.attachEvent("onreadystatechange",function(){(a.readyState=="loaded"||a.readyState=="complete")&&c()})}function a(a,e){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(){e()});g.appendChild(f)}return function(b,c){for(var g=0,f=0;f<b.length;f++)a(b[f],function(){b.length==
+++g&&setTimeout(c,0)})}}();
+(function(){!window.WebSocket&&window.MozWebSocket&&(window.WebSocket=window.MozWebSocket);if(window.WebSocket)Pusher.Transport=window.WebSocket,Pusher.TransportType="native";var c=(document.location.protocol=="http:"?Pusher.cdn_http:Pusher.cdn_https)+Pusher.VERSION,a=[];window.JSON||a.push(c+"/json2"+Pusher.dependency_suffix+".js");if(!window.WebSocket)window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=!0,a.push(c+"/flashfallback"+Pusher.dependency_suffix+".js");var b=function(){return window.WebSocket?function(){Pusher.ready()}:
+function(){window.WebSocket?(Pusher.Transport=window.WebSocket,Pusher.TransportType="flash",window.WEB_SOCKET_SWF_LOCATION=c+"/WebSocketMain.swf",WebSocket.__addTask(function(){Pusher.ready()}),WebSocket.__initialize()):(Pusher.Transport=null,Pusher.TransportType="none",Pusher.ready())}}(),e=function(a){var b=function(){document.body?a():setTimeout(b,0)};b()},g=function(){e(b)};a.length>0?_require(a,g):g()})();
Something went wrong with that request. Please try again.