Some checks are pending
Tests & Quality Checks / Test on Python 3.11 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.10 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-2 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-2 (push) Waiting to run
Tests & Quality Checks / Build Artifacts (push) Blocked by required conditions
Tests & Quality Checks / Build Artifacts-1 (push) Blocked by required conditions
239 lines
9.7 KiB
JavaScript
239 lines
9.7 KiB
JavaScript
// WebDrop Bridge - Intercept Version
|
|
// Prevents browser drag for ALT+drag, hands off to Qt for file drag
|
|
|
|
(function() {
|
|
try {
|
|
if (window.__webdrop_intercept_injected) return;
|
|
window.__webdrop_intercept_injected = true;
|
|
|
|
// Intercept mode enabled
|
|
var INTERCEPT_ENABLED = true;
|
|
|
|
console.log('%c[WebDrop Intercept] Script loaded - INTERCEPT_ENABLED=' + INTERCEPT_ENABLED, 'background: #2196F3; color: white; font-weight: bold; padding: 4px 8px;');
|
|
|
|
var currentDragUrls = []; // Array to support multiple URLs
|
|
var angularDragHandlers = [];
|
|
var originalAddEventListener = EventTarget.prototype.addEventListener;
|
|
var listenerPatchActive = true;
|
|
var dragHandlerInstalled = false;
|
|
|
|
// Capture Authorization token from XHR requests (only if checkout is enabled)
|
|
window.capturedAuthToken = null;
|
|
if (window.webdropConfig && window.webdropConfig.enableCheckout) {
|
|
console.log('[Intercept] Auth token capture enabled (checkout feature active)');
|
|
var originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
|
|
if (header === 'Authorization' && value.startsWith('Bearer ')) {
|
|
window.capturedAuthToken = value;
|
|
console.log('[Intercept] Captured auth token');
|
|
}
|
|
return originalXHRSetRequestHeader.apply(this, arguments);
|
|
};
|
|
} else {
|
|
console.log('[Intercept] Auth token capture disabled (checkout feature inactive)');
|
|
}
|
|
|
|
// Only patch addEventListener for dragstart events
|
|
// This minimizes impact on other event listeners (mouseover, mouseenter, etc.)
|
|
EventTarget.prototype.addEventListener = function(type, listener, options) {
|
|
if (listenerPatchActive && type === 'dragstart' && listener) {
|
|
// Store Angular's dragstart handler instead of registering it
|
|
console.log('[Intercept] Storing Angular dragstart listener for', this.tagName || this.constructor.name);
|
|
angularDragHandlers.push({
|
|
target: this,
|
|
listener: listener,
|
|
options: options
|
|
});
|
|
return; // Don't actually register it yet
|
|
}
|
|
// All other events (mouseover, mouseenter, mousedown, etc.): use original
|
|
// This is critical to ensure mouseover/hover events work properly
|
|
return originalAddEventListener.call(this, type, listener, options);
|
|
};
|
|
|
|
// ============================================================================
|
|
// PART 2: Intercept DataTransfer.setData to capture URL
|
|
// ============================================================================
|
|
|
|
var originalSetData = DataTransfer.prototype.setData;
|
|
|
|
DataTransfer.prototype.setData = function(format, data) {
|
|
if (format === 'text/plain' || format === 'text/uri-list') {
|
|
// text/uri-list contains newline-separated URLs
|
|
// text/plain may be single URL or multiple newline-separated URLs
|
|
currentDragUrls = data.trim().split('\n').filter(function(url) {
|
|
return url.trim().length > 0;
|
|
}).map(function(url) {
|
|
return url.trim();
|
|
});
|
|
console.log('%c[Intercept] Captured ' + currentDragUrls.length + ' URL(s)', 'color: #4CAF50; font-weight: bold;', currentDragUrls[0].substring(0, 60));
|
|
}
|
|
return originalSetData.call(this, format, data);
|
|
};
|
|
|
|
console.log('[Intercept] DataTransfer.setData patched ✓');
|
|
|
|
// ============================================================================
|
|
// PART 3: Install OUR dragstart handler in capture phase
|
|
// ============================================================================
|
|
|
|
function installDragHandler() {
|
|
console.log('[Intercept] Installing dragstart handler, have', angularDragHandlers.length, 'Angular handlers');
|
|
|
|
if (angularDragHandlers.length === 0) {
|
|
console.warn('[Intercept] No Angular handlers found yet, will retry in 1s...');
|
|
setTimeout(installDragHandler, 1000);
|
|
return;
|
|
}
|
|
|
|
// Only install once, even if called multiple times
|
|
if (dragHandlerInstalled) {
|
|
console.log('[Intercept] Handler already installed, skipping');
|
|
return;
|
|
}
|
|
|
|
dragHandlerInstalled = true;
|
|
|
|
// NOTE: Keep listenerPatchActive = true to catch new Angular handlers registered later
|
|
// This is important for page reloads where Angular might register handlers at different times
|
|
|
|
// Register OUR handler in capture phase
|
|
originalAddEventListener.call(document, 'dragstart', function(e) {
|
|
currentDragUrls = []; // Reset
|
|
|
|
// Call Angular's handlers first to let them set the data
|
|
var handled = 0;
|
|
for (var i = 0; i < angularDragHandlers.length; i++) {
|
|
var h = angularDragHandlers[i];
|
|
if (h.target === document || h.target === e.target ||
|
|
(h.target.contains && h.target.contains(e.target))) {
|
|
try {
|
|
h.listener.call(e.target, e);
|
|
handled++;
|
|
} catch(err) {
|
|
console.error('[Intercept] Error calling Angular handler:', err);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('[Intercept] Called', handled, 'Angular handlers, URLs:', currentDragUrls.length, 'URL(s)', currentDragUrls.length > 0 ? currentDragUrls[0].substring(0, 60) : 'none');
|
|
|
|
// NOW check if we should intercept
|
|
// Intercept any drag with URLs that match our configured mappings
|
|
if (currentDragUrls.length > 0) {
|
|
var shouldIntercept = false;
|
|
|
|
// Check each URL against configured URL mappings
|
|
// Intercept if ANY URL matches
|
|
if (window.webdropConfig && window.webdropConfig.urlMappings) {
|
|
for (var k = 0; k < currentDragUrls.length; k++) {
|
|
var dragUrl = currentDragUrls[k];
|
|
for (var j = 0; j < window.webdropConfig.urlMappings.length; j++) {
|
|
var mapping = window.webdropConfig.urlMappings[j];
|
|
if (dragUrl.toLowerCase().startsWith(mapping.url_prefix.toLowerCase())) {
|
|
shouldIntercept = true;
|
|
console.log('[Intercept] URL #' + (k+1) + ' matches mapping for:', mapping.local_path);
|
|
break;
|
|
}
|
|
}
|
|
if (shouldIntercept) break;
|
|
}
|
|
} else {
|
|
// Fallback: Check for legacy Z: drive pattern if no config available
|
|
for (var k = 0; k < currentDragUrls.length; k++) {
|
|
if (/^z:/i.test(currentDragUrls[k])) {
|
|
shouldIntercept = true;
|
|
console.warn('[Intercept] Using fallback Z: drive pattern (no URL mappings configured)');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shouldIntercept) {
|
|
console.log('%c[Intercept] PREVENTING browser drag, using Qt for ' + currentDragUrls.length + ' file(s)',
|
|
'background: #F44336; color: white; font-weight: bold; padding: 4px 8px;');
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
ensureChannel(function() {
|
|
if (window.bridge && typeof window.bridge.start_file_drag === 'function') {
|
|
console.log('%c[Intercept] → Qt: start_file_drag with ' + currentDragUrls.length + ' file(s)', 'color: #9C27B0; font-weight: bold;');
|
|
// Pass as JSON string to avoid Qt WebChannel array conversion issues
|
|
window.bridge.start_file_drag(JSON.stringify(currentDragUrls));
|
|
} else {
|
|
console.error('[Intercept] bridge.start_file_drag not available!');
|
|
}
|
|
});
|
|
|
|
currentDragUrls = [];
|
|
return false;
|
|
}
|
|
}
|
|
|
|
console.log('[Intercept] Normal drag, allowing browser');
|
|
}, true); // Capture phase
|
|
|
|
console.log('[Intercept] dragstart handler installed ✓');
|
|
}
|
|
|
|
// Wait for Angular to register its listeners, then install our handler
|
|
// Start checking after 3 seconds (give Angular time to load), then retry for up to 30 seconds
|
|
var installRetries = 0;
|
|
var maxRetries = 27; // 3 initial + 27 retries * 1s = 30s total
|
|
|
|
function scheduleInstall() {
|
|
if (dragHandlerInstalled) return; // Already done
|
|
installRetries++;
|
|
console.log('[Intercept] Install attempt', installRetries, '/', maxRetries + 3);
|
|
installDragHandler();
|
|
if (!dragHandlerInstalled && installRetries < maxRetries) {
|
|
setTimeout(scheduleInstall, 1000);
|
|
} else if (!dragHandlerInstalled) {
|
|
console.warn('[Intercept] Gave up waiting for Angular handlers after 30s');
|
|
}
|
|
}
|
|
|
|
setTimeout(scheduleInstall, 3000);
|
|
|
|
// ============================================================================
|
|
// PART 3: QWebChannel connection
|
|
// ============================================================================
|
|
|
|
function ensureChannel(callback) {
|
|
if (window.bridge) {
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
if (window.QWebChannel && window.qt && window.qt.webChannelTransport) {
|
|
new QWebChannel(window.qt.webChannelTransport, function(channel) {
|
|
window.bridge = channel.objects.bridge;
|
|
console.log('[WebDrop Intercept] QWebChannel connected ✓');
|
|
callback();
|
|
});
|
|
} else {
|
|
console.error('[WebDrop Intercept] QWebChannel not available!');
|
|
}
|
|
}
|
|
|
|
// Initialize channel on load
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
ensureChannel(function() {
|
|
console.log('[WebDrop Intercept] Bridge ready ✓');
|
|
});
|
|
});
|
|
} else {
|
|
ensureChannel(function() {
|
|
console.log('[WebDrop Intercept] Bridge ready ✓');
|
|
});
|
|
}
|
|
|
|
console.log('%c[WebDrop Intercept] Ready! URL-mapped drags will use Qt file drag.',
|
|
'background: #4CAF50; color: white; font-weight: bold; padding: 4px 8px;');
|
|
} catch(e) {
|
|
console.error('[WebDrop Intercept] FATAL ERROR in bridge script:', e);
|
|
console.error('[WebDrop Intercept] Stack:', e.stack);
|
|
}
|
|
})();
|