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