0
# Extensions
1
2
Plugin system for extending NWSAPI with custom selectors, attribute operators, and combinators. The extension framework allows developers to add new CSS selector capabilities and modify parsing behavior.
3
4
## Capabilities
5
6
### Register Selector
7
8
Registers a new pseudo-class or pseudo-element selector with its matching pattern and resolver function.
9
10
```javascript { .api }
11
/**
12
* Registers new selector pattern and resolver
13
* @param name - Unique name for the selector
14
* @param rexp - Regular expression pattern to match selector syntax
15
* @param func - Resolver function that generates matching logic
16
*/
17
function registerSelector(name, rexp, func);
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
const nwsapi = require("nwsapi");
24
25
// Register custom :control pseudo-class
26
nwsapi.registerSelector('Controls', /^\:(control)(.*)/i,
27
(function(global) {
28
return function(match, source, mode, callback) {
29
var status = true;
30
source = 'if(/^(button|input|select|textarea)/i.test(e.nodeName)){' + source + '}';
31
return { 'source': source, 'status': status };
32
};
33
})(this)
34
);
35
36
// Use the custom selector
37
const controls = nwsapi.select(':control');
38
39
// Register :contains() pseudo-class
40
nwsapi.registerSelector('Contains', /^\:contains\(\s*(.+)\s*\)/i,
41
function(match, source, mode, callback) {
42
const text = match[1].replace(/['"]/g, '');
43
const condition = `e.textContent.indexOf('${text}') !== -1`;
44
source = `if(${condition}){${source}}`;
45
return { source: source, status: true };
46
}
47
);
48
49
// Use :contains() selector
50
const elements = nwsapi.select('div:contains("Hello World")');
51
```
52
53
### Register Operator
54
55
Registers a new attribute operator with its symbol and resolver configuration.
56
57
```javascript { .api }
58
/**
59
* Registers new attribute operator
60
* @param operator - Operator symbol (e.g., '!=', '*=')
61
* @param resolver - Resolver configuration object
62
*/
63
function registerOperator(operator, resolver);
64
```
65
66
**Usage Examples:**
67
68
```javascript
69
const nwsapi = require("nwsapi");
70
71
// Register not-equal operator
72
nwsapi.registerOperator('!=', {
73
p1: '^',
74
p2: '$',
75
p3: 'false'
76
});
77
78
// Use the custom operator
79
const elements = nwsapi.select('[class!="hidden"]');
80
81
// Register case-insensitive contains operator
82
nwsapi.registerOperator('*=i', {
83
p1: 'i',
84
p2: 'i',
85
p3: 'true'
86
});
87
88
// Use case-insensitive matching
89
const insensitive = nwsapi.select('[data-name*=i"john"]');
90
```
91
92
### Register Combinator
93
94
Registers a new combinator symbol with its resolver logic for connecting selector parts.
95
96
```javascript { .api }
97
/**
98
* Registers new combinator symbol and resolver
99
* @param combinator - Combinator symbol (e.g., '^', '||')
100
* @param resolver - JavaScript code for combinator logic
101
*/
102
function registerCombinator(combinator, resolver);
103
```
104
105
**Usage Examples:**
106
107
```javascript
108
const nwsapi = require("nwsapi");
109
110
// Register parent combinator (^)
111
nwsapi.registerCombinator('^', 'e.parentElement');
112
113
// Use parent combinator: find divs whose parent is a section
114
const elements = nwsapi.select('section ^ div');
115
116
// Register shadow DOM combinator
117
nwsapi.registerCombinator('>>>', 'e.shadowRoot && e.shadowRoot.querySelector("*")');
118
119
// Find elements inside shadow DOM
120
const shadowElements = nwsapi.select('div >>> span');
121
```
122
123
## Extension Patterns
124
125
### Complex Selector Extensions
126
127
```javascript
128
const nwsapi = require("nwsapi");
129
130
// Register :nth-match() pseudo-class
131
nwsapi.registerSelector('NthMatch', /^\:nth-match\(\s*(\d+)\s*,\s*(.+)\s*\)/i,
132
function(match, source, mode, callback) {
133
const index = parseInt(match[1]) - 1; // Convert to 0-based
134
const selector = match[2];
135
136
source = `
137
var matchingElements = NW.Dom.select('${selector}', e.parentElement);
138
if (matchingElements[${index}] === e) {
139
${source}
140
}
141
`;
142
143
return { source: source, status: true };
144
}
145
);
146
147
// Use :nth-match() to find 3rd paragraph in each container
148
const thirdParagraphs = nwsapi.select('.container p:nth-match(3, p)');
149
```
150
151
### Attribute Operator Extensions
152
153
```javascript
154
const nwsapi = require("nwsapi");
155
156
// Register numeric comparison operators
157
const numericOperators = {
158
'>': 'parseFloat(a) > parseFloat(v)',
159
'<': 'parseFloat(a) < parseFloat(v)',
160
'>=': 'parseFloat(a) >= parseFloat(v)',
161
'<=': 'parseFloat(a) <= parseFloat(v)'
162
};
163
164
Object.keys(numericOperators).forEach(op => {
165
nwsapi.registerOperator(op, {
166
p1: `(function(a,v){return ${numericOperators[op]}})`,
167
p2: '',
168
p3: 'true'
169
});
170
});
171
172
// Use numeric operators
173
const highPriority = nwsapi.select('[data-priority>="5"]');
174
const lowValues = nwsapi.select('[data-value<"100"]');
175
```
176
177
### Combinator Extensions
178
179
```javascript
180
const nwsapi = require("nwsapi");
181
182
// Register column combinator for tables
183
nwsapi.registerCombinator('||', `
184
(function(element) {
185
var table = element.closest('table');
186
if (!table) return null;
187
var cellIndex = Array.from(element.parentElement.children).indexOf(element);
188
var rows = table.querySelectorAll('tr');
189
var column = [];
190
for (var i = 0; i < rows.length; i++) {
191
if (rows[i].children[cellIndex]) {
192
column.push(rows[i].children[cellIndex]);
193
}
194
}
195
return column;
196
})(e)
197
`);
198
199
// Find all cells in the same column
200
const columnCells = nwsapi.select('th || td');
201
```
202
203
## jQuery Compatibility Extension
204
205
When `nwsapi-jquery.js` is loaded, additional jQuery-compatible selectors become available:
206
207
### Structural Selectors
208
209
```javascript
210
// These become available after loading nwsapi-jquery.js
211
212
// Position-based selectors
213
const odd = nwsapi.select('tr:odd'); // Odd-indexed rows
214
const even = nwsapi.select('tr:even'); // Even-indexed rows
215
const first = nwsapi.select('p:first'); // First paragraph
216
const last = nwsapi.select('p:last'); // Last paragraph
217
218
// Index-based selectors
219
const third = nwsapi.select('li:eq(2)'); // Third list item (0-based)
220
const after = nwsapi.select('li:gt(2)'); // Items after index 2
221
const before = nwsapi.select('li:lt(2)'); // Items before index 2
222
const nth = nwsapi.select('li:nth(3)'); // Fourth list item
223
```
224
225
### Content and State Selectors
226
227
```javascript
228
// Content-based selectors
229
const hasImages = nwsapi.select('div:has(img)'); // Divs containing images
230
const parents = nwsapi.select('div:parent'); // Divs with children
231
const visible = nwsapi.select('div:visible'); // Visible divs
232
const hidden = nwsapi.select('div:hidden'); // Hidden divs
233
234
// Form control selectors
235
const buttons = nwsapi.select(':button'); // Button elements
236
const inputs = nwsapi.select(':input'); // All input elements
237
const textInputs = nwsapi.select(':text'); // Text inputs
238
const checkboxes = nwsapi.select(':checkbox'); // Checkboxes
239
const radios = nwsapi.select(':radio'); // Radio buttons
240
const files = nwsapi.select(':file'); // File inputs
241
const images = nwsapi.select(':image'); // Image inputs
242
const passwords = nwsapi.select(':password'); // Password inputs
243
const submits = nwsapi.select(':submit'); // Submit buttons
244
const resets = nwsapi.select(':reset'); // Reset buttons
245
246
// Header selector
247
const headers = nwsapi.select(':header'); // h1, h2, h3, h4, h5, h6
248
```
249
250
## DOM Traversal Extension
251
252
When `nwsapi-traversal.js` is loaded, DOM traversal methods become available:
253
254
### Traversal Methods
255
256
```javascript { .api }
257
/**
258
* Walk up to parent elements
259
* @param element - Starting element
260
* @param expr - CSS selector or index
261
* @returns Matching parent element or null
262
*/
263
function up(element, expr);
264
265
/**
266
* Walk down to descendant elements
267
* @param element - Starting element
268
* @param expr - CSS selector or index
269
* @returns Matching descendant element or null
270
*/
271
function down(element, expr);
272
273
/**
274
* Walk to next sibling elements
275
* @param element - Starting element
276
* @param expr - CSS selector or index
277
* @returns Matching next sibling or null
278
*/
279
function next(element, expr);
280
281
/**
282
* Walk to previous sibling elements
283
* @param element - Starting element
284
* @param expr - CSS selector or index
285
* @returns Matching previous sibling or null
286
*/
287
function previous(element, expr);
288
```
289
290
**Usage Examples:**
291
292
```javascript
293
// After loading nwsapi-traversal.js
294
const nwsapi = require("nwsapi");
295
296
// Navigate by index
297
const secondParent = nwsapi.up(element, 2); // 2nd parent up
298
const firstChild = nwsapi.down(element, 0); // First child
299
const nextSibling = nwsapi.next(element, 1); // Next sibling
300
const prevSibling = nwsapi.previous(element, 1); // Previous sibling
301
302
// Navigate by selector
303
const form = nwsapi.up(element, 'form'); // Closest form ancestor
304
const button = nwsapi.down(element, 'button'); // First button descendant
305
const link = nwsapi.next(element, 'a'); // Next link sibling
306
const input = nwsapi.previous(element, 'input'); // Previous input sibling
307
```
308
309
## Extension Registries
310
311
### Operators Registry
312
313
Direct access to the registered attribute operators registry.
314
315
```javascript { .api }
316
/**
317
* Registry of attribute operators and their resolver configurations
318
* @type { [operator: string]: OperatorResolver }
319
*/
320
const Operators: {
321
[operator: string]: {
322
p1: string; // Pattern prefix
323
p2: string; // Pattern suffix
324
p3: string; // Match result
325
}
326
};
327
```
328
329
**Usage Examples:**
330
331
```javascript
332
const nwsapi = require("nwsapi");
333
334
// View all registered operators
335
console.log('Registered operators:', Object.keys(nwsapi.Operators));
336
337
// Check specific operator configuration
338
console.log('Equals operator:', nwsapi.Operators['=']);
339
console.log('Contains operator:', nwsapi.Operators['*=']);
340
341
// Iterate through all operators
342
Object.entries(nwsapi.Operators).forEach(([op, config]) => {
343
console.log(`Operator ${op}:`, config);
344
});
345
```
346
347
### Selectors Registry
348
349
Direct access to the registered custom selectors registry.
350
351
```javascript { .api }
352
/**
353
* Registry of custom selectors with their patterns and callbacks
354
* @type { [name: string]: { Expression: RegExp; Callback: Function } }
355
*/
356
const Selectors: {
357
[name: string]: {
358
Expression: RegExp; // Pattern to match selector syntax
359
Callback: Function; // Resolver function
360
}
361
};
362
```
363
364
**Usage Examples:**
365
366
```javascript
367
const nwsapi = require("nwsapi");
368
369
// View all registered custom selectors
370
console.log('Custom selectors:', Object.keys(nwsapi.Selectors));
371
372
// Check specific selector registration
373
if (nwsapi.Selectors['Controls']) {
374
console.log('Controls selector pattern:', nwsapi.Selectors['Controls'].Expression);
375
}
376
377
// List all selector patterns
378
Object.entries(nwsapi.Selectors).forEach(([name, config]) => {
379
console.log(`Selector ${name}:`, config.Expression.source);
380
});
381
```
382
383
## Best Practices
384
385
### Extension Development
386
387
- **Unique naming**: Use descriptive, unique names for custom selectors
388
- **Performance**: Keep resolver functions efficient, avoid complex operations
389
- **Compatibility**: Test extensions with various selector combinations
390
- **Documentation**: Document custom extensions for team usage
391
392
### Extension Management
393
394
```javascript
395
const nwsapi = require("nwsapi");
396
397
// Organized extension registration
398
const customExtensions = {
399
selectors: {
400
'control': /^\:(control)(.*)/i,
401
'contains': /^\:contains\(\s*(.+)\s*\)/i
402
},
403
operators: {
404
'!=': { p1: '^', p2: '$', p3: 'false' },
405
'>': { p1: 'parseFloat', p2: '>', p3: 'true' }
406
},
407
combinators: {
408
'^': 'e.parentElement',
409
'>>>': 'e.shadowRoot'
410
}
411
};
412
413
// Register all extensions
414
function registerCustomExtensions() {
415
Object.keys(customExtensions.selectors).forEach(name => {
416
nwsapi.registerSelector(name, customExtensions.selectors[name], /* resolver */);
417
});
418
419
Object.keys(customExtensions.operators).forEach(op => {
420
nwsapi.registerOperator(op, customExtensions.operators[op]);
421
});
422
423
Object.keys(customExtensions.combinators).forEach(comb => {
424
nwsapi.registerCombinator(comb, customExtensions.combinators[comb]);
425
});
426
}
427
428
registerCustomExtensions();
429
```