I have an array of objects, each of which is assigned an ID when it is first created. I give the user the ability to visually reorder the objects, which changes their position in the array. They then have the option to save that order using a flash sharedObject or "cookie" and then later, if they reopen the flash file, I want them to be able to hit a button to restore that order. I'm just not sure what the syntax would be to set the object's index within the array. Here's my code:
VARIABLES:
var project_settings = SharedObject.getLocal("settings"); //saves all project settings for the next time the file is opened
var project_order:Array = []; //saves project order for the next time the file is opened
var project_display:Array = []; //saves whether each project should be displayed or hidden for the next time the file is opened
SAVE CODE:
function saveOrder(){
for (var i=0;i<project_array.length;i++){
project_order[i] = project_array[i].id;
project_display[i] = project_array[i].projectThumb.thumbActive;
}
project_settings.data.order = project_order;
project_settings.data.active = project_display;
//trace (project_settings.data.active[1]);
project_settings.flush(); //saves most recent "cookie"
}
RESTORE CODE:
function loadOrder(){
for (var i=0;i<project_array.length;i++){
/* NEED THE CODE THAT GOES HERE. BASICALLY, PROJECT_ARRAY[i] SHOULD BE THE ITEM WITH AN ID EQUAL TO PROJECT_SETTINGS.DATA.ORDER[i] */
}
}
Something like this should work:
function loadOrder()
{
var dict = new Dictionary();
for (var i = 0; i < project_array.length; i++)
dict[project_array[i].id] = project_array[i];
project_array = [];
for (var i = 0; i < project_settings.data.order.length; i++)
project_array[i] = dict[project_settings.data.order[i]];
}
Just load in your array and sort on the ID. Something like this should work:
private function _loadArray():void
{
// fill in your array
project_array.sort( this._sortFunc );
}
// replace the * by whatever your object type is
private function _sortFunc( a:*, b:* ):int
{
return a.id - b.id;
}
More info: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#sort()
Or even the sortOn() function (which might be easier) should work:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#sortOn()
Related
Whenever I open a PDF-file in Illustrator for edithing, there are a lot of ungrouped and uncategorized Elemetns in it.
So I tried to select multiple elements with a spicific name with the below Script, but since the name of the elements are between Angle-brackets "<someName>" script wont select them:
function selectPageItemsByName(items, name) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.name === name) {
item.selected = true;
}
}
}
function main() {
var document = app.activeDocument;
var name = '<someFile>';
document.selection = null;
selectPageItemsByName(document.pageItems, name);
}
main();
Femkeblanko from Adobe Community says: Items with angle brackets in their label (unless user-created) are unnamed. They correspond to an empty string, i.e. "".
If I remove the Brackets from the name of the Elemetns, the script works but I have a lot of Elements and it needs time.
So, isn't there a way to salve it?
this is a pretty creative way:
// Select->Objects->Clipping Mask
app.executeMenuCommand("Clipping Masks menu item");
// Edit->Clear
app.executeMenuCommand("clear");
but it isn't really documented very well
some links for future reference:
Where is the perfect reference of adobe illustrator script?
https://ai-scripting.docsforadobe.dev/index.html
https://ten-artai.com/illustrator-ccver-22-menu-commands-list/
https://github.com/ten-A/AiMenuObject
What is the best effective way to collect data from a table using protractor.
I collecting the data shown in the code below and it is taking 20-30 seconds for 10 rows.
The buildStr counter is for creating a object for every row, 8 is the number of columns.
row = {};
gridRows = [];
element.all(by.css('#contenttableGrid div[role="gridcell"] div')).each(function(element){
element.getText().then(function(text){
row[headerName[buildStr]] = text;
buildStr++;
if(buildStr === 8){
buildStr = 0;
gridRows[rowCounter] = row;
rowCounter++;
row = {};
}
});
});
One way to speed it up that I see is to extract all the data right on the page by injecting script on it. It could be done with the help of browser.executeScript() (docs). In your sample Protractor has to make a request to the browser any time you call getText(), nubmer of calls it makes = number of cells in your table. But using browser.executeScript() it will make one call and do all the stuff in the browser, which can be really fast. Then you can simply return this data back to a test spec and use it via Promises.
var headerName = {};
// I assume that this variable holds names for headers
// you can pass data from test spec to injected script as arguments (below)
// promise will be resolved with the value you return from `executeScript`
var promise = browser.executeScript(function (headerName) {
// all the stuff inside this function happens on your page under test
// it is not a Protractor environment
var buildStr = 0;
var rowCounter = 0;
var row = {};
var gridRows = [];
var cells = document.querySelectorAll('#contenttableGrid div[role="gridcell"] div');
for (var i = 0, l = cells.length; i < l; i++) {
var text = cells[i].textContent;
// `headerName` object is passed as an argument from test spec
row[headerName[buildStr]] = text;
buildStr++;
if (buildStr === 8) {
buildStr = 0;
gridRows[rowCounter] = row;
rowCounter++;
row = {};
}
}
// return all collected data back to test spec
return gridRows;
}, headerName); // pass helper object from test spec to injectable function
promise.then(function (gridData) {
console.log(gridData); // result of computations
});
Make sure to read docs for browser.executeScript() if you want to use it, because it has a lot of specific moments.
was wondering if someone could show me what I'm doing wrong here.
I have some old AS2 flash code I'm trying to get working.
First I create a few arrays in frame 1 of the main timeline like so-
var typeArr:Array = new Array();
for (var i:Number = 1; i < 5; i++)
{
_root.typeArr[i] = "data goes here";
}
Then I have a movieclip dynamically attached on the main stage that when clicked appends one of the arrays we created by pushing the string 'foo' to it-
stop();
_root.myType=3;//this can be any of our array numbers
this.onPress=function(){
var foo:String="test";
_root.typeArr[_root.myType].push(foo);
trace(_root.typeArr[_root.myType]);
}
Where _root.typeArr[_root.myType] is the array name and number _root.typeArr3, but pushing the data does not work and returns nothing.
However, if I test it directly using-
_root.typeArr[_root.myType]=foo;
It will store the data once (_root.typeArr3=test), so I can't see why it won't push to that array as multiple elements each time like- "test,test,test"
It's driving me crazy.
Thanks! :)
_root.typeArr[_root.myType] is equal to "data goes here" so you are pushing string to a string, which doesn't work.
If you would like to append the new string, you should do something like:
_root.typeArr[_root.myType]+=foo;
and you will get: data goes heretest
If you have different data structure instead of "data goes here" the key may lie in the format of this data.
var typeArr:Array = new Array();
// 'i' must start from 0 because the first element is typeArr[0]
for (var i:Number = 0; i < 5; i++)
{
typeArr[i] = i;
// trace(typeArr[i]); // 0,1,2,3,4
}
// trace(typeArr); // 0,1,2,3,4
myType = 3;
bt.onPress = function()
{
var foo:String = "test";
// push method puts the element at the end of your array
// typeArr.push(foo);
// trace(typeArr); // 0,1,2,3,4,test
// splice method replace the 4e element (index 3) of your array
typeArr.splice(myType, 1, foo);
trace(typeArr); // 0,1,2,test,4
}
I am trying to load folder names into an array for later use. It works fine on my desktop but when I test it on a mobile device (both Android and iPhone) and it is calling another function - displayFlags(); - before I get the folder names into an array.
displayFlags() needs this array of names to work and I am getting errors from this line in of code in displayFlags(); Again its because the array is empty.
flag.load(new URLRequest(LangPath[i] + "/flag.png"));
Below is the code that loads file and the array.
Is there a way to check when the array is available and call a function after its loaded?
var folderLanguages:File = File.applicationDirectory.resolvePath("Languages");
var availLang:Array = folderLanguages.getDirectoryListing();
var Lang:Array;
var LangPath:Array;
var flagButton:MovieClip;
function getLang(evt:Event = null)
{
Lang = new Array();
LangPath = new Array();
for (var i:uint = 0; i < availLang.length; i++)
{
if(availLang[i].isDirectory)
{
//trace(availLang[i].name);// gets the name
Lang.push(availLang[i].name);
LangPath.push(availLang[i].nativePath);
trace(availLang[i].nativePath);// gets the name
}
}
displayFlags();
}
edit: I can't believe I didn't catch this sooner. Turns out my problem was re-declaring my first variables over and over again, essentially starting the program fresh instead of continuing it. To fix it, I replaced the first two lines with this:
if (initialized === undefined) {
trace("INITIALIZING");
var MCs = [];
var lastPos = "intializer";
var initialized = 1;
}
Now it works like a charm. I feel like a noob for this one; sorry to anyone whose time I wasted. I'd post this as an answer to my own question, but it won't let me since I'm still new.
Original Post follows:
I'm trying to make a flash that will randomly choose an ad, play it, and then randomly play another. To that end, I've succeeded by shuffling an array, and then gotoAndPlay-ing the label in the first element of the array, and then removing that element. At the end of each ad is gotoAndPlay(1); with all the main code being on the first frame. If the array is empty, it rebuilds it and reshuffles it.
The problem is, I don't want it to repeat any ads until its run through all of them; I think I've got that down, but I'm not positive. Further, I don't want the last element in the array to be the same as the first in the new one, so the same ad won't ever show twice in a row. I'm trying to have it detect if the element it just used matches the one it's about to use, and reshuffle if that happens, but in my testing it continues to occasionally show the same ad twice in a row.
I'm obviously doing something wrong, but being entirely new to ActionScript3 (and in fact to flash) I'm having a lot of trouble identifying what it is. Here's what I have right now:
var MCs = [];
var lastPos = "intializer";
if (MCs.length == 0) {
MCs = reset();
if (lastPos == MCs[0]) {
while (lastPos == MCs[0]) {
MCs = reset();
}
}
}
if (MCs.length > 0) {
lastPos = MCs[0];
MCs.splice(0,1);
gotoAndPlay(lastPos+"MC");
}
function reset(){
var PrepMCs = new Array("Image1", "Image2", "Image3");
var WorkMCs = new Array(PrepMCs.length);
var randomPos:Number = 0;
for (var i:int = 0; i < WorkMCs.length; i++)
{
randomPos = int(Math.random() * PrepMCs.length);
WorkMCs[i] = PrepMCs.splice(randomPos, 1)[0];
}
return WorkMCs;
}
Personally, I'd rather just do this with JavaScript, HTML, and images; it'd be really simple. But for hosting/CMS reasons I don't have any control over, I'm limited to a single file or a single block of code; I can't host anything externally, which as far as I can tell leaves Flash as my best option for this.
Any help would be greatly appreciated, thanks! If I've done something horribly, horribly wrong, and it's a wonder this even runs at all, don't hesitate to tell me!
edit: It just occurred to me, it is perfectly fine if the second run is in the same order as the first run, etc. The main thing is, it needs to be random. This is probably much easier to implement.
edit 2: MASSIVE DERP HERE. Every time it runs, it re-initializes MCs and lastPos... in other words, it's shuffling every time and starting over. What I should be researching is how to only run a line of code if a variable hasn't been initialized yet.
Blatantly stealing from #32bitKid, this is my version.
The main problem I have with his solution is the push/splice idea. As much as possible, I like to create once, and reuse. Shrinking and growing arrays is bulky, even if effective.
Also, this method does not re-order the array, which may or may not be valuable.
BTW, I like the way that he prevents a repeat of the previous item ("almost empty").
So here is another method:
package
{
public class RandomizedList
{
private var _items:Array;
private var idxs:Array;
private var rnd:int;
private var priorItemIdx:int;
private var curIdx:int;
public function RandomizedList(inarr:Array)
{
items = inarr;
}
private function initRandomize():void
{
idxs = new Array();
//Fisher-Yates initialization (http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle):
idxs[i] = 0;
for (var i:int = 1; i < items.length; i++)
{
rnd = int(Math.random() * (i + 1));
idxs[i] = idxs[rnd];
idxs[rnd] = rnd;
}
curIdx = 0;
priorItemIdx = -1;
}
private function randomize():void
{
var tempint:int;
//Fisher-Yates (http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle):
for (var i:int = items.length; i >= 1; i--)
{
rnd = int(Math.random() * (i + 1));
tempint = idxs[i];
idxs[i] = idxs[rnd];
idxs[rnd] = tempint;
}
curIdx = 0;
}
public function next():void
{
if (curIdx >= idxs.length)
{
randomize();
}
if (items.length > 1 && priorItemIdx == idxs[curIdx])
{
curIdx++;
}
priorItemIdx = idxs[curIdx++];
return items[priorItemIdx];
}
public function get items():Array
{
return _items;
}
public function set items(value:Array):void
{
_items = value;
initRandomize();
}
}
}
I would use a utility class like this to abstract out the behavior I wanted:
import flash.text.TextField;
class Randomizer {
private var unused:Array = [];
private var used:Array;
public function Randomizer(playList:Array) {
used = playList;
}
public function next():* {
// If almost empty, refill the unused array
if(unused.length <= 1) refill();
// Get the first item off the playList
var item:* = unused.shift();
// Shove it into the bucket
used.push(item);
// return it back
return item;
}
public function refill():void {
var i:int;
// Fisher-Yates shuffle to refill the unused array
while(used.length > 0) {
i = Math.floor(Math.random() * used.length)
unused.push(used.splice(i,1)[0])
}
}
}
Notice that it refills the unused array when the unused array still has one item in it, this makes it impossible for the last result to repeat twice in a row. This will return each item once before before looping, and will never repeat the same item twice.
You would use it by saying something like:
var ads:Randomizer = new Randomizer(["Image1", "Image2", "Image3"]);
ads.next(); // will return something
ads.next(); // will return something
ads.next(); // will return something
ads.next(); // will return something
// Keep going into infinity...
There is a little test example of this code working here.
See if this makes any sense
//create your array of all your ad names/frame labels
var PrepMCs:Array = new Array("Image1", "Image2", "Image3");
var shuffledMCs:Array = [];
//store the name of the last played ad in this var
var lastAdPlayed:String;
//shuffle the array
shuffleArray(PrepMCs);
function shuffleArray(arrayToShuffle:Array):void {
//clear the array
shuffledMCs = [];
var len:int = arrayToShuffle.length;
for(var i:int = 0; i<len; i++) {
shuffledMCs[i] = arrayToShuffle.splice(int(Math.random() * (len - i)), 1)[0];
}
//test to see if the new first ad is the same as the last played ad
if (lastAdPlayed == shuffledMCs[0]) {
//reshuffle
shuffleArray(PrepMCs);
} else {
lastAdPlayed = [0];
trace(shuffledMCs);
playAds();
}
}
//after each ad has played, call this function
function playAds():void {
if (shuffledMCs.length > 0) {
gotoAndPlay(shuffledMCs[0]);
shuffledMCs.splice(0,1);
} else {
//array is empty so we have played all the ads
shuffleArray(PrepMCs);
}
}