New "selected text" AppleScript property

Ah, I think I am conflating two separate entities. If the selected text contains and embedded link, that is what appelscript is returning.

Is there a way for me to access the “selection link” URL that is now available in 3.9?

Rick

What would that URL be for a selected text? BTW: as it’s apparently part of text suite, I doubt that there’s anything new about it.

That’s right, these features exist for quite a while (as far as I remember since DEVONthink Pro 1.x, probably 1.0)

For an AppleScript example:

on run
    set mdLinks to mdFromLabelledLinks(selectedTextLabelLinkPairs())
    
    set the clipboard to mdLinks
    return mdLinks
end run


on selectedTextLabelLinkPairs()
    tell application "DEVONthink 3"
        set thinkWindow to its front think window
        
        if exists thinkWindow then
            tell thinkWindow to ¬
                set refText to a reference to (its selected text)
            
            if exists refText then
                set runs to a reference to (attribute runs of refText)
                
                my zip(text of runs, URL of runs)
            else
                {}
            end if
        else
            {}
        end if
    end tell
end selectedTextLabelLinkPairs


on mdFromLabelledLinks(kvs)
    script f
        on |λ|(kv)
            set {k, v} to kv
            
            if missing value is not v then
                {"[" & k & "](" & v & ")"}
            else
                {}
            end if
        end |λ|
    end script
    
    my unlines(my concatMap(f, kvs))
end mdFromLabelledLinks


on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    
    tell f
        repeat with i from 1 to lng
            set acc to acc & (|λ|(item i of xs, i, xs))
        end repeat
    end tell
    acc
end concatMap


on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min


on unlines(xs)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, linefeed}
    set s to xs as text
    set my text item delimiters to dlm
    s
end unlines


on zip(xs, ys)
    set n to min(length of xs, length of ys)
    
    set vs to {}
    repeat with i from 1 to n
        set end of vs to {item i of xs, item i of ys}
    end repeat
    return vs
end zip
1 Like

Functional programming in AppleScript – impressive!

It can, of course, be expressed more parsimoniously, but perhaps not without straining a reader’s patience with the overloading of it and its.

Extracting just the main function:

on selectedTextLabelLinkPairs()
    tell application "DEVONthink 3"
        
        tell front think window
            if exists then
                tell (a reference to (its selected text))
                    if exists then
                        tell attribute runs
                            my zip(text of it, URL of it)
                        end tell
                    else
                        {}
                    end if
                end tell
            else
                {}
            end if
        end tell
    end tell
end selectedTextLabelLinkPairs
1 Like

I realized my error after dissecting winter’s solution.

When I saw the feature in 3.9 allowing a direct link to a selection in pdfs, I dove into my automation scripts to make things in my workflow better. Because I know you all love automation as much as I do, I assumed that there would be a new AppleScript link to that feature so when I saw “selected text” as an option in the AS dictionary my self-delusion was complete. :slight_smile:

Thank you for a great product and for loving making it better and AppleScript-able for those of us geeks who love to use it!

Rick

2 Likes

Wow. Thanks for this impressive script. I will dive into it when I can!

Rick

Currently there is no AppleScript property for a record’s selection link, page link, paragraph link, or annotation link. There is only the reference url and item link parameters that potentially can be strung together to create such entities.

Thanks, Jim. I will look at creating this myself.

The page reference looks pretty easy to find, and the length parameter is obviously easy. Finding the start parameter though doesn’t seem straight forward other than "find"ing the selection within the total text and then hoping it’s the only text like that in the document…

I’ll keep looking and think on it some more.

Thanks!

Rick

this code creates a link into devonthink equivalent to a “selection link”. Let me know any improvements you see that need to be done.

-- Return md link to selected text

tell application id "DNtp"
	set fWindow to its front window
	if exists fWindow then
		set refText to a reference to selected text of fWindow
		
		if exists refText then
			set attr to a reference to (attribute runs of refText)
			
			set theText to text of attr
			set theText to first item of theText
			set theURL to URL of attr
			set theURL to first item of theURL
			
			-- build link
			set theRecord to content record of fWindow
			set allText to plain text of theRecord
			set theStart to offset of theText in allText
			set theStart to theStart - 1
			set theLength to count of theText
			set theLink to reference URL of theRecord & ¬
				"?page=" & current page of fWindow & ¬
				"&start=" & (theStart as string) & ¬
				"&length=" & (length of theText) as string
			
			if missing value is not theURL then
				set theText to "[" & theText & "](" & theURL & ")"
			end if
			
			set returnText to ((theText & "[^" & theStart as string) & "]}" & return & return & ¬
				"[^" & theStart as string) & "]: " & theLink
			return returnText
		end if
	end if
	
	return ""
end tell

Rick

This parameter is actually relative to beginning of the page. However, it’s possible to create selection links without having to use start/length parameters.

The page and search parameters are sufficient if the search parameter contains the escaped content of the selected text.

Thanks, Chris

So to “fix” theLink portion in my code above, I have come up with:

set theLink to reference URL of theRecord & ¬
	"?page=" & current page of fWindow & ¬
				"&search=" & my urlencode(theText)

and pulled some code from stack overflow to url encode some text as follows:

on urlencode(theText)
	set theTextEnc to ""
	repeat with eachChar in characters of theText
		set useChar to eachChar
		set eachCharNum to ASCII number of eachChar
		if eachCharNum = 32 then
			set useChar to "%20"
		else if (eachCharNum ≠ 42) and (eachCharNum ≠ 95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
			set firstDig to round (eachCharNum / 16) rounding down
			set secondDig to eachCharNum mod 16
			if firstDig > 9 then
				set aNum to firstDig + 55
				set firstDig to ASCII character aNum
			end if
			if secondDig > 9 then
				set aNum to secondDig + 55
				set secondDig to ASCII character aNum
			end if
			set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
			set useChar to numHex
		end if
		set theTextEnc to theTextEnc & useChar as string
	end repeat
	return theTextEnc
end urlencode

Rick

Hey, Rick… let me save you a little suffering and learn you something :wink:

You can run ad-hoc JavaScript in an AppleScript via the do Javascript command. The only requirement is it has to run in a think window. However, you don’t have to specify a window nor does the window need to be even remotely related to the command. It’s literally do javascript "<insert script here>" in think window 1 :open_mouth: :slight_smile:

set theText to "Hey, Rick! Here's an easier way to URL encode some text via AppleScript…"

tell application id "DNtp"
	do JavaScript "encodeURIComponent(\"" & theText & "\");" in think window 1 -- The argument needs to be in escaped double quotes
end tell

---> Hey%2C%20Rick!%20Here's%20an%20easier%20way%20to%20URL%20encode%20some%20text%20via%20AppleScript%E2%80%A6

And yes @chrillek, I sometimes use this to inject a little JS into AS as the situation needs it. :wink:

1 Like

This parameter should actually be optional, fixed for the next release.

2 Likes

Cool trick, indeed. Though I’m not sure if calling encodeURIComponent from AS shouldn’t be considered cheating :stuck_out_tongue_winking_eye:

2 Likes

I give JS its credit. I prefer my beloved AS, but there are definitely some great JS functions that can be employed as needed.

1 Like

You guys are the best. Appreciate you!

1 Like

Making this open where it can be used at any time will make this a killer feature across all scripting. Seriously.

@chrillek and I were looking at an issue recently and he had a perfect JavaScript solution. The problem was I already had a giant applescript so rewriting in JXA wouldn’t have worked for me.

But doing this would have! Nice!

It is cheating! It’s awesomely the right kind of cheating!!