OSCON
Brian Capouch, Ben Davisson, Danilo Zekovic
Saint Joseph's College
A client application loads instead of a traditional page
Views are generated on the client, instead of server trips
By analogy, control belongs to a client-side "shell"
The page context theoretically never changes
Routing is done on both client and server
Navigation and SEO semantics are fraught
<body>
<main id="main"/>
<script src="/js/bundle.js"></script>
</body>
public/index.html:15
The entire body of index.html
ReactDOM.render((
<Router history={ browserHistory }>
<Route path="/" component={ osconSPA } history={ browserHistory }>
<IndexRoute name="home" component={ Home }/>
<Route path="browse" component= { Browse }/>
<Route path="zoomer" component= { Zoom }/>
<Route path="zoomer/:imageId" component={ Zoom }/>
<Route path="upload" component={ Upload }/>
<Route path="slides" component={ SlideShow } />
<Route path="slides/:viewSet" component={ SlideShow } />
<Route path="*" component={ Home }/>
</Route>
</Router>
), document.getElementById("main"))
js/Shell.jsx:39
The top-level code that fills the main div
A system to assist in archiving, cataloging, finding, and rendering high-definition scanned images
Each of the view modules exports a single default
Awesome syntactic feature: the name you assign to the default export DOESN'T MATTER!!!!
export default class UniqueNewYork extends React.Component {
constructor(props) {
super(props)
}
render() {
// This is the default case, i.e. Zoomer with no parameters
let sendParms = "../tiles/bremer"
// This fires if properties are sent explictly
if ( this.props.params.imageId ) {
sendParms = '/tiles/' + this.props.params.imageId
}
return (
<Section>
<center><ZoomBox image={sendParms}/></center>
</Section>
)
}
}
/js/Zoom.js:51
// Render composite component
export default React.createClass ( {
render() {
return (
<div>
<InfoTable
url={ assetBase + queryTarget}/>
</div>
)
}
})
js/Browse.js:198
// fieldValues provide a form's default input--in this case empty
let fieldValues = {
title : null,
description : null,
source : null,
taglist : null
}
// We blank fields after each data entry event
const blankFieldValues = {
title: null,
description: null,
source: null,
taglist: null
}
js/Upload.js:74
fieldValues = Object.assign({}, fieldValues, fields)
js/Upload.js:103
export default {
...image,
...geopoint
};
graphql/queries/index.js:4
At the moment I worked this out I thought, "WUT?"
this.setState({fetchURL: searchURL}, function(){
this.loadRecordsFromServer()
sessionStorage.setItem('browse', JSON.stringify(this.state))
}.bind(this))
js/Browse.js:148
This is the basic operation we want, to set a React state variable
this.setState({fetchURL: searchURL})
But the operation is asynchronous - i.e. we have to use a callback when it finishes
// Once we know our change is in place, reload current working set and cache component state
function(){
this.loadRecordsFromServer()
sessionStorage.setItem('browse', JSON.stringify(this.state))
}
exports.operation = (function() {
var num1 = 4; // this is a local variable
function addNumbers (num2) {
var sum = num1 + num2;
return sum;
}
return {
addNumbers : addNumbers
};
})();
Module as function
ES6 solves many problems from past
let, const and var
let num1 = 4
function addNumbers (num2) {
return num1 + num2
}
export default addNumbers
ES6 modules
import addNumbers from './addNumbers
function moduleName() {
// do something
...
}
module.exports = moduleName;
CommonJS
var moduleName = require("moduleName");
define([], function) {
var foo = "some value"; // local variable
return {
bar : function(){
// some code to be returned
}
}
}
AMD
require(['someFile'], function ( bar ) {
// your code
bar.doSomething();
});
Client
Server
HTTP request
HTML page
Server
Client
Router
HTTP request
View
Client Side
Server Side
router.get('/', function(req, res) {
res.sendFile('index.html', options)
})
router.get('/upload', function(req, res) {
res.sendFile('index.html', options)
})
Server Side (Express)
ON MATCH "route/example"
FUNCTION execute functionality
VIEW render requested page
AngularJS
.when("/routes/example", {
templateUrl : "views/example.html",
controller : "exampleController"
})
Third party routing library (page.js)
page('/', index);
page('/upload', upload);
page('/browse', browse);
...
htmlElement.hide()
htmlElemetn.show()
jQuery
render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="/about" component={About}/>
<Route path="/contact" component={Contact}/>
</Route>
</Router>
), document.getElementById('app'))
React Router
<NavItem><NavLink to="/about" className="nav-link">About</NavLink></NavItem>
<NavItem><NavLink to="/contact" className="nav-link">Contact</NavLink></NavItem>
Default router
Parameters (:someoption)
History (HTML5)
Master
Two variations:
- uses online storage
- uses local storage
Switched by Source Code
Electron-dist
- storage on cloud
Electron-local
- storage on local file system
Switched by Branches
New Concept:
Single Codebase serves as Web App & Desktop App
Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS
Large Files on GitHub
Assets must be installed Locally
Requires change in the source code
Browse.js
SlideShow.js
Zoom.js
Line 20
// 1.
// Select one of the two to configure for local/cloud access
// Local assets
// const assetBase = '/oscon-test?'
//
// Cloud assets
const assetBase = 'http://oscon.saintjoe-cs.org:2016/oscon-test?'
// 2.
// Local assets
// let searchURL = assetBase + queryTarget
// Cloud assets
let searchURL = 'http://oscon.saintjoe-cs.org:2016/oscon-test?' + queryTarget
Line 141
Line 14
// 2.
// cloud assets:
const urlBase = 'http://oscon.saintjoe-cs.org:2016/'
// local assets:
// const urlBase = '/'
Line 67
// 1.
// Cloud assets
const assetBase = 'http://oscon.saintjoe-cs.org:2016/oscon-test?'
//
// Local assets
// const assetBase = '/oscon-test?'
Line 15
// 1.
// Use cloud-based image assets
const assetBase ="http://oscon.saintjoe-cs.org:2016/"
//
// Use local assets
// const assetBase = ''