/**
 * iBox version 2.17b 
 * For more info & download: http://labs.ibegin.com/ibox/
 * Created as a part of the iBegin iBegin Labs Project - http://labs.ibegin.com/
 * For licensing please see readme.html (MIT Open Source License)
 *
 * Modifications (by r.j.)
 * The script was modified to satisfy the special needs for project puma. It cannot be considered to be
 * generally usable.
 * 
 * Browsers calculate the height of the iBox too small, when the content must be scrolled. It seems to be a timing
 * problem. Therefore a limited sequence of delayed queries are initiated to give the browsers a new chance.
 * TODO:
 * - resize box on window resize
 * - set window overlay on complete document not just on visible area / implementation relies on not positioned ancestors
 * - refactoring of drawCSS function 
 * - height of elements is calculated incorrect when padding is present (offsetHeight != style.height)
 * - adjusting height to content elements of different height doesn't work (switching content with display: none)
 * - height calculation doesn't take scrollbar into account
 * 
 * Remarks: this script still needs a lot of improvements!
*/
var iBox = function()
{
  var _pub = {
    // label for the close link
    close_label: 'Close',

    // padding around the box
    padding: 100,
    
    // show iframed content in the parent window
    // this *does not* work with #containers
    inherit_frames: false,

    // how fast to fade in the overlay/ibox (this is each step in ms)
    fade_in_speed: 0,

    // our attribute identifier for our iBox elements
    attribute_name: 'rel',
    
    // tags to hide when we show our box
    tags_to_hide: ['select', 'embed', 'object'],

    // default width of the box (when displaying html only)
    // height is calculated automatically
    default_width: 800,
    
    // height of header (difference between wrapper and content height)
    header_height: 43,

    // browser checks
    is_opera: navigator.userAgent.indexOf('Opera/9') != -1,
    is_ie: navigator.userAgent.indexOf("MSIE ") != -1,
    is_ie6: false /*@cc_on || @_jscript_version < 5.7 @*/,
    is_firefox: navigator.appName == "Netscape" && navigator.userAgent.indexOf("Gecko") != -1 && navigator.userAgent.indexOf("Netscape") == -1,
    is_mac: navigator.userAgent.indexOf('Macintosh') != -1,
    
    base_url: '',
     	
    /**
     * Updates the base_url variable.
     * @param {String} path Relative or absolute path to this file.
     */
    setPath: function(path)
    {
      _pub.base_url = path;
    },
    
    /**
     * Binds arguments to a callback function
     */
    bind: function(fn)
    {
        var args = [];
        for (var n=1; n<arguments.length; n++) args.push(arguments[n]);
        return function(e) { return fn.apply(this, [e].concat(args)); };
    },

    /**
     * Sets the content of the ibox and inital size.
     * @param {String} content HTML content
     * @param {Object} params
     */
    html: function(content, params) {
    	var height, width;
    	
      if (content === undefined) return els.content;
      if (cancelled) return;
      _pub.clear();
      els.wrapper.style.display = "block";
      els.wrapper.style.visibility = "hidden";
      els.content.style.height = 'auto';

      if (typeof(content) == 'string') els.content.innerHTML = content;
      else {
      	els.content.appendChild(content);
      	for (var i=0; i<_pub.tags_to_hide.length; i++) fixTags(_pub.tags_to_hide[i]);
      }

      if (params.can_resize === undefined) params.can_resize = true;
      if (params.fade_in === undefined) params.use_fade = true;

      if (params.width) width = parseInt(params.width);
      else width = _pub.default_width;

      var elemSize = _pub.getElementSize(els.content);
      if (params.height) height = parseInt(params.height);
      else height = elemSize.height;

      if (params.can_resize) { 
      	_pub.resizeObjectToScreen(els.wrapper, width, height, params.constrain);
      	width = parseInt(els.wrapper.style.width);
      	height = parseInt(els.wrapper.style.height);
      }
			_pub.setContentSize(els.content, width, height);
			
      _pub.reposition();
      els.wrapper.style.visibility = "visible"; 
      _pub.fadeIn(els.wrapper, 10, params.fade_in ? _pub.fade_in_speed : 0);
      _pub.fireEvent('rendered');
    },
   
    /*
     * @param {Object} obj elemenmt object to set the dimensions for
     * @param (Number) width width of content area (style property width)
     * @param (Number) height width of content area (style property height)
     */
    setContentSize: function (obj, width, height) {
    	obj.style.width = width + 'px'; 
    	obj.style.height = height - _pub.header_height + 'px'; 
    },
    
    /**
     * Empties the content of the iBox (also hides the loading indicator)
     */
    clear: function()
    {
      els.loading.style.display = "none";
      while (els.content.firstChild) els.content.removeChild(els.content.firstChild);
    },
    
    toInitQueue: function(entry)
    {
    	iBox.initQueue = entry;
    },
    
    /**
     * Loads text into the ibox
     * @param {String} url
     * @param {String} title
     * @param {Object} params
     */
    show: function(text, title, params)
    {
      if (!iBox.initialized) {
      	this.toInitQueue({
      		type: 'show',
      		text: text,
      		title: title,
      		params: params
      	});
      } else {
	      params = params || {};
	      _pub.hide();
	      showInit(title, params, function(){
	        _pub.html(text, params);
	      });
	  }
    },
    /**
     * Loads a url into the ibox
     * @param {String} url
     * @param {String} title
     * @param {Object} params
     */
    showURL: function(url, title, params)
    {
      if (!iBox.initialized) {
      	this.toInitQueue({
      		type: 'showURL',
      		url: url,
      		title: title,
      		params: params
      	});
      } else {
	      params = params || {};
	      showInit(title, params, function(){
	        cancelled = false;
	        for (var i=0; i<_pub.plugins.list.length; i++)
	        {
	          var plugin = _pub.plugins.list[i];
	          if (plugin.match(url))
	          {
	            active_plugin = plugin;
	            plugin.render(url, params);
	            break;
	          }
	        }
	      });
	  }
    },
    
    /**
     * Shows the loading layer of the iBox.
     */
    showLoading: function()
    {
    	showInit();
    },

    /**
     * Hides the iBox
     */
    hide: function()
    {
      if (active_plugin)
      {
        // call the plugins unload method
        if (active_plugin.unload) active_plugin.unload();
        active_plugin = null;
      }
      window.onscroll = null;
      _pub.clear();
      // restore elements that were hidden
      for (var i=0; i<_pub.tags_to_hide.length; i++) showTags(_pub.tags_to_hide[i]);

      els.loading.style.display = 'none';
      els.overlay.style.display = 'none'; 
      els.wrapper.style.display = 'none';
      _pub.fireEvent('hide');
    },

    /**
     * Resizes an object to fit on screen (Beware: the content dimensions of the object are resized)
     * @param {Object} obj
     * @param {Integer} width: the content width of the element
     * @param {Integer} height:
     * @param {Boolean} constrain
     */
    resizeObjectToScreen: function(obj, width, height, constrain) {
			var pagesize = _pub.getPageSize();
      var x = pagesize.width - _pub.padding;
      var y = pagesize.height - _pub.padding;

			var width = width || _pub.default_width;
			var height = height || y;
			
      if (width > x) {
        if (constrain) height = height * (x/width);
        width = x;
      }
      if (height > y) {
        if (constrain) width = width * (y/height);
        height = y;
      }
      obj.style.width = width + 'px';
      obj.style.height = height + 'px';
    },

    /**
     * Repositions the iBox wrapper (from events)
     */
    reposition: function(e)
    {
      // verify height doesnt overreach browser's viewpane
      _pub.center(els.loading);
      _pub.center(els.wrapper);
      var pageSize = _pub.getPageSize();
      var scrollPos = _pub.getScrollPos();
      
      if (_pub.is_ie6) els.overlay.style.width = document.documentElement.clientWidth + 'px';
      var height = Math.max(document.documentElement.clientHeight, document.body.clientHeight);
      els.overlay.style.top = scrollPos.scrollY + 'px';
      els.overlay.style.height = height + 'px';
    },

    /**
     * Centers an object
     * @param {Object} obj
     */
    center: function(obj)
    {
      var pageSize = _pub.getPageSize();
      var scrollPos = _pub.getScrollPos();
      var emSize = _pub.getElementSize(obj);
      var x = Math.round((pageSize.width - emSize.width) / 2 + scrollPos.scrollX);
      //var y = Math.round((pageSize.height - emSize.height) / 2 + scrollPos.scrollY);
      if ( obj.id == 'ibox_loading' ) {
    	  var y = Math.round(150 + scrollPos.scrollY);
      } else {
        var y = Math.round(30 + scrollPos.scrollY);
      }
      obj.style.left = x + 'px';
      obj.style.top = y + 'px';
    },
    
    getStyle: function(obj, styleProp)
    {
      if (obj.currentStyle)
        return obj.currentStyle[styleProp];
      else if (window.getComputedStyle)
        return document.defaultView.getComputedStyle(obj,null).getPropertyValue(styleProp);
    },

    /**
     * Gets the scroll positions
     */
    getScrollPos: function()
    {
      var docElem = document.documentElement;
      return {
        scrollX: document.body.scrollLeft || window.pageXOffset || (docElem && docElem.scrollLeft),
        scrollY: document.body.scrollTop || window.pageYOffset || (docElem && docElem.scrollTop)
      };
    },

    /**
     * Gets the page constraints
     */
    getPageSize: function()
    {
      return {
        width: window.innerWidth || (document.documentElement && document.documentElement.clientWidth) || document.body.clientWidth,
        height: window.innerHeight || (document.documentElement && document.documentElement.clientHeight) || document.body.clientHeight
      };
    },

    /**
     * Gets an objects dimensions (overall size including padding, border)
     * TODO: fallback value (0) may be not reasonable
     * @param {Object} obj
     */
    getElementSize: function(obj) {
	      return {
	        width: obj && obj.offsetWidth || 0,
	        height: obj && obj.offsetHeight || 0
	      };
    },

    fadeIn: function(obj, level, speed, callback)
    {
      if (level === undefined) var level = 100;
      if (speed === undefined) var speed = 70;
      if (!speed)
      {
        _pub.setOpacity(null, obj, level*10);
        if (callback) callback();
        return;
      }
    
      _pub.setOpacity(null, obj, 0);
      for (var i=0; i<=level; i++)
      {
        setTimeout(_pub.bind(_pub.setOpacity, obj, i*10), speed*i);
      }
      if (callback) setTimeout(callback, speed*(i+1));
    },

    /**
     * Sets the opacity of an element
     * @param {Object} obj
     * @param {Integer} value
     */
    setOpacity: function(e, obj, value)
    {
      obj.style.opacity = value/100;
      obj.style.filter = 'alpha(opacity=' + value + ')';
    },
    
    /**
     * Creates a new XMLHttpRequest object based on browser
     */
    createXMLHttpRequest: function()
    {
      var http;
      if (window.XMLHttpRequest)
      { // Mozilla, Safari,...
        http = new XMLHttpRequest();
        if (http.overrideMimeType)
        {
          // set type accordingly to anticipated content type
          http.overrideMimeType('text/html');
        }
      }
      else if (window.ActiveXObject)
      { // IE
        try {
          http = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
          try {
            http = new ActiveXObject("Microsoft.XMLHTTP");
          } catch (e) {}
        }
      }
      if (!http)
      {
        alert('Cannot create XMLHTTP instance');
        return false;
      }
      return http;
    },
    
    addEvent: function(obj, evType, fn)
    {
      if (obj.addEventListener)
      {
        obj.addEventListener(evType, fn, false);
        return true;
      }
      else if (obj.attachEvent)
      {
        var r = obj.attachEvent("on"+evType, fn);
        return r;
      }
      else
      {
        return false;
      }
    },
    
    addEventListener: function(name, callback)
    {
      if (!events[name]) events[name] = new Array();
      events[name].push(callback);
    },
    
    removeEventListeners: function(name)
    {
    	if (events[name]) {
    		events[name] = [];
    	}
    },
    
    fireEvent: function(name)
    {
        if (events[name] && events[name].length)
        {
          for (var i=0; i<events[name].length; i++)
          {
            var args = [];
            for (var n=1; n<arguments.length; n++) args.push(arguments[n]);
            // Events returning false stop propagation
            if (events[name][i](args) === false) break;
          }
        }
    },
    
    /**
     * Parses the arguments in the rel attribute
     * @param {String} query
     */
    parseQuery: function(query)
    {
       var params = new Object();
       if (!query) return params; 
       var pairs = query.split(/[;&]/);
       var end_token;
       for (var i=0; i<pairs.length; i++)
       {
          var keyval = pairs[i].split('=');
          if (!keyval || keyval.length != 2) continue;
          var key = unescape(keyval[0]);
          var val = unescape(keyval[1]);
          val = val.replace(/\+/g, ' ');
          if (val[0] == '"') var token = '"';
          else if (val[0] == "'") var token = "'";
          else var token = null;
          if (token)
          {
            if (val[val.length-1] != token)
            {
              do
              {
                i += 1;
                val += '&'+pairs[i];
              }
              while ((end_token = pairs[i][pairs[i].length-1]) != token)
            }
            val = val.substr(1, val.length-2);
          }
          params[key] = val;
       }
       return params;
    },
    handleTag: function(e)
    {
      var t = this.getAttribute('rel');
      var params = _pub.parseQuery(t.substr(5,999));
      if (params.target) var url = params.target
      else if (this.target && !params.ignore_target) var url = this.target;
      else var url = this.href;
      var title = this.title;
      if (_pub.inherit_frames && window.parent) window.parent.iBox.showURL(url, title, params);
      else _pub.showURL(url, title, params);
      return false;
    },
    
    plugins: {
      list: new Array(),
      register: function(func, last)
      {
        if (!last)
        {
          _pub.plugins.list = _pub.plugins.list.concat([func],_pub.plugins.list);
        }
        else
        {
          _pub.plugins.list.push(func);
        }
      }
    },
    
    initialize: function() 
    {
	    // elements here start the look up from the start non <a> tags
	    var els = document.getElementsByTagName("a");
	    for (var i=0; i<els.length; i++)
	    {
	      if (els[i].getAttribute(_pub.attribute_name))
	      {
	        var t = els[i].getAttribute(_pub.attribute_name);
	        if ((t.indexOf("ibox") != -1) || t.toLowerCase() == "ibox")
	        { // check if this element is an iBox element
	          els[i].onclick = _pub.handleTag;
	        }
	      }
	    }
	    create(document.body);
	    _pub.http = _pub.createXMLHttpRequest();
	    iBox.initialized = true;
	    
		// add Handler to start a sequence of queries to find out the proper height
	    if (iBox.initQueue) {
	    	if (iBox.initQueue.params.height == null ) 
	    	iBox.addEventListener('show', iBox.checkBoxHeight);
	    }
	    	
	    if (iBox.initQueue) {
	    	if (iBox.initQueue.type == "show") {
	    		iBox.show(iBox.initQueue.text, iBox.initQueue.title, iBox.initQueue.params);
	    	} else if (iBox.initQueue.type == "showURL") {
	    		iBox.showURL(iBox.initQueue.url, iBox.initQueue.title, iBox.initQueue.params);
	    	}
	    }
		},
  	
  	// delay between queries for correct height (ms)
  	DELAY: 200,
  	// stop query for correct height (ms)
  	DELAYMAX: 4000,
  	// elapsed time
  	elapsedTime: 0,
  	// ID of timeout
  	timeoutID: null,
  	
  	/*
  	 * Give the browsers some time to find out the correct height of the content element.
		 * TODO if box has the maximum height, check for resizing is nonsensical
  	 * @author: r.j.
  	 */
  	checkBoxHeight: function() {
	  	// Box must be resized  TODO: set sizing relevant element per parameter
			var obj = $(els.content).firstDescendant();

  		if (obj && obj.scrollHeight > obj.clientHeight) {
   			// clear timeout
  			if (_pub.timeoutID) window.clearTimeout(_pub.timeoutID);
	 	  	_pub.resizeObjectToScreen(els.wrapper, null, (obj.scrollHeight + _pub.header_height), null);
  			_pub.setContentSize(els.content, parseInt(els.wrapper.style.width), parseInt(els.wrapper.style.height));		
  			// reset elapsed time
  			_pub.elapsedTime = 0; 
  		}
  		// wait another time
  		else {
  			_pub.elapsedTime += _pub.DELAY;
  			if ( _pub.elapsedTime < _pub.DELAYMAX ) {
  				_pub.timeoutID = window.setTimeout(_pub.checkBoxHeight, _pub.DELAY);
  			}
  			else {
	  			// reset elapsed time
  				_pub.elapsedTime = 0;
				}
  		}
		}
    
  };
  
  // private methods and variables
  var cancelled = false;
  var active_plugin = null;
  
  // events
  var events = {};

  // some containers
  // we store these in memory instead of finding them each time
  var els = {
    wrapper: null,
    footer: null,
    content: null,
    overlay: null,
    loading: null
  };

  /**
   * Creates the iBox container and appends it to an element
   * @param {Object} elem Container to attach to
   * @return {Object} iBox element
   */
  var create = function(elem)
  {
    // TODO: why isnt this using DOM tools
    // a trick on just creating an ibox wrapper then doing an innerHTML on our root ibox element
    var container = document.createElement('div');
    container.id = 'ibox';
    container.style.display = 'block';

    els.overlay = document.createElement('div');
    els.overlay.style.display = 'none';
    els.overlay.id = 'ibox_overlay';
    els.overlay.onclick = _pub.hide;
    container.appendChild(els.overlay);

    els.loading = document.createElement('div');
    els.loading.id = 'ibox_loading';
    if (document.getElementById('ibox_loading_content') == null) {
    	els.loading.innerHTML = 'Loading...';
    }
    else {
    	els.loading.innerHTML = document.getElementById('ibox_loading_content').innerHTML;
    }
    els.loading.style.display = 'none';
    els.loading.onclick = function() {
      _pub.hide();
      cancelled = true;
    }
    container.appendChild(els.loading);

    els.wrapper = document.createElement('div')
    els.wrapper.id = 'ibox_wrapper';
    els.wrapper.style.display = 'none';

    els.content = document.createElement('div');
    els.content.id = 'ibox_content';
    els.wrapper.appendChild(els.content);
  
    var child = document.createElement('div');
    child.id = 'ibox_footer_wrapper';
  
    var child2 = document.createElement('a');
    child2.innerHTML = _pub.close_label;
    child2.href = 'javascript:void(0)';
    child2.onclick = _pub.hide;
    child.appendChild(child2);
  
    els.footer = document.createElement('div');
    els.footer.id = 'ibox_footer';
    els.footer.innerHTML = '&nbsp;';
    child.appendChild(els.footer);
    els.wrapper.appendChild(child);

    container.appendChild(els.wrapper);

    elem.appendChild(container);
    return container;
  };
  
  var hideTags = function(tag)
  {
    var list = $$('#container '+tag);
    for (var i=0; i<list.length; i++)
    {
      if (_pub.getStyle(list[i], 'visibility') != 'hidden' && list[i].style.display != 'none')
      {
        list[i].style.visibility = 'hidden';
        list[i].wasHidden = true;
      }
    }
  };
  
  var showTags = function(tag)
  {
    var list = $$('#container '+tag);
    for (var i=0; i<list.length; i++)
    {
      if (list[i].wasHidden)
      {
        list[i].style.visibility = 'visible';
        list[i].wasHidden = null;
      }
    }
  };

  var fixTags = function(tag)
  {
    var list = $$('#ibox '+tag);
    for (var i=0; i<list.length; i++)
    {
      if (list[i].wasHidden)
      {
        list[i].style.visibility = 'visible';
        list[i].wasHidden = null;
      }
    }
  };
  
  var showInit = function(title, params, callback)
  {
    els.loading.style.display = "block";
    els.overlay.style.display = "block";  
  
    _pub.center(els.loading);
    _pub.reposition();
    
    if (!_pub.is_firefox) var amount = 8;
    else var amount = 10;
    for (var i=0; i<_pub.tags_to_hide.length; i++) hideTags(_pub.tags_to_hide[i]);

    window.onscroll = _pub.reposition;

    // set title here
    els.footer.innerHTML = title || "&nbsp;";

    els.overlay.style.display = "block";
    
    if ( _pub.is_ie ) {
    	els.overlay.style.backgroundColor = "#000";
    	if ( _pub.is_ie6 ) { els.overlay.style.width = "100%"; }
    } else {
    	els.overlay.style.backgroundImage = "url('" + _pub.base_url + "images/bg.png')";
    }
    
    // opacity of overlay layer
    var opac = 100;
    if (params && params.overopac !== 'undefined') opac = params.overopac;
    _pub.fadeIn(els.overlay, opac, _pub.fade_in_speed, callback);
    _pub.fireEvent('show');
  };

  _pub.addEvent(window, 'keypress', function(e){ if (e.keyCode == (window.event ? 27 : e.DOM_VK_ESCAPE)) { iBox.hide(); }});
  _pub.addEvent(window, 'resize', _pub.reposition);
  _pub.addEvent(window, 'load', _pub.initialize);

  // DEFAULT PLUGINS

  /**
   * Handles embedded containers in the page based on url of #container.
   * This _ONLY_ works with hidden containers.
   */
  var iBoxPlugin_Container = function()
  {
    var was_error = false;
    var original_wrapper = null;
    return {
      /**
       * Matches the url and returns true if it fits this plugin.
       */
      match: function(url)
      {
        return url.indexOf('#') != -1;
      },
      /**
       * Called when this plugin is unloaded.
       */
      unload: function()
      {
        if (was_error) return;
        var elemSrc = _pub.html().firstChild;
        elemSrc.style.display = 'none';
        original_wrapper.appendChild(elemSrc);
      },
      /**
       * Handles the output
       * @param {iBox} ibox
       * @param {String} url
       * @return {iBoxContent} an instance or subclass of iBoxContent
       */
      render: function(url, params)
      {
        was_error = false;
        var elemSrcId = url.substr(url.indexOf("#") + 1);
        var elemSrc = document.getElementById(elemSrcId);
        // If the element doesnt exist, break the switch
        if (!elemSrc)
        {
          was_error = true;
          _pub.html(document.createTextNode('There was an error loading the document.'), params);
        }
        else
        {
          original_wrapper = elemSrc.parentNode;
          elemSrc.style.display = 'block';
          _pub.html(elemSrc, params);
        }
      }
    }
  }();
  _pub.plugins.register(iBoxPlugin_Container, true);

  /**
   * Handles images
   */
  var iBoxPlugin_Image = function()
  {
    // Image types (for auto detection of image display)
    var image_types = /\.jpg|\.jpeg|\.png|\.gif/gi;

    return {
      match: function(url)
      {
        return url.match(image_types);
      },

      render: function(url, params)
      {  
        var img = document.createElement('img');
        img.onclick = _pub.hide;
        img.className = 'ibox_image'
        img.style.cursor = 'pointer';
        img.onload = function()
        {
          _pub.html(img, {height: img.height, width: img.width, constrain: true})
        }
        img.onerror = function()
        {
          _pub.html(document.createTextNode('There was an error loading the document.'), params);
        }
        img.src = url;
      }
    }
  }();
  _pub.plugins.register(iBoxPlugin_Image);

  var iBoxPlugin_YouTube = function()
  {
    var youtube_url = /(?:http:\/\/)?(?:www\d*\.)?(youtube\.(?:[a-z]+))\/(?:v\/|(?:watch(?:\.php)?)?\?(?:.+&)?v=)([^&]+).*/;
    return {
      match: function(url)
      {
        return url.match(youtube_url);
      },

      render: function(url, params)
      {
        var _match = url.match(youtube_url);
        var domain = _match[1];
        var id = _match[2];
        params.width = 425;
        params.height = 355;
        params.can_resize = false;
        var html = '<div><object width="425" height="355"><param name="movie" value="http://www.' + domain + '/v/' + id + '"/><param name="wmode" value="transparent"/><embed src="http://www.' + domain + '/v/' + id + '" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"></embed></object></div>';
        _pub.html(html, params);
      }
    }
  }();
  _pub.plugins.register(iBoxPlugin_YouTube);

  var iBoxPlugin_Document = function()
  {
    return {
      match: function(url)
      {
        return true;
      },

      render: function(url, params)
      {
        _pub.http.open('get', url, true);

        _pub.http.onreadystatechange = function()
        {
          if (_pub.http.readyState == 4)
          {
            // XXX: why does status return 0?
            if (_pub.http.status == 200 || _pub.http.status == 0)
            {
              _pub.html(_pub.http.responseText, params);
            }
            else
            {
              _pub.html(document.createTextNode('There was an error loading the document.'), params);
            }
          }
        }
        _pub.http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        _pub.http.send(null);
      }
    };
  }();
  _pub.plugins.register(iBoxPlugin_Document);

  return _pub;
}();