// S.W.A.T :: SCRIPTABLE WEB APPLICATION TOOLKIT
// Copyright (C) 1999 Joe Hewitt
// Distributed under the terms of the GNU Library General Public License
// For more information, visit http://www.joehewitt.com/swat

function SWAT() {
  this.mLibraryPath = '.';
  this.mSWIDs = [];
  this.global = [];
  this.mLoaded = [];
  this.mDepQueue = [];
  
  this.mDebugText = '';
  this.mDebugMode = 1; // (0=off,1=statusbar,2=save)
    
  this.mClassIndex = {};
}

function SWAT_debug(aText) {
  this.mDebugText += aText;
  if (this.mDebugMode == 1) {
    window.status = this.mDebugText;
  }
}

function SWAT_loadLibrary(aClass, aPath, aBrowser) {
  var path = this.mLibraryPath+'/'+aPath+'/'+aClass+
    (aBrowser ? '_'+aBrowser : '');

  if (!this.mLoaded[path]) {
    this.mLoaded[path] = true;
    document.write(
      '<SCRIPT LANGUAGE="Javascript1.2" SRC="'+path+
      '.js" TYPE="text/javascript"><\/SCRIPT>');
  }
}

function SWAT_loadLocal(aClass, aPath, aBrowser) {
  var path = aPath+'/'+aClass+(aBrowser ? '_'+aBrowser : '');

  if (!this.mLoaded[path]) {
    this.mLoaded[path] = true;
      document.write('<SCRIPT LANGUAGE="Javascript1.2" SRC="'+path+'.js" TYPE="text/javascript"><\/SCRIPT>');
  }
}

function SWAT_setLibraryPath(aPath) { this.mLibraryPath = aPath; }
function SWAT_getLibraryPath(aIsRelative) { return this.mLibraryPath; }

function SWAT_checkInClass(aClass) {
  var exists = this.mClassIndex[aClass];
  if (!exists) {
    var index = 
     { Properties: [],
       Methods: [],
       Events: [],
       Inheritance: [],
       Interfaces: [],
       Dependencies: [],
       Functions: [],
       Fields: []};
    index.registered = false;
    this.mClassIndex[aClass] = index;
  }
  return exists;
}

var stuff = [ 'Properties', 'Methods', 'Events', 
              'Inheritance', 'Interfaces', 'Dependencies',
              'Functions', 'Fields'];
for (var i in stuff)
  eval(
    'function SWAT_attach'+stuff[i]+'(aClass) {' +
      'this.checkInClass(aClass);' +
      'for (var i = 1; i < arguments.length; i++) {' +
        'var c = this.mClassIndex[aClass]["'+stuff[i]+'"]; '+
        'c[c.length] = arguments[i];' +
      '}' +
    '}'
  );

function SWAT_registerClass(aClass) {
  this.mSWIDs[aClass] = 0;
  this.attachStuff(aClass, aClass);  
  this.regFunctions(aClass);
  this.regFields(aClass);
  eval(aClass+'.prototype.mSWClass = "'+aClass+'"');
}

function SWAT_registerInterface(aClass) {
  this.tryRegQueued();
}

function SWAT_attachStuff(aClassFrom, aClassTo, aIsInterfaces) {
  if (this.checkDependencies(aClassFrom)) {
    this.regProperties(aClassFrom, aClassTo);
    this.regEvents(aClassFrom, aClassTo);
    this.regInheritances(aClassFrom, aClassTo);
    this.regInterfaces(aClassFrom, aClassTo);
    this.regMethods(aClassFrom, aClassTo, aIsInterfaces); 
    this.tryRegQueued();
  }
}

function SWAT_checkDependencies(aClass) {
  var result = this.checkDepList(
    this.mClassIndex[aClass]['Dependencies']);;
  result = this.checkDepList(
    this.mClassIndex[aClass]['Interfaces']);;
  result = this.checkDepList(
    this.mClassIndex[aClass]['Inheritance']);;

  if (result)
    this.mClassIndex[aClass].registered = true;
  else
    this.mDepQueue[aClass] = true;

  return result;
}

function SWAT_checkDepList(aArray) {
  var result = true;
  for (var i in aArray) 
    if (!this.mClassIndex[aArray[i]].registered)
      result = false;
  
  return result;
}

function SWAT_tryRegQueued() {
  for (var qclass in this.mDepQueue) {
    if (this.checkDependencies(qclass)) {
      delete this.mDepQueue[qclass];
      this.attachStuff(qclass, qclass);
    }
  }
}

function SWAT_regInheritances(aClassFrom, aClassTo) {
  var inherits = this.mClassIndex[aClassFrom]['Inheritance'];
  for (var i in inherits) {
    eval(aClassTo+'.prototype.'+inherits[i]+' = '+inherits[i]);
    this.attachStuff(inherits[i], aClassTo);
  }
}

function SWAT_regInterfaces(aClassFrom, aClassTo) {
  var interfaces = this.mClassIndex[aClassFrom]['Interfaces'];
  for (var i in interfaces)
    this.attachStuff(interfaces[i], aClassTo, true);
}

function SWAT_regProperties(aClassFrom, aClassTo) {
  var props = this.mClassIndex[aClassFrom]['Properties'];
  var p = eval(aClassTo+'.prototype');
  for (var i=0; i < props.length; i+=2) {
    if (props[i+1] == 1 || props[i+1] == 3)
      p['get'+props[i]] = new Function("return this.m"+props[i]);
    if (props[i+1] == 2 || props[i+1] == 3)
      p['set'+props[i]] = new Function("aVal", "this.m"+props[i]+"=aVal");
  }
}

function SWAT_regMethods(aClassFrom, aClassTo, aIsInterfaces) {
  var methods = this.mClassIndex[aClassFrom]['Methods'];
  if (aIsInterfaces) var implementer = aClassTo;
  else var implementer = aClassFrom;
  var p = eval(aClassTo+'.prototype');
  for (var i in methods) {
    var method = eval(implementer+'_'+methods[i]);
    method.pSWClass = aClassFrom;
    var inherited = eval(aClassTo+'.prototype.'+methods[i]);
    if (inherited)
      p[inherited.pSWClass+'_'+methods[i]] = inherited;
    p[methods[i]] = method;
  }  
}

function SWAT_regEvents(aClassFrom, aClassTo) {
  var events = this.mClassIndex[aClassFrom]['Events'];
  for (var i=0; i < events.length; i++) {
    var name = events[i];
    var handler = new Function(
      'var listeners = this.mListeners["'+name+'"]; '+
      'for (var key in listeners) {'+
        'if (!event) ' +
          'if ("'+name+'" == "MouseEvent") swat.debug("o ");' +
          'var event = new '+events[i+1]+'("'+name+'", this, arguments);'+
        'var obj = listeners[key]; ' +
        'if (typeof(obj) == "function") ' +
          'obj(event);' +
        'else ' +
          'obj.on'+name+'(event); ' +
      '} ' +
  		'return event;'
    );
    var p = eval(aClassTo+'.prototype');
    p['fire'+name] = handler;
    p.mListeners = true;
    p.addEventListener = SWAT_addEventListener;
    p.removeEventListener = SWAT_removeEventListener;
  }
}

function SWAT_regFields(aClass) {
  var fields = this.mClassIndex[aClass]['Fields'];
  var constr = eval(aClass);
  for (var i=0; i < fields.length; i+=2)
    constr[fields[i]] = fields[i+1];
}

function SWAT_regFunctions(aClass) {
  var funcs = this.mClassIndex[aClass]['Functions'];
  var constr = eval(aClass);
  for (var i in funcs)
    constr[funcs[i]] = eval(aClass+'_'+funcs[i]);
}

function SWAT_registerGlobal(aInstance) {
  this.global[aInstance.getSWName()] = aInstance;
  aInstance.getGlobalName = SWAT_getGlobalName;
}

function SWAT_unregisterGlobal(aInstance) {
  this.global[aInstance.getSWName()] = null;
  aInstance.getGlobalName = null;
}

function SWAT_getGlobalName() {
  return 'swat.global["'+this.getSWName()+'"]';
} 

function SWAT_pauseForIE4(aObj, aStr) {
  if (is.ie4) {
    swat.registerGlobal(aObj);
    window.setTimeout(aObj.getGlobalName()+'.'+aStr+'()', 1);
  } else
    aObj[aStr]();
}

var funcs = 
[ // public
  'getLibraryPath',
  'setLibraryPath',
  'loadLibrary',
  'loadLocal',
  'attachProperties',
  'attachMethods',
  'attachEvents',
  'attachInheritance',
  'attachInterfaces',
  'attachDependencies',
  'attachFields',
  'attachFunctions',
  'registerInterface',
  'registerClass',
  'registerGlobal',
  'unregisterGlobal',
  // private
  'checkInClass',
  'regProperties',
  'regMethods',
  'regEvents',
  'regFields',
  'regFunctions',
  'regInheritances',
  'regInterfaces',
  'checkDependencies',
  'checkDepList',
  'tryRegQueued',
  'attachStuff',
  'pauseForIE4',
  'debug'];

var p = SWAT.prototype;
for (var i in funcs) {
  p[funcs[i]] = eval('SWAT_'+funcs[i]);
}

var swat = new SWAT(); // singleton

///////////////////

function SWAT_addEventListener(aEventName, aHandler) {
  if (this.mListeners == true) this.mListeners = new Array();
	var ls = this.mListeners[aEventName];
  if (!ls) {
    ls = new Array();
    this.mListeners[aEventName] = ls;
  }
  ls[ls.length] = aHandler;
}

function SWAT_removeEventListener(aEventName, aHandler) {
  var ls = this.mListeners[aEventName];
	for (var i in ls) {
		if (ls[i] == aHandler) {
			this.mListeners[aEventName] = XArray.splice(ls, i, 1);
			return;
		}
  }
}

///////////////////

swat.attachProperties("XObject",
 'SWID', 1,
 'SWClass', 1);
swat.attachMethods("XObject",
  'getSWName');
swat.registerClass('XObject');

function XObject() {
  if (this.mSWID == null) 
    this.mSWID = swat.mSWIDs[this.mSWClass]++;
}

function XObject_getSWName() {
  return this.mSWClass+this.mSWID;
}

///////

swat.attachFunctions("XArray",
  'clone',
  'intersect',
  'subtract',
  'splice',
  'findIndex',
  'getValue',
  'getKey',
  'getLength');
swat.registerClass('XArray');
function XArray() {}

function XArray_clone(aArray) {
  var a = [];
  for (var i in aArray) {
    a[i] = aArray[i];
  }
  return a;
}

function XArray_intersect(aA1, aA2) {
  for (var key in aA2)
    if (aA1[key] == null)
      aA1[key] = aA2[key];
}

function XArray_subtract(aA1, aA2) {
  for (var key in aA2)
    if (aA1[aA2[key]] != null)
      delete aA1[aA2[key]];
}

function XArray_splice(aArray, aStart, aCount) {
  if (is.ns) {
    aArray.splice(aStart, aCount);
    return aArray;
  } else if (is.ie)
    return aArray.slice(0, aStart).concat(aArray.slice(aStart+1));
}

function XArray_findIndex(aArray, aObj) {
  var idx = 0;
  for (var i in aArray) {
    if (aArray[i] == aObj)
      return idx;
    idx++;
  }
  return null;   
}

function XArray_getValue(aArray, aIndex) {
  var idx = 0;
  for (var key in aArray) {
    if (idx == aIndex)
      return aArray[key];
    idx++;
  }
	return null;
}

function XArray_getKey(aArray, aIndex) {
  var idx = 0;
  for (var key in aArray) {
    if (idx == aIndex)
      return key;
    idx++;
  }
	return null;
}

function XArray_getLength(aArray) {
  var len = 0;
  for (var key in aArray)
    len++;
  return len;
}

///////////////////

swat.attachProperties("XEvent", 
  "Type", 1,
  "Target", 1,
  "Cancel", 1,
  "Return", 3);
swat.attachMethods("XEvent", "cancel");
swat.attachInheritance("XEvent", "XObject");
swat.registerClass('XEvent');

function XEvent(aType, aTarget) {
  this.XObject();
  this.mType = aType;
  this.mTarget = aTarget;
  
  this.mCancel = false;
  this.mReturn = null;
}

function XEvent_cancel() { this.mCancel = true; }

///////////////

function Sniffer() {
  var names = new Array('Netscape', 'Microsoft Internet Explorer');
  var aliases = new Array('ns', 'ie');
  var versions = new Array(new Array(4, 5),
                           new Array(4, 5));
  var sniffers = new Array(Sniff_ns, Sniff_ie);
  
  var browser = '?';
  for (var i in names)
    if (Sniff_name(names[i])) {
      eval('this.'+aliases[i]+'=true');
      browser = aliases[i];

      for (k in versions[i])
        if (sniffers[i](versions[i][k])) {
          browser += versions[i][k];
          eval('this.'+browser+'=true');
        }
    }
  this.browser = browser;

  if (!this.ns4 && !this.ie4 && !this.ie5)
    this.notSupported = true;
  else
    this.notSupported = false;
}

function Sniff_name(aName) {
  return navigator.appName == aName;
}

function Sniff_ns(aVersion) {
  return parseInt(navigator.appVersion) == aVersion;
}

function Sniff_ie(aVersion) {
  var v = navigator.appVersion;
  var re = new RegExp('MSIE '+aVersion);
  return v.search(re) >= 0;
}

var is = new Sniffer();

