App Studio Plug-Ins

From KickApps Documentation

Jump to: navigation, search

The KIT Cloud Social App Studio supports two kinds of plug-ins:


Contents

Developing Plug-Ins

Prerequisites

Use pure AS3 (no Flex)

The App Studio plug-in framework does not support plug-ins that require the Flex framework. Plug-ins should be written in pure AS3.








Prepare your .swf for inclusion in the App Studio

The App Studio environment introduces some considerations that your .swf may not have been designed to accommodate. We recommend reviewing these three steps to ensure that your .swf works properly.

  1. If using AS3, ensure your .swf can technically be loaded into another .swf
  2. Add Security.allowDomain("serve.a-widget")
  3. If your .swf is framerate dependent, use a framerate of 24


If using AS3, ensure your .swf can technically be loaded into another .swf

If written in AS3, unchecked references to "stage" in the class constructor (or first frame of a timeline .swf) can keep the .swf from loading into another .swf like the Widget Studio. This is because in Actionscript 3, when one .swf loads another, Flash always initializes the loaded .swf before it is added to the display tree of the parent .swf. If you find your .swf works fine stand-alone but you receive a flash debugger error: "TypeError: Error #1009: Cannot access a property or method of a null object reference." when loading into the Widget Studio this is probably the issue. The solution is to wait for the stage property to become available before accessing it like so:

Constructor(){
   if (stage){
      onAddedToStage();
   } else {
      addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
   }
}

private function onAddedToStage(evt:Event=null):void {
   removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
   stage.align = StageAlign.TOP_LEFT;
   stage.scaleMode = StageScaleMode.NO_SCALE;
   initApp();
}

Another "stage" reference pitfall is sizing your .swf's internal content relative to stage.width/height or stage.stageWidth/stageHeight. This will reference the entire size of the widget studio instead of the size of your .swf. Please use the "root" property if needing a global reference to the top-most display object in your .swf file.

Add Security.allowDomain("serve.a-widget")

The App Studio generates thumbnails of widgets for use in the Widget Manager and for posting to Facebook and MySpace. In order for your ..swf to appear in these thumbnails, it must grant pixel access to the domain from which we serve widgets, serve.a-widget.com. You can do this by setting Security.allowDomain("*") or Security.allowDomain("serve.a-widget.com") in your .swf. This also prevents specific cross-domain security sandbox restrictions that may break functionality in your widget (see [[1]]).

If your .swf is framerate dependent, use a framerate of 24

KickApps widgets use a framerate of 24. If your .swf uses timeline based animations you may find that its animations run faster or slower unless it is published to this framerate which is also the default framerate of Flex and Flash.


Getting around

Get a reference to your plug-in's wrapper

Your plug-in is wrapped in a DisplayObject component. You can obtain a reference to this wrapper like this:

wrapper = this.parent;

// this is the component that wraps your plugin
private var wrapper: DisplayObject;


Access properties of KIT Cloud Social components

Many plugins need to know about various other components in the widget. To access another component, you first need to know its ID, which is the label given to it by the user in the Layers panel.

LayersPanelRename2.png


You can let the user specify the ID of the component with which they want your plugin to work by including an input field in your plugin's configuration panel:

PluginComponentIdInput.png

Then, include code like the following in your plugin to secure a reference to the component and access its properties. The following code sets up a reference to a text component:

		/**
		 * This property is injected by the wrapper that loads this plugin.
		 * You can use the kickAppsShell to access other components and widget properties.
		 * 
		 **/
		private var _shell: Object;
		public function set kickAppsShell(value: Object):void
		{
			_shell = value;
			
			if (value)
				findTextField();
		}


		private function findTextField(): void
		{
			if (_textFieldId && _shell)
				intervalId = setInterval(getTextComponent, 300);
		}

		
		private function getTextComponent(): void
		{
		    textField = _shell.getComponentById(textFieldId);  // the id is the label assigned in the App Studio
		    
		    if (textField)
		    {
			    clearInterval(intervalId);
		    }
		}

In the first function, we're adding a property called kickAppsShell, which signals the framework to automatically inject the shell into this property. The second function kicks off a timer loop that hits the third function every 300ms. This is necessary because the component you're looking for may load before or after your plugin; components don't load in a definite order.

Once you have a reference to the component, you can access its properties like this:

textField.width
textField.height

Please refer to the AS Docs for detailed documentation on each component. In some cases the internal name of a component differs from its productized name in the App Studio. Internal names, along with key properties, are listed in the Properties Reference.


Detect whether the widget is being edited or previewed in the App Studio

Preview mode is the state that the widget is in when the user clicks Menu > Preview in the App Studio. Editing mode is the state the widget is in when the user can edit it on the stage in the App Studio.

With kickAppsShell set per the previous section, you can access a boolean indicating whether the widget is in Preview mode like so:

kickAppsShell.model.previewMode == true

...and another indicating whether the widget is in Editing mode like this:

kickAppsShell.model.inDesignMode == true


Make your plug-in visible in fullscreen mode

When video players go into fullscreen mode, the video component expands to fill the screen and is moved to the top layer of the widget, eclipsing all other components. In order to make your plug-in visible within fullscreen mode, you'll need to implement a solution like the following:

// constructor
public function CustomPlugin()
{
    if (stage){
        onAddedToStage();
    } else {
        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    }
}

private function onAddedToStage(evt:Event=null):void 
{
   removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
   stage.addEventListener(FullScreenEvent.FULL_SCREEN, onFullScreen);

   wrapper = this.parent;
   
}

private function onFullScreen(event: FullScreenEvent): void
{
    if (event.fullScreen)
   {
     addEventListener(Event.ENTER_FRAME, unhide)
   }
   else if (originalParent )
  {
    addEventListener(Event.ENTER_FRAME, returnToOriginalPosition)

  }
    
}

private function unhide(event: Event): void
{
    removeEventListener(Event.ENTER_FRAME, unhide);
    originalParent = wrapper.parent;
    originalIndex = originalParent .getChildIndex( wrapper);
    stage.addChild(wrapper);
}

private function returnToOriginalPosition(event: Event): void
{
    removeEventListener(Event.ENTER_FRAME, returnToOriginalPosition);
    originalParent.addChildAt(wrapper, originalIndex )

}

// this is the component that wraps your plugin
private var wrapper: DisplayObject;
private var originalParent: DisplayObject;
private var originalIndex: int;


Access events issued by KIT Cloud Social players

Plug-ins can access the events issued by KIT Cloud Social video players and audio players by including the following in their packages:

import flash.events.Event;
import flash.events.IEventDispatcher;

With these dependencies injected, plug-ins can access any event in the kickAppsEventBus by adding event listeners. The following code shows a sample plug-in that prints the contents of various events to a text component. It also documents the events that are currently supported (as of 10/30/09).

package
{
	import com.bit101.components.Text;
	
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	
	
	// this plugin extends the Text component from the MinimalComps framework
	public class TextPlugin extends Text
	{
		/**
		 * This array contains the names of the media event types that the 
		 * KickApps players dispatch.
		 * 
		 * The events contain several important properties
		 * 
		 * This property contains different values for different events.
		 * For mediaTimeElapsed it contains the elapsed time.
		 * For mediaPercentComplete it contains the percent value.
		 * public var data: Object;
		 * 
		 * The URL for the media.
		 * public var source: String;
		 * 
		 * The KickApps media id.
		 * public var mediaId: String;
		 * 
		 * The title of the media.
		 * public var mediaTitle: String;
		 * 
		 * The duration of the media.
		 * public var mediaDuration: Number;
		 * 
		 * This property contains a reference to the object that originally dispatched this event.
		 * public var originalDispatcher: Object;
		 * 
		 * This object contains all of the metadata for the item that is currently selected
		 * public var mediaItem: MediaItem;
		 *  
		 * This property indicates which type of player dispatched the event.
		 * Potential values include "video" or "audio".
		 * public var playerType: String;
		 * 
		 **/		
		private var events: Array = [
						"mediaLoaded",
						"mediaPlaying",
						"mediaPaused",
						"mediaTimeElapsed",
						"mediaPercentComplete",
						"mediaStart",
						"mediaEnd",
						"mediaTimeUpdate"			
					    ];
		/**
		 * This property is injected by the wrapper that loads this plugin.
		 * You can use the kickAppsEventBus to send messages to and receive messages
		 * from other components in your application.
		 * 
		 **/
		private var _eventBus: IEventDispatcher;
		public function set kickAppsEventBus(value: IEventDispatcher):void
		{
			if (_eventBus)
				removeEventListeners();
			
			_eventBus = value;
			
			if (_eventBus)
				addEventListenters();
		}
		
		public function TextPlugin(parent:DisplayObjectContainer=null, xpos:Number=0, ypos:Number=0, text:String="")
		{
			super(parent, xpos, ypos, text);
		}
		
		
		private function addEventListenters(): void
		{
			for each (var event: String in events)
			{
				_eventBus.addEventListener(event, eventHandler, false, 0, true);
			}
		}
		
		private function removeEventListeners(): void
		{
			for each (var event: String in events)
			{
				_eventBus.removeEventListener(event, eventHandler);
			}
		}
		
		private function eventHandler(event: Event): void
		{
			this.text += event.toString() + "\n---------------------------------------------\n";
		}
	}
}


MediaItem & MediaModuleData

The mediaItem event shown in the code above returns an object containing all of the metadata for the item that is currently selected. The mediaItem object is defined as:

MediaItem
  authors: Array
  date: Date
  description: String
  enclosure: String
  enclosuretype: String
  hasParent: Boolean
  image: String
  kickAppsModuleData: com.kickapps.mediawidget.data::KickAppsMediaModuleData
  lat: Number
  long: Number
  nextSibling: com.kickapps.mediawidget.data::MediaList
  parent: com.kickapps.mediawidget.data::MediaList
  point: String
  prevSibling: com.kickapps.mediawidget.data::MediaList
  thumbnails: Array
  title: String
  url: String

The mediaItem object references kickAppsModuleData, which contains the KickApps social metadata associated with the mediaItem object:

KickAppsMediaModuleData
  category: String
  city: String
  country: String
  favorites: Number
  gadChannel: String
  gadPublisher: String
  gadhost: String
  gadtype: String
  id: String
  keywords: String
  mediaType: String
  numOfComments: Number
  rating: Number
  state: String
  uploadedByThumbnail: String
  uploadedByUrl: String
  userDisabled: String
  views: Number
  votes: Number
  zip: String


Dispatch events to other widget components

Plug-ins can also dispatch events that other components can listen for by including code like this:

/**

* This property is injected by the wrapper that loads this plugin.

* You can use the kickAppsEventBus to send messages to and receive messages

* from other components in your application.

* 

**/

private var _eventBus: IEventDispatcher;

public function set kickAppsEventBus(value: IEventDispatcher):void

{


_eventBus = value;


if (_eventBus)

_eventBus.dispatchEvent(new Event('somethingHappened'));

}


Debugging

To make it easier to debug plug-ins, we've created a Debug Preview option that loads the current App Studio project in debug mode and pipes logs from both App Studio and OSMF into the Firebug console.


Sample: Closed Captioning Plug-In

The following code shows how to use the KIT Cloud Social event bus to create a simple closed captioning plug-in that uses the mediaTimeElapsed event to display different text strings in a text component at different times during the video.

ClosedCaptionPlugin.png


Properties Panel

Caption_plugin.png


YAML File

Download this YAML file

panels:
  title: "Close Caption"
  items:
    - title: "SETTINGS"
      items:
        - !panel!PanelItem
            control: !control!SingleTextAreaWithApplyButton 
                      label: "Content"
            componentProperty: closeCaptioningContent
            itemProperty: text

        - !panel!PanelItem
            control: !control!SingleTextInputWithApplyButton
                      label: "Text Field ID"
            componentProperty: textFieldId
            itemProperty: text     


Widget JSON Code

You can import a demo widget that uses this plug-in into your App Studio by copying the code below and then pasting into the App Studio import window under File > Import.

{"widgetHost":"http://localhost:8080","cs_rwid":"","ssoRegisterUrl":"","feedAkHost":"http://localhost:8080","adBuyOut":false,"widgetJson":{"properties":{"affiliateOmnitureAccount":"","widgetLabels":{"KickTextAreaMain1524243798":"t1","VideoPlayerComponentMain1524278589":"Video Player Component 1","Captioner1524123806":"Captioner"},"omniturePlayerName":"","omnitureVisitorNamespace":"","omnitureDc":"","omnitureTrackMilestones":"10,50,90","omnitureTrackSeconds":0,"omnitureAutoTrackClicks":false,"savedWithRevision":"416","wmode":"transparent","windowTarget":"_self","autoResizeCanvas":false,"height":420,"varsToAppendToLinks":"","revision":3,"externalName":"captiontest","width":420,"affiliateGaAccount":""},"children":[{"properties":{"id":"KickTextAreaMain1524243798","fontColor":16777215,"actions":{},"height":25,"x":75,"y":302,"width":272},"filePath":"KickTextAreaMain.swf","index":0},{"properties":{"id":"Captioner1524123806","textFieldId":"t1","actions":{},"height":64,"x":-118,"closeCaptioningContent":"1-3: This is my awesome movie.\r\r4-8: More of my awesome movie.\r\r12-18: The end of my awesome movie.","y":217,"width":64},"filePath":"http://dev.kickapps.com:9000/static/67749/plugins/131.swf?timestamp=1261524581676","index":1},{"properties":{"id":"VideoPlayerComponentMain1524278589","adData":{},"playOnLoad":true,"mediaURL":"http://affiliate.kickapps.com/service/getFeed.kickAction?as=12058&mediaType=video&quantity=1","x":12,"y":-19,"actions":{}},"filePath":"VideoPlayerComponentMain.swf","index":2}]},"ssoLoginUrl":"","cs_wid":"","widgetAkHost":"http://localhost:8080"}


Source Code & .swf

Download this .swf

package
{
	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.events.IEventDispatcher;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;

	public class CloseCaptioningSamplePlugin extends Sprite
	{
		
		public function CloseCaptioningSamplePlugin()
		{
			var iconBm:Bitmap = new icon() as Bitmap;
			this.addChild(iconBm);
			
			
			//closeCaptioningContent = "1-3: This is my awesome movie.\n\n4-8: More of my awesome movie.\n\n12-18: The end of my awesome movie."
		}
		
		/**
		 * This property is injected by the wrapper that loads this plugin.
		 * You can use the kickAppsShell to access other components and widget properties.
		 * 
		 **/
		private var _shell: Object;
		public function set kickAppsShell(value: Object):void
		{
			_shell = value;
			
			if (value)
				findTextField();
		}
		
		/**
		 * This property is injected by the wrapper that loads this plugin.
		 * You can use the kickAppsEventBus to send messages to and receive messages
		 * from other components in your application.
		 * 
		 **/
		private var _eventBus: IEventDispatcher;
		public function set kickAppsEventBus(value: IEventDispatcher):void
		{
			if (_eventBus)
				removeEventListeners();
			
			_eventBus = value;
			
			if (_eventBus)
				addEventListenters();
		}		
		
		private var _textFieldId: String;

		public function get textFieldId():String
		{
			return _textFieldId;
		}

		public function set textFieldId(value:String):void
		{
			if (_textFieldId != value)
			{
				if (textField)
					textField = null;
				
				_textFieldId = value;
				
				findTextField();
			}
			
			_textFieldId = value;
		}

		
		private var _closeCaptioningContent: String;

		public function get closeCaptioningContent():String
		{
			return _closeCaptioningContent;
		}

		public function set closeCaptioningContent(value:String):void
		{
			_closeCaptioningContent = value;
			
			if (_closeCaptioningContent)
			{
				processCloseCaptioningContent();
			}
		}
		
		private function addEventListenters(): void
		{
			_eventBus.addEventListener("mediaTimeElapsed", timeElapsedHandler, false, 0, true);
		}
		
		private function removeEventListeners(): void
		{
			_eventBus.removeEventListener("mediaTimeElapsed", timeElapsedHandler);
		}		
		
		private function findTextField(): void
		{
			if (_textFieldId && _shell)
				intervalId = setInterval(getTextComponent, 300);
		}
		
		private function getTextComponent(): void
		{
		    textField = _shell.getComponentById(textFieldId);  // the id is the label assigned in the App Studio
		    
		    if (textField)
		    {
			    clearInterval(intervalId);
		    }
		}
		
		private function processCloseCaptioningContent():void
		{
			contentByTime = [];
				
			var splitContentByDoubleLineBreak:Array = _closeCaptioningContent.split("\r\r");
			var splitByColon: Array;
			var contentByTimeMap: String;
			var times: Array;
			var content:String;
			
			for each (contentByTimeMap in splitContentByDoubleLineBreak)
			{
				splitByColon = contentByTimeMap.split(":");
				times = splitByColon[0].split("-");
				content = splitByColon[1];
				
				contentByTime.push({start:Number(times[0]), end: Number(times[1]), content: content});
			}
			
			trace(contentByTime);
		}
		
		// Main event handler
		private function timeElapsedHandler(event: Object): void
		{
			var time: Number = event.data as Number;
			if (textField && contentByTime.length > 0 && !isNaN(time))
			{
				textField.text = "";
				
				for each (var contentTimeObj:Object in contentByTime)
				{
					if (time >= contentTimeObj.start && time < contentTimeObj.end)
					{
						textField.text = contentTimeObj.content;
					}
				}
			}
		}
		

		[Embed('gettext.png')]
		private var icon: Class;
		
		private var intervalId:int;
		private var textField:Object;
		private var contentByTime: Array;
	}
}


Adding your plug-in to the App Studio

Compose a YAML descriptor file to create a properties panel

Users customize and configure App Studio components and plug-ins via properties panels. To create one for your plug-in, simply compose a YAML descriptor file to map configurable properties of your plug-in to UI controls. The App Studio will use this file to generate a properties panel for your plug-in. See the YAML descriptor file reference for full details.


Upload your plug-in and YAML file

In the App Studio, open the My Plug-Ins window by clicking Window > My Plug-Ins. You can test and configure your App Studio plug-in through this window. If you're interested in making your plugin available to over 100,000 KIT Cloud Social customers, contact us at plugins at kickapps dot com, and we'll work with you to make that happen.

  1. In the App Studio, select Windows > My Plugins.
  2. In the window that appears, click the Add button:
    :

MyPlugins.png

  1. Select your .swf plugin file, your YAML descriptor file, and, optionally, an icon file (46px x 40px).
  2. Your plugin will appear in the App Studio Plugins window. To test it out, drag it to the stage and try out all the options in its properties panel.
  3. If you need to make changes to your YAML file, .swf, or icon file, select your plugin in the App Studio Plugins window, click the Edit button, and re-upload the relevant files.


Submitting an OSMF plug-in

If you have an OSMF plug-in or are interested in developing one, please email your contact information and a short description about your OSMF plug-in to osmf@kickapps.com and we'll work with you to get your plug-in added to our OSMF plug-in list.