fscss 1.1.6 → 1.1.7

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.
@@ -0,0 +1,1060 @@
1
+
2
+ function procNum(css){
3
+ const regex = /num\((.*?)\)/g;
4
+ function evaluateExpression(expression) {
5
+ try {
6
+ return eval(expression);
7
+ } catch (e) {
8
+ console.error('Invalid expression:', expression);
9
+ return expression;
10
+ }
11
+ }
12
+
13
+ const processedCSS = css.replace(regex, (match, expression) => {
14
+
15
+ return evaluateExpression(expression);
16
+ });
17
+
18
+ return (processedCSS);
19
+ }
20
+ const arraysExfscss = {}; // Renamed the global variable
21
+ const orderedxFscssRandom = {};
22
+
23
+ const exfMAX_DEPTH = 10; // Prevent infinite recursion
24
+ function extractBlock(css, startIndex) {
25
+ let depth = 0;
26
+ let i = startIndex;
27
+ while (i < css.length) {
28
+ if (css[i] === '{') depth++;
29
+ else if (css[i] === '}') depth--;
30
+ if (depth === 0) break;
31
+ i++;
32
+ }
33
+ return css.slice(startIndex, i + 1);
34
+ }
35
+
36
+ function parseConditionBlocks(block) {
37
+ const blocks = [];
38
+ // Adjusted regex to correctly capture the block content within curly braces
39
+ const conditionRegex = /(if|el-if|el)\s*([^{}]*?)\s*\{([\s\S]*?)\}/g;
40
+ let match;
41
+ while ((match = conditionRegex.exec(block)) !== null) {
42
+ blocks.push({
43
+ type: match[1],
44
+ condition: match[2].trim(),
45
+ block: match[3].trim()
46
+ });
47
+ }
48
+ return blocks;
49
+ }
50
+ function procExC(css) {
51
+ const regex = /exec\((_log|_error|_warn|_info),\s*(?:"([^"]*)"|'([^']*)'|([^)]*))\)/g;
52
+ let jsCode = '';
53
+ let match;
54
+
55
+ // Replace exec(...) with nothing (remove from CSS) while collecting code
56
+ const cleanedCSS = css.replace(regex, (full, method, dQ, sQ, raw) => {
57
+ const arg = dQ || sQ || raw;
58
+
59
+ if (!['_log', '_error', '_warn', '_info'].includes(method)) {
60
+ console.warn(`fscss[exec(console)]: Unsupported method: ${method}`);
61
+ return ''; // strip it from CSS
62
+ }
63
+
64
+ if (!arg) {
65
+ console.warn(`fscss[exec(console)]: Empty argument for method: ${method}`);
66
+ return ''; // strip it from CSS
67
+ }
68
+
69
+ jsCode += `console.${method.slice(1)}("${arg.replace(/"/g, '\\"')}");\n`;
70
+ return ''; // ensure CSS isn’t broken
71
+ });
72
+
73
+ // Run console code safely
74
+ if (jsCode) {
75
+ try {
76
+ new Function(jsCode)();
77
+ } catch (e) {
78
+ console.error("fscss[exec(console)]: Error executing transformed code:", e);
79
+ }
80
+ }
81
+
82
+ return cleanedCSS;
83
+ }
84
+
85
+
86
+ function procEv(css) {
87
+ const functionMap = {};
88
+ const funcDefRegex = /@event\s+([\w-]+)\(([^)]*)\)\s*:?{/g;
89
+ let funcMatch;
90
+ let modifiedCSS = css;
91
+ const removalRanges = [];
92
+
93
+ // First pass: extract and mark function definitions
94
+ while ((funcMatch = funcDefRegex.exec(css)) !== null) {
95
+ const funcName = funcMatch[1];
96
+ const argsStr = funcMatch[2];
97
+ const blockStart = funcMatch.index + funcMatch[0].length - 1;
98
+
99
+ if (blockStart >= css.length) {
100
+ console.warn(`fscss[parsing] Warning: Unexpected end of CSS after @event ${funcName} definition.`);
101
+ continue;
102
+ }
103
+
104
+ const fullBlock = extractBlock(css, blockStart);
105
+
106
+ if (fullBlock.length === 0 || fullBlock[fullBlock.length - 1] !== '}') {
107
+ console.warn(`fscss[parsing] Warning: Malformed block for @event '${funcName}'. Missing closing '}'.`);
108
+ continue;
109
+ }
110
+
111
+ const fullFunc = css.slice(funcMatch.index, blockStart + fullBlock.length);
112
+
113
+ const conditionBlocks = parseConditionBlocks(fullBlock);
114
+ const args = argsStr.split(',').map(arg => arg.trim()).filter(arg => arg !== '');
115
+
116
+ if (functionMap[funcName]) {
117
+ console.warn(`fscss[definition] Warning: Duplicate @event definition for '${funcName}'. The last one will be used.`);
118
+ }
119
+ functionMap[funcName] = { args, conditionBlocks };
120
+
121
+ removalRanges.push([funcMatch.index, blockStart + fullBlock.length]);
122
+ }
123
+ for (let i = removalRanges.length - 1; i >= 0; i--) {
124
+ const [start, end] = removalRanges[i];
125
+ modifiedCSS = modifiedCSS.slice(0, start) + modifiedCSS.slice(end);
126
+ }
127
+ modifiedCSS = modifiedCSS.replace(/@event\.([\w-]+)\(([^)]*)\)/g, (match, funcName, argValuesStr) => {
128
+ const func = functionMap[funcName];
129
+ if (!func) {
130
+ console.warn(`fscss[call] Warning: @event function '${funcName}' not found during call.`);
131
+ return match;
132
+ }
133
+
134
+ const context = {};
135
+ const argValues = argValuesStr.split(',').map(v => v.trim()).filter(v => v !== '');
136
+
137
+ if (argValues.length !== func.args.length) {
138
+ console.warn(`fscss[call] Warning: Argument count mismatch for @event '${funcName}'. Expected ${func.args.length}, got ${argValues.length}.`);
139
+ }
140
+
141
+ func.args.forEach((argName, i) => {
142
+ if (argValues[i] !== undefined) {
143
+ context[argName] = argValues[i];
144
+ } else {
145
+ console.warn(`fscss[call] Warning: Missing value for argument '${argName}' in @event '${funcName}' call.`);
146
+ }
147
+ });
148
+
149
+ let result = '';
150
+ let matched = false;
151
+ let elBlockFound = false;
152
+
153
+ for (const block of func.conditionBlocks) {
154
+ if (block.type === 'el') {
155
+ if (elBlockFound) {
156
+ console.warn(`fscss[logic] Warning: Multiple 'el' (else) blocks found in @event '${funcName}'. Only the first 'el' block will be considered.`);
157
+ }
158
+ elBlockFound = true;
159
+ }
160
+
161
+ if (matched && block.type !== 'el') {
162
+ continue;
163
+ }
164
+
165
+ if (block.type === 'el') {
166
+ if (!matched) {
167
+ matched = true;
168
+ } else {
169
+ continue;
170
+ }
171
+ } else {
172
+ const conditions = block.condition.split(',').map(c => c.trim()).filter(c => c !== '');
173
+ if (conditions.length === 0) {
174
+ console.warn(`fscss[logic] Warning: Empty condition in '${block.type}' block for @event '${funcName}'.`);
175
+ matched = true;
176
+ } else {
177
+ matched = conditions.every(cond => {
178
+ const comparisonMatch = cond.match(/^(\w+)\s*(==|!=|>=|<=|>|<)\s*([^]+)$/);
179
+ if (comparisonMatch) {
180
+ const [, varName, operator, expected] = comparisonMatch;
181
+ if (!(varName in context)) {
182
+ console.warn(`fscss[logic] Warning: Condition variable '${varName}' not provided in @event '${funcName}' context. Treating as false.`);
183
+ return false;
184
+ }
185
+ const actual = context[varName];
186
+
187
+ const numActual = isNaN(actual) ? actual : Number(actual);
188
+ const numExpected = isNaN(expected) ? expected : Number(expected);
189
+ switch (operator) {
190
+ case '==': return numActual == numExpected;
191
+ case '!=': return numActual != numExpected;
192
+ case '>': return numActual > numExpected;
193
+ case '<': return numActual < numExpected;
194
+ case '>=': return numActual >= numExpected;
195
+ case '<=': return numActual <= numExpected;
196
+ default: return false;
197
+ }
198
+ } else {
199
+ const parts = cond.split(':').map(s => s.trim());
200
+ if (parts.length !== 2) {
201
+ console.warn(`fscss[logic] Warning: Malformed condition '${cond}' in @event '${funcName}'. Expected 'variable operator value' or 'variable:value'.`);
202
+ return false;
203
+ }
204
+ const [varName, expected] = parts;
205
+ if (!(varName in context)) {
206
+ console.warn(`fscss[logic] Warning: Condition variable '${varName}' not provided in @event '${funcName}' context. Treating as false.`);
207
+ return false;
208
+ }
209
+ return context[varName] === expected;
210
+ }
211
+ });
212
+ }
213
+ }
214
+
215
+ if (matched) {
216
+ const assignMatch = block.block.match(/(\w+)\s*(?:\:\s*([^;]*);?|\|([^\|]+)\|?)/);
217
+ if (assignMatch && assignMatch[2]) {
218
+ result = assignMatch[2].trim();
219
+ }
220
+ else if (assignMatch && assignMatch[3]) {
221
+ result = assignMatch[3].trim();
222
+ }
223
+ else {
224
+ console.warn(`fscss[logic] Warning: No valid CSS property assignment found in matched block for @event '${funcName}'. Block content: '${block.block}'.`);
225
+ }
226
+ break;
227
+ }
228
+ }
229
+
230
+
231
+ if (!result && func.conditionBlocks.length > 0 && !matched) {
232
+ console.warn(`fscss[call] Warning: No condition matched for @event '${funcName}' with provided arguments. Returning original call string.`);
233
+ } else if (!result && func.conditionBlocks.length === 0) {
234
+ console.warn(`fscss[definition] Warning: @event '${funcName}' has no condition blocks defined. Returning original call string.`);
235
+ }
236
+
237
+ return result || match;
238
+ });
239
+
240
+ return modifiedCSS.trim();
241
+ }
242
+ function initlibraries(css){
243
+ css = css.replace(/exec\(\s*_init\sisjs\s*\)/g, "exec(https://cdn.jsdelivr.net/gh/fscss-ttr/FSCSS@main/xf/styles/isjs.fscss)");
244
+ css = css.replace(/exec\(\s*_init\sthemes\s*\)/g, "exec(https://cdn.jsdelivr.net/gh/fscss-ttr/FSCSS@main/xf/styles/trshapes.fthemes.fscss)")
245
+ css = css.replace(/exec\(_init\sarray1to500\s*\)/g, "exec(https://cdn.jsdelivr.net/gh/fscss-ttr/FSCSS@main/xf/styles/1to500.fscss)");
246
+ return css;
247
+ }
248
+ function procVar(vcss) {
249
+ function processSCSS(scssCode) {
250
+ const globalVars = {};
251
+ const processedLines = [];
252
+ const lines = scssCode.split('\n');
253
+
254
+ let inBlock = false;
255
+ const blockVars = {};
256
+
257
+ for (let i = 0; i < lines.length; i++) {
258
+ let line = lines[i].trim();
259
+
260
+ if (line.includes('{')) {
261
+ inBlock = true;
262
+ processedLines.push(line);
263
+ continue;
264
+ }
265
+
266
+ if (line.includes('}')) {
267
+ inBlock = false;
268
+ for (const varName in blockVars) {
269
+ delete blockVars[varName];
270
+ }
271
+ processedLines.push(line);
272
+ continue;
273
+ }
274
+
275
+ const varDeclarationRegex = /^\s*\$([a-zA-Z0-9_-]+)\s*:\s*([^;]+);/;
276
+ const varMatch = line.match(varDeclarationRegex);
277
+
278
+ if (varMatch) {
279
+ const [, varName, varValue] = varMatch;
280
+ if (inBlock) {
281
+ blockVars[varName] = varValue.trim();
282
+ // Do not include block-scoped declarations in the final CSS
283
+ } else {
284
+ globalVars[varName] = varValue.trim();
285
+ // Include global variable declarations in the final CSS
286
+ processedLines.push(line);
287
+ }
288
+ continue;
289
+ }
290
+
291
+ const varUsageRegex = /\$\/?([a-zA-Z0-9_-]+)(!)?/g;
292
+
293
+ line = line.replace(varUsageRegex, (match, varName) => {
294
+ if (blockVars[varName] !== undefined) {
295
+ return blockVars[varName];
296
+ } else if (globalVars[varName] !== undefined) {
297
+ return globalVars[varName];
298
+ }
299
+ return match;
300
+ });
301
+
302
+ processedLines.push(line);
303
+ }
304
+
305
+ function getVariable(varName) {
306
+ return globalVars[varName] || null;
307
+ }
308
+ const finalCss = processedLines.join('\n');
309
+
310
+ return {
311
+ css: finalCss,
312
+ getVariable
313
+ };
314
+ }
315
+
316
+ const result = processSCSS(vcss);
317
+ return result.css;
318
+ }
319
+
320
+ function procExt(css) {
321
+ let extractedVariables = {};
322
+ let tempCSS = css;
323
+
324
+ // Step 1: Process string literals
325
+ tempCSS = tempCSS.replace(/("(?:[^"\\]|\\.)*")|('(?:[^'\\]|\\.)*')/g, function(fullMatch) {
326
+ let quote = fullMatch[0];
327
+ let content = fullMatch.slice(1, -1);
328
+ const directiveRegex = /@ext\((-?\d+),(\d+):\s*([^)]+)\)/g;
329
+ let match;
330
+ let directivesToProcess = [];
331
+
332
+ while ((match = directiveRegex.exec(content)) !== null) {
333
+ directivesToProcess.push({
334
+ fullMatch: match[0],
335
+ start: parseInt(match[1]),
336
+ length: parseInt(match[2]),
337
+ varName: match[3].trim(),
338
+ index: match.index
339
+ });
340
+ }
341
+
342
+ for (let i = directivesToProcess.length - 1; i >= 0; i--) {
343
+ let d = directivesToProcess[i];
344
+ let s = d.start < 0 ? content.length + d.start : d.start;
345
+ s = Math.max(0, s);
346
+ let extracted = content.substring(s, s + d.length);
347
+
348
+ if (s + d.length > content.length || s < 0) {
349
+ console.warn(`fscss:[@ext]Warning: @ext directive for variable '${d.varName}' in string literal specifies an out-of-bounds range. Extraction may be incomplete or incorrect.`);
350
+ }
351
+
352
+ if (extractedVariables[d.varName] !== undefined) {
353
+ console.warn(`fscss:[@ext]Warning: Duplicate variable name '${d.varName}' found in string literal. The last extracted value will be used.`);
354
+ }
355
+ extractedVariables[d.varName] = extracted;
356
+
357
+ // Remove @ext from content
358
+ content = content.slice(0, d.index) + content.slice(d.index + d.fullMatch.length);
359
+ }
360
+
361
+ return quote + content + quote;
362
+ });
363
+
364
+ // Step 2: Outside strings
365
+ tempCSS = tempCSS.replace(/([#.\w-]+)\s*@ext\((-?\d+),(\d+):\s*([^)]+)\)/g, function(match, token, start, len, varName) {
366
+ start = parseInt(start);
367
+ len = parseInt(len);
368
+ varName = varName.trim();
369
+ let s = start < 0 ? token.length + start : start;
370
+ s = Math.max(0, s);
371
+ let extracted = token.substring(s, s + len);
372
+
373
+ if (s + len > token.length || s < 0) {
374
+ console.warn(`fscss:[@ext]Warning: @ext directive for variable '${varName}' on token '${token}' specifies an out-of-bounds range. Extraction may be incomplete or incorrect.`);
375
+ }
376
+
377
+ if (extractedVariables[varName] !== undefined) {
378
+ console.warn(`fscss:[@ext]Warning: Duplicate variable name '${varName}' found outside string literals. The last extracted value will be used.`);
379
+ }
380
+ extractedVariables[varName] = extracted;
381
+ return token;
382
+ });
383
+
384
+ // Step 3: Replace @ext.varName references
385
+ tempCSS = tempCSS.replace(/@ext\.(\w+)\!?/g, function(match, varName) {
386
+ if (extractedVariables[varName] === undefined) {
387
+ console.warn(`fscss:[@ext]Warning: Reference to undefined variable '@ext.${varName}'. It will not be replaced.`);
388
+ return match;
389
+ }
390
+ return extractedVariables[varName];
391
+ });
392
+
393
+ return tempCSS;
394
+ }
395
+
396
+
397
+ function procRan(input) {
398
+ return input.replace(/@random\(\[([^\]]+)\](?:, *ordered)?\)/g, (match, valuesStr) => {
399
+ const isOrdered = /, *ordered\)/.test(match);
400
+ const values = valuesStr.split(',').map(v => v.trim());
401
+
402
+ if (values.length === 0) {
403
+ console.warn("fscss[@random] Warning: Empty array provided for @random. Returning empty string.");
404
+ return '';
405
+ }
406
+
407
+ if (isOrdered) {
408
+ // Create consistent key for value sequences
409
+ const sequenceKey = values.join(':');
410
+
411
+ if (!orderedxFscssRandom[sequenceKey]) {
412
+ orderedxFscssRandom[sequenceKey] = {
413
+ values,
414
+ index: 0,
415
+ };
416
+ console.warn(`fscss[@random] Warning: New ordered sequence created for [${valuesStr}].`);
417
+ }
418
+
419
+ const store = orderedxFscssRandom[sequenceKey];
420
+ const val = store.values[store.index % store.values.length];
421
+
422
+ if (store.index >= store.values.length && store.index % store.values.length === 0) {
423
+ console.warn(`fscss[@random] Warning: Ordered sequence [${valuesStr}] is looping back to the beginning.`);
424
+ }
425
+
426
+ store.index++;
427
+ return val;
428
+ } else {
429
+ // Regular random selection
430
+ const randIndex = Math.floor(Math.random() * values.length);
431
+ return values[randIndex];
432
+ }
433
+ });
434
+ }
435
+ function procArr(input) {
436
+ // 1. Parse array declarations
437
+ const arrayDeclarationRegex = /@arr\(?\s*([\w\-_—0-9]+)\)?\[([^\]]+)\]\)?/g;
438
+ let match;
439
+ while ((match = arrayDeclarationRegex.exec(input)) !== null) {
440
+ const arrayName = match[1];
441
+ const arrayValues = match[2].split(',').map(item => item.trim());
442
+ arraysExfscss[arrayName] = arrayValues;
443
+ }
444
+
445
+ let output = input;
446
+
447
+ // 2. Process loops using @arr.name[]
448
+ output = output.replace(/([^\{\}]+)\{\s*([^}]*@arr\.([\w\-_—0-9]+)\[\][^}]*)\s*\}/g,
449
+ (fullMatch, selector, content, arrayName) => {
450
+ const arr = arraysExfscss[arrayName];
451
+ if (!arr) {
452
+ console.warn(`fscss[@arr] Warning: Array '${arrayName}' not found for loop processing.`);
453
+ return fullMatch;
454
+ }
455
+
456
+ return arr.map((value, index) => {
457
+ const sel = selector.replace(new RegExp(`@arr\\.${arrayName}\\[\\]`, 'g'), index + 1);
458
+ const body = content.replace(new RegExp(`@arr\\.${arrayName}\\[\\]`, 'g'), value);
459
+ return `${sel.trim()} {\n ${body.trim()}\n}`;
460
+ }).join('\n');
461
+ });
462
+
463
+ // 3. Specific array access: @arr.name[index]
464
+ output = output.replace(/@arr\.([\w\-_—0-9]+)\[(\d+)\]/g,
465
+ (fullMatch, arrayName, index) => {
466
+ const idx = parseInt(index) - 1;
467
+ const arr = arraysExfscss[arrayName];
468
+ if (!arr) {
469
+ console.warn(`fscss[@arr] Warning: Array '${arrayName}' not found.`);
470
+ return fullMatch;
471
+ }
472
+ return arr[idx] !== undefined ? arr[idx] : fullMatch;
473
+ });
474
+
475
+ // 4. Direct array access: @arr.name or @arr.name(separator)
476
+ output = output.replace(/@arr\.([\w\-_—0-9]+)(?:\(([^)]*)\))?/g,
477
+ (fullMatch, arrayName, separator) => {
478
+ const arr = arraysExfscss[arrayName];
479
+ if (!arr) {
480
+ console.warn(`fscss[@arr] Warning: Array '${arrayName}' not found for direct access.`);
481
+ return fullMatch;
482
+ }
483
+ const sep = (separator !== undefined && separator !== "") ? separator : ' ';
484
+ return arr.join(sep);
485
+ });
486
+
487
+ // 5. Clean up array declarations
488
+ return output
489
+ .replace(arrayDeclarationRegex, '')
490
+ .replace(/\n{3,}/g, '\n\n')
491
+ .trim();
492
+ }
493
+ function procFun(code) {
494
+ const variables = {};
495
+
496
+ function parseStyle(styleStr) {
497
+ const props = {};
498
+ const lines = styleStr.split(';');
499
+ for (let line of lines) {
500
+ line = line.trim();
501
+ if (!line) continue;
502
+ const colonIdx = line.indexOf(':');
503
+ if (colonIdx === -1) {
504
+ console.warn(`fscss[@fun] Invalid style line (missing colon): "${line}"`);
505
+ continue;
506
+ }
507
+ const prop = line.substring(0, colonIdx).trim();
508
+ const value = line.substring(colonIdx + 1).trim();
509
+ if (prop) {
510
+ props[prop] = value;
511
+ } else {
512
+ console.warn(`fscss[@fun] Empty property name in line: "${line}"`);
513
+ }
514
+ }
515
+ return props;
516
+ }
517
+
518
+ const funRegex = /@fun\(([\w\-\_\—0-9]+)\)\s*\{([\s\S]*?)\}\s*/g;
519
+ let funMatch;
520
+ while ((funMatch = funRegex.exec(code)) !== null) {
521
+ const varName = funMatch[1];
522
+ const rawStyles = funMatch[2].trim();
523
+ if (variables[varName]) {
524
+ console.warn(`fscss[@fun] Duplicate @fun variable declaration: "${varName}". The last one will overwrite previous declarations.`);
525
+ }
526
+ variables[varName] = {
527
+ raw: rawStyles,
528
+ props: parseStyle(rawStyles)
529
+ };
530
+ }
531
+
532
+ let processedCode = code;
533
+
534
+ // Handle value extraction (e.g., @fun.varname2.bg.value)
535
+ processedCode = processedCode.replace(/@fun\.([\w\-\_\—0-9]+)\.([\w\-\_\—0-9]+)\.value\!?/g, (match, varName, prop) => {
536
+ if (variables[varName] && variables[varName].props[prop]) {
537
+ return variables[varName].props[prop];
538
+ } else {
539
+ console.warn(`fscss[@fun] Value extraction failed for "@fun.${varName}.${prop}.value". Variable or property not found.`);
540
+ }
541
+ return match;
542
+ });
543
+
544
+ // Handle single property rule (e.g., @fun.varname2.background)
545
+ processedCode = processedCode.replace(/@fun\.([\w\-\_\—0-9]+)\.([\w\-\_\—0-9]+)\!?/g, (match, varName, prop) => {
546
+ if (variables[varName] && variables[varName].props[prop]) {
547
+ return `${prop}: ${variables[varName].props[prop]};`;
548
+ } else {
549
+ console.warn(`fscss[@fun] Single property rule failed for "@fun.${varName}.${prop}". Variable or property not found.`);
550
+ }
551
+ return match;
552
+ });
553
+
554
+ // Handle full variable block (e.g., @fun.varname2)
555
+ processedCode = processedCode.replace(/@fun\.([\w\-\_\—0-9]+)(?=[\s;}])\!?/g, (match, varName) => {
556
+ if (variables[varName]) {
557
+ return variables[varName].raw;
558
+ } else {
559
+ console.warn(`[@fun] Full variable block replacement failed for "@fun.${varName}". Variable not found.`);
560
+ }
561
+ return match;
562
+ });
563
+
564
+ // Clean up code
565
+ processedCode = processedCode.replace(/@fun\(([\w\-\_\d\—]+)\s*\{[\s\S]*?\}\s*/g, '');
566
+ processedCode = processedCode.replace(/^\s*[\r\n]/gm, '');
567
+ processedCode = processedCode.trim();
568
+
569
+ return processedCode;
570
+ }
571
+
572
+ // Extracts values using copy() and creates CSS custom properties
573
+ function flattenNestedCSS(css, options = {}) {
574
+ const {
575
+ preserveComments = false,
576
+ indent = ' ',
577
+ validate = true,
578
+ errorHandler = (msg) => console.warn(msg),
579
+ } = options;
580
+
581
+ // Remove comments unless preserved
582
+ if (!preserveComments) {
583
+ css = css.replace(/\/\*[\s\S]*?\*\//g, '').trim();
584
+ }
585
+
586
+ function isValidSelector(selector) {
587
+ // Allow modern CSS features (:has(), > selector, etc.)
588
+ return selector && selector.trim() !== '' &&
589
+ !/[^a-zA-Z0-9\-_@*.\#:,\s>&~+()\[\]'"]|\/\//.test(selector);
590
+ }
591
+
592
+ function isValidProperty(prop) {
593
+ const [name, ...rest] = prop.split(':').map(s => s.trim());
594
+ return !validate || /^(--|[\w-]+)$/.test(name);
595
+ }
596
+
597
+ function parseBlock(css, start, parentSelector = '') {
598
+ let output = '';
599
+ let pos = start;
600
+ const stack = [];
601
+ let current = '';
602
+ let inString = false;
603
+ let quote = null;
604
+ let depth = 0;
605
+
606
+ while (pos < css.length) {
607
+ const char = css[pos];
608
+
609
+ if (char === '\\' && inString) {
610
+ current += char;
611
+ pos++;
612
+ if (pos < css.length) {
613
+ current += css[pos];
614
+ }
615
+ pos++;
616
+ continue;
617
+ }
618
+
619
+ if ((char === '"' || char === "'") && !inString) {
620
+ inString = true;
621
+ quote = char;
622
+ current += char;
623
+ } else if (char === quote && inString) {
624
+ inString = false;
625
+ quote = null;
626
+ current += char;
627
+ } else if (char === '{' && !inString) {
628
+ if (depth === 0) {
629
+ const selector = current.trim();
630
+ current = '';
631
+ stack.push({ selector, parent: parentSelector });
632
+ } else {
633
+ current += char;
634
+ }
635
+ depth++;
636
+ } else if (char === '}' && !inString) {
637
+ depth--;
638
+ if (depth === 0) {
639
+ const block = stack.pop();
640
+ if (!block) continue;
641
+
642
+ let fullSelector = '';
643
+ if (block.selector.includes('&')) {
644
+ fullSelector = block.selector.replace(/&/g, block.parent);
645
+ } else {
646
+ fullSelector = block.parent ? `${block.parent} ${block.selector}` : block.selector;
647
+ }
648
+
649
+ // Parse nested content
650
+ const nested = parseNestedContent(current, fullSelector);
651
+
652
+ if (nested.properties.length > 0 || nested.keyframes.length > 0) {
653
+ output += `${fullSelector} {\n`;
654
+ if (nested.properties.length > 0) {
655
+ output += indent + nested.properties.join(`;\n${indent}`) + ';\n';
656
+ }
657
+ output += nested.keyframes.join('\n');
658
+ output += '}\n\n';
659
+ }
660
+
661
+ output += nested.nestedBlocks;
662
+ current = '';
663
+ } else {
664
+ current += char;
665
+ }
666
+ } else if (char === '@' && !inString && depth === 0) {
667
+ // Handle at-rules at root level
668
+ const atRuleEnd = findAtRuleEnd(css, pos);
669
+ if (atRuleEnd === -1) break;
670
+
671
+ output += css.substring(pos, atRuleEnd).trim() + '\n\n';
672
+ pos = atRuleEnd;
673
+ continue;
674
+ } else {
675
+ current += char;
676
+ }
677
+
678
+ pos++;
679
+ }
680
+
681
+ return { output, pos };
682
+ }
683
+
684
+ function findAtRuleEnd(css, start) {
685
+ let depth = 0;
686
+ let inString = false;
687
+ let quote = null;
688
+ let pos = start;
689
+
690
+ while (pos < css.length) {
691
+ const char = css[pos];
692
+
693
+ if (char === '\\' && inString) {
694
+ pos += 2;
695
+ continue;
696
+ }
697
+
698
+ if ((char === '"' || char === "'") && !inString) {
699
+ inString = true;
700
+ quote = char;
701
+ } else if (char === quote && inString) {
702
+ inString = false;
703
+ quote = null;
704
+ } else if (char === '{' && !inString) {
705
+ depth++;
706
+ } else if (char === '}' && !inString) {
707
+ depth--;
708
+ if (depth === 0) {
709
+ return pos + 1;
710
+ }
711
+ }
712
+
713
+ pos++;
714
+ }
715
+
716
+ return -1;
717
+ }
718
+
719
+ function parseNestedContent(content, parentSelector) {
720
+ const result = {
721
+ properties: [],
722
+ nestedBlocks: '',
723
+ keyframes: []
724
+ };
725
+
726
+ let current = '';
727
+ let inString = false;
728
+ let quote = null;
729
+ let depth = 0;
730
+ let pos = 0;
731
+
732
+ while (pos < content.length) {
733
+ const char = content[pos];
734
+
735
+ if (char === '\\' && inString) {
736
+ current += char;
737
+ pos++;
738
+ if (pos < content.length) {
739
+ current += content[pos];
740
+ }
741
+ pos++;
742
+ continue;
743
+ }
744
+
745
+ if ((char === '"' || char === "'") && !inString) {
746
+ inString = true;
747
+ quote = char;
748
+ current += char;
749
+ } else if (char === quote && inString) {
750
+ inString = false;
751
+ quote = null;
752
+ current += char;
753
+ } else if (char === '{' && !inString) {
754
+ depth++;
755
+ current += char;
756
+ } else if (char === '}' && !inString) {
757
+ depth--;
758
+ current += char;
759
+ if (depth === 0) {
760
+ // Found a complete nested block
761
+ const block = parseBlock(current, 0, parentSelector).output;
762
+ result.nestedBlocks += block;
763
+ current = '';
764
+ }
765
+ } else if (char === ';' && !inString && depth === 0) {
766
+ // Property handling
767
+ const prop = current.trim();
768
+ if (prop) {
769
+ if (isValidProperty(prop)) {
770
+ result.properties.push(prop);
771
+ } else if (validate) {
772
+ errorHandler(`Invalid property: ${prop}`);
773
+ }
774
+ }
775
+ current = '';
776
+ } else if (char === '@' && !inString && depth === 0) {
777
+ // Handle keyframes inside blocks
778
+ const atEnd = findAtRuleEnd(content, pos);
779
+ if (atEnd === -1) break;
780
+
781
+ const atContent = content.substring(pos, atEnd);
782
+ result.keyframes.push(atContent.trim());
783
+ pos = atEnd;
784
+ current = '';
785
+ continue;
786
+ } else {
787
+ current += char;
788
+ }
789
+
790
+ pos++;
791
+ }
792
+
793
+ // Handle trailing property
794
+ const lastProp = current.trim();
795
+ if (lastProp && depth === 0) {
796
+ if (isValidProperty(lastProp)) {
797
+ result.properties.push(lastProp);
798
+ } else if (validate) {
799
+ errorHandler(`Invalid property: ${lastProp}`);
800
+ }
801
+ }
802
+
803
+ return result;
804
+ }
805
+
806
+ const result = parseBlock(css, 0);
807
+ return result.output;
808
+ }
809
+ function procP(text) {
810
+ return text.replace(/%(\d+)\(([^[]+)\[\s*([^\]]+)\]\)/g, (match, number, properties, value) => {
811
+ const propList = properties.split(',').map(p => p.trim());
812
+ if (propList.length != number) {
813
+ console.warn(`Number of properties ${propList.length} does not match %${number}`);
814
+ return match;
815
+ }
816
+ return propList.map(prop => `${prop}${value}`).join("");
817
+ });
818
+ }
819
+
820
+ function transformCssValues(css) {
821
+ const customProperties = new Set();
822
+ const copyRegex = /(:\s*)(["']?)(.*?)(["']?)\s*copy\(([-]?\d+),\s*([^\;^\)^\(^,^ ]*)\)/g;
823
+
824
+ const transformedCss = css.replace(copyRegex, (match, prefix, quote1, value, quote2, lengthStr, variableName) => {
825
+ const length = parseInt(lengthStr);
826
+ const sanitizedVar = variableName.replace(/[^a-zA-Z0-9_-]/g, '');
827
+ let extractedValue = '';
828
+
829
+ if (length >= 0) {
830
+ extractedValue = value.substring(0, length);
831
+ } else {
832
+ extractedValue = value.substring(value.length + length);
833
+ }
834
+
835
+ customProperties.add(`--${sanitizedVar}:${extractedValue};`);
836
+ return `${prefix}${quote1}${value}${quote2}`;
837
+ });
838
+
839
+ // Append custom properties to :root if any were created
840
+ if (customProperties.size > 0) {
841
+ const rootBlock = `:root{${Array.from(customProperties).join('\n')}\n}`;
842
+ return transformedCss + `\n${rootBlock}`;
843
+ }
844
+ return transformedCss;
845
+ }
846
+
847
+ // Repeats a string while handling quotes
848
+ function repeatString(str, count) {
849
+ return str.replace(/^['"]|['"]$/g, '').repeat(Math.max(0, parseInt(count)));
850
+ }
851
+
852
+ // Processes recursive CSS patterns (re() function)
853
+ function replaceRe(css) {
854
+ // Enhanced regex to capture re() declarations with flexibility
855
+ const reRegex = /(?:store|str|re)\(\s*([^:,]+)\s*[,:]\s*(?:"([^"]*)"|'([^']*)')\s*\)/gi;
856
+ const variableMap = new Map();
857
+
858
+ // Step 1: Remove re() declarations and store variable-value mappings
859
+ let cleanedCss = css.replace(reRegex, (match, variable, dqValue, sqValue) => {
860
+ const value = dqValue || sqValue;
861
+ variable = variable.trim();
862
+ variableMap.set(variable, value);
863
+ return ''; // Completely remove the re() call
864
+ });
865
+
866
+ // If no variables found, return cleaned CSS
867
+ if (variableMap.size === 0) return cleanedCss;
868
+
869
+ // Step 2: Replace variables throughout the CSS
870
+ let changed;
871
+ let iterations = 0;
872
+ const maxIterations = 100;
873
+ let current = cleanedCss;
874
+
875
+ do {
876
+ changed = false;
877
+ for (const [variable, value] of variableMap.entries()) {
878
+ // Use word boundaries to avoid partial replacements
879
+ const varRegex = new RegExp(`\\b${escapeRegExp(variable)}\\b`, 'g');
880
+ const newCss = current.replace(varRegex, value);
881
+
882
+ if (newCss !== current) {
883
+ changed = true;
884
+ current = newCss;
885
+ }
886
+ }
887
+ iterations++;
888
+ } while (changed && iterations < maxIterations);
889
+
890
+ if (iterations >= maxIterations) {
891
+ console.warn('Maximum iterations reached. Possible circular dependency.');
892
+ }
893
+
894
+ return current;
895
+ }
896
+
897
+ function escapeRegExp(string) {
898
+ return string.replace(/[.*+?^${}|[\]\\]/g, '\\$&');
899
+ }
900
+
901
+
902
+ // Applies all FSCSS transformations to CSS content
903
+ function applyFscssTransformations(css) {
904
+ // Handle mx/mxs padding shorthands
905
+ css = css.replace(/(?:mxs|\$p)\((([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,\s*)?("([^"]*)"|'([^']*)')\)/gi, '$2:$14$15;$4:$14$15;$6:$14$15;$8:$14$15;$10:$14$15;$12:$14$15;')
906
+ .replace(/(?:mx|\$m)\((([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,)?(([^\,]*)\,\s*)?("([^"]*)"|'([^']*)')\)/gi, '$2$14$15$4$14$15$6$14$15$8$14$15$10$14$15$12$14$15')
907
+
908
+ // Handle string repetition (rpt)
909
+ .replace(/rpt\((\d+)\,\s*("([^"]*)"|'([^']*)')\)/gi, (match, count, quotedStr) => repeatString(quotedStr, count))
910
+
911
+ // Process CSS variable declarations and references
912
+ .replace(/\$(([\_\-\d\w]+)\:(\"[^\"]*\"|\'[^\']*\'|[^\;]*)\;)/gi, ':root{--$1}')
913
+ .replace(/\$([^\!\s]+)!/gi, 'var(--$1)')
914
+ .replace(/\$([\w\-\_\d]+)/gi, 'var(--$1)')
915
+
916
+ // Handle vendor prefix expansion
917
+ .replace(/\-\*\-(([^\:]+)\:(\"[^\"]*\"|\'[^\']*\'|[^\;]*)\;)/gi, '-webkit-$1-moz-$1-ms-$1-o-$1')
918
+ // Process list-based shorthands (%i, %6-%1)
919
+ .replace(/%i\((([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\]\[]*)\,)?(([^\,\]\[]*)\,)?(([^\,\[\]]*))?\s*\[([^\]\[]*)\]\)/gi, '$2$21$4$21$6$21$8$21$10$21$12$21$14$21$16$21$18$21$20$21')
920
+ .replace(/%6\((([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\]\[]*)\,)?(([^\,\]\[]*)\,)?(([^\,\[\]]*))?\s*\[([^\]\[]*)\]\)/gi, '$2$13$4$13$6$13$8$13$10$13$12$13')
921
+ .replace(/%5\((([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\]\[]*)\,)?(([^\,\]\[]*))?\s*\[([^\]\[]*)\]\)/gi, '$2$11$4$11$6$11$8$11$10$11')
922
+ .replace(/%4\((([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*))?\s*\[([^\]\[]*)\]\)/gi, '$2$9$4$9$6$9$8$9')
923
+ .replace(/%3\((([^\,\[\]]*)\,)?(([^\,\[\]]*)\,)?(([^\,\[\]]*))?\s*\[([^\]\[]*)\]\)/gi, '$2$7$4$7$6$7')
924
+ .replace(/%2\((([^\,\[\]]*)\,)?(([^\,\]\[]*))?\s*\[([^\]\[]*)\]\)/gi, '$2$5$4$5')
925
+ .replace(/%1\((([^\,\]\[]*))?\s*\[([^\]\[]*)\]\)/gi, '$2$3');
926
+ css = procP(css);
927
+ css = css.replace(/\$\(\s*@keyframes\s*(\S+)\)/gi, '$1{animation-name:$1;}@keyframes $1')
928
+ .replace(/\$\(\s*(\@[\w\-\*]*)\s*([^\{\}\,&]*)(\s*,\s*[^\{\}&]*)?&?(\[([^\{\}]*)\])?\s*\)/gi, '$2$3{animation:$2 $5;}$1 $2')
929
+
930
+ // Process property references
931
+ .replace(/\$\(\s*--([^\{\}]*)\)/gi, '$1')
932
+ .replace(/\$\(([^\:]*):\s*([^\)\:]*)\)/gi, '[$1=\'$2\']')
933
+
934
+ // Handle grouping syntax (g)
935
+ .replace(/g\(([^"'\s]*)\,\s*(("([^"]*)"|'([^']*)')\,\s*)?("([^"]*)"|'([^']*)')\s*\)/gi, '$1 $4$5$1 $7$8')
936
+ .replace(/\$\(([^\:]*):\s*([^\)\:]*)\)/gi, '[$1=\'$2\']')
937
+ .replace(/\$\(([^\:^\)]*)\)/gi, '[$1]');
938
+ return css;
939
+ }
940
+ async function impSel(text) {
941
+ const validImpExt = [".fscss", ".css", ".txt", ".scss", ".less", "xfscss"]
942
+ const regex = /@import\(exec\(([^)]+)\)\s*\.\s*(?:pick|find)\(([^)]+)\)\)/g;
943
+ const matches = [...text.matchAll(regex)];
944
+
945
+ let result = text;
946
+
947
+ for (const match of matches) {
948
+ const [fullMatch, urlSrc, part] = match;
949
+ try {
950
+ const impUrl = urlSrc.replace(/["']/g, "");
951
+ const impExt = impUrl.slice(impUrl.lastIndexOf(".")).toLowerCase();
952
+ if (impUrl.trim().startsWith("_init") && impUrl.includes(" ")) {
953
+ console.warn(`fscss[@import] library not found for: ${impUrl}`);
954
+ return;
955
+ }
956
+
957
+ if (!validImpExt.includes(impExt)) {
958
+ console.warn(`fscss[@import] invalid extension for: ${impUrl}`);
959
+ return;
960
+ }
961
+
962
+ const response = await fetch(impUrl);
963
+ if (!response.ok) throw new Error(`fscss[@import] HTTP ${response.status} for ${urlSrc}`);
964
+ const resText = await response.text();
965
+ const extracted = extractOnlyBlock(resText, part.trim());
966
+ result = result.replace(fullMatch, extracted);
967
+ } catch (err) {
968
+ console.error(`fscss[@import] Failed: ${urlSrc} `, err);
969
+ result = result.replace(fullMatch, `/* Failed import: ${urlSrc} */`);
970
+
971
+ }
972
+ }
973
+
974
+ return result;
975
+ }
976
+
977
+ function extractOnlyBlock(cssText, blockName) {
978
+ const regex = new RegExp(`${blockName}\\s*{[^}]*}`, "g");
979
+ const match = cssText.match(regex);
980
+ return match ? match.join("\n") : console.warn(`fscss[@import pick] No block matches: ${blockName} `);
981
+ }
982
+
983
+ const VALID_EXTENSIONS = ['.fscss', '.css', '.txt', '.scss', '.less', '.xfscss'];
984
+
985
+ async function processImports(cssText, depth = 0, baseURL = window.location.href) {
986
+ if (depth > exfMAX_DEPTH) {
987
+ console.warn('fscss[@import]\n Maximum import depth exceeded. Skipping further imports.');
988
+ return cssText;
989
+ }
990
+
991
+ const importRegex = /@import\s*\(\s*exec\s*\(\s*((?:'[^']*'|"[^"]*"|[^'")]\S*)\s*)\)\s*\)/g;
992
+ const matches = Array.from(cssText.matchAll(importRegex));
993
+
994
+ if (matches.length === 0) return cssText;
995
+
996
+ const fetchedContents = await Promise.all(
997
+ matches.map(async (match) => {
998
+ const [fullMatch, urlSpec] = match;
999
+ try {
1000
+ const cleanUrl = urlSpec.replace(/^['"](.*)['"]$/, '$1').trim();
1001
+ const absoluteUrl = new URL(cleanUrl, baseURL).href;
1002
+
1003
+ // --- New code for extension validation ---
1004
+ const urlPath = new URL(absoluteUrl).pathname;
1005
+ const extension = urlPath.slice(urlPath.lastIndexOf('.')).toLowerCase();
1006
+
1007
+ if (absoluteUrl.trim().startsWith("_init") && absoluteUrl.includes(" ")){
1008
+ console.warn(`fscss[@import] library not found for: ${absoluteUrl}`);
1009
+ return;
1010
+ }
1011
+
1012
+ if (!VALID_EXTENSIONS.includes(extension)) {
1013
+ console.warn(`fscss[@import] \n Invalid import URL extension "${extension}" for "${absoluteUrl}". Only ${VALID_EXTENSIONS.join(', ')} are allowed.`);
1014
+ return `/* Invalid extension for "${absoluteUrl}" */`;
1015
+ }
1016
+ // --- End of new code ---
1017
+
1018
+ const response = await fetch(absoluteUrl);
1019
+ if (!response.ok) throw new Error(`HTTP ${response.status} for ${absoluteUrl}`);
1020
+
1021
+ const importedText = await response.text();
1022
+ return processImports(importedText, depth + 1, absoluteUrl);
1023
+ } catch (error) {
1024
+ console.warn(`fscss[@import]\n Failed to import "${urlSpec}" from "${baseURL}":`, error);
1025
+ return `/* Error importing "${urlSpec}": ${error.message} */`;
1026
+ }
1027
+ })
1028
+ );
1029
+
1030
+ let lastIndex = 0;
1031
+ let result = '';
1032
+ matches.forEach((match, i) => {
1033
+ result += cssText.slice(lastIndex, match.index);
1034
+ result += fetchedContents[i];
1035
+ lastIndex = match.index + match[0].length;
1036
+ });
1037
+ result += cssText.slice(lastIndex);
1038
+
1039
+ return result;
1040
+ }
1041
+
1042
+ async function procImp(css) {
1043
+ try {
1044
+ const processedCSS = await processImports(css);
1045
+ return processedCSS;
1046
+ } catch (error) {
1047
+ console.warn('fscss[]\n Processing failed:', error);
1048
+ console.warn(`fscss[@import] Warning: can't resolve imports`);
1049
+ return css;
1050
+ }
1051
+ }
1052
+
1053
+ function execObj(css){
1054
+ return css.replace(/exec\.obj\.block\([^\)\n]*\)\;?/g, "");
1055
+ }
1056
+
1057
+ export { initlibraries,
1058
+ impSel, procImp, replaceRe, procExt, procVar,procFun, procArr, procEv, procRan, transformCssValues, procNum,applyFscssTransformations,procExC, execObj
1059
+ }
1060
+