// camera viewer

/////////////////////////////////////////////////////////
// CLASS EXTENDER
/*
 *  Copyright (c) 2006, Greg Wiley
 *
 *  Permission is hereby granted, free of charge, to
 *  any person obtaining a copy of this software and
 *  associated documentation files (the "Software"),
 *  to deal in the Software without restriction,
 *  including without limitation the rights to use,
 *  copy, modify, merge, publish, distribute,
 *  sublicense, and/or sell copies of the Software,
 *  and to permit persons to whom the Software is
 *  furnished to do so, subject to the following
 *  conditions:
 *
 *    The above copyright notice and this permission
 *    notice shall be included in all copies or
 *    substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
 *  OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 *  LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
 *  OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 *  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *  OTHER DEALINGS IN THE SOFTWARE.
 *
 */


/*
 * Class creation. Create a new class following
 * Prototype initialization conventions.
 * (note: can still use the no-argument syntax with
 * the only semantic difference being that an
 * initializer will be created if no prototype
 * is provided).
 *
 * param proto: object to install as prototype
 *    of returned constructor
 */
/////////////////////////////////////////////////////////

Class.create = function( proto ) {
    var base =  function() {
      this.initialize.apply(this, arguments);
    }
    if ( proto )
	    // prototype provided, assign it
    	base.prototype = proto;
    	
    if ( !proto || !proto.initialize )
    	// provide a default initializer
		base.prototype.initialize = function() {
		}
		    	
    return base; 
}

/**
 * Class extension with constructor chain. This inserts
 * a method, "superInit," that should be called from within
 * a subclass's "initialize" method. superInit calls
 * the initialize method defined by the superclass.
 * 
 * Like Class.create above, this inserts an "initialize"
 * member if one is not provided
 *
 * naming note: sorry for the lame "superInit" member.
 *  "super" is reserved for future use by ECMA (plus
 *  using it blows up in IE). I thought of "init" but
 *  that's not descriptive enough and doesn't really
 *  differentiate it from "initialize"
 *
 * param superclass: constructor of superclass--
 *   assumed to have been created with Class.create
 *   (i.e. uses the initialize convention)
 * param proto: object to merge with subclass constructor
 *   prototype
 */
Class.extend = function( superclass, proto ) {
	// create new Prototype class
	
	var subclass =  function() {
	  // make sure object has a copy of init chain
	  //  -- fixes muli-object create bug 3/3/2006 -gjw
	  this.__inits = $A( subclass.prototype.__inits);
      this.initialize.apply(this, arguments);
    }
	
	// copy the superclass prototype
	Object.extend( subclass.prototype, superclass.prototype );
	
	if ( !subclass.prototype.superInit ) {
		// superclass is not an extension, so
		// set it up
		
		// create the initializer stack
		subclass.prototype.__inits = new Array();
		
		// create the superinitializer
		subclass.prototype.superInit = function() {
			// pop the immediate superclass initializer
			var init = this.__inits.pop();
			// ...and call it
			init.apply(this,arguments);
		} 
	} else {
		// make a shallow copy of the superclass init chain
		subclass.prototype.__inits = $A(superclass.prototype.__inits);
	}
	// push the superclass initializer
	subclass.prototype.__inits.push(superclass.prototype.initialize);

	// merge provided prototype
	if ( proto )
		// prototype provided
		Object.extend( subclass.prototype, proto );
		
	if (!proto || !proto.initialize )
		// provide a default initializer
		subclass.prototype.initialize = function() {
			this.superInit.apply(this,arguments);
		}
	
	return subclass;

} 

/*
 * class-extend.js-0.2
 * Feb-2006 Greg Wiley <greg@orthogony.com>
 */

/*
 * Changes:
 *   0.2; gjw -- fixed problem where object construction
 *    of a derived class would pop its initializer functions
 *    from the prototype's copy of the chain.
 */

/*
 * "I can't complain but sometimes I still do." -- Walsh
*/



Object.extend(Element, {
	getWidth: function(element) {
	   	element = $(element);
	   	return element.offsetWidth; 
	},
	setWidth: function(element,w) {
	   	element = $(element);
    	element.style.width = w +"px";
	},
	setHeight: function(element,h) {
   		element = $(element);
    	element.style.height = h +"px";
	},
	setTop: function(element,t) {
	   	element = $(element);
    	element.style.top = t +"px";
	},
	setSrc: function(element,src) {
    	element = $(element);
    	element.src = src; 
	},
	setHref: function(element,href) {
    	element = $(element);
    	element.href = href; 
	},
	setInnerHTML: function(element,content) {
		element = $(element);
		element.innerHTML = content;
	}
});

PeriodicalExecuter.prototype.registerCallback = function() {
    this.intervalID = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
}

PeriodicalExecuter.prototype.stop = function() {
    clearInterval(this.intervalID);
}

var _js_snapshot_url = null;
var _js_timer = 0;
var _js_frequency = 0;
var _js_pe = null;
var _js_timeout = 0;

var NWLViewer = Class.create( {

	initialize: function(options) {
	
		this.address			= options.address;
		this.username			= options.username;
		this.password			= options.password;
		this.camera				= options.camera;
		this.firmware     = options.firmware || null;
		this.apiversion   = options.apiversion || null;
		this.manufacturer = options.manufacturer || null;
		this.model        = options.model || null;
		this.url          = options.url || null;
		this.resolution	  = options.resolution || '320x240';
		this.refresh_rate	= options.refresh || 3;
		this.timeout			= options.timeout || 600;
		this.minflashver	= options.minflashver || 5;
		this.type         = options.type || 'basic';
		this.relay        = options.relay || false;
		this.context      = options.context || 'tb';
		
		this.allow_video = options.allow_video;

		this.snapshot_url = options.snapshot_url;
		this.stream_url		= options.stream_url;
		
		this.stream_address = options.stream_address;
		this.stream_path = options.stream_path;
		this.snapshot_address = options.snapshot_address;
		this.snapshot_path = options.snapshot_path;
		
		this.truestream = options.truestream
		this.truestream_url = options.truestream_url
		this.allow_relay_fallback_viewer = options.allow_relay_fallback_viewer

		// derived configuration
		this.browser					= new BrowserDetect();
		this.use_activex			= (this.allow_video && this.browser.isIE && this.browser.isWin);
		this.use_java					= (this.allow_video && this.browser.java);
		this.use_flash		    = (detectFlash(6));
		this.allow_switch     = options.allow_switch || true;
		
		if (!this.allow_video) this.allow_switch = false;
		
		this.host							= location.host;
		this.realhost         = options.realhost;
		
		d = this.resolution.split('x');
		this.width 	= d[0];
		this.height	= d[1];
		
		this.flash_viewer			= '/common/swf/viewer.swf';
		// 'http://' + this.host + 
		this.activex_viewer		= 'http://' + this.host + '/common/activex/MJPEGRender.ocx#version=1,2,0,0';
		this.activex_config   = 'http://' + this.host + '/common/activex/config.xml?camera=' + this.camera + '&w=' + this.width + '&h=' + this.height + '&ctx=' + this.context;
		
		this.debug = false;
		
		this._viewer = null; // store which viewer we actually rendered
		this._timer  = 0; // timer for javascript viewer
		this._switchHTML = null;
		this._activex_object_tag = null;
		
		// media player plugin
		this.has_media_player = detectPlugin('MediaPlayer.MediaPlayer.1', 'application/x-mplayer2');

	},
	
	getViewer: function() {
	  
	  return this._viewer;
	    
	  
	},
	
	getHTML: function() {
		
		var v = '';
		var s = '';
		
		// disable live view in IE until ActiveX control is fixed
		// no_live = ((/Axis/.test(this.manufacturer)) && this.width >=640 && browser.isIE && this.use_activex) ? true : false;
								
		if (this.truestream && this.truestream_url.length > 0) {		   		    
		  
		  this.allow_switch = false;
		  
		  var width = (this.width * 1) + 2;
		    
		  if(this.has_media_player) {
		    
		     if(browser.isIE) {
               s+= '<div class="clearfix" style="background-color:#666; width: ' + width + 'px; text-align:right;">';
               s+= '<div id="fullScreenControl">';
               s+= '<div class="img"><img onclick="if(MMPlayer1.playState == 3) { MMPlayer1.fullScreen = true }" src="/common/img/icon-fullscreen.gif" alt="Full Screen" /></div>';  
               s+= '<span onclick="if(MMPlayer1.playState == 3) { MMPlayer1.fullScreen = true }"><strong>Full Screen</strong><br /> (press esc to return)</span>';
               s+= '</div>';		 
  		       s+= '</div>';
  		     }  
		    
		    v = this._getTruestreamViewer();
		    
		  } else if (!this.allow_relay_fallback_viewer || this.relay != true) {
		    		    
		    v  = '<div style="text-align:left; background-color:white; padding:0px; height:100%;"><div style="padding: 5px"><strong>Windows Media Plugin Required</strong><p>Unfortunately, we have not detected Windows Media Player support in your browser. This stream can only be viewed using Windows Media Player at this time.</p><p>You can download the player from <a href="http://www.microsoft.com/windows/windowsmedia/download/">www.microsoft.com/windows/windowsmedia/download</a></p><p>If you are not using Windows you should install a Windows Media compatible player.</p></div></div>';
		    
		  }
		
	  } // end if truestream camera
		
		if(v == '') {
		
  		if (this.relay != true && this.use_activex && this.type != 'basic') {
  	      v = this._getActiveXViewer();
  		} else if (this.relay != true && this.use_java && this.type != 'basic') {
  		  	v = this._getAppletViewer();
  		} else if (this.use_flash) {
  		  	v = this._getFlashViewer();
  		} else {
  		  	v = this._getJavascriptViewer();
  		  	this.allow_switch = false;
  		}
		
	  }
				

		if (this.allow_switch == true && this.relay != true) {
			s += '<div style="text-align:right;align:right;padding-top:1px;width: ' + this.width + 'px">';
			if (this.type == 'basic') {
				s += '<a href="?viewer=advanced" target="_self">View live video</a>';
				// s += '<a href="javascript:toggleViewer(myViewer);" target="_self">Switch to Advanced viewer</a>';
			} else if (this.type == 'advanced' && (this.use_activex || this.use_java)) {
				s += '<a href="?viewer=basic" target="_self">No image? Try this viewer</a>';
				// s += '<a href="javascript:toggleViewer(myViewer);" target="_self">Switch to Basic viewer</a>';
			}
			s += '</div>';
		}
		
		this._switchHTML = s.escapeHTML();
		this.switchHTML = s;
		return v;	
	
	},
	
	draw: function(html) {
	
		var viewer = $('nwlviewer');
		
		Element.setInnerHTML('nwlviewer', '');
		
		var viewerContainer = document.createElement("div");
		viewerContainer.setAttribute('id','viewerContainer');
		viewer.appendChild(viewerContainer);
	
	  Element.setWidth('viewerContainer', this.width);
	  Element.setHeight('viewerContainer', this.height);
		Element.setInnerHTML('viewerContainer', html);
		
		if (this.switchHTML) {
			var viewerSwitcher = document.createElement("div");
			viewerSwitcher.setAttribute('id','viewerSwitcher');
			viewer.appendChild(viewerSwitcher);
			Element.setInnerHTML('viewerSwitcher', this.switchHTML);
		}
	},

	hostspec: function(a,p,u) {
		return 'http://' + ((u==true) ? this.username + ':' + this.password + '@' : '') + a + p;
	},


  _getTruestreamViewer: function() {
	  	  
	  this._viewer = 'truestream';
	  this.type = 'advanced';
	  
	  var str = '';
	    
    str = '';
	  str+= '<div style="position: absolute; width: ' + this.width + 'px; height: ' + this.height + 'px; z-index: 255;">';
	  str+= '<img src="/common/img/pxl.gif" width="' + this.width + '" height="' + this.height + '" /></div>';			
    str+= '<div style="position:relative; z-index:254;">';
    str+= '<object id="MMPlayer1" type="application/x-oleobject" style="z-index: 254;" width="' + this.width + '" height="' + this.height + '" align="absmiddle" classid="CLSID:6BF52A52-394A-11D3-B153-00C04F79FAA6">' + "\n";
    str+= '<param name="URL" value="/common/activex/truestream.php?camera=' + this.camera + '">' + "\n";
    str+= '<param name="enableContextMenu" value="false">' + "\n";
    str+= '<param name="stretchToFit" value="true">' + "\n";   
    str+= '<param name="uiMode" value="none">' + "\n";
    str+= '<param name="AutoStart" value="true">' + "\n";
    str+= '<param name="animationAtStart" value="true">' + "\n";    
    str+= '<param name="SendPlayStateChangeEvents" value="true">' + "\n";
    str+= '<embed src="/common/activex/truestream.php?camera=' + this.camera + '" width="' + this.width + '" height="' + this.height + '" align="absmiddle" type="application/x-mplayer2" pluginspage="http://www.microsoft.com/Windows/MediaPlayer/download/default.asp" autostart="1" showcontrols="0" showdisplay="0" showstatusbar="0" enableContextMenu="false"></embed>' + "\n";
    str+= '</object>' + "\n";
    str+= '</div>' + "\n";
 
    return str;

  },

	_getActiveXViewer: function() {
	
		var str = '';

		str += '<object id="nwlviewer_object" classid="CLSID:96816368-C1E3-414D-A193-63C3CC921990" ';
	  str += 'codebase="' + this.activex_viewer + '" ';
	  str += 'width="' + this.width + '" height="' + this.height + '" ';
	  str += 'align="center" hspace="0" vspace="0">';
	  str += '<param name="ConfigURL" value="' + this.activex_config + '&_nocache=' + Math.random() + '" />';
	  str += '</object>';
	  this._activex_object_tag = str.escapeHTML();
	  return str;
	},
	
	_getAppletViewer: function() {
		return "Sorry, a compatible viewer could not be found.";
	},
	
	_getFlashViewer: function() {
	  swf = this.flash_viewer + '?ver=1157990672&rate=' + this.refresh_rate + '&width=' + this.width + '&height=' + this.height + '&snapshotURL=' + escape(this.snapshot_url) + '&timeout=' + this.timeout + '&host=' + this.host;
		var so = new SWFObject(swf, "nwlviewer_object", this.width, this.height, this.minflashver, "#ffffff");
		so.addParam('allowScriptAccess', 'always');
		so.addParam('menu', 'false');
		// so.addParam('wmode', 'transparent');
		so.addParam('quality', 'high');
		return so.getSWFHTML();
	},
	
	_getJavascriptViewer: function() {
      _js_snapshot_url = this.snapshot_url; // make this global
      _js_timer = 0;
      _js_frequency = this.refresh_rate;
      _js_timeout = this.timeout;
			_js_pe = new PeriodicalExecuter(this._updateJavascriptViewer, _js_frequency);
			
   		return '<img src="' + this.snapshot_url + '" name="nwlviewer_object" id="nwlviewer_object" ' + 
	    			 'width="' + this.width + '" height="' + this.height + '" />';
	},
	
	_updateJavascriptViewer: function() {
		if (_js_timer > _js_timeout) {
				_js_pe.stop();
				return;
		}
		window.status = "Refreshing image every " + _js_frequency + " seconds";
		if (i = $('nwlviewer_object')) {
			i.src = _js_snapshot_url + ((_js_snapshot_url.indexOf('?')!=-1)?'&':'?') + 'cache=' + Math.random();
			_js_timer += _js_frequency + 0;
		}
	},
	
	_printDebugInfo: function() {
		var s = '';
	  for (p in this) {
	  	n = eval('this.'+p);
	  	if (typeof n !== "function") s += 'this.'+p+' = '+n+'<br />';
	  }
	  document.write('<code>' + s + '</code>');
	}

});

function toggleViewer(viewer) {

	viewer.type = (viewer.type == 'basic') ? 'advanced' : 'basic';
	viewer.draw(viewer.getHTML());

}

function BrowserDetect() {

   var ua = navigator.userAgent.toLowerCase(); 

   // browser engine name
   this.isGecko       = (ua.indexOf('gecko') != -1 && ua.indexOf('safari') == -1);
   this.isAppleWebKit = (ua.indexOf('applewebkit') != -1);

   // browser name
   this.isKonqueror   = (ua.indexOf('konqueror') != -1); 
   this.isSafari      = (ua.indexOf('safari') != - 1);
   this.isOmniweb     = (ua.indexOf('omniweb') != - 1);
   this.isOpera       = (ua.indexOf('opera') != -1); 
   this.isIcab        = (ua.indexOf('icab') != -1); 
   this.isAol         = (ua.indexOf('aol') != -1); 
   this.isIE          = (ua.indexOf('msie') != -1 && !this.isOpera && (ua.indexOf('webtv') == -1) ); 
   this.isMozilla     = (this.isGecko && ua.indexOf('gecko/') + 14 == ua.length);
   this.isFirebird    = (ua.indexOf('firebird/') != -1);
   this.isNS          = ( (this.isGecko) ? (ua.indexOf('netscape') != -1) : ( (ua.indexOf('mozilla') != -1) && !this.isOpera && !this.isSafari && (ua.indexOf('spoofer') == -1) && (ua.indexOf('compatible') == -1) && (ua.indexOf('webtv') == -1) && (ua.indexOf('hotjava') == -1) ) );
   
   // spoofing and compatible browsers
   this.isIECompatible = ( (ua.indexOf('msie') != -1) && !this.isIE);
   this.isNSCompatible = ( (ua.indexOf('mozilla') != -1) && !this.isNS && !this.isMozilla);
   
   // rendering engine versions
   this.geckoVersion = ( (this.isGecko) ? ua.substring( (ua.lastIndexOf('gecko/') + 6), (ua.lastIndexOf('gecko/') + 14) ) : -1 );
   this.equivalentMozilla = ( (this.isGecko) ? parseFloat( ua.substring( ua.indexOf('rv:') + 3 ) ) : -1 );
   this.appleWebKitVersion = ( (this.isAppleWebKit) ? parseFloat( ua.substring( ua.indexOf('applewebkit/') + 12) ) : -1 );
   
   // browser version
   this.versionMinor = parseFloat(navigator.appVersion); 
   
   // correct version number
   if (this.isGecko && !this.isMozilla) {
      this.versionMinor = parseFloat( ua.substring( ua.indexOf('/', ua.indexOf('gecko/') + 6) + 1 ) );
   }
   else if (this.isMozilla) {
      this.versionMinor = parseFloat( ua.substring( ua.indexOf('rv:') + 3 ) );
   }
   else if (this.isIE && this.versionMinor >= 4) {
      this.versionMinor = parseFloat( ua.substring( ua.indexOf('msie ') + 5 ) );
   }
   else if (this.isKonqueror) {
      this.versionMinor = parseFloat( ua.substring( ua.indexOf('konqueror/') + 10 ) );
   }
   else if (this.isSafari) {
      this.versionMinor = parseFloat( ua.substring( ua.lastIndexOf('safari/') + 7 ) );
   }
   else if (this.isOmniweb) {
      this.versionMinor = parseFloat( ua.substring( ua.lastIndexOf('omniweb/') + 8 ) );
   }
   else if (this.isOpera) {
      this.versionMinor = parseFloat( ua.substring( ua.indexOf('opera') + 6 ) );
   }
   else if (this.isIcab) {
      this.versionMinor = parseFloat( ua.substring( ua.indexOf('icab') + 5 ) );
   }
   
   this.versionMajor = parseInt(this.versionMinor); 
   
   // dom support
   this.isDOM1 = (document.getElementById);
   this.isDOM2Event = (document.addEventListener && document.removeEventListener);
   
   // css compatibility mode
   this.mode = document.compatMode ? document.compatMode : 'BackCompat';

   // platform
   this.isWin    = (ua.indexOf('win') != -1);
   this.isWin32  = (this.isWin && ( ua.indexOf('95') != -1 || ua.indexOf('98') != -1 || ua.indexOf('nt') != -1 || ua.indexOf('win32') != -1 || ua.indexOf('32bit') != -1 || ua.indexOf('xp') != -1) );
   this.isMac    = (ua.indexOf('mac') != -1);
   this.isUnix   = (ua.indexOf('unix') != -1 || ua.indexOf('sunos') != -1 || ua.indexOf('bsd') != -1 || ua.indexOf('x11') != -1)
   this.isLinux  = (ua.indexOf('linux') != -1);
   
   // specific browser shortcuts
   this.isNS4x = (this.isNS && this.versionMajor == 4);
   this.isNS40x = (this.isNS4x && this.versionMinor < 4.5);
   this.isNS47x = (this.isNS4x && this.versionMinor >= 4.7);
   this.isNS4up = (this.isNS && this.versionMinor >= 4);
   this.isNS6x = (this.isNS && this.versionMajor == 6);
   this.isNS6up = (this.isNS && this.versionMajor >= 6);
   this.isNS7x = (this.isNS && this.versionMajor == 7);
   this.isNS7up = (this.isNS && this.versionMajor >= 7);
   
   this.isIE4x = (this.isIE && this.versionMajor == 4);
   this.isIE4up = (this.isIE && this.versionMajor >= 4);
   this.isIE5x = (this.isIE && this.versionMajor == 5);
   this.isIE55 = (this.isIE && this.versionMinor == 5.5);
   this.isIE5up = (this.isIE && this.versionMajor >= 5);
   this.isIE6x = (this.isIE && this.versionMajor == 6);
   this.isIE6up = (this.isIE && this.versionMajor >= 6);
   
   this.isIE4xMac = (this.isIE4x && this.isMac);
   
   this.java = navigator.javaEnabled();
   
  
}

function getFlashVersion() {
	  var flashversion = 0;
	  if (navigator.plugins && navigator.plugins.length) {
		var x = navigator.plugins["Shockwave Flash"];
		if(x){
			if (x.description) {
				var y = x.description;
				regex = /Shockwave Flash (\d+)\.(\d+)*/;
				var match = regex.exec(y);
				flashversion = match[1];
			}
		}
	  } else {
		result = false;
	    for(var i = 15; i >= 3 && result != true; i--){
   			execScript('on error resume next: result = IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash.'+i+'"))','VBScript');
   			flashversion = (result) ? i : 0;
   		}
	  }
	  return flashversion;
}

function detectFlash(ver) {	
	if (getFlashVersion() >= ver) {
		return true;
	} else {
		return false;
	}
}


function detectMJPEGRender() {
	if (browser.isIE && browser.isWin) {
		result = false;
		execScript('on error resume next: result = IsObject(CreateObject("MJPEGRENDER.MJPEGRenderCtrl.1"))','VBScript');
   			return (result) ? true : false;
	}
}

function detectPlugin(IeClassId, NsMimeType) {

  if (browser.isIE && browser.isWin) {
		result = false;
		execScript('on error resume next: result = IsObject(CreateObject("' + IeClassId + '"))','VBScript');
   			return (result) ? true : false;
	} else if (navigator.mimeTypes) {
	  
	  NsMimeType = NsMimeType.toLowerCase();
	  
	  for (var i=0; i<navigator.mimeTypes.length; i++) {
	    if (NsMimeType == navigator.mimeTypes[i].type.toLowerCase() && navigator.mimeTypes[i].enabledPlugin != null) {
	      return true;
	    }
	  }
	  return false;
	
	}
	  
  return false;  

}
