Welcome, Guest. Please Login
Rorohiko Workflow Resources
  Welcome the Message Board of Rorohiko Workflow Resources. To register, please e-mail [email protected] - automatic registration has been turned off because of relentless spambots and spammers. This Message Board is not actively monitored. If you have an urgent question, please e-mail [email protected]. If you post it here, it will probably be many weeks before we notice.
  HomeHelpSearchLogin  
 
Page Index Toggle Pages: 1
Send Topic Print
subjectDelete event question (Read 15773 times)
Kasyan
YaBB Newbies
*
Offline


I Love YaBB 2!

Posts: 21
Kiev, Ukraine
Gender: male
subjectDelete event question
04/21/10 at 21:37:35
 
Hi there,

I have a question regarding subjectDelete event: the reference states:
"subjectDelete -- Sent when one of the observed page items is about to be deleted."

As far as I understand, it means that I can do something with the object (event source) before it is actually deleted.

I try
alert(theItem.eventSource.constructor.name);
and it says
undefined
so the script is triggered after the object has been deleted

In fact, I want to do something with the object before it is cut by the user.
So far I came to the following workaround:

List of Subjects: *
Event Filters: subjectDelete

Code:
if (app.documents[0].undoName == "Cut") {
   app.pasteInPlace();
   theObj = app.selection[0];
   // do something with the object
   theObj.fillColor = app.documents[0].swatches[5];
   app.cut();
} 



Is there a better solution to the problem? Is there a way to make the script run before the object is cut?

Regards,
Kasyan
Back to top
 
WWW 225729910  
IP Logged
 
Harbs
YaBB Newbies
*
Offline


Kris Does Great Work ;-)
!

Posts: 13
Gender: male
Re: subjectDelete event question
Reply #1 - 04/21/10 at 23:05:29
 
I seem to remember this being asked on this forum some time ago...

No. You can not do anything with page items with a subjectDelete event.

These events must be dealt with in unique ways because of the fragile state that objects are in when the event runs. I'm sure Kris will give some more details...

Good luck!  Smiley
Harbs
Back to top
 
WWW  
IP Logged
 
Kasyan
YaBB Newbies
*
Offline


I Love YaBB 2!

Posts: 21
Kiev, Ukraine
Gender: male
Re: subjectDelete event question
Reply #2 - 04/22/10 at 01:09:38
 
Thank you for the prompt reply, Harbs.

Kasyan
Back to top
 
WWW 225729910  
IP Logged
 
Kris Coppieters
YaBB Administrator
*****
Offline



Posts: 181
New Zealand
Gender: male
Re: subjectDelete event question
Reply #3 - 04/23/10 at 07:42:11
 
Hi Kasyan,

Here's a bit more info about the delete event:

subjectDelete (and delete as well ) was purposely disabled in 1.0.47 - subjectDelete had a lethal design flaw, which caused InDesign to crash.

The problem was that by the time the event gets processed, the deletion of the underlying page item is already somewhat underway, and when the script then referred to the page item being deleted, things simply crashed; I cannot seem to detect deletion until it has already half-happened.

In 1.0.48, I've re-enabled subjectDelete, but with a twist: when subjectDelete is received, your 'theItem.eventSource' is not a page item - instead it is an integer (the id of the page item that is being deleted). You also get access to the dataStore of the item being deleted - but the item itself is 'out of reach'; like the Cheshire cat, only its smile (id) remains. subjectDelete needs to be taken as 'subjectIsBeingDeletedButNotQuiteDoneYet'.

That seems to be a workable approach - i.e. to replace the item that's being deleted by a proxy (an integer).

The 'delete' event will remain disabled - only subjectDelete can reasonably be re-enabled through this approach.
Back to top
 

Kris
WWW  
IP Logged
 
Kris Coppieters
YaBB Administrator
*****
Offline



Posts: 181
New Zealand
Gender: male
Re: subjectDelete event question
Reply #4 - 04/23/10 at 07:46:36
 
Further to that:

When processing subjectDelete in 1.0.48, theItem.eventSource contains an integer instead of an object - the value is the id of the object that is being deleted.

I have also added an additional parameter to getDataStore, which can be used when handling a subject... event code:

value = theItem.getDataStore("key",0,0);

The third parameter is called 'subjectDataStoreIdx' or something like it. It can only be used if the second parameter is 0. If it is present, and >= 0, then you'd be accessing the data store of one of the eventSource items instead of the dataStore of theItem itself.

In other words, the following is more or less equivalent

theSource = theItem.eventSource;
if (theSource instanceof Array)
{
  theSource = theSource[0];
}
theValue = theSource.getDataStore("key");

with this:

theValue = theItem.getDataStore("key",0,0);

In most cases you'll use 0 for the index, but when events are grouped, you might want to use higher indices for the third parameter to access multiple event source object data stores.

The main difference is that when you capture subjectDelete you can use theItem.getDataStore("key",0,0) even though the subject has already been partially or completely deleted.
Back to top
 

Kris
WWW  
IP Logged
 
Kasyan
YaBB Newbies
*
Offline


I Love YaBB 2!

Posts: 21
Kiev, Ukraine
Gender: male
Re: subjectDelete event question
Reply #5 - 06/02/10 at 03:25:07
 
Hi all,

First of all, I want to thank you, Kris, for your detailed explanation on my question. I thanked you in my previous post here about a month ago, but for some unknown reason, it's not here.
Now after a break, I'd like to get back to my issue.
For beginning, a little 'background' information to show you what I am trying to achieve.
I wrote a script for my client that creates cross-references by converting tags to x-refs sources and destinations from tagged text.
There are two kinds of tags that look like so:
<s=Anchor 1> -- for sources
<d=Anchor 1> -- for destinations

The tags and their numbers are generated by the clients software. The tagged text is imported to InDesign via xTags plug-in (by Em software), because, in addition to text, images are imported and placed as in line objects. A destination tag always follows the inline image it points to.
But my client wanted the destination marker to always stick with the image, so that when it is cut and pasted onto another page, the destination marker goes with it.
To achieve this I wrote a function that creates a text frame inside a graphic frame holding the image and moves the destination tag into it:
Code:
function MoveDestinationTags() {
	app.findGrepPreferences = app.changeGrepPreferences = NothingEnum.nothing;
	app.findGrepPreferences.findWhat = "<d=Anchor\\s\\d+>";
	var myFinds = myDoc.findGrep();
	if (myFinds.length == 0) ErrorExit("No destination tags have been found.");
	WriteToFile("\rMoving destination tags into images:\n");

	var myPrBarWin = new Window ( "window", "Creating Cross-References" );
	var myPrBar = myPrBarWin.add ("progressbar", [12, 12, 350, 24], 0, myFinds.length);
	var myPrBarText = myPrBarWin.add("statictext");
	myPrBarText.bounds = [0, 0, 340, 20];
	myPrBarText.alignment = "left";
	myPrBarWin.show();
	var myPrBarCounter = 1;

	for ( var j = myFinds.length-1; j >= 0; j-- ) {
		try {      
			var myFound = myFinds[j];
			var myTagName = myFound.contents + "";
			myPrBar.value = myPrBarCounter;
			myPrBarText.text = String("Moving destination tags \"" + myTagName  + "\" (" + myPrBarCounter + " out of " + myFinds.length + ")");

			$.writeln("MoveDestinationTags - " + j + " - " + myFound.contents);

			var myPrevChar = myFound.characters.previousItem(myFound.characters[0]);
			var myRectangle = myPrevChar.rectangles.everyItem().getElements()[0];
			if (myRectangle != null) {
				var myTextFrame = myRectangle.textFrames.add({geometricBounds:myRectangle.geometricBounds});
				myFound.move(LocationOptions.BEFORE, myTextFrame);
				WriteToFile(myPrBarCounter + " - " + myTagName + " -- Ok\n");
			}
		}
		catch(e) {
			$.writeln(e);
			WriteToFile(myPrBarCounter + " - ERROR  -- Can't move destination tag \"" + myFound.contents + "\"\n");
		}

		myPrBarCounter++;
	}

	myPrBarWin.close();
}
 


However, here comes a problem: the script works normally on a test document containing 3 pairs of tags, but works very slowly on a real document containing 59 pairs of tags (it takes about 40 minutes to complete).
Here is an excerpt from a log file:

MoveDestinationTags - 58 - <d=Anchor 59>
myDuration2-58= 101.506
MoveDestinationTags - 57 - <d=Anchor 58>
myDuration2-57= 98.782
MoveDestinationTags - 56 - <d=Anchor 57>
myDuration2-56= 95.828
......
MoveDestinationTags - 2 - <d=Anchor 3>
myDuration2-2= 6.599
MoveDestinationTags - 1 - <d=Anchor 2>
myDuration2-1= 5.819
MoveDestinationTags - 0 - <d=Anchor 1>
myDuration2-0= 4.476
myDuration1 = 2648.399


All tags are processed backwards: from #58 (Anchor 59) to #0 (Anchor 1).
myDuration1 is a variable showing the total time for processing all tags in secs.
myDuration2 shows time for every particular tag. Notice that it takes  101.5 secs to complete MoveDestinationTags function for the first tag in the loop, then the time gradually reduces and it takes just about 4.5 secs for the last tag.
Why the duration is so different? Can I somehow reduce the time required to complete the function?

As a workaround, I want to use what I've learned from Kris's posts to make a new scripted plug-in and rework the original script. My idea is to create a text frame only when an image is being cut, instead of creating it for every image. The script will mark every image with the name of the corresponding tag using setDataStore method. When a user cuts an image, the plug-in temporarily pastes it back into the document, creates a text frame inside the image, removes the corresponding destination marker and recreates it inside the text frame and finally cuts it. I believe the user won’t notice anything on a fast computer.

Here is a code to illustrate what I mean:

List of Subjects: *
Event Filter: subjectDelete
Code:
if (app.documents[0].undoName == "Cut" ) {
   var theValue = theItem.getDataStore("kas",0,0);
   if (theValue.indexOf("Anchor") == 0) {
   app.pasteInPlace();
   var theObj = app.selection[0];
   var tf = theObj.textFrames.add({geometricBounds:theObj.geometricBounds});
   tf.contents = theValue; // x-ref destination will be created here
   app.cut();
  }
}
 


(I am on CS3 at the moment, so I'm not creating the x-ref destination --  just adding anchor's name to the contents of the text frame)

Do you foresee any pitfalls of using this approach?

Thank you in advance.

Kasyan
Back to top
 
WWW 225729910  
IP Logged
 
Kris Coppieters
YaBB Administrator
*****
Offline



Posts: 181
New Zealand
Gender: male
Re: subjectDelete event question
Reply #6 - 06/02/10 at 08:19:17
 
Hi Kasyan,

I am currently very busy with a big project so I cannot give your question the attention it deserves - but just for laughs, can you change a line from

var myFinds = myDoc.findGrep();

to

var myFinds = myDoc.findGrep(); myFinds = myFinds.slice(0);

If that does not help, I'd try copying the relevant info from myFinds into another structure - I suspect the slowness comes from either myFinds, or else the 'things' you have stored in 'myFinds' being 'live objects' that remain linked to the document.

Each time you change the document, InDesign has to update all these 'live objects', which takes time.

So, what I'd try is

1) get myFinds
2) create a new array myCopiedFinds
3) loop through myFinds and extract the data you need and put it into copied objects - something like


var myFound = myFinds[j];
var myCopiedFound = {
contents: myFound.contents.substr(0),
myPrevCharIdx: myFound.characters.previousItem(myFound.characters[0]).index,
...


i.e. build a 'dead' object myCopiedFound that contains all the info you need from myFinds,but no 'live' stuff - it should be purely Strings and Integers (e.g. character indexes instead of references to characters etc...)

Then destroy myFinds, and then loop through myCopiedFinds. myCopiedFinds should be a 'dead', bog-standard JavaScript object that only contains arrays, integers, and strings - no references to 'live' InDesign objects.

Then use the character indexes to re-calculate the proper character reference on-the-fly.

I reckon that might speed up things a lot.
Back to top
 

Kris
WWW  
IP Logged
 
Kasyan
YaBB Newbies
*
Offline


I Love YaBB 2!

Posts: 21
Kiev, Ukraine
Gender: male
Re: subjectDelete event question
Reply #7 - 06/03/10 at 20:52:38
 
Hi Kris,

Thank you for the quick reply.

Applying slice(0) to myFinds didn't help.
So I tried your second suggestion: created a temporary array and 'pushed' the elements I need into it -- as a result, text frames are created significantly faster, but the bottleneck moved to another place -- the line in which I reference to inline graphic frames:
Code:
myRectangle = myPrevChar.rectangles.everyItem().getElements()[0]; 


Thus overall time required to complete the function didn't change.

Then I found a workaround -- working through selection (Yes, I know, most scripters advise against doing this, but it works much faster this way, although I don't know why):
Code:
myPrevChar.select();
myRectangle = app.selection[0].rectangles.everyItem().getElements()[0]; 



Here is where I got so far:
Code:
var myDoc = app.activeDocument;
var myStartTime1 = new Date();
$.writeln("Start ---------------------");
var myStartTime1 = new Date();
MoveDestinationTags();
var myEndTime1 = new Date();
var myDuration1 = (myEndTime1 - myStartTime1)/1000;
$.writeln("myDuration1 - " + myDuration1);
$.writeln("End ---------------------");

function MoveDestinationTags() {
	var myPrevChar, myRectangle, myTmpArr;
	app.findGrepPreferences = app.changeGrepPreferences = NothingEnum.nothing;
	app.findGrepPreferences.findWhat = "<d=Anchor\\s\\d+>";
	var myFinds = myDoc.findGrep();

	var myArray = [];

	for ( var x = myFinds.length-1; x >= 0; x-- ) {
		myPrevChar = myFinds[x].characters.previousItem(myFinds[x].characters[0]);
		myPrevChar.select();
		var myStartTime3 = new Date();
		myRectangle = app.selection[0].rectangles.everyItem().getElements()[0];
		var myEndTime3 = new Date();
		var myDuration3 = (myEndTime3 - myStartTime3)/1000;
		$.writeln("myDuration3 - (" + x+ ") - " + myDuration3);

		myTmpArr = [];
		myTmpArr[0] = myRectangle;
		myTmpArr[1] = myFinds[x].contents + "";
		myArray.push(myTmpArr);
		myFinds[x].remove();
	}

	for ( var j = myArray.length-1; j >= 0; j-- ) {
		try {
			var myStartTime2 = new Date();

			if ( myArray[j][0] != null ) {
				var myTextFrame = myArray[j][0].textFrames.add({geometricBounds:myArray[j][0].geometricBounds, contents:myArray[j][1]});
			}
		}
		catch(e) {
			$.writeln(e);
		}

		var myEndTime2 = new Date();
		var myDuration2 = (myEndTime2 - myStartTime2)/1000;
		$.writeln("myDuration2 - (" + j + ") - " + myDuration2);
	}

}

function GetDate() {
	var myDate = new Date();
	if ((myDate.getYear() - 100) < 10) {
		var myYear = "0" + new String((myDate.getYear() - 100));
	} else {
		var myYear = new String ((myDate.getYear() - 100));
	}
	var myDateString = (myDate.getMonth() + 1) + "/" + myDate.getDate() + "/" + myYear + " " + myDate.getHours() + ":" + myDate.getMinutes() + ":" + myDate.getSeconds();
	return myDateString;
 } 



I tested MoveDestinationTags function on my home computer (CS3, Windows); it takes 12 minutes to create 59 frames. I'll try to test it on CS4 later today.

Regards,
Kasyan
Back to top
 
WWW 225729910  
IP Logged
 
Harbs
YaBB Newbies
*
Offline


Kris Does Great Work ;-)
!

Posts: 13
Gender: male
Re: subjectDelete event question
Reply #8 - 06/03/10 at 22:18:47
 
Try changing this:
Code:
myPrevChar = myFinds[x].characters.previousItem(myFinds[x].characters[0]);
		myPrevChar.select();
		var myStartTime3 = new Date();
		myRectangle = app.selection[0].rectangles.everyItem().getElements()[0]; 



to this:
Code:
		var myStory = myFinds[x].parentStory;
		var myRectangle = myStory.characters.item(myFinds[x].index-1).rectangles.item(0).getElements()[0];
 

Back to top
 
WWW  
IP Logged
 
Kasyan
YaBB Newbies
*
Offline


I Love YaBB 2!

Posts: 21
Kiev, Ukraine
Gender: male
Re: subjectDelete event question
Reply #9 - 06/04/10 at 05:46:01
 
Hi Harbs,

Yes, this finally have solved my problem. Thank you.
Now the script runs for about 35 seconds instead of about 40 minutes. It's fantastic!  Grin
A  small correction:
Code:
var myStory = myFinds[x].parentStory; 


should be
Code:
var myStory = myFinds[x].parent; 



Thank you again guys. I'd never figured this out on my own.

Kasyan
Back to top
 
WWW 225729910  
IP Logged
 
Page Index Toggle Pages: 1
Send Topic Print