-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
173 lines (149 loc) · 4.58 KB
/
index.js
File metadata and controls
173 lines (149 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
'use strict';
/**
* Representation of one single transport policy.
*
* @constructor
* @param {String} name Name of the policy
* @param {TransportLayer} Transport Constructor of a TransportLayer.
* @param {Object} options Options for the transport & strategy instructions.
* @api public
*/
function Policy(name, Transport, options) {
var policy = this;
if ('string' !== typeof name) {
options = Transport;
Transport = name;
name = undefined;
}
if ('function' !== typeof Transport) {
throw new Error('Transport should be a constructor.');
}
policy.name = (name || Transport.prototype.name).toLowerCase();
policy.Transport = Transport;
policy.options = options || {};
policy.id = 0;
}
/**
* Transport selection strategy.
*
* @constructor
* @param {Array} transports Array of transports that should be added.
* @param {Object} options Optional configuration.
* @api public
*/
function Strategy(transports, options) {
var strategy = this;
if (!(strategy instanceof Strategy)) return new Strategy(transports, options);
if (Object.prototype.toString.call(transports) !== '[object Array]') {
options = transports;
transports = [];
}
strategy.transports = []; // List of active transports.
strategy.transport = 0; // Current selected transport id.
strategy.length = 0; // Amount of transports available.
strategy.id = 0; // ID generation pool.
for (var i = 0; i < transports.length; i++) {
strategy.push(transports[i]);
}
}
/**
* Add a new strategy to the internal transports selection.
*
* @param {String} name Name of the transport.
* @param {TransportLayer} Transport Constructor of a TransportLayer.
* @param {Object} options Options for the transport & strategy instructions.
* @returns {Strategy}
* @api public
*/
Strategy.prototype.push = function push(name, Transport, options) {
var strategy = this
, policy;
if (!(name instanceof Policy)) policy = new Policy(name, Transport, options);
else policy = name;
if (!policy.id) policy.id = strategy.id++;
strategy.length = strategy.transports.push(policy);
return strategy;
};
/**
* Select a new policy from the strategy.
*
* Options:
*
* - crossdomain: The transport should work cross domain.
* - not: The transport should not be in the given list.
* - available: The transport should be.
* - readable: The transport should be readable.
* - writable: The transport should be readable.
* - id: The id we should start at.
*
* @param {Object} config Configuration for selecting transports.
* @param {Function} fn Completion callback.
* @returns {Strategy}
* @api public
*/
Strategy.prototype.select = function select(config, fn) {
//
// I: Start with some preliminary filtering to ensure that all get all
// we only have transports that satisfy the given set of requirements.
// Availability doesn't matter yet.
//
var transports = []
, strategy = this
, Transport
, policy
, i = 0;
for (0; i < strategy.transports.length; i++) {
policy = strategy.transports[i];
Transport = policy.Transport;
if (
'crossdomain' in config && config.crossdomain !== Transport.crossdomain
|| 'writable' in config && config.writable !== Transport.writable
|| 'readable' in config && config.readable !== Transport.readable
|| 'not' in config && policy.name in config.not
|| 'id' in config && policy.id < config.id
|| !Transport.supported
) continue;
transports.push(policy);
}
//
// Bail out early if we have nothing to upgrade to any more.
//
if (!transports.length) return fn(), strategy;
//
// II: Now that we have a set transports we can figure out we need to search
// for
//
if ('available' in config) {
for (i = 0; i < transports.length; i++) {
if (config.available === transports[i].Transport.available()) {
strategy.transport = transports[i].id;
return fn(undefined, transports[i]), strategy;
}
}
fn();
} else {
policy = transports.shift();
strategy.transport = policy.id;
policy.Transport.available(function ready() {
fn(undefined, policy);
});
}
return strategy;
};
/**
* Destroy the Strategy instance so all references can be garbage collected.
*
* @returns {Boolean}
* @api public
*/
Strategy.prototype.destroy = function destroy() {
var strategy = this;
if (!strategy.transports) return false;
strategy.transports = strategy.transport = strategy.length = strategy.id = null;
return true;
};
//
// Expose the strategy.
//
Strategy.Policy = Policy;
module.exports = Strategy;