Copy link to clipboard
Copied
Hi everyone, I started scripting for after effects 3 days ago and I came across tons of disappointments.
I think the $.write() and $.writeln methods don't do enough. (I'm used to console.log in web development). My biggest problem right now is that if you log an object, it'll only output [object Object].
I'm trying to print the full object with all its properties and sub/sub_sub... objects.
Question #1: Am I missing something? Is there an easy way to do this?
Question #2: if #1's answer is no, do you guys have an idea why I can't print the properties of the sub-objects using this method. (I recommend you guys try it)
var testObj = {
property_string : "string",
property_int : 999,
property_arr : ["test",1],
property_obj : {
sub_property_string : "hello"
}
};
objLog(testObj);
function objLog(object){
var log = object.toString() + " {";
var line;
for (var property in object) {
line = "\r" + tabsString(1) + property.toString() + " : " + object[property].toString();
log += line;
objLoop(property,object);
}
log += "\r }"
$.writeln(log);
/*
* Creates Tabs for the formatting
* */
function tabsString(amount){
var string = "";
for(var i = 0; i < amount; i++){
string += " ";
}
return string;
};
/*
* Should be an infinite-able recursive loop to always print the properties of any object at any level.
* */
function objLoop(prop,obj){
var subObject = obj[prop].toString();
if(subObject.indexOf("[object Object]") !== -1){
line = "{\r"
log += line;
for (var subProperty in subObject){
/*
* Can't get anything to work in this part.
* Any object contained in an object won't print it's properties
* */
line = tabsString(2) + "*Should be printing propeties here*"
log += line;
line = subProperty + "\r" + subObject.toString() + " \r";
line = "\r" + tabsString(2) + subProperty.toString() + " : " + subObject[subProperty].toString();
log += line;
}
log += "\r" + tabsString(2) + "}"
}
};
}
Currently, this will output the following in the javascript console:
[object Object] {
property_string : string
property_int : 999
property_arr : test,1
property_obj : [object Object]{
*Should be printing propeties here*
toJSON :
function () {
return this.valueOf();
}
}
}
I'm really confused on why it prints
"toJson : function(){ return this.valueOf(); }"
instead of
sub_property_string : hello
Anyway, I hope you guys have better ways than me to print lots of information in the console because I really feel like a newbie.
Thanks a lot!
Hey buddy, welcome to the club!
I am not a trained programmer, but I've been doing lots of scripting for AE so I will try to explain as best as I can.
1. No, you cannot print/log AE objects. Why? Really hard to tell.AE has some logic to it, that no one knows about. Some time ago tried writing some function that would parse all objects properties and print it's values, but at some point it's still failing. You can test it out AE Script: AE Object to Source — Bitbucket just pass an AE object to ob
...Copy link to clipboard
Copied
Hey buddy, welcome to the club!
I am not a trained programmer, but I've been doing lots of scripting for AE so I will try to explain as best as I can.
1. No, you cannot print/log AE objects. Why? Really hard to tell.AE has some logic to it, that no one knows about. Some time ago tried writing some function that would parse all objects properties and print it's values, but at some point it's still failing. You can test it out AE Script: AE Object to Source — Bitbucket just pass an AE object to objectToSource() function and it should print some stuff (by default resulting file is saved to desktop).
2. If you want to print/log regular objects, do not reinvent the wheel. Use JSON. However, since ESTK is operating on ECMA 3 (duhhhh) it doesn't natively support JSON. To fix this you have to download/clone this repository GitHub - douglascrockford/JSON-js: JSON in JavaScript and then do #include "path/to/json2.js" in your script file. Next time you know it, you can to do alert(JSON.stringify(myObject, false, 4));
-------------
I just re-read your question and I see my #1 reply probably makes no sense. With #1 I was implying to if you want to print LayerObject or CompObject or any other AE object, it's probably not possible
-------------
PS. Sorry I didn't try your function, maybe I should have, but there JSON.stringify for that.
Copy link to clipboard
Copied
Thanks a lot for your reply. It's very appreciated. I'll test it out and tag your answer as "correct answer" if this all works out.
Oh and thanks for talking about #include it's a concept I didn't know was possible in scripting.
Copy link to clipboard
Copied
Oh yeah, #include "" is great. However, if JShint or ESLint or any other linting process is giving you errors with #include "" preprocessor, feel free to use //@include "" instead. Linting process will treat it as a comment, but ESTK will still be able to work with it.
Copy link to clipboard
Copied
Hey, I had the time to test this all out and I'm very happy with the outcome. If I create an object myself it prints exactly what I need. So that answers the question. There is still a little issue that I'm trying to fix to really be able to use an awesome object workflow.
Here's an exemple that won't print using Json2.
var ui = {};
ui.window = this;
ui.content = {};
function addUi(name,type,pos,content,params){
var position = !!pos ? [pos[0],pos[1], pos[0]+pos[2], pos[1]+pos[3]] : undefined;
if(!!params){ ui.content[name] = ui.window.add(type, position, content, params) }
else{ ui.content[name] = ui.window.add(type, position, content) }
};
addUi("btnOk", "button", [10, 10, 80, 40], "Ok");
addUi("btnCancel", "button", [100, 10, 80, 40], "Cancel");
addUi("textArea", "edittext", [10,100,200,150], "text", {multiline:true});
the object would look like this:
ui{
window : this,
content: {
btnOk: [button object],
btnCancel: [button object],
textArea: [edittext object]
}
}
So basically I add all the UI elements inside an object named ui. That way they are well organised and if I find a way to print this object, I'll easily be able to find all the objects I need to modify my script UI.
Any idea how I could be able to print that? if not, I might just go all-in and try to modify the json2 script to display something.
Copy link to clipboard
Copied
I know I am very late to the party, but this answer may be useful for someone.
/**
* Prints an entire object (and sub objects).
* @param {Object} obj Object to be printed in the console (keys and values).
* @returns Void.
*/
function printEntireObject(obj) {
for (var key in obj) {
if (typeof obj[key] === "object") {
$.writeln(key + ": ");
printEntireObject(obj[key]); // Recursion
continue;
}
if (typeof obj[key] === "string") {
$.writeln(key + ': "' + obj[key] + '"');
continue;
}
$.writeln(key + ": " + obj[key]);
}
return;
}
var myObject = {
day: "monday",
hour: 12,
greet: function () {
$.writeln("Hello");
},
subObj: {
subObjString: "This is the sub object string property",
subObjMethod: function () {
$.writeln("Hello from sub object!");
},
},
};
printEntireObject(myObject);
day: "monday"
hour: 12
greet:
function () {
$.writeln("Hello");
}
subObj:
subObjString: "This is the sub object string property"
subObjMethod:
function () {
$.writeln("Hello from sub object!");
}
Copy link to clipboard
Copied
I worked on the code of my previous answer a little bit more. I do not know why, but I cannot find a way to edit the answer... Anyway, below is my new code:
First the polyfill:
/**
* @SEE {@link https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#polyfill Polyfill}
*/
// This polyfill will be necessary below.
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
};
}
Then, the function to create the string:
/**
* Creates a string from the entire object (and sub objects).
* @Param {Object} obj Object to be printed in the console (keys and values).
* @Returns {String} The formatted string of the object.
*/
function objectToString(obj, subObjTab) {
var log;
if (Object.prototype.toString.call(obj) === "[object Array]") {
log = "Array " + " [\r";
} else {
log = obj.toString() + " {\r";
}
var line = "";
tab = " ";
if (!subObjTab) {
subObjTab = "";
}
var value;
for (var key in obj) {
if (typeof obj[key] === "object") {
subObjTab = " ";
line = tab + key + ": ";
line = line + objectToString(obj[key], subObjTab); // Recursion
subObjTab = "";
log += line;
continue;
}
if (typeof obj[key] === "string") {
line = subObjTab + tab + key + ': "' + obj[key] + '"\r';
log += line;
continue;
}
if (typeof obj[key] === "function") {
value = obj[key].toString().trim();
line = subObjTab + tab + key + ": " + value + "\r" + subObjTab;
log += line;
continue;
}
line = subObjTab + tab + key + ": " + obj[key] + "\r";
log += line;
}
if (Object.prototype.toString.call(obj) === "[object Array]") {
log += subObjTab + "]\r";
} else {
log += "}\r";
}
return log;
}
Then, the code that uses the function:
var myObject = {
day: "monday",
hour: 12,
propertyBoolean: true,
property_arr: ["test", 1],
greet: function () {
$.writeln("Hello");
},
subObj: {
subObjString: "This is the sub object string property",
subObjMethod: function () {
$.writeln("Hello from sub object!");
},
},
};
$.writeln(objectToString(myObject));
And the expected result:
[object Object] {
day: "monday"
hour: 12
propertyBoolean: true
property_arr: Array [
0: "test"
1: 1
]
greet: function () {
$.writeln("Hello");
}
subObj: [object Object] {
subObjString: "This is the sub object string property"
subObjMethod: function () {
$.writeln("Hello from sub object!");
}
}
}
I hope this helps someone in the future. Peace.
Copy link to clipboard
Copied
hi, thank you so much!
in order to print objects with 'parent' key like: app.selection or app.document[0], i added to the line:
for (var key in obj) {
if (typeof obj[key] === "object" && key!=="parent") { // added ' && key!=="parent' ----> in order to Prevent Infinite loop
...
}
I got 2 errors in the function or those lines:
so, I wrapped it with try and catch.
your code with my additions:
/**
* @SEE {@link https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#polyfill Polyfill}
*/
// This polyfill will be necessary below.
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
};
}
/**
* Creates a string from the entire object (and sub objects).
* @Param {Object} obj Object to be printed in the console (keys and values).
* @Returns {String} The formatted string of the object.
*/
function objectToString(obj, subObjTab) {
var log;
if (Object.prototype.toString.call(obj) === "[object Array]") {
log = "Array " + " [\r";
} else {
if(obj){ // added--> to prevent null Error
log = obj.toString() + " {\r";
}
}
var line = "";
tab = " ";
if (!subObjTab) {
subObjTab = "";
}
var value;
try{
if(obj){ //added
for (var key in obj) {
if (typeof obj[key] === "object" && key!=="parent") { // added ' && key!=="parent' ----> in order to Prevent Infinite loop
subObjTab = " ";
line = tab + key + ": ";
line = line + objectToString(obj[key], subObjTab); // Recursion
subObjTab = "";
log += line;
continue;
}
if (typeof obj[key] === "string") {
line = subObjTab + tab + key + ': "' + obj[key] + '"\r';
log += line;
continue;
}
if (typeof obj[key] === "function") {
value = obj[key].toString().trim();
line = subObjTab + tab + key + ": " + value + "\r" + subObjTab;
log += line;
continue;
}
line = subObjTab + tab + key + ": " + obj[key] + "\r";
log += line;
}
}
}
catch(e)
{
$.writeln(e)
}
if (Object.prototype.toString.call(obj) === "[object Array]") {
log += subObjTab + "]\r";
} else {
log += "}\r";
}
return log;
}
Copy link to clipboard
Copied
i actually arrange it a little bit and used also '@Tomas Sinkunas' code for saving text as file
from the question: https://community.adobe.com/t5/after-effects-discussions/create-a-txt-file-in-extendscript/td-p/9645...
the part of printing the function inside of an object is still needs a little work.
example of saving the object in javascript file:
the object is: app.selection:
.
the selected path:
the 2 files i edited is attached.
in order to use them you'll need to change their suffix from 'txt' to 'jsx'
thank you all for your help- and especially@leonardpin and @Tomas Sinkunas.
i hope it's helping someone.
Copy link to clipboard
Copied
It is good that the code helped someone.
I changed my code since I posted it here. Only used it a few times...
I did not totally comprehend your usage of it.
I updated mine using your suggestions. I noted a strange thing though:
$.writeln(typeof app.selection);
Running in Illustrator, the engine main could not execute the script. I received an error. Changing the engine to transient, I could execute it. Changing it back to main, I received no error.
This is my code with your additions:
/**
* Creates a string from the entire object (and sub objects).
*
* @SEE {@link https://community.adobe.com/t5/after-effects-discussions/console-printing-objects-with-more-details/m-p/12611158#messageView2_1_8e4f5f8fbc23d1_5 m0riya's answer}
*
* @Param {Object} obj Object to be printed in the console (keys and values).
* @Param {number} [level=1] Sublevel number. Optional. Default 1.
* @Returns {String} The formatted string of the object.
*/
function objectToString(obj, level) {
var level = level || 1;
var log;
var line; // Represents each line inside object.
var trimmedStr; // For functions.
var tabInClosingFunction; // For functions.
function createTab() {
return " "; // 4 spaces
}
// The end of the object.
function closeObj(level) {
level--;
var tab = "";
// Creates the tabulation.
for (var i = 0; i < level; i++) {
tab += createTab();
}
if (Object.prototype.toString.call(obj) === "[object Array]") {
log += tab + "]\r";
} else {
log += tab + "}\r";
}
}
// The beginning of the object.
if (Object.prototype.toString.call(obj) === "[object Array]") {
log = "Array(" + obj.length + ") [\r";
} else {
// m0riya wrapped the line below with an if(obj) block to prevent null Error.
if (obj) {
// log = obj.toString() + " {\r";
log = obj.reflect.name + " {\r";
}
}
// Added by m0riya.
try {
// Added by m0riya.
if (obj) {
// Loop through all keys and values.
for (var key in obj) {
/* Clears the line and tabs (for closing } of functions) for the
next loop iteration. */
line = "";
tabInClosingFunction = "";
// Creates the tabulation.
for (var i = 0; i < level; i++) {
line += createTab();
}
// In case the value is a string.
if (typeof obj[key] === "string") {
line += key + ': "' + obj[key] + '"\n';
log += line;
continue;
/* In case the value is an object (objects or arrays).
&& key !== "parent" by m0riya */
} else if (typeof obj[key] === "object" && key !== "parent") {
// Do not print inherited properties.
if (obj.hasOwnProperty(key)) {
line +=
key + ": " + objectToString(obj[key], level + 1); // Recursion
log += line;
continue;
}
// In case the value is a function.
} else if (typeof obj[key] === "function") {
if (obj.hasOwnProperty(key)) {
trimmedStr = obj[key].toString().trim();
// Creates the tabulation at the end of the function.
for (var j = 0; j < level; j++) {
tabInClosingFunction += createTab();
}
trimmedStr = trimmedStr.replace(
/\t+}$/,
tabInClosingFunction + "}"
);
line += key + ": " + trimmedStr + "\n";
log += line;
continue;
}
// In any other case (boolean or number).
} else {
line += key + ": " + obj[key] + "\n";
log += line;
}
}
}
} catch (e) {
$.writeln(e);
}
closeObj(level);
return log;
}
Using this function with
app.selection
I receive the errors you mentioned too.
After reading this reference:
I tried to apply it with
var topObject = app.activeDocument.selection[0];
$.writeln(topObject.typename); // PathItem
Then, I searched for
There, I found the list of methods.
I saved the files you shared and tried them a little bit.
But, as I said at the begining, I did not fully comprehend what you are trying to do.
Copy link to clipboard
Copied
I scrolled through the results and see alternative solutions proposed, but it doesn't seem like anybody helped actually identify why the provided code wasn't working?
The original code mostly works, it was just a small issue with the assignment of subObject and updating the if condition. I tested the original code with this fix and it prints recursively as the OP desired
function objLoop(prop,obj){
// :cross_mark: SOURCE OF PROBLEM: subObject is a STRING, not an Object
var subObject = obj[prop].toString();
// :white_heavy_check_mark: CORRECTION:
// var subObject = obj[prop]
if(subObject.indexOf("[object Object]") !== -1){
// :white_heavy_check_mark: CORRECTION:
// if (subObject) {
line = "{\r"
log += line;
// :warning: Because you created subObject as a string, you are attempting to iterate a string rather than an object
for (var subProperty in subObject){
line = tabsString(2) + "*Should be printing propeties here*"
log += line;
line = subProperty + "\r" + subObject.toString() + " \r";
line = "\r" + tabsString(2) + subProperty.toString() + " : " + subObject[subProperty].toString();
log += line;
}
log += "\r" + tabsString(2) + "}"
}
};