/* DynaMan version .7 March,2007
copyright (c) 2007 Henrik Bechmann, Bechmann Software Services, Toronto Canada, all rights reserved.
www.bechmann.ca. General Public Licence.
*/

//for dataman: REST parameters, qualifiers allowed: context, action, source, selection, index, format

Array.prototype.in_array=function(theString) {
 var theLength=this.length;
 for (var i=0;i<theLength;i++) {
  var s = this[i].toString();
  if (s==theString) return true;
 }
 return false;
}

Array.prototype.clone=function() {
 var theNewArray=[],theLength=this.length;
 for (var i=0;i<theLength;i++) {
  theNewArray[i]=this[i];
 }
 return theNewArray;
}

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}
String.prototype.ltrim = function() {
	return this.replace(/^\s+/,"");
}
String.prototype.rtrim = function() {
	return this.replace(/\s+$/,"");
}

//ADLML SPECIFICATIONS...
adlml$xmlelements = {
  dlengine:{},
  dlobject:{},
  dlproperty:{}
}

//utility routines

adlml$parseboolean = function(theString) {
 return (theString=="true")?true:false;
}

//*** add parsenumber, parsedate, applydefaults

adlml$parsestyle = function(theString,theComponent) {
 var theStylePairs=theString.split(";");
 var theLength=theStylePairs.length;
 var theStyleName;
 if (!theComponent.styles) theComponent.styles={};
 for (var i=0;i<theLength;i++) {
  theStyle=theStylePairs[i].split(":");
  theStyleName=adlml$stylefuncmap[theStyle[0].trim()];
  if (theStyleName!='undefined') {
   theComponent.styles[theStyleName]=theStyle[1];
  }  
 }
 return theString;
}

// dlobject CLASSES - slot values are dynamic
// must be a class instance for every prototype instance for FireFox bug.
adlml$classes = {
 dlengine:function(){
  this.debugon=false;
  this.debugoutputelementid='debugblock';
 }
}

// dlobject PROTOTYPES - common slots - static
//must be a class for each prototype for FireFox
adlml$prototypes = {
 dlengine:{metadata:{
   className:'dlengine',
   attributes:['debugon','debugoutputelementid','debuginputelementid','runat'],
   conversions:{debugon:adlml$parseboolean}
 }}
}

//filter to convert style indexes to function names...
adlml$stylefuncmap=[];
//outerelement
adlml$stylefuncmap["top"]="top";
adlml$stylefuncmap["left"]="left";
adlml$stylefuncmap["width"]="width";
adlml$stylefuncmap["display"]="display";
//middleelement
adlml$stylefuncmap["background"]="background";
adlml$stylefuncmap["background-color"]="backgroundColor";
adlml$stylefuncmap["background-image"]="backgroundImage";
adlml$stylefuncmap["background-position"]="backgroundPosition";
adlml$stylefuncmap["background-repeat"]="backgroundRepeat";
adlml$stylefuncmap["border"]="border";
adlml$stylefuncmap["border-color"]="borderColor";
adlml$stylefuncmap["border-style"]="borderStyle";
adlml$stylefuncmap["border-width"]="borderWidth";
adlml$stylefuncmap["border-bottom"]="borderBottom";
adlml$stylefuncmap["border-left"]="borderLeft";
adlml$stylefuncmap["border-top"]="borderTop";
adlml$stylefuncmap["border-right"]="borderRight";
adlml$stylefuncmap["border-bottom-color"]="borderBottomColor";
adlml$stylefuncmap["border-left-color"]="borderLeftColor";
adlml$stylefuncmap["border-top-color"]="borderTopColor";
adlml$stylefuncmap["border-right-color"]="borderRightColor";
adlml$stylefuncmap["border-bottom-style"]="borderBottomStyle";
adlml$stylefuncmap["border-left-style"]="borderLeftStyle";
adlml$stylefuncmap["border-top-style"]="borderTopStyle";
adlml$stylefuncmap["border-right-style"]="borderRightStyle";
adlml$stylefuncmap["border-bottom-width"]="borderBottomWidth";
adlml$stylefuncmap["border-left-width"]="borderLeftWidth";
adlml$stylefuncmap["border-top-width"]="borderTopWidth";
adlml$stylefuncmap["border-right-width"]="borderRightWidth";
adlml$stylefuncmap["color"]="color";
adlml$stylefuncmap["float"]="float";
adlml$stylefuncmap["font"]="font";
adlml$stylefuncmap["font-family"]="fontFamily";
adlml$stylefuncmap["font-size"]="fontSize";
adlml$stylefuncmap["font-style"]="fontStyle";
adlml$stylefuncmap["font-variant"]="fontVariant";
adlml$stylefuncmap["font-weight"]="fontWeight";
adlml$stylefuncmap["padding"]="padding";
adlml$stylefuncmap["padding-top"]="paddingTop";
adlml$stylefuncmap["padding-bottom"]="paddingBottom";
adlml$stylefuncmap["padding-left"]="paddingLeft";
adlml$stylefuncmap["padding-right"]="paddingRight";
adlml$stylefuncmap["text-align"]="textAlign";
//innerelement
adlml$stylefuncmap["height"]="height";

//references...
adlml$tagtoslot=[];
adlml$objects=[];
adlml$objects$length=0;
adlml$engines={};

adlml$registerengine = function(theIndex,theEngine) {
 adlml$engines[theIndex]=theEngine;
}

//=========================================================================================
//configure for xml handling
adlml$setelementmaps = function() {
 var theNode=document.createElement('div');
 var theTestList,theLength,theElement,theTagName,theAttribute,theTestString;
 var recognizesnamespaces=false,returnscaps=false,returnsnamespaceprefix=false,usesattributeprefix=false;
 theNode.innerHTML='<d:dlobject xmlns:d="www.osscommons.ca/xml/adlml" d:value="value"></d:dlobject>'
 theTestString='d:dlobject';
 theTestList=theNode.getElementsByTagName(theTestString);
 theLength=theTestList.length;
 if (!theLength) {
  theTestString='dlobject';
  theTestList=theNode.getElementsByTagName(theTestString);
  theLength=theTestList.length;
  if (theLength) {recognizesnamespaces=true};
 }
 if (!theLength) {return};
 theElement=theTestList[0];
 theAttribute=theElement.getAttribute("d:value");
 if (theAttribute) {
  usesattributeprefix=true
 } else {
  theAttribute=theElement.getAttribute("value");
  if (!theAttribute) {return};
 }
 theTagName=theElement.tagName;
 switch (theTagName) {
  case "dlobject": break;
  case "DLOBJECT": returnscaps=true; break;
  case "d:dlobject": returnsnamespaceprefix=true; break;
  case "D:DLOBJECT": returnscaps=true; returnsnamespaceprefix=true; break;
 }
 adlml$initmaps(recognizesnamespaces,returnscaps,returnsnamespaceprefix,usesattributeprefix);
}

adlml$initmaps = function(recognizesnamespaces,returnscaps,returnsnamespaceprefix,usesattributeprefix) {
 var theTag,theElement;
 for (var theSlot in adlml$xmlelements) {
  theElement=theSlot;
  theTag=returnsnamespaceprefix?"d:"+theSlot:theSlot;
  theTag=returnscaps?theTag.toUpperCase():theTag;
  adlml$xmlelements[theSlot].tagName=theTag;
  adlml$xmlelements[theSlot].elementName=recognizesnamespaces?theElement:"d:"+theElement;
  adlml$tagtoslot[theTag]=theSlot;
 }
 adlml$dlattributeprefix=usesattributeprefix?'d:':'';
}

//=========================================================================================

//driver... generate engine, then objects, then render objects, use onbeforerender for custom config
adlml$setdatamodel = function(theRootHTMLElement) {
 var theObjectElementList,theScanList,theLength;
 var theElement,theObject;
 //set engines
 theObjectElementList=theRootHTMLElement.getElementsByTagName(adlml$xmlelements.dlengine.elementName);
 theLength=theObjectElementList.length;
 theScanList=[];
 for (var i=0;i<theLength;i++) {
  theScanList.push(theObjectElementList[i]);
 } 
 for (var i=0;i<theLength;i++) {
  theElement=theScanList[i];
  theObject=adlml$setengine(theElement);
 }
 //set objects
 theObjectElementList=theRootHTMLElement.getElementsByTagName(adlml$xmlelements.dlobject.elementName);
 theLength=theObjectElementList.length;
 theScanList=[];
 for (var i=0;i<theLength;i++) {
  theScanList.push(theObjectElementList[i]);
 } 
 for (var i=0;i<theLength;i++) {
  theElement=theScanList[i];
  theObject=adlml$setobject(theElement);
 }
 adlml$clearcontentwrappers(theRootHTMLElement); //kludge for FF
// document.getElementById("debugblock").innerHTML=adlml$cleanhtml(document.getElementById("menusystem").innerHTML);
 adlml.debug(adlml);
}//setdatamodel

// set engine
adlml$setengine = function(theElement) {
 var theObject,theName,theIndex;
 var theClass=theElement.getAttribute(adlml$dlattributeprefix+"dlclass");
 if (adlml$engines[theClass]) { 
  theObject=adlml$engines[theClass];
 } else {
 try { 
  theObject = new adlml$classes[theClass]();
  theObject.className=theClass;
 } catch (e) {
  return theObject;
 }
 }
 if (theObject) {
  adlml$assignattributes(theObject,theElement,theObject.metadata,theObject);
  adlml$assignproperties(theObject,theElement,theObject.metadata,theObject);
 }
 adlml$removenode(theElement);
 
 if (theObject.onsetup) theObject.onsetup(theObject);
 
 return theObject;
}

//set object
adlml$setobject = function(theElement) {
 var theObject,theName,theIndex;
 var theClass=theElement.getAttribute(adlml$dlattributeprefix+"dlclass");
 try { 
  theObject = new adlml$classes[theClass]();
  theObject.className=theClass;
 } catch (e) {
  return theObject;
 }
 theName=theElement.getAttribute(adlml$dlattributeprefix+"name");
 if (theName) theObject.name=theName;
 if(theElement.id) theObject.elementid=theElement.id;
 theObject.xmlelement=theElement;
 
 theIndex = adlml$objects$length;adlml$objects$length++;
 theObject.sessionid=theIndex;
 adlml$objects[theIndex]=theObject;
 adlml$setobjectrelations(theObject);
  
 adlml$assigndefaults(theObject,theObject.metadata,theObject);
 adlml$assignattributes(theObject,theElement,theObject.metadata,theObject);
 adlml$assignproperties(theObject,theElement,theObject.metadata,theObject);

 adlml$swapelements(theObject);
 theElement=theObject.outerelement;
 theElement.setAttribute("dlsessionid",theIndex.toString());
 
 adlml$applystyles(theObject);
 
 if (theObject.onsetup) theObject.onsetup(theObject);
 
 return theObject;
}//setobject

//swap elements
adlml$swapelements = function(theObject) {
 var theTempNode=document.createElement('div');
 var theElements,theLength,thexmlelement=theObject.xmlelement;
 theTempNode.innerHTML=theObject.metadata.html;
 theElements=theTempNode.getElementsByTagName('div');
 theLength=theElements.length;
 theObject.outerelement=theElements[0];
 theObject.middleelement=theElements[Math.min(1,theLength-1)];
 theObject.innerelement=theElements[Math.min(2,theLength-1)];
 while (thexmlelement.firstChild) {
  theObject.innerelement.appendChild(thexmlelement.firstChild);  
 }
 thexmlelement.parentNode.replaceChild(theObject.outerelement,theObject.xmlelement);
 delete theObject.xmlelement;
}

//clear content wrappers
adlml$clearcontentwrappers =function(theRootHTMLElement) {
 var theList=theRootHTMLElement.getElementsByTagName('span'),theLength,theScanList=[],theElement,theParent;
 theLength=theList.length;
 for (var i=0;i<theLength;i++) {
  if (theList[i].className=='dlcontent') theScanList.push(theList[i]);
 }
 theLength=theScanList.length;
 for (var i=0;i<theLength;i++) {
  theElement=theScanList[i];
  theParent=theElement.parentNode;
  while (theElement.firstChild) {
   theParent.insertBefore(theElement.firstChild,theElement);
  }
  adlml$removenode(theElement);
 }
}

//set object relations
adlml$setobjectrelations = function(theObject) {
 var theNode,theParentNode,theSessionid,theParentObject;
 theNode=theObject.xmlelement;
 theParentNode=theNode.parentNode;
 while (theParentNode&&theParentNode.tagName&&(theParentNode.tagName!='BODY')) {
  theSessionid=theParentNode.getAttribute("dlsessionid");
  if (theSessionid) {
   theParentObject=theObject.parent=adlml$objects[theSessionid];
   theObject.parentid=theSessionid;
   if (theParentObject.manager) {
   	  theObject.owner=theParentObject;
   } else {
   	  theObject.owner=theParentObject.owner;
   }
   theObject.ownerid=theObject.owner.sessionid;
   if (theParentObject.iscontainer) {
    theObject.container=theParentObject;
	theObject.containerid=theParentObject.sessionid;
	if (!theParentObject.components) {
	 theParentObject.components=[];
	 theParentObject.componentsid=[];
	}
   } else if (theParentObject.container) {
    theObject.container=theParentObject.container;
	theObject.containerid=theParentObject.containerid;
   }
   if (theObject.container) {
	theObject.container.components.push(theObject);
	theObject.container.componentsid.push(theObject.sessionid);
   }
   if (!theObject.parent.children) theObject.parent.children=[];
   theObject.parent.children.push(theObject);
   if (!theObject.parent.childrenid) theObject.parent.childrenid=[];
   theObject.parent.childrenid.push(theObject.sessionid);
   break;
  }
  theParentNode=theParentNode.parentNode;
 }
 if (!theObject.parent) {
  adlml.root.children.push(theObject);
  adlml.root.childrenid.push(theObject.sessionid);
 }
}

adlml$assigndefaults = function(theComponent,theMetadata,theObject) {
 if (theMetadata.setdefaults) {
  theMetadata.setdefaults(theComponent,theMetadata,theObject);
 }
 return theComponent;
}
//assign attributes
adlml$assignattributes = function(theComponent,theElement,theMetadata,theObject) {
 var theAttributes,theAttribute,theAttributeName,theLength;
 theAttributes=theMetadata.attributes;
 if (!theAttributes) return theComponent;
 theLength=theAttributes.length;
 for (var i=0;i<theLength;i++) {
  theAttributeName=theAttributes[i];
  theAttribute=theElement.getAttribute(adlml$dlattributeprefix+theAttributeName);
  if (theAttribute) {
   try {
    theAttribute=theMetadata.conversions[theAttributeName](theAttribute,theComponent)
   } catch (e) {};
   theComponent[theAttributeName]=theAttribute;
  }
 }
}//assignattributes

//assign properties
adlml$assignproperties = function(theComponent,theElement,theMetadata,theObject) {
 var theChildren,theLength,theNode,theTagName,theName;
 theChildren=adlml$getelementchildren(theElement);
 var theLength=theChildren.length
 for (var i=0;i<theLength;i++) {
  theNode=theChildren[i];
  if (theNode && (theNode.nodeType==1)) { //ELEMENT_NODE
   theTagName=theNode.tagName;
   if (theTagName==adlml$xmlelements.dlproperty.tagName) {
    theName=theNode.getAttribute(adlml$dlattributeprefix+"name");
	if (theName) {
	 if (theMetadata.properties&&theMetadata.properties[theName])
	  if (!theComponent[theName]) theComponent[theName]={};
  	  adlml$assigndefaults(theComponent[theName],theMetadata.properties[theName],theObject);
	  adlml$assignattributes(theComponent[theName],theNode,theMetadata.properties[theName],theObject);
	  adlml$assignproperties(theComponent[theName],theNode,theMetadata.properties[theName],theObject)
	}
    adlml$removenode(theNode);
   }
  }
 }
}//assignproperties

adlml$applystyles = function(theObject) {
var theSlot;
 if (theObject.styles) {
  adlml$applystylelist(theObject,theObject.styles);
 }
 if (theObject.onapplystyles) theObject.onapplystyles(theObject);
}

adlml$applystylelist = function(theObject,theStyleList) {
  for (theSlot in theStyleList) {
   switch (theSlot) {
    case "height":
     theObject.innerelement.style[theSlot]=theStyleList[theSlot];
     break;
    case "display":
    case "width":
    case "top":
    case "left":
     theObject.outerelement.style[theSlot]=theStyleList[theSlot];
     break;
    default:
     theObject.middleelement.style[theSlot]=theStyleList[theSlot];
   }
  }
}

//utilities
adlml$getelementchildren = function(theElement) {
 var theElement,theChildrenTemp,theLength,theChildren;
 theChildrenTemp=theElement.childNodes;
 theLength=theChildrenTemp.length;
 theChildren=[];
 for (i=0;i<theLength;i++) {//avoid dynamic shortening in IE
  theChildren[i]=theChildrenTemp[i];
 }
 return theChildren;
}//getelementchildren

adlml$removenode = function(theNode) {
 theNode.parentNode.removeChild(theNode);
}

//=====================================================================================
//debug
adlml$debug = function(theObject) {
 if (!adlml$engines.dlengine.debugon) return;
 var theHTML='DEBUG data:<br>'
 theHTML=adlml$displayobject(theObject,["metadata","parent","owner","children","outerelement","middleelement","innerelement","container","components","menuitem"]);
 document.getElementById(adlml$engines.dlengine.debugoutputelementid).innerHTML=theHTML;
}
adlml$displayobject = function(theObject,theExceptions) {
 var theHTML='',theSlot,displayblockstart='<div class="debug">',displayblockend='</div>';
 for (theSlot in theObject) {
  if (theExceptions && theExceptions.in_array(theSlot)) {
   theHTML+=displayblockstart+theSlot+": (suppressed:"+((theObject[theSlot] instanceof Array)?" array:->"+theObject[theSlot].length+")":" object)")+displayblockend;
   continue;
  }
  theHTML+=displayblockstart+theSlot+": ";
  theHTML+=adlml$displayitem(theObject[theSlot],theExceptions);
  theHTML+=displayblockend;
 }
// alert("theObject.className:"+theObject.className+": "+theHTML);
 return theHTML;
}
adlml$displayitem = function(theItem,theExceptions){
 var theHTML='',displayblockstart='<div class="debug">',displayblockend='</div>';
  //function
  if (typeof theItem=="function") {
   theHTML='(function)'+displayblockend;
  //object
  } else if (typeof theItem=="object") {
    //array
    if (theItem instanceof Array) {
	 theHTML+="(array:) "+displayblockstart;
	 for (var i=0;i<theItem.length;i++) {
	  var theArrayObject=theItem[i];
	  theHTML+=i+":"+adlml$displayitem(theArrayObject,theExceptions)+'<br>';
 	 } 
     theHTML+=displayblockend;
	//object
   } else {
	 if (theItem) {
	  theHTML+="(object:) "+adlml$displayobject(theItem,theExceptions);
	 } else {
      theHTML+='(empty)';
	 }
   }
  //data type
  } else {
   if (theItem) {
    theHTML+="("+(typeof theItem)+":) " + adlml$cleanhtml(theItem);
   } else {
    theHTML+="("+(typeof theItem)+":) ";
   }
  }
  return theHTML;
}
adlml$cleanhtml = function(str){
 str=str.toString();
 str = str.replace(/\&/g,"&amp;");
 str = str.replace(/\</g,"&lt;");
 str = str.replace(/\>/g,"&gt;");
 str = str.replace(/\"/g,"&quot;");
 return str;
}

//====================================================================

adlml$initialize=function() {
 // apply prototypes to classes
 for (var theClassName in adlml$prototypes) {
  adlml$classes[theClassName].prototype=adlml$prototypes[theClassName];
 }
 adlml.registerengine('dlengine',new adlml$classes['dlengine']);
 //init xml handling info
 adlml$setelementmaps()
}

adlml = {
 className:'adlml',
 registerengine:adlml$registerengine,
 setdatamodel:adlml$setdatamodel,
 engines:adlml$engines,
 root:{children:[],childrenid:[]},
 heap:adlml$objects,
 debug:adlml$debug,
 cleanhtml:adlml$cleanhtml,
 classes:adlml$classes,
 prototypes:adlml$prototypes,
 initialize:adlml$initialize,
 applystylelist:adlml$applystylelist
}


