From e44a7e37b6c7b5961adaffc62b9042b8d442938e Mon Sep 17 00:00:00 2001 From: mensonge Date: Thu, 13 Nov 2008 09:49:11 +0000 Subject: New feature: basic Ajax suggestion for tags and implementation of Dojo toolkit git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@151 b3834d28-1941-0410-a4f8-b48e95affb8f --- includes/js/dijit/layout/ContentPane.js | 445 ++++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 includes/js/dijit/layout/ContentPane.js (limited to 'includes/js/dijit/layout/ContentPane.js') diff --git a/includes/js/dijit/layout/ContentPane.js b/includes/js/dijit/layout/ContentPane.js new file mode 100644 index 0000000..c3a4ca4 --- /dev/null +++ b/includes/js/dijit/layout/ContentPane.js @@ -0,0 +1,445 @@ +if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.layout.ContentPane"] = true; +dojo.provide("dijit.layout.ContentPane"); + +dojo.require("dijit._Widget"); +dojo.require("dijit.layout._LayoutWidget"); + +dojo.require("dojo.parser"); +dojo.require("dojo.string"); +dojo.requireLocalization("dijit", "loading", null, "zh,pt,da,tr,ru,ROOT,de,sv,ja,he,fi,nb,el,ar,pt-pt,cs,fr,es,ko,nl,zh-tw,pl,it,hu"); + +dojo.declare( + "dijit.layout.ContentPane", + dijit._Widget, +{ + // summary: + // A widget that acts as a Container for other widgets, and includes a ajax interface + // description: + // A widget that can be used as a standalone widget + // or as a baseclass for other widgets + // Handles replacement of document fragment using either external uri or javascript + // generated markup or DOM content, instantiating widgets within that content. + // Don't confuse it with an iframe, it only needs/wants document fragments. + // It's useful as a child of LayoutContainer, SplitContainer, or TabContainer. + // But note that those classes can contain any widget as a child. + // example: + // Some quick samples: + // To change the innerHTML use .setContent('new content') + // + // Or you can send it a NodeList, .setContent(dojo.query('div [class=selected]', userSelection)) + // please note that the nodes in NodeList will copied, not moved + // + // To do a ajax update use .setHref('url') + // + // href: String + // The href of the content that displays now. + // Set this at construction if you want to load data externally when the + // pane is shown. (Set preload=true to load it immediately.) + // Changing href after creation doesn't have any effect; see setHref(); + href: "", + + // extractContent: Boolean + // Extract visible content from inside of .... + extractContent: false, + + // parseOnLoad: Boolean + // parse content and create the widgets, if any + parseOnLoad: true, + + // preventCache: Boolean + // Cache content retreived externally + preventCache: false, + + // preload: Boolean + // Force load of data even if pane is hidden. + preload: false, + + // refreshOnShow: Boolean + // Refresh (re-download) content when pane goes from hidden to shown + refreshOnShow: false, + + // loadingMessage: String + // Message that shows while downloading + loadingMessage: "${loadingState}", + + // errorMessage: String + // Message that shows if an error occurs + errorMessage: "${errorState}", + + // isLoaded: Boolean + // Tells loading status see onLoad|onUnload for event hooks + isLoaded: false, + + // class: String + // Class name to apply to ContentPane dom nodes + // TODO: this should be called "baseClass" like in the other widgets + "class": "dijitContentPane", + + // doLayout: String/Boolean + // false - don't adjust size of children + // true - looks for the first sizable child widget (ie, having resize() method) and sets it's size to + // however big the ContentPane is (TODO: implement) + // auto - if there is a single sizable child widget (ie, having resize() method), set it's size to + // however big the ContentPane is + doLayout: "auto", + + postCreate: function(){ + // remove the title attribute so it doesn't show up when i hover + // over a node + this.domNode.title = ""; + + if(!this.containerNode){ + // make getDescendants() work + this.containerNode = this.domNode; + } + + if(this.preload){ + this._loadCheck(); + } + + var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang); + this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages); + this.errorMessage = dojo.string.substitute(this.errorMessage, messages); + var curRole = dijit.getWaiRole(this.domNode); + if (!curRole){ + dijit.setWaiRole(this.domNode, "group"); + } + + // for programatically created ContentPane (with tag), need to muck w/CSS + // or it's as though overflow:visible is set + dojo.addClass(this.domNode, this["class"]); + }, + + startup: function(){ + if(this._started){ return; } + if(this.doLayout != "false" && this.doLayout !== false){ + this._checkIfSingleChild(); + if(this._singleChild){ + this._singleChild.startup(); + } + } + this._loadCheck(); + this.inherited(arguments); + }, + + _checkIfSingleChild: function(){ + // summary: + // Test if we have exactly one widget as a child, and if so assume that we are a container for that widget, + // and should propogate startup() and resize() calls to it. + + // TODO: if there are two child widgets (a data store and a TabContainer, for example), + // should still find the TabContainer + var childNodes = dojo.query(">", this.containerNode || this.domNode), + childWidgets = childNodes.filter("[widgetId]"); + + if(childNodes.length == 1 && childWidgets.length == 1){ + this.isContainer = true; + this._singleChild = dijit.byNode(childWidgets[0]); + }else{ + delete this.isContainer; + delete this._singleChild; + } + }, + + refresh: function(){ + // summary: + // Force a refresh (re-download) of content, be sure to turn off cache + + // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane + return this._prepareLoad(true); + }, + + setHref: function(/*String|Uri*/ href){ + // summary: + // Reset the (external defined) content of this pane and replace with new url + // Note: It delays the download until widget is shown if preload is false + // href: + // url to the page you want to get, must be within the same domain as your mainpage + this.href = href; + + // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane + return this._prepareLoad(); + }, + + setContent: function(/*String|DomNode|Nodelist*/data){ + // summary: + // Replaces old content with data content, include style classes from old content + // data: + // the new Content may be String, DomNode or NodeList + // + // if data is a NodeList (or an array of nodes) nodes are copied + // so you can import nodes from another document implicitly + + // clear href so we cant run refresh and clear content + // refresh should only work if we downloaded the content + if(!this._isDownloaded){ + this.href = ""; + this._onUnloadHandler(); + } + + this._setContent(data || ""); + + this._isDownloaded = false; // must be set after _setContent(..), pathadjust in dojox.layout.ContentPane + + if(this.parseOnLoad){ + this._createSubWidgets(); + } + + if(this.doLayout != "false" && this.doLayout !== false){ + this._checkIfSingleChild(); + if(this._singleChild && this._singleChild.resize){ + this._singleChild.startup(); + this._singleChild.resize(this._contentBox || dojo.contentBox(this.containerNode || this.domNode)); + } + } + + this._onLoadHandler(); + }, + + cancel: function(){ + // summary: + // Cancels a inflight download of content + if(this._xhrDfd && (this._xhrDfd.fired == -1)){ + this._xhrDfd.cancel(); + } + delete this._xhrDfd; // garbage collect + }, + + destroy: function(){ + // if we have multiple controllers destroying us, bail after the first + if(this._beingDestroyed){ + return; + } + // make sure we call onUnload + this._onUnloadHandler(); + this._beingDestroyed = true; + this.inherited("destroy",arguments); + }, + + resize: function(size){ + dojo.marginBox(this.domNode, size); + + // Compute content box size in case we [later] need to size child + // If either height or width wasn't specified by the user, then query node for it. + // But note that setting the margin box and then immediately querying dimensions may return + // inaccurate results, so try not to depend on it. + var node = this.containerNode || this.domNode, + mb = dojo.mixin(dojo.marginBox(node), size||{}); + + this._contentBox = dijit.layout.marginBox2contentBox(node, mb); + + // If we have a single widget child then size it to fit snugly within my borders + if(this._singleChild && this._singleChild.resize){ + this._singleChild.resize(this._contentBox); + } + }, + + _prepareLoad: function(forceLoad){ + // sets up for a xhrLoad, load is deferred until widget onShow + // cancels a inflight download + this.cancel(); + this.isLoaded = false; + this._loadCheck(forceLoad); + }, + + _isShown: function(){ + // summary: returns true if the content is currently shown + if("open" in this){ + return this.open; // for TitlePane, etc. + }else{ + var node = this.domNode; + return (node.style.display != 'none') && (node.style.visibility != 'hidden'); + } + }, + + _loadCheck: function(/*Boolean*/ forceLoad){ + // call this when you change onShow (onSelected) status when selected in parent container + // it's used as a trigger for href download when this.domNode.display != 'none' + + // sequence: + // if no href -> bail + // forceLoad -> always load + // this.preload -> load when download not in progress, domNode display doesn't matter + // this.refreshOnShow -> load when download in progress bails, domNode display !='none' AND + // this.open !== false (undefined is ok), isLoaded doesn't matter + // else -> load when download not in progress, if this.open !== false (undefined is ok) AND + // domNode display != 'none', isLoaded must be false + + var displayState = this._isShown(); + + if(this.href && + (forceLoad || + (this.preload && !this._xhrDfd) || + (this.refreshOnShow && displayState && !this._xhrDfd) || + (!this.isLoaded && displayState && !this._xhrDfd) + ) + ){ + this._downloadExternalContent(); + } + }, + + _downloadExternalContent: function(){ + this._onUnloadHandler(); + + // display loading message + this._setContent( + this.onDownloadStart.call(this) + ); + + var self = this; + var getArgs = { + preventCache: (this.preventCache || this.refreshOnShow), + url: this.href, + handleAs: "text" + }; + if(dojo.isObject(this.ioArgs)){ + dojo.mixin(getArgs, this.ioArgs); + } + + var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs); + + hand.addCallback(function(html){ + try{ + self.onDownloadEnd.call(self); + self._isDownloaded = true; + self.setContent.call(self, html); // onload event is called from here + }catch(err){ + self._onError.call(self, 'Content', err); // onContentError + } + delete self._xhrDfd; + return html; + }); + + hand.addErrback(function(err){ + if(!hand.cancelled){ + // show error message in the pane + self._onError.call(self, 'Download', err); // onDownloadError + } + delete self._xhrDfd; + return err; + }); + }, + + _onLoadHandler: function(){ + this.isLoaded = true; + try{ + this.onLoad.call(this); + }catch(e){ + console.error('Error '+this.widgetId+' running custom onLoad code'); + } + }, + + _onUnloadHandler: function(){ + this.isLoaded = false; + this.cancel(); + try{ + this.onUnload.call(this); + }catch(e){ + console.error('Error '+this.widgetId+' running custom onUnload code'); + } + }, + + _setContent: function(cont){ + this.destroyDescendants(); + + try{ + var node = this.containerNode || this.domNode; + while(node.firstChild){ + dojo._destroyElement(node.firstChild); + } + if(typeof cont == "string"){ + // dijit.ContentPane does only minimal fixes, + // No pathAdjustments, script retrieval, style clean etc + // some of these should be available in the dojox.layout.ContentPane + if(this.extractContent){ + match = cont.match(/]*>\s*([\s\S]+)\s*<\/body>/im); + if(match){ cont = match[1]; } + } + node.innerHTML = cont; + }else{ + // domNode or NodeList + if(cont.nodeType){ // domNode (htmlNode 1 or textNode 3) + node.appendChild(cont); + }else{// nodelist or array such as dojo.Nodelist + dojo.forEach(cont, function(n){ + node.appendChild(n.cloneNode(true)); + }); + } + } + }catch(e){ + // check if a domfault occurs when we are appending this.errorMessage + // like for instance if domNode is a UL and we try append a DIV + var errMess = this.onContentError(e); + try{ + node.innerHTML = errMess; + }catch(e){ + console.error('Fatal '+this.id+' could not change content due to '+e.message, e); + } + } + }, + + _onError: function(type, err, consoleText){ + // shows user the string that is returned by on[type]Error + // overide on[type]Error and return your own string to customize + var errText = this['on' + type + 'Error'].call(this, err); + if(consoleText){ + console.error(consoleText, err); + }else if(errText){// a empty string won't change current content + this._setContent.call(this, errText); + } + }, + + _createSubWidgets: function(){ + // summary: scan my contents and create subwidgets + var rootNode = this.containerNode || this.domNode; + try{ + dojo.parser.parse(rootNode, true); + }catch(e){ + this._onError('Content', e, "Couldn't create widgets in "+this.id + +(this.href ? " from "+this.href : "")); + } + }, + + // EVENT's, should be overide-able + onLoad: function(e){ + // summary: + // Event hook, is called after everything is loaded and widgetified + }, + + onUnload: function(e){ + // summary: + // Event hook, is called before old content is cleared + }, + + onDownloadStart: function(){ + // summary: + // called before download starts + // the string returned by this function will be the html + // that tells the user we are loading something + // override with your own function if you want to change text + return this.loadingMessage; + }, + + onContentError: function(/*Error*/ error){ + // summary: + // called on DOM faults, require fault etc in content + // default is to display errormessage inside pane + }, + + onDownloadError: function(/*Error*/ error){ + // summary: + // Called when download error occurs, default is to display + // errormessage inside pane. Overide function to change that. + // The string returned by this function will be the html + // that tells the user a error happend + return this.errorMessage; + }, + + onDownloadEnd: function(){ + // summary: + // called when download is finished + } +}); + +} -- cgit v1.2.3