Friday, June 20, 2014

CPResponder and Event Flow in Cappuccino

Events are central to giving any application life. When you are reading about any question regarding event handling in the Google group mail list, the person providing the answer will inevitably mention the CPResponder or one of the methods regarding this class such as acceptsFirstResponder or becomeFirstResponder.

As a result, it might be good to spend some time looking into how Cappuccino decides where events are sent, how first responder status passes between views, and how windows become "key."

Injecting console logging into Cappuccino AppKit

One can attempt to manipulate the object files in the cappuccino root installation folders and create new projects, but in my experience, this does not cause your changes to take effect in the new application.  If you look at the application folder itself, you will notice that the class files like CPWindow.j and CPResponder.j are not present. It is my guess that these are optimized into single Foundation.j and AppKit.j files at the time the project is created (or more likely at the time the Cappuccino application is first built, since changes to the object files in the cappuccino folder have no effect on newly created applications). Since we cannot change the object file, and it is not practical to edit the optimized AppKit.j file, we will have to be clever.

Objective-J gives us a feature called "Categories".  In the Objective-J tutorial on the Cappuccino-Project's website, we learn that we can extend and build upon any existing class by writing a new @implementation section and naming a "category" in parenthesis.  As it turns out, if a category is loaded that has a function which was previously used, it overwrites the previous function.  For example:
@implementation AClassDefinition : CPObject
-(CPString)aFunction
{
    return "A";
}
@end
@implementation AClassDefinition (OverwrittingFunctions)
-(CPString)aFunction
{
    return "B";
}
If the code above were used to create an instance of an AClassDefinition object, calling the function aFunction would return the string "B".

We can use this to inject console.log statements into the code. We copy the class files from cappuccino's folder to our project as if it were a new object.  Remove all functions, variable definitions and import statements, but leave all functions that regard our event processing, and add "debugger" lines and console.log lines as you see fit.  Remember, to remove the inheritance code (": SUPERCLASS") when you are adding your category name, and make the category name anything you like. When you import this modified file into your AppController after the "@import <AppKit/AppKit.j>" line, it effectively overwrites the original code.

The natural thing to do in this process is to start with CPResponder class. In CPResponder class, I placed the following line at the beginning of each function to see what functions were being called in what order:
//the string value is customized for each function.
console.log( "[" + [self description] + " functionName:param1 functParam2:param2]" );
We must remember that not every class will use this superclass's function implementation. Some classes will write over the method with their own implementation.  So, we must look through the children objects that we are testing to determine if any of the children must also have console logging code inserted.  Ultimately, for my initial tests, I modified CPResponder, CPWindow, CPView, and CPTextField; you should modify whichever objects you personally have chosen to implement in your testing program.

Looking at Cappuccino Behavior

mouseDown event

As I run the application I have created (not included in this review), I am overwhelmed by the number of events that are firing as I drag my mouse across the page.  Because of the sheer simplicity of it, I decide to focus first on mouse events.  With so many events, it is difficult to determine where the last event fired prior to our test, so I used the events themselves as markers.  I drag my mouse to the test location, and I wait.  If I have done this correctly, I can see that the last event in the console log is a mouseMoved event.  As long as I do not move the mouse again, that last mouseMoved event becomes a marker indicating the beginning of a new series of processes.

Clicking the mouse, I see a lot of behavior. There is some requests regarding "acceptsFirstResponder" to the view that is immediately below my mouse, and then begins a series of mouseUp event handlers starting with the top-most layered CPView and trickling down to the displayed superviews holding the view that first responded.  Interestingly, there is even a special view which we don't often see in the conceptual layout of Cappuccino. Behind the window's contentView there is another view which handles displaying the window itself.  After the window's view, the window itself responds to the event and then the Application as a whole.

Inserting a debugger line into the code allows us to look at the stack and really dissect the process.  While one might think that we would be debugging the mouseUp event, as this is the only event that displayed in the console window, the story actually starts with mouseDown, as it rightfully should. After all, there cannot be a mouseUp without a mouseDown first.  Displayed below is the sequence diagram showing the communication of these mouseDown events.

The CPPlatformWindow class is not one I had noticed before this analysis.  This class determines which CPWindow is best situated to handle the event, and sends the information wrapped in a CPEvent object to the CPApplication class by the method sendEvent:. In my test application, I used a CPWindow and two CPPanel views to make these observations.  The Window or Panel immediately below the click event is the one that was selected to respond to the event.

Sequence diagram for mouseDown designed in MagicDraw PE
After the platform window starts the process, the following happens:

  1. The application asks the event for the id of the window which is affected.
  2. The event responds with a window identifier.
  3. The application passes the event to the window via sendEvent. From this point, the window controls the event handling.
  4. The window then asks it's own view to look at the coordinates of the mouse click to determine the subview which is both displayed and on the top-most layer. We will call that subview "topView."
  5. The window then asks that topView if it will accept the role as first responder calling [topView acceptsFirstResponder].
  6. If the topView responds YES, the window calls itself with makeFirstResponder:topView
  7.  (We will look at this in more detail a moment later).
  8. Next, the window inspects itself to determine if it is the active window (in other words: the "key" window).  If NO, then it calls a function in itself to become the key window and raise the window to the top of the view stack.
  9. Lastly, the mouseDown event is passed to topView, which in turn passes it "down" to it's superview, and so on, all the way back to the AppController.  This is done rather generically by using the function _performSelector:_cmd withObject:_object.  For this event, _cmd is set to the mouseDown selector, and the passed reference to the object is the event.  Each instance of _performSelector calls _performSelector on the "_nextResponder", which is invariably assigned to the superview.
We shall look more closely at some of these processes.  An important note appears in step 9 above.  The event is passed to the _nextResponder, and not necessarily to the superview as we often expect.  When adding a subview, the CPView object automatically sets the _nextResponder attribute of the subview to point to the superview's self.  However, this behavior, like all behaviors can be changed if ever we need it to be.

Some interesting points to observe are the following:
  • The Window is responsible for dispatching the event to the "first responder".
  • The "first responder" is not necessarily set until the event is actually processed. If your object responds "YES" with the acceptsFirstResponder method call, then your object is given an opportunity to make the firstResponder hand-off.
  • Events naturally pass through the entire view hierarchy all the way back to the application itself, giving us multiple points at which to determine how to best handle the event that is being passed.

mouseUp

The mouseDown process was relatively involved.  Does every event go through all that processing?

The answer is, No!  They certainly don't.  This is seen in the handling of mouseUp:. The event handling for this event is much shorter and less complex.  All the heavy lifting was handled when the mouse was put into the mouseDown position.  The window was already made key, and the first responder already changed hands.  It is generally assumed, apparently, that not much happens between the time that the mouseDown and the mouseUp events occur, and anything that does happen can be handled by the mouseDragged events.

As a result, mouseUp does not check the key window, and it does not check the first responder, it just responds.  The window identified by the CPPlatformWindow receives the event, identifies it as a a mouseUp event, and then immediately calls the _processSelector:_withObject: method on the firstResonder last selected.

The process looks like this:
Sequence Diagram for mouseUp created in MagicDraw PE

[CPWindow makeFirstResponder:aResponder]

Earlier in this overview, we read that the "topView" has the "opportunity" to become the first responder. The window passes the topView to the method which handles this transfer of responsibility. Many people refer to this process as the "hand-over", and we will shortly see why.

The very first thing that the CPWindow does is looks at the topView and compares it to the first responder. If the topView is already the first responder, then we save a lot of pointless processing and just return a success value (YES).

If the topView is different from the current first responder, the window asks the current first responder for permission to transfer the first responder role to a new location. If the first responder is "selfish," it can elect to hold on to the first responder status, and no change is made. This often happens with CPControl fields which need to maintain focus under certain conditions. In this case is up to the control to determine whether it is an appropriate time to release the first responder status. If the control decides to keep the control, the window makes no change to the first responder identity and responds with a failed transfer status (NO).

If the topView is not the first responder and the current first responder gives permission to transfer the status, the window next asks the topView if it is willing to become the first responder.  It asks the question three different ways.  First, it ensures that the proposed first responder is not Nil. (It would not make sense to make a non-existent object the first handler for an event.) Second, it asks the topView if it will accept the role by calling acceptsFirstResponder.  The default behavior for this function is to return NO, but you can override this in your classes. Third, the window asks the topView if it can become the first responder by calling becomesFirstResponder. The default behavior for this function is to return YES, but, again, this can be overridden.  If the topView responds NO to either function call, then the window respects the object's decision to pass on the opportunity to be the first responder, and it sets its self (the window) to be the first responder and returns a failed transfer status to the calling method (NO).

If we have passed all the previous checks, then we know the following:
  1. the proposed responder is not the same as the current responder
  2. the current responder has given permission to transfer the role to a new object instance
  3. the proposed responder, which is not a Nil object, has indicated two separate times that it will accept the responsibility of being first responder.
All of these conditions being true, the window sets the topView to the first responder status and then sends a notification to the notification center that the first responder has changed (Note, this provides an opportunity to any objects that are listening for this notification to "steal" the first responder status from the newly assigned first responder). The very last thing the window will do after making the change and sending the notification is return a successful transfer status (YES) to the initiating method.

I find it interesting that the notification center is only notified of the first responder change when the first responder change is made to the suggested new responder.  If the suggested new responder refuses the role, the first responder changes from the current responder to the window itself, but no notification is sent to the notification center.

The entire process is outlined in the Activity Diagram here:
Activity diagram depicting makeFirstResponder: is prepared in MagicDraw PE

Thursday, June 19, 2014

Communicating With CPWebView

Special thanks to Alexander Ljungberg for pointing me in the correct direction for this exploratio into event handling and Cappuccino <-> iFrame communication.

We learned earlier that we can add custom HTML to a webpage, if we choose, by using the CPWebView class.  This class adds an iframe to our document.  IFrames are particularly difficult to work with in javascript if you have not done so before, so we must review how to do some basic things with our iframe tags and make them more functional.  This tutorial regards javascript more than cappuccino, but it will be useful in your overall Cappuccino experience.

In this tutorial, we will primarily learn:

  • How to access the DOM of the iframe
  • How to pass messages between the iframe and Cappuccino

To learn these things, we will be building a custom text editor to introduce a editable wrapping text field into our program, that does not rely on the <input /> form field in order to edit the string data. This tutorial is for the purpose of learning about Cappuccino and how it relates to javascript. If you are just interested in a text editor, I highly recommend WKTextView, which we review in this blog in an earlier post covering WKTextView and CPWebView. (The input field of CPTextField is limited to a single line in all browsers, so the text does not wrap while being edited, and only displays as being wrapped after all the editing is done.)

Our WebView

Let us create a web view to display a text area.  Later we will abandon the text area, because the text area does not allow us to display text of differing formats.  However, as far as wrapping and editable text is concerned, the text area will server our purposes well (at first).

In AppControler.j, add the following to the applicationDidFinishLaunching method:
/*****************
 textview will be our Cappuccino object. It is an instance of CPWebView. When we create it, we define the origin and size by using CGRectMake. This web view is located at (10,10) and has dimensions of 500 x 500.
********************/
var textview = [[CPWebView alloc] initWithFrame:CGRectMake( 10, 10, 500, 500 )];
/********************
 our html will be hard coded for now.  We create a textarea element, and set the style settings so that it fills the entire available display area.  So that we know where the textarea is, we will color our text area "lightgreen"; this will make it easy to see on our white page background, which exists outside the web view. We use the style property of the textarea so that we do not have to worry about accidentally affecting other elements within the page.  Because iFrames are independent web page views, the style applied here cannot effect other areas of the document; it is entirely self contained. However, this is a safe practice anyway, especially when we are generating our code programmatically. On the other hand, setting style on an element by element basis restricts our ability to make page-wide changes later by changing the css.
*********************/
var textareaHTML = "<html><body><textarea style=\"background-color:lightgreen; position:absolute; left:0; top:0; width:100%; height:100%; border:none;\" id=\"textarea_7129\" placeholder=\"Enter some text here.\"></textarea></html>";
/*******************
now we can apply the HTML string to the web view
********************/
[textview loadHTMLString:textareaHTML];
/******************
and add the web view to the Cappuccino contentView variable defined earlier within the applicationDidFinishLaunching method.
******************/
[contentView addSubview:textview];

Load your application, and you will see the following. Note, mine still shows the "Hello World!" text, because I did not remove the label.  If you have removed the label from your default application, your page will appear slightly different:
Our <textarea> displayed with a CPWebView as seen in Google Chrome.

Javascript, the Console, and getting to our new DOMElement

A Note On The Environment Used in These Screenshots

If you are using Google Chrome, you are able to open the console (part of the Developer Tools, which is included in all Google Chrome installations).  We can play with the page from here and make changes using javascript.  Sometimes this is a good place to experiment with code ideas or just to get familiar with the structure of the DOM (Document Object Model).  If you are not using Google Chrome, you may have an alternative method of doing these same tests; worst case scenario, edit the HTML on your index.html page and add a <script> element that you can modify directly.

Understanding the Console while Playing with our Objects through Javascript

First, let's play a bit to see how this works:

If we type a variable into the console and hit enter, the console will display the contents of the variable and then offer us a new prompt for further instructions.  If the variable content is an object, then the console gives us a series of drill-down options to open each object and view the attributes of the object.  In this screenshot, we see that the #document object contains two nodes: "<!DOCTYPE html>" and another DOM Element containing the contents of the "<html...>...</html>" tags.
We can call functions as well.  For example, in javascript, if we don't know the name or id of an object, we can use the tag name to get an array of all the elements having that tag name in the DOM.  In the next screenshot, we look for all iframes and return an array, which is displayed in JSON notation as: [ arrayElement1, arrayElement2, arrayElement3, ... ]
The array contains javascript objects, so much like the document, we can drill down into the iframe located at array index 0.  We also can assign these values to variables that can be recalled later, just like in javascript, these variables become globally scoped objects that exist within the page.  Essentially, any javascript entered into the console is run as if it were written on the main page.  We can create, destroy and access all javascript objects which would normally be available to us through the <script> code employed on the page or in a linked javascript code snippet.
You will notice that the returned value from our variable definition is "undefined".  This is normal and expected. When we ask the console to display the value of our new variable, we see that it is the iframe we found previously, but because we pulled just the [0] element from the array, it is the iframe itself and not the array we looked at in the previous screenshot.

Getting to our DOMElement Object for the <textarea> field

What happens when we try to get our textarea object?  We know the id, because we hardcoded it as textarea_7129. Let's try to get it through the console using javascript:

We try two times to get the textarea, but both times we fail!  The function getElementById does not return any result, so we look by tag name and find only one textarea, but that textarea is not the one we defined.  We know that our textarea exists, because we can see the light green area on our screen; we can type into it; and we can manipulate it as we would any textarea element.

Our failure should not be too surprising if you remember one thing: iframe elements are containers of completely separate web pages.  Their content is not part of our current DOM, which means that it is not available through the document variable.

We must first get access to the correct document variable by asking the iframe to provide it.  Remember how we saved our iframe into the variable pageFrame?  We will use our pageFrame variable to interact with this other webpage:
Now we have success!  We first asked the iframe (through the pageFrame variable) to provide us a link to the contentDocument.  Then, we used that object to search for DOM elements having the id "textarea_7129", and we received one such element, which can now be modified as we see fit.

We can now use this element to set event handlers as well.  For example, let us create an event handler that shows us the event fired for every key down and key up event.  We will send the events to the console to be logged.  Type the following javascript into the console:
var ourTextArea = pageFrame.contentDocument.getElementById("textarea_7129");
ourTextArea.onkeydown = function(e){ console.log("KEY DOWN"); console.log( e ); }
ourTextArea.onkeyup = function(e){ console.log("KEY UP"); console.log(e); }
Now, when you type in the text area, you see the console fill with a series of KEY DOWN and KEY UP messages. When we look at the object listed immediately after the KEY DOWN and the KEY UP message, we see the contents of the event that was sent to the event handlers "onkeydown" and "onkeyup".  (NOTE: with javascript event handlers, the first argument is always the event itself.  You can add additional variables to these functions to gain access to other objects that are also passed to the handler. Even though these objects were passed, we chose not to have access to them, because we did not provide a variable name for javascript to attach the objects to.)

For my experiment with our new event handlers, I typed the following three key combinations into my text area:
  • j
  • Shift + j
  • Command + j
I'm using a Macintosh to do these tests.  You may not have a Command + j option if you are using a different system, but you might have another alternative like Window + j.  Let's look at the output in the console:

Understanding Our Output

If you are satisfied with just knowing how to attach events as we did above, then you can skip this section and move to the next.  The key combinations used in the example, reveal a fascinating and important fact about how events are triggered for onkeydown and onkeyup event handlers.  This section analyzes the output from our test and explains some important details about processing keyboard events.

For our very first character, we see a KEY DOWN and a KEY UP sequence from me pressing and releasing the "j" key.  Expanding the event shows that the key I pressed down is identified by keyCode 74 (equating to the "j" key), and that there were no other keys pressed at the same time.  Similarly, the key up event shows the same.

For our second character combination, I have four events.  Two KEY DOWN events, followed by two KEY UP events.  The first KEY DOWN event has a keyCode of 16, a keyIdentifier of "Shift" and shows that no other keys are pressed at the same time.  The second KEY DOWN event looks just like our lower-case j event, except that the field "shiftKey" now equals "true" instead of "false" as it did when I was just typing a lower-case j.  Next, we see the first KEY UP event, which is attached to the "J" key release, and after that we see the second KEY UP event, which is attached to the release of the "Shift" button.

For our third character combination, we see something very different from our SHIFT+J example.  We see two KEY DOWN events, but just one KEY UP events.  Did I release the keys simultaneously?

No. Actually, this is an important thing to notice. The "metaKey" named Command on Mac computers, actively suppresses the key up event on keys that are pressed while it is active.  So, we see a KEY DOWN for the pressing of the Command button identified with a "keyIdentifier" value of "Meta". Next, we see a KEY DOWN for our keyCode 74 ("J") key.  Then we see a KEY UP for our "Meta" key again.  There is no KEY UP for keyCode 74 in this instance.  Also, just as we could tell from the J event with the SHIFT example that the shift is currently depressed through the (BOOL)shiftKey value, we can tell if the meta key is depressed in this event example by looking at the (BOOL)metaKey value.  In this example, for the J event, "metaKey" = true.

For most keyboard keys on the USA layout, the keyCode relates directly to the ASCII value for the capital character on the key face.  ASCII 74 translates to "J".  This can be troublesome if you forget that if "shiftKey" is set to false on the event that we actually want a lower-case "j", which is ASCII 106.  It is up to the programmer to be responsible and check the values of shiftKey, metaKey, ctrlKey and altKey to determine the combination of keys that might be simultaneously depressed. And to respond to those key down and key up events which are triggered in the process of creating a key combination.

For a complete list of keyCodes and their corresponding ASCII values, refer to this handy reference provided by Adobe on their website: Tables of keyCodes and ASCII Values

Getting Values and Setting Values in our TextArea

A word of caution

Now that we know how to access our textarea with javascript, we can get and set values.  Before we continue, I must give a word of caution.  You probably already know this, but reminders are always nice: Browsers will render content in the DOM differently.

I am using Google Chrome for this example, so setting or changing the innerHTML value of my textarea has no effect on my displayed information.  However, in other browsers, I can use innerHTML to display the text value of the textarea on the page, or even to display editable formatted text.  It is because this behavior is not universal that we will look at creating editable text without a textarea element in the next section, but for now, we will play with our text area element in Google Chrome, and you will need to make adjustments to handle your browser of choice or before making any code for a production ready implementation which must handle all browser makes and models.

Getting and Setting Values

Now that we have stored our textarea DOMElement in the ourTextArea variable, we will use this to get and set values into our display.  Enter the following lines of code into the console and see how your text field responds. Enter them one at a time.

ourTextArea.innerHTML = "TEST HTML";
ourTextArea.outerHTML; //display the current tag plus the contents within
ourTextArea.innerHTML = "";

You will notice that after setting the innerHTML value our string "TEST HTML" appears between the open and close tags of the textarea element.  In Google Chrome, this does not affect the display.

ourTextArea.value = "TEST VALUE";
ourTextArea.outerHTML;
ourTextArea.value; //display the contents of this variable to the console log.
ourTextArea.value = "";

This example of manipulating value does have an effect in Google Chrome.  As of this writing, W3Schools reports that the textarea.value property is recognized by ALL major browsers. (textarea.value page at W3Schools)

ourTextArea.textContent = "TEST CONTENT";
ourTextArea.outerHTML;
ourTextArea.textContent; //display the contents of this variable to the console log.
ourTextArea.textContent = "";

According to W3Schools, textContent is utilized by browsers when dealing with XML pages (textContent page at W3Schools).  If you have chosen to use your WebView to display XML, then this might be more relevant to you.  W3Schools also recommends that the nodeValue should be used instead of textContent (nodeValue page at W3Schools). 

As you play with Google Chrome, entering these lines of text, you will notice that Google Chrome exposes the entire object and all that is available to you, which is how we stumble on things we might not have seen before, such as the textContent field.  I am always a proponent of playing with these objects in order to learn more about what they can do and how they are used.

Editable Text with Formatting!

If you are trying to create a more complex editor, and you do not wish to use Google's editor, implemented in the Cappuccino Object WKTextView (downloadable from git, and covered in this blog in a previous article on CPWebView), then you will need to stretch beyond the textarea element.  While textarea can display formatted text in some browsers, it fails in my favorite browser, so this gives us an excuse to play with events and iframes to create something different.

We are going to create a web view that has NO content, and we are going to put text into it.  First, replace the HTML definition used above with the following:
var textareaHTML = "<html><body></body></html>";
This just creates a basic iframe with a blank page.  We will reload our project, and use the console to make some changes.
How your page should look before proceeding.
On loading the program, we notice that our green area is gone and our text "Hello World!" is still partially covered by "something."  Our CPWebView is in the same exact location and frame as it was before.  The green is gone, because it was the textarea that was green, and we removed the textarea.  We also have lost the ability to type into our CPWebView, because the textarea element provided all that functionality.

What we have in front of us is a "world of possibilities."  We have a blank web page, which means that we can add text and remove text at will, and we can put it in any formatting that we choose.  Furthermore, we have complete formatting control, because (chances are) you are extremely familiar with HTML and CSS.

Using what we learned before, we can use javascript to prove this to ourselves.  Type the following into the console:
var frame = document.getElementsByTagName("iframe")[0];
//this only works, because we have only one webview, if there were more, we would need to cycle through them to find the correct one.var frameWindow = frame.contentWindow;
var frameDocument = frame.contentDocument;
var htmlBodyInFrame = frameDocument.getElementsByTagName("body")[0];
htmlBodyInFrame.innerHTML = "<h1>Header Formatted Title</h1><p>Body paragraph with <b>bold</b>, <i>italic</i>, and <u>underlined</u> text.</p><p style=\"font-size:18px;\">A paragraph at 18px font size.</p><hr/>";
 This produces:
We see that we can use javascript and the DOMElement.innerHTML property to set and get the value of the web view text with formatting.  This is fantastic, but it is not editable!  How can we use this as a text editor if we cannot type into the page?

We have several options, but the first thing that I want to consider is this: What if we consider the innerHTML text string a document that can be edited, and we use event handlers to record key presses and add to the string, which we then repopulate with each key stroke?

If we can do this, we can add text.  If we program our event handlers to recognize the backspace key as an indication to remove characters, then we can shorten that string element by element.

Let's try this.

Type the following into the console:
var htmlBodyInFrame = document.getElementsByTagName("iframe")[0].contentDocument.getElementsByTagName("body")[0];
var innerHTML = htmlBodyInFrame.innerHTML;
var keyPress = function( e, oldText ){
     var event = e || window.event; //for IE < 8 compatability
     var keyCode = event.keyCode;
     var shiftValue = ( event.shiftKey ? 0 : (97 - 65) );
     var shiftKey = event.shiftKey;
     //if shift is not pressed, we add the difference between "a" and "A" to get the lower case value of any character
     var letterOrNumPressed = ( event.keyCode >= 65 && event.keyCode <= 90 ? true : false );
     var backspace = ( event.keyCode == 8 ? true : false );
     var tabChar = (event.keyCode == 9 ? true : false );
     var enterChar = (event.keyCode == 13 ? true : false);
     var spaceBarPressed = (event.keyCode == 32 ? true : false );
     var punctuation = ((event.keyCode >= 186 && event.keyCode <=191) || (event.keyCode >= 219 && event.keyCode <=222) ? true : false );
     var ASCIIvalue = keyCode + shiftValue;
   
     /*****
       we will stop this event from going any further after it is processed, or else our backspace
       will be interpreted by the browser as a "Go Back" command.
     ******/
      //if this property does not exist, it will now be created.
    //debugger;
   
     e.cancelBubble = true;
     if( e.stopPropagation ){
        e.stopPropagation();          e.preventDefault();      }
     if( backspace ){
          return oldText.substr( 0, oldText.length - 1 );
     }else if( tabChar ){
          return oldText;
      }else if(enterChar){
          return oldText + "<br/>";
      }else if(spaceBarPressed){
          return oldText + "&nbsp;";
      }else if( letterOrNumPressed){
          return oldText + String.fromCharCode( ASCIIvalue );
      }else if( punctuation & !shiftKey ){
           var punctChar = "";
           switch( keyCode ){
               case 186:
                      //semicolon
                      punctChar = ";";
                      break;
               case 187:
                      //equal sign
                      punctChar = "=";
                      break;
               case 188:
                      //comma
                      punctChar = ",";
                      break;
               case 189:
                       //dash
                      punctChar = "-";
                      break;
               case 190:
                      //period
                      punctChar = ".";
                      break;
                case 191:
                       //forward slash
                       punctChar = "/";
                       break;
                case 192:
                       //grave accent
                       punctChar = "`";
                       break;
                 case 219:
                       //open bracket
                       punctChar = "[";
                       break;
                 case 220:
                       //close bracket
                        punctChar = "]";
                        break;
                  case 221:
                       //single quote
                       punctChar = "'";
                       break;
          }
          return oldText + punctChar;
      }else if( punctuation & shiftKey ){
           var punctChar = "";
           switch( keyCode ){
               case 186:
                      //shift semicolon
                      punctChar = ":";
                      break;
               case 187:
                      //shift equal sign
                      punctChar = "+";
                      break;
               case 188:
                      //shift comma
                      punctChar = "<";
                      break;
               case 189:
                       //shift dash
                      punctChar = "_";
                      break;
               case 190:
                      //shift period
                      punctChar = ">";
                      break;
                case 191:
                       //shift forward slash
                       punctChar = "?";
                       break;
                case 192:
                       //shift grave accent
                       punctChar = "~";
                       break;
                 case 219:
                       //shift open bracket
                       punctChar = "{";
                       break;
                 case 220:
                       //shift close bracket
                        punctChar = "}";
                        break;
                  case 221:
                       //shift single quote
                       punctChar = "\"";
                       break;
          }
          return oldText + punctChar;
      }else{
          return oldText;
      }
}
htmlBodyInFrame.onkeydown = function(e){
   innerHTML = keyPress( e, innerHTML );
   htmlBodyInFrame.innerHTML = innerHTML;
}
When we attempt to type into our non-editable web view with this new event handler, we get the following.  It's not perfect, but it is a huge start.  The text in my example image addresses some of the issues we currently face with the above implementation.

 This is much closer to a format friendly text area.  We know from previous examples that we can insert html into the innerHTML string and produce text that displays in a controlled HTML format.  We also now have the ability to type into a non-editable window and effect a stored string.  There are some problems with this implementation, which we will address in future page updates.  Until then, I am posting this for others to benefit from the tutorial as it stands in its current state.  Come back later and see how we have modified the tutorial to improve on this editable text area.

Thursday, June 12, 2014

Communicating with the Server

This article and tutorial continues from the article on data serialization. I have modified the code slightly for the purposes of this demonstration. In this particular example, we make use of a PHP server script. You will need to make modifications to the code if you are using a different server side script or if your script is located at a different URL than the one described in the code.

Synchronous Data Requests

To the code that we finished the previous tutorial with, add the following at the end of the applicationDidFinishLaunching method:
/**********
     send data to server
     **********/
    var urlReq = [CPURLRequest requestWithURL:@"http://localhost/DataTransfer/backend.php"];
    [urlReq setHTTPMethod:@"POST"];
  
    var jsonServerResponse = [self getServerResponse:urlReq useSerializedData:_JSONString],
        P280NServerResponse = [self getServerResponse:urlReq useSerializedData:_280String],
        XMLServerResponse = [self getServerResponse:urlReq useSerializedData:_XMLString];
    console.log( "JSON" );
    console.log( jsonServerResponse._rawString );
    console.log( "P280N" );
    console.log( P280NServerResponse._rawString );
    console.log( "XML" );
    console.log( XMLServerResponse._rawString );
Also, add the following method to AppController:
-(CPString)getServerResponse:(CPURLRequest)urlReq useSerializedData:(CPString)string
{
    [urlReq setHTTPBody:string];
    return [CPURLConnection sendSynchronousRequest:urlReq returningResponse:Nil];
}
If you are running PHP on your server, then in your web root, create a file called 'backend.php', and put into it the following code:
<?php
echo $HTTP_RAW_POST_DATA;
?>
Run the application, and you will see the following:

What is more important is the console's display.  Open the console and reload the page to see the following:
What we have done here is the following:

We created three serialized strings. We created a url request object, and set the method to POST, because POST methods do not contain the content within the url string and are considered more secure than GET methods. Then we added our string to the body of the url request and used a url connection object to send the data to the server at the url address encoded in the url request.  The server processed the string, but for those familiar with PHP, we note that the data was not parsed by the server side script in the usual manner (we will address this a little later). Rather than store the data, we simply put the received data in the response data so that our program has access to it once again. Doing this, we simulate both the 'sending' and the 'retrieving' actions which you will need to communicate with your server applications.

Asynchronous Data Requests

The class-scoped method used above sends a synchronous data request. We started with this, because it is simple and often all that is needed. Sometimes, we want to make the request and let the processing continue while we wait for the response. This is called an asynchronous request.

When we make asynchronous requests, the system gets just a little more complicated.  In a synchronous request, we dump the result directly into a variable and continue. With an asynchronous request, we are continuing before we have data to process, so we must use a delegate. With a delegate, the url connection will send updates as the server sends responses.  On the final response, the delegate is able to grab the data and put it in the intended container as well as start any processing methods to handle the data.

The delegate functions called by CPURLConnection are as follows:

  • -(void)connection:(CPURLConnection)connection didReceiveData:(CPString)data;
  • -(void)connectionDidFinishLoading:(CPURLConnection)connection;
  • -(void)connectionDidReceiveAuthenticationChallenge:(id)connection;

We implement these methods in any object we choose and set that object as the delegate, and we are then able to receive data, be notified when the URL has finished loading and be notified when the URL responds by requesting authentication from the user.

For now, we are just interested in receiving the data, so lets add the following implementations to the AppController.j file:
@implementation ASyncLogger : CPObject
{
    CPString indicatorText;
}
-(id)initWithString:(CPString)text
{
    if( self = [super init] ){
        indicatorText = text;
    }
    return self;
}
-(void)connection:(CPURLConnection)connection didReceiveData:(CPString)data
{
    console.log( "DATA RECEIVED FOR " + indicatorText );
    console.log( data );
}
@end
@implementation JSONLogger : ASyncLogger
{
}
+(id)init
{
    return [[JSONLogger alloc] initWithString:"JSON"];
}
@end
@implementation P280NLogger : ASyncLogger
{
}
+(id)init
{
    return [[P280NLogger alloc] initWithString:"P280N"];
}
@end
@implementation XMLLogger : ASyncLogger
{
}
+(id)init
{
    return [[XMLLogger alloc] initWithString:"XML"];
}
@end
Change the data communication section of applicationDidFinishLoading to the following:
    /**********
     send data to server
     **********/
    [self useSerializedData:_JSONString withDelegate:[JSONLogger init]];
    [self useSerializedData:_280String withDelegate:
        [P280NLogger init]];
    [self useSerializedData:_XMLString withDelegate:
        [XMLLogger init]];

We are not using the same url request for each call any more.  With asynchronous requests, it is best that we keep the elements separate so that we do not alter any ongoing actions.  Add the following method to the AppController class to create and send the url requests:
-(void)useSerializedData:(CPString)string withDelegate:(id)aDelegate
{
    var urlReq = [CPURLRequest requestWithURL:@"http://localhost/DataTransfer/backend.php"];
    [urlReq setHTTPMethod:@"POST"];
    [urlReq setHTTPBody:string];
    [[CPURLConnection alloc] initWithRequest:urlReq delegate:aDelegate startImmediately:YES];
}
When we run the code, we see the following in the console:

This is a success. We see that the data service calls are successful, because our new objects are listing data to the console along with the "DATA RECEIVED FOR XYZ" text that is unique to those new classes. We can prove that this call is asynchronous by adding the following lines to our PHP code:
    if( preg_match('/^[{]/', $HTTP_RAW_POST_DATA) ){
                   sleep(5);
    }
When we re-run the script, the output in the console changes to this:
 The JSON data is now listed after the other data, but why is this so important, and how did it happen?

The additional code that we added to the PHP script was a time waster.  It checked the post data for the JSON format by looking at the first character.  If the data passed is JSON, then the script goes to sleep for a short period of time and does nothing, but then it responds as normal.

We made our function calls in the same order as before. The JSON function call is made first, but because of the time wasting script, it takes much longer to processes. The other two server calls are made after the JSON call, and each is returned before the JSON call. The shows that the program was able to continue operating after the JSON call was made, and that it received the server calls independent of the order of the written code.

PHP "Anomalies" (Note:This information can be useful to other Server-side languages)

Earlier in the code, we saw an unexpected response from PHP. For those familiar with PHP, they might expect the PHP code to receive the data into the $_POST object. When the $_POST object is empty, you can always check the $HTTP_RAW_POST_DATA variable or dump the $GLOBALS variable to find your communicated data string.  According to the PHP manuals, data communicated in the form "key1=value1&key2=value2" in the raw data will be parsed into a key-valued array, but the parsing behavior only happens when we set the header field for Content-Type.

I have tried many values for Content-Type to try to cause the behavior.  So far, the only value that I have tried that is successful is: "application/x-www-form-urlencoded".  All others fail to cause the $_POST value to populate.

For the following example, I have changed the php code in the backend.php file to the following:
<?php
    var_dump( file_get_contents( "php://input" ) );
    echo "\n\n";
    var_dump( $GLOBALS );
?>

This script will dump the raw input followed by the full array of global variables available for use in the script.  In the first screenshot, we see the results of the Content-Type being set to "application/x-www-form-urlencoded".  In the second screen shot, we have changed it to one of several other settings.  An easily accessible list is available on Wikipedia's Mime Type page.

Content-Type: application/x-www-form-urlencoded
Content-Type: application/json
It is important to note that when the data is parsed into $_POST that it is no longer available in $HTTP_RAW_POST_DATA. If you are using server-side languages other than PHP, you should note that the data might go through similar processes. Take some time to familiarize yourself with your server-side language, or at the very least, learn how to access the raw, unprocessed data.  In PHP, accessing raw unprocessed data is as simple as reading from the file-stream "php://input".

Cappuccino Data Serialization

While it is possible to create client-side applications that exist entirely on the client-side and make no communication with a server, this is an unlikely scenario.  At some point, you will want or need to send data back to the server for storage or to initiate data retrieval.

Because Cappuccino is rooted in JavaScript, you can use traditional AJAX methods to make server requests.  You can even step this up a programming notch by loading the jQuery libraries in your index.html file and using jQuery to handle the AJAX, but there must be a Cappuccino way of handling these actions, and there is.

In this article, we will review various data serialization methods; later, we will look at server communication.

Data Serialization

I do not know of any way to communicate objects in their native state.  The alternative is to convert the object into a byte array or string that communicates its structure and attribute values.  This is the nature of Data Serialization.

In Cappuccino, there is one way to serialize data, which is built into the language.  This method creates a 280NPLIST (280 North is the name of the company that first started coding the Cappuccino framework).  This property list format is not interpreted by server-side languages, but it is amazing if you are just passing data for storage and retrieving it later so that all processing is handled by Cappuccino.

If you require the ability to parse and handle data on the server-side in addition to the client-side, then communicating data in a different property list format or JSON notation is more practical.  Cappuccino has methods for interpreting other pList formats as well as XML and JSON notation to make these alternatives more convenient.  Sadly, there is no easy method to generate these formats in the current Framework design.

Working with 280NPLISTs

Cappuccino gives us the CPKeyedArchiver and CPKeyedUnarchiver classes.  Each is derived from the CPCoder class, and each is used in handling 280NPLISTs.  The CPKeyedArchiver creates the format, and the CPKeyedUnarchiver reads the format to produce Cappuccino object results.

The coder is passed to your object for handling, as apposed to handling your object. This gives you the flexibility to control what gets saved and what is not saved.  To enable your classes to interact with the coder, add the following two methods:
  • -(void)encodeWithCoder:(CPCoder)coder
  • -(id)initWithCoder:(CPCoder)coder
In each case, the coder (a.k.a. the data container) is passed to the object.  In encode with coder, the user calls encodeObject:forKey: on the coder to insert data elements at various key locations, just like a CPDictionary.  In initWithCoder:, the method calls decodeObjectForKey: on the coder object in order to retrieve the value that was stored there.  Using these methods, we are able to use the coder like a filing cabinet.  We put all relevant data into the filing cabinet under certain file names, and then later, we attempt to get those items back by recalling the file names that we used previously.

What sets the CPKeyedArchiver apart from a CPDictionary object is this: the archiver is able to output a serialized string in 280NPLIST format to store the data contents in a database or file record. This data is not easily read by human eyes, but it is possible. We will see an example later.

JSON objects

JSON is an easy format to understand, and there are many resources available for those who need to study it.  We will not cover the format here.

The easiest method for converting an object into a JSON string is to call the javascript object method JSON.stringify().  However, this method does not work on all Cappuccino objects; specifically, it has particular difficulty with CPDictionary objects and custom objects. The closer to native javascript that code is, the easier it converts.  CPString, CPInteger, CPArray objects all convert very easily, because these are extensions of the javascript base classes.  CPDictionary however, creates a circular reference which is too much for the JSON object to handle in the stringify() method.

For this reason, it is a good idea to handle JSON creation on a case by case basis. I prefer to create getJSONFriendlyObject methods in my objects or a getJSONString method on the root objects. The getJSONFriendlyObject method can be used for nesting custom objects within other custom objects, and getJSONString is excellent on the base object to get around having to stringify the resulting structure.

Often creating an initWithJSON method is helpful, but for many Cappuccino objects, this method already exists.  The CPDictionary object has an often overlooked method.  Many people notice the instantiation method: +dictionaryWithJSObject:, but there is another one: +dictionaryWithJSObject:recursively:.  This second method allows the CPDictionary to traverse deeper into JSON objects that have multiple levels.  To create the JSON object for this method as well as all the other instantiation methods, simply call the method jsonFromString on the string value that is returned from your stored or communicated JSON text.

Here is an example that I use to extend the CPDictionary class to handle all basic data types.  This method works perfectly well when CPDictionary is holding arrays, strings and numerical values:
@implementation CPDictionary (JSON)
{
}
-(object)getJSONFriendlyObject{
    var keys = [self keyEnumerator],
        object = {},
        string = Nil,
        data = Nil,
        key = Nil;
    while( key = [keys nextObject] )
    {
        string = key;
        data = [self objectForKey:key];
        object[ string ] = data;
    }
    return object;
}
@end
I can now call this method on any CPDictionary (that is being used for simple means) and add the returned result to a basic javascript object.  Then, I can call JSON.stringify( result ) to get the string representation without the complications caused by Cappuccino's sometimes complex structure.

Property List Serialization

CPPropertyListSerialization is a class that creates other types property lists. We can even create a property list in XML format, which is both human readable and easily transferred to other server-side scripting languages.

This Cappuccino object handles CPDictionaries and other Cappuccino objects very well, but it fails when a JavaScript native object is passed.  Another negative aspect of this particular object and encoding method is that it works primarily on CPDictionaries and CPArray objects, so your data must be put into a CPDictionary first, and then passed to the Cappuccino object.

Examples in Code

Our examples need to look at several different scenarios.  We will look at the strengths and weaknesses of each of the methods.

Create a new project.  Remove the contents of the applicationDidFinishLaunching method.  Insert the following code:
    /**********
        test data transfer to and from server
        ********/
    var data = [[StorableData alloc] init],
        jsonObj = {};
    [self fillData:data];
    /*************
         Our data object is set up
         *************/
    var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
        contentView = [theWindow contentView],
        _280NPLIST = [[CPTextField alloc] initWithFrame:CGRectMakeZero()],
        _JSON = [[CPTextField alloc] initWithFrame:CGRectMakeZero()],
        _XML = [[CPTextField alloc] initWithFrame:CGRectMakeZero()],
        _280String = Nil,
        _JSONString = Nil,
        _XMLString = Nil;
    /**************
        Extract the data in three methods.  We use try blocks, for the case that any might fail.
        ***********/
  
    try{
        var coder = [CPKeyedArchiver archivedDataWithRootObject: data];
        _280String = [coder string];
    }catch( e ){
        _280String = "Coder Failed";
    }
  
    try{
        _JSONString = JSON.stringify( data );
        /**** temporarily removed
        var JSONObj = {};
        [data encodeJSON:JSONObj];
        _JSONString = JSON.stringify( JSONObj );
       ****/
    }catch( e ){
        _JSONString = "JSON stringify Failed";
    }
  
    try{
        _XMLString = [CPPropertyListSerialization dataFromPropertyList:data format:CPPropertyListXMLFormat_v1_0 ];
        //_XMLString = [data getXML];
    }catch( e ){
        _XMLString = "PropertyList Failed";
    }
  
    /***********
        Display serialization results
        *******/
      
    [self setStringValue:_280String atOrigin:CGPointMake(10,10) onLabel:_280NPLIST];
    [self setStringValue:_JSONString atOrigin:CGPointMake(410,10) onLabel:_JSON];
    [self setStringValue:_XMLString atOrigin:CGPointMake(810,10) onLabel:_XML];
  
    [contentView addSubview:_280NPLIST];
    [contentView addSubview:_JSON];
    [contentView addSubview:_XML];
    [theWindow orderFront:self];
Then add the following methods to the AppController object:
-(void)setStringValue:(CPString)dataString atOrigin:(CGPoint)frameOrigin onLabel:(CPTextField)label
{
    [label setLineBreakMode:CPLineBreakByWordWrapping];
    [label setFrameSize:CGSizeMake(350,200)];
    [label setStringValue: dataString];
    [label sizeToFit];
    [label setFrameOrigin:frameOrigin];
}
-(void)fillData:(id)data
{
    var array = [@"Array Item 1", 23, { "class":"anObject","data":"someData" }],
        string = @"This is a good string to use.",
        number = 546789.99,
        dict = [[CPDictionary alloc] init];
    //array = [@"Array Item 1", 23];
    [dict setObject:@"Dict Object 1" forKey:@"Object 1"];
    [dict setObject:53 forKey:@"Object 2"];
    [data setAnArray:array];
    [data setAString:string];
    [data setAnInteger:number];
    [data setADictionary:dict];
}
Within the AppController.j file, we will add two object definitions:
@implementation StorableData : CPObject
{
    CPArray anArray @accessors;
    CPString aString @accessors;
    CPInteger anInteger @accessors;
    CPDictionary aDictionary @accessors;
}
-(id)init
{
    if( self = [super init] ){
        anArray = Nil;
        aString = Nil;
        anInteger = Nil;
        aDictionary = Nil;
    }
    return self;
}
-(void)encodeWithCoder:(CPCoder)coder
{
    if( [super respondsToSelector:@selector(encodeWithCoder:)] ){
        //[super encodeWithCoder:coder];
    }
    [coder encodeObject:anArray forKey:@"anArray"];
[coder encodeObject:aString forKey:@"aString"];
[coder encodeObject:anInteger forKey:@"anInteger"];
[coder encodeObject:aDictionary forKey:@"aDictionary"];
}
-(id)initWithCoder:(CPCoder)coder
{
if( self = [super initWithCoder:coder] ){
if( self = [super init] ){
anArray = [coder decodeObjectForKey:@"anArray"];
aString = [coder decodeObjectForKey:@"aString"];
anInteger = [coder decodeObjectForKey:@"anInteger"];
aDictionary = [coder decodeObjectForKey:@"aDictionary"];
}
}
return self;
}
-(void)encodeJSON:(id)object
{
    object.anArray = anArray;
    object.aString = aString;
    object.anInteger = anInteger;
    object.aDictionary = [aDictionary getJSONFriendlyObject];
}
-(object)getXML
{
    console.log( anArray );
    var keys = ["aString","anInteger","aDictionary","anArray"],
        objects = [aString, anInteger,aDictionary, anArray],
        dict = [[CPDictionary alloc] initWithObjects:objects forKeys:keys],
        errorString = Nil;
    console.log( dict );
    var data = [CPPropertyListSerialization dataFromPropertyList:dict format:CPPropertyListXMLFormat_v1_0];
     return [data string];
}
@end
@implementation CPDictionary (JSON)
{
}
-(object)getJSONFriendlyObject{
    var keys = [self keyEnumerator],
        object = {},
        string = Nil,
        data = Nil,
        key = Nil;
    while( key = [keys nextObject] )
    {
        string = key;
        data = [self objectForKey:key];
        object[ string ] = data;
    }
    return object;
}
@end 
Load the application, and you should see the following:
For this first run, we see that the 280NPLIST handles the data perfectly well.  There is no issue with objects of any type.  However, there is difficulty in this.  It is not easily decoded, which means that if we pass the object to a server-side script, all we can really do with it is store it and retrieve it.  Processing this data is not convenient or easy outside having a Cappuccino Keyed Unarchiver decode the text.

The JSON stringify function failed as a result of the CPDictionary object, and the Property List failed to generate, because the data object was passed as a raw, unprocessed element.

Let's adjust this.  Change the try block for JSON to the following:
        //_JSONString = JSON.stringify( data );
        var JSONObj = {};
        [data encodeJSON:JSONObj];
        _JSONString = JSON.stringify( JSONObj );
And change the try block for XML to this:
        //_XMLString = [CPPropertyListSerialization dataFromPropertyList:data format:CPPropertyListXMLFormat_v1_0 ];
        _XMLString = [data getXML];
The JSON object now is what we would expect to see, but the XML property list still fails.

There is more to getting the JSON object to work than altering changing a few lines of commented code. This uncommented code calls a method that is part of the custom data object.  The method takes a simple javascript object and modifies it to model the data we need to store. We model the CPDictionary by extending the class to create a javascript object where each key is a property name and the object associated with the key is the property value.  The returned object is easily worked through stringify(), because it is native javascript.

The XML property list now is using a dictionary as is intended, but it fails anyway. One more alteration, and we will see the cause of the problem.  Uncomment the line of code that reads:

//array = [@"Array Item 1", 23];
When we reload the page, we see the following:
Hooray! We now have a XML property list, but at what cost?

By uncommenting the line of code that we did, we changed the value of the CPArray object and effectively removed just one element of the array.  This one element was causing the failure.  The element was not a Cappuccino object, it was a javascript object defined as follows (in JSON):
var troublesomeObject = {
   "class":"anObject",
   "data":"someData"
}
Naturally, it is up to each of us to know our own data structures, particularly when it comes to storage, so now that we know the limitations of the various serialization methods, we can choose a method that is right for us, or find a way to work around the defects.  None of the issues listed above is insurmountable.

Given the ease of working with JSON, I prefer that as a communication method.  Add a layer of encryption to encode the data in transit, and you have an easily readable, albeit slightly large (as compared to binary) format, that can be made and unmade with little complication but a little extra coding (each JSON object really should be constructed and deconstructed by "hand").

Wednesday, June 11, 2014

CPPopUpButton: A Combo Box

The name does not imply to me the behavior.  If Cocoa had named this object a combo box, then Cappuccino would have followed suit.  However, what this control does is what one would expect a combo box to do.

On adding this control to your view, one will see a field that has a blue arrow to the right.  This arrow changes in appearance depending on the initialization option.  Regardless the choice made on initialization, clicking on the control causes a list of items to appear, or pop-up, from which the user can make a selection.

There are two initialization options, which are obtained by passing one of two boolean values to the method initWithFrame:(CGRect)pullsDown:(BOOL).

Pop-up Button initialized with "pullsDown:NO"
Displayed options from a Pop-up Button
initialized with "pullsDown:NO"
Selecting "NO" provides a box which displays the current choice. Clicking the blue double arrow pops-up a list of choices, which indicate the current selection with a round dot.  Changing the selection, changes the displayed text in the control's primary view.


Pop-up Button initialized with "pullsDown:YES"
Displayed options from a Pop-up Button
initialized with "pullsDown:YES"
Selecting "YES" provides a box which displays a fixed text matching the first item in the list.  This item is never a viable selection.  Clicking the single, downward-facing arrow on the blue button drops a list from the control, which continues to remain visible while displaying what can be used as prompt text.  Making a selection does not change the displayed text in the control.

For each of these selection types, one can extract the items from the list by their index number and assign targets and actions to them so that when a user selects the option, a method of our choosing activates.  This can make the pop-up button a useful choice in initializing menu commands if one is apposed to using a menu bar object, or for propagating a sequence of changes to the form or other elements of the application based on the selection.

In the following example, we will create one menu of each, and we will assign actions to three of the selections.

Example

Start a new project, and remove the code that creates the text that reads "Hello World!" from the applicationDidFinishLaunching method.

To the applicationDidFinishLaunching method, add the following:
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(20, 200, 200, 20) pullsDown:NO];
[button addItemWithTitle:@"Selection Option 1"];
[button addItemWithTitle:@"Selection Option 2"];
    [button addItemWithTitle:@"Selection Option 3"];
    [button sizeToFit];
[contentView addSubview:button];
  
    var testMenuItem = [button itemAtIndex:2];
    [testMenuItem setTarget:self];
    [testMenuItem setAction:@selector(startListening:)];
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(20, 250, 200, 20) pullsDown:YES];
[button addItemWithTitle:@"Choose an action:"];
[button addItemWithTitle:@"Start Listening"];
    [button addItemWithTitle:@"Stop Listening"];
var menuItem = [button itemAtIndex:1];
[menuItem setTarget:self];
[menuItem setAction:@selector(startListening:)];
menuItem = [button itemAtIndex:2];
[menuItem setTarget:self];
[menuItem setAction:@selector(stopListening:)];
    [button sizeToFit];
[contentView addSubview:button];
Elsewhere in the AppController object, add the following methods.  If you do not like using the console to log messages, you can change these code lines to "alert()" function calls:
-(void) option3:(id)sender
{
    console.log("Option 3 selected");
}
-(void) startListening:(id)sender
{
    console.log("Start Listening");
}
-(void) stopListening:(id)sender
{
    console.log("Stop Listening");
Load the page, and you will have an opportunity to play with the various methods and choices.  As you change the option selection on each box, if you select the modified options, you will see messages that indicate each "action" was handled appropriately.

A list of all the available methods can be found at the API Documentation Page.