@@ -1,5 +1,5 @@ |
||
1 | 1 |
|
2 |
-$light: #F3F3F3 !default; |
|
2 |
+$light: #FFFFFF !default; |
|
3 | 3 |
$stable: #F3F3F3 !default; |
4 | 4 |
$positive: #55A2DA !default; |
5 | 5 |
$calm: #AFB4BF !default; |
@@ -163,3 +163,6 @@ |
||
163 | 163 |
.task-checkbox .task-retrys.flaged { |
164 | 164 |
top: -28px; |
165 | 165 |
} |
166 |
+ |
|
167 |
+.pull-right { float: right; } |
|
168 |
+.pull-left {float: left; } |
@@ -19,11 +19,14 @@ |
||
19 | 19 |
<script src="cordova.js"></script> |
20 | 20 |
|
21 | 21 |
<!-- your app's js --> |
22 |
+ <script src="lib/faye-browser.js"></script> |
|
22 | 23 |
<script src="js/app.js"></script> |
24 |
+ <script src="js/factories/ionic-utils.js"></script> |
|
23 | 25 |
<script src="js/controllers.js"></script> |
24 | 26 |
<script src="js/controllers/login-ctrl.js"></script> |
25 | 27 |
<script src="js/controllers/mission-ctrl.js"></script> |
26 | 28 |
<script src="js/controllers/briefing-ctrl.js"></script> |
29 |
+ <script src="js/controllers/activity-ctrl.js"></script> |
|
27 | 30 |
<script src="js/services/data-service.js"></script> |
28 | 31 |
<script src="js/services/oauth-service.js"></script> |
29 | 32 |
<script src="js/services/realtime-service.js"></script> |
@@ -12,11 +12,13 @@ window.ionic.Platform.ready(function() { |
||
12 | 12 |
angular.module('avalanche3mobile', [ |
13 | 13 |
'ionic', |
14 | 14 |
'ngCordova', |
15 |
+ 'ionic.utils', |
|
15 | 16 |
'avalanche3mobile.controllers', |
16 | 17 |
'avalanche3mobile.missionData', |
17 | 18 |
'avalanche3mobile.LoginCtrl', |
18 | 19 |
'avalanche3mobile.MissionCtrl', |
19 | 20 |
'avalanche3mobile.BriefingCtrl', |
21 |
+ 'avalanche3mobile.ActivityCtrl', |
|
20 | 22 |
'avalancheServices.Oauth', |
21 | 23 |
'avalancheServices.Data', |
22 | 24 |
'avalancheServices.Realtime' |
@@ -82,18 +82,6 @@ angular.module('avalanche3mobile.controllers', []) |
||
82 | 82 |
]; |
83 | 83 |
}) |
84 | 84 |
|
85 |
-.controller('MissionActivityCtrl', function($scope, $stateParams, mission) { |
|
86 |
- $scope.mission = mission; |
|
87 |
- console.log(mission) |
|
88 |
- $scope.activityIconClass = function(activity){ |
|
89 |
- if(activity.trackable_type == "Message" || activity.trackable_type == "Task") { |
|
90 |
- return "item-avatar"; |
|
91 |
- } else { |
|
92 |
- return "item-icon-left"; |
|
93 |
- } |
|
94 |
- } |
|
95 |
-}) |
|
96 |
- |
|
97 | 85 |
.controller('MissionTasksCtrl', function($scope, $stateParams, mission) { |
98 | 86 |
$scope.mission = mission; |
99 | 87 |
console.log(mission) |
@@ -0,0 +1,49 @@ |
||
1 |
+angular.module('avalanche3mobile.ActivityCtrl', []) |
|
2 |
+ .controller('MissionActivityCtrl', function($scope, $rootScope, $stateParams, OAuthService, DataService, RealtimeService, $state) { |
|
3 |
+ |
|
4 |
+ // Refresh Activities from server |
|
5 |
+ |
|
6 |
+ DataService.get('/missions/' + $state.params.missionId + "/activities", "", OAuthService.getToken()) |
|
7 |
+ |
|
8 |
+ $rootScope.$on('$stateChangeSuccess', |
|
9 |
+ function(event, toState, toParams, fromState, fromParams){ |
|
10 |
+ DataService.get('/missions/' + $state.params.missionId + "/activities", "", OAuthService.getToken()) |
|
11 |
+ }); |
|
12 |
+ |
|
13 |
+ $rootScope.$on('get-data-success:/missions/' + $state.params.missionId + '/activities', function() { |
|
14 |
+ if(!$scope.$$phase) { |
|
15 |
+ $scope.$apply(function(){ |
|
16 |
+ $scope.mission.activities = DataService.getResponse().data; |
|
17 |
+ }); |
|
18 |
+ } else { |
|
19 |
+ $scope.mission.activities = DataService.getResponse().data; |
|
20 |
+ } |
|
21 |
+ }); |
|
22 |
+ |
|
23 |
+ // Start Realtime subscription to new activities |
|
24 |
+ |
|
25 |
+ RealtimeService.subscribe('/missions/' + $state.params.missionId, function(activity) { |
|
26 |
+ $scope.$apply(function() { |
|
27 |
+ $scope.mission.activities.push(activity); |
|
28 |
+ console.log(activity); |
|
29 |
+ }); |
|
30 |
+ }); |
|
31 |
+ |
|
32 |
+ // Send message |
|
33 |
+ |
|
34 |
+ $scope.message = ""; |
|
35 |
+ $scope.sendMessage = function() { |
|
36 |
+ DataService.post('/missions/' + $state.params.missionId + "/messages", {content : $scope.message }, OAuthService.getToken()) |
|
37 |
+ $scope.message = ""; |
|
38 |
+ } |
|
39 |
+ |
|
40 |
+ // Misc |
|
41 |
+ |
|
42 |
+ $scope.activityIconClass = function(activity){ |
|
43 |
+ if(activity.trackable_type == "Message" || activity.trackable_type == "Task") { |
|
44 |
+ return "item-avatar"; |
|
45 |
+ } else { |
|
46 |
+ return "item-icon-left"; |
|
47 |
+ } |
|
48 |
+ } |
|
49 |
+ }) |
@@ -1,6 +1,30 @@ |
||
1 | 1 |
angular.module('avalanche3mobile.BriefingCtrl', []) |
2 | 2 |
|
3 |
- .controller('MissionBriefingCtrl', function($scope, $stateParams, mission, OAuthService, $state) { |
|
4 |
- $scope.mission = OAuthService.getMission($state.params.missionId); |
|
3 |
+ .controller('MissionBriefingCtrl', function($scope, $stateParams, mission, OAuthService, $state, DataService) { |
|
4 |
+ |
|
5 |
+ $scope.mission = DataService.getMission($state.params.missionId); |
|
5 | 6 |
console.log(mission) |
7 |
+ |
|
8 |
+ $scope.status = function(status_code) { |
|
9 |
+ switch (status_code) { |
|
10 |
+ case 1: |
|
11 |
+ return "Planning"; |
|
12 |
+ break; |
|
13 |
+ case 2: |
|
14 |
+ return "Launched"; |
|
15 |
+ break; |
|
16 |
+ case 3: |
|
17 |
+ return "Completed"; |
|
18 |
+ break; |
|
19 |
+ case 4: |
|
20 |
+ return "Failed"; |
|
21 |
+ break; |
|
22 |
+ case 5: |
|
23 |
+ return "Canceled"; |
|
24 |
+ break; |
|
25 |
+ default: |
|
26 |
+ return "Error" |
|
27 |
+ break; |
|
28 |
+ } |
|
29 |
+ } |
|
6 | 30 |
}) |
@@ -1,6 +1,6 @@ |
||
1 | 1 |
angular.module('avalanche3mobile.MissionCtrl', []) |
2 | 2 |
|
3 |
-.controller('MissionCtrl', function($scope, $rootScope, $location, $ionicModal, $timeout, missionData, $ionicSideMenuDelegate, $cordovaStatusbar, $state, DataService, OAuthService) { |
|
3 |
+.controller('MissionCtrl', function($scope, $rootScope, $location, $ionicModal, $timeout, missionData, $ionicSideMenuDelegate, $cordovaStatusbar, $state, DataService, OAuthService, $localstorage) { |
|
4 | 4 |
|
5 | 5 |
|
6 | 6 |
|
@@ -11,24 +11,25 @@ angular.module('avalanche3mobile.MissionCtrl', []) |
||
11 | 11 |
//$scope.$on('$ionicView.enter', function(e) { |
12 | 12 |
//}); |
13 | 13 |
|
14 |
- $scope.currentMissions = OAuthService.getMissions(); |
|
15 |
- $scope.mission = OAuthService.getMission($state.params.missionId); |
|
14 |
+ $scope.currentMissions = $localstorage.getObject('missions'); |
|
15 |
+ console.log("> Current Mission: " + $state.params.missionId) |
|
16 |
+ $scope.mission = DataService.getMission($state.params.missionId); |
|
16 | 17 |
|
17 | 18 |
$rootScope.$on('$stateChangeSuccess', |
18 | 19 |
function(event, toState, toParams, fromState, fromParams){ |
19 |
- $scope.mission = OAuthService.getMission($state.params.missionId); |
|
20 |
+ $scope.mission = DataService.getMission($state.params.missionId); |
|
20 | 21 |
}); |
21 | 22 |
|
22 | 23 |
$scope.isActive = function (viewLocation) { |
23 | 24 |
//console.log(viewLocation + " | " + $location.path()) |
24 |
- console.log($state.params.missionId) |
|
25 |
+ |
|
25 | 26 |
if($state.params.missionId == viewLocation) { return true; } |
26 | 27 |
else { return false; } |
27 | 28 |
}; |
28 | 29 |
|
29 | 30 |
|
30 | 31 |
$scope.toggleLeft = function() { |
31 |
- console.log("Opening left menu") |
|
32 |
+ //console.log("Opening left menu") |
|
32 | 33 |
$ionicSideMenuDelegate.toggleLeft(); |
33 | 34 |
// if($cordovaStatusbar.isVisible()){ |
34 | 35 |
// //$cordovaStatusbar.hide(); |
@@ -0,0 +1,18 @@ |
||
1 |
+angular.module('ionic.utils', []) |
|
2 |
+ |
|
3 |
+.factory('$localstorage', ['$window', function($window) { |
|
4 |
+ return { |
|
5 |
+ set: function(key, value) { |
|
6 |
+ $window.localStorage[key] = value; |
|
7 |
+ }, |
|
8 |
+ get: function(key, defaultValue) { |
|
9 |
+ return $window.localStorage[key] || defaultValue; |
|
10 |
+ }, |
|
11 |
+ setObject: function(key, value) { |
|
12 |
+ $window.localStorage[key] = JSON.stringify(value); |
|
13 |
+ }, |
|
14 |
+ getObject: function(key) { |
|
15 |
+ return JSON.parse($window.localStorage[key] || '{}'); |
|
16 |
+ } |
|
17 |
+ } |
|
18 |
+}]); |
@@ -1,6 +1,7 @@ |
||
1 | 1 |
angular.module('avalancheServices.Data', []) |
2 |
-.service('DataService', [ '$rootScope', '$http', function($rootScope, $http) { |
|
2 |
+.service('DataService', [ '$rootScope', '$http', '$localstorage', function($rootScope, $http, $localstorage) { |
|
3 | 3 |
var response = {}; |
4 |
+ var domain = "http://localhost:5000/api" |
|
4 | 5 |
|
5 | 6 |
// GET |
6 | 7 |
this.get = function(url, inputs, token){ |
@@ -9,7 +10,7 @@ angular.module('avalancheServices.Data', []) |
||
9 | 10 |
console.log(token); |
10 | 11 |
$http({ |
11 | 12 |
method: 'GET', |
12 |
- url: url, |
|
13 |
+ url: domain + url, |
|
13 | 14 |
headers: { 'Authorization' : "Bearer " + token }, |
14 | 15 |
params: inputs |
15 | 16 |
}).then(function(data, status, headers, config) { |
@@ -19,7 +20,7 @@ angular.module('avalancheServices.Data', []) |
||
19 | 20 |
response.headers = data.headers; |
20 | 21 |
response.config = data.config; |
21 | 22 |
console.log(response) |
22 |
- $rootScope.$broadcast('get-data:finished'); |
|
23 |
+ $rootScope.$broadcast('get-data-success:' + url); |
|
23 | 24 |
}, |
24 | 25 |
function(data, status) { |
25 | 26 |
console.log("GET Request FAILED") |
@@ -28,7 +29,7 @@ angular.module('avalancheServices.Data', []) |
||
28 | 29 |
response.headers = data.headers; |
29 | 30 |
response.config = data.config; |
30 | 31 |
console.log(response) |
31 |
- $rootScope.$broadcast('get-data:finished'); |
|
32 |
+ $rootScope.$broadcast('get-data-failure:' + url); |
|
32 | 33 |
}); |
33 | 34 |
} |
34 | 35 |
|
@@ -39,7 +40,7 @@ angular.module('avalancheServices.Data', []) |
||
39 | 40 |
console.log(token); |
40 | 41 |
$http({ |
41 | 42 |
method: 'POST', |
42 |
- url: url, |
|
43 |
+ url: domain + url, |
|
43 | 44 |
headers: { 'Authorization' : "Bearer " + token }, |
44 | 45 |
params: inputs |
45 | 46 |
}).then(function(data, status, headers, config) { |
@@ -67,4 +68,14 @@ angular.module('avalancheServices.Data', []) |
||
67 | 68 |
return response; |
68 | 69 |
} |
69 | 70 |
|
71 |
+ this.getMission = function(slug) { |
|
72 |
+ var missions = $localstorage.getObject('missions'); |
|
73 |
+ for (var i = 0; i < missions.length; i++) { |
|
74 |
+ if(missions[i].slug == slug) { |
|
75 |
+ return missions[i]; |
|
76 |
+ } |
|
77 |
+ } |
|
78 |
+ return false; |
|
79 |
+ } |
|
80 |
+ |
|
70 | 81 |
}]) |
@@ -1,8 +1,9 @@ |
||
1 | 1 |
angular.module('avalancheServices.Oauth', []) |
2 |
-.service('OAuthService', [ '$rootScope', '$http', '$location', function($rootScope, $http, $location) { |
|
2 |
+.service('OAuthService', [ '$rootScope', '$http', '$location', '$localstorage', function($rootScope, $http, $location, $localstorage) { |
|
3 | 3 |
var code = {}; |
4 |
+ //var token = ""; |
|
4 | 5 |
//var token = $cookies.get('avalanche_docs_token'); |
5 |
- var token = ""; |
|
6 |
+ var token = $localstorage.get('token'); |
|
6 | 7 |
var user = {}; |
7 | 8 |
var missions = []; |
8 | 9 |
|
@@ -38,6 +39,9 @@ angular.module('avalancheServices.Oauth', []) |
||
38 | 39 |
user = data.data.user; |
39 | 40 |
missions = data.data.missions; |
40 | 41 |
//$cookies.put('avalanche_docs_token', token); |
42 |
+ $localstorage.set('token', token); |
|
43 |
+ $localstorage.setObject('user', data.data.user); |
|
44 |
+ $localstorage.setObject('missions', data.data.missions); |
|
41 | 45 |
console.log("Resource Owner Password Credentials flow SUCCESSFULL") |
42 | 46 |
console.log(data.data); |
43 | 47 |
$rootScope.$broadcast('auth:success'); |
@@ -0,0 +1,2765 @@ |
||
1 |
+(function() { |
|
2 |
+'use strict'; |
|
3 |
+ |
|
4 |
+var Faye = { |
|
5 |
+ VERSION: '1.1.1', |
|
6 |
+ |
|
7 |
+ BAYEUX_VERSION: '1.0', |
|
8 |
+ ID_LENGTH: 160, |
|
9 |
+ JSONP_CALLBACK: 'jsonpcallback', |
|
10 |
+ CONNECTION_TYPES: ['long-polling', 'cross-origin-long-polling', 'callback-polling', 'websocket', 'eventsource', 'in-process'], |
|
11 |
+ |
|
12 |
+ MANDATORY_CONNECTION_TYPES: ['long-polling', 'callback-polling', 'in-process'], |
|
13 |
+ |
|
14 |
+ ENV: (typeof window !== 'undefined') ? window : global, |
|
15 |
+ |
|
16 |
+ extend: function(dest, source, overwrite) { |
|
17 |
+ if (!source) return dest; |
|
18 |
+ for (var key in source) { |
|
19 |
+ if (!source.hasOwnProperty(key)) continue; |
|
20 |
+ if (dest.hasOwnProperty(key) && overwrite === false) continue; |
|
21 |
+ if (dest[key] !== source[key]) |
|
22 |
+ dest[key] = source[key]; |
|
23 |
+ } |
|
24 |
+ return dest; |
|
25 |
+ }, |
|
26 |
+ |
|
27 |
+ random: function(bitlength) { |
|
28 |
+ bitlength = bitlength || this.ID_LENGTH; |
|
29 |
+ var maxLength = Math.ceil(bitlength * Math.log(2) / Math.log(36)); |
|
30 |
+ var string = csprng(bitlength, 36); |
|
31 |
+ while (string.length < maxLength) string = '0' + string; |
|
32 |
+ return string; |
|
33 |
+ }, |
|
34 |
+ |
|
35 |
+ validateOptions: function(options, validKeys) { |
|
36 |
+ for (var key in options) { |
|
37 |
+ if (this.indexOf(validKeys, key) < 0) |
|
38 |
+ throw new Error('Unrecognized option: ' + key); |
|
39 |
+ } |
|
40 |
+ }, |
|
41 |
+ |
|
42 |
+ clientIdFromMessages: function(messages) { |
|
43 |
+ var connect = this.filter([].concat(messages), function(message) { |
|
44 |
+ return message.channel === '/meta/connect'; |
|
45 |
+ }); |
|
46 |
+ return connect[0] && connect[0].clientId; |
|
47 |
+ }, |
|
48 |
+ |
|
49 |
+ copyObject: function(object) { |
|
50 |
+ var clone, i, key; |
|
51 |
+ if (object instanceof Array) { |
|
52 |
+ clone = []; |
|
53 |
+ i = object.length; |
|
54 |
+ while (i--) clone[i] = Faye.copyObject(object[i]); |
|
55 |
+ return clone; |
|
56 |
+ } else if (typeof object === 'object') { |
|
57 |
+ clone = (object === null) ? null : {}; |
|
58 |
+ for (key in object) clone[key] = Faye.copyObject(object[key]); |
|
59 |
+ return clone; |
|
60 |
+ } else { |
|
61 |
+ return object; |
|
62 |
+ } |
|
63 |
+ }, |
|
64 |
+ |
|
65 |
+ commonElement: function(lista, listb) { |
|
66 |
+ for (var i = 0, n = lista.length; i < n; i++) { |
|
67 |
+ if (this.indexOf(listb, lista[i]) !== -1) |
|
68 |
+ return lista[i]; |
|
69 |
+ } |
|
70 |
+ return null; |
|
71 |
+ }, |
|
72 |
+ |
|
73 |
+ indexOf: function(list, needle) { |
|
74 |
+ if (list.indexOf) return list.indexOf(needle); |
|
75 |
+ |
|
76 |
+ for (var i = 0, n = list.length; i < n; i++) { |
|
77 |
+ if (list[i] === needle) return i; |
|
78 |
+ } |
|
79 |
+ return -1; |
|
80 |
+ }, |
|
81 |
+ |
|
82 |
+ map: function(object, callback, context) { |
|
83 |
+ if (object.map) return object.map(callback, context); |
|
84 |
+ var result = []; |
|
85 |
+ |
|
86 |
+ if (object instanceof Array) { |
|
87 |
+ for (var i = 0, n = object.length; i < n; i++) { |
|
88 |
+ result.push(callback.call(context || null, object[i], i)); |
|
89 |
+ } |
|
90 |
+ } else { |
|
91 |
+ for (var key in object) { |
|
92 |
+ if (!object.hasOwnProperty(key)) continue; |
|
93 |
+ result.push(callback.call(context || null, key, object[key])); |
|
94 |
+ } |
|
95 |
+ } |
|
96 |
+ return result; |
|
97 |
+ }, |
|
98 |
+ |
|
99 |
+ filter: function(array, callback, context) { |
|
100 |
+ if (array.filter) return array.filter(callback, context); |
|
101 |
+ var result = []; |
|
102 |
+ for (var i = 0, n = array.length; i < n; i++) { |
|
103 |
+ if (callback.call(context || null, array[i], i)) |
|
104 |
+ result.push(array[i]); |
|
105 |
+ } |
|
106 |
+ return result; |
|
107 |
+ }, |
|
108 |
+ |
|
109 |
+ asyncEach: function(list, iterator, callback, context) { |
|
110 |
+ var n = list.length, |
|
111 |
+ i = -1, |
|
112 |
+ calls = 0, |
|
113 |
+ looping = false; |
|
114 |
+ |
|
115 |
+ var iterate = function() { |
|
116 |
+ calls -= 1; |
|
117 |
+ i += 1; |
|
118 |
+ if (i === n) return callback && callback.call(context); |
|
119 |
+ iterator(list[i], resume); |
|
120 |
+ }; |
|
121 |
+ |
|
122 |
+ var loop = function() { |
|
123 |
+ if (looping) return; |
|
124 |
+ looping = true; |
|
125 |
+ while (calls > 0) iterate(); |
|
126 |
+ looping = false; |
|
127 |
+ }; |
|
128 |
+ |
|
129 |
+ var resume = function() { |
|
130 |
+ calls += 1; |
|
131 |
+ loop(); |
|
132 |
+ }; |
|
133 |
+ resume(); |
|
134 |
+ }, |
|
135 |
+ |
|
136 |
+ // http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/ |
|
137 |
+ toJSON: function(object) { |
|
138 |
+ if (!this.stringify) return JSON.stringify(object); |
|
139 |
+ |
|
140 |
+ return this.stringify(object, function(key, value) { |
|
141 |
+ return (this[key] instanceof Array) ? this[key] : value; |
|
142 |
+ }); |
|
143 |
+ } |
|
144 |
+}; |
|
145 |
+ |
|
146 |
+if (typeof module !== 'undefined') |
|
147 |
+ module.exports = Faye; |
|
148 |
+else if (typeof window !== 'undefined') |
|
149 |
+ window.Faye = Faye; |
|
150 |
+ |
|
151 |
+Faye.Class = function(parent, methods) { |
|
152 |
+ if (typeof parent !== 'function') { |
|
153 |
+ methods = parent; |
|
154 |
+ parent = Object; |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ var klass = function() { |
|
158 |
+ if (!this.initialize) return this; |
|
159 |
+ return this.initialize.apply(this, arguments) || this; |
|
160 |
+ }; |
|
161 |
+ |
|
162 |
+ var bridge = function() {}; |
|
163 |
+ bridge.prototype = parent.prototype; |
|
164 |
+ |
|
165 |
+ klass.prototype = new bridge(); |
|
166 |
+ Faye.extend(klass.prototype, methods); |
|
167 |
+ |
|
168 |
+ return klass; |
|
169 |
+}; |
|
170 |
+ |
|
171 |
+(function() { |
|
172 |
+var EventEmitter = Faye.EventEmitter = function() {}; |
|
173 |
+ |
|
174 |
+/* |
|
175 |
+Copyright Joyent, Inc. and other Node contributors. All rights reserved. |
|
176 |
+Permission is hereby granted, free of charge, to any person obtaining a copy of |
|
177 |
+this software and associated documentation files (the "Software"), to deal in |
|
178 |
+the Software without restriction, including without limitation the rights to |
|
179 |
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
|
180 |
+of the Software, and to permit persons to whom the Software is furnished to do |
|
181 |
+so, subject to the following conditions: |
|
182 |
+ |
|
183 |
+The above copyright notice and this permission notice shall be included in all |
|
184 |
+copies or substantial portions of the Software. |
|
185 |
+ |
|
186 |
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
187 |
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
188 |
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
189 |
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
190 |
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
191 |
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
192 |
+SOFTWARE. |
|
193 |
+*/ |
|
194 |
+ |
|
195 |
+var isArray = typeof Array.isArray === 'function' |
|
196 |
+ ? Array.isArray |
|
197 |
+ : function (xs) { |
|
198 |
+ return Object.prototype.toString.call(xs) === '[object Array]' |
|
199 |
+ } |
|
200 |
+; |
|
201 |
+function indexOf (xs, x) { |
|
202 |
+ if (xs.indexOf) return xs.indexOf(x); |
|
203 |
+ for (var i = 0; i < xs.length; i++) { |
|
204 |
+ if (x === xs[i]) return i; |
|
205 |
+ } |
|
206 |
+ return -1; |
|
207 |
+} |
|
208 |
+ |
|
209 |
+ |
|
210 |
+EventEmitter.prototype.emit = function(type) { |
|
211 |
+ // If there is no 'error' event listener then throw. |
|
212 |
+ if (type === 'error') { |
|
213 |
+ if (!this._events || !this._events.error || |
|
214 |
+ (isArray(this._events.error) && !this._events.error.length)) |
|
215 |
+ { |
|
216 |
+ if (arguments[1] instanceof Error) { |
|
217 |
+ throw arguments[1]; // Unhandled 'error' event |
|
218 |
+ } else { |
|
219 |
+ throw new Error("Uncaught, unspecified 'error' event."); |
|
220 |
+ } |
|
221 |
+ return false; |
|
222 |
+ } |
|
223 |
+ } |
|
224 |
+ |
|
225 |
+ if (!this._events) return false; |
|
226 |
+ var handler = this._events[type]; |
|
227 |
+ if (!handler) return false; |
|
228 |
+ |
|
229 |
+ if (typeof handler == 'function') { |
|
230 |
+ switch (arguments.length) { |
|
231 |
+ // fast cases |
|
232 |
+ case 1: |
|
233 |
+ handler.call(this); |
|
234 |
+ break; |
|
235 |
+ case 2: |
|
236 |
+ handler.call(this, arguments[1]); |
|
237 |
+ break; |
|
238 |
+ case 3: |
|
239 |
+ handler.call(this, arguments[1], arguments[2]); |
|
240 |
+ break; |
|
241 |
+ // slower |
|
242 |
+ default: |
|
243 |
+ var args = Array.prototype.slice.call(arguments, 1); |
|
244 |
+ handler.apply(this, args); |
|
245 |
+ } |
|
246 |
+ return true; |
|
247 |
+ |
|
248 |
+ } else if (isArray(handler)) { |
|
249 |
+ var args = Array.prototype.slice.call(arguments, 1); |
|
250 |
+ |
|
251 |
+ var listeners = handler.slice(); |
|
252 |
+ for (var i = 0, l = listeners.length; i < l; i++) { |
|
253 |
+ listeners[i].apply(this, args); |
|
254 |
+ } |
|
255 |
+ return true; |
|
256 |
+ |
|
257 |
+ } else { |
|
258 |
+ return false; |
|
259 |
+ } |
|
260 |
+}; |
|
261 |
+ |
|
262 |
+// EventEmitter is defined in src/node_events.cc |
|
263 |
+// EventEmitter.prototype.emit() is also defined there. |
|
264 |
+EventEmitter.prototype.addListener = function(type, listener) { |
|
265 |
+ if ('function' !== typeof listener) { |
|
266 |
+ throw new Error('addListener only takes instances of Function'); |
|
267 |
+ } |
|
268 |
+ |
|
269 |
+ if (!this._events) this._events = {}; |
|
270 |
+ |
|
271 |
+ // To avoid recursion in the case that type == "newListeners"! Before |
|
272 |
+ // adding it to the listeners, first emit "newListeners". |
|
273 |
+ this.emit('newListener', type, listener); |
|
274 |
+ |
|
275 |
+ if (!this._events[type]) { |
|
276 |
+ // Optimize the case of one listener. Don't need the extra array object. |
|
277 |
+ this._events[type] = listener; |
|
278 |
+ } else if (isArray(this._events[type])) { |
|
279 |
+ // If we've already got an array, just append. |
|
280 |
+ this._events[type].push(listener); |
|
281 |
+ } else { |
|
282 |
+ // Adding the second element, need to change to array. |
|
283 |
+ this._events[type] = [this._events[type], listener]; |
|
284 |
+ } |
|
285 |
+ |
|
286 |
+ return this; |
|
287 |
+}; |
|
288 |
+ |
|
289 |
+EventEmitter.prototype.on = EventEmitter.prototype.addListener; |
|
290 |
+ |
|
291 |
+EventEmitter.prototype.once = function(type, listener) { |
|
292 |
+ var self = this; |
|
293 |
+ self.on(type, function g() { |
|
294 |
+ self.removeListener(type, g); |
|
295 |
+ listener.apply(this, arguments); |
|
296 |
+ }); |
|
297 |
+ |
|
298 |
+ return this; |
|
299 |
+}; |
|
300 |
+ |
|
301 |
+EventEmitter.prototype.removeListener = function(type, listener) { |
|
302 |
+ if ('function' !== typeof listener) { |
|
303 |
+ throw new Error('removeListener only takes instances of Function'); |
|
304 |
+ } |
|
305 |
+ |
|
306 |
+ // does not use listeners(), so no side effect of creating _events[type] |
|
307 |
+ if (!this._events || !this._events[type]) return this; |
|
308 |
+ |
|
309 |
+ var list = this._events[type]; |
|
310 |
+ |
|
311 |
+ if (isArray(list)) { |
|
312 |
+ var i = indexOf(list, listener); |
|
313 |
+ if (i < 0) return this; |
|
314 |
+ list.splice(i, 1); |
|
315 |
+ if (list.length == 0) |
|
316 |
+ delete this._events[type]; |
|
317 |
+ } else if (this._events[type] === listener) { |
|
318 |
+ delete this._events[type]; |
|
319 |
+ } |
|
320 |
+ |
|
321 |
+ return this; |
|
322 |
+}; |
|
323 |
+ |
|
324 |
+EventEmitter.prototype.removeAllListeners = function(type) { |
|
325 |
+ if (arguments.length === 0) { |
|
326 |
+ this._events = {}; |
|
327 |
+ return this; |
|
328 |
+ } |
|
329 |
+ |
|
330 |
+ // does not use listeners(), so no side effect of creating _events[type] |
|
331 |
+ if (type && this._events && this._events[type]) this._events[type] = null; |
|
332 |
+ return this; |
|
333 |
+}; |
|
334 |
+ |
|
335 |
+EventEmitter.prototype.listeners = function(type) { |
|
336 |
+ if (!this._events) this._events = {}; |
|
337 |
+ if (!this._events[type]) this._events[type] = []; |
|
338 |
+ if (!isArray(this._events[type])) { |
|
339 |
+ this._events[type] = [this._events[type]]; |
|
340 |
+ } |
|
341 |
+ return this._events[type]; |
|
342 |
+}; |
|
343 |
+ |
|
344 |
+})(); |
|
345 |
+ |
|
346 |
+Faye.Namespace = Faye.Class({ |
|
347 |
+ initialize: function() { |
|
348 |
+ this._used = {}; |
|
349 |
+ }, |
|
350 |
+ |
|
351 |
+ exists: function(id) { |
|
352 |
+ return this._used.hasOwnProperty(id); |
|
353 |
+ }, |
|
354 |
+ |
|
355 |
+ generate: function() { |
|
356 |
+ var name = Faye.random(); |
|
357 |
+ while (this._used.hasOwnProperty(name)) |
|
358 |
+ name = Faye.random(); |
|
359 |
+ return this._used[name] = name; |
|
360 |
+ }, |
|
361 |
+ |
|
362 |
+ release: function(id) { |
|
363 |
+ delete this._used[id]; |
|
364 |
+ } |
|
365 |
+}); |
|
366 |
+ |
|
367 |
+(function() { |
|
368 |
+'use strict'; |
|
369 |
+ |
|
370 |
+var timeout = setTimeout, defer; |
|
371 |
+ |
|
372 |
+if (typeof setImmediate === 'function') |
|
373 |
+ defer = function(fn) { setImmediate(fn) }; |
|
374 |
+else if (typeof process === 'object' && process.nextTick) |
|
375 |
+ defer = function(fn) { process.nextTick(fn) }; |
|
376 |
+else |
|
377 |
+ defer = function(fn) { timeout(fn, 0) }; |
|
378 |
+ |
|
379 |
+var PENDING = 0, |
|
380 |
+ FULFILLED = 1, |
|
381 |
+ REJECTED = 2; |
|
382 |
+ |
|
383 |
+var RETURN = function(x) { return x }, |
|
384 |
+ THROW = function(x) { throw x }; |
|
385 |
+ |
|
386 |
+var Promise = function(task) { |
|
387 |
+ this._state = PENDING; |
|
388 |
+ this._onFulfilled = []; |
|
389 |
+ this._onRejected = []; |
|
390 |
+ |
|
391 |
+ if (typeof task !== 'function') return; |
|
392 |
+ var self = this; |
|
393 |
+ |
|
394 |
+ task(function(value) { fulfill(self, value) }, |
|
395 |
+ function(reason) { reject(self, reason) }); |
|
396 |
+}; |
|
397 |
+ |
|
398 |
+Promise.prototype.then = function(onFulfilled, onRejected) { |
|
399 |
+ var next = new Promise(); |
|
400 |
+ registerOnFulfilled(this, onFulfilled, next); |
|
401 |
+ registerOnRejected(this, onRejected, next); |
|
402 |
+ return next; |
|
403 |
+}; |
|
404 |
+ |
|
405 |
+var registerOnFulfilled = function(promise, onFulfilled, next) { |
|
406 |
+ if (typeof onFulfilled !== 'function') onFulfilled = RETURN; |
|
407 |
+ var handler = function(value) { invoke(onFulfilled, value, next) }; |
|
408 |
+ |
|
409 |
+ if (promise._state === PENDING) { |
|
410 |
+ promise._onFulfilled.push(handler); |
|
411 |
+ } else if (promise._state === FULFILLED) { |
|
412 |
+ handler(promise._value); |
|
413 |
+ } |
|
414 |
+}; |
|
415 |
+ |
|
416 |
+var registerOnRejected = function(promise, onRejected, next) { |
|
417 |
+ if (typeof onRejected !== 'function') onRejected = THROW; |
|
418 |
+ var handler = function(reason) { invoke(onRejected, reason, next) }; |
|
419 |
+ |
|
420 |
+ if (promise._state === PENDING) { |
|
421 |
+ promise._onRejected.push(handler); |
|
422 |
+ } else if (promise._state === REJECTED) { |
|
423 |
+ handler(promise._reason); |
|
424 |
+ } |
|
425 |
+}; |
|
426 |
+ |
|
427 |
+var invoke = function(fn, value, next) { |
|
428 |
+ defer(function() { _invoke(fn, value, next) }); |
|
429 |
+}; |
|
430 |
+ |
|
431 |
+var _invoke = function(fn, value, next) { |
|
432 |
+ var outcome; |
|
433 |
+ |
|
434 |
+ try { |
|
435 |
+ outcome = fn(value); |
|
436 |
+ } catch (error) { |
|
437 |
+ return reject(next, error); |
|
438 |
+ } |
|
439 |
+ |
|
440 |
+ if (outcome === next) { |
|
441 |
+ reject(next, new TypeError('Recursive promise chain detected')); |
|
442 |
+ } else { |
|
443 |
+ fulfill(next, outcome); |
|
444 |
+ } |
|
445 |
+}; |
|
446 |
+ |
|
447 |
+var fulfill = Promise.fulfill = Promise.resolve = function(promise, value) { |
|
448 |
+ var called = false, type, then; |
|
449 |
+ |
|
450 |
+ try { |
|
451 |
+ type = typeof value; |
|
452 |
+ then = value !== null && (type === 'function' || type === 'object') && value.then; |
|
453 |
+ |
|
454 |
+ if (typeof then !== 'function') return _fulfill(promise, value); |
|
455 |
+ |
|
456 |
+ then.call(value, function(v) { |
|
457 |
+ if (!(called ^ (called = true))) return; |
|
458 |
+ fulfill(promise, v); |
|
459 |
+ }, function(r) { |
|
460 |
+ if (!(called ^ (called = true))) return; |
|
461 |
+ reject(promise, r); |
|
462 |
+ }); |
|
463 |
+ } catch (error) { |
|
464 |
+ if (!(called ^ (called = true))) return; |
|
465 |
+ reject(promise, error); |
|
466 |
+ } |
|
467 |
+}; |
|
468 |
+ |
|
469 |
+var _fulfill = function(promise, value) { |
|
470 |
+ if (promise._state !== PENDING) return; |
|
471 |
+ |
|
472 |
+ promise._state = FULFILLED; |
|
473 |
+ promise._value = value; |
|
474 |
+ promise._onRejected = []; |
|
475 |
+ |
|
476 |
+ var onFulfilled = promise._onFulfilled, fn; |
|
477 |
+ while (fn = onFulfilled.shift()) fn(value); |
|
478 |
+}; |
|
479 |
+ |
|
480 |
+var reject = Promise.reject = function(promise, reason) { |
|
481 |
+ if (promise._state !== PENDING) return; |
|
482 |
+ |
|
483 |
+ promise._state = REJECTED; |
|
484 |
+ promise._reason = reason; |
|
485 |
+ promise._onFulfilled = []; |
|
486 |
+ |
|
487 |
+ var onRejected = promise._onRejected, fn; |
|
488 |
+ while (fn = onRejected.shift()) fn(reason); |
|
489 |
+}; |
|
490 |
+ |
|
491 |
+Promise.all = function(promises) { |
|
492 |
+ return new Promise(function(fulfill, reject) { |
|
493 |
+ var list = [], |
|
494 |
+ n = promises.length, |
|
495 |
+ i; |
|
496 |
+ |
|
497 |
+ if (n === 0) return fulfill(list); |
|
498 |
+ |
|
499 |
+ for (i = 0; i < n; i++) (function(promise, i) { |
|
500 |
+ Promise.fulfilled(promise).then(function(value) { |
|
501 |
+ list[i] = value; |
|
502 |
+ if (--n === 0) fulfill(list); |
|
503 |
+ }, reject); |
|
504 |
+ })(promises[i], i); |
|
505 |
+ }); |
|
506 |
+}; |
|
507 |
+ |
|
508 |
+Promise.defer = defer; |
|
509 |
+ |
|
510 |
+Promise.deferred = Promise.pending = function() { |
|
511 |
+ var tuple = {}; |
|
512 |
+ |
|
513 |
+ tuple.promise = new Promise(function(fulfill, reject) { |
|
514 |
+ tuple.fulfill = tuple.resolve = fulfill; |
|
515 |
+ tuple.reject = reject; |
|
516 |
+ }); |
|
517 |
+ return tuple; |
|
518 |
+}; |
|
519 |
+ |
|
520 |
+Promise.fulfilled = Promise.resolved = function(value) { |
|
521 |
+ return new Promise(function(fulfill, reject) { fulfill(value) }); |
|
522 |
+}; |
|
523 |
+ |
|
524 |
+Promise.rejected = function(reason) { |
|
525 |
+ return new Promise(function(fulfill, reject) { reject(reason) }); |
|
526 |
+}; |
|
527 |
+ |
|
528 |
+if (typeof Faye === 'undefined') |
|
529 |
+ module.exports = Promise; |
|
530 |
+else |
|
531 |
+ Faye.Promise = Promise; |
|
532 |
+ |
|
533 |
+})(); |
|
534 |
+ |
|
535 |
+Faye.Set = Faye.Class({ |
|
536 |
+ initialize: function() { |
|
537 |
+ this._index = {}; |
|
538 |
+ }, |
|
539 |
+ |
|
540 |
+ add: function(item) { |
|
541 |
+ var key = (item.id !== undefined) ? item.id : item; |
|
542 |
+ if (this._index.hasOwnProperty(key)) return false; |
|
543 |
+ this._index[key] = item; |
|
544 |
+ return true; |
|
545 |
+ }, |
|
546 |
+ |
|
547 |
+ forEach: function(block, context) { |
|
548 |
+ for (var key in this._index) { |
|
549 |
+ if (this._index.hasOwnProperty(key)) |
|
550 |
+ block.call(context, this._index[key]); |
|
551 |
+ } |
|
552 |
+ }, |
|
553 |
+ |
|
554 |
+ isEmpty: function() { |
|
555 |
+ for (var key in this._index) { |
|
556 |
+ if (this._index.hasOwnProperty(key)) return false; |
|
557 |
+ } |
|
558 |
+ return true; |
|
559 |
+ }, |
|
560 |
+ |
|
561 |
+ member: function(item) { |
|
562 |
+ for (var key in this._index) { |
|
563 |
+ if (this._index[key] === item) return true; |
|
564 |
+ } |
|
565 |
+ return false; |
|
566 |
+ }, |
|
567 |
+ |
|
568 |
+ remove: function(item) { |
|
569 |
+ var key = (item.id !== undefined) ? item.id : item; |
|
570 |
+ var removed = this._index[key]; |
|
571 |
+ delete this._index[key]; |
|
572 |
+ return removed; |
|
573 |
+ }, |
|
574 |
+ |
|
575 |
+ toArray: function() { |
|
576 |
+ var array = []; |
|
577 |
+ this.forEach(function(item) { array.push(item) }); |
|
578 |
+ return array; |
|
579 |
+ } |
|
580 |
+}); |
|
581 |
+ |
|
582 |
+Faye.URI = { |
|
583 |
+ isURI: function(uri) { |
|
584 |
+ return uri && uri.protocol && uri.host && uri.path; |
|
585 |
+ }, |
|
586 |
+ |
|
587 |
+ isSameOrigin: function(uri) { |
|
588 |
+ var location = Faye.ENV.location; |
|
589 |
+ return uri.protocol === location.protocol && |
|
590 |
+ uri.hostname === location.hostname && |
|
591 |
+ uri.port === location.port; |
|
592 |
+ }, |
|
593 |
+ |
|
594 |
+ parse: function(url) { |
|
595 |
+ if (typeof url !== 'string') return url; |
|
596 |
+ var uri = {}, parts, query, pairs, i, n, data; |
|
597 |
+ |
|
598 |
+ var consume = function(name, pattern) { |
|
599 |
+ url = url.replace(pattern, function(match) { |
|
600 |
+ uri[name] = match; |
|
601 |
+ return ''; |
|
602 |
+ }); |
|
603 |
+ uri[name] = uri[name] || ''; |
|
604 |
+ }; |
|
605 |
+ |
|
606 |
+ consume('protocol', /^[a-z]+\:/i); |
|
607 |
+ consume('host', /^\/\/[^\/\?#]+/); |
|
608 |
+ |
|
609 |
+ if (!/^\//.test(url) && !uri.host) |
|
610 |
+ url = Faye.ENV.location.pathname.replace(/[^\/]*$/, '') + url; |
|
611 |
+ |
|
612 |
+ consume('pathname', /^[^\?#]*/); |
|
613 |
+ consume('search', /^\?[^#]*/); |
|
614 |
+ consume('hash', /^#.*/); |
|
615 |
+ |
|
616 |
+ uri.protocol = uri.protocol || Faye.ENV.location.protocol; |
|
617 |
+ |
|
618 |
+ if (uri.host) { |
|
619 |
+ uri.host = uri.host.substr(2); |
|
620 |
+ parts = uri.host.split(':'); |
|
621 |
+ uri.hostname = parts[0]; |
|
622 |
+ uri.port = parts[1] || ''; |
|
623 |
+ } else { |
|
624 |
+ uri.host = Faye.ENV.location.host; |
|
625 |
+ uri.hostname = Faye.ENV.location.hostname; |
|
626 |
+ uri.port = Faye.ENV.location.port; |
|
627 |
+ } |
|
628 |
+ |
|
629 |
+ uri.pathname = uri.pathname || '/'; |
|
630 |
+ uri.path = uri.pathname + uri.search; |
|
631 |
+ |
|
632 |
+ query = uri.search.replace(/^\?/, ''); |
|
633 |
+ pairs = query ? query.split('&') : []; |
|
634 |
+ data = {}; |
|
635 |
+ |
|
636 |
+ for (i = 0, n = pairs.length; i < n; i++) { |
|
637 |
+ parts = pairs[i].split('='); |
|
638 |
+ data[decodeURIComponent(parts[0] || '')] = decodeURIComponent(parts[1] || ''); |
|
639 |
+ } |
|
640 |
+ |
|
641 |
+ uri.query = data; |
|
642 |
+ |
|
643 |
+ uri.href = this.stringify(uri); |
|
644 |
+ return uri; |
|
645 |
+ }, |
|
646 |
+ |
|
647 |
+ stringify: function(uri) { |
|
648 |
+ var string = uri.protocol + '//' + uri.hostname; |
|
649 |
+ if (uri.port) string += ':' + uri.port; |
|
650 |
+ string += uri.pathname + this.queryString(uri.query) + (uri.hash || ''); |
|
651 |
+ return string; |
|
652 |
+ }, |
|
653 |
+ |
|
654 |
+ queryString: function(query) { |
|
655 |
+ var pairs = []; |
|
656 |
+ for (var key in query) { |
|
657 |
+ if (!query.hasOwnProperty(key)) continue; |
|
658 |
+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(query[key])); |
|
659 |
+ } |
|
660 |
+ if (pairs.length === 0) return ''; |
|
661 |
+ return '?' + pairs.join('&'); |
|
662 |
+ } |
|
663 |
+}; |
|
664 |
+ |
|
665 |
+Faye.Error = Faye.Class({ |
|
666 |
+ initialize: function(code, params, message) { |
|
667 |
+ this.code = code; |
|
668 |
+ this.params = Array.prototype.slice.call(params); |
|
669 |
+ this.message = message; |
|
670 |
+ }, |
|
671 |
+ |
|
672 |
+ toString: function() { |
|
673 |
+ return this.code + ':' + |
|
674 |
+ this.params.join(',') + ':' + |
|
675 |
+ this.message; |
|
676 |
+ } |
|
677 |
+}); |
|
678 |
+ |
|
679 |
+Faye.Error.parse = function(message) { |
|
680 |
+ message = message || ''; |
|
681 |
+ if (!Faye.Grammar.ERROR.test(message)) return new this(null, [], message); |
|
682 |
+ |
|
683 |
+ var parts = message.split(':'), |
|
684 |
+ code = parseInt(parts[0]), |
|
685 |
+ params = parts[1].split(','), |
|
686 |
+ message = parts[2]; |
|
687 |
+ |
|
688 |
+ return new this(code, params, message); |
|
689 |
+}; |
|
690 |
+ |
|
691 |
+ |
|
692 |
+ |
|
693 |
+ |
|
694 |
+Faye.Error.versionMismatch = function() { |
|
695 |
+ return new this(300, arguments, 'Version mismatch').toString(); |
|
696 |
+}; |
|
697 |
+ |
|
698 |
+Faye.Error.conntypeMismatch = function() { |
|
699 |
+ return new this(301, arguments, 'Connection types not supported').toString(); |
|
700 |
+}; |
|
701 |
+ |
|
702 |
+Faye.Error.extMismatch = function() { |
|
703 |
+ return new this(302, arguments, 'Extension mismatch').toString(); |
|
704 |
+}; |
|
705 |
+ |
|
706 |
+Faye.Error.badRequest = function() { |
|
707 |
+ return new this(400, arguments, 'Bad request').toString(); |
|
708 |
+}; |
|
709 |
+ |
|
710 |
+Faye.Error.clientUnknown = function() { |
|
711 |
+ return new this(401, arguments, 'Unknown client').toString(); |
|
712 |
+}; |
|
713 |
+ |
|
714 |
+Faye.Error.parameterMissing = function() { |
|
715 |
+ return new this(402, arguments, 'Missing required parameter').toString(); |
|
716 |
+}; |
|
717 |
+ |
|
718 |
+Faye.Error.channelForbidden = function() { |
|
719 |
+ return new this(403, arguments, 'Forbidden channel').toString(); |
|
720 |
+}; |
|
721 |
+ |
|
722 |
+Faye.Error.channelUnknown = function() { |
|
723 |
+ return new this(404, arguments, 'Unknown channel').toString(); |
|
724 |
+}; |
|
725 |
+ |
|
726 |
+Faye.Error.channelInvalid = function() { |
|
727 |
+ return new this(405, arguments, 'Invalid channel').toString(); |
|
728 |
+}; |
|
729 |
+ |
|
730 |
+Faye.Error.extUnknown = function() { |
|
731 |
+ return new this(406, arguments, 'Unknown extension').toString(); |
|
732 |
+}; |
|
733 |
+ |
|
734 |
+Faye.Error.publishFailed = function() { |
|
735 |
+ return new this(407, arguments, 'Failed to publish').toString(); |
|
736 |
+}; |
|
737 |
+ |
|
738 |
+Faye.Error.serverError = function() { |
|
739 |
+ return new this(500, arguments, 'Internal server error').toString(); |
|
740 |
+}; |
|
741 |
+ |
|
742 |
+ |
|
743 |
+Faye.Deferrable = { |
|
744 |
+ then: function(callback, errback) { |
|
745 |
+ var self = this; |
|
746 |
+ if (!this._promise) |
|
747 |
+ this._promise = new Faye.Promise(function(fulfill, reject) { |
|
748 |
+ self._fulfill = fulfill; |
|
749 |
+ self._reject = reject; |
|
750 |
+ }); |
|
751 |
+ |
|
752 |
+ if (arguments.length === 0) |
|
753 |
+ return this._promise; |
|
754 |
+ else |
|
755 |
+ return this._promise.then(callback, errback); |
|
756 |
+ }, |
|
757 |
+ |
|
758 |
+ callback: function(callback, context) { |
|
759 |
+ return this.then(function(value) { callback.call(context, value) }); |
|
760 |
+ }, |
|
761 |
+ |
|
762 |
+ errback: function(callback, context) { |
|
763 |
+ return this.then(null, function(reason) { callback.call(context, reason) }); |
|
764 |
+ }, |
|
765 |
+ |
|
766 |
+ timeout: function(seconds, message) { |
|
767 |
+ this.then(); |
|
768 |
+ var self = this; |
|
769 |
+ this._timer = Faye.ENV.setTimeout(function() { |
|
770 |
+ self._reject(message); |
|
771 |
+ }, seconds * 1000); |
|
772 |
+ }, |
|
773 |
+ |
|
774 |
+ setDeferredStatus: function(status, value) { |
|
775 |
+ if (this._timer) Faye.ENV.clearTimeout(this._timer); |
|
776 |
+ |
|
777 |
+ this.then(); |
|
778 |
+ |
|
779 |
+ if (status === 'succeeded') |
|
780 |
+ this._fulfill(value); |
|
781 |
+ else if (status === 'failed') |
|
782 |
+ this._reject(value); |
|
783 |
+ else |
|
784 |
+ delete this._promise; |
|
785 |
+ } |
|
786 |
+}; |
|
787 |
+ |
|
788 |
+Faye.Publisher = { |
|
789 |
+ countListeners: function(eventType) { |
|
790 |
+ return this.listeners(eventType).length; |
|
791 |
+ }, |
|
792 |
+ |
|
793 |
+ bind: function(eventType, listener, context) { |
|
794 |
+ var slice = Array.prototype.slice, |
|
795 |
+ handler = function() { listener.apply(context, slice.call(arguments)) }; |
|
796 |
+ |
|
797 |
+ this._listeners = this._listeners || []; |
|
798 |
+ this._listeners.push([eventType, listener, context, handler]); |
|
799 |
+ return this.on(eventType, handler); |
|
800 |
+ }, |
|
801 |
+ |
|
802 |
+ unbind: function(eventType, listener, context) { |
|
803 |
+ this._listeners = this._listeners || []; |
|
804 |
+ var n = this._listeners.length, tuple; |
|
805 |
+ |
|
806 |
+ while (n--) { |
|
807 |
+ tuple = this._listeners[n]; |
|
808 |
+ if (tuple[0] !== eventType) continue; |
|
809 |
+ if (listener && (tuple[1] !== listener || tuple[2] !== context)) continue; |
|
810 |
+ this._listeners.splice(n, 1); |
|
811 |
+ this.removeListener(eventType, tuple[3]); |
|
812 |
+ } |
|
813 |
+ } |
|
814 |
+}; |
|
815 |
+ |
|
816 |
+Faye.extend(Faye.Publisher, Faye.EventEmitter.prototype); |
|
817 |
+Faye.Publisher.trigger = Faye.Publisher.emit; |
|
818 |
+ |
|
819 |
+Faye.Timeouts = { |
|
820 |
+ addTimeout: function(name, delay, callback, context) { |
|
821 |
+ this._timeouts = this._timeouts || {}; |
|
822 |
+ if (this._timeouts.hasOwnProperty(name)) return; |
|
823 |
+ var self = this; |
|
824 |
+ this._timeouts[name] = Faye.ENV.setTimeout(function() { |
|
825 |
+ delete self._timeouts[name]; |
|
826 |
+ callback.call(context); |
|
827 |
+ }, 1000 * delay); |
|
828 |
+ }, |
|
829 |
+ |
|
830 |
+ removeTimeout: function(name) { |
|
831 |
+ this._timeouts = this._timeouts || {}; |
|
832 |
+ var timeout = this._timeouts[name]; |
|
833 |
+ if (!timeout) return; |
|
834 |
+ Faye.ENV.clearTimeout(timeout); |
|
835 |
+ delete this._timeouts[name]; |
|
836 |
+ }, |
|
837 |
+ |
|
838 |
+ removeAllTimeouts: function() { |
|
839 |
+ this._timeouts = this._timeouts || {}; |
|
840 |
+ for (var name in this._timeouts) this.removeTimeout(name); |
|
841 |
+ } |
|
842 |
+}; |
|
843 |
+ |
|
844 |
+Faye.Logging = { |
|
845 |
+ LOG_LEVELS: { |
|
846 |
+ fatal: 4, |
|
847 |
+ error: 3, |
|
848 |
+ warn: 2, |
|
849 |
+ info: 1, |
|
850 |
+ debug: 0 |
|
851 |
+ }, |
|
852 |
+ |
|
853 |
+ writeLog: function(messageArgs, level) { |
|
854 |
+ if (!Faye.logger) return; |
|
855 |
+ |
|
856 |
+ var args = Array.prototype.slice.apply(messageArgs), |
|
857 |
+ banner = '[Faye', |
|
858 |
+ klass = this.className, |
|
859 |
+ |
|
860 |
+ message = args.shift().replace(/\?/g, function() { |
|
861 |
+ try { |
|
862 |
+ return Faye.toJSON(args.shift()); |
|
863 |
+ } catch (e) { |
|
864 |
+ return '[Object]'; |
|
865 |
+ } |
|
866 |
+ }); |
|
867 |
+ |
|
868 |
+ for (var key in Faye) { |
|
869 |
+ if (klass) continue; |
|
870 |
+ if (typeof Faye[key] !== 'function') continue; |
|
871 |
+ if (this instanceof Faye[key]) klass = key; |
|
872 |
+ } |
|
873 |
+ if (klass) banner += '.' + klass; |
|
874 |
+ banner += '] '; |
|
875 |
+ |
|
876 |
+ if (typeof Faye.logger[level] === 'function') |
|
877 |
+ Faye.logger[level](banner + message); |
|
878 |
+ else if (typeof Faye.logger === 'function') |
|
879 |
+ Faye.logger(banner + message); |
|
880 |
+ } |
|
881 |
+}; |
|
882 |
+ |
|
883 |
+(function() { |
|
884 |
+ for (var key in Faye.Logging.LOG_LEVELS) |
|
885 |
+ (function(level) { |
|
886 |
+ Faye.Logging[level] = function() { |
|
887 |
+ this.writeLog(arguments, level); |
|
888 |
+ }; |
|
889 |
+ })(key); |
|
890 |
+})(); |
|
891 |
+ |
|
892 |
+Faye.Grammar = { |
|
893 |
+ CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/, |
|
894 |
+ CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/, |
|
895 |
+ ERROR: /^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/, |
|
896 |
+ VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/ |
|
897 |
+}; |
|
898 |
+ |
|
899 |
+Faye.Extensible = { |
|
900 |
+ addExtension: function(extension) { |
|
901 |
+ this._extensions = this._extensions || []; |
|
902 |
+ this._extensions.push(extension); |
|
903 |
+ if (extension.added) extension.added(this); |
|
904 |
+ }, |
|
905 |
+ |
|
906 |
+ removeExtension: function(extension) { |
|
907 |
+ if (!this._extensions) return; |
|
908 |
+ var i = this._extensions.length; |
|
909 |
+ while (i--) { |
|
910 |
+ if (this._extensions[i] !== extension) continue; |
|
911 |
+ this._extensions.splice(i,1); |
|
912 |
+ if (extension.removed) extension.removed(this); |
|
913 |
+ } |
|
914 |
+ }, |
|
915 |
+ |
|
916 |
+ pipeThroughExtensions: function(stage, message, request, callback, context) { |
|
917 |
+ this.debug('Passing through ? extensions: ?', stage, message); |
|
918 |
+ |
|
919 |
+ if (!this._extensions) return callback.call(context, message); |
|
920 |
+ var extensions = this._extensions.slice(); |
|
921 |
+ |
|
922 |
+ var pipe = function(message) { |
|
923 |
+ if (!message) return callback.call(context, message); |
|
924 |
+ |
|
925 |
+ var extension = extensions.shift(); |
|
926 |
+ if (!extension) return callback.call(context, message); |
|
927 |
+ |
|
928 |
+ var fn = extension[stage]; |
|
929 |
+ if (!fn) return pipe(message); |
|
930 |
+ |
|
931 |
+ if (fn.length >= 3) extension[stage](message, request, pipe); |
|
932 |
+ else extension[stage](message, pipe); |
|
933 |
+ }; |
|
934 |
+ pipe(message); |
|
935 |
+ } |
|
936 |
+}; |
|
937 |
+ |
|
938 |
+Faye.extend(Faye.Extensible, Faye.Logging); |
|
939 |
+ |
|
940 |
+Faye.Channel = Faye.Class({ |
|
941 |
+ initialize: function(name) { |
|
942 |
+ this.id = this.name = name; |
|
943 |
+ }, |
|
944 |
+ |
|
945 |
+ push: function(message) { |
|
946 |
+ this.trigger('message', message); |
|
947 |
+ }, |
|
948 |
+ |
|
949 |
+ isUnused: function() { |
|
950 |
+ return this.countListeners('message') === 0; |
|
951 |
+ } |
|
952 |
+}); |
|
953 |
+ |
|
954 |
+Faye.extend(Faye.Channel.prototype, Faye.Publisher); |
|
955 |
+ |
|
956 |
+Faye.extend(Faye.Channel, { |
|
957 |
+ HANDSHAKE: '/meta/handshake', |
|
958 |
+ CONNECT: '/meta/connect', |
|
959 |
+ SUBSCRIBE: '/meta/subscribe', |
|
960 |
+ UNSUBSCRIBE: '/meta/unsubscribe', |
|
961 |
+ DISCONNECT: '/meta/disconnect', |
|
962 |
+ |
|
963 |
+ META: 'meta', |
|
964 |
+ SERVICE: 'service', |
|
965 |
+ |
|
966 |
+ expand: function(name) { |
|
967 |
+ var segments = this.parse(name), |
|
968 |
+ channels = ['/**', name]; |
|
969 |
+ |
|
970 |
+ var copy = segments.slice(); |
|
971 |
+ copy[copy.length - 1] = '*'; |
|
972 |
+ channels.push(this.unparse(copy)); |
|
973 |
+ |
|
974 |
+ for (var i = 1, n = segments.length; i < n; i++) { |
|
975 |
+ copy = segments.slice(0, i); |
|
976 |
+ copy.push('**'); |
|
977 |
+ channels.push(this.unparse(copy)); |
|
978 |
+ } |
|
979 |
+ |
|
980 |
+ return channels; |
|
981 |
+ }, |
|
982 |
+ |
|
983 |
+ isValid: function(name) { |
|
984 |
+ return Faye.Grammar.CHANNEL_NAME.test(name) || |
|
985 |
+ Faye.Grammar.CHANNEL_PATTERN.test(name); |
|
986 |
+ }, |
|
987 |
+ |
|
988 |
+ parse: function(name) { |
|
989 |
+ if (!this.isValid(name)) return null; |
|
990 |
+ return name.split('/').slice(1); |
|
991 |
+ }, |
|
992 |
+ |
|
993 |
+ unparse: function(segments) { |
|
994 |
+ return '/' + segments.join('/'); |
|
995 |
+ }, |
|
996 |
+ |
|
997 |
+ isMeta: function(name) { |
|
998 |
+ var segments = this.parse(name); |
|
999 |
+ return segments ? (segments[0] === this.META) : null; |
|
1000 |
+ }, |
|
1001 |
+ |
|
1002 |
+ isService: function(name) { |
|
1003 |
+ var segments = this.parse(name); |
|
1004 |
+ return segments ? (segments[0] === this.SERVICE) : null; |
|
1005 |
+ }, |
|
1006 |
+ |
|
1007 |
+ isSubscribable: function(name) { |
|
1008 |
+ if (!this.isValid(name)) return null; |
|
1009 |
+ return !this.isMeta(name) && !this.isService(name); |
|
1010 |
+ }, |
|
1011 |
+ |
|
1012 |
+ Set: Faye.Class({ |
|
1013 |
+ initialize: function() { |
|
1014 |
+ this._channels = {}; |
|
1015 |
+ }, |
|
1016 |
+ |
|
1017 |
+ getKeys: function() { |
|
1018 |
+ var keys = []; |
|
1019 |
+ for (var key in this._channels) keys.push(key); |
|
1020 |
+ return keys; |
|
1021 |
+ }, |
|
1022 |
+ |
|
1023 |
+ remove: function(name) { |
|
1024 |
+ delete this._channels[name]; |
|
1025 |
+ }, |
|
1026 |
+ |
|
1027 |
+ hasSubscription: function(name) { |
|
1028 |
+ return this._channels.hasOwnProperty(name); |
|
1029 |
+ }, |
|
1030 |
+ |
|
1031 |
+ subscribe: function(names, callback, context) { |
|
1032 |
+ var name; |
|
1033 |
+ for (var i = 0, n = names.length; i < n; i++) { |
|
1034 |
+ name = names[i]; |
|
1035 |
+ var channel = this._channels[name] = this._channels[name] || new Faye.Channel(name); |
|
1036 |
+ if (callback) channel.bind('message', callback, context); |
|
1037 |
+ } |
|
1038 |
+ }, |
|
1039 |
+ |
|
1040 |
+ unsubscribe: function(name, callback, context) { |
|
1041 |
+ var channel = this._channels[name]; |
|
1042 |
+ if (!channel) return false; |
|
1043 |
+ channel.unbind('message', callback, context); |
|
1044 |
+ |
|
1045 |
+ if (channel.isUnused()) { |
|
1046 |
+ this.remove(name); |
|
1047 |
+ return true; |
|
1048 |
+ } else { |
|
1049 |
+ return false; |
|
1050 |
+ } |
|
1051 |
+ }, |
|
1052 |
+ |
|
1053 |
+ distributeMessage: function(message) { |
|
1054 |
+ var channels = Faye.Channel.expand(message.channel); |
|
1055 |
+ |
|
1056 |
+ for (var i = 0, n = channels.length; i < n; i++) { |
|
1057 |
+ var channel = this._channels[channels[i]]; |
|
1058 |
+ if (channel) channel.trigger('message', message.data); |
|
1059 |
+ } |
|
1060 |
+ } |
|
1061 |
+ }) |
|
1062 |
+}); |
|
1063 |
+ |
|
1064 |
+Faye.Publication = Faye.Class(Faye.Deferrable); |
|
1065 |
+ |
|
1066 |
+Faye.Subscription = Faye.Class({ |
|
1067 |
+ initialize: function(client, channels, callback, context) { |
|
1068 |
+ this._client = client; |
|
1069 |
+ this._channels = channels; |
|
1070 |
+ this._callback = callback; |
|
1071 |
+ this._context = context; |
|
1072 |
+ this._cancelled = false; |
|
1073 |
+ }, |
|
1074 |
+ |
|
1075 |
+ cancel: function() { |
|
1076 |
+ if (this._cancelled) return; |
|
1077 |
+ this._client.unsubscribe(this._channels, this._callback, this._context); |
|
1078 |
+ this._cancelled = true; |
|
1079 |
+ }, |
|
1080 |
+ |
|
1081 |
+ unsubscribe: function() { |
|
1082 |
+ this.cancel(); |
|
1083 |
+ } |
|
1084 |
+}); |
|
1085 |
+ |
|
1086 |
+Faye.extend(Faye.Subscription.prototype, Faye.Deferrable); |
|
1087 |
+ |
|
1088 |
+Faye.Client = Faye.Class({ |
|
1089 |
+ UNCONNECTED: 1, |
|
1090 |
+ CONNECTING: 2, |
|
1091 |
+ CONNECTED: 3, |
|
1092 |
+ DISCONNECTED: 4, |
|
1093 |
+ |
|
1094 |
+ HANDSHAKE: 'handshake', |
|
1095 |
+ RETRY: 'retry', |
|
1096 |
+ NONE: 'none', |
|
1097 |
+ |
|
1098 |
+ CONNECTION_TIMEOUT: 60, |
|
1099 |
+ |
|
1100 |
+ DEFAULT_ENDPOINT: '/bayeux', |
|
1101 |
+ INTERVAL: 0, |
|
1102 |
+ |
|
1103 |
+ initialize: function(endpoint, options) { |
|
1104 |
+ this.info('New client created for ?', endpoint); |
|
1105 |
+ options = options || {}; |
|
1106 |
+ |
|
1107 |
+ Faye.validateOptions(options, ['interval', 'timeout', 'endpoints', 'proxy', 'retry', 'scheduler', 'websocketExtensions', 'tls', 'ca']); |
|
1108 |
+ |
|
1109 |
+ this._endpoint = endpoint || this.DEFAULT_ENDPOINT; |
|
1110 |
+ this._channels = new Faye.Channel.Set(); |
|
1111 |
+ this._dispatcher = new Faye.Dispatcher(this, this._endpoint, options); |
|
1112 |
+ |
|
1113 |
+ this._messageId = 0; |
|
1114 |
+ this._state = this.UNCONNECTED; |
|
1115 |
+ |
|
1116 |
+ this._responseCallbacks = {}; |
|
1117 |
+ |
|
1118 |
+ this._advice = { |
|
1119 |
+ reconnect: this.RETRY, |
|
1120 |
+ interval: 1000 * (options.interval || this.INTERVAL), |
|
1121 |
+ timeout: 1000 * (options.timeout || this.CONNECTION_TIMEOUT) |
|
1122 |
+ }; |
|
1123 |
+ this._dispatcher.timeout = this._advice.timeout / 1000; |
|
1124 |
+ |
|
1125 |
+ this._dispatcher.bind('message', this._receiveMessage, this); |
|
1126 |
+ |
|
1127 |
+ if (Faye.Event && Faye.ENV.onbeforeunload !== undefined) |
|
1128 |
+ Faye.Event.on(Faye.ENV, 'beforeunload', function() { |
|
1129 |
+ if (Faye.indexOf(this._dispatcher._disabled, 'autodisconnect') < 0) |
|
1130 |
+ this.disconnect(); |
|
1131 |
+ }, this); |
|
1132 |
+ }, |
|
1133 |
+ |
|
1134 |
+ addWebsocketExtension: function(extension) { |
|
1135 |
+ return this._dispatcher.addWebsocketExtension(extension); |
|
1136 |
+ }, |
|
1137 |
+ |
|
1138 |
+ disable: function(feature) { |
|
1139 |
+ return this._dispatcher.disable(feature); |
|
1140 |
+ }, |
|
1141 |
+ |
|
1142 |
+ setHeader: function(name, value) { |
|
1143 |
+ return this._dispatcher.setHeader(name, value); |
|
1144 |
+ }, |
|
1145 |
+ |
|
1146 |
+ // Request |
|
1147 |
+ // MUST include: * channel |
|
1148 |
+ // * version |
|
1149 |
+ // * supportedConnectionTypes |
|
1150 |
+ // MAY include: * minimumVersion |
|
1151 |
+ // * ext |
|
1152 |
+ // * id |
|
1153 |
+ // |
|
1154 |
+ // Success Response Failed Response |
|
1155 |
+ // MUST include: * channel MUST include: * channel |
|
1156 |
+ // * version * successful |
|
1157 |
+ // * supportedConnectionTypes * error |
|
1158 |
+ // * clientId MAY include: * supportedConnectionTypes |
|
1159 |
+ // * successful * advice |
|
1160 |
+ // MAY include: * minimumVersion * version |
|
1161 |
+ // * advice * minimumVersion |
|
1162 |
+ // * ext * ext |
|
1163 |
+ // * id * id |
|
1164 |
+ // * authSuccessful |
|
1165 |
+ handshake: function(callback, context) { |
|
1166 |
+ if (this._advice.reconnect === this.NONE) return; |
|
1167 |
+ if (this._state !== this.UNCONNECTED) return; |
|
1168 |
+ |
|
1169 |
+ this._state = this.CONNECTING; |
|
1170 |
+ var self = this; |
|
1171 |
+ |
|
1172 |
+ this.info('Initiating handshake with ?', Faye.URI.stringify(this._endpoint)); |
|
1173 |
+ this._dispatcher.selectTransport(Faye.MANDATORY_CONNECTION_TYPES); |
|
1174 |
+ |
|
1175 |
+ this._sendMessage({ |
|
1176 |
+ channel: Faye.Channel.HANDSHAKE, |
|
1177 |
+ version: Faye.BAYEUX_VERSION, |
|
1178 |
+ supportedConnectionTypes: this._dispatcher.getConnectionTypes() |
|
1179 |
+ |
|
1180 |
+ }, {}, function(response) { |
|
1181 |
+ |
|
1182 |
+ if (response.successful) { |
|
1183 |
+ this._state = this.CONNECTED; |
|
1184 |
+ this._dispatcher.clientId = response.clientId; |
|
1185 |
+ |
|
1186 |
+ this._dispatcher.selectTransport(response.supportedConnectionTypes); |
|
1187 |
+ |
|
1188 |
+ this.info('Handshake successful: ?', this._dispatcher.clientId); |
|
1189 |
+ |
|
1190 |
+ this.subscribe(this._channels.getKeys(), true); |
|
1191 |
+ if (callback) Faye.Promise.defer(function() { callback.call(context) }); |
|
1192 |
+ |
|
1193 |
+ } else { |
|
1194 |
+ this.info('Handshake unsuccessful'); |
|
1195 |
+ Faye.ENV.setTimeout(function() { self.handshake(callback, context) }, this._dispatcher.retry * 1000); |
|
1196 |
+ this._state = this.UNCONNECTED; |
|
1197 |
+ } |
|
1198 |
+ }, this); |
|
1199 |
+ }, |
|
1200 |
+ |
|
1201 |
+ // Request Response |
|
1202 |
+ // MUST include: * channel MUST include: * channel |
|
1203 |
+ // * clientId * successful |
|
1204 |
+ // * connectionType * clientId |
|
1205 |
+ // MAY include: * ext MAY include: * error |
|
1206 |
+ // * id * advice |
|
1207 |
+ // * ext |
|
1208 |
+ // * id |
|
1209 |
+ // * timestamp |
|
1210 |
+ connect: function(callback, context) { |
|
1211 |
+ if (this._advice.reconnect === this.NONE) return; |
|
1212 |
+ if (this._state === this.DISCONNECTED) return; |
|
1213 |
+ |
|
1214 |
+ if (this._state === this.UNCONNECTED) |
|
1215 |
+ return this.handshake(function() { this.connect(callback, context) }, this); |
|
1216 |
+ |
|
1217 |
+ this.callback(callback, context); |
|
1218 |
+ if (this._state !== this.CONNECTED) return; |
|
1219 |
+ |
|
1220 |
+ this.info('Calling deferred actions for ?', this._dispatcher.clientId); |
|
1221 |
+ this.setDeferredStatus('succeeded'); |
|
1222 |
+ this.setDeferredStatus('unknown'); |
|
1223 |
+ |
|
1224 |
+ if (this._connectRequest) return; |
|
1225 |
+ this._connectRequest = true; |
|
1226 |
+ |
|
1227 |
+ this.info('Initiating connection for ?', this._dispatcher.clientId); |
|
1228 |
+ |
|
1229 |
+ this._sendMessage({ |
|
1230 |
+ channel: Faye.Channel.CONNECT, |
|
1231 |
+ clientId: this._dispatcher.clientId, |
|
1232 |
+ connectionType: this._dispatcher.connectionType |
|
1233 |
+ |
|
1234 |
+ }, {}, this._cycleConnection, this); |
|
1235 |
+ }, |
|
1236 |
+ |
|
1237 |
+ // Request Response |
|
1238 |
+ // MUST include: * channel MUST include: * channel |
|
1239 |
+ // * clientId * successful |
|
1240 |
+ // MAY include: * ext * clientId |
|
1241 |
+ // * id MAY include: * error |
|
1242 |
+ // * ext |
|
1243 |
+ // * id |
|
1244 |
+ disconnect: function() { |
|
1245 |
+ if (this._state !== this.CONNECTED) return; |
|
1246 |
+ this._state = this.DISCONNECTED; |
|
1247 |
+ |
|
1248 |
+ this.info('Disconnecting ?', this._dispatcher.clientId); |
|
1249 |
+ var promise = new Faye.Publication(); |
|
1250 |
+ |
|
1251 |
+ this._sendMessage({ |
|
1252 |
+ channel: Faye.Channel.DISCONNECT, |
|
1253 |
+ clientId: this._dispatcher.clientId |
|
1254 |
+ |
|
1255 |
+ }, {}, function(response) { |
|
1256 |
+ if (response.successful) { |
|
1257 |
+ this._dispatcher.close(); |
|
1258 |
+ promise.setDeferredStatus('succeeded'); |
|
1259 |
+ } else { |
|
1260 |
+ promise.setDeferredStatus('failed', Faye.Error.parse(response.error)); |
|
1261 |
+ } |
|
1262 |
+ }, this); |
|
1263 |
+ |
|
1264 |
+ this.info('Clearing channel listeners for ?', this._dispatcher.clientId); |
|
1265 |
+ this._channels = new Faye.Channel.Set(); |
|
1266 |
+ |
|
1267 |
+ return promise; |
|
1268 |
+ }, |
|
1269 |
+ |
|
1270 |
+ // Request Response |
|
1271 |
+ // MUST include: * channel MUST include: * channel |
|
1272 |
+ // * clientId * successful |
|
1273 |
+ // * subscription * clientId |
|
1274 |
+ // MAY include: * ext * subscription |
|
1275 |
+ // * id MAY include: * error |
|
1276 |
+ // * advice |
|
1277 |
+ // * ext |
|
1278 |
+ // * id |
|
1279 |
+ // * timestamp |
|
1280 |
+ subscribe: function(channel, callback, context) { |
|
1281 |
+ if (channel instanceof Array) |
|
1282 |
+ return Faye.map(channel, function(c) { |
|
1283 |
+ return this.subscribe(c, callback, context); |
|
1284 |
+ }, this); |
|
1285 |
+ |
|
1286 |
+ var subscription = new Faye.Subscription(this, channel, callback, context), |
|
1287 |
+ force = (callback === true), |
|
1288 |
+ hasSubscribe = this._channels.hasSubscription(channel); |
|
1289 |
+ |
|
1290 |
+ if (hasSubscribe && !force) { |
|
1291 |
+ this._channels.subscribe([channel], callback, context); |
|
1292 |
+ subscription.setDeferredStatus('succeeded'); |
|
1293 |
+ return subscription; |
|
1294 |
+ } |
|
1295 |
+ |
|
1296 |
+ this.connect(function() { |
|
1297 |
+ this.info('Client ? attempting to subscribe to ?', this._dispatcher.clientId, channel); |
|
1298 |
+ if (!force) this._channels.subscribe([channel], callback, context); |
|
1299 |
+ |
|
1300 |
+ this._sendMessage({ |
|
1301 |
+ channel: Faye.Channel.SUBSCRIBE, |
|
1302 |
+ clientId: this._dispatcher.clientId, |
|
1303 |
+ subscription: channel |
|
1304 |
+ |
|
1305 |
+ }, {}, function(response) { |
|
1306 |
+ if (!response.successful) { |
|
1307 |
+ subscription.setDeferredStatus('failed', Faye.Error.parse(response.error)); |
|
1308 |
+ return this._channels.unsubscribe(channel, callback, context); |
|
1309 |
+ } |
|
1310 |
+ |
|
1311 |
+ var channels = [].concat(response.subscription); |
|
1312 |
+ this.info('Subscription acknowledged for ? to ?', this._dispatcher.clientId, channels); |
|
1313 |
+ subscription.setDeferredStatus('succeeded'); |
|
1314 |
+ }, this); |
|
1315 |
+ }, this); |
|
1316 |
+ |
|
1317 |
+ return subscription; |
|
1318 |
+ }, |
|
1319 |
+ |
|
1320 |
+ // Request Response |
|
1321 |
+ // MUST include: * channel MUST include: * channel |
|
1322 |
+ // * clientId * successful |
|
1323 |
+ // * subscription * clientId |
|
1324 |
+ // MAY include: * ext * subscription |
|
1325 |
+ // * id MAY include: * error |
|
1326 |
+ // * advice |
|
1327 |
+ // * ext |
|
1328 |
+ // * id |
|
1329 |
+ // * timestamp |
|
1330 |
+ unsubscribe: function(channel, callback, context) { |
|
1331 |
+ if (channel instanceof Array) |
|
1332 |
+ return Faye.map(channel, function(c) { |
|
1333 |
+ return this.unsubscribe(c, callback, context); |
|
1334 |
+ }, this); |
|
1335 |
+ |
|
1336 |
+ var dead = this._channels.unsubscribe(channel, callback, context); |
|
1337 |
+ if (!dead) return; |
|
1338 |
+ |
|
1339 |
+ this.connect(function() { |
|
1340 |
+ this.info('Client ? attempting to unsubscribe from ?', this._dispatcher.clientId, channel); |
|
1341 |
+ |
|
1342 |
+ this._sendMessage({ |
|
1343 |
+ channel: Faye.Channel.UNSUBSCRIBE, |
|
1344 |
+ clientId: this._dispatcher.clientId, |
|
1345 |
+ subscription: channel |
|
1346 |
+ |
|
1347 |
+ }, {}, function(response) { |
|
1348 |
+ if (!response.successful) return; |
|
1349 |
+ |
|
1350 |
+ var channels = [].concat(response.subscription); |
|
1351 |
+ this.info('Unsubscription acknowledged for ? from ?', this._dispatcher.clientId, channels); |
|
1352 |
+ }, this); |
|
1353 |
+ }, this); |
|
1354 |
+ }, |
|
1355 |
+ |
|
1356 |
+ // Request Response |
|
1357 |
+ // MUST include: * channel MUST include: * channel |
|
1358 |
+ // * data * successful |
|
1359 |
+ // MAY include: * clientId MAY include: * id |
|
1360 |
+ // * id * error |
|
1361 |
+ // * ext * ext |
|
1362 |
+ publish: function(channel, data, options) { |
|
1363 |
+ Faye.validateOptions(options || {}, ['attempts', 'deadline']); |
|
1364 |
+ var publication = new Faye.Publication(); |
|
1365 |
+ |
|
1366 |
+ this.connect(function() { |
|
1367 |
+ this.info('Client ? queueing published message to ?: ?', this._dispatcher.clientId, channel, data); |
|
1368 |
+ |
|
1369 |
+ this._sendMessage({ |
|
1370 |
+ channel: channel, |
|
1371 |
+ data: data, |
|
1372 |
+ clientId: this._dispatcher.clientId |
|
1373 |
+ |
|
1374 |
+ }, options, function(response) { |
|
1375 |
+ if (response.successful) |
|
1376 |
+ publication.setDeferredStatus('succeeded'); |
|
1377 |
+ else |
|
1378 |
+ publication.setDeferredStatus('failed', Faye.Error.parse(response.error)); |
|
1379 |
+ }, this); |
|
1380 |
+ }, this); |
|
1381 |
+ |
|
1382 |
+ return publication; |
|
1383 |
+ }, |
|
1384 |
+ |
|
1385 |
+ _sendMessage: function(message, options, callback, context) { |
|
1386 |
+ message.id = this._generateMessageId(); |
|
1387 |
+ |
|
1388 |
+ var timeout = this._advice.timeout |
|
1389 |
+ ? 1.2 * this._advice.timeout / 1000 |
|
1390 |
+ : 1.2 * this._dispatcher.retry; |
|
1391 |
+ |
|
1392 |
+ this.pipeThroughExtensions('outgoing', message, null, function(message) { |
|
1393 |
+ if (!message) return; |
|
1394 |
+ if (callback) this._responseCallbacks[message.id] = [callback, context]; |
|
1395 |
+ this._dispatcher.sendMessage(message, timeout, options || {}); |
|
1396 |
+ }, this); |
|
1397 |
+ }, |
|
1398 |
+ |
|
1399 |
+ _generateMessageId: function() { |
|
1400 |
+ this._messageId += 1; |
|
1401 |
+ if (this._messageId >= Math.pow(2,32)) this._messageId = 0; |
|
1402 |
+ return this._messageId.toString(36); |
|
1403 |
+ }, |
|
1404 |
+ |
|
1405 |
+ _receiveMessage: function(message) { |
|
1406 |
+ var id = message.id, callback; |
|
1407 |
+ |
|
1408 |
+ if (message.successful !== undefined) { |
|
1409 |
+ callback = this._responseCallbacks[id]; |
|
1410 |
+ delete this._responseCallbacks[id]; |
|
1411 |
+ } |
|
1412 |
+ |
|
1413 |
+ this.pipeThroughExtensions('incoming', message, null, function(message) { |
|
1414 |
+ if (!message) return; |
|
1415 |
+ if (message.advice) this._handleAdvice(message.advice); |
|
1416 |
+ this._deliverMessage(message); |
|
1417 |
+ if (callback) callback[0].call(callback[1], message); |
|
1418 |
+ }, this); |
|
1419 |
+ }, |
|
1420 |
+ |
|
1421 |
+ _handleAdvice: function(advice) { |
|
1422 |
+ Faye.extend(this._advice, advice); |
|
1423 |
+ this._dispatcher.timeout = this._advice.timeout / 1000; |
|
1424 |
+ |
|
1425 |
+ if (this._advice.reconnect === this.HANDSHAKE && this._state !== this.DISCONNECTED) { |
|
1426 |
+ this._state = this.UNCONNECTED; |
|
1427 |
+ this._dispatcher.clientId = null; |
|
1428 |
+ this._cycleConnection(); |
|
1429 |
+ } |
|
1430 |
+ }, |
|
1431 |
+ |
|
1432 |
+ _deliverMessage: function(message) { |
|
1433 |
+ if (!message.channel || message.data === undefined) return; |
|
1434 |
+ this.info('Client ? calling listeners for ? with ?', this._dispatcher.clientId, message.channel, message.data); |
|
1435 |
+ this._channels.distributeMessage(message); |
|
1436 |
+ }, |
|
1437 |
+ |
|
1438 |
+ _cycleConnection: function() { |
|
1439 |
+ if (this._connectRequest) { |
|
1440 |
+ this._connectRequest = null; |
|
1441 |
+ this.info('Closed connection for ?', this._dispatcher.clientId); |
|
1442 |
+ } |
|
1443 |
+ var self = this; |
|
1444 |
+ Faye.ENV.setTimeout(function() { self.connect() }, this._advice.interval); |
|
1445 |
+ } |
|
1446 |
+}); |
|
1447 |
+ |
|
1448 |
+Faye.extend(Faye.Client.prototype, Faye.Deferrable); |
|
1449 |
+Faye.extend(Faye.Client.prototype, Faye.Publisher); |
|
1450 |
+Faye.extend(Faye.Client.prototype, Faye.Logging); |
|
1451 |
+Faye.extend(Faye.Client.prototype, Faye.Extensible); |
|
1452 |
+ |
|
1453 |
+Faye.Dispatcher = Faye.Class({ |
|
1454 |
+ MAX_REQUEST_SIZE: 2048, |
|
1455 |
+ DEFAULT_RETRY: 5, |
|
1456 |
+ |
|
1457 |
+ UP: 1, |
|
1458 |
+ DOWN: 2, |
|
1459 |
+ |
|
1460 |
+ initialize: function(client, endpoint, options) { |
|
1461 |
+ this._client = client; |
|
1462 |
+ this.endpoint = Faye.URI.parse(endpoint); |
|
1463 |
+ this._alternates = options.endpoints || {}; |
|
1464 |
+ |
|
1465 |
+ this.cookies = Faye.Cookies && new Faye.Cookies.CookieJar(); |
|
1466 |
+ this._disabled = []; |
|
1467 |
+ this._envelopes = {}; |
|
1468 |
+ this.headers = {}; |
|
1469 |
+ this.retry = options.retry || this.DEFAULT_RETRY; |
|
1470 |
+ this._scheduler = options.scheduler || Faye.Scheduler; |
|
1471 |
+ this._state = 0; |
|
1472 |
+ this.transports = {}; |
|
1473 |
+ this.wsExtensions = []; |
|
1474 |
+ |
|
1475 |
+ this.proxy = options.proxy || {}; |
|
1476 |
+ if (typeof this._proxy === 'string') this._proxy = {origin: this._proxy}; |
|
1477 |
+ |
|
1478 |
+ var exts = options.websocketExtensions; |
|
1479 |
+ if (exts) { |
|
1480 |
+ exts = [].concat(exts); |
|
1481 |
+ for (var i = 0, n = exts.length; i < n; i++) |
|
1482 |
+ this.addWebsocketExtension(exts[i]); |
|
1483 |
+ } |
|
1484 |
+ |
|
1485 |
+ this.tls = options.tls || {}; |
|
1486 |
+ this.tls.ca = this.tls.ca || options.ca; |
|
1487 |
+ |
|
1488 |
+ for (var type in this._alternates) |
|
1489 |
+ this._alternates[type] = Faye.URI.parse(this._alternates[type]); |
|
1490 |
+ |
|
1491 |
+ this.maxRequestSize = this.MAX_REQUEST_SIZE; |
|
1492 |
+ }, |
|
1493 |
+ |
|
1494 |
+ endpointFor: function(connectionType) { |
|
1495 |
+ return this._alternates[connectionType] || this.endpoint; |
|
1496 |
+ }, |
|
1497 |
+ |
|
1498 |
+ addWebsocketExtension: function(extension) { |
|
1499 |
+ this.wsExtensions.push(extension); |
|
1500 |
+ }, |
|
1501 |
+ |
|
1502 |
+ disable: function(feature) { |
|
1503 |
+ this._disabled.push(feature); |
|
1504 |
+ }, |
|
1505 |
+ |
|
1506 |
+ setHeader: function(name, value) { |
|
1507 |
+ this.headers[name] = value; |
|
1508 |
+ }, |
|
1509 |
+ |
|
1510 |
+ close: function() { |
|
1511 |
+ var transport = this._transport; |
|
1512 |
+ delete this._transport; |
|
1513 |
+ if (transport) transport.close(); |
|
1514 |
+ }, |
|
1515 |
+ |
|
1516 |
+ getConnectionTypes: function() { |
|
1517 |
+ return Faye.Transport.getConnectionTypes(); |
|
1518 |
+ }, |
|
1519 |
+ |
|
1520 |
+ selectTransport: function(transportTypes) { |
|
1521 |
+ Faye.Transport.get(this, transportTypes, this._disabled, function(transport) { |
|
1522 |
+ this.debug('Selected ? transport for ?', transport.connectionType, Faye.URI.stringify(transport.endpoint)); |
|
1523 |
+ |
|
1524 |
+ if (transport === this._transport) return; |
|
1525 |
+ if (this._transport) this._transport.close(); |
|
1526 |
+ |
|
1527 |
+ this._transport = transport; |
|
1528 |
+ this.connectionType = transport.connectionType; |
|
1529 |
+ }, this); |
|
1530 |
+ }, |
|
1531 |
+ |
|
1532 |
+ sendMessage: function(message, timeout, options) { |
|
1533 |
+ options = options || {}; |
|
1534 |
+ |
|
1535 |
+ var id = message.id, |
|
1536 |
+ attempts = options.attempts, |
|
1537 |
+ deadline = options.deadline && new Date().getTime() + (options.deadline * 1000), |
|
1538 |
+ envelope = this._envelopes[id], |
|
1539 |
+ scheduler; |
|
1540 |
+ |
|
1541 |
+ if (!envelope) { |
|
1542 |
+ scheduler = new this._scheduler(message, {timeout: timeout, interval: this.retry, attempts: attempts, deadline: deadline}); |
|
1543 |
+ envelope = this._envelopes[id] = {message: message, scheduler: scheduler}; |
|
1544 |
+ } |
|
1545 |
+ |
|
1546 |
+ this._sendEnvelope(envelope); |
|
1547 |
+ }, |
|
1548 |
+ |
|
1549 |
+ _sendEnvelope: function(envelope) { |
|
1550 |
+ if (!this._transport) return; |
|
1551 |
+ if (envelope.request || envelope.timer) return; |
|
1552 |
+ |
|
1553 |
+ var message = envelope.message, |
|
1554 |
+ scheduler = envelope.scheduler, |
|
1555 |
+ self = this; |
|
1556 |
+ |
|
1557 |
+ if (!scheduler.isDeliverable()) { |
|
1558 |
+ scheduler.abort(); |
|
1559 |
+ delete this._envelopes[message.id]; |
|
1560 |
+ return; |
|
1561 |
+ } |
|
1562 |
+ |
|
1563 |
+ envelope.timer = Faye.ENV.setTimeout(function() { |
|
1564 |
+ self.handleError(message); |
|
1565 |
+ }, scheduler.getTimeout() * 1000); |
|
1566 |
+ |
|
1567 |
+ scheduler.send(); |
|
1568 |
+ envelope.request = this._transport.sendMessage(message); |
|
1569 |
+ }, |
|
1570 |
+ |
|
1571 |
+ handleResponse: function(reply) { |
|
1572 |
+ var envelope = this._envelopes[reply.id]; |
|
1573 |
+ |
|
1574 |
+ if (reply.successful !== undefined && envelope) { |
|
1575 |
+ envelope.scheduler.succeed(); |
|
1576 |
+ delete this._envelopes[reply.id]; |
|
1577 |
+ Faye.ENV.clearTimeout(envelope.timer); |
|
1578 |
+ } |
|
1579 |
+ |
|
1580 |
+ this.trigger('message', reply); |
|
1581 |
+ |
|
1582 |
+ if (this._state === this.UP) return; |
|
1583 |
+ this._state = this.UP; |
|
1584 |
+ this._client.trigger('transport:up'); |
|
1585 |
+ }, |
|
1586 |
+ |
|
1587 |
+ handleError: function(message, immediate) { |
|
1588 |
+ var envelope = this._envelopes[message.id], |
|
1589 |
+ request = envelope && envelope.request, |
|
1590 |
+ self = this; |
|
1591 |
+ |
|
1592 |
+ if (!request) return; |
|
1593 |
+ |
|
1594 |
+ request.then(function(req) { |
|
1595 |
+ if (req && req.abort) req.abort(); |
|
1596 |
+ }); |
|
1597 |
+ |
|
1598 |
+ var scheduler = envelope.scheduler; |
|
1599 |
+ scheduler.fail(); |
|
1600 |
+ |
|
1601 |
+ Faye.ENV.clearTimeout(envelope.timer); |
|
1602 |
+ envelope.request = envelope.timer = null; |
|
1603 |
+ |
|
1604 |
+ if (immediate) { |
|
1605 |
+ this._sendEnvelope(envelope); |
|
1606 |
+ } else { |
|
1607 |
+ envelope.timer = Faye.ENV.setTimeout(function() { |
|
1608 |
+ envelope.timer = null; |
|
1609 |
+ self._sendEnvelope(envelope); |
|
1610 |
+ }, scheduler.getInterval() * 1000); |
|
1611 |
+ } |
|
1612 |
+ |
|
1613 |
+ if (this._state === this.DOWN) return; |
|
1614 |
+ this._state = this.DOWN; |
|
1615 |
+ this._client.trigger('transport:down'); |
|
1616 |
+ } |
|
1617 |
+}); |
|
1618 |
+ |
|
1619 |
+Faye.extend(Faye.Dispatcher.prototype, Faye.Publisher); |
|
1620 |
+Faye.extend(Faye.Dispatcher.prototype, Faye.Logging); |
|
1621 |
+ |
|
1622 |
+Faye.Scheduler = function(message, options) { |
|
1623 |
+ this.message = message; |
|
1624 |
+ this.options = options; |
|
1625 |
+ this.attempts = 0; |
|
1626 |
+}; |
|
1627 |
+ |
|
1628 |
+Faye.extend(Faye.Scheduler.prototype, { |
|
1629 |
+ getTimeout: function() { |
|
1630 |
+ return this.options.timeout; |
|
1631 |
+ }, |
|
1632 |
+ |
|
1633 |
+ getInterval: function() { |
|
1634 |
+ return this.options.interval; |
|
1635 |
+ }, |
|
1636 |
+ |
|
1637 |
+ isDeliverable: function() { |
|
1638 |
+ var attempts = this.options.attempts, |
|
1639 |
+ made = this.attempts, |
|
1640 |
+ deadline = this.options.deadline, |
|
1641 |
+ now = new Date().getTime(); |
|
1642 |
+ |
|
1643 |
+ if (attempts !== undefined && made >= attempts) |
|
1644 |
+ return false; |
|
1645 |
+ |
|
1646 |
+ if (deadline !== undefined && now > deadline) |
|
1647 |
+ return false; |
|
1648 |
+ |
|
1649 |
+ return true; |
|
1650 |
+ }, |
|
1651 |
+ |
|
1652 |
+ send: function() { |
|
1653 |
+ this.attempts += 1; |
|
1654 |
+ }, |
|
1655 |
+ |
|
1656 |
+ succeed: function() {}, |
|
1657 |
+ |
|
1658 |
+ fail: function() {}, |
|
1659 |
+ |
|
1660 |
+ abort: function() {} |
|
1661 |
+}); |
|
1662 |
+ |
|
1663 |
+Faye.Transport = Faye.extend(Faye.Class({ |
|
1664 |
+ DEFAULT_PORTS: {'http:': 80, 'https:': 443, 'ws:': 80, 'wss:': 443}, |
|
1665 |
+ SECURE_PROTOCOLS: ['https:', 'wss:'], |
|
1666 |
+ MAX_DELAY: 0, |
|
1667 |
+ |
|
1668 |
+ batching: true, |
|
1669 |
+ |
|
1670 |
+ initialize: function(dispatcher, endpoint) { |
|
1671 |
+ this._dispatcher = dispatcher; |
|
1672 |
+ this.endpoint = endpoint; |
|
1673 |
+ this._outbox = []; |
|
1674 |
+ this._proxy = Faye.extend({}, this._dispatcher.proxy); |
|
1675 |
+ |
|
1676 |
+ if (!this._proxy.origin && Faye.NodeAdapter) { |
|
1677 |
+ this._proxy.origin = Faye.indexOf(this.SECURE_PROTOCOLS, this.endpoint.protocol) >= 0 |
|
1678 |
+ ? (process.env.HTTPS_PROXY || process.env.https_proxy) |
|
1679 |
+ : (process.env.HTTP_PROXY || process.env.http_proxy); |
|
1680 |
+ } |
|
1681 |
+ }, |
|
1682 |
+ |
|
1683 |
+ close: function() {}, |
|
1684 |
+ |
|
1685 |
+ encode: function(messages) { |
|
1686 |
+ return ''; |
|
1687 |
+ }, |
|
1688 |
+ |
|
1689 |
+ sendMessage: function(message) { |
|
1690 |
+ this.debug('Client ? sending message to ?: ?', |
|
1691 |
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), message); |
|
1692 |
+ |
|
1693 |
+ if (!this.batching) return Faye.Promise.fulfilled(this.request([message])); |
|
1694 |
+ |
|
1695 |
+ this._outbox.push(message); |
|
1696 |
+ this._flushLargeBatch(); |
|
1697 |
+ this._promise = this._promise || new Faye.Promise(); |
|
1698 |
+ |
|
1699 |
+ if (message.channel === Faye.Channel.HANDSHAKE) { |
|
1700 |
+ this.addTimeout('publish', 0.01, this._flush, this); |
|
1701 |
+ return this._promise; |
|
1702 |
+ } |
|
1703 |
+ |
|
1704 |
+ if (message.channel === Faye.Channel.CONNECT) |
|
1705 |
+ this._connectMessage = message; |
|
1706 |
+ |
|
1707 |
+ this.addTimeout('publish', this.MAX_DELAY, this._flush, this); |
|
1708 |
+ return this._promise; |
|
1709 |
+ }, |
|
1710 |
+ |
|
1711 |
+ _flush: function() { |
|
1712 |
+ this.removeTimeout('publish'); |
|
1713 |
+ |
|
1714 |
+ if (this._outbox.length > 1 && this._connectMessage) |
|
1715 |
+ this._connectMessage.advice = {timeout: 0}; |
|
1716 |
+ |
|
1717 |
+ Faye.Promise.fulfill(this._promise, this.request(this._outbox)); |
|
1718 |
+ delete this._promise; |
|
1719 |
+ |
|
1720 |
+ this._connectMessage = null; |
|
1721 |
+ this._outbox = []; |
|
1722 |
+ }, |
|
1723 |
+ |
|
1724 |
+ _flushLargeBatch: function() { |
|
1725 |
+ var string = this.encode(this._outbox); |
|
1726 |
+ if (string.length < this._dispatcher.maxRequestSize) return; |
|
1727 |
+ var last = this._outbox.pop(); |
|
1728 |
+ this._flush(); |
|
1729 |
+ if (last) this._outbox.push(last); |
|
1730 |
+ }, |
|
1731 |
+ |
|
1732 |
+ _receive: function(replies) { |
|
1733 |
+ if (!replies) return; |
|
1734 |
+ replies = [].concat(replies); |
|
1735 |
+ |
|
1736 |
+ this.debug('Client ? received from ? via ?: ?', |
|
1737 |
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, replies); |
|
1738 |
+ |
|
1739 |
+ for (var i = 0, n = replies.length; i < n; i++) |
|
1740 |
+ this._dispatcher.handleResponse(replies[i]); |
|
1741 |
+ }, |
|
1742 |
+ |
|
1743 |
+ _handleError: function(messages, immediate) { |
|
1744 |
+ messages = [].concat(messages); |
|
1745 |
+ |
|
1746 |
+ this.debug('Client ? failed to send to ? via ?: ?', |
|
1747 |
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, messages); |
|
1748 |
+ |
|
1749 |
+ for (var i = 0, n = messages.length; i < n; i++) |
|
1750 |
+ this._dispatcher.handleError(messages[i]); |
|
1751 |
+ }, |
|
1752 |
+ |
|
1753 |
+ _getCookies: function() { |
|
1754 |
+ var cookies = this._dispatcher.cookies, |
|
1755 |
+ url = Faye.URI.stringify(this.endpoint); |
|
1756 |
+ |
|
1757 |
+ if (!cookies) return ''; |
|
1758 |
+ |
|
1759 |
+ return Faye.map(cookies.getCookiesSync(url), function(cookie) { |
|
1760 |
+ return cookie.cookieString(); |
|
1761 |
+ }).join('; '); |
|
1762 |
+ }, |
|
1763 |
+ |
|
1764 |
+ _storeCookies: function(setCookie) { |
|
1765 |
+ var cookies = this._dispatcher.cookies, |
|
1766 |
+ url = Faye.URI.stringify(this.endpoint), |
|
1767 |
+ cookie; |
|
1768 |
+ |
|
1769 |
+ if (!setCookie || !cookies) return; |
|
1770 |
+ setCookie = [].concat(setCookie); |
|
1771 |
+ |
|
1772 |
+ for (var i = 0, n = setCookie.length; i < n; i++) { |
|
1773 |
+ cookie = Faye.Cookies.Cookie.parse(setCookie[i]); |
|
1774 |
+ cookies.setCookieSync(cookie, url); |
|
1775 |
+ } |
|
1776 |
+ } |
|
1777 |
+ |
|
1778 |
+}), { |
|
1779 |
+ get: function(dispatcher, allowed, disabled, callback, context) { |
|
1780 |
+ var endpoint = dispatcher.endpoint; |
|
1781 |
+ |
|
1782 |
+ Faye.asyncEach(this._transports, function(pair, resume) { |
|
1783 |
+ var connType = pair[0], klass = pair[1], |
|
1784 |
+ connEndpoint = dispatcher.endpointFor(connType); |
|
1785 |
+ |
|
1786 |
+ if (Faye.indexOf(disabled, connType) >= 0) |
|
1787 |
+ return resume(); |
|
1788 |
+ |
|
1789 |
+ if (Faye.indexOf(allowed, connType) < 0) { |
|
1790 |
+ klass.isUsable(dispatcher, connEndpoint, function() {}); |
|
1791 |
+ return resume(); |
|
1792 |
+ } |
|
1793 |
+ |
|
1794 |
+ klass.isUsable(dispatcher, connEndpoint, function(isUsable) { |
|
1795 |
+ if (!isUsable) return resume(); |
|
1796 |
+ var transport = klass.hasOwnProperty('create') ? klass.create(dispatcher, connEndpoint) : new klass(dispatcher, connEndpoint); |
|
1797 |
+ callback.call(context, transport); |
|
1798 |
+ }); |
|
1799 |
+ }, function() { |
|
1800 |
+ throw new Error('Could not find a usable connection type for ' + Faye.URI.stringify(endpoint)); |
|
1801 |
+ }); |
|
1802 |
+ }, |
|
1803 |
+ |
|
1804 |
+ register: function(type, klass) { |
|
1805 |
+ this._transports.push([type, klass]); |
|
1806 |
+ klass.prototype.connectionType = type; |
|
1807 |
+ }, |
|
1808 |
+ |
|
1809 |
+ getConnectionTypes: function() { |
|
1810 |
+ return Faye.map(this._transports, function(t) { return t[0] }); |
|
1811 |
+ }, |
|
1812 |
+ |
|
1813 |
+ _transports: [] |
|
1814 |
+}); |
|
1815 |
+ |
|
1816 |
+Faye.extend(Faye.Transport.prototype, Faye.Logging); |
|
1817 |
+Faye.extend(Faye.Transport.prototype, Faye.Timeouts); |
|
1818 |
+ |
|
1819 |
+Faye.Event = { |
|
1820 |
+ _registry: [], |
|
1821 |
+ |
|
1822 |
+ on: function(element, eventName, callback, context) { |
|
1823 |
+ var wrapped = function() { callback.call(context) }; |
|
1824 |
+ |
|
1825 |
+ if (element.addEventListener) |
|
1826 |
+ element.addEventListener(eventName, wrapped, false); |
|
1827 |
+ else |
|
1828 |
+ element.attachEvent('on' + eventName, wrapped); |
|
1829 |
+ |
|
1830 |
+ this._registry.push({ |
|
1831 |
+ _element: element, |
|
1832 |
+ _type: eventName, |
|
1833 |
+ _callback: callback, |
|
1834 |
+ _context: context, |
|
1835 |
+ _handler: wrapped |
|
1836 |
+ }); |
|
1837 |
+ }, |
|
1838 |
+ |
|
1839 |
+ detach: function(element, eventName, callback, context) { |
|
1840 |
+ var i = this._registry.length, register; |
|
1841 |
+ while (i--) { |
|
1842 |
+ register = this._registry[i]; |
|
1843 |
+ |
|
1844 |
+ if ((element && element !== register._element) || |
|
1845 |
+ (eventName && eventName !== register._type) || |
|
1846 |
+ (callback && callback !== register._callback) || |
|
1847 |
+ (context && context !== register._context)) |
|
1848 |
+ continue; |
|
1849 |
+ |
|
1850 |
+ if (register._element.removeEventListener) |
|
1851 |
+ register._element.removeEventListener(register._type, register._handler, false); |
|
1852 |
+ else |
|
1853 |
+ register._element.detachEvent('on' + register._type, register._handler); |
|
1854 |
+ |
|
1855 |
+ this._registry.splice(i,1); |
|
1856 |
+ register = null; |
|
1857 |
+ } |
|
1858 |
+ } |
|
1859 |
+}; |
|
1860 |
+ |
|
1861 |
+if (Faye.ENV.onunload !== undefined) Faye.Event.on(Faye.ENV, 'unload', Faye.Event.detach, Faye.Event); |
|
1862 |
+ |
|
1863 |
+/* |
|
1864 |
+ json2.js |
|
1865 |
+ 2013-05-26 |
|
1866 |
+ |
|
1867 |
+ Public Domain. |
|
1868 |
+ |
|
1869 |
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. |
|
1870 |
+ |
|
1871 |
+ See http://www.JSON.org/js.html |
|
1872 |
+ |
|
1873 |
+ |
|
1874 |
+ This code should be minified before deployment. |
|
1875 |
+ See http://javascript.crockford.com/jsmin.html |
|
1876 |
+ |
|
1877 |
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO |
|
1878 |
+ NOT CONTROL. |
|
1879 |
+ |
|
1880 |
+ |
|
1881 |
+ This file creates a global JSON object containing two methods: stringify |
|
1882 |
+ and parse. |
|
1883 |
+ |
|
1884 |
+ JSON.stringify(value, replacer, space) |
|
1885 |
+ value any JavaScript value, usually an object or array. |
|
1886 |
+ |
|
1887 |
+ replacer an optional parameter that determines how object |
|
1888 |
+ values are stringified for objects. It can be a |
|
1889 |
+ function or an array of strings. |
|
1890 |
+ |
|
1891 |
+ space an optional parameter that specifies the indentation |
|
1892 |
+ of nested structures. If it is omitted, the text will |
|
1893 |
+ be packed without extra whitespace. If it is a number, |
|
1894 |
+ it will specify the number of spaces to indent at each |
|
1895 |
+ level. If it is a string (such as '\t' or ' '), |
|
1896 |
+ it contains the characters used to indent at each level. |
|
1897 |
+ |
|
1898 |
+ This method produces a JSON text from a JavaScript value. |
|
1899 |
+ |
|
1900 |
+ When an object value is found, if the object contains a toJSON |
|
1901 |
+ method, its toJSON method will be called and the result will be |
|
1902 |
+ stringified. A toJSON method does not serialize: it returns the |
|
1903 |
+ value represented by the name/value pair that should be serialized, |
|
1904 |
+ or undefined if nothing should be serialized. The toJSON method |
|
1905 |
+ will be passed the key associated with the value, and this will be |
|
1906 |
+ bound to the value |
|
1907 |
+ |
|
1908 |
+ For example, this would serialize Dates as ISO strings. |
|
1909 |
+ |
|
1910 |
+ Date.prototype.toJSON = function (key) { |
|
1911 |
+ function f(n) { |
|
1912 |
+ // Format integers to have at least two digits. |
|
1913 |
+ return n < 10 ? '0' + n : n; |
|
1914 |
+ } |
|
1915 |
+ |
|
1916 |
+ return this.getUTCFullYear() + '-' + |
|
1917 |
+ f(this.getUTCMonth() + 1) + '-' + |
|
1918 |
+ f(this.getUTCDate()) + 'T' + |
|
1919 |
+ f(this.getUTCHours()) + ':' + |
|
1920 |
+ f(this.getUTCMinutes()) + ':' + |
|
1921 |
+ f(this.getUTCSeconds()) + 'Z'; |
|
1922 |
+ }; |
|
1923 |
+ |
|
1924 |
+ You can provide an optional replacer method. It will be passed the |
|
1925 |
+ key and value of each member, with this bound to the containing |
|
1926 |
+ object. The value that is returned from your method will be |
|
1927 |
+ serialized. If your method returns undefined, then the member will |
|
1928 |
+ be excluded from the serialization. |
|
1929 |
+ |
|
1930 |
+ If the replacer parameter is an array of strings, then it will be |
|
1931 |
+ used to select the members to be serialized. It filters the results |
|
1932 |
+ such that only members with keys listed in the replacer array are |
|
1933 |
+ stringified. |
|
1934 |
+ |
|
1935 |
+ Values that do not have JSON representations, such as undefined or |
|
1936 |
+ functions, will not be serialized. Such values in objects will be |
|
1937 |
+ dropped; in arrays they will be replaced with null. You can use |
|
1938 |
+ a replacer function to replace those with JSON values. |
|
1939 |
+ JSON.stringify(undefined) returns undefined. |
|
1940 |
+ |
|
1941 |
+ The optional space parameter produces a stringification of the |
|
1942 |
+ value that is filled with line breaks and indentation to make it |
|
1943 |
+ easier to read. |
|
1944 |
+ |
|
1945 |
+ If the space parameter is a non-empty string, then that string will |
|
1946 |
+ be used for indentation. If the space parameter is a number, then |
|
1947 |
+ the indentation will be that many spaces. |
|
1948 |
+ |
|
1949 |
+ Example: |
|
1950 |
+ |
|
1951 |
+ text = JSON.stringify(['e', {pluribus: 'unum'}]); |
|
1952 |
+ // text is '["e",{"pluribus":"unum"}]' |
|
1953 |
+ |
|
1954 |
+ |
|
1955 |
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); |
|
1956 |
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' |
|
1957 |
+ |
|
1958 |
+ text = JSON.stringify([new Date()], function (key, value) { |
|
1959 |
+ return this[key] instanceof Date ? |
|
1960 |
+ 'Date(' + this[key] + ')' : value; |
|
1961 |
+ }); |
|
1962 |
+ // text is '["Date(---current time---)"]' |
|
1963 |
+ |
|
1964 |
+ |
|
1965 |
+ JSON.parse(text, reviver) |
|
1966 |
+ This method parses a JSON text to produce an object or array. |
|
1967 |
+ It can throw a SyntaxError exception. |
|
1968 |
+ |
|
1969 |
+ The optional reviver parameter is a function that can filter and |
|
1970 |
+ transform the results. It receives each of the keys and values, |
|
1971 |
+ and its return value is used instead of the original value. |
|
1972 |
+ If it returns what it received, then the structure is not modified. |
|
1973 |
+ If it returns undefined then the member is deleted. |
|
1974 |
+ |
|
1975 |
+ Example: |
|
1976 |
+ |
|
1977 |
+ // Parse the text. Values that look like ISO date strings will |
|
1978 |
+ // be converted to Date objects. |
|
1979 |
+ |
|
1980 |
+ myData = JSON.parse(text, function (key, value) { |
|
1981 |
+ var a; |
|
1982 |
+ if (typeof value === 'string') { |
|
1983 |
+ a = |
|
1984 |
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); |
|
1985 |
+ if (a) { |
|
1986 |
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], |
|
1987 |
+ +a[5], +a[6])); |
|
1988 |
+ } |
|
1989 |
+ } |
|
1990 |
+ return value; |
|
1991 |
+ }); |
|
1992 |
+ |
|
1993 |
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { |
|
1994 |
+ var d; |
|
1995 |
+ if (typeof value === 'string' && |
|
1996 |
+ value.slice(0, 5) === 'Date(' && |
|
1997 |
+ value.slice(-1) === ')') { |
|
1998 |
+ d = new Date(value.slice(5, -1)); |
|
1999 |
+ if (d) { |
|
2000 |
+ return d; |
|
2001 |
+ } |
|
2002 |
+ } |
|
2003 |
+ return value; |
|
2004 |
+ }); |
|
2005 |
+ |
|
2006 |
+ |
|
2007 |
+ This is a reference implementation. You are free to copy, modify, or |
|
2008 |
+ redistribute. |
|
2009 |
+*/ |
|
2010 |
+ |
|
2011 |
+/*jslint evil: true, regexp: true */ |
|
2012 |
+ |
|
2013 |
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, |
|
2014 |
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, |
|
2015 |
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, |
|
2016 |
+ lastIndex, length, parse, prototype, push, replace, slice, stringify, |
|
2017 |
+ test, toJSON, toString, valueOf |
|
2018 |
+*/ |
|
2019 |
+ |
|
2020 |
+ |
|
2021 |
+// Create a JSON object only if one does not already exist. We create the |
|
2022 |
+// methods in a closure to avoid creating global variables. |
|
2023 |
+ |
|
2024 |
+if (typeof JSON !== 'object') { |
|
2025 |
+ JSON = {}; |
|
2026 |
+} |
|
2027 |
+ |
|
2028 |
+(function () { |
|
2029 |
+ 'use strict'; |
|
2030 |
+ |
|
2031 |
+ function f(n) { |
|
2032 |
+ // Format integers to have at least two digits. |
|
2033 |
+ return n < 10 ? '0' + n : n; |
|
2034 |
+ } |
|
2035 |
+ |
|
2036 |
+ if (typeof Date.prototype.toJSON !== 'function') { |
|
2037 |
+ |
|
2038 |
+ Date.prototype.toJSON = function () { |
|
2039 |
+ |
|
2040 |
+ return isFinite(this.valueOf()) |
|
2041 |
+ ? this.getUTCFullYear() + '-' + |
|
2042 |
+ f(this.getUTCMonth() + 1) + '-' + |
|
2043 |
+ f(this.getUTCDate()) + 'T' + |
|
2044 |
+ f(this.getUTCHours()) + ':' + |
|
2045 |
+ f(this.getUTCMinutes()) + ':' + |
|
2046 |
+ f(this.getUTCSeconds()) + 'Z' |
|
2047 |
+ : null; |
|
2048 |
+ }; |
|
2049 |
+ |
|
2050 |
+ String.prototype.toJSON = |
|
2051 |
+ Number.prototype.toJSON = |
|
2052 |
+ Boolean.prototype.toJSON = function () { |
|
2053 |
+ return this.valueOf(); |
|
2054 |
+ }; |
|
2055 |
+ } |
|
2056 |
+ |
|
2057 |
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
|
2058 |
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
|
2059 |
+ gap, |
|
2060 |
+ indent, |
|
2061 |
+ meta = { // table of character substitutions |
|
2062 |
+ '\b': '\\b', |
|
2063 |
+ '\t': '\\t', |
|
2064 |
+ '\n': '\\n', |
|
2065 |
+ '\f': '\\f', |
|
2066 |
+ '\r': '\\r', |
|
2067 |
+ '"' : '\\"', |
|
2068 |
+ '\\': '\\\\' |
|
2069 |
+ }, |
|
2070 |
+ rep; |
|
2071 |
+ |
|
2072 |
+ |
|
2073 |
+ function quote(string) { |
|
2074 |
+ |
|
2075 |
+// If the string contains no control characters, no quote characters, and no |
|
2076 |
+// backslash characters, then we can safely slap some quotes around it. |
|
2077 |
+// Otherwise we must also replace the offending characters with safe escape |
|
2078 |
+// sequences. |
|
2079 |
+ |
|
2080 |
+ escapable.lastIndex = 0; |
|
2081 |
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) { |
|
2082 |
+ var c = meta[a]; |
|
2083 |
+ return typeof c === 'string' |
|
2084 |
+ ? c |
|
2085 |
+ : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
|
2086 |
+ }) + '"' : '"' + string + '"'; |
|
2087 |
+ } |
|
2088 |
+ |
|
2089 |
+ |
|
2090 |
+ function str(key, holder) { |
|
2091 |
+ |
|
2092 |
+// Produce a string from holder[key]. |
|
2093 |
+ |
|
2094 |
+ var i, // The loop counter. |
|
2095 |
+ k, // The member key. |
|
2096 |
+ v, // The member value. |
|
2097 |
+ length, |
|
2098 |
+ mind = gap, |
|
2099 |
+ partial, |
|
2100 |
+ value = holder[key]; |
|
2101 |
+ |
|
2102 |
+// If the value has a toJSON method, call it to obtain a replacement value. |
|
2103 |
+ |
|
2104 |
+ if (value && typeof value === 'object' && |
|
2105 |
+ typeof value.toJSON === 'function') { |
|
2106 |
+ value = value.toJSON(key); |
|
2107 |
+ } |
|
2108 |
+ |
|
2109 |
+// If we were called with a replacer function, then call the replacer to |
|
2110 |
+// obtain a replacement value. |
|
2111 |
+ |
|
2112 |
+ if (typeof rep === 'function') { |
|
2113 |
+ value = rep.call(holder, key, value); |
|
2114 |
+ } |
|
2115 |
+ |
|
2116 |
+// What happens next depends on the value's type. |
|
2117 |
+ |
|
2118 |
+ switch (typeof value) { |
|
2119 |
+ case 'string': |
|
2120 |
+ return quote(value); |
|
2121 |
+ |
|
2122 |
+ case 'number': |
|
2123 |
+ |
|
2124 |
+// JSON numbers must be finite. Encode non-finite numbers as null. |
|
2125 |
+ |
|
2126 |
+ return isFinite(value) ? String(value) : 'null'; |
|
2127 |
+ |
|
2128 |
+ case 'boolean': |
|
2129 |
+ case 'null': |
|
2130 |
+ |
|
2131 |
+// If the value is a boolean or null, convert it to a string. Note: |
|
2132 |
+// typeof null does not produce 'null'. The case is included here in |
|
2133 |
+// the remote chance that this gets fixed someday. |
|
2134 |
+ |
|
2135 |
+ return String(value); |
|
2136 |
+ |
|
2137 |
+// If the type is 'object', we might be dealing with an object or an array or |
|
2138 |
+// null. |
|
2139 |
+ |
|
2140 |
+ case 'object': |
|
2141 |
+ |
|
2142 |
+// Due to a specification blunder in ECMAScript, typeof null is 'object', |
|
2143 |
+// so watch out for that case. |
|
2144 |
+ |
|
2145 |
+ if (!value) { |
|
2146 |
+ return 'null'; |
|
2147 |
+ } |
|
2148 |
+ |
|
2149 |
+// Make an array to hold the partial results of stringifying this object value. |
|
2150 |
+ |
|
2151 |
+ gap += indent; |
|
2152 |
+ partial = []; |
|
2153 |
+ |
|
2154 |
+// Is the value an array? |
|
2155 |
+ |
|
2156 |
+ if (Object.prototype.toString.apply(value) === '[object Array]') { |
|
2157 |
+ |
|
2158 |
+// The value is an array. Stringify every element. Use null as a placeholder |
|
2159 |
+// for non-JSON values. |
|
2160 |
+ |
|
2161 |
+ length = value.length; |
|
2162 |
+ for (i = 0; i < length; i += 1) { |
|
2163 |
+ partial[i] = str(i, value) || 'null'; |
|
2164 |
+ } |
|
2165 |
+ |
|
2166 |
+// Join all of the elements together, separated with commas, and wrap them in |
|
2167 |
+// brackets. |
|
2168 |
+ |
|
2169 |
+ v = partial.length === 0 |
|
2170 |
+ ? '[]' |
|
2171 |
+ : gap |
|
2172 |
+ ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' |
|
2173 |
+ : '[' + partial.join(',') + ']'; |
|
2174 |
+ gap = mind; |
|
2175 |
+ return v; |
|
2176 |
+ } |
|
2177 |
+ |
|
2178 |
+// If the replacer is an array, use it to select the members to be stringified. |
|
2179 |
+ |
|
2180 |
+ if (rep && typeof rep === 'object') { |
|
2181 |
+ length = rep.length; |
|
2182 |
+ for (i = 0; i < length; i += 1) { |
|
2183 |
+ if (typeof rep[i] === 'string') { |
|
2184 |
+ k = rep[i]; |
|
2185 |
+ v = str(k, value); |
|
2186 |
+ if (v) { |
|
2187 |
+ partial.push(quote(k) + (gap ? ': ' : ':') + v); |
|
2188 |
+ } |
|
2189 |
+ } |
|
2190 |
+ } |
|
2191 |
+ } else { |
|
2192 |
+ |
|
2193 |
+// Otherwise, iterate through all of the keys in the object. |
|
2194 |
+ |
|
2195 |
+ for (k in value) { |
|
2196 |
+ if (Object.prototype.hasOwnProperty.call(value, k)) { |
|
2197 |
+ v = str(k, value); |
|
2198 |
+ if (v) { |
|
2199 |
+ partial.push(quote(k) + (gap ? ': ' : ':') + v); |
|
2200 |
+ } |
|
2201 |
+ } |
|
2202 |
+ } |
|
2203 |
+ } |
|
2204 |
+ |
|
2205 |
+// Join all of the member texts together, separated with commas, |
|
2206 |
+// and wrap them in braces. |
|
2207 |
+ |
|
2208 |
+ v = partial.length === 0 |
|
2209 |
+ ? '{}' |
|
2210 |
+ : gap |
|
2211 |
+ ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' |
|
2212 |
+ : '{' + partial.join(',') + '}'; |
|
2213 |
+ gap = mind; |
|
2214 |
+ return v; |
|
2215 |
+ } |
|
2216 |
+ } |
|
2217 |
+ |
|
2218 |
+// If the JSON object does not yet have a stringify method, give it one. |
|
2219 |
+ |
|
2220 |
+ Faye.stringify = function (value, replacer, space) { |
|
2221 |
+ |
|
2222 |
+// The stringify method takes a value and an optional replacer, and an optional |
|
2223 |
+// space parameter, and returns a JSON text. The replacer can be a function |
|
2224 |
+// that can replace values, or an array of strings that will select the keys. |
|
2225 |
+// A default replacer method can be provided. Use of the space parameter can |
|
2226 |
+// produce text that is more easily readable. |
|
2227 |
+ |
|
2228 |
+ var i; |
|
2229 |
+ gap = ''; |
|
2230 |
+ indent = ''; |
|
2231 |
+ |
|
2232 |
+// If the space parameter is a number, make an indent string containing that |
|
2233 |
+// many spaces. |
|
2234 |
+ |
|
2235 |
+ if (typeof space === 'number') { |
|
2236 |
+ for (i = 0; i < space; i += 1) { |
|
2237 |
+ indent += ' '; |
|
2238 |
+ } |
|
2239 |
+ |
|
2240 |
+// If the space parameter is a string, it will be used as the indent string. |
|
2241 |
+ |
|
2242 |
+ } else if (typeof space === 'string') { |
|
2243 |
+ indent = space; |
|
2244 |
+ } |
|
2245 |
+ |
|
2246 |
+// If there is a replacer, it must be a function or an array. |
|
2247 |
+// Otherwise, throw an error. |
|
2248 |
+ |
|
2249 |
+ rep = replacer; |
|
2250 |
+ if (replacer && typeof replacer !== 'function' && |
|
2251 |
+ (typeof replacer !== 'object' || |
|
2252 |
+ typeof replacer.length !== 'number')) { |
|
2253 |
+ throw new Error('JSON.stringify'); |
|
2254 |
+ } |
|
2255 |
+ |
|
2256 |
+// Make a fake root object containing our value under the key of ''. |
|
2257 |
+// Return the result of stringifying the value. |
|
2258 |
+ |
|
2259 |
+ return str('', {'': value}); |
|
2260 |
+ }; |
|
2261 |
+ |
|
2262 |
+ if (typeof JSON.stringify !== 'function') { |
|
2263 |
+ JSON.stringify = Faye.stringify; |
|
2264 |
+ } |
|
2265 |
+ |
|
2266 |
+// If the JSON object does not yet have a parse method, give it one. |
|
2267 |
+ |
|
2268 |
+ if (typeof JSON.parse !== 'function') { |
|
2269 |
+ JSON.parse = function (text, reviver) { |
|
2270 |
+ |
|
2271 |
+// The parse method takes a text and an optional reviver function, and returns |
|
2272 |
+// a JavaScript value if the text is a valid JSON text. |
|
2273 |
+ |
|
2274 |
+ var j; |
|
2275 |
+ |
|
2276 |
+ function walk(holder, key) { |
|
2277 |
+ |
|
2278 |
+// The walk method is used to recursively walk the resulting structure so |
|
2279 |
+// that modifications can be made. |
|
2280 |
+ |
|
2281 |
+ var k, v, value = holder[key]; |
|
2282 |
+ if (value && typeof value === 'object') { |
|
2283 |
+ for (k in value) { |
|
2284 |
+ if (Object.prototype.hasOwnProperty.call(value, k)) { |
|
2285 |
+ v = walk(value, k); |
|
2286 |
+ if (v !== undefined) { |
|
2287 |
+ value[k] = v; |
|
2288 |
+ } else { |
|
2289 |
+ delete value[k]; |
|
2290 |
+ } |
|
2291 |
+ } |
|
2292 |
+ } |
|
2293 |
+ } |
|
2294 |
+ return reviver.call(holder, key, value); |
|
2295 |
+ } |
|
2296 |
+ |
|
2297 |
+ |
|
2298 |
+// Parsing happens in four stages. In the first stage, we replace certain |
|
2299 |
+// Unicode characters with escape sequences. JavaScript handles many characters |
|
2300 |
+// incorrectly, either silently deleting them, or treating them as line endings. |
|
2301 |
+ |
|
2302 |
+ text = String(text); |
|
2303 |
+ cx.lastIndex = 0; |
|
2304 |
+ if (cx.test(text)) { |
|
2305 |
+ text = text.replace(cx, function (a) { |
|
2306 |
+ return '\\u' + |
|
2307 |
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
|
2308 |
+ }); |
|
2309 |
+ } |
|
2310 |
+ |
|
2311 |
+// In the second stage, we run the text against regular expressions that look |
|
2312 |
+// for non-JSON patterns. We are especially concerned with '()' and 'new' |
|
2313 |
+// because they can cause invocation, and '=' because it can cause mutation. |
|
2314 |
+// But just to be safe, we want to reject all unexpected forms. |
|
2315 |
+ |
|
2316 |
+// We split the second stage into 4 regexp operations in order to work around |
|
2317 |
+// crippling inefficiencies in IE's and Safari's regexp engines. First we |
|
2318 |
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we |
|
2319 |
+// replace all simple value tokens with ']' characters. Third, we delete all |
|
2320 |
+// open brackets that follow a colon or comma or that begin the text. Finally, |
|
2321 |
+// we look to see that the remaining characters are only whitespace or ']' or |
|
2322 |
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. |
|
2323 |
+ |
|
2324 |
+ if (/^[\],:{}\s]*$/ |
|
2325 |
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') |
|
2326 |
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') |
|
2327 |
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
|
2328 |
+ |
|
2329 |
+// In the third stage we use the eval function to compile the text into a |
|
2330 |
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity |
|
2331 |
+// in JavaScript: it can begin a block or an object literal. We wrap the text |
|
2332 |
+// in parens to eliminate the ambiguity. |
|
2333 |
+ |
|
2334 |
+ j = eval('(' + text + ')'); |
|
2335 |
+ |
|
2336 |
+// In the optional fourth stage, we recursively walk the new structure, passing |
|
2337 |
+// each name/value pair to a reviver function for possible transformation. |
|
2338 |
+ |
|
2339 |
+ return typeof reviver === 'function' |
|
2340 |
+ ? walk({'': j}, '') |
|
2341 |
+ : j; |
|
2342 |
+ } |
|
2343 |
+ |
|
2344 |
+// If the text is not JSON parseable, then a SyntaxError is thrown. |
|
2345 |
+ |
|
2346 |
+ throw new SyntaxError('JSON.parse'); |
|
2347 |
+ }; |
|
2348 |
+ } |
|
2349 |
+}()); |
|
2350 |
+ |
|
2351 |
+Faye.Transport.WebSocket = Faye.extend(Faye.Class(Faye.Transport, { |
|
2352 |
+ UNCONNECTED: 1, |
|
2353 |
+ CONNECTING: 2, |
|
2354 |
+ CONNECTED: 3, |
|
2355 |
+ |
|
2356 |
+ batching: false, |
|
2357 |
+ |
|
2358 |
+ isUsable: function(callback, context) { |
|
2359 |
+ this.callback(function() { callback.call(context, true) }); |
|
2360 |
+ this.errback(function() { callback.call(context, false) }); |
|
2361 |
+ this.connect(); |
|
2362 |
+ }, |
|
2363 |
+ |
|
2364 |
+ request: function(messages) { |
|
2365 |
+ this._pending = this._pending || new Faye.Set(); |
|
2366 |
+ for (var i = 0, n = messages.length; i < n; i++) this._pending.add(messages[i]); |
|
2367 |
+ |
|
2368 |
+ var promise = new Faye.Promise(); |
|
2369 |
+ |
|
2370 |
+ this.callback(function(socket) { |
|
2371 |
+ if (!socket) return; |
|
2372 |
+ socket.send(Faye.toJSON(messages)); |
|
2373 |
+ Faye.Promise.fulfill(promise, socket); |
|
2374 |
+ }, this); |
|
2375 |
+ |
|
2376 |
+ this.connect(); |
|
2377 |
+ |
|
2378 |
+ return { |
|
2379 |
+ abort: function() { promise.then(function(ws) { ws.close() }) } |
|
2380 |
+ }; |
|
2381 |
+ }, |
|
2382 |
+ |
|
2383 |
+ connect: function() { |
|
2384 |
+ if (Faye.Transport.WebSocket._unloaded) return; |
|
2385 |
+ |
|
2386 |
+ this._state = this._state || this.UNCONNECTED; |
|
2387 |
+ if (this._state !== this.UNCONNECTED) return; |
|
2388 |
+ this._state = this.CONNECTING; |
|
2389 |
+ |
|
2390 |
+ var socket = this._createSocket(); |
|
2391 |
+ if (!socket) return this.setDeferredStatus('failed'); |
|
2392 |
+ |
|
2393 |
+ var self = this; |
|
2394 |
+ |
|
2395 |
+ socket.onopen = function() { |
|
2396 |
+ if (socket.headers) self._storeCookies(socket.headers['set-cookie']); |
|
2397 |
+ self._socket = socket; |
|
2398 |
+ self._state = self.CONNECTED; |
|
2399 |
+ self._everConnected = true; |
|
2400 |
+ self._ping(); |
|
2401 |
+ self.setDeferredStatus('succeeded', socket); |
|
2402 |
+ }; |
|
2403 |
+ |
|
2404 |
+ var closed = false; |
|
2405 |
+ socket.onclose = socket.onerror = function() { |
|
2406 |
+ if (closed) return; |
|
2407 |
+ closed = true; |
|
2408 |
+ |
|
2409 |
+ var wasConnected = (self._state === self.CONNECTED); |
|
2410 |
+ socket.onopen = socket.onclose = socket.onerror = socket.onmessage = null; |
|
2411 |
+ |
|
2412 |
+ delete self._socket; |
|
2413 |
+ self._state = self.UNCONNECTED; |
|
2414 |
+ self.removeTimeout('ping'); |
|
2415 |
+ self.setDeferredStatus('unknown'); |
|
2416 |
+ |
|
2417 |
+ var pending = self._pending ? self._pending.toArray() : []; |
|
2418 |
+ delete self._pending; |
|
2419 |
+ |
|
2420 |
+ if (wasConnected) { |
|
2421 |
+ self._handleError(pending, true); |
|
2422 |
+ } else if (self._everConnected) { |
|
2423 |
+ self._handleError(pending); |
|
2424 |
+ } else { |
|
2425 |
+ self.setDeferredStatus('failed'); |
|
2426 |
+ } |
|
2427 |
+ }; |
|
2428 |
+ |
|
2429 |
+ socket.onmessage = function(event) { |
|
2430 |
+ var replies = JSON.parse(event.data); |
|
2431 |
+ if (!replies) return; |
|
2432 |
+ |
|
2433 |
+ replies = [].concat(replies); |
|
2434 |
+ |
|
2435 |
+ for (var i = 0, n = replies.length; i < n; i++) { |
|
2436 |
+ if (replies[i].successful === undefined) continue; |
|
2437 |
+ self._pending.remove(replies[i]); |
|
2438 |
+ } |
|
2439 |
+ self._receive(replies); |
|
2440 |
+ }; |
|
2441 |
+ }, |
|
2442 |
+ |
|
2443 |
+ close: function() { |
|
2444 |
+ if (!this._socket) return; |
|
2445 |
+ this._socket.close(); |
|
2446 |
+ }, |
|
2447 |
+ |
|
2448 |
+ _createSocket: function() { |
|
2449 |
+ var url = Faye.Transport.WebSocket.getSocketUrl(this.endpoint), |
|
2450 |
+ headers = this._dispatcher.headers, |
|
2451 |
+ extensions = this._dispatcher.wsExtensions, |
|
2452 |
+ cookie = this._getCookies(), |
|
2453 |
+ tls = this._dispatcher.tls, |
|
2454 |
+ options = {extensions: extensions, headers: headers, proxy: this._proxy, tls: tls}; |
|
2455 |
+ |
|
2456 |
+ if (cookie !== '') options.headers['Cookie'] = cookie; |
|
2457 |
+ |
|
2458 |
+ if (Faye.WebSocket) return new Faye.WebSocket.Client(url, [], options); |
|
2459 |
+ if (Faye.ENV.MozWebSocket) return new MozWebSocket(url); |
|
2460 |
+ if (Faye.ENV.WebSocket) return new WebSocket(url); |
|
2461 |
+ }, |
|
2462 |
+ |
|
2463 |
+ _ping: function() { |
|
2464 |
+ if (!this._socket) return; |
|
2465 |
+ this._socket.send('[]'); |
|
2466 |
+ this.addTimeout('ping', this._dispatcher.timeout / 2, this._ping, this); |
|
2467 |
+ } |
|
2468 |
+ |
|
2469 |
+}), { |
|
2470 |
+ PROTOCOLS: { |
|
2471 |
+ 'http:': 'ws:', |
|
2472 |
+ 'https:': 'wss:' |
|
2473 |
+ }, |
|
2474 |
+ |
|
2475 |
+ create: function(dispatcher, endpoint) { |
|
2476 |
+ var sockets = dispatcher.transports.websocket = dispatcher.transports.websocket || {}; |
|
2477 |
+ sockets[endpoint.href] = sockets[endpoint.href] || new this(dispatcher, endpoint); |
|
2478 |
+ return sockets[endpoint.href]; |
|
2479 |
+ }, |
|
2480 |
+ |
|
2481 |
+ getSocketUrl: function(endpoint) { |
|
2482 |
+ endpoint = Faye.copyObject(endpoint); |
|
2483 |
+ endpoint.protocol = this.PROTOCOLS[endpoint.protocol]; |
|
2484 |
+ return Faye.URI.stringify(endpoint); |
|
2485 |
+ }, |
|
2486 |
+ |
|
2487 |
+ isUsable: function(dispatcher, endpoint, callback, context) { |
|
2488 |
+ this.create(dispatcher, endpoint).isUsable(callback, context); |
|
2489 |
+ } |
|
2490 |
+}); |
|
2491 |
+ |
|
2492 |
+Faye.extend(Faye.Transport.WebSocket.prototype, Faye.Deferrable); |
|
2493 |
+Faye.Transport.register('websocket', Faye.Transport.WebSocket); |
|
2494 |
+ |
|
2495 |
+if (Faye.Event && Faye.ENV.onbeforeunload !== undefined) |
|
2496 |
+ Faye.Event.on(Faye.ENV, 'beforeunload', function() { |
|
2497 |
+ Faye.Transport.WebSocket._unloaded = true; |
|
2498 |
+ }); |
|
2499 |
+ |
|
2500 |
+Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, { |
|
2501 |
+ initialize: function(dispatcher, endpoint) { |
|
2502 |
+ Faye.Transport.prototype.initialize.call(this, dispatcher, endpoint); |
|
2503 |
+ if (!Faye.ENV.EventSource) return this.setDeferredStatus('failed'); |
|
2504 |
+ |
|
2505 |
+ this._xhr = new Faye.Transport.XHR(dispatcher, endpoint); |
|
2506 |
+ |
|
2507 |
+ endpoint = Faye.copyObject(endpoint); |
|
2508 |
+ endpoint.pathname += '/' + dispatcher.clientId; |
|
2509 |
+ |
|
2510 |
+ var socket = new EventSource(Faye.URI.stringify(endpoint)), |
|
2511 |
+ self = this; |
|
2512 |
+ |
|
2513 |
+ socket.onopen = function() { |
|
2514 |
+ self._everConnected = true; |
|
2515 |
+ self.setDeferredStatus('succeeded'); |
|
2516 |
+ }; |
|
2517 |
+ |
|
2518 |
+ socket.onerror = function() { |
|
2519 |
+ if (self._everConnected) { |
|
2520 |
+ self._handleError([]); |
|
2521 |
+ } else { |
|
2522 |
+ self.setDeferredStatus('failed'); |
|
2523 |
+ socket.close(); |
|
2524 |
+ } |
|
2525 |
+ }; |
|
2526 |
+ |
|
2527 |
+ socket.onmessage = function(event) { |
|
2528 |
+ self._receive(JSON.parse(event.data)); |
|
2529 |
+ }; |
|
2530 |
+ |
|
2531 |
+ this._socket = socket; |
|
2532 |
+ }, |
|
2533 |
+ |
|
2534 |
+ close: function() { |
|
2535 |
+ if (!this._socket) return; |
|
2536 |
+ this._socket.onopen = this._socket.onerror = this._socket.onmessage = null; |
|
2537 |
+ this._socket.close(); |
|
2538 |
+ delete this._socket; |
|
2539 |
+ }, |
|
2540 |
+ |
|
2541 |
+ isUsable: function(callback, context) { |
|
2542 |
+ this.callback(function() { callback.call(context, true) }); |
|
2543 |
+ this.errback(function() { callback.call(context, false) }); |
|
2544 |
+ }, |
|
2545 |
+ |
|
2546 |
+ encode: function(messages) { |
|
2547 |
+ return this._xhr.encode(messages); |
|
2548 |
+ }, |
|
2549 |
+ |
|
2550 |
+ request: function(messages) { |
|
2551 |
+ return this._xhr.request(messages); |
|
2552 |
+ } |
|
2553 |
+ |
|
2554 |
+}), { |
|
2555 |
+ isUsable: function(dispatcher, endpoint, callback, context) { |
|
2556 |
+ var id = dispatcher.clientId; |
|
2557 |
+ if (!id) return callback.call(context, false); |
|
2558 |
+ |
|
2559 |
+ Faye.Transport.XHR.isUsable(dispatcher, endpoint, function(usable) { |
|
2560 |
+ if (!usable) return callback.call(context, false); |
|
2561 |
+ this.create(dispatcher, endpoint).isUsable(callback, context); |
|
2562 |
+ }, this); |
|
2563 |
+ }, |
|
2564 |
+ |
|
2565 |
+ create: function(dispatcher, endpoint) { |
|
2566 |
+ var sockets = dispatcher.transports.eventsource = dispatcher.transports.eventsource || {}, |
|
2567 |
+ id = dispatcher.clientId; |
|
2568 |
+ |
|
2569 |
+ var url = Faye.copyObject(endpoint); |
|
2570 |
+ url.pathname += '/' + (id || ''); |
|
2571 |
+ url = Faye.URI.stringify(url); |
|
2572 |
+ |
|
2573 |
+ sockets[url] = sockets[url] || new this(dispatcher, endpoint); |
|
2574 |
+ return sockets[url]; |
|
2575 |
+ } |
|
2576 |
+}); |
|
2577 |
+ |
|
2578 |
+Faye.extend(Faye.Transport.EventSource.prototype, Faye.Deferrable); |
|
2579 |
+Faye.Transport.register('eventsource', Faye.Transport.EventSource); |
|
2580 |
+ |
|
2581 |
+Faye.Transport.XHR = Faye.extend(Faye.Class(Faye.Transport, { |
|
2582 |
+ encode: function(messages) { |
|
2583 |
+ return Faye.toJSON(messages); |
|
2584 |
+ }, |
|
2585 |
+ |
|
2586 |
+ request: function(messages) { |
|
2587 |
+ var href = this.endpoint.href, |
|
2588 |
+ xhr = Faye.ENV.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest(), |
|
2589 |
+ self = this; |
|
2590 |
+ |
|
2591 |
+ xhr.open('POST', href, true); |
|
2592 |
+ xhr.setRequestHeader('Content-Type', 'application/json'); |
|
2593 |
+ xhr.setRequestHeader('Pragma', 'no-cache'); |
|
2594 |
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); |
|
2595 |
+ |
|
2596 |
+ var headers = this._dispatcher.headers; |
|
2597 |
+ for (var key in headers) { |
|
2598 |
+ if (!headers.hasOwnProperty(key)) continue; |
|
2599 |
+ xhr.setRequestHeader(key, headers[key]); |
|
2600 |
+ } |
|
2601 |
+ |
|
2602 |
+ var abort = function() { xhr.abort() }; |
|
2603 |
+ if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.on(Faye.ENV, 'beforeunload', abort); |
|
2604 |
+ |
|
2605 |
+ xhr.onreadystatechange = function() { |
|
2606 |
+ if (!xhr || xhr.readyState !== 4) return; |
|
2607 |
+ |
|
2608 |
+ var replies = null, |
|
2609 |
+ status = xhr.status, |
|
2610 |
+ text = xhr.responseText, |
|
2611 |
+ successful = (status >= 200 && status < 300) || status === 304 || status === 1223; |
|
2612 |
+ |
|
2613 |
+ if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.detach(Faye.ENV, 'beforeunload', abort); |
|
2614 |
+ xhr.onreadystatechange = function() {}; |
|
2615 |
+ xhr = null; |
|
2616 |
+ |
|
2617 |
+ if (!successful) return self._handleError(messages); |
|
2618 |
+ |
|
2619 |
+ try { |
|
2620 |
+ replies = JSON.parse(text); |
|
2621 |
+ } catch (e) {} |
|
2622 |
+ |
|
2623 |
+ if (replies) |
|
2624 |
+ self._receive(replies); |
|
2625 |
+ else |
|
2626 |
+ self._handleError(messages); |
|
2627 |
+ }; |
|
2628 |
+ |
|
2629 |
+ xhr.send(this.encode(messages)); |
|
2630 |
+ return xhr; |
|
2631 |
+ } |
|
2632 |
+}), { |
|
2633 |
+ isUsable: function(dispatcher, endpoint, callback, context) { |
|
2634 |
+ callback.call(context, Faye.URI.isSameOrigin(endpoint)); |
|
2635 |
+ } |
|
2636 |
+}); |
|
2637 |
+ |
|
2638 |
+Faye.Transport.register('long-polling', Faye.Transport.XHR); |
|
2639 |
+ |
|
2640 |
+Faye.Transport.CORS = Faye.extend(Faye.Class(Faye.Transport, { |
|
2641 |
+ encode: function(messages) { |
|
2642 |
+ return 'message=' + encodeURIComponent(Faye.toJSON(messages)); |
|
2643 |
+ }, |
|
2644 |
+ |
|
2645 |
+ request: function(messages) { |
|
2646 |
+ var xhrClass = Faye.ENV.XDomainRequest ? XDomainRequest : XMLHttpRequest, |
|
2647 |
+ xhr = new xhrClass(), |
|
2648 |
+ headers = this._dispatcher.headers, |
|
2649 |
+ self = this, |
|
2650 |
+ key; |
|
2651 |
+ |
|
2652 |
+ xhr.open('POST', Faye.URI.stringify(this.endpoint), true); |
|
2653 |
+ |
|
2654 |
+ if (xhr.setRequestHeader) { |
|
2655 |
+ xhr.setRequestHeader('Pragma', 'no-cache'); |
|
2656 |
+ for (key in headers) { |
|
2657 |
+ if (!headers.hasOwnProperty(key)) continue; |
|
2658 |
+ xhr.setRequestHeader(key, headers[key]); |
|
2659 |
+ } |
|
2660 |
+ } |
|
2661 |
+ |
|
2662 |
+ var cleanUp = function() { |
|
2663 |
+ if (!xhr) return false; |
|
2664 |
+ xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onprogress = null; |
|
2665 |
+ xhr = null; |
|
2666 |
+ }; |
|
2667 |
+ |
|
2668 |
+ xhr.onload = function() { |
|
2669 |
+ var replies = null; |
|
2670 |
+ try { |
|
2671 |
+ replies = JSON.parse(xhr.responseText); |
|
2672 |
+ } catch (e) {} |
|
2673 |
+ |
|
2674 |
+ cleanUp(); |
|
2675 |
+ |
|
2676 |
+ if (replies) |
|
2677 |
+ self._receive(replies); |
|
2678 |
+ else |
|
2679 |
+ self._handleError(messages); |
|
2680 |
+ }; |
|
2681 |
+ |
|
2682 |
+ xhr.onerror = xhr.ontimeout = function() { |
|
2683 |
+ cleanUp(); |
|
2684 |
+ self._handleError(messages); |
|
2685 |
+ }; |
|
2686 |
+ |
|
2687 |
+ xhr.onprogress = function() {}; |
|
2688 |
+ xhr.send(this.encode(messages)); |
|
2689 |
+ return xhr; |
|
2690 |
+ } |
|
2691 |
+}), { |
|
2692 |
+ isUsable: function(dispatcher, endpoint, callback, context) { |
|
2693 |
+ if (Faye.URI.isSameOrigin(endpoint)) |
|
2694 |
+ return callback.call(context, false); |
|
2695 |
+ |
|
2696 |
+ if (Faye.ENV.XDomainRequest) |
|
2697 |
+ return callback.call(context, endpoint.protocol === Faye.ENV.location.protocol); |
|
2698 |
+ |
|
2699 |
+ if (Faye.ENV.XMLHttpRequest) { |
|
2700 |
+ var xhr = new Faye.ENV.XMLHttpRequest(); |
|
2701 |
+ return callback.call(context, xhr.withCredentials !== undefined); |
|
2702 |
+ } |
|
2703 |
+ return callback.call(context, false); |
|
2704 |
+ } |
|
2705 |
+}); |
|
2706 |
+ |
|
2707 |
+Faye.Transport.register('cross-origin-long-polling', Faye.Transport.CORS); |
|
2708 |
+ |
|
2709 |
+Faye.Transport.JSONP = Faye.extend(Faye.Class(Faye.Transport, { |
|
2710 |
+ encode: function(messages) { |
|
2711 |
+ var url = Faye.copyObject(this.endpoint); |
|
2712 |
+ url.query.message = Faye.toJSON(messages); |
|
2713 |
+ url.query.jsonp = '__jsonp' + Faye.Transport.JSONP._cbCount + '__'; |
|
2714 |
+ return Faye.URI.stringify(url); |
|
2715 |
+ }, |
|
2716 |
+ |
|
2717 |
+ request: function(messages) { |
|
2718 |
+ var head = document.getElementsByTagName('head')[0], |
|
2719 |
+ script = document.createElement('script'), |
|
2720 |
+ callbackName = Faye.Transport.JSONP.getCallbackName(), |
|
2721 |
+ endpoint = Faye.copyObject(this.endpoint), |
|
2722 |
+ self = this; |
|
2723 |
+ |
|
2724 |
+ endpoint.query.message = Faye.toJSON(messages); |
|
2725 |
+ endpoint.query.jsonp = callbackName; |
|
2726 |
+ |
|
2727 |
+ var cleanup = function() { |
|
2728 |
+ if (!Faye.ENV[callbackName]) return false; |
|
2729 |
+ Faye.ENV[callbackName] = undefined; |
|
2730 |
+ try { delete Faye.ENV[callbackName] } catch (e) {} |
|
2731 |
+ script.parentNode.removeChild(script); |
|
2732 |
+ }; |
|
2733 |
+ |
|
2734 |
+ Faye.ENV[callbackName] = function(replies) { |
|
2735 |
+ cleanup(); |
|
2736 |
+ self._receive(replies); |
|
2737 |
+ }; |
|
2738 |
+ |
|
2739 |
+ script.type = 'text/javascript'; |
|
2740 |
+ script.src = Faye.URI.stringify(endpoint); |
|
2741 |
+ head.appendChild(script); |
|
2742 |
+ |
|
2743 |
+ script.onerror = function() { |
|
2744 |
+ cleanup(); |
|
2745 |
+ self._handleError(messages); |
|
2746 |
+ }; |
|
2747 |
+ |
|
2748 |
+ return {abort: cleanup}; |
|
2749 |
+ } |
|
2750 |
+}), { |
|
2751 |
+ _cbCount: 0, |
|
2752 |
+ |
|
2753 |
+ getCallbackName: function() { |
|
2754 |
+ this._cbCount += 1; |
|
2755 |
+ return '__jsonp' + this._cbCount + '__'; |
|
2756 |
+ }, |
|
2757 |
+ |
|
2758 |
+ isUsable: function(dispatcher, endpoint, callback, context) { |
|
2759 |
+ callback.call(context, true); |
|
2760 |
+ } |
|
2761 |
+}); |
|
2762 |
+ |
|
2763 |
+Faye.Transport.register('callback-polling', Faye.Transport.JSONP); |
|
2764 |
+ |
|
2765 |
+})(); |
@@ -1,3 +1,3 @@ |
||
1 |
-<img src="{{activity.data.user.avatar}}"> |
|
2 |
-<h3><b>{{activity.data.user.username}}</b></h3> |
|
3 |
-<div ng-bind-html="activity.data.message"></div> |
|
1 |
+<img src="http://localhost:5000/{{activity.trackable.user.avatar.url}}"> |
|
2 |
+<h3><b>{{activity.trackable.user.username}}</b></h3> |
|
3 |
+<div ng-bind-html="activity.trackable.content"></div> |
@@ -1,7 +1,23 @@ |
||
1 | 1 |
<ion-view view-title="Activity Feed" class="activity-feed"> |
2 | 2 |
<ion-content> |
3 | 3 |
<ion-list> |
4 |
- <ion-item ng-repeat="activity in mission.activities" ng-include="activity.template" ng-class="activityIconClass(activity)" class="item-icon-left activity-feed-item"></ion-item> |
|
4 |
+ <ion-item ng-repeat="activity in mission.activities" ng-include="'templates/activities/'+ activity.trackable_type + '.html'" ng-class="activityIconClass(activity)" class="item-icon-left activity-feed-item"></ion-item> |
|
5 | 5 |
</ion-list> |
6 | 6 |
</ion-content> |
7 |
+ <ion-footer-bar align-title="left" class="bar-stable"> |
|
8 |
+ <form ng-submit="sendMessage()"> |
|
9 |
+ <div class="bar bar-footer item-input-inset"> |
|
10 |
+ <label class="item-input-wrapper"> |
|
11 |
+ |
|
12 |
+ <input type="search" placeholder="" ng-model="message"> |
|
13 |
+ </label> |
|
14 |
+ <button class="button button-outline button-balanced" type="submit" >Send</button> |
|
15 |
+ </div> |
|
16 |
+ |
|
17 |
+ |
|
18 |
+ |
|
19 |
+ </form> |
|
20 |
+ |
|
21 |
+ </ion-footer-bar> |
|
22 |
+ |
|
7 | 23 |
</ion-view> |
@@ -1,11 +1,11 @@ |
||
1 | 1 |
<ion-view view-title="Mission Briefing"> |
2 | 2 |
<ion-content> |
3 |
- <div class="cover-image"><img src="{{mission.cover}}"></div> |
|
3 |
+ <div class="cover-image"><img src="http://127.0.0.1:5000{{mission.cover_img.thumb.url}}"></div> |
|
4 | 4 |
<div class="briefing-header"> |
5 | 5 |
<small>Mission</small> |
6 |
- <span class="mission-status {{mission.status}} pull-right">{{mission.status}}</span> |
|
6 |
+ <span class="mission-status {{status(mission.status)}} pull-right">{{status(mission.status)}}</span> |
|
7 | 7 |
<h1>{{mission.title}}</h1> |
8 | 8 |
</div> |
9 |
- <div class="mission-description-text" ng-bind-html="mission.description"></div> |
|
9 |
+ <div class="mission-description-text" ng-bind-html="mission.briefing"></div> |
|
10 | 10 |
</ion-content> |
11 | 11 |
</ion-view> |