1import { Controller } from "@hotwired/stimulus" 2import { hasAdvertisingConsent } from "controllers/iubenda_consent" 3 4// Loads SourceForge / Slashdot / Top Business Software review badges 5// only after advertising consent (Iubenda purpose 5). 6// 7// Consent category rationale: Purpose 5 (targeting & advertising) 8// rather than Purpose 4 (analytics), because SF badges are third-party 9// marketing widgets showcasing the product on a lead-generation directory 10// â they serve promotional, not measurement, purposes. Same classification 11// as the LinkedIn Insight Tag and Google Ads gtag loaders in this repo. 12// 13// GDPR-compliant flow: 14// 1. Controller connects on page load 15// 2. Checks Iubenda consent cookie for advertising permission 16// 3. Only injects third-party badge scripts if consent is granted 17// 4. When Iubenda refreshes page after consent, controller re-runs and loads scripts 18// 19// The fallback <a href> links inside .sf-root divs render unconditionally 20// in the ERB template, so dofollow backlinks remain visible to Googlebot 21// and no-consent visitors â that is the primary SEO value. 22// 23// Three SourceForge-family variants are loaded: 24// - sf â sourceforge.net listing 25// - sd â slashdot.org listing 26// - tbs â topbusinesssoftware.com listing 27// 28// Usage: Add to the container wrapping the three .sf-root divs: 29// data-controller="sourceforge-badges" 30// data-sourceforge-badges-sf-id-value="<product-sf-id>" 31// 32// @see https://sourceforge.net/ (badge generator in vendor dashboard) 33export default class extends Controller { 34 static values = { sfId: String } 35 static VARIANTS = ["sf", "sd", "tbs"] 36 37 connect() { 38 if (!this.sfIdValue) return 39 if (!hasAdvertisingConsent()) return 40 41 this.constructor.VARIANTS.forEach((variant) => this.injectBadgeScript(variant)) 42 } 43 44 injectBadgeScript(variant) { 45 // Guard against duplicate loading (Turbo navigation re-connects controllers) 46 if (document.querySelector(`script[data-sf-badge-variant="${variant}"]`)) return 47 48 const script = document.createElement("script") 49 script.async = true 50 script.src = `https://b.sf-syn.com/badge_js?sf_id=${this.sfIdValue}&variant_id=${variant}` 51 script.dataset.sfBadgeVariant = variant 52 document.head.appendChild(script) 53 } 54}
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.