// Javascript Library of general helper functions / objects
// $Id: mx_library.js,v 1.19.2.1 2008-12-10 11:46:06 cvs-daniel Exp $
// Author: Daniel Kabs
// Copyright 2006 MOBOTIX AG, Kaiserslautern

/*
  FireBug available on Firefox only
  
  FireBug lite can be activated on IE.
  see http://www.getfirebug.com/lite.html
*/

//window.MxDebugIE = true;

if ("MxDebugIE" in window && document.all) {
  // load Firebug lite
  document.documentElement.debug = "true";
  document.write('<script language="javascript" type="text/javascript" src="/control/firebug/firebug.js"><\/script>');
} else if (!("console" in window)) {
    console = new Object();
    console.log = console.debug = console.info = console.warn =
    console.error =  function() {  };
    console.error = alert;
}

// avoid dependency on prototype
// note: this implementation is different from prototype's
if (typeof $ == "undefined") {
  var $ = function (id) {
    return document.getElementById(id);
  }
}

/* Javascript does not have block statement scope
   instead variables introduced are scoped to the containing function.
   So create namespace by calling an anonymous function that returns
   an object which contains public functions.
*/
var MxJsLib = (function() {
  var URLCOUNTER = Math.floor(Math.random() * 10000000);
  var self = {
    URLNoRecordingsImage           : '/decor/m1m-error.jpg',
    URLNoRecordingsImage_PDA       : '/decor/m1m-error_PDA.jpg',
    URLNoImagesImage               : '/decor/err_no_images.jpg',
    URLNoImagesImage_PDA           : '/decor/err_no_images_PDA.jpg',
    URLDummyCameraImage     : '/decor/m1m-foto-l.jpg',
    URLDummyCameraImage_PDA : '/decor/m1m-foto-l_PDA.jpg',

    registerBodyOnloadHandler : function(onloadHandlerFunction) {
      if (typeof onloadHandlerFunction != 'undefined') {
        var oldOnloadHandlerFunction = window.onload;
        if (typeof(oldOnloadHandlerFunction) != 'function') {
          window.onload = onloadHandlerFunction;
        } else {
          window.onload = function() {
            oldOnloadHandlerFunction();
            onloadHandlerFunction();
          }
        }
      }
    },
    registerBodyOnunloadHandler : function(FuncRef) {
      if (typeof FuncRef != 'undefined') {
        var oldHandlerFunction = window.onunload;
        if (typeof(oldHandlerFunction) != 'function') {
          window.onunload = FuncRef;
        } else {
          window.onunload = function() {
            oldHandlerFunction();
            FuncRef();
          }
        }
      }
    },
    clearNode : function(node) {
      if (!node) return;
      while (node.hasChildNodes()) {
        self.discardElement(node.firstChild);
      }
    },
    /* first remove the element from the DOM,
       then avoid non-permanent IE leak, code shamelessly copied from
       http://outofhanwell.com/ieleak/index.php?title=Fixing_Leaks
    */
    discardElement : function(element) {
      if (element.parentNode) {
        element.parentNode.removeChild(element);
      }
      if (!document.all) {
        return;
      }
      var garbageBin = document.getElementById('IELeakGarbageBin');
      if (!garbageBin) {
        garbageBin = document.createElement('DIV');
        garbageBin.id = 'IELeakGarbageBin';
        garbageBin.style.display = 'none';
        document.body.appendChild(garbageBin);
      }
      // move the element to the garbage bin
      garbageBin.appendChild(element);
      garbageBin.innerHTML = '';
    },
    makeClosureToCallObjectMethod : function(obj,fun) {
      return function() { obj[fun]() };
    },
    Exception : function(msg) {
      this.message = msg;
    },
    makeUncachedURL : function (path) {
      if (path.substr(-1,1) == "&") {
        ;
      } else if (path.indexOf("?") < 0) {
        path += "?";
      } else {
        path += "&";
      }
      return path + (URLCOUNTER++);
    },
    convertAlarmAddress : function(Address) {
      /* input  : address (<sequence number> . <offset>)*/
      /* output : array ([0]=sequence, [1]=alarm image offset) */
      if (typeof Address != "string") {
        Address = Address.toString();
      }
      if (Address == '') {
        throw new  MxJsLib.Exception("Address must not be empty.");
      }
      if (Address.indexOf(".") == -1) {
        Address += ".0";
      }
      var AddressArray = Address.split(".",2);
      if (AddressArray[0] < 0 || AddressArray[1] < 0) {
        throw new  MxJsLib.Exception("Address '"+Address+"' must not be negative.");
      }
      if (isNaN(AddressArray[0]) || isNaN(AddressArray[1])) {
        throw new  MxJsLib.Exception("Address '"+Address+"' must be numerical.");
      }
      return AddressArray;
    },
    openCenteredPopup : function (URL,Name,Options) {
      /* for Options see http://developer.mozilla.org/en/docs/DOM:window.open */
      if (!Options) {
        Options = { width:200, height:200 };
      }
      var Position = self.calcPopupPositionOnWindow(window, null,
                                   Options.width, Options.height);
      if (Position) {
        Options['left'] = Position[0];
        Options['top'] = Position[1];
      }
      var OptionsString = "";
      for (var i in Options) {
        if (OptionsString.length > 0) {
          OptionsString += ",";
        }
        OptionsString += i + "=" + Options[i];
      }
      return window.open(URL,Name,OptionsString);
    },
    calcPopupPositionOnWindow : function (WindowReference, PopupReference,
                      /* optional -> */    PopupWidth, PopupHeight) {
      /*
        Either PopupReference or
        both PopupWidth and PopupHeight have to be given.

        Returns array of left and top offset (in user's desktop viewport).
        These coordinates can be fed to e.g. window.moveTo();
      */
      var wp_x = 0, wp_y = 0;
      if (WindowReference.screenLeft) { // IE
        if (PopupReference) {
          PopupWidth = PopupReference.document.body.offsetWidth;
          PopupHeight = PopupReference.document.body.offsetHeight
        }
        wp_x = parseInt(WindowReference.screenLeft
                      + WindowReference.document.body.offsetWidth / 2
                      - PopupWidth / 2);
        wp_y = parseInt(WindowReference.screenTop
                      + WindowReference.document.body.offsetHeight / 2
                      - PopupHeight / 2);
        return [ wp_x, wp_y ];
      } else if (WindowReference.screenX) { // Moz/FF
        if (PopupReference) {
          PopupWidth = PopupReference.outerWidth;
          PopupHeight = PopupReference.outerWidth;
        }
        wp_x = parseInt(WindowReference.screenX
                      + WindowReference.outerWidth / 2
                      - PopupWidth / 2);
        wp_y = parseInt(WindowReference.screenY
                      + WindowReference.outerHeight / 2
                      - PopupHeight / 2);
        return [ wp_x, wp_y ];
      }
      return null;
    },
    optimizeWindowSize : function (w,h) {
      /*
        Resize window so given dimensions and then optimize height
        so that content fits the window.
      */
      window.resizeTo(w,h);
      if (window.sizeToContent) {// Gecko
        window.sizeToContent();
      } else if (document.compatMode
              && document.compatMode == "BackCompat"
              && document.body
              && document.body.scrollHeight > 0
              && document.body.clientHeight > 0) { // IE quirks mode
        window.resizeBy(0, document.body.scrollHeight - document.body.clientHeight);
      } else if (document.documentElement
              && document.documentElement.scrollHeight > 0
              && document.documentElement.clientHeight > 0) {
        window.resizeBy(0, document.documentElement.scrollHeight - document.documentElement.clientHeight);
      }
    },
    getScrollPositionY : function () {
      /* return vertical scrollbar position */
     return window.pageYOffset
         || document.documentElement.scrollTop
         || document.body.scrollTop
         || 0;
    }
  };
  self.Exception.prototype.toString = function() {
    if (console.trace) {
      console.trace();
    }
    return this.message;
  }
  return self;
})();

/*
    Adding some important features that Javascript lacks.
    grabbed from http://javascript.crockford.com/remedial.html
*/

function isFunction(a) {
  return typeof a == 'function';
}
function isObject(a) {
  return (a && typeof a == 'object') || isFunction(a);
}
function isArray(a) {
  return isObject(a) && a.constructor == Array;
}

/****************************************************************************
    MX UI Library
 ****************************************************************************/

var MxUILib = new Object();
MxUILib.ElementCounter = 0;
MxUILib.getUniqueElementID = function() {return 'MxUIElement' + (++MxUILib.ElementCounter);}


/*
  UIToggleController
  Implements a controller for a view (UI element) that can handle
  two states (true or false).

  Note:  Toggling the view calls registered event listeners with
         new state but does NOT update the view.
         View has to be updated through one event listener or by
         adding setState as event listener:
           MyInstance.addToggleEventListener(MyInstance.setState);


  Parameters to constructor function:
    button_view: view instance of button
    initial_state: initial button state, either true or false.

  A view for the button has to support the following methods:
    create(listener);
      creates the HTML representation of the view. View calls 'listener'
      in case of a toggle / click event.
    setState(state);
      set the state of the view, 'state' can be either true or false;

  methods:
    addToggleEventListener(callback)
      add callback function which is called every time the users toggles
      the button state by clicking on it. Callback function is called with
      new toggle state as parameter.
    getState
      returns button state.
    setState(state)
      set button state and update view.
      NOTE: Calling this function will NOT call the registered event listeners.
*/
MxUILib.UIToggleController = function(button_view, initial_state) {
  // private variables
  if ( !button_view
    || !isFunction(button_view.create)
    || !isFunction(button_view.setState)) {
    throw new MxJsLib.Exception("UIToggleController: view missing.");
  }
  if (typeof initial_state == 'undefined') initial_state = false;
  var ButtonState = !!initial_state;
  var CallBacks = new Array();
  var toggleButtonState = function() {
    var NewState = !ButtonState;
    for (var i = 0; i < CallBacks.length; i++) {
      CallBacks[i](NewState);
    }
  };
  // public methods
  var self = {
    getState:function() {
      return ButtonState;
    },
    setState:function(on_off) {
      if (arguments.length < 1)
        return;
      on_off = !!on_off;
      if (ButtonState != on_off) {
        ButtonState = on_off;
        button_view.setState(ButtonState);
      }
    },
    addToggleEventListener: function (cb_function) {
      if (! isFunction(cb_function)) return;
      CallBacks.push(cb_function);
    }
  };
  button_view.create(toggleButtonState);
  button_view.setState(ButtonState);
  return self;
};

/*
  UIButtonToggleView
  Implements a toggle button view providing create() and setState()
  using HTML element <button>.

  Parameters to constructor function:
    node_id:  ID of container HTML element where button view is to be created.
              Note: Button is APPENDED to the container!
    button_html_false, button_html_true: HTML for button text.

  Methods used before applying Controller:
    setCSSClass(class_name);
      sets 'class_name' as CSS class for the <button> element.
    setBubbleHelpText(text_false,text_true);
      sets bubble help text.
  Methods used by Controller:
    create(event_listener)
      creates the ui element and registers click event listener function "event_listener".
    setState(state)
      sets state of ui element to "state"
      NOTE: Calling this method will NOT call the registered click event listener function.

*/

MxUILib.UIButtonToggleView = function(node_id,
                                      button_html_false,
                                      button_html_true) {
  // parameters
  if (!node_id || !$(node_id)) {
    throw new MxJsLib.Exception("UIButtonToggleView: node does not exist.");
  }
  // private variables
  var ViewID = "myview" + MxUILib.getUniqueElementID(); // access button by ID to
                                                        // avoid circular references
                                                        // BT -> Controller -> View -> BT
  var CSSClassName = '';
  var HelpTextFalse = '';
  var HelpTextTrue = '';
  // public methods providing view interface.
  this.create = function(ToggleEventListener) {
     var ButtonNode = document.createElement("button");
     ButtonNode.id = ViewID;
     ButtonNode.innerHTML = "";
     ButtonNode.setAttribute('type', 'button'); // type defaults to "submit" on FF
     if (CSSClassName) ButtonNode.className = CSSClassName;
     $(node_id).appendChild(ButtonNode);
    // add event handler
    ButtonNode.onclick = ToggleEventListener;
    // Catch double click as well, otherwise IE annoys fast clicking users...
    if (typeof ButtonNode.ondblclick == "object") {
      ButtonNode.ondblclick = ToggleEventListener;
    }
  };
  this.setState = function(state) {
    var ButtonNode = $(ViewID);
    if (state) {
      ButtonNode.innerHTML = button_html_true;
      ButtonNode.title = HelpTextTrue;
    } else {
      ButtonNode.innerHTML = button_html_false;
      ButtonNode.title = HelpTextFalse;
    }
  };
  // public methods changing appearance of view.
  this.setBubbleHelpText = function(text_false,text_true) {
    if (typeof text_false == 'undefined') text_false = '';
    if (typeof text_true == 'undefined') text_true = '';
    HelpTextFalse = text_false;
    HelpTextTrue = text_true;
  };
  this.setCSSClass = function(class_name) {
    if (typeof class_name == 'undefined') return;
    CSSClassName = class_name;
    var ButtonNode = $(ViewID);
    if (ButtonNode) ButtonNode.className = CSSClassName;
  };
};

MxUILib.createCenteredDiv = function(w,h) {
  var OuterDiv  = document.createElement("DIV");
  OuterDiv.style.position = "absolute";
  OuterDiv.style.top = (150 + MxJsLib.getScrollPositionY()) + "px";
  OuterDiv.style.left = "0px";
  OuterDiv.style.right = "0px";

  var InnerDiv = document.createElement("DIV");
  if (isNaN(w)) w = 200;
  InnerDiv.style.width  = w + "px";
  if (!isNaN(h)) InnerDiv.style.height  = h + "px";
  InnerDiv.style.margin = "0px auto";
  InnerDiv.style.backgroundColor = "#EEEEEE";
  InnerDiv.style.border = "2px solid #000088";
// OuterDiv.style.border = "1px solid red"; // for CSS debugging
  OuterDiv.appendChild(InnerDiv);
  document.body.appendChild(OuterDiv);
  return {
    getNode : function() { return InnerDiv; },
    getNode1 : function() { return OuterDiv; },
    close   : function() {
      if (OuterDiv) {
        MxJsLib.discardElement(OuterDiv);
        InnerDiv = OuterDiv = null;
      }
    }
  };
};




/****************************************************************************
    MX Remote Scripting
 ****************************************************************************/

/*
  MxJsLib.RemoteScripting

  Remote scripting facility.


  Parameters to constructor function: none

  Methods:
    setRequestURL(url)
      set URL for remote request
    setResponseListener(func)

    execute()
      start request
    
#   startet einen Request und ruft die URL ab.
#   Die Antwortseite muss Javascript-Code enthalten, welcher
#   parent.js_callback(self) aufruft.
*/

MxJsLib.RemoteScripting = function() {
  // public properties
  this.name='RemoteScripting';
  // public methods
  this.setRequestURL = function(url) {
    // referencing private data here is valid as all variables
    // declared in a function are defined throughout the function
    // no matter where they are declared.
    if (!url) throw new MxJsLib.Exception("RemoteScripting: url missing.");
    RequestURL = url;
  };
  this.setResponseListener = function(func) {
    if (!isFunction(func)) throw new MxJsLib.Exception("RemoteScripting: callback missing.");
    ResponseListener = func;
  };
  this.setResponseTypeJavascript = function() {
    ResponseTypeJavascript = true;
  }
  this.isExecuting = function() { return (TimeoutHandler != null); };
  this.execute = function() {
    if ( !RequestURL
      || !ResponseListener
      || !IFrameElement ) {
      throw new MxJsLib.Exception("RemoteScripting: not initialized properly.");
    }
    if (TimeoutHandler != null) return false;
    var url = MxJsLib.makeUncachedURL(RequestURL);
    if (IFrameElement.onload == null) {
      IFrameElement.onload = responseSuccess;
      IFrameElement.onerror = responseError;
    }
    TimeoutHandler = window.setTimeout(responseTimeout, TimeoutTime * 1000);
    if (ResponseTypeJavascript) {
      var html = '<html><header><scrip'+'t SRC=\"' + url + '\"></sc'+'ript></header><body>' + url + '</body></html>';
      IFrameWindow.document.open();
      IFrameWindow.document.write(html);
      IFrameWindow.document.close();
    } else {
      IFrameElement.src = url;
    }
    return true;
  };
  this.cancel = function() {
    if (TimeoutHandler != null) {
      window.clearTimeout(TimeoutHandler);
      TimeoutHandler = null;
      IFrameElement.onload = IFrameElement.onerror = null;
      IFrameElement.src = "";
    }
  };
  
  // private data
  var MyID = MxUILib.getUniqueElementID();
  var RequestURL = "";
  var ResponseListener = null;
  var TimeoutHandler = null;
  var TimeoutTime = 5;
  var ResponseTypeJavascript = false;
  // private methods
  var responseTimeout = function() {
    TimeoutHandler = null;
    IFrameElement.onload = IFrameElement.onerror = null;
    IFrameElement.src = "";
    console.log("result timeout:");
    ResponseListener(null);
  }
  var responseSuccess = function() { responseHandler(true); };
  var responseError = function() {   responseHandler(false); };
  var responseHandler = function(success) {
    if (TimeoutHandler != null) {
      window.clearTimeout(TimeoutHandler);
      TimeoutHandler = null;
    } else {
      throw new MxJsLib.Exception("response while timeout");
    }
    console.log("result:"+success);
    ResponseListener(IFrameWindow);
  };
  // initialization
  this.name += MyID;
  var IFrameElement = document.createElement("iframe");
  IFrameElement.name = this.name;
//  IFrameElement.style.display = 'none';
  document.body.appendChild(IFrameElement);
  var IFrameWindow = window.frames[this.name];
}

/*
var o = new MxJsLib.RemoteScripting();
o.setResponseListener( function(a) {global=a;console.log("listener:"+a)});

o.setRequestURL('/control/test.cgi?html=0&data=1&sleep=1');
o.setResponseTypeJavascript();

o.setRequestURL('/control/test.cgi?html=1&data=1&sleep=1');

o.execute();
*/


