/**
* Copyright tepe ( http://wonderfl.net/user/tepe )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/sGPd
*/
package {
import flash.utils.Dictionary;
import flash.display.*;
import flash.text.*;
import flash.events.*;
import flash.net.*;
import flash.utils.*;
import net.hires.debug.Stats;
public class FlashTest extends Sprite {
private var t1:TextEditor = new TextEditor();//原文
private var t2:TextField = new TextField();
public function FlashTest() {
// write as3 code here..
init();
//FPS計測
addChild( new Stats() );
Wonderfl.capture(stage);
onChange(null);
}
private function init():void{
//原文テキスト入力ボックス
t1.border = true;
t1.width = 230;
t1.height = 360;
t1.y=100;
t1.type = "input";
//t1.multiline = true;
t1.text = "1234567890\ntest\nあいうえお test";
addChild(t1);
t1.addEventListener(Event.CHANGE,onChange);
t2.x = 230;
t2.width=230;
t2.height=460;
t2.wordWrap =true;
addChild(t2);
}
private var tk:token;
private function onChange(e:Event=null):void{
var cnt:int=0;
tk = new token();
var i:int;
var index:int=0;
var len:int = t1.length;
//tk.getToken(t1.text,0,100);
tk.addEventListener(Event.COMPLETE,onComplete);
tk.addEventListener(Event.CHANGE,onChange2);
count1=0;
tk.getToken2(t1.text);
// t2.text = String(Math.random()*50);
//tk.func2();
//tk.setKeywordAS3();
}
private var count1:int=0;
private function onChange2(e:Event):void{
t2.text = count1.toString();
count1++;
}
//スペースとコメント以外が出るまで進める
private function spaceCut(cnt:int):int{//
var i:int;
for(i=cnt;i<tk.listLength;i++){
if(tk.list[i].type=="space")continue;
if(tk.list[i].type=="comment")continue;
break;
}
return i;
}
//token.textが同一のものをリスト化
private var nameList:Dictionary;
private function addNameList(name:String,tokenID:int):int{
if(nameList.hasOwnProperty(name)){//
nameList[name].push(tokenID);
}
else{//
var idList:Array = new Array();
idList.push(tokenID);
nameList[name] = idList;
}
return nameList[name].length;
}
private function onComplete(e:Event):void{//
tk.removeEventListener(Event.COMPLETE,onComplete);
t2.text ="";
nameList = null;
nameList = new Dictionary();
var i:int;
for(i=0; i<tk.listLength; i++){//
//i=spaceCut(i);
if(tk.list[i].type=="space")continue;
if(i==tk.listLength)break;
t2.appendText(tk.list[i].text +" "+tk.list[i].type+ "\n");
if(tk.list[i].type == "text"){
addNameList(tk.list[i].text,i);
//t2.appendText(tk.list[i].text + "\n"+ tk.list[i].tag);
}
}
//t2.text ="";
for(var str:String in nameList){//
// t2.appendText("["+str+"] ");
//t2.appendText(nameList[str].length.toString());
// t2.appendText("\n");
}
return;
for(i=0;i<tk.listLength;i++){
//スコープ
/*if(tk.list[i].type=='}'){
//t2.text = tk.func3(tk.list[i].pair);
t2.text = tk.getBlockText(tk.list[i].pair);
return;
}*/
if(tk.list[i].type=="statement"){//
t2.appendText(tk.list[i].text+" ");//要素の種類
//import
if(tk.list[i].text=="import"){
i+=2;//スペース
while(tk.list[i].type!="space"){
t2.appendText(tk.list[i].text);
i++;
}
t2.appendText("\n");
continue;
}
//class
if(tk.list[i].text=="class"){
i+=2;
//識別子
if(tk.list[i].type=="string")t2.appendText(tk.list[i].text);
i++;
i=spaceCut(i);
//extends
if(tk.list[i].text == "extends"){//
t2.appendText(" "+tk.list[i].text+" ");
i++;
i=spaceCut(i);
t2.appendText(tk.list[i].text);
}
t2.appendText("\n");
continue;
}
//function
if(tk.list[i].text=="function"){
i+=2;
//識別子
if(tk.list[i].type=="string"){//
t2.appendText(tk.list[i].text);
i++;
}
// ()
i=spaceCut(i);
if(tk.list[i].text=="("){
t2.appendText("("+tk.getBlockText(i)+"):");
i=tk.list[i].pair;
}
else t2.appendText("():");
i++;
// :
i=spaceCut(i);
if(tk.list[i].text == ":"){//
i++;
i=spaceCut(i);
t2.appendText(tk.list[i].text);
}
else t2.appendText("void");
t2.appendText("\n");
continue;
}
//var const ※Vector未対応
if(tk.list[i].text=="var" || tk.list[i].text=="const"){
i+=2;
while(true){//
i=spaceCut(i);
//識別子
if(tk.list[i].type=="string"){//
t2.appendText(tk.list[i].text);
i++;
}
i=spaceCut(i);
//型
if(tk.list[i].text == ":"){//
t2.appendText(" "+tk.list[i].text+" ");
i++;
i=spaceCut(i);
t2.appendText(tk.list[i].text);
}
i++;
i=spaceCut(i);
//初期化
if(tk.list[i].text == "="){//
t2.appendText(" "+tk.list[i].text+" ");
i++;
while(i < tk.listLength){
if(tk.list[i].text == ",")break;
if(tk.list[i].text == ";")break;
if(tk.list[i].type == "statement")break;
if(tk.list[i].type == "operation")break;
t2.appendText(tk.list[i].text);
i++;
}
}
else{//初期化なし
//i++;
}
//i++;
i=spaceCut(i);
//宣言が続くならループ
if(tk.list[i].text!=",")break;
else{
t2.appendText("\n"+"var ");
i++;
}
}
t2.appendText("\n");
continue;
}
//識別子
if(tk.list[i+2].type=="string")t2.appendText(tk.list[i+2].text+"\n");
else t2.appendText("\n");
}
//if(tk.list[i].type=="space")continue;
//t2.appendText(tk.list[i].tag+" "+tk.list[i].text+"\n");
}
}
}
}
import flash.display.*;
import flash.events.*;
import flash.text.*;
class editorBase extends Sprite{
private var color1:uint = 0xdddddd;
private var s1:Sprite = new Sprite();
private var tytle:TextField = new TextField();
private var source:TextEditor = new TextEditor();
private var btn:Sprite = new Sprite();
private var mode:String = new String();
public function editorBase(name:String="tytle",t:String=""){
//mode = "open";
source.type = "input";
source.y = 20;
source.text = t;
source.width = 400;
source.height = source.textHeight+20;
tytle.text = name;
tytle.doubleClickEnabled = true;
//tytle.type = "input";
tytle.selectable = false;
tytle.height = 20;
tytle.width = tytle.textWidth+20;
s1.addChild(tytle);
addChild(s1);
func1();
addEventListener(MouseEvent.MOUSE_DOWN,onDown);
addEventListener(MouseEvent.DOUBLE_CLICK,onDouble);
}
private function onDouble(e:MouseEvent):void{
this.tytle.type = "input";
this.tytle.selectable = true;
stage.focus = this.tytle;
if(mode != "open")func2();
else func1();
}
private function onDown(e:MouseEvent):void{
e.currentTarget.startDrag();
var _parent:DisplayObjectContainer = this.parent;
this.parent.removeChild(this);
_parent.addChild(this);
//this.parent.addChild(this);
addEventListener(MouseEvent.MOUSE_UP,onUp);
}
private function onUp(e:MouseEvent):void{
e.currentTarget.stopDrag();
removeEventListener(MouseEvent.MOUSE_UP,onUp);
}
//省略モードで表示
public function func1():void{
if(mode == "open")removeChild(source);
s1.graphics.clear();
s1.graphics.beginFill(color1,0.8);
s1.graphics.drawRoundRect(0,0,tytle.width,tytle.height,15,15);
s1.graphics.endFill();
mode = "close";
}
public function func2():void{
if(mode != "open")addChild(source);
s1.graphics.clear();
s1.graphics.beginFill(color1,0.8);
s1.graphics.drawRect(0,0,tytle.width,tytle.height);
s1.graphics.drawRect(0,20,source.width,source.height);
s1.graphics.endFill();
mode = "open";
}
}
import org.libspark.thread.Thread;
import org.libspark.thread.threads.display.LoaderThread;
class MainThread extends Thread {
private var func:Function;
//private var
public function MainThread(func1:Function=null) {//
func = func1;
}
//Thread.start()で実行される
private var cnt:int=0;
override protected function run():void {
if(func==null)return;
next(mainloop);
//next(run);//次に実行するスレッドを指定指定しなければスレッド終了
}
private function mainloop():void{
func();
if(_stop!=true)next(mainloop);//次に実行するスレッドを指定指定しなければスレッド終了
}
private var _stop:Boolean=false;
public function stop():void{
_stop=true;
}
public function setFunction(func1:Function=null):void{
if(func1==null)return;
func = func1;
}
//スレッド終了時に実行される
override protected function finalize():void {
}
}
//////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////
import flash.display.*;
import flash.events.*;
import org.libspark.thread.Thread;
import org.libspark.thread.EnterFrameThreadExecutor;
//テキストからトークンを抽出する
class token extends Sprite{
public var list:Object;//トークン列
private var source:String;
public var listLength:int = 0;
private var thread:MainThread;
public function token(){
list = new Object();
if (!Thread.isReady) Thread.initialize(new EnterFrameThreadExecutor());//スレッドライブラリ初期化
}
public function getToken2(str:String):void{
if(str!=source){
source=str;
list=null;
list=new Object;
listLength = 0;
index01=0;
thread = new MainThread(func05);//スレッド作成
thread.start();
thread.join();
this.dispatchEvent(new Event(Event.CHANGE));
}
}
private function func05():void{//
this.dispatchEvent(new Event(Event.CHANGE));
var currentIndex:int=0;
var cnt:int = 0;
while(currentIndex<source.length){
currentIndex = func01();
cnt++;
if(200<cnt)break;
}
currentIndex = func01();
if(currentIndex==source.length){//
setBlock();
setKeywordAS3();
this.dispatchEvent(new Event(Event.COMPLETE));
thread.stop();
}
}
//指定インデクスからmax個までトークン抽出
//抽出したトークンはlistに追加
//抽出したところまでのインデクスを返す
public function getToken(str:String,index:int=0,max:int=1000):int{
if(str!=source){
source=str;
list=null;
list=new Object;
listLength = 0;
index01=0;
}
var currentIndex:int = index;
var cnt:int = 0;
while(currentIndex<source.length){
currentIndex = func01();
cnt++;
if(max<cnt)break;
}
return currentIndex;
}
/*
public function func4():void{
// ;
else if(list[i].type==';'){
//list[i].step = stepCnt[stepCnt.length-1];
//list[i].group = stac[stac.length-1];
//stepCnt[stepCnt.length-1]++;//ステップをカウントアップ
continue;
}
// space
else if(list[i].type=="space"){
//list[i].step = stepCnt[stepCnt.length-1];
//list[i].group = stac[stac.length-1];
if(list[i].text.search(/[\n\r]/gm)!=-1){
list[i].tag += "onReturn\n";//改行が含まれる
//lineCnt[stepCnt.length-1]++;
}
if(list[i].text.search(/ /gm)!=-1)list[i].tag += "onFullwidthSpace\n";//全角スペースが含まれる
continue;
}
// その他
else{
//list[i].step = stepCnt[stepCnt.length-1];
//list[i].group = stac[stac.length-1];
continue;
}
}
*/
//ActionScript3
public function setKeywordAS3():void{//
//宣言系
const statement:Array = new Array("function", "var", "class", "const", "package","import","namespace");
//処理系
const operation:Array = new Array("for","while","if","else","do","switch","each","case","with",
"break","return","continue","default","in","is","as","to",
"try","catch","finally","throw","typeof","super","this","label");
//データ型
const DataType:Array = new Array("null","void","int","uint",
"Boolean","Number","String","Object","Array",
"Date","RegExp","Error","Function","XML","XMLList");
//アクセス制御系
const access:Array = new Array("private","protected","internal","public",
"static","internal","dynamic","final","native","override");
getStringToken();
var i:int;
//変数、定数、関数、クラス、
for(i=0;i<statement.length;i++){
setType(statement[i],"statement");
}
//制御
for(i=0;i<operation.length;i++){
setType(operation[i],"operation");
}
//標準データ型
for(i=0;i<DataType.length;i++){
setType(DataType[i],"datatype");
}
//アクセス修飾子
for(i=0;i<access.length;i++){
setType(access[i],"access");
}
}
//token.type="string"からタイプを書き換える
private var strToken:Array;
private function setType(str:String,type:String):void{
for(var i:int=0;i<strToken.length;i++){
if(list[strToken[i]].text==str){
list[strToken[i]].type=type;
}
}
regetStringToken();
}
private function regetStringToken(str:String="string"):void{
var ary:Array=strToken;
var ary2:Array = new Array();
for(var i:int=0;i<ary.length;i++){
if(list[ary[i]].type==str){
ary2.push(ary[i]);
}
}
strToken = null;
strToken = ary2;
}
//type == string のトークンid列を取得
private function getStringToken(str:String="string"):void{//
strToken = new Array();
for(var i:int=0;i<listLength;i++){
if(list[i].type==str){
strToken.push(i);
}
}
}
//func2から得たArrayで指定
public function getBlockTokenList(entry:int=-1):Array{
var i:int;
var lastToken:int;
var tokenIdList:Array = new Array();
if(entry == -1){//トークン全体全体
lastToken = listLength;
for(i=0;i<lastToken;i++){
tokenIdList.push(i);
}
return tokenIdList;
}
else{
lastToken = list[entry].pair;
for(i=entry+1;i<lastToken;i++){
tokenIdList.push(i);
}
return tokenIdList;
}
}
//指定したスコープ内のテキスト取得
public function getBlockText(entry:int=-1):String{
var i:int;
var lastToken:int;
var str:String = new String();
if(entry == -1){//トークン全体全体
lastToken = listLength;
for(i=0;i<lastToken;i++){
str += list[i].text;
}
return str;
}
else{
lastToken = list[entry].pair;
for(i=entry+1;i<lastToken;i++){
str += list[i].text;
}
return str;
}
}
//スコープ別にテキストを取り出す
public function func3(tokenID:int):String{
var n:int=tokenID;
var str:String = new String();
if(list[n].type!='{'){
n=list[n].group;
}
for(var i:int = n;i<list[n].pair+1;i++){
str+=list[i].text;
if(list[i].type=='{')i=list[i].pair-1;//スコープ内を省略
}
return str;
}
//トークンにブロックを割り当てる { } ( ) [ ]
private var scopeEntry:Array;
public function setBlock():void{
const err1:String = "scopeError1\n";//スコープの終了がない
const err2:String = "scopeError2\n";//ペアの種類が異なる
const err3:String = "scopeError3\n";//スコープの開始がない
var stac:Array = new Array();
scopeEntry = null;
scopeEntry = new Array();//スコープの開始位置のトークンID列
scopeEntry.push(-1);//最上位登録
for(var i:int=0;i<listLength;i++){
if(list[i].type != "mark")continue;//記号以外はスルー
// { }
if(list[i].text=='{'){
stac.push(i);
continue;
}
else if(list[i].text=='}'){
if(stac.length==0){
list[i].tag += err3;//エラー
continue;
}
if(list[stac[stac.length-1]].text != '{'){
list[i].tag += err2;//エラー
continue;
}
//ペア登録
list[i].pair = stac.pop();
list[list[i].pair].pair = i;
scopeEntry.push(list[i].pair);
list[i].type="block";
list[list[i].pair].type ="block";
continue;
}
// ( )
else if(list[i].text=='('){
stac.push(i);
continue;
}
else if(list[i].text==')'){
if(stac.length==0){
list[i].tag += err3;//エラー
continue;
}
if(list[stac[stac.length-1]].text != '('){
list[i].tag += err2;//エラー
continue;
}
//ペア登録
list[i].pair = stac.pop();
list[list[i].pair].pair = i;
scopeEntry.push(list[i].pair);
list[i].type="block";
list[list[i].pair].type ="block";
continue;
}
// [ ]
else if(list[i].text=='['){
stac.push(i);
continue;
}
else if(list[i].text==']'){
if(stac.length==0){//スコープ開始位置が存在しない
list[i].tag += err3;//エラー
continue;
}
if(list[stac[stac.length-1]].text != '['){//種類が異なる
list[i].tag += err2;//エラー
continue;
}
//ペア登録
list[i].pair = stac.pop();
list[list[i].pair].pair = i;
scopeEntry.push(list[i].pair);
list[i].type="block";
list[list[i].pair].type ="block";
continue;
}
}//for
if(stac.length!=0){//スコープが閉じられる前に最期まで到達した時
for(var j:int=0;j<stac.length;j++){
list[stac[j]].tag += err1;
}
}
return;
}
//-------------------------------------------------------------
//開始インデクスを受け取りトークンのタイプを返す
private var index01:int=0;
private function func01():int{//
if(index01>=source.length)return source.length;
var index:int = index01;
var token:Object = new Object();
var reg:RegExp = new RegExp();
var result:Object;
list[listLength] = token;
listLength++;
token.tag = "";
//トークンタイプ:空白か?
if( source.charAt(index)== " " ||
source.charAt(index)=="\t" ||
source.charAt(index)=="\n" ||
source.charAt(index)=="\r" ){
token.type = "space";
reg = /[^\s]/mg;
reg.lastIndex = index;
result = reg.exec(source);
//トークン作成
if(result == null){//
token.text = source.substring(index,source.length);
index01 = source.length;//次の開始位置
return index01;
}
else{//
token.text = source.substring(index,result.index);
index01 = result.index;
return index01;
}
}
//トークンタイプ:文字列定数1
if(source.charAt(index)== "'"){
token.type = "text";
var indexA:int = index;
reg = /['\n\r]/mg;//改行か次の[']が現れる位置
do{//エスケープ処理
reg.lastIndex = indexA+1;
result = reg.exec(source);
if(result==null)break;
else indexA = result.index;
}while(source.charAt(indexA-1)=="\\");//["]が現れてもその前がエスケープなら再検索
//トークン作成
if(result == null){//エラー
token.text = "'";
token.type = "other";
index01 = index+1;//次の開始位置
return index01;
}
else{//
token.text = source.substring(index,result.index+1);
index01 = result.index+1;//次の開始位置
return index01;
}
}
//トークンタイプ:文字列定数2
if(source.charAt(index)== '"'){
token.type = "text";
var indexB:int = index;
reg = /["\n\r]/mg;//改行か次の["]が現れる位置
do{//エスケープ処理
reg.lastIndex = indexB+1;
result = reg.exec(source);
if(result==null)break;
else indexB = result.index;
}while(source.charAt(indexB-1)=="\\");//["]が現れてもその前がエスケープなら再検索
//トークン作成
if(result == null){//エラー
token.text = '"';
token.type = "other";
index01 = index+1;//次の開始位置
return index01;
}
else{//
token.text = source.substring(index,result.index+1);
index01 = result.index+1;//次の開始位置
return index01;
}
}
if(source.charAt(index)== "/"){
//一行コメント
if(source.charAt(index+1)== "/"){//
reg = /[\n\r]/mg;
reg.lastIndex = index+2;
result = reg.exec(source);
//トークン作成
token.type = "comment";
if(result == null){
token.text = source.substring(index,source.length);
index01 = source.length;//次の開始位置
return index01;
}
else{//
token.text = source.substring(index,result.index);
index01 = result.index;//次の開始位置
return index01;
}
}
//複数行コメント
if(source.charAt(index+1)== "*"){
reg = /\*\//mg;
reg.lastIndex = index+2;
result = reg.exec(source);
//トークン作成
token.type = "comment";
if(result == null){//エラー
token.type = "other";
token.text = '/*';
index01 = index+2;//次の開始位置
return index01;
}
else{//
token.text = source.substring(index,result.index+2);
index01 = result.index+2;//次の開始位置
return index01;
}
}
}
//英数字
if((source.charCodeAt(index) > 64 && source.charCodeAt(index) < 91) ||// A-Z
(source.charCodeAt(index) > 96 && source.charCodeAt(index) < 123)||// a-z
source.charCodeAt(index) == 95 ){// _
reg = /[^a-zA-Z0-9_]/mg;//英数字以外が現れる位置
reg.lastIndex = index+1;
result = reg.exec(source);
token.type = "string";//半角英数字
if(result == null){//
token.text = source.substring(index,source.length);
index01 = source.length;//次の開始位置
return index01;
}
else{//
token.text = source.substring(index,result.index);
index01 = result.index;//次の開始位置
return index01;
}
}
//数値
if(source.charCodeAt(index) > 47 && source.charCodeAt(index) < 58){
reg = /[^a-zA-Z0-9_.]/mg;
reg.lastIndex = index+1;
result = reg.exec(source);
token.type = "number";//半角英数字
if(result == null){
token.text = source.substring(index,source.length);
index01 = source.length;//次の開始位置
//return index01;
}
else{//
token.text = source.substring(index,result.index);
index01 = result.index;//次の開始位置
//return index01;
}
num();//種類判別
return index01;
}
//半角記号
if(source.charCodeAt(index)<127 && source.charCodeAt(index)>32){
token.type = "mark";
token.text = source.charAt(index);
index01 = index+1;
return index01;
}
//その他文字列 マルチバイト文字等はここに入る
token.type = "other";
reg = /[\x01-\x7f]/mg;
reg.lastIndex = index+1;
result = reg.exec(source);
if(result == null){
token.text = source.substring(index,source.length);
index01 = source.length;//次の開始位置
return index01;
}
else{//
token.text = source.substring(index,result.index);
index01 = result.index;//次の開始位置
return index01;
}
}
//数値の処理
//-------------------------------------------------------------------
private function num():void{
var token:Object= list[listLength-1];
var index:int = token.index;
//0~9だけなら整数値。小数点が1つだけあるなら実数値。どちらにも該当しなければ数値ではない。
if(token.text.search(/[^0-9]/mg)== -1){//数字のみ →整数値or8進数
if(token.text.charAt(0)=="0" && 1<token.text.length){//0から始まれば8進数かも(ただし0以降に数字があれば)
if(token.text.search(/[^0-7]/mg)!= -1){//0から始まったけど8進数ではなかった
token.type = "other";
token.tag += "oct:1\n";
}
//8進数確定
token.tag += "oct\n";
token.value = uint(parseInt(token.text,8));
}
//整数
token.tag += "int\n";
token.value = uint(parseInt(token.text));
}
else{
var a:Array = token.text.match(/[.]/mg);//小数点が1つだけなら実数。それ以外はよくわからない何か。
if(a.length == 1){//実数値
if(token.text.search(/[^0-9.]/mg) != -1 || //数字と小数点以外の文字が入ってたらNG
token.text.search(/[.]/mg) == token.text.length-1){//小数点で終わってたらNG
token.type = "other";
token.tag += "real:NG1\n";
}
token.tag += "real\n";
token.value = Number(token.text);
}
else{//整数、実数以外
if(token.text.charAt(0)=="0"){//
//2進
if(token.text.charAt(1)=="b" || token.text.charAt(1)=="B"){
a = token.text.match(/[^01]/mg);
if(a.length != 1 || token.text.length<3)token.type = "other";
else{
token.tag += "bin\n";
var bin:String = token.text.substring(2,token.text.length);
token.value = uint(parseInt(bin,2));
}
}
//16進
else if(token.text.charAt(1)=="x" || token.text.charAt(1)=="X"){//
a = token.text.match(/[^0-9a-fA-F]/mg);
if(a.length != 1 || token.text.length<3)token.type = "other";
else{
token.tag += "hex\n";
token.value = uint(parseInt(token.text,16));
}
}
//8進
else if(token.text.charAt(1)=="o" || token.text.charAt(1)=="O"){
a = token.text.match(/[^0-7]/mg);
if(a.length != 1 || token.text.length<3)token.type = "other";
else{
token.tag += "oct\n";
var oct:String = token.text.substring(2,token.text.length);
token.value = uint(parseInt(oct,8));
}
}
else{
//8進
a = token.text.match(/[^0-7]/mg);
if(a.length != 0 || token.text.length<2)token.type = "other";
else{//
token.tag += "oct\n";
token.value = uint(parseInt(token.text,8));
}
}
}
else{//
token.type = "other";
}
}
}
}//function
//-------------------------------
}//class
////////////////////////////////////////////////////////////////////////
// TextEditor class
////////////////////////////////////////////////////////////////////////
/*
--TextFieldへの追加機能--
・改行入力
・タブ入力
・オートインデント
・アンドゥ/リドゥ
・エディタの前後関係
*/
import flash.text.TextField;
import flash.events.*;
class TextEditor extends TextField {
private var prevText:String = "";//text変更前の状態
private var indentStr:String;
private var sc:StringComparator = new StringComparator();
private var historyManager:HistoryManager = new HistoryManager();
private var comparator:StringComparator = new StringComparator();
public function TextEditor():void {
this.type = "input";//入力可能
//this.multiline = true;//マルチライン
this.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
this.addEventListener(FocusEvent.KEY_FOCUS_CHANGE,focusChangeListener);//タブキーによるフォーカス変更をキャンセル
this.addEventListener(Event.CHANGE,onChange);//textの変更
}
private var sw:Boolean;
private function onChange(e:Event):void{
if(sw==false)addHistory();
if(sw2==true){
this.multiline = false;
sw2=false;
}
prevText = this.text;
sw=false;
}
private function focusChangeListener(e:FocusEvent):void{
e.preventDefault();
}
//入力履歴更新
private function addHistory():void{
comparator.compare(prevText, this.text);
var entry:HistoryEntry = new HistoryEntry(comparator.commonPrefixLength);
entry.removeText = prevText.substring(comparator.commonPrefixLength, prevText.length - comparator.commonSuffixLength);
entry.addText = this.text.substring(comparator.commonPrefixLength, this.text.length - comparator.commonSuffixLength);
historyManager.appendEntry(entry);
prevText = this.text;
}
//キー入力
private var sw2:Boolean;
private function onKey(e:KeyboardEvent):void {
//カーソル操作
if(e.keyCode <=40 && e.keyCode>=37){
moveCaret(e.keyCode);
}
// Ctrl+Z : UNDO
if (e.keyCode == 90 && e.ctrlKey) {
sw=true;
undo();
return;
}
// Ctrl+Y : REDO
if (e.keyCode == 89 && e.ctrlKey) {
sw=true;
redo();
return;
}
//ペースト
if(e.keyCode == 86 && e.ctrlKey){
this.multiline = true;//改行を含むコピーに対応
sw2=true;
}
//キャレット位置に改行文字を挿入
if(e.keyCode == 13 || e.keyCode == 108){
lineFeed();
return;
}
//tab
if (e.keyCode == 9) {
tab();
return;
}
}
private function moveCaret(keyCode:int):void{
if(keyCode == 40 && this.caretIndex == this.length){// ↓
if(nextTF==null)return;
stage.focus = nextTF;
//キャレット位置セット
nextTF.setSelection(0,0);
}
if(keyCode == 39 && this.caretIndex == this.length){// →
if(nextTF==null)return;
stage.focus = nextTF;
nextTF.setSelection(0,0);
}
if(keyCode == 38 && this.caretIndex == 0){// ↑
if(prevTF==null)return;
stage.focus = prevTF;
prevTF.setSelection(prevTF.length,prevTF.length);
}
if(keyCode == 37 && this.caretIndex == 0){// ←
if(prevTF==null)return;
stage.focus = prevTF;
prevTF.setSelection(prevTF.length,prevTF.length);
}
}
//テキストフィールド間のキャレット移動
public function setNextTF(TF:TextField):void{
nextTF=TF;
}
public function setPrevTF(TF:TextField):void{
prevTF=TF;
}
private var prevTF:TextField = null;
private var nextTF:TextField = null;
//改行
public function lineFeed():void{
//sw2=true;
var str1:String;
var str2:String;
var scrV:int = this.scrollV;
str1 = this.text.substring(0, this.caretIndex);
str2 = this.text.substring(this.caretIndex, this.length);
this.text = str1;
this.appendText("\n");//キャレット位置で改行
//インデント構造(タブ&スペースの構成)を調べる
var indent:int=0;
var prevReturn:int = this.text.lastIndexOf('\r', this.caretIndex-1);//前回の改行位置
//一つ前の改行直後に続くタブコードの数=インデント深度
for (var j:int = prevReturn+1; j < this.caretIndex; j++) {
if (this.text.charAt(j) == '\t' || this.text.charAt(j) == ' ' ) indent++;
else break;
}
//上の行のインデントに従う
var iStr:String = this.text.slice(prevReturn + 1, prevReturn + 1 + indent);
indentStr = iStr;
this.appendText(iStr);//インデント
this.text += str2;//結合
//キャレット位置をインクリメント
this.scrollV = scrV;
this.setSelection(this.caretIndex +indent + 1, this.caretIndex +indent + 1);
this.dispatchEvent(new Event(Event.CHANGE));
}
//タブ
public function tab():void{
var str1:String;
var str2:String;
str1 = this.text.substring(0, this.caretIndex);
str2 = this.text.substring(this.caretIndex, this.length);
this.text = str1 + '\t' + str2;
//キャレット位置をインクリメント
this.setSelection(this.caretIndex + 1, this.caretIndex + 1);
this.dispatchEvent(new Event(Event.CHANGE));
}
//アンドゥ
public function undo():void {
if (historyManager.canBack) {
var entry:HistoryEntry = historyManager.back();
//テキストセット
this.replaceText(entry.index, entry.index + entry.addText.length, entry.removeText);
//キャレット位置セット
this.setSelection(entry.index + entry.removeText.length, entry.index + entry.removeText.length);
}
this.dispatchEvent(new Event(Event.CHANGE));
}
//リドゥ
public function redo():void {
if (historyManager.canForward) {
var entry:HistoryEntry = historyManager.forward();
//テキストセット
this.replaceText(entry.index, entry.index + entry.removeText.length, entry.addText);
//キャレット位置セット
this.setSelection(entry.index + entry.addText.length, entry.index + entry.addText.length);
}
this.dispatchEvent(new Event(Event.CHANGE));
}
}
import __AS3__.vec.Vector;
class HistoryManager {
public var currentIndex:int = 0;
public var length:int=0;
private var entries:Vector.<HistoryEntry>;
public function HistoryManager() {
entries = new Vector.<HistoryEntry>();
}
//履歴追記
public function appendEntry(entry:HistoryEntry):void {
entries.length = currentIndex;
length = entries.length;
entries.push(entry);
currentIndex++;// = entries.length;
}
//履歴削除
public function clear():void {
currentIndex = 0;
entries.length = 0;
}
//リドゥ可能
public function get canForward():Boolean {
return currentIndex < entries.length;
}
//アンドゥ可能
public function get canBack():Boolean {
return currentIndex > 0;
}
//リドゥ
public function forward():HistoryEntry {
return entries[currentIndex++];
}
//アンドゥ
public function back():HistoryEntry {
return entries[--currentIndex];
}
}
class HistoryEntry {
public var index:int;
public var removeText:String;
public var addText:String;
public function HistoryEntry(index:int=0, remove:String="", add:String="") {
this.index = index;//文字列先頭位置
this.removeText = remove;//消した文字列
this.addText = add;//追加した文字列
}
}
// 文字列の左右一致を数える
class StringComparator {
// 左側の共通文字列長
public var commonPrefixLength:int;
// 右側の共通文字列長
public var commonSuffixLength:int;
/**
* 2つの文字列を比較し、commonPrefixLengthとcommonSuffixLengthをセットする
*
* @param str1 比較する文字列の一方
* @param str2 比較する文字列の他方
*/
public function compare(str1:String, str2:String):void {
var minLength:int = Math.min(str1.length, str2.length);
var step:int, l:int, r:int;
step = Math.pow(2, Math.floor(Math.log(minLength) / Math.log(2)));
for (l=0; l<minLength; ) {
if (str1.substr(0, l + step) != str2.substr(0, l + step)) {
if (step == 1) { break; }
step >>= 1;
} else {
l += step;
}
}
l = Math.min(l, minLength);
minLength -= l;
step = Math.pow(2, Math.floor(Math.log(minLength) / Math.log(2)));
for (r=0; r<minLength; ) {
if (str1.substr(-r - step) != str2.substr(-r - step)) {
if (step == 1) { break; }
step >>= 1;
} else {
r += step;
}
}
r = Math.min(r, minLength);
commonPrefixLength = l;
commonSuffixLength = r;
}
}
//スクロールバー
import flash.display.*;
class scroolBar extends Sprite{
private var s1:Sprite;//スライダー
private var s2:Sprite;//スクロールバー
public function scroolBar():void{
}
}