<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="creationCompleteHandler()"
alwaysInFront="{keepInFront_chk.selected}"
viewSourceURL="srcview/index.html">
<mx:Style source="css/screen.css" />
<mx:VBox width="100%" height="100%" styleName="container" id="container">
<mx:HBox width="100%">
<mx:Label text="XPanelOnAir (version 0.2)" styleName="title" />
<mx:Spacer width="100%" />
<mx:CheckBox label="Keep in front" id="keepInFront_chk" change="keepInFrontChangeHandler(event)" />
</mx:HBox>
<mx:Accordion width="100%" height="100%" id="tabNav" change="{tabNavChangeHandler()}">
<mx:VBox label="Log to panel" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" width="100%" height="100%">
<mx:HBox horizontalAlign="right" width="100%">
<mx:HBox horizontalGap="1">
<mx:Button toggle="true" id="allButton" label="ALL" click="{optionClickHandler(event)}" />
<mx:Button toggle="true" id="debugButton" toolTip="DEBUG" click="{optionClickHandler(event)}" styleName="debugButton" />
<mx:Button toggle="true" id="infoButton" toolTip="INFO" click="{optionClickHandler(event)}" styleName="infoButton" />
<mx:Button toggle="true" id="warnButton" toolTip="WARN" click="{optionClickHandler(event)}" styleName="warnButton" />
<mx:Button toggle="true" id="errorButton" toolTip="ERROR / FATAL" click="{optionClickHandler(event)}" styleName="errorButton" />
</mx:HBox>
<mx:HBox horizontalGap="1">
<mx:Label text="Max. lines:" styleName="myLabel" />
<mx:NumericStepper id="nbLines" minimum="0" maximum="10000" change="{nbLinesChangeHandler()}" />
</mx:HBox>
<mx:HBox verticalAlign="middle" paddingLeft="10" horizontalGap="1">
<mx:Label text="Word filter:" />
<mx:TextInput id="wordFilter_ti" textInput="{wordFilterChange()}" change="{wordFilterChange()}" focusOut="{wordFilterChange()}" toolTip="Filter the list of log messages..." />
</mx:HBox>
<mx:Spacer width="100%" />
<mx:Button label="Export to CSV" click="{exportData()}" />
<mx:Button label="Clear" click="{clearGrid()}" />
</mx:HBox>
<mx:DataGrid id="dg" dataProvider="{ac}" width="100%" height="100%" doubleClickEnabled="true" doubleClick="{datagridDoubleClickHandler()}">
<mx:columns>
<mx:DataGridColumn dataField="index" headerText="#" minWidth="5" width="5">
<mx:itemRenderer>
<mx:Component>
<mx:Label color="{outerDocument.getLabelColor(data.level)}" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="time" headerText="Time" minWidth="10" width="10">
<mx:itemRenderer>
<mx:Component>
<mx:Label color="{outerDocument.getLabelColor(data.level)}" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="flashtime" headerText="Flashtime" minWidth="10" width="10">
<mx:itemRenderer>
<mx:Component>
<mx:Label color="{outerDocument.getLabelColor(data.level)}" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="typeLabel" headerText="Type" minWidth="10" width="10">
<mx:itemRenderer>
<mx:Component>
<mx:Label color="{outerDocument.getLabelColor(data.level)}" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="message" headerText="Message" minWidth="75" width="65">
<mx:itemRenderer>
<mx:Component>
<mx:Label color="{outerDocument.getLabelColor(data.level)}" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
<mx:VBox label="Log to file" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" width="100%" height="100%">
<mx:Text width="100%">
<mx:htmlText><![CDATA[Please select or create a text file in which the logs can be written to...]]></mx:htmlText>
</mx:Text>
<mx:HBox width="100%">
<mx:TextInput id="selectedFilePathTxt" editable="false" />
<mx:Button label="Select file" click="{selectLogFile()}" />
</mx:HBox>
<mx:TextArea width="100%" height="100%" id="ta" borderThickness="0" backgroundAlpha="0" focusEnabled="false" />
</mx:VBox>
</mx:Accordion>
</mx:VBox>
<mx:Script>
<![CDATA[
import mx.core.IFlexDisplayObject;
import popup.LogMessageDetail;
import mx.managers.PopUpManager;
import mx.controls.Text;
import com.abdulqabiz.utils.DataGridDataExporter;
import mx.controls.Alert;
import mx.controls.ToggleButtonBar;
import mx.events.ItemClickEvent;
import mx.formatters.DateFormatter;
import flash.utils.getTimer;
import mx.collections.ArrayCollection;
[Bindable] private var ac:ArrayCollection;
private var tabNavIndex:int = 0;
public const ALL_LEVEL:int = 15;
private var created:Boolean = false;
private var lc:LocalConnection;
private var df:DateFormatter;
private var filterLevel:Number = 0;
private var counter:Number = 0;
private var so:SharedObject;
private var logFile:File;
private var textLogFile:File;
private var stream:FileStream;
private var toggleButtonArray:Array;
private var filterWord:String = "";
private var hasFilterWord:Boolean = false;
/**
* Called by the logging target class via local connection
*/
public function dispatchMessage(timeDiff:Number, msg:String, level:Number):void
{
if(!created) return;
counter++;
if(tabNavIndex == 0)
{
ac.addItem({index:counter, flashtime:timeDiff, time:df.format(new Date()), message:msg, level:level, typeLabel:getTypeNameForLevel(level)});
updateLogLength();
dg.scrollToIndex((dg.dataProvider as ArrayCollection).length);
}
else
{
var newline:String = "#"+counter+", flashtime:"+timeDiff+", time:"+df.format(new Date())+", message:"+msg+", level:"+level+", type:"+getTypeNameForLevel(level)+File.lineEnding;
appendLineToTextLog(newline);
}
}
/**
* Label color function
*/
public function getLabelColor(level:Number):Number
{
switch(level)
{
case 8:
return 0xff0000;
default:
return 0x000000;
}
}
/**
* Empty the grid data
*/
public function clearGrid():void
{
ac.removeAll();
ac.refresh();
}
public function clearText():void
{
txt = "";
}
/**
* Get label for level
*/
protected function getTypeNameForLevel(level:int):String
{
var typeLabel:String = "";
switch(level)
{
case 1:
typeLabel = "DEBUG";
break;
case 2:
typeLabel = "INFO";
break;
case 4:
typeLabel = "WARN";
break;
case 8:
typeLabel = "ERROR";
break;
default:
}
return typeLabel;
}
/**
* Everything's on stage is rendered and ready to use
*/
private function creationCompleteHandler():void
{
toggleButtonArray = [allButton, debugButton, infoButton, warnButton, errorButton];
ac = new ArrayCollection();
ac.filterFunction = filterLogEntries;
lc = new LocalConnection();
lc.allowDomain('*');
lc.connect("_xpanel1");
lc.client = this;
df = new DateFormatter();
df.formatString = "HH:NN:SS";
created = true;
so = SharedObject.getLocal("xpanelonair", "/");
if(so.data.hasOwnProperty("keepInFront"))
{
keepInFront_chk.selected = so.data.keepInFront;
}
else
{
keepInFront_chk.selected = true;
}
if(so.data.hasOwnProperty("filterLevel"))
{
setFilterLevel(so.data.filterLevel);
}
else
{
setFilterLevel(ALL_LEVEL);
}
if(so.data.hasOwnProperty("nbLines"))
{
nbLines.value = parseInt(""+so.data.nbLines);
}
else
{
so.data.nbLines = nbLines.value = 100;
}
}
/**
*
*/
private function datagridDoubleClickHandler():void
{
var popup:IFlexDisplayObject = PopUpManager.createPopUp(this, LogMessageDetail, true);
(popup as LogMessageDetail).setItem(dg.selectedItem);
PopUpManager.centerPopUp(popup);
}
/**
*
*/
protected function setFilterLevel(value:int):void
{
filterLevel = value;
var addUp:int = 1;
for(var j:uint = 1; j<toggleButtonArray.length; j++)
{
var btn:Button = toggleButtonArray[j] as Button;
btn.selected = ((addUp & filterLevel) == addUp) ? true : false;
addUp = addUp*2;
}
if(filterLevel == ALL_LEVEL) allButton.selected = true;
ac.refresh();
}
/**
* Data collection filter
*/
private function filterLogEntries(item:Object):Boolean
{
if ((item.level & filterLevel) == item.level) {
if(hasFilterWord)
{
if((item.message as String).indexOf(filterWord) != -1) return true;
else return false;
}
else return true;
}
else
{
return false;
}
}
/**
* BUTTON STATES calculated based on bitwise operators
* For more information, check out http://www.moock.org/asdg/technotes/bitwise/
*/
protected function optionClickHandler(event:Event):void
{
if(event.target == allButton)
{
filterLevel = (allButton.selected) ? ALL_LEVEL : 0;
for(var i:uint = 1; i<toggleButtonArray.length; i++) (toggleButtonArray[i] as Button).selected = allButton.selected;
}
else
{
allButton.selected = false;
filterLevel = 0;
var addUp:int = 1;
for(var j:uint = 1; j<toggleButtonArray.length; j++)
{
if((toggleButtonArray[j] as Button).selected) filterLevel += addUp;
addUp = addUp*2;
}
}
so.data.filterLevel = filterLevel;
ac.refresh();
}
private function wordFilterChange():void
{
filterWord = wordFilter_ti.text;
if(filterWord.length > 0) hasFilterWord = true;
else hasFilterWord = false;
ac.refresh();
}
private function tabNavChangeHandler():void
{
ac = new ArrayCollection();
tabNavIndex = tabNav.selectedIndex;
if(tabNavIndex == 0)
{
closeTextLogFile();
}
else
{
openTextLogFile();
}
}
private function keepInFrontChangeHandler(event:Event):void
{
so.data.keepInFront = (event.target as CheckBox).selected
}
private function nbLinesChangeHandler():void
{
so.data.nbLines = nbLines.value;
updateLogLength();
}
private function saveFile():void
{
if (logFile) {
if (stream != null)
{
stream.close();
}
stream = new FileStream();
stream.openAsync(logFile, FileMode.WRITE);
stream.addEventListener(IOErrorEvent.IO_ERROR, writeIOErrorHandler);
var str:String = DataGridDataExporter.exportCSV(dg);
stream.writeUTFBytes(str);
stream.close();
}
else
{
exportData();
}
}
private function openTextLogFile():void
{
if(textLogFile)
{
stream = new FileStream();
stream.openAsync(textLogFile, FileMode.APPEND);
selectedFilePathTxt.text = textLogFile.nativePath;
ta.text = "File opened succesfully.";
}
}
private function closeTextLogFile():void
{
if(textLogFile && stream)
{
stream.close();
}
}
/**
* Called when the user clicks the "Export" button. Opens a Save As dialog box, in which the
* user selects a logFile path.
*/
private function exportData():void
{
logFile = File.desktopDirectory.resolvePath("log.csv");
var fileChooser:File;
fileChooser = logFile;
fileChooser.browseForSave("Save As");
fileChooser.addEventListener(Event.SELECT, saveAsFileSelected);
}
private function selectLogFile():void
{
textLogFile = File.desktopDirectory.resolvePath("log.txt");
var fileChooser:File;
fileChooser = textLogFile;
fileChooser.browseForSave("Select log file");
fileChooser.addEventListener(Event.SELECT, saveTextLogFileSelected);
}
/**
* Adjust the length of the collection of log entries to be displayed in the datagrid
*/
private function updateLogLength():void
{
while(ac.length > nbLines.value)
{
ac.removeItemAt(0);
}
}
/**
* Adds a line to the log file
*/
private function appendLineToTextLog(line:String):void
{
try
{
stream.writeUTFBytes(line);
ta.text = "";
}
catch(e:Error)
{
ta.text = "(Error!) Could not write to file...";
}
}
/**
* Called when the user selects the file path in the Save As dialog box. The method passes the selected
* logFile to the File object and calls the saveFile() method, which saves the logFile.
*/
private function saveAsFileSelected(event:Event):void
{
logFile = event.target as File;
saveFile();
logFile.removeEventListener(Event.SELECT, saveAsFileSelected);
}
private function saveTextLogFileSelected(event:Event):void
{
textLogFile = event.target as File;
openTextLogFile();
textLogFile.removeEventListener(Event.SELECT, saveTextLogFileSelected);
}
/**
* Handles I/O errors that may come about when writing the logFile.
*/
private function writeIOErrorHandler(event:Event):void
{
Alert.show("The data could not be exported to file.", "Error", Alert.OK, this);
}
]]>
</mx:Script>
</mx:WindowedApplication>