-
Notifications
You must be signed in to change notification settings - Fork 22
/
options.js
283 lines (227 loc) · 8.64 KB
/
options.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
var currentSiteSettings;
/**
* Retrieves the saved site definitions from localstorage
* @return an Array of Objects containing { URL:String, headers:[ {name:String, value:String} ] }
*/
function retrieveSiteSettings() {
var siteSettings = localStorage['corsSites'];
if (!siteSettings) {
return null;
}
return JSON.parse(siteSettings);
}
/**
* Stores the provided settings in the extension's localstorage area
* @param settings an Array of an Array of Objects containing { URL:String, headers:[ {name:String, value:String} ] }
*/
function storeSiteSettings(settings) {
localStorage['corsSites'] = JSON.stringify(settings);
}
/**
* Displays the headers for the given item in the settings table
* @param item an Object containing { URL:String, headers:[ {name:String, value:String} ] }
* @param tableBody jQuery reference to the tbody tag where we will inject the header name/values
*/
function populateHeadersTable(item, tableBody) {
tableBody.empty();
if (!item) return;
if (!item.headers) return;
// creates a click handler to turn on editing of the cell.
// Requires a reference to the header data and a property name to bind to
var clickFunctionGenerator = function (header, prop) {
return function (event) {
var target = $(event.target),
input = $('<input type="text"/>').val(target.text()),
saveBtn = $('<button class="btn">Save</button>'),
saveFunc = function () {
//remove event listeners
saveBtn.unbind();
input.unbind();
//capture the new value
header[prop] = $(input).val();
//return the field to the non-editing state
target.html(header[prop]);
};
saveBtn.bind('click', saveFunc);
input.bind('keydown', function (e) {
if (e.which == 13) saveFunc();
});
target.html(input).append(saveBtn);
input.focus();
}
};
/**
* Creates a <tr> element for injection into the header settings table
* @param header {Object} {name:String, value:String}
* @return a jQuery reference to a <tr> node containing the header display
*/
var generateHeaderTR = function (header) {
var tr = $('<tr></tr>'),
name = $('<td></td>').text(header.name),
value = $('<td></td>').text(header.value);
//bind the click event on each of the fields to make them editable
name.bind('click', clickFunctionGenerator(header, 'name'));
value.bind('click', clickFunctionGenerator(header, 'value'));
tr.append(name);
tr.append(value);
return tr;
};
//iterate the headers and append each <tr>
for (var i = 0, l = item.headers.length; i < l; i++) {
tableBody.append(generateHeaderTR(item.headers[i]));
}
//When clicking the "add header" btn
// add a new empty header setting to the array, and render the new entry
var addHeaderBtn = $('.addHeader');
addHeaderBtn.unbind();
addHeaderBtn.bind('click', function () {
item.headers.push({name:'', value:''});
var tr = generateHeaderTR(item.headers[ item.headers.length - 1 ]);
tableBody.append(tr);
tr.find('td').first().click();
});
}
/**
* Handles the change event from the URL select box
* @param event the jQuery event
*/
function urlSelectionChanged(event) {
var item = currentSiteSettings[ $(event.target).find('option:selected').index() ];
populateHeadersTable(item, $('#headersTable').find('tbody'));
}
function renderSelectOptions() {
//initialize the URL selector
var urlSelect = $('#activeURL');
urlSelect.unbind();
urlSelect.empty();
//populate select with all currently tracked URLs
for (var i = 0, l = currentSiteSettings.length; i < l; i++) {
var item = $('<option></option>').attr('value', i).text(currentSiteSettings[i].URL);
if (i == 0) {
item.attr("selected", "selected");
}
urlSelect.append(item);
}
urlSelect.bind('change', urlSelectionChanged);
//initialize to the first item
urlSelect.change();
return urlSelect;
}
$(document).ready(function () {
//check localStorage for any existing URL settings
currentSiteSettings = retrieveSiteSettings() || [ ];
var urlSelect = renderSelectOptions();
//allow user to add new URLs to manage
var addURLForm = $('#addNewURLForm');
addURLForm.bind('submit', function () {
//create the new, blank rule set
var $input = $('#newURL'),
newRule = {
URL:$input.val(),
headers:[]
};
currentSiteSettings.push(newRule);
//add a new option to the select
urlSelect.append($('<option></option>').attr('value', currentSiteSettings.length - 1).text(newRule.URL));
//make the new option selected
urlSelect.find('option').attr('selected', false).last().attr('selected', true);
//trigger the change handler
urlSelect.change();
//clear the old text
$input.val('');
//add a header right away
$('.addHeader').click();
return false;
});
//hide the alerts
$('.alert').hide();
//check to see if user wants to display intercept count
var displayCountCB = $('#displayCount'),
displayCount = localStorage['displayInterceptCount'];
try {
displayCount = JSON.parse( displayCount );
}catch(e){
displayCount = true;
}
displayCountCB.attr('checked', displayCount ? 'checked' : null);
//enable the save button
var saveBtn = $('#saveAll');
saveBtn.bind('click', storeAndAlert);
//enable deleting all settings
var delAll = $('#deleteAll');
delAll.bind('click', function () {
currentSiteSettings = [];
storeAndAlert();
renderSelectOptions();
});
//enable deleting a single setting
var delSel = $('#deleteSelected');
delSel.bind('click',function(){
var index = urlSelect.find('option:selected').val();
currentSiteSettings.splice(index,1);
storeAndAlert();
renderSelectOptions();
});
//import/export
var exportModal = $('#exportModal');
exportModal.on('show',function(){
var modal = $(this),
ta = modal.find('textarea'),
cg = modal.find('.control-group'),
submit = modal.find('.btn-primary'),
errMsg = modal.find('.help-inline');
ta.text(FormatJSON(currentSiteSettings));
submit.bind('click', function(){
cg.removeClass('error');
errMsg.hide();
require(['validate','dataSchema'],function(validate, schema){
try {
var value = JSON.parse(ta.val());
var validation = validate(value, schema);
if(validation.valid){
currentSiteSettings = value;
modal.modal('hide');
storeAndAlert();
}else{
var msg = "Encountered errors parsing your JSON:<ul>";
for(var i=0,l=validation.errors.length;i<l;i++){
msg += "<li>" + validation.errors[i].property +" "+validation.errors[i].message +"</li>";
}
msg += "</ul>";
errMsg.html(msg);
errMsg.show();
cg.addClass('error');
}
}catch(e){
errMsg.text('Unable to parse the input as JSON: '+e.toString());
errMsg.show();
cg.addClass('error');
}
});
})
}).on('hide',function(){
var modal = $(this),
submit = modal.find('.btn-primary');
submit.unbind();
})
});
/**
* Stores the current settings and reports on success or failure to the UI
*/
function storeAndAlert() {
var successAlert = $('.alert-success'),
errorAlert = $('.alert-error');
try {
storeSiteSettings(currentSiteSettings);
localStorage['displayInterceptCount'] = $('#displayCount').attr('checked') === 'checked';
chrome.extension.sendRequest('update', function(){
renderSelectOptions();
successAlert.show();
successAlert.fadeOut(2500);
});
} catch (e) {
errorAlert.text('An error occurred: ' + e.toString());
errorAlert.show();
errorAlert.fadeOut(5000);
}
}