-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
28c6912
commit b9d9b93
Showing
31 changed files
with
2,555 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,18 @@ | ||
# tvDemo | ||
A NodeJS version of the AppleTV demo project | ||
# tvDemo Introduction | ||
|
||
**tvDemo** is a test project (written with NodeJS, ExpressJS and EJS) for the new Apple tvOS. This is actually a modification to the demo code that Apple uses to give an overview of the template functionality of tvOS. I thought the best way to get to grips with it was to try and using some other technolgies to create a duplicate app, and though now whwre near perfect, it does appear to work and I do lile the user of EJS to render the TVML templates. | ||
|
||
|
||
## Prerequisites | ||
Yo will need the latest (at the moment) beta of XCode 7.1 as well as this project if you want to run the code and see what it does (you don't need a physical developer kit to run the app as XCode has a simulator). | ||
|
||
You should create a simple Swift tvOS app project (as explained in the developer docs) and then just set the following to connect to the server when it is running: | ||
|
||
``` | ||
static let TVBaseURL = "http://localhost:9001/" | ||
static let TVBootURL = "\(AppDelegate.TVBaseURL)js/application.js" | ||
``` | ||
|
||
Setting tvBaselURL to wherever the Node server is running. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* This is the main Node app.js | ||
* This is the first file that is run when the server starts | ||
* | ||
* developed by Steve Rogers (@sarmcon) | ||
* Oct 2015 | ||
*/ | ||
|
||
var express = require('express'), | ||
app = express(); | ||
|
||
// set the view engine to ejs | ||
app.set('view engine', 'ejs'); | ||
app.set('views', __dirname + '/views'); | ||
|
||
|
||
// set up our static file route | ||
app.use(express.static(__dirname + '/public')); | ||
|
||
// set up our routes | ||
app.use(require('./routers/tvml')) | ||
|
||
// Start our Server | ||
var server = app.listen(9001, function () { | ||
console.log('Node tvOS sample app now listening on port: %s', server.address().port); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* storage.js | ||
* a quick and dirty helper module to store temp vars while the app is running | ||
* should probably use somthing a bit better - but you get the idea! | ||
* | ||
* developed by Steve Rogers (@sarmcon) | ||
* Oct 2015 | ||
*/ | ||
var store={}; | ||
|
||
|
||
/** | ||
* Exported Functions | ||
*/ | ||
exports.get = getData; | ||
exports.set = saveData; | ||
|
||
|
||
/** | ||
* Save a value to our temp store | ||
* @param key | ||
* @param value | ||
*/ | ||
function saveData(key, value){ | ||
store[key] = value; | ||
} | ||
|
||
/** | ||
* Get a value to our temp store | ||
* @param key | ||
*/ | ||
function getData(key){ | ||
return store[key]||false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"name": "tott", | ||
"version": "1.0.0", | ||
"description": "A Test Node TV Server", | ||
"main": "app.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"author": "Steve Rogers", | ||
"license": "ISC", | ||
"dependencies": { | ||
"ejs": "^2.3.4", | ||
"express": "^4.13.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
* Presenter.js | ||
* | ||
* from Original Apple example code - modfied to use XMLHttpRequest loading in rResouceLoader | ||
* | ||
*/ | ||
|
||
var Presenter = { | ||
|
||
// The default Presenter | ||
defaultPresenter: function(xml) { | ||
if(this.loadingIndicatorVisible) { | ||
navigationDocument.replaceDocument(xml, this.loadingIndicator); | ||
this.loadingIndicatorVisible = false; | ||
} else { | ||
navigationDocument.pushDocument(xml); | ||
} | ||
}, | ||
|
||
// modal presenter | ||
modalDialogPresenter: function(xml) { | ||
navigationDocument.presentModal(xml); | ||
}, | ||
|
||
// menu bar presenter | ||
menuBarItemPresenter: function(xml, ele) { | ||
|
||
var feature = ele.parentNode.getFeature("MenuBarDocument"); | ||
if (feature) { | ||
var currentDoc = feature.getDocument(ele); | ||
if (!currentDoc) { | ||
feature.setDocument(xml, ele); | ||
} | ||
} | ||
}, | ||
|
||
|
||
// document load event handler | ||
load: function(event) { | ||
console.log(event); | ||
|
||
var self = this, | ||
ele = event.target, | ||
templateURL = ele.getAttribute("template"), | ||
presentation = ele.getAttribute("presentation"); | ||
|
||
if (templateURL) { | ||
|
||
self.showLoadingIndicator(presentation); | ||
|
||
// modified to use our XMLHttpRequest Loader | ||
resourceLoader.loadResource(templateURL, function(doc) { | ||
|
||
doc.addEventListener("select", self.load.bind(self)); | ||
doc.addEventListener("highlight", self.load.bind(self)); | ||
|
||
if (self[presentation] instanceof Function) { | ||
self[presentation].call(self, doc, ele); | ||
} else { | ||
self.defaultPresenter.call(self, doc); | ||
} | ||
|
||
}); | ||
} | ||
}, | ||
|
||
// generate doc from TVML code | ||
makeDocument: function(resource) { | ||
if (!Presenter.parser) { | ||
Presenter.parser = new DOMParser(); | ||
} | ||
|
||
var doc = Presenter.parser.parseFromString(resource, "application/xml"); | ||
return doc; | ||
}, | ||
|
||
|
||
// display a loading indicator | ||
showLoadingIndicator: function(presentation) { | ||
|
||
if (!this.loadingIndicator) { | ||
this.loadingIndicator = this.makeDocument(this.loadingTemplate); | ||
} | ||
|
||
if (!this.loadingIndicatorVisible && presentation != "modalDialogPresenter" && presentation != "menuBarItemPresenter") { | ||
navigationDocument.pushDocument(this.loadingIndicator); | ||
this.loadingIndicatorVisible = true; | ||
} | ||
}, | ||
|
||
// remove loading indicator | ||
removeLoadingIndicator: function() { | ||
if (this.loadingIndicatorVisible) { | ||
navigationDocument.removeDocument(this.loadingIndicator); | ||
this.loadingIndicatorVisible = false; | ||
} | ||
}, | ||
|
||
/** | ||
* @description Instead of a loading a template from the server, it can stored in a property | ||
* or variable for convenience. This is generally employed for templates that can be reused and | ||
* aren't going to change often, like a loadingIndicator. | ||
*/ | ||
loadingTemplate: `<?xml version="1.0" encoding="UTF-8" ?> | ||
<document> | ||
<loadingTemplate> | ||
<activityIndicator> | ||
<text>Loading...</text> | ||
</activityIndicator> | ||
</loadingTemplate> | ||
</document>` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* ResourceLoader.js | ||
* | ||
* Helper to load our requested resource using XMLHttpRequest | ||
*/ | ||
|
||
|
||
function ResourceLoader(baseurl) { | ||
this.BASEURL = baseurl; | ||
} | ||
|
||
|
||
/** | ||
* This will load the specified resource and then invoke the callback | ||
* This version uses the XMLHttpRequest method as we do not need to parse the result before using | ||
* as EJS does this for us. | ||
*/ | ||
ResourceLoader.prototype.loadResource = function(url, callback) { | ||
|
||
var self = this; | ||
|
||
var templateXHR = new XMLHttpRequest(); | ||
templateXHR.responseType = "document"; | ||
templateXHR.addEventListener("load", function() { | ||
callback.call(self,templateXHR.responseXML); | ||
}, false); | ||
|
||
templateXHR.open("GET", `${self.BASEURL}` + url, true); | ||
templateXHR.send(); | ||
return templateXHR; | ||
|
||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* application.js | ||
* | ||
* This is the entry point to the application and handles the initial loading of required JavaScript files. | ||
* | ||
* developed by Steve Rogers (@sarmcon) | ||
* Oct 2015 | ||
*/ | ||
var resourceLoader; | ||
|
||
/** | ||
* Run when the App launches | ||
*/ | ||
App.onLaunch = function(options){ | ||
|
||
var javascriptFiles = [ | ||
`${options.BASEURL}js/ResourceLoader.js`, | ||
`${options.BASEURL}js/Presenter.js` | ||
]; | ||
|
||
evaluateScripts(javascriptFiles, function(success) { | ||
|
||
if(success){ | ||
resourceLoader = new ResourceLoader(options.BASEURL); | ||
resourceLoader.loadResource("", function(doc) { | ||
doc.addEventListener("select", Presenter.load.bind(Presenter)); | ||
navigationDocument.pushDocument(doc); | ||
}); | ||
} else { | ||
var alert = createAlert("Evaluate Scripts Error", "There was an error attempting to evaluate the external JavaScript files.\n\n Please check your network connection and try again later."); | ||
navigationDocument.presentModal(alert); | ||
|
||
throw ("Playback Example: unable to evaluate scripts."); | ||
} | ||
|
||
}); | ||
|
||
} | ||
|
||
/** | ||
* This convenience function returns an alert template, which can be used to present errors to the user. | ||
*/ | ||
var createAlert = function(title, description) { | ||
|
||
var alertString = `<?xml version="1.0" encoding="UTF-8" ?> | ||
<document> | ||
<alertTemplate> | ||
<title>${title}</title> | ||
<description>${description}</description> | ||
</alertTemplate> | ||
</document>` | ||
|
||
var parser = new DOMParser(); | ||
|
||
var alertDoc = parser.parseFromString(alertString, "application/xml"); | ||
|
||
return alertDoc | ||
} |
Oops, something went wrong.