続き・・・
jetspeed.page.retrievePsml();を見てみよう。まず、pageContentListenerにnew jetspeed.om.PageContentListenerCreateWidget()を与える(と思われる、null だから)。PageContentListenerCreateWidget()は、
// ... jetspeed.om.PageContentListenerCreateWidget jetspeed.om.PageContentListenerCreateWidget = function() { }; jetspeed.om.PageContentListenerCreateWidget.prototype = { notifySuccess: function( /* XMLDocument */ data, /* String */ requestUrl, /* Page */ page ) { page.getPortletsFromPSML( data ); jetspeed.loadPortletWindows(); }, notifyFailure: function( /* String */ type, /* String */ error, /* String */ requestUrl, /* Page */ page ) { dojo.raise( "PageContentListenerCreateWidget notifyFailure url=" + requestUrl + " type=" + type + " error=" + error ) ; } };
と言う感じで、コンストラクタ?では何もしない。メソッドとして、notifySuccessとnotifyFailureを持っている。この段階では、これらのメソッドは関係ないと思うけど。
っで、次にretrievePsml()でpsmlUrlとmimeTypeを取得する。psmlUrlのgetPsmlUrl()は以下のような感じ。
getPsmlUrl: function() { if ( this.psmlPath == null ) this.setPsmlPathFromDocumentUrl() ; return jetspeed.url.basePortalUrl() + this.psmlPath ; },
この段階では、psmlPathはnullだろうから、
setPsmlPathFromDocumentUrl: function() { var psmlPath = jetspeed.url.path.AJAX_API ; var docPath = document.location.pathname ; var contextAndServletPath = jetspeed.url.path.DESKTOP ; var contextAndServletPathPos = docPath.indexOf( contextAndServletPath ) ; if ( contextAndServletPathPos != -1 && docPath.length > ( contextAndServletPathPos + contextAndServletPath.length ) ) { psmlPath = psmlPath + docPath.substring( contextAndServletPathPos + contextAndServletPath.length ) ; } this.psmlPath = psmlPath ; },
psmlPathはAJAX用のパス名をセットしている(たとえば、/palportal/ajaxかな)。docPathは現在のURLのパス名でそれに対して、jetspeed.url.path.DESKTOPは/palportal/desktopのようなものだから、それのindexOfを取ろうとしている。docPathが長ければ、psmlPathにそれをくっつけて、最終的なpsmlPathにしているみたいだな。
getPsmlUrlに戻って、jetspeed.url.basePortalUrl()をくっつけて、return して、psmlUrlの完成 🙂 ちなみにbasePortalUrl()は
jetspeed.url.basePortalUrl = function() { if ( ! jetspeed.url.path.initialized ) jetspeed.url.pathInitialize(); return jetspeed.url.path.SERVER; // return document.location.protocol + "//" + document.location.host ; };
と言う感じで、既にjetspeed.url.path.initializedはtrueだから、単純にjetspeed.url.path.SERVERを返してくる。この値は、http://localhost:8080/palportalみたいな感じかな。
そんで、jetspeed.url.retrieveContentへGo!
jetspeed.url.retrieveContent = function( requestUrl, contentListener, formObject, mimeType, domainModelObject, debugContentDumpIds ) { if ( ! requestUrl ) { dojo.raise( "retrieveContent null requestUrl is illegal" ); return; } if ( ! mimeType ) mimeType = "text/html"; dojo.io.bind({ url: requestUrl, formNode: formObject, mimetype: mimeType, load: function( type, data, evt ) { //dojo.debug( "loaded content for url: " + this.url ); //dojo.debug( "r e t r i e v e C o n t e n t . l o a d" ) ; //dojo.debug( " type:" ); //dojo.debugShallow( type ) ; //dojo.debug( " evt:" ); //dojo.debugShallow( evt ) ; if ( debugContentDumpIds ) { var dmId = ( ( domainModelObject && dojo.lang.isFunction( domainModelObject.getId ) ) ? domainModelObject.getId() : "" ); for ( var debugContentIndex = 0 ; debugContentIndex < debugContentDumpIds.length; debugContentIndex++ ) { if ( dmId.match( new RegExp( debugContentDumpIds[ debugContentIndex ] ) ) ) { if ( dojo.lang.isString( data ) ) dojo.debug( "retrieveContent [" + ( dmId ? dmId : requestUrl ) + "] content: " + data ); else { var textContent = dojo.dom.innerXML( data ); if ( ! textContent ) textContent = ( data != null ? "!= null (IE no XMLSerializer)" : "null" ); dojo.debug( "retrieveContent [" + ( dmId ? dmId : requestUrl ) + "] xml-content: " + textContent ); } } } } if ( contentListener && dojo.lang.isFunction( contentListener.notifySuccess ) ) { contentListener.notifySuccess( data, requestUrl, domainModelObject ) ; } else { dojo.debug( "retrieveContent [" + ( dmId ? dmId : requestUrl ) + "] no valid contentListener" ); } }, error: function( type, error ) { //dojo.debug( "r e t r i e v e C o n t e n t . e r r o r" ) ; //dojo.debug( " type:" ); //dojo.debugShallow( type ) ; //dojo.debug( " error:" ); //dojo.debugShallow( error ) ; if ( contentListener && dojo.lang.isFunction( contentListener.notifyFailure ) ) { contentListener.notifyFailure( type, error, requestUrl, domainModelObject ); } } }); };
これは、ちょっと長いな・・・。でも、見てみると、requestUrlとmimeTypeをチェックして、dojo.io.bindの呼び出しか。dojo.io.bind内のdebugContentDumpIdsのブロックはデバッグコードっぽいから、まぁ、ここでは読み飛ばすとして、アクセスに成功すると、contentListener.notifySuccessになるのか。これは、前述したnotifySuccessだね。引数は、アクセスして帰ってきたデータ、アクセスしたURL(PSML の URL)、domainModelObjectはjetspeed.url.retrieveContentを呼び出したjetspeed.om.Pageのインスタンスだね。というわけで、
notifySuccess: function( /* XMLDocument */ data, /* String */ requestUrl, /* Page */ page ) { page.getPortletsFromPSML( data ); jetspeed.loadPortletWindows(); },
に戻る(今回はアクセス失敗時の動作は追わないでおこ)。dojo.io.bindについては http://manual.dojotoolkit.org/io.html を参照。
では、まず、getPortletsFromPSMLを見てみましょ。
getPortletsFromPSML: function( psml ) { var pageElements = psml.getElementsByTagName( "page" ); if ( ! pageElements || pageElements.length > 1 ) dojo.raise( "unexpected zero or multiple <page> elements in psml" ); var pageElement = pageElements[0]; var children = pageElement.childNodes; var simpleValueLNames = new RegExp( "(name|path|title|short-title)" ); for ( var i = 0 ; i < children.length ; i++ ) { var child = children[i]; if ( child.nodeType != dojo.dom.ELEMENT_NODE ) continue; var childLName = child.nodeName; if ( childLName == "defaults" ) { this.layoutDecorator = child.getAttribute( "layout-decorator" ); this.portletDecorator = child.getAttribute( "portlet-decorator" ); } else if ( childLName && childLName.match( simpleValueLNames ) ) { this[ jetspeed.purifyIdentifier( childLName, "", "lo" ) ] = ( ( child && child.firstChild ) ? child.firstChild.nodeValue : null ); } } var lis = pageElement.getElementsByTagName( "fragment" ); for( var x=0; x < lis.length; x++ ) { var fragType = lis[x].getAttribute( "type" ); if ( fragType == "portlet" ) { var portletName = lis[x].getAttribute( "name" ); var portletEntityId = lis[x].getAttribute( "id" ); var portlet = new jetspeed.om.Portlet( portletName, portletEntityId ) ; var props = lis[x].getElementsByTagName( "property" ); for( var propsIdx=0; propsIdx < props.length; propsIdx++ ) { var propName = props[propsIdx].getAttribute( "name" ) ; var propValue = props[propsIdx].getAttribute( "value" ) ; portlet.putProperty( propName, propValue ) ; } portlet.initialize(); this.putPortlet( portlet ) ; } } },
dojo.io.bindで取った、data は、XMLDocumentになるのね。帰ってくるPSMLデータについては、 http://jetspeed-japan.sourceforge.jp/jetspeed-2-trans/ja/guides/guide-ajax-api.html を参照。
getPortletsFromPSMLを見て、現状の問題点に気づく。まず、title の meta タグが無視されているので、ページのタイトルが国際化されない。あとは、25%,75%の2列のレイアウトを選んでも、2列に表示されるが50%,50%くらいに表示されるな~っと思っていたら、ここで、無視していたからなのね。
内容についてみてみると、前半部分ではページに関する情報を取得しているだけだね(まぁ、あとで、meta タグの処理をいれんといかんけど)。後半部分では、各ポートレット情報の取得だな。portletName と portletEntityIdをセットして、jetspeed.om.Portlet を作成している。portletName は、たとえば、blog::BlogTitleViewPortletみたいな感じで、portletEntityId はフラグメントの ID だから、ポータル全体でユニークな値だね。次にjetspeed.om.Portletは
jetspeed.om.Portlet = function( /* String */ portletName, /* String */ portletEntityId, /* special */ alternateContentRetriever ) { this.name = portletName; this.entityId = portletEntityId; this.properties = {}; if ( alternateContentRetriever ) this.contentRetriever = alternateContentRetriever; };
だから、name, entityId と使う properties を作成して、alternateContentRetriever をセットするみたいだね。ここでは、alternateContentRetriever はなしと思われる。というわけで、getPortletsFromPSMLに戻る。
var props = lis[x].getElementsByTagName( "property" ); for( var propsIdx=0; propsIdx < props.length; propsIdx++ ) { var propName = props[propsIdx].getAttribute( "name" ) ; var propValue = props[propsIdx].getAttribute( "value" ) ; portlet.putProperty( propName, propValue ) ; }
っで、fragmentタグ内にあるpropertyタグを処理していくのだね。propertyタグのnameとvalue属性を順番にportletインスタンスに入れていっている。ちなみに、Portlet クラスの putProperty は
putProperty: function(name, value) { this.properties[name] = value; },
と言う感じで、properties に入れていっているだけだね。そして、getPortletsFromPSMLでは、次に、portlet.initialize();を呼び出しとるね。
initialize: function() { // must be called once init sensitive putProperty calls are complete if ( ! this.getProperty( jetspeed.id.PORTLET_PROP_WIDGET_ID ) ) { this.putProperty( jetspeed.id.PORTLET_PROP_WIDGET_ID, jetspeed.id.PORTLET_WINDOW_ID_PREFIX + this.entityId ); } if ( ! this.getProperty( jetspeed.id.PORTLET_PROP_CONTENT_RETRIEVER ) ) { this.putProperty( jetspeed.id.PORTLET_PROP_CONTENT_RETRIEVER, this.contentRetriever ); } var colSpan = this.getProperty( jetspeed.id.PORTLET_PROP_COLUMN_SPAN ); if ( colSpan != null ) { if ( ! jetspeed.prefs.windowTiling || ! dojo.lang.isNumber( colSpan ) || colSpan <= 1 ) { colSpan = null ; this.putProperty( jetspeed.id.PORTLET_PROP_COLUMN_SPAN, null ); } } var posStatic = this.getProperty( jetspeed.id.PORTLET_PROP_WINDOW_POSITION_STATIC ); if ( posStatic != null && posStatic && ( ! jetspeed.prefs.windowTiling || colSpan != null ) ) { posStatic = false ; this.putProperty( jetspeed.id.PORTLET_PROP_WINDOW_POSITION_STATIC, false ); } else if ( posStatic == null ) { if ( jetspeed.prefs.windowTiling ) this.putProperty( jetspeed.id.PORTLET_PROP_WINDOW_POSITION_STATIC, true ); else this.putProperty( jetspeed.id.PORTLET_PROP_WINDOW_POSITION_STATIC, false ); } var windowtitle = this.getProperty( jetspeed.id.PORTLET_PROP_WINDOW_TITLE ); if ( ! windowtitle && this.name ) { var re = (/^[^:]*:*/); windowtitle = this.name.replace( re, "" ); this.putProperty( jetspeed.id.PORTLET_PROP_WINDOW_TITLE, windowtitle ); } },
ここでは、いくつか定数があるな・・・。これを見る前にjetspeed.idを確認。
jetspeed.id = { DESKTOP: "jetspeedDesktop", TASKBAR: "jetspeedTaskbar", COLUMNS: "jetspeedColumns", SELECTOR: "jetspeedSelector", PORTLET_STYLE_CLASS: "portlet", PORTLET_WINDOW_STYLE_CLASS: "dojoFloatingPane", PORTLET_WINDOW_GHOST_STYLE_CLASS: "ghostPane", PORTLET_WINDOW_ID_PREFIX: "portletWindow_", PORTLET_PROP_WIDGET_ID: "widgetId", PORTLET_PROP_CONTENT_RETRIEVER: "contentRetriever", PORTLET_PROP_WINDOW_POSITION_STATIC: "windowPositionStatic", PORTLET_PROP_COLUMN_SPAN: "windowColumnSpan", PORTLET_PROP_WINDOW_THEME: "windowTheme", PORTLET_PROP_WINDOW_TITLE: "title", PORTLET_PROP_WINDOW_ICON: "windowIcon", PORTLET_PROP_WIDTH: "width", PORTLET_PROP_HEIGHT: "height", PORTLET_PROP_LEFT: "left", PORTLET_PROP_TOP: "top", PORTLET_PROP_COLUMN: "column", PORTLET_PROP_ROW: "row", PORTLET_PROP_EXCLUDE_PCONTENT: "excludePContent", PORTLET_PROP_WINDOW_STATE: "windowState", MENU_WIDGET_ID_PREFIX: "jetspeed-menu-", WINDOW_THEMES: [ "tigris", "blueocean" ] // temporary validation to avoid trying to use an undefined window theme };
initializeを見てみると、初期設定という感じだな。jetspeed.id.PORTLET_PROP_WIDGET_IDはIDだな。jetspeed.id.PORTLET_PROP_CONTENT_RETRIEVERは、デフォルトの値が入ると思われるから、
dojo.inherits( jetspeed.om.Portlet, jetspeed.om.Id); dojo.lang.extend( jetspeed.om.Portlet, { name: null, entityId: null, contentRetriever: new jetspeed.om.PortletContentRetriever(), windowFactory: null, lastSavedWindowState: null, JAVASCRIPT_ACTION_PREFIX: "javascript:doAction(", JAVASCRIPT_RENDER_PREFIX: "javascript:doRender(", JAVASCRIPT_ARG_QUOTE: "&" + "quot;", PORTLET_REQUEST_ACTION: "action", PORTLET_REQUEST_RENDER: "render",
にあるjetspeed.om.PortletContentRetriever()だな。コンストラクタでは、何もしていないみたい。
jetspeed.om.PortletContentRetriever = function() { };
と言う感じでjetspeed.id.PORTLET_PROP_CONTENT_RETRIEVERが決定。
jetspeed.id.PORTLET_PROP_COLUMN_SPANはfragementタグのpropertyタグに値があれば、それを使うけど、変な値があれば、nullにしているっぽい。そんで、jetspeed.id.PORTLET_PROP_WINDOW_POSITION_STATICは、場所を固定するかどうかだな。これもpsmlで設定できるのか・・・。jetspeed.id.PORTLET_PROP_WINDOW_TITLEはその名の通り、タイトルかね。これまた、psmlでできるのかね?でも、デフォルトだと、タイトルはポートレット名から取得している。ポートレットのインスタンスからはとらんのか?あとで、セットしているのかもしれんけど。
そして、getPortletsFromPSML間で戻って、portlet.initialize()の次は、putPortletは、
putPortlet: function( /* Portlet */ portlet ) { if (!portlet) return ; if (! this.portlets) this.portlets = [] ; this.portlets[ portlet.entityId ] = portlet ; },
portlets配列がなければ、作成して、ポートレットインスタンスを格納。
ふ~、これで、getPortletsFromPSMLが終わった・・・。つまりのところ、こいつで、ポートレット情報の取得と初期化だな。で、次は、notifySuccessまで戻って、jetspeed.loadPortletWindows();だそうな。
今日のところはこれまで・・・