feat: added copy to clipboard feature
This commit is contained in:
parent
9ecf73e046
commit
7f591382db
30 changed files with 14583 additions and 12 deletions
165
.kiro/specs/copy-to-clipboard-toggle/design.md
Normal file
165
.kiro/specs/copy-to-clipboard-toggle/design.md
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
# Design Document
|
||||
|
||||
## Overview
|
||||
|
||||
The copy to clipboard toggle feature will be implemented as an additional setting in the extension popup, similar to the existing background color selector. The feature will use the modern Clipboard API with appropriate fallbacks for older browsers. The implementation will integrate seamlessly with the existing screenshot capture workflow, adding clipboard functionality without disrupting the current download behavior.
|
||||
|
||||
## Architecture
|
||||
|
||||
The feature will be implemented across three main components:
|
||||
|
||||
1. **Popup UI Enhancement**: Add toggle control and persistence logic
|
||||
2. **Content Script Integration**: Modify screenshot capture to include clipboard operations
|
||||
3. **Storage Management**: Extend existing preference storage system
|
||||
|
||||
The clipboard functionality will be implemented as an optional enhancement to the existing screenshot workflow, ensuring backward compatibility and graceful degradation.
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### Popup Interface (`popup.html` & `popup.js`)
|
||||
|
||||
**New UI Elements:**
|
||||
- Toggle switch control for "Copy to Clipboard" option
|
||||
- Positioned between background selector and start button
|
||||
- Consistent styling with existing controls
|
||||
|
||||
**Storage Integration:**
|
||||
- Extend existing `chrome.storage.sync` usage to include `copyToClipboard` preference
|
||||
- Default value: `true` (enabled by default for better user experience)
|
||||
- Load and save preferences alongside existing background preference
|
||||
|
||||
**Message Passing:**
|
||||
- Include `copyToClipboard` setting in the `startSelector` message to content script
|
||||
- No changes needed to existing message structure, just additional property
|
||||
|
||||
### Content Script Enhancement (`content.js`)
|
||||
|
||||
**ScreenshotSelector Class Modifications:**
|
||||
- Add `copyToClipboard` property to constructor
|
||||
- Modify `init()` method to accept and store clipboard preference
|
||||
- Enhance `captureElement()` method to include clipboard operations
|
||||
|
||||
**Clipboard Implementation:**
|
||||
```javascript
|
||||
async copyCanvasToClipboard(canvas) {
|
||||
try {
|
||||
// Convert canvas to blob
|
||||
const blob = await new Promise(resolve =>
|
||||
canvas.toBlob(resolve, 'image/png')
|
||||
);
|
||||
|
||||
// Use Clipboard API
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({ 'image/png': blob })
|
||||
]);
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Handling:**
|
||||
- Graceful fallback when Clipboard API is unavailable
|
||||
- User-friendly error messages for clipboard failures
|
||||
- Ensure screenshot download continues even if clipboard fails
|
||||
|
||||
### User Feedback System
|
||||
|
||||
**Success States:**
|
||||
- "Screenshot saved and copied to clipboard!" - when both operations succeed
|
||||
- "Screenshot saved! (Clipboard copy failed: [reason])" - when download succeeds but clipboard fails
|
||||
|
||||
**Loading States:**
|
||||
- Update existing loading message to indicate clipboard operation when enabled
|
||||
- "Capturing screenshot and preparing clipboard..." vs "Capturing screenshot..."
|
||||
|
||||
## Data Models
|
||||
|
||||
### Settings Storage Schema
|
||||
```javascript
|
||||
{
|
||||
backgroundPreference: 'black' | 'transparent' | 'white',
|
||||
copyToClipboard: boolean // New addition
|
||||
}
|
||||
```
|
||||
|
||||
### Message Interface Extension
|
||||
```javascript
|
||||
// startSelector message
|
||||
{
|
||||
action: 'startSelector',
|
||||
background: string,
|
||||
copyToClipboard: boolean // New addition
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Clipboard API Availability
|
||||
- Check for `navigator.clipboard` and `ClipboardItem` support
|
||||
- Provide clear error messages when API is unavailable
|
||||
- Continue with download-only functionality as fallback
|
||||
|
||||
### Permission Handling
|
||||
- Clipboard write operations may require user gesture
|
||||
- Handle permission denied scenarios gracefully
|
||||
- Provide user guidance for enabling clipboard permissions if needed
|
||||
|
||||
### Browser Compatibility
|
||||
- Primary support: Chrome/Edge 76+, Firefox 87+, Safari 13.1+
|
||||
- Fallback behavior for older browsers: disable clipboard feature with notification
|
||||
- No impact on core screenshot functionality
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Testing Approach
|
||||
- Mock Clipboard API for testing clipboard operations
|
||||
- Test preference storage and retrieval
|
||||
- Verify message passing between popup and content script
|
||||
- Test error handling scenarios
|
||||
|
||||
### Integration Testing
|
||||
- Test complete workflow: toggle setting → capture screenshot → verify clipboard content
|
||||
- Test with different background settings and clipboard combinations
|
||||
- Verify graceful degradation when Clipboard API unavailable
|
||||
|
||||
### Browser Compatibility Testing
|
||||
- Test across Chrome, Firefox, Safari, Edge
|
||||
- Verify behavior on older browser versions
|
||||
- Test permission scenarios and user gesture requirements
|
||||
|
||||
### User Experience Testing
|
||||
- Verify toggle state persistence across browser sessions
|
||||
- Test visual feedback for success/error states
|
||||
- Confirm tooltip and help text clarity
|
||||
|
||||
## Implementation Considerations
|
||||
|
||||
### Performance Impact
|
||||
- Clipboard operations are asynchronous and won't block screenshot download
|
||||
- Canvas-to-blob conversion adds minimal overhead
|
||||
- No impact on screenshot capture performance
|
||||
|
||||
### Security Considerations
|
||||
- Clipboard API requires secure context (HTTPS)
|
||||
- User gesture requirement for clipboard write operations
|
||||
- No sensitive data exposure through clipboard operations
|
||||
|
||||
### Accessibility
|
||||
- Toggle control will include proper ARIA labels
|
||||
- Keyboard navigation support for toggle control
|
||||
- Screen reader compatible success/error messages
|
||||
|
||||
## Browser API Dependencies
|
||||
|
||||
### Required APIs
|
||||
- `navigator.clipboard.write()` - Modern clipboard API
|
||||
- `ClipboardItem` constructor - For image data handling
|
||||
- `canvas.toBlob()` - Convert canvas to blob format
|
||||
|
||||
### Fallback Strategy
|
||||
- Detect API availability before attempting clipboard operations
|
||||
- Provide clear user feedback when clipboard features unavailable
|
||||
- Maintain full functionality for screenshot download regardless of clipboard support
|
||||
51
.kiro/specs/copy-to-clipboard-toggle/requirements.md
Normal file
51
.kiro/specs/copy-to-clipboard-toggle/requirements.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
This feature adds a copy to clipboard toggle option to the Full Screenshot Selector extension, allowing users to choose whether captured screenshots should be automatically copied to the clipboard in addition to being downloaded as files. This provides users with more flexibility in how they handle their screenshots, enabling quick pasting into other applications without needing to locate and open the downloaded file.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1
|
||||
|
||||
**User Story:** As a user of the screenshot extension, I want to toggle whether screenshots are copied to clipboard, so that I can quickly paste them into other applications without downloading files.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the popup is opened THEN the system SHALL display a toggle control for "Copy to Clipboard"
|
||||
2. WHEN the user toggles the copy to clipboard option THEN the system SHALL save this preference to browser storage
|
||||
3. WHEN the extension is reopened THEN the system SHALL restore the previously saved copy to clipboard preference
|
||||
4. WHEN a screenshot is captured AND copy to clipboard is enabled THEN the system SHALL copy the image data to the clipboard
|
||||
5. WHEN a screenshot is captured AND copy to clipboard is enabled THEN the system SHALL still download the file as before (both actions occur)
|
||||
|
||||
### Requirement 2
|
||||
|
||||
**User Story:** As a user, I want the copy to clipboard feature to work reliably across different browsers, so that I can depend on this functionality regardless of my browser choice.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN copy to clipboard is enabled THEN the system SHALL use the Clipboard API if available
|
||||
2. IF the Clipboard API is not available THEN the system SHALL provide appropriate fallback behavior
|
||||
3. WHEN clipboard copying fails THEN the system SHALL display an error message to the user
|
||||
4. WHEN clipboard copying succeeds THEN the system SHALL display a success confirmation
|
||||
|
||||
### Requirement 3
|
||||
|
||||
**User Story:** As a user, I want clear visual feedback about the copy to clipboard feature, so that I understand when it's enabled and when clipboard operations succeed or fail.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the copy to clipboard toggle is enabled THEN the system SHALL display visual indication of the enabled state
|
||||
2. WHEN a screenshot is captured with clipboard copying enabled THEN the system SHALL show a success message indicating both download and clipboard copy
|
||||
3. WHEN clipboard copying fails THEN the system SHALL show an error message while still confirming the download succeeded
|
||||
4. WHEN hovering over the copy to clipboard toggle THEN the system SHALL display helpful tooltip text
|
||||
|
||||
### Requirement 4
|
||||
|
||||
**User Story:** As a user, I want the copy to clipboard feature to handle different image formats appropriately, so that the clipboard content works well with various applications.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN copying to clipboard THEN the system SHALL copy the image in PNG format
|
||||
2. WHEN copying to clipboard THEN the system SHALL preserve the same image quality as the downloaded file
|
||||
3. WHEN copying to clipboard THEN the system SHALL handle transparency settings according to the background preference
|
||||
64
.kiro/specs/copy-to-clipboard-toggle/tasks.md
Normal file
64
.kiro/specs/copy-to-clipboard-toggle/tasks.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Implementation Plan
|
||||
|
||||
- [x] 1. Add copy to clipboard toggle UI to popup
|
||||
- Add HTML toggle control between background selector and start button in popup.html
|
||||
- Style the toggle control to match existing UI design patterns
|
||||
- Include proper ARIA labels and accessibility attributes for the toggle
|
||||
- _Requirements: 1.1, 3.1, 3.4_
|
||||
|
||||
- [x] 2. Implement clipboard preference storage in popup
|
||||
- Extend existing storage logic in popup.js to handle copyToClipboard preference
|
||||
- Set default value to true for better user experience
|
||||
- Load saved clipboard preference on popup initialization
|
||||
- Save clipboard preference changes to chrome.storage.sync
|
||||
- _Requirements: 1.2, 1.3_
|
||||
|
||||
- [x] 3. Update message passing to include clipboard setting
|
||||
- Modify startSelector message in popup.js to include copyToClipboard property
|
||||
- Update content script message handler to receive and store clipboard preference
|
||||
- Ensure backward compatibility with existing message structure
|
||||
- _Requirements: 1.1, 1.4_
|
||||
|
||||
- [x] 4. Implement clipboard API functionality in content script
|
||||
- Add copyCanvasToClipboard method to ScreenshotSelector class
|
||||
- Implement canvas to blob conversion for PNG format
|
||||
- Use navigator.clipboard.write() with ClipboardItem for image data
|
||||
- Handle async clipboard operations with proper error catching
|
||||
- _Requirements: 2.1, 4.1, 4.2, 4.3_
|
||||
|
||||
- [x] 5. Add clipboard API availability detection
|
||||
- Create method to check for navigator.clipboard and ClipboardItem support
|
||||
- Implement graceful fallback when Clipboard API is unavailable
|
||||
- Provide user feedback when clipboard features are not supported
|
||||
- _Requirements: 2.2_
|
||||
|
||||
- [x] 6. Integrate clipboard operations into screenshot capture workflow
|
||||
- Modify captureElement method to include clipboard operations when enabled
|
||||
- Ensure clipboard copying happens after successful canvas generation
|
||||
- Maintain existing download functionality regardless of clipboard success/failure
|
||||
- _Requirements: 1.4, 1.5_
|
||||
|
||||
- [x] 7. Implement comprehensive error handling for clipboard operations
|
||||
- Handle clipboard write permission errors gracefully
|
||||
- Provide specific error messages for different failure scenarios
|
||||
- Ensure screenshot download continues even when clipboard operations fail
|
||||
- _Requirements: 2.3_
|
||||
|
||||
- [x] 8. Update user feedback messages for clipboard operations
|
||||
- Modify loading message to indicate clipboard preparation when enabled
|
||||
- Update success message to confirm both download and clipboard copy
|
||||
- Create error messages that distinguish between download and clipboard failures
|
||||
- _Requirements: 2.4, 3.2, 3.3_
|
||||
|
||||
- [x] 9. Add tooltip and help text for clipboard toggle
|
||||
- Implement tooltip showing clipboard feature explanation
|
||||
- Add help text explaining clipboard functionality and browser requirements
|
||||
- Ensure tooltip is accessible and keyboard navigable
|
||||
- _Requirements: 3.4_
|
||||
|
||||
- [x] 10. Test clipboard functionality across different scenarios
|
||||
- Write test cases for clipboard API availability detection
|
||||
- Test clipboard operations with different background settings (transparent, black, white)
|
||||
- Verify error handling when clipboard API is unavailable or permissions denied
|
||||
- Test preference persistence across browser sessions
|
||||
- _Requirements: 2.1, 2.2, 2.3, 4.3_
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"kiroAgent.configureMCP": "Disabled"
|
||||
}
|
||||
380
content.js
380
content.js
|
|
@ -6,15 +6,17 @@ class ScreenshotSelector {
|
|||
this.overlay = null;
|
||||
this.style = null;
|
||||
this.background = 'black';
|
||||
this.copyToClipboard = true; // Default to true
|
||||
}
|
||||
|
||||
async init(background = 'black') {
|
||||
async init(background = 'black', copyToClipboard = true) {
|
||||
if (this.isActive) {
|
||||
console.log('Screenshot selector already active');
|
||||
return;
|
||||
}
|
||||
|
||||
this.background = background;
|
||||
this.copyToClipboard = copyToClipboard;
|
||||
this.isActive = true;
|
||||
|
||||
this.createUI();
|
||||
|
|
@ -363,12 +365,23 @@ class ScreenshotSelector {
|
|||
|
||||
async captureElement(element) {
|
||||
try {
|
||||
// Determine loading message based on clipboard settings
|
||||
let loadingMessage = 'Capturing screenshot...';
|
||||
if (this.copyToClipboard) {
|
||||
const availability = this.checkClipboardAPIAvailability();
|
||||
if (availability.available) {
|
||||
loadingMessage = 'Capturing screenshot and preparing for clipboard...';
|
||||
} else {
|
||||
loadingMessage = 'Capturing screenshot... (Clipboard not available)';
|
||||
}
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
this.overlay.innerHTML = `
|
||||
<div style="position: fixed; top: 20px; left: 20px; background: rgba(0,0,0,0.9); color: white; padding: 12px 16px; border-radius: 8px; z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<div style="width: 16px; height: 16px; border: 2px solid #fff; border-top: 2px solid transparent; border-radius: 50%; animation: spin 1s linear infinite;"></div>
|
||||
<span>Capturing screenshot...</span>
|
||||
<span>${loadingMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
|
|
@ -467,18 +480,60 @@ class ScreenshotSelector {
|
|||
// Restore original styles
|
||||
Object.assign(element.style, originalStyles);
|
||||
|
||||
// Download image
|
||||
// Always download the image first (core functionality)
|
||||
const link = document.createElement('a');
|
||||
link.href = canvas.toDataURL('image/png');
|
||||
link.download = `screenshot-${new Date().toISOString().slice(0, 19).replace(/[:.]/g, '-')}.png`;
|
||||
link.click();
|
||||
|
||||
// Success message
|
||||
// Handle clipboard operations if enabled (after successful canvas generation and download)
|
||||
let clipboardResult = null;
|
||||
if (this.copyToClipboard) {
|
||||
const availability = this.checkClipboardAPIAvailability();
|
||||
if (availability.available) {
|
||||
try {
|
||||
// Attempt clipboard operation with comprehensive error handling
|
||||
clipboardResult = await this.copyCanvasToClipboard(canvas);
|
||||
} catch (error) {
|
||||
// Ensure clipboard errors don't break the workflow - this is a safety net
|
||||
// The copyCanvasToClipboard method should handle all errors internally
|
||||
console.error('Unexpected clipboard error caught in captureElement:', error);
|
||||
clipboardResult = {
|
||||
success: false,
|
||||
error: 'Unexpected clipboard error occurred',
|
||||
errorType: 'unexpected'
|
||||
};
|
||||
}
|
||||
} else {
|
||||
clipboardResult = {
|
||||
success: false,
|
||||
error: availability.reason,
|
||||
errorType: 'api_unavailable'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Generate user feedback message based on clipboard results
|
||||
let successMessage = 'Screenshot downloaded successfully!';
|
||||
let messageColor = 'rgba(39, 174, 96, 0.95)'; // Green for success
|
||||
|
||||
if (this.copyToClipboard) {
|
||||
if (clipboardResult && clipboardResult.success) {
|
||||
successMessage = 'Screenshot downloaded and copied to clipboard!';
|
||||
// Keep green color for full success
|
||||
} else {
|
||||
// Download succeeded but clipboard failed - provide specific error feedback
|
||||
const errorMessage = this.getClipboardErrorMessage(clipboardResult);
|
||||
successMessage = `Screenshot downloaded successfully! ${errorMessage}`;
|
||||
messageColor = 'rgba(243, 156, 18, 0.95)'; // Orange for partial success
|
||||
}
|
||||
}
|
||||
|
||||
this.overlay.innerHTML = `
|
||||
<div style="position: fixed; top: 20px; left: 20px; background: rgba(39, 174, 96, 0.95); color: white; padding: 12px 16px; border-radius: 8px; z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
|
||||
<div style="position: fixed; top: 20px; left: 20px; background: ${messageColor}; color: white; padding: 12px 16px; border-radius: 8px; z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<span>✅</span>
|
||||
<span>Screenshot saved successfully!</span>
|
||||
<span>${successMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -490,18 +545,314 @@ class ScreenshotSelector {
|
|||
|
||||
} catch (error) {
|
||||
console.error('Screenshot failed:', error);
|
||||
|
||||
// Provide specific error messages for screenshot failures
|
||||
let errorMessage = 'Screenshot capture failed. Please try again.';
|
||||
|
||||
if (error.message) {
|
||||
if (error.message.includes('html2canvas')) {
|
||||
errorMessage = 'Screenshot rendering failed. Try selecting a different element or refresh the page.';
|
||||
} else if (error.message.includes('timeout')) {
|
||||
errorMessage = 'Screenshot capture timed out. Try selecting a smaller area or simpler element.';
|
||||
} else if (error.message.includes('network') || error.message.includes('CORS')) {
|
||||
errorMessage = 'Screenshot failed: Some content is blocked by security restrictions.';
|
||||
} else if (error.message.includes('memory') || error.message.includes('quota')) {
|
||||
errorMessage = 'Screenshot failed: Not enough memory. Try capturing a smaller area.';
|
||||
} else if (error.message.includes('canvas')) {
|
||||
errorMessage = 'Screenshot failed: Unable to create image. Try a different element.';
|
||||
}
|
||||
}
|
||||
|
||||
this.overlay.innerHTML = `
|
||||
<div style="position: fixed; top: 20px; left: 20px; background: rgba(231, 76, 60, 0.95); color: white; padding: 12px 16px; border-radius: 8px; z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<span>❌</span>
|
||||
<span>Screenshot failed. Please try again.</span>
|
||||
<span>${errorMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Notify popup about the failure
|
||||
chrome.runtime.sendMessage({ action: 'selectorStopped', reason: 'screenshot-failed' });
|
||||
|
||||
setTimeout(() => this.cleanup(), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Clipboard API is available and supported
|
||||
* @returns {{available: boolean, reason?: string}} Clipboard API availability status
|
||||
*/
|
||||
checkClipboardAPIAvailability() {
|
||||
// Check for basic Clipboard API support
|
||||
if (!navigator.clipboard) {
|
||||
return {
|
||||
available: false,
|
||||
reason: 'Clipboard API not available in this browser'
|
||||
};
|
||||
}
|
||||
|
||||
// Check for ClipboardItem constructor (required for image data)
|
||||
if (!window.ClipboardItem) {
|
||||
return {
|
||||
available: false,
|
||||
reason: 'ClipboardItem not supported in this browser'
|
||||
};
|
||||
}
|
||||
|
||||
// Check if we're in a secure context (HTTPS required for clipboard)
|
||||
if (!window.isSecureContext) {
|
||||
return {
|
||||
available: false,
|
||||
reason: 'Clipboard API requires a secure context (HTTPS)'
|
||||
};
|
||||
}
|
||||
|
||||
// Check for write permission (this is the basic check, actual permission is checked during write)
|
||||
if (!navigator.clipboard.write) {
|
||||
return {
|
||||
available: false,
|
||||
reason: 'Clipboard write functionality not available'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
available: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user-friendly feedback message about clipboard feature availability
|
||||
* @returns {{supported: boolean, message: string}} User feedback about clipboard support
|
||||
*/
|
||||
getClipboardSupportFeedback() {
|
||||
const availability = this.checkClipboardAPIAvailability();
|
||||
|
||||
if (availability.available) {
|
||||
return {
|
||||
supported: true,
|
||||
message: 'Clipboard copying is available and ready to use.'
|
||||
};
|
||||
}
|
||||
|
||||
// Provide user-friendly messages for different scenarios
|
||||
let userMessage = '';
|
||||
|
||||
if (availability.reason.includes('not available') || availability.reason.includes('not supported')) {
|
||||
userMessage = 'Your browser doesn\'t support clipboard copying. Screenshots will still be downloaded.';
|
||||
} else if (availability.reason.includes('secure context')) {
|
||||
userMessage = 'Clipboard copying requires HTTPS. Screenshots will still be downloaded.';
|
||||
} else if (availability.reason.includes('write functionality')) {
|
||||
userMessage = 'Clipboard writing is not available. Screenshots will still be downloaded.';
|
||||
} else {
|
||||
userMessage = 'Clipboard copying is not available. Screenshots will still be downloaded.';
|
||||
}
|
||||
|
||||
return {
|
||||
supported: false,
|
||||
message: userMessage
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate user-friendly error message for clipboard operation failures
|
||||
* @param {Object} clipboardResult - Result object from clipboard operation
|
||||
* @returns {string} User-friendly error message
|
||||
*/
|
||||
getClipboardErrorMessage(clipboardResult) {
|
||||
if (!clipboardResult || !clipboardResult.error) {
|
||||
return 'Clipboard copy failed due to unknown error.';
|
||||
}
|
||||
|
||||
const errorType = clipboardResult.errorType;
|
||||
const errorMessage = clipboardResult.error;
|
||||
|
||||
// Provide contextual error messages based on error type
|
||||
switch (errorType) {
|
||||
case 'permission_denied':
|
||||
return 'Clipboard copy failed: Permission denied. Please allow clipboard access in your browser settings.';
|
||||
|
||||
case 'not_supported':
|
||||
case 'api_unavailable':
|
||||
return 'Clipboard copy not available in this browser. Screenshot was still downloaded.';
|
||||
|
||||
case 'security_error':
|
||||
return 'Clipboard copy blocked by browser security. Try using HTTPS or check site permissions.';
|
||||
|
||||
case 'size_limit':
|
||||
case 'quota_exceeded':
|
||||
return 'Clipboard copy failed: Image too large. Try capturing a smaller area.';
|
||||
|
||||
case 'timeout':
|
||||
return 'Clipboard copy timed out. The image may be too large or complex.';
|
||||
|
||||
case 'network_error':
|
||||
return 'Clipboard copy failed due to network error. Please try again.';
|
||||
|
||||
case 'canvas_conversion':
|
||||
return 'Clipboard copy failed: Unable to prepare image. Try capturing a different element.';
|
||||
|
||||
case 'clipboard_item_creation':
|
||||
return 'Clipboard copy not supported: Your browser doesn\'t support image clipboard operations.';
|
||||
|
||||
case 'invalid_state':
|
||||
return 'Clipboard copy failed: Browser clipboard is busy. Please try again.';
|
||||
|
||||
case 'data_error':
|
||||
return 'Clipboard copy failed: Invalid image data. Please try capturing again.';
|
||||
|
||||
case 'unexpected':
|
||||
return 'Clipboard copy failed due to unexpected error. Screenshot was still downloaded.';
|
||||
|
||||
default:
|
||||
// For unknown error types, try to extract meaningful info from the error message
|
||||
if (errorMessage.includes('permission') || errorMessage.includes('denied')) {
|
||||
return 'Clipboard copy failed: Access denied. Check browser permissions.';
|
||||
} else if (errorMessage.includes('not supported') || errorMessage.includes('unavailable')) {
|
||||
return 'Clipboard copy not supported in this browser.';
|
||||
} else if (errorMessage.includes('timeout') || errorMessage.includes('timed out')) {
|
||||
return 'Clipboard copy timed out. Try capturing a smaller area.';
|
||||
} else if (errorMessage.includes('large') || errorMessage.includes('size')) {
|
||||
return 'Clipboard copy failed: Image too large for clipboard.';
|
||||
} else {
|
||||
return `Clipboard copy failed: ${errorMessage}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy canvas content to clipboard using the Clipboard API
|
||||
* @param {HTMLCanvasElement} canvas - The canvas element to copy
|
||||
* @returns {Promise<{success: boolean, error?: string, errorType?: string}>} Result of clipboard operation
|
||||
*/
|
||||
async copyCanvasToClipboard(canvas) {
|
||||
try {
|
||||
// Check clipboard API availability first
|
||||
const availability = this.checkClipboardAPIAvailability();
|
||||
if (!availability.available) {
|
||||
return {
|
||||
success: false,
|
||||
error: availability.reason,
|
||||
errorType: 'api_unavailable'
|
||||
};
|
||||
}
|
||||
|
||||
// Convert canvas to blob in PNG format with timeout
|
||||
const blob = await Promise.race([
|
||||
new Promise((resolve, reject) => {
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob) {
|
||||
resolve(blob);
|
||||
} else {
|
||||
reject(new Error('Canvas to blob conversion returned null'));
|
||||
}
|
||||
}, 'image/png');
|
||||
}),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Canvas to blob conversion timed out')), 10000)
|
||||
)
|
||||
]);
|
||||
|
||||
// Validate blob size (prevent extremely large clipboard operations)
|
||||
const maxBlobSize = 50 * 1024 * 1024; // 50MB limit
|
||||
if (blob.size > maxBlobSize) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Image too large for clipboard (${Math.round(blob.size / 1024 / 1024)}MB). Try capturing a smaller area.`,
|
||||
errorType: 'size_limit'
|
||||
};
|
||||
}
|
||||
|
||||
// Create ClipboardItem with PNG image data
|
||||
let clipboardItem;
|
||||
try {
|
||||
clipboardItem = new ClipboardItem({
|
||||
'image/png': blob
|
||||
});
|
||||
} catch (clipboardItemError) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to create clipboard item. Your browser may not support image clipboard operations.',
|
||||
errorType: 'clipboard_item_creation'
|
||||
};
|
||||
}
|
||||
|
||||
// Write to clipboard with timeout
|
||||
await Promise.race([
|
||||
navigator.clipboard.write([clipboardItem]),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Clipboard write operation timed out')), 15000)
|
||||
)
|
||||
]);
|
||||
|
||||
return { success: true };
|
||||
|
||||
} catch (error) {
|
||||
console.error('Clipboard operation failed:', error);
|
||||
|
||||
// Comprehensive error handling with specific error types and user-friendly messages
|
||||
let errorMessage = error.message || 'Unknown clipboard error';
|
||||
let errorType = 'unknown';
|
||||
|
||||
// Permission-related errors
|
||||
if (error.name === 'NotAllowedError') {
|
||||
errorMessage = 'Clipboard access denied. Please allow clipboard permissions in your browser settings.';
|
||||
errorType = 'permission_denied';
|
||||
}
|
||||
// API support errors
|
||||
else if (error.name === 'NotSupportedError') {
|
||||
errorMessage = 'Clipboard API not supported in this browser context.';
|
||||
errorType = 'not_supported';
|
||||
}
|
||||
// Security context errors
|
||||
else if (error.name === 'SecurityError') {
|
||||
errorMessage = 'Clipboard access blocked by security policy. Try using HTTPS.';
|
||||
errorType = 'security_error';
|
||||
}
|
||||
// Network or resource errors
|
||||
else if (error.name === 'NetworkError') {
|
||||
errorMessage = 'Network error during clipboard operation. Please try again.';
|
||||
errorType = 'network_error';
|
||||
}
|
||||
// Timeout errors
|
||||
else if (error.message.includes('timed out')) {
|
||||
errorMessage = 'Clipboard operation timed out. The image may be too large.';
|
||||
errorType = 'timeout';
|
||||
}
|
||||
// Canvas conversion errors
|
||||
else if (error.message.includes('canvas') || error.message.includes('blob')) {
|
||||
errorMessage = 'Failed to prepare image for clipboard. Try capturing a different area.';
|
||||
errorType = 'canvas_conversion';
|
||||
}
|
||||
// Quota or storage errors
|
||||
else if (error.name === 'QuotaExceededError' || error.message.includes('quota')) {
|
||||
errorMessage = 'Clipboard storage quota exceeded. Try capturing a smaller image.';
|
||||
errorType = 'quota_exceeded';
|
||||
}
|
||||
// DOM or state errors
|
||||
else if (error.name === 'InvalidStateError') {
|
||||
errorMessage = 'Clipboard is in an invalid state. Please try again.';
|
||||
errorType = 'invalid_state';
|
||||
}
|
||||
// Data errors
|
||||
else if (error.name === 'DataError') {
|
||||
errorMessage = 'Invalid image data for clipboard. Please try again.';
|
||||
errorType = 'data_error';
|
||||
}
|
||||
// Generic fallback for unknown errors
|
||||
else {
|
||||
errorMessage = `Clipboard operation failed: ${error.message}`;
|
||||
errorType = 'unknown';
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
errorType: errorType
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
document.removeEventListener('mouseover', this.handleMouseOver);
|
||||
document.removeEventListener('click', this.handleClick, true);
|
||||
|
|
@ -540,7 +891,9 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
try {
|
||||
switch (message.action) {
|
||||
case 'startSelector':
|
||||
screenshotSelector.init(message.background);
|
||||
// Extract clipboard preference from message, default to true for backward compatibility
|
||||
const copyToClipboard = message.copyToClipboard !== undefined ? message.copyToClipboard : true;
|
||||
screenshotSelector.init(message.background, copyToClipboard);
|
||||
sendResponse({ success: true });
|
||||
break;
|
||||
|
||||
|
|
@ -553,6 +906,15 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
sendResponse({ isActive: screenshotSelector.isActive });
|
||||
break;
|
||||
|
||||
case 'checkClipboardSupport':
|
||||
const availability = screenshotSelector.checkClipboardAPIAvailability();
|
||||
const feedback = screenshotSelector.getClipboardSupportFeedback();
|
||||
sendResponse({
|
||||
availability: availability,
|
||||
feedback: feedback
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
sendResponse({ error: 'Unknown action: ' + message.action });
|
||||
}
|
||||
|
|
@ -569,7 +931,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.action === 'activateFromShortcut') {
|
||||
if (!screenshotSelector.isActive) {
|
||||
screenshotSelector.init('black'); // Default background for shortcut
|
||||
screenshotSelector.init('black', true); // Default background and clipboard enabled for shortcut
|
||||
}
|
||||
}
|
||||
});
|
||||
224
coverage/base.css
Normal file
224
coverage/base.css
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
body, html {
|
||||
margin:0; padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
font-family: Helvetica Neue, Helvetica, Arial;
|
||||
font-size: 14px;
|
||||
color:#333;
|
||||
}
|
||||
.small { font-size: 12px; }
|
||||
*, *:after, *:before {
|
||||
-webkit-box-sizing:border-box;
|
||||
-moz-box-sizing:border-box;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
h1 { font-size: 20px; margin: 0;}
|
||||
h2 { font-size: 14px; }
|
||||
pre {
|
||||
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-moz-tab-size: 2;
|
||||
-o-tab-size: 2;
|
||||
tab-size: 2;
|
||||
}
|
||||
a { color:#0074D9; text-decoration:none; }
|
||||
a:hover { text-decoration:underline; }
|
||||
.strong { font-weight: bold; }
|
||||
.space-top1 { padding: 10px 0 0 0; }
|
||||
.pad2y { padding: 20px 0; }
|
||||
.pad1y { padding: 10px 0; }
|
||||
.pad2x { padding: 0 20px; }
|
||||
.pad2 { padding: 20px; }
|
||||
.pad1 { padding: 10px; }
|
||||
.space-left2 { padding-left:55px; }
|
||||
.space-right2 { padding-right:20px; }
|
||||
.center { text-align:center; }
|
||||
.clearfix { display:block; }
|
||||
.clearfix:after {
|
||||
content:'';
|
||||
display:block;
|
||||
height:0;
|
||||
clear:both;
|
||||
visibility:hidden;
|
||||
}
|
||||
.fl { float: left; }
|
||||
@media only screen and (max-width:640px) {
|
||||
.col3 { width:100%; max-width:100%; }
|
||||
.hide-mobile { display:none!important; }
|
||||
}
|
||||
|
||||
.quiet {
|
||||
color: #7f7f7f;
|
||||
color: rgba(0,0,0,0.5);
|
||||
}
|
||||
.quiet a { opacity: 0.7; }
|
||||
|
||||
.fraction {
|
||||
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
background: #E8E8E8;
|
||||
padding: 4px 5px;
|
||||
border-radius: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.path a:link, div.path a:visited { color: #333; }
|
||||
table.coverage {
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.coverage td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.coverage td.line-count {
|
||||
text-align: right;
|
||||
padding: 0 5px 0 20px;
|
||||
}
|
||||
table.coverage td.line-coverage {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
min-width:20px;
|
||||
}
|
||||
|
||||
table.coverage td span.cline-any {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
}
|
||||
.missing-if-branch {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
background: #333;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.skip-if-branch {
|
||||
display: none;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
background: #ccc;
|
||||
color: white;
|
||||
}
|
||||
.missing-if-branch .typ, .skip-if-branch .typ {
|
||||
color: inherit !important;
|
||||
}
|
||||
.coverage-summary {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
||||
.keyline-all { border: 1px solid #ddd; }
|
||||
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
||||
.coverage-summary tbody { border: 1px solid #bbb; }
|
||||
.coverage-summary td { border-right: 1px solid #bbb; }
|
||||
.coverage-summary td:last-child { border-right: none; }
|
||||
.coverage-summary th {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.coverage-summary th.file { border-right: none !important; }
|
||||
.coverage-summary th.pct { }
|
||||
.coverage-summary th.pic,
|
||||
.coverage-summary th.abs,
|
||||
.coverage-summary td.pct,
|
||||
.coverage-summary td.abs { text-align: right; }
|
||||
.coverage-summary td.file { white-space: nowrap; }
|
||||
.coverage-summary td.pic { min-width: 120px !important; }
|
||||
.coverage-summary tfoot td { }
|
||||
|
||||
.coverage-summary .sorter {
|
||||
height: 10px;
|
||||
width: 7px;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
||||
}
|
||||
.coverage-summary .sorted .sorter {
|
||||
background-position: 0 -20px;
|
||||
}
|
||||
.coverage-summary .sorted-desc .sorter {
|
||||
background-position: 0 -10px;
|
||||
}
|
||||
.status-line { height: 10px; }
|
||||
/* yellow */
|
||||
.cbranch-no { background: yellow !important; color: #111; }
|
||||
/* dark red */
|
||||
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
||||
.low .chart { border:1px solid #C21F39 }
|
||||
.highlighted,
|
||||
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
||||
background: #C21F39 !important;
|
||||
}
|
||||
/* medium red */
|
||||
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
||||
/* light red */
|
||||
.low, .cline-no { background:#FCE1E5 }
|
||||
/* light green */
|
||||
.high, .cline-yes { background:rgb(230,245,208) }
|
||||
/* medium green */
|
||||
.cstat-yes { background:rgb(161,215,106) }
|
||||
/* dark green */
|
||||
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
||||
.high .chart { border:1px solid rgb(77,146,33) }
|
||||
/* dark yellow (gold) */
|
||||
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
||||
.medium .chart { border:1px solid #f9cd0b; }
|
||||
/* light yellow */
|
||||
.medium { background: #fff4c2; }
|
||||
|
||||
.cstat-skip { background: #ddd; color: #111; }
|
||||
.fstat-skip { background: #ddd; color: #111 !important; }
|
||||
.cbranch-skip { background: #ddd !important; color: #111; }
|
||||
|
||||
span.cline-neutral { background: #eaeaea; }
|
||||
|
||||
.coverage-summary td.empty {
|
||||
opacity: .5;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
line-height: 1;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.cover-fill, .cover-empty {
|
||||
display:inline-block;
|
||||
height: 12px;
|
||||
}
|
||||
.chart {
|
||||
line-height: 0;
|
||||
}
|
||||
.cover-empty {
|
||||
background: white;
|
||||
}
|
||||
.cover-full {
|
||||
border-right: none !important;
|
||||
}
|
||||
pre.prettyprint {
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
.com { color: #999 !important; }
|
||||
.ignore-none { color: #999; font-weight: normal; }
|
||||
|
||||
.wrapper {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
margin: 0 auto -48px;
|
||||
}
|
||||
.footer, .push {
|
||||
height: 48px;
|
||||
}
|
||||
87
coverage/block-navigation.js
Normal file
87
coverage/block-navigation.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* eslint-disable */
|
||||
var jumpToCode = (function init() {
|
||||
// Classes of code we would like to highlight in the file view
|
||||
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
||||
|
||||
// Elements to highlight in the file listing view
|
||||
var fileListingElements = ['td.pct.low'];
|
||||
|
||||
// We don't want to select elements that are direct descendants of another match
|
||||
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
||||
|
||||
// Selecter that finds elements on the page to which we can jump
|
||||
var selector =
|
||||
fileListingElements.join(', ') +
|
||||
', ' +
|
||||
notSelector +
|
||||
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
||||
|
||||
// The NodeList of matching elements
|
||||
var missingCoverageElements = document.querySelectorAll(selector);
|
||||
|
||||
var currentIndex;
|
||||
|
||||
function toggleClass(index) {
|
||||
missingCoverageElements
|
||||
.item(currentIndex)
|
||||
.classList.remove('highlighted');
|
||||
missingCoverageElements.item(index).classList.add('highlighted');
|
||||
}
|
||||
|
||||
function makeCurrent(index) {
|
||||
toggleClass(index);
|
||||
currentIndex = index;
|
||||
missingCoverageElements.item(index).scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
inline: 'center'
|
||||
});
|
||||
}
|
||||
|
||||
function goToPrevious() {
|
||||
var nextIndex = 0;
|
||||
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
||||
nextIndex = missingCoverageElements.length - 1;
|
||||
} else if (missingCoverageElements.length > 1) {
|
||||
nextIndex = currentIndex - 1;
|
||||
}
|
||||
|
||||
makeCurrent(nextIndex);
|
||||
}
|
||||
|
||||
function goToNext() {
|
||||
var nextIndex = 0;
|
||||
|
||||
if (
|
||||
typeof currentIndex === 'number' &&
|
||||
currentIndex < missingCoverageElements.length - 1
|
||||
) {
|
||||
nextIndex = currentIndex + 1;
|
||||
}
|
||||
|
||||
makeCurrent(nextIndex);
|
||||
}
|
||||
|
||||
return function jump(event) {
|
||||
if (
|
||||
document.getElementById('fileSearch') === document.activeElement &&
|
||||
document.activeElement != null
|
||||
) {
|
||||
// if we're currently focused on the search input, we don't want to navigate
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.which) {
|
||||
case 78: // n
|
||||
case 74: // j
|
||||
goToNext();
|
||||
break;
|
||||
case 66: // b
|
||||
case 75: // k
|
||||
case 80: // p
|
||||
goToPrevious();
|
||||
break;
|
||||
}
|
||||
};
|
||||
})();
|
||||
window.addEventListener('keydown', jumpToCode);
|
||||
2893
coverage/content.js.html
Normal file
2893
coverage/content.js.html
Normal file
File diff suppressed because it is too large
Load diff
BIN
coverage/favicon.png
Normal file
BIN
coverage/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 445 B |
131
coverage/index.html
Normal file
131
coverage/index.html
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for All files</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="prettify.css" />
|
||||
<link rel="stylesheet" href="base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type='text/css'>
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class='wrapper'>
|
||||
<div class='pad1'>
|
||||
<h1>All files</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/463</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/263</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/61</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/445</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input type="search" id="fileSearch">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
|
||||
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
|
||||
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
|
||||
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
|
||||
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
|
||||
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
|
||||
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="content.js"><a href="content.js.html">content.js</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="371" class="abs low">0/371</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="214" class="abs low">0/214</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="44" class="abs low">0/44</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="356" class="abs low">0/356</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="popup.js"><a href="popup.js.html">popup.js</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="92" class="abs low">0/92</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="49" class="abs low">0/49</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="17" class="abs low">0/17</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="89" class="abs low">0/89</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class='push'></div><!-- for sticky footer -->
|
||||
</div><!-- /wrapper -->
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2025-08-13T15:07:03.182Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="sorter.js"></script>
|
||||
<script src="block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
224
coverage/lcov-report/base.css
Normal file
224
coverage/lcov-report/base.css
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
body, html {
|
||||
margin:0; padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
font-family: Helvetica Neue, Helvetica, Arial;
|
||||
font-size: 14px;
|
||||
color:#333;
|
||||
}
|
||||
.small { font-size: 12px; }
|
||||
*, *:after, *:before {
|
||||
-webkit-box-sizing:border-box;
|
||||
-moz-box-sizing:border-box;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
h1 { font-size: 20px; margin: 0;}
|
||||
h2 { font-size: 14px; }
|
||||
pre {
|
||||
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-moz-tab-size: 2;
|
||||
-o-tab-size: 2;
|
||||
tab-size: 2;
|
||||
}
|
||||
a { color:#0074D9; text-decoration:none; }
|
||||
a:hover { text-decoration:underline; }
|
||||
.strong { font-weight: bold; }
|
||||
.space-top1 { padding: 10px 0 0 0; }
|
||||
.pad2y { padding: 20px 0; }
|
||||
.pad1y { padding: 10px 0; }
|
||||
.pad2x { padding: 0 20px; }
|
||||
.pad2 { padding: 20px; }
|
||||
.pad1 { padding: 10px; }
|
||||
.space-left2 { padding-left:55px; }
|
||||
.space-right2 { padding-right:20px; }
|
||||
.center { text-align:center; }
|
||||
.clearfix { display:block; }
|
||||
.clearfix:after {
|
||||
content:'';
|
||||
display:block;
|
||||
height:0;
|
||||
clear:both;
|
||||
visibility:hidden;
|
||||
}
|
||||
.fl { float: left; }
|
||||
@media only screen and (max-width:640px) {
|
||||
.col3 { width:100%; max-width:100%; }
|
||||
.hide-mobile { display:none!important; }
|
||||
}
|
||||
|
||||
.quiet {
|
||||
color: #7f7f7f;
|
||||
color: rgba(0,0,0,0.5);
|
||||
}
|
||||
.quiet a { opacity: 0.7; }
|
||||
|
||||
.fraction {
|
||||
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
background: #E8E8E8;
|
||||
padding: 4px 5px;
|
||||
border-radius: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.path a:link, div.path a:visited { color: #333; }
|
||||
table.coverage {
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.coverage td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.coverage td.line-count {
|
||||
text-align: right;
|
||||
padding: 0 5px 0 20px;
|
||||
}
|
||||
table.coverage td.line-coverage {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
min-width:20px;
|
||||
}
|
||||
|
||||
table.coverage td span.cline-any {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
}
|
||||
.missing-if-branch {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
background: #333;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.skip-if-branch {
|
||||
display: none;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
background: #ccc;
|
||||
color: white;
|
||||
}
|
||||
.missing-if-branch .typ, .skip-if-branch .typ {
|
||||
color: inherit !important;
|
||||
}
|
||||
.coverage-summary {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
||||
.keyline-all { border: 1px solid #ddd; }
|
||||
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
||||
.coverage-summary tbody { border: 1px solid #bbb; }
|
||||
.coverage-summary td { border-right: 1px solid #bbb; }
|
||||
.coverage-summary td:last-child { border-right: none; }
|
||||
.coverage-summary th {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.coverage-summary th.file { border-right: none !important; }
|
||||
.coverage-summary th.pct { }
|
||||
.coverage-summary th.pic,
|
||||
.coverage-summary th.abs,
|
||||
.coverage-summary td.pct,
|
||||
.coverage-summary td.abs { text-align: right; }
|
||||
.coverage-summary td.file { white-space: nowrap; }
|
||||
.coverage-summary td.pic { min-width: 120px !important; }
|
||||
.coverage-summary tfoot td { }
|
||||
|
||||
.coverage-summary .sorter {
|
||||
height: 10px;
|
||||
width: 7px;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
||||
}
|
||||
.coverage-summary .sorted .sorter {
|
||||
background-position: 0 -20px;
|
||||
}
|
||||
.coverage-summary .sorted-desc .sorter {
|
||||
background-position: 0 -10px;
|
||||
}
|
||||
.status-line { height: 10px; }
|
||||
/* yellow */
|
||||
.cbranch-no { background: yellow !important; color: #111; }
|
||||
/* dark red */
|
||||
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
||||
.low .chart { border:1px solid #C21F39 }
|
||||
.highlighted,
|
||||
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
||||
background: #C21F39 !important;
|
||||
}
|
||||
/* medium red */
|
||||
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
||||
/* light red */
|
||||
.low, .cline-no { background:#FCE1E5 }
|
||||
/* light green */
|
||||
.high, .cline-yes { background:rgb(230,245,208) }
|
||||
/* medium green */
|
||||
.cstat-yes { background:rgb(161,215,106) }
|
||||
/* dark green */
|
||||
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
||||
.high .chart { border:1px solid rgb(77,146,33) }
|
||||
/* dark yellow (gold) */
|
||||
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
||||
.medium .chart { border:1px solid #f9cd0b; }
|
||||
/* light yellow */
|
||||
.medium { background: #fff4c2; }
|
||||
|
||||
.cstat-skip { background: #ddd; color: #111; }
|
||||
.fstat-skip { background: #ddd; color: #111 !important; }
|
||||
.cbranch-skip { background: #ddd !important; color: #111; }
|
||||
|
||||
span.cline-neutral { background: #eaeaea; }
|
||||
|
||||
.coverage-summary td.empty {
|
||||
opacity: .5;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
line-height: 1;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.cover-fill, .cover-empty {
|
||||
display:inline-block;
|
||||
height: 12px;
|
||||
}
|
||||
.chart {
|
||||
line-height: 0;
|
||||
}
|
||||
.cover-empty {
|
||||
background: white;
|
||||
}
|
||||
.cover-full {
|
||||
border-right: none !important;
|
||||
}
|
||||
pre.prettyprint {
|
||||
border: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
.com { color: #999 !important; }
|
||||
.ignore-none { color: #999; font-weight: normal; }
|
||||
|
||||
.wrapper {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
margin: 0 auto -48px;
|
||||
}
|
||||
.footer, .push {
|
||||
height: 48px;
|
||||
}
|
||||
87
coverage/lcov-report/block-navigation.js
Normal file
87
coverage/lcov-report/block-navigation.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* eslint-disable */
|
||||
var jumpToCode = (function init() {
|
||||
// Classes of code we would like to highlight in the file view
|
||||
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
||||
|
||||
// Elements to highlight in the file listing view
|
||||
var fileListingElements = ['td.pct.low'];
|
||||
|
||||
// We don't want to select elements that are direct descendants of another match
|
||||
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
||||
|
||||
// Selecter that finds elements on the page to which we can jump
|
||||
var selector =
|
||||
fileListingElements.join(', ') +
|
||||
', ' +
|
||||
notSelector +
|
||||
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
||||
|
||||
// The NodeList of matching elements
|
||||
var missingCoverageElements = document.querySelectorAll(selector);
|
||||
|
||||
var currentIndex;
|
||||
|
||||
function toggleClass(index) {
|
||||
missingCoverageElements
|
||||
.item(currentIndex)
|
||||
.classList.remove('highlighted');
|
||||
missingCoverageElements.item(index).classList.add('highlighted');
|
||||
}
|
||||
|
||||
function makeCurrent(index) {
|
||||
toggleClass(index);
|
||||
currentIndex = index;
|
||||
missingCoverageElements.item(index).scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
inline: 'center'
|
||||
});
|
||||
}
|
||||
|
||||
function goToPrevious() {
|
||||
var nextIndex = 0;
|
||||
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
||||
nextIndex = missingCoverageElements.length - 1;
|
||||
} else if (missingCoverageElements.length > 1) {
|
||||
nextIndex = currentIndex - 1;
|
||||
}
|
||||
|
||||
makeCurrent(nextIndex);
|
||||
}
|
||||
|
||||
function goToNext() {
|
||||
var nextIndex = 0;
|
||||
|
||||
if (
|
||||
typeof currentIndex === 'number' &&
|
||||
currentIndex < missingCoverageElements.length - 1
|
||||
) {
|
||||
nextIndex = currentIndex + 1;
|
||||
}
|
||||
|
||||
makeCurrent(nextIndex);
|
||||
}
|
||||
|
||||
return function jump(event) {
|
||||
if (
|
||||
document.getElementById('fileSearch') === document.activeElement &&
|
||||
document.activeElement != null
|
||||
) {
|
||||
// if we're currently focused on the search input, we don't want to navigate
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.which) {
|
||||
case 78: // n
|
||||
case 74: // j
|
||||
goToNext();
|
||||
break;
|
||||
case 66: // b
|
||||
case 75: // k
|
||||
case 80: // p
|
||||
goToPrevious();
|
||||
break;
|
||||
}
|
||||
};
|
||||
})();
|
||||
window.addEventListener('keydown', jumpToCode);
|
||||
2893
coverage/lcov-report/content.js.html
Normal file
2893
coverage/lcov-report/content.js.html
Normal file
File diff suppressed because it is too large
Load diff
BIN
coverage/lcov-report/favicon.png
Normal file
BIN
coverage/lcov-report/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 445 B |
131
coverage/lcov-report/index.html
Normal file
131
coverage/lcov-report/index.html
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for All files</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="prettify.css" />
|
||||
<link rel="stylesheet" href="base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type='text/css'>
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class='wrapper'>
|
||||
<div class='pad1'>
|
||||
<h1>All files</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/463</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/263</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/61</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/445</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input type="search" id="fileSearch">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
|
||||
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
|
||||
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
|
||||
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
|
||||
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
|
||||
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
|
||||
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="content.js"><a href="content.js.html">content.js</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="371" class="abs low">0/371</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="214" class="abs low">0/214</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="44" class="abs low">0/44</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="356" class="abs low">0/356</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="popup.js"><a href="popup.js.html">popup.js</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="92" class="abs low">0/92</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="49" class="abs low">0/49</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="17" class="abs low">0/17</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="89" class="abs low">0/89</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class='push'></div><!-- for sticky footer -->
|
||||
</div><!-- /wrapper -->
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2025-08-13T15:07:03.165Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="sorter.js"></script>
|
||||
<script src="block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
643
coverage/lcov-report/popup.js.html
Normal file
643
coverage/lcov-report/popup.js.html
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for popup.js</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="prettify.css" />
|
||||
<link rel="stylesheet" href="base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type='text/css'>
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class='wrapper'>
|
||||
<div class='pad1'>
|
||||
<h1><a href="index.html">All files</a> popup.js</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/92</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/49</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/17</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/89</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input type="search" id="fileSearch">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a>
|
||||
<a name='L91'></a><a href='#L91'>91</a>
|
||||
<a name='L92'></a><a href='#L92'>92</a>
|
||||
<a name='L93'></a><a href='#L93'>93</a>
|
||||
<a name='L94'></a><a href='#L94'>94</a>
|
||||
<a name='L95'></a><a href='#L95'>95</a>
|
||||
<a name='L96'></a><a href='#L96'>96</a>
|
||||
<a name='L97'></a><a href='#L97'>97</a>
|
||||
<a name='L98'></a><a href='#L98'>98</a>
|
||||
<a name='L99'></a><a href='#L99'>99</a>
|
||||
<a name='L100'></a><a href='#L100'>100</a>
|
||||
<a name='L101'></a><a href='#L101'>101</a>
|
||||
<a name='L102'></a><a href='#L102'>102</a>
|
||||
<a name='L103'></a><a href='#L103'>103</a>
|
||||
<a name='L104'></a><a href='#L104'>104</a>
|
||||
<a name='L105'></a><a href='#L105'>105</a>
|
||||
<a name='L106'></a><a href='#L106'>106</a>
|
||||
<a name='L107'></a><a href='#L107'>107</a>
|
||||
<a name='L108'></a><a href='#L108'>108</a>
|
||||
<a name='L109'></a><a href='#L109'>109</a>
|
||||
<a name='L110'></a><a href='#L110'>110</a>
|
||||
<a name='L111'></a><a href='#L111'>111</a>
|
||||
<a name='L112'></a><a href='#L112'>112</a>
|
||||
<a name='L113'></a><a href='#L113'>113</a>
|
||||
<a name='L114'></a><a href='#L114'>114</a>
|
||||
<a name='L115'></a><a href='#L115'>115</a>
|
||||
<a name='L116'></a><a href='#L116'>116</a>
|
||||
<a name='L117'></a><a href='#L117'>117</a>
|
||||
<a name='L118'></a><a href='#L118'>118</a>
|
||||
<a name='L119'></a><a href='#L119'>119</a>
|
||||
<a name='L120'></a><a href='#L120'>120</a>
|
||||
<a name='L121'></a><a href='#L121'>121</a>
|
||||
<a name='L122'></a><a href='#L122'>122</a>
|
||||
<a name='L123'></a><a href='#L123'>123</a>
|
||||
<a name='L124'></a><a href='#L124'>124</a>
|
||||
<a name='L125'></a><a href='#L125'>125</a>
|
||||
<a name='L126'></a><a href='#L126'>126</a>
|
||||
<a name='L127'></a><a href='#L127'>127</a>
|
||||
<a name='L128'></a><a href='#L128'>128</a>
|
||||
<a name='L129'></a><a href='#L129'>129</a>
|
||||
<a name='L130'></a><a href='#L130'>130</a>
|
||||
<a name='L131'></a><a href='#L131'>131</a>
|
||||
<a name='L132'></a><a href='#L132'>132</a>
|
||||
<a name='L133'></a><a href='#L133'>133</a>
|
||||
<a name='L134'></a><a href='#L134'>134</a>
|
||||
<a name='L135'></a><a href='#L135'>135</a>
|
||||
<a name='L136'></a><a href='#L136'>136</a>
|
||||
<a name='L137'></a><a href='#L137'>137</a>
|
||||
<a name='L138'></a><a href='#L138'>138</a>
|
||||
<a name='L139'></a><a href='#L139'>139</a>
|
||||
<a name='L140'></a><a href='#L140'>140</a>
|
||||
<a name='L141'></a><a href='#L141'>141</a>
|
||||
<a name='L142'></a><a href='#L142'>142</a>
|
||||
<a name='L143'></a><a href='#L143'>143</a>
|
||||
<a name='L144'></a><a href='#L144'>144</a>
|
||||
<a name='L145'></a><a href='#L145'>145</a>
|
||||
<a name='L146'></a><a href='#L146'>146</a>
|
||||
<a name='L147'></a><a href='#L147'>147</a>
|
||||
<a name='L148'></a><a href='#L148'>148</a>
|
||||
<a name='L149'></a><a href='#L149'>149</a>
|
||||
<a name='L150'></a><a href='#L150'>150</a>
|
||||
<a name='L151'></a><a href='#L151'>151</a>
|
||||
<a name='L152'></a><a href='#L152'>152</a>
|
||||
<a name='L153'></a><a href='#L153'>153</a>
|
||||
<a name='L154'></a><a href='#L154'>154</a>
|
||||
<a name='L155'></a><a href='#L155'>155</a>
|
||||
<a name='L156'></a><a href='#L156'>156</a>
|
||||
<a name='L157'></a><a href='#L157'>157</a>
|
||||
<a name='L158'></a><a href='#L158'>158</a>
|
||||
<a name='L159'></a><a href='#L159'>159</a>
|
||||
<a name='L160'></a><a href='#L160'>160</a>
|
||||
<a name='L161'></a><a href='#L161'>161</a>
|
||||
<a name='L162'></a><a href='#L162'>162</a>
|
||||
<a name='L163'></a><a href='#L163'>163</a>
|
||||
<a name='L164'></a><a href='#L164'>164</a>
|
||||
<a name='L165'></a><a href='#L165'>165</a>
|
||||
<a name='L166'></a><a href='#L166'>166</a>
|
||||
<a name='L167'></a><a href='#L167'>167</a>
|
||||
<a name='L168'></a><a href='#L168'>168</a>
|
||||
<a name='L169'></a><a href='#L169'>169</a>
|
||||
<a name='L170'></a><a href='#L170'>170</a>
|
||||
<a name='L171'></a><a href='#L171'>171</a>
|
||||
<a name='L172'></a><a href='#L172'>172</a>
|
||||
<a name='L173'></a><a href='#L173'>173</a>
|
||||
<a name='L174'></a><a href='#L174'>174</a>
|
||||
<a name='L175'></a><a href='#L175'>175</a>
|
||||
<a name='L176'></a><a href='#L176'>176</a>
|
||||
<a name='L177'></a><a href='#L177'>177</a>
|
||||
<a name='L178'></a><a href='#L178'>178</a>
|
||||
<a name='L179'></a><a href='#L179'>179</a>
|
||||
<a name='L180'></a><a href='#L180'>180</a>
|
||||
<a name='L181'></a><a href='#L181'>181</a>
|
||||
<a name='L182'></a><a href='#L182'>182</a>
|
||||
<a name='L183'></a><a href='#L183'>183</a>
|
||||
<a name='L184'></a><a href='#L184'>184</a>
|
||||
<a name='L185'></a><a href='#L185'>185</a>
|
||||
<a name='L186'></a><a href='#L186'>186</a>
|
||||
<a name='L187'></a><a href='#L187'>187</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">// Popup script for Chrome extension
|
||||
<span class="cstat-no" title="statement not covered" >document.addEventListener('DOMContentLoaded', <span class="fstat-no" title="function not covered" >as</span>ync () => {</span>
|
||||
const backgroundSelect = <span class="cstat-no" title="statement not covered" >document.getElementById('background-select');</span>
|
||||
const clipboardToggle = <span class="cstat-no" title="statement not covered" >document.getElementById('clipboard-toggle');</span>
|
||||
const startBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('start-selector');</span>
|
||||
const stopBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('stop-selector');</span>
|
||||
const status = <span class="cstat-no" title="statement not covered" >document.getElementById('status');</span>
|
||||
|
||||
// Load saved preferences
|
||||
const saved = <span class="cstat-no" title="statement not covered" >await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (saved.backgroundPreference) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > backgroundSelect.value = saved.backgroundPreference;</span>
|
||||
}
|
||||
|
||||
// Set clipboard preference (default to true if not set)
|
||||
<span class="cstat-no" title="statement not covered" > clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;</span>
|
||||
|
||||
// Save background preference when changed
|
||||
<span class="cstat-no" title="statement not covered" > backgroundSelect.addEventListener('change', <span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > chrome.storage.sync.set({ backgroundPreference: backgroundSelect.value });</span>
|
||||
});
|
||||
|
||||
// Save clipboard preference when changed
|
||||
<span class="cstat-no" title="statement not covered" > clipboardToggle.addEventListener('change', <span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > chrome.storage.sync.set({ copyToClipboard: clipboardToggle.checked });</span>
|
||||
});
|
||||
|
||||
// Start selector
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.addEventListener('click', <span class="fstat-no" title="function not covered" >as</span>ync () => {</span>
|
||||
const [tab] = <span class="cstat-no" title="statement not covered" >await chrome.tabs.query({ active: true, currentWindow: true });</span>
|
||||
|
||||
// Check if page is compatible
|
||||
<span class="cstat-no" title="statement not covered" > if (tab.url.startsWith('chrome://') ||</span>
|
||||
tab.url.startsWith('chrome-extension://') ||
|
||||
tab.url.startsWith('edge://') ||
|
||||
tab.url.startsWith('about:') ||
|
||||
tab.url === 'chrome://newtab/' ||
|
||||
tab.url === 'edge://newtab/') {
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Error: Cannot capture screenshots on this page type.', 'error');</span>
|
||||
<span class="cstat-no" title="statement not covered" > return;</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
<span class="cstat-no" title="statement not covered" > await chrome.tabs.sendMessage(tab.id, {</span>
|
||||
action: 'startSelector',
|
||||
background: backgroundSelect.value,
|
||||
copyToClipboard: clipboardToggle.checked
|
||||
});
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Selector activated! Hover over elements and click to capture.', 'success');</span>
|
||||
<span class="cstat-no" title="statement not covered" > toggleButtons(true);</span>
|
||||
|
||||
// Auto-close popup after starting
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => <span class="cstat-no" title="statement not covered" >window.close(),</span> 1500);</span>
|
||||
|
||||
} catch (error) {
|
||||
<span class="cstat-no" title="statement not covered" > console.error('Full error details:', error);</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (error.message.includes('Could not establish connection')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Error: Content script not loaded. Try refreshing the page.', 'error');</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Error: Could not activate on this page.', 'error');</span>
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Stop selector
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.addEventListener('click', <span class="fstat-no" title="function not covered" >as</span>ync () => {</span>
|
||||
const [tab] = <span class="cstat-no" title="statement not covered" >await chrome.tabs.query({ active: true, currentWindow: true });</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
<span class="cstat-no" title="statement not covered" > await chrome.tabs.sendMessage(tab.id, { action: 'stopSelector' });</span>
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Selector stopped.', 'success');</span>
|
||||
<span class="cstat-no" title="statement not covered" > toggleButtons(false);</span>
|
||||
} catch (error) {
|
||||
<span class="cstat-no" title="statement not covered" > console.error('Error stopping selector:', error);</span>
|
||||
}
|
||||
});
|
||||
|
||||
// Check current state
|
||||
<span class="cstat-no" title="statement not covered" > checkSelectorState();</span>
|
||||
|
||||
// Handle tooltip keyboard navigation
|
||||
<span class="cstat-no" title="statement not covered" > setupTooltipAccessibility();</span>
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >toggleButtons(</span>isActive) {
|
||||
<span class="cstat-no" title="statement not covered" > if (isActive) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.style.display = 'none';</span>
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.style.display = 'block';</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.style.display = 'block';</span>
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.style.display = 'none';</span>
|
||||
}
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >showStatus(</span>message, type = <span class="branch-0 cbranch-no" title="branch not covered" >'success')</span> {
|
||||
<span class="cstat-no" title="statement not covered" > status.textContent = message;</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.className = `status ${type}`;</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.style.display = 'block';</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.style.display = 'none';</span>
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
async function <span class="fstat-no" title="function not covered" >checkSelectorState(</span>) {
|
||||
const [tab] = <span class="cstat-no" title="statement not covered" >await chrome.tabs.query({ active: true, currentWindow: true });</span>
|
||||
|
||||
// Skip check for incompatible pages
|
||||
<span class="cstat-no" title="statement not covered" > if (tab.url.startsWith('chrome://') ||</span>
|
||||
tab.url.startsWith('chrome-extension://') ||
|
||||
tab.url.startsWith('edge://') ||
|
||||
tab.url.startsWith('about:')) {
|
||||
<span class="cstat-no" title="statement not covered" > return;</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
const response = <span class="cstat-no" title="statement not covered" >await chrome.tabs.sendMessage(tab.id, { action: 'checkState' });</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (response && response.isActive) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > toggleButtons(true);</span>
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Selector is currently active', 'success');</span>
|
||||
}
|
||||
} catch (error) {
|
||||
// Content script not ready or selector not active
|
||||
<span class="cstat-no" title="statement not covered" > console.log('Content script not ready or selector inactive:', error.message);</span>
|
||||
}
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >setupTooltipAccessibility(</span>) {
|
||||
const tooltipIcon = <span class="cstat-no" title="statement not covered" >document.querySelector('.tooltip-icon');</span>
|
||||
const tooltipContent = <span class="cstat-no" title="statement not covered" >document.querySelector('.tooltip-content');</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (!tooltipIcon || !tooltipContent) <span class="cstat-no" title="statement not covered" >return;</span></span>
|
||||
|
||||
// Handle keyboard events for tooltip
|
||||
<span class="cstat-no" title="statement not covered" > tooltipIcon.addEventListener('keydown', <span class="fstat-no" title="function not covered" >(e</span>) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (e.key === 'Enter' || e.key === ' ') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault();</span>
|
||||
// Toggle tooltip visibility on Enter/Space
|
||||
const isVisible = <span class="cstat-no" title="statement not covered" >tooltipContent.style.visibility === 'visible';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = isVisible ? 'hidden' : 'visible';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = isVisible ? '0' : '1';</span>
|
||||
} else <span class="cstat-no" title="statement not covered" >if (e.key === 'Escape') {</span>
|
||||
// Hide tooltip on Escape
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = 'hidden';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = '0';</span>
|
||||
}
|
||||
});
|
||||
|
||||
// Hide tooltip when clicking outside
|
||||
<span class="cstat-no" title="statement not covered" > document.addEventListener('click', <span class="fstat-no" title="function not covered" >(e</span>) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!tooltipIcon.contains(e.target)) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = 'hidden';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = '0';</span>
|
||||
}
|
||||
});
|
||||
|
||||
// Handle focus loss
|
||||
<span class="cstat-no" title="statement not covered" > tooltipIcon.addEventListener('blur', <span class="fstat-no" title="function not covered" >(e</span>) => {</span>
|
||||
// Small delay to allow for focus to move to tooltip content if needed
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!tooltipIcon.matches(':focus-within')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = 'hidden';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = '0';</span>
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for messages from content script
|
||||
<span class="cstat-no" title="statement not covered" >chrome.runtime.onMessage.addListener(<span class="fstat-no" title="function not covered" >(m</span>essage, sender, sendResponse) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (message.action === 'selectorStopped') {</span>
|
||||
const startBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('start-selector');</span>
|
||||
const stopBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('stop-selector');</span>
|
||||
const status = <span class="cstat-no" title="statement not covered" >document.getElementById('status');</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.style.display = 'block';</span>
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.style.display = 'none';</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (message.reason === 'screenshot-taken') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.textContent = 'Screenshot captured!';</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.className = 'status success';</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.style.display = 'block';</span>
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => <span class="cstat-no" title="statement not covered" >status.style.display = 'none',</span> 2000);</span>
|
||||
}
|
||||
}
|
||||
});</pre></td></tr></table></pre>
|
||||
|
||||
<div class='push'></div><!-- for sticky footer -->
|
||||
</div><!-- /wrapper -->
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2025-08-13T15:07:03.165Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="sorter.js"></script>
|
||||
<script src="block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
1
coverage/lcov-report/prettify.css
Normal file
1
coverage/lcov-report/prettify.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
|
||||
2
coverage/lcov-report/prettify.js
Normal file
2
coverage/lcov-report/prettify.js
Normal file
File diff suppressed because one or more lines are too long
BIN
coverage/lcov-report/sort-arrow-sprite.png
Normal file
BIN
coverage/lcov-report/sort-arrow-sprite.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 B |
196
coverage/lcov-report/sorter.js
Normal file
196
coverage/lcov-report/sorter.js
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/* eslint-disable */
|
||||
var addSorting = (function() {
|
||||
'use strict';
|
||||
var cols,
|
||||
currentSort = {
|
||||
index: 0,
|
||||
desc: false
|
||||
};
|
||||
|
||||
// returns the summary table element
|
||||
function getTable() {
|
||||
return document.querySelector('.coverage-summary');
|
||||
}
|
||||
// returns the thead element of the summary table
|
||||
function getTableHeader() {
|
||||
return getTable().querySelector('thead tr');
|
||||
}
|
||||
// returns the tbody element of the summary table
|
||||
function getTableBody() {
|
||||
return getTable().querySelector('tbody');
|
||||
}
|
||||
// returns the th element for nth column
|
||||
function getNthColumn(n) {
|
||||
return getTableHeader().querySelectorAll('th')[n];
|
||||
}
|
||||
|
||||
function onFilterInput() {
|
||||
const searchValue = document.getElementById('fileSearch').value;
|
||||
const rows = document.getElementsByTagName('tbody')[0].children;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i];
|
||||
if (
|
||||
row.textContent
|
||||
.toLowerCase()
|
||||
.includes(searchValue.toLowerCase())
|
||||
) {
|
||||
row.style.display = '';
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loads the search box
|
||||
function addSearchBox() {
|
||||
var template = document.getElementById('filterTemplate');
|
||||
var templateClone = template.content.cloneNode(true);
|
||||
templateClone.getElementById('fileSearch').oninput = onFilterInput;
|
||||
template.parentElement.appendChild(templateClone);
|
||||
}
|
||||
|
||||
// loads all columns
|
||||
function loadColumns() {
|
||||
var colNodes = getTableHeader().querySelectorAll('th'),
|
||||
colNode,
|
||||
cols = [],
|
||||
col,
|
||||
i;
|
||||
|
||||
for (i = 0; i < colNodes.length; i += 1) {
|
||||
colNode = colNodes[i];
|
||||
col = {
|
||||
key: colNode.getAttribute('data-col'),
|
||||
sortable: !colNode.getAttribute('data-nosort'),
|
||||
type: colNode.getAttribute('data-type') || 'string'
|
||||
};
|
||||
cols.push(col);
|
||||
if (col.sortable) {
|
||||
col.defaultDescSort = col.type === 'number';
|
||||
colNode.innerHTML =
|
||||
colNode.innerHTML + '<span class="sorter"></span>';
|
||||
}
|
||||
}
|
||||
return cols;
|
||||
}
|
||||
// attaches a data attribute to every tr element with an object
|
||||
// of data values keyed by column name
|
||||
function loadRowData(tableRow) {
|
||||
var tableCols = tableRow.querySelectorAll('td'),
|
||||
colNode,
|
||||
col,
|
||||
data = {},
|
||||
i,
|
||||
val;
|
||||
for (i = 0; i < tableCols.length; i += 1) {
|
||||
colNode = tableCols[i];
|
||||
col = cols[i];
|
||||
val = colNode.getAttribute('data-value');
|
||||
if (col.type === 'number') {
|
||||
val = Number(val);
|
||||
}
|
||||
data[col.key] = val;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// loads all row data
|
||||
function loadData() {
|
||||
var rows = getTableBody().querySelectorAll('tr'),
|
||||
i;
|
||||
|
||||
for (i = 0; i < rows.length; i += 1) {
|
||||
rows[i].data = loadRowData(rows[i]);
|
||||
}
|
||||
}
|
||||
// sorts the table using the data for the ith column
|
||||
function sortByIndex(index, desc) {
|
||||
var key = cols[index].key,
|
||||
sorter = function(a, b) {
|
||||
a = a.data[key];
|
||||
b = b.data[key];
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
},
|
||||
finalSorter = sorter,
|
||||
tableBody = document.querySelector('.coverage-summary tbody'),
|
||||
rowNodes = tableBody.querySelectorAll('tr'),
|
||||
rows = [],
|
||||
i;
|
||||
|
||||
if (desc) {
|
||||
finalSorter = function(a, b) {
|
||||
return -1 * sorter(a, b);
|
||||
};
|
||||
}
|
||||
|
||||
for (i = 0; i < rowNodes.length; i += 1) {
|
||||
rows.push(rowNodes[i]);
|
||||
tableBody.removeChild(rowNodes[i]);
|
||||
}
|
||||
|
||||
rows.sort(finalSorter);
|
||||
|
||||
for (i = 0; i < rows.length; i += 1) {
|
||||
tableBody.appendChild(rows[i]);
|
||||
}
|
||||
}
|
||||
// removes sort indicators for current column being sorted
|
||||
function removeSortIndicators() {
|
||||
var col = getNthColumn(currentSort.index),
|
||||
cls = col.className;
|
||||
|
||||
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
|
||||
col.className = cls;
|
||||
}
|
||||
// adds sort indicators for current column being sorted
|
||||
function addSortIndicators() {
|
||||
getNthColumn(currentSort.index).className += currentSort.desc
|
||||
? ' sorted-desc'
|
||||
: ' sorted';
|
||||
}
|
||||
// adds event listeners for all sorter widgets
|
||||
function enableUI() {
|
||||
var i,
|
||||
el,
|
||||
ithSorter = function ithSorter(i) {
|
||||
var col = cols[i];
|
||||
|
||||
return function() {
|
||||
var desc = col.defaultDescSort;
|
||||
|
||||
if (currentSort.index === i) {
|
||||
desc = !currentSort.desc;
|
||||
}
|
||||
sortByIndex(i, desc);
|
||||
removeSortIndicators();
|
||||
currentSort.index = i;
|
||||
currentSort.desc = desc;
|
||||
addSortIndicators();
|
||||
};
|
||||
};
|
||||
for (i = 0; i < cols.length; i += 1) {
|
||||
if (cols[i].sortable) {
|
||||
// add the click event handler on the th so users
|
||||
// dont have to click on those tiny arrows
|
||||
el = getNthColumn(i).querySelector('.sorter').parentElement;
|
||||
if (el.addEventListener) {
|
||||
el.addEventListener('click', ithSorter(i));
|
||||
} else {
|
||||
el.attachEvent('onclick', ithSorter(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// adds sorting functionality to the UI
|
||||
return function() {
|
||||
if (!getTable()) {
|
||||
return;
|
||||
}
|
||||
cols = loadColumns();
|
||||
loadData();
|
||||
addSearchBox();
|
||||
addSortIndicators();
|
||||
enableUI();
|
||||
};
|
||||
})();
|
||||
|
||||
window.addEventListener('load', addSorting);
|
||||
848
coverage/lcov.info
Normal file
848
coverage/lcov.info
Normal file
|
|
@ -0,0 +1,848 @@
|
|||
TN:
|
||||
SF:content.js
|
||||
FN:3,(anonymous_0)
|
||||
FN:12,(anonymous_1)
|
||||
FN:26,(anonymous_2)
|
||||
FN:76,(anonymous_3)
|
||||
FN:79,(anonymous_4)
|
||||
FN:89,(anonymous_5)
|
||||
FN:105,(anonymous_6)
|
||||
FN:114,(anonymous_7)
|
||||
FN:120,(anonymous_8)
|
||||
FN:176,(anonymous_9)
|
||||
FN:178,(anonymous_10)
|
||||
FN:182,(anonymous_11)
|
||||
FN:184,(anonymous_12)
|
||||
FN:201,(anonymous_13)
|
||||
FN:215,(anonymous_14)
|
||||
FN:249,(anonymous_15)
|
||||
FN:273,(anonymous_16)
|
||||
FN:275,(anonymous_17)
|
||||
FN:291,(anonymous_18)
|
||||
FN:311,(anonymous_19)
|
||||
FN:332,(anonymous_20)
|
||||
FN:336,(anonymous_21)
|
||||
FN:346,(anonymous_22)
|
||||
FN:347,(anonymous_23)
|
||||
FN:361,(anonymous_24)
|
||||
FN:366,(anonymous_25)
|
||||
FN:435,(anonymous_26)
|
||||
FN:441,(anonymous_27)
|
||||
FN:455,(anonymous_28)
|
||||
FN:544,(anonymous_29)
|
||||
FN:578,(anonymous_30)
|
||||
FN:586,(anonymous_31)
|
||||
FN:628,(anonymous_32)
|
||||
FN:662,(anonymous_33)
|
||||
FN:728,(anonymous_34)
|
||||
FN:742,(anonymous_35)
|
||||
FN:743,(anonymous_36)
|
||||
FN:751,(anonymous_37)
|
||||
FN:752,(anonymous_38)
|
||||
FN:783,(anonymous_39)
|
||||
FN:784,(anonymous_40)
|
||||
FN:856,(anonymous_41)
|
||||
FN:890,(anonymous_42)
|
||||
FN:931,(anonymous_43)
|
||||
FNF:44
|
||||
FNH:0
|
||||
FNDA:0,(anonymous_0)
|
||||
FNDA:0,(anonymous_1)
|
||||
FNDA:0,(anonymous_2)
|
||||
FNDA:0,(anonymous_3)
|
||||
FNDA:0,(anonymous_4)
|
||||
FNDA:0,(anonymous_5)
|
||||
FNDA:0,(anonymous_6)
|
||||
FNDA:0,(anonymous_7)
|
||||
FNDA:0,(anonymous_8)
|
||||
FNDA:0,(anonymous_9)
|
||||
FNDA:0,(anonymous_10)
|
||||
FNDA:0,(anonymous_11)
|
||||
FNDA:0,(anonymous_12)
|
||||
FNDA:0,(anonymous_13)
|
||||
FNDA:0,(anonymous_14)
|
||||
FNDA:0,(anonymous_15)
|
||||
FNDA:0,(anonymous_16)
|
||||
FNDA:0,(anonymous_17)
|
||||
FNDA:0,(anonymous_18)
|
||||
FNDA:0,(anonymous_19)
|
||||
FNDA:0,(anonymous_20)
|
||||
FNDA:0,(anonymous_21)
|
||||
FNDA:0,(anonymous_22)
|
||||
FNDA:0,(anonymous_23)
|
||||
FNDA:0,(anonymous_24)
|
||||
FNDA:0,(anonymous_25)
|
||||
FNDA:0,(anonymous_26)
|
||||
FNDA:0,(anonymous_27)
|
||||
FNDA:0,(anonymous_28)
|
||||
FNDA:0,(anonymous_29)
|
||||
FNDA:0,(anonymous_30)
|
||||
FNDA:0,(anonymous_31)
|
||||
FNDA:0,(anonymous_32)
|
||||
FNDA:0,(anonymous_33)
|
||||
FNDA:0,(anonymous_34)
|
||||
FNDA:0,(anonymous_35)
|
||||
FNDA:0,(anonymous_36)
|
||||
FNDA:0,(anonymous_37)
|
||||
FNDA:0,(anonymous_38)
|
||||
FNDA:0,(anonymous_39)
|
||||
FNDA:0,(anonymous_40)
|
||||
FNDA:0,(anonymous_41)
|
||||
FNDA:0,(anonymous_42)
|
||||
FNDA:0,(anonymous_43)
|
||||
DA:4,0
|
||||
DA:5,0
|
||||
DA:6,0
|
||||
DA:7,0
|
||||
DA:8,0
|
||||
DA:9,0
|
||||
DA:13,0
|
||||
DA:14,0
|
||||
DA:15,0
|
||||
DA:18,0
|
||||
DA:19,0
|
||||
DA:20,0
|
||||
DA:22,0
|
||||
DA:23,0
|
||||
DA:28,0
|
||||
DA:29,0
|
||||
DA:30,0
|
||||
DA:41,0
|
||||
DA:42,0
|
||||
DA:72,0
|
||||
DA:73,0
|
||||
DA:76,0
|
||||
DA:80,0
|
||||
DA:81,0
|
||||
DA:82,0
|
||||
DA:84,0
|
||||
DA:85,0
|
||||
DA:86,0
|
||||
DA:90,0
|
||||
DA:93,0
|
||||
DA:94,0
|
||||
DA:95,0
|
||||
DA:98,0
|
||||
DA:99,0
|
||||
DA:102,0
|
||||
DA:106,0
|
||||
DA:107,0
|
||||
DA:108,0
|
||||
DA:110,0
|
||||
DA:111,0
|
||||
DA:115,0
|
||||
DA:116,0
|
||||
DA:121,0
|
||||
DA:122,0
|
||||
DA:123,0
|
||||
DA:125,0
|
||||
DA:128,0
|
||||
DA:129,0
|
||||
DA:130,0
|
||||
DA:131,0
|
||||
DA:134,0
|
||||
DA:135,0
|
||||
DA:136,0
|
||||
DA:137,0
|
||||
DA:140,0
|
||||
DA:147,0
|
||||
DA:148,0
|
||||
DA:151,0
|
||||
DA:152,0
|
||||
DA:153,0
|
||||
DA:167,0
|
||||
DA:168,0
|
||||
DA:169,0
|
||||
DA:170,0
|
||||
DA:172,0
|
||||
DA:177,0
|
||||
DA:178,0
|
||||
DA:184,0
|
||||
DA:185,0
|
||||
DA:186,0
|
||||
DA:187,0
|
||||
DA:191,0
|
||||
DA:194,0
|
||||
DA:195,0
|
||||
DA:198,0
|
||||
DA:199,0
|
||||
DA:201,0
|
||||
DA:203,0
|
||||
DA:204,0
|
||||
DA:209,0
|
||||
DA:210,0
|
||||
DA:211,0
|
||||
DA:216,0
|
||||
DA:218,0
|
||||
DA:221,0
|
||||
DA:249,0
|
||||
DA:250,0
|
||||
DA:251,0
|
||||
DA:252,0
|
||||
DA:253,0
|
||||
DA:261,0
|
||||
DA:264,0
|
||||
DA:265,0
|
||||
DA:267,0
|
||||
DA:268,0
|
||||
DA:274,0
|
||||
DA:275,0
|
||||
DA:276,0
|
||||
DA:277,0
|
||||
DA:280,0
|
||||
DA:281,0
|
||||
DA:289,0
|
||||
DA:290,0
|
||||
DA:291,0
|
||||
DA:292,0
|
||||
DA:293,0
|
||||
DA:294,0
|
||||
DA:297,0
|
||||
DA:300,0
|
||||
DA:301,0
|
||||
DA:302,0
|
||||
DA:313,0
|
||||
DA:314,0
|
||||
DA:315,0
|
||||
DA:322,0
|
||||
DA:329,0
|
||||
DA:330,0
|
||||
DA:332,0
|
||||
DA:333,0
|
||||
DA:334,0
|
||||
DA:336,0
|
||||
DA:337,0
|
||||
DA:339,0
|
||||
DA:340,0
|
||||
DA:341,0
|
||||
DA:342,0
|
||||
DA:343,0
|
||||
DA:344,0
|
||||
DA:346,0
|
||||
DA:347,0
|
||||
DA:348,0
|
||||
DA:349,0
|
||||
DA:352,0
|
||||
DA:358,0
|
||||
DA:361,0
|
||||
DA:367,0
|
||||
DA:369,0
|
||||
DA:370,0
|
||||
DA:371,0
|
||||
DA:372,0
|
||||
DA:373,0
|
||||
DA:375,0
|
||||
DA:380,0
|
||||
DA:396,0
|
||||
DA:397,0
|
||||
DA:400,0
|
||||
DA:411,0
|
||||
DA:412,0
|
||||
DA:413,0
|
||||
DA:414,0
|
||||
DA:415,0
|
||||
DA:418,0
|
||||
DA:421,0
|
||||
DA:437,0
|
||||
DA:438,0
|
||||
DA:441,0
|
||||
DA:442,0
|
||||
DA:443,0
|
||||
DA:445,0
|
||||
DA:446,0
|
||||
DA:447,0
|
||||
DA:448,0
|
||||
DA:449,0
|
||||
DA:450,0
|
||||
DA:452,0
|
||||
DA:453,0
|
||||
DA:454,0
|
||||
DA:455,0
|
||||
DA:456,0
|
||||
DA:459,0
|
||||
DA:461,0
|
||||
DA:465,0
|
||||
DA:470,0
|
||||
DA:471,0
|
||||
DA:472,0
|
||||
DA:473,0
|
||||
DA:476,0
|
||||
DA:481,0
|
||||
DA:484,0
|
||||
DA:485,0
|
||||
DA:486,0
|
||||
DA:487,0
|
||||
DA:490,0
|
||||
DA:491,0
|
||||
DA:492,0
|
||||
DA:493,0
|
||||
DA:494,0
|
||||
DA:496,0
|
||||
DA:500,0
|
||||
DA:501,0
|
||||
DA:508,0
|
||||
DA:517,0
|
||||
DA:518,0
|
||||
DA:520,0
|
||||
DA:521,0
|
||||
DA:522,0
|
||||
DA:526,0
|
||||
DA:527,0
|
||||
DA:528,0
|
||||
DA:532,0
|
||||
DA:542,0
|
||||
DA:544,0
|
||||
DA:547,0
|
||||
DA:550,0
|
||||
DA:552,0
|
||||
DA:553,0
|
||||
DA:554,0
|
||||
DA:555,0
|
||||
DA:556,0
|
||||
DA:557,0
|
||||
DA:558,0
|
||||
DA:559,0
|
||||
DA:560,0
|
||||
DA:561,0
|
||||
DA:562,0
|
||||
DA:566,0
|
||||
DA:576,0
|
||||
DA:578,0
|
||||
DA:588,0
|
||||
DA:589,0
|
||||
DA:596,0
|
||||
DA:597,0
|
||||
DA:604,0
|
||||
DA:605,0
|
||||
DA:612,0
|
||||
DA:613,0
|
||||
DA:619,0
|
||||
DA:629,0
|
||||
DA:631,0
|
||||
DA:632,0
|
||||
DA:639,0
|
||||
DA:641,0
|
||||
DA:642,0
|
||||
DA:643,0
|
||||
DA:644,0
|
||||
DA:645,0
|
||||
DA:646,0
|
||||
DA:648,0
|
||||
DA:651,0
|
||||
DA:663,0
|
||||
DA:664,0
|
||||
DA:667,0
|
||||
DA:668,0
|
||||
DA:671,0
|
||||
DA:673,0
|
||||
DA:677,0
|
||||
DA:680,0
|
||||
DA:684,0
|
||||
DA:687,0
|
||||
DA:690,0
|
||||
DA:693,0
|
||||
DA:696,0
|
||||
DA:699,0
|
||||
DA:702,0
|
||||
DA:705,0
|
||||
DA:709,0
|
||||
DA:710,0
|
||||
DA:711,0
|
||||
DA:712,0
|
||||
DA:713,0
|
||||
DA:714,0
|
||||
DA:715,0
|
||||
DA:716,0
|
||||
DA:718,0
|
||||
DA:729,0
|
||||
DA:731,0
|
||||
DA:732,0
|
||||
DA:733,0
|
||||
DA:741,0
|
||||
DA:743,0
|
||||
DA:744,0
|
||||
DA:745,0
|
||||
DA:747,0
|
||||
DA:752,0
|
||||
DA:757,0
|
||||
DA:758,0
|
||||
DA:759,0
|
||||
DA:768,0
|
||||
DA:769,0
|
||||
DA:773,0
|
||||
DA:781,0
|
||||
DA:784,0
|
||||
DA:788,0
|
||||
DA:791,0
|
||||
DA:794,0
|
||||
DA:795,0
|
||||
DA:798,0
|
||||
DA:799,0
|
||||
DA:800,0
|
||||
DA:803,0
|
||||
DA:804,0
|
||||
DA:805,0
|
||||
DA:808,0
|
||||
DA:809,0
|
||||
DA:810,0
|
||||
DA:813,0
|
||||
DA:814,0
|
||||
DA:815,0
|
||||
DA:818,0
|
||||
DA:819,0
|
||||
DA:820,0
|
||||
DA:823,0
|
||||
DA:824,0
|
||||
DA:825,0
|
||||
DA:828,0
|
||||
DA:829,0
|
||||
DA:830,0
|
||||
DA:833,0
|
||||
DA:834,0
|
||||
DA:835,0
|
||||
DA:838,0
|
||||
DA:839,0
|
||||
DA:840,0
|
||||
DA:844,0
|
||||
DA:845,0
|
||||
DA:848,0
|
||||
DA:857,0
|
||||
DA:858,0
|
||||
DA:859,0
|
||||
DA:861,0
|
||||
DA:862,0
|
||||
DA:866,0
|
||||
DA:868,0
|
||||
DA:869,0
|
||||
DA:870,0
|
||||
DA:873,0
|
||||
DA:874,0
|
||||
DA:875,0
|
||||
DA:878,0
|
||||
DA:879,0
|
||||
DA:884,0
|
||||
DA:887,0
|
||||
DA:890,0
|
||||
DA:891,0
|
||||
DA:892,0
|
||||
DA:895,0
|
||||
DA:896,0
|
||||
DA:897,0
|
||||
DA:898,0
|
||||
DA:901,0
|
||||
DA:902,0
|
||||
DA:903,0
|
||||
DA:906,0
|
||||
DA:907,0
|
||||
DA:910,0
|
||||
DA:911,0
|
||||
DA:912,0
|
||||
DA:916,0
|
||||
DA:919,0
|
||||
DA:922,0
|
||||
DA:923,0
|
||||
DA:927,0
|
||||
DA:931,0
|
||||
DA:932,0
|
||||
DA:933,0
|
||||
DA:934,0
|
||||
LF:356
|
||||
LH:0
|
||||
BRDA:12,0,0,0
|
||||
BRDA:12,1,0,0
|
||||
BRDA:13,2,0,0
|
||||
BRDA:13,2,1,0
|
||||
BRDA:90,3,0,0
|
||||
BRDA:90,3,1,0
|
||||
BRDA:93,4,0,0
|
||||
BRDA:93,4,1,0
|
||||
BRDA:106,5,0,0
|
||||
BRDA:106,5,1,0
|
||||
BRDA:115,6,0,0
|
||||
BRDA:115,6,1,0
|
||||
BRDA:125,7,0,0
|
||||
BRDA:125,7,1,0
|
||||
BRDA:125,8,0,0
|
||||
BRDA:125,8,1,0
|
||||
BRDA:134,9,0,0
|
||||
BRDA:134,9,1,0
|
||||
BRDA:134,10,0,0
|
||||
BRDA:134,10,1,0
|
||||
BRDA:143,11,0,0
|
||||
BRDA:143,11,1,0
|
||||
BRDA:144,12,0,0
|
||||
BRDA:144,12,1,0
|
||||
BRDA:168,13,0,0
|
||||
BRDA:168,13,1,0
|
||||
BRDA:169,14,0,0
|
||||
BRDA:169,14,1,0
|
||||
BRDA:186,15,0,0
|
||||
BRDA:186,15,1,0
|
||||
BRDA:186,16,0,0
|
||||
BRDA:186,16,1,0
|
||||
BRDA:194,17,0,0
|
||||
BRDA:194,17,1,0
|
||||
BRDA:198,18,0,0
|
||||
BRDA:198,18,1,0
|
||||
BRDA:198,19,0,0
|
||||
BRDA:198,19,1,0
|
||||
BRDA:203,20,0,0
|
||||
BRDA:203,20,1,0
|
||||
BRDA:209,21,0,0
|
||||
BRDA:209,21,1,0
|
||||
BRDA:216,22,0,0
|
||||
BRDA:216,22,1,0
|
||||
BRDA:216,23,0,0
|
||||
BRDA:216,23,1,0
|
||||
BRDA:251,24,0,0
|
||||
BRDA:251,24,1,0
|
||||
BRDA:251,25,0,0
|
||||
BRDA:251,25,1,0
|
||||
BRDA:251,25,2,0
|
||||
BRDA:251,25,3,0
|
||||
BRDA:251,25,4,0
|
||||
BRDA:280,26,0,0
|
||||
BRDA:280,26,1,0
|
||||
BRDA:280,27,0,0
|
||||
BRDA:280,27,1,0
|
||||
BRDA:280,27,2,0
|
||||
BRDA:293,28,0,0
|
||||
BRDA:293,28,1,0
|
||||
BRDA:293,29,0,0
|
||||
BRDA:293,29,1,0
|
||||
BRDA:293,29,2,0
|
||||
BRDA:313,30,0,0
|
||||
BRDA:313,30,1,0
|
||||
BRDA:313,31,0,0
|
||||
BRDA:313,31,1,0
|
||||
BRDA:337,32,0,0
|
||||
BRDA:337,32,1,0
|
||||
BRDA:370,33,0,0
|
||||
BRDA:370,33,1,0
|
||||
BRDA:372,34,0,0
|
||||
BRDA:372,34,1,0
|
||||
BRDA:424,35,0,0
|
||||
BRDA:424,35,1,0
|
||||
BRDA:425,36,0,0
|
||||
BRDA:425,36,1,0
|
||||
BRDA:428,37,0,0
|
||||
BRDA:428,37,1,0
|
||||
BRDA:443,38,0,0
|
||||
BRDA:443,38,1,0
|
||||
BRDA:450,39,0,0
|
||||
BRDA:450,39,1,0
|
||||
BRDA:450,40,0,0
|
||||
BRDA:450,40,1,0
|
||||
BRDA:472,41,0,0
|
||||
BRDA:472,41,1,0
|
||||
BRDA:472,42,0,0
|
||||
BRDA:472,42,1,0
|
||||
BRDA:491,43,0,0
|
||||
BRDA:491,43,1,0
|
||||
BRDA:493,44,0,0
|
||||
BRDA:493,44,1,0
|
||||
BRDA:520,45,0,0
|
||||
BRDA:520,45,1,0
|
||||
BRDA:521,46,0,0
|
||||
BRDA:521,46,1,0
|
||||
BRDA:521,47,0,0
|
||||
BRDA:521,47,1,0
|
||||
BRDA:552,48,0,0
|
||||
BRDA:552,48,1,0
|
||||
BRDA:553,49,0,0
|
||||
BRDA:553,49,1,0
|
||||
BRDA:555,50,0,0
|
||||
BRDA:555,50,1,0
|
||||
BRDA:557,51,0,0
|
||||
BRDA:557,51,1,0
|
||||
BRDA:557,52,0,0
|
||||
BRDA:557,52,1,0
|
||||
BRDA:559,53,0,0
|
||||
BRDA:559,53,1,0
|
||||
BRDA:559,54,0,0
|
||||
BRDA:559,54,1,0
|
||||
BRDA:561,55,0,0
|
||||
BRDA:561,55,1,0
|
||||
BRDA:588,56,0,0
|
||||
BRDA:588,56,1,0
|
||||
BRDA:596,57,0,0
|
||||
BRDA:596,57,1,0
|
||||
BRDA:604,58,0,0
|
||||
BRDA:604,58,1,0
|
||||
BRDA:612,59,0,0
|
||||
BRDA:612,59,1,0
|
||||
BRDA:631,60,0,0
|
||||
BRDA:631,60,1,0
|
||||
BRDA:641,61,0,0
|
||||
BRDA:641,61,1,0
|
||||
BRDA:641,62,0,0
|
||||
BRDA:641,62,1,0
|
||||
BRDA:643,63,0,0
|
||||
BRDA:643,63,1,0
|
||||
BRDA:645,64,0,0
|
||||
BRDA:645,64,1,0
|
||||
BRDA:663,65,0,0
|
||||
BRDA:663,65,1,0
|
||||
BRDA:663,66,0,0
|
||||
BRDA:663,66,1,0
|
||||
BRDA:671,67,0,0
|
||||
BRDA:671,67,1,0
|
||||
BRDA:671,67,2,0
|
||||
BRDA:671,67,3,0
|
||||
BRDA:671,67,4,0
|
||||
BRDA:671,67,5,0
|
||||
BRDA:671,67,6,0
|
||||
BRDA:671,67,7,0
|
||||
BRDA:671,67,8,0
|
||||
BRDA:671,67,9,0
|
||||
BRDA:671,67,10,0
|
||||
BRDA:671,67,11,0
|
||||
BRDA:671,67,12,0
|
||||
BRDA:671,67,13,0
|
||||
BRDA:709,68,0,0
|
||||
BRDA:709,68,1,0
|
||||
BRDA:709,69,0,0
|
||||
BRDA:709,69,1,0
|
||||
BRDA:711,70,0,0
|
||||
BRDA:711,70,1,0
|
||||
BRDA:711,71,0,0
|
||||
BRDA:711,71,1,0
|
||||
BRDA:713,72,0,0
|
||||
BRDA:713,72,1,0
|
||||
BRDA:713,73,0,0
|
||||
BRDA:713,73,1,0
|
||||
BRDA:715,74,0,0
|
||||
BRDA:715,74,1,0
|
||||
BRDA:715,75,0,0
|
||||
BRDA:715,75,1,0
|
||||
BRDA:732,76,0,0
|
||||
BRDA:732,76,1,0
|
||||
BRDA:744,77,0,0
|
||||
BRDA:744,77,1,0
|
||||
BRDA:758,78,0,0
|
||||
BRDA:758,78,1,0
|
||||
BRDA:794,79,0,0
|
||||
BRDA:794,79,1,0
|
||||
BRDA:798,80,0,0
|
||||
BRDA:798,80,1,0
|
||||
BRDA:803,81,0,0
|
||||
BRDA:803,81,1,0
|
||||
BRDA:808,82,0,0
|
||||
BRDA:808,82,1,0
|
||||
BRDA:813,83,0,0
|
||||
BRDA:813,83,1,0
|
||||
BRDA:818,84,0,0
|
||||
BRDA:818,84,1,0
|
||||
BRDA:823,85,0,0
|
||||
BRDA:823,85,1,0
|
||||
BRDA:823,86,0,0
|
||||
BRDA:823,86,1,0
|
||||
BRDA:828,87,0,0
|
||||
BRDA:828,87,1,0
|
||||
BRDA:828,88,0,0
|
||||
BRDA:828,88,1,0
|
||||
BRDA:833,89,0,0
|
||||
BRDA:833,89,1,0
|
||||
BRDA:838,90,0,0
|
||||
BRDA:838,90,1,0
|
||||
BRDA:861,91,0,0
|
||||
BRDA:861,91,1,0
|
||||
BRDA:868,92,0,0
|
||||
BRDA:868,92,1,0
|
||||
BRDA:873,93,0,0
|
||||
BRDA:873,93,1,0
|
||||
BRDA:892,94,0,0
|
||||
BRDA:892,94,1,0
|
||||
BRDA:892,94,2,0
|
||||
BRDA:892,94,3,0
|
||||
BRDA:892,94,4,0
|
||||
BRDA:895,95,0,0
|
||||
BRDA:895,95,1,0
|
||||
BRDA:932,96,0,0
|
||||
BRDA:932,96,1,0
|
||||
BRDA:933,97,0,0
|
||||
BRDA:933,97,1,0
|
||||
BRF:214
|
||||
BRH:0
|
||||
end_of_record
|
||||
TN:
|
||||
SF:popup.js
|
||||
FN:2,(anonymous_0)
|
||||
FN:19,(anonymous_1)
|
||||
FN:24,(anonymous_2)
|
||||
FN:29,(anonymous_3)
|
||||
FN:54,(anonymous_4)
|
||||
FN:67,(anonymous_5)
|
||||
FN:85,toggleButtons
|
||||
FN:95,showStatus
|
||||
FN:100,(anonymous_8)
|
||||
FN:105,checkSelectorState
|
||||
FN:128,setupTooltipAccessibility
|
||||
FN:135,(anonymous_11)
|
||||
FN:150,(anonymous_12)
|
||||
FN:158,(anonymous_13)
|
||||
FN:160,(anonymous_14)
|
||||
FN:171,(anonymous_15)
|
||||
FN:184,(anonymous_16)
|
||||
FNF:17
|
||||
FNH:0
|
||||
FNDA:0,(anonymous_0)
|
||||
FNDA:0,(anonymous_1)
|
||||
FNDA:0,(anonymous_2)
|
||||
FNDA:0,(anonymous_3)
|
||||
FNDA:0,(anonymous_4)
|
||||
FNDA:0,(anonymous_5)
|
||||
FNDA:0,toggleButtons
|
||||
FNDA:0,showStatus
|
||||
FNDA:0,(anonymous_8)
|
||||
FNDA:0,checkSelectorState
|
||||
FNDA:0,setupTooltipAccessibility
|
||||
FNDA:0,(anonymous_11)
|
||||
FNDA:0,(anonymous_12)
|
||||
FNDA:0,(anonymous_13)
|
||||
FNDA:0,(anonymous_14)
|
||||
FNDA:0,(anonymous_15)
|
||||
FNDA:0,(anonymous_16)
|
||||
DA:2,0
|
||||
DA:3,0
|
||||
DA:4,0
|
||||
DA:5,0
|
||||
DA:6,0
|
||||
DA:7,0
|
||||
DA:10,0
|
||||
DA:11,0
|
||||
DA:12,0
|
||||
DA:16,0
|
||||
DA:19,0
|
||||
DA:20,0
|
||||
DA:24,0
|
||||
DA:25,0
|
||||
DA:29,0
|
||||
DA:30,0
|
||||
DA:33,0
|
||||
DA:39,0
|
||||
DA:40,0
|
||||
DA:43,0
|
||||
DA:44,0
|
||||
DA:50,0
|
||||
DA:51,0
|
||||
DA:54,0
|
||||
DA:57,0
|
||||
DA:58,0
|
||||
DA:59,0
|
||||
DA:61,0
|
||||
DA:67,0
|
||||
DA:68,0
|
||||
DA:70,0
|
||||
DA:71,0
|
||||
DA:72,0
|
||||
DA:73,0
|
||||
DA:75,0
|
||||
DA:80,0
|
||||
DA:83,0
|
||||
DA:86,0
|
||||
DA:87,0
|
||||
DA:88,0
|
||||
DA:90,0
|
||||
DA:91,0
|
||||
DA:96,0
|
||||
DA:97,0
|
||||
DA:98,0
|
||||
DA:100,0
|
||||
DA:101,0
|
||||
DA:106,0
|
||||
DA:109,0
|
||||
DA:113,0
|
||||
DA:116,0
|
||||
DA:117,0
|
||||
DA:118,0
|
||||
DA:119,0
|
||||
DA:120,0
|
||||
DA:124,0
|
||||
DA:129,0
|
||||
DA:130,0
|
||||
DA:132,0
|
||||
DA:135,0
|
||||
DA:136,0
|
||||
DA:137,0
|
||||
DA:139,0
|
||||
DA:140,0
|
||||
DA:141,0
|
||||
DA:142,0
|
||||
DA:144,0
|
||||
DA:145,0
|
||||
DA:150,0
|
||||
DA:151,0
|
||||
DA:152,0
|
||||
DA:153,0
|
||||
DA:158,0
|
||||
DA:160,0
|
||||
DA:161,0
|
||||
DA:162,0
|
||||
DA:163,0
|
||||
DA:171,0
|
||||
DA:172,0
|
||||
DA:173,0
|
||||
DA:174,0
|
||||
DA:175,0
|
||||
DA:177,0
|
||||
DA:178,0
|
||||
DA:180,0
|
||||
DA:181,0
|
||||
DA:182,0
|
||||
DA:183,0
|
||||
DA:184,0
|
||||
LF:89
|
||||
LH:0
|
||||
BRDA:11,0,0,0
|
||||
BRDA:11,0,1,0
|
||||
BRDA:16,1,0,0
|
||||
BRDA:16,1,1,0
|
||||
BRDA:33,2,0,0
|
||||
BRDA:33,2,1,0
|
||||
BRDA:33,3,0,0
|
||||
BRDA:33,3,1,0
|
||||
BRDA:33,3,2,0
|
||||
BRDA:33,3,3,0
|
||||
BRDA:33,3,4,0
|
||||
BRDA:33,3,5,0
|
||||
BRDA:58,4,0,0
|
||||
BRDA:58,4,1,0
|
||||
BRDA:86,5,0,0
|
||||
BRDA:86,5,1,0
|
||||
BRDA:95,6,0,0
|
||||
BRDA:109,7,0,0
|
||||
BRDA:109,7,1,0
|
||||
BRDA:109,8,0,0
|
||||
BRDA:109,8,1,0
|
||||
BRDA:109,8,2,0
|
||||
BRDA:109,8,3,0
|
||||
BRDA:118,9,0,0
|
||||
BRDA:118,9,1,0
|
||||
BRDA:118,10,0,0
|
||||
BRDA:118,10,1,0
|
||||
BRDA:132,11,0,0
|
||||
BRDA:132,11,1,0
|
||||
BRDA:132,12,0,0
|
||||
BRDA:132,12,1,0
|
||||
BRDA:136,13,0,0
|
||||
BRDA:136,13,1,0
|
||||
BRDA:136,14,0,0
|
||||
BRDA:136,14,1,0
|
||||
BRDA:140,15,0,0
|
||||
BRDA:140,15,1,0
|
||||
BRDA:141,16,0,0
|
||||
BRDA:141,16,1,0
|
||||
BRDA:142,17,0,0
|
||||
BRDA:142,17,1,0
|
||||
BRDA:151,18,0,0
|
||||
BRDA:151,18,1,0
|
||||
BRDA:161,19,0,0
|
||||
BRDA:161,19,1,0
|
||||
BRDA:172,20,0,0
|
||||
BRDA:172,20,1,0
|
||||
BRDA:180,21,0,0
|
||||
BRDA:180,21,1,0
|
||||
BRF:49
|
||||
BRH:0
|
||||
end_of_record
|
||||
643
coverage/popup.js.html
Normal file
643
coverage/popup.js.html
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for popup.js</title>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" href="prettify.css" />
|
||||
<link rel="stylesheet" href="base.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style type='text/css'>
|
||||
.coverage-summary .sorter {
|
||||
background-image: url(sort-arrow-sprite.png);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class='wrapper'>
|
||||
<div class='pad1'>
|
||||
<h1><a href="index.html">All files</a> popup.js</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/92</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/49</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/17</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/89</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<p class="quiet">
|
||||
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
|
||||
</p>
|
||||
<template id="filterTemplate">
|
||||
<div class="quiet">
|
||||
Filter:
|
||||
<input type="search" id="fileSearch">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<pre><table class="coverage">
|
||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||
<a name='L2'></a><a href='#L2'>2</a>
|
||||
<a name='L3'></a><a href='#L3'>3</a>
|
||||
<a name='L4'></a><a href='#L4'>4</a>
|
||||
<a name='L5'></a><a href='#L5'>5</a>
|
||||
<a name='L6'></a><a href='#L6'>6</a>
|
||||
<a name='L7'></a><a href='#L7'>7</a>
|
||||
<a name='L8'></a><a href='#L8'>8</a>
|
||||
<a name='L9'></a><a href='#L9'>9</a>
|
||||
<a name='L10'></a><a href='#L10'>10</a>
|
||||
<a name='L11'></a><a href='#L11'>11</a>
|
||||
<a name='L12'></a><a href='#L12'>12</a>
|
||||
<a name='L13'></a><a href='#L13'>13</a>
|
||||
<a name='L14'></a><a href='#L14'>14</a>
|
||||
<a name='L15'></a><a href='#L15'>15</a>
|
||||
<a name='L16'></a><a href='#L16'>16</a>
|
||||
<a name='L17'></a><a href='#L17'>17</a>
|
||||
<a name='L18'></a><a href='#L18'>18</a>
|
||||
<a name='L19'></a><a href='#L19'>19</a>
|
||||
<a name='L20'></a><a href='#L20'>20</a>
|
||||
<a name='L21'></a><a href='#L21'>21</a>
|
||||
<a name='L22'></a><a href='#L22'>22</a>
|
||||
<a name='L23'></a><a href='#L23'>23</a>
|
||||
<a name='L24'></a><a href='#L24'>24</a>
|
||||
<a name='L25'></a><a href='#L25'>25</a>
|
||||
<a name='L26'></a><a href='#L26'>26</a>
|
||||
<a name='L27'></a><a href='#L27'>27</a>
|
||||
<a name='L28'></a><a href='#L28'>28</a>
|
||||
<a name='L29'></a><a href='#L29'>29</a>
|
||||
<a name='L30'></a><a href='#L30'>30</a>
|
||||
<a name='L31'></a><a href='#L31'>31</a>
|
||||
<a name='L32'></a><a href='#L32'>32</a>
|
||||
<a name='L33'></a><a href='#L33'>33</a>
|
||||
<a name='L34'></a><a href='#L34'>34</a>
|
||||
<a name='L35'></a><a href='#L35'>35</a>
|
||||
<a name='L36'></a><a href='#L36'>36</a>
|
||||
<a name='L37'></a><a href='#L37'>37</a>
|
||||
<a name='L38'></a><a href='#L38'>38</a>
|
||||
<a name='L39'></a><a href='#L39'>39</a>
|
||||
<a name='L40'></a><a href='#L40'>40</a>
|
||||
<a name='L41'></a><a href='#L41'>41</a>
|
||||
<a name='L42'></a><a href='#L42'>42</a>
|
||||
<a name='L43'></a><a href='#L43'>43</a>
|
||||
<a name='L44'></a><a href='#L44'>44</a>
|
||||
<a name='L45'></a><a href='#L45'>45</a>
|
||||
<a name='L46'></a><a href='#L46'>46</a>
|
||||
<a name='L47'></a><a href='#L47'>47</a>
|
||||
<a name='L48'></a><a href='#L48'>48</a>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a>
|
||||
<a name='L51'></a><a href='#L51'>51</a>
|
||||
<a name='L52'></a><a href='#L52'>52</a>
|
||||
<a name='L53'></a><a href='#L53'>53</a>
|
||||
<a name='L54'></a><a href='#L54'>54</a>
|
||||
<a name='L55'></a><a href='#L55'>55</a>
|
||||
<a name='L56'></a><a href='#L56'>56</a>
|
||||
<a name='L57'></a><a href='#L57'>57</a>
|
||||
<a name='L58'></a><a href='#L58'>58</a>
|
||||
<a name='L59'></a><a href='#L59'>59</a>
|
||||
<a name='L60'></a><a href='#L60'>60</a>
|
||||
<a name='L61'></a><a href='#L61'>61</a>
|
||||
<a name='L62'></a><a href='#L62'>62</a>
|
||||
<a name='L63'></a><a href='#L63'>63</a>
|
||||
<a name='L64'></a><a href='#L64'>64</a>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a>
|
||||
<a name='L67'></a><a href='#L67'>67</a>
|
||||
<a name='L68'></a><a href='#L68'>68</a>
|
||||
<a name='L69'></a><a href='#L69'>69</a>
|
||||
<a name='L70'></a><a href='#L70'>70</a>
|
||||
<a name='L71'></a><a href='#L71'>71</a>
|
||||
<a name='L72'></a><a href='#L72'>72</a>
|
||||
<a name='L73'></a><a href='#L73'>73</a>
|
||||
<a name='L74'></a><a href='#L74'>74</a>
|
||||
<a name='L75'></a><a href='#L75'>75</a>
|
||||
<a name='L76'></a><a href='#L76'>76</a>
|
||||
<a name='L77'></a><a href='#L77'>77</a>
|
||||
<a name='L78'></a><a href='#L78'>78</a>
|
||||
<a name='L79'></a><a href='#L79'>79</a>
|
||||
<a name='L80'></a><a href='#L80'>80</a>
|
||||
<a name='L81'></a><a href='#L81'>81</a>
|
||||
<a name='L82'></a><a href='#L82'>82</a>
|
||||
<a name='L83'></a><a href='#L83'>83</a>
|
||||
<a name='L84'></a><a href='#L84'>84</a>
|
||||
<a name='L85'></a><a href='#L85'>85</a>
|
||||
<a name='L86'></a><a href='#L86'>86</a>
|
||||
<a name='L87'></a><a href='#L87'>87</a>
|
||||
<a name='L88'></a><a href='#L88'>88</a>
|
||||
<a name='L89'></a><a href='#L89'>89</a>
|
||||
<a name='L90'></a><a href='#L90'>90</a>
|
||||
<a name='L91'></a><a href='#L91'>91</a>
|
||||
<a name='L92'></a><a href='#L92'>92</a>
|
||||
<a name='L93'></a><a href='#L93'>93</a>
|
||||
<a name='L94'></a><a href='#L94'>94</a>
|
||||
<a name='L95'></a><a href='#L95'>95</a>
|
||||
<a name='L96'></a><a href='#L96'>96</a>
|
||||
<a name='L97'></a><a href='#L97'>97</a>
|
||||
<a name='L98'></a><a href='#L98'>98</a>
|
||||
<a name='L99'></a><a href='#L99'>99</a>
|
||||
<a name='L100'></a><a href='#L100'>100</a>
|
||||
<a name='L101'></a><a href='#L101'>101</a>
|
||||
<a name='L102'></a><a href='#L102'>102</a>
|
||||
<a name='L103'></a><a href='#L103'>103</a>
|
||||
<a name='L104'></a><a href='#L104'>104</a>
|
||||
<a name='L105'></a><a href='#L105'>105</a>
|
||||
<a name='L106'></a><a href='#L106'>106</a>
|
||||
<a name='L107'></a><a href='#L107'>107</a>
|
||||
<a name='L108'></a><a href='#L108'>108</a>
|
||||
<a name='L109'></a><a href='#L109'>109</a>
|
||||
<a name='L110'></a><a href='#L110'>110</a>
|
||||
<a name='L111'></a><a href='#L111'>111</a>
|
||||
<a name='L112'></a><a href='#L112'>112</a>
|
||||
<a name='L113'></a><a href='#L113'>113</a>
|
||||
<a name='L114'></a><a href='#L114'>114</a>
|
||||
<a name='L115'></a><a href='#L115'>115</a>
|
||||
<a name='L116'></a><a href='#L116'>116</a>
|
||||
<a name='L117'></a><a href='#L117'>117</a>
|
||||
<a name='L118'></a><a href='#L118'>118</a>
|
||||
<a name='L119'></a><a href='#L119'>119</a>
|
||||
<a name='L120'></a><a href='#L120'>120</a>
|
||||
<a name='L121'></a><a href='#L121'>121</a>
|
||||
<a name='L122'></a><a href='#L122'>122</a>
|
||||
<a name='L123'></a><a href='#L123'>123</a>
|
||||
<a name='L124'></a><a href='#L124'>124</a>
|
||||
<a name='L125'></a><a href='#L125'>125</a>
|
||||
<a name='L126'></a><a href='#L126'>126</a>
|
||||
<a name='L127'></a><a href='#L127'>127</a>
|
||||
<a name='L128'></a><a href='#L128'>128</a>
|
||||
<a name='L129'></a><a href='#L129'>129</a>
|
||||
<a name='L130'></a><a href='#L130'>130</a>
|
||||
<a name='L131'></a><a href='#L131'>131</a>
|
||||
<a name='L132'></a><a href='#L132'>132</a>
|
||||
<a name='L133'></a><a href='#L133'>133</a>
|
||||
<a name='L134'></a><a href='#L134'>134</a>
|
||||
<a name='L135'></a><a href='#L135'>135</a>
|
||||
<a name='L136'></a><a href='#L136'>136</a>
|
||||
<a name='L137'></a><a href='#L137'>137</a>
|
||||
<a name='L138'></a><a href='#L138'>138</a>
|
||||
<a name='L139'></a><a href='#L139'>139</a>
|
||||
<a name='L140'></a><a href='#L140'>140</a>
|
||||
<a name='L141'></a><a href='#L141'>141</a>
|
||||
<a name='L142'></a><a href='#L142'>142</a>
|
||||
<a name='L143'></a><a href='#L143'>143</a>
|
||||
<a name='L144'></a><a href='#L144'>144</a>
|
||||
<a name='L145'></a><a href='#L145'>145</a>
|
||||
<a name='L146'></a><a href='#L146'>146</a>
|
||||
<a name='L147'></a><a href='#L147'>147</a>
|
||||
<a name='L148'></a><a href='#L148'>148</a>
|
||||
<a name='L149'></a><a href='#L149'>149</a>
|
||||
<a name='L150'></a><a href='#L150'>150</a>
|
||||
<a name='L151'></a><a href='#L151'>151</a>
|
||||
<a name='L152'></a><a href='#L152'>152</a>
|
||||
<a name='L153'></a><a href='#L153'>153</a>
|
||||
<a name='L154'></a><a href='#L154'>154</a>
|
||||
<a name='L155'></a><a href='#L155'>155</a>
|
||||
<a name='L156'></a><a href='#L156'>156</a>
|
||||
<a name='L157'></a><a href='#L157'>157</a>
|
||||
<a name='L158'></a><a href='#L158'>158</a>
|
||||
<a name='L159'></a><a href='#L159'>159</a>
|
||||
<a name='L160'></a><a href='#L160'>160</a>
|
||||
<a name='L161'></a><a href='#L161'>161</a>
|
||||
<a name='L162'></a><a href='#L162'>162</a>
|
||||
<a name='L163'></a><a href='#L163'>163</a>
|
||||
<a name='L164'></a><a href='#L164'>164</a>
|
||||
<a name='L165'></a><a href='#L165'>165</a>
|
||||
<a name='L166'></a><a href='#L166'>166</a>
|
||||
<a name='L167'></a><a href='#L167'>167</a>
|
||||
<a name='L168'></a><a href='#L168'>168</a>
|
||||
<a name='L169'></a><a href='#L169'>169</a>
|
||||
<a name='L170'></a><a href='#L170'>170</a>
|
||||
<a name='L171'></a><a href='#L171'>171</a>
|
||||
<a name='L172'></a><a href='#L172'>172</a>
|
||||
<a name='L173'></a><a href='#L173'>173</a>
|
||||
<a name='L174'></a><a href='#L174'>174</a>
|
||||
<a name='L175'></a><a href='#L175'>175</a>
|
||||
<a name='L176'></a><a href='#L176'>176</a>
|
||||
<a name='L177'></a><a href='#L177'>177</a>
|
||||
<a name='L178'></a><a href='#L178'>178</a>
|
||||
<a name='L179'></a><a href='#L179'>179</a>
|
||||
<a name='L180'></a><a href='#L180'>180</a>
|
||||
<a name='L181'></a><a href='#L181'>181</a>
|
||||
<a name='L182'></a><a href='#L182'>182</a>
|
||||
<a name='L183'></a><a href='#L183'>183</a>
|
||||
<a name='L184'></a><a href='#L184'>184</a>
|
||||
<a name='L185'></a><a href='#L185'>185</a>
|
||||
<a name='L186'></a><a href='#L186'>186</a>
|
||||
<a name='L187'></a><a href='#L187'>187</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">// Popup script for Chrome extension
|
||||
<span class="cstat-no" title="statement not covered" >document.addEventListener('DOMContentLoaded', <span class="fstat-no" title="function not covered" >as</span>ync () => {</span>
|
||||
const backgroundSelect = <span class="cstat-no" title="statement not covered" >document.getElementById('background-select');</span>
|
||||
const clipboardToggle = <span class="cstat-no" title="statement not covered" >document.getElementById('clipboard-toggle');</span>
|
||||
const startBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('start-selector');</span>
|
||||
const stopBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('stop-selector');</span>
|
||||
const status = <span class="cstat-no" title="statement not covered" >document.getElementById('status');</span>
|
||||
|
||||
// Load saved preferences
|
||||
const saved = <span class="cstat-no" title="statement not covered" >await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (saved.backgroundPreference) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > backgroundSelect.value = saved.backgroundPreference;</span>
|
||||
}
|
||||
|
||||
// Set clipboard preference (default to true if not set)
|
||||
<span class="cstat-no" title="statement not covered" > clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;</span>
|
||||
|
||||
// Save background preference when changed
|
||||
<span class="cstat-no" title="statement not covered" > backgroundSelect.addEventListener('change', <span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > chrome.storage.sync.set({ backgroundPreference: backgroundSelect.value });</span>
|
||||
});
|
||||
|
||||
// Save clipboard preference when changed
|
||||
<span class="cstat-no" title="statement not covered" > clipboardToggle.addEventListener('change', <span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > chrome.storage.sync.set({ copyToClipboard: clipboardToggle.checked });</span>
|
||||
});
|
||||
|
||||
// Start selector
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.addEventListener('click', <span class="fstat-no" title="function not covered" >as</span>ync () => {</span>
|
||||
const [tab] = <span class="cstat-no" title="statement not covered" >await chrome.tabs.query({ active: true, currentWindow: true });</span>
|
||||
|
||||
// Check if page is compatible
|
||||
<span class="cstat-no" title="statement not covered" > if (tab.url.startsWith('chrome://') ||</span>
|
||||
tab.url.startsWith('chrome-extension://') ||
|
||||
tab.url.startsWith('edge://') ||
|
||||
tab.url.startsWith('about:') ||
|
||||
tab.url === 'chrome://newtab/' ||
|
||||
tab.url === 'edge://newtab/') {
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Error: Cannot capture screenshots on this page type.', 'error');</span>
|
||||
<span class="cstat-no" title="statement not covered" > return;</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
<span class="cstat-no" title="statement not covered" > await chrome.tabs.sendMessage(tab.id, {</span>
|
||||
action: 'startSelector',
|
||||
background: backgroundSelect.value,
|
||||
copyToClipboard: clipboardToggle.checked
|
||||
});
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Selector activated! Hover over elements and click to capture.', 'success');</span>
|
||||
<span class="cstat-no" title="statement not covered" > toggleButtons(true);</span>
|
||||
|
||||
// Auto-close popup after starting
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => <span class="cstat-no" title="statement not covered" >window.close(),</span> 1500);</span>
|
||||
|
||||
} catch (error) {
|
||||
<span class="cstat-no" title="statement not covered" > console.error('Full error details:', error);</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (error.message.includes('Could not establish connection')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Error: Content script not loaded. Try refreshing the page.', 'error');</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Error: Could not activate on this page.', 'error');</span>
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Stop selector
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.addEventListener('click', <span class="fstat-no" title="function not covered" >as</span>ync () => {</span>
|
||||
const [tab] = <span class="cstat-no" title="statement not covered" >await chrome.tabs.query({ active: true, currentWindow: true });</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
<span class="cstat-no" title="statement not covered" > await chrome.tabs.sendMessage(tab.id, { action: 'stopSelector' });</span>
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Selector stopped.', 'success');</span>
|
||||
<span class="cstat-no" title="statement not covered" > toggleButtons(false);</span>
|
||||
} catch (error) {
|
||||
<span class="cstat-no" title="statement not covered" > console.error('Error stopping selector:', error);</span>
|
||||
}
|
||||
});
|
||||
|
||||
// Check current state
|
||||
<span class="cstat-no" title="statement not covered" > checkSelectorState();</span>
|
||||
|
||||
// Handle tooltip keyboard navigation
|
||||
<span class="cstat-no" title="statement not covered" > setupTooltipAccessibility();</span>
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >toggleButtons(</span>isActive) {
|
||||
<span class="cstat-no" title="statement not covered" > if (isActive) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.style.display = 'none';</span>
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.style.display = 'block';</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.style.display = 'block';</span>
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.style.display = 'none';</span>
|
||||
}
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >showStatus(</span>message, type = <span class="branch-0 cbranch-no" title="branch not covered" >'success')</span> {
|
||||
<span class="cstat-no" title="statement not covered" > status.textContent = message;</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.className = `status ${type}`;</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.style.display = 'block';</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.style.display = 'none';</span>
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
async function <span class="fstat-no" title="function not covered" >checkSelectorState(</span>) {
|
||||
const [tab] = <span class="cstat-no" title="statement not covered" >await chrome.tabs.query({ active: true, currentWindow: true });</span>
|
||||
|
||||
// Skip check for incompatible pages
|
||||
<span class="cstat-no" title="statement not covered" > if (tab.url.startsWith('chrome://') ||</span>
|
||||
tab.url.startsWith('chrome-extension://') ||
|
||||
tab.url.startsWith('edge://') ||
|
||||
tab.url.startsWith('about:')) {
|
||||
<span class="cstat-no" title="statement not covered" > return;</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
const response = <span class="cstat-no" title="statement not covered" >await chrome.tabs.sendMessage(tab.id, { action: 'checkState' });</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (response && response.isActive) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > toggleButtons(true);</span>
|
||||
<span class="cstat-no" title="statement not covered" > showStatus('Selector is currently active', 'success');</span>
|
||||
}
|
||||
} catch (error) {
|
||||
// Content script not ready or selector not active
|
||||
<span class="cstat-no" title="statement not covered" > console.log('Content script not ready or selector inactive:', error.message);</span>
|
||||
}
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >setupTooltipAccessibility(</span>) {
|
||||
const tooltipIcon = <span class="cstat-no" title="statement not covered" >document.querySelector('.tooltip-icon');</span>
|
||||
const tooltipContent = <span class="cstat-no" title="statement not covered" >document.querySelector('.tooltip-content');</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (!tooltipIcon || !tooltipContent) <span class="cstat-no" title="statement not covered" >return;</span></span>
|
||||
|
||||
// Handle keyboard events for tooltip
|
||||
<span class="cstat-no" title="statement not covered" > tooltipIcon.addEventListener('keydown', <span class="fstat-no" title="function not covered" >(e</span>) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (e.key === 'Enter' || e.key === ' ') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault();</span>
|
||||
// Toggle tooltip visibility on Enter/Space
|
||||
const isVisible = <span class="cstat-no" title="statement not covered" >tooltipContent.style.visibility === 'visible';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = isVisible ? 'hidden' : 'visible';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = isVisible ? '0' : '1';</span>
|
||||
} else <span class="cstat-no" title="statement not covered" >if (e.key === 'Escape') {</span>
|
||||
// Hide tooltip on Escape
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = 'hidden';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = '0';</span>
|
||||
}
|
||||
});
|
||||
|
||||
// Hide tooltip when clicking outside
|
||||
<span class="cstat-no" title="statement not covered" > document.addEventListener('click', <span class="fstat-no" title="function not covered" >(e</span>) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!tooltipIcon.contains(e.target)) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = 'hidden';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = '0';</span>
|
||||
}
|
||||
});
|
||||
|
||||
// Handle focus loss
|
||||
<span class="cstat-no" title="statement not covered" > tooltipIcon.addEventListener('blur', <span class="fstat-no" title="function not covered" >(e</span>) => {</span>
|
||||
// Small delay to allow for focus to move to tooltip content if needed
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!tooltipIcon.matches(':focus-within')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.visibility = 'hidden';</span>
|
||||
<span class="cstat-no" title="statement not covered" > tooltipContent.style.opacity = '0';</span>
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for messages from content script
|
||||
<span class="cstat-no" title="statement not covered" >chrome.runtime.onMessage.addListener(<span class="fstat-no" title="function not covered" >(m</span>essage, sender, sendResponse) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (message.action === 'selectorStopped') {</span>
|
||||
const startBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('start-selector');</span>
|
||||
const stopBtn = <span class="cstat-no" title="statement not covered" >document.getElementById('stop-selector');</span>
|
||||
const status = <span class="cstat-no" title="statement not covered" >document.getElementById('status');</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > startBtn.style.display = 'block';</span>
|
||||
<span class="cstat-no" title="statement not covered" > stopBtn.style.display = 'none';</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (message.reason === 'screenshot-taken') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.textContent = 'Screenshot captured!';</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.className = 'status success';</span>
|
||||
<span class="cstat-no" title="statement not covered" > status.style.display = 'block';</span>
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >()</span> => <span class="cstat-no" title="statement not covered" >status.style.display = 'none',</span> 2000);</span>
|
||||
}
|
||||
}
|
||||
});</pre></td></tr></table></pre>
|
||||
|
||||
<div class='push'></div><!-- for sticky footer -->
|
||||
</div><!-- /wrapper -->
|
||||
<div class='footer quiet pad2 space-top1 center small'>
|
||||
Code coverage generated by
|
||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||
at 2025-08-13T15:07:03.182Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
prettyPrint();
|
||||
};
|
||||
</script>
|
||||
<script src="sorter.js"></script>
|
||||
<script src="block-navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
1
coverage/prettify.css
Normal file
1
coverage/prettify.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
|
||||
2
coverage/prettify.js
Normal file
2
coverage/prettify.js
Normal file
File diff suppressed because one or more lines are too long
BIN
coverage/sort-arrow-sprite.png
Normal file
BIN
coverage/sort-arrow-sprite.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 B |
196
coverage/sorter.js
Normal file
196
coverage/sorter.js
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/* eslint-disable */
|
||||
var addSorting = (function() {
|
||||
'use strict';
|
||||
var cols,
|
||||
currentSort = {
|
||||
index: 0,
|
||||
desc: false
|
||||
};
|
||||
|
||||
// returns the summary table element
|
||||
function getTable() {
|
||||
return document.querySelector('.coverage-summary');
|
||||
}
|
||||
// returns the thead element of the summary table
|
||||
function getTableHeader() {
|
||||
return getTable().querySelector('thead tr');
|
||||
}
|
||||
// returns the tbody element of the summary table
|
||||
function getTableBody() {
|
||||
return getTable().querySelector('tbody');
|
||||
}
|
||||
// returns the th element for nth column
|
||||
function getNthColumn(n) {
|
||||
return getTableHeader().querySelectorAll('th')[n];
|
||||
}
|
||||
|
||||
function onFilterInput() {
|
||||
const searchValue = document.getElementById('fileSearch').value;
|
||||
const rows = document.getElementsByTagName('tbody')[0].children;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i];
|
||||
if (
|
||||
row.textContent
|
||||
.toLowerCase()
|
||||
.includes(searchValue.toLowerCase())
|
||||
) {
|
||||
row.style.display = '';
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loads the search box
|
||||
function addSearchBox() {
|
||||
var template = document.getElementById('filterTemplate');
|
||||
var templateClone = template.content.cloneNode(true);
|
||||
templateClone.getElementById('fileSearch').oninput = onFilterInput;
|
||||
template.parentElement.appendChild(templateClone);
|
||||
}
|
||||
|
||||
// loads all columns
|
||||
function loadColumns() {
|
||||
var colNodes = getTableHeader().querySelectorAll('th'),
|
||||
colNode,
|
||||
cols = [],
|
||||
col,
|
||||
i;
|
||||
|
||||
for (i = 0; i < colNodes.length; i += 1) {
|
||||
colNode = colNodes[i];
|
||||
col = {
|
||||
key: colNode.getAttribute('data-col'),
|
||||
sortable: !colNode.getAttribute('data-nosort'),
|
||||
type: colNode.getAttribute('data-type') || 'string'
|
||||
};
|
||||
cols.push(col);
|
||||
if (col.sortable) {
|
||||
col.defaultDescSort = col.type === 'number';
|
||||
colNode.innerHTML =
|
||||
colNode.innerHTML + '<span class="sorter"></span>';
|
||||
}
|
||||
}
|
||||
return cols;
|
||||
}
|
||||
// attaches a data attribute to every tr element with an object
|
||||
// of data values keyed by column name
|
||||
function loadRowData(tableRow) {
|
||||
var tableCols = tableRow.querySelectorAll('td'),
|
||||
colNode,
|
||||
col,
|
||||
data = {},
|
||||
i,
|
||||
val;
|
||||
for (i = 0; i < tableCols.length; i += 1) {
|
||||
colNode = tableCols[i];
|
||||
col = cols[i];
|
||||
val = colNode.getAttribute('data-value');
|
||||
if (col.type === 'number') {
|
||||
val = Number(val);
|
||||
}
|
||||
data[col.key] = val;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// loads all row data
|
||||
function loadData() {
|
||||
var rows = getTableBody().querySelectorAll('tr'),
|
||||
i;
|
||||
|
||||
for (i = 0; i < rows.length; i += 1) {
|
||||
rows[i].data = loadRowData(rows[i]);
|
||||
}
|
||||
}
|
||||
// sorts the table using the data for the ith column
|
||||
function sortByIndex(index, desc) {
|
||||
var key = cols[index].key,
|
||||
sorter = function(a, b) {
|
||||
a = a.data[key];
|
||||
b = b.data[key];
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
},
|
||||
finalSorter = sorter,
|
||||
tableBody = document.querySelector('.coverage-summary tbody'),
|
||||
rowNodes = tableBody.querySelectorAll('tr'),
|
||||
rows = [],
|
||||
i;
|
||||
|
||||
if (desc) {
|
||||
finalSorter = function(a, b) {
|
||||
return -1 * sorter(a, b);
|
||||
};
|
||||
}
|
||||
|
||||
for (i = 0; i < rowNodes.length; i += 1) {
|
||||
rows.push(rowNodes[i]);
|
||||
tableBody.removeChild(rowNodes[i]);
|
||||
}
|
||||
|
||||
rows.sort(finalSorter);
|
||||
|
||||
for (i = 0; i < rows.length; i += 1) {
|
||||
tableBody.appendChild(rows[i]);
|
||||
}
|
||||
}
|
||||
// removes sort indicators for current column being sorted
|
||||
function removeSortIndicators() {
|
||||
var col = getNthColumn(currentSort.index),
|
||||
cls = col.className;
|
||||
|
||||
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
|
||||
col.className = cls;
|
||||
}
|
||||
// adds sort indicators for current column being sorted
|
||||
function addSortIndicators() {
|
||||
getNthColumn(currentSort.index).className += currentSort.desc
|
||||
? ' sorted-desc'
|
||||
: ' sorted';
|
||||
}
|
||||
// adds event listeners for all sorter widgets
|
||||
function enableUI() {
|
||||
var i,
|
||||
el,
|
||||
ithSorter = function ithSorter(i) {
|
||||
var col = cols[i];
|
||||
|
||||
return function() {
|
||||
var desc = col.defaultDescSort;
|
||||
|
||||
if (currentSort.index === i) {
|
||||
desc = !currentSort.desc;
|
||||
}
|
||||
sortByIndex(i, desc);
|
||||
removeSortIndicators();
|
||||
currentSort.index = i;
|
||||
currentSort.desc = desc;
|
||||
addSortIndicators();
|
||||
};
|
||||
};
|
||||
for (i = 0; i < cols.length; i += 1) {
|
||||
if (cols[i].sortable) {
|
||||
// add the click event handler on the th so users
|
||||
// dont have to click on those tiny arrows
|
||||
el = getNthColumn(i).querySelector('.sorter').parentElement;
|
||||
if (el.addEventListener) {
|
||||
el.addEventListener('click', ithSorter(i));
|
||||
} else {
|
||||
el.attachEvent('onclick', ithSorter(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// adds sorting functionality to the UI
|
||||
return function() {
|
||||
if (!getTable()) {
|
||||
return;
|
||||
}
|
||||
cols = loadColumns();
|
||||
loadData();
|
||||
addSearchBox();
|
||||
addSortIndicators();
|
||||
enableUI();
|
||||
};
|
||||
})();
|
||||
|
||||
window.addEventListener('load', addSorting);
|
||||
4466
package-lock.json
generated
Normal file
4466
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
37
package.json
Normal file
37
package.json
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "full-screenshot-selector-tests",
|
||||
"version": "1.0.0",
|
||||
"description": "Test suite for Full Screenshot Selector Chrome Extension clipboard functionality",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:clipboard": "jest tests/clipboard-functionality.test.js",
|
||||
"test:persistence": "jest tests/preference-persistence.test.js",
|
||||
"test:integration": "jest tests/integration-scenarios.test.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.8",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/tests/setup.js"
|
||||
],
|
||||
"collectCoverageFrom": [
|
||||
"content.js",
|
||||
"popup.js",
|
||||
"!node_modules/**"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"text",
|
||||
"lcov",
|
||||
"html"
|
||||
],
|
||||
"testMatch": [
|
||||
"**/tests/**/*.test.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
167
popup.html
167
popup.html
|
|
@ -80,6 +80,140 @@
|
|||
background: #545b62;
|
||||
}
|
||||
|
||||
.toggle-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
transition: 0.3s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: white;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .toggle-slider {
|
||||
background-color: #007bff;
|
||||
}
|
||||
|
||||
input:focus + .toggle-slider {
|
||||
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
|
||||
input:checked + .toggle-slider:before {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.toggle-help {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.tooltip-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tooltip-content {
|
||||
visibility: hidden;
|
||||
width: 280px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
text-align: left;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
bottom: 125%;
|
||||
left: 50%;
|
||||
margin-left: -140px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.tooltip-content::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: #333 transparent transparent transparent;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltip-content,
|
||||
.tooltip:focus-within .tooltip-content {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tooltip-icon:focus {
|
||||
outline: 2px solid #007bff;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.shortcut-info {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
|
|
@ -124,6 +258,39 @@
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<div class="setting-group">
|
||||
<div class="toggle-container">
|
||||
<label for="clipboard-toggle" class="toggle-label">
|
||||
Copy to Clipboard
|
||||
<div class="tooltip">
|
||||
<div class="tooltip-icon" tabindex="0" role="button" aria-label="Show clipboard feature information">?</div>
|
||||
<div class="tooltip-content" role="tooltip">
|
||||
<strong>Clipboard Feature</strong><br>
|
||||
When enabled, screenshots are automatically copied to your clipboard for quick pasting into other applications.<br><br>
|
||||
<strong>Browser Requirements:</strong><br>
|
||||
• Chrome 76+, Firefox 87+, Safari 13.1+<br>
|
||||
• Secure connection (HTTPS)<br>
|
||||
• Clipboard permissions may be required<br><br>
|
||||
<strong>Note:</strong> Screenshots are always downloaded as files regardless of this setting.
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div class="toggle-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="clipboard-toggle"
|
||||
checked
|
||||
aria-describedby="clipboard-help"
|
||||
aria-label="Toggle copy screenshot to clipboard"
|
||||
>
|
||||
<span class="toggle-slider" role="presentation"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="clipboard-help" class="toggle-help">
|
||||
Automatically copy screenshots to clipboard for quick pasting. Hover over the "?" for more details.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="start-selector" class="btn btn-primary">
|
||||
Start Element Selector
|
||||
</button>
|
||||
|
|
|
|||
60
popup.js
60
popup.js
|
|
@ -1,21 +1,30 @@
|
|||
// Popup script for Chrome extension
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const backgroundSelect = document.getElementById('background-select');
|
||||
const clipboardToggle = document.getElementById('clipboard-toggle');
|
||||
const startBtn = document.getElementById('start-selector');
|
||||
const stopBtn = document.getElementById('stop-selector');
|
||||
const status = document.getElementById('status');
|
||||
|
||||
// Load saved background preference
|
||||
const saved = await chrome.storage.sync.get(['backgroundPreference']);
|
||||
// Load saved preferences
|
||||
const saved = await chrome.storage.sync.get(['backgroundPreference', 'copyToClipboard']);
|
||||
if (saved.backgroundPreference) {
|
||||
backgroundSelect.value = saved.backgroundPreference;
|
||||
}
|
||||
|
||||
// Set clipboard preference (default to true if not set)
|
||||
clipboardToggle.checked = saved.copyToClipboard !== undefined ? saved.copyToClipboard : true;
|
||||
|
||||
// Save background preference when changed
|
||||
backgroundSelect.addEventListener('change', () => {
|
||||
chrome.storage.sync.set({ backgroundPreference: backgroundSelect.value });
|
||||
});
|
||||
|
||||
// Save clipboard preference when changed
|
||||
clipboardToggle.addEventListener('change', () => {
|
||||
chrome.storage.sync.set({ copyToClipboard: clipboardToggle.checked });
|
||||
});
|
||||
|
||||
// Start selector
|
||||
startBtn.addEventListener('click', async () => {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
|
|
@ -34,7 +43,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
try {
|
||||
await chrome.tabs.sendMessage(tab.id, {
|
||||
action: 'startSelector',
|
||||
background: backgroundSelect.value
|
||||
background: backgroundSelect.value,
|
||||
copyToClipboard: clipboardToggle.checked
|
||||
});
|
||||
|
||||
showStatus('Selector activated! Hover over elements and click to capture.', 'success');
|
||||
|
|
@ -69,6 +79,9 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
// Check current state
|
||||
checkSelectorState();
|
||||
|
||||
// Handle tooltip keyboard navigation
|
||||
setupTooltipAccessibility();
|
||||
|
||||
function toggleButtons(isActive) {
|
||||
if (isActive) {
|
||||
startBtn.style.display = 'none';
|
||||
|
|
@ -111,6 +124,47 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
console.log('Content script not ready or selector inactive:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function setupTooltipAccessibility() {
|
||||
const tooltipIcon = document.querySelector('.tooltip-icon');
|
||||
const tooltipContent = document.querySelector('.tooltip-content');
|
||||
|
||||
if (!tooltipIcon || !tooltipContent) return;
|
||||
|
||||
// Handle keyboard events for tooltip
|
||||
tooltipIcon.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
// Toggle tooltip visibility on Enter/Space
|
||||
const isVisible = tooltipContent.style.visibility === 'visible';
|
||||
tooltipContent.style.visibility = isVisible ? 'hidden' : 'visible';
|
||||
tooltipContent.style.opacity = isVisible ? '0' : '1';
|
||||
} else if (e.key === 'Escape') {
|
||||
// Hide tooltip on Escape
|
||||
tooltipContent.style.visibility = 'hidden';
|
||||
tooltipContent.style.opacity = '0';
|
||||
}
|
||||
});
|
||||
|
||||
// Hide tooltip when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!tooltipIcon.contains(e.target)) {
|
||||
tooltipContent.style.visibility = 'hidden';
|
||||
tooltipContent.style.opacity = '0';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle focus loss
|
||||
tooltipIcon.addEventListener('blur', (e) => {
|
||||
// Small delay to allow for focus to move to tooltip content if needed
|
||||
setTimeout(() => {
|
||||
if (!tooltipIcon.matches(':focus-within')) {
|
||||
tooltipContent.style.visibility = 'hidden';
|
||||
tooltipContent.style.opacity = '0';
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for messages from content script
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue