Markdown TOC for active page in Safari (JXA ⇄ Browser JS)

A simple macro which just generates a Markdown list of the headers in the web page currently loaded in Safari. (Or, with a minor edit, in Chrome).

Simple Markdown TOC for current Safari page (demo).kmmacros (21.3 KB)

Really intended as an illustration of how you can pass a JS function from Javascript for Applications to Safari or Chrome Browser JS, and get a result back. There's a lot of power in the browser, particularly for parsing, so it can be a useful resource.

A generic JXA function

evalJSinBrowser(fnMain, lstArgs, strBrowser)

Takes:

  1. a reference to a function defined in Javascript for Applications
  2. an array of arguments to that function, and
  3. the name of a browser: "Safari" or "Google Chrome"

and returns the result of the browser evaluation as a string.

// Evaluate a JS function application in a named browser (Chrome | Safari)
// fn --> [arg] --> strBrowserName --> a
function evalJSinBrowser(fnMain, lstArgs, strBrowser) {
	
	// Interface to the browser,
	var strDefault = "Safari",
		blnSafari = ((
			strBrowser = ((strBrowser || '').indexOf(strDefault) === 0) ?
			strDefault : "Google Chrome"
		) === strDefault),
		appBrowser = Application(strBrowser),
		
		// and an open browser window (new if none exists).
		lstWins = appBrowser.windows(),
		lngWins = lstWins.length,
		oWin = lngWins ? lstWins[0] : blnSafari ?
		appBrowser.Document().make() && appBrowser.windows[0] :
		appBrowser.Window().make(),

		// Code of an fnMain().apply(null, lstArgs) function application
		strJS = [
			'(',
			fnMain.toString(),
			').apply(null, ',
			JSON.stringify(lstArgs), ');'
		].join('');

	// Result of the function application
	return (
		blnSafari ?
		appBrowser.doJavaScript(
			strJS, {
				"in": oWin.tabs[0]
			}
		) :
		oWin.activeTab.execute({
			"javascript": strJS
		})
	);
}

For most purposes it's probably easier to use one of KM's built-in Execute JS in Browser actions, but this approach can be a useful way, within a JXA script, of flexibly delegating something (parsing in particular) to a browser engine.

( It also eases the editing of the browser JS function(s) – you can do this in Script Editor or another text editor as part of the JXA code, and take advantage of linters, code formatters etc.

evalJSinBrowser(fnMain, lstArgs, strBrowser) takes care of converting the referenced function to a code string in which it is applied to the array of arguments).

An updated example:

  1. Now works for the front page in either Chrome or Safari
  2. Adds any anchor-linked urls (for document sections) to the TOC headings
  3. Uses an indented bullet outline (rather than hash headings) for the TOC

Markdown Table of Contents for current Safari or Chrome page (v.03).kmmacros (24.5 KB)