1// Platform and feature handling 2const PLATFORM_SUPPORT = { 3 limited: [ 4 'mcgraw-hill', 5 'cengage-mindtap', 6 'pearson-mylab', 7 'macmillan-learning' 8 ], 9 pending: [ 10 'khan-academy', 11 'classmaker', 12 'moodle', 13 'schoology', 14 'edmodo', 15 'myeducator', 16 'exam-builder' 17 ] 18}; 19 20const buttonPlatforms = [ 21 'canvas-instructure', 22 'brightspace-d2l', 23 'google-classroom-forms', 24 'tophat', 25 'blackboard' 26]; 27 28let selectedPlatforms = new Set(); 29let selectedFeatures = new Map(); 30let selectedPauseDuration = null; 31let additionalFeedback = { 32 issues: new Set(), 33 comments: '' 34}; 35 36// Platform handlers 37function handlePlatformSelection(platform) { 38 const button = event.target; 39 const continueBtn = document.querySelector('.cancel-flow-continue'); 40 41 // Don't allow selecting if it's the other-input 42 if (button.classList.contains('other-input')) { 43 return; 44 } 45 46 if (button.classList.contains('selected')) { 47 button.classList.remove('selected'); 48 selectedPlatforms.delete(platform); 49 // Only remove feature selections for the platform being removed 50 selectedFeatures.delete(platform); 51 } else { 52 button.classList.add('selected'); 53 if (platform) { 54 selectedPlatforms.add(platform); 55 } 56 } 57 58 // Update continue button state 59 if (selectedPlatforms.size > 0) { 60 continueBtn.classList.remove('disabled'); 61 continueBtn.style.pointerEvents = 'auto'; 62 } else { 63 continueBtn.classList.add('disabled'); 64 continueBtn.style.pointerEvents = 'none'; 65 } 66} 67 68function handleFeatureSelection(platform, feature) { 69 if (!selectedFeatures.has(platform)) { 70 selectedFeatures.set(platform, new Set()); 71 } 72 73 const platformFeatures = selectedFeatures.get(platform); 74 const button = event.target; 75 76 if (button.classList.contains('selected')) { 77 button.classList.remove('selected'); 78 platformFeatures.delete(feature); 79 } else { 80 button.classList.add('selected'); 81 platformFeatures.add(feature); 82 } 83 84 // Clear existing additional feedback when features change 85 additionalFeedback.issues.clear(); 86} 87 88function handlePauseSelection(months) { 89 selectedPauseDuration = months; 90 91 const buttons = document.querySelectorAll('.pause-button'); 92 const pauseBtn = document.querySelector('.cancel-flow-pause'); 93 const resumeDateSpan = document.querySelector('.resume-date span'); 94 95 if (buttons) { 96 buttons.forEach(btn => btn.classList.remove('selected')); 97 event.target.classList.add('selected'); 98 } 99 100 if (pauseBtn) { 101 pauseBtn.classList.remove('disabled'); 102 } 103 104 if (resumeDateSpan) { 105 const resumeDate = new Date(); 106 resumeDate.setMonth(resumeDate.getMonth() + months); 107 const formattedDate = resumeDate.toLocaleDateString('en-US', { 108 month: 'long', 109 day: 'numeric', 110 year: 'numeric' 111 }); 112 resumeDateSpan.textContent = formattedDate; 113 } 114} 115 116function handleReasonSelection(reason) { 117 reasonCancellationTrack(reason); 118 selectedReason = reason; 119 120 const buttons = document.querySelectorAll('.reason-button'); 121 const continueBtn = document.querySelector('.cancel-flow-continue'); 122 123 if (buttons) { 124 buttons.forEach(btn => btn.classList.remove('selected', 'selected-flash')); 125 event.target.classList.add('selected', 'selected-flash'); 126 } 127 128 // Enable continue button when a reason is selected 129 if (continueBtn) { 130 continueBtn.classList.remove('disabled'); 131 continueBtn.style.pointerEvents = 'auto'; 132 } 133 134 setTimeout(() => { 135 handleContinue(); 136 }, 500); 137} 138 139// Feature generation 140function generateFeatureSections() { 141 const container = document.querySelector('.platform-features-container'); 142 container.innerHTML = ''; // Clear existing content 143 144 // Features to show for each platform 145 const features = ['Text Selection', 'Button', 'Screenshot', 'AI Chat']; 146 147 // List of platforms with limited support 148 const limitedSupportPlatforms = [ 149 'mcgraw-hill', 150 'cengage-mindtap', 151 'pearson-mylab', 152 'macmillan-learning' 153 ]; 154 155 // Create a section for each selected platform 156 selectedPlatforms.forEach(platform => { 157 const section = document.createElement('div'); 158 section.className = 'platform-feature-section'; 159 section.setAttribute('data-platform', platform); 160 161 const titleContainer = document.createElement('div'); 162 titleContainer.className = 'platform-feature-header'; 163 164 const title = document.createElement('h3'); 165 title.className = 'platform-feature-title'; 166 const platformName = platform.split('-') 167 .map(word => word.charAt(0).toUpperCase() + word.slice(1)) 168 .join(' '); 169 title.textContent = `Platform: ${platformName}`; 170 171 titleContainer.appendChild(title); 172 173 // Check platform support status 174 const isLimitedSupport = PLATFORM_SUPPORT.limited.includes(platform.toLowerCase());
175 const isPendingSupport = PLATFORM_SUPPORT.pending.includes(platform.toLowerCase()) || 176 (!PLATFORM_SUPPORT.limited.includes(platform.toLowerCase()) && 177 !buttonPlatforms.includes(platform.toLowerCase()) && 178 platform.toLowerCase() !== 'other'); 179 180 if (isLimitedSupport) { 181 const warning = document.createElement('div'); 182 warning.className = 'platform-support-warning'; 183 warning.innerHTML = ` 184 <i class="fas fa-exclamation-circle"></i> 185 <span>Direct support unavailable. Use AI Chat or Web Solver instead.</span> 186 <button onclick="showIncompatibleModal()" class="warning-learn-more"> 187 Learn more 188 <i class="fas fa-arrow-right"></i> 189 </button> 190 `; 191 titleContainer.appendChild(warning); 192 } else if (isPendingSupport) { 193 const info = document.createElement('div'); 194 info.className = 'platform-support-info'; 195 info.innerHTML = ` 196 <i class="fas fa-info-circle"></i> 197 <span>Platform not directly supported yet</span> 198 <button onclick="requestPlatformSupport('${platform}')" class="request-platform-button"> 199 Request platform 200 <i class="fas fa-arrow-right"></i> 201 </button> 202 `; 203 titleContainer.appendChild(info); 204 } 205 206 const buttonsContainer = document.createElement('div'); 207 buttonsContainer.className = 'feature-buttons'; 208 209 features.forEach(feature => { 210 const button = document.createElement('button'); 211 button.className = 'feature-button'; 212 const feature_id = feature.toLowerCase().replace(/\s+/g, '_'); 213 button.setAttribute('data-feature', feature_id); 214 215 // Get existing selections for this platform 216 const platformFeatures = selectedFeatures.get(platform) || new Set(); 217 218 // For limited support platforms, pre-select and disable all specified features 219 if (isLimitedSupport && ['Text Selection', 'Button', 'Screenshot'].includes(feature)) { 220 button.classList.add('selected', 'locked'); 221 if (!selectedFeatures.has(platform)) { 222 selectedFeatures.set(platform, new Set()); 223 } 224 selectedFeatures.get(platform).add(feature); 225 } 226 // For pending support platforms, only pre-select and disable Button 227 else if (isPendingSupport && feature === 'Button') { 228 button.classList.add('selected', 'locked'); 229 if (!selectedFeatures.has(platform)) { 230 selectedFeatures.set(platform, new Set()); 231 } 232 selectedFeatures.get(platform).add(feature); 233 } 234 else if (platformFeatures.has(feature)) { 235 // Restore previous selection 236 button.classList.add('selected'); 237 } 238 239 button.textContent = feature; 240 button.addEventListener('click', (e) => { 241 // Prevent deselection of locked features 242 if (!e.target.classList.contains('locked')) { 243 handleFeatureSelection(platform, feature); 244 } 245 }); 246 buttonsContainer.appendChild(button); 247 }); 248 249 section.appendChild(titleContainer); 250 section.appendChild(buttonsContainer); 251 container.appendChild(section); 252 }); 253} 254 255function generateAdditionalFeedback() { 256 const checkboxGroup = document.querySelector('.checkbox-group'); 257 if (!checkboxGroup) return; 258 259 // Store the original base questions (without the category wrapper) 260 const baseQuestions = ` 261 <label class="checkbox-label"> 262 <input type="checkbox" class="feedback-checkbox" data-issue="response-time"> 263 <span>Response times were consistently slow</span> 264 </label> 265 266 <label class="checkbox-label"> 267 <input type="checkbox" class="feedback-checkbox" data-issue="accuracy"> 268 <span>Responses weren't accurate enough</span> 269 </label> 270 271 <label class="checkbox-label"> 272 <input type="checkbox" class="feedback-checkbox" data-issue="technical"> 273 <span>I experienced technical issues or errors</span> 274 </label> 275 276 <label class="checkbox-label"> 277 <input type="checkbox" class="feedback-checkbox" data-issue="installation"> 278 <span>I couldn't install the extension</span> 279 </label> 280 281 <label class="checkbox-label"> 282 <input type="checkbox" class="feedback-checkbox" data-issue="usage"> 283 <span>The extension was difficult to use</span> 284 </label> 285 `; 286 287 let additionalQuestions = ''; 288 289 // Check if screenshot was manually selected (not auto-selected) 290 const hasScreenshotIssue = Array.from(selectedFeatures.entries()).some(([platform, features]) => { 291 const isLimitedPlatform = [ 292 'mcgraw-hill', 293 'cengage-mindtap', 294 'pearson-mylab', 295 'macmillan-learning' 296 ].includes(platform.toLowerCase()); 297 298 return features.has('Screenshot') && !isLimitedPlatform; 299 }); 300 301 // Check if button was selected for specific platforms 302 const buttonPlatforms = [ 303 'canvas-instructure', 304 'brightspace-d2l', 305 'google-classroom-forms', 306 'tophat', 307 'blackboard' 308 ]; 309 310 const hasButtonIssue = Array.from(selectedFeatures.entries()).some(([platform, features]) => 311 buttonPlatforms.includes(platform.toLowerCase()) && features.has('Button') 312 ); 313 314 // Build the complete HTML 315 let allContent = ` 316 <div class="feedback-category"> 317 <h4 class="feedback-category-title">General Issues</h4> 318 ${baseQuestions} 319 </div> 320 `; 321 322 // Add screenshot-related questions 323 if (hasScreenshotIssue) { 324 allContent += ` 325 <div class="feedback-category"> 326 <h4 class="feedback-category-title">Screenshot Issues</h4> 327 <label class="checkbox-label"> 328 <input type="checkbox" class="feedback-checkbox" data-issue="screenshot-off-topic">
329 <span>The answers were not related to the screenshot content</span> 330 </label> 331 <label class="checkbox-label"> 332 <input type="checkbox" class="feedback-checkbox" data-issue="screenshot-no-answer"> 333 <span>No answer was provided for the screenshot</span> 334 </label> 335 </div> 336 `; 337 } 338 339 // Add button-related questions 340 if (hasButtonIssue) { 341 // Get the platform name for the title 342 const platformWithButtonIssue = Array.from(selectedFeatures.entries()) 343 .find(([platform, features]) => 344 buttonPlatforms.includes(platform.toLowerCase()) && features.has('Button') 345 )[0]; 346 347 const platformName = platformWithButtonIssue.split('-') 348 .map(word => word.charAt(0).toUpperCase() + word.slice(1)) 349 .join(' '); 350 351 allContent += ` 352 <div class="feedback-category"> 353 <h4 class="feedback-category-title">Button integration issue (${platformName})</h4> 354 355 <div class="feedback-input-group"> 356 <label class="feedback-input-label"> 357 Domain 358 <div class="info-tooltip"> 359 <i class="fas fa-info-circle"></i> 360 <span class="tooltip-text">The URL where you use the platform (e.g., myschool.instructure.com)</span> 361 </div> 362 </label> 363 <input type="text" 364 class="feedback-text-input" 365 placeholder="e.g., myschool.instructure.com" 366 data-issue="platform-domain"> 367 </div> 368 369 <div class="feedback-input-group"> 370 <label class="feedback-input-label">Question Type Trying to Solve</label> 371 <select class="feedback-select" data-issue="question-type"> 372 <option value="">Select question type...</option> 373 <option value="multiple-choice">Multiple Choice</option> 374 <option value="true-false">True/False</option> 375 <option value="fill-blank">Fill in the Blank</option> 376 <option value="matching">Matching</option> 377 <option value="essay">Essay/Open Response</option> 378 <option value="numerical">Numerical/Math</option> 379 <option value="other">Other</option> 380 </select> 381 </div> 382 </div> 383 `; 384 } 385 386 // Update the checkbox group content 387 checkboxGroup.innerHTML = allContent; 388 389 // Reattach event listeners 390 document.querySelectorAll('.feedback-checkbox').forEach(checkbox => { 391 checkbox.addEventListener('change', handleCheckboxChange); 392 }); 393} 394 395function handleCheckboxChange(event) { 396 const checkbox = event.target; 397 const issue = checkbox.dataset.issue; 398 399 if (checkbox.checked) { 400 additionalFeedback.issues.add(issue); 401 } else { 402 additionalFeedback.issues.delete(issue); 403 } 404} 405 406function handleCommentsChange(event) { 407 additionalFeedback.comments = event.target.value.trim(); 408} 409 410function transformToInput(button) { 411 button.classList.add('other-input'); 412 button.innerHTML = ` 413 <input type="text" placeholder="Type platform name..." /> 414 <button class="add-button" disabled>Add</button> 415 `; 416 417 const input = button.querySelector('input'); 418 const addButton = button.querySelector('.add-button'); 419 420 input.focus(); 421 422 input.addEventListener('input', () => { 423 addButton.disabled = !input.value.trim(); 424 }); 425 426 addButton.addEventListener('click', () => { 427 const platformName = input.value.trim(); 428 if (platformName) { 429 // Create new platform button 430 const newButton = document.createElement('button'); 431 newButton.className = 'platform-button selected'; 432 newButton.setAttribute('data-platform', platformName.toLowerCase().replace(/\s+/g, '-')); 433 newButton.textContent = platformName; 434 435 // Add random transform 436 const tx = (Math.random() - 0.5) * 20; 437 const ty = (Math.random() - 0.5) * 20; 438 const rot = (Math.random() - 0.5) * 5; 439 newButton.style.setProperty('--tx', `${tx}px`); 440 newButton.style.setProperty('--ty', `${ty}px`); 441 newButton.style.setProperty('--rot', `${rot}deg`); 442 443 // Add click handler 444 newButton.addEventListener('click', (e) => { 445 handlePlatformSelection(e.target.dataset.platform); 446 }); 447 448 // Insert before the Other button 449 button.parentNode.insertBefore(newButton, button); 450 451 // Add to selected platforms 452 if (platformName) { 453 selectedPlatforms.add(platformName.toLowerCase().replace(/\s+/g, '-')); 454 } 455 456 // Reset the input 457 input.value = ''; 458 addButton.disabled = true; 459 input.focus(); 460 } 461 }); 462 463 // Handle enter key 464 input.addEventListener('keypress', (e) => { 465 if (e.key === 'Enter' && !addButton.disabled) { 466 addButton.click(); 467 } 468 }); 469} 470 471// Event listeners 472document.addEventListener('DOMContentLoaded', () => { 473 // Platform buttons 474 document.querySelectorAll('.platform-button').forEach(btn => { 475 const tx = (Math.random() - 0.5) * 20; 476 const ty = (Math.random() - 0.5) * 20; 477 const rot = (Math.random() - 0.5) * 5; 478 btn.style.setProperty('--tx', `${tx}px`); 479 btn.style.setProperty('--ty', `${ty}px`); 480 btn.style.setProperty('--rot', `${rot}deg`); 481 482 btn.addEventListener('click', (e) => { 483 if (e.target.dataset.platform === 'other' && !e.target.classList.contains('other-input')) { 484 transformToInput(e.target); 485 } else { 486 handlePlatformSelection(e.target.dataset.platform); 487 } 488 }); 489 }); 490 491 // Pause buttons 492 document.querySelectorAll('.pause-button').forEach(btn => { 493 btn.addEventListener('click', (e) => { 494 handlePauseSelection(parseInt(e.target.dataset.months)); 495 }); 496 }); 497 498 // Reason buttons 499 document.querySelectorAll('.reason-button').forEach(btn => { 500 btn.addEventListener('click', (e) => { 501 handleReasonSelection(e.target.dataset.reason); 502 }); 503 }); 504 505 // Feature buttons (these are dynamically added, but initial ones) 506 document.querySelectorAll('.feature-button').forEach(btn => { 507 btn.addEventListener('click', (e) => { 508 if (!e.target.classList.contains('locked')) { 509 handleFeatureSelection(e.target.dataset.platform, e.target.textContent); 510 } 511 }); 512 }); 513 514 // Feedback checkboxes 515 document.querySelectorAll('.feedback-checkbox').forEach(checkbox => { 516 checkbox.addEventListener('change', handleCheckboxChange); 517 }); 518 519 // Additional comments textarea 520 const textarea = document.getElementById('additional-comments'); 521 if (textarea) { 522 textarea.addEventListener('input', handleCommentsChange); 523 } 524 525 // Feature request textarea 526 const featureRequestTextarea = document.getElementById('feature-request'); 527 if (featureRequestTextarea) { 528 featureRequestTextarea.addEventListener('input', handleFeatureRequestChange); 529 } 530 531 // Password inputs 532 const passwordInputs = document.querySelectorAll('.password-input-group input');
533 passwordInputs.forEach(input => { 534 input.addEventListener('input', validatePasswords); 535 }); 536}); 537 538async function handlePause() { 539 if (selectedPauseDuration) { 540 pauseCancellationTrack(selectedPauseDuration); 541 console.log(`Pausing subscription for ${selectedPauseDuration} months`); 542 const pauseBtn = document.querySelector('.cancel-flow-pause'); 543 pauseBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>'; 544 pauseBtn.classList.add('disabled'); 545 546 const result = await pauseSubscription(selectedPauseDuration); 547 548 // Enable the button and hide the spinner 549 pauseBtn.textContent = "Pause"; 550 pauseBtn.classList.remove("disabled"); 551 552 if (result.success === false && result.message === "Paypal") { 553 showPaypalFixModal(result.link); 554 } else if (result.success) { 555 showPauseSuccessModal(); 556 } else { 557 showToast(result.message, 'error'); 558 } 559 } 560} 561 562 563// Add function to check for incompatible platforms 564function checkIncompatiblePlatforms() { 565 const incompatiblePlatforms = [ 566 'mcgraw-hill', 567 'cengage-mindtap', 568 'pearson-mylab', 569 'macmillan-learning' 570 ]; 571 572 const hasIncompatiblePlatform = Array.from(selectedPlatforms) 573 .some(platform => incompatiblePlatforms.includes(platform.toLowerCase())); 574 575 if (hasIncompatiblePlatform) { 576 showIncompatibleModal(); 577 } 578} 579 580// Add modal control functions 581function showIncompatibleModal() { 582 const modal = document.getElementById('incompatible-platforms-modal'); 583 if (modal) { 584 modal.style.display = 'flex'; 585 } 586} 587 588function closeIncompatibleModal() { 589 const modal = document.getElementById('incompatible-platforms-modal'); 590 if (modal) { 591 modal.style.display = 'none'; 592 } 593}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.