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()"