2626</ style >
2727
2828< div class ="communities-wrap pt-44 pb-16 px-6 max-w-4xl mx-auto ">
29- < h1 > {{ page.title | default: "Communities" }}</ h1 >
29+ < h2 > {{ page.title | default: "Communities" }}</ h2 >
3030
3131 < div class ="controls ">
3232 < input id ="search " type ="search " placeholder ="Search speakers (name / topic / linkedin)... " />
@@ -48,10 +48,12 @@ <h1>{{ page.title | default: "Communities" }}</h1>
4848 {% for event in site.events %}
4949 {% if event.speakers %}
5050 {% for sp in event.speakers %}
51- {% comment %} original display url (may include https) or fallback to name {% endcomment %}
52- {% assign display_url = sp.url | default: sp.name %}
53- {% comment %} create a normalized key for dedupe:
54- strip scheme, www, remove slashes, lower-case {% endcomment %}
51+ {% if sp.url contains "linkedin.com" %}
52+ {% assign display_url = sp.url %}
53+ {% else %}
54+ {% continue %}
55+ {% endif %}
56+
5557 {% assign key = display_url | remove: "https://" | remove: "http://" | remove: "www." | replace: "/", "" | downcase %}
5658
5759 {% assign updated = false %}
@@ -65,7 +67,6 @@ <h1>{{ page.title | default: "Communities" }}</h1>
6567 {% assign ex_name = parts[2] %}
6668 {% assign ex_count = parts[3] %}
6769 {% if ex_key == key %}
68- {% comment %} choose longest name and increment count {% endcomment %}
6970 {% assign cur_name = sp.name %}
7071 {% if cur_name.size > ex_name.size %}
7172 {% assign chosen_name = cur_name %}
@@ -92,27 +93,61 @@ <h1>{{ page.title | default: "Communities" }}</h1>
9293 {% endif %}
9394 {% endfor %}
9495
95- <!-- Render the grid (render order is the insertion order; client JS will sort) -->
96+ <!-- Render the grid -->
9697 < div id ="speakers-grid ">
97- {% for item in speakers %}
98- {% if item != "" %}
99- {% assign parts = item | split: "|||" %}
100- {% assign key = parts[0] %}
101- {% assign display_url = parts[1] %}
102- {% assign name = parts[2] %}
103- {% assign count = parts[3] %}
104- <!-- anchor to open linkedin in new tab; data attributes used for JS sorting/filtering -->
105- < a class ="speaker-card " href ="{{ display_url }} " target ="_blank " rel ="noopener noreferrer "
106- data-name ="{{ name | escape }} " data-url ="{{ display_url | escape }} " data-count ="{{ count }} ">
107- <!-- avatar via unavatar.io; pass URL-encoded display_url -->
108- < img src ="https://unavatar.io/{{ display_url | url_encode }} " alt ="{{ name | escape }} "
109- onerror ="this.onerror=null;this.src='/assets/img/avatar-placeholder.png' ">
110- < div class ="speaker-name "> {{ name }}</ div >
111- < div class ="speaker-badge "> {{ count }}x</ div >
112- </ a >
113- {% endif %}
114- {% endfor %}
115- </ div >
98+ {% for item in speakers %}
99+ {% if item != "" %}
100+ {% assign parts = item | split: "|||" %}
101+ {% assign key = parts[0] %}
102+ {% assign display_url = parts[1] %}
103+ {% assign name = parts[2] %}
104+ {% assign count = parts[3] %}
105+
106+ {%- comment -%}
107+ Image filename resolution order (priority jpeg > jpg > png):
108+ 1. slug-first.jpeg / .jpg / .png
109+ 2. full-name.jpeg / .jpg / .png
110+ 3. first-word.jpeg / .jpg / .png
111+ 4. placeholder.png
112+ {%- endcomment -%}
113+
114+ {% assign slug = display_url | split: "/" | last %}
115+ {% assign slug_first = slug | split: "-" | first %}
116+ {% assign name_file = name | downcase | replace: " ", "-" %}
117+ {% assign first_word = name | split: " " | first | downcase %}
118+
119+ {% assign file_jpeg_slug = "../assets/lombokdev/speakers/" | append: slug_first | append: ".jpeg" %}
120+ {% assign file_jpg_slug = "../assets/lombokdev/speakers/" | append: slug_first | append: ".jpg" %}
121+ {% assign file_png_slug = "../assets/lombokdev/speakers/" | append: slug_first | append: ".png" %}
122+
123+ {% assign file_jpeg_name = "../assets/lombokdev/speakers/" | append: name_file | append: ".jpeg" %}
124+ {% assign file_jpg_name = "../assets/lombokdev/speakers/" | append: name_file | append: ".jpg" %}
125+ {% assign file_png_name = "../assets/lombokdev/speakers/" | append: name_file | append: ".png" %}
126+
127+ {% assign file_jpeg_first = "../assets/lombokdev/speakers/" | append: first_word | append: ".jpeg" %}
128+ {% assign file_jpg_first = "../assets/lombokdev/speakers/" | append: first_word | append: ".jpg" %}
129+ {% assign file_png_first = "../assets/lombokdev/speakers/" | append: first_word | append: ".png" %}
130+
131+ < a class ="speaker-card " href ="{{ display_url }} " target ="_blank " rel ="noopener noreferrer "
132+ data-name ="{{ name | escape }} " data-url ="{{ display_url | escape }} " data-count ="{{ count }} ">
133+ < img src ="{{ file_jpeg_slug }} " alt ="{{ name | escape }} "
134+ onerror ="this.onerror=null;
135+ this.src='{{ file_jpg_slug }}';
136+ this.onerror=function(){this.src='{{ file_png_slug }}'};
137+ this.onerror=function(){this.src='{{ file_jpeg_name }}'};
138+ this.onerror=function(){this.src='{{ file_jpg_name }}'};
139+ this.onerror=function(){this.src='{{ file_png_name }}'};
140+ this.onerror=function(){this.src='{{ file_jpeg_first }}'};
141+ this.onerror=function(){this.src='{{ file_jpg_first }}'};
142+ this.onerror=function(){this.src='{{ file_png_first }}'};
143+ this.onerror=function(){this.src='../assets/lombokdev/speakers/placeholder.png'}; ">
144+ < div class ="speaker-name "> {{ name }}</ div >
145+ < div class ="speaker-badge "> {{ count }}x</ div >
146+ </ a >
147+ {% endif %}
148+ {% endfor %}
149+ </ div >
150+
116151</ div >
117152
118153< script >
@@ -128,7 +163,6 @@ <h1>{{ page.title | default: "Communities" }}</h1>
128163 function filterCards ( ) {
129164 const term = ( searchInput . value || '' ) . trim ( ) . toLowerCase ( ) ;
130165 getCards ( ) . forEach ( card => {
131- // searchable text: name + url
132166 const name = card . dataset . name . toLowerCase ( ) ;
133167 const url = card . dataset . url . toLowerCase ( ) ;
134168 const visible = name . includes ( term ) || url . includes ( term ) ;
@@ -137,7 +171,7 @@ <h1>{{ page.title | default: "Communities" }}</h1>
137171 }
138172
139173 function sortCards ( order ) {
140- const cards = getCards ( ) . slice ( ) ; // copy
174+ const cards = getCards ( ) . slice ( ) ;
141175 cards . sort ( ( a , b ) => {
142176 const nameA = a . dataset . name . toLowerCase ( ) ;
143177 const nameB = b . dataset . name . toLowerCase ( ) ;
@@ -146,13 +180,11 @@ <h1>{{ page.title | default: "Communities" }}</h1>
146180 if ( order === 'most' ) {
147181 const ca = parseInt ( a . dataset . count || '0' , 10 ) ;
148182 const cb = parseInt ( b . dataset . count || '0' , 10 ) ;
149- // if same count, tie-break by name A→Z
150183 if ( cb === ca ) return nameA . localeCompare ( nameB ) ;
151184 return cb - ca ;
152185 }
153186 return 0 ;
154187 } ) ;
155- // append in sorted order (preserves only visible display settings)
156188 cards . forEach ( c => grid . appendChild ( c ) ) ;
157189 }
158190
0 commit comments