Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ while it is read is left out, and the rest of the scrape continues.

`CanopyBrowseHtmlHandler` wraps it. Requests without `text/html` in `Accept` are passed through
unchanged. A browser gets a table per branch, with links into child branches and an editable field
with a Save button per leaf.
with a Save button per leaf. A dropdown below the title reloads the page every 1 to 60 seconds; the
choice is kept in the browser, and no reload happens while an input has the focus.

```smalltalk
Canopy registerSystemMetrics.
Expand Down
24 changes: 23 additions & 1 deletion source/Canopy-Core-Tests/CanopyBrowseHtmlHandlerTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,33 @@ CanopyBrowseHtmlHandlerTest >> testHandleRequestEscapesHtmlSpecialCharactersInVa
] ensure: [ Canopy reset ]
]

{ #category : 'tests' }
CanopyBrowseHtmlHandlerTest >> testHandleRequestRendersAutoRefreshControlOnBranchPage [
"The dropdown sits right below the title, so it is the same place on every page."
| body |
[ self registerBrowseTestDomain.
body := (CanopyBrowseHtmlHandler new handleRequest: (self requestFor: #GET url: 'http://localhost/browseTest' accept: 'text/html' body: nil)) entity contents.
self assert: (body includesSubstring: '</h1><p><select id="refresh">').
self assert: (body includesSubstring: '<option value="0">Auto refresh: off</option>').
self assert: (body includesSubstring: '<option value="5">every 5 s</option>').
self assert: (body includesSubstring: 'location.reload()')
] ensure: [ Canopy reset ]
]

{ #category : 'tests' }
CanopyBrowseHtmlHandlerTest >> testHandleRequestRendersAutoRefreshControlOnLeafPage [
| body |
[ self registerBrowseTestDomain.
body := (CanopyBrowseHtmlHandler new handleRequest: (self requestFor: #GET url: 'http://localhost/browseTest/a' accept: 'text/html' body: nil)) entity contents.
self assert: (body includesSubstring: '</h1><p><select id="refresh">')
] ensure: [ Canopy reset ]
]

{ #category : 'tests' }
CanopyBrowseHtmlHandlerTest >> testHandleRequestRendersHtmlPageForLeafWhenAcceptIsHtml [
[ self registerBrowseTestDomain.
self assert: (CanopyBrowseHtmlHandler new handleRequest: (self requestFor: #GET url: 'http://localhost/browseTest/a' accept: 'text/html' body: nil)) entity contents
equals: '<!DOCTYPE html><html><body><h1>/browseTest/a</h1><p>Value: <code>&quot;x&quot;</code></p><form id="f"><input id="v" value="&quot;x&quot;"><button type="submit">Save</button></form><script>document.getElementById("f").addEventListener("submit",function(e){e.preventDefault();fetch(window.location.pathname,{method:"PUT",body:document.getElementById("v").value}).then(function(){location.reload();});});</script></body></html>'
equals: '<!DOCTYPE html><html><body><h1>/browseTest/a</h1>' , CanopyBrowseHtmlHandler new refreshControl , '<p>Value: <code>&quot;x&quot;</code></p><form id="f"><input id="v" value="&quot;x&quot;"><button type="submit">Save</button></form><script>document.getElementById("f").addEventListener("submit",function(e){e.preventDefault();fetch(window.location.pathname,{method:"PUT",body:document.getElementById("v").value}).then(function(){location.reload();});});</script></body></html>'
] ensure: [ Canopy reset ]
]

Expand Down
11 changes: 11 additions & 0 deletions source/Canopy-Core/CanopyBrowseHtmlHandler.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ CanopyBrowseHtmlHandler >> branchPageFor: node path: pathString [
^ String streamContents: [ :s |
s nextPutAll: '<!DOCTYPE html><html><body>'.
s nextPutAll: '<h1>/'; nextPutAll: (self htmlEscape: pathString); nextPutAll: '</h1>'.
s nextPutAll: self refreshControl.
s nextPutAll: '<table>'.
node keysAndValuesDo: [ :key :child |
| href |
Expand Down Expand Up @@ -97,12 +98,22 @@ CanopyBrowseHtmlHandler >> pageFor: node path: pathString [
^ String streamContents: [ :s |
s nextPutAll: '<!DOCTYPE html><html><body>'.
s nextPutAll: '<h1>/'; nextPutAll: (self htmlEscape: pathString); nextPutAll: '</h1>'.
s nextPutAll: self refreshControl.
s nextPutAll: '<p>Value: <code>'; nextPutAll: (self htmlEscape: currentJson); nextPutAll: '</code></p>'.
s nextPutAll: '<form id="f"><input id="v" value="'; nextPutAll: (self htmlEscape: currentJson); nextPutAll: '"><button type="submit">Save</button></form>'.
s nextPutAll: '<script>document.getElementById("f").addEventListener("submit",function(e){e.preventDefault();fetch(window.location.pathname,{method:"PUT",body:document.getElementById("v").value}).then(function(){location.reload();});});</script>'.
s nextPutAll: '</body></html>' ]
]

{ #category : 'private' }
CanopyBrowseHtmlHandler >> refreshControl [
"A dropdown that reloads the page every n seconds, for watching metrics move. The choice is
kept in the browser's localStorage, so it survives the reload and a click into a child - and
stays with the viewer rather than with the tree. While an input has the focus the reload
waits for the next round, or a value being typed would be thrown away."
^ '<p><select id="refresh"><option value="0">Auto refresh: off</option><option value="1">every 1 s</option><option value="5">every 5 s</option><option value="10">every 10 s</option><option value="30">every 30 s</option><option value="60">every 60 s</option></select></p><script>(function(){var s=document.getElementById("refresh"),k="canopyRefresh",t;try{s.value=localStorage.getItem(k)||"0";}catch(e){}function arm(){clearTimeout(t);var n=parseInt(s.value,10);if(n>0){t=setTimeout(function(){var a=document.activeElement;if(a&&a.tagName==="INPUT"){arm();}else{location.reload();}},n*1000);}}s.addEventListener("change",function(){try{localStorage.setItem(k,s.value);}catch(e){}arm();});arm();})();</script>'
]

{ #category : 'accessing' }
CanopyBrowseHtmlHandler >> restHandler [
^ restHandler ifNil: [ restHandler := CanopyBrowseHandler new ]
Expand Down
Loading