Photoshop Script to rename layers turns them all visible - javascript

I have a little JS script I wrote to rename layer names so that they're all unique. It just recursively loops through all the layers in a Photoshop document you have open and will make a list of all the names. If it finds that a name is already in that list, it will append a number to the layer name until it finds a unique name.
I have that working, however one odd behaviour is that in the course of this script running it turns every layer visible. No part of my script is intended to affect visibility. And for testing, I added lines to turn off visibility on every layer after they were checked/renamed, but that doesn't change anything. Every layer is still visible at the end.
For reference, I'm using Photoshop CS2, and the scripts are run through File > Scripts > Rename Duplicate Layers
Here's the two lines I used for disabling visibility (that didn't work):
layerSet.artLayers[i].visibility = false;
and
layerSet.layerSets[i].visibility = false;
And here's the full body of the regular script, that doesn't attempt to change visibility at all.
function main()
{
if (app.documents.length <= 0)
{
return;
}
var doc = app.activeDocument;
layerNames = [];
renameLayerNames(doc, layerNames);
alert("Script complete.");
}
// Recursively iterate over layers to rename them
function renameLayerNames(layerSet, layerNames)
{
var name = '';
for (var i = 0; i < layerSet.artLayers.length; i++)
{
name = layerSet.artLayers[i].name;
name = uniqueName(name, layerNames);
layerSet.artLayers[i].name = name;
}
for (var i = 0; i < layerSet.layerSets.length; i++)
{
name = layerSet.layerSets[i].name;
name = uniqueName(name, layerNames);
layerSet.layerSets[i].name = name;
// Recurse
renameLayerNames(layerSet.layerSets[i], layerNames);
}
}
// Ensure name is unique, or add an incrementing number to it.
function uniqueName(name, layerNames)
{
dupe = 0;
original = name;
while (contains(layerNames, name)){
dupe++;
name = original + ' ' + dupe.toString();
}
layerNames.push(name);
return name;
}
// Check if array contains object
function contains(array, object)
{
for (var i = 0; i < array.length; i++){
if (array[i] == object){
return true
}
}
return false
}

I believe the property is visible not visibility i.e.
layerSet.artLayers[i].visible = false;
layerSet.layerSets[i].visible = false;

Related

How can I change the type of a Javascript object?

I am building a webpage-based text adventure game, like Hitchhiker's Guide to the Galaxy or the Zork series. I save objects in this game, like locations or player data, in localStorage, so that the player can continue their progress.
I am using CircularJSON to stringify my circular references within these objects in order to save them.
However, when these objects are parsed, they are of the default Object type.
This is a problem because the functions in types like Area:
var Area = function (tempdescription, tempinitialDesc, tempobjArr) {
this.isExplored = false;
this.description = tempdescription;
this.objArr = tempobjArr;
this.paths = [];
this.initialDesc = tempinitialDesc;
};
Area.prototype.getIndex = function (tempstr) {
if(thePlayer.playerLocation.objArr.length > 0) {
for(var i = 0; i < thePlayer.playerLocation.objArr.length; i++) {
if(thePlayer.playerLocation.objArr[i].name.indexOf(tempstr) != -1) {
return i;
}
}
}
return -1;
};
or Player:
var Player = function (defaultLocation) {
this.inv = []; // an array of game objects
this.playerLocation = defaultLocation; // the player's current location
this.moveCount = 0;
this.score = 0;
};
Player.prototype.getIndex = function (tempstr) {
if(thePlayer.inv.length > 0) {
for(var i = 0; i < thePlayer.inv.length; i++) {
if(thePlayer.inv[i].name.indexOf(tempstr) != -1) {
return i;
}
}
}
return -1;
};
which were created by me, need to be present in each object order for my other code to work.
I need to change the type of several objects in a simple way, if it exists, because when I save these objects there will probably be several dozen of them by the time the game is done.
Is there any way to change the type of a Javascript object?
Option #1 (using ES2015): const newObject = Object.setPrototypeOf(oldObject, Player.prototype)
This is pretty slow though, check the documentation:
Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine.
That's an alternative to...
Option #2 (deprecated): oldObject.__proto__ = Player.prototype
Option #3: const newObject = new Person(/* Use the properties of oldObject */)

why is the console giving me an exception, when startsWith is defined?

ok i am trying to run the following code and i am getting back an exception that startsWith method is undefined. shouldnt this automatically be defined within javascript?
var mailArchive = retrieveMails();
var livingCats = {"Spot": true};
for (var mail = 0; mail < mailArchive.length; mail++) {
var paragraphs = mailArchive[mail].split("\n");
for (var paragraph = 0;
paragraph < paragraphs.length;
paragraph++) {
if (startsWith(paragraphs[paragraph], "born")) {
var names = catNames(paragraphs[paragraph]);
for (var name = 0; name < names.length; name++)
livingCats[names[name]] = true;
}
else if (startsWith(paragraphs[paragraph], "died")) {
var names = catNames(paragraphs[paragraph]);
for (var name = 0; name < names.length; name++)
delete livingCats[names[name]];
}
}
}
show(livingCats);
Some versions of javascript (an experimental method in Firefox) have a .startsWith() string method that you would use like:
if (paragraphs[paragraph].startsWith("died"))
Regular javascript does not have a global function startsWith() like you are trying to use so unless you're loading some library that has that function in it, that's why the browser is telling you it's undefined.
A recommended solution that would work in any version of JS, would be:
if (paragraphs[paragraph].indexOf("died") === 0)
instead.
Or, you could define the global function you're using:
function startsWith(src, find) {
return src.substr(0, find.length) == find;
}
It's a method of String, not a global function.
It should be:
paragraphs[paragraph].startsWith("born")

Accessing the value of an observable inside a plain JS array in Knockout JS?

Background
I have a plain JS array, initially empty. I later populate it with values. The values sent to it are numbers that are Knockout observables. Later, I want to compare those values to values in another, knockout observable array. My problem is that whenever I pass the index of the current item in my array loop, and pass that index value (a number!), the array returns a function. To get an idea, look at the JS that follows.
Note that my project and actual script is viewable on JSBin. Further, to view the problem in the console, you have to add assignments, then press 'sort'.
JSBin: http://jsbin.com/fehoq/177/edit]1
JS
//example script that follows actual script
var _this = this;
//initialize my array
this. lowest = [];
// I want to compare values in lowest to values in this array
this.scores = ko.observableArray();
// method that does comparison
this.myMethod = function(){
// initialize my helper, k
var k;
...
// loop through one array
ko.utils.arrayForEach(_this.scores(), function (score) {
// make sure my value is a number...
if (!isNaN(parseFloat(score()))) {
// this is important, I need to current index for comparison
k = _this.scores.indexOf(score);
console.log(k);
// this is where things break - it prints a function, not a value!
console.log(_this.lowest[k]);
// useless check, the value is a function, so they're always different
if (score()!=_this.lowest[k]){
// do stuff
}
}
}
}
Update
Putting the method I'm using, maybe someone will notice something I missed given that my syntax is correct(?).
this.mean = (function(scores,i) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
console.log(i);
console.log(_this.lowest[i]);
if (score() != _this.lowest[i]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
}
Update 2
Just in case someone else wanders over here since my problem isn't solve still. The following code is how I set the value of lowest:
this.dropLowestScores = function() {
ko.utils.arrayForEach(_this.students(), function(student){
var comparator = function(a,b){
if(a()<b()){
return 1;
} else if(a() > b()){
return -1;
} else {
return 0;
}
};
var tmp = student.scores().slice(0);
tmp.sort(comparator);
student.lowest = ko.observableArray(tmp.splice((tmp.length-2),tmp.length-1));
});
};
Outstanding Questions, 5/9/2014
Jeremy's script runs but without the desired effects. For example, console.log(_this.lowest[k]) prints undefined, just as mine does. Further, the matched scores aren't skipped, which they should be.
Jeremy's script specifies lowest as a ko.observable. My script also now has lowest as a ko.observable, but why shouldn't a plain JS array work for this? I only need lowest to update when the button it's bound to is clicked, and those bindings are already taken care of.
That is how observables work in Knockout.
When you create one, you are creating a function.
var myObservable1 = ko.observable(); // Create it.
var myObservable2 = ko.observable("Hola!"); // Create it with a value.
console.log(typeof myObservable2); // It is indeed a function
console.log(typeof myObservable2()); // That returns a string
console.log(myObservable2()); // And get the value.
EDIT BASED ON QUESTION IN COMMENTS
var koTest = ko.observableArray();
koTest.push("Line0");
koTest.push("Line1");
koTest.push("Line2");
koTest.push("Line3");
koTest.push("Line4");
var jsTest = [];
jsTest.push("Line0");
jsTest.push("Line1");
jsTest.push("Line2");
jsTest.push("Line3");
jsTest.push("Line4");
alert(koTest()[2]);
alert(jsTest[2]);
alert(koTest()[2] === jsTest[2]);
Test Code
I went ahead and make a runnable test of your code and everything was working just fine for me. I had to make some assumptions about the contents of _this -- in particular the declaration of lowest, which I made an observableArray based on how you were accessing it.
Anyways, this code runs:
var _this = {
scores: ko.observableArray(),
lowest: ko.observableArray()
};
var mean = (function(scores) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
k = _this.scores.indexOf(score);
console.log(k);
console.log(_this.lowest[k]);
if (score() != _this.lowest[k]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
for (var i = 0; i < 10; i++) {
_this.scores.push(ko.observable(i));
}
var m = mean();
alert(m);

how to turn this to into a tree?

I was doing a challenge of building a tree from all html elements. And I am 90% done, but I got stuck...
How do I change this string into a tree?:
mystring= "red1/(none)-red2/red1-blue1/red2-blue2/red2-blue3/red2-red3/red1-red4/red3-red5/red4-red6/red5-blue4/red6";
After splitting them by "-" we will have:
10 groups of -> (parameter1)/(parameter2)
The first parameter it is the object,
The second parameter is the 'in-what-it-will-be-contained'
I have no idea how to move every 'parameter1' inside 'parameter2'. (note: sometimes the parameter1 will be the parameter2 of a parameter1)
Visual example of what I mean with a parameter is inside another parameter: (this example uses exactly the string above)
Probably we should use arrays?, idk... I am totally lost :sadface:
I think this is a little more concise and straight forward. It uses an object as a dictionary to lookup the parent, rather than a function that has to recursively iterate the tree to find the parent. That recursive function is expensive. An object lookup is quick.
First, for convenience, I'd define an object type:
function TreeNode(name) {
this.Name = name;
this.Children = [];
}
Then I'd add a method to do the work. This parses your tree string:
TreeNode.ParseTree = function (treeString) {
var root = new TreeNode("");
var nodes = {};
var pairs = treeString.split("-");
pairs.forEach(function(pair) {
var parts = pair.split("/");
var parentName = parts[1];
var childName = parts[0];
var node;
if (parentName == "(none)") {
node = root;
root.Name = childName;
}
else {
node = new TreeNode(childName);
nodes[parentName].Children.push(node);
}
nodes[childName] = node;
});
return root;
};
That's it! Now, to get visual representations of your tree, you can add some prototype methods to TreeNode. First, override .toString():
TreeNode.prototype.toString = function(indent) {
indent = indent || "";
var strings = [indent + this.Name];
this.Children.forEach(function(child) {
strings.push(child.toString(indent + " "));
});
return strings.join("\n");
};
Then, add a .Render() method to display the tree within a web page:
TreeNode.prototype.Render = function(container) {
var nodeEl = container.appendChild(document.createElement("div"));
nodeEl.className = "treeNode";
var nameEl = nodeEl.appendChild(document.createElement("div"));
nameEl.className = "treeNodeName";
nameEl.appendChild(document.createTextNode(this.Name));
var childrenEl = nodeEl.appendChild(document.createElement("div"));
childrenEl.className = "treeNodeChildren";
this.Children.forEach(function(child) {
child.Render(childrenEl);
});
return nodeEl;
};
Here it is in action: http://jsfiddle.net/gilly3/wwFBx/
Edit: I didn't notice the jQuery tag in your post, here's a render method that's all jQuery, and produces simpler HTML which you seem to imply is what you want:
TreeNode.prototype.Render = function(container) {
var el = $("<div>").appendTo(container).text(this.Name);
$.each(this.Children, function() {
this.Render(el);
});
return el;
};
This JSFiddle uses jQuery, even replacing Array.forEach with $.each: http://jsfiddle.net/wwFBx/1/
As an alternative, you might consider just serializing your tree as JSON. Eg:
"{\"Name\":\"red1\",\"Children\":[{\"Name\":\"red2\",\"Children\":[{\"Name\":\"blue1\",\"Children\":[]},{\"Name\":\"blue2\",\"Children\":[]},{\"Name\":\"blue3\",\"Children\":[]}]},{\"Name\":\"red3\",\"Children\":[{\"Name\":\"red4\",\"Children\":[{\"Name\":\"red5\",\"Children\":[{\"Name\":\"red6\",\"Children\":[{\"Name\":\"blue4\",\"Children\":[]}]}]}]}]}]}"
or maybe:
"{\"red1\":{\"red2\":{\"blue1\":{},\"blue2\":{},\"blue3\":{}},\"red4\":{\"red5\":{\"red6\":{\"blue4\":{}}}}}}"
Parse the string via JSON.parse().
Disclaimer: I've referenced Array.forEach() and JSON.parse() which are built-in to modern browsers but are not supported by older browsers. To enable these functions in older browsers, see this documentation on Array.forEach() and this shim for JSON.parse().
Here's about how I would do it, using an array of "unplaced" elements and looping through it until they're all placed:
var str = "red1/(none)-red2/red1-blue1/red2-blue2/red2-blue3/red2-red3/red1-red4/red3-red5/red4-red6/red5-blue4/red6";
var unplaced = [];
var tree = null;
var elements = str.split(/[\/\-]/);
function findNodeByName(nodeName, context) {
if(context.name === nodeName) return context;
for(var i = 0; i < context.children.length; i++) {
var subSearch = findNodeByName(nodeName, context.children[i]);
if(subSearch) return subSearch;
}
return null;
}
var element, node, parent, thisElement, i;
for(i = 0; node = elements[i]; i += 2) {
node = elements[i];
parent = elements[i + 1];
thisElement = {name: node, children: []};
if(!tree && parent === '(none)') {
tree = thisElement;
} else if(tree) {
var parentNode = findNodeByName(parent, tree);
if(parentNode) {
parentNode.children.push(thisElement);
} else {
unplaced.push(thisElement);
}
}
}
var oldLength;
while(unplaced.length) {
oldLength = unplaced.length;
for(i = 0; element = unplaced[i]; i++) {
var parentNode = findNodeByName(parent, tree);
if(parentNode) {
parentNode.children.push(element);
unplaced.splice(i, 1);
i--;
}
}
if(oldLength === unplaced.length) {
throw new SyntaxError("The string is not a valid tree.");
}
}
// The result is contained in "tree".
You can see the result at: http://jsfiddle.net/minitech/tJSpN/
One with a function: http://jsfiddle.net/minitech/tJSpN/1/
And one with more error-checking: http://jsfiddle.net/minitech/tJSpN/2/
Actually, I found a simpler/shorter/neater way using the JQuery function AppendTo()
We just need to:
Split the parameters...
Create one div for each (parameter1)
Use a loop to move every (parameter1) inside (parameter2) using the
AWESOME AppendTo() function that JQuery offers
The best thing is that they are actually inside them, so you can easily put a Hide/Show effect to make a cool effect
You may try to create tree nodes of the form :
node = {
str:"red1",
subBranches : new Array()
}
Once you have that, you may add the sub-branches iterating through the array, adding such nodes for each found correct couple, and removing the couples already placed in rootNode.subBranches. Then you recursively do the same for every sub-branche.

Update happens only on the last row, instead of first

function createTextFields(obj) {
for (var i = 0; i < obj.length; i++) {
var dataDump = {};
for (var key in obj[i]) {
var textField = Ti.UI.createTextField(pm.combine($$.labelBrown, {
left: 200,
height:35,
value:obj[i][key],
width:550,
keyboardType:Ti.UI.KEYBOARD_NUMBER_PAD,
layout:'horizontal',
backgroundColor:'transparent',
id:i
}));
dataDump[key] = textField.value;
var callback = function (vbKey) {
return function (e) {
dataDump[vbKey] = e.source.value;
};
}(key);
}
globalData.push(dataDump);
}
}
I am using the simlar code for Adding the data and it works fine. I posted the problem yesterday and it got resolved...
Last Object is always getting updated?
Now when i go to edit page, it shows me four text fields or number of text fields added... now when i edit something and click on save... the value get's updated on the fourth or the last TextFields Object...
Don't define functions inside loops. Computationally expensive and leads to problems, like this one. Here's a fix that should solve it:
function createTextFields(obj) {
var callback = function (vbKey, localDump) {
return function (e) {
localDump[vbKey] = e.source.value;
};
}
var i;
var max = obj.length;
for (i = 0; i < max; i++) {
var dataDump = {};
for (var key in obj[i]) {
dataDump[key] = textField.value;
var callBackInstance = function(keyn, dataDump);
}
globalData.push(dataDump);
}
}
JavaScript does not have block level scope, so your variables dataDump and callback, though "declared" inside for-loops actually belong to the function. As in, you're saving a value to dataDump, then you're overwriting it, each time you go through the loop. Which is why finally only the code that operated on the last value remains.
Take a look at What is the scope of variables in JavaScript? too.

Categories

Resources