webring-rails 1.4.0 → 1.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f2abb7d64673f6596274eaeb143aa6378f0ce099a86880765a10ba0c951a9be
4
- data.tar.gz: c5ad3ea3677093b17e271784910c47c57b3a3f45b3f222cd0ca736bbfca4ab8a
3
+ metadata.gz: bad76f5ea0fcb10729377a5cc42b2bc858a1460d929ca0711e490d26822dfe5e
4
+ data.tar.gz: 72995b854fdcaeeea0e710ad3a7deaa5fd101e1e87365d50a822ac439f89ce01
5
5
  SHA512:
6
- metadata.gz: 18c794c278923c41f060667a806d3a5968e5b05f9e133c989817c881980939568c9aacccffc63b349fbcd6e4475f5fe94e27be8b8253bee41bf3d0aeb21b84a9
7
- data.tar.gz: d83a28c05cedaec133e26c4936b33a3c786bf276ade1318665cc8ea2542bf01ea9e8180fdb32a92a83a9f3ce5ac9b92399efad4b7ea2e01e1877ae87050d8c09
6
+ metadata.gz: ee9b231f41c8165590650ff6fce9ef53f7eb48660195a39cf9592eeeef698d59eb9bc61b3ab39231785978ee8f8263fa6a0b9938893f48132c6928c3e9cbefdb
7
+ data.tar.gz: 82f951215f76f067ca9023c27e7665e56e7399894ec597db46ed598d02e84c470c6e5da6ee9457de93b29050a61ab42b71a8de32c512d28c3f62dbd21f6d648a
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Webring for Rails
4
4
 
5
- [Webring for Rails (webring-rails)](https://github.com/lstpsche/webring-rails) is a flexible engine for creating and managing a webring system in your Ruby on Rails application. A webring is a collection of websites linked together in a circular structure, allowing visitors to navigate from one site to another.
5
+ [Webring for Rails (webring-rails)](https://github.com/cybergizer-hq/webring-rails) is a flexible engine for creating and managing a webring system in your Ruby on Rails application. A webring is a collection of websites linked together in a circular structure, allowing visitors to navigate from one site to another.
6
6
 
7
7
  ## Features
8
8
 
@@ -20,7 +20,7 @@
20
20
  Add this line to your application's Gemfile:
21
21
 
22
22
  ```ruby
23
- gem 'webring_rails'
23
+ gem 'webring-rails'
24
24
  ```
25
25
 
26
26
  Run:
@@ -36,7 +36,7 @@ rails generate webring:install
36
36
  rails generate webring:member
37
37
 
38
38
  # Create the navigation controller
39
- rails generate webring:controller:navigation
39
+ rails generate webring:navigation_controller
40
40
  ```
41
41
 
42
42
  ### Optional Features
@@ -46,7 +46,7 @@ rails generate webring:controller:navigation
46
46
  rails generate webring:membership_request
47
47
 
48
48
  # Add membership request controller and routes
49
- rails generate webring:controller:membership_requests
49
+ rails generate webring:membership_requests_controller
50
50
  ```
51
51
 
52
52
  Finally, run the migrations:
@@ -0,0 +1,3 @@
1
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M13 3L6 14H12L11 21L18 10H12L13 3Z" fill="currentColor"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="${width}" height="${height}" style="${style}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M13 3L6 14H12L11 21L18 10H12L13 3Z" fill="currentColor"/>
3
+ </svg>
@@ -30,28 +30,22 @@
30
30
 
31
31
  (function() {
32
32
  // Configuration constants
33
- const WIDGET_CONFIG = {
34
- VALID_TYPES: ['full', 'no-text', 'two-way', 'one-way', 'random-only'],
33
+ const WIDGET_CONFIG = Object.freeze({
34
+ VALID_TYPES: Object.freeze(['full', 'no-text', 'two-way', 'one-way', 'random-only']),
35
35
  DEFAULT_TYPE: 'full',
36
36
  DEFAULT_TARGET_ID: 'webring-widget',
37
37
  STYLE_ID: 'webring-widget-styles',
38
- VALID_STYLE_TYPES: ['full', 'layout', 'none'],
39
- DEFAULT_STYLE_TYPE: 'full',
40
- DEFAULT_TEXTS: {
41
- PREV: '« Prev',
42
- RANDOM: 'Random',
43
- NEXT: 'Next »',
44
- WIDGET: 'Webring'
45
- }
46
- };
38
+ VALID_STYLE_TYPES: Object.freeze(['full', 'layout', 'none']),
39
+ DEFAULT_STYLE_TYPE: 'full'
40
+ });
47
41
 
48
42
  // Default text configurations
49
- const TEXT_DEFAULTS = {
43
+ const TEXT_DEFAULTS = Object.freeze({
50
44
  prev: { default: '« Prev', enforced: false },
51
45
  random: { default: 'Random', enforced: false },
52
46
  next: { default: 'Next »', enforced: false },
53
47
  widgetTitle: { default: 'Webring', enforced: false }
54
- };
48
+ });
55
49
 
56
50
  // These defaults will be provided by the widget_controller
57
51
  const PROVIDED_TEXT_DEFAULTS = "<<REPLACE_ME_TEXT_DEFAULTS>>";
@@ -62,18 +56,21 @@
62
56
  ? (typeof PROVIDED_TEXT_DEFAULTS === 'string' ? JSON.parse(PROVIDED_TEXT_DEFAULTS) : PROVIDED_TEXT_DEFAULTS)
63
57
  : {};
64
58
 
65
- // Merge defaults with provided defaults, with provided taking priority
66
- const FULL_TEXT_DEFAULTS = Object.keys(TEXT_DEFAULTS).reduce((acc, key) => {
67
- acc[key] = {
68
- default: parsedProvidedDefaults[key]?.default || TEXT_DEFAULTS[key].default,
69
- enforced: parsedProvidedDefaults[key]?.enforced || TEXT_DEFAULTS[key].enforced
70
- };
71
- return acc;
72
- }, {});
59
+ // Merge defaults with provided defaults
60
+ const FULL_TEXT_DEFAULTS = Object.freeze(
61
+ Object.keys(TEXT_DEFAULTS).reduce((acc, key) => {
62
+ const providedValue = parsedProvidedDefaults[key];
63
+ acc[key] = {
64
+ default: providedValue?.default ?? TEXT_DEFAULTS[key].default,
65
+ enforced: providedValue?.enforced ?? TEXT_DEFAULTS[key].enforced
66
+ };
67
+ return acc;
68
+ }, {})
69
+ );
73
70
 
74
- const logoSvg = (width = 20, height = 20, style = "") => `<<REPLACE_ME_LOGO_SVG>>`;
71
+ const logoSvg = "<<REPLACE_ME_LOGO_SVG_FUNCTION>>";
75
72
 
76
- const NAVIGATION_ACTIONS = {
73
+ const NAVIGATION_ACTIONS = Object.freeze({
77
74
  prev: {
78
75
  symbol: '«',
79
76
  text: `« ${FULL_TEXT_DEFAULTS.prev.default}`,
@@ -104,18 +101,17 @@
104
101
  path: '',
105
102
  additionalClass: 'logo-only'
106
103
  }
107
- };
104
+ });
108
105
 
109
- const WIDGET_TYPE_CONFIG = {
106
+ const WIDGET_TYPE_CONFIG = Object.freeze({
110
107
  'full': { showTitle: true, actions: ['prev', 'random', 'next'] },
111
108
  'no-text': { showTitle: false, actions: ['prev', 'random', 'next'] },
112
109
  'two-way': { showTitle: false, actions: ['prev', 'logoOnly', 'next'], showLogoInMiddle: true },
113
110
  'one-way': { showTitle: false, actions: ['next'], showLogoInButton: true },
114
111
  'random-only': { showTitle: false, actions: ['random'] }
115
- };
112
+ });
116
113
 
117
- // Define styles outside the function to avoid duplication
118
- const STYLES = {
114
+ const STYLES = Object.freeze({
119
115
  layout: `
120
116
  .webring-nav {
121
117
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
@@ -137,17 +133,17 @@
137
133
  justify-content: center;
138
134
  align-items: center;
139
135
  }
140
- .webring-nav a.webring-btn {
136
+ .webring-btn {
141
137
  display: flex;
142
138
  align-items: center;
143
139
  justify-content: center;
144
140
  padding: 6px 12px;
145
141
  text-decoration: none;
146
142
  }
147
- .webring-nav a.webring-btn.random-btn {
148
- padding: 6px 8px 6px 8px;
143
+ .random-btn {
144
+ padding: 6px 8px;
149
145
  }
150
- .webring-nav .logo-only {
146
+ .logo-only {
151
147
  padding: 8px 3px 6px 3px;
152
148
  }
153
149
  .webring-nav[data-widget-type="no-text"] {
@@ -162,13 +158,11 @@
162
158
  margin-right: 6px;
163
159
  margin-top: 1px;
164
160
  }
165
- /* no-text prev button */
166
- .webring-nav[data-button-text="false"] a.webring-btn.prev-btn {
161
+ [data-button-text="false"] .prev-btn {
167
162
  padding-top: 5px;
168
163
  padding-right: 12.5px;
169
164
  }
170
- /* no-text next button */
171
- .webring-nav[data-button-text="false"] a.webring-btn.next-btn {
165
+ [data-button-text="false"] .next-btn {
172
166
  padding-top: 5px;
173
167
  padding-left: 12.5px;
174
168
  }
@@ -180,7 +174,7 @@
180
174
  .webring-title {
181
175
  font-weight: 600;
182
176
  }
183
- .webring-nav a.webring-btn {
177
+ .webring-btn {
184
178
  text-wrap: nowrap;
185
179
  color: #000000;
186
180
  font-weight: 600;
@@ -188,40 +182,41 @@
188
182
  border: 2.5px solid #000000;
189
183
  transition: background-color 0.2s ease, color 0.2s ease;
190
184
  }
191
- .webring-nav a.webring-btn:hover {
185
+ .webring-btn:hover {
192
186
  background-color: #000000;
193
187
  color: #ffffff;
194
188
  }
195
- .webring-nav a.webring-btn:focus {
189
+ .webring-btn:focus {
196
190
  outline: none;
197
191
  background-color: transparent;
198
192
  color: #000000;
199
193
  }
200
- .webring-nav a.webring-btn:active {
194
+ .webring-btn:active {
201
195
  border-color: #000000;
202
196
  background-color: #000000;
203
197
  color: #ffffff;
204
198
  }
205
199
  `
206
- };
200
+ });
201
+
202
+ // Track if styles have been applied
203
+ let stylesApplied = false;
207
204
 
208
205
  // Run immediately for widget initialization
209
206
  createWidget();
210
207
 
211
208
  function createWidget(scriptElement) {
212
- const script = scriptElement || document.currentScript || (function() {
213
- const scripts = document.getElementsByTagName('script');
214
- return scripts[scripts.length - 1];
215
- })();
209
+ // Find the script element, using a passed element, current script, or last script
210
+ const script = scriptElement || document.currentScript || document.getElementsByTagName('script')[document.getElementsByTagName('script').length - 1];
216
211
 
217
212
  if (!script) return;
218
213
 
219
214
  // Config from data attributes
220
215
  const memberUid = script.getAttribute('data-member-uid');
221
- const widgetType = script.getAttribute('data-widget-type') || WIDGET_CONFIG.DEFAULT_TYPE;
222
- const targetId = script.getAttribute('data-target-id') || WIDGET_CONFIG.DEFAULT_TARGET_ID;
216
+ const widgetType = script.getAttribute('data-widget-type') ?? WIDGET_CONFIG.DEFAULT_TYPE;
217
+ const targetId = script.getAttribute('data-target-id') ?? WIDGET_CONFIG.DEFAULT_TARGET_ID;
223
218
  const buttonText = script.getAttribute('data-button-text') !== 'false';
224
- const stylesType = script.getAttribute('data-styles') || WIDGET_CONFIG.DEFAULT_STYLE_TYPE;
219
+ const stylesType = script.getAttribute('data-styles') ?? WIDGET_CONFIG.DEFAULT_STYLE_TYPE;
225
220
  const stylesOption = WIDGET_CONFIG.VALID_STYLE_TYPES.includes(stylesType) ? stylesType : WIDGET_CONFIG.DEFAULT_STYLE_TYPE;
226
221
 
227
222
  // Custom text data attributes
@@ -250,36 +245,67 @@
250
245
  return;
251
246
  }
252
247
 
253
- // Apply custom texts if provided or use defaults based on enforcement settings
248
+ // Apply styles only once per page
249
+ if (!stylesApplied) {
250
+ applyStyles(stylesOption);
251
+ stylesApplied = true;
252
+ }
253
+
254
+ // Use DocumentFragment for DOM operations
255
+ const fragment = document.createDocumentFragment();
256
+
257
+ // Create main wrapper div
258
+ const wrapperDiv = document.createElement('div');
259
+ wrapperDiv.className = 'webring-nav';
260
+ wrapperDiv.setAttribute('data-widget-type', widgetType);
261
+ wrapperDiv.setAttribute('data-button-text', buttonText.toString());
262
+
263
+ // Apply custom texts efficiently
254
264
  const customTexts = {
255
- prev: NAVIGATION_ACTIONS.prev.text_enforced ?
256
- { ...NAVIGATION_ACTIONS.prev } :
257
- (prevText ? { ...NAVIGATION_ACTIONS.prev, text: `« ${prevText}` } : NAVIGATION_ACTIONS.prev),
258
- random: NAVIGATION_ACTIONS.random.text_enforced ?
259
- { ...NAVIGATION_ACTIONS.random } :
260
- (randomText ? {
261
- ...NAVIGATION_ACTIONS.random,
262
- text: `${logoSvg(20, 20, "margin-right: 4px; margin-top: 1px;")} ${randomText}`
263
- } : NAVIGATION_ACTIONS.random),
264
- next: NAVIGATION_ACTIONS.next.text_enforced ?
265
- { ...NAVIGATION_ACTIONS.next } :
266
- (nextText ? { ...NAVIGATION_ACTIONS.next, text: `${nextText} »` } : NAVIGATION_ACTIONS.next),
265
+ prev: NAVIGATION_ACTIONS.prev.text_enforced
266
+ ? NAVIGATION_ACTIONS.prev
267
+ : (prevText ? {...NAVIGATION_ACTIONS.prev, text: `« ${prevText}`} : NAVIGATION_ACTIONS.prev),
268
+ random: NAVIGATION_ACTIONS.random.text_enforced
269
+ ? NAVIGATION_ACTIONS.random
270
+ : (randomText ? {...NAVIGATION_ACTIONS.random, text: `${logoSvg(20, 20, "margin-right: 4px; margin-top: 1px;")} ${randomText}`} : NAVIGATION_ACTIONS.random),
271
+ next: NAVIGATION_ACTIONS.next.text_enforced
272
+ ? NAVIGATION_ACTIONS.next
273
+ : (nextText ? {...NAVIGATION_ACTIONS.next, text: `${nextText} »`} : NAVIGATION_ACTIONS.next),
267
274
  logoOnly: NAVIGATION_ACTIONS.logoOnly
268
275
  };
269
276
 
270
- // Navigation links
277
+ // Add title if needed
271
278
  const config = WIDGET_TYPE_CONFIG[widgetType];
272
- const linkElements = config.actions.map(action => {
273
- const actionConfig = customTexts[action] || NAVIGATION_ACTIONS[action];
279
+ if (config.showTitle) {
280
+ const titleElem = document.createElement('span');
281
+ titleElem.className = 'webring-title';
282
+ titleElem.innerHTML = FULL_TEXT_DEFAULTS.widgetTitle.enforced ?
283
+ FULL_TEXT_DEFAULTS.widgetTitle.default :
284
+ (widgetText ?? FULL_TEXT_DEFAULTS.widgetTitle.default);
285
+ wrapperDiv.appendChild(titleElem);
286
+ }
287
+
288
+ // Create navigation container
289
+ const navElem = document.createElement('nav');
290
+ navElem.className = 'webring-buttons';
291
+
292
+ // Create buttons
293
+ config.actions.forEach(action => {
294
+ const actionConfig = customTexts[action];
274
295
 
275
296
  // Logo-only block
276
297
  if (action === 'logoOnly') {
277
- return `<div class="${actionConfig.additionalClass}">${actionConfig.symbol}</div>`;
298
+ const logoDiv = document.createElement('div');
299
+ logoDiv.className = actionConfig.additionalClass;
300
+ logoDiv.innerHTML = actionConfig.symbol;
301
+ navElem.appendChild(logoDiv);
302
+ return;
278
303
  }
279
304
 
280
- const url = `${baseUrl}/webring/${actionConfig.path}?source_member_uid=${memberUid}`;
281
- let label = buttonText ? actionConfig.text : actionConfig.symbol;
282
- const btnClass = `webring-btn${actionConfig.additionalClass ? ` ${actionConfig.additionalClass}` : ''}`;
305
+ const linkElem = document.createElement('a');
306
+ linkElem.href = `${baseUrl}/webring/${actionConfig.path}?source_member_uid=${memberUid}`;
307
+ linkElem.title = actionConfig.title;
308
+ linkElem.className = `webring-btn ${actionConfig.additionalClass}`;
283
309
 
284
310
  // One-way type case
285
311
  if (widgetType === 'one-way' && config.showLogoInButton && action === 'next') {
@@ -287,30 +313,21 @@
287
313
  FULL_TEXT_DEFAULTS.next.default :
288
314
  (nextText || customTexts.next.text.replace(' »', ''));
289
315
 
290
- label = buttonText
316
+ linkElem.innerHTML = buttonText
291
317
  ? `<span class="webring-logo-inline">${logoSvg(20, 20)}</span> ${buttonTextContent} »`
292
318
  : `<span class="webring-logo-inline">${logoSvg(20, 20)}</span> ${actionConfig.symbol}`;
319
+ } else {
320
+ linkElem.innerHTML = buttonText ? actionConfig.text : actionConfig.symbol;
293
321
  }
294
322
 
295
- return `<a href="${url}" title="${actionConfig.title}" class="${btnClass}">${label}</a>`;
296
- }).join('\n ');
297
-
298
- // Create widget HTML
299
- const titleText = FULL_TEXT_DEFAULTS.widgetTitle.enforced ?
300
- FULL_TEXT_DEFAULTS.widgetTitle.default :
301
- (widgetText || FULL_TEXT_DEFAULTS.widgetTitle.default);
302
-
303
- const title = config.showTitle ? `<span class="webring-title">${titleText}</span>` : '';
304
- container.innerHTML = `
305
- <div class="webring-nav" data-widget-type="${widgetType}" data-button-text="${buttonText}">
306
- ${title}
307
- <nav class="webring-buttons">
308
- ${linkElements}
309
- </nav>
310
- </div>
311
- `;
312
-
313
- applyStyles(stylesOption);
323
+ navElem.appendChild(linkElem);
324
+ });
325
+
326
+ wrapperDiv.appendChild(navElem);
327
+ fragment.appendChild(wrapperDiv);
328
+
329
+ container.innerHTML = '';
330
+ container.appendChild(fragment);
314
331
  };
315
332
 
316
333
  function applyStyles(styleOption) {
@@ -0,0 +1 @@
1
+ !function(){function e(e){function n(e){let n=document.getElementById(t.STYLE_ID);switch(n||(n=document.createElement("style"),n.id=t.STYLE_ID,document.head.appendChild(n)),e){case"none":n.textContent="";break;case"layout":n.textContent=s.layout;break;default:n.textContent=s.layout+s.design}}const o=e||document.currentScript||document.getElementsByTagName("script")[document.getElementsByTagName("script").length-1];if(!o)return;const a=o.getAttribute("data-member-uid"),g=o.getAttribute("data-widget-type")??t.DEFAULT_TYPE,p=o.getAttribute("data-target-id")??t.DEFAULT_TARGET_ID,f="false"!==o.getAttribute("data-button-text"),u=o.getAttribute("data-styles")??t.DEFAULT_STYLE_TYPE,x=t.VALID_STYLE_TYPES.includes(u)?u:t.DEFAULT_STYLE_TYPE,b=o.getAttribute("data-prev-text"),m=o.getAttribute("data-random-text"),w=o.getAttribute("data-next-text"),y=o.getAttribute("data-widget-text");if(!a)return void console.error("Webring Widget: Missing data-member-uid attribute on script tag.");if(!t.VALID_TYPES.includes(g))return void console.error(`Webring Widget: Invalid widget type "${g}". Valid types: ${t.VALID_TYPES.join(", ")}`);const T=o.getAttribute("src"),E=new URL(T,window.location.href).origin,_=function(){const e=document.getElementById(p);if(!e)return void console.error(`Webring Widget: No element with id "${p}" found.`);c||(n(x),c=!0);const t=document.createDocumentFragment(),o=document.createElement("div");o.className="webring-nav",o.setAttribute("data-widget-type",g),o.setAttribute("data-button-text",f.toString());const s={prev:d.prev.text_enforced?d.prev:b?{...d.prev,text:`\xab ${b}`}:d.prev,random:d.random.text_enforced?d.random:m?{...d.random,text:`${r(20,20,"margin-right: 4px; margin-top: 1px;")} ${m}`}:d.random,next:d.next.text_enforced?d.next:w?{...d.next,text:`${w} \xbb`}:d.next,logoOnly:d.logoOnly},u=l[g];if(u.showTitle){const e=document.createElement("span");e.className="webring-title",e.innerHTML=i.widgetTitle.enforced?i.widgetTitle.default:y??i.widgetTitle.default,o.appendChild(e)}const T=document.createElement("nav");T.className="webring-buttons",u.actions.forEach((e=>{const t=s[e];if("logoOnly"===e){const e=document.createElement("div");return e.className=t.additionalClass,e.innerHTML=t.symbol,void T.appendChild(e)}const n=document.createElement("a");if(n.href=`${E}/webring/${t.path}?source_member_uid=${a}`,n.title=t.title,n.className=`webring-btn ${t.additionalClass}`,"one-way"===g&&u.showLogoInButton&&"next"===e){const e=d.next.text_enforced?i.next.default:w||s.next.text.replace(" \xbb","");n.innerHTML=f?`<span class="webring-logo-inline">${r(20,20)}</span> ${e} \xbb`:`<span class="webring-logo-inline">${r(20,20)}</span> ${t.symbol}`}else n.innerHTML=f?t.text:t.symbol;T.appendChild(n)})),o.appendChild(T),t.appendChild(o),e.innerHTML="",e.appendChild(t)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",_):_()}const t=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"}),n=Object.freeze({prev:{default:"\xab Prev",enforced:!1},random:{default:"Random",enforced:!1},next:{default:"Next \xbb",enforced:!1},widgetTitle:{default:"Webring",enforced:!1}}),o="<<REPLACE_ME_TEXT_DEFAULTS>>",a="<<REPLACE_ME_TEXT_DEFAULTS>>"!==o?"string"==typeof o?JSON.parse(o):o:{},i=Object.freeze(Object.keys(n).reduce(((e,t)=>{const o=a[t];return e[t]={default:o?.default??n[t].default,enforced:o?.enforced??n[t].enforced},e}),{})),r="<<REPLACE_ME_LOGO_SVG_FUNCTION>>",d=Object.freeze({prev:{symbol:"\xab",text:`\xab ${i.prev.default}`,text_enforced:i.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;")} ${i.random.default}`,text_enforced:i.random.enforced,title:"Random site",path:"random",additionalClass:"random-btn"},next:{symbol:"\xbb",text:`${i.next.default} \xbb`,text_enforced:i.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"]}}),s=Object.freeze({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 }\n .webring-title {\n margin-bottom: 8px;\n }\n .webring-nav nav {\n display: flex;\n gap: 10px;\n width: 100%;\n justify-content: center;\n align-items: center;\n }\n .webring-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px 12px;\n text-decoration: none;\n }\n .random-btn {\n padding: 6px 8px;\n }\n .logo-only {\n padding: 8px 3px 6px 3px;\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-logo-inline {\n display: inline-block;\n vertical-align: middle;\n margin-right: 6px;\n margin-top: 1px;\n }\n [data-button-text="false"] .prev-btn {\n padding-top: 5px;\n padding-right: 12.5px;\n }\n [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-title {\n font-weight: 600;\n }\n .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-btn:hover {\n background-color: #000000;\n color: #ffffff;\n }\n .webring-btn:focus {\n outline: none;\n background-color: transparent;\n color: #000000;\n }\n .webring-btn:active {\n border-color: #000000;\n background-color: #000000;\n color: #ffffff;\n }\n '});let c=!1;e()}();
@@ -1,5 +1,5 @@
1
1
  module Webring
2
- class MembershipRequestsController < ApplicationController
2
+ class MembershipRequestsController < ::ApplicationController
3
3
  def new
4
4
  @membership_request = MembershipRequest.new
5
5
  end
@@ -13,12 +13,12 @@ module Webring
13
13
  format.js do
14
14
  response.headers['Content-Type'] = 'application/javascript'
15
15
 
16
- # Take the JavaScript file from the engine's assets and replace the customizable Logo SVG
16
+ # Take the minified JavaScript file from the engine's assets and replace the customizable Logo SVG
17
17
  widget_js =
18
18
  Webring::Engine
19
- .root.join('app/assets/javascripts/webring/widget.js')
19
+ .root.join('app/assets/javascripts/webring/widget.min.js')
20
20
  .read
21
- .gsub('<<REPLACE_ME_LOGO_SVG>>', logo_svg)
21
+ .gsub('"<<REPLACE_ME_LOGO_SVG_FUNCTION>>"', logo_svg_function)
22
22
  .gsub('"<<REPLACE_ME_TEXT_DEFAULTS>>"', JSON.generate(text_defaults))
23
23
 
24
24
  render js: widget_js
@@ -35,13 +35,16 @@ module Webring
35
35
  response.headers['Access-Control-Max-Age'] = '86400'
36
36
  end
37
37
 
38
+ # This function is used to generate the logo SVG function for the compiled widget.js file to overcome args names compression issue
39
+ def logo_svg_function
40
+ <<~JS
41
+ (width = 20, height = 20, style = "") => `#{logo_svg}`
42
+ JS
43
+ end
44
+
38
45
  # should include `${width}`, `${height}`, `${style}` in order to be customizable
39
46
  def logo_svg
40
- <<~SVG
41
- <svg width="${width}" height="${height}" style="${style}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
42
- <path d="M13 3L6 14H12L11 21L18 10H12L13 3Z" fill="currentColor"/>
43
- </svg>
44
- SVG
47
+ Webring::Engine.root.join('app/assets/images/webring/logo_template.svg').read
45
48
  end
46
49
 
47
50
  # Provide default texts for the widget
@@ -10,6 +10,7 @@ module Webring
10
10
  validates :url, presence: true, uniqueness: true
11
11
  validates :name, presence: true
12
12
  validates :description, presence: true
13
+ validates :callback_email, presence: true
13
14
 
14
15
  enum :status, { pending: 0, approved: 1, rejected: 2 }, default: :pending
15
16
  end
data/lib/generators/USAGE CHANGED
@@ -6,7 +6,7 @@ Example:
6
6
  rails generate webring:install
7
7
 
8
8
  This will create:
9
- # Mount the Webring engine in your routes.rb file
9
+ # Mount the Webring engine and widget.js path in your routes.rb file
10
10
 
11
11
  Example:
12
12
  rails generate webring:member
@@ -5,9 +5,9 @@ module Webring
5
5
  # should include `${width}`, `${height}`, `${style}` in order to be customizable
6
6
  # remove the method or call `super` to use the default logo
7
7
  def logo_svg
8
- <<~SVG
8
+ <<~HTML
9
9
  Add your custom logo SVG here
10
- SVG
10
+ HTML
11
11
  end
12
12
 
13
13
  # Override default texts for the widget
@@ -8,6 +8,7 @@ module Webring
8
8
  validates :url, presence: true, uniqueness: true
9
9
  validates :name, presence: true
10
10
  validates :description, presence: true
11
+ validates :callback_email, presence: true
11
12
 
12
13
  enum :status, { pending: 0, approved: 1, rejected: 2 }, default: :pending
13
14
 
@@ -1,5 +1,5 @@
1
1
  module Webring
2
- class MembershipRequestsController < ApplicationController
2
+ class MembershipRequestsController < ::ApplicationController
3
3
  def new
4
4
  @membership_request = MembershipRequest.new
5
5
  end
@@ -1,3 +1,3 @@
1
1
  module Webring
2
- VERSION = '1.4.0'.freeze
2
+ VERSION = '1.4.3'.freeze
3
3
  end
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.0
4
+ version: 1.4.3
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-08 00:00:00.000000000 Z
11
+ date: 2025-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.31'
55
+ - !ruby/object:Gem::Dependency
56
+ name: terser
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.2'
55
69
  description: Mountable Rails Engine for webring implementation with an embeddable
56
70
  styled widget
57
71
  email:
@@ -62,13 +76,15 @@ extra_rdoc_files: []
62
76
  files:
63
77
  - MIT-LICENSE
64
78
  - README.md
79
+ - app/assets/images/webring/logo.svg
80
+ - app/assets/images/webring/logo_template.svg
65
81
  - app/assets/javascripts/webring/widget.js
82
+ - app/assets/javascripts/webring/widget.min.js
66
83
  - app/controllers/webring/application_controller.rb
67
84
  - app/controllers/webring/members_controller.rb
68
85
  - app/controllers/webring/membership_requests_controller.rb
69
86
  - app/controllers/webring/navigation_controller.rb
70
87
  - app/controllers/webring/widget_controller.rb
71
- - app/helpers/webring/application_helper.rb
72
88
  - app/models/concerns/webring/membership_request_actions.rb
73
89
  - app/models/concerns/webring/navigation.rb
74
90
  - app/models/webring/application_record.rb
@@ -94,15 +110,14 @@ files:
94
110
  - lib/generators/webring/navigation_controller/navigation_controller_generator.rb
95
111
  - lib/generators/webring/navigation_controller/templates/navigation_controller.rb
96
112
  - lib/generators/webring/shared/route_injector.rb
97
- - lib/generators/webring_generator.rb
98
113
  - lib/webring-rails.rb
99
114
  - lib/webring/engine.rb
100
115
  - lib/webring/version.rb
101
- homepage: https://github.com/lstpsche/webring_rails
116
+ homepage: https://github.com/cybergizer-hq/webring_rails
102
117
  licenses:
103
118
  - MIT
104
119
  metadata:
105
- homepage_uri: https://github.com/lstpsche/webring_rails
120
+ homepage_uri: https://github.com/cybergizer-hq/webring_rails
106
121
  post_install_message:
107
122
  rdoc_options: []
108
123
  require_paths:
@@ -1,4 +0,0 @@
1
- module Webring
2
- module ApplicationHelper
3
- end
4
- end
@@ -1,17 +0,0 @@
1
- module Webring
2
- module Generators
3
- class WebringGenerator < Rails::Generators::NamedBase
4
- namespace 'webring'
5
-
6
- desc 'Creates a Webring configuration for the given model name'
7
-
8
- def invoke_webring_member
9
- invoke 'webring:member'
10
- end
11
-
12
- def invoke_webring_controller
13
- invoke 'webring:member_controller'
14
- end
15
- end
16
- end
17
- end