Zapatec Utils Overview

Overview | File | Class | Tree | Index | Help

Zapatec Utils

PREV NEXT | FRAMES NO FRAMES

## pane.js

#### Summary

No overview generated for 'pane.js'

Class Summary
Zapatec.Pane |

    // $Id: pane.js 7112 2007-05-10 19:37:05Z vkulov $
    /*
     * <pre>
     * Copyright (c) 2004-2006 by Zapatec, Inc.
     * http://www.zapatec.com
     * 1700 MLK Way, Berkeley, California,
     * 94709, U.S.A.
     * All rights reserved.
     * </pre>
     */

    /**
    * Zapatec Pane object. Creates the element for displaying content
    * and gives an interface to work with it.
    * @param config [object] - pane config.
    *
    * Constructor recognizes the following properties of the config object
    * \code
    *	property name		| description
    *-------------------------------------------------------------------------------------------------
    *	parent					| [string or object] Reference to DOM element where
    *							| newly created Pane will be placed. By default -
    *							| document.body
    *	containerType			| [string] Required. Possible values: div|iframe|current -
    *							| what type of pane to create
    *							| * div - create new DIV element and add it into parent
    *							| * iframe - create new IFRAME element and add it into parent
    *							| * current - use parent as pane container
    *	sourceType				| [string] see Zapatec.Widget documentation for this option
    *	source					| [string or object] see Zapatec.Widget documentation
    *							| for this option
    *   width                   | [number] width of the Pane in "px"
    *   height                  | [number] height of the Pane in "px"
    *   autoContentWidth        | [boolean] Option which determines if the width of the Pane will be
    *                           | determined automatically.
    *   autoContentHeight       | [boolean] Option which determines if the height of the Pane will be
    *                           | determined automatically.
    *   id                      | [string] Optionally you can pass some string to be able to seek Pane by this ID
    *   onlyInit                | [boolean] For developing needs this option will disable calling create method
    *                           | of the Pane from init, so HTML structure will not be created after calling
    *                           | constructor.
    *   showLoadingIndicator    | [boolean] Determines if a loading progress indicator is to be shown
    *                           | inside pane during loading. Default is true.
    *   overflow                | [string] Determines pane content overflow style. Default to "auto".
    * \endcode
    */
    Zapatec.Pane = function(objArgs){
    	this.config = {};

    	if(arguments.length == 0){
    		objArgs = {};
    	}

    	//type of the widget - pane in our case :)
    	this.widgetType = "pane";
    	// internal variable that indicates if iframe was loaded
    	this.ready = false;
    	//is the content still loading
    	this.loading = false;
    	// internal variable to detect if Pane was prepared (prepareHtml was called)
    	this.prepared = false;
    	// variable to store reference to pane container
    	Zapatec.Utils.createProperty(this, "container", null);
    	// variable to store the reference to the content element
    	Zapatec.Utils.createProperty(this, "contentElement", null);
    	// internal variable stores reference to IFRAME's document object
    	Zapatec.Utils.createProperty(this, "iframeDocument", null);
    	//calling superconstructor
    	Zapatec.Pane.SUPERconstructor.call(this, objArgs);
    }

    Zapatec.Pane.id = "Zapatec.Pane";

    // Inherit SuperClass
    Zapatec.inherit(Zapatec.Pane, Zapatec.Widget);

    /**
    * Init function. Actually this function does the creation of element
    * itself, not the constructor.
    */
    Zapatec.Pane.prototype.init = function(objArgs){
    	//parent element of the Pane
    	Zapatec.Utils.createProperty(this.config, "parent", document.body);
    	//theme
    	this.config.theme = null;
    	//initial width
    	this.config.width = null;
    	//initial height
    	this.config.height = null;
    	//container type "div"/"iframe"/"current"
    	this.config.containerType = "div";
    	//content source type
    	this.config.sourceType = null;
    	//source to load content from
    	this.config.source = null;
    	//is the Pane resized due to its content
    	this.config.autoContentWidth = false;
    	this.config.autoContentHeight = false;
    	//this option is used for disabling creation of HTML elements for the Pane.
    	//Developer should manually call create method if he defines this option as true.
    	this.config.onlyInit = false;
    	this.config.showLoadingIndicator = true;
    	// Determines pane overflow style. Defaults to auto for backward compatibility.
      this.config.overflow = "auto";
      // Determines pane id
      this.config.id = null;

    	// processing Widget functionality
    	Zapatec.Pane.SUPERclass.init.call(this, objArgs);
    	//default for containerType is "div"
    	if(this.config.containerType == null){
    		this.config.containerType = "div";
    	}

    	//caling create if enabled
    	if (!this.config.onlyInit) {
    		this.create(this.config.width, this.config.height);
    	}
    	var self = this;
    	function update() {
    		//if there is loader we need to update it
    		if (self.loader) {
    			self.loader.update();
    		}
    	}
    	//updating loader
    	this.addEventListener("fetchSourceStart", update);
    	this.addEventListener("fetchSourceEnd", update);
    }

    /**
     * Creates all needed HTML elements, if we are not reusing parent.
     */
    Zapatec.Pane.prototype.prepareHtml = function() {
    	if(this.config.containerType.toLowerCase() == 'iframe'){
    		// create new IFRAME element
    		var iframe = document.createElement("iframe");
    		iframe.src = Zapatec.zapatecPath + "pane_files/blank.html#" + this.id;
    		//filling the container property
    		this.container = iframe;
    		iframe = null;
    	} else if(this.config.containerType.toLowerCase() == 'div'){
    		// create new DIV element
    		this.container = document.createElement("div");
        // If id config option is specified
        if (this.config.id) {
          // set pane container div id
          this.container.id = this.config.id;
        }
        this.contentElement = this.container;
    	} else if (this.config.parent && this.config.parent.nodeType == 1) {
    		this.container = this.config.parent;
    		this.contentElement = this.container;
    	}
    	//flag to detect that this function was called
    	this.prepared = true;
    };

    /**
     * Creates the HTML part of the Pane by calling prepareHtml,
     * if it was not called before and adds the container to
     * the document tree.
     * @param width [number] - optional, width that can be different from
     * the config's one
     * @param height [number] - optional, height that can be different from
     * the config's one
     */
    Zapatec.Pane.prototype.create = function(width, height) {
    	//if there was no preparation made we need to do it.
    	//this is for separating operations that do depend
    	//on body load from that which are indepenedent
    	if (!this.prepared) {
    		this.prepareHtml();
    	}

    	//searching Pane's parent
    	if (!(this.config.parent = Zapatec.Widget.getElementById(this.config.parent))) {
    		Zapatec.Log({description: "No reference to parent element."});
    		return null;
    	}
    	//I need this kind of interface to be able to pass widget objects as
    	//parent property of config.
    	if (this.config.parent.requestParentFor && !(this.config.parent = this.config.parent.requestParentFor(this))) {
    		Zapatec.Log({description: "No reference to parent element after request to the Parent Widget!"});
    		return null;
    	}

    	//we can work with a container if it exists, otherwise we
    	//need to reuse parent
    	if (this.config.containerType.toLowerCase() == 'div' ||
    	    this.config.containerType.toLowerCase() == 'iframe') {
    			//appending it to the tree
    			this.ready = false;
    			this.config.parent.appendChild(this.container);
    			//initing Pane to be ready for work
    			if (this.config.containerType.toLowerCase() != 'iframe') {
    				this.initPane();
    			}
    	} else if (this.config.containerType.toLowerCase() == 'current') {
    		//reusing parent
    		this.container = this.config.parent;
    		this.contentElement = this.container;
    		this.initPane();
    	} else {
    		Zapatec.Log({description: "Unknown container type: " + this.config.containerType + ". Possible values: iframe|div"})
    	}
    	//adding a theme className
    	Zapatec.Utils.addClass(this.container, this.getClassName({prefix: "zpPane"}));
    	//setting sizes
    	if (width || this.config.width) {
    		this.setWidth(width || this.config.width);
    	}
    	if (height || this.config.height) {
    		this.setHeight(height || this.config.height);
    	}
    	this.getContainer().style.display = "block";
    	this.setPaneContent();
    };

    /*
    * \internal For containerType=iframe this function stores reference to internal
    * document.body element and loads data from given source.
    */
    Zapatec.Pane.prototype.initPane = function(){
    	if(this.config.containerType.toLowerCase() == 'iframe'){
    		var doc = null;
    		//this variable determines if the Pane has the content from the same domain
    		var sameDomain = true;
    		//iframe's src
    		var url = this.getContainer().src;
    		//anchor element to make a workaround while calculating absolute URL from src
    		var anchorEl = document.createElement("a");
    		//is there a protocol definition in the SRC
    		//cause if not than this must be the same domain
    		//and we don't have to worry about anything
    		var protocolSeparatorPos = url.indexOf("://");

    		//if there is protocol definition than we must check
    		//if domain is the same as ours(I mean the page where we use the Pane)
    		if (protocolSeparatorPos != -1) {
    			//retreiving the domain
    			var domainSeparatorPos = url.indexOf("/", protocolSeparatorPos + 3);
    			var domain = url.substring(
    				(protocolSeparatorPos > 0 ? protocolSeparatorPos + 3 : 0),
    				(domainSeparatorPos > 0 ? domainSeparatorPos : url.length)
    			);
    			//checking if it is the same
    			if (domain != window.location.host) {
    				sameDomain = false;
    			}
    		}
    		//if it is the same domain than we can easily work with its content
    		//otherwise this.contentElement will be null and all methods
    		//that are working with content will block their work
    		if (sameDomain) {
    			//checking if iframes document is avaliable
    			if(this.container.contentDocument != null){
    				doc = this.container.contentDocument;
    			} else if(this.container.contentWindow && this.container.contentWindow.document != null){
    				doc = this.container.contentWindow.document;
    			}

    			var self = this;
    			//a workaround to get absolute url from src property
    			anchorEl.href = url;
    			url = anchorEl.href;
    			//if iframe's document is unavaliable still
    			//fire this function again with some timeout
    			if (doc == null || doc.body == null || (Zapatec.is_gecko && url != this.container.contentWindow.location.href)) {
    				setTimeout(function(){self.initPane()}, 50);
    				return false;
    			}
    			// store reference to iframe's document
    			this.iframeDocument = doc;
    			this.contentElement = doc.body;
    			if (typeof this.container.contentWindow.Zapatec != "object" && typeof this.container.contentWindow.Zapatec != "function") {
    				this.container.contentWindow.Zapatec = {};
    				this.container.contentWindow.Zapatec.windowLoaded = typeof(doc.readyState) != 'undefined' ?
    					(
    						doc.readyState == 'loaded' || // Konqueror
    						doc.readyState == 'complete' // IE/Opera
    					) :
    					// Mozilla
    					doc.getElementsByTagName != null && typeof(doc.getElementsByTagName('body')[0]) != 'undefined'
    				;

    				Zapatec.Utils.addEvent(this.container.contentWindow, "load", function() {self.container.contentWindow.Zapatec.windowLoaded = true;});
    			}
    			if (!this.container.contentWindow.Zapatec || !this.container.contentWindow.Zapatec.windowLoaded) {
    				setTimeout(function(){self.initPane()}, 50);
    				return false;
    			}

    			doc = null;
    		}
    	}

      // Check if overflow config option is specified
      if (this.config.overflow) {
        this.getContainer().style.overflow = this.config.overflow;
      }
      this.ready = true;
      //firing the ready event
    	this.fireEvent("onReady", this);
    	//hiding loader
    	this.hideIndicator();
    	//pointing the end of loading
    	this.loading = false;
    	//TODO: move this functionality to EventDriven or any other place
    	this.removeEvent("onReady");
    }

    /*
    * Returns reference to data container - do not use this element to resize it or
    * do any other DOM changes!
    */
    Zapatec.Pane.prototype.getContainer = function(){
    	return this.container;
    }

    /*
    * Returns reference to IFRAME's document object.
    */
    Zapatec.Pane.prototype.getIframeDocument = function(){
    	return this.iframeDocument;
    }

    /*
    * Returns the element which represents the content
    */
    Zapatec.Pane.prototype.getContentElement = function() {
    	return this.contentElement;
    }

    /*
    * Returns true if iframe was loaded succesfully
    */
    Zapatec.Pane.prototype.isReady = function(){
    	return this.ready;
    }

    /**
    * Loads data from the HTML source.
    * \param objSource [object] source HTMLElement object.
    */
    Zapatec.Pane.prototype.loadDataJson = function(objSource){
    	return objSource != null ? this.setContent(objSource.content) : null;
    }

    /**
    * Sets the content of the pane.
    * @param content [string or object] - string content or reference to DOM element
    * @return [boolean] - true if successfull, otherwise false.
    */
    Zapatec.Pane.prototype.setContent = function(content){
    	if(!this.isReady()){
    		// this can happen when containerType=iframe but it is not created yet.
    		var self = this;
    		setTimeout(function(){self.setContent(content)}, 50);
    		return null;
    	}
    	//if there is no contentElement than this is an iframe with src from
    	//another domain and we can not work with it
    	this.loading = false;
    	if (!this.getContentElement()) {
    		this.hideIndicator();
    		return false;
    	}
    	//no content no action :)
    	if(content === null){
    		this.hideIndicator();
    		return null;
    	} else {
    		//if this is not iframe and we have auto sizes we need to allow the
    		//content to be visible
    		if (this.config.containerType.toLowerCase() != "iframe") {
    			//saving old overflow
    			var oldOverflow = this.getContainer().style.overflow;
    			//setting needed sizes to "auto"
    			if (this.config.autoContentWidth) {
    				//setting overflow visible
    				this.getContainer().style.overflow = "visible";
    				this.getContainer().style.width = "auto";
    			}
    			if (this.config.autoContentHeight) {
    				//setting overflow visible
    				this.getContainer().style.overflow = "visible";
    				this.getContainer().style.height = "auto";
    			}
    		}
    		if(typeof(content) == 'string'){
    			//setting new content if it is string
    			Zapatec.Transport.setInnerHtml({container : this.getContentElement(), html : content});
    		} else {
    			try{
    				//this is temporary fix for IE as it can not appendChild in iframe
    				//when node was created not in iframe document
    				if ((Zapatec.is_ie || Zapatec.is_opera) && this.config.containerType.toLowerCase() == "iframe") {
    					Zapatec.Transport.setInnerHtml({container : this.getContentElement(), html : content.outerHTML});
    				} else {
    		        	// If content is not already added to container
    		        	if (content.parentNode != this.getContentElement()) {
    		        		//empty the element
    		        		this.getContentElement().innerHTML = "";
    		        		this.getContentElement().appendChild(content);
    		        	}
    		        }
    			} catch(ex){
    				this.hideIndicator();
    				return null;
    			}
    		}
    		//if this is not an iframe we get new sizes and set them as the size of Pane
    		if (this.config.containerType.toLowerCase() != "iframe") {
    			var newWidth = this.getWidth();
    			var newHeight = this.getHeight();
    		} else {
    			//if this is iframe we are using scrollWidth and scrollHeight to resize iframe.
    			//FIXME: This is not working correctly so needed to be fixed in new version
    			var newWidth = this.getContentElement().scrollWidth + 5;
    			var newHeight = this.getContentElement().scrollHeight + 5;
    		}
    		//restoreing overflow
    		if (typeof oldOverflow != "undefined") this.getContainer().style.overflow = oldOverflow;
    		//setting new sizes
    		if (this.config.autoContentWidth) {
    			this.setWidth(newWidth);
    		}
    		if (this.config.autoContentHeight) {
    			this.setHeight(newHeight);
    		}
    	}
    	//calling listeners for contentLoad event
    	//use this to resize your widget which uses auto sizes feature of Pane
    	this.fireEvent("contentLoaded", this);
    	this.hideIndicator();
    	return true;
    }

    /**
    * Loads data from the HTML|Xml fragment source.
    * \param strSource [string] source HTML fragment.
    */
    Zapatec.Pane.prototype.loadDataHtml = Zapatec.Pane.prototype.loadDataXml = Zapatec.Pane.prototype.setContent;

    Zapatec.Pane.prototype.loadDataHtmlText = function(content) {
    	this.setContent(content);
    };

    /*
    * Set pane width
    * \param width [int] - width to set.
    */
    Zapatec.Pane.prototype.setWidth = function(width){
    	var self = this;
    	this.fireWhenReady(function() {
    		self.getContainer().style.width = width + "px";
    		//we do this to have the size passed be equal to offset size
    		if (self.getContainer().offsetWidth != width) {
    			var newWidth = width - (self.getContainer().offsetWidth - width);
    			if (newWidth < 0) newWidth = 0;
    			self.getContainer().style.width = newWidth + "px";
    		}
    	});
    }

    /*
    * Returns pane width
    */
    Zapatec.Pane.prototype.getWidth = function(){
    	return this.getContainer().offsetWidth;
    }

    /*
    * Set pane height
    * \param height [int] - height to set.
    */
    Zapatec.Pane.prototype.setHeight = function(height){
    	var self = this;
    	this.fireWhenReady(function() {
    		self.getContainer().style.height = height + "px";
    		//we do this to have the size passed be equal to offset size
    		if (self.getContainer().offsetHeight != height) {
    			var newHeight = height - (self.getContainer().offsetHeight - height);
    			if (newHeight < 0) newHeight = 0;
    			self.getContainer().style.height = newHeight + "px";
    		}
    	});
    }

    /*
    * Returns pane height
    */
    Zapatec.Pane.prototype.getHeight = function(){
    	return this.getContainer().offsetHeight;
    }

    /*
     * Removes the border for iframe.
     */
    Zapatec.Pane.prototype.removeBorder = function() {
    	if (this.config.containerType.toLowerCase() != "iframe") {
    		return false;
    	}
    	var self = this;
    	this.fireWhenReady(function() {
    		//trying to remove border
    		if (!Zapatec.is_ie) {
    			self.getContainer().style.border = "none";
    		} else {
    			if (self.getContentElement()) {
    				self.getContentElement().style.border = "none";
    			}
    		}
    	});
    };

    /* The method to cover setting of content. Simply it sets the content
     * depending on its type.
     * @param content [mixed] - value for the content
     * @param type [string] - type of content: "html", "html/text", "html/url"
     * @return [boolean] - true if successfull, otherwise false.
     */
    Zapatec.Pane.prototype.setPaneContent = function(content, type) {
    	//if we have no arguments passed we need to set default
    	//ones from config
    	if (!content && content !== "") {
    		content = this.config.source;
    	}
    	if (!type) {
    		type = this.config.sourceType;
    	}
    	//we need to save the content description for reloading
    	this.config.source = content;
    	this.config.sourceType = type;
    	//if containerType == "iframe" and we are seting its content from URL,
    	//we should use its SRC attribute
    	var self = this;
    	// If loading indicator is enabled
    	if (this.config.showLoadingIndicator) {
    		this.showIndicator();
    		this.loading = true;
    	}
    	if (this.config.containerType.toLowerCase() == "iframe" && type == "html/url") {
    		this.ready = false;
    		this.fireWhenReady(function() {
    			//if Pane have auto sizes we need to set them
    			if (self.getContentElement()) {
    				try {
    					var newWidth = self.getContentElement().scrollWidth;
    					var newHeight = self.getContentElement().scrollHeight;
    					if (self.config.autoContentWidth) {
    						self.setWidth(newWidth);
    					}
    					if (self.config.autoContentHeight) {
    						self.setHeight(newHeight);
    					}
    				} catch(e) {}
    			}
    			self.fireEvent("contentLoaded", self);
    			if (self.events["contentLoaded"]) {
    				self.events["contentLoaded"].listeners = [];
    			}
    		});
    		this.getContainer().src = content;
    		setTimeout(function(){self.initPane()}, 50);

    		return true;
    	}

    	if (this.config.containerType.toLowerCase() == "iframe" && this.getContainer().src.indexOf((Zapatec.zapatecPath + "pane_files/blank.html#" + this.id).replace(/\.\.\//g, "")) < 0) {
    		this.ready = false;
    		this.getContainer().src = Zapatec.zapatecPath + "pane_files/blank.html#" + this.id;
    	}

    	//otherwise we use zpwidget's possibilities
    	this.loadData();

    	return true;
    };

    /**
     * Shows the Pane container.
     */
    Zapatec.Pane.prototype.show = function() {
    	this.getContainer().style.display = "";
    	if (this.loading) {
    		this.showIndicator();
    	}
    };

    /**
     * Hides the Pane container.
     */
    Zapatec.Pane.prototype.hide = function() {
    	this.getContainer().style.display = "none";
    	if (this.loading) {
    		this.hideIndicator();
    	}
    };

    /**
     * Shows the indicater.
     * @param message {string} message to show.
     */
    Zapatec.Pane.prototype.showIndicator = function(message) {
    	if (Zapatec.Indicator) {
    		this.hideIndicator();
    		//creating loader
    		if (!this.loader) {
    			this.loader = new Zapatec.Indicator({
    				container: this.container,
    				themePath: Zapatec.zapatecPath + "../zpextra/themes/indicator/"
    			});
    		}
    		this.loader.start(message || 'loading');
    	}
    };

    /**
     * Hides the indicater.
     */
    Zapatec.Pane.prototype.hideIndicator = function() {
    	if (this.loader && this.loader.isActive()) {
    		this.loader.stop();
    	}
    };

    /*
     * Fires the action when widget is ready.
     * @param func [function] - the function to be fired when the widget is ready
     */
    Zapatec.Pane.prototype.fireWhenReady = function(func) {
    	if (!this.isReady()) {
    		this.addEventListener("onReady", func);
    	} else {
    		func.call(this, this);
    	}
    }

    /**
    * Destroys current pane instance removing all related HTML elements.
    */

    Zapatec.Pane.prototype.destroy = function(){
    	this.hideIndicator();
    	this.config.parent = null;
    	this.contentElement = null;
    	this.iframeDocument = null;
    	if(Zapatec.is_ie && this.config.containerType.toLowerCase() == 'iframe'){
    		this.container.src = "javascript:void(0)";
    	}

    	if (this.container.outerHTML) {
    		this.container.outerHTML = "";
    	} else {
    		Zapatec.Utils.destroy(this.container);
    	}
    	this.container = null;
    	this.ready = false;
    	this.prepared = false;
    }

Overview | File | Class | Tree | Index | Help

Zapatec Utils

PREV NEXT | FRAMES NO FRAMES

Documentation generated by JSDoc on Thu Aug 16 12:18:39 2007