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 +4 -4
- data/README.md +4 -4
- data/app/assets/images/webring/logo.svg +3 -0
- data/app/assets/images/webring/logo_template.svg +3 -0
- data/app/assets/javascripts/webring/widget.js +105 -88
- data/app/assets/javascripts/webring/widget.min.js +1 -0
- data/app/controllers/webring/membership_requests_controller.rb +1 -1
- data/app/controllers/webring/widget_controller.rb +11 -8
- data/app/models/webring/membership_request.rb +1 -0
- data/lib/generators/USAGE +1 -1
- data/lib/generators/webring/custom_widget_controller/templates/custom_widget_controller.rb +2 -2
- data/lib/generators/webring/membership_request/templates/model.rb +1 -0
- data/lib/generators/webring/membership_requests_controller/templates/membership_requests_controller.rb +1 -1
- data/lib/webring/version.rb +1 -1
- metadata +21 -6
- data/app/helpers/webring/application_helper.rb +0 -4
- data/lib/generators/webring_generator.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bad76f5ea0fcb10729377a5cc42b2bc858a1460d929ca0711e490d26822dfe5e
|
4
|
+
data.tar.gz: 72995b854fdcaeeea0e710ad3a7deaa5fd101e1e87365d50a822ac439f89ce01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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 '
|
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:
|
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:
|
49
|
+
rails generate webring:membership_requests_controller
|
50
50
|
```
|
51
51
|
|
52
52
|
Finally, run the migrations:
|
@@ -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
|
-
|
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
|
66
|
-
const FULL_TEXT_DEFAULTS = Object.
|
67
|
-
acc
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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 =
|
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
|
-
|
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-
|
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
|
-
.
|
148
|
-
padding: 6px 8px
|
143
|
+
.random-btn {
|
144
|
+
padding: 6px 8px;
|
149
145
|
}
|
150
|
-
.
|
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
|
-
|
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
|
-
|
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-
|
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-
|
185
|
+
.webring-btn:hover {
|
192
186
|
background-color: #000000;
|
193
187
|
color: #ffffff;
|
194
188
|
}
|
195
|
-
.webring-
|
189
|
+
.webring-btn:focus {
|
196
190
|
outline: none;
|
197
191
|
background-color: transparent;
|
198
192
|
color: #000000;
|
199
193
|
}
|
200
|
-
.webring-
|
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
|
-
|
213
|
-
|
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')
|
222
|
-
const targetId = script.getAttribute('data-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')
|
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
|
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
|
-
|
257
|
-
|
258
|
-
random: NAVIGATION_ACTIONS.random.text_enforced
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
-
//
|
277
|
+
// Add title if needed
|
271
278
|
const config = WIDGET_TYPE_CONFIG[widgetType];
|
272
|
-
|
273
|
-
const
|
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
|
-
|
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
|
281
|
-
|
282
|
-
|
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
|
-
|
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
|
-
|
296
|
-
})
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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()}();
|
@@ -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('<<
|
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
|
-
|
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
@@ -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
|
-
<<~
|
8
|
+
<<~HTML
|
9
9
|
Add your custom logo SVG here
|
10
|
-
|
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
|
|
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.
|
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-
|
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/
|
116
|
+
homepage: https://github.com/cybergizer-hq/webring_rails
|
102
117
|
licenses:
|
103
118
|
- MIT
|
104
119
|
metadata:
|
105
|
-
homepage_uri: https://github.com/
|
120
|
+
homepage_uri: https://github.com/cybergizer-hq/webring_rails
|
106
121
|
post_install_message:
|
107
122
|
rdoc_options: []
|
108
123
|
require_paths:
|
@@ -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
|