flash on 2013-2-10
/**
* Copyright go-space ( http://wonderfl.net/user/go-space )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/p6bT
*/
package
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.SharedObject;
import flash.net.Socket;
import flash.system.Capabilities;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
import com.bit101.components.CheckBox;
import com.bit101.components.ComboBox;
import com.bit101.components.NumericStepper;
import com.bit101.components.Label;
import com.bit101.components.PushButton;
import com.bit101.components.Style;
import com.bit101.components.Text;
public class BouyomiChatClient extends Sprite
{
public static var stage:Stage;
private static var nameCheck:CheckBox;
private static var cacheByte:ByteArray;
private static var clearButton:PushButton;
private static var commentText:Text;
private static var connectButton:PushButton;
private static var ipText:Text;
private static var nameText:Text;
private static var portText:Text;
private static var sharedObject:SharedObject;
private static var socket:Socket;
private static var twitButton:PushButton;
private static var logTextArea:TextAreaLogCustom;
private static var speedCheck:CheckBox;
private static var speedNumericStepper:NumericStepper;
private static var toneCheck:CheckBox;
private static var toneNumericStepper:NumericStepper;
private static var volumeCheck:CheckBox;
private static var volumeNumericStepper:NumericStepper;
private static var voiceComboBox:ComboBox;
public function BouyomiChatClient ()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
public static function set log (value:String):void
{
logTextArea.text = value;
}
private function init (event:Event = null):void
{
var i:uint;
var n:uint;
var label:Label;
var fmt:TextFormat;
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
BouyomiChatClient.stage = stage;
stage.align = StageAlign.TOP_LEFT;
stage.quality = StageQuality.HIGH;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.showDefaultContextMenu = false;
stage.stageFocusRect = false;
Style.embedFonts = false;
Style.fontName = '_等幅';
Style.fontSize = 10;
sharedObject = SharedObject.getLocal('BouyomiClient') as SharedObject;
ipText = new Text(this, 36, 50);
fmt = ipText.textField.defaultTextFormat;
fmt.align = TextFormatAlign.CENTER;
ipText.textField.defaultTextFormat = fmt;
ipText.textField.multiline = false;
ipText.width = 100;
ipText.height = 20;
ipText.text = sharedObject.data.ip == undefined ? '127.0.0.1' : sharedObject.data.ip;
label = new Label(this, 0, 53, 'IP:');
label.x = ipText.x - label.width;
portText = new Text(this, 190, 50);
fmt = portText.textField.defaultTextFormat;
fmt.align = TextFormatAlign.CENTER;
portText.textField.defaultTextFormat = fmt;
portText.textField.multiline = false;
portText.width = 100;
portText.height = 20;
portText.text = sharedObject.data.port == undefined ? '843' : sharedObject.data.port;
label = new Label(this, 0, 53, 'Port:');
label.x = portText.x - label.width;
connectButton = new PushButton(this, portText.x + portText.width + 20, 50, '', onConnectClick);
nameText = new Text(this, 36, 100);
fmt = nameText.textField.defaultTextFormat;
fmt.align = TextFormatAlign.CENTER;
nameText.textField.defaultTextFormat = fmt;
nameText.textField.multiline = false;
nameText.width = 100;
nameText.height = 20;
nameText.text = sharedObject.data.name == undefined ? '名無し' : sharedObject.data.name;
label = new Label(this, 10, 103, '名前:');
label.x = nameText.x - label.width;
commentText = new Text(this, 190, 100);
commentText
commentText.width = 200;
commentText.height = 20;
commentText.addEventListener(KeyboardEvent.KEY_UP, onKeyup);
label = new Label(this, nameText.x + nameText.width + 10, 103, 'コメント:');
label.x = commentText.x - label.width;
nameCheck = new CheckBox(this, 36, 150, '名乗る');
nameCheck.selected = true;
speedCheck = new CheckBox(this, 36, 180, 'デフォルスピード');
speedCheck.selected = true;
speedNumericStepper = new NumericStepper(this, 170);
speedNumericStepper.maximum = 300;
speedNumericStepper.minimum = 50;
speedNumericStepper.y = speedCheck.y + speedCheck.height * 0.5 - speedNumericStepper.height * 0.5;
toneCheck = new CheckBox(this, 36, 210, 'デフォルトトーン');
toneCheck.selected = true;
toneNumericStepper = new NumericStepper(this, 170);
toneNumericStepper.maximum = 200;
toneNumericStepper.minimum = 50;
toneNumericStepper.y = toneCheck.y + toneCheck.height * 0.5 - toneNumericStepper.height * 0.5;
volumeCheck = new CheckBox(this, 36, 240, 'デフォルトボリューム');
volumeCheck.selected = true;
volumeNumericStepper = new NumericStepper(this, 170);
volumeNumericStepper.maximum = 200;
volumeNumericStepper.minimum = 50;
volumeNumericStepper.y = volumeCheck.y + volumeCheck.height * 0.5 - volumeNumericStepper.height * 0.5;
voiceComboBox = new ComboBox(this, 36, 270);
voiceComboBox.addItem( { label:'デフォルト音声' } );
voiceComboBox.addItem( { label:'女性1' } );
voiceComboBox.addItem( { label:'女性2' } );
voiceComboBox.addItem( { label:'男性1' } );
voiceComboBox.addItem( { label:'男性2' } );
voiceComboBox.addItem( { label:'中性' } );
voiceComboBox.addItem( { label:'ロボット' } );
voiceComboBox.addItem( { label:'機械1' } );
voiceComboBox.addItem( { label:'機械2' } );
voiceComboBox.selectedIndex = 0;
twitButton = new PushButton(this, voiceComboBox.x + voiceComboBox.width + 20, 270, '棒つぶやく', onTwitClick);
clearButton = new PushButton(this, 0, 0, 'Clear', onClearClick);
clearButton.x = stage.stageWidth - (clearButton.width + 10);
clearButton.y = stage.stageHeight - clearButton.height - 10;
logTextArea = new TextAreaLogCustom(this, 10, 0);
logTextArea.width = stage.stageWidth - 20;
logTextArea.y = clearButton.y - logTextArea.height - 10;
close();
}
private function onClearClick (event:MouseEvent):void
{
BouyomiChatClient.log = null;
}
private function onConnectClick (event:MouseEvent):void
{
switch (connectButton.label)
{
case 'Connect' :
open();
break;
case 'Close' :
close();
break;
}
}
private function open ():void
{
close();
cacheByte = new ByteArray();
connectButton.enabled = false;
connectButton.label = 'Connecting';
socket = new Socket();
socket.addEventListener(Event.CLOSE, onClose);
socket.addEventListener(Event.CONNECT, onConnect);
socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
try
{
socket.connect(ipText.text, int(portText.text));
}
catch (error:Error)
{
BouyomiChatClient.log = error.message;
}
}
private function close ():void
{
if (socket && socket.connected)
{
try
{
socket.close();
}
catch (error:Error)
{
BouyomiChatClient.log = error.message;
}
socket = null;
}
connectButton.enabled = true;
connectButton.label = 'Connect';
twitButton.enabled = false;
}
private function onClose (event:Event):void
{
BouyomiChatClient.log = '【onClose】';
connectButton.enabled = true;
connectButton.label = 'Connect';
twitButton.enabled = false;
}
private function onConnect (event:Event):void
{
BouyomiChatClient.log = '【onConnect】';
connectButton.enabled = true;
connectButton.label = 'Close';
twitButton.enabled = true;
sharedObject.data.ip = ipText.text;
sharedObject.data.port = portText.text;
}
private function onIOError (event:IOErrorEvent):void
{
BouyomiChatClient.log = '【onIOError】';
close();
}
private function onSocketData (event:ProgressEvent):void
{
// BouyomiChatClient.log = 'onSocketData';
socket.readBytes(cacheByte, cacheByte.length, socket.bytesAvailable);
parseData();
}
private function onSecurityError (event:SecurityErrorEvent):void
{
BouyomiChatClient.log = '【onSecurityError】';
close();
}
private function parseData ():void
{
var str:String;
var pattern:RegExp = /^hdr:([0-9]+?)\n/;
var result:Object;
var headerLength:uint;
var contentLength:uint;
var headerByte:ByteArray;
var bodyByte:ByteArray;
var remainder:ByteArray;
// ヘッダから容量を取得
cacheByte.position = 0;
str = cacheByte.readUTFBytes(cacheByte.bytesAvailable);
result = pattern.exec(str);
if (!result)
{
// パターンにマッチしない
return;
}
else
{
// パターンにマッチした
contentLength = uint(result[1]);
}
// ヘッダを再現し長さを取得
headerByte = new ByteArray();
headerByte.writeUTFBytes('hdr:' + String(contentLength) + '\n');
headerLength = headerByte.length;
// キャッシュがヘッダ容量+コンテンツ容量を上回るか?
if (cacheByte.length < headerLength + contentLength)
{
trace('容量を下回っている' + cacheByte.length + '<' + (headerLength + contentLength));
return;
}
else
{
// 上回っているので、ボディの最初へポインタの位置を移動する
cacheByte.position = headerByte.length;
}
// ボディを生成
bodyByte = new ByteArray();
cacheByte.readBytes(bodyByte, 0, contentLength);
parseBody(bodyByte);
//trace(MZTool.print_r(bodyByte.readObject()));
// 余りを生成
remainder = new ByteArray();
remainder.writeBytes(cacheByte, headerLength + contentLength, cacheByte.bytesAvailable);
// 余りをキャッシュに書き込む
cacheByte.length = 0;
cacheByte.writeBytes(remainder, 0, remainder.bytesAvailable);
// キャッシュがあればもう一回パースする
if (cacheByte.length > 0)
{
parseData();
}
}
private function parseBody (bodyByte:ByteArray):void
{
var obj:Object = bodyByte.readObject();
var name:String = obj.name ? obj.name : 'unknown';
var comment:String = obj.comment ? obj.comment : '・・・';
BouyomiChatClient.log = name + 'さん:' + comment;
}
private function onKeyup (event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.ENTER)
twit();
}
private function onTwitClick (event:Event):void
{
twit();
}
private function twit ():void
{
var bodyByte:ByteArray;
var contentLength:uint;
var headerByte:ByteArray;
var speed:int = speedCheck.selected ? -1 : speedNumericStepper.value;
var tone:int = toneCheck.selected ? -1 : toneNumericStepper.value;
var volume:int = volumeNumericStepper.value;
var voice:uint = voiceComboBox.selectedIndex;
/*
name:名前
comment:コメント
speed:速度(-1:デフォルト, 50~300)
tone:音程(-1:デフォルト, 50~200)
volume:音量(-1:デフォルト, 0~100)
voice:声質(0:棒読みちゃん画面上の設定、1:女性1、2:女性2、3:男性1、4:男性2、5:中性、6:ロボット、7:機械1、8:機械2、10001~:SAPI5)
*/
if (commentText.text != '' && socket && socket.connected)
{
bodyByte = new ByteArray();
bodyByte.writeObject( {
isName:nameCheck.selected,
name:nameText.text,
comment:commentText.text,
speed:speed,
tone:tone,
volume:volume,
voice:voice
} );
contentLength = bodyByte.length;
headerByte = new ByteArray();
headerByte.writeUTFBytes('hdr:' + String(contentLength) + '\n');
socket.writeBytes(headerByte, 0, headerByte.length);
socket.writeBytes(bodyByte, 0, bodyByte.length);
socket.flush();
commentText.text = '';
}
}
}
}
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.text.TextField;
import com.bit101.components.TextArea;
import com.bit101.components.VScrollBar;
class TextAreaLogCustom extends TextArea
{
public function TextAreaLogCustom (parent:DisplayObjectContainer = null, xpos:Number = 0, ypos:Number = 0, text:String = '')
{
super(parent, xpos, ypos, text);
}
override public function set text (t:String):void
{
if (_text != '')
{
_text += '\n' + t;
}
else
{
_text += t;
}
if (t == null)
_text = "";
invalidate();
}
public function get tf ():TextField
{
return _tf;
}
public function get scrollbar ():VScrollBar
{
return _scrollbar;
}
override public function draw ():void
{
super.draw();
_tf.width = _width - _scrollbar.width - 4;
_scrollbar.x = _width - _scrollbar.width;
_scrollbar.height = _height;
_scrollbar.draw();
addEventListener(Event.ENTER_FRAME, onTextScrollDelay);
_tf.scrollV = _tf.maxScrollV;
}
}