This discussion started in another thread. It is a very good discussion, but completely off-topic to the other thread. So I moved it here.
ORIGINAL TOPIC:
This discussion started in another thread. It is a very good discussion, but completely off-topic to the other thread. So I moved it here.
ORIGINAL TOPIC:
Rob, thanks for posting this JXA script. I always learn a lot from your scripts.
When I was testing it and trying to understand how it worked, I ran across this bug:
Your test for valid strClip
fails if only an image is on the clipboard.
Here's a small snippet from your script:
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a),
strClip = sa.theClipboard();
if (strClip)
The (strClip)
returns true
if there is only an object (and no text) on the clipboard.
Here's a test script you can run to verify this:
'use strict';
var app = Application.currentApplication()
app.includeStandardAdditions = true
//--- COPY ONLY AN IMAGE TO CLIPBOARD ---
// Then run this script
var clipStr = app.theClipboard();
//--- This is NOT a valid test for the variable containing text ---
// It also returns true if the variable is an object, like an
// image on the clipboard.
if (clipStr) {
console.log ("'clipStr IS TRUE'")
console.log(typeof clipStr)
}
//---RESULTS---
/* 'clipStr IS TRUE' */
/* object */
Here is a revised script that fixes this issue, as well as adds some comments to help those like me who are still learning JXA.
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SAVE TEXT ON CLIPBOARD TO FILE
------------------------------
Ver: 2.0 2016-05-11
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DATE: May 11, 2016
AUTHOR: Ver 1: Rob Trew (@ComplexPoint)
Ver 2: JMichaelTX
CHANGE LOG:
β’ Ver 2 2016-05-11 17:49 CT JMichaelTX
β’ Fixed issue where image only on clipboard caused script to fail.
β’ Added return if no text was found, to indicate error.
β’ Added clarity to code by decompressing some statements, and adding comments
REF:
β’ Copy to New TXT File? - general - Keyboard Maestro Discourse
β’ https://forum.keyboardmaestro.com/t/copy-to-new-txt-file/3689/2
REQUIREMENTS:
β’ Use first line as file name
β’ Save as TEXT file.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
function run() {
var app = Application.currentApplication()
app.includeStandardAdditions = true
//--- READ CLIPBOARD ---
var clipStr = app.theClipboard();
//--- IF TEXT WAS FOUND ON CLIPBOARD, THEN PROCESS ---
if (typeof clipStr === "string") {
//--- SPLIT TEXT BY LINE INTO ARRAY ---
var clipArray = clipStr.split(/[\n\r]/)
var numLines = clipArray.length
//--- GET FIRST LINE TO USE AS FILE NAME ---
var fileNameStr = numLines > 1 ? clipArray[0] : undefined
//--- JOIN REMAINDER OF ELEMENTS INTO STRING FOR FILE CONTENTS ---
var fileContentsStr = fileNameStr ? clipArray.slice(1).join('\n') : undefined;
//--- ASK USER FOR FILE NAME/PATH ---
if (fileContentsStr) {
//--- ACTIVE CURRENT APP TO DISPLAY DIALOG ---
app.activate
var outputFilePathStr = (app.chooseFileName({
withPrompt: 'Save As',
defaultName: fileNameStr + '.txt'
})
).toString();
//--- OUTPUT TO SELECTED FILE ---
writeFile(outputFilePathStr, fileContentsStr);
return (outputFilePathStr);
}
} // END if (clipStr)
else {
return ("ERROR - No text was found on clipboard")
}
//~~~~~~~~~ END OF MAIN SCRIPT ~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function writeFile(pOutputFilePathStr, pFileContentsStr) {
// writeFile :: FilePath -> String -> IO ()
$.NSString.alloc.initWithUTF8String(pFileContentsStr)
.writeToFileAtomicallyEncodingError(
pOutputFilePathStr, true,
$.NSUTF8StringEncoding, null
);
} // END function writeFile()
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // END function run()
(strClip && (typeof strClip === 'string'))
to screen out empty strings as well as imagesfunction run() {
// writeFile :: FilePath -> String -> IO ()
function writeFile(strPath, strText) {
$.NSString.alloc.initWithUTF8String(strText)
.writeToFileAtomicallyEncodingError(
strPath, true,
$.NSUTF8StringEncoding, null
);
}
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a),
strClip = sa.theClipboard();
if (strClip && (typeof strClip === 'string')) {
var lines = strClip.split(/[\n\r]/),
strHead = lines.length > 1 ? lines[0] : undefined,
strTail = strHead ? lines.slice(1)
.join('\n') : undefined;
return strTail ? (
writeFile(
(sa.activate,
sa.chooseFileName({
withPrompt: 'Save As',
defaultName: strHead + '.txt'
})
).toString(),
strTail
),
strTail
) : undefined;
}
}
PS if you are in search of any further distraction, you can experiment with applying Object.keys() to the clipboard object.
String contents will yield a simple series of numeric indices (character positions), while graphic contents will yield the keys to specific serialisations:
If "distraction" is learning, then I guess I'm always in search of more.
Thanks. That is interesting and helpful.
One note: this snippet from your script gives me an error that
"clip is not a function
":
var clip = sa.theClipboard
var strType = typeof clip()
It works fine if I just use "clip
"
Do you have a function named clip()
?
There was a slight shift in object referencing between 10.10 and 10.11
If you look at it in the 10.11 debugger, you will see that sa.theClipboard
(and hence clip
, the variable name bound to it) is an uncalled function.
(I donβt have a system running 10.10 here)
Source:
function run() {
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a);
var clip = sa.theClipboard,
strType = typeof clip();
if (clip() && strType !== 'string') {
var lstTypes = Object.keys(clip());
if (lstTypes.indexOf('PDF ') !== -1) {
return clip()['PDF ']
}
}
}
Thanks, but I'm now running 10.11:
Script Editor 2.8.1 (183.1) on OSX 10.11.4
The Safari Debugger shows clip
as an object
, NOT as a function
:
I figured out why we are seeing different results:
var clip = sa.theClipboard,
var clip = sa.theClipboard(),
So, you are setting the variable clip
to the FUNCTION theClipboard
,
whereas I am setting it to the VALUE of theClipboard
Using your script, but:
// ##CHG: Added () to theClipboard
// ##CHG: Removed () from "clip()"