<?xml version="1.0"?>

<st-source>
<!-- 
Name: SeasideForWebToolKit
Notice: All rights reserved
BundleName: SeasideForWebToolKit
Comment: Seaside for WebToolkit in VisualWorks

Ported from Seaside2.7b1-mb.23.mcz      1 October 2007 7:48:45 am

This parcel/bundle provides a VisualWorks port and basic integration of the "Seaside" continuation-based web application development framework. Note that Seaside and this port are not a supported part of Cincom Smalltalk. For more information on Seaside see http://www.seaside.st/

Seaside provides a layered set of abstractions over HTTP and HTML that let you build highly interactive web applications quickly, reusably and maintainably. Seaside includes:

    * Programmatic HTML generation. A lot of markup is boilerplate: the same patterns of lists, links, forms and tables show up on page after page. Seaside has a rich API for generating HTML that lets you abstract these patterns into convenient methods rather than pasting the same sequence of tags into templates every time.

    * Callback-based request handling. Why should you have to come up with a unique name for every link and form input on your page, only to extract them from the URL and request fields later? Seaside automates this process by letting you associate blocks, not names, with inputs and links, so you can think about objects and methods instead of ids and strings.

    * Embedded components. Stop thinking a whole page at a time; Seaside lets you build your UI as a tree of individual, stateful component objects, each encapsulating a small part of a page. Often, these can be used over and over again, within and between applications - nearly every application, for example, needs a way to present a batched list of search results, or a table with sortable columns, and Seaside includes components for these out the box.

    * Modal session management. What if you could express a complex, multi-page workflow in a single method? Unlike servlet models which require a separate handler for each page or request, Seaside models an entire user session as a continuous piece of code, with natural, linear control flow. In Seaside, components can call and return to each other like subroutines; string a few of those calls together in a method, just as if you were using console I/O or opening modal dialog boxes, and you have a workflow. And yes, the back button will still work.


Seaside also has good support for CSS and Javascript, excellent web-based development tools and debugging support, a rich configuration and preferences framework, and more.

Seaside is currently developed and supported by Avi Bryant, with the help of the Seaside community. Commercial support is available, and donations are welcome. Special thanks to Julian Fitzell, Adrian Lienhard, and Lukas Renggli for their contributions to Seaside, to http://www.netstyle.ch for sponsoring this site, and to Peter Macsadi for providing the Seaside logo.

This port relies on the VisualWorks Web Toolkit, and creates a SeasideServlet which handles all seaside requests and delegates them to the Seaside framework. The port was done primarily by Michel Bany, mbany@cincom.com.

Contents
	The integration pieces between Seaside and WebToolkit
	A few patches to Seaside
	A few patches to WebToolkit

Installation Notes

When loaded from a parcel:
	The parcel needs to be loaded after the SeasideBase parcel
	SeasideBase and WebToolkit are declared as a pre-requisites of the parcel
	and therefore will be automatically installed while loading this parcel, if necessary.

When loaded from the Cincom public Store repository:
	The bundle needs to be loaded after the Seaside-VW and Seaside bundles
	Seaside-VW, Seaside and WebToolkit are declared as a pre-requisites of the bundle
	and therefore will be automatically installed while loading this bundle, if necessary.

Some options are selectable via dialogs during the load of this bundle/parcel.
	1. Automatic setup of a sample HTTP server (WaveHTTPRequestBroker on port 8008)
	2. Automatic setup of a web site (seaside on namespace Seaside)

	If you elected to use the automatic setups, you should be able to surf to http://host:port/seaside/go/counter.

	If you elected not to use the automatic setups, you will have to do things manually:
	Click the 'globe' icon on the button bar then press 'Create Server'. 
	ServerType should be WaveHTTPRequestBroker,
	leave 'Use default resolvers' unchecked. Press 'Create and Start'.
	Surf to http://host:port enter a site name in the input field and click 'Create New Site'. Make
	sure you specify 'Seaside' as the namespace before pressing 'Submit'. You should be
	able to surf to http://host:port/site/servlet/SeasideServlet/counter.

Keywords: Web, Continuations, HTML, Internet, Squeak
DevelopmentPrerequisites: #(#(#any 'WebToolkit' '') #(#any 'SeasideBase' ''))
Parcel: #('SeasideForWebToolKit')
ParcelName: SeasideForWebToolKit
PreReadBlock: [Smalltalk at: #SeasideName put: 'SeasideForWebToolkit']
PrerequisiteParcels: #(#('WebToolkit' '') #('SeasideBase' ''))
Version: 2.7b1.23
Pre-Load Block: 
	[:package | Smalltalk at: #SeasideBundleLoader put: package]
Post-Load Block: 
	[:package |
	Smalltalk removeKey: #SeasideBundleLoader.
	Seaside.AAADocumentation new postInitialization ]
Pre-Read Block: 
	[Smalltalk at: #SeasideName put: 'SeasideForWebToolkit']
Date: 10:35:43 am November 13, 2007
 -->
<time-stamp>From VisualWorks®, 7.5 of April 16, 2007 on November 13, 2007 at 10:35:43 am</time-stamp>


<do-it>(Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned.  Choose terminate or close.']</do-it>

<class>
<name>WAServletAdmin</name>
<environment>Seaside</environment>
<super>Seaside.WAComponent</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Crossfade-Demo</category>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</class>

<comment>
<class-id>Seaside.WAServletAdmin</class-id>
<body>This Seaside component offers a web UI for enabling/disabling the streaming servlet, see class SeasideStreamingServlet</body>
</comment>

<class>
<name>AAADocumentation</name>
<environment>Seaside</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside/WebToolkit</category>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</class>

<comment>
<class-id>Seaside.AAADocumentation</class-id>
<body>This class provides some documentation and some examples for common tasks.
Some of these examples are used during the installation of Seaside.

</body>
</comment>

<class>
<name>SeasideResponseStreamer</name>
<environment>Seaside</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>buffer response streamResponse </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside/WebToolkit</category>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</class>

<comment>
<class-id>Seaside.SeasideResponseStreamer</class-id>
<body>This is a wrapper for web Response objects to provide the streaming api needed by the Streaming servlet

</body>
</comment>

<class>
<name>SeasideServlet</name>
<environment>Seaside</environment>
<super>VisualWave.HttpServlet</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside/WebToolkit</category>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</class>

<comment>
<class-id>Seaside.SeasideServlet</class-id>
<body>SeasideServlet is receiving all user input to be processed by Seaside.

There is normally one instance of this class, known as the Seaside default servlet.
The default servlet is the one that is created upon the very first access to Seaside.
The Seaside default servlet owns the default Seaside dispatcher.

Optionally additional servlet instances may be activated, one for each different access path as defined by WebToolkit sites.
Each of the additional servlets owns a copy of the default Seaside dispatcher.
There is a mechanism to ensure that the dispatcher copies are kept consistent.
This works only when the config app is used through the default servlet.

When the SeasideShortPath parcel is loaded from the Seaside Bonus Pack another servlet will created.
The Seaside alternate servlet owns an alternate Seaside dispatcher.
The alternate Seaside dispatcher is a copy of the default dispatcher.
</body>
</comment>

<class>
<name>SeasideStreamingServlet</name>
<environment>Seaside</environment>
<super>Seaside.SeasideServlet</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside/WebToolkit</category>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</class>

<comment>
<class-id>Seaside.SeasideStreamingServlet</class-id>
<body>The regular servlet (SeasideServlet) builds the entire HTML response object in memory
before writing the HTML out. With the streaming servlet (this class), the HTML is
instantly written out as it is generated by the Seaside components.

Restrictions :

There are a couple of restrictions with the streaming servlet. WATask and its subclasses, cookies
and authentication do not work. More generally anything that uses #returnResponse: does not work.
See more details on the Seaside mailing list, topic WAListener.</body>
</comment>

<shared-variable>
<name>DefaultDispatcherInitialized</name>
<environment>Seaside.SeasideServlet</environment>
<private>false</private>
<constant>false</constant>
<category>Globals</category>
<initializer>false</initializer>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</shared-variable>

<shared-variable>
<name>Dispatchers</name>
<environment>Seaside.SeasideServlet</environment>
<private>false</private>
<constant>false</constant>
<category>Globals</category>
<initializer>nil</initializer>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</shared-variable>

<shared-variable>
<name>DispatcherVersions</name>
<environment>Seaside.SeasideServlet</environment>
<private>false</private>
<constant>false</constant>
<category>Globals</category>
<initializer>nil</initializer>
<attributes>
<package>Seaside/WebToolkit</package>
</attributes>
</shared-variable>

<methods>
<class-id>Seaside.WAServletAdmin</class-id> <category>services</category>

<body package="Seaside/WebToolkit" selector="addStreamingServlet">addStreamingServlet
	self logicals at: self streamLogical
		put: 'servlet/' , self streamingServletName</body>

<body package="Seaside/WebToolkit" selector="basePath">basePath
	^WADispatcher default basePath.</body>

<body package="Seaside/WebToolkit" selector="canChangeStreamingServletStatus">canChangeStreamingServletStatus
	^self session currentRequest url beginsWith: self basePath</body>

<body package="Seaside/WebToolkit" selector="changeStreamingServletStatus">changeStreamingServletStatus
	^self hasStreamingServlet 
		ifTrue: [self removeStreamingServlet]
		ifFalse: [self addStreamingServlet]</body>

<body package="Seaside/WebToolkit" selector="hasStreamingServlet">hasStreamingServlet
	^self logicals includesKey: self streamLogical</body>

<body package="Seaside/WebToolkit" selector="logicals">logicals
	^self site urlManager catalog</body>

<body package="Seaside/WebToolkit" selector="regularServletUrl">regularServletUrl
	^self basePath , '/' , self class appName</body>

<body package="Seaside/WebToolkit" selector="removeStreamingServlet">removeStreamingServlet
	self logicals removeKey: self streamLogical</body>

<body package="Seaside/WebToolkit" selector="site">site
	^self session wtkSite</body>

<body package="Seaside/WebToolkit" selector="streamingServletActionText">streamingServletActionText
	^self hasStreamingServlet ifTrue: ['Remove'] ifFalse: ['Add']</body>

<body package="Seaside/WebToolkit" selector="streamingServletStatusText">streamingServletStatusText
	^self hasStreamingServlet ifTrue: ['Present'] ifFalse: ['Absent']</body>
</methods>

<methods>
<class-id>Seaside.WAServletAdmin</class-id> <category>constants</category>

<body package="Seaside/WebToolkit" selector="documentation">documentation
	^
'The regular servlet (SeasideServlet) builds the entire HTML response object in memory
before writing the HTML out. With the streaming servlet (SeasideStreamingServlet), the HTML is
instantly written out as it is generated by the Seaside components.'</body>

<body package="Seaside/WebToolkit" selector="restrictions">restrictions
	^
'See more details on the Seaside mailing list, topic WAListener or streaming server.'</body>

<body package="Seaside/WebToolkit" selector="streamingServletName">streamingServletName
	^'SeasideStreamingServlet'</body>

<body package="Seaside/WebToolkit" selector="streamLogical">streamLogical
	^'stream'</body>
</methods>

<methods>
<class-id>Seaside.WAServletAdmin</class-id> <category>rendering</category>

<body package="Seaside/WebToolkit" selector="renderContentOn:">renderContentOn: html 
	(html div)
		class: 'servletadmin';
		with: 
				[html heading: 'Streaming servlet setup' level: 3.
				self renderDialogOn: html.
				self renderDocumentationOn: html]</body>

<body package="Seaside/WebToolkit" selector="renderDialogOn:">renderDialogOn: html
	SeasidePlatformSupport isWebToolkit ifFalse:
		[^html strong: 'This feature is only available with WebToolkit'].
	html div: 
			[html strong: 'Secondary streaming servlet:'.
			html space: 3.
			html text: self streamingServletStatusText.
			html space: 3.
			self canChangeStreamingServletStatus 
				ifTrue: 
					[(html anchor)
						callback: [self changeStreamingServletStatus];
						text: self streamingServletActionText]
				ifFalse: 
					[html text: 'Cannot be changed,'.
					html space.
					(html anchor)
						url: self regularServletUrl;
						text: 'switch to regular servlet']]</body>

<body package="Seaside/WebToolkit" selector="renderDocumentationOn:">renderDocumentationOn: html 
	(html div)
		class: 'documentation';
		with: self documentation.
	(html div)
		class: 'documentation';
		with: self restrictions</body>

<body package="Seaside/WebToolkit" selector="rendererClass">rendererClass
	^WARenderCanvas</body>

<body package="Seaside/WebToolkit" selector="style">style
	^'
.servletadmin {
	font-family: Tahoma;
	font-size: 80%;
}
.documentation {
	width: 32em;
	margin: 2em;
	padding: 0.5em;
	background-color: #ddd;
	border: 1px dotted #444;
}
'</body>
</methods>

<methods>
<class-id>Seaside.WAServletAdmin class</class-id> <category>class initialization</category>

<body package="Seaside/WebToolkit" selector="appName">appName
	^'tools/servletadmin'</body>

<body package="Seaside/WebToolkit" selector="description">description
	^'Servlet administration'</body>

<body package="Seaside/WebToolkit" selector="initialize">initialize
	self registerAsApplication: self appName</body>
</methods>

<methods>
<class-id>Seaside.AAADocumentation</class-id> <category>samples</category>

<body package="Seaside/WebToolkit" selector="adjustTimeZone">adjustTimeZone
	"((( AAADocumentation new adjustTimeZone )))"
	"1. Establish VW Time Zone as Central Europe Time"
	Core.TimeZone setDefaultTimeZone: 
		(Core.TimeZone timeDifference: 1
			DST: 1 start: 2 end: 3
			from: 90 "on March 31"
			to: 304 "until October 31"
			startDay: #Sunday).
	"2. Propagate to Squeak, propagation required for cookie support"
	Squeak.DateAndTime.LocalTimeZone :=
		(Squeak.TimeZone 
			offset: (Core.TimeZone default secondsFromGMTAtLocal: Timestamp now)  seconds 
			name: 'Central Europe' 
			abbreviation: 'CET').
	Squeak.Week startDay: #Monday.</body>

<body package="Seaside/WebToolkit" selector="attachToDefaultWebSite">attachToDefaultWebSite
	"((( AAADocumentation new attachToDefaultWebSite )))"

	| siteFactory site configParameters logicals |
	siteFactory := #{VisualWave.WebSite} value.
	site := siteFactory siteNamed: 'default'.
	configParameters := site configParameters.
	configParameters at: 'environment' put: 'Seaside'.	" = Name space"
	configParameters at: 'home' put: self webSiteUrl.
	configParameters at: 'description'
		put: 'Default site with Seaside plugged-in'.
	logicals := Dictionary new.
	logicals at: self webSiteUrl put: self seasideServletName.
	site urlManager addInternalLogicalNames: logicals.
	^site</body>

<body package="Seaside/WebToolkit" selector="browseUrl">browseUrl
 	^self installationOptions browseUrl</body>

<body package="Seaside/WebToolkit" selector="createCGIServer">createCGIServer

	"((( AAADocumentation new createCGIServer )))"
	| aServer |
	self isVersion72OrMore
		ifTrue: 
			["Version 7.2"
			aServer := #{VisualWave.WaveIPRequestBroker} value newMinimal: self serverPortNumber]
		ifFalse:
			["Version 7.1 or older"
			aServer := #{VisualWave.TinyHttpServer} value newMinimal.	"To be fixed"
			aServer port: self serverPortNumber].
	aServer service trapErrors: false.
	aServer autoStart: true.
	aServer hostname: 'localhost'.
	aServer addDefaultResolvers.
	aServer startRequest.</body>

<body package="Seaside/WebToolkit" selector="createHTTPServer">createHTTPServer

	"((( AAADocumentation new createHTTPServer )))"
	| aServer |
	self isVersion72OrMore
		ifTrue: 
			["Version 7.2"
			aServer := #{VisualWave.WaveHTTPRequestBroker} value newMinimal: self serverPortNumber]
		ifFalse:
			["Version 7.1 or older"
			aServer := #{VisualWave.TinyHttpServer} value newMinimal.
			aServer port: self serverPortNumber].
	aServer service trapErrors: false.
	aServer autoStart: true.
	aServer hostname: 'localhost'.
	aServer addDefaultResolvers.
	aServer startRequest.</body>

<body package="Seaside/WebToolkit" selector="createWebSite">createWebSite
	"((( AAADocumentation new createWebSite )))"
	
	| siteFactory site default parameters configParameters logicals |
	SeasideServlet isVisualWorks75OrOlder ifTrue: [^self createWebSite75].
	siteFactory := #{VisualWave.WebSiteConfiguration} value configureServer
		globalConfiguration.
	default := siteFactory siteNamed: 'default'.
	site := #{VisualWave.WebSite} value new: self webSiteName.
	siteFactory
		removeSiteNamed: site name;
		addSite: site.
	parameters := Dictionary new.
	configParameters := Dictionary new.
	logicals := Dictionary new.
	parameters at: 'configuration' put: configParameters.
	parameters at: 'logical-names' put: logicals.
	configParameters
		at: 'directory'
		put: default directoryName.
	configParameters at: 'environment' put: 'Seaside'.	" = Name space"
	configParameters
		at: 'home'
		put: self seasideGo.
	configParameters at: 'description' put:
			'Copy of the default site adjusted for Seaside'.
	logicals
		at: self seasideGo
		put: self seasideServletName.
	site initializeFrom: parameters.
	^site</body>

<body package="Seaside/WebToolkit" selector="createWebSite75">createWebSite75
	"((( AAADocumentation new createWebSite )))"

	| siteFactory site default parameters configParameters logicals |
	siteFactory := #{VisualWave.WebSite} value.
	default := siteFactory siteNamed: 'default'.
	site := siteFactory
				removeSiteNamed: self webSiteName;
				newMinimal: self webSiteName.
	parameters := Dictionary new.
	configParameters := Dictionary new.
	logicals := Dictionary new.
	parameters at: 'configuration' put: configParameters.
	parameters at: 'logical-names' put: logicals.
	configParameters at: 'directory' put: default directoryName.
	configParameters at: 'environment' put: 'Seaside'.	" = Name space"
	configParameters at: 'home' put: self seasideGo.
	configParameters at: 'description'
		put: 'Copy of the default site adjusted for Seaside'.
	logicals at: self seasideGo put: self seasideServletName.
	site initializeFrom: parameters.
	siteFactory addSite: site.
	^site</body>

<body package="Seaside/WebToolkit" selector="detachFromDefaultWebSite">detachFromDefaultWebSite
	"((( AAADocumentation new detachFromDefaultWebSite )))"

	| siteFactory site configParameters |
	siteFactory := #{VisualWave.WebSite} value.
	site := siteFactory siteNamed: 'default'.
	configParameters := site configParameters.
	configParameters at: 'environment' put: 'Smalltalk'.	" = Name space"
	configParameters at: 'home' put: 'readme.html'.
	configParameters at: 'description'
		put: 'Default site without Seaside plugged-in'.
	site urlManager catalog removeKey: self webSiteUrl.
	^site</body>

<body package="Seaside/WebToolkit" selector="isVersion72OrMore">isVersion72OrMore
"
	Dirty, but it works
"
	^ #{VisualWave.WaveHTTPRequestBroker} ifDefinedDo: [:v | true ] elseDo: [false].</body>

<body package="Seaside/WebToolkit" selector="removeCGIServer">removeCGIServer
	"((( AAADocumentation new removeCGIServer )))"

	| aServer servers |
	servers := self isVersion72OrMore 
				ifTrue: 
					["Version 7.2"

					#{VisualWave.WaveIPRequestBroker} value allServers]
				ifFalse: 
					["Version 7.1 or older"

					#{VisualWave.TinyHttpServer} value allServers].	"To be fixed"
	aServer := servers detect: [:server | server port = self serverPortNumber] ifNone: [^self].
	aServer shutdown.
	aServer release.</body>

<body package="Seaside/WebToolkit" selector="removeHTTPServer">removeHTTPServer

	"((( AAADocumentation new removeHTTPServer )))"
	| aServer servers |
	servers := self isVersion72OrMore
		ifTrue: 
			["Version 7.2"
			#{VisualWave.WaveHTTPRequestBroker} value allServers]
		ifFalse:
			["Version 7.1 or older"
			#{VisualWave.TinyHttpServer} value allServers].
	aServer := servers detect: [:server | server port = self serverPortNumber] ifNone: [^self].
	aServer shutdown.
	aServer release.</body>

<body package="Seaside/WebToolkit" selector="removeWebSite">removeWebSite

	"((( AAADocumentation new removeWebSite )))"
	| siteFactory |
	siteFactory := #{VisualWave.WebSite} value.
	siteFactory removeSiteNamed: self webSiteName</body>

<body package="Seaside/WebToolkit" selector="seasideGo">seasideGo
	^ self installationOptions seasideGo</body>

<body package="Seaside/WebToolkit" selector="seasideServletName">seasideServletName
	^'servlet/SeasideServlet'</body>

<body package="Seaside/WebToolkit" selector="serverPortNumber">serverPortNumber
	^ self installationOptions portNumber</body>

<body package="Seaside/WebToolkit" selector="unhookSeasideFromDefaultWebSite">unhookSeasideFromDefaultWebSite

	"((( AAADocumentation new unhookSeasideFromDefaultWebSite )))"
	#{VisualWave.WebConfigurationManager} value clearAndReconfigure</body>

<body package="Seaside/WebToolkit" selector="webSiteName">webSiteName
	^ self installationOptions siteName</body>

<body package="Seaside/WebToolkit" selector="webSiteUrl">webSiteUrl
	^ self installationOptions siteUrl</body>
</methods>

<methods>
<class-id>Seaside.AAADocumentation</class-id> <category>private-install</category>

<body package="Seaside/WebToolkit" selector="basicInstall">basicInstall
	self isVersion72OrMore 
		ifFalse: 
			[Dialog 
				warn: 'Warning:\Since Seaside needs at least WebToolKit 7.2,\it will not run correctly on this system.' 
						withCRs].
	self installationOptions createServer 
		ifTrue: 
			[self installationOptions serverType == #http 
				ifTrue: [self createHTTPServer]
				ifFalse: [self createCGIServer]].
	self installationOptions createSeasideSite ifTrue: [self createWebSite].
	self installationOptions attachSeasideToDefaultSite ifTrue: [self attachToDefaultWebSite].
	self installationOptions setTimeZone ifTrue: [self adjustTimeZone].
	SeasideInstallerComplete open.</body>

<body package="Seaside/WebToolkit" selector="basicRemoval">basicRemoval
	self installationOptions createServer 
		ifTrue: 
			[self installationOptions serverType == #http 
				ifTrue: [self removeHTTPServer]
				ifFalse: [self removeCGIServer]].
	self installationOptions createSeasideSite ifTrue: [self removeWebSite].
	self installationOptions attachSeasideToDefaultSite ifTrue: [self detachFromDefaultWebSite].</body>

<body package="Seaside/WebToolkit" selector="checkObjectStudio">checkObjectStudio
	"Ugly, awful, naughty hack to deal with semantic differences of + 
	Alter the behavior only if ObjectStudio is there"

	'ObjectStudio.GlobalDictionary' asQualifiedReference 
		ifDefinedDo: [:obj | self modifySqueakBehavior]</body>

<body package="Seaside/WebToolkit" selector="installationOptions">installationOptions
	^Seaside.SeasideInstallationOptions default</body>

<body package="Seaside/WebToolkit" selector="isUpgrade">isUpgrade
	^Smalltalk at: #SeasideUpgrade ifAbsent: [false]</body>

<body package="Seaside/WebToolkit" selector="modifySqueakBehavior">modifySqueakBehavior

	String compile:
'+ arg
	"This code was dynamically created during installation of Seaside,
	because of the presence of ObjectStudio.
	See AAADocumentation &gt;&gt; checkObjectStudio"

	| env |
	env := thisContext sender mclass environment.
	env == Seaside ifTrue: [^arg adaptToString: self andSend: #+.].
	env == Squeak ifTrue: [^arg adaptToString: self andSend: #+.].
	^self , arg asString.
' classified: 'Seaside+ObjectStudio'.</body>

<body package="Seaside/WebToolkit" selector="postInitialization">postInitialization
	"Sent by the bundle post-load"

	"((( AAADocumentation new postInitialization )))"

	self isUpgrade 
		ifTrue: 
			[self report: 'Seaside upgraded to version ' 
						, SeasidePlatformSupport seasideVersion.
			^self report: ''].
	SeasidePlatformSupport isConfigured 
		ifFalse: [self basicInstall]
		ifTrue: [SeasidePlatformSupport install].
	self checkObjectStudio.
	self report: 'Seaside installation complete, version ' 
				, SeasidePlatformSupport seasideVersion.
	self report: ''</body>

<body package="Seaside/WebToolkit" selector="postUnload">postUnload
	"Done by the bundle post-unload"
	self report: 'Seaside removal complete, version ', SeasidePlatformSupport seasideVersion.</body>

<body package="Seaside/WebToolkit" selector="preInitialization">preInitialization
	"Done by the bundle pre-load"
	self report: 'Seaside installation started'.</body>

<body package="Seaside/WebToolkit" selector="preUnload">preUnload
	"Sent by the bundle pre-unload"

	"((( AAADocumentation new preUnload )))"

	self isUpgrade ifTrue: [^self].
	self report: ''.
	self report: 'Seaside removal started'.
	SeasidePlatformSupport isConfigured 
		ifFalse: [self basicRemoval]
		ifTrue: [SeasidePlatformSupport removeSeaside].
	SeasideServlet terminate</body>

<body package="Seaside/WebToolkit" selector="report:">report: aString 
	Transcript
		cr;
		show: aString</body>
</methods>

<methods>
<class-id>Seaside.AAADocumentation</class-id> <category>documentation</category>

<body package="Seaside/WebToolkit" selector="settingThingsUp">settingThingsUp
"
	If you loaded the Seaside-WebToolKit bundle succesfully, you should have WebToolkit loaded as well.
	
	If you answered 'yes' to the question 'Do you want to set up a sample HTTP server ?', you should have
	a Server running already.

	If you answered 'no',  you may need to create and start a server. Here is how you should proceed :
		- Open the Server Console window by pressing the 'globe' icon in the Launcher button bar
		- Press 'Create Server'
		- Choose 'Smalltalk HTTP Server (Tiny HTTP)' for the server type and accept the default port
		- Press 'Create and Start'
		- For details on creating a server, see the VisualWorks documentation (Web Server Configuration Guide)

	If you answered 'yes' to the question 'Do you want to set up a sample Seaside site ?', you should have
	a Seaside site named 'seaside' defined already.

	If you answered 'no',  you may need to define a site. Here is how you should proceed :
		- From your web browser surf to the WebToolkit configuration site, usually at http://localhost:8008
		- Click on the 'Manage Sites' link
		- Type in the name of your site (for instance 'seaside') into the entry field
		- Click on the 'Create New Site' link
		- Type in 'Seaside' (case sensitive) into the 'Namespace' entry field
		- Press 'Submit'
		- For details on creating a site, see the VisualWorks documentation (Web Application Developer's Guide)

	This version of Seaside runs as a servlet, all Seaside applications reside under
	 		&lt;your server&gt;/&lt;your site&gt;/servlet/SeasideServlet.
	For instance
	 		http://localhost:8008/seaside/servlet/SeasideServlet/counter
	 		http://localhost:8008/seaside/servlet/SeasideServlet/store
	 		http://localhost:8008/seaside/servlet/SeasideServlet/config

	If you answered 'yes' to the question 'Do you want to set up a sample Seaside site ?', the defined Seaside
	site named 'seaside' includes the definition of a logical name and you will be able to use the following
	shorter URL's to reach the Seaside applications
	 		http://localhost:8008/seaside/go/counter
	 		http://localhost:8008/seaside/go/store
	 		http://localhost:8008/seaside/go/config

	Then proceed with the Seaside tutorial at the link below
		http://www.cincomsmalltalk.com/CincomSmalltalkWiki/Seaside+Tutorial
"</body>
</methods>

<methods>
<class-id>Seaside.SeasideResponseStreamer</class-id> <category>accessing</category>

<body package="Seaside/WebToolkit" selector="response">response
	^response</body>

<body package="Seaside/WebToolkit" selector="response:">response: anObject 
	response := anObject.
	response buffer: false.
	buffer := String new writeStream</body>

<body package="Seaside/WebToolkit" selector="responseStream">responseStream
	^self response responseStream</body>
</methods>

<methods>
<class-id>Seaside.SeasideResponseStreamer</class-id> <category>streaming</category>

<body package="Seaside/WebToolkit" selector="crlf">crlf
	self nextPut: Character cr.
	self nextPut: Character lf</body>

<body package="Seaside/WebToolkit" selector="destroy">destroy
	"Not sure this is right"
	self responseStream commit.</body>

<body package="Seaside/WebToolkit" selector="nextPut:">nextPut: aChar 
	self headersWritten 
		ifTrue: 
			[self tracing ifTrue: [Transcript nextPut: aChar].
			self responseStream nextPut: aChar]
		ifFalse: [buffer nextPut: aChar]</body>

<body package="Seaside/WebToolkit" selector="nextPutAll:">nextPutAll: aCollection
	(streamResponse isNil and: [aCollection beginsWith: WAResponse defaultHttpVersion]) 
		ifTrue: [streamResponse := self getStreamResponseFromContext: thisContext].
	self headersWritten 
		ifTrue: 
			[self tracing ifTrue: [Transcript nextPutAll: aCollection].
			self responseStream nextPutAll: aCollection]
		ifFalse: [buffer nextPutAll: aCollection]</body>

<body package="Seaside/WebToolkit" selector="space">space
	self nextPut: Character space.</body>
</methods>

<methods>
<class-id>Seaside.SeasideResponseStreamer</class-id> <category>testing</category>

<body package="Seaside/WebToolkit" selector="headersWritten">headersWritten
	(self response headersWritten not and: 
			[streamResponse class == WAStreamResponse 
				and: [streamResponse hasWrittenHeaders]]) 
		ifTrue: [response ensureHeadersWritten].
	^self response headersWritten</body>

<body package="Seaside/WebToolkit" selector="tracing">tracing
	^false</body>
</methods>

<methods>
<class-id>Seaside.SeasideResponseStreamer</class-id> <category>private hacks</category>

<body package="Seaside/WebToolkit" selector="getStreamResponseFromContext:">getStreamResponseFromContext: aContext 
	| seasideStreamer |
	seasideStreamer := aContext sender receiver.
	^(seasideStreamer isKindOf: WAResponse) 
		ifTrue: [seasideStreamer]
		ifFalse: 
			[self error: 'Debug me'.
			nil]</body>
</methods>

<methods>
<class-id>Seaside.SeasideResponseStreamer</class-id> <category>pushing</category>

<body package="Seaside/WebToolkit" selector="pushData:">pushData: aCollection 
	"Send aStringOrByteArray to the remote host trough the given socket. 
	Push the data immediately and ignore all kinds of errors. 
	Return true if all the data could be sent successfully, 
	n case of problems return false and destroy the socket."

	self tracing 
		ifTrue: 
			[Transcript
				cr;
				nextPutAll: 'comet=&gt;';
				nextPutAll: aCollection].
	
	[(self responseStream)
		nextPutAll: aCollection;
		flush] on: Error
			do: 
				[:ex | 
				Transcript
					cr;
					print: ex.
				^false].
	^true</body>

<body package="Seaside/WebToolkit" selector="socket">socket
	^self</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>accessing</category>

<body package="Seaside/WebToolkit" selector="dispatchers">dispatchers
	^self class dispatchers</body>

<body package="Seaside/WebToolkit" selector="dispatcherVersions">dispatcherVersions
	^self class dispatcherVersions</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>private-dispatchers</category>

<body package="Seaside/WebToolkit" selector="defaultDispatcher">defaultDispatcher
	^self waDispatcherClass default</body>

<body package="Seaside/WebToolkit" selector="dispatcherForRequest:">dispatcherForRequest: aWaveRequest 
	| basePath dispatcher reason |
	self initializeDefaultDispatcherFrom: aWaveRequest.
	"
	There are multiple dispatchers possible, because we may want to reach
	the Seaside servlet through multiple channels : TinyHTTPServer, CGI, etc.
	with different path structures. For instance :
		http://host:port/seaside/go/counter
		http://host/cgi-bin/cgi2bin.exe/seaside/go/counter
		http://host/cgi-bin/visualworks.pl/seaside/go/counter
		http://host:port/counter
		etc.
	We dynamically set up a new dispatcher for each possible channel
	as needed.
	"
	basePath := self urlPrefixFromRequest: aWaveRequest.
	dispatcher := self dispatchers at: basePath ifAbsent: [nil].
	"
	In a pure development system, the default dispatcher is frequently
	modified with new applications. Therefore we need to refresh the
	dynamic dispatchers so that they remain up-to-date.
	In a production system this will normally not happen.
	"
	dispatcher isNil 
		ifTrue: [reason := 'New']
		ifFalse: 
			[dispatcher := self verifiedDispatcher: dispatcher.
			dispatcher isNil ifTrue: [reason := 'Updated']].
	dispatcher isNil 
		ifTrue: 
			[dispatcher := self defaultDispatcher copy.
			self installDispatcher: dispatcher atBasePath: basePath.
			self 
				log: reason , ' Seaside dispatcher activated for path ' 
						, dispatcher basePath , '/'].
	^dispatcher</body>

<body package="Seaside/WebToolkit" selector="initializeDefaultDispatcherFrom:">initializeDefaultDispatcherFrom: aWaveRequest 
	"The first servlet instantiated comes here, initializes the default dispatcher"

	| defaultDispatcher basePath |
	DefaultDispatcherInitialized == true 
		ifFalse: 
			[defaultDispatcher := self defaultDispatcher.
			basePath := self urlPrefixFromRequest: aWaveRequest.
			self installDispatcher: defaultDispatcher atBasePath: basePath.
			self 
				log: 'Seaside default dispatcher activated for path ' 
						, defaultDispatcher basePath , '/'].
	DefaultDispatcherInitialized := true</body>

<body package="Seaside/WebToolkit" selector="installDispatcher:atBasePath:">installDispatcher: aDispatcher atBasePath: basePath 
	aDispatcher setName: basePath allButFirst.
	self dispatchers at: basePath put: aDispatcher.
	aDispatcher == self defaultDispatcher ifTrue: [^self].
	"
	We remember what the version of the default  dispatcher was
	at the point in time when a dynamic dispatcher is installed.
	The dynamic dispatcher is allowed to be modified from then on,
	i.e. its version may change. However as soon as the version of
	the default dispatcher changes, the dynamic dispatcher becomes
	invalid, will be thrown away and refreshed with a new copy
	"
	self dispatcherVersions at: aDispatcher put: aDispatcher version.</body>

<body package="Seaside/WebToolkit" selector="optionKeepDispatchersSimilar">optionKeepDispatchersSimilar
	"
	Dispatchers are initialized with a copy of the default dispatcher
		WADispatcher default.
	As the default dispatcher changes, it may be necessary to keep
	all dispatchers consistent with the default dispatcher.

	Answer true if all dispatchers should be kept consistent with
		the default dispatcher.
	Answer false if all dispatchers should be allowed to have their
		own life.

"
	^true</body>

<body package="Seaside/WebToolkit" selector="verifiedDispatcher:">verifiedDispatcher: aDispatcher 
	"
	In a pure development system, the default dispatcher is frequently
	modified with new applications. Therefore we need to check the
	dynamic dispatchers so that they remain up-to-date.
	When a dynamic dispatcher is out of date, answer nil.
	This behavior can be disabled (#optionKeepDispatchersSimilar)
	"

	self optionKeepDispatchersSimilar ifFalse: [^aDispatcher].
	aDispatcher isNil ifTrue: [^nil].
	(self versionOf: aDispatcher) = self defaultDispatcher version ifTrue: [^aDispatcher].
	self dispatcherVersions removeKey: aDispatcher.
	^nil</body>

<body package="Seaside/WebToolkit" selector="versionOf:">versionOf: aDispatcher
	"
	Answer with the version of the default dispatcher as it was
	when the dynamic dispatcher was installed.
	The dynamic dispatcher itself may have a different version
	it has been modified. However if the default dispatcher has been
	changed, the dynamic dispatcher becomes invalid
	"
	aDispatcher == self defaultDispatcher ifTrue: [^aDispatcher version].
	^self dispatcherVersions at: aDispatcher</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>private</category>

<body package="Seaside/WebToolkit" selector="assertIsOrderedCollectionOfStrings:">assertIsOrderedCollectionOfStrings: field 
	((field class == OrderedCollection and: [field size &gt; 0]) 
		and: [field first isString]) 
			ifFalse: [self error: 'Should not occur, ' , field printString]</body>

<body package="Seaside/WebToolkit" selector="convertRequest:">convertRequest: aWaveRequest

	"See WAKom &gt;&gt; convertRequest: in Squeak"
	| waRequest |
	waRequest := self waRequestClass 
			method: aWaveRequest method
			url: (aWaveRequest requestURI path readStream upTo: $?)
			headers: (self unwrapHeaders: aWaveRequest)
			fields: (self unwrapFields: aWaveRequest)
			cookies: (self unwrapCookies: aWaveRequest).
	aWaveRequest method = 'PUT' ifTrue:
		[self error: 'Unsupported HTTP method PUT'.
		waRequest fields at: 'PUTData' put: String new].
	^ waRequest.</body>

<body package="Seaside/WebToolkit" selector="convertResponse:to:">convertResponse: waResponse to: aResponse

	"See WAKom &gt;&gt; convertResponse: in Squeak"
	| tokens |
	aResponse status: waResponse status asNumber.
	waResponse headers associationsDo:
		[:eachHeader |
		"Seaside provides an 'expires' HTTP header to control the brower's cache
		The response object has instance variables for that purpose"
		eachHeader key asLowercase = 'expires' ifTrue:
			[aResponse expires: eachHeader value.
			aResponse cacheControl: 'Public']
		ifFalse:
			[aResponse addHeader: eachHeader key value: eachHeader value]].
	waResponse cookies do:
		[:eachCookie | | cookie |
		cookie := VisualWave.HTTPCookie named: eachCookie key value: eachCookie value.
		"Seaside provides UTC in cookies, while WebToolkit expects local time"
		[cookie expires: eachCookie expiry asLocal ] on: Error do: [:ex | ].
		cookie path: eachCookie path.
		aResponse addCookie: cookie].
	"We have to parse the content-type from Seaside to populate
	the response object instance variables"
	tokens := waResponse contentType findTokens: ';'.
	tokens size &gt;= 1 ifTrue:
		[aResponse	contentType: tokens first trimBlanks].
	tokens size &gt;= 2 ifTrue: 
		[aResponse charSet: (tokens second findTokens: '=') second trimBlanks].
	aResponse setCharSetAsNeeded.
	aResponse write: waResponse contents contents.</body>

<body package="Seaside/WebToolkit" selector="internalServerMalfunction:response:">internalServerMalfunction: anException response: aResponse

	aResponse write:
		'&lt;html&gt;&lt;head&gt;&lt;title&gt;Internal server malfunction&lt;/title&gt;&lt;/head&gt;',
		'&lt;body&gt;&lt;h1&gt;Internal server malfunction&lt;/h1&gt;&lt;h2 style="color: blue"&gt;'
		, anException errorString ,
		'&lt;/h2&gt;&lt;div style="font-family: sans-serif"&gt;You should contact the system administrator&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;'</body>

<body package="Seaside/WebToolkit" selector="log:">log: aString
	Transcript cr; show: aString; flush.
	super log: aString.</body>

<body package="Seaside/WebToolkit" selector="unwrapCookies:">unwrapCookies: aWaveRequest

	| cookies |
	"Seaside expects a dictionary where values are strings
	WebToolkit provides a dictionary where values are collections of strings
	We will supply Seaside with the first strings of each collection"	
	cookies := Dictionary new.
	aWaveRequest cookies keysAndValuesDo:
		[ :k :field |
		cookies at: k put: field first].	
	^ cookies</body>

<body package="Seaside/WebToolkit" selector="unwrapFields:">unwrapFields: aWaveRequest 
	"Seaside expects a dictionary where values are either strings or WAFile objects for uploaded MIME entities
	WebToolkit provides a dictionary where values are either collections of strings or MimeEntity instances"

	| fields waFields |
	waFields := Dictionary new.
	fields := aWaveRequest method = 'POST' 
				ifTrue: [aWaveRequest form]
				ifFalse: [aWaveRequest query].
	fields keysAndValuesDo: 
			[:k :parts | 
			| value |
			parts do: 
					[:field | 
					field class == Net.MimeEntity 
						ifTrue: [value := self unwrapMimeEntityFrom: field]
						ifFalse: 
							[
							aWaveRequest method = 'GET' 
								ifTrue: [self unwrapUrlFragmentFrom: field into: waFields].
							value := self unwrapValueFromField: field request: aWaveRequest].
					value isNil 
						ifTrue: [self error: 'Unsupported post/form data: ' , field printString].
					(waFields at: k ifAbsentPut: OrderedCollection new) add: value].
			(waFields at: k) size = 1 ifTrue: [waFields at: k put: (waFields at: k) first].
		].
	^waFields</body>

<body package="Seaside/WebToolkit" selector="unwrapHeaders:">unwrapHeaders: aWaveRequest 
	"Seaside expects a dictionary where values are strings
	WebToolkit provides exactly that
	We supply a copy of the WebToolkit headers"
	| headers httpRequest |
	headers := aWaveRequest headers copy.
	httpRequest := aWaveRequest webRequest httpRequest.
	"We need to add the entity headers if any
	These headers may come from Ajax processing"
	httpRequest isNil
		ifFalse:
			[headers addAll: httpRequest entityHeaders].
	"When running behind Apache, we need to transform
	the environment headers"
	httpRequest isNil
		ifTrue:
			[(headers keys select: [:each | each beginsWith: 'HTTP_'])
				do:
					[:each |
					| newKey |
					newKey := (each copyFrom: 6 to: each size) asLowercase.
					headers at: newKey put: (headers at: each).
					headers removeKey: each]].
	^headers</body>

<body package="Seaside/WebToolkit" selector="unwrapMimeEntityFrom:">unwrapMimeEntityFrom: field 
	| tempStream |
	^field fileName isNil 
		ifTrue: [field value value]
		ifFalse: 
			[tempStream := field seasideContentsStream.
			field isBinary ifTrue: [tempStream binary].
			(self waFileClass new)
				fileName: field fileName;
				contents: tempStream contents;
				contentType: field contentType]</body>

<body package="Seaside/WebToolkit" selector="unwrapUrlFragmentFrom:into:">unwrapUrlFragmentFrom: field into: waFields 
	| tempStream |
	(field includes: self urlFragmentPrefix) 
		ifTrue: 
			[tempStream := field readStream.
			tempStream upTo: self urlFragmentPrefix.
			waFields at: #url_fragment put: tempStream upToEnd]</body>

<body package="Seaside/WebToolkit" selector="unwrapValueFromField:request:">unwrapValueFromField: field request: aWaveRequest 
	| tempStream |
	tempStream := field readStream.
	^aWaveRequest method = 'GET' 
		ifTrue: [tempStream upTo: self urlFragmentPrefix]
		ifFalse: [tempStream upToEnd]</body>

<body package="Seaside/WebToolkit" selector="urlFragmentPrefix">urlFragmentPrefix
	^$#</body>

<body package="Seaside/WebToolkit" selector="waFileClass">waFileClass

	^#'Seaside.WAFile' asQualifiedReference value</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>executing</category>

<body package="Seaside/WebToolkit" selector="doGet:response:">doGet: aRequest response: aResponse 

	self handle: aRequest response: aResponse</body>

<body package="Seaside/WebToolkit" selector="doPost:response:">doPost: aRequest response: aResponse

	self handle: aRequest response: aResponse</body>

<body package="Seaside/WebToolkit" selector="handle:response:">handle: aRequest response: aResponse 

	| waRequest waResponse |
	[waRequest := self convertRequest: aRequest]
		on: Error do:
		[:e | ^self internalServerMalfunction: e response: aResponse].
	"Establish relationships between objects
	Seaside Request -&gt; Wave Request -&gt; Wave Session -&gt; Wave Response"
	waRequest nativeRequest: aRequest.
	aRequest session at: 'WaveResponse' put: aResponse.
	"Handle the request and get the response"
	waResponse := self handleRequest: waRequest.
	self convertResponse: waResponse to: aResponse.
	"Break relationships between objects"
	aRequest session removeKey: 'WaveResponse' ifAbsent: []</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>private-dispatching</category>

<body package="Seaside/WebToolkit" selector="handlerForRequest:">handlerForRequest: waRequest

	| waveRequest |
	waveRequest := waRequest nativeRequest.
	^ (self dispatcherForRequest: waveRequest) handlerForRequest: waRequest.</body>

<body package="Seaside/WebToolkit" selector="urlPrefixFromRequest:">urlPrefixFromRequest: aRequest 
	| basePath path prefix |
	basePath := aRequest requestURI path.
	path := aRequest webRequest unconsumedPath asOrderedCollection.
	prefix := path isEmpty 
				ifTrue: [basePath]
				ifFalse: 
					["Get rid of all leading '/'"

					[path first isEmpty] whileTrue: [path removeFirst].
					basePath readStream upToAll: path first].
	"Get rid of all trailing '/'"
	[prefix notEmpty and: [prefix last = $/]] 
		whileTrue: [prefix := prefix allButLast].
	^prefix</body>

<body package="Seaside/WebToolkit" selector="waDispatcherClass">waDispatcherClass
	"Avoid circular dependency.
	For convenience, this package is part of the Seaside-VW bundle which needs to be loaded before the rest of the Seaside
	packages"

	^#'Seaside.WADispatcher' asQualifiedReference value</body>

<body package="Seaside/WebToolkit" selector="waRequestClass">waRequestClass
	"Avoid circular dependency.
	For convenience, this package is part of the Seaside-VW bundle which needs to be loaded before the rest of the Seaside
	packages"

	^#'Seaside.WARequest' asQualifiedReference value</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>initialization</category>

<body package="Seaside/WebToolkit" selector="init:">init: config
	"Use this method rather than #initialize that gets executed twice"
	super init: config.
	self showReadyness</body>

<body package="Seaside/WebToolkit" selector="showReadyness">showReadyness
	self log: self printString , ' ready'.</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>testing</category>

<body package="Seaside/WebToolkit" selector="isAlternateServlet">isAlternateServlet
	"This method gets subclassed when the SeasideShortPath
	parcel from the BonusPack is loaded"
	^ false</body>

<body package="Seaside/WebToolkit" selector="isDefaultServlet">isDefaultServlet
	"The default servlet is the one that gets instantiated for a
	request with any of the regular Seaside URLs.
	When the SeasideShortPath parcel from the BonusPack
	is loaded (and only then), the alternate servlet is the one that
	gets defaulted for a request with an unknown URL"
	^ self isAlternateServlet not</body>

<body package="Seaside/WebToolkit" selector="isStreaming">isStreaming
	^false</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>printing</category>

<body package="Seaside/WebToolkit" selector="printString">printString
	"This method gets subclassed when the SeasideShortPath
	parcel from the BonusPack is loaded"
	^'Seaside default servlet'.</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet</class-id> <category>private-handling</category>

<body package="Seaside/WebToolkit" selector="handleRequest:">handleRequest: waRequest 
	| waResponse |
	waResponse := [(self handlerForRequest: waRequest) handleRequest: waRequest] 
				on: Error
				do: [:ex | WAResponse internalError: ex].
	"This happens sometimes, no response object"
	waResponse isNil 
		ifTrue: 
			[waResponse := WAResponse 
						internalError: 'Response is missing.&lt;br /&gt;&lt;br /&gt;' 
								, '&lt;span style="font-size: 60%"&gt;Most probable cause : you probably terminated a debugger.&lt;/span&gt;'].
	^waResponse</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet class</class-id> <category>accessing</category>

<body package="Seaside/WebToolkit" selector="configurationManager">configurationManager
	^self isVisualWorks75OrOlder 
		ifTrue: [#{VisualWave.WebConfigurationManager} value]
		ifFalse: [#{VisualWave.WebSiteConfiguration} value]</body>

<body package="Seaside/WebToolkit" selector="dispatchers">dispatchers
	Dispatchers isNil ifTrue: [ self dispatchers: Dictionary new].
	^Dispatchers</body>

<body package="Seaside/WebToolkit" selector="dispatchers:">dispatchers: aCollection
	
self == SeasideServlet
		ifFalse: [^SeasideServlet dispatchers: aCollection].
	aCollection isNil
		ifFalse:
			["This servlet will need some clean-up when VW / Web Toolkit is started/restarted"
			ObjectMemory addDependent: self.
			self configurationManager when: #finishedServerConfiguration send:
					#reset to: self]
		ifTrue:
			["Clean-up no longer needed"
			DispatcherVersions := nil.
			ObjectMemory removeDependent: self.
			self configurationManager removeActionsWithReceiver: self forEvent:
					#finishedServerConfiguration].
	Dispatchers := aCollection</body>

<body package="Seaside/WebToolkit" selector="dispatcherVersions">dispatcherVersions
	^DispatcherVersions ifNil: [ DispatcherVersions := Dictionary new].</body>

<body package="Seaside/WebToolkit" selector="isVisualWorks75OrOlder">isVisualWorks75OrOlder
	^#{VisualWave.WebConfigurationManager} isDefined</body>
</methods>

<methods>
<class-id>Seaside.SeasideServlet class</class-id> <category>initialization-release</category>

<body package="Seaside/WebToolkit" selector="reset">reset
	"Entered when Web Toolkit is started/restarted and when image is started"

	DefaultDispatcherInitialized ifFalse: [^nil].
	self dispatchers: nil.
	DefaultDispatcherInitialized := false.
	Transcript
		cr;
		show: 'Seaside dispatchers re-initializing ';
		show: '(version ' , SeasidePlatformSupport seasideVersion , ')'</body>

<body package="Seaside/WebToolkit" selector="terminate">terminate
	"Entered when bundle unloaded"
	self dispatchers: nil.</body>

<body package="Seaside/WebToolkit" selector="update:">update: aSymbol
	"Here when VisualWorks starts up"
	aSymbol == #returnFromSnapshot ifTrue: [self reset].</body>
</methods>

<methods>
<class-id>Seaside.SeasideStreamingServlet</class-id> <category>testing</category>

<body package="Seaside/WebToolkit" selector="isStreaming">isStreaming
	^true</body>
</methods>

<methods>
<class-id>Seaside.SeasideStreamingServlet</class-id> <category>executing</category>

<body package="Seaside/WebToolkit" selector="handle:response:">handle: aRequest response: aResponse 
	| waRequest waResponse |
	[waRequest := self convertRequest: aRequest] on: Error
		do: [:e | ^self internalServerMalfunction: e response: aResponse].
	"Establish relationships between objects
	Seaside Request -&gt; Wave Request -&gt; Wave Session -&gt; Wave Response"
	waRequest nativeRequest: aRequest.
	aRequest session at: 'WaveResponse' put: aResponse.
	"Handle the request and get the response"
	waRequest responseStream: aResponse responseStreamer.
	waResponse := self handleRequest: waRequest.
	(waResponse notNil 
		and: [waResponse class == WAResponse or: [waResponse hasWrittenHeaders not]]) 
			ifTrue: [self convertResponse: waResponse to: aResponse].
	"Break relationships between objects"
	aRequest session removeKey: 'WaveResponse' ifAbsent: []</body>
</methods>

<methods>
<class-id>VisualWave.Request</class-id> <category>api</category>

<body package="WebToolkit/Patches" selector="headers">headers

	^ webRequest httpHeaders</body>

<body package="WebToolkit/Patches" selector="isHttp">isHttp

	^ webRequest isHttp</body>

<body package="WebToolkit/Patches" selector="remoteAddress">remoteAddress

	^self remoteAddr</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>content-disposition</category>

<body package="WebToolkit/Patches" selector="fileName">fileName

	"Seaside : this method override is only needed with VW before 7.3.1"

	| headerField f |
	headerField := self 
					fieldAt: 'content-disposition' 
					ifAbsent: [self fieldAt: 'content-type' ifAbsent:[^nil]].
	^((f := headerField fileName) isNil or: [ f isEmpty])
		ifTrue: [ nil ]
		ifFalse: [ f ]</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>seaside</category>

<body package="WebToolkit/Patches" selector="seasideContentsStream">seasideContentsStream
	"This method is needed for file upload"
	^ SystemUtils.SystemVersionName &gt; '7.2.1'
		ifTrue:
			["VisualWorks 7.3 and up"
			self isBinary
				ifFalse:
					[self body valueAsStream]
				ifTrue:
					[self body byteValueAsStream]]
		ifFalse:
			["VisualWorks before 7.3"
			self body valueAsStream]</body>
</methods>

<methods>
<class-id>VisualWave.IM_ApplicationWWWFormUrlEncoded</class-id> <category>reading</category>

<body package="WebToolkit/Patches" selector="decodeDataOver:separator:">decodeDataOver: aStream separator: sepChar
	"Decode name values pairs from aStream assuming the data separator is sepChar."

	"Seaside : this method override is only needed with VW before 7.3.1"

	| encoder |
	encoder := URLEncoder new.
	encoder setEncoder: self charsetEncoder.
	[aStream atEnd]
		whileFalse: 
			[| nameAndValue name value association |
			nameAndValue := (encoder decode: (aStream upTo: sepChar)) readStream.
			name := encoder decode: (nameAndValue upTo: $=).
			value := nameAndValue upToEnd.
			aStream peekFor: Character space.	"eat space if found (e.g., in cookies)"
			association := name-&gt;value. 
			orderedNameValues add: association. 
			(nameValueDictionary at: name ifAbsent: [nameValueDictionary at: name put: OrderedCollection new])
				add: value].</body>
</methods>

<methods>
<class-id>VisualWave.IM_MultipartFormData</class-id> <category>reading</category>

<body package="WebToolkit/Patches" selector="postProcess:">postProcess: request 
	"Build the results into a dictionary of parts, indexed by name."

	| tempParts |
	tempParts := request value parts copy.
	parts := Dictionary new.
	tempParts do: 
			[:each | 
			(parts at: ((each fieldAt: 'content-disposition') parameterAt: 'name')
				ifAbsentPut: OrderedCollection new) add: each]</body>
</methods>

<methods>
<class-id>VisualWave.WebRequest</class-id> <category>As yet unclassified</category>

<body package="WebToolkit/Patches" selector="httpHeaders">httpHeaders
	"This patch provides support for Seaside running behind Apache"
	^ self isHttp
		ifTrue: 
			[Dictionary withAll: httpRequest httpHeaders]
		ifFalse:
			["Should select only the HTTP_ headers ?"
			serverEnvironment copy]</body>

<body package="WebToolkit/Patches" selector="isHttp">isHttp
	^ httpRequest notNil</body>
</methods>

<methods>
<class-id>VisualWave.WebRequest</class-id> <category>accessing-common environment variables</category>

<body package="WebToolkit/Patches" selector="REQUEST_URI">REQUEST_URI

	^self envAt: 'REQUEST_URI'</body>
</methods>

<methods>
<class-id>VisualWave.WebRequest</class-id> <category>As yet unclassified</category>

<body package="WebToolkit/Patches" selector="uri">uri
	"This patch provides support for Seaside running behind Apache"
	^self isHttp
		ifFalse: [URI new path: self REQUEST_URI]
		ifTrue: [httpRequest uri].</body>
</methods>

<methods>
<class-id>VisualWave.Response</class-id> <category>private-headers</category>

<body package="WebToolkit/Patches" selector="expiryString">expiryString
	"Try to be tolerant of whatever people might put in there"

	"Seaside : this method override is only needed with VW before 7.4"

	| time |
	expires = 0 ifTrue: [^'0'].
	expires isString ifTrue: [^expires].
	time := expires respondsToArithmetic ifTrue: [Timestamp nowGMT addSeconds: expires * 60] ifFalse: [expires].
	^HTTPMessage dateString: time.</body>

<body package="WebToolkit/Patches" selector="setUpHeaders">setUpHeaders

	"Seaside : this method override is only needed with VW before 7.4"

	| now |
	self addMissingHeader: 'Content-Type' value: self contentTypeString.
	expires isNil ifFalse: [self addHeader: 'Expires' value: self expiryString].
	self addMissingHeader: 'Cache-Control' value: cacheControl.
	(cacheControl sameAs: 'public') 
		ifFalse: 
			[
			self addHeader: 'Cache-Control' value: 'no-cache'.
			self addMissingHeader: 'Expires' value: '-1'.
			self addMissingHeader: 'Pragma' value: 'no-cache'].
	self server notNil 
		ifTrue: 
			[
			now := HTTPMessage nowString.
			self addMissingHeader: 'Date' value: now.
			self addMissingHeader: 'Server' value: self server serverName.
			self addMissingHeader: 'MIME-version' value: '1.0'].
	self setSessionKey	"send the session cookie"</body>

<body package="WebToolkit/Patches" selector="writeCookies:">writeCookies: aStream

	"Seaside : this method override is only needed with VW before 7.5"

	self cookies isNil ifTrue: [^self].
	self cookies
		do:
			[:cookie | 
			cookie isNil
				ifFalse:
					[aStream
						nextPutAll:
								'Set-Cookie: ' ,
											"(self headerEncode:" cookie valueString")";
						crlf]].</body>
</methods>

<methods>
<class-id>VisualWave.MessageHeaders</class-id> <category>reading</category>

<body package="WebToolkit/Patches" selector="sendOver:">sendOver: aStream

	"Seaside : this method override is only needed with VW before 7.4"

	self do: [ :header |
		aStream 
			nextPutAll: header key;
			nextPutAll: ': '.
		aStream nextPutAll: 
			(self headerEncode: header).
		aStream crlf.
	].</body>
</methods>

<methods>
<class-id>VisualWave.GIFImage</class-id> <category>seaside</category>

<body package="Seaside/WebToolkit" selector="asMIMEDocument">asMIMEDocument
	^ Seaside.MIMEDocument  contentType: self mediaType content: gifBytes.</body>

<body package="Seaside/WebToolkit" selector="asMIMEDocumentType:">asMIMEDocumentType: mimeType 
	^self asMIMEDocument</body>

<body package="Seaside/WebToolkit" selector="mimeType">mimeType
	^self mediaType</body>
</methods>

<methods>
<class-id>Seaside.WASession</class-id> <category>accessing</category>

<body package="Seaside/WebToolkit" selector="wtkRequest">wtkRequest
	^self currentRequest nativeRequest</body>

<body package="Seaside/WebToolkit" selector="wtkSession">wtkSession
	^self wtkRequest session</body>

<body package="Seaside/WebToolkit" selector="wtkSite">wtkSite
	^self wtkSession webSite</body>

<body package="Seaside/WebToolkit" selector="wtkUrl">wtkUrl
	^self wtkRequest url</body>
</methods>

<methods>
<class-id>SeasidePlatformSupport class</class-id> <category>defaults</category>

<body package="Seaside/WebToolkit" selector="defaultCharSet">defaultCharSet
	^'utf-8'</body>

<body package="Seaside/WebToolkit" selector="defaultContentType">defaultContentType
	^'text/html'</body>
</methods>

<methods>
<class-id>SeasidePlatformSupport class</class-id> <category>testing</category>

<body package="Seaside/WebToolkit" selector="isWebToolkit">isWebToolkit

	^ true</body>
</methods>

<methods>
<class-id>SeasidePlatformSupport class</class-id> <category>installation</category>

<body package="Seaside/WebToolkit" selector="removeSeaside">removeSeaside
	self halt: 'Implement me'</body>
</methods>

<methods>
<class-id>VisualWave.Response</class-id> <category>accessing</category>

<body package="Seaside/WebToolkit" selector="responseStreamer">responseStreamer
	^Seaside.SeasideResponseStreamer new response: self.</body>
</methods>

<methods>
<class-id>VisualWave.Response</class-id> <category>private-headers</category>

<body package="Seaside/WebToolkit" selector="setCharSetAsNeeded">setCharSetAsNeeded
	"This forces content-type = text/html when missing.
	This forces charset = utf-8 when missing and applicable,
	i.e. when content-type is comething like text/xxx
	or when content-type is something like xxxxxxxx/aaa+xml+bbb"

	| mimeTypeTokens |
	self contentType isNil 
		ifTrue: [self contentType: SeasidePlatformSupport defaultContentType].
	self charSet notNil ifTrue: [^self].
	mimeTypeTokens := self contentType findTokens: '/'.
	(mimeTypeTokens first = 'text' or: 
			[| mimeSubTypes |
			mimeSubTypes := mimeTypeTokens size &gt; 1 
						ifTrue: [mimeTypeTokens second findTokens: '+']
						ifFalse: [#()].
			mimeSubTypes includes: 'xml']) 
		ifTrue: [self charSet: SeasidePlatformSupport defaultCharSet]</body>
</methods>

<initialize>
<class-id>Seaside.WAServletAdmin</class-id>
</initialize>

<do-it>"Imported Classes:"</do-it>

<do-it>self error: 'Attempting to file-in parcel imports.  Choose terminate or close'</do-it>

<class>
<name>Object</name>
<environment>Core</environment>
<super></super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Kernel-Objects</category>
<attributes>
<package>Kernel-Objects</package>
</attributes>
</class>

<class>
<name>Response</name>
<environment>VisualWave</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>webRequest handler headersWritten headers cookies cacheControl contentType charSet expires locale buffered bufferedStream </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Web Toolkit-ASP</category>
<attributes>
<package>ASP</package>
</attributes>
</class>

<class>
<name>WAComponent</name>
<environment>Seaside</environment>
<super>Seaside.WAPresenter</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>decoration </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Component</category>
<attributes>
<package>Seaside-Component</package>
</attributes>
</class>

<class>
<name>HttpServlet</name>
<environment>VisualWave</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>servletConfig </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Web Toolkit-Servlet</category>
<attributes>
<package>Servlet</package>
</attributes>
</class>

<class>
<name>IM_MultipartFormData</name>
<environment>VisualWave</environment>
<super>VisualWave.NetBasedFormData</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>parts </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Wave-Net</category>
<attributes>
<package>Templating</package>
</attributes>
</class>

<class>
<name>GIFImage</name>
<environment>VisualWave</environment>
<super>Graphics.PixelArray</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>gifBytes height width bitsPerPixel palette cachedVisual </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Wave-Support-Bitmaps</category>
<attributes>
<package>Wave-Server</package>
</attributes>
</class>

<class>
<name>MimeEntity</name>
<environment>Net</environment>
<super>Net.MessageElement</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>header builder headerCharset </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<class>
<name>MessageHeaders</name>
<environment>VisualWave</environment>
<super>Core.OrderedCollection</super>
<private>false</private>
<indexed-type>objects</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Wave-Net</category>
<attributes>
<package>Wave-Server</package>
</attributes>
</class>

<class>
<name>Request</name>
<environment>VisualWave</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>webRequest cookies form query attributes handler </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Web Toolkit-ASP</category>
<attributes>
<package>ASP</package>
</attributes>
</class>

<class>
<name>WASession</name>
<environment>Seaside</environment>
<super>Seaside.WAExpiringHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>continuations escapeContinuation monitor state currentRequest scripts jumpTo internalError cookiesEnabled </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Session</category>
<attributes>
<package>Seaside-Session</package>
</attributes>
</class>

<class>
<name>IM_ApplicationWWWFormUrlEncoded</name>
<environment>VisualWave</environment>
<super>VisualWave.InternetMedia</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>nameValueDictionary orderedNameValues charsetEncoder </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Wave-Net</category>
<attributes>
<package>Wave-Server</package>
</attributes>
</class>

<class>
<name>WebRequest</name>
<environment>VisualWave</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>httpRequest serverEnvironment queryData cookieData responseStream consumedPath unconsumedPath initiator entities webSite session isAnswered </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Wave-Support-Web-Framework</category>
<attributes>
<package>Wave-Server</package>
</attributes>
</class>

</st-source>
