@gxp-dev/tools 2.0.14 → 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,555 +1,499 @@
1
- // Environment defaults loaded from defaults.json (generated by launch script)
2
- let envDefaults = {};
3
-
4
- // Load environment defaults synchronously via XHR (needed before DEFAULT_CONFIG)
5
- try {
6
- const xhr = new XMLHttpRequest();
7
- xhr.open('GET', browser.runtime.getURL('defaults.json'), false);
8
- xhr.send();
9
- if (xhr.status === 200) {
10
- envDefaults = JSON.parse(xhr.responseText);
11
- console.log('[JavaScript Proxy] Loaded environment defaults:', envDefaults);
12
- }
13
- } catch (error) {
14
- console.log('[JavaScript Proxy] No environment defaults found, using hardcoded defaults');
15
- }
16
-
17
- // Build default redirect URL from env defaults or fallback
18
- const defaultRedirectUrl = envDefaults.jsRedirectUrl || "https://localhost:3060/src/Plugin.vue";
19
-
20
- // Configuration defaults (merged with environment defaults)
21
- const DEFAULT_CONFIG = {
22
- enabled: envDefaults.enabled || false,
23
- // Legacy fields for backward compatibility
24
- redirectUrl: defaultRedirectUrl,
25
- urlPattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
26
- useCustomPattern: envDefaults.jsUseCustomPattern || false,
27
- // New rules-based configuration
28
- rules: {
29
- js: {
30
- enabled: true,
31
- pattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
32
- redirectUrl: defaultRedirectUrl,
33
- useCustomPattern: envDefaults.jsUseCustomPattern || false,
34
- },
35
- css: {
36
- enabled: envDefaults.cssRuleEnabled !== false,
37
- pattern:
38
- "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
39
- redirectUrl: envDefaults.cssRedirectUrl || "",
40
- returnBlank: envDefaults.cssReturnBlank !== false,
41
- useCustomPattern: envDefaults.cssUseCustomPattern || false,
42
- },
43
- },
44
- maskingMode: false,
45
- clearCacheOnEnable: envDefaults.clearCacheOnEnable !== false,
46
- disableCacheForRedirects: envDefaults.disableCacheForRedirects !== false,
47
- };
48
-
49
- // Global state
50
- let config = { ...DEFAULT_CONFIG };
51
- let pendingChanges = false;
52
-
53
- // DOM elements
54
- const elements = {
55
- // Status and main toggle
56
- statusMessage: null,
57
- masterToggle: null,
58
- statusInfo: null,
59
-
60
- // JavaScript rule elements
61
- jsRuleContainer: null,
62
- jsRuleToggle: null,
63
- jsRedirectUrl: null,
64
- jsUseCustomPattern: null,
65
- jsPattern: null,
66
- jsPatternGroup: null,
67
-
68
- // CSS rule elements
69
- cssRuleContainer: null,
70
- cssRuleToggle: null,
71
- cssRedirectUrl: null,
72
- cssReturnBlank: null,
73
- cssUseCustomPattern: null,
74
- cssPattern: null,
75
- cssPatternGroup: null,
76
- cssRedirectGroup: null,
77
-
78
- // Advanced settings
79
- advancedHeader: null,
80
- advancedContent: null,
81
- advancedToggle: null,
82
- maskingMode: null,
83
- clearCacheOnEnable: null,
84
- disableCacheForRedirects: null,
85
-
86
- // Action buttons
87
- saveBtn: null,
88
- clearCacheBtn: null,
89
- };
90
-
91
- // Initialize popup
92
- document.addEventListener("DOMContentLoaded", async () => {
93
- try {
94
- initializeElements();
95
- setupEventListeners();
96
- await loadConfiguration();
97
- updateUI();
98
- console.log("[JavaScript Proxy] Popup initialized successfully");
99
- } catch (error) {
100
- console.error("[JavaScript Proxy] Error initializing popup:", error);
101
- showStatus("Error initializing popup: " + error.message, "error");
102
- }
103
- });
104
-
105
- // Initialize DOM elements
106
- function initializeElements() {
107
- // Get all elements and store in global object
108
- for (const key in elements) {
109
- const element = document.getElementById(key);
110
- if (!element) {
111
- throw new Error(`Required element not found: ${key}`);
112
- }
113
- elements[key] = element;
114
- }
115
- }
116
-
117
- // Setup event listeners
118
- function setupEventListeners() {
119
- // Master toggle
120
- elements.masterToggle.addEventListener("change", handleMasterToggle);
121
-
122
- // Rule toggles
123
- elements.jsRuleToggle.addEventListener("change", () =>
124
- handleRuleToggle("js")
125
- );
126
- elements.cssRuleToggle.addEventListener("change", () =>
127
- handleRuleToggle("css")
128
- );
129
-
130
- // JavaScript rule events
131
- elements.jsRedirectUrl.addEventListener("input", () =>
132
- handleFieldChange("js", "redirectUrl")
133
- );
134
- elements.jsUseCustomPattern.addEventListener("change", () =>
135
- handleCustomPatternToggle("js")
136
- );
137
- elements.jsPattern.addEventListener("input", () =>
138
- handleFieldChange("js", "pattern")
139
- );
140
-
141
- // CSS rule events
142
- elements.cssRedirectUrl.addEventListener("input", () =>
143
- handleFieldChange("css", "redirectUrl")
144
- );
145
- elements.cssReturnBlank.addEventListener("change", handleCssBlankToggle);
146
- elements.cssUseCustomPattern.addEventListener("change", () =>
147
- handleCustomPatternToggle("css")
148
- );
149
- elements.cssPattern.addEventListener("input", () =>
150
- handleFieldChange("css", "pattern")
151
- );
152
-
153
- // Advanced settings toggle
154
- elements.advancedHeader.addEventListener("click", toggleAdvancedSettings);
155
-
156
- // Advanced settings checkboxes
157
- elements.maskingMode.addEventListener("change", () =>
158
- handleAdvancedSettingChange("maskingMode")
159
- );
160
- elements.clearCacheOnEnable.addEventListener("change", () =>
161
- handleAdvancedSettingChange("clearCacheOnEnable")
162
- );
163
- elements.disableCacheForRedirects.addEventListener("change", () =>
164
- handleAdvancedSettingChange("disableCacheForRedirects")
165
- );
166
-
167
- // Action buttons
168
- elements.saveBtn.addEventListener("click", saveConfiguration);
169
- elements.clearCacheBtn.addEventListener("click", clearCache);
170
- }
171
-
172
- // Load configuration from storage
173
- async function loadConfiguration() {
1
+ document.addEventListener("DOMContentLoaded", async function () {
2
+ // Get all DOM elements
3
+ const toggleButton = document.getElementById("toggleButton");
4
+ const toggleText = document.getElementById("toggleText");
5
+ const saveBtn = document.getElementById("saveBtn");
6
+ const clearCacheBtn = document.getElementById("clearCacheBtn");
7
+ const statusMessage = document.getElementById("statusMessage");
8
+ const maskingMode = document.getElementById("maskingMode");
9
+ const clearCacheOnEnable = document.getElementById("clearCacheOnEnable");
10
+ const disableCacheForRedirects = document.getElementById("disableCacheForRedirects");
11
+
12
+ // Tab elements
13
+ const tabs = document.querySelectorAll(".tab");
14
+ const tabContents = document.querySelectorAll(".tab-content");
15
+ const jsBadge = document.getElementById("jsBadge");
16
+ const cssBadge = document.getElementById("cssBadge");
17
+
18
+ // Inspector elements
19
+ const inspectorToggle = document.getElementById("inspectorToggle");
20
+ const inspectorText = document.getElementById("inspectorText");
21
+
22
+ // Inspector state
23
+ let inspectorEnabled = false;
24
+
25
+ // JS Rule elements
26
+ const jsRuleToggle = document.getElementById("jsRuleToggle");
27
+ const jsRuleContent = document.getElementById("jsRuleContent");
28
+ const jsRedirectUrl = document.getElementById("jsRedirectUrl");
29
+ const jsUseCustomPattern = document.getElementById("jsUseCustomPattern");
30
+ const jsPatternGroup = document.getElementById("jsPatternGroup");
31
+ const jsPattern = document.getElementById("jsPattern");
32
+
33
+ // CSS Rule elements
34
+ const cssRuleToggle = document.getElementById("cssRuleToggle");
35
+ const cssRuleContent = document.getElementById("cssRuleContent");
36
+ const cssRedirectUrl = document.getElementById("cssRedirectUrl");
37
+ const cssReturnBlank = document.getElementById("cssReturnBlank");
38
+ const cssRedirectGroup = document.getElementById("cssRedirectGroup");
39
+ const cssUseCustomPattern = document.getElementById("cssUseCustomPattern");
40
+ const cssPatternGroup = document.getElementById("cssPatternGroup");
41
+ const cssPattern = document.getElementById("cssPattern");
42
+
43
+ // Load environment-based defaults from defaults.json (generated by launch script)
44
+ let envDefaults = {};
174
45
  try {
175
- const result = await browser.runtime.sendMessage({ action: "getConfig" });
176
- if (result) {
177
- config = { ...DEFAULT_CONFIG, ...result };
178
- // Ensure rules exist and have proper structure
179
- config = migrateConfig(config);
46
+ const response = await fetch(browser.runtime.getURL("defaults.json"));
47
+ if (response.ok) {
48
+ envDefaults = await response.json();
49
+ console.log("[GxP DevTools] Loaded environment defaults:", envDefaults);
180
50
  }
181
- console.log("[JavaScript Proxy] Loaded configuration:", config);
182
51
  } catch (error) {
183
- console.error("[JavaScript Proxy] Error loading configuration:", error);
184
- showStatus("Error loading configuration: " + error.message, "error");
52
+ console.log("[GxP DevTools] No environment defaults found, using hardcoded defaults");
185
53
  }
186
- }
187
54
 
188
- // Migrate legacy configuration to new rules format
189
- function migrateConfig(config) {
190
- // If rules don't exist, create them from legacy config
191
- if (!config.rules) {
192
- config.rules = {
55
+ // Build default redirect URL from env defaults or fallback
56
+ const defaultRedirectUrl = envDefaults.jsRedirectUrl || "https://localhost:3060/src/Plugin.vue";
57
+
58
+ // Default configuration (merged with environment defaults)
59
+ const defaultConfig = {
60
+ enabled: envDefaults.enabled || false,
61
+ redirectUrl: defaultRedirectUrl,
62
+ urlPattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
63
+ useCustomPattern: envDefaults.jsUseCustomPattern || false,
64
+ rules: {
193
65
  js: {
194
66
  enabled: true,
195
- pattern: config.urlPattern || DEFAULT_CONFIG.rules.js.pattern,
196
- redirectUrl: config.redirectUrl || DEFAULT_CONFIG.rules.js.redirectUrl,
197
- useCustomPattern: config.useCustomPattern || false,
67
+ pattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
68
+ redirectUrl: defaultRedirectUrl,
69
+ useCustomPattern: envDefaults.jsUseCustomPattern || false,
198
70
  },
199
71
  css: {
200
- enabled: true,
201
- pattern: DEFAULT_CONFIG.rules.css.pattern,
202
- redirectUrl: "",
203
- returnBlank: true,
204
- useCustomPattern: false,
72
+ enabled: envDefaults.cssRuleEnabled !== false,
73
+ pattern: "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
74
+ redirectUrl: envDefaults.cssRedirectUrl || "",
75
+ returnBlank: envDefaults.cssReturnBlank !== false,
76
+ useCustomPattern: envDefaults.cssUseCustomPattern || false,
205
77
  },
206
- };
207
- } else {
208
- // Ensure all required fields exist
209
- if (!config.rules.js) {
210
- config.rules.js = {
211
- enabled: true,
212
- pattern: config.urlPattern || DEFAULT_CONFIG.rules.js.pattern,
213
- redirectUrl: config.redirectUrl || DEFAULT_CONFIG.rules.js.redirectUrl,
214
- useCustomPattern: config.useCustomPattern || false,
215
- };
78
+ },
79
+ maskingMode: false,
80
+ clearCacheOnEnable: envDefaults.clearCacheOnEnable !== false,
81
+ disableCacheForRedirects: envDefaults.disableCacheForRedirects !== false,
82
+ };
83
+
84
+ // Load current configuration
85
+ let config = {};
86
+ try {
87
+ const result = await browser.runtime.sendMessage({ action: "getConfig" });
88
+ if (!result || Object.keys(result).length === 0) {
89
+ config = defaultConfig;
90
+ console.log("[GxP DevTools] Initialized with environment defaults");
91
+ } else {
92
+ config = { ...defaultConfig, ...result };
93
+ config = migrateConfig(config, defaultConfig);
216
94
  }
217
- if (!config.rules.css) {
218
- config.rules.css = {
219
- enabled: true,
220
- pattern: DEFAULT_CONFIG.rules.css.pattern,
221
- redirectUrl: "",
222
- returnBlank: true,
223
- useCustomPattern: false,
95
+ } catch (error) {
96
+ console.error("[GxP DevTools] Error loading config:", error);
97
+ config = defaultConfig;
98
+ }
99
+
100
+ // Initialize UI
101
+ updateUI();
102
+ setupTabNavigation();
103
+
104
+ function migrateConfig(config, defaults = defaultConfig) {
105
+ if (!config.rules) {
106
+ config.rules = {
107
+ js: {
108
+ enabled: true,
109
+ pattern: config.urlPattern || defaults.rules.js.pattern,
110
+ redirectUrl: config.redirectUrl || defaults.rules.js.redirectUrl,
111
+ useCustomPattern: config.useCustomPattern || defaults.rules.js.useCustomPattern,
112
+ },
113
+ css: {
114
+ enabled: defaults.rules.css.enabled,
115
+ pattern: defaults.rules.css.pattern,
116
+ redirectUrl: defaults.rules.css.redirectUrl,
117
+ returnBlank: defaults.rules.css.returnBlank,
118
+ useCustomPattern: defaults.rules.css.useCustomPattern,
119
+ },
224
120
  };
225
121
  }
122
+ return config;
226
123
  }
227
124
 
228
- return config;
229
- }
230
-
231
- // Update UI based on current configuration
232
- function updateUI() {
233
- // Update master toggle
234
- elements.masterToggle.checked = config.enabled;
235
- updateStatusInfo();
236
-
237
- // Update rule toggles
238
- elements.jsRuleToggle.checked = config.rules.js.enabled;
239
- elements.cssRuleToggle.checked = config.rules.css.enabled;
240
-
241
- // Update JavaScript rule fields
242
- elements.jsRedirectUrl.value = config.rules.js.redirectUrl || "";
243
- elements.jsUseCustomPattern.checked = config.rules.js.useCustomPattern;
244
- elements.jsPattern.value = config.rules.js.pattern || "";
245
-
246
- // Update CSS rule fields
247
- elements.cssRedirectUrl.value = config.rules.css.redirectUrl || "";
248
- elements.cssReturnBlank.checked = config.rules.css.returnBlank;
249
- elements.cssUseCustomPattern.checked = config.rules.css.useCustomPattern;
250
- elements.cssPattern.value = config.rules.css.pattern || "";
251
-
252
- // Update advanced settings
253
- elements.maskingMode.checked = config.maskingMode;
254
- elements.clearCacheOnEnable.checked = config.clearCacheOnEnable;
255
- elements.disableCacheForRedirects.checked = config.disableCacheForRedirects;
256
-
257
- // Update UI visibility and states
258
- updateRuleContainerStates();
259
- updatePatternVisibility("js");
260
- updatePatternVisibility("css");
261
- updateCssRedirectVisibility();
262
- updateSaveButtonState();
263
- }
264
-
265
- // Update status info text
266
- function updateStatusInfo() {
267
- const activeRules = [];
268
- if (config.enabled) {
269
- if (config.rules.js.enabled) activeRules.push("JS redirects");
270
- if (config.rules.css.enabled) {
125
+ function setupTabNavigation() {
126
+ tabs.forEach((tab) => {
127
+ tab.addEventListener("click", () => {
128
+ const tabName = tab.dataset.tab;
129
+
130
+ // Update active tab
131
+ tabs.forEach((t) => t.classList.remove("active"));
132
+ tab.classList.add("active");
133
+
134
+ // Show corresponding content
135
+ tabContents.forEach((content) => {
136
+ content.classList.remove("active");
137
+ if (content.id === `${tabName}Tab`) {
138
+ content.classList.add("active");
139
+ }
140
+ });
141
+ });
142
+ });
143
+ }
144
+
145
+ function updateUI() {
146
+ // Update toggle button
147
+ if (config.enabled) {
148
+ toggleButton.classList.add("enabled");
149
+ toggleText.textContent = "ON";
150
+ } else {
151
+ toggleButton.classList.remove("enabled");
152
+ toggleText.textContent = "OFF";
153
+ }
154
+
155
+ // Update global settings
156
+ maskingMode.checked = config.maskingMode || false;
157
+ clearCacheOnEnable.checked = config.clearCacheOnEnable !== false;
158
+ disableCacheForRedirects.checked = config.disableCacheForRedirects !== false;
159
+
160
+ // Update JS rule
161
+ if (config.rules && config.rules.js) {
162
+ jsRuleToggle.checked = config.rules.js.enabled;
163
+ jsRedirectUrl.value = config.rules.js.redirectUrl || "";
164
+ jsUseCustomPattern.checked = config.rules.js.useCustomPattern || false;
165
+ jsPattern.value = config.rules.js.pattern || "";
166
+
167
+ // Update JS badge
168
+ if (config.rules.js.enabled) {
169
+ jsBadge.textContent = "ON";
170
+ jsBadge.classList.remove("disabled");
171
+ jsBadge.classList.add("enabled");
172
+ jsRuleContent.classList.remove("rule-disabled");
173
+ } else {
174
+ jsBadge.textContent = "OFF";
175
+ jsBadge.classList.remove("enabled");
176
+ jsBadge.classList.add("disabled");
177
+ jsRuleContent.classList.add("rule-disabled");
178
+ }
179
+
180
+ // Toggle custom pattern input visibility
181
+ if (config.rules.js.useCustomPattern) {
182
+ jsPatternGroup.classList.remove("hidden");
183
+ } else {
184
+ jsPatternGroup.classList.add("hidden");
185
+ }
186
+ }
187
+
188
+ // Update CSS rule
189
+ if (config.rules && config.rules.css) {
190
+ cssRuleToggle.checked = config.rules.css.enabled;
191
+ cssRedirectUrl.value = config.rules.css.redirectUrl || "";
192
+ cssReturnBlank.checked = config.rules.css.returnBlank || false;
193
+ cssUseCustomPattern.checked = config.rules.css.useCustomPattern || false;
194
+ cssPattern.value = config.rules.css.pattern || "";
195
+
196
+ // Update CSS badge
197
+ if (config.rules.css.enabled) {
198
+ cssBadge.textContent = "ON";
199
+ cssBadge.classList.remove("disabled");
200
+ cssBadge.classList.add("enabled");
201
+ cssRuleContent.classList.remove("rule-disabled");
202
+ } else {
203
+ cssBadge.textContent = "OFF";
204
+ cssBadge.classList.remove("enabled");
205
+ cssBadge.classList.add("disabled");
206
+ cssRuleContent.classList.add("rule-disabled");
207
+ }
208
+
209
+ // Toggle redirect section based on blank return setting
271
210
  if (config.rules.css.returnBlank) {
272
- activeRules.push("CSS blank returns");
211
+ cssRedirectGroup.classList.add("hidden");
212
+ } else {
213
+ cssRedirectGroup.classList.remove("hidden");
214
+ }
215
+
216
+ // Toggle custom pattern input visibility
217
+ if (config.rules.css.useCustomPattern) {
218
+ cssPatternGroup.classList.remove("hidden");
273
219
  } else {
274
- activeRules.push("CSS redirects");
220
+ cssPatternGroup.classList.add("hidden");
275
221
  }
276
222
  }
277
223
  }
278
224
 
279
- if (config.enabled && activeRules.length > 0) {
280
- elements.statusInfo.textContent = `Extension is active: ${activeRules.join(
281
- ", "
282
- )}`;
283
- } else if (config.enabled) {
284
- elements.statusInfo.textContent =
285
- "Extension is enabled but no rules are active";
286
- } else {
287
- elements.statusInfo.textContent = "Extension is currently disabled";
288
- }
289
- }
290
-
291
- // Update rule container visual states
292
- function updateRuleContainerStates() {
293
- // Disable rule containers when master toggle is off
294
- elements.jsRuleContainer.classList.toggle("disabled", !config.enabled);
295
- elements.cssRuleContainer.classList.toggle("disabled", !config.enabled);
296
- }
297
-
298
- // Update pattern field visibility for a rule
299
- function updatePatternVisibility(ruleType) {
300
- const rule = config.rules[ruleType];
301
- const patternGroup = elements[`${ruleType}PatternGroup`];
302
-
303
- if (rule.useCustomPattern) {
304
- patternGroup.classList.remove("hidden");
305
- } else {
306
- patternGroup.classList.add("hidden");
307
- }
308
- }
309
-
310
- // Update CSS redirect URL visibility based on blank return setting
311
- function updateCssRedirectVisibility() {
312
- if (config.rules.css.returnBlank) {
313
- elements.cssRedirectGroup.classList.add("hidden");
314
- } else {
315
- elements.cssRedirectGroup.classList.remove("hidden");
225
+ function showStatus(message, isSuccess = true) {
226
+ statusMessage.textContent = message;
227
+ statusMessage.className = `status ${isSuccess ? "success" : "error"}`;
228
+
229
+ setTimeout(() => {
230
+ statusMessage.className = "status";
231
+ }, 3000);
316
232
  }
317
- }
318
233
 
319
- // Update save button state
320
- function updateSaveButtonState() {
321
- elements.saveBtn.disabled = !pendingChanges;
322
- elements.saveBtn.textContent = pendingChanges ? "Save Changes" : "No Changes";
323
- }
234
+ function validateRedirectUrl(url) {
235
+ if (!url || url.trim() === "") return null;
324
236
 
325
- // Handle master toggle change
326
- async function handleMasterToggle() {
327
- const enabled = elements.masterToggle.checked;
237
+ try {
238
+ new URL(url);
239
+ return null;
240
+ } catch {
241
+ if (url.includes("://")) return "Invalid URL format";
328
242
 
329
- try {
330
- // Send toggle message to background script
331
- const response = await browser.runtime.sendMessage({
332
- action: "toggleProxy",
333
- enabled: enabled,
334
- });
243
+ try {
244
+ new URL("https://" + url);
245
+ return null;
246
+ } catch {
247
+ return "Invalid URL format";
248
+ }
249
+ }
250
+ }
335
251
 
336
- if (response && response.success) {
337
- config.enabled = enabled;
338
- updateUI();
339
- showStatus(
340
- enabled
341
- ? "Extension enabled successfully"
342
- : "Extension disabled successfully",
343
- "success"
344
- );
345
- } else {
346
- throw new Error(response ? response.error : "Unknown error");
252
+ function validatePattern(pattern) {
253
+ if (!pattern || pattern.trim() === "") return "URL pattern is required";
254
+
255
+ try {
256
+ new RegExp(pattern);
257
+ return null;
258
+ } catch {
259
+ return "Invalid regular expression pattern";
347
260
  }
348
- } catch (error) {
349
- console.error("[JavaScript Proxy] Error toggling proxy:", error);
350
- showStatus("Error toggling extension: " + error.message, "error");
351
- // Revert the toggle
352
- elements.masterToggle.checked = config.enabled;
353
261
  }
354
- }
355
262
 
356
- // Handle rule toggle change
357
- function handleRuleToggle(ruleType) {
358
- const toggle = elements[`${ruleType}RuleToggle`];
359
- config.rules[ruleType].enabled = toggle.checked;
360
- setPendingChanges(true);
361
- updateUI();
362
- }
363
-
364
- // Handle field changes
365
- function handleFieldChange(ruleType, fieldName) {
366
- const element =
367
- elements[
368
- `${ruleType}${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}`
369
- ];
370
- config.rules[ruleType][fieldName] = element.value;
371
- setPendingChanges(true);
372
- updateUI();
373
- }
374
-
375
- // Handle custom pattern toggle
376
- function handleCustomPatternToggle(ruleType) {
377
- const toggle = elements[`${ruleType}UseCustomPattern`];
378
- config.rules[ruleType].useCustomPattern = toggle.checked;
379
- setPendingChanges(true);
380
- updatePatternVisibility(ruleType);
381
- updateSaveButtonState();
382
- }
383
-
384
- // Handle CSS blank return toggle
385
- function handleCssBlankToggle() {
386
- config.rules.css.returnBlank = elements.cssReturnBlank.checked;
387
- setPendingChanges(true);
388
- updateCssRedirectVisibility();
389
- updateUI();
390
- }
391
-
392
- // Handle advanced setting changes
393
- function handleAdvancedSettingChange(settingName) {
394
- config[settingName] = elements[settingName].checked;
395
- setPendingChanges(true);
396
- updateSaveButtonState();
397
- }
398
-
399
- // Toggle advanced settings panel
400
- function toggleAdvancedSettings() {
401
- const isHidden = elements.advancedContent.classList.contains("hidden");
402
-
403
- if (isHidden) {
404
- elements.advancedContent.classList.remove("hidden");
405
- elements.advancedToggle.textContent = "Hide ▲";
406
- } else {
407
- elements.advancedContent.classList.add("hidden");
408
- elements.advancedToggle.textContent = "Show ▼";
263
+ function normalizeUrl(url) {
264
+ if (!url || url.trim() === "") return "";
265
+ if (!url.includes("://")) return "https://" + url;
266
+ return url;
409
267
  }
410
- }
411
-
412
- // Set pending changes state
413
- function setPendingChanges(hasPendingChanges) {
414
- pendingChanges = hasPendingChanges;
415
- updateSaveButtonState();
416
- }
417
-
418
- // Validate configuration
419
- function validateConfig() {
420
- const errors = [];
421
-
422
- // Check JavaScript rule
423
- if (config.rules.js.enabled) {
424
- if (!config.rules.js.redirectUrl) {
425
- errors.push(
426
- "JavaScript redirect URL is required when JS rule is enabled"
427
- );
428
- } else {
429
- try {
430
- new URL(config.rules.js.redirectUrl);
431
- } catch (e) {
432
- errors.push("JavaScript redirect URL is not valid");
433
- }
434
- }
435
268
 
436
- if (config.rules.js.useCustomPattern && !config.rules.js.pattern) {
437
- errors.push(
438
- "JavaScript custom pattern is required when custom pattern is enabled"
439
- );
440
- }
269
+ // Event listeners
270
+ toggleButton.addEventListener("click", async function () {
271
+ config.enabled = !config.enabled;
441
272
 
442
- if (config.rules.js.pattern) {
443
- try {
444
- new RegExp(config.rules.js.pattern);
445
- } catch (e) {
446
- errors.push("JavaScript URL pattern is not a valid regular expression");
273
+ try {
274
+ const response = await browser.runtime.sendMessage({
275
+ action: "toggleProxy",
276
+ enabled: config.enabled,
277
+ });
278
+
279
+ if (response && response.success) {
280
+ updateUI();
281
+ showStatus(config.enabled ? "Proxy enabled" : "Proxy disabled");
282
+ } else {
283
+ throw new Error(response ? response.error : "Unknown error");
447
284
  }
285
+ } catch (error) {
286
+ console.error("[GxP DevTools] Error toggling proxy:", error);
287
+ showStatus("Error toggling proxy", false);
288
+ // Revert the change
289
+ config.enabled = !config.enabled;
448
290
  }
449
- }
291
+ });
292
+
293
+ // Global settings event listeners
294
+ maskingMode.addEventListener("change", function () {
295
+ config.maskingMode = this.checked;
296
+ });
297
+
298
+ clearCacheOnEnable.addEventListener("change", function () {
299
+ config.clearCacheOnEnable = this.checked;
300
+ });
301
+
302
+ disableCacheForRedirects.addEventListener("change", function () {
303
+ config.disableCacheForRedirects = this.checked;
304
+ });
305
+
306
+ // JS rule event listeners
307
+ jsRuleToggle.addEventListener("change", function () {
308
+ if (!config.rules) config.rules = {};
309
+ if (!config.rules.js) config.rules.js = { ...defaultConfig.rules.js };
310
+ config.rules.js.enabled = this.checked;
311
+ updateUI();
312
+ });
450
313
 
451
- // Check CSS rule
452
- if (config.rules.css.enabled) {
453
- if (!config.rules.css.returnBlank && !config.rules.css.redirectUrl) {
454
- errors.push(
455
- "CSS redirect URL is required when CSS rule is enabled and not returning blank"
456
- );
457
- }
314
+ jsUseCustomPattern.addEventListener("change", function () {
315
+ if (!config.rules) config.rules = {};
316
+ if (!config.rules.js) config.rules.js = { ...defaultConfig.rules.js };
317
+ config.rules.js.useCustomPattern = this.checked;
318
+ updateUI();
319
+ });
458
320
 
459
- if (config.rules.css.redirectUrl) {
460
- try {
461
- new URL(config.rules.css.redirectUrl);
462
- } catch (e) {
463
- errors.push("CSS redirect URL is not valid");
321
+ // CSS rule event listeners
322
+ cssRuleToggle.addEventListener("change", function () {
323
+ if (!config.rules) config.rules = {};
324
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
325
+ config.rules.css.enabled = this.checked;
326
+ updateUI();
327
+ });
328
+
329
+ cssReturnBlank.addEventListener("change", function () {
330
+ if (!config.rules) config.rules = {};
331
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
332
+ config.rules.css.returnBlank = this.checked;
333
+ updateUI();
334
+ });
335
+
336
+ cssUseCustomPattern.addEventListener("change", function () {
337
+ if (!config.rules) config.rules = {};
338
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
339
+ config.rules.css.useCustomPattern = this.checked;
340
+ updateUI();
341
+ });
342
+
343
+ clearCacheBtn.addEventListener("click", async function () {
344
+ try {
345
+ this.textContent = "Clearing...";
346
+ this.disabled = true;
347
+
348
+ const response = await browser.runtime.sendMessage({ action: "clearCache" });
349
+
350
+ if (response && response.success) {
351
+ showStatus("Cache cleared successfully");
352
+ } else {
353
+ showStatus("Error clearing cache: " + (response?.error || "Unknown error"), false);
464
354
  }
355
+ } catch (error) {
356
+ console.error("[GxP DevTools] Error clearing cache:", error);
357
+ showStatus("Error clearing cache", false);
358
+ } finally {
359
+ this.textContent = "Clear Cache";
360
+ this.disabled = false;
465
361
  }
362
+ });
363
+
364
+ saveBtn.addEventListener("click", async function () {
365
+ if (!config.rules) config.rules = {};
366
+ if (!config.rules.js) config.rules.js = { ...defaultConfig.rules.js };
367
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
368
+
369
+ // Get values from inputs
370
+ const jsRedirectUrlValue = jsRedirectUrl.value.trim();
371
+ const jsPatternValue = config.rules.js.useCustomPattern
372
+ ? jsPattern.value.trim()
373
+ : defaultConfig.rules.js.pattern;
374
+
375
+ const cssRedirectUrlValue = cssRedirectUrl.value.trim();
376
+ const cssPatternValue = config.rules.css.useCustomPattern
377
+ ? cssPattern.value.trim()
378
+ : defaultConfig.rules.css.pattern;
379
+
380
+ // Validate JS rule if enabled
381
+ if (config.rules.js.enabled) {
382
+ if (!jsRedirectUrlValue) {
383
+ showStatus("JavaScript redirect URL is required when JS rule is enabled", false);
384
+ return;
385
+ }
466
386
 
467
- if (config.rules.css.useCustomPattern && !config.rules.css.pattern) {
468
- errors.push(
469
- "CSS custom pattern is required when custom pattern is enabled"
470
- );
471
- }
387
+ const jsUrlError = validateRedirectUrl(jsRedirectUrlValue);
388
+ if (jsUrlError) {
389
+ showStatus("JavaScript rule: " + jsUrlError, false);
390
+ return;
391
+ }
472
392
 
473
- if (config.rules.css.pattern) {
474
- try {
475
- new RegExp(config.rules.css.pattern);
476
- } catch (e) {
477
- errors.push("CSS URL pattern is not a valid regular expression");
393
+ if (config.rules.js.useCustomPattern) {
394
+ const jsPatternError = validatePattern(jsPatternValue);
395
+ if (jsPatternError) {
396
+ showStatus("JavaScript rule: " + jsPatternError, false);
397
+ return;
398
+ }
478
399
  }
479
400
  }
480
- }
481
401
 
482
- return errors;
483
- }
402
+ // Validate CSS rule if enabled
403
+ if (config.rules.css.enabled) {
404
+ if (!config.rules.css.returnBlank && !cssRedirectUrlValue) {
405
+ showStatus("CSS redirect URL is required when not returning blank", false);
406
+ return;
407
+ }
484
408
 
485
- // Save configuration
486
- async function saveConfiguration() {
487
- if (!pendingChanges) return;
409
+ if (!config.rules.css.returnBlank && cssRedirectUrlValue) {
410
+ const cssUrlError = validateRedirectUrl(cssRedirectUrlValue);
411
+ if (cssUrlError) {
412
+ showStatus("CSS rule: " + cssUrlError, false);
413
+ return;
414
+ }
415
+ }
488
416
 
489
- try {
490
- // Validate configuration
491
- const errors = validateConfig();
492
- if (errors.length > 0) {
493
- showStatus("Validation errors: " + errors.join(", "), "error");
494
- return;
417
+ if (config.rules.css.useCustomPattern) {
418
+ const cssPatternError = validatePattern(cssPatternValue);
419
+ if (cssPatternError) {
420
+ showStatus("CSS rule: " + cssPatternError, false);
421
+ return;
422
+ }
423
+ }
495
424
  }
496
425
 
497
- // Disable save button during save
498
- elements.saveBtn.disabled = true;
499
- elements.saveBtn.textContent = "Saving...";
426
+ // Update config with form values
427
+ config.rules.js.redirectUrl = normalizeUrl(jsRedirectUrlValue);
428
+ config.rules.js.pattern = jsPatternValue;
429
+ config.rules.css.redirectUrl = normalizeUrl(cssRedirectUrlValue);
430
+ config.rules.css.pattern = cssPatternValue;
500
431
 
501
- // Send configuration to background script
502
- const response = await browser.runtime.sendMessage({
503
- action: "updateConfig",
504
- config: config,
505
- });
432
+ try {
433
+ this.disabled = true;
434
+ this.textContent = "Saving...";
435
+
436
+ const response = await browser.runtime.sendMessage({
437
+ action: "updateConfig",
438
+ config: config,
439
+ });
440
+
441
+ if (response && response.success) {
442
+ updateUI();
443
+ showStatus("Configuration saved successfully");
444
+ } else {
445
+ throw new Error(response ? response.error : "Unknown error");
446
+ }
447
+ } catch (error) {
448
+ console.error("[GxP DevTools] Error saving config:", error);
449
+ showStatus("Error saving configuration", false);
450
+ } finally {
451
+ this.disabled = false;
452
+ this.textContent = "Save Configuration";
453
+ }
454
+ });
506
455
 
507
- if (response && response.success) {
508
- setPendingChanges(false);
509
- showStatus("Configuration saved successfully", "success");
456
+ // Component Inspector
457
+ function updateInspectorUI() {
458
+ if (inspectorEnabled) {
459
+ inspectorToggle.classList.add("enabled");
460
+ inspectorText.textContent = "ON";
510
461
  } else {
511
- throw new Error(response ? response.error : "Unknown error");
462
+ inspectorToggle.classList.remove("enabled");
463
+ inspectorText.textContent = "OFF";
512
464
  }
513
- } catch (error) {
514
- console.error("[JavaScript Proxy] Error saving configuration:", error);
515
- showStatus("Error saving configuration: " + error.message, "error");
516
- } finally {
517
- updateSaveButtonState();
518
465
  }
519
- }
520
-
521
- // Clear cache
522
- async function clearCache() {
523
- try {
524
- elements.clearCacheBtn.disabled = true;
525
- elements.clearCacheBtn.textContent = "Clearing...";
526
466
 
527
- const response = await browser.runtime.sendMessage({
528
- action: "clearCache",
529
- });
530
-
531
- if (response && response.success) {
532
- showStatus("Cache cleared successfully", "success");
533
- } else {
534
- throw new Error(response ? response.error : "Unknown error");
467
+ async function getInspectorState() {
468
+ try {
469
+ const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
470
+ if (tab?.id) {
471
+ const response = await browser.tabs.sendMessage(tab.id, { action: "getInspectorState" });
472
+ if (response) {
473
+ inspectorEnabled = response.enabled;
474
+ updateInspectorUI();
475
+ }
476
+ }
477
+ } catch (error) {
478
+ console.log("[GxP DevTools] Could not get inspector state:", error);
535
479
  }
536
- } catch (error) {
537
- console.error("[JavaScript Proxy] Error clearing cache:", error);
538
- showStatus("Error clearing cache: " + error.message, "error");
539
- } finally {
540
- elements.clearCacheBtn.disabled = false;
541
- elements.clearCacheBtn.textContent = "Clear Cache";
542
480
  }
543
- }
544
-
545
- // Show status message
546
- function showStatus(message, type = "success") {
547
- elements.statusMessage.textContent = message;
548
- elements.statusMessage.className = `status-message status-${type}`;
549
- elements.statusMessage.classList.remove("hidden");
550
-
551
- // Auto-hide after 5 seconds
552
- setTimeout(() => {
553
- elements.statusMessage.classList.add("hidden");
554
- }, 5000);
555
- }
481
+
482
+ inspectorToggle.addEventListener("click", async function () {
483
+ try {
484
+ const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
485
+ if (tab?.id) {
486
+ const response = await browser.tabs.sendMessage(tab.id, { action: "toggleInspector" });
487
+ if (response) {
488
+ inspectorEnabled = response.enabled;
489
+ updateInspectorUI();
490
+ showStatus(inspectorEnabled ? "Inspector enabled" : "Inspector disabled");
491
+ }
492
+ }
493
+ } catch (error) {
494
+ showStatus("Could not toggle inspector. Make sure you're on a web page.", false);
495
+ }
496
+ });
497
+
498
+ getInspectorState();
499
+ });