Smalltalk current createPackage: 'Helios-Core'! Object subclass: #HLModel instanceVariableNames: 'announcer environment' package: 'Helios-Core'! !HLModel commentStamp! I am the abstract superclass of all models of Helios. I am the "Model" part of the MVC pattern implementation in Helios. I provide access to an `Environment` object and both a local (model-specific) and global (system-specific) announcer. The `#withChangesDo:` method is handy for performing model changes ensuring that all widgets are aware of the change and can prevent it from happening. Modifications of the system should be done via commands (see `HLCommand` and subclasses).! !HLModel methodsFor: 'accessing'! announcer ^ announcer ifNil: [ announcer := Announcer new ] ! environment ^ environment ifNil: [ self manager environment ] ! environment: anEnvironment environment := anEnvironment ! manager ^ HLManager current ! systemAnnouncer ^ self environment systemAnnouncer ! ! !HLModel methodsFor: 'error handling'! withChangesDo: aBlock [ self announcer announce: (HLAboutToChange new actionBlock: aBlock). aBlock value. ] on: HLChangeForbidden do: [ :ex | ] ! ! !HLModel methodsFor: 'testing'! isBrowserModel ^ false ! isReferencesModel ^ false ! isToolModel ^ false ! ! HLModel subclass: #HLToolModel instanceVariableNames: 'selectedClass selectedPackage selectedProtocol selectedSelector' package: 'Helios-Core'! !HLToolModel commentStamp! I am a model specific to package and class manipulation. All browsers should either use me or a subclass as their model. I provide methods for package, class, protocol and method manipulation and access, forwarding to my environment. I also handle compilation of classes and methods as well as compilation and parsing errors.! !HLToolModel methodsFor: 'accessing'! allSelectors ^ self environment allSelectors ! availableClassNames ^ self environment availableClassNames ! availablePackageNames ^ self environment availablePackageNames ! availablePackages ^ self environment availablePackageNames ! availableProtocols ^ self environment availableProtocolsFor: self selectedClass ! packages ^ self environment packages ! selectedClass ^ selectedClass ! selectedClass: aClass (self selectedClass = aClass and: [ aClass isNil ]) ifTrue: [ ^ self ]. self withChangesDo: [ selectedClass = aClass ifTrue: [ self selectedProtocol: nil ]. aClass ifNil: [ selectedClass := nil ] ifNotNil: [ self selectedPackage: aClass theNonMetaClass package. self showInstance ifTrue: [ selectedClass := aClass theNonMetaClass ] ifFalse: [ selectedClass := aClass theMetaClass ] ]. self selectedProtocol: nil. self announcer announce: (HLClassSelected on: self selectedClass) ] ! selectedMethod ^ self selectedClass ifNotNil: [ self selectedClass methodDictionary at: selectedSelector ifAbsent: [ nil ] ] ! selectedMethod: aCompiledMethod selectedSelector = aCompiledMethod ifTrue: [ ^ self ]. self withChangesDo: [ aCompiledMethod ifNil: [ selectedSelector := nil ] ifNotNil: [ selectedClass := aCompiledMethod methodClass. selectedPackage := selectedClass theNonMetaClass package. selectedSelector := aCompiledMethod selector ]. self announcer announce: (HLMethodSelected on: aCompiledMethod) ] ! selectedPackage ^ selectedPackage ! selectedPackage: aPackage selectedPackage = aPackage ifTrue: [ ^ self ]. self withChangesDo: [ selectedPackage := aPackage. self selectedClass: nil. self announcer announce: (HLPackageSelected on: aPackage) ] ! selectedProtocol ^ selectedProtocol ! selectedProtocol: aString selectedProtocol = aString ifTrue: [ ^ self ]. self withChangesDo: [ selectedProtocol := aString. self selectedMethod: nil. self announcer announce: (HLProtocolSelected on: aString) ] ! ! !HLToolModel methodsFor: 'actions'! addInstVarNamed: aString self environment addInstVarNamed: aString to: self selectedClass. self announcer announce: (HLInstVarAdded new theClass: self selectedClass; variableName: aString; yourself) ! save: aString self announcer announce: HLSourceCodeSaved new. (self shouldCompileClassDefinition: aString) ifTrue: [ self compileClassDefinition: aString ] ifFalse: [ self compileMethod: aString ] ! saveSourceCode self announcer announce: HLSaveSourceCode new ! ! !HLToolModel methodsFor: 'commands actions'! commitPackage "self withHelperLabelled: 'Committing package ', self selectedPackage name, '...' do: [ "self environment commitPackage: self selectedPackage" ]" ! copyClassTo: aClassName self withChangesDo: [ self environment copyClass: self selectedClass theNonMetaClass to: aClassName ] ! moveClassToPackage: aPackageName self withChangesDo: [ self environment moveClass: self selectedClass theNonMetaClass toPackage: aPackageName ] ! moveMethodToClass: aClassName self withChangesDo: [ self environment moveMethod: self selectedMethod toClass: aClassName ] ! moveMethodToProtocol: aProtocol self withChangesDo: [ self environment moveMethod: self selectedMethod toProtocol: aProtocol ] ! openClassNamed: aString | class | self withChangesDo: [ class := self environment classNamed: aString. self selectedPackage: class package. self selectedClass: class ] ! removeClass self withChangesDo: [ self manager confirm: 'Do you REALLY want to remove class ', self selectedClass name ifTrue: [ self environment removeClass: self selectedClass ] ] ! removeMethod self withChangesDo: [ self manager confirm: 'Do you REALLY want to remove method ', self selectedMethod methodClass name,' >> #', self selectedMethod selector ifTrue: [ self environment removeMethod: self selectedMethod ] ] ! removeProtocol self withChangesDo: [ self manager confirm: 'Do you REALLY want to remove protocol ', self selectedProtocol ifTrue: [ self environment removeProtocol: self selectedProtocol from: self selectedClass ] ] ! renameClassTo: aClassName self withChangesDo: [ self environment renameClass: self selectedClass theNonMetaClass to: aClassName ] ! renameProtocolTo: aString self withChangesDo: [ self environment renameProtocol: self selectedProtocol to: aString in: self selectedClass ] ! ! !HLToolModel methodsFor: 'compiling'! compileClassComment: aString self environment compileClassComment: aString for: self selectedClass ! compileClassDefinition: aString self environment compileClassDefinition: aString ! compileMethod: aString | method | self withCompileErrorHandling: [ method := self environment compileMethod: aString for: self selectedClass protocol: self compilationProtocol. self selectedMethod: method ] ! ! !HLToolModel methodsFor: 'defaults'! allProtocol ^ '-- all --' ! unclassifiedProtocol ^ 'as yet unclassified' ! ! !HLToolModel methodsFor: 'error handling'! handleCompileError: anError self announcer announce: (HLCompileErrorRaised new error: anError; yourself) ! handleParseError: anError | split line column messageToInsert | split := anError messageText tokenize: ' : '. messageToInsert := split second. "21 = 'Parse error on line ' size + 1" split := split first copyFrom: 21 to: split first size. split := split tokenize: ' column '. line := split first. column := split second. self announcer announce: (HLParseErrorRaised new line: line asNumber; column: column asNumber; message: messageToInsert; error: anError; yourself) ! handleUnkownVariableError: anError self announcer announce: (HLUnknownVariableErrorRaised new error: anError; yourself) ! withCompileErrorHandling: aBlock self environment evaluate: [ self environment evaluate: [ self environment evaluate: aBlock on: ParseError do: [:ex | self handleParseError: ex ] ] on: UnknownVariableError do: [ :ex | self handleUnkownVariableError: ex ] ] on: CompilerError do: [ :ex | self handleCompileError: ex ] ! ! !HLToolModel methodsFor: 'private'! compilationProtocol | currentProtocol | currentProtocol := self selectedProtocol. currentProtocol ifNil: [ currentProtocol := self unclassifiedProtocol ]. self selectedMethod ifNotNil: [ currentProtocol := self selectedMethod protocol ]. ^ currentProtocol = self allProtocol ifTrue: [ self unclassifiedProtocol ] ifFalse: [ currentProtocol ] ! withHelperLabelled: aString do: aBlock "TODO: doesn't belong here" '#helper' asJQuery remove. [ :html | html div id: 'helper'; with: aString ] appendToJQuery: 'body' asJQuery. [ aBlock value. '#helper' asJQuery remove ] valueWithTimeout: 10 ! ! !HLToolModel methodsFor: 'testing'! isToolModel ^ true ! shouldCompileClassDefinition: aString ^ self selectedClass isNil or: [ aString match: '^[A-Z]' ] ! ! !HLToolModel class methodsFor: 'actions'! on: anEnvironment ^ self new environment: anEnvironment; yourself ! ! ProgressHandler subclass: #HLProgressHandler instanceVariableNames: '' package: 'Helios-Core'! !HLProgressHandler commentStamp! I am a specific progress handler for Helios, displaying progresses in a modal window.! !HLProgressHandler methodsFor: 'progress handling'! do: aBlock on: aCollection displaying: aString HLProgressWidget default do: aBlock on: aCollection displaying: aString ! ! Widget subclass: #HLTabWidget instanceVariableNames: 'widget label root' package: 'Helios-Core'! !HLTabWidget commentStamp! I am a widget specialized into building another widget as an Helios tab. I should not be used directly, `HLWidget class >> #openAsTab` should be used instead. ## Example HLWorkspace openAsTab! !HLTabWidget methodsFor: 'accessing'! activate self manager activate: self ! add self manager addTab: self ! cssClass ^ self widget tabClass ! displayLabel ^ self label size > 20 ifTrue: [ (self label first: 20), '...' ] ifFalse: [ self label ] ! focus self widget canHaveFocus ifTrue: [ self widget focus ] ! label ^ label ifNil: [ '' ] ! label: aString label := aString ! manager ^ HLManager current ! widget ^ widget ! widget: aWidget widget := aWidget ! ! !HLTabWidget methodsFor: 'actions'! hide root ifNotNil: [ root asJQuery css: 'visibility' put: 'hidden' ] ! registerBindings self widget registerBindings ! remove self widget unregister. root ifNotNil: [ root asJQuery remove ] ! show root ifNil: [ self appendToJQuery: 'body' asJQuery ] ifNotNil: [ root asJQuery css: 'visibility' put: 'visible' ] ! ! !HLTabWidget methodsFor: 'rendering'! renderOn: html root := html div class: 'tab'; yourself. self renderTab ! renderTab root contents: [ :html | html div class: 'amber_box'; with: [ self widget renderOn: html ] ] ! ! !HLTabWidget methodsFor: 'testing'! isActive ^ self manager activeTab = self ! ! !HLTabWidget class methodsFor: 'instance creation'! on: aWidget labelled: aString ^ self new widget: aWidget; label: aString; yourself ! ! Widget subclass: #HLWidget instanceVariableNames: 'wrapper' package: 'Helios-Core'! !HLWidget commentStamp! I am the abstract superclass of all Helios widgets. I provide common methods, additional behavior to widgets useful for Helios, like dialog creation, command execution and tab creation. ## API 1. Rendering Instead of overriding `#renderOn:` as with other Widget subclasses, my subclasses should override `#renderContentOn:`. 2. Refreshing To re-render a widget, use `#refresh`. 3. Key bindings registration and tabs When displayed as a tab, the widget has a chance to register keybindings with the `#registerBindingsOn:` hook method. 4. Unregistration When a widget has subscribed to announcements or other actions that need to be cleared when closing the tab, the hook method `#unregister` will be called by helios. 5. Tabs To enable a widget class to be open as a tab, override the class-side `#canBeOpenAsTab` method to answer `true`. `#tabClass` and `#tabPriority` can be overridden too to respectively change the css class of the tab and the order of tabs in the main menu. 6. Command execution An helios command (instance of `HLCommand` or one of its subclass) can be executed with `#execute:`.! !HLWidget methodsFor: 'accessing'! manager ^ HLManager current ! tabClass ^ self class tabClass ! wrapper ^ wrapper ! ! !HLWidget methodsFor: 'actions'! confirm: aString ifTrue: aBlock self manager confirm: aString ifTrue: aBlock ! execute: aCommand HLManager current keyBinder activate; applyBinding: aCommand asBinding ! openAsTab HLManager current addTab: (HLTabWidget on: self labelled: self class tabLabel) ! request: aString do: aBlock self manager request: aString do: aBlock ! request: aString value: valueString do: aBlock self manager request: aString value: valueString do: aBlock ! unregister "This method is called whenever the receiver is closed (as a tab). Widgets subscribing to announcements should unregister there" ! ! !HLWidget methodsFor: 'keybindings'! bindKeyDown: keyDownBlock keyUp: keyUpBlock self wrapper asJQuery keydown: keyDownBlock; keyup: keyUpBlock ! registerBindings self registerBindingsOn: self manager keyBinder bindings ! registerBindingsOn: aBindingGroup ! unbindKeyDownKeyUp self wrapper asJQuery unbind: 'keydown'; unbind: 'keyup' ! ! !HLWidget methodsFor: 'rendering'! renderContentOn: html ! renderOn: html wrapper := html div. [ :renderer | self renderContentOn: renderer ] appendToJQuery: wrapper asJQuery ! ! !HLWidget methodsFor: 'testing'! canHaveFocus ^ false ! ! !HLWidget methodsFor: 'updating'! refresh self wrapper ifNil: [ ^ self ]. self wrapper asJQuery empty. [ :html | self renderContentOn: html ] appendToJQuery: self wrapper asJQuery ! ! !HLWidget class methodsFor: 'accessing'! openAsTab HLManager current addTab: (HLTabWidget on: self new labelled: self tabLabel) ! tabClass ^ '' ! tabLabel ^ 'Tab' ! tabPriority ^ 500 ! ! !HLWidget class methodsFor: 'testing'! canBeOpenAsTab ^ false ! ! HLWidget subclass: #HLFocusableWidget instanceVariableNames: '' package: 'Helios-Core'! !HLFocusableWidget commentStamp! I am a widget that can be focused. ## API Instead of overriding `#renderOn:` as with other `Widget` subclasses, my subclasses should override `#renderContentOn:`. To bring the focus to the widget, use the `#focus` method.! !HLFocusableWidget methodsFor: 'accessing'! focusClass ^ 'focused' ! ! !HLFocusableWidget methodsFor: 'events'! blur self wrapper asJQuery blur ! focus self wrapper asJQuery focus ! ! !HLFocusableWidget methodsFor: 'rendering'! renderContentOn: html ! renderOn: html wrapper := html div class: 'hl_widget'; yourself. wrapper with: [ self renderContentOn: html ]. wrapper at: 'tabindex' put: '0'; onBlur: [ self wrapper asJQuery removeClass: self focusClass ]; onFocus: [ self wrapper asJQuery addClass: self focusClass ] ! ! !HLFocusableWidget methodsFor: 'testing'! canHaveFocus ^ true ! hasFocus ^ self wrapper notNil and: [ self wrapper asJQuery hasClass: self focusClass ] ! ! HLFocusableWidget subclass: #HLListWidget instanceVariableNames: 'items selectedItem mapping' package: 'Helios-Core'! !HLListWidget methodsFor: 'accessing'! cssClassForItem: anObject ^ '' ! items ^ items ifNil: [ items := self defaultItems ] ! items: aCollection items := aCollection ! listCssClassForItem: anObject ^ self selectedItem = anObject ifTrue: [ 'active' ] ifFalse: [ 'inactive' ] ! positionOf: aListItem < return aListItem.parent().children().get().indexOf(aListItem.get(0)) + 1 > ! selectedItem ^ selectedItem ! selectedItem: anObject selectedItem := anObject ! ! !HLListWidget methodsFor: 'actions'! activateFirstListItem self activateListItem: ((wrapper asJQuery find: 'li.inactive') eq: 0) ! activateItem: anObject self activateListItem: (mapping at: anObject ifAbsent: [ ^ self ]) asJQuery ! activateListItem: aListItem | item | (aListItem get: 0) ifNil: [ ^self ]. aListItem parent children removeClass: 'active'. aListItem addClass: 'active'. self ensureVisible: aListItem. "Activate the corresponding item" item := (self items at: (aListItem attr: 'list-data') asNumber). self selectedItem == item ifFalse: [ self selectItem: item ] ! activateNextListItem self activateListItem: (self wrapper asJQuery find: 'li.active') next. "select the first item if none is selected" (self wrapper asJQuery find: ' .active') get ifEmpty: [ self activateFirstListItem ] ! activatePreviousListItem self activateListItem: (self wrapper asJQuery find: 'li.active') prev ! ensureVisible: aListItem "Move the scrollbar to show the active element" | parent position | position := self positionOf: aListItem. parent := aListItem parent. aListItem position top < 0 ifTrue: [ (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem position top - 10) ]. aListItem position top + aListItem height > parent height ifTrue: [ (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem height - (parent height - aListItem position top)) +10 ] ! focus super focus. self items isEmpty ifFalse: [ self selectedItem ifNil: [ self activateFirstListItem ] ] ! refresh super refresh. self ensureVisible: (mapping at: self selectedItem ifAbsent: [ ^ self ]) asJQuery ! selectItem: anObject self selectedItem: anObject ! ! !HLListWidget methodsFor: 'defaults'! defaultItems ^ #() ! ! !HLListWidget methodsFor: 'events'! setupKeyBindings (HLRepeatedKeyDownHandler on: self) whileKeyDown: 38 do: [ self activatePreviousListItem ]; whileKeyDown: 40 do: [ self activateNextListItem ]; rebindKeys ! ! !HLListWidget methodsFor: 'initialization'! initialize super initialize. mapping := Dictionary new. ! ! !HLListWidget methodsFor: 'private'! registerMappingFrom: anObject to: aTag mapping at: anObject put: aTag ! ! !HLListWidget methodsFor: 'rendering'! renderButtonsOn: html ! renderContentOn: html html ul class: 'nav nav-pills nav-stacked'; with: [ self renderListOn: html ]. html div class: 'pane_actions form-actions'; with: [ self renderButtonsOn: html ]. self setupKeyBindings ! renderItem: anObject on: html | li | li := html li. self registerMappingFrom: anObject to: li. li at: 'list-data' put: (self items indexOf: anObject) asString; class: (self listCssClassForItem: anObject); with: [ html a with: [ (html tag: 'i') class: (self cssClassForItem: anObject). self renderItemLabel: anObject on: html ]; onClick: [ self activateListItem: li asJQuery ] ] ! renderItemLabel: anObject on: html html with: anObject asString ! renderListOn: html mapping := Dictionary new. self items do: [ :each | self renderItem: each on: html ] ! ! HLListWidget subclass: #HLNavigationListWidget instanceVariableNames: 'previous next' package: 'Helios-Core'! !HLNavigationListWidget methodsFor: 'accessing'! next ^ next ! next: aWidget next := aWidget. aWidget previous = self ifFalse: [ aWidget previous: self ] ! previous ^ previous ! previous: aWidget previous := aWidget. aWidget next = self ifFalse: [ aWidget next: self ] ! ! !HLNavigationListWidget methodsFor: 'actions'! nextFocus self next ifNotNil: [ self next focus ] ! previousFocus self previous ifNotNil: [ self previous focus ] ! ! !HLNavigationListWidget methodsFor: 'events'! setupKeyBindings super setupKeyBindings. self wrapper asJQuery keydown: [ :e | e which = 39 ifTrue: [ self nextFocus ]. e which = 37 ifTrue: [ self previousFocus ] ] ! ! HLNavigationListWidget subclass: #HLToolListWidget instanceVariableNames: 'model' package: 'Helios-Core'! !HLToolListWidget methodsFor: 'accessing'! commandCategory ^ self label ! label ^ 'List' ! menuCommands "Answer a collection of commands to be put in the cog menu" ^ ((HLToolCommand concreteClasses select: [ :each | each isValidFor: self model ]) collect: [ :each | each for: self model ]) select: [ :each | each category = self commandCategory and: [ each isAction and: [ each isActive ] ] ] ! model ^ model ! model: aBrowserModel model := aBrowserModel. self observeSystem; observeModel ! selectedItem: anItem "Selection changed, update the cog menu" super selectedItem: anItem. self updateMenu ! ! !HLToolListWidget methodsFor: 'actions'! activateListItem: anItem self model withChangesDo: [ super activateListItem: anItem ] ! activateNextListItem self model withChangesDo: [ super activateNextListItem ] ! activatePreviousListItem self model withChangesDo: [ super activatePreviousListItem ] ! observeModel ! observeSystem ! unregister super unregister. self model announcer unsubscribe: self. self model systemAnnouncer unsubscribe: self ! ! !HLToolListWidget methodsFor: 'rendering'! renderContentOn: html self renderHeadOn: html. super renderContentOn: html ! renderHeadOn: html html div class: 'list-label'; with: [ html with: self label. self renderMenuOn: html ] ! renderMenuOn: html | commands | commands := self menuCommands. commands isEmpty ifTrue: [ ^ self ]. html div class: 'btn-group cog'; with: [ html a class: 'btn dropdown-toggle'; at: 'data-toggle' put: 'dropdown'; with: [ (html tag: 'i') class: 'icon-cog' ]. html ul class: 'dropdown-menu pull-right'; with: [ self menuCommands do: [ :each | html li with: [ html a with: each menuLabel; onClick: [ self execute: each ] ] ] ] ] ! ! !HLToolListWidget methodsFor: 'updating'! updateMenu (self wrapper asJQuery find: '.cog') remove. [ :html | self renderMenuOn: html ] appendToJQuery: (self wrapper asJQuery find: '.list-label') ! ! !HLToolListWidget class methodsFor: 'instance creation'! on: aModel ^ self new model: aModel; yourself ! ! HLListWidget subclass: #HLTabListWidget instanceVariableNames: 'callback' package: 'Helios-Core'! !HLTabListWidget commentStamp! I am a widget used to display a list of helios tabs. When a tab is selected, `callback` is evaluated with the selected tab as argument.! !HLTabListWidget methodsFor: 'accessing'! callback ^ callback ifNil: [ [] ] ! callback: aBlock callback := aBlock ! ! !HLTabListWidget methodsFor: 'actions'! selectItem: aTab super selectItem: aTab. self callback value: aTab ! ! !HLTabListWidget methodsFor: 'rendering'! renderItemLabel: aTab on: html html span class: aTab cssClass; with: aTab label ! ! HLWidget subclass: #HLManager instanceVariableNames: 'tabs activeTab environment history' package: 'Helios-Core'! !HLManager methodsFor: 'accessing'! activeTab ^ activeTab ! environment "The default environment used by all Helios objects" ^ environment ifNil: [ environment := self defaultEnvironment ] ! environment: anEnvironment environment := anEnvironment ! history ^ history ifNil: [ history := OrderedCollection new ] ! history: aCollection history := aCollection ! keyBinder ^ HLKeyBinder current ! tabs ^ tabs ifNil: [ tabs := OrderedCollection new ] ! ! !HLManager methodsFor: 'actions'! activate: aTab self keyBinder flushBindings. aTab registerBindings. activeTab := aTab. self refresh; addToHistory: aTab; show: aTab ! addTab: aTab self tabs add: aTab. self activate: aTab ! addToHistory: aTab self removeFromHistory: aTab. self history add: aTab ! confirm: aString ifFalse: aBlock HLConfirmationWidget new confirmationString: aString; cancelBlock: aBlock; show ! confirm: aString ifTrue: aBlock HLConfirmationWidget new confirmationString: aString; actionBlock: aBlock; show ! registerErrorHandler: anErrorHandler self environment registerErrorHandler: anErrorHandler ! registerInspector: anInspector self environment registerInspector: anInspector ! registerProgressHandler: aProgressHandler self environment registerProgressHandler: aProgressHandler ! removeActiveTab self removeTab: self activeTab ! removeFromHistory: aTab self history: (self history reject: [ :each | each == aTab ]) ! removeTab: aTab (self tabs includes: aTab) ifFalse: [ ^ self ]. self removeFromHistory: aTab. self tabs remove: aTab. self keyBinder flushBindings. aTab remove. self refresh. self history ifNotEmpty: [ self history last activate ] ! request: aString do: aBlock self request: aString value: '' do: aBlock ! request: aString value: valueString do: aBlock HLRequestWidget new confirmationString: aString; actionBlock: aBlock; value: valueString; show ! ! !HLManager methodsFor: 'defaults'! defaultEnvironment "If helios is loaded from within a frame, answer the parent window environment" | parent | parent := window opener ifNil: [ window parent ]. parent ifNil: [ ^ Environment new ]. ^ (((parent at: 'requirejs') value: 'amber_vm/smalltalk') at: 'Environment') new ! ! !HLManager methodsFor: 'initialization'! initialize super initialize. HLErrorHandler register. HLProgressHandler register. self registerInspector: HLInspector. self registerErrorHandler: ErrorHandler current. self registerProgressHandler: ProgressHandler current. self keyBinder setupEvents ! ! !HLManager methodsFor: 'rendering'! refresh '.navbar' asJQuery remove. self appendToJQuery: 'body' asJQuery ! renderAddOn: html html li class: 'dropdown'; with: [ html a class: 'dropdown-toggle'; at: 'data-toggle' put: 'dropdown'; with: [ html with: 'Open...'. (html tag: 'b') class: 'caret' ]. html ul class: 'dropdown-menu'; with: [ ((HLWidget withAllSubclasses select: [ :each | each canBeOpenAsTab ]) sorted: [ :a :b | a tabPriority < b tabPriority ]) do: [ :each | html li with: [ html a with: each tabLabel; onClick: [ each openAsTab ] ] ] ] ] ! renderContentOn: html html div class: 'navbar navbar-fixed-top'; with: [ html div class: 'navbar-inner'; with: [ self renderTabsOn: html ] ] ! renderTabsOn: html html ul class: 'nav'; with: [ self tabs do: [ :each | html li class: (each isActive ifTrue: [ 'active' ] ifFalse: [ 'inactive' ]); with: [ html a with: [ ((html tag: 'i') class: 'close') onClick: [ self removeTab: each ]. html span class: each cssClass; with: each displayLabel ]; onClick: [ each activate ] ] ]. self renderAddOn: html ] ! show: aTab self tabs do: [ :each | each hide ]. aTab show; focus ! ! HLManager class instanceVariableNames: 'current'! !HLManager class methodsFor: 'accessing'! current ^ current ifNil: [ current := self basicNew initialize ] ! ! !HLManager class methodsFor: 'initialization'! initialize self current appendToJQuery: 'body' asJQuery. self clearKeydownEvent ! ! !HLManager class methodsFor: 'instance creation'! new "Use current instead" self shouldNotImplement ! ! !HLManager class methodsFor: 'private'! clearKeydownEvent "Prevent default keydown event with arrow key from scrolling the parent page when helios is run inside a frame." 'body' asJQuery keydown: [ :e | (e keyCode >= 37 and: [ e keyCode <= 40 ]) ifTrue: [ false ] ] ! ! HLWidget subclass: #HLModalWidget instanceVariableNames: '' package: 'Helios-Core'! !HLModalWidget commentStamp! I implement an abstract modal widget.! !HLModalWidget methodsFor: 'accessing'! cssClass ^ '' ! ! !HLModalWidget methodsFor: 'actions'! cancel self remove ! confirm "Override in subclasses" self remove ! remove '.dialog' asJQuery removeClass: 'active'. [ '#overlay' asJQuery remove. '.dialog' asJQuery remove ] valueWithTimeout: 300 ! show self appendToJQuery: 'body' asJQuery ! ! !HLModalWidget methodsFor: 'rendering'! hasButtons ^ true ! renderButtonsOn: html | confirmButton | html div class: 'buttons'; with: [ html button class: 'button'; with: 'Cancel'; onClick: [ self cancel ]. confirmButton := html button class: 'button default'; with: 'Confirm'; onClick: [ self confirm ] ]. confirmButton asJQuery focus ! renderContentOn: html | confirmButton | html div id: 'overlay'. html div class: 'dialog ', self cssClass; with: [ self renderMainOn: html. self hasButtons ifTrue: [ self renderButtonsOn: html ] ]. '.dialog' asJQuery addClass: 'active'. self setupKeyBindings ! renderMainOn: html ! setupKeyBindings '.dialog' asJQuery keyup: [ :e | e keyCode = String esc asciiValue ifTrue: [ self cancel ] ] ! ! HLModalWidget subclass: #HLConfirmationWidget instanceVariableNames: 'confirmationString actionBlock cancelBlock' package: 'Helios-Core'! !HLConfirmationWidget commentStamp! I display confirmation messages. Instead of creating an instance directly, use `HLWidget >> #confirm:ifTrue:`.! !HLConfirmationWidget methodsFor: 'accessing'! actionBlock ^ actionBlock ifNil: [ [] ] ! actionBlock: aBlock actionBlock := aBlock ! cancelBlock ^ cancelBlock ifNil: [ [] ] ! cancelBlock: aBlock cancelBlock := aBlock ! confirmationString ^ confirmationString ifNil: [ 'Confirm' ] ! confirmationString: aString confirmationString := aString ! ! !HLConfirmationWidget methodsFor: 'actions'! cancel self cancelBlock value. super cancel ! confirm super confirm. self actionBlock value ! ! !HLConfirmationWidget methodsFor: 'rendering'! renderMainOn: html html span with: self confirmationString ! ! HLConfirmationWidget subclass: #HLRequestWidget instanceVariableNames: 'input value' package: 'Helios-Core'! !HLRequestWidget commentStamp! I display a modal window requesting user input. Instead of creating instances manually, use `HLWidget >> #request:do:` and `#request:value:do:`.! !HLRequestWidget methodsFor: 'accessing'! cssClass ^ 'large' ! value ^ value ifNil: [ '' ] ! value: aString value := aString ! ! !HLRequestWidget methodsFor: 'actions'! confirm super confirm. self actionBlock value: input asJQuery val ! ! !HLRequestWidget methodsFor: 'rendering'! renderMainOn: html super renderMainOn: html. input := html textarea. input asJQuery val: self value ! ! HLModalWidget subclass: #HLProgressWidget instanceVariableNames: 'progressBars visible' package: 'Helios-Core'! !HLProgressWidget commentStamp! I am a widget used to display progress modal dialogs. My default instance is accessed with `HLProgressWidget class >> #default`. See `HLProgressHandler` for usage.! !HLProgressWidget methodsFor: 'accessing'! progressBars ^ progressBars ifNil: [ progressBars := OrderedCollection new ] ! ! !HLProgressWidget methodsFor: 'actions'! addProgressBar: aProgressBar self show. self progressBars add: aProgressBar. aProgressBar appendToJQuery: (self wrapper asJQuery find: '.dialog') ! do: aBlock on: aCollection displaying: aString | progressBar | progressBar := HLProgressBarWidget new parent: self; label: aString; workBlock: aBlock; collection: aCollection; yourself. self addProgressBar: progressBar. progressBar start ! flush self progressBars do: [ :each | self removeProgressBar: each ] ! remove self isVisible ifTrue: [ visible := false. super remove ] ! removeProgressBar: aProgressBar self progressBars remove: aProgressBar ifAbsent: []. aProgressBar wrapper asJQuery remove. self progressBars ifEmpty: [ self remove ] ! show self isVisible ifFalse: [ visible := true. super show ] ! ! !HLProgressWidget methodsFor: 'rendering'! renderMainOn: html self progressBars do: [ :each | html with: each ] ! ! !HLProgressWidget methodsFor: 'testing'! hasButtons ^ false ! isVisible ^ visible ifNil: [ false ] ! ! HLProgressWidget class instanceVariableNames: 'default'! !HLProgressWidget class methodsFor: 'accessing'! default ^ default ifNil: [ default := self new ] ! ! HLModalWidget subclass: #HLTabSelectionWidget instanceVariableNames: 'tabs tabList selectedTab selectCallback cancelCallback confirmCallback' package: 'Helios-Core'! !HLTabSelectionWidget commentStamp! I am a modal window used to select or create tabs.! !HLTabSelectionWidget methodsFor: 'accessing'! cancelCallback ^ cancelCallback ifNil: [ [] ] ! cancelCallback: aBlock cancelCallback := aBlock ! confirmCallback ^ confirmCallback ifNil: [ [] ] ! confirmCallback: aBlock confirmCallback := aBlock ! selectCallback ^ selectCallback ifNil: [ [] ] ! selectCallback: aBlock selectCallback := aBlock ! selectedTab ^ selectedTab ! selectedTab: aTab selectedTab := aTab ! tabs ^ tabs ifNil: [ #() ] ! tabs: aCollection tabs := aCollection ! ! !HLTabSelectionWidget methodsFor: 'actions'! cancel super cancel. self cancelCallback value ! confirm super confirm. self confirmCallback value: self selectedTab ! selectTab: aTab self selectedTab: aTab. self selectCallback value: aTab ! setupKeyBindings super setupKeyBindings. '.dialog' asJQuery keyup: [ :e | e keyCode = String cr asciiValue ifTrue: [ self confirm ] ] ! ! !HLTabSelectionWidget methodsFor: 'rendering'! renderContentOn: html super renderContentOn: html. self tabList focus ! renderMainOn: html html div class: 'title'; with: 'Tab selection'. html with: self tabList ! renderTab: aTab on: html html span class: aTab cssClass; with: aTab label ! renderTabsOn: html self tabs do: [ :each | html li with: [ html a with: [ self renderTab: each on: html ]; onClick: [ self selectTab: each ] ] ] ! tabList tabList ifNil: [ tabList := HLTabListWidget new. tabList callback: [ :tab | self selectTab: tab. tabList focus ]; selectedItem: self selectedTab; items: self tabs ]. ^ tabList ! ! HLWidget subclass: #HLProgressBarWidget instanceVariableNames: 'label parent workBlock collection bar' package: 'Helios-Core'! !HLProgressBarWidget commentStamp! I am a widget used to display a progress bar while iterating over a collection.! !HLProgressBarWidget methodsFor: 'accessing'! collection ^ collection ! collection: aCollection collection := aCollection ! label ^ label ! label: aString label := aString ! parent ^ parent ! parent: aProgress parent := aProgress ! workBlock ^ workBlock ! workBlock: aBlock workBlock := aBlock ! ! !HLProgressBarWidget methodsFor: 'actions'! evaluateAt: anInteger self updateProgress: (anInteger / self collection size) * 100. anInteger <= self collection size ifTrue: [ [ self workBlock value: (self collection at: anInteger). self evaluateAt: anInteger + 1 ] valueWithTimeout: 10 ] ifFalse: [ [ self remove ] valueWithTimeout: 500 ] ! remove self parent removeProgressBar: self ! start "Make sure the UI has some time to update itself between each iteration" self evaluateAt: 1 ! updateProgress: anInteger bar asJQuery css: 'width' put: anInteger asString, '%' ! ! !HLProgressBarWidget methodsFor: 'rendering'! renderContentOn: html html span with: self label. html div class: 'progress'; with: [ bar := html div class: 'bar'; style: 'width: 0%' ] ! ! HLProgressBarWidget class instanceVariableNames: 'default'! !HLProgressBarWidget class methodsFor: 'accessing'! default ^ default ifNil: [ default := self new ] ! ! HLWidget subclass: #HLSUnit instanceVariableNames: '' package: 'Helios-Core'! !HLSUnit class methodsFor: 'accessing'! tabClass ^ 'sunit' ! tabLabel ^ 'SUnit' ! tabPriority ^ 1000 ! ! !HLSUnit class methodsFor: 'testing'! canBeOpenAsTab ^ true ! !