or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compilation.mdconfiguration.mddom-helpers.mddom-selection.mdextensions.mdindex.mdinstallation.md

extensions.mddocs/

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

```