var KEY_LEFT = "LEFT key code";
var KEY_UP = "UP key code";
var KEY_RIGHT = "RIGHT key code";
var KEY_DOWN = "DOWN key code";
var KEY_ENTER = "ENTER key code";
/*focusGroup class*/
/* focusGroup is a class which manages 4 direction key, ok key and mouse
* navigation among members it has. Developer can create an object of focusGroup
* class and add members to it.
* "gotFocus" function will be executed when a group newly get focus
* "lostFocus" function will be executed when a group lost focus
*/
var focusGroup = function(){
this.curFocusX = null; //int
this.curFocusY = null; //int
this.curFocusMember = null; //focusMember
this.members = new Array(); //array of focusMember
this.gotFocus = null; //function
this.lostFocus = null; //function
}
/*focusMember class*/
var focusMember = function(){
this.groupBelong = null;
this.eventObject = null;
this.fromX = null;
this.toX = null;
this.fromY = null;
this.toY = null;
this.showFocus = null;
this.handleMouseOver = null;
this.handleMouseOut = null;
this.showUnfocus = null;
this.doWork = null;
this.navLeft = null;
this.navRight = null;
this.navUp = null;
this.navDown = null;
}
/*addMember to focusGroup*/
/* param : pEventObject - Optional. If this is not null, "onmouseover" and "onclick" event will be set automatically.
* pFromX - Start row of area one member occupy. Relative to other members, smaller means upper. "int" only
* pToX - End row of area one member occupy. Relative to other members, smaller means upper. "int" only
* pFromY - Start column of area one member occupy. Relative to other members, smaller means left. "int" only
* pToY - End column of area one member occupy. Relative to other members, smaller means left. "int" only
* pShowFocus - Function need to be executed to show focus on designated member
* pShowUnfocus - Function need to be executed to show unfocus on designated member
* pDoWork - Function need to be executed when a member is clicked or ok button is pressed when it has focus
* pNavLeft - Optional. Function need to be executed to do something rather than normal 4 direction key navigation.
* pNavRight - Optional. Function need to be executed to do something rather than normal 4 direction key navigation.
* pNavUp - Optional. Function need to be executed to do something rather than normal 4 direction key navigation.
* pNavDown - Optional. Function need to be executed to do something rather than normal 4 direction key navigation.
*/
focusGroup.prototype.addMember = function(pEventObject, pFromX, pToX, pFromY, pToY, pShowFocus, pShowUnfocus, pDoWork, pNavLeft, pNavRight, pNavUp, pNavDown){
/*check if index values are integer*/
if((pFromX - Math.floor(pFromX)) != 0){alert("Index should be integer.\n" + pFromX + " is not acceptable.\nIgnored."); return;}
if((pToX - Math.floor(pToX)) != 0){alert("Index should be integer.\n" + pToX + " is not acceptable.\nIgnored."); return;}
if((pFromY - Math.floor(pFromY)) != 0){alert("Index should be integer.\n" + pFromY + " is not acceptable.\nIgnored."); return;}
if((pToY - Math.floor(pToY)) != 0){alert("Index should be integer.\n" + pToY + " is not acceptable.\nIgnored."); return;}
/*check whether the range is correct*/
if(pFromX > pToX){alert("toX shoule be bigger than fromX\nfromX : " + pFromX + "\ntoX : " + pToX + "\nIgnored."); return;}
if(pFromY > pToY){alert("toY shoule be bigger than fromY\nfromY : " + pFromY + "\ntoY : " + pToY + "\nIgnored."); return;}
/*check if specified area is already occupied*/
for(var i = 0; i < this.members.length ; i++){
if(
(
((Number(this.members[i].fromX) <= Number(pFromX)) && (Number(pFromX) <= Number(this.members[i].toX)))
||
((Number(this.members[i].fromX) <= Number(pToX)) && (Number(pToX) <= Number(this.members[i].toX)))
)
&&
(
((Number(this.members[i].fromY) <= Number(pFromY)) && (Number(pFromY) <= Number(this.members[i].toY)))
||
((Number(this.members[i].fromY) <= Number(pToY)) && (Number(pToY) <= Number(this.members[i].toY)))
)
)
{
alert(pFromX + ", " + pToX + ", " + pFromY + ", " + pToY + " overlaps existing one.\nIgnored.");
return;
}
}
var newMember = new focusMember();
newMember.groupBelong = this;
newMember.eventObject = pEventObject;
newMember.fromX = pFromX;
newMember.toX = pToX;
newMember.fromY = pFromY;
newMember.toY = pToY;
newMember.showFocus = pShowFocus;
newMember.showUnfocus = pShowUnfocus;
newMember.doWork = pDoWork;
newMember.clickHandler = function(){
if(newMember.groupBelong.curFocusMember != newMember){
if(newMember.groupBelong.curFocusMember == null && newMember.groupBelong.gotFocus != null){newMember.groupBelong.gotFocus();}
/*unfocus current focus*/
newMember.groupBelong.releaseFocus();
/*focus new one*/
newMember.groupBelong.curFocusX = newMember.fromX;
newMember.groupBelong.curFocusY = newMember.fromY;
newMember.groupBelong.curFocusMember = newMember;
if(newMember.groupBelong.curFocusMember.showFocus != null){newMember.groupBelong.curFocusMember.showFocus();}
}
newMember.doWork();
}
newMember.navLeft = pNavLeft;
newMember.navRight = pNavRight;
newMember.navUp = pNavUp;
newMember.navDown = pNavDown;
this.members[this.members.length] = newMember;
/*set onmouseover and onclick event handler*/
if(pEventObject){
if(pShowFocus){
var thisGroup = this;
var xIndexToSet = Math.floor((pFromX + pToX)/2);
var yIndexToSet = Math.floor((pFromY + pToY)/2);
newMember.handleMouseOver = function(){
if(thisGroup.curFocusMember && thisGroup.curFocusMember.showUnfocus){
thisGroup.curFocusMember.showUnfocus();
}else{
if(thisGroup.gotFocus){thisGroup.gotFocus();}
}
thisGroup.curFocusX = xIndexToSet;
thisGroup.curFocusY = yIndexToSet;
thisGroup.curFocusMember = newMember;
pShowFocus();}
addEventHandler(pEventObject, "mouseover", newMember.handleMouseOver);
}
//if(pDoWork){addEventHandler(pEventObject, "click", newMember.doWork);}
if(pDoWork){addEventHandler(pEventObject, "click", newMember.clickHandler);}
//add onmouseout event handler
if(pShowUnfocus){
newMember.handleMouseOut = function(){pShowUnfocus();}
addEventHandler(pEventObject, "mouseout", newMember.handleMouseOut);
}
}
}
/*delete member from focusGroup*/
/* param : xIndex - x(row) point of a member to delete
* yIndex - y(column) point of a member to delete
* deleteMember(xIndex, yIndex) will delete a member of which area contain designated point from it's group.
* If no member was found, nothing will be done.
* If found member is currently focused, it's group will release focus.
*/
focusGroup.prototype.deleteMember = function(xIndex, yIndex){
for(var i = 0; i < this.members.length ; i++){
if((Number(xIndex) >= Number(this.members[i].fromX))
&& (Number(xIndex) <= Number(this.members[i].toX))
&& (Number(yIndex) >= Number(this.members[i].fromY))
&& (Number(yIndex) <= Number(this.members[i].toY))){
var memberToDelete = this.members[i];
/*remove registered event handler*/
if(memberToDelete.eventObject){
if(memberToDelete.showFocus){removeEventHandler(memberToDelete.eventObject, "mouseover", memberToDelete.handleMouseOver);}
//if(memberToDelete.doWork){removeEventHandler(memberToDelete.eventObject, "click", memberToDelete.doWork);}
if(memberToDelete.doWork){removeEventHandler(memberToDelete.eventObject, "click", memberToDelete.clickHandler);}
if(memberToDelete.handleMouseOut){removeEventHandler(memberToDelete.eventObject, "mouseout", memberToDelete.handleMouseOut);}
}
/*if the member to delete is currently focused, release focus*/
if(this.curFocusMember == memberToDelete){this.releaseFocus();}
this.members = this.members.slice(0, i).concat(this.members.slice(i + 1));
}
}
}
/*clear members from focusGroup*/
/* clear members will delete all member of a group.
* group will release focus.
*/
focusGroup.prototype.clearMembers = function(){
//release focus
this.releaseFocus();
for(var i = 0; i < this.members.length ; i++){
var memberToDelete = this.members[i];
/*remove registered event handler*/
if(memberToDelete.eventObject){
if(memberToDelete.showFocus){removeEventHandler(memberToDelete.eventObject, "mouseover", memberToDelete.handleMouseOver);}
//if(memberToDelete.doWork){removeEventHandler(memberToDelete.eventObject, "click", memberToDelete.doWork);}
if(memberToDelete.doWork){removeEventHandler(memberToDelete.eventObject, "click", memberToDelete.clickHandler);}
}
}
//initiate members array
this.members = new Array();
}
/*execute member*/
/* param : xIndex - x(row) point of a member to execute
* yIndex - y(column) point of a member to execute
* executeMember(xIndex, yIndex) will execute "doWork" function of a member whose area contains designated point.
* If no member was found, nothing will be done.
* If no function is defined as "doWork" of found member, nothing will be done.
*/
focusGroup.prototype.executeMember = function(xIndex, yIndex){
for(var i = 0; i < this.members.length ; i++){
if((Number(xIndex) >= Number(this.members[i].fromX))
&& (Number(xIndex) <= Number(this.members[i].toX))
&& (Number(yIndex) >= Number(this.members[i].fromY))
&& (Number(yIndex) <= Number(this.members[i].toY))){
if(this.members[i].doWork){this.members[i].doWork();}
return;
}
}
}
/*handle focus*/
/* param : xIndex - x(row) point of a member to focus
* yIndex - y(column) point of a member to focus
* focusMember(xIndex, yIndex) will focus a member whose area contain designated point.
* If no member was found, nothing will be done.
* If a member found, currently focused member will be unfocused.
*/
focusGroup.prototype.focusMember = function(xIndex, yIndex){
/*If a group get a focus newly and "gotFocus" function is not null,call it.*/
//if(this.curFocusMember == null && this.gotFocus != null){this.gotFocus();}
if(xIndex != null && yIndex != null){
/*check if specified index is in range of a member*/
for(var i = 0; i < this.members.length ; i++){
if((Number(xIndex) >= Number(this.members[i].fromX))
&& (Number(yIndex) >= Number(this.members[i].fromY))
&& (Number(xIndex) <= Number(this.members[i].toX))
&& (Number(yIndex) <= Number(this.members[i].toY))){
if(this.curFocusMember == null && this.gotFocus != null){this.gotFocus();}
/*unfocus current focus*/
this.releaseFocus();
/*focus new one*/
this.curFocusX = xIndex;
this.curFocusY = yIndex;
this.curFocusMember = this.members[i];
if(this.curFocusMember.showFocus != null){this.curFocusMember.showFocus();}
break;
}
}
}
}
/*handle key input*/
/* param : keyInput - KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_ENTER
*
* HandleKeyInput(keyInput) manages navigation by key input.
* If the keyInput is one of four direction key(KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN) it will move focus to
* designated direction. The navigation logic works in general manner. The focus will be moved to the
* closest member in designated direction. Even if a member occupies an area rather than one point,
* focus is always one point internally. Which mean event if in case the focused member is same,
* movement of focus can differ as the internal focus is different case by case. If there is no other
* member to designated direction, nothing will be done.
*
* To make an exception rather than general focus movement, "navLeft", "navRight", "navUp" or "navDown" can be
* defined to each member's each direction when add a member to a group. This also can be used to move focus
* out of a group. Be sure to release focus of a group when move focus out of the group.
*
* If the keyInput is KEY_ENTER, "doWork" function of focused member, which is set when add a member, will be executed.
*/
focusGroup.prototype.handleKeyInput = function(keyInput){
/*if there is no focus in this group at the moment, ignore key input*/
if(this.curFocusX == null || this.curFocusY == null || this.curFocusMember == null){return;}
else if(keyInput == KEY_LEFT){
if(this.curFocusMember.navLeft != null){
this.curFocusMember.navLeft();
}else{
/*check if there is member to the left of currently focused member(X : in range, Y : biggest among smaller)*/
var xToMove = null;
var yToMove = null;
var foundMemberIndex = null;
var xToScan = this.curFocusX;
var leftEnd = false;
var rightEnd = false;
for(var adj = 0, swing = 1 ; !(leftEnd && rightEnd) ; adj++, swing *= -1){
xToScan += (adj * swing);
if(xToScan < this.curFocusMember.fromX){leftEnd = true; continue;}
else if(xToScan > this.curFocusMember.toX){rightEnd = true; continue;}
for(var i = 0 ; i < this.members.length ; i++){
if((Number(xToScan) >= Number(this.members[i].fromX))
&&(Number(xToScan) <= Number(this.members[i].toX))
&&(Number(this.curFocusY) > Number(this.members[i].toY))){
if(yToMove == null){
yToMove = Number(this.members[i].toY);
xToMove = xToScan;
foundMemberIndex = i;
}else{
if(yToMove < Number(this.members[i].toY)){
yToMove = Number(this.members[i].toY);
xToMove = xToScan;
foundMemberIndex = i;
}
}
}
}
}
/*if nearest member found, focus it*/
if(yToMove != null && xToMove != null){
this.curFocusY = yToMove;
this.curFocusX = xToMove;
if(this.curFocusMember.showUnfocus != null){this.curFocusMember.showUnfocus();}
this.curFocusMember = this.members[foundMemberIndex];
if(this.curFocusMember.showFocus != null){this.curFocusMember.showFocus();}
}
}
}else if(keyInput == KEY_RIGHT){
if(this.curFocusMember.navRight != null){
this.curFocusMember.navRight();
}else{
/*check if there is member to the right of currently focused member(X : in range, Y : smallest among bigger)*/
var xToMove = null;
var yToMove = null;
var foundMemberIndex = null;
var xToScan = this.curFocusX;
var leftEnd = false;
var rightEnd = false;
for(var adj = 0, swing = 1 ; !(leftEnd && rightEnd) ; adj++, swing *= -1){
xToScan += (adj * swing);
if(xToScan < this.curFocusMember.fromX){leftEnd = true; continue;}
else if(xToScan > this.curFocusMember.toX){rightEnd = true; continue;}
for(var i = 0; i < this.members.length ; i++){
if((Number(xToScan) >= Number(this.members[i].fromX))
&&(Number(xToScan) <= Number(this.members[i].toX))
&&(Number(this.curFocusY) < Number(this.members[i].fromY))){
if(yToMove == null){
yToMove = Number(this.members[i].fromY);
xToMove = xToScan;
foundMemberIndex = i;
}else{
if(yToMove > Number(this.members[i].fromY)){
yToMove = Number(this.members[i].fromY);
xToMove = xToScan;
foundMemberIndex = i;
}
}
}
}
}
/*if nearest member found, focus it*/
if(yToMove != null && xToMove != null){
this.curFocusY = yToMove;
this.curFocusX = xToMove;
if(this.curFocusMember.showUnfocus != null){this.curFocusMember.showUnfocus();}
this.curFocusMember = this.members[foundMemberIndex];
if(this.curFocusMember.showFocus != null){this.curFocusMember.showFocus();}
}
}
}else if(keyInput == KEY_UP){
if(this.curFocusMember.navUp != null){
this.curFocusMember.navUp();
}else{
/*check if there is member to the up of currently focused member(X : biggest among smaller, Y : in range)*/
var xToMove = null;
var yToMove = null;
var foundMemberIndex = null;
var yToScan = this.curFocusY;
var leftEnd = false;
var rightEnd = false;
for(var adj = 0, swing = 1 ; !(leftEnd && rightEnd) ; adj++, swing *= -1){
yToScan += (adj * swing);
if(yToScan < this.curFocusMember.fromY){leftEnd = true; continue;}
else if(yToScan > this.curFocusMember.toY){rightEnd = true; continue;}
for(var i = 0; i < this.members.length ; i++){
if((Number(yToScan) >= Number(this.members[i].fromY))
&&(Number(yToScan) <= Number(this.members[i].toY))
&&(Number(this.curFocusX) > Number(this.members[i].toX))){
if(xToMove == null){
xToMove = Number(this.members[i].toX);
yToMove = yToScan;
foundMemberIndex = i;
}else{
if(xToMove < Number(this.members[i].toX)){
xToMove = Number(this.members[i].toX);
yToMove = yToScan;
foundMemberIndex = i;
}
}
}
}
}
/*if nearest member found, focus it*/
if(xToMove != null && yToMove != null){
this.curFocusX = xToMove;
this.curFocusY = yToMove;
if(this.curFocusMember.showUnfocus != null){this.curFocusMember.showUnfocus();}
this.curFocusMember = this.members[foundMemberIndex];
if(this.curFocusMember.showFocus != null){this.curFocusMember.showFocus();}
}
}
}else if(keyInput == KEY_DOWN){
if(this.curFocusMember.navDown != null){
this.curFocusMember.navDown();
}else{
/*check if there is member to the up of currently focused member(X : smallest among bigger, Y : in range)*/
var xToMove = null;
var yToMove = null;
var foundMemberIndex = null;
var yToScan = this.curFocusY;
var leftEnd = false;
var rightEnd = false;
for(var adj = 0, swing = 1 ; !(leftEnd && rightEnd) ; adj++, swing *= -1){
yToScan += (adj * swing);
if(yToScan < this.curFocusMember.fromY){leftEnd = true; continue;}
else if(yToScan > this.curFocusMember.toY){rightEnd = true; continue;}
for(var i = 0; i < this.members.length ; i++){
if((Number(yToScan) >= Number(this.members[i].fromY))
&&(Number(yToScan) <= Number(this.members[i].toY))
&&(Number(this.curFocusX) < Number(this.members[i].fromX))){
if(xToMove == null){
xToMove = Number(this.members[i].fromX);
yToMove = yToScan;
foundMemberIndex = i;
}else{
if(xToMove > Number(this.members[i].fromX)){
xToMove = Number(this.members[i].fromX);
yToMove = yToScan;
foundMemberIndex = i;
}
}
}
}
}
/*if nearest member found, focus it*/
if(xToMove != null){
this.curFocusX = xToMove;
this.curFocusY = yToMove;
if(this.curFocusMember.showUnfocus != null){this.curFocusMember.showUnfocus();}
this.curFocusMember = this.members[foundMemberIndex];
if(this.curFocusMember.showFocus != null){this.curFocusMember.showFocus();}
}
}
}else if(keyInput == KEY_ENTER){
if(this.curFocusMember != null && this.curFocusMember.doWork != null){this.curFocusMember.doWork();}
}else{
//alert("keyInput should be one of KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_ENTER");
}
}
/*ask group to release it's focus*/
focusGroup.prototype.releaseFocus = function(){
if(this.curFocusMember != null && this.curFocusMember.showUnfocus != null){this.curFocusMember.showUnfocus();}
this.curFocusX = null;
this.curFocusY = null;
this.curFocusMember = null;
if(this.lostFocus != null){this.lostFocus();}
}
/*show all members*/
focusGroup.prototype.showMembers = function(){
var stringToPrint = "Members\n";
for(var i = 0; i < this.members.length ; i++){
stringToPrint += "\n" + i + " : " + this.members[i].fromX + ", " + this.members[i].toX + ", " + this.members[i].fromY + ", " + this.members[i].toY;
}
alert(stringToPrint);
}
Comments
Post a Comment