Copy selected link(s) from browser as markdown?

If you use an Execute Javascript action in Chrome or Safari, you can gather document title and url, but also use XPath expressions to look for the text of the most recent heading (before the selection) and any #anchor id or name that may be attached to that heading.

// ver 0.1 Rob Trew @complexpoint 2015
(function () {
	'use strict';

	var nodeSeln = window.getSelection().anchorNode,
		lstH = [1, 2, 3, 4, 5, 6],
		strInHeading = lstH.map(function (x) {
			return 'ancestor-or-self::h' + x;
		}).join(' or '),

		lstAnchor = [
			'*[' + lstH.map(function (x) {
				return 'self::h' + x;
			}).join(' or ') + ']//*',
			'a[string(@name) and (' + strInHeading + ')]',
			'*[string(@id) and (' + strInHeading + ')]'
		].map(function (strType) {
			var strPath = './preceding::' + strType + '[1]',
				oNode = document.evaluate(
					strPath,
					nodeSeln,
					null, 0, 0
				).iterateNext(),
				strTag;

			return oNode ? {
				text: oNode.hasChildNodes() ?
					oNode.firstChild.textContent : oNode.textContent,
				tag: (strTag = oNode.getAttribute('name')) ?
					strTag : oNode.getAttribute('id')
			} : {};
		}),

		strHead = lstAnchor[0].text || lstAnchor[1].text || lstAnchor[2].text,
		dctName = lstAnchor[1],
		dctID = lstAnchor[2];

	// Page name, url, heading text, heading #id or #name
	return [
		document.title,
		document.URL.split('#')[0],  // drop any page=load anchor
		strHead,
		strHead ? (  // get any local anchor for the selection
			(dctName.text === strHead) ? dctName.tag :
			(dctID.text === strHead) ? dctID.tag : ''
		) : ''
	].join('	');
})();

Here's a first rough (Chrome-only) sketch.

Copy from Chrome as linked RTF.kmmacros (25.8 KB)

I'll see if can get a moment tomorrow evening to do various things:

  • make it work with Safari
  • add optional 'under the heading:' and 'source:' prefixes
  • sort out the css

etc

( I'd like to wrap it as something like this:

Rob, I'm a bit confused. Is this macro supposed to create the markdown code? If so, it's not working for me.

When I try to paste here, I get nothing.
When I paste into a RTF document (Evernote), I get the link I selected and a named link for the page.

Forgive me – I should have explained and labelled

That draft just creates the RTF version, with links to source and to any header#anchor

Life was a bit full this evening, but tomorrow night I’ll aim to package it as a plugin with a markdown option.

No problem. Take your time. I just wanted to make sure I wasn't missing something. :smile:

FWIW here is a simple interim macro:

Copy slngle selected link as Markdown (Chrome or Safari)

Copy single link as Markdown.kmmacros (24.0 KB)

It executes this in the foreground browser (Chrome or Safari):

(function (oNode) {

	var oLink = document.evaluate(
		"./ancestor-or-self::a",
		oNode,
		null, 0, 0
	).iterateNext();

	return oLink ? 
		"[" + oLink.text + "](" + oLink.href + ")" :
		"";

})(window.getSelection().anchorNode);

Works great after I made one change:
In the "Execute JavaScript..." Action, set the output to "paste results"

Thanks for sharing all the great work with us.

If you can tell me (or point me to an example) of how to put BOTH plain text (like on this macro) AND RTF on the same clipboard, I will combine your macros to give us the best of both worlds. :smile:

From Applescript you would essentially create a record with two fields:

  • Unicode Text :
  • «class RTF » : «data RTF »

and then set the clipboard contents to that record.

Skipping the details of obtaining hex-encoded RTF:

set strText to "hello world!"
set strRTF to "{\\rtf1\\ansi\\ansicpg1252\\cocoartf1348\\cocoasubrtf170
{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;}
{\\colortbl;\\red255\\green255\\blue255;\\red255\\green39\\blue18;}
\\pard\\tx566\\tx1133\\tx1700\\tx2267\\tx2834\\tx3401\\tx3968\\tx4535\\tx5102\\tx5669\\tx6236\\tx6803\\pardirnatural

\\f0\\fs24 \\cf2 hello\\cf0
\\b world
\\b0  !\\
\\
\\
"
-- Hex encoding process not shown
set dataPackedRTF to «data RTF 7B5C727466315C616E73695C616E7369637067313235325C636F636F61727466313334385C636F636F617375627274663137300A7B5C666F6E7474626C5C66305C6673776973735C6663686172736574302048656C7665746963613B7D0A7B5C636F6C6F7274626C3B5C7265643235355C677265656E3235355C626C75653235353B5C7265643235355C677265656E33395C626C756531383B7D0A5C706172645C74783536365C7478313133335C7478313730305C7478323236375C7478323833345C7478333430315C7478333936385C7478343533355C7478353130325C7478353636395C7478363233365C7478363830335C7061726469726E61747572616C0A0A5C66305C66733234205C6366322068656C6C6F5C63663020200A5C6220776F726C640A5C62302020217D»

set recClip to {Unicode text:strText, «class RTF »:dataPackedRTF}
set the clipboard to recClip

UPDATE

PS you shouldn’t have to hex-encode raw RTF strings because textutil can place RTF in the clipboard for you, but if you did need to, you should be able to use the bash xxd command:

set dataPackedRTF to (run script "«data RTF " & (do shell script "echo " & quoted form of strRTF & " | xxd -p -u ") & "»")

OK, I'm almost there, but I am so lost with the shell script commands.

From your previous macro, you used the following to put RTF on the clipboard:

    set lstrCMD to "echo " & quoted form of pstrHTML & " | textutil -format html -convert rtf -stdin -stdout | pbcopy -Prefer rtf"

do shell script lstrCMD

How do I combine the two to set the dataPackedRTF using HTML code (the pstrHTML variable) ?

BTW, how do you get the color code in your above post?

Combining the two

  1. Assemble the content you want in HTML, and place an RTF version of it in the clipboard with textutil
  2. Adjust the text content of the clipboard, leaving the RTF unchanged:

Syntax highlighting

just use 3 backticks before and after the code – see under Github Markdown fenced code

-- Read the text and RTF components of the clipboard
set recClip to the clipboard as record
set strText to Unicode text of recClip
set dataRTF to «class RTF » of recClip

-- Derive an MD link version of the text
set {dlm, my text item delimiters} to {my text item delimiters, space}
set lstTokens to (text items of strText)
set my text item delimiters to "+"
set strTokens to lstTokens as string
set my text item delimiters to dlm

set strMDLink to "[" & strText & "](http://www.google.com/?=" & strTokens & ")"


-- New text contents, with existing RTF contents
set the clipboard to {Unicode text:strMDLink, «class RTF »:dataRTF}

-- inspect the result
the clipboard as record

OK, great! I got it! Many thanks.
Now let's see what I can do . . .

1 Like

OK, I patched together a very simple example/test, based mostly on your above code.
However, I got one error on your code.
The line:

set strText to Unicode text of recClip

gave this error:
Can’t get Unicode text of {«class RTF »:«data RTF

So, to keep things real simple for the example, I replaced your code to create the MD link with simple assignments.

Download AppleScript from here:
CB - How to Put Plain Text and Rich Text on Clipboard.scpt.zip (6.6 KB)

(*
How to Put BOTH Plain Text AND Rich Text on the same Clipboard

Assemble the content you want in HTML, and place an RTF version of it in the clipboard with textutil
Adjust the text content of the clipboard, leaving the RTF unchanged

When you paste into a Rich Text document, the rich text will be pasted.
When you paste into a plain text document (like the KM forum), the markdown text will be pasted.

AUTHOR:
	• ComplexPoint 
		• provided all of the hard code to create RTF and combine on clipboard
	• JMichaelTX 
		• simply added some simple prep code to this
	
VER:  0.2		DATE: Mon, Jul 20, 2015
*)

--- VERY SIMPLE EXAMPLE OF HTML CODE TO BE CONVERTED TO RICH TEXT ---

property gStyleLink : "color:blue"
set strFont to "font-family:verdana,geneva,sans-serif;"
set strLinkFontSize to "font-size:14px;"


set strHTMLLink to my createHTMLLink("Evernote Site", "http:/www.evernote.com")
set strHTMLLink to "<span style=\"" & strFont & strLinkFontSize & "\">" & strHTMLLink & "</span>"
set pstrHTML to strHTMLLink


-- PUT THE RTF Text on the Clipboard
set lstrCMD to "echo " & quoted form of pstrHTML & " | textutil -format html -convert rtf -stdin -stdout | pbcopy -Prefer rtf"
do shell script lstrCMD

-- Read the text and RTF components of the clipboard
set recClip to the clipboard as record
### set strText to Unicode text of recClip  -- gives error
set dataRTF to «class RTF » of recClip

--- My Simple Code to set Markdown Link ---

set strText to "Evernote Web Site"
set strURL to "http://www.evernote.com"
set strMDLink to "[" & strText & "](" & strURL & ")"


-- New text contents, with existing RTF contents
set the clipboard to {Unicode text:strMDLink, «class RTF »:dataRTF}

-- inspect the result
the clipboard as record

--—————————————————————————————————————————————————

on createHTMLLink(pstrLinkText, pstrURL)
	
	return "<a href=\"" & pstrURL & "\" style=\"" & gStyleLink & "\">" & pstrLinkText & "<a>"
	
end createHTMLLink

Comments/suggestions anyone?

Good !

The line:

set strText to Unicode text of recClip

gave this error: Can’t get Unicode text of {«class RTF »:«data RTF

The clipboard won't always have a Unicode text field.

( Applescript at its least impressive, alas – you can't ask a record for a list of the available fields, and error-handling is the only way to learn whether a particular field exists – in short no introspection )

Another approach – does all the Markdown conversion of the currently selected link or sentence in the browser.

( based on David Bengoa's https://gist.github.com/YouWoTMA/1762527, and using the following XPath to define the scope of the copy, in relation to the selection:

``
./ancestor-or-self::*[self::a or self::stuck_out_tongue: or self::ul or self::ol or self::tr or self::blockquote or " +
"self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6][1]`

)

<a class="attachment" href="/uploads/default/original/2X/7/75552bd4a807fcf8205288205fbac8506cc73250.kmmacros">Copy link or sentence from Browser as Markdown.kmmacros</a> (31.3 KB) 

<img src="/uploads/default/original/2X/8/854c939198930d2f40733bbef24bbc5ff78ed101.png" width="452" height="642">

Hey Rob,

This is not entirely true now with advent of ASObjC:

use framework "Foundation"

set myRecord to {var1:"User", var2:"Record"}
set recordKeyList to (current application's NSDictionary's dictionaryWithDictionary:myRecord)'s allKeys() as list

Unfortunately this method will only work with user-records and not system-records.

In this particular instance with the clipboard we can do something like this:

clipboardContainsUnicodeText()

on clipboardContainsUnicodeText()
  repeat with i in (get clipboard info)
    if i contains Unicode text then
      return true
    end if
  end repeat
  return false
end clipboardContainsUnicodeText

Although I will admit it is seriously kludgey.

Or to be a bit more flexible:

recordLabelsContain(Unicode text)

on recordLabelsContain(labelName)
  set labelList to {}
  set clipboardInfoRec to clipboard info
  repeat with i in clipboardInfoRec
    set end of labelList to item 1 of i
  end repeat
  return labelName is in labelList
end recordLabelsContain

-Chris

1 Like

Thanks Chris – that’s helpful, and it would be interesting to experiment with reading and writing NSPasteboard through the Yosemite ObjC bridge (built into the AS and JXA libraries).

OK, after much help from @ComplexPoint, @peternlewis, & @ccstone, I have compiled an AppleScript that achieves #1 on my ideal solution list: Copy selection of hyperlink from any document and convert into clean RTF hyperlink and Plain Text Markdown Link.

Download the AppleScript from here:
CB MD Put Hyperlink & MD Link on Clipboard.scpt.zip (15.8 KB)

Here's just the top of the file:

property gstrScriptName : "CB MD Put Hyperlink & MD Link on Clipboard"
property gstrScriptVer : "BETA 0.2"
property gstrScriptDate : "Wed, Jul 22, 2015"

(*
——————————————————————————————————————————————————
STATUS:  This script is BETA.  It is INCOMPLETE and is a work in progress.  Use for testing/commenting only.

PURPOSE:
	• Based on the User's selection, create and put on Clipboard:
		• RTF formatted Hyperlink
		• Plain Text Markdown text for hyperlink

METHOD:
	• The data for the OUTPUT hyperlinks will be determined as follows:
	
1st BETA	• IF Selection is a RTF/HTML Hyperlink from a Web page or RTF document
			• URL        = URL of the hyperlink
			• Link Text = Link Text of the hyperlink
			
NOT Done	• IF Selection is only Text with no link:
			• Link Text = Selected text
			• URL        =
				• IF Web page, THEN URL of page
				• ELSE set to "NoURL"
				
NOT Done	• IF NO Selection is made AND Current App is a Web app:
			• Link Text = Web page Title
			• URL        = Web page URL

NOT Done	• IF NO Selection is made AND Current App is NOT a Web app:
			• Display ERROR msg, asking user to select a hyperlink or a web page.
*)

It is still very much a BETA, actually an ALPHA, since it is INCOMPLETE, and has had only very limited test. I have NOT yet put any error checking/handling into it.

@ComplexPoint, I know you preferred to use Javascript to parse the hyperlink, but since I want this to work with both RTF documents and web pages, I have chosen to use RegEx. Seems to work OK, but definitely needs more testing.

If anyone has a better RegEx pattern, please let me know.

This requires Satimage AppleScript Extensions

Please give me your bug reports, comments, suggestions, improvements. Feel free to be as blunt as you'd like -- I have a thick skin. :slight_smile: I just want to make this be a great tool for all of us.

As I said, it is INCOMPLETE and doesn't have any error checking/handling.
It is a rough draft of the 1st use case. At this point I'm not sure what the best integration with KM is, so let me know if you have any ideas about that.

@ComplexPoint, cases #2 and #3 will require access to the web app/page. I know you've given a number of code examples and suggestions, but let me know you think is best at this point. I'm not sure how to best use JavaScript (web) from AppleScript. Go through KM?

I decided not to post the code here because it is so long due to header comments. It's about 4 screen, but I have a lot of comments and white space.
You guys let me know if you'd like it posted here.

I was on my way for a little bit of reading (old spy novels) and then bed when it hit me on how to make best use of KM.

These are my late night thoughts, so you guys feel free to jump in.

Start the process with KM.
Using KM Actions, determine the front app.
If it is a web app, get the page title and URL into KM variables, using whatever Javascript is best. Or can KM Actions get this?
IAC, pass to AppleScript as KM variables.
Also determine whether or not the user has selected anything, and set a KM variable.

Basically pass to AppleScript via KM variable all the data that’s needed to decide how to build the links, and the required data.

OK, I’m rambling now, so I’m off to reading/bed.
Look forward to your feedback tomorrow (or later today actually) :smile:

There are text tokens for Safari/Chrome front window title and URL (eg SafariTitle).