forked from ericelliott/feature-toggle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
feature-toggle-client.js
173 lines (156 loc) · 4.99 KB
/
feature-toggle-client.js
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';
var union = require('mout/array/union'),
contains = require('mout/array/contains'),
EventEmitter = require('events').EventEmitter,
stampit = require('stampit'),
/**
* Grab the url parameters and build a map of
* parameter names and values.
* @return {Object} params object
*/
getParams = function getParams() {
var params = {};
if (window.location.search) {
var parts = window.location.search.slice(1).split('&');
parts.forEach(function (part) {
var pair = part.split('=');
pair[0] = decodeURIComponent(pair[0]);
pair[1] = decodeURIComponent(pair[1]);
params[pair[0]] = (pair[1] !== 'undefined') ?
pair[1] : true;
});
}
return params;
},
/**
* Get a list of feature names from the url
* parameters.
* @return {Array} Features list
*/
getParamFeatures = function getParamFeatures() {
var features = getParams().ft;
return features ? features.split(',') : undefined;
},
/**
* Get a list of deselected features that need
* to be turned off. This list will override
* settings from baseFeatures.
* @return {Array} Features list
*/
getInactiveFeatures = function getInactiveFeatures() {
var features = getParams()['ft-off'];
return features ? features.split(',') : undefined;
},
/**
* Combine the list of base features with
* the features passed in via URL parameters.
* @type {Array} active features
*/
getActiveFeatures =
function getActiveFeatures(baseFeatures,
paramFeatures, inactiveFeatures) {
var features = union(baseFeatures, paramFeatures);
inactiveFeatures = inactiveFeatures || [];
return features.filter(function (feature) {
return inactiveFeatures.indexOf(feature) === -1;
});
},
/**
* Takes an array of features and creates a class for
* each of them on the body tag.
* New features should be hidden in CSS by default
* and revealed only when the feature toggle is set:
*
* .new-feature { display: none; }
* .ft-new-feature .new-feature { display: block; }
*
* @param {Array} features An array of active features.
*/
setFlags = function setFlags(features) {
var featureClasses = features.map(function (feature) {
return 'ft-' + feature;
}).join(' '),
classNames = document.getElementsByTagName('body')[0]
.className.split(' ').filter(function (className) {
return !className.match(/^ft/);
});
document.getElementsByTagName('body')[0].className =
classNames.join(' ') + ' ' + featureClasses;
},
/**
* Take an optional list of features, set the feature
* classes on the body tag, and return the feature
* toggle object.
* @param {Array} baseFeatures List of base features.
* @return {Object} feature object
*/
setFeatures = function setFeatures(baseFeatures) {
var paramFeatures = getParamFeatures(),
inactiveFeatures = getInactiveFeatures(),
activeFeatures = getActiveFeatures(baseFeatures,
paramFeatures, inactiveFeatures),
methods = {
/**
* Check to see if a feature is active.
* @param {String} feature
* @return {Boolean}
*/
active: function active(feature) {
var testFeature = feature && feature.trim &&
feature.trim();
return contains(activeFeatures, testFeature);
},
/**
* Activate a list of features.
* @emits activated
* @param {Array} features
* @return {Object} this (for chaining)
*/
/**
* activated event.
*
* @event activated
* @type {Array} activated features
*/
activate: function activate(features) {
activeFeatures = union(activeFeatures, features);
setFlags(activeFeatures);
this.emit('activated', features);
return this;
},
/**
* Deactivate a list of features.
* @emits deactivated
* @param {Array} features
* @return {Object} this (for chaining)
*/
/**
* deactivated event.
*
* @event deactivated
* @type {Array} deactivated features
*/
deactivate: function deactivate(features) {
activeFeatures =
activeFeatures.filter(function (feature) {
return !contains(features, feature);
});
setFlags(activeFeatures);
this.emit('deactivated', features);
return this;
}
},
// Creates the feature toggle object by
// composing the methods above with an
// event emitter using the Stampit
// prototypal inheritance library.
ft = stampit.compose(
stampit.convertConstructor(EventEmitter),
stampit(methods)
).create();
// Kick things off by setting feature classes
// for the currently active features.
setFlags(activeFeatures);
return ft;
};
module.exports = setFeatures;