Widget subclass: #TabManager
	instanceVariableNames: 'selectedTab tabs opened'
	category: 'IDE'!

TabManager class instanceVariableNames: 'current'!

!TabManager class methodsFor: 'instance creation'!

current
    ^current ifNil: [current := super new]
!

new
    self shouldNotImplement
! !

!TabManager methodsFor: 'initialization'!

initialize
    super initialize.
    opened := true.
    'body' asJQuery 
	append: self;
	addClass: 'jtalk'.
    self 
	addTab: Transcript current;
	addTab: Workspace new.
    self selectTab: self tabs last.
! !

!TabManager methodsFor: 'accessing'!

tabs
    ^tabs ifNil: [tabs := Array new]
! !

!TabManager methodsFor: 'adding/Removing'!

addTab: aWidget
    self tabs add: aWidget.
    'body' asJQuery append: aWidget.
    aWidget root asJQuery hide
!

removeTab: aWidget
    self tabs remove: aWidget.
    self update
! !

!TabManager methodsFor: 'actions'!

open
    opened ifFalse: [
	self root asJQuery show.
	'body' asJQuery addClass: 'jtalk'.
	selectedTab root asJQuery show.
	opened := true]
!

close
    opened ifTrue: [
	self tabs do: [:each |
	    each root asJQuery hide].
	self root asJQuery hide.
	'body' asJQuery removeClass: 'jtalk'.
	opened := false]
!

newBrowserTab
    Browser open
!

selectTab: aWidget
    self open.
    selectedTab := aWidget.
    self tabs do: [:each |
	each root asJQuery hide].
    aWidget root asJQuery show.
    self update
!

closeTab: aWidget
    self removeTab: aWidget.
    self selectTab: self tabs last.
    aWidget root asJQuery remove.
    self update
! !

!TabManager methodsFor: 'rendering'!

renderOn: html
    html ul
	id: 'jtalkTabs';
	with: [
	    html li 
		class: 'closeAll';
		with: 'x';
		onClick: [self close].
	    self tabs do: [:each |
		self renderTabFor: each on: html].
	    html li
		class: 'newtab';
		with: ' + ';
		onClick: [self newBrowserTab]]
!

renderTabFor: aWidget on: html
    | li |
    li := html li.
    selectedTab = aWidget ifTrue: [
	li class: 'selected'].
    li with: [
	html span
	    with: aWidget label;
	    onClick: [self selectTab: aWidget].
	aWidget canBeClosed ifTrue: [
	    html span 
		class: 'close';
		with: 'x';
		onClick: [self closeTab: aWidget]]]
! !


Widget subclass: #TabWidget
	instanceVariableNames: ''
	category: 'IDE'!

!TabWidget class methodsFor: 'instance creation'!

open
    ^self new open
! !

!TabWidget methodsFor: 'accessing'!

label
    self subclassResponsibility
! !

!TabWidget methodsFor: 'actions'!

open
    TabManager current
	addTab: self;
	selectTab: self
! !
   
!TabWidget methodsFor: 'testing'!

canBeClosed
    ^false
! !

!TabWidget methodsFor: 'rendering'!

renderOn: html
    html div 
	class: 'jtalkTool';
	with: [
	    html div
		class: 'box';
		with: [self renderBoxOn: html].
	    html div
		class: 'buttons';
		with: [self renderButtonsOn: html]]
!

renderBoxOn: html
!

renderButtonsOn: html
! !



TabWidget subclass: #Workspace
	instanceVariableNames: 'textarea'
	category: 'IDE'!

!Workspace methodsFor: 'accessing'!

label
    ^'[Workspace]'
!

selection
    ^{'return document.selection'}
!

selectionStart
    ^{'return jQuery(''.workspace'')[0].selectionStart'}
!

selectionEnd
    ^{'return jQuery(''.workspace'')[0].selectionEnd'}
!

selectionStart: anInteger
    {'jQuery(''.workspace'')[0].selectionStart = anInteger'}
!

selectionEnd: anInteger
    {'jQuery(''.workspace'')[0].selectionEnd = anInteger'}
!

currentLine
    | lines startLine endLine|
    lines := textarea asJQuery val tokenize: String cr.
    startLine := endLine := 0.
    lines do: [:each |
	endLine := startLine + each size.
	startLine := endLine + 1.
	endLine >= self selectionStart ifTrue: [
	    self selectionEnd: endLine.
	    ^each]]
! !

!Workspace methodsFor: 'actions'!

handleKeyDown: anEvent
    ^{'if(anEvent.ctrlKey) {
		if(anEvent.keyCode === 68) { //ctrl+p
			self._printIt();
			return false;
		}
		if(anEvent.keyCode === 80) { //ctrl+d
			self._doIt();
			return false;
		}
	}'}
!

clearWorkspace
    textarea asJQuery val: ''
!

doIt
    self printIt
!

printIt
    | selection |
    textarea asJQuery focus.
    self selectionStart = self selectionEnd
	ifTrue: [selection := self currentLine]
	ifFalse: [
	    selection := textarea asJQuery val copyFrom: self selectionStart + 1 to: self selectionEnd + 1].
    self print: (self eval: selection) printString
!

print: aString
    | start |
    start := self selectionEnd.
    textarea asJQuery val: (
	(textarea asJQuery val copyFrom: 1 to: start),
	' ', aString, ' ',
	(textarea asJQuery val copyFrom: start + 1 to: textarea asJQuery val size)).
    self selectionStart: start.
    self selectionEnd: start + aString size + 2
!

eval: aString
    ^Compiler new loadExpression: aString
! !

!Workspace methodsFor: 'rendering'!

renderBoxOn: html
    textarea := html textarea.
    textarea onKeyDown: [:e | self handleKeyDown: e].
    textarea 
	class: 'workspace';
	at: 'spellcheck' put: 'false'
!

renderButtonsOn: html
    html button
	with: 'DoIt';
	title: 'ctrl+d';
	onClick: [self doIt].
    html button
	with: 'PrintIt';
	title: 'ctrl+p';
	onClick: [self printIt].
    html button
	with: 'Clear workspace';
	onClick: [self clearWorkspace]
! !



TabWidget subclass: #Transcript
	instanceVariableNames: 'textarea'
	category: 'IDE'!

Transcript class instanceVariableNames: 'current'!

!Transcript class methodsFor: 'instance creation'!

open
    self current open
!

new
    self shouldNotImplement
!

current
    ^current ifNil: [current := super new]
! !

!Transcript class methodsFor: 'printing'!

show: anObject
    self current show: anObject
!

cr
    self current show: String cr
!

clear
    self current clear
! !


!Transcript methodsFor: 'accessing'!

label
    ^'[Transcript]'
! !

!Transcript methodsFor: 'actions'!

show: anObject
    textarea asJQuery val: textarea asJQuery val, anObject asString.

!

cr
    textarea asJQuery val: textarea asJQuery val, String cr.
!

clear
    textarea asJQuery val: ''
! !

!Transcript methodsFor: 'rendering'!

renderBoxOn: html
    textarea := html textarea.
    textarea 
	class: 'transcript';
	at: 'spellcheck' put: 'false'
!

renderButtonsOn: html
    html button
	with: 'Clear transcript';
	onClick: [self clear]
! !

TabWidget subclass: #Browser
	instanceVariableNames: 'selectedCategory selectedClass selectedProtocol selectedMethod categoriesList classesList protocolsList methodsList sourceTextarea tabsList selectedTab saveButton classButtons methodButtons'
	category: 'IDE'!

!Browser class methodsFor: 'convenience'!

open
    self new open
! !

!Browser methodsFor: 'initialization'!

initialize
    super initialize.
    selectedTab := #instance
! !

!Browser methodsFor: 'accessing'!

label
    ^selectedClass 
	ifNil: ['Browser (nil)']
	ifNotNil: [selectedClass name]
!

categories
    | categories |
    categories := Array new.
    Smalltalk current classes do: [:each |
	(categories includes: each category) ifFalse: [
	    categories add: each category]].
    ^categories sort
!

classes
    ^(Smalltalk current classes 
	select: [:each | each category = selectedCategory])
	sort: [:a :b | a name > b name]
!

protocols
    | class protocols |
    protocols := Array new.
    selectedClass ifNotNil: [
	selectedTab = #comment ifTrue: [^#()].
	class := selectedTab = #instance
	    ifTrue: [selectedClass]
	    ifFalse: [selectedClass class].
	class methodDictionary isEmpty ifTrue: [
	    protocols add: 'not yet classified'].
	class methodDictionary do: [:each |
	    (protocols includes: each category) ifFalse: [
		protocols add: each category]]].
    ^protocols sort
!

methods
    | class |
    selectedTab = #comment ifTrue: [^#()].
    selectedClass ifNotNil: [
	class := selectedTab = #instance
	    ifTrue: [selectedClass]
	    ifFalse: [selectedClass class]].
    ^(selectedProtocol 
	ifNil: [
	    class 
		ifNil: [#()] 
		ifNotNil: [class methodDictionary values]]
	ifNotNil: [
	    class methodDictionary values select: [:each |
		each category = selectedProtocol]]) sort: [:a :b | a selector > b selector]
!

source
    selectedTab = #comment ifFalse: [
	^(selectedProtocol notNil or: [selectedMethod notNil])
	    ifFalse: [self declarationSource]
	    ifTrue: [self methodSource]].
    ^selectedClass
	ifNil: ['']
	ifNotNil: [self classCommentSource]
!

methodSource
    ^selectedMethod
	ifNil: [self dummyMethodSource]
	ifNotNil: [selectedMethod source]
!

dummyMethodSource
    ^'messageSelectorAndArgumentNames
	"comment stating purpose of message"

	| temporary variable names |
	statements'
!

declarationSource
    ^selectedTab = #instance
	ifTrue: [self classDeclarationSource]
	ifFalse: [self metaclassDeclarationSource]
!

classDeclarationSource
    | stream |
    stream := '' writeStream.
    selectedClass ifNotNil: [
	stream 
	    nextPutAll: selectedClass superclass asString;
	    nextPutAll: ' subclass: #';
	    nextPutAll: selectedClass name;
	    nextPutAll: String cr, String tab;
	    nextPutAll: 'instanceVariableNames: '''.
	selectedClass instanceVariableNames 
	    do: [:each | stream nextPutAll: each] 
	    separatedBy: [stream nextPutAll: ' '].
	stream
	    nextPutAll: '''', String cr, String tab;
	    nextPutAll: 'category: ''';
	    nextPutAll: selectedClass category;
	    nextPutAll: ''''].
    ^stream contents
!

metaclassDeclarationSource
    | stream |
    stream := '' writeStream.
    selectedClass ifNotNil: [
	stream 
	    nextPutAll: selectedClass asString;
	    nextPutAll: ' class ';
	    nextPutAll: 'instanceVariableNames: '''.
	selectedClass class instanceVariableNames
	    do: [:each | stream nextPutAll: each]
	    separatedBy: [stream nextPutAll: ' '].
	stream nextPutAll: ''''].
    ^stream contents
!

classCommentSource
    ^selectedClass comment
! !

!Browser methodsFor: 'actions'!

enableSaveButton
    saveButton removeAt: 'disabled'
!

disableSaveButton
    saveButton ifNotNil: [
	saveButton at: 'disabled' put: true]
!

hideClassButtons
    classButtons asJQuery hide
!

showClassButtons
    classButtons asJQuery show
!

hideMethodButtons
    methodButtons asJQuery hide
!

showMethodButtons
    methodButtons asJQuery show
!

compile
    selectedTab = #comment ifTrue: [
	selectedClass ifNotNil: [
	    self compileClassComment]].
    (selectedProtocol notNil or: [selectedMethod notNil])
	ifFalse: [self compileDefinition]
	ifTrue: [self compileMethodDefinition].
    self disableSaveButton
!

compileClassComment
    selectedClass comment: sourceTextarea asJQuery val
!

compileMethodDefinition
    selectedTab = #instance
	ifTrue: [self compileMethodDefinitionFor: selectedClass]
	ifFalse: [self compileMethodDefinitionFor: selectedClass class]
!

compileMethodDefinitionFor: aClass
    | method |
    method := Compiler new load: sourceTextarea asJQuery val forClass: selectedClass.
    method category: selectedProtocol.
    aClass addCompiledMethod: method.
    self updateMethodsList.
    self selectMethod: method
!

compileDefinition
    | newClass |
    newClass := Compiler new loadExpression: sourceTextarea asJQuery val.
    self 
	updateCategoriesList;
	updateClassesList
!

selectCategory: aCategory
    selectedCategory := aCategory.
    selectedClass := selectedProtocol := selectedMethod :=  nil.
    self 
	updateCategoriesList;
	updateClassesList;
	updateProtocolsList;
	updateMethodsList;
	updateSourceAndButtons
!

selectClass: aClass
    selectedClass := aClass.
    selectedProtocol := selectedMethod := nil.
    self 
	updateClassesList;
	updateProtocolsList;
	updateMethodsList;
	updateSourceAndButtons
!

selectProtocol: aString
    selectedProtocol := aString.
    selectedMethod := nil.
    self 
	updateProtocolsList;
	updateMethodsList;
	updateSourceAndButtons
!

selectMethod: aMethod
    selectedMethod := aMethod.
    self 
	updateProtocolsList;
	updateMethodsList;
	updateSourceAndButtons
!

selectTab: aString
    selectedTab := aString.
    self selectProtocol: nil.
    self updateTabsList.
! !


!Browser methodsFor: 'rendering'!

renderBoxOn: html
    self 
	renderTopPanelOn: html;
	renderTabsOn: html;
	renderBottomPanelOn: html
!

renderTopPanelOn: html
    html div 
	class: 'top'; 
	with: [
	    categoriesList := html ul class: 'column categories'.
	    classesList := html ul class: 'column classes'.
	    protocolsList := html ul class: 'column protocols'.
	    methodsList := html ul class: 'column methods'.
	    self
		updateCategoriesList;
		updateClassesList;
		updateProtocolsList;
		updateMethodsList.
	    html div class: 'clear']
!

renderTabsOn: html
    tabsList := html ul class: 'tabs'.
    self updateTabsList.
!

renderBottomPanelOn: html
    html div
	class: 'sourceCode';
	with: [
	    sourceTextarea := html textarea 
		onKeyPress: [self enableSaveButton];
		class: 'source';
		at: 'spellcheck' put: 'false']
!

renderButtonsOn: html
    saveButton := html button.
    saveButton 
	with: 'Save';
	onClick: [self compile].
    methodButtons := html span with: [
	html button
	    with: 'Remove method';
	    onClick: [self removeMethod]].
    classButtons := html span with: [
	html button
	    with: 'Remove class';
	    onClick: [self removeClass]].
    self updateSourceAndButtons
! !

!Browser methodsFor: 'updating'!

updateCategoriesList
    categoriesList contents: [:html |
	self categories do: [:each || li |
	    li := html li.
	    selectedCategory = each ifTrue: [
		li class: 'selected'].
	    li
		with: each;
		onClick: [self selectCategory: each]]]
!

updateClassesList
    TabManager current update.
    classesList contents: [:html |
	self classes do: [:each || li |
	    li := html li.
	    selectedClass = each ifTrue: [
		li class: 'selected'].
	    li
		with: each name;
		onClick: [self selectClass: each]]]
!

updateProtocolsList
    protocolsList contents: [:html |
	self protocols do: [:each || li |
	    li := html li.
	    selectedProtocol = each ifTrue: [
		li class: 'selected'].
	    li 
		with: each;
		onClick: [self selectProtocol: each]]]
!

updateMethodsList
    methodsList contents: [:html |
	self methods do: [:each || li |
	    li := html li.
	    selectedMethod = each ifTrue: [
		li class: 'selected'].
	    li
		with: each selector;
		onClick: [self selectMethod: each]]]
!

updateTabsList
    tabsList contents: [:html || li |
	li := html li.
	selectedTab = #instance ifTrue: [li class: 'selected'].
	li
	    with: 'Instance';
	    onClick: [self selectTab: #instance].
	li := html li.
	selectedTab = #class ifTrue: [li class: 'selected'].
	li
	    with: 'Class';
	    onClick: [self selectTab: #class].
	li := html li.
	selectedTab = #comment ifTrue: [li class: 'selected'].
	li
	    with: 'Comment';
	    onClick: [self selectTab: #comment]]
!

updateSourceAndButtons
    self disableSaveButton.
    selectedMethod 
	ifNil: [
	    self hideMethodButtons.
	    selectedClass 
		ifNil: [self hideClassButtons]
		ifNotNil: [self showClassButtons]]
	ifNotNil: [
	    self hideClassButtons
	    self showMethodButtons].
    sourceTextarea asJQuery val: self source
! !

!Browser methodsFor: 'testing'!

canBeClosed
    ^true
! !