CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nwsapi

Fast CSS Selectors API Engine that serves as a cross-browser replacement for native CSS selection and matching functionality

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

extensions.mddocs/

Extensions

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.

Capabilities

Register Selector

Registers a new pseudo-class or pseudo-element selector with its matching pattern and resolver function.

/**
 * Registers new selector pattern and resolver
 * @param name - Unique name for the selector
 * @param rexp - Regular expression pattern to match selector syntax
 * @param func - Resolver function that generates matching logic
 */
function registerSelector(name, rexp, func);

Usage Examples:

const nwsapi = require("nwsapi");

// Register custom :control pseudo-class
nwsapi.registerSelector('Controls', /^\:(control)(.*)/i, 
  (function(global) {
    return function(match, source, mode, callback) {
      var status = true;
      source = 'if(/^(button|input|select|textarea)/i.test(e.nodeName)){' + source + '}';
      return { 'source': source, 'status': status };
    };
  })(this)
);

// Use the custom selector
const controls = nwsapi.select(':control');

// Register :contains() pseudo-class
nwsapi.registerSelector('Contains', /^\:contains\(\s*(.+)\s*\)/i,
  function(match, source, mode, callback) {
    const text = match[1].replace(/['"]/g, '');
    const condition = `e.textContent.indexOf('${text}') !== -1`;
    source = `if(${condition}){${source}}`;
    return { source: source, status: true };
  }
);

// Use :contains() selector
const elements = nwsapi.select('div:contains("Hello World")');

Register Operator

Registers a new attribute operator with its symbol and resolver configuration.

/**
 * Registers new attribute operator
 * @param operator - Operator symbol (e.g., '!=', '*=')
 * @param resolver - Resolver configuration object
 */
function registerOperator(operator, resolver);

Usage Examples:

const nwsapi = require("nwsapi");

// Register not-equal operator
nwsapi.registerOperator('!=', { 
  p1: '^', 
  p2: '$', 
  p3: 'false' 
});

// Use the custom operator
const elements = nwsapi.select('[class!="hidden"]');

// Register case-insensitive contains operator
nwsapi.registerOperator('*=i', {
  p1: 'i',
  p2: 'i', 
  p3: 'true'
});

// Use case-insensitive matching
const insensitive = nwsapi.select('[data-name*=i"john"]');

Register Combinator

Registers a new combinator symbol with its resolver logic for connecting selector parts.

/**
 * Registers new combinator symbol and resolver
 * @param combinator - Combinator symbol (e.g., '^', '||')
 * @param resolver - JavaScript code for combinator logic
 */
function registerCombinator(combinator, resolver);

Usage Examples:

const nwsapi = require("nwsapi");

// Register parent combinator (^)
nwsapi.registerCombinator('^', 'e.parentElement');

// Use parent combinator: find divs whose parent is a section
const elements = nwsapi.select('section ^ div');

// Register shadow DOM combinator
nwsapi.registerCombinator('>>>', 'e.shadowRoot && e.shadowRoot.querySelector("*")');

// Find elements inside shadow DOM
const shadowElements = nwsapi.select('div >>> span');

Extension Patterns

Complex Selector Extensions

const nwsapi = require("nwsapi");

// Register :nth-match() pseudo-class
nwsapi.registerSelector('NthMatch', /^\:nth-match\(\s*(\d+)\s*,\s*(.+)\s*\)/i,
  function(match, source, mode, callback) {
    const index = parseInt(match[1]) - 1; // Convert to 0-based
    const selector = match[2];
    
    source = `
      var matchingElements = NW.Dom.select('${selector}', e.parentElement);
      if (matchingElements[${index}] === e) {
        ${source}
      }
    `;
    
    return { source: source, status: true };
  }
);

// Use :nth-match() to find 3rd paragraph in each container
const thirdParagraphs = nwsapi.select('.container p:nth-match(3, p)');

Attribute Operator Extensions

const nwsapi = require("nwsapi");

// Register numeric comparison operators
const numericOperators = {
  '>': 'parseFloat(a) > parseFloat(v)',
  '<': 'parseFloat(a) < parseFloat(v)', 
  '>=': 'parseFloat(a) >= parseFloat(v)',
  '<=': 'parseFloat(a) <= parseFloat(v)'
};

Object.keys(numericOperators).forEach(op => {
  nwsapi.registerOperator(op, {
    p1: `(function(a,v){return ${numericOperators[op]}})`,
    p2: '',
    p3: 'true'
  });
});

// Use numeric operators
const highPriority = nwsapi.select('[data-priority>="5"]');
const lowValues = nwsapi.select('[data-value<"100"]');

Combinator Extensions

const nwsapi = require("nwsapi");

// Register column combinator for tables
nwsapi.registerCombinator('||', `
  (function(element) {
    var table = element.closest('table');
    if (!table) return null;
    var cellIndex = Array.from(element.parentElement.children).indexOf(element);
    var rows = table.querySelectorAll('tr');
    var column = [];
    for (var i = 0; i < rows.length; i++) {
      if (rows[i].children[cellIndex]) {
        column.push(rows[i].children[cellIndex]);
      }
    }
    return column;
  })(e)
`);

// Find all cells in the same column
const columnCells = nwsapi.select('th || td');

jQuery Compatibility Extension

When nwsapi-jquery.js is loaded, additional jQuery-compatible selectors become available:

Structural Selectors

// These become available after loading nwsapi-jquery.js

// Position-based selectors
const odd = nwsapi.select('tr:odd');      // Odd-indexed rows
const even = nwsapi.select('tr:even');     // Even-indexed rows
const first = nwsapi.select('p:first');    // First paragraph
const last = nwsapi.select('p:last');      // Last paragraph

// Index-based selectors  
const third = nwsapi.select('li:eq(2)');   // Third list item (0-based)
const after = nwsapi.select('li:gt(2)');   // Items after index 2
const before = nwsapi.select('li:lt(2)');  // Items before index 2
const nth = nwsapi.select('li:nth(3)');    // Fourth list item

Content and State Selectors

// Content-based selectors
const hasImages = nwsapi.select('div:has(img)');     // Divs containing images
const parents = nwsapi.select('div:parent');          // Divs with children
const visible = nwsapi.select('div:visible');         // Visible divs
const hidden = nwsapi.select('div:hidden');           // Hidden divs

// Form control selectors
const buttons = nwsapi.select(':button');             // Button elements
const inputs = nwsapi.select(':input');               // All input elements
const textInputs = nwsapi.select(':text');            // Text inputs
const checkboxes = nwsapi.select(':checkbox');        // Checkboxes
const radios = nwsapi.select(':radio');               // Radio buttons
const files = nwsapi.select(':file');                 // File inputs
const images = nwsapi.select(':image');               // Image inputs
const passwords = nwsapi.select(':password');         // Password inputs
const submits = nwsapi.select(':submit');             // Submit buttons
const resets = nwsapi.select(':reset');               // Reset buttons

// Header selector
const headers = nwsapi.select(':header');             // h1, h2, h3, h4, h5, h6

DOM Traversal Extension

When nwsapi-traversal.js is loaded, DOM traversal methods become available:

Traversal Methods

/**
 * Walk up to parent elements
 * @param element - Starting element
 * @param expr - CSS selector or index
 * @returns Matching parent element or null
 */
function up(element, expr);

/**
 * Walk down to descendant elements
 * @param element - Starting element  
 * @param expr - CSS selector or index
 * @returns Matching descendant element or null
 */
function down(element, expr);

/**
 * Walk to next sibling elements
 * @param element - Starting element
 * @param expr - CSS selector or index
 * @returns Matching next sibling or null
 */
function next(element, expr);

/**
 * Walk to previous sibling elements
 * @param element - Starting element
 * @param expr - CSS selector or index  
 * @returns Matching previous sibling or null
 */
function previous(element, expr);

Usage Examples:

// After loading nwsapi-traversal.js
const nwsapi = require("nwsapi");

// Navigate by index
const secondParent = nwsapi.up(element, 2);        // 2nd parent up
const firstChild = nwsapi.down(element, 0);        // First child
const nextSibling = nwsapi.next(element, 1);       // Next sibling
const prevSibling = nwsapi.previous(element, 1);   // Previous sibling

// Navigate by selector
const form = nwsapi.up(element, 'form');           // Closest form ancestor
const button = nwsapi.down(element, 'button');     // First button descendant
const link = nwsapi.next(element, 'a');            // Next link sibling
const input = nwsapi.previous(element, 'input');   // Previous input sibling

Extension Registries

Operators Registry

Direct access to the registered attribute operators registry.

/**
 * Registry of attribute operators and their resolver configurations
 * @type { [operator: string]: OperatorResolver }
 */
const Operators: {
  [operator: string]: {
    p1: string;  // Pattern prefix
    p2: string;  // Pattern suffix  
    p3: string;  // Match result
  }
};

Usage Examples:

const nwsapi = require("nwsapi");

// View all registered operators
console.log('Registered operators:', Object.keys(nwsapi.Operators));

// Check specific operator configuration
console.log('Equals operator:', nwsapi.Operators['=']);
console.log('Contains operator:', nwsapi.Operators['*=']);

// Iterate through all operators
Object.entries(nwsapi.Operators).forEach(([op, config]) => {
  console.log(`Operator ${op}:`, config);
});

Selectors Registry

Direct access to the registered custom selectors registry.

/**
 * Registry of custom selectors with their patterns and callbacks
 * @type { [name: string]: { Expression: RegExp; Callback: Function } }
 */
const Selectors: {
  [name: string]: {
    Expression: RegExp;  // Pattern to match selector syntax
    Callback: Function;  // Resolver function
  }
};

Usage Examples:

const nwsapi = require("nwsapi");

// View all registered custom selectors
console.log('Custom selectors:', Object.keys(nwsapi.Selectors));

// Check specific selector registration
if (nwsapi.Selectors['Controls']) {
  console.log('Controls selector pattern:', nwsapi.Selectors['Controls'].Expression);
}

// List all selector patterns
Object.entries(nwsapi.Selectors).forEach(([name, config]) => {
  console.log(`Selector ${name}:`, config.Expression.source);
});

Best Practices

Extension Development

  • Unique naming: Use descriptive, unique names for custom selectors
  • Performance: Keep resolver functions efficient, avoid complex operations
  • Compatibility: Test extensions with various selector combinations
  • Documentation: Document custom extensions for team usage

Extension Management

const nwsapi = require("nwsapi");

// Organized extension registration
const customExtensions = {
  selectors: {
    'control': /^\:(control)(.*)/i,
    'contains': /^\:contains\(\s*(.+)\s*\)/i
  },
  operators: {
    '!=': { p1: '^', p2: '$', p3: 'false' },
    '>': { p1: 'parseFloat', p2: '>', p3: 'true' }
  },
  combinators: {
    '^': 'e.parentElement',
    '>>>': 'e.shadowRoot'
  }
};

// Register all extensions
function registerCustomExtensions() {
  Object.keys(customExtensions.selectors).forEach(name => {
    nwsapi.registerSelector(name, customExtensions.selectors[name], /* resolver */);
  });
  
  Object.keys(customExtensions.operators).forEach(op => {
    nwsapi.registerOperator(op, customExtensions.operators[op]);
  });
  
  Object.keys(customExtensions.combinators).forEach(comb => {
    nwsapi.registerCombinator(comb, customExtensions.combinators[comb]);
  });
}

registerCustomExtensions();

Install with Tessl CLI

npx tessl i tessl/npm-nwsapi

docs

compilation.md

configuration.md

dom-helpers.md

dom-selection.md

extensions.md

index.md

installation.md

tile.json