create-gardener 1.1.6 → 1.1.8

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.
Files changed (32) hide show
  1. package/Readme.md +2 -313
  2. package/package.json +1 -1
  3. package/template/package.json +11 -12
  4. package/template/pnpm-lock.yaml +1545 -0
  5. package/template/src/backend/controllers/gardener.controller.ts +7 -32
  6. package/template/src/backend/routes/gardener.route.ts +2 -1
  7. package/template/src/backend/server.ts +1 -1
  8. package/template/src/frontend/frontendtemplate.ejs +4 -4
  9. package/template/src/frontend/gardenerST.js +430 -0
  10. package/template/src/frontend/static/components/emailsvg.js +55 -0
  11. package/template/src/frontend/{components → static/components}/eyeoff.js +1 -1
  12. package/template/src/frontend/static/components/eyeon.js +43 -0
  13. package/template/src/frontend/{components → static/components}/notification.js +1 -1
  14. package/template/src/frontend/{components → static/components}/passwordBox.js +3 -3
  15. package/template/src/frontend/static/components/test.js +54 -0
  16. package/template/src/frontend/{gardener.js → static/gardener.js} +76 -4
  17. package/template/src/frontend/static/style.css +1048 -0
  18. package/template/src/frontend/style.css +148 -350
  19. package/template/src/frontend/views/_.ejs +17 -8
  20. package/template/src/frontend/views/_login.ejs +8 -8
  21. package/template/tsconfig.json +1 -1
  22. package/.direnv/bin/nix-direnv-reload +0 -19
  23. package/.direnv/flake-profile-a5d5b61aa8a61b7d9d765e1daf971a9a578f1cfa.rc +0 -2140
  24. package/.envrc +0 -1
  25. package/template/Readme.md +0 -349
  26. package/template/src/frontend/components/emailsvg.js +0 -55
  27. package/template/src/frontend/components/eyeon.js +0 -44
  28. package/template/src/frontend/components/test.js +0 -54
  29. /package/template/src/{backend → frontend/static}/cache/gardener_500x500.webp +0 -0
  30. /package/template/src/frontend/{components → static/components}/nonui/api.js +0 -0
  31. /package/template/src/frontend/{global.js → static/global.js} +0 -0
  32. /package/template/src/frontend/{style2.css → static/style2.css} +0 -0
@@ -19,20 +19,8 @@ interface AddComponentBody {
19
19
  export function addComponent(req: Request<{}, {}, AddComponentBody>, res: Response) {
20
20
  try {
21
21
  const { path: filePath, component } = req.body;
22
- const parsed = JSON.parse(component);
23
22
 
24
- // pretty format (2 spaces)
25
- const formatted = JSON.stringify(parsed, null, 2);
26
-
27
- const filecontent = `
28
- import { gardener, fetchElement, replaceElement } from '../gardener.js'
29
-
30
- export default function thisfun() {
31
- return gardener(${formatted})
32
- }
33
- `;
34
-
35
- fs.writeFileSync(`./src/frontend/${filePath}`, filecontent, "utf8");
23
+ fs.writeFileSync(`./src/frontend/${filePath}`, component, "utf8");
36
24
 
37
25
  res.json({ success: true });
38
26
  } catch (err) {
@@ -46,7 +34,7 @@ export async function imageOptimiser(req: Request, res: Response) {
46
34
  try {
47
35
  const { name } = req.params;
48
36
 
49
- if (!name) return;
37
+ if (typeof name !== 'string') return;
50
38
  // name format: test_500x300.webp
51
39
  const match = name.match(/^(.+?)_(\d+)x(\d+)\.webp$/);
52
40
 
@@ -60,7 +48,7 @@ export async function imageOptimiser(req: Request, res: Response) {
60
48
  const width = parseInt(widthStr, 10);
61
49
  const height = parseInt(heightStr, 10);
62
50
 
63
- const cacheDir = path.join(__dirname, "../cache");
51
+ const cacheDir = path.join(__dirname, "../../frontend/static/cache");
64
52
  await fsp.mkdir(cacheDir, { recursive: true });
65
53
 
66
54
  const outputPath = path.join(cacheDir, name);
@@ -129,22 +117,13 @@ export async function createStatic(req: Request, res: Response) {
129
117
  const outDir = path.resolve("src/tempfrontend");
130
118
  const finalOut = path.resolve("src/frontendStatic");
131
119
 
132
- const otherAssets = path.resolve("src/frontend");
133
120
  await fsp.mkdir(outDir, { recursive: true });
121
+ await fsp.mkdir(finalOut, { recursive: true });
134
122
 
135
- const entries2 = await fsp.readdir(otherAssets, { withFileTypes: true });
136
123
  const entries = await fsp.readdir(viewsDir, { withFileTypes: true });
137
124
 
138
125
  const rendered: string[] = [];
139
126
 
140
- for (const entry of entries2) {
141
- if (!entry.isFile()) continue;
142
- const srcPath = path.join(otherAssets, entry.name);
143
- const outputPath = path.join(finalOut, entry.name);
144
-
145
- await fsp.copyFile(srcPath, outputPath);
146
-
147
- }
148
127
 
149
128
  for (const entry of entries) {
150
129
  // skip folders (partials, layouts, etc.)
@@ -190,15 +169,11 @@ export async function createStatic(req: Request, res: Response) {
190
169
  }
191
170
  await fsp.rm(outDir, { recursive: true, force: true });
192
171
  await fsp.cp(
193
- path.resolve("src/frontend/components"),
194
- path.join(finalOut, 'components'),
195
- { recursive: true }
196
- );
197
- await fsp.cp(
198
- path.resolve("src/backend/cache"),
199
- path.join(finalOut, 'cache'),
172
+ path.resolve("src/frontend/static"),
173
+ path.join(finalOut, 'static'),
200
174
  { recursive: true }
201
175
  );
176
+
202
177
  return res.json({
203
178
  success: true,
204
179
  generated: rendered,
@@ -7,7 +7,7 @@ export default router;
7
7
 
8
8
 
9
9
 
10
- router.route("/cache/:name").get(imageOptimiser);
10
+ router.route("/static/cache/:name").get(imageOptimiser);
11
11
  router.route("/createstatic").get(createStatic);
12
12
  router.route('/addcomponent').post(addComponent);
13
13
  router.route('/addpage').post(addPage);
@@ -18,3 +18,4 @@ router.route('/addpage').post(addPage);
18
18
 
19
19
  router.route('/').get((req, res) => res.render('_'));
20
20
  router.route('/login').get((req, res) => res.render('_login'));
21
+
@@ -13,7 +13,7 @@ app.use(express.static('./src/frontend'));
13
13
  app.use(express.json());
14
14
  app.use(frontendRoute);
15
15
 
16
- const PORT = 5000;
16
+ const PORT = process.env.PORT || 3000;
17
17
  //
18
18
  // initDB().then(
19
19
  // () => {
@@ -3,8 +3,8 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <link rel="stylesheet" href="/style.css">
7
- <link rel="stylesheet" href="/style2.css">
6
+ <link href="/static/style.css" rel="stylesheet"/>
7
+ <link href="/static/style2.css" rel="stylesheet"/>
8
8
  <title>Redvent:Login</title>
9
9
  </head>
10
10
  <body>
@@ -24,10 +24,10 @@
24
24
  <img src="/cache/w_500x500.webp" alt="logo" class="w-500">
25
25
  </div>
26
26
  </div>
27
- <script src='/global.js' type="module"></script>
27
+ <script src='/static/global.js' type="module"></script>
28
28
  <script type="module">
29
29
 
30
- import { parser, fetchElement, replaceElement, appendElement } from "/gardener.js";
30
+ import { parser, fetchElement, replaceElement, appendElement } from "/static/gardener.js";
31
31
  </script>
32
32
  </body>
33
33
  </html>
@@ -0,0 +1,430 @@
1
+ //Standalone gardener version for no backend environment
2
+
3
+ const config = {
4
+ mode: 'dev',
5
+ hotreload: false
6
+ }
7
+
8
+ let hotReloadtimeout;
9
+ const body = fetchElement('body');
10
+
11
+
12
+ function applyHotReloadState() {
13
+ const hrcheck = fetchElement('#hrcheckbox');
14
+ const checkbox = fetchElement('.hrcheckbox');
15
+
16
+ if (hotReload) {
17
+ hrcheck.style.background = '#66e666';
18
+ checkbox.checked = true;
19
+ hotReloadtimeout = setTimeout(() => window.location.reload(), 1000);
20
+ } else {
21
+ hrcheck.style.background = 'red';
22
+ checkbox.checked = false;
23
+ clearTimeout(hotReloadtimeout);
24
+ }
25
+ }
26
+
27
+ let hotReload = localStorage.getItem('hotreload');
28
+ if (hotReload === null) hotReload = config.hotreload;
29
+ else if (hotReload === 'true') hotReload = true
30
+ else if (hotReload === 'false') hotReload = false
31
+
32
+
33
+
34
+
35
+ if (config.mode === 'dev') {
36
+ appendElement(body, gardener({
37
+ t: 'p',
38
+ attr: {
39
+ style: `
40
+ position:fixed;
41
+ bottom:0;
42
+ right:0;
43
+ z-index:100;
44
+ background:#e5e7eb;
45
+ padding:8px;
46
+ border-radius:6px;
47
+ font-family:sans-serif;
48
+ color:black;
49
+ font-size:14px;
50
+ `
51
+ },
52
+ children: [
53
+ { t: 'span', txt: 'Press ' },
54
+ {
55
+ t: 'span',
56
+ attr: { style: 'color:#22c55e;font-weight:bold;' },
57
+ txt: 'Ctrl+h'
58
+ },
59
+ { t: 'span', txt: ' to toggle Hot Reload' },
60
+ {
61
+ t: 'form',
62
+ attr: {
63
+ id: 'hrcheckbox',
64
+ style: 'margin-top:6px;padding:6px;background:red;border-radius:4px;cursor:pointer;'
65
+ },
66
+ events: { click: togglehotreload },
67
+ children: [
68
+ { t: 'label', txt: 'Hot Reload ' },
69
+ {
70
+ t: 'input',
71
+ cn: ['hrcheckbox'],
72
+ attr: { type: 'checkbox' }
73
+ }
74
+ ]
75
+ }
76
+ ]
77
+ }));
78
+
79
+ applyHotReloadState();
80
+
81
+ document.addEventListener('keydown', function(e) {
82
+ // Detect Ctrl + H
83
+ if (e.ctrlKey && e.key.toLowerCase() === 'h') {
84
+ e.preventDefault(); // Stop browser from opening history
85
+ // Your logic here...
86
+ togglehotreload();
87
+ }
88
+ });
89
+ }
90
+
91
+ //appendElement(body, gardener())
92
+
93
+
94
+
95
+
96
+ // togglehotreload();
97
+
98
+
99
+
100
+
101
+ //if (config.mode === 'dev') {
102
+
103
+
104
+ function togglehotreload() {
105
+ hotReload = !hotReload;
106
+ localStorage.setItem('hotreload', String(hotReload));
107
+
108
+ const hrcheck = fetchElement('#hrcheckbox');
109
+ const checkbox = fetchElement('.hrcheckbox');
110
+
111
+ if (hotReload) {
112
+ hrcheck.style.background = '#66e666';
113
+ checkbox.checked = true;
114
+ hotReloadtimeout = setTimeout(() => window.location.reload(), 1000);
115
+ } else {
116
+ hrcheck.style.background = 'red';
117
+ checkbox.checked = false;
118
+ clearTimeout(hotReloadtimeout);
119
+ }
120
+ }
121
+
122
+ function parserWindow(input) {
123
+ if (config.mode !== 'dev') return;
124
+
125
+ let text;
126
+ try {
127
+ text = JSON.stringify(JSON.parse(input), null, 2);
128
+ } catch {
129
+ text = input;
130
+ }
131
+
132
+ const result = gardener({
133
+ t: 'div',
134
+ attr: {
135
+ style: `
136
+ position:fixed;
137
+ top:25%;
138
+ left:25%;
139
+ width:50%;
140
+ height:50%;
141
+ background:#6b7280;
142
+ color:white;
143
+ border:2px solid black;
144
+ border-radius:8px;
145
+ z-index:90;
146
+ display:flex;
147
+ flex-direction:column;
148
+ justify-content:space-between;
149
+ `
150
+ },
151
+ children: [
152
+ {
153
+ t: 'div',
154
+ attr: {
155
+ style: `
156
+ background:#e5e7eb;
157
+ color:black;
158
+ padding:10px;
159
+ display:flex;
160
+ justify-content:space-between;
161
+ align-items:center;
162
+ border-top-left-radius:8px;
163
+ border-top-right-radius:8px;
164
+ `
165
+ },
166
+ children: [
167
+ { t: 'h3', txt: 'Parser Window' },
168
+ {
169
+ t: 'button',
170
+ txt: 'Copy JSON',
171
+ attr: {
172
+ style: `
173
+ padding:6px 10px;
174
+ background:#f87171;
175
+ border-radius:6px;
176
+ cursor:pointer;
177
+ `
178
+ },
179
+ events: {
180
+ click: () => copyTextToClipboard(text)
181
+ }
182
+ }
183
+ ]
184
+ },
185
+ {
186
+ t: 'pre',
187
+ txt: text,
188
+ attr: {
189
+ style: `
190
+ padding:12px;
191
+ overflow:auto;
192
+ font-size:12px;
193
+ white-space:pre-wrap;
194
+ `
195
+ }
196
+ }
197
+ ]
198
+ });
199
+
200
+ appendElement(body, result);
201
+ }
202
+
203
+
204
+
205
+ async function copyTextToClipboard(txt) {
206
+ try {
207
+ await navigator.clipboard.writeText(txt);
208
+ alert('Component copied to clipboard');
209
+ } catch (err) {
210
+ console.error('Clipboard copy failed', err);
211
+ }
212
+ }
213
+
214
+
215
+
216
+
217
+ export function fetchElement(param) {
218
+ return document.querySelector(param);
219
+ }
220
+
221
+ export function appendElement(parent, child) {
222
+ if (typeof parent === 'string') parent = fetchElement(parent);
223
+ parent.appendChild(child);
224
+ }
225
+
226
+ export function createElement(type, classname) {
227
+ let element = document.createElement(type);
228
+ if (classname)
229
+ element.classList.add(...classname);
230
+ return element;
231
+ }
232
+
233
+ export function insertText(element, text) {
234
+ element.innerText = text;
235
+ }
236
+
237
+ export function replaceElement(original, New) {
238
+ if (typeof original === 'string') original = fetchElement(original);
239
+ original.replaceWith(New);
240
+ }
241
+
242
+ export function gardener(Dom) {
243
+
244
+ if (Dom.nodeType === 1) return Dom;
245
+ // detect if this is an SVG element
246
+ const isSVG = [
247
+ 'svg', 'path', 'circle', 'rect', 'line', 'polygon', 'polyline', 'g', 'defs', 'clipPath', 'use'
248
+ ].includes(Dom.t);
249
+
250
+ // create element accordingly
251
+ let element;
252
+
253
+ if (isSVG) {
254
+ element = document.createElementNS('http://www.w3.org/2000/svg', Dom.t);
255
+ if (Dom.cn)
256
+ element.classList.add(...Dom.cn);
257
+ }
258
+ else {
259
+ element = createElement(Dom.t, Dom.cn);
260
+ }
261
+
262
+ // text content (skip for SVG like <path>)
263
+ if (Dom.txt) {
264
+ insertText(element, Dom.txt);
265
+ }
266
+
267
+ // apply attributes safely
268
+ const propertyNames = new Set([
269
+ 'value', 'selected', 'muted', 'disabled',
270
+ 'selectedIndex', 'volume', // etc.
271
+ ]);
272
+
273
+ if (Dom.attr) {
274
+ for (const [key, value] of Object.entries(Dom.attr)) {
275
+ if (isSVG || key.startsWith('data-') || key.startsWith('aria-')) {
276
+ element.setAttribute(key, value);
277
+ } else if (key in element && !propertyNames.has(key)) {
278
+ // Prefer property for known safe cases
279
+ try { element[key] = value === '' ? true : value; } catch (e) { element.setAttribute(key, value); }
280
+ } else {
281
+ element.setAttribute(key, value);
282
+ }
283
+ }
284
+ }
285
+
286
+ if (Dom.events) {
287
+ Object.entries(Dom.events).forEach(([eventName, handler]) => {
288
+ element.addEventListener(eventName, handler);
289
+ });
290
+ }
291
+
292
+ // recursively handle children
293
+ if (Dom.children) {
294
+ Dom.children.forEach(child => appendElement(element, gardener(child)));
295
+ }
296
+
297
+ return element;
298
+ }
299
+
300
+ function cleanStringAndList(input) {
301
+ const pattern = /\?"?(\w+)"?\?/g;
302
+ const vars = new Set();
303
+ let match;
304
+
305
+ while ((match = pattern.exec(input)) !== null) {
306
+ vars.add(match[1]);
307
+ }
308
+
309
+ // Replace ?var? with "+var+" and clean up resulting empty strings or double quotes
310
+ const cleanedString = input
311
+ .replace(pattern, '"+$1+"')
312
+ .replace(/^""\+/, '')
313
+ .replace(/\+""$/, '');
314
+
315
+ return {
316
+ cleanedString,
317
+ extractedList: [...vars].join(', ')
318
+ };
319
+ }
320
+
321
+ function generateFile(obj) {
322
+
323
+
324
+ const formatted = JSON.stringify(obj, null, 2);
325
+ const { cleanedString, extractedList } = cleanStringAndList(formatted);
326
+
327
+ return `
328
+ import { gardener, fetchElement, replaceElement } from '../gardener.js'
329
+
330
+ export default function thisfun({${extractedList}}) {
331
+ return gardener(${cleanedString})
332
+ }
333
+ `;
334
+ }
335
+
336
+
337
+
338
+
339
+ export function parser(element, isParent = true) {
340
+ if (typeof element === 'string') {
341
+ element = fetchElement(element);
342
+ }
343
+
344
+ console.log(element)
345
+ const obj = {
346
+ t: element.tagName.toLowerCase(),
347
+ };
348
+
349
+ // add classes if present
350
+ if (element.classList.length) {
351
+ obj.cn = Array.from(element.classList);
352
+ }
353
+
354
+ // add attributes if present
355
+ const attrs = {};
356
+ for (const attr of element.attributes) {
357
+ if (attr.name !== 'class') attrs[attr.name] = attr.value;
358
+ }
359
+ if (Object.keys(attrs).length) obj.attr = attrs;
360
+ // add text content (only if no children)
361
+ if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
362
+ obj.txt = element.textContent.trim();
363
+
364
+ if (isParent) {
365
+
366
+ parserWindow(generateFile(obj))
367
+ }
368
+
369
+ return obj;
370
+ }
371
+
372
+
373
+ // add children recursively
374
+ const children = [];
375
+ for (const child of element.childNodes) {
376
+ if (child.nodeType === Node.COMMENT_NODE) continue;
377
+
378
+ if (child.nodeType === Node.TEXT_NODE && child.textContent.trim() === '') continue;
379
+
380
+ if (child.nodeType === Node.TEXT_NODE) {
381
+ children.push({ t: 'span', txt: child.textContent.trim() });
382
+ continue;
383
+ }
384
+ children.push(parser(child, false));
385
+ }
386
+ if (children.length) obj.children = children;
387
+
388
+
389
+ if (isParent) {
390
+
391
+
392
+ parserWindow(generateFile(obj))
393
+ }
394
+
395
+ return obj
396
+
397
+
398
+ //Let Browser do the migration from html to json and then use copy paste
399
+ }
400
+
401
+
402
+ export function addEL(parent, event, fun) {
403
+ if (typeof parent === 'string') {
404
+ parent = fetchElement(parent);
405
+ }
406
+ parent.addEventListener(event, fun)
407
+ }
408
+
409
+ export function imagePreloader(images) {
410
+ const body = fetchElement('body')
411
+ images.forEach(entry => {
412
+ appendElement(body, gardener({
413
+ t: 'img',
414
+ cn: ['preloaderimage'],
415
+ attr: {
416
+ src: entry,
417
+ alt: entry
418
+ }
419
+ }));
420
+
421
+ setTimeout(() => {
422
+ const images = document.querySelectorAll('.preloaderimage');
423
+ images.forEach(entry => { entry.style.display = 'none' });
424
+ }, 0)
425
+
426
+ })
427
+ }
428
+
429
+
430
+
@@ -0,0 +1,55 @@
1
+
2
+ import { gardener, fetchElement, replaceElement } from '/static/gardener.js'
3
+
4
+ export default function thisfun() {
5
+ return gardener({
6
+ "t": "span",
7
+ "cn": [
8
+ "emailsvg"
9
+ ],
10
+ "children": [
11
+ {
12
+ "t": "svg",
13
+ "cn": [
14
+ "icon",
15
+ "icon-tabler",
16
+ "icons-tabler-outline",
17
+ "icon-tabler-mail"
18
+ ],
19
+ "attr": {
20
+ "xmlns": "http://www.w3.org/2000/svg",
21
+ "width": "24",
22
+ "height": "24",
23
+ "viewBox": "0 0 24 24",
24
+ "fill": "none",
25
+ "stroke": "currentColor",
26
+ "stroke-width": "2",
27
+ "stroke-linecap": "round",
28
+ "stroke-linejoin": "round"
29
+ },
30
+ "children": [
31
+ {
32
+ "t": "path",
33
+ "attr": {
34
+ "stroke": "none",
35
+ "d": "M0 0h24v24H0z",
36
+ "fill": "none"
37
+ }
38
+ },
39
+ {
40
+ "t": "path",
41
+ "attr": {
42
+ "d": "M3 7a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-10"
43
+ }
44
+ },
45
+ {
46
+ "t": "path",
47
+ "attr": {
48
+ "d": "M3 7l9 6l9 -6"
49
+ }
50
+ }
51
+ ]
52
+ }
53
+ ]
54
+ })
55
+ }
@@ -1,5 +1,5 @@
1
1
 
2
- import { gardener } from '../gardener.js'
2
+ import { gardener } from '/static/gardener.js'
3
3
 
4
4
  export default function () {
5
5
  return gardener({
@@ -0,0 +1,43 @@
1
+
2
+ import { gardener } from '/static/gardener.js'
3
+ export default function() {
4
+ return gardener({
5
+ "t": "svg",
6
+ "cn": [
7
+ "eye"
8
+ ],
9
+ "attr": {
10
+ "xmlns": "http://www.w3.org/2000/svg",
11
+ "width": "24",
12
+ "height": "24",
13
+ "viewBox": "0 0 24 24",
14
+ "fill": "none",
15
+ "stroke": "currentColor",
16
+ "stroke-width": "2",
17
+ "stroke-linecap": "round",
18
+ "stroke-linejoin": "round"
19
+ },
20
+ "children": [
21
+ {
22
+ "t": "path",
23
+ "attr": {
24
+ "stroke": "none",
25
+ "d": "M0 0h24v24H0z",
26
+ "fill": "none"
27
+ }
28
+ },
29
+ {
30
+ "t": "path",
31
+ "attr": {
32
+ "d": "M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0"
33
+ }
34
+ },
35
+ {
36
+ "t": "path",
37
+ "attr": {
38
+ "d": "M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6"
39
+ }
40
+ }
41
+ ]
42
+ })
43
+ }
@@ -1,4 +1,4 @@
1
- import { gardener, replaceElement, fetchElement } from '/gardener.js';
1
+ import { gardener, replaceElement, fetchElement } from '/static/gardener.js';
2
2
 
3
3
  export default function addNotification(noti) {
4
4
  console.log('clicked');
@@ -1,6 +1,6 @@
1
- import { gardener, replaceElement, fetchElement } from '/gardener.js'
2
- import eyeoff from '/components/eyeoff.js'
3
- import eyeon from '/components/eyeon.js'
1
+ import { gardener, replaceElement, fetchElement } from '/static/gardener.js'
2
+ import eyeoff from '/static/components/eyeoff.js'
3
+ import eyeon from '/static/components/eyeon.js'
4
4
  export default function thisfun(visible) {
5
5
 
6
6
  const svg = visible ? eyeoff() : eyeon();