pusher-fake 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/features/channel_presence.feature +21 -0
- data/features/channel_subscribe.feature +33 -0
- data/features/channel_trigger.feature +74 -0
- data/features/client_connect.feature +8 -0
- data/features/step_definitions/channel_steps.rb +82 -0
- data/features/step_definitions/client_steps.rb +28 -0
- data/features/step_definitions/event_steps.rb +41 -0
- data/features/step_definitions/navigation_steps.rb +3 -0
- data/features/step_definitions/presence_steps.rb +6 -0
- data/features/support/application.rb +29 -0
- data/features/support/application/public/javascripts/vendor/pusher-1.11.js +1155 -0
- data/features/support/application/views/index.erb +43 -0
- data/features/support/environment.rb +12 -0
- data/features/support/pusher-fake.rb +7 -0
- data/features/support/wait.rb +9 -0
- data/lib/pusher-fake.rb +35 -0
- data/lib/pusher-fake/channel.rb +51 -0
- data/lib/pusher-fake/channel/presence.rb +50 -0
- data/lib/pusher-fake/channel/private.rb +44 -0
- data/lib/pusher-fake/channel/public.rb +63 -0
- data/lib/pusher-fake/configuration.rb +31 -0
- data/lib/pusher-fake/connection.rb +54 -0
- data/lib/pusher-fake/server.rb +48 -0
- data/lib/pusher-fake/server/application.rb +60 -0
- data/spec/lib/pusher-fake/channel/presence_spec.rb +133 -0
- data/spec/lib/pusher-fake/channel/private_spec.rb +125 -0
- data/spec/lib/pusher-fake/channel/public_spec.rb +90 -0
- data/spec/lib/pusher-fake/channel_spec.rb +117 -0
- data/spec/lib/pusher-fake/configuration_spec.rb +10 -0
- data/spec/lib/pusher-fake/connection_spec.rb +193 -0
- data/spec/lib/pusher-fake/server/application_spec.rb +133 -0
- data/spec/lib/pusher-fake/server_spec.rb +150 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/have_configuration_option_matcher.rb +19 -0
- metadata +265 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@javascript
|
|
2
|
+
Feature: Client on a presence channel
|
|
3
|
+
|
|
4
|
+
Background:
|
|
5
|
+
Given I am connected
|
|
6
|
+
|
|
7
|
+
Scenario: Client subscribes to a presence channel
|
|
8
|
+
When I subscribe to the "presence-game-1" channel with presence events
|
|
9
|
+
Then I should see 1 client
|
|
10
|
+
When Bob is connected
|
|
11
|
+
Then I should see 1 client
|
|
12
|
+
When Bob is subscribed to the "presence-game-1" channel
|
|
13
|
+
Then I should see 2 clients
|
|
14
|
+
|
|
15
|
+
Scenario: Client unsubscribes from a presence channel, with other clients
|
|
16
|
+
Given Bob is connected
|
|
17
|
+
And Bob is subscribed to the "presence-game-1" channel
|
|
18
|
+
When I subscribe to the "presence-game-1" channel with presence events
|
|
19
|
+
Then I should see 2 clients
|
|
20
|
+
When Bob unsubscribes from the "presence-game-1" channel
|
|
21
|
+
Then I should see 1 client
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@javascript
|
|
2
|
+
Feature: Client subscribing to a channel
|
|
3
|
+
|
|
4
|
+
Background:
|
|
5
|
+
Given I am connected
|
|
6
|
+
|
|
7
|
+
Scenario: Client subscribes to a channel
|
|
8
|
+
When I subscribe to the "chat-message" channel
|
|
9
|
+
Then I should be subscribed to the "chat-message" channel
|
|
10
|
+
|
|
11
|
+
Scenario: Client subscribes to multiple channels
|
|
12
|
+
When I subscribe to the "chat-enter" channel
|
|
13
|
+
And I subscribe to the "chat-exit" channel
|
|
14
|
+
Then I should be subscribed to the "chat-enter" channel
|
|
15
|
+
And I should be subscribed to the "chat-exit" channel
|
|
16
|
+
|
|
17
|
+
Scenario: Client subscribes to a private channel
|
|
18
|
+
When I subscribe to the "private-message-bob" channel
|
|
19
|
+
Then I should be subscribed to the "private-message-bob" channel
|
|
20
|
+
|
|
21
|
+
Scenario: Client unsuccessfully subscribes to a private channel
|
|
22
|
+
Given I change my socket ID
|
|
23
|
+
When I subscribe to the "private-message-bob" channel
|
|
24
|
+
Then I should not be subscribed to the "private-message-bob" channel
|
|
25
|
+
|
|
26
|
+
Scenario: Client subscribes to a presence channel
|
|
27
|
+
When I subscribe to the "presence-game-1" channel
|
|
28
|
+
Then I should be subscribed to the "presence-game-1" channel
|
|
29
|
+
|
|
30
|
+
Scenario: Client unsuccessfully subscribes to a presence channel
|
|
31
|
+
Given I change my socket ID
|
|
32
|
+
When I subscribe to the "presence-game-1" channel
|
|
33
|
+
Then I should not be subscribed to the "presence-game-1" channel
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
@javascript
|
|
2
|
+
Feature: Triggering events on a channel
|
|
3
|
+
|
|
4
|
+
Background:
|
|
5
|
+
Given I am connected
|
|
6
|
+
And Bob is connected
|
|
7
|
+
|
|
8
|
+
Scenario: Server triggers an event on a subscribed public channel
|
|
9
|
+
Given I am subscribed to the "chat" channel
|
|
10
|
+
And Bob is subscribed to the "chat" channel
|
|
11
|
+
When a "message" event is triggered on the "chat" channel
|
|
12
|
+
Then I should receive a "message" event on the "chat" channel
|
|
13
|
+
And Bob should receive a "message" event on the "chat" channel
|
|
14
|
+
|
|
15
|
+
Scenario: Server triggers an event on a previously subscribed public channel
|
|
16
|
+
Given I am subscribed to the "chat" channel
|
|
17
|
+
And Bob is subscribed to the "chat" channel
|
|
18
|
+
And I unsubscribe from the "chat" channel
|
|
19
|
+
When a "message" event is triggered on the "chat" channel
|
|
20
|
+
Then I should not receive a "message" event on the "chat" channel
|
|
21
|
+
And Bob should receive a "message" event on the "chat" channel
|
|
22
|
+
|
|
23
|
+
Scenario: Server triggers an event on an unsubscribed public channel
|
|
24
|
+
When a "message" event is triggered on the "chat" channel
|
|
25
|
+
Then I should not receive a "message" event on the "chat" channel
|
|
26
|
+
And Bob should not receive a "message" event on the "chat" channel
|
|
27
|
+
|
|
28
|
+
Scenario: Server triggers an event on a subscribed private channel
|
|
29
|
+
Given I am subscribed to the "private-chat" channel
|
|
30
|
+
And Bob is subscribed to the "private-chat" channel
|
|
31
|
+
When a "message" event is triggered on the "private-chat" channel
|
|
32
|
+
Then I should receive a "message" event on the "private-chat" channel
|
|
33
|
+
And Bob should receive a "message" event on the "private-chat" channel
|
|
34
|
+
|
|
35
|
+
Scenario: Server triggers an event on a previously subscribed private channel
|
|
36
|
+
Given I am subscribed to the "private-chat" channel
|
|
37
|
+
And Bob is subscribed to the "private-chat" channel
|
|
38
|
+
And I unsubscribe from the "private-chat" channel
|
|
39
|
+
When a "message" event is triggered on the "private-chat" channel
|
|
40
|
+
Then I should not receive a "message" event on the "private-chat" channel
|
|
41
|
+
And Bob should receive a "message" event on the "private-chat" channel
|
|
42
|
+
|
|
43
|
+
Scenario: Server triggers an event on an unsubscribed private channel
|
|
44
|
+
When a "message" event is triggered on the "private-chat" channel
|
|
45
|
+
Then I should not receive a "message" event on the "private-chat" channel
|
|
46
|
+
And Bob should not receive a "message" event on the "private-chat" channel
|
|
47
|
+
|
|
48
|
+
Scenario: Client triggers a client event on a subscribed private channel
|
|
49
|
+
Given I am subscribed to the "private-chat" channel
|
|
50
|
+
And Bob is subscribed to the "private-chat" channel
|
|
51
|
+
When I trigger the "client-message" event on the "private-chat" channel
|
|
52
|
+
Then I should receive a "client-message" event on the "private-chat" channel
|
|
53
|
+
And Bob should receive a "client-message" event on the "private-chat" channel
|
|
54
|
+
|
|
55
|
+
Scenario: Client triggers a client event on a previously subscribed private channel
|
|
56
|
+
Given I am subscribed to the "private-chat" channel
|
|
57
|
+
And Bob is subscribed to the "private-chat" channel
|
|
58
|
+
And I unsubscribe from the "private-chat" channel
|
|
59
|
+
When I manually trigger the "client-message" event on the "private-chat" channel
|
|
60
|
+
Then I should not receive a "client-message" event on the "private-chat" channel
|
|
61
|
+
And Bob should not receive a "client-message" event on the "private-chat" channel
|
|
62
|
+
|
|
63
|
+
Scenario: Client triggers a client event on an unsubscribed private channel
|
|
64
|
+
Given Bob is subscribed to the "private-chat" channel
|
|
65
|
+
When I manually trigger the "client-message" event on the "private-chat" channel
|
|
66
|
+
Then I should not receive a "client-message" event on the "private-chat" channel
|
|
67
|
+
And Bob should not receive a "client-message" event on the "private-chat" channel
|
|
68
|
+
|
|
69
|
+
Scenario: Client triggers a client event on a subscribed public channel
|
|
70
|
+
Given I am subscribed to the "chat" channel
|
|
71
|
+
And Bob is subscribed to the "chat" channel
|
|
72
|
+
When I trigger the "client-message" event on the "chat" channel
|
|
73
|
+
Then I should not receive a "client-message" event on the "chat" channel
|
|
74
|
+
And Bob should not receive a "client-message" event on the "chat" channel
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Given %{I am subscribed to the "$channel" channel} do |channel|
|
|
2
|
+
step %{I subscribe to the "#{channel}" channel}
|
|
3
|
+
step %{I should be subscribed to the "#{channel}" channel}
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
Given %{$name is subscribed to the "$channel" channel} do |name, channel|
|
|
7
|
+
using_session(name) do
|
|
8
|
+
step %{I am subscribed to the "#{channel}" channel}
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
When %{I subscribe to the "$channel" channel} do |channel|
|
|
13
|
+
page.execute_script("Pusher.instance.subscribe(#{channel.to_json})")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
When %{I subscribe to the "$channel" channel with presence events} do |channel|
|
|
17
|
+
page.execute_script(%{
|
|
18
|
+
var list = document.querySelector("#presence ul"),
|
|
19
|
+
channel = Pusher.instance.subscribe(#{channel.to_json});
|
|
20
|
+
|
|
21
|
+
channel.bind("pusher:subscription_succeeded", function(clients) {
|
|
22
|
+
var
|
|
23
|
+
count = document.querySelector("#presence header h1 span");
|
|
24
|
+
count.innerHTML = clients.count;
|
|
25
|
+
|
|
26
|
+
clients.each(function(client) {
|
|
27
|
+
var
|
|
28
|
+
element = list.appendChild(document.createElement("li"));
|
|
29
|
+
element.setAttribute("id", "client-" + client.id);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
channel.bind("pusher:member_added", function(client) {
|
|
33
|
+
var
|
|
34
|
+
count = document.querySelector("#presence header h1 span");
|
|
35
|
+
count.innerHTML = parseInt(count.innerHTML, 10) + 1;
|
|
36
|
+
|
|
37
|
+
var
|
|
38
|
+
element = list.appendChild(document.createElement("li"));
|
|
39
|
+
element.setAttribute("id", "client-" + client.id);
|
|
40
|
+
});
|
|
41
|
+
channel.bind("pusher:member_removed", function(client) {
|
|
42
|
+
var item = list.querySelector("li#client-" + client.id),
|
|
43
|
+
count = document.querySelector("#presence header h1 span");
|
|
44
|
+
|
|
45
|
+
count.innerHTML = parseInt(count.innerHTML, 10) - 1;
|
|
46
|
+
|
|
47
|
+
list.removeChild(item);
|
|
48
|
+
});
|
|
49
|
+
})
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
When %{I unsubscribe from the "$channel" channel} do |channel|
|
|
53
|
+
page.execute_script("Pusher.instance.unsubscribe(#{channel.to_json})")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
When %{$name unsubscribes from the "$channel" channel} do |name, channel|
|
|
57
|
+
using_session(name) do
|
|
58
|
+
step %{I unsubscribe from the "#{channel}" channel}
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Then %{I should be subscribed to the "$channel" channel} do |channel|
|
|
63
|
+
wait_until do
|
|
64
|
+
subscribed = page.evaluate_script(%{
|
|
65
|
+
var
|
|
66
|
+
channel = Pusher.instance.channel(#{channel.to_json});
|
|
67
|
+
channel && channel.subscribed;
|
|
68
|
+
})
|
|
69
|
+
subscribed == true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Then %{I should not be subscribed to the "$channel" channel} do |channel|
|
|
74
|
+
wait do
|
|
75
|
+
subscribed = page.evaluate_script(%{
|
|
76
|
+
var
|
|
77
|
+
channel = Pusher.instance.channel(#{channel.to_json});
|
|
78
|
+
channel && channel.subscribed;
|
|
79
|
+
})
|
|
80
|
+
subscribed.should be_false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Given "I am connected" do
|
|
2
|
+
step %{I am on the homepage}
|
|
3
|
+
step %{I should be connected}
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
Given %{$name is connected} do |name|
|
|
7
|
+
using_session(name) do
|
|
8
|
+
step %{I am connected}
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Given "I change my socket ID" do
|
|
13
|
+
page.execute_script("Pusher.instance.connection.socket_id = -1;")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Then "I should be connected" do
|
|
17
|
+
wait_until do
|
|
18
|
+
state = page.evaluate_script("Pusher.instance.connection.state")
|
|
19
|
+
state == "connected"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Then "I should not be connected" do
|
|
24
|
+
wait_until do
|
|
25
|
+
state = page.evaluate_script("Pusher.instance.connection.state")
|
|
26
|
+
state == "unavailable"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
When %{a "$event" event is triggered on the "$channel" channel} do |event, channel|
|
|
2
|
+
Pusher[channel].trigger(event, {})
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
When %{I trigger the "$event" event on the "$channel" channel} do |event, channel|
|
|
6
|
+
page.execute_script(%{
|
|
7
|
+
var
|
|
8
|
+
channel = Pusher.instance.channel(#{channel.to_json});
|
|
9
|
+
channel.trigger(#{event.to_json}, {});
|
|
10
|
+
})
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
When %{I manually trigger the "$event" event on the "$channel" channel} do |event, channel|
|
|
14
|
+
page.execute_script(%{Pusher.instance.send_event(#{event.to_json}, {}, #{channel.to_json})})
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Then /^I should receive a "([^"]+)" event on the "([^"]+)" channel$/ do |event, channel|
|
|
18
|
+
wait do
|
|
19
|
+
events = page.evaluate_script("Pusher.instance.events[#{[channel, event].join(":").to_json}]")
|
|
20
|
+
events.length.should == 1
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Then /^I should not receive a "([^"]+)" event on the "([^"]+)" channel$/ do |event, channel|
|
|
25
|
+
wait do
|
|
26
|
+
events = page.evaluate_script("Pusher.instance.events[#{[channel, event].join(":").to_json}]")
|
|
27
|
+
events.should be_nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Then /^([^I]+) should receive a "([^"]+)" event on the "([^"]+)" channel$/ do |name, event, channel|
|
|
32
|
+
using_session(name) do
|
|
33
|
+
step %{I should receive a "#{event}" event on the "#{channel}" channel}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Then /^([^I]+) should not receive a "([^"]+)" event on the "([^"]+)" channel$/ do |name, event, channel|
|
|
38
|
+
using_session(name) do
|
|
39
|
+
step %{I should not receive a "#{event}" event on the "#{channel}" channel}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "pusher"
|
|
2
|
+
require "sinatra"
|
|
3
|
+
|
|
4
|
+
Pusher.url = "http://PUSHER_API_KEY:PUSHER_API_SECRET@localhost:8081/apps/PUSHER_APP_ID"
|
|
5
|
+
|
|
6
|
+
class Sinatra::Application
|
|
7
|
+
set :root, Proc.new { File.join(File.dirname(__FILE__), "application") }
|
|
8
|
+
set :views, Proc.new { File.join(root, "views") }
|
|
9
|
+
set :public_folder, Proc.new { File.join(root, "public") }
|
|
10
|
+
|
|
11
|
+
disable :logging
|
|
12
|
+
|
|
13
|
+
get "/" do
|
|
14
|
+
erb :index
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
post "/pusher/auth" do
|
|
18
|
+
channel = Pusher[params[:channel_name]]
|
|
19
|
+
|
|
20
|
+
if params[:channel_name] =~ /^private-/
|
|
21
|
+
channel.authenticate(params[:socket_id]).to_json
|
|
22
|
+
elsif params[:channel_name] =~ /^presence-/
|
|
23
|
+
channel.authenticate(params[:socket_id], {
|
|
24
|
+
user_id: params[:socket_id],
|
|
25
|
+
user_info: {}
|
|
26
|
+
}).to_json
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,1155 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Pusher JavaScript Library v1.11.0
|
|
3
|
+
* http://pusherapp.com/
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2011, Pusher
|
|
6
|
+
* Released under the MIT licence.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
if (Function.prototype.scopedTo === undefined) {
|
|
10
|
+
Function.prototype.scopedTo = function(context, args) {
|
|
11
|
+
var f = this;
|
|
12
|
+
return function() {
|
|
13
|
+
return f.apply(context, Array.prototype.slice.call(args || [])
|
|
14
|
+
.concat(Array.prototype.slice.call(arguments)));
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
var Pusher = function(app_key, options) {
|
|
20
|
+
this.options = options || {};
|
|
21
|
+
this.key = app_key;
|
|
22
|
+
this.channels = new Pusher.Channels();
|
|
23
|
+
this.global_emitter = new Pusher.EventsDispatcher()
|
|
24
|
+
|
|
25
|
+
var self = this;
|
|
26
|
+
|
|
27
|
+
this.checkAppKey();
|
|
28
|
+
|
|
29
|
+
this.connection = new Pusher.Connection(this.key, this.options);
|
|
30
|
+
|
|
31
|
+
// Setup / teardown connection
|
|
32
|
+
this.connection
|
|
33
|
+
.bind('connected', function() {
|
|
34
|
+
self.subscribeAll();
|
|
35
|
+
})
|
|
36
|
+
.bind('message', function(params) {
|
|
37
|
+
var internal = (params.event.indexOf('pusher_internal:') === 0);
|
|
38
|
+
if (params.channel) {
|
|
39
|
+
var channel;
|
|
40
|
+
if (channel = self.channel(params.channel)) {
|
|
41
|
+
channel.emit(params.event, params.data);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Emit globaly [deprecated]
|
|
45
|
+
if (!internal) self.global_emitter.emit(params.event, params.data);
|
|
46
|
+
})
|
|
47
|
+
.bind('disconnected', function() {
|
|
48
|
+
self.channels.disconnect();
|
|
49
|
+
})
|
|
50
|
+
.bind('error', function(err) {
|
|
51
|
+
Pusher.warn('Error', err);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
Pusher.instances.push(this);
|
|
55
|
+
|
|
56
|
+
if (Pusher.isReady) self.connect();
|
|
57
|
+
};
|
|
58
|
+
Pusher.instances = [];
|
|
59
|
+
Pusher.prototype = {
|
|
60
|
+
channel: function(name) {
|
|
61
|
+
return this.channels.find(name);
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
connect: function() {
|
|
65
|
+
this.connection.connect();
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
disconnect: function() {
|
|
69
|
+
this.connection.disconnect();
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
bind: function(event_name, callback) {
|
|
73
|
+
this.global_emitter.bind(event_name, callback);
|
|
74
|
+
return this;
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
bind_all: function(callback) {
|
|
78
|
+
this.global_emitter.bind_all(callback);
|
|
79
|
+
return this;
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
subscribeAll: function() {
|
|
83
|
+
var channel;
|
|
84
|
+
for (channel in this.channels.channels) {
|
|
85
|
+
if (this.channels.channels.hasOwnProperty(channel)) {
|
|
86
|
+
this.subscribe(channel);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
subscribe: function(channel_name) {
|
|
92
|
+
var self = this;
|
|
93
|
+
var channel = this.channels.add(channel_name, this);
|
|
94
|
+
if (this.connection.state === 'connected') {
|
|
95
|
+
channel.authorize(this, function(err, data) {
|
|
96
|
+
if (err) {
|
|
97
|
+
channel.emit('pusher:subscription_error', data);
|
|
98
|
+
} else {
|
|
99
|
+
self.send_event('pusher:subscribe', {
|
|
100
|
+
channel: channel_name,
|
|
101
|
+
auth: data.auth,
|
|
102
|
+
channel_data: data.channel_data
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return channel;
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
unsubscribe: function(channel_name) {
|
|
111
|
+
this.channels.remove(channel_name);
|
|
112
|
+
if (this.connection.state === 'connected') {
|
|
113
|
+
this.send_event('pusher:unsubscribe', {
|
|
114
|
+
channel: channel_name
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
send_event: function(event_name, data, channel) {
|
|
120
|
+
return this.connection.send_event(event_name, data, channel);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
checkAppKey: function() {
|
|
124
|
+
if(this.key === null || this.key === undefined) {
|
|
125
|
+
Pusher.warn('Warning', 'You must pass your app key when you instantiate Pusher.');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
Pusher.Util = {
|
|
131
|
+
extend: function extend(target, extensions) {
|
|
132
|
+
for (var property in extensions) {
|
|
133
|
+
if (extensions[property] && extensions[property].constructor &&
|
|
134
|
+
extensions[property].constructor === Object) {
|
|
135
|
+
target[property] = extend(target[property] || {}, extensions[property]);
|
|
136
|
+
} else {
|
|
137
|
+
target[property] = extensions[property];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return target;
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
stringify: function stringify() {
|
|
144
|
+
var m = ["Pusher"]
|
|
145
|
+
for (var i = 0; i < arguments.length; i++){
|
|
146
|
+
if (typeof arguments[i] === "string") {
|
|
147
|
+
m.push(arguments[i])
|
|
148
|
+
} else {
|
|
149
|
+
if (window['JSON'] == undefined) {
|
|
150
|
+
m.push(arguments[i].toString());
|
|
151
|
+
} else {
|
|
152
|
+
m.push(JSON.stringify(arguments[i]))
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
return m.join(" : ")
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
arrayIndexOf: function(array, item) { // MSIE doesn't have array.indexOf
|
|
160
|
+
var nativeIndexOf = Array.prototype.indexOf;
|
|
161
|
+
if (array == null) return -1;
|
|
162
|
+
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
|
163
|
+
for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
|
|
164
|
+
return -1;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// To receive log output provide a Pusher.log function, for example
|
|
169
|
+
// Pusher.log = function(m){console.log(m)}
|
|
170
|
+
Pusher.debug = function() {
|
|
171
|
+
if (!Pusher.log) return
|
|
172
|
+
Pusher.log(Pusher.Util.stringify.apply(this, arguments))
|
|
173
|
+
}
|
|
174
|
+
Pusher.warn = function() {
|
|
175
|
+
if (window.console && window.console.warn) {
|
|
176
|
+
window.console.warn(Pusher.Util.stringify.apply(this, arguments));
|
|
177
|
+
} else {
|
|
178
|
+
if (!Pusher.log) return
|
|
179
|
+
Pusher.log(Pusher.Util.stringify.apply(this, arguments));
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Pusher defaults
|
|
184
|
+
Pusher.VERSION = '1.11.0';
|
|
185
|
+
|
|
186
|
+
Pusher.host = 'ws.pusherapp.com';
|
|
187
|
+
Pusher.ws_port = 80;
|
|
188
|
+
Pusher.wss_port = 443;
|
|
189
|
+
Pusher.channel_auth_endpoint = '/pusher/auth';
|
|
190
|
+
Pusher.cdn_http = 'http://js.pusher.com/'
|
|
191
|
+
Pusher.cdn_https = 'https://d3dy5gmtp8yhk7.cloudfront.net/'
|
|
192
|
+
Pusher.dependency_suffix = '';
|
|
193
|
+
Pusher.channel_auth_transport = 'ajax';
|
|
194
|
+
Pusher.activity_timeout = 120000;
|
|
195
|
+
Pusher.pong_timeout = 30000;
|
|
196
|
+
|
|
197
|
+
Pusher.isReady = false;
|
|
198
|
+
Pusher.ready = function() {
|
|
199
|
+
Pusher.isReady = true;
|
|
200
|
+
for (var i = 0, l = Pusher.instances.length; i < l; i++) {
|
|
201
|
+
Pusher.instances[i].connect();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
;(function() {
|
|
206
|
+
/* Abstract event binding
|
|
207
|
+
Example:
|
|
208
|
+
|
|
209
|
+
var MyEventEmitter = function(){};
|
|
210
|
+
MyEventEmitter.prototype = new Pusher.EventsDispatcher;
|
|
211
|
+
|
|
212
|
+
var emitter = new MyEventEmitter();
|
|
213
|
+
|
|
214
|
+
// Bind to single event
|
|
215
|
+
emitter.bind('foo_event', function(data){ alert(data)} );
|
|
216
|
+
|
|
217
|
+
// Bind to all
|
|
218
|
+
emitter.bind_all(function(event_name, data){ alert(data) });
|
|
219
|
+
|
|
220
|
+
--------------------------------------------------------*/
|
|
221
|
+
function EventsDispatcher(failThrough) {
|
|
222
|
+
this.callbacks = {};
|
|
223
|
+
this.global_callbacks = [];
|
|
224
|
+
// Run this function when dispatching an event when no callbacks defined
|
|
225
|
+
this.failThrough = failThrough;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
EventsDispatcher.prototype.bind = function(event_name, callback) {
|
|
229
|
+
this.callbacks[event_name] = this.callbacks[event_name] || [];
|
|
230
|
+
this.callbacks[event_name].push(callback);
|
|
231
|
+
return this;// chainable
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
EventsDispatcher.prototype.unbind = function(eventName, callback) {
|
|
235
|
+
if(this.callbacks[eventName]) {
|
|
236
|
+
var index = Pusher.Util.arrayIndexOf(this.callbacks[eventName], callback);
|
|
237
|
+
this.callbacks[eventName].splice(index, 1);
|
|
238
|
+
}
|
|
239
|
+
return this;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
EventsDispatcher.prototype.emit = function(event_name, data) {
|
|
243
|
+
// Global callbacks
|
|
244
|
+
for (var i = 0; i < this.global_callbacks.length; i++) {
|
|
245
|
+
this.global_callbacks[i](event_name, data);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Event callbacks
|
|
249
|
+
var callbacks = this.callbacks[event_name];
|
|
250
|
+
if (callbacks) {
|
|
251
|
+
for (var i = 0; i < callbacks.length; i++) {
|
|
252
|
+
callbacks[i](data);
|
|
253
|
+
}
|
|
254
|
+
} else if (this.failThrough) {
|
|
255
|
+
this.failThrough(event_name, data)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return this;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
EventsDispatcher.prototype.bind_all = function(callback) {
|
|
262
|
+
this.global_callbacks.push(callback);
|
|
263
|
+
return this;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
this.Pusher.EventsDispatcher = EventsDispatcher;
|
|
267
|
+
}).call(this);
|
|
268
|
+
|
|
269
|
+
;(function() {
|
|
270
|
+
var Pusher = this.Pusher;
|
|
271
|
+
|
|
272
|
+
/*-----------------------------------------------
|
|
273
|
+
Helpers:
|
|
274
|
+
-----------------------------------------------*/
|
|
275
|
+
|
|
276
|
+
function capitalize(str) {
|
|
277
|
+
return str.substr(0, 1).toUpperCase() + str.substr(1);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
function safeCall(method, obj, data) {
|
|
282
|
+
if (obj[method] !== undefined) {
|
|
283
|
+
obj[method](data);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/*-----------------------------------------------
|
|
288
|
+
The State Machine
|
|
289
|
+
-----------------------------------------------*/
|
|
290
|
+
function Machine(initialState, transitions, stateActions) {
|
|
291
|
+
Pusher.EventsDispatcher.call(this);
|
|
292
|
+
|
|
293
|
+
this.state = undefined;
|
|
294
|
+
this.errors = [];
|
|
295
|
+
|
|
296
|
+
// functions for each state
|
|
297
|
+
this.stateActions = stateActions;
|
|
298
|
+
|
|
299
|
+
// set up the transitions
|
|
300
|
+
this.transitions = transitions;
|
|
301
|
+
|
|
302
|
+
this.transition(initialState);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
Machine.prototype.transition = function(nextState, data) {
|
|
306
|
+
var prevState = this.state;
|
|
307
|
+
var stateCallbacks = this.stateActions;
|
|
308
|
+
|
|
309
|
+
if (prevState && (Pusher.Util.arrayIndexOf(this.transitions[prevState], nextState) == -1)) {
|
|
310
|
+
throw new Error('Invalid transition [' + prevState + ' to ' + nextState + ']');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// exit
|
|
314
|
+
safeCall(prevState + 'Exit', stateCallbacks, data);
|
|
315
|
+
|
|
316
|
+
// tween
|
|
317
|
+
safeCall(prevState + 'To' + capitalize(nextState), stateCallbacks, data);
|
|
318
|
+
|
|
319
|
+
// pre
|
|
320
|
+
safeCall(nextState + 'Pre', stateCallbacks, data);
|
|
321
|
+
|
|
322
|
+
// change state:
|
|
323
|
+
this.state = nextState;
|
|
324
|
+
|
|
325
|
+
// handy to bind to
|
|
326
|
+
this.emit('state_change', {
|
|
327
|
+
oldState: prevState,
|
|
328
|
+
newState: nextState
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Post:
|
|
332
|
+
safeCall(nextState + 'Post', stateCallbacks, data);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
Machine.prototype.is = function(state) {
|
|
336
|
+
return this.state === state;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
Machine.prototype.isNot = function(state) {
|
|
340
|
+
return this.state !== state;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
Pusher.Util.extend(Machine.prototype, Pusher.EventsDispatcher.prototype);
|
|
344
|
+
|
|
345
|
+
this.Pusher.Machine = Machine;
|
|
346
|
+
}).call(this);
|
|
347
|
+
|
|
348
|
+
;(function() {
|
|
349
|
+
/*
|
|
350
|
+
A little bauble to interface with window.navigator.onLine,
|
|
351
|
+
window.ononline and window.onoffline. Easier to mock.
|
|
352
|
+
*/
|
|
353
|
+
var NetInfo = function() {
|
|
354
|
+
var self = this;
|
|
355
|
+
Pusher.EventsDispatcher.call(this);
|
|
356
|
+
// This is okay, as IE doesn't support this stuff anyway.
|
|
357
|
+
if (window.addEventListener !== undefined) {
|
|
358
|
+
window.addEventListener("online", function() {
|
|
359
|
+
self.emit('online', null);
|
|
360
|
+
}, false);
|
|
361
|
+
window.addEventListener("offline", function() {
|
|
362
|
+
self.emit('offline', null);
|
|
363
|
+
}, false);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// Offline means definitely offline (no connection to router).
|
|
368
|
+
// Inverse does NOT mean definitely online (only currently supported in Safari
|
|
369
|
+
// and even there only means the device has a connection to the router).
|
|
370
|
+
NetInfo.prototype.isOnLine = function() {
|
|
371
|
+
if (window.navigator.onLine === undefined) {
|
|
372
|
+
return true;
|
|
373
|
+
} else {
|
|
374
|
+
return window.navigator.onLine;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
Pusher.Util.extend(NetInfo.prototype, Pusher.EventsDispatcher.prototype);
|
|
379
|
+
|
|
380
|
+
this.Pusher.NetInfo = NetInfo;
|
|
381
|
+
}).call(this);
|
|
382
|
+
|
|
383
|
+
;(function() {
|
|
384
|
+
var Pusher = this.Pusher;
|
|
385
|
+
|
|
386
|
+
var machineTransitions = {
|
|
387
|
+
'initialized': ['waiting', 'failed'],
|
|
388
|
+
'waiting': ['connecting', 'permanentlyClosed'],
|
|
389
|
+
'connecting': ['open', 'permanentlyClosing', 'impermanentlyClosing', 'waiting'],
|
|
390
|
+
'open': ['connected', 'permanentlyClosing', 'impermanentlyClosing', 'waiting'],
|
|
391
|
+
'connected': ['permanentlyClosing', 'impermanentlyClosing', 'waiting'],
|
|
392
|
+
'impermanentlyClosing': ['waiting', 'permanentlyClosing'],
|
|
393
|
+
'permanentlyClosing': ['permanentlyClosed'],
|
|
394
|
+
'permanentlyClosed': ['waiting'],
|
|
395
|
+
'failed': ['permanentlyClosing']
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
// Amount to add to time between connection attemtpts per failed attempt.
|
|
400
|
+
var UNSUCCESSFUL_CONNECTION_ATTEMPT_ADDITIONAL_WAIT = 2000;
|
|
401
|
+
var UNSUCCESSFUL_OPEN_ATTEMPT_ADDITIONAL_TIMEOUT = 2000;
|
|
402
|
+
var UNSUCCESSFUL_CONNECTED_ATTEMPT_ADDITIONAL_TIMEOUT = 2000;
|
|
403
|
+
|
|
404
|
+
var MAX_CONNECTION_ATTEMPT_WAIT = 5 * UNSUCCESSFUL_CONNECTION_ATTEMPT_ADDITIONAL_WAIT;
|
|
405
|
+
var MAX_OPEN_ATTEMPT_TIMEOUT = 5 * UNSUCCESSFUL_OPEN_ATTEMPT_ADDITIONAL_TIMEOUT;
|
|
406
|
+
var MAX_CONNECTED_ATTEMPT_TIMEOUT = 5 * UNSUCCESSFUL_CONNECTED_ATTEMPT_ADDITIONAL_TIMEOUT;
|
|
407
|
+
|
|
408
|
+
function resetConnectionParameters(connection) {
|
|
409
|
+
connection.connectionWait = 0;
|
|
410
|
+
|
|
411
|
+
if (Pusher.TransportType === 'flash') {
|
|
412
|
+
// Flash needs a bit more time
|
|
413
|
+
connection.openTimeout = 5000;
|
|
414
|
+
} else {
|
|
415
|
+
connection.openTimeout = 2000;
|
|
416
|
+
}
|
|
417
|
+
connection.connectedTimeout = 2000;
|
|
418
|
+
connection.connectionSecure = connection.compulsorySecure;
|
|
419
|
+
connection.connectionAttempts = 0;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function Connection(key, options) {
|
|
423
|
+
var self = this;
|
|
424
|
+
|
|
425
|
+
Pusher.EventsDispatcher.call(this);
|
|
426
|
+
|
|
427
|
+
this.options = Pusher.Util.extend({encrypted: false}, options);
|
|
428
|
+
|
|
429
|
+
this.netInfo = new Pusher.NetInfo();
|
|
430
|
+
|
|
431
|
+
this.netInfo.bind('online', function(){
|
|
432
|
+
if (self._machine.is('waiting')) {
|
|
433
|
+
self._machine.transition('connecting');
|
|
434
|
+
triggerStateChange('connecting');
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
this.netInfo.bind('offline', function() {
|
|
439
|
+
if (self._machine.is('connected')) {
|
|
440
|
+
// These are for Chrome 15, which ends up
|
|
441
|
+
// having two sockets hanging around.
|
|
442
|
+
self.socket.onclose = undefined;
|
|
443
|
+
self.socket.onmessage = undefined;
|
|
444
|
+
self.socket.onerror = undefined;
|
|
445
|
+
self.socket.onopen = undefined;
|
|
446
|
+
|
|
447
|
+
self.socket.close();
|
|
448
|
+
self.socket = undefined;
|
|
449
|
+
self._machine.transition('waiting');
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// define the state machine that runs the connection
|
|
454
|
+
this._machine = new Pusher.Machine('initialized', machineTransitions, {
|
|
455
|
+
|
|
456
|
+
// TODO: Use the constructor for this.
|
|
457
|
+
initializedPre: function() {
|
|
458
|
+
self.compulsorySecure = self.options.encrypted;
|
|
459
|
+
|
|
460
|
+
self.key = key;
|
|
461
|
+
self.socket = null;
|
|
462
|
+
self.socket_id = null;
|
|
463
|
+
|
|
464
|
+
self.state = 'initialized';
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
waitingPre: function() {
|
|
468
|
+
if (self.connectionWait > 0) {
|
|
469
|
+
self.emit('connecting_in', self.connectionWait);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (self.netInfo.isOnLine() === false || self.connectionAttempts > 4){
|
|
473
|
+
triggerStateChange('unavailable');
|
|
474
|
+
} else {
|
|
475
|
+
triggerStateChange('connecting');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (self.netInfo.isOnLine() === true) {
|
|
479
|
+
self._waitingTimer = setTimeout(function() {
|
|
480
|
+
self._machine.transition('connecting');
|
|
481
|
+
}, self.connectionWait);
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
waitingExit: function() {
|
|
486
|
+
clearTimeout(self._waitingTimer);
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
connectingPre: function() {
|
|
490
|
+
// Case that a user manages to get to the connecting
|
|
491
|
+
// state even when offline.
|
|
492
|
+
if (self.netInfo.isOnLine() === false) {
|
|
493
|
+
self._machine.transition('waiting');
|
|
494
|
+
triggerStateChange('unavailable');
|
|
495
|
+
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// removed: if not closed, something is wrong that we should fix
|
|
500
|
+
// if(self.socket !== undefined) self.socket.close();
|
|
501
|
+
var url = formatURL(self.key, self.connectionSecure);
|
|
502
|
+
Pusher.debug('Connecting', url);
|
|
503
|
+
self.socket = new Pusher.Transport(url);
|
|
504
|
+
// now that the socket connection attempt has been started,
|
|
505
|
+
// set up the callbacks fired by the socket for different outcomes
|
|
506
|
+
self.socket.onopen = ws_onopen;
|
|
507
|
+
self.socket.onclose = transitionToWaiting;
|
|
508
|
+
self.socket.onerror = ws_onError;
|
|
509
|
+
|
|
510
|
+
// allow time to get ws_onOpen, otherwise close socket and try again
|
|
511
|
+
self._connectingTimer = setTimeout(TransitionToImpermanentClosing, self.openTimeout);
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
connectingExit: function() {
|
|
515
|
+
clearTimeout(self._connectingTimer);
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
connectingToWaiting: function() {
|
|
519
|
+
updateConnectionParameters();
|
|
520
|
+
|
|
521
|
+
// FUTURE: update only ssl
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
connectingToImpermanentlyClosing: function() {
|
|
525
|
+
updateConnectionParameters();
|
|
526
|
+
|
|
527
|
+
// FUTURE: update only timeout
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
openPre: function() {
|
|
531
|
+
self.socket.onmessage = ws_onMessageOpen;
|
|
532
|
+
self.socket.onerror = ws_onError;
|
|
533
|
+
self.socket.onclose = transitionToWaiting;
|
|
534
|
+
|
|
535
|
+
// allow time to get connected-to-Pusher message, otherwise close socket, try again
|
|
536
|
+
self._openTimer = setTimeout(TransitionToImpermanentClosing, self.connectedTimeout);
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
openExit: function() {
|
|
540
|
+
clearTimeout(self._openTimer);
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
openToWaiting: function() {
|
|
544
|
+
updateConnectionParameters();
|
|
545
|
+
},
|
|
546
|
+
|
|
547
|
+
openToImpermanentlyClosing: function() {
|
|
548
|
+
updateConnectionParameters();
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
connectedPre: function(socket_id) {
|
|
552
|
+
self.socket_id = socket_id;
|
|
553
|
+
|
|
554
|
+
self.socket.onmessage = ws_onMessageConnected;
|
|
555
|
+
self.socket.onerror = ws_onError;
|
|
556
|
+
self.socket.onclose = transitionToWaiting;
|
|
557
|
+
|
|
558
|
+
resetConnectionParameters(self);
|
|
559
|
+
|
|
560
|
+
resetActivityCheck();
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
connectedPost: function() {
|
|
564
|
+
triggerStateChange('connected');
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
connectedExit: function() {
|
|
568
|
+
stopActivityCheck();
|
|
569
|
+
triggerStateChange('disconnected');
|
|
570
|
+
},
|
|
571
|
+
|
|
572
|
+
impermanentlyClosingPost: function() {
|
|
573
|
+
if (self.socket) {
|
|
574
|
+
self.socket.onclose = transitionToWaiting;
|
|
575
|
+
self.socket.close();
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
|
|
579
|
+
permanentlyClosingPost: function() {
|
|
580
|
+
if (self.socket) {
|
|
581
|
+
self.socket.onclose = function() {
|
|
582
|
+
resetConnectionParameters(self);
|
|
583
|
+
self._machine.transition('permanentlyClosed');
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
self.socket.close();
|
|
587
|
+
} else {
|
|
588
|
+
resetConnectionParameters(self);
|
|
589
|
+
self._machine.transition('permanentlyClosed');
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
|
|
593
|
+
failedPre: function() {
|
|
594
|
+
triggerStateChange('failed');
|
|
595
|
+
Pusher.debug('WebSockets are not available in this browser.');
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
/*-----------------------------------------------
|
|
600
|
+
-----------------------------------------------*/
|
|
601
|
+
|
|
602
|
+
function updateConnectionParameters() {
|
|
603
|
+
if (self.connectionWait < MAX_CONNECTION_ATTEMPT_WAIT) {
|
|
604
|
+
self.connectionWait += UNSUCCESSFUL_CONNECTION_ATTEMPT_ADDITIONAL_WAIT;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (self.openTimeout < MAX_OPEN_ATTEMPT_TIMEOUT) {
|
|
608
|
+
self.openTimeout += UNSUCCESSFUL_OPEN_ATTEMPT_ADDITIONAL_TIMEOUT;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (self.connectedTimeout < MAX_CONNECTED_ATTEMPT_TIMEOUT) {
|
|
612
|
+
self.connectedTimeout += UNSUCCESSFUL_CONNECTED_ATTEMPT_ADDITIONAL_TIMEOUT;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (self.compulsorySecure !== true) {
|
|
616
|
+
self.connectionSecure = !self.connectionSecure;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
self.connectionAttempts++;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function formatURL(key, isSecure) {
|
|
623
|
+
var port = Pusher.ws_port;
|
|
624
|
+
var protocol = 'ws://';
|
|
625
|
+
|
|
626
|
+
// Always connect with SSL if the current page has
|
|
627
|
+
// been loaded via HTTPS.
|
|
628
|
+
//
|
|
629
|
+
// FUTURE: Always connect using SSL.
|
|
630
|
+
//
|
|
631
|
+
if (isSecure || document.location.protocol === 'https:') {
|
|
632
|
+
port = Pusher.wss_port;
|
|
633
|
+
protocol = 'wss://';
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return protocol + Pusher.host + ':' + port + '/app/' + key + '?client=js&version=' + Pusher.VERSION;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// callback for close and retry. Used on timeouts.
|
|
640
|
+
function TransitionToImpermanentClosing() {
|
|
641
|
+
self._machine.transition('impermanentlyClosing');
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function resetActivityCheck() {
|
|
645
|
+
if (self._activityTimer) { clearTimeout(self._activityTimer); }
|
|
646
|
+
// Send ping after inactivity
|
|
647
|
+
self._activityTimer = setTimeout(function() {
|
|
648
|
+
self.send_event('pusher:ping', {})
|
|
649
|
+
// Wait for pong response
|
|
650
|
+
self._activityTimer = setTimeout(function() {
|
|
651
|
+
self.socket.close();
|
|
652
|
+
}, (self.options.pong_timeout || Pusher.pong_timeout))
|
|
653
|
+
}, (self.options.activity_timeout || Pusher.activity_timeout))
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function stopActivityCheck() {
|
|
657
|
+
if (self._activityTimer) { clearTimeout(self._activityTimer); }
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/*-----------------------------------------------
|
|
661
|
+
WebSocket Callbacks
|
|
662
|
+
-----------------------------------------------*/
|
|
663
|
+
|
|
664
|
+
// no-op, as we only care when we get pusher:connection_established
|
|
665
|
+
function ws_onopen() {
|
|
666
|
+
self._machine.transition('open');
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
function ws_onMessageOpen(event) {
|
|
670
|
+
var params = parseWebSocketEvent(event);
|
|
671
|
+
if (params !== undefined) {
|
|
672
|
+
if (params.event === 'pusher:connection_established') {
|
|
673
|
+
self._machine.transition('connected', params.data.socket_id);
|
|
674
|
+
} else if (params.event === 'pusher:error') {
|
|
675
|
+
// first inform the end-developer of this error
|
|
676
|
+
self.emit('error', {type: 'PusherError', data: params.data});
|
|
677
|
+
|
|
678
|
+
switch (params.data.code) {
|
|
679
|
+
case 4000:
|
|
680
|
+
Pusher.warn(params.data.message);
|
|
681
|
+
|
|
682
|
+
self.compulsorySecure = true;
|
|
683
|
+
self.connectionSecure = true;
|
|
684
|
+
self.options.encrypted = true;
|
|
685
|
+
break;
|
|
686
|
+
case 4001:
|
|
687
|
+
// App not found by key - close connection
|
|
688
|
+
self._machine.transition('permanentlyClosing');
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function ws_onMessageConnected(event) {
|
|
696
|
+
resetActivityCheck();
|
|
697
|
+
|
|
698
|
+
var params = parseWebSocketEvent(event);
|
|
699
|
+
if (params !== undefined) {
|
|
700
|
+
Pusher.debug('Event recd', params);
|
|
701
|
+
|
|
702
|
+
switch (params.event) {
|
|
703
|
+
case 'pusher:error':
|
|
704
|
+
self.emit('error', {type: 'PusherError', data: params.data});
|
|
705
|
+
break;
|
|
706
|
+
case 'pusher:ping':
|
|
707
|
+
self.send_event('pusher:pong', {})
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
self.emit('message', params);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Parses an event from the WebSocket to get
|
|
718
|
+
* the JSON payload that we require
|
|
719
|
+
*
|
|
720
|
+
* @param {MessageEvent} event The event from the WebSocket.onmessage handler.
|
|
721
|
+
**/
|
|
722
|
+
function parseWebSocketEvent(event) {
|
|
723
|
+
try {
|
|
724
|
+
var params = JSON.parse(event.data);
|
|
725
|
+
|
|
726
|
+
if (typeof params.data === 'string') {
|
|
727
|
+
try {
|
|
728
|
+
params.data = JSON.parse(params.data);
|
|
729
|
+
} catch (e) {
|
|
730
|
+
if (!(e instanceof SyntaxError)) {
|
|
731
|
+
throw e;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return params;
|
|
737
|
+
} catch (e) {
|
|
738
|
+
self.emit('error', {type: 'MessageParseError', error: e, data: event.data});
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function transitionToWaiting() {
|
|
743
|
+
self._machine.transition('waiting');
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function ws_onError() {
|
|
747
|
+
self.emit('error', {
|
|
748
|
+
type: 'WebSocketError'
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
// note: required? is the socket auto closed in the case of error?
|
|
752
|
+
self.socket.close();
|
|
753
|
+
self._machine.transition('impermanentlyClosing');
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function triggerStateChange(newState, data) {
|
|
757
|
+
// avoid emitting and changing the state
|
|
758
|
+
// multiple times when it's the same.
|
|
759
|
+
if (self.state === newState) return;
|
|
760
|
+
|
|
761
|
+
var prevState = self.state;
|
|
762
|
+
|
|
763
|
+
self.state = newState;
|
|
764
|
+
|
|
765
|
+
Pusher.debug('State changed', prevState + ' -> ' + newState);
|
|
766
|
+
|
|
767
|
+
self.emit('state_change', {previous: prevState, current: newState});
|
|
768
|
+
self.emit(newState, data);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
Connection.prototype.connect = function() {
|
|
773
|
+
// no WebSockets
|
|
774
|
+
if (Pusher.Transport === null || Pusher.Transport === undefined) {
|
|
775
|
+
this._machine.transition('failed');
|
|
776
|
+
}
|
|
777
|
+
// initial open of connection
|
|
778
|
+
else if(this._machine.is('initialized')) {
|
|
779
|
+
resetConnectionParameters(this);
|
|
780
|
+
this._machine.transition('waiting');
|
|
781
|
+
}
|
|
782
|
+
// user skipping connection wait
|
|
783
|
+
else if (this._machine.is('waiting') && this.netInfo.isOnLine() === true) {
|
|
784
|
+
this._machine.transition('connecting');
|
|
785
|
+
}
|
|
786
|
+
// user re-opening connection after closing it
|
|
787
|
+
else if(this._machine.is("permanentlyClosed")) {
|
|
788
|
+
this._machine.transition('waiting');
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
Connection.prototype.send = function(data) {
|
|
793
|
+
if (this._machine.is('connected')) {
|
|
794
|
+
this.socket.send(data);
|
|
795
|
+
return true;
|
|
796
|
+
} else {
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
Connection.prototype.send_event = function(event_name, data, channel) {
|
|
802
|
+
var payload = {
|
|
803
|
+
event: event_name,
|
|
804
|
+
data: data
|
|
805
|
+
};
|
|
806
|
+
if (channel) payload['channel'] = channel;
|
|
807
|
+
|
|
808
|
+
Pusher.debug('Event sent', payload);
|
|
809
|
+
return this.send(JSON.stringify(payload));
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
Connection.prototype.disconnect = function() {
|
|
813
|
+
if (this._machine.is('permanentlyClosed')) return;
|
|
814
|
+
|
|
815
|
+
if (this._machine.is('waiting')) {
|
|
816
|
+
this._machine.transition('permanentlyClosed');
|
|
817
|
+
} else {
|
|
818
|
+
this._machine.transition('permanentlyClosing');
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
Pusher.Util.extend(Connection.prototype, Pusher.EventsDispatcher.prototype);
|
|
823
|
+
this.Pusher.Connection = Connection;
|
|
824
|
+
}).call(this);
|
|
825
|
+
|
|
826
|
+
Pusher.Channels = function() {
|
|
827
|
+
this.channels = {};
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
Pusher.Channels.prototype = {
|
|
831
|
+
add: function(channel_name, pusher) {
|
|
832
|
+
var existing_channel = this.find(channel_name);
|
|
833
|
+
if (!existing_channel) {
|
|
834
|
+
var channel = Pusher.Channel.factory(channel_name, pusher);
|
|
835
|
+
this.channels[channel_name] = channel;
|
|
836
|
+
return channel;
|
|
837
|
+
} else {
|
|
838
|
+
return existing_channel;
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
find: function(channel_name) {
|
|
843
|
+
return this.channels[channel_name];
|
|
844
|
+
},
|
|
845
|
+
|
|
846
|
+
remove: function(channel_name) {
|
|
847
|
+
delete this.channels[channel_name];
|
|
848
|
+
},
|
|
849
|
+
|
|
850
|
+
disconnect: function () {
|
|
851
|
+
for(var channel_name in this.channels){
|
|
852
|
+
this.channels[channel_name].disconnect()
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
Pusher.Channel = function(channel_name, pusher) {
|
|
858
|
+
var self = this;
|
|
859
|
+
Pusher.EventsDispatcher.call(this, function(event_name, event_data) {
|
|
860
|
+
Pusher.debug('No callbacks on ' + channel_name + ' for ' + event_name);
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
this.pusher = pusher;
|
|
864
|
+
this.name = channel_name;
|
|
865
|
+
this.subscribed = false;
|
|
866
|
+
|
|
867
|
+
this.bind('pusher_internal:subscription_succeeded', function(data) {
|
|
868
|
+
self.onSubscriptionSucceeded(data);
|
|
869
|
+
});
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
Pusher.Channel.prototype = {
|
|
873
|
+
// inheritable constructor
|
|
874
|
+
init: function() {},
|
|
875
|
+
disconnect: function() {},
|
|
876
|
+
|
|
877
|
+
onSubscriptionSucceeded: function(data) {
|
|
878
|
+
this.subscribed = true;
|
|
879
|
+
this.emit('pusher:subscription_succeeded');
|
|
880
|
+
},
|
|
881
|
+
|
|
882
|
+
authorize: function(pusher, callback){
|
|
883
|
+
callback(false, {}); // normal channels don't require auth
|
|
884
|
+
},
|
|
885
|
+
|
|
886
|
+
trigger: function(event, data) {
|
|
887
|
+
return this.pusher.send_event(event, data, this.name);
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
Pusher.Util.extend(Pusher.Channel.prototype, Pusher.EventsDispatcher.prototype);
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
Pusher.auth_callbacks = {};
|
|
896
|
+
|
|
897
|
+
Pusher.authorizers = {
|
|
898
|
+
ajax: function(pusher, callback){
|
|
899
|
+
var self = this, xhr;
|
|
900
|
+
|
|
901
|
+
if (Pusher.XHR) {
|
|
902
|
+
xhr = new Pusher.XHR();
|
|
903
|
+
} else {
|
|
904
|
+
xhr = (window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
xhr.open("POST", Pusher.channel_auth_endpoint, true);
|
|
908
|
+
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
|
|
909
|
+
xhr.onreadystatechange = function() {
|
|
910
|
+
if (xhr.readyState == 4) {
|
|
911
|
+
if (xhr.status == 200) {
|
|
912
|
+
var data, parsed = false;
|
|
913
|
+
|
|
914
|
+
try {
|
|
915
|
+
data = JSON.parse(xhr.responseText);
|
|
916
|
+
parsed = true;
|
|
917
|
+
} catch (e) {
|
|
918
|
+
callback(true, 'JSON returned from webapp was invalid, yet status code was 200. Data was: ' + xhr.responseText);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (parsed) { // prevents double execution.
|
|
922
|
+
callback(false, data);
|
|
923
|
+
}
|
|
924
|
+
} else {
|
|
925
|
+
Pusher.warn("Couldn't get auth info from your webapp", status);
|
|
926
|
+
callback(true, xhr.status);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
xhr.send('socket_id=' + encodeURIComponent(pusher.connection.socket_id) + '&channel_name=' + encodeURIComponent(self.name));
|
|
931
|
+
},
|
|
932
|
+
jsonp: function(pusher, callback){
|
|
933
|
+
var qstring = 'socket_id=' + encodeURIComponent(pusher.connection.socket_id) + '&channel_name=' + encodeURIComponent(this.name);
|
|
934
|
+
var script = document.createElement("script");
|
|
935
|
+
// Hacked wrapper.
|
|
936
|
+
Pusher.auth_callbacks[this.name] = function(data) {
|
|
937
|
+
callback(false, data);
|
|
938
|
+
};
|
|
939
|
+
var callback_name = "Pusher.auth_callbacks['" + this.name + "']";
|
|
940
|
+
script.src = Pusher.channel_auth_endpoint+'?callback='+encodeURIComponent(callback_name)+'&'+qstring;
|
|
941
|
+
var head = document.getElementsByTagName("head")[0] || document.documentElement;
|
|
942
|
+
head.insertBefore( script, head.firstChild );
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
Pusher.Channel.PrivateChannel = {
|
|
947
|
+
authorize: function(pusher, callback){
|
|
948
|
+
Pusher.authorizers[Pusher.channel_auth_transport].scopedTo(this)(pusher, callback);
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
Pusher.Channel.PresenceChannel = {
|
|
953
|
+
init: function(){
|
|
954
|
+
this.bind('pusher_internal:member_added', function(data){
|
|
955
|
+
var member = this.members.add(data.user_id, data.user_info);
|
|
956
|
+
this.emit('pusher:member_added', member);
|
|
957
|
+
}.scopedTo(this))
|
|
958
|
+
|
|
959
|
+
this.bind('pusher_internal:member_removed', function(data){
|
|
960
|
+
var member = this.members.remove(data.user_id);
|
|
961
|
+
if (member) {
|
|
962
|
+
this.emit('pusher:member_removed', member);
|
|
963
|
+
}
|
|
964
|
+
}.scopedTo(this))
|
|
965
|
+
},
|
|
966
|
+
|
|
967
|
+
disconnect: function(){
|
|
968
|
+
this.members.clear();
|
|
969
|
+
},
|
|
970
|
+
|
|
971
|
+
onSubscriptionSucceeded: function(data) {
|
|
972
|
+
this.members._members_map = data.presence.hash;
|
|
973
|
+
this.members.count = data.presence.count;
|
|
974
|
+
this.subscribed = true;
|
|
975
|
+
|
|
976
|
+
this.emit('pusher:subscription_succeeded', this.members);
|
|
977
|
+
},
|
|
978
|
+
|
|
979
|
+
members: {
|
|
980
|
+
_members_map: {},
|
|
981
|
+
count: 0,
|
|
982
|
+
|
|
983
|
+
each: function(callback) {
|
|
984
|
+
for(var i in this._members_map) {
|
|
985
|
+
callback({
|
|
986
|
+
id: i,
|
|
987
|
+
info: this._members_map[i]
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
|
|
992
|
+
add: function(id, info) {
|
|
993
|
+
this._members_map[id] = info;
|
|
994
|
+
this.count++;
|
|
995
|
+
return this.get(id);
|
|
996
|
+
},
|
|
997
|
+
|
|
998
|
+
remove: function(user_id) {
|
|
999
|
+
var member = this.get(user_id);
|
|
1000
|
+
if (member) {
|
|
1001
|
+
delete this._members_map[user_id];
|
|
1002
|
+
this.count--;
|
|
1003
|
+
}
|
|
1004
|
+
return member;
|
|
1005
|
+
},
|
|
1006
|
+
|
|
1007
|
+
get: function(user_id) {
|
|
1008
|
+
if (this._members_map.hasOwnProperty(user_id)) { // have heard of this user user_id
|
|
1009
|
+
return {
|
|
1010
|
+
id: user_id,
|
|
1011
|
+
info: this._members_map[user_id]
|
|
1012
|
+
}
|
|
1013
|
+
} else { // have never heard of this user
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
clear: function() {
|
|
1019
|
+
this._members_map = {};
|
|
1020
|
+
this.count = 0;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
Pusher.Channel.factory = function(channel_name, pusher){
|
|
1026
|
+
var channel = new Pusher.Channel(channel_name, pusher);
|
|
1027
|
+
if (channel_name.indexOf('private-') === 0) {
|
|
1028
|
+
Pusher.Util.extend(channel, Pusher.Channel.PrivateChannel);
|
|
1029
|
+
} else if (channel_name.indexOf('presence-') === 0) {
|
|
1030
|
+
Pusher.Util.extend(channel, Pusher.Channel.PrivateChannel);
|
|
1031
|
+
Pusher.Util.extend(channel, Pusher.Channel.PresenceChannel);
|
|
1032
|
+
};
|
|
1033
|
+
channel.init();
|
|
1034
|
+
return channel;
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
var _require = (function () {
|
|
1038
|
+
|
|
1039
|
+
var handleScriptLoaded;
|
|
1040
|
+
if (document.addEventListener) {
|
|
1041
|
+
handleScriptLoaded = function (elem, callback) {
|
|
1042
|
+
elem.addEventListener('load', callback, false)
|
|
1043
|
+
}
|
|
1044
|
+
} else {
|
|
1045
|
+
handleScriptLoaded = function(elem, callback) {
|
|
1046
|
+
elem.attachEvent('onreadystatechange', function () {
|
|
1047
|
+
if(elem.readyState == 'loaded' || elem.readyState == 'complete') callback()
|
|
1048
|
+
})
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
return function (deps, callback) {
|
|
1053
|
+
var dep_count = 0,
|
|
1054
|
+
dep_length = deps.length;
|
|
1055
|
+
|
|
1056
|
+
function checkReady (callback) {
|
|
1057
|
+
dep_count++;
|
|
1058
|
+
if ( dep_length == dep_count ) {
|
|
1059
|
+
// Opera needs the timeout for page initialization weirdness
|
|
1060
|
+
setTimeout(callback, 0);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function addScript (src, callback) {
|
|
1065
|
+
callback = callback || function(){}
|
|
1066
|
+
var head = document.getElementsByTagName('head')[0];
|
|
1067
|
+
var script = document.createElement('script');
|
|
1068
|
+
script.setAttribute('src', src);
|
|
1069
|
+
script.setAttribute("type","text/javascript");
|
|
1070
|
+
script.setAttribute('async', true);
|
|
1071
|
+
|
|
1072
|
+
handleScriptLoaded(script, function () {
|
|
1073
|
+
checkReady(callback);
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
head.appendChild(script);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
for(var i = 0; i < dep_length; i++) {
|
|
1080
|
+
addScript(deps[i], callback);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
})();
|
|
1084
|
+
|
|
1085
|
+
;(function() {
|
|
1086
|
+
var cdn = (document.location.protocol == 'http:') ? Pusher.cdn_http : Pusher.cdn_https;
|
|
1087
|
+
var root = cdn + Pusher.VERSION;
|
|
1088
|
+
var deps = [];
|
|
1089
|
+
|
|
1090
|
+
if (window['JSON'] === undefined) {
|
|
1091
|
+
deps.push(root + '/json2' + Pusher.dependency_suffix + '.js');
|
|
1092
|
+
}
|
|
1093
|
+
if (window['WebSocket'] === undefined && window['MozWebSocket'] === undefined) {
|
|
1094
|
+
// We manually initialize web-socket-js to iron out cross browser issues
|
|
1095
|
+
window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
|
|
1096
|
+
deps.push(root + '/flashfallback' + Pusher.dependency_suffix + '.js');
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
var initialize = function() {
|
|
1100
|
+
if (window['WebSocket'] === undefined && window['MozWebSocket'] === undefined) {
|
|
1101
|
+
return function() {
|
|
1102
|
+
// This runs after flashfallback.js has loaded
|
|
1103
|
+
if (window['WebSocket'] !== undefined && window['MozWebSocket'] === undefined) {
|
|
1104
|
+
// window['WebSocket'] is a flash emulation of WebSocket
|
|
1105
|
+
Pusher.Transport = window['WebSocket'];
|
|
1106
|
+
Pusher.TransportType = 'flash';
|
|
1107
|
+
|
|
1108
|
+
window.WEB_SOCKET_SWF_LOCATION = root + "/WebSocketMain.swf";
|
|
1109
|
+
WebSocket.__addTask(function() {
|
|
1110
|
+
Pusher.ready();
|
|
1111
|
+
})
|
|
1112
|
+
WebSocket.__initialize();
|
|
1113
|
+
} else {
|
|
1114
|
+
// Flash must not be installed
|
|
1115
|
+
Pusher.Transport = null;
|
|
1116
|
+
Pusher.TransportType = 'none';
|
|
1117
|
+
Pusher.ready();
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
} else {
|
|
1121
|
+
return function() {
|
|
1122
|
+
// This is because Mozilla have decided to
|
|
1123
|
+
// prefix the WebSocket constructor with "Moz".
|
|
1124
|
+
if (window['MozWebSocket'] !== undefined) {
|
|
1125
|
+
Pusher.Transport = window['MozWebSocket'];
|
|
1126
|
+
} else {
|
|
1127
|
+
Pusher.Transport = window['WebSocket'];
|
|
1128
|
+
}
|
|
1129
|
+
// We have some form of a native websocket,
|
|
1130
|
+
// even if the constructor is prefixed:
|
|
1131
|
+
Pusher.TransportType = 'native';
|
|
1132
|
+
|
|
1133
|
+
// Initialise Pusher.
|
|
1134
|
+
Pusher.ready();
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}();
|
|
1138
|
+
|
|
1139
|
+
var ondocumentbody = function(callback) {
|
|
1140
|
+
var load_body = function() {
|
|
1141
|
+
document.body ? callback() : setTimeout(load_body, 0);
|
|
1142
|
+
}
|
|
1143
|
+
load_body();
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
var initializeOnDocumentBody = function() {
|
|
1147
|
+
ondocumentbody(initialize);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (deps.length > 0) {
|
|
1151
|
+
_require(deps, initializeOnDocumentBody);
|
|
1152
|
+
} else {
|
|
1153
|
+
initializeOnDocumentBody();
|
|
1154
|
+
}
|
|
1155
|
+
})();
|