conditional-logic / layouts /task_layout.html
davidjurgens's picture
Deploy: Potato — Conditional Logic
3615cc8 verified
Raw
History Blame Contribute Delete
34.9 kB
<!-- CONFIG_HASH: c28ba23017d684f932b3aaf5f3b4cbe4_41b56a9063c42cffcce0f799609c68a3 -->
<!-- Generated annotation layout file -->
<!-- This file was automatically generated based on the annotation schemes in your config -->
<!-- You can customize this file to modify the layout of your annotation interface -->
<!-- Changes to this file will be preserved across server restarts -->
<div class="annotation_schema">
<form id="contains_pii" class="annotation-form radio shadcn-radio-container" action="javascript:void(0)" data-annotation-id="0" data-annotation-type="radio" data-schema-name="contains_pii" data-grid-columns="1">
<fieldset schema="contains_pii">
<legend class="shadcn-radio-title">Does this text contain personally identifiable information (PII)?</legend>
<div class="shadcn-radio-options">
<div class="shadcn-radio-option">
<input class="contains_pii shadcn-radio-input annotation-input"
type="radio"
id="contains_pii_Yes_radio"
name="contains_pii"
value="Yes"
selection_constraint="single"
schema="contains_pii"
label_name="Yes"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="contains_pii_Yes_radio" class="shadcn-radio-label" >Yes</label>
</div>
<div class="shadcn-radio-option">
<input class="contains_pii shadcn-radio-input annotation-input"
type="radio"
id="contains_pii_No_radio"
name="contains_pii"
value="No"
selection_constraint="single"
schema="contains_pii"
label_name="No"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="contains_pii_No_radio" class="shadcn-radio-label" >No</label>
</div>
<div class="shadcn-radio-option">
<input class="contains_pii shadcn-radio-input annotation-input"
type="radio"
id="contains_pii_Uncertain_radio"
name="contains_pii"
value="Uncertain"
selection_constraint="single"
schema="contains_pii"
label_name="Uncertain"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="contains_pii_Uncertain_radio" class="shadcn-radio-label" >Uncertain</label>
</div>
</div></fieldset></form>
<div class="display-logic-container display-logic-hidden"
data-display-logic="{&quot;show_when&quot;: [{&quot;schema&quot;: &quot;contains_pii&quot;, &quot;operator&quot;: &quot;equals&quot;, &quot;value&quot;: [&quot;Yes&quot;, &quot;Uncertain&quot;]}]}"
data-schema-name="pii_types"
data-display-logic-target="true">
<form id="pii_types" class="annotation-form multiselect shadcn-multiselect-container" action="javascript:void(0)" data-annotation-id="1" data-annotation-type="multiselect" data-schema-name="pii_types" data-grid-columns="1">
<fieldset schema="pii_types">
<legend class="shadcn-multiselect-title">What types of PII are present? (select all that apply)</legend>
<div class="shadcn-multiselect-grid" style="grid-template-columns: repeat(1, 1fr);">
<div class="shadcn-multiselect-item">
<input class="pii_types shadcn-multiselect-checkbox annotation-input"
type="checkbox"
id="pii_types_Name_checkbox"
name="pii_types:::Name"
value="Name"
label_name="Name"
schema="pii_types"
onclick="whetherNone(this);registerAnnotation(this)"
validation="">
<label for="pii_types_Name_checkbox" schema="pii_types" class="shadcn-multiselect-label">
Name
</label>
</div>
<div class="shadcn-multiselect-item">
<input class="pii_types shadcn-multiselect-checkbox annotation-input"
type="checkbox"
id="pii_types_Email_checkbox"
name="pii_types:::Email"
value="Email"
label_name="Email"
schema="pii_types"
onclick="whetherNone(this);registerAnnotation(this)"
validation="">
<label for="pii_types_Email_checkbox" schema="pii_types" class="shadcn-multiselect-label">
Email
</label>
</div>
<div class="shadcn-multiselect-item">
<input class="pii_types shadcn-multiselect-checkbox annotation-input"
type="checkbox"
id="pii_types_Phone_checkbox"
name="pii_types:::Phone"
value="Phone"
label_name="Phone"
schema="pii_types"
onclick="whetherNone(this);registerAnnotation(this)"
validation="">
<label for="pii_types_Phone_checkbox" schema="pii_types" class="shadcn-multiselect-label">
Phone
</label>
</div>
<div class="shadcn-multiselect-item">
<input class="pii_types shadcn-multiselect-checkbox annotation-input"
type="checkbox"
id="pii_types_Address_checkbox"
name="pii_types:::Address"
value="Address"
label_name="Address"
schema="pii_types"
onclick="whetherNone(this);registerAnnotation(this)"
validation="">
<label for="pii_types_Address_checkbox" schema="pii_types" class="shadcn-multiselect-label">
Address
</label>
</div>
<div class="shadcn-multiselect-item">
<input class="pii_types shadcn-multiselect-checkbox annotation-input"
type="checkbox"
id="pii_types_SSN_checkbox"
name="pii_types:::SSN"
value="SSN"
label_name="SSN"
schema="pii_types"
onclick="whetherNone(this);registerAnnotation(this)"
validation="">
<label for="pii_types_SSN_checkbox" schema="pii_types" class="shadcn-multiselect-label">
SSN
</label>
</div>
<div class="shadcn-multiselect-item">
<input class="pii_types shadcn-multiselect-checkbox annotation-input"
type="checkbox"
id="pii_types_Credit Card_checkbox"
name="pii_types:::Credit Card"
value="Credit Card"
label_name="Credit Card"
schema="pii_types"
onclick="whetherNone(this);registerAnnotation(this)"
validation="">
<label for="pii_types_Credit Card_checkbox" schema="pii_types" class="shadcn-multiselect-label">
Credit Card
</label>
</div>
<div class="shadcn-multiselect-item">
<input class="pii_types shadcn-multiselect-checkbox annotation-input"
type="checkbox"
id="pii_types_Other_checkbox"
name="pii_types:::Other"
value="Other"
label_name="Other"
schema="pii_types"
onclick="whetherNone(this);registerAnnotation(this)"
validation="">
<label for="pii_types_Other_checkbox" schema="pii_types" class="shadcn-multiselect-label">
Other
</label>
</div>
</div></fieldset></form>
</div>
<div class="display-logic-container display-logic-hidden"
data-display-logic="{&quot;show_when&quot;: [{&quot;schema&quot;: &quot;pii_types&quot;, &quot;operator&quot;: &quot;contains&quot;, &quot;value&quot;: &quot;Other&quot;}]}"
data-schema-name="other_pii_description"
data-display-logic-target="true">
<form id="other_pii_description" class="annotation-form textbox shadcn-textbox-container " action="javascript:void(0)" data-annotation-id="2" data-annotation-type="text" data-schema-name="other_pii_description" data-grid-columns="1">
<fieldset schema_name="other_pii_description">
<legend class="shadcn-textbox-title">Describe the other type of PII you found:</legend>
<div class="shadcn-textbox-item">
<input class="other_pii_description shadcn-textbox-input annotation-input"
type="text"
id="other_pii_description_text_box_text"
name="other_pii_description:::text_box"
validation=""
schema="other_pii_description"
label_name="text_box"
style=""
>
</div></fieldset></form>
</div>
<div class="display-logic-container display-logic-hidden"
data-display-logic="{&quot;show_when&quot;: [{&quot;schema&quot;: &quot;pii_types&quot;, &quot;operator&quot;: &quot;contains&quot;, &quot;value&quot;: &quot;SSN&quot;}]}"
data-schema-name="ssn_context"
data-display-logic-target="true">
<form id="ssn_context" class="annotation-form radio shadcn-radio-container" action="javascript:void(0)" data-annotation-id="3" data-annotation-type="radio" data-schema-name="ssn_context" data-grid-columns="1">
<fieldset schema="ssn_context">
<legend class="shadcn-radio-title">Is the SSN used in an appropriate context?</legend>
<div class="shadcn-radio-options">
<div class="shadcn-radio-option">
<input class="ssn_context shadcn-radio-input annotation-input"
type="radio"
id="ssn_context_Yes, appropriate use_radio"
name="ssn_context"
value="Yes, appropriate use"
selection_constraint="single"
schema="ssn_context"
label_name="Yes, appropriate use"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="ssn_context_Yes, appropriate use_radio" class="shadcn-radio-label" >Yes, Appropriate Use</label>
</div>
<div class="shadcn-radio-option">
<input class="ssn_context shadcn-radio-input annotation-input"
type="radio"
id="ssn_context_No, inappropriate_radio"
name="ssn_context"
value="No, inappropriate"
selection_constraint="single"
schema="ssn_context"
label_name="No, inappropriate"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="ssn_context_No, inappropriate_radio" class="shadcn-radio-label" >No, Inappropriate</label>
</div>
<div class="shadcn-radio-option">
<input class="ssn_context shadcn-radio-input annotation-input"
type="radio"
id="ssn_context_Cannot determine_radio"
name="ssn_context"
value="Cannot determine"
selection_constraint="single"
schema="ssn_context"
label_name="Cannot determine"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="ssn_context_Cannot determine_radio" class="shadcn-radio-label" >Cannot Determine</label>
</div>
</div></fieldset></form>
</div>
<div class="display-logic-container display-logic-hidden"
data-display-logic="{&quot;show_when&quot;: [{&quot;schema&quot;: &quot;contains_pii&quot;, &quot;operator&quot;: &quot;equals&quot;, &quot;value&quot;: &quot;No&quot;}]}"
data-schema-name="confidence"
data-display-logic-target="true">
<form id="confidence" class="annotation-form slider" action="javascript:void(0)" data-annotation-id="4" >
<fieldset schema="confidence">
<legend class="custom-slider-title">How confident are you that there is NO PII in this text?</legend>
<div class="custom-slider-container" id="customSlider_confidence_slider_range" tabindex="0">
<!-- Hidden actual input for form submission -->
<input type="range"
min="1"
max="10"
step="1"
value="5"
class="custom-slider-input annotation-input"
onclick="registerAnnotation(this);"
oninput="updateCustomSlider(this);"
label_name="slider"
name="confidence:::slider"
id="confidence_slider_range"
schema="confidence"
validation="">
<!-- Custom visual elements -->
<div class="custom-slider-track">
<div class="custom-slider-track-active" id="sliderTrackActive_confidence_slider_range"></div>
<div class="custom-slider-thumb" id="sliderThumb_confidence_slider_range">
<!-- Tooltip attached directly to thumb -->
<div class="slider-tooltip" id="sliderTooltip_confidence_slider_range">5</div>
</div>
</div>
<div class="custom-slider-ticks" id="sliderTicks_confidence_slider_range"></div>
</div>
</fieldset>
</form>
<script>
// Initialize the slider on page load
(function() {
const sliderId = "confidence_slider_range";
const sliderInput = document.getElementById(sliderId);
if (!sliderInput) return;
// Set up the slider initially
setupCustomSlider(sliderInput);
// Update positions on input change
sliderInput.addEventListener('input', function() {
updateCustomSlider(this);
});
})();
function setupCustomSlider(sliderInput) {
const sliderId = sliderInput.id;
const min = parseInt(sliderInput.min);
const max = parseInt(sliderInput.max);
const step = parseInt(sliderInput.step) || 1;
// Get references to custom elements
const container = document.getElementById('customSlider_' + sliderId);
const sliderThumb = document.getElementById('sliderThumb_' + sliderId);
const sliderTrackActive = document.getElementById('sliderTrackActive_' + sliderId);
const sliderTicks = document.getElementById('sliderTicks_' + sliderId);
const tooltip = document.getElementById('sliderTooltip_' + sliderId);
if (!container || !sliderThumb || !sliderTrackActive || !sliderTicks || !tooltip) return;
// Initialize slider position
updateSliderPosition(5, min, max, sliderThumb, sliderTrackActive, tooltip);
// Create tick marks
createTicks(min, max, step, sliderTicks);
container.addEventListener('click', function(e) {
// Skip if the click is on the input or thumb (to prevent jumps during dragging)
if (e.target === sliderThumb || e.target === sliderInput) return;
// Get click position relative to the track
const rect = container.getBoundingClientRect();
const clickPosition = e.clientX - rect.left;
const percentClicked = (clickPosition / rect.width) * 100;
// Calculate the new value based on the click position
let newValue = min + (percentClicked / 100) * (max - min);
// Snap to the nearest step
newValue = Math.round(newValue / step) * step;
// Ensure the value is within bounds
newValue = Math.max(min, Math.min(max, newValue));
// Update the input value
sliderInput.value = newValue;
// Update the visual position
updateSliderPosition(newValue, min, max, sliderThumb, sliderTrackActive, tooltip);
// Trigger change event for any listeners
const event = new Event('input', { bubbles: true });
sliderInput.dispatchEvent(event);
// If registerAnnotation exists, call it
if (typeof registerAnnotation === 'function') {
registerAnnotation(sliderInput);
}
});
// Add keyboard control
container.addEventListener('keydown', function(e) {
// Only process arrow keys
if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
// Prevent default to avoid page scrolling
e.preventDefault();
// Get current value
let currentValue = parseFloat(sliderInput.value);
// Update value based on arrow key
if (e.key === 'ArrowDown') {
currentValue = Math.max(min, currentValue - step);
} else if (e.key === 'ArrowUp') {
currentValue = Math.min(max, currentValue + step);
}
// Update the input value
sliderInput.value = currentValue;
// Update the visual position
updateSliderPosition(currentValue, min, max, sliderThumb, sliderTrackActive, tooltip);
// Trigger change event for any listeners
const event = new Event('input', { bubbles: true });
sliderInput.dispatchEvent(event);
// If registerAnnotation exists, call it
if (typeof registerAnnotation === 'function') {
registerAnnotation(sliderInput);
}
});
// Add focus styles for keyboard users
container.addEventListener('focus', function() {
container.classList.add('focus');
});
container.addEventListener('blur', function() {
container.classList.remove('focus');
});
// Add drag and hold functionality
let isDragging = false;
// Mouse events for drag and hold
sliderThumb.addEventListener('mousedown', function(e) {
isDragging = true;
e.preventDefault(); // Prevent text selection during drag
// Show tooltip during drag
sliderThumb.classList.add('dragging');
// Add event listeners for drag and release
document.addEventListener('mousemove', handleMouseDrag);
document.addEventListener('mouseup', stopDrag);
});
// Touch events for mobile devices
sliderThumb.addEventListener('touchstart', function(e) {
isDragging = true;
e.preventDefault(); // Prevent scrolling during drag
// Show tooltip during drag
sliderThumb.classList.add('dragging');
// Add event listeners for drag and release
document.addEventListener('touchmove', handleTouchDrag);
document.addEventListener('touchend', stopDrag);
document.addEventListener('touchcancel', stopDrag);
});
// Handle mouse drag
function handleMouseDrag(e) {
if (!isDragging) return;
// Get the slider's position and dimensions
const rect = container.getBoundingClientRect();
// Calculate position within the slider (constrain to slider width)
let position = e.clientX - rect.left;
position = Math.max(0, Math.min(position, rect.width));
// Calculate value and update slider
updateSliderFromPosition(position, rect.width);
}
// Handle touch drag
function handleTouchDrag(e) {
if (!isDragging || !e.touches[0]) return;
const touch = e.touches[0];
const rect = container.getBoundingClientRect();
// Calculate position within the slider (constrain to slider width)
let position = touch.clientX - rect.left;
position = Math.max(0, Math.min(position, rect.width));
// Calculate value and update slider
updateSliderFromPosition(position, rect.width);
}
// Helper function to update slider from mouse/touch position
function updateSliderFromPosition(position, width) {
// Convert position to a percentage
const percent = (position / width) * 100;
// Calculate the value based on percentage
let newValue = min + (percent / 100) * (max - min);
// Snap to the nearest step
newValue = Math.round(newValue / step) * step;
// Ensure the value is within bounds
newValue = Math.max(min, Math.min(max, newValue));
// Update the input value
sliderInput.value = newValue;
// Update the visual position
updateSliderPosition(newValue, min, max, sliderThumb, sliderTrackActive, tooltip);
// Trigger change event
const event = new Event('input', { bubbles: true });
sliderInput.dispatchEvent(event);
// If registerAnnotation exists, call it
if (typeof registerAnnotation === 'function') {
registerAnnotation(sliderInput);
}
}
// Stop dragging
function stopDrag() {
if (!isDragging) return;
isDragging = false;
sliderThumb.classList.remove('dragging');
// Remove the event listeners
document.removeEventListener('mousemove', handleMouseDrag);
document.removeEventListener('touchmove', handleTouchDrag);
document.removeEventListener('mouseup', stopDrag);
document.removeEventListener('touchend', stopDrag);
document.removeEventListener('touchcancel', stopDrag);
}
}
function updateCustomSlider(sliderInput) {
const sliderId = sliderInput.id;
const min = parseInt(sliderInput.min);
const max = parseInt(sliderInput.max);
const sliderThumb = document.getElementById('sliderThumb_' + sliderId);
const sliderTrackActive = document.getElementById('sliderTrackActive_' + sliderId);
const tooltip = document.getElementById('sliderTooltip_' + sliderId);
if (!sliderThumb || !sliderTrackActive || !tooltip) return;
updateSliderPosition(sliderInput.value, min, max, sliderThumb, sliderTrackActive, tooltip);
}
function updateSliderPosition(value, min, max, thumbElement, trackElement, tooltipElement) {
const percent = ((value - min) / (max - min)) * 100;
thumbElement.style.left = `${percent}%`;
trackElement.style.width = `${percent}%`;
// Update tooltip content only - it moves with the thumb automatically
if (tooltipElement) {
tooltipElement.textContent = value;
}
}
function createTicks(min, max, step, tickContainer) {
// Clear existing ticks
tickContainer.innerHTML = '';
// Calculate optimal ticks
const ticks = calculateOptimalTicks(min, max, step);
ticks.forEach(tick => {
const percent = ((tick.value - min) / (max - min)) * 100;
const tickElement = document.createElement('div');
tickElement.className = 'custom-slider-tick';
tickElement.style.left = `${percent}%`;
const tickMark = document.createElement('div');
tickMark.className = tick.showLabel ? 'custom-slider-tick-mark major' : 'custom-slider-tick-mark';
tickElement.appendChild(tickMark);
if (tick.showLabel) {
const tickLabel = document.createElement('div');
tickLabel.className = 'custom-slider-tick-label';
tickLabel.textContent = tick.value;
tickElement.appendChild(tickLabel);
}
tickContainer.appendChild(tickElement);
});
}
function calculateOptimalTicks(min, max, sliderStep, maxTicks = 8) {
const range = max - min;
// Generate all possible slider positions based on the step
const possibleValues = [];
for (let value = min; value <= max; value += sliderStep) {
possibleValues.push(Math.round(value));
}
// If we have few enough values, show them all
if (possibleValues.length <= maxTicks) {
return possibleValues.map(value => ({ value, showLabel: true }));
}
// Calculate a "nice" interval for major ticks
const targetInterval = range / (maxTicks - 1);
// Find nice intervals: 1, 2, 5, 10, 20, 50, 100, etc.
const candidates = [];
for (let magnitude = 1; magnitude <= range; magnitude *= 10) {
candidates.push(magnitude); // 1, 10, 100...
candidates.push(2 * magnitude); // 2, 20, 200...
candidates.push(5 * magnitude); // 5, 50, 500...
}
// Choose the candidate closest to our target
let bestInterval = candidates[0];
let bestDiff = Math.abs(candidates[0] - targetInterval);
for (const candidate of candidates) {
const diff = Math.abs(candidate - targetInterval);
if (diff < bestDiff) {
bestInterval = candidate;
bestDiff = diff;
}
}
// Generate major ticks using the best interval
const majorTicks = [];
// Always start with min
majorTicks.push({ value: min, showLabel: true });
// Add ticks at nice intervals
for (let tickValue = bestInterval; tickValue < max; tickValue += bestInterval) {
if (tickValue > min) {
majorTicks.push({ value: tickValue, showLabel: true });
}
}
// Always end with max (if it's not the same as the last tick)
if (majorTicks[majorTicks.length - 1].value !== max) {
majorTicks.push({ value: max, showLabel: true });
}
// Add minor ticks for values between major ticks
const allTicks = [...majorTicks];
// Only add minor ticks if there's reasonable spacing
if (majorTicks.length > 1) {
const majorSpacing = bestInterval;
if (majorSpacing > sliderStep * 2) {
possibleValues.forEach(value => {
if (!allTicks.some(t => t.value === value)) {
allTicks.push({ value, showLabel: false });
}
});
}
}
return allTicks.sort((a, b) => a.value - b.value);
}
</script>
</div>
<div class="display-logic-container display-logic-hidden"
data-display-logic="{&quot;show_when&quot;: [{&quot;schema&quot;: &quot;confidence&quot;, &quot;operator&quot;: &quot;in_range&quot;, &quot;value&quot;: [1, 3]}]}"
data-schema-name="uncertainty_reason"
data-display-logic-target="true">
<form id="uncertainty_reason" class="annotation-form textbox shadcn-textbox-container " action="javascript:void(0)" data-annotation-id="5" data-annotation-type="text" data-schema-name="uncertainty_reason" data-grid-columns="1">
<fieldset schema_name="uncertainty_reason">
<legend class="shadcn-textbox-title">What makes you uncertain? (Optional)</legend>
<div class="shadcn-textbox-item">
<input class="uncertainty_reason shadcn-textbox-input annotation-input"
type="text"
id="uncertainty_reason_text_box_text"
name="uncertainty_reason:::text_box"
validation=""
schema="uncertainty_reason"
label_name="text_box"
style=""
>
</div></fieldset></form>
</div>
<div class="display-logic-container display-logic-hidden"
data-display-logic="{&quot;show_when&quot;: [{&quot;schema&quot;: &quot;contains_pii&quot;, &quot;operator&quot;: &quot;equals&quot;, &quot;value&quot;: &quot;Uncertain&quot;}]}"
data-schema-name="review_needed"
data-display-logic-target="true">
<form id="review_needed" class="annotation-form radio shadcn-radio-container" action="javascript:void(0)" data-annotation-id="6" data-annotation-type="radio" data-schema-name="review_needed" data-grid-columns="1">
<fieldset schema="review_needed">
<legend class="shadcn-radio-title">Should this item be reviewed by another annotator?</legend>
<div class="shadcn-radio-options">
<div class="shadcn-radio-option">
<input class="review_needed shadcn-radio-input annotation-input"
type="radio"
id="review_needed_Yes, needs review_radio"
name="review_needed"
value="Yes, needs review"
selection_constraint="single"
schema="review_needed"
label_name="Yes, needs review"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="review_needed_Yes, needs review_radio" class="shadcn-radio-label" >Yes, Needs Review</label>
</div>
<div class="shadcn-radio-option">
<input class="review_needed shadcn-radio-input annotation-input"
type="radio"
id="review_needed_No, I&#x27;m confident enough_radio"
name="review_needed"
value="No, I&#x27;m confident enough"
selection_constraint="single"
schema="review_needed"
label_name="No, I&#x27;m confident enough"
onclick="onlyOne(this);registerAnnotation(this);"
validation=""
>
<label for="review_needed_No, I&#x27;m confident enough_radio" class="shadcn-radio-label" >No, I&#x27;m Confident Enough</label>
</div>
</div></fieldset></form>
</div>
<form id="notes" class="annotation-form textbox shadcn-textbox-container " action="javascript:void(0)" data-annotation-id="7" data-annotation-type="text" data-schema-name="notes" data-grid-columns="1">
<fieldset schema_name="notes">
<legend class="shadcn-textbox-title">Any additional notes about this text (optional):</legend>
<div class="shadcn-textbox-item">
<input class="notes shadcn-textbox-input annotation-input"
type="text"
id="notes_text_box_text"
name="notes:::text_box"
validation=""
schema="notes"
label_name="text_box"
style=""
>
</div></fieldset></form>
</div>