David Lance

Solutions For Adobe's Idiosyncrasies

The ‘with’ Operator

I have yet to personally see this implemented in code, so in hopes of remembering it or somehow promoting it’s popularity… I present ActionScript’s ‘with’ operator.

with (object:Object) { 
	// statement(s)
}

Establishes a default object to be used for the execution of a statement or statements, potentially reducing the amount of code that needs to be written.

The object parameter becomes the context in which the properties, variables, and functions in the statement(s) parameter are read. For example, if objectis my_array, and two of the properties specified are length and concat, those properties are automatically read as my_array.length and my_array.concat. In another example, if object is state.california, any actions or statements inside the with statement are called from inside the california instance.

To find the value of an identifier in the statement(s) parameter, ActionScript starts at the beginning of the scope chain specified by object and searches for the identifier at each level of the scope chain, in a specific order.

The scope chain used by the with statement to resolve identifiers starts with the first item in the following list and continues to the last item:

  • The object specified in the object parameter in the innermost with statement
  • The object specified in the object parameter in the outermost with statement
  • The Activation object (a temporary object that is automatically created when the script calls a function that holds the local variables called in the function)
  • The object that contains the currently executing script
  • The Global object (built-in objects such as Math and String)

To set a variable inside a with statement, you must have declared the variable outside the with statement, or you must enter the full path to the Timeline on which you want the variable to live. If you set a variable in a with statement without declaring it, the with statement will look for the value according to the scope chain. If the variable doesn’t already exist, the new value will be set on the Timeline from which the with statement was called.

Parameters

object:Object — An instance of an ActionScript object or movie clip.

Example
How to use this example 
The following example sets the _x and _y properties of the someOther_mc instance, and then instructs someOther_mc to go to Frame 3 and stop. with (someOther_mc) { _x = 50; _y = 100; gotoAndStop(3); } The following code snippet shows how to write the preceding code without using a with statement. someOther_mc._x = 50; someOther_mc._y = 100; someOther_mc.gotoAndStop(3); The with statement is useful for accessing multiple items in a scope chain list simultaneously. In the following example, the built-in Math object is placed at the front of the scope chain. Setting Math as a default object resolves the identifiers cos, sin, and PI to Math.cos, Math.sin, and Math.PI, respectively. The identifiers a, x, y, and r are not methods or properties of the Math object, but because they exist in the object activation scope of the function polar(), they resolve to the corresponding local variables.

function polar(r:Number):void
{
    var a:Number, x:Number, y:Number;
    with (Math)
    {
        a = PI * pow(r, 2);
        x = r * cos(PI);
        y = r * sin(PI / 2);
    }

    trace("area = " + a);
    trace("x = " + x);
    trace("y = " + y);
}

polar(3);
/* area = 28.2743338823081 x = -3 y = 3 */

Straight Left Quotation Mark

Thank you ActionScript! All these years of using a keyboard and today I’ve found the answer to a question you never asked. Please, do tell…

Q: What is that thing sharing a key with the tilde?

A: A “straight left quotation mark”.

And your follow up question, naturally…

Q: What the hell is it used for?

A: Funny you should ask. I finally have an answer. Thanks to ActionScript and regular expressions, combine it with a $ and it’ll fetch the portion of a string before the match when searching via regex.

Wow! You learn something new everyday.

$` The portion of the string that precedes the matched substring. This code uses the straight left single quotation mark character (`), not the straight single quotation mark (‘) or the left curly single quotation mark (‘ ).

http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f00.html

Modules and Singleton Manager

This isn’t out there enough, so I’m writing it down. Any singleton based manager, such as PopUpManager or DragManager must be initialized in the parent application before use in the module. This is so that the parent application becomes the first to register the manager as a singleton (first in wins) permitting, therefore, the manager to be shared across modules. The easiest workaround is to simply add an import of the manager you’re using and a dummy variable to the main Application class.

import mx.managers.PopUpManager;
public var pum:PopUpManager;

For reference, here’s the error I was getting…

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mx.managers::PopUpManagerImpl/addPopUp()[E:\dev\hero_private\frameworks\projects\framework\src\mx\managers\PopUpManagerImpl.as:433]
at mx.managers::PopUpManager$/addPopUp()[E:\dev\hero_private\frameworks\projects\framework\src\mx\managers\PopUpManager.as:193]

PS: I noticed the effects of this mostly on Window7 using Internet Explorer. My typical development browser, Opera, was much more forgiving in regular mode although the debugger would blow up. Not sure if that has to do with different Flash plugins or not, but I suspect so.

Flex Associative Arrays

Never put much thought into it, but it strikes me tonight that OO writers like to use concrete objects. Argue if you will that they’re more maintainable, or the debugger is easier using them, but sometimes they’re just boiler plate if you don’t need a fully developed object.

I mention all this because I’m looking over the LCDS documentation on Publisher objects and they reference that associative arrays are used for AsyncMessage.headers parameters. No sense throwing a Dictionary object or an array of dynamic objects with key and value fully labeled in there to overkill the array. Rather, keep it simple.

var message:AsyncMessage = new AsyncMessage();
                message.headers = new Array();
                message.headers["prop1"] = 5;
                message.body = input.text;
                producer.send(message);

Short. Sweet. To the point. Value doesn’t need to be labeled ‘value’. Brings me back to the Java days. I like it.

Rotating Flex Text

Rotating text in Flex is simple enough, but it frustratingly took me a sufficient amount of digging to find the solution. Thus, a quick note on the topic.

<s:RichText breakOpportunity="all" fontSize="36" fontWeight="bold" lineHeight="20" text="text" textRotation="rotate270"/>

Doing this effectively breaks each character into a row and rotates it. The consequence is lineHeight becomes your new kerning. Not a biggie, but you’ll have to play with it a bit based on the fontSize you’re using.

Web Service BasicAuthentication Solution

I’ve shown how to add the Authorization header to your client. Now, lets take it server side. How do we receive that header and either allow access or reject the request with a SoapException. Lets start at the authentication piece. This object will be instantiated from the endpoint method and references a remote ActiveDirectory LDAP. I also happen to be using log4net as my logging solution. Cut that part out if not needed.

namespace
{
    public class BasicAuthenticator
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(BasicAuthenticator));
        private static string searchString = "OU= ...";
        HttpContext context;
        LdapConnection ldapConn = null;

        public BasicAuthenticator(HttpContext context)
        {
            this.context = context;

            string server1 = ConfigurationManager.AppSettings["LDAP_Server1"];
            string server2 = ConfigurationManager.AppSettings["LDAP_Server2"];
            string port = ConfigurationManager.AppSettings["LDAP_PORT"];
            LdapDirectoryIdentifier ldapIdentity = new LdapDirectoryIdentifier(new string[] { server1, server2 }, Convert.ToInt32(port), false, false);
            ldapConn = new LdapConnection(ldapIdentity);
        }

        public bool authenticate()
        {
            bool authentic = false;

            string authStr = context.Request.Headers["Authorization"];

            if (authStr == null || authStr.Length == 0)
                return false;

            authStr = authStr.Trim();

            string encodedCredentials = authStr.Substring(6);

            byte[] decodedBytes = Convert.FromBase64String(encodedCredentials);
            string s = new ASCIIEncoding().GetString(decodedBytes);

            string[] userPass = s.Split(new char[] { ':' });
            string username = userPass[0];
            string password = userPass[1];

            log.Info("Authentication Attempt: " + username);

            if (TryAuth(context.ApplicationInstance, username, password))
            {
                context.User = new GenericPrincipal(new GenericIdentity(username, "ImporterWS.Basic"), null);
                authentic = true;
            }

            return authentic;
        }

        private bool TryAuth(HttpApplication app, string username, string password)
        {
            bool authentic = false;
            try
            {
                ldapConn.Credential = new NetworkCredential(username, password);

                DirectoryEntry entry = new DirectoryEntry("LDAP://" + searchString, username, password);
                object nativeObject = entry.NativeObject;
                authentic = true;
            }
            catch (DirectoryServicesCOMException dsce)
            {
                log.Warn(dsce.Message);
            }

            log.Debug("Authentic: " + authentic);
            return authentic;
        }
    }
}

 


[WebMethod]

public string HelloWorld()

{
    Authenticator authenticator = new Authenticator(Context);
    if (authenticator.authenticate())
    {
        return "Hello World!";
    }
    else
    {
        throw new SoapException("Authentication Failure", SoapException.ClientFaultCode, Context.Request.Url.AbsoluteUri, node);
    }
}

Add BasicAuthentication To Your Web Service Client

Building on my last post using the wsdl.exe app to build a web service client proxy, we now need to add authentication. A common practice is to send your credentials using BasicAuthentication (over HTTPS of course).

All over the web you’ll find simple examples showing you how to set the Credentials object on your proxy and voila it works. That’s great if you have a webservice that challenges you and gives your a client a chance to respond. Unfortunately though, web services typically are stateless, one time calls. There’s no chance for “hey, how ya doin?”. Here’s my solution for adding the Authorization header to the outgoing request (the first outgoing request).

You’ll still use the Credentials object in your client.

WSProxy proxy = new WSProxy();
proxy.Credentials = new NetworkCredential(username, password);
string response = proxy.HelloWorld();

The proxy object will use the Credentials object you just set if you insert this code into the WSProxy object.

/// <summary>
/// Manually insert HTTP Authorization header for Basic Authentication.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
protected override WebRequest GetWebRequest(Uri uri)
{
    string username = ((NetworkCredential)Credentials).UserName;
    string password = ((NetworkCredential)Credentials).Password;
    HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
    string creds = String.Format("{0}:{1}", username, password);
    byte[] bytes = Encoding.ASCII.GetBytes(cre);
    string base64 = Convert.ToBase64String(bytes);
    request.Headers.Add("Authorization", "basic " + base64);
    return request;
}

WSDL.exe vs. Service Reference

The first time I tried to write a web service client in Visual Studio 2010, my instinct said “create a service reference”. After all, Microsoft loves to tie things in through references. So I did… and I quickly realized I was going to have to create a service reference for each environment if I was going to use the same client for accessing endpoints in multiple environments.

A better way to go is to jump outside Visual Studio and use the command line application wsdl.exe.

wsdl /out:WSProxy.cs http://domain/MyWebService/Endpoint.asmx?WSDL

The stub this lovely little creates has a constructor that directly sets the URL.

public MyWebService()
{
    this.Url = "http://localhost/MyWebService/Endpoint.asmx";
}

Add an override constructor to provide your own URL to make this class more diverse.

public MyWebService(string url)
{
    this.Url = url;
}

Then, get really lazy like me and add a switch based on a command line arg.

if (arg1.Equals("LOCAL"))
    proxy = new WSProxy()

else if (arg1.Equals("QA"))
    proxy = new WSProxy("http://domain/MyWebService/Endpoint.asmx");

About Face

A career shift has shoved me down a road I surely thought I would never undertake… Microsoft. Nevertheless, here I sit a couple months down the road coding C#, writing web services for IIS, and implementing SSIS. Its a huge learning curve so as I come across the idiosyncrasies of the .NET world, I’ll post them here. Bare with me.

Accessing DataGrid Info from ContextMenu

Adding a ContextMenu to a DataGrid component is a simple enough task. Accessing the data from the row you right-clicked on is another matter. Logically (to my mind) the ContextMenuEvent should contain a reference to the DataGrid, and it does (contextMenuOwner). Search through your debugger, however, and you’ll quickly discover the DataGrid has not set it’s selectedItem object yet. There is a protected property called lastHighlightItemRenderer, but apparantly Adobe doesn’t feel that’s information you should be privy to. To solve this issue and access the row information under your mouse at the instance right-click is performed, you’ll need to do a little extra work.

private var dgContextMenu:ContextMenu;
private var lastRollOverIndex:Number;

private function init():void
{
  dgContextMenu = new ContextMenu();
  dgContextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, menuSelectHandler);

  var menuItemEdit:ContextMenuItem = new ContextMenuItem("Edit Job");
  dgContextMenu.customItems.push(menuItemEdit);
  menuItemEdit.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuItemEditSelectHandler);

  grid.contextMenu = dgContextMenu;
}

private function menuSelectHandler(event:ContextMenuEvent):void
{
  grid.selectedIndex = lastRollOverIndex;
}

private function menuItemEditSelectHandler(event:ContextMenuEvent):void
{
  var data:Object = grid.selectedItem;
}

The trick to this code is in the menuSelectHandler function. That function is ran immediately on the right-click of the mouse and therefore sets up your DataGrid.selectedIndex property.  The next issue you need to solve is how to keep lastRollOverIndex in sync with where your mouse is. To do that, code the rollOver event of the DataGrid to update that value.

<mx:DataGrid id="grid" width="100%" height="100%" dataProvider="{jobs}"
  itemRollOver="lastRollOverIndex = event.rowIndex">
  <mx:columns>
     <mx:DataGridColumn headerText="Column One" dataField="columnOneData"/>
     <mx:DataGridColumn headerText="Column Two" dataField="columnTwoData"/>
   </mx:columns>
 </mx:DataGrid>

Post Navigation

Follow

Get every new post delivered to your Inbox.