<?xml version="1.0"?>

<st-source>
<!-- 
Name: SeasideAssistants
Notice: All rights reserved
BundleName: SeasideAssistants
Comment: SeasideAssistants for VisualWorks

Among other things, proper compliance to XHTML implies
(1) Form tags are not be nested,
(2) The id attribute values are unique.
The later is especially important when using AJAX techniques where it is critical for the Javascript functions to uniquely identify the XML elements that are manipulated.
In complex constructs that are using many nested components generated by many different rendering methods from many different classes, it may be difficult to ensure XHTML compliance. As a consequence, nested form tags or duplicate id attribute values are likely to occur. 
This package slightly modifies Seaside so that nested form tags and duplicate id attribute values are producing an error. This makes it possible to immediately and precisely spot where to look in the Seaside application.
The exact behavior is decided in the following methods :
	#shouldRaiseDuplicateCssIdErrors
	#shouldRaiseNestedFormErrors
	#shouldSilentlyDiscardNestedForms
These methods are available in classes WAHtmlRenderer and WARenderCanvas.
When this package is loaded the default behavior is the following:
(1) Duplicate id attribute values are allowed (#shouldRaiseDuplicateCssIdErrors answers false)
(2) Nested form tags are allowed (#shouldRaiseNestedFormErrors answers false)
(3) Nested form tags are silently discarded (#shouldSilentlyDiscardNestedForms answers true)
This default behavior ensures that an existing Seaside application does not break.
To enable a different behavior, there are two possibilities.
(1) Modifies any of the six methods listed above
(2) Subclass WAHtmlRenderer or WARenderCanvas and re-implement methods as needed
Contributors: Michel Bany

Keywords: Web, Continuations, HTML, XHTML, CSS, Internet, Squeak

Port from SeasideAssistants-mb.12.mcz      24 July 2007 3:13:27 pm

DevelopmentPrerequisites: #(#(#any 'SeasideBase' ''))
Parcel: #('SeasideAssistants')
ParcelName: SeasideAssistants
PrerequisiteParcels: #(#('SeasideBase' ''))
Version: 1.12
Date: 3:18:42 pm July 24, 2007
 -->
<time-stamp>From VisualWorks®, 7.5 of April 16, 2007 on July 24, 2007 at 3:18:42 pm</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>WAAssistantsCanvasRenderer</name>
<environment>Seaside</environment>
<super>Seaside.WARenderCanvas</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>shouldRaiseNestedFormErrors shouldSilentlyDiscardNestedForms shouldRaiseDuplicateCssIdErrors </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WAMoreTests</name>
<environment>Seaside</environment>
<super>Seaside.WAFrameComponent</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WAMoreClassicTests</name>
<environment>Seaside</environment>
<super>Seaside.WAMoreTests</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WANestedTagTest</name>
<environment>Seaside</environment>
<super>Seaside.WAComponent</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>isNested shouldRaiseNestedFormErrors shouldSilentlyDiscardNestedForms name nickName </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WASmartHtmlDocument</name>
<environment>Seaside</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>document tagStack cssIds </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Core</category>
<attributes>
<package>SeasideAssistants-Core</package>
</attributes>
</class>

<class>
<name>WANestedTagCanvasTest</name>
<environment>Seaside</environment>
<super>Seaside.WANestedTagTest</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WAAssistantsRenderer</name>
<environment>Seaside</environment>
<super>Seaside.WAHtmlRenderer</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>shouldRaiseNestedFormErrors shouldSilentlyDiscardNestedForms shouldRaiseDuplicateCssIdErrors </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WADuplicateIdTest</name>
<environment>Seaside</environment>
<super>Seaside.WAComponent</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>shouldRaiseDuplicateCssIdErrors </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WADuplicateIdCanvasTest</name>
<environment>Seaside</environment>
<super>Seaside.WADuplicateIdTest</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WAManyTests</name>
<environment>Seaside</environment>
<super>Seaside.WAComponent</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>allTests </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

<class>
<name>WAManyClassicTests</name>
<environment>Seaside</environment>
<super>Seaside.WAManyTests</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>SeasideAssistants-Tests</category>
<attributes>
<package>SeasideAssistants-Tests</package>
</attributes>
</class>

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

<body package="SeasideAssistants-Tests" selector="shouldRaiseDuplicateCssIdErrors">shouldRaiseDuplicateCssIdErrors
	^shouldRaiseDuplicateCssIdErrors ifNil: [super shouldRaiseDuplicateCssIdErrors]</body>

<body package="SeasideAssistants-Tests" selector="shouldRaiseDuplicateCssIdErrors:">shouldRaiseDuplicateCssIdErrors: anObject
	shouldRaiseDuplicateCssIdErrors := anObject</body>

<body package="SeasideAssistants-Tests" selector="shouldRaiseNestedFormErrors">shouldRaiseNestedFormErrors
	^shouldRaiseNestedFormErrors ifNil: [super shouldRaiseNestedFormErrors]</body>

<body package="SeasideAssistants-Tests" selector="shouldRaiseNestedFormErrors:">shouldRaiseNestedFormErrors: anObject
	"Set the value of shouldRaiseNestedFormErrors"

	shouldRaiseNestedFormErrors := anObject</body>

<body package="SeasideAssistants-Tests" selector="shouldSilentlyDiscardNestedForms">shouldSilentlyDiscardNestedForms
	^shouldSilentlyDiscardNestedForms ifNil: [super shouldSilentlyDiscardNestedForms]
</body>

<body package="SeasideAssistants-Tests" selector="shouldSilentlyDiscardNestedForms:">shouldSilentlyDiscardNestedForms: anObject
	"Set the value of shouldSilentlyDiscardNestedForms"

	shouldSilentlyDiscardNestedForms := anObject</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="initialize">initialize
	super initialize.
	shouldRaiseNestedFormErrors := super shouldRaiseNestedFormErrors.
	shouldSilentlyDiscardNestedForms := super shouldSilentlyDiscardNestedForms.
	shouldRaiseDuplicateCssIdErrors := super shouldRaiseDuplicateCssIdErrors.
</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="navigation">navigation
	 ^WASimpleNavigation new
		add: WANestedTagCanvasTest new label: 'Nested forms';
		add: WADuplicateIdCanvasTest new label: 'Duplicate id=';
		yourself</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="initialize">initialize
	self contents: self navigation</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="renderContentOn:">renderContentOn: html
	html heading
			level: 2; 
			with: 'He ! Another row of tests !'.
	html render: contents.
</body>

<body package="SeasideAssistants-Tests" selector="rendererClass">rendererClass
	^WARenderCanvas</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="navigation">navigation
	 ^WASimpleNavigation new
		add: WANestedTagTest new label: 'Nested forms';
		add: WADuplicateIdTest new label: 'Duplicate id=';
		yourself</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="renderContentOn:">renderContentOn: html
	self renderNestingStatusOn: html.
	html break; break.
	self renderOuterFormOn: html</body>

<body package="SeasideAssistants-Tests" selector="rendererClass">rendererClass
	^WAAssistantsRenderer</body>

<body package="SeasideAssistants-Tests" selector="renderInnerForm:on:">renderInnerForm: aBlock on: html
	isNested 
		ifTrue: [html form: aBlock]
		ifFalse: aBlock.
</body>

<body package="SeasideAssistants-Tests" selector="renderInnerFormContentOn:">renderInnerFormContentOn: html 
	isNested 
		ifTrue: [html cssClass: #nested].
	html div: 
			[html
				text: 'Enter your nickname';
				space;
				textInputWithValue: nickName
					callback: [:v | nickName := v];
				space;
				submitButtonWithAction: [self inform: 'Good bye ' , nickName]
					text: 'Say good bye']</body>

<body package="SeasideAssistants-Tests" selector="renderNestedFormDiscardOptionOn:">renderNestedFormDiscardOptionOn: html
	html shouldSilentlyDiscardNestedForms: shouldSilentlyDiscardNestedForms.
	html shouldSilentlyDiscardNestedForms
		ifTrue:
			[html
				text: 'Silent discard of nested &lt;form&gt; currently enabled :';
				space;
				anchorWithAction: [shouldSilentlyDiscardNestedForms := false]
					text: 'disable it']
		ifFalse:
			[html
				text: 'Silent discard of nested &lt;form&gt; currently disabled :';
				space;
				anchorWithAction: [shouldSilentlyDiscardNestedForms := true]
					text: 'enable it'].
</body>

<body package="SeasideAssistants-Tests" selector="renderNestedFormErrorOptionOn:">renderNestedFormErrorOptionOn: html
	html shouldRaiseNestedFormErrors: shouldRaiseNestedFormErrors.
	html shouldRaiseNestedFormErrors
		ifTrue:
			[html
				text: 'Nested &lt;form&gt; detection currently enabled :';
				space;
				anchorWithAction: [shouldRaiseNestedFormErrors := false]
					text: 'disable it']
		ifFalse:
			[html
				text: 'Nested &lt;form&gt; detection currently disabled :';
				space;
				anchorWithAction: [shouldRaiseNestedFormErrors := true]
					text: 'enable it'].
</body>

<body package="SeasideAssistants-Tests" selector="renderNestingStatusOn:">renderNestingStatusOn: html
	isNested 
		ifTrue: 
			[html
				anchorWithAction: [isNested := false] text: 'Suppress &lt;form&gt; nesting';
				space;
				space;
				bold: 'Note:';
				space;
				text: 'By default, nested &lt;form&gt; are silently discarded';
				break;
				cssClass: #withMargins;
				div:
					[self renderNestedFormErrorOptionOn: html.	
					html break.
					self renderNestedFormDiscardOptionOn: html]]
		ifFalse: 
			[html
				anchorWithAction: [isNested := true]
					text: 'Add &lt;form&gt; nesting'].
</body>

<body package="SeasideAssistants-Tests" selector="renderOuterFormOn:">renderOuterFormOn: html
	html form: 
		[html cssClass: #outer.
		html div: 
			[html
				text: 'Enter your name';
				space;
				textInputWithValue: name 
					callback: [:v | name := v];
				space;
				submitButtonWithAction: [self inform: 'Hello ' , name]
					text: 'Say hello';
				break.
			self
				renderInnerForm: [self renderInnerFormContentOn: html]
				on: html]]</body>

<body package="SeasideAssistants-Tests" selector="style">style
	^'
div.nested {border: red solid 1px; width: 90%; margin: 10px; padding: 5px; background: lightblue;}
div.outer{border: green solid 1px; width: 50%; padding: 5px; background: lightyellow;}
div.withMargins{margin: 1em 0 0 5em;}
'
</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="initialize">initialize
	isNested := false.
	nickName := String new.
	name := String new.
</body>
</methods>

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

<body package="SeasideAssistants-Core" selector="initialize">initialize
	self clearTagStack.	
	</body>
</methods>

<methods>
<class-id>Seaside.WASmartHtmlDocument</class-id> <category>delegating</category>

<body package="SeasideAssistants-Core" selector="close">close
	document close</body>

<body package="SeasideAssistants-Core" selector="closeTag:">closeTag: aString
	document closeTag: aString.
	self popTag	</body>

<body package="SeasideAssistants-Core" selector="forHtml">forHtml
	^document forHtml</body>

<body package="SeasideAssistants-Core" selector="initializeWithRenderer:">initializeWithRenderer: aRenderer
	document initializeWithRenderer: aRenderer.
	self clearTagStack.</body>

<body package="SeasideAssistants-Core" selector="initializeWithRoot:">initializeWithRoot: aRoot
	document initializeWithRoot: aRoot.
	self clearTagStack.</body>

<body package="SeasideAssistants-Core" selector="javascriptCharacterData:">javascriptCharacterData: aString
	^document javascriptCharacterData: aString</body>

<body package="SeasideAssistants-Core" selector="openTag:attributes:">openTag: aString attributes: anAttributes
	self openTag: aString attributes: anAttributes renderer: nil</body>

<body package="SeasideAssistants-Core" selector="openTag:attributes:renderer:">openTag: aString attributes: anAttributes renderer: aRenderer
	self checkCssIds: anAttributes renderer: aRenderer. "May raise an error"
	document openTag: aString attributes: anAttributes.
	self pushTag: aString.
</body>

<body package="SeasideAssistants-Core" selector="root">root
	^document root
</body>

<body package="SeasideAssistants-Core" selector="shouldPrintCloseTagFor:">shouldPrintCloseTagFor: aString
	^document shouldPrintCloseTagFor: aString
</body>

<body package="SeasideAssistants-Core" selector="stream:">stream: aStream
	document stream: aStream
</body>

<body package="SeasideAssistants-Core" selector="text:">text: aString
	document text: aString
</body>
</methods>

<methods>
<class-id>Seaside.WASmartHtmlDocument</class-id> <category>duplicate css id</category>

<body package="SeasideAssistants-Core" selector="checkCssIds:renderer:">checkCssIds: attributes renderer: aRenderer
	| id |
	attributes isNil ifTrue: [^self].
	id := (attributes at: 'id' ifAbsent: [^self]) value asSymbol.
	cssIds isNil ifTrue: [cssIds := Set new].
	
	((cssIds includes: id) 
		and: [aRenderer shouldRaiseDuplicateCssIdErrors])
			ifTrue:
				[self error: 'Duplicate id in HTML document']
			ifFalse:
				[cssIds add: id].
</body>
</methods>

<methods>
<class-id>Seaside.WASmartHtmlDocument</class-id> <category>nested forms</category>

<body package="SeasideAssistants-Core" selector="clearTagStack">clearTagStack
	tagStack := OrderedCollection new.</body>

<body package="SeasideAssistants-Core" selector="isWithinTag:">isWithinTag: aString
	^ tagStack includes: aString</body>

<body package="SeasideAssistants-Core" selector="popTag">popTag
	^ tagStack removeLast
</body>

<body package="SeasideAssistants-Core" selector="pushTag:">pushTag: aString
	tagStack add: aString
</body>
</methods>

<methods>
<class-id>Seaside.WASmartHtmlDocument</class-id> <category>private - accessing</category>

<body package="SeasideAssistants-Core" selector="document:">document: aDocument
	document := aDocument</body>
</methods>

<methods>
<class-id>Seaside.WASmartHtmlDocument class</class-id> <category>instance creation</category>

<body package="SeasideAssistants-Core" selector="on:">on: aDocument
	^self new document: aDocument</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="rendererClass">rendererClass
	^ WAAssistantsCanvasRenderer
</body>

<body package="SeasideAssistants-Tests" selector="renderInnerForm:on:">renderInnerForm: aBlock on: html
	isNested 
		ifTrue: [html form with: aBlock]
		ifFalse: aBlock.
</body>

<body package="SeasideAssistants-Tests" selector="renderInnerFormContentOn:">renderInnerFormContentOn: html 
	html div
		class: #nested if: isNested;
		with:
			[html text: 'Enter your nickname'.
			html space.
			(html textInput) 
				value: nickName; 
				callback: [:v | nickName := v].
			html space.
			(html submitButton)
				callback: [self inform: 'Good bye ' , nickName];
				text: 'Say good bye'].
</body>

<body package="SeasideAssistants-Tests" selector="renderNestedFormDiscardOptionOn:">renderNestedFormDiscardOptionOn: html
	html shouldSilentlyDiscardNestedForms: shouldSilentlyDiscardNestedForms.
	html shouldSilentlyDiscardNestedForms
		ifTrue:
			[html
				text: 'Silent discard of nested &lt;form&gt; currently enabled :';
				space.
			(html anchor)
				callback: [shouldSilentlyDiscardNestedForms := false];
				with: 'disable it']
		ifFalse:
			[html
				text: 'Silent discard of nested &lt;form&gt; currently disabled :';
				space.
			(html anchor)
				callback: [shouldSilentlyDiscardNestedForms := true];
				with: 'enable it'].
</body>

<body package="SeasideAssistants-Tests" selector="renderNestedFormErrorOptionOn:">renderNestedFormErrorOptionOn: html
	html shouldRaiseNestedFormErrors: shouldRaiseNestedFormErrors.
	html shouldRaiseNestedFormErrors
		ifTrue:
			[html
				text: 'Nested &lt;form&gt; detection currently enabled :';
				space.
			(html anchor)
				callback: [shouldRaiseNestedFormErrors := false];
				with: 'disable it']
		ifFalse:
			[html
				text: 'Nested &lt;form&gt; detection currently disabled :';
				space.
			(html anchor)
				callback: [shouldRaiseNestedFormErrors := true];
				with: 'enable it'].
</body>

<body package="SeasideAssistants-Tests" selector="renderNestingStatusOn:">renderNestingStatusOn: html 
	isNested 
		ifTrue: 
			[(html anchor)
				callback: [isNested := false];
				with: 'Suppress &lt;form&gt; nesting'.
			html
				space;
				space;
				strong: 'Note:';
				space;
				text: 'by default, nested &lt;form&gt; are silently discarded'.
			(html div)
				class: #withMargins;
				with:
					[self renderNestedFormErrorOptionOn: html.	
					html break.
					self renderNestedFormDiscardOptionOn: html]]
		ifFalse: 
			[(html anchor)
				callback: [isNested := true];
				with: 'Add &lt;form&gt; nesting'].
</body>

<body package="SeasideAssistants-Tests" selector="renderOuterFormOn:">renderOuterFormOn: html
	(html form)
		defaultAction: [self inform: 'You do not like to click with a mouse, do you ?'];
		with: 
				[(html div)
					class: #outer;
					with: 
							[html text: 'Enter your name'.
							html space.
							(html textInput)
								value: name;
								callback: [:v | name := v].
							html space.
							(html submitButton)
								callback: [self inform: 'Hello ' , name];
								text: 'Say hello'.
							html break.
							self renderInnerForm: [self renderInnerFormContentOn: html] on: html]]
</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="shouldRaiseDuplicateCssIdErrors">shouldRaiseDuplicateCssIdErrors
	^shouldRaiseDuplicateCssIdErrors ifNil: [super shouldRaiseDuplicateCssIdErrors]</body>

<body package="SeasideAssistants-Tests" selector="shouldRaiseDuplicateCssIdErrors:">shouldRaiseDuplicateCssIdErrors: anObject
	shouldRaiseDuplicateCssIdErrors := anObject</body>

<body package="SeasideAssistants-Tests" selector="shouldRaiseNestedFormErrors">shouldRaiseNestedFormErrors
	^shouldRaiseNestedFormErrors ifNil: [super shouldRaiseNestedFormErrors]</body>

<body package="SeasideAssistants-Tests" selector="shouldRaiseNestedFormErrors:">shouldRaiseNestedFormErrors: anObject
	"Set the value of shouldRaiseNestedFormErrors"

	shouldRaiseNestedFormErrors := anObject</body>

<body package="SeasideAssistants-Tests" selector="shouldSilentlyDiscardNestedForms">shouldSilentlyDiscardNestedForms
	^shouldSilentlyDiscardNestedForms ifNil: [super shouldSilentlyDiscardNestedForms]
</body>

<body package="SeasideAssistants-Tests" selector="shouldSilentlyDiscardNestedForms:">shouldSilentlyDiscardNestedForms: anObject
	"Set the value of shouldSilentlyDiscardNestedForms"

	shouldSilentlyDiscardNestedForms := anObject</body>
</methods>

<methods>
<class-id>Seaside.WAAssistantsRenderer</class-id> <category>initialize-release</category>

<body package="SeasideAssistants-Tests" selector="initialize">initialize
	super initialize.
	shouldRaiseNestedFormErrors := super shouldRaiseNestedFormErrors.
	shouldSilentlyDiscardNestedForms := super shouldSilentlyDiscardNestedForms.
	shouldRaiseDuplicateCssIdErrors := super shouldRaiseDuplicateCssIdErrors.
</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="renderContentOn:">renderContentOn: html

	self renderDuplicateIdErrorOptionOn: html.	
	html break; break.
	html cssClass: #demo.
	html spanNamed: 'abcdef' with: 'This is a span with id="abcdef"'.
	html break; break.
	html cssClass: #demo.
	html divNamed: 'abcdef' with: 
			[html 
				text: 'This is a div';
				break;
				text: 'with id="abcdef"']
			
</body>

<body package="SeasideAssistants-Tests" selector="renderDuplicateIdErrorOptionOn:">renderDuplicateIdErrorOptionOn: html
	html shouldRaiseDuplicateCssIdErrors: shouldRaiseDuplicateCssIdErrors.
	html shouldRaiseDuplicateCssIdErrors
		ifTrue:
			[html text: 'Duplicate id detection currently enabled :'.
			html space.
			html anchorWithAction: [shouldRaiseDuplicateCssIdErrors := false] text: 'disable it']
		ifFalse:
			[html text: 'Duplicate id detection currently disabled :'.
			html space.
			html anchorWithAction: [shouldRaiseDuplicateCssIdErrors := true] text: 'enable it'].
</body>

<body package="SeasideAssistants-Tests" selector="rendererClass">rendererClass
	^WAAssistantsRenderer</body>

<body package="SeasideAssistants-Tests" selector="style">style
	^'
span.demo {background: blue; color: yellow;}
div.demo {border: 1px red solid; width: 25%; background: lightgreen; color: blue;}
'</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="renderContentOn:">renderContentOn: html

	self renderDuplicateIdErrorOptionOn: html.	
	html break; break.
	(html span)
		class: #demo;
		id: 'abcdef';
		with: 'This is a span with id="abcdef"'.
	html break; break.
	(html div)
		class: #demo;
		id: 'abcdef';
		with: 
			[html 
				text: 'This is a div';
				break;
				text: 'with id="abcdef"']
			
</body>

<body package="SeasideAssistants-Tests" selector="renderDuplicateIdErrorOptionOn:">renderDuplicateIdErrorOptionOn: html
	html shouldRaiseDuplicateCssIdErrors: shouldRaiseDuplicateCssIdErrors.
	html shouldRaiseDuplicateCssIdErrors
		ifTrue:
			[html text: 'Duplicate id detection currently enabled :'.
			html space.
			(html anchor)
				callback: [shouldRaiseDuplicateCssIdErrors := false];
				with: 'disable it']
		ifFalse:
			[html text: 'Duplicate id detection currently disabled :'.
			html space.
			(html anchor)
				callback: [shouldRaiseDuplicateCssIdErrors := true];
				with: 'enable it'].
</body>

<body package="SeasideAssistants-Tests" selector="rendererClass">rendererClass
	^ WAAssistantsCanvasRenderer
</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="allTestsClass">allTestsClass
	^WAAllCanvasTests
</body>

<body package="SeasideAssistants-Tests" selector="initialize">initialize
	| moreTests row1Size row1 row2 allComponents evenMoreTests |
	allTests := self allTestsClass new.
	moreTests := self moreTestsClass new.
	evenMoreTests := self moreTestsClass new.
	allComponents := allTests contents components.
	row1Size := (allComponents size / 2) truncated.
	row1 := allComponents copyFrom: 1 to: row1Size.
	row2 := allComponents copyFrom: row1Size + 1 to: allComponents size.
	allTests contents removeAll.
	row1 do:
		[:each |
		allTests contents add: each value label: each key].
	allTests contents add: moreTests label: 'More...'.
	moreTests contents removeAll.
	row2 do:
		[:each |
		moreTests contents add: each value label: each key].
	moreTests contents add: evenMoreTests label: 'Even more...'

</body>

<body package="SeasideAssistants-Tests" selector="moreTestsClass">moreTestsClass
	^WAMoreTests
</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="children">children
	^Array with: allTests</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="renderContentOn:">renderContentOn: html
	html render: allTests</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="description">description
	^'Canvas rendering tests with detection of nested forms and duplicate ids'</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="initialize">initialize
	self registerAsApplication: 'tests/allcanvastests'.</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="allTestsClass">allTestsClass
	^WAAllTests
</body>

<body package="SeasideAssistants-Tests" selector="moreTestsClass">moreTestsClass
	^ WAMoreClassicTests</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="description">description
	^'Classic rendering tests (deprecated) with detection of nested forms and duplicate ids'</body>
</methods>

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

<body package="SeasideAssistants-Tests" selector="initialize">initialize
	self registerAsApplication: 'tests/alltests'.</body>
</methods>

<methods>
<class-id>Seaside.WAHtmlRenderer</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="form:">form: aBlock 
	((self document isWithinTag: 'form') not or: 
			[self shouldRaiseNestedFormErrors not 
				and: [self shouldSilentlyDiscardNestedForms not]]) 
		ifTrue: 
			[self 
				formWithMethod: 'post'
				action: context actionUrl urlString
				do: 
					[self div: 
							[context actionUrl parameters keysAndValuesDo: 
									[:k :v | 
									self 
										inputWithType: 'hidden'
										named: k
										value: v].
							aBlock value]]]
		ifFalse: 
			[self shouldRaiseNestedFormErrors 
				ifTrue: [self error: 'Nested &lt;form&gt; !']
				ifFalse: [aBlock value]]
</body>

<body package="SeasideAssistants-Extensions" selector="shouldRaiseDuplicateCssIdErrors">shouldRaiseDuplicateCssIdErrors
	"Answer true to raise an error in case of duplicate CSS id in page
	Can be subclassed"
	^false
</body>

<body package="SeasideAssistants-Extensions" selector="shouldRaiseNestedFormErrors">shouldRaiseNestedFormErrors
	"Answer true to raise an error in case of nested form tags
	Can be subclassed"
	^false
</body>

<body package="SeasideAssistants-Extensions" selector="shouldSilentlyDiscardNestedForms">shouldSilentlyDiscardNestedForms
	"Answer true to ignore nested form tags
	Can be subclassed"
	^true
</body>
</methods>

<methods>
<class-id>Seaside.WAHtmlCanvas</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="tag:">tag: aString
	aString = 'form' ifTrue: [^self form].
	^ self brush: (WAGenericTag tag: aString)</body>
</methods>

<methods>
<class-id>Seaside.WAHtmlDocument class</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="newWithSmartDecoration">newWithSmartDecoration
	^Seaside.WASmartHtmlDocument on: self basicNew</body>

<body package="SeasideAssistants-Extensions" selector="root:">root: anHtmlRoot
	^self newWithSmartDecoration initializeWithRoot: anHtmlRoot </body>
</methods>

<methods>
<class-id>Seaside.WAHtmlDocument</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="openTag:attributes:renderer:">openTag: aString attributes: anAttributes renderer: aRenderer
	self openTag: aString attributes: anAttributes</body>
</methods>

<methods>
<class-id>Seaside.WATagBrush</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="openTag">openTag
	self document openTag: self tag attributes: attributes renderer: canvas.</body>
</methods>

<methods>
<class-id>Seaside.WAPrettyPrintedDocument class</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="renderer:">renderer: aRenderer
	^ self newWithSmartDecoration initializeWithRenderer: aRenderer</body>
</methods>

<methods>
<class-id>Seaside.WAScriptTag</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="with:">with: aBlock
	isClosed := true.
	self attributes at: 'type' ifAbsentPut: ['text/javascript'].
	self document openTag: self tag attributes: attributes renderer: self.
	canvas html: aBlock.
	self document closeTag: self tag.
</body>
</methods>

<methods>
<class-id>Seaside.WASimpleNavigation</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="components">components
	^components</body>

<body package="SeasideAssistants-Extensions" selector="removeAll">removeAll
	^components := OrderedCollection new.</body>

<body package="SeasideAssistants-Extensions" selector="removeLabel:">removeLabel: aString 
	| association |
	association := components
				detect: [:ea | ea key = aString]
				ifNone: [^ self].
	components remove: association</body>
</methods>

<methods>
<class-id>Seaside.WAAbstractHtmlBuilder</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="openTag:">openTag: aString 
	self document
		openTag: aString
		attributes: self attributes
		renderer: self.
	attributeBuffer := nil</body>
</methods>

<methods>
<class-id>Seaside.WAFormTag</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="isWithinForm">isWithinForm

	^ self document isWithinTag: self tag</body>

<body package="SeasideAssistants-Extensions" selector="with:">with: aBlock 
	(self isWithinForm not or: 
			[canvas shouldRaiseNestedFormErrors not 
				and: [canvas shouldSilentlyDiscardNestedForms not]]) 
		ifTrue: [super with: aBlock]
		ifFalse: 
			[canvas shouldRaiseNestedFormErrors 
				ifTrue: [self error: 'Nested &lt;form&gt; !']
				ifFalse: 
					[canvas nest: aBlock.
					isClosed := true]]</body>
</methods>

<methods>
<class-id>Seaside.WARenderCanvas</class-id> <category>*SeasideAssistants</category>

<body package="SeasideAssistants-Extensions" selector="shouldRaiseDuplicateCssIdErrors">shouldRaiseDuplicateCssIdErrors
	"Answer true to raise an error in case of duplicate CSS id in page
	Can be subclassed"
	^false
</body>

<body package="SeasideAssistants-Extensions" selector="shouldRaiseNestedFormErrors">shouldRaiseNestedFormErrors
	"Answer true to raise an error in case of nested form tags
	Can be subclassed"
	^false
</body>

<body package="SeasideAssistants-Extensions" selector="shouldSilentlyDiscardNestedForms">shouldSilentlyDiscardNestedForms
	"Answer true to ignore nested form tags
	Can be subclassed"
	^true
</body>
</methods>

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

<initialize>
<class-id>Seaside.WAManyClassicTests</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>WAHtmlDocument</name>
<environment>Seaside</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>root </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Document</category>
<attributes>
<package>Seaside-Document</package>
</attributes>
</class>

<class>
<name>WAAbstractHtmlBuilder</name>
<environment>Seaside</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>attributeBuffer </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Builder</category>
<attributes>
<package>Seaside-Builder</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>WASimpleNavigation</name>
<environment>Seaside</environment>
<super>Seaside.WANavigation</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>components </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Components-Frames</category>
<attributes>
<package>Seaside-Components-Frames</package>
</attributes>
</class>

<class>
<name>WAFrameComponent</name>
<environment>Seaside</environment>
<super>Seaside.WAComponent</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>contents </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Components-Frames</category>
<attributes>
<package>Seaside-Components-Frames</package>
</attributes>
</class>

<class>
<name>WAHtmlCanvas</name>
<environment>Seaside</environment>
<super>Seaside.WACanvas</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Canvas</category>
<attributes>
<package>Seaside-Canvas</package>
</attributes>
</class>

<class>
<name>WAHtmlRenderer</name>
<environment>Seaside</environment>
<super>Seaside.WAAbstractHtmlBuilder</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>context callbacks shortcutBuffer </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Builder</category>
<attributes>
<package>Seaside-Builder</package>
</attributes>
</class>

<class>
<name>WATagBrush</name>
<environment>Seaside</environment>
<super>Seaside.WABrush</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>attributes </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Canvas</category>
<attributes>
<package>Seaside-Canvas</package>
</attributes>
</class>

<class>
<name>WAScriptTag</name>
<environment>Seaside</environment>
<super>Seaside.WATagBrush</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Canvas-Tags</category>
<attributes>
<package>Seaside-Canvas-Tags</package>
</attributes>
</class>

<class>
<name>WAFormTag</name>
<environment>Seaside</environment>
<super>Seaside.WATagBrush</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>defaultAction </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Canvas-Tags</category>
<attributes>
<package>Seaside-Canvas-Tags</package>
</attributes>
</class>

<class>
<name>WARenderCanvas</name>
<environment>Seaside</environment>
<super>Seaside.WAHtmlCanvas</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>context callbacks </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Seaside-Canvas</category>
<attributes>
<package>Seaside-Canvas</package>
</attributes>
</class>

</st-source>
