webring-rails 1.4.4 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/webring/widget.js +66 -24
- data/app/assets/javascripts/webring/widget.min.js +1 -1
- data/app/controllers/webring/members_controller.rb +17 -3
- data/app/controllers/webring/navigation_controller.rb +3 -3
- data/app/models/concerns/webring/navigation.rb +20 -18
- data/app/models/webring/member.rb +3 -0
- data/lib/generators/webring/member/templates/migration.rb +1 -0
- data/lib/generators/webring/member/templates/model.rb +3 -0
- data/lib/generators/webring/navigation_controller/templates/navigation_controller.rb +10 -3
- data/lib/webring/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82bc3c0e725233f97c81c7f5d93300155cf4a926618b9f0cfc0b34728411ce90
|
4
|
+
data.tar.gz: 81704a8acd70cf5f060f6208eed0512ace47504e28ddf4071caee9d04e7f4589
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b83576e412c50d3e060bdfc056d2695da79f576aae7c398d85e4a0a8efe77c68d3dd57bdcef5cdbb5c1654dc7e7331f4894817cf05b0c6c853060bd36fcab269
|
7
|
+
data.tar.gz: 1a717972d57d3361d51d6f250b1ada90ea38d7d5142dba6bbf573ab6fd57a39d9f5431dd835f2ede7be9969f166805f1fd8d1541620254e2e4eaffa6c2cb96ba
|
@@ -22,6 +22,7 @@
|
|
22
22
|
* - full: Apply all styles (default)
|
23
23
|
* - layout: Only layout styles, no visual design
|
24
24
|
* - none: No styles applied
|
25
|
+
* - data-theme="light|dark": Sets the theme for the widget. Default: light
|
25
26
|
* - data-prev-text="Custom Text": Sets custom text for the "previous" button. Default: "« Prev"
|
26
27
|
* - data-random-text="Custom Text": Sets custom text for the "random" button (keeps the logo). Default: "Random"
|
27
28
|
* - data-next-text="Custom Text": Sets custom text for the "next" button. Default: "Next »"
|
@@ -36,7 +37,9 @@
|
|
36
37
|
DEFAULT_TARGET_ID: 'webring-widget',
|
37
38
|
STYLE_ID: 'webring-widget-styles',
|
38
39
|
VALID_STYLE_TYPES: Object.freeze(['full', 'layout', 'none']),
|
39
|
-
DEFAULT_STYLE_TYPE: 'full'
|
40
|
+
DEFAULT_STYLE_TYPE: 'full',
|
41
|
+
VALID_THEMES: Object.freeze(['light', 'dark']),
|
42
|
+
DEFAULT_THEME: 'light'
|
40
43
|
});
|
41
44
|
|
42
45
|
// Default text configurations
|
@@ -204,8 +207,7 @@
|
|
204
207
|
box-sizing: border-box;
|
205
208
|
}
|
206
209
|
.webring-nav .webring-title {
|
207
|
-
margin
|
208
|
-
margin-top: 0;
|
210
|
+
margin: 0 0 8px 0;
|
209
211
|
padding: 0;
|
210
212
|
font-size: inherit;
|
211
213
|
line-height: normal;
|
@@ -245,48 +247,84 @@
|
|
245
247
|
.webring-nav .webring-logo-inline {
|
246
248
|
display: inline-block;
|
247
249
|
vertical-align: middle;
|
248
|
-
margin
|
249
|
-
margin-top: 1px;
|
250
|
-
margin-bottom: 0;
|
251
|
-
margin-left: 0;
|
250
|
+
margin: 1px 6px 0 0;
|
252
251
|
}
|
253
252
|
.webring-nav[data-button-text="false"] .prev-btn {
|
254
|
-
padding
|
255
|
-
padding-right: 12.5px;
|
253
|
+
padding: 5px 12.5px 6px 12px;
|
256
254
|
}
|
257
255
|
.webring-nav[data-button-text="false"] .next-btn {
|
258
|
-
padding
|
259
|
-
padding-left: 12.5px;
|
256
|
+
padding: 5px 12px 6px 12.5px;
|
260
257
|
}
|
261
258
|
`,
|
262
259
|
design: `
|
260
|
+
/* CSS Custom Properties for easy theming */
|
261
|
+
.webring-nav {
|
262
|
+
/* Internal variables that use public API with fallbacks */
|
263
|
+
--webring-bg-color: var(--webring-theme-bg);
|
264
|
+
--webring-border-color: var(--webring-theme-border);
|
265
|
+
--webring-text-color: var(--webring-theme-text);
|
266
|
+
--webring-btn-bg: var(--webring-theme-btn-bg);
|
267
|
+
--webring-btn-border: var(--webring-theme-btn-border);
|
268
|
+
--webring-btn-text: var(--webring-theme-btn-text);
|
269
|
+
--webring-btn-hover-bg: var(--webring-theme-btn-hover-bg);
|
270
|
+
--webring-btn-hover-text: var(--webring-theme-btn-hover-text);
|
271
|
+
}
|
272
|
+
|
273
|
+
/* Common theme styles using CSS variables */
|
274
|
+
.webring-nav {
|
275
|
+
background-color: var(--webring-bg-color);
|
276
|
+
}
|
263
277
|
.webring-nav[data-widget-type="full"] {
|
264
|
-
border: 2.5px solid
|
278
|
+
border: 2.5px solid var(--webring-border-color);
|
265
279
|
}
|
266
280
|
.webring-nav .webring-title {
|
267
281
|
font-weight: 600;
|
282
|
+
color: var(--webring-text-color);
|
268
283
|
}
|
269
284
|
.webring-nav .webring-btn {
|
270
285
|
text-wrap: nowrap;
|
271
|
-
color: #000000;
|
272
286
|
font-weight: 600;
|
273
|
-
|
274
|
-
|
287
|
+
color: var(--webring-btn-text);
|
288
|
+
background-color: var(--webring-btn-bg);
|
289
|
+
border: 2.5px solid var(--webring-btn-border);
|
275
290
|
transition: background-color 0.2s ease, color 0.2s ease;
|
276
291
|
}
|
277
|
-
.webring-nav .webring-btn:hover {
|
278
|
-
background-color: #000000;
|
279
|
-
color: #ffffff;
|
280
|
-
}
|
281
292
|
.webring-nav .webring-btn:focus {
|
282
293
|
outline: none;
|
283
294
|
background-color: transparent;
|
284
|
-
color:
|
295
|
+
color: var(--webring-btn-text);
|
285
296
|
}
|
297
|
+
.webring-nav .webring-btn:hover,
|
286
298
|
.webring-nav .webring-btn:active {
|
287
|
-
|
288
|
-
|
289
|
-
|
299
|
+
background-color: var(--webring-btn-hover-bg);
|
300
|
+
color: var(--webring-btn-hover-text);
|
301
|
+
}
|
302
|
+
.webring-nav svg {
|
303
|
+
fill: currentColor;
|
304
|
+
}
|
305
|
+
|
306
|
+
/* Light theme defaults - these have lower specificity than member's rules */
|
307
|
+
.webring-nav {
|
308
|
+
--webring-theme-bg: #ffffff;
|
309
|
+
--webring-theme-border: #000000;
|
310
|
+
--webring-theme-text: #000000;
|
311
|
+
--webring-theme-btn-bg: #ffffff;
|
312
|
+
--webring-theme-btn-border: #000000;
|
313
|
+
--webring-theme-btn-text: #000000;
|
314
|
+
--webring-theme-btn-hover-bg: #000000;
|
315
|
+
--webring-theme-btn-hover-text: #ffffff;
|
316
|
+
}
|
317
|
+
|
318
|
+
/* Dark theme overrides */
|
319
|
+
.webring-nav[data-theme="dark"] {
|
320
|
+
--webring-theme-bg: #000000;
|
321
|
+
--webring-theme-border: #ffffff;
|
322
|
+
--webring-theme-text: #ffffff;
|
323
|
+
--webring-theme-btn-bg: #000000;
|
324
|
+
--webring-theme-btn-border: #ffffff;
|
325
|
+
--webring-theme-btn-text: #ffffff;
|
326
|
+
--webring-theme-btn-hover-bg: #ffffff;
|
327
|
+
--webring-theme-btn-hover-text: #000000;
|
290
328
|
}
|
291
329
|
`
|
292
330
|
});
|
@@ -310,6 +348,8 @@
|
|
310
348
|
const buttonText = script.getAttribute('data-button-text') !== 'false';
|
311
349
|
const stylesType = script.getAttribute('data-styles') ?? WIDGET_CONFIG.DEFAULT_STYLE_TYPE;
|
312
350
|
const stylesOption = WIDGET_CONFIG.VALID_STYLE_TYPES.includes(stylesType) ? stylesType : WIDGET_CONFIG.DEFAULT_STYLE_TYPE;
|
351
|
+
const themeType = script.getAttribute('data-theme') ?? WIDGET_CONFIG.DEFAULT_THEME;
|
352
|
+
const theme = WIDGET_CONFIG.VALID_THEMES.includes(themeType) ? themeType : WIDGET_CONFIG.DEFAULT_THEME;
|
313
353
|
|
314
354
|
// Custom text data attributes
|
315
355
|
const prevText = script.getAttribute('data-prev-text');
|
@@ -351,6 +391,7 @@
|
|
351
391
|
wrapperDiv.className = 'webring-nav';
|
352
392
|
wrapperDiv.setAttribute('data-widget-type', widgetType);
|
353
393
|
wrapperDiv.setAttribute('data-button-text', buttonText.toString());
|
394
|
+
wrapperDiv.setAttribute('data-theme', theme);
|
354
395
|
|
355
396
|
// Apply custom texts efficiently
|
356
397
|
const customTexts = {
|
@@ -427,7 +468,8 @@
|
|
427
468
|
if (!styleElement) {
|
428
469
|
styleElement = document.createElement('style');
|
429
470
|
styleElement.id = WIDGET_CONFIG.STYLE_ID;
|
430
|
-
|
471
|
+
// Insert at the beginning of head so user styles can override
|
472
|
+
document.head.insertBefore(styleElement, document.head.firstChild);
|
431
473
|
}
|
432
474
|
|
433
475
|
switch (styleOption) {
|
@@ -1 +1 @@
|
|
1
|
-
!function(){function n(n){function t(n){let t=document.getElementById(e.STYLE_ID);switch(t||(t=document.createElement("style"),t.id=e.STYLE_ID,document.head.appendChild(t)),n){case"none":t.textContent=g.reset;break;case"layout":t.textContent=g.reset+g.layout;break;default:t.textContent=g.reset+g.layout+g.design}}const i=n||document.currentScript||document.getElementsByTagName("script")[document.getElementsByTagName("script").length-1];if(!i)return;const a=i.getAttribute("data-member-uid"),b=i.getAttribute("data-widget-type")??e.DEFAULT_TYPE,c=i.getAttribute("data-target-id")??e.DEFAULT_TARGET_ID,p="false"!==i.getAttribute("data-button-text"),f=i.getAttribute("data-styles")??e.DEFAULT_STYLE_TYPE,w=e.VALID_STYLE_TYPES.includes(f)?f:e.DEFAULT_STYLE_TYPE,m=i.getAttribute("data-prev-text"),u=i.getAttribute("data-random-text"),x=i.getAttribute("data-next-text"),v=i.getAttribute("data-widget-text");if(!a)return void console.error("Webring Widget: Missing data-member-uid attribute on script tag.");if(!e.VALID_TYPES.includes(b))return void console.error(`Webring Widget: Invalid widget type "${b}". Valid types: ${e.VALID_TYPES.join(", ")}`);const h=i.getAttribute("src"),y=new URL(h,window.location.href).origin,T=function(){const n=document.getElementById(c);if(!n)return void console.error(`Webring Widget: No element with id "${c}" found.`);s||(t(w),s=!0);const e=document.createDocumentFragment(),i=document.createElement("div");i.className="webring-nav",i.setAttribute("data-widget-type",b),i.setAttribute("data-button-text",p.toString());const g={prev:d.prev.text_enforced?d.prev:m?{...d.prev,text:`\xab ${m}`}:d.prev,random:d.random.text_enforced?d.random:u?{...d.random,text:`${r(20,20,"margin-right: 4px; margin-top: 1px;")} ${u}`}:d.random,next:d.next.text_enforced?d.next:x?{...d.next,text:`${x} \xbb`}:d.next,logoOnly:d.logoOnly},f=l[b];if(f.showTitle){const n=document.createElement("span");n.className="webring-title",n.innerHTML=o.widgetTitle.enforced?o.widgetTitle.default:v??o.widgetTitle.default,i.appendChild(n)}const h=document.createElement("nav");h.className="webring-buttons",f.actions.forEach((n=>{const e=g[n];if("logoOnly"===n){const n=document.createElement("div");return n.className=e.additionalClass,n.innerHTML=e.symbol,void h.appendChild(n)}const t=document.createElement("a");if(t.href=`${y}/webring/${e.path}?source_member_uid=${a}`,t.title=e.title,t.className=`webring-btn ${e.additionalClass}`,"one-way"===b&&f.showLogoInButton&&"next"===n){const n=d.next.text_enforced?o.next.default:x||g.next.text.replace(" \xbb","");t.innerHTML=p?`<span class="webring-logo-inline">${r(20,20)}</span> ${n} \xbb`:`<span class="webring-logo-inline">${r(20,20)}</span> ${e.symbol}`}else t.innerHTML=p?e.text:e.symbol;h.appendChild(t)})),i.appendChild(h),e.appendChild(i),n.innerHTML="",n.appendChild(e)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",T):T()}const e=Object.freeze({VALID_TYPES:Object.freeze(["full","no-text","two-way","one-way","random-only"]),DEFAULT_TYPE:"full",DEFAULT_TARGET_ID:"webring-widget",STYLE_ID:"webring-widget-styles",VALID_STYLE_TYPES:Object.freeze(["full","layout","none"]),DEFAULT_STYLE_TYPE:"full"}),t=Object.freeze({prev:{default:"\xab Prev",enforced:!1},random:{default:"Random",enforced:!1},next:{default:"Next \xbb",enforced:!1},widgetTitle:{default:"Webring",enforced:!1}}),i="<<REPLACE_ME_TEXT_DEFAULTS>>",a="<<REPLACE_ME_TEXT_DEFAULTS>>"!==i?"string"==typeof i?JSON.parse(i):i:{},o=Object.freeze(Object.keys(t).reduce(((n,e)=>{const i=a[e];return n[e]={default:i?.default??t[e].default,enforced:i?.enforced??t[e].enforced},n}),{})),r="<<REPLACE_ME_LOGO_SVG_FUNCTION>>",d=Object.freeze({prev:{symbol:"\xab",text:`\xab ${o.prev.default}`,text_enforced:o.prev.enforced,title:"Previous site",path:"previous",additionalClass:"prev-btn"},random:{symbol:r(23,23),text:`${r(20,20,"margin-right: 4px; margin-top: 1px;")} ${o.random.default}`,text_enforced:o.random.enforced,title:"Random site",path:"random",additionalClass:"random-btn"},next:{symbol:"\xbb",text:`${o.next.default} \xbb`,text_enforced:o.next.enforced,title:"Next site",path:"next",additionalClass:"next-btn"},logoOnly:{symbol:r(23,23),text:r(23,23),path:"",additionalClass:"logo-only"}}),l=Object.freeze({full:{showTitle:!0,actions:["prev","random","next"]},"no-text":{showTitle:!1,actions:["prev","random","next"]},"two-way":{showTitle:!1,actions:["prev","logoOnly","next"],showLogoInMiddle:!0},"one-way":{showTitle:!1,actions:["next"],showLogoInButton:!0},"random-only":{showTitle:!1,actions:["random"]}}),g=Object.freeze({reset:"\n .webring-nav,\n .webring-nav div,\n .webring-nav span,\n .webring-nav nav,\n .webring-nav a,\n .webring-nav div::before,\n .webring-nav span::before,\n .webring-nav nav::before,\n .webring-nav a::before,\n .webring-nav div::after,\n .webring-nav span::after,\n .webring-nav nav::after,\n .webring-nav a::after {\n all: initial;\n box-sizing: border-box;\n }\n\n // .webring-nav svg,\n // .webring-nav svg * {\n // all: revert;\n // fill: currentColor;\n // }\n\n .webring-nav,\n .webring-nav div,\n .webring-nav span,\n .webring-nav nav,\n .webring-nav a {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n color: inherit;\n }\n\n .webring-nav div,\n .webring-nav span,\n .webring-nav nav,\n .webring-nav a {\n display: initial;\n margin: 0;\n padding: 0;\n border: none;\n background: none;\n text-decoration: none;\n color: inherit;\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n vertical-align: baseline;\n box-shadow: none;\n outline: none;\n transform: none;\n opacity: 1;\n visibility: visible;\n position: static;\n float: none;\n clear: none;\n overflow: visible;\n white-space: normal;\n text-align: left;\n letter-spacing: normal;\n word-spacing: normal;\n text-transform: none;\n text-shadow: none;\n list-style: none;\n border-radius: 0;\n min-width: auto;\n max-width: none;\n min-height: auto;\n max-height: none;\n width: auto;\n height: auto;\n top: auto;\n right: auto;\n bottom: auto;\n left: auto;\n z-index: auto;\n }\n\n .webring-nav a {\n cursor: pointer;\n }\n ",layout:'\n .webring-nav {\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;\n font-size: 16px;\n display: flex;\n flex-direction: column;\n align-items: center;\n max-width: 350px;\n margin: 0 auto;\n padding: 10px;\n box-sizing: border-box;\n }\n .webring-nav .webring-title {\n margin-bottom: 8px;\n margin-top: 0;\n padding: 0;\n font-size: inherit;\n line-height: normal;\n }\n .webring-nav nav {\n display: flex;\n gap: 10px;\n width: 100%;\n justify-content: center;\n align-items: center;\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n .webring-nav .webring-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px 12px;\n text-decoration: none;\n box-sizing: border-box;\n margin: 0;\n }\n .webring-nav .webring-btn.random-btn {\n padding: 6px 8px;\n }\n .webring-nav nav .logo-only {\n padding: 8px 3px 6px 3px;\n margin: 0;\n }\n .webring-nav[data-widget-type="no-text"] {\n padding: 8px 10px;\n }\n .webring-nav[data-widget-type="one-way"] {\n max-width: 200px;\n }\n .webring-nav .webring-logo-inline {\n display: inline-block;\n vertical-align: middle;\n margin-right: 6px;\n margin-top: 1px;\n margin-bottom: 0;\n margin-left: 0;\n }\n .webring-nav[data-button-text="false"] .prev-btn {\n padding-top: 5px;\n padding-right: 12.5px;\n }\n .webring-nav[data-button-text="false"] .next-btn {\n padding-top: 5px;\n padding-left: 12.5px;\n }\n ',design:'\n .webring-nav[data-widget-type="full"] {\n border: 2.5px solid #000000;\n }\n .webring-nav .webring-title {\n font-weight: 600;\n }\n .webring-nav .webring-btn {\n text-wrap: nowrap;\n color: #000000;\n font-weight: 600;\n background-color: #ffffff;\n border: 2.5px solid #000000;\n transition: background-color 0.2s ease, color 0.2s ease;\n }\n .webring-nav .webring-btn:hover {\n background-color: #000000;\n color: #ffffff;\n }\n .webring-nav .webring-btn:focus {\n outline: none;\n background-color: transparent;\n color: #000000;\n }\n .webring-nav .webring-btn:active {\n border-color: #000000;\n background-color: #000000;\n color: #ffffff;\n }\n '});let s=!1;n()}();
|
1
|
+
!function(){function n(n){function t(n){let t=document.getElementById(e.STYLE_ID);switch(t||(t=document.createElement("style"),t.id=e.STYLE_ID,document.head.insertBefore(t,document.head.firstChild)),n){case"none":t.textContent=g.reset;break;case"layout":t.textContent=g.reset+g.layout;break;default:t.textContent=g.reset+g.layout+g.design}}const r=n||document.currentScript||document.getElementsByTagName("script")[document.getElementsByTagName("script").length-1];if(!r)return;const i=r.getAttribute("data-member-uid"),s=r.getAttribute("data-widget-type")??e.DEFAULT_TYPE,c=r.getAttribute("data-target-id")??e.DEFAULT_TARGET_ID,w="false"!==r.getAttribute("data-button-text"),f=r.getAttribute("data-styles")??e.DEFAULT_STYLE_TYPE,m=e.VALID_STYLE_TYPES.includes(f)?f:e.DEFAULT_STYLE_TYPE,v=r.getAttribute("data-theme")??e.DEFAULT_THEME,x=e.VALID_THEMES.includes(v)?v:e.DEFAULT_THEME,p=r.getAttribute("data-prev-text"),u=r.getAttribute("data-random-text"),h=r.getAttribute("data-next-text"),E=r.getAttribute("data-widget-text");if(!i)return void console.error("Webring Widget: Missing data-member-uid attribute on script tag.");if(!e.VALID_TYPES.includes(s))return void console.error(`Webring Widget: Invalid widget type "${s}". Valid types: ${e.VALID_TYPES.join(", ")}`);const y=r.getAttribute("src"),T=new URL(y,window.location.href).origin,_=function(){const n=document.getElementById(c);if(!n)return void console.error(`Webring Widget: No element with id "${c}" found.`);l||(t(m),l=!0);const e=document.createDocumentFragment(),r=document.createElement("div");r.className="webring-nav",r.setAttribute("data-widget-type",s),r.setAttribute("data-button-text",w.toString()),r.setAttribute("data-theme",x);const g={prev:b.prev.text_enforced?b.prev:p?{...b.prev,text:`\xab ${p}`}:b.prev,random:b.random.text_enforced?b.random:u?{...b.random,text:`${o(20,20,"margin-right: 4px; margin-top: 1px;")} ${u}`}:b.random,next:b.next.text_enforced?b.next:h?{...b.next,text:`${h} \xbb`}:b.next,logoOnly:b.logoOnly},f=d[s];if(f.showTitle){const n=document.createElement("span");n.className="webring-title",n.innerHTML=a.widgetTitle.enforced?a.widgetTitle.default:E??a.widgetTitle.default,r.appendChild(n)}const v=document.createElement("nav");v.className="webring-buttons",f.actions.forEach((n=>{const e=g[n];if("logoOnly"===n){const n=document.createElement("div");return n.className=e.additionalClass,n.innerHTML=e.symbol,void v.appendChild(n)}const t=document.createElement("a");if(t.href=`${T}/webring/${e.path}?source_member_uid=${i}`,t.title=e.title,t.className=`webring-btn ${e.additionalClass}`,"one-way"===s&&f.showLogoInButton&&"next"===n){const n=b.next.text_enforced?a.next.default:h||g.next.text.replace(" \xbb","");t.innerHTML=w?`<span class="webring-logo-inline">${o(20,20)}</span> ${n} \xbb`:`<span class="webring-logo-inline">${o(20,20)}</span> ${e.symbol}`}else t.innerHTML=w?e.text:e.symbol;v.appendChild(t)})),r.appendChild(v),e.appendChild(r),n.innerHTML="",n.appendChild(e)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",_):_()}const e=Object.freeze({VALID_TYPES:Object.freeze(["full","no-text","two-way","one-way","random-only"]),DEFAULT_TYPE:"full",DEFAULT_TARGET_ID:"webring-widget",STYLE_ID:"webring-widget-styles",VALID_STYLE_TYPES:Object.freeze(["full","layout","none"]),DEFAULT_STYLE_TYPE:"full",VALID_THEMES:Object.freeze(["light","dark"]),DEFAULT_THEME:"light"}),t=Object.freeze({prev:{default:"\xab Prev",enforced:!1},random:{default:"Random",enforced:!1},next:{default:"Next \xbb",enforced:!1},widgetTitle:{default:"Webring",enforced:!1}}),r="<<REPLACE_ME_TEXT_DEFAULTS>>",i="<<REPLACE_ME_TEXT_DEFAULTS>>"!==r?"string"==typeof r?JSON.parse(r):r:{},a=Object.freeze(Object.keys(t).reduce(((n,e)=>{const r=i[e];return n[e]={default:r?.default??t[e].default,enforced:r?.enforced??t[e].enforced},n}),{})),o="<<REPLACE_ME_LOGO_SVG_FUNCTION>>",b=Object.freeze({prev:{symbol:"\xab",text:`\xab ${a.prev.default}`,text_enforced:a.prev.enforced,title:"Previous site",path:"previous",additionalClass:"prev-btn"},random:{symbol:o(23,23),text:`${o(20,20,"margin-right: 4px; margin-top: 1px;")} ${a.random.default}`,text_enforced:a.random.enforced,title:"Random site",path:"random",additionalClass:"random-btn"},next:{symbol:"\xbb",text:`${a.next.default} \xbb`,text_enforced:a.next.enforced,title:"Next site",path:"next",additionalClass:"next-btn"},logoOnly:{symbol:o(23,23),text:o(23,23),path:"",additionalClass:"logo-only"}}),d=Object.freeze({full:{showTitle:!0,actions:["prev","random","next"]},"no-text":{showTitle:!1,actions:["prev","random","next"]},"two-way":{showTitle:!1,actions:["prev","logoOnly","next"],showLogoInMiddle:!0},"one-way":{showTitle:!1,actions:["next"],showLogoInButton:!0},"random-only":{showTitle:!1,actions:["random"]}}),g=Object.freeze({reset:"\n .webring-nav,\n .webring-nav div,\n .webring-nav span,\n .webring-nav nav,\n .webring-nav a,\n .webring-nav div::before,\n .webring-nav span::before,\n .webring-nav nav::before,\n .webring-nav a::before,\n .webring-nav div::after,\n .webring-nav span::after,\n .webring-nav nav::after,\n .webring-nav a::after {\n all: initial;\n box-sizing: border-box;\n }\n\n .webring-nav,\n .webring-nav div,\n .webring-nav span,\n .webring-nav nav,\n .webring-nav a {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n color: inherit;\n }\n\n .webring-nav div,\n .webring-nav span,\n .webring-nav nav,\n .webring-nav a {\n display: initial;\n margin: 0;\n padding: 0;\n border: none;\n background: none;\n text-decoration: none;\n color: inherit;\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n vertical-align: baseline;\n box-shadow: none;\n outline: none;\n transform: none;\n opacity: 1;\n visibility: visible;\n position: static;\n float: none;\n clear: none;\n overflow: visible;\n white-space: normal;\n text-align: left;\n letter-spacing: normal;\n word-spacing: normal;\n text-transform: none;\n text-shadow: none;\n list-style: none;\n border-radius: 0;\n min-width: auto;\n max-width: none;\n min-height: auto;\n max-height: none;\n width: auto;\n height: auto;\n top: auto;\n right: auto;\n bottom: auto;\n left: auto;\n z-index: auto;\n }\n\n .webring-nav a {\n cursor: pointer;\n }\n ",layout:'\n .webring-nav {\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;\n font-size: 16px;\n display: flex;\n flex-direction: column;\n align-items: center;\n max-width: 350px;\n margin: 0 auto;\n padding: 10px;\n box-sizing: border-box;\n }\n .webring-nav .webring-title {\n margin: 0 0 8px 0;\n padding: 0;\n font-size: inherit;\n line-height: normal;\n }\n .webring-nav nav {\n display: flex;\n gap: 10px;\n width: 100%;\n justify-content: center;\n align-items: center;\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n .webring-nav .webring-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px 12px;\n text-decoration: none;\n box-sizing: border-box;\n margin: 0;\n }\n .webring-nav .webring-btn.random-btn {\n padding: 6px 8px;\n }\n .webring-nav nav .logo-only {\n padding: 8px 3px 6px 3px;\n margin: 0;\n }\n .webring-nav[data-widget-type="no-text"] {\n padding: 8px 10px;\n }\n .webring-nav[data-widget-type="one-way"] {\n max-width: 200px;\n }\n .webring-nav .webring-logo-inline {\n display: inline-block;\n vertical-align: middle;\n margin: 1px 6px 0 0;\n }\n .webring-nav[data-button-text="false"] .prev-btn {\n padding: 5px 12.5px 6px 12px;\n }\n .webring-nav[data-button-text="false"] .next-btn {\n padding: 5px 12px 6px 12.5px;\n }\n ',design:'\n /* CSS Custom Properties for easy theming */\n .webring-nav {\n /* Internal variables that use public API with fallbacks */\n --webring-bg-color: var(--webring-theme-bg);\n --webring-border-color: var(--webring-theme-border);\n --webring-text-color: var(--webring-theme-text);\n --webring-btn-bg: var(--webring-theme-btn-bg);\n --webring-btn-border: var(--webring-theme-btn-border);\n --webring-btn-text: var(--webring-theme-btn-text);\n --webring-btn-hover-bg: var(--webring-theme-btn-hover-bg);\n --webring-btn-hover-text: var(--webring-theme-btn-hover-text);\n }\n\n /* Common theme styles using CSS variables */\n .webring-nav {\n background-color: var(--webring-bg-color);\n }\n .webring-nav[data-widget-type="full"] {\n border: 2.5px solid var(--webring-border-color);\n }\n .webring-nav .webring-title {\n font-weight: 600;\n color: var(--webring-text-color);\n }\n .webring-nav .webring-btn {\n text-wrap: nowrap;\n font-weight: 600;\n color: var(--webring-btn-text);\n background-color: var(--webring-btn-bg);\n border: 2.5px solid var(--webring-btn-border);\n transition: background-color 0.2s ease, color 0.2s ease;\n }\n .webring-nav .webring-btn:focus {\n outline: none;\n background-color: transparent;\n color: var(--webring-btn-text);\n }\n .webring-nav .webring-btn:hover,\n .webring-nav .webring-btn:active {\n background-color: var(--webring-btn-hover-bg);\n color: var(--webring-btn-hover-text);\n }\n .webring-nav svg {\n fill: currentColor;\n }\n\n /* Light theme defaults - these have lower specificity than member\'s rules */\n .webring-nav {\n --webring-theme-bg: #ffffff;\n --webring-theme-border: #000000;\n --webring-theme-text: #000000;\n --webring-theme-btn-bg: #ffffff;\n --webring-theme-btn-border: #000000;\n --webring-theme-btn-text: #000000;\n --webring-theme-btn-hover-bg: #000000;\n --webring-theme-btn-hover-text: #ffffff;\n }\n\n /* Dark theme overrides */\n .webring-nav[data-theme="dark"] {\n --webring-theme-bg: #000000;\n --webring-theme-border: #ffffff;\n --webring-theme-text: #ffffff;\n --webring-theme-btn-bg: #000000;\n --webring-theme-btn-border: #ffffff;\n --webring-theme-btn-text: #ffffff;\n --webring-theme-btn-hover-bg: #ffffff;\n --webring-theme-btn-hover-text: #000000;\n }\n '});let l=!1;n()}();
|
@@ -31,9 +31,9 @@ module Webring
|
|
31
31
|
# PATCH/PUT /webring/members/1
|
32
32
|
def update
|
33
33
|
if member.update(member_params)
|
34
|
-
|
34
|
+
update_success_response
|
35
35
|
else
|
36
|
-
|
36
|
+
update_error_response
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -46,6 +46,20 @@ module Webring
|
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
|
+
def update_success_response
|
50
|
+
respond_to do |format|
|
51
|
+
format.html { redirect_to admin_panel_member_path(member), notice: 'Member was successfully updated.' }
|
52
|
+
format.json { head :ok }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_error_response
|
57
|
+
respond_to do |format|
|
58
|
+
format.html { render :edit, status: :unprocessable_entity }
|
59
|
+
format.json { render json: { status: 'error', errors: member.errors }, status: :unprocessable_entity }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
49
63
|
def members
|
50
64
|
@members ||= Member.all.order(created_at: :desc)
|
51
65
|
end
|
@@ -55,7 +69,7 @@ module Webring
|
|
55
69
|
end
|
56
70
|
|
57
71
|
def member_params
|
58
|
-
params.require(:member).permit(:name, :url, :description)
|
72
|
+
params.require(:member).permit(:name, :url, :description, :status)
|
59
73
|
end
|
60
74
|
end
|
61
75
|
end
|
@@ -35,7 +35,7 @@ module Webring
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def find_member
|
38
|
-
@member = Webring::Member.find_by(uid: permitted_params[:source_member_uid])
|
38
|
+
@member = Webring::Member.active.find_by(uid: permitted_params[:source_member_uid])
|
39
39
|
return if @member
|
40
40
|
|
41
41
|
render_member_not_found
|
@@ -43,13 +43,13 @@ module Webring
|
|
43
43
|
|
44
44
|
def check_member_exists
|
45
45
|
member_uid = permitted_params[:source_member_uid]
|
46
|
-
return unless member_uid.present? && !Webring::Member.exists?(uid: member_uid)
|
46
|
+
return unless member_uid.present? && !Webring::Member.active.exists?(uid: member_uid)
|
47
47
|
|
48
48
|
render_member_not_found
|
49
49
|
end
|
50
50
|
|
51
51
|
def ensure_members_exist
|
52
|
-
return if Webring::Member.exists?
|
52
|
+
return if Webring::Member.active.exists?
|
53
53
|
|
54
54
|
render plain: 'No members in the webring', status: :not_found
|
55
55
|
end
|
@@ -4,55 +4,57 @@ module Webring
|
|
4
4
|
# Find the next member in the webring after the current one
|
5
5
|
# If current member is the last, return the first member (ring concept)
|
6
6
|
def find_next(source_member_uid)
|
7
|
-
source_member = find_by(uid: source_member_uid)
|
8
|
-
return
|
7
|
+
source_member = active.find_by(uid: source_member_uid)
|
8
|
+
return first_active_member_by_creation unless source_member
|
9
9
|
|
10
|
-
|
10
|
+
find_next_active_member(source_member) || first_active_member_by_creation
|
11
11
|
end
|
12
12
|
|
13
13
|
# Find the previous member in the webring before the current one
|
14
14
|
# If current member is the first, return the last member (ring concept)
|
15
15
|
def find_previous(source_member_uid)
|
16
|
-
source_member = find_by(uid: source_member_uid)
|
17
|
-
return
|
16
|
+
source_member = active.find_by(uid: source_member_uid)
|
17
|
+
return last_active_member_by_creation unless source_member
|
18
18
|
|
19
|
-
|
19
|
+
find_previous_active_member(source_member) || last_active_member_by_creation
|
20
20
|
end
|
21
21
|
|
22
22
|
# Find a random member, excluding the current one if provided
|
23
23
|
# If current member is the only one, return it
|
24
24
|
def find_random(source_member_uid: nil)
|
25
|
-
return order('RANDOM()').first if source_member_uid.blank?
|
25
|
+
return active.order('RANDOM()').first if source_member_uid.blank?
|
26
26
|
|
27
27
|
# Use exists? check to avoid loading records when not needed
|
28
|
-
excluded_scope = where.not(uid: source_member_uid)
|
28
|
+
excluded_scope = active.where.not(uid: source_member_uid)
|
29
29
|
|
30
30
|
if excluded_scope.exists?
|
31
31
|
excluded_scope.order('RANDOM()').first
|
32
32
|
else
|
33
|
-
# if only one member exists (the current one), return it
|
34
|
-
find_by(uid: source_member_uid)
|
33
|
+
# if only one active member exists (the current one), return it
|
34
|
+
active.find_by(uid: source_member_uid)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
def
|
41
|
-
order(created_at: :asc).first
|
40
|
+
def first_active_member_by_creation
|
41
|
+
active.order(created_at: :asc).first
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
order(created_at: :desc).first
|
44
|
+
def last_active_member_by_creation
|
45
|
+
active.order(created_at: :desc).first
|
46
46
|
end
|
47
47
|
|
48
|
-
def
|
49
|
-
|
48
|
+
def find_next_active_member(source_member)
|
49
|
+
active
|
50
|
+
.where('created_at > ?', source_member.created_at)
|
50
51
|
.order(created_at: :asc)
|
51
52
|
.first
|
52
53
|
end
|
53
54
|
|
54
|
-
def
|
55
|
-
|
55
|
+
def find_previous_active_member(source_member)
|
56
|
+
active
|
57
|
+
.where('created_at < ?', source_member.created_at)
|
56
58
|
.order(created_at: :desc)
|
57
59
|
.first
|
58
60
|
end
|
@@ -5,6 +5,7 @@ class CreateWebringMembers < ActiveRecord::Migration<%= "[#{Rails::VERSION::MAJO
|
|
5
5
|
t.string :name, null: false
|
6
6
|
t.string :url, null: false
|
7
7
|
t.text :description, null: false
|
8
|
+
t.integer :status, null: false, default: 0
|
8
9
|
|
9
10
|
t.index :uid, unique: true
|
10
11
|
t.index :name, unique: true
|
@@ -35,7 +35,7 @@ module Webring
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def find_member
|
38
|
-
@member = Webring::Member.find_by(uid: permitted_params[:source_member_uid])
|
38
|
+
@member = Webring::Member.active.find_by(uid: permitted_params[:source_member_uid])
|
39
39
|
return if @member
|
40
40
|
|
41
41
|
render_member_not_found
|
@@ -43,13 +43,13 @@ module Webring
|
|
43
43
|
|
44
44
|
def check_member_exists
|
45
45
|
member_uid = permitted_params[:source_member_uid]
|
46
|
-
return unless member_uid.present? && !Webring::Member.exists?(uid: member_uid)
|
46
|
+
return unless member_uid.present? && !Webring::Member.active.exists?(uid: member_uid)
|
47
47
|
|
48
48
|
render_member_not_found
|
49
49
|
end
|
50
50
|
|
51
51
|
def ensure_members_exist
|
52
|
-
return if Webring::Member.exists?
|
52
|
+
return if Webring::Member.active.exists?
|
53
53
|
|
54
54
|
render plain: 'No members in the webring', status: :not_found
|
55
55
|
end
|
@@ -57,5 +57,12 @@ module Webring
|
|
57
57
|
def render_member_not_found
|
58
58
|
render plain: 'Member not found', status: :not_found
|
59
59
|
end
|
60
|
+
|
61
|
+
def set_cors_headers
|
62
|
+
headers['Access-Control-Allow-Origin'] = '*'
|
63
|
+
headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
|
64
|
+
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept'
|
65
|
+
headers['Access-Control-Max-Age'] = '86400'
|
66
|
+
end
|
60
67
|
end
|
61
68
|
end
|
data/lib/webring/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: webring-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nikita Shkoda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-06-
|
11
|
+
date: 2025-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|