:: Web Design, Ruby on Rails, JavaScript, Photography, and more ::

Friday, October 2, 2009

Fireworks CS4 automated scripting with Javascript

Finding any information on command line scripting for Fireworks CS4 was difficult. Below is an example script to get you started, along with some hard-to-find resources below. Feel free to add other examples / links to the comments section, so that hopefully this page can become a helpful resource to others.

This script goes across all layers in a Fireworks animated GIF source file (PNG format). It replaces all text layers of a specific name with specific text. I used Ruby to dynamically generate the "process_image( ... )" calls, as a way to iterate through many source files and save animated GIF's with variable text in them.

try
{
process_image( "hello world" );
process_image( "konichi wa sekai")
}
catch(e)
{
alert(e);
}

// this will insert text into every text layer (of every state, if an animated GIF) called "dynamic_text"
function process_image( text )
{
var dom = fw.openDocument( "foo.png" );

if ( dom == null )
{
alert( "Could not open fireworks file: foo.png" );
return;
}

if ( !iterate_and_replace_layers( dom, text ) )
{
alert( "Did not find a layer to replace in " + source_file + ". Be sure you have the updated source files." );
return;
}

var opts = new ExportOptions();
opts.exportFormat = "GIF animation";

var output = save_directory + source_file_name + '_' + phone_number + '_' + creative_id + '.gif';
var save_result = fw.exportDocumentAs( dom, output, opts );

fw.closeDocument( dom, false );
}

function iterate_and_replace_layers( dom, text )
{
var layer_replaced = false;

for(var i = 0 ; i < dom.frames.length ; i++)
{
for(var j = 0 ; j < dom.frames[i].topLayers.length - 1 ; j++)
{
var topLayer = dom.frames[i].topLayers[j];
var elems = topLayer.elemsandsublayers;
for(var k = 0; k < elems.length; k++)
{
// add to layer
if ( elems[k].name == "dynamic_text" )
{
set_text( elems[k], number );
layer_replaced = true;
}
}
}
}

return layer_replaced;
}


function set_text( element, number )
{
var runs = element.textRuns;
var runsArray = new Array();
runsArray[0] = { changedAttrs: {}, characters: number };
runs.textRuns = runsArray;
element.textRuns = runs;
}

Save the file with a .jsf extension, and to call it from a Ruby command script, simply do:
system( "open script_fw.jsf" )


Article with some information, and a few helpful links to resources:
http://www.adobe.com/devnet/fireworks/articles/demo_current_document_command.html

Link to PDF API documentation:
http://help.adobe.com/en_US/Fireworks/9.0_Extending/fireworks_cs3_extending.pdf

Fireworks Help
http://help.adobe.com/en_US/Fireworks/10.0_Using - (Automating Tasks)

Javscript source of existing Fireworks plugins found here (on a Mac machine using CS4). This is a good way to learn the scripting language by viewing plugins' source code:
~/Applications/Adobe Fireworks CS4/Configuration/commands/

Labels: ,


Saturday, May 30, 2009

Check if cookies are enabled on client side

Using just Javascript (with the help of jQuery and the jQuery cookie plugin), this bit of code will do the trick on checking whether the user's browser accepts cookies or not. This is helpful for posting a warning message on a login screen or when adding things to a shopping cart, etc.

<script>
jQuery(document).ready(function()
{
var TEST_COOKIE = 'test_cookie';
jQuery.cookie( TEST_COOKIE, true );
if ( jQuery.cookie ( TEST_COOKIE ) )
{
jQuery.cookie( TEST_COOKIE, null );
alert( 'Good news, cookies are enabled.' );
}
else
{
alert( 'Cookies are not enabled. Please enable and try again.' );
}
}
</script>

Labels: ,


Tuesday, April 7, 2009

Add javascript tabs to browser history (for "back" navigation)

As fancy AJAX websites become more popular, the "back" button is getting less useful and less functional. Often times you perform an action that perhaps repaints most of the browser window (think, viewing an email in Gmail), and you want to go back to your preview view (list of email in your inbox). In the old days, you would hit back and go to the previous page. But in Gmail and other AJAX-ey sites, you didn't go to a "new page" but instead had javascript repaint the divs. So hitting back would take you to the login screen or some other site. Fortunately Gmail has solved this by inserting various actions into your browser history, so that you can actually use the back and forward buttons to navigate through Gmail.

jQuery has a good history plugin that lets you add actions to the browser history, and on every page load you can register for a callback that gives you a value based on where you are in the history. It is up to you to write your JavaScript in a generic way that can save and respond to history save points. But jQuery does the cross-browser, behind the scenes heavy-lifting for you.

My use of the history plugin was for a tabbed navigation where the tabs were all done using JavaScript to show/hide divs. It became a problem when a user would load the page, click a different tab, and while viewing that 2nd tab, click a link to another page. Then the user would click back and expect to be on that 2nd tab again, but instead would be shown the first tab.

Here is some sample code that I used to easily solve this issue and add back/forward support for my AJAX tabs:


var default_tab = 'home'; // this can be set by query string, url, etc

function on_page_loaded( history_key )
{
if( history_key )
{
// this gets run for every back/forward, if something was stored in
// the browser history's location.hash
toggle_tab( history_key );
}
else
{
// this gets run for every page load where back/forward is not pressed
toggle_tab( default_tab );
}
}

jq(document).ready( function() {
// Initialize history plugin, and give it the function name
// of your callback above
jq.historyInit( on_page_loaded );

jq('a.tab_with_history').click( function()
{
// actually toggle the tab in UI
var tab_name = toggle_to_selected_tab( this );
// save to the history, the tab name -- important step!
jq.historyLoad( tab_name );
return false;
});
});


I will leave "toggle_tab" and "toggle_to_selected_tab" implementation to you as an exercise. I created a custom tab class with initialization methods that are generated from Ruby hashes and then toggling tabs is just a matter of finding the tab by name and flipping some css display values of the corresponding divs that each tab owns

HTML:


<ul>
<li><a id="tab_1" class="tab_with_history" href="#1">Tab 1</a></li>
<li><a id="tab_2" class="tab_with_history" href="#2">Tab 2</a></li>
</ul>

<div id="content_1" style="display: none">contents of tab 1</div>
<div id="content_2" style="display: none">contents of tab 2</div>

Labels: ,


Tuesday, March 10, 2009

Use Rails url_for in Ext grids

In rails apps, it is best practice to always use link_to and url_for when generating URLs. This prevents your application from having hard-coded links that may break if you refactor controllers later. It also allows an easy way of creating links that send a put/post/delete command to your server.

I found that I wanted that abstraction and power in an Ext grid that was in a view. If you have used Ext grids at all, you know that you set up a store and populate it with data (json object, async request, etc) and then give it some column information and Ext does the rest. In other words, it iterates across your records and displays the rows, so there's no way to use <%= link_to ... %> for a cell in each row because the rendering is done client-side in JavaScript.

My quick solution, that perhaps some will find helpful:

var delete_link_template = '<%= link_to( 'Delete', { :controller => 'my_controller', :action => 'destroy', :id => '__id__' },
{ :method => :delete, :confirm => 'Are you sure you want to leave this item?' } ).gsub( "\'", "\\\\'" ) %>';

function actions_renderer( val, metadata, record ) {
var object_id = record.get("id");
return val + ': ' + delete_link_template.replace( "__id__", object_id );
}


// this is a portion of an example Ext grid being declared. Notice it uses 'actions_renderer', which is the function defined above
var my_grid = new Ext.grid.GridPanel(
{
columns:
[
...,
{ header: "Manage", width: 140, dataIndex: 'name', renderer: actions_renderer }
],
...
} );

Labels: , ,