//-----------------------------------------------------------------------#
// IdvFlashMovie.js - Copyright (C) 2007 IDV Solutions                   #
//-----------------------------------------------------------------------#

// make sure IDV namespace exists
if(typeof idv == "undefined") { var idv = new Object(); }

// String Constants
idv.FlashPlayerLink = "http://www.macromedia.com/go/getflashplayer",
idv.FlashCodeBase   = "http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=",

// Enumerations
idv.WindowMode   = { Window: "window", Opaque: "opaque", Transparent: "transparent" };
idv.MovieQuality = { High:"high", Medium:"medium", Low:"low" };
idv.ScriptAccess = { SameDomain:"sameDomain", Always:"always", Never:"never" };

// Setup Global Variables
idv.FlashMovieList  = new Object();
idv.PageInstanceID  = Math.random();
//++idv.OnLoadInstalled = false;
//++idv.OnLoadPrevious  = null;


// Creates a new IDV JavaScript class (for internal use only)
// this method should appear BEFORE any IDV classes are created
function IdvCreateClass(baseClass, methods)
{
   var newClass;
   var newProto;

   // create the new class object
   newClass = function() {
      if(arguments.length > 0 || (
         this.$type.$construct != null &&
         this.$type.$construct.length == 0)) {
         IdvCallMethodN(this.$type.$construct, this, arguments);
      } 
   }

   // associate the base class
   newProto = new baseClass();

   // setup this new class
   newClass.prototype = newProto; 

   // bind data type to the class
   newProto.$type  = newProto;
   newProto.$super = baseClass.prototype;

   // setup the methods in the class
   for(var i in methods) {
      newProto[i] = methods[i];
   }

   // return the result
   return newClass;
}

// Creates an empty XML document
function IdvCreateDocument()
{
   var result;

   // handle Mozilla/Fire Fox or IE
   if(window.document.implementation.createDocument != null) {
      result = document.implementation.createDocument("","",null);
   } else {
      try {
         result = new ActiveXObject("MSXML2.DOMDocument");
      } catch(e) {
         result = new ActiveXObject("Microsoft.XMLDOM"); 
      }
   }

   // return the result
   return result;
}

//++// Initializes all of the movies that have been written
//++function IdvInitialize()
//++{
//++   if(idv.OnLoadPrevious != null) {
//++      idv.OnLoadPrevious();
//++   }
//++}

// Proxies calls from AS to JS
function IdvProxyASCall(movieName, methodName, msg)
{
   var movie;

   // access the target movie
   movie = idv.FlashMovieList[movieName];

   // let the movie handle the message
   if(movie != null) {
      movie.handleASMessage(methodName, msg);
   }
}

// Calls a method with N parameters
function IdvCallMethodN(method, target, p)
{
   method.call(target, p[0], p[1], p[2], p[3], p[4], p[5]); 
}


// Base class for all Flash movies
idv.FlashMovie = IdvCreateClass(Object, {

   // tag name constants
   AttrName:  "n",
   TagMethod: "m",
   TagEvent:  "e",
   TagNull:   "n",
   TagBool:   "b",
   TagNumber: "f",
   TagString: "s",
   TagXmlDoc: "x",
   TagObject: "o",
   TagArray:  "a",

   // virtual method called when map viewer is loaded
   mapViewerLoaded: function() {
   },

   // Obtains the name of this SWF movie
   getName: function() {
      return this.mID;
   },

   // Sets the version of flash to use
   // (only useful BEFORE writeEmbedHtml)
   setFlashVersion: function(version) {
      this.mVersion = version;
   },

   // Sets the window mode for the flash movie
   // (only useful BEFORE writeEmbedHtml)
   setWindowMode: function(value) {
      this.mWMode = value;
   },

   // Sets the background color of flash
   // movie (only useful BEFORE writeEmbedHtml)
   setBackgroundColor: function(value) {
      this.mBkgnd = value;
   },

   // Sets the initial quality of the movie
   // (only useful BEFORE writeEmbedHtml)
   setInitialQuality: function(value) {  
      this.mQaulity = value;
   },

   // Sets the script access level (only
   // useful BEFORE writeEmbedHtml called)
   setScriptAccess: function(value) {
      this.mAllow = value;
   },

   // Sets the dimensions of the movie
   // (only useful BEFORE writeEmbedHtml)
   setDimensions: function(width, height)
   {
      this.mWidth  = width;
      this.mHeight = height;
   },

   // sets all HTTP query params as varaibles in SWF
   setHttpQueryParams: function()
   {
      var param; // entire HTTP query string
      var plist; // split list of parameters
      var entry; // current param [0]=name,[1]=value

      // get the query string
      param = window.location.search;

      // make sure query string exists   
      if(param != null && param.length > 0) {

         // trim off question mark if needed
         if(param.indexOf('?') == 0) {
            param = param.substr(1, param.length - 1);
         }

         // split up the query string
         plist = param.split('&');

         // process each of the parameters
         for(var i = 0; i < plist.length; i++) {
            entry = plist[i].split('=');
            this.setVariable(entry[0], entry[1]);
         }
      }
   },

   // Sets a variable on this flash movie
   setVariable: function(name, value)
   {
      if(this.mMovie != null) {
         this.mMovie.SetVariable(name, value);
      } else {
         this.mParams[name] = value;
      }
   },

   // Obtains the value of a variable
   getVariable: function(name)
   {
      if(this.mMovie != null) {
         return this.mMovie.GetVariable(name);
      } else {
         return mParams[name];
      }
   },

   // Adds an event listener to this movie [method
   // should have prototype function(sender, data)]
   addListener: function(evtname, target, method)
   {
      var info;

      // make sure method is valid
      if(method != null) {

         // create listener info object
         info = new Object();

         // setup listener info parameters
         info.evtnam = evtname;
         info.method = method;
         info.target = target;

         // store the event listener
         this.mListen.push(info);
         this.setVariable("__has_event_listener", "1");
      }
   },

   // removes all listeners    
   removeListener: function(evtname, target, method)
   {
      var i, j;
      var info;

      // copy over all non equal values
      for(i = j = 0; i < this.mListen.length; i++) {

         // access info about the listener
         info = this.mListen[i];

         // copy over event info if should be kept      
         if(info.evtnam != evtname ||
            info.method != method  ||
            info.target != target) {
            this.mListen[j++] = info;
         }
      }

      // trim length of the array
      this.mListen.length = j;   
   },

   // Executes an event for this movie
   executeEvent: function(evtname, param)
   {
      var info;
      var list;

      // access the event list
      list = this.mListen;

      // go through all of the event listeners
      for(var i = 0; i < list.length; i++) {

         // access current listener info
         info = list[i];

         // dispatch the event if needed
         if(info.evtnam == evtname) {
            info.method.call(info.target, this, param);
         }
      }
   },

   // Invokes an ActionScript method on the movie
   invokeASMethod: function(methodName)
   {
      var msg;

      // make sure the movie exists
      if(this.mMovie != null) {

         // get the encoded message
         msg = this.encodeMessage(methodName, arguments);

         // call the second frame of the movie
         this.mMovie.SetVariable('__js_msg_value', msg);
         this.mMovie.TCallFrame ('/',1);
      }
   },

   // Writes out the HTML to embed this movie
   // into the current position in the document
   writeEmbedHtml: function()
   {
      // write out the HTML
      //++node = document.getElementById(divId);   
      //++node.innerHTML = html;
      document.write(this.createEmbedHtml());

      //++// setup OnLoad callback if needed
      //++if(!idv.OnLoadInstalled) {
      //++   idv.OnLoadInstalled = true;
      //++   idv.OnLoadPrevious  = window.onload;
      //++   window.onload       = IdvInitialize;
      //++}

      // TODO: make sure IE works w/ forms and in IE 6
      // access the object tag
      this.mMovie = window.document[this.mID];
      idv.FlashMovieList[this.mID] = this;
   },

   // Generates the HTML to embed this movie
   // into the current position in the document
   createEmbedHtml: function()
   {
      var html;
      var goodVer;

      // determine the correct version
      goodVer = this.isCorrectVer();

      // generate the HTML for the SWF
      if(goodVer) {
         html = this.createSwfHtml();
      } else {
         html = this.createWrongVer();
      }

      // return the result
      return html;
   },

   // (private) creates normal SWF html
   createSwfHtml: function()
   {
      var vars;

      // get flash vars and allow script
      vars = this.createFlashVars();

      // handle Netscape/Mozilla/FireFox
	   // or handle Internet Explorer
	   if(navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) {
	      return this.createEmbedSwfHtml(vars);
	   } else {
	      return this.createObjectSwfHtml(vars);
	   }
   },

   // (private) creates bad version HTML
   createWrongVer: function()
   {
      return (
         '<table bgcolor="#d0d0d0" width="'+this.mWidth+'" height="'+this.mHeight+'">'+
         '<tr align="center"><td valign="center">'+
         '<font face="arial" size="2">'+
         'This application requires the Flash Player '+this.mVersion+' plugin</br>'+
         '<a href="'+ idv.FlashPlayerLink +'" target="_new">'+
         'Click here to get this free plugin</a>'+
         '</font>'+
         '</tr></td></table>'
      );
   },

   // (private)  create an Object (for IE) HTML tag
   createObjectSwfHtml: function(vars)
   {
      return (
         '<script language="VBScript">' +
         'On Error Resume Next\n'+
         'Sub '+this.mID+'_FSCommand(ByVal command, ByVal args)\n'+
         ' Call IdvProxyASCall("'+this.mID+'", command, args)\n'+
         'End Sub'+
         '</script>'+
	      '<object' +
	      ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+
	      ' codebase="'+idv.FlashCodeBase + this.mVersion+',0,0,0"'+
	      ' align="middle"'+
	      ' id="'+this.mID+'"'+
	      ' width="'+this.mWidth+'"'+
	      ' height="'+this.mHeight+'">'+
	      '<param name="FlashVars" value="'+vars+'"/>'+
	      '<param name="allowScriptAccess" value="'+this.mAllow+'"/>'+
	      '<param name="movie"   value="'+this.mURL    +'"/>'+
	      '<param name="quality" value="'+this.mQuality+'"/>'+
	      '<param name="bgcolor" value="'+this.mBkgnd  +'"/>'+
	      '<param name="wmode"   value="'+this.mWMode  +'"/>'+
              '<param name="allowFullScreen" value="true" />'+
	      '</object>'
	   );
   },

   // (private)  creates an Embed (non IE) HTML tag
   createEmbedSwfHtml: function(vars)
   {
	  return (
	      '<script>'+
	      'function '+this.mID+'_DoFSCommand(command, args) {\n'+
	      ' IdvProxyASCall("'+this.mID+'", command, args);\n'+
	      '}\n'+
         '</script>'+
	      '<embed'+
	      ' type="application/x-shockwave-flash"'+
	      ' pluginspage="'+ idv.FlashPlayerLink +'"'+
	      ' align="middle"'+
	      ' allowScriptAccess="'+this.mAllow+'"'+
              ' allowFullScreen="true"'+
	      ' FlashVars="'+vars+'"'+
	      ' src="'    +this.mURL   +'"'+
	      ' width="'  +this.mWidth +'"'+
	      ' height="' +this.mHeight+'"'+
	      ' id="'     +this.mID    +'"'+
	      ' name="'   +this.mID    +'"'+
	      ' wmode="'  +this.mWMode +'"'+
	      ' bgcolor="'+this.mBkgnd +'"/>'
	   );
   },

   // (private)  creates string for flash vars
   createFlashVars: function()
   {
      var v = "";

      // generate flash vars string
      for(var i in this.mParams) {
         v += "&"+i+"="+ this.mParams[i];
      }
	  
      // add the page instance id
      v += "&"+"__page_instance="+idv.PageInstanceID;
      v += "&"+"__movie_name="+this.mID;
//++      v += "&"+"__has_event_listener="+this.mHasLstnrs;

      // return the result
      if(v.length > 0) {
         return v.substr(1);
      } else {
         return v;
      }
   },

   // (private)  determins if correct version of flash is installed
   isCorrectVer: function()
   {
      var actx; // active X reference
      var list; // version numbers
      var inst; // installed version
      var vstr; // version string
      
      // clear installed version
      inst = 0;

      // handle Netscape/Mozilla/FireFox
	   if(navigator.plugins && navigator.mimeTypes.length) {
		   var x = navigator.plugins["Shockwave Flash"];
		   if(x && x.description) {

            // split up the version number
		      list = x.description
			      .replace(/([a-z]|[A-Z]|\s)+/, "")
			      .replace(/(\s+r|\s+b[0-9]+)/, ".")
			      .split(".");

			   // get the installed major version
			   inst = parseInt(list[0]);
		   }

	   // handle Internet Explorer
	   } else {
	      try {
	         vstr = "ShockwaveFlash.ShockwaveFlash."+this.mVersion;
			   actx = new ActiveXObject(vstr);
			   inst = this.mVersion;
	      } catch(e) {
	      }
	   }

	   // return the result
	   return inst >= this.mVersion;
   },

   // (private) handles a message from ActionScript
   handleASMessage: function(method, msg)
   {
      var doc;  // message as XML
      var root; // root of message
      var cmnd; // command to execute
      var args; // arguments for method

      // parse the document
      args = new Array();
      doc  = this.parseDocument(msg);
      root = doc.documentElement;
      cmnd = root.nodeName;

      // decode the parameters
      for(var i = root.firstChild; i != null; i = i.nextSibling) {
         args.push(this.decodeNode(i));
      }

      // execute the method
      if(cmnd == this.TagMethod) {
         IdvCallMethodN(this[method], this, args);
         
      // execute the event
      } else if(cmnd == this.TagEvent) {
         this.executeEvent(method, args[0]);
      }
   },

   // (private) Serializes a JavaScript object as an XML
   // string note that the first parameter in p is ignored
   encodeMessage: function(msg, p)
   {
	   var doc;  // document object
	   var root; // root node in document

	   // create a new document
	   doc  = IdvCreateDocument();
	   root = doc.createElement(this.TagMethod);

	   // setup the document
	   root.setAttribute(this.AttrName, msg);
	   doc.appendChild(root);

      // append the parameters
      for(var i = 1; i < p.length; i++) {
	      root.appendChild(this.encodeNode(doc, p[i]));
	   }

	   // return the result
	   return this.docToString(doc);
   },

   // (private) Encodes a JavaScript object as an XML node
   encodeNode: function(doc, value)
   {
      // create element for null value
      if(value == null) {
         return doc.createElement(this.TagNull);
      }

      // handle simple value types
      switch(typeof(value)) {

      // handle number data type
      case "number":
         return this.encodeValue(doc, this.TagNumber, value);

      // handle string data type
      case "string":
         return this.encodeValue(doc, this.TagString, value);

      // handle boolean data type
      case "boolean":
         return this.encodeValue(doc, this.TagBool, (value? "1":"0"));

      // handle object or array
      default:
         if(value["nodeName"] == "#document") {
            return this.encodeXmlDoc(doc, value);
         } else if(value["length"] != null) {
		      return this.encodeArray(doc, value);
	      } else {
		      return this.encodeObject(doc, value);
	      }
      }
   },

   // (private) Encodes a JavaScript object as an XML node
   encodeObject: function(doc, value)
   {
	   var c; // current child node
	   var n; // resulting node

	   // create a new object element
	   n = doc.createElement(this.TagObject);

	   // decode parameters of the object
	   for(var i in value) {
		   c = this.encodeNode(doc, value[i]);
		   c.setAttribute(this.AttrName, i);
		   n.appendChild(c);
	   }

	   // return the result
	   return n;
   },
   
   // (private) Encodes a JavaScript array as an XML node
   encodeArray: function(doc, value)
   {
	   var n;

	   // create a new array element
	   n = doc.createElement(this.TagArray);
      
	   // process the content of the array
	   for(var i = 0; i < value.length; i++) {
		   n.appendChild(this.encodeNode(doc, value[i]));
	   }

	   // return the result
	   return n;
   },

   // (private) Encodes an XML document
   encodeXmlDoc: function(doc, value)
   {
      var n, t;

      // create a new element and cdata section
      n = doc.createElement(this.TagXmlDoc);
      t = doc.createCDATASection(this.docToString(value));

      // append the CDATA section
      n.appendChild(t);

      // return the result
      return n;
   },

   // (private) Encodes a JavaScript value as an XML node
   encodeValue: function(doc, tag, value)
   {
	   var n, t;

	   // create a new 'v' element
	   n = doc.createElement(tag);
	   t = doc.createTextNode(value);

	   // append text to the node
	   n.appendChild(t);

	   // return the result
	   return n;
   },

   // (private) Deseralizes a particular node into a JavaScript object
   decodeNode: function(n)
   {
	   switch(n.nodeName) {

	   // handle known node types
	   case this.TagNumber: return parseFloat(n.firstChild.nodeValue);
	   case this.TagString: return n.firstChild != null ? n.firstChild.nodeValue:null;
	   case this.TagBool:   return (n.firstChild.nodeValue != "0");
	   case this.TagArray:  return this.decodeArray(n);
	   case this.TagObject: return this.decodeObject(n);
	   case this.TagXmlDoc: return this.decodeXmlDoc(n);

	   // handle unknown/null nodes
	   default: 
	      return null;
	   }
   },

   // (private) Deseralizes a XML node that describes an array
   decodeArray: function(node)
   {
	   var a;

	   // allocate a new array
	   a = new Array();

	   // decode the contents of the array
	   for(var i = node.firstChild; i != null; i = i.nextSibling) {
		   a[a.length] = this.decodeNode(i);
	   }

	   // return the result
	   return a;
   },

   // (private) Deseralizes a XML node that describes an object
   decodeObject: function(node)
   {
	   var o;
      
	   // allocate a new object
	   o = new Object();

	   // decode contents of the object
	   for(var i = node.firstChild; i != null; i = i.nextSibling) {
		   o[i.getAttribute(this.AttrName)] = this.decodeNode(i);
	   }

	   // return the result
	   return o;
   },

   // (private) Decodes an XML node containing an XML document
   decodeXmlDoc: function(node)
   {
      return this.parseDocument(node.firstChild.nodeValue);
   },

   // (private) Converts a document into a string
   docToString: function(doc)
   {
      var writer; // XML serializer
      var result; // resulting string

      // generate string in mozilla/firefox or
      // generate string in Internet Explorer
      if(window.XMLSerializer) {
         writer = new XMLSerializer();
         result = writer.serializeToString(doc);
      } else { 
         result = doc.xml;
      }

      // return the result
      return result;
   },

   // (private) Parse a string as an XML document
   parseDocument: function(value)
   {
      var result;
      var parser;

      // parse the document in mozilla/firefox
      if(window.DOMParser) {
         parser = new DOMParser();
         result = parser.parseFromString(value, "text/xml");

      // parse the document in Internet Explorer
      } else if(window.ActiveXObject) {
         result = new ActiveXObject("Microsoft.XMLDOM");
         result.async = "false";
         result.loadXML(value);
      }

      // return the result
      return result;
   },

   // Constructs a new flash movie object
   $construct: function(name, file)
   {
      // setup internal variables
      this.mID        = name;
      this.mURL       = file;
      this.mBkgnd     = "#ffffff";
      this.mAllow     = "sameDomain";
      this.mWidth     = "100%";
      this.mHeight    = "100%";
      this.mVersion   = "9";
      this.mQuality   = "high";
      this.mWMode     = "window";
      //++this.mHasLstnrs = "0";
      this.mMovie     = null;
      this.mParams    = new Object();
      this.mListen    = new Array();

      // pass name of map to side panel
      if(idv.MapInstance != undefined) {
         this.setVariable("__map_id", idv.MapInstance.getName());
      }
      
      // clear has event listener flag
      this.setVariable("__has_event_listener", "0");
   }
});

