mirror of
https://github.com/SEPPDROID/JellyCAT.git
synced 2025-10-22 16:04:20 +00:00
Some testing & cleanup
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
// /\_/|
|
||||
// { ' ' } JellyCAT
|
||||
// \____\
|
||||
|
||||
overrideConsoleLog();
|
||||
console.log("Successfully overwritten console log, sending logs to JCATHOST now.")
|
||||
|
||||
// ***************************************************
|
||||
// JellyCAT Logger | Jclogger
|
||||
// Function to override console.log, console.error, and console.warn and send logs to the JellyCAT stHack server
|
||||
// We shall never send any sensitive information!!
|
||||
function overrideConsoleLog() {
|
||||
var originalConsoleLog = console.log;
|
||||
var originalConsoleError = console.error;
|
||||
var originalConsoleWarn = console.warn;
|
||||
|
||||
console.log = function () {
|
||||
// Call the original console.log
|
||||
originalConsoleLog.apply(console, arguments);
|
||||
|
||||
// Send the log to the server
|
||||
logToServer("LOG: " + JSON.stringify(arguments));
|
||||
};
|
||||
|
||||
console.error = function () {
|
||||
// Call the original console.error
|
||||
originalConsoleError.apply(console, arguments);
|
||||
|
||||
// Send the error to the server
|
||||
logToServer("ERROR: " + JSON.stringify(arguments));
|
||||
};
|
||||
|
||||
console.warn = function () {
|
||||
// Call the original console.warn
|
||||
originalConsoleWarn.apply(console, arguments);
|
||||
|
||||
// Send the warning to the server
|
||||
logToServer("WARNING: " + JSON.stringify(arguments));
|
||||
};
|
||||
}
|
||||
|
||||
// Function to log console information to the server
|
||||
function logToServer(logData) {
|
||||
var logEndpoint = "http://jcathost.dns/log"; // insecure for now
|
||||
|
||||
var logRequest = new XMLHttpRequest();
|
||||
logRequest.open("POST", logEndpoint, true);
|
||||
logRequest.setRequestHeader("Content-Type", "application/json");
|
||||
|
||||
var logPayload = {
|
||||
timestamp: new Date().toISOString(),
|
||||
logData: logData
|
||||
};
|
||||
|
||||
logRequest.send(JSON.stringify(logPayload));
|
||||
}
|
562
app/js/jcm.js
Normal file
562
app/js/jcm.js
Normal file
@@ -0,0 +1,562 @@
|
||||
// /\_/|
|
||||
// { ' ' } JellyCAT
|
||||
// \____\
|
||||
|
||||
fetchSettings(function() {
|
||||
overrideConsoleLog();
|
||||
console.log("Successfully retrieved settings & overwritten console log (JCM), sending logs to JCATHOST now.")
|
||||
});
|
||||
|
||||
// ***************************************************
|
||||
// JellyCAT Logger | Jclogger
|
||||
// Function to override console.log, console.error, and console.warn and send logs to the JellyCAT stHack server
|
||||
|
||||
function overrideConsoleLog() {
|
||||
var originalConsoleLog = console.log;
|
||||
var originalConsoleError = console.error;
|
||||
var originalConsoleWarn = console.warn;
|
||||
|
||||
console.log = function () {
|
||||
// Call the original console.log
|
||||
originalConsoleLog.apply(console, arguments);
|
||||
|
||||
// Send the log to the server
|
||||
logToServer("LOG: " + JSON.stringify(arguments));
|
||||
};
|
||||
|
||||
console.error = function () {
|
||||
// Call the original console.error
|
||||
originalConsoleError.apply(console, arguments);
|
||||
|
||||
// Send the error to the server
|
||||
logToServer("ERROR: " + JSON.stringify(arguments));
|
||||
};
|
||||
|
||||
console.warn = function () {
|
||||
// Call the original console.warn
|
||||
originalConsoleWarn.apply(console, arguments);
|
||||
|
||||
// Send the warning to the server
|
||||
logToServer("WARNING: " + JSON.stringify(arguments));
|
||||
};
|
||||
}
|
||||
|
||||
// Function to log console information to the server
|
||||
// This however opens up a POI for attackers, so only leave this exposed in a dev environment. #todo: add in RM.MD
|
||||
function logToServer(logData) {
|
||||
var logEndpoint = "https://" + atv.jcathost.SigHost + "/log";
|
||||
|
||||
var logRequest = new XMLHttpRequest();
|
||||
logRequest.open("POST", logEndpoint, true);
|
||||
logRequest.setRequestHeader("Content-Type", "application/json");
|
||||
|
||||
var logPayload = {
|
||||
timestamp: new Date().toISOString(),
|
||||
logData: logData
|
||||
};
|
||||
|
||||
logRequest.send(JSON.stringify(logPayload));
|
||||
}
|
||||
|
||||
// ***************************************************
|
||||
// JellyCAT Host fetcher
|
||||
// Function to fetch information we need from the host server
|
||||
// fetch JSON from the HTTP URL using XMLHttpRequest
|
||||
|
||||
function fetchSettings(callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'http://jcathost.dns/atvcsettings', true);
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
var data = JSON.parse(xhr.responseText);
|
||||
|
||||
// Store all properties in atv.jcathost
|
||||
atv.jcathost = {
|
||||
SigHost: data.sig_host,
|
||||
SigHostPort: data.sig_host_p,
|
||||
HostIP: data.host_ip,
|
||||
System: data.system,
|
||||
Version: data.version,
|
||||
HelloMessage: data.hello,
|
||||
// Add other properties as needed
|
||||
};
|
||||
|
||||
// Execute the callback after setting atv.jcathost
|
||||
callback();
|
||||
} catch (jsonError) {
|
||||
console.error('Error parsing JSON:', jsonError);
|
||||
}
|
||||
} else {
|
||||
console.error('Error fetching settings. Status:', xhr.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
// ***************************************************
|
||||
// ATVUtils - a JavaScript helper library for Apple TV
|
||||
// Copied & Edited in full from:
|
||||
// https://kortv.com/appletv/js/application.js
|
||||
// https://github.com/wahlmanj/sample-aTV/blob/master/js/application.js
|
||||
|
||||
var atvutils = ATVUtils = {
|
||||
makeRequest: function(url, method, headers, body, callback) {
|
||||
if ( !url ) {
|
||||
throw "loadURL requires a url argument";
|
||||
}
|
||||
|
||||
var method = method || "GET",
|
||||
headers = headers || {},
|
||||
body = body || "";
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() {
|
||||
try {
|
||||
if (xhr.readyState == 4 ) {
|
||||
if ( xhr.status == 200) {
|
||||
callback(xhr.responseXML);
|
||||
} else {
|
||||
console.log("makeRequest received HTTP status " + xhr.status + " for " + url);
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('makeRequest caught exception while processing request for ' + url + '. Aborting. Exception: ' + e);
|
||||
xhr.abort();
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
xhr.open(method, url, true);
|
||||
|
||||
for(var key in headers) {
|
||||
xhr.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
|
||||
xhr.send();
|
||||
return xhr;
|
||||
},
|
||||
|
||||
makeErrorDocument: function(message, description) {
|
||||
if ( !message ) {
|
||||
message = "";
|
||||
}
|
||||
if ( !description ) {
|
||||
description = "";
|
||||
}
|
||||
|
||||
var errorXML = '<?xml version="1.0" encoding="UTF-8"?> \
|
||||
<atv> \
|
||||
<body> \
|
||||
<dialog id="com.sample.error-dialog"> \
|
||||
<title><![CDATA[' + message + ']]></title> \
|
||||
<description><![CDATA[' + description + ']]></description> \
|
||||
</dialog> \
|
||||
</body> \
|
||||
</atv>';
|
||||
|
||||
return atv.parseXML(errorXML);
|
||||
},
|
||||
|
||||
siteUnavailableError: function() {
|
||||
// TODO: localize
|
||||
return this.makeErrorDocument("JellyCAT is currently unavailable. Try again later.", "Check JCHOST for log information.");
|
||||
},
|
||||
|
||||
loadError: function(message, description) {
|
||||
atv.loadXML(this.makeErrorDocument(message, description));
|
||||
},
|
||||
|
||||
loadAndSwapError: function(message, description) {
|
||||
atv.loadAndSwapXML(this.makeErrorDocument(message, description));
|
||||
},
|
||||
|
||||
loadURLInternal: function(url, method, headers, body, loader) {
|
||||
var me = this,
|
||||
xhr,
|
||||
proxy = new atv.ProxyDocument;
|
||||
|
||||
proxy.show();
|
||||
|
||||
proxy.onCancel = function() {
|
||||
if ( xhr ) {
|
||||
xhr.abort();
|
||||
}
|
||||
};
|
||||
|
||||
xhr = me.makeRequest(url, method, headers, body, function(xml) {
|
||||
try {
|
||||
loader(proxy, xml);
|
||||
} catch(e) {
|
||||
console.error("Caught exception in for " + url + ". " + e);
|
||||
loader(me.siteUnavailableError());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
loadURL: function( options ) { //url, method, headers, body, processXML) {
|
||||
var me = this;
|
||||
if( typeof( options ) === "string" ) {
|
||||
var url = options;
|
||||
} else {
|
||||
var url = options.url,
|
||||
method = options.method || null,
|
||||
headers = options.headers || null,
|
||||
body = options.body || null,
|
||||
processXML = options.processXML || null;
|
||||
}
|
||||
|
||||
this.loadURLInternal(url, method, headers, body, function(proxy, xml) {
|
||||
if(typeof(processXML) == "function") processXML.call(this, xml);
|
||||
try {
|
||||
proxy.loadXML(xml, function(success) {
|
||||
if ( !success ) {
|
||||
console.log("loadURL failed to load " + url);
|
||||
proxy.loadXML(me.siteUnavailableError());
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("loadURL caught exception while loading " + url + ". " + e);
|
||||
proxy.loadXML(me.siteUnavailableError());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// loadAndSwapURL can only be called from page-level JavaScript of the page that wants to be swapped out.
|
||||
loadAndSwapURL: function( options ) { //url, method, headers, body, processXML) {
|
||||
var me = this;
|
||||
if( typeof( options ) === "string" ) {
|
||||
var url = options;
|
||||
} else {
|
||||
var url = options.url,
|
||||
method = options.method || null,
|
||||
headers = options.headers || null,
|
||||
body = options.body || null,
|
||||
processXML = options.processXML || null;
|
||||
}
|
||||
|
||||
this.loadURLInternal(url, method, headers, body, function(proxy, xml) {
|
||||
if(typeof(processXML) == "function") processXML.call(this, xml);
|
||||
try {
|
||||
proxy.loadXML(xml, function(success) {
|
||||
if ( success ) {
|
||||
atv.unloadPage();
|
||||
} else {
|
||||
console.log("loadAndSwapURL failed to load " + url);
|
||||
proxy.loadXML(me.siteUnavailableError(), function(success) {
|
||||
if ( success ) {
|
||||
atv.unloadPage();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("loadAndSwapURL caught exception while loading " + url + ". " + e);
|
||||
proxy.loadXML(me.siteUnavailableError(), function(success) {
|
||||
if ( success ) {
|
||||
atv.unloadPage();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to manage setting and retrieving data from local storage
|
||||
*/
|
||||
data: function(key, value) {
|
||||
if(key && value) {
|
||||
try {
|
||||
atv.localStorage.setItem(key, value);
|
||||
return value;
|
||||
} catch(error) {
|
||||
console.error('Failed to store data element: '+ error);
|
||||
}
|
||||
|
||||
} else if(key) {
|
||||
try {
|
||||
return atv.localStorage.getItem(key);
|
||||
} catch(error) {
|
||||
console.error('Failed to retrieve data element: '+ error);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
deleteData: function(key) {
|
||||
try {
|
||||
atv.localStorage.removeItem(key);
|
||||
} catch(error) {
|
||||
console.error('Failed to remove data element: '+ error);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @params options.name - string node name
|
||||
* @params options.text - string textContent
|
||||
* @params options.attrs - array of attribute to set {"name": string, "value": string, bool}
|
||||
* @params options.children = array of childNodes same values as options
|
||||
* @params doc - document to attach the node to
|
||||
* returns node
|
||||
*/
|
||||
createNode: function(options, doc) {
|
||||
var doc = doc || document;
|
||||
options = options || {};
|
||||
|
||||
if(options.name && options.name != '') {
|
||||
var newElement = doc.makeElementNamed(options.name);
|
||||
|
||||
if(options.text) newElement.textContent = options.text;
|
||||
|
||||
if(options.attrs) {
|
||||
options.attrs.forEach(function(e, i, a) {
|
||||
newElement.setAttribute(e.name, e.value);
|
||||
}, this);
|
||||
}
|
||||
|
||||
if(options.children) {
|
||||
options.children.forEach(function(e,i,a) {
|
||||
newElement.appendChild( this.createNode( e, doc ) );
|
||||
}, this)
|
||||
}
|
||||
|
||||
return newElement;
|
||||
}
|
||||
},
|
||||
|
||||
validEmailAddress: function( email ) {
|
||||
var emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
|
||||
isValid = email.search( emailRegex );
|
||||
return ( isValid > -1 );
|
||||
},
|
||||
|
||||
softwareVersionIsAtLeast: function( version ) {
|
||||
var deviceVersion = atv.device.softwareVersion.split('.'),
|
||||
requestedVersion = version.split('.');
|
||||
|
||||
// We need to pad the device version length with "0" to account for 5.0 vs 5.0.1
|
||||
if( deviceVersion.length < requestedVersion.length ) {
|
||||
var difference = requestedVersion.length - deviceVersion.length,
|
||||
dvl = deviceVersion.length;
|
||||
|
||||
for( var i = 0; i < difference; i++ ) {
|
||||
deviceVersion[dvl + i] = "0";
|
||||
};
|
||||
};
|
||||
|
||||
// compare the same index from each array.
|
||||
for( var c = 0; c < deviceVersion.length; c++ ) {
|
||||
var dv = deviceVersion[c],
|
||||
rv = requestedVersion[c] || "0";
|
||||
|
||||
if( parseInt( dv ) > parseInt( rv ) ) {
|
||||
return true;
|
||||
} else if( parseInt( dv ) < parseInt( rv ) ) {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
// If we make it this far the two arrays are identical, so we're true
|
||||
return true;
|
||||
},
|
||||
|
||||
shuffleArray: function( arr ) {
|
||||
var tmp, current, top = arr.length;
|
||||
|
||||
if(top) {
|
||||
while(--top) {
|
||||
current = Math.floor(Math.random() * (top + 1));
|
||||
tmp = arr[current];
|
||||
arr[current] = arr[top];
|
||||
arr[top] = tmp;
|
||||
};
|
||||
};
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
loadTextEntry: function( textEntryOptions ) {
|
||||
var textView = new atv.TextEntry;
|
||||
|
||||
textView.type = textEntryOptions.type || "emailAddress";
|
||||
textView.title = textEntryOptions.title || "";
|
||||
textView.image = textEntryOptions.image || null;
|
||||
textView.instructions = textEntryOptions.instructions || "";
|
||||
textView.label = textEntryOptions.label || "";
|
||||
textView.footnote = textEntryOptions.footnote || "";
|
||||
textView.defaultValue = textEntryOptions.defaultValue || null;
|
||||
textView.defaultToAppleID = textEntryOptions.defaultToAppleID || false;
|
||||
textView.onSubmit = textEntryOptions.onSubmit,
|
||||
textView.onCancel = textEntryOptions.onCancel,
|
||||
|
||||
textView.show();
|
||||
},
|
||||
|
||||
log: function ( message , level ) {
|
||||
var debugLevel = atv.sessionStorage.getItem( "DEBUG_LEVEL" ),
|
||||
level = level || 0;
|
||||
|
||||
if( level <= debugLevel ) {
|
||||
console.log( message );
|
||||
}
|
||||
},
|
||||
|
||||
accessibilitySafeString: function ( string ) {
|
||||
var string = unescape( string );
|
||||
|
||||
string = string
|
||||
.replace( /&/g, 'and' )
|
||||
.replace( /&/g, 'and' )
|
||||
.replace( /</g, 'less than' )
|
||||
.replace( /\</g, 'less than' )
|
||||
.replace( />/g, 'greater than' )
|
||||
.replace( /\>/g, 'greater than' );
|
||||
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
// Extend atv.ProxyDocument to load errors from a message and description.
|
||||
if( atv.ProxyDocument ) {
|
||||
atv.ProxyDocument.prototype.loadError = function(message, description) {
|
||||
var doc = atvutils.makeErrorDocument(message, description);
|
||||
this.loadXML(doc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// atv.Document extensions
|
||||
if( atv.Document ) {
|
||||
atv.Document.prototype.getElementById = function(id) {
|
||||
var elements = this.evaluateXPath("//*[@id='" + id + "']", this);
|
||||
if ( elements && elements.length > 0 ) {
|
||||
return elements[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// atv.Element extensions
|
||||
if( atv.Element ) {
|
||||
atv.Element.prototype.getElementsByTagName = function(tagName) {
|
||||
return this.ownerDocument.evaluateXPath("descendant::" + tagName, this);
|
||||
}
|
||||
|
||||
atv.Element.prototype.getElementByTagName = function(tagName) {
|
||||
var elements = this.getElementsByTagName(tagName);
|
||||
if ( elements && elements.length > 0 ) {
|
||||
return elements[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple Array Sorting methods
|
||||
Array.prototype.sortAsc = function() {
|
||||
this.sort(function( a, b ){
|
||||
return a - b;
|
||||
});
|
||||
};
|
||||
|
||||
Array.prototype.sortDesc = function() {
|
||||
this.sort(function( a, b ){
|
||||
return b - a;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Date methods and properties
|
||||
Date.lproj = {
|
||||
"DAYS": {
|
||||
"en": {
|
||||
"full": ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
||||
"abbrv": ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
},
|
||||
"en_GB": {
|
||||
"full": ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
||||
"abbrv": ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
}
|
||||
},
|
||||
"MONTHS": {
|
||||
"en": {
|
||||
"full": ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
"abbrv": ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
},
|
||||
"en_GB": {
|
||||
"full": ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
"abbrv": ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Date.prototype.getLocaleMonthName = function( type ) {
|
||||
var language = atv.device.language,
|
||||
type = ( type === true ) ? "abbrv" : "full",
|
||||
MONTHS = Date.lproj.MONTHS[ language ] || Date.lproj.MONTHS[ "en" ];
|
||||
|
||||
return MONTHS[ type ][ this.getMonth() ];
|
||||
};
|
||||
|
||||
Date.prototype.getLocaleDayName = function( type ) {
|
||||
var language = atv.device.language,
|
||||
type = ( type === true ) ? "abbrv" : "full",
|
||||
DAYS = Date.lproj.DAYS[ language ] || Date.lproj.DAYS[ "en" ];
|
||||
|
||||
return DAYS[ type ][ this.getDay() ];
|
||||
};
|
||||
|
||||
Date.prototype.nextDay = function( days ) {
|
||||
var oneDay = 86400000,
|
||||
days = days || 1;
|
||||
this.setTime( new Date( this.valueOf() + ( oneDay * days ) ) );
|
||||
};
|
||||
|
||||
Date.prototype.prevDay = function( days ) {
|
||||
var oneDay = 86400000,
|
||||
days = days || 1;
|
||||
this.setTime( new Date( this.valueOf() - ( oneDay * days ) ) );
|
||||
};
|
||||
|
||||
|
||||
// String Trim methods
|
||||
String.prototype.trim = function ( ch )
|
||||
{
|
||||
var ch = ch || '\\s',
|
||||
s = new RegExp( '^['+ch+']+|['+ch+']+$','g');
|
||||
return this.replace(s,'');
|
||||
};
|
||||
|
||||
String.prototype.trimLeft = function ( ch )
|
||||
{
|
||||
var ch = ch || '\\s',
|
||||
s = new RegExp( '^['+ch+']+','g');
|
||||
return this.replace(s,'');
|
||||
};
|
||||
|
||||
String.prototype.trimRight = function ( ch )
|
||||
{
|
||||
var ch = ch || '\\s',
|
||||
s = new RegExp( '['+ch+']+$','g');
|
||||
return this.replace(s,'');
|
||||
};
|
||||
|
||||
String.prototype.xmlEncode = function()
|
||||
{
|
||||
var string = unescape( this );
|
||||
|
||||
string = string
|
||||
.replace( /&/g, '&' )
|
||||
.replace( /\</g, '<' )
|
||||
.replace( /\>/g, '>' );
|
||||
|
||||
return string;
|
||||
};
|
||||
|
||||
console.log('Reached EOF!');
|
||||
|
205
app/js/jsvtestpage.js
Normal file
205
app/js/jsvtestpage.js
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* https://github.com/wahlmanj/sample-aTV/blob/dba73806c21183fb35d6edca94b960691d8e5d66/js/views/text-view/text-view.js
|
||||
* text-view.js - Demonstrates atv.TextView
|
||||
* TextView's have an attributedString parameter that is an Object with two required properties: string and attributes.
|
||||
* - string is the string displayed in the text view on screen.
|
||||
* - attributes is an Object with two required properties (pointSize and color) and optional properties (alignment, weight, and breakMode).
|
||||
*
|
||||
* attribute Object properties
|
||||
* pointSize: Required. A number that is the size of the font in points
|
||||
* color: Required. An object with 3 required properties (red, green, and blue) and one optional property (alpha). Each of the color components is a number from 0 to 1.
|
||||
* alignment: Optional. left, right, center, or justify
|
||||
* weight: Optional. light, normal, or heavy
|
||||
* breakMode: Optional. clip, word-wrap, truncate-head, truncate-tail, or truncate-middle.
|
||||
clip: the text will be displayed on a single line and cut off at the end of the text view
|
||||
word-wrap: the text will word wrap until there is no more space, at which point it is clipped to the text view
|
||||
truncate-head: the text will be displayed on a single line with an ellipsis at the beginning if the string would be clipped otherwise
|
||||
truncate-tail: the text will be displayed on a single line with an ellipsis at the end if the string would be clipped otherwise
|
||||
truncate-middle: the text will be displayed on a single line with an ellipsis in the middle if the string would be clipped otherwise
|
||||
*/
|
||||
|
||||
var loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ut arcu ut lectus pellentesque ultrices. Sed eu neque et nisl feugiat interdum. Praesent rutrum magna in massa consectetur semper. Nullam gravida odio sit amet purus mattis porta. Sed in est suscipit ipsum imperdiet dapibus. Curabitur cursus fermentum lectus, quis aliquam magna convallis vitae. Maecenas egestas molestie rhoncus. Morbi erat neque, egestas quis condimentum quis, mattis a est. Maecenas vel ipsum a dui posuere tincidunt. Suspendisse tincidunt posuere pretium. Sed in quam eget nibh ultricies dictum. Aliquam est lorem, vestibulum nec congue id, tincidunt vel nunc. Nulla in sem massa, a fermentum purus. In hac habitasse platea dictumst. Vestibulum euismod iaculis justo id rhoncus.";
|
||||
|
||||
var screenFrame = atv.device.screenFrame;
|
||||
var containerView = new atv.View();
|
||||
var exampleTextViews = [];
|
||||
containerView.frame = screenFrame;
|
||||
|
||||
var startY = screenFrame.height * 0.05;
|
||||
var currentY = startY;
|
||||
var hPadding = screenFrame.width * 0.03;
|
||||
var currentX = hPadding;
|
||||
var height = screenFrame.height * 0.12;
|
||||
var width = screenFrame.width * 0.25;
|
||||
|
||||
function addTextView () {
|
||||
var textView = new atv.TextView();
|
||||
textView.backgroundColor = { red: 0.2, blue: 0.2, green: 0.2 };
|
||||
|
||||
var vSpacing = screenFrame.height * 0.01;
|
||||
|
||||
textView.frame = {
|
||||
x: currentX,
|
||||
y: currentY,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
|
||||
currentY += height + vSpacing;
|
||||
|
||||
if ( currentY + height + vSpacing > containerView.frame.height )
|
||||
{
|
||||
currentY = startY;
|
||||
currentX += width + hPadding;
|
||||
}
|
||||
|
||||
exampleTextViews.push(textView);
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
//
|
||||
// TextView using only the required attributes
|
||||
//
|
||||
var onlyRequiredAttributesView = addTextView();
|
||||
onlyRequiredAttributesView.attributedString = {
|
||||
string: "Only Required Attributes: " + loremIpsum,
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 }
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// TextView's demonstrating alignments
|
||||
//
|
||||
var rightAlignmentView = addTextView();
|
||||
rightAlignmentView.attributedString = {
|
||||
string: "Right Alignment",
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
alignment: "right"
|
||||
}
|
||||
};
|
||||
|
||||
var centerAlignmentView = addTextView();
|
||||
centerAlignmentView.attributedString = {
|
||||
string: "Center Alignment",
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
alignment: "center"
|
||||
}
|
||||
};
|
||||
|
||||
var leftAlignmentView = addTextView();
|
||||
leftAlignmentView.attributedString = {
|
||||
string: "Left Alignment",
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
alignment: "left"
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// TextView's demonstrating weights
|
||||
//
|
||||
var normalWeight = addTextView();
|
||||
normalWeight.attributedString = {
|
||||
string: "Normal Weight",
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 40.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
weight: "normal"
|
||||
}
|
||||
};
|
||||
|
||||
var heavyWeight = addTextView();
|
||||
heavyWeight.attributedString = {
|
||||
string: "Heavy Weight",
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 40.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
weight: "heavy"
|
||||
}
|
||||
};
|
||||
|
||||
var lightWeight = addTextView();
|
||||
lightWeight.attributedString = {
|
||||
string: "Light Weight",
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 40.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
weight: "light"
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// TextView's demonstrating breakModes
|
||||
//
|
||||
var clip = addTextView();
|
||||
clip.attributedString = {
|
||||
string: "clip: " + loremIpsum,
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
breakMode: "clip"
|
||||
}
|
||||
};
|
||||
|
||||
var wordWrap = addTextView();
|
||||
wordWrap.attributedString = {
|
||||
string: "word-wrap: " + loremIpsum,
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
breakMode: "word-wrap"
|
||||
}
|
||||
};
|
||||
|
||||
var truncateHead = addTextView();
|
||||
truncateHead.attributedString = {
|
||||
string: loremIpsum + " truncate-head",
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
breakMode: "truncate-head"
|
||||
}
|
||||
};
|
||||
|
||||
var truncateTail = addTextView();
|
||||
truncateTail.attributedString = {
|
||||
string: "truncate-tail: " + loremIpsum,
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
breakMode: "truncate-tail"
|
||||
}
|
||||
};
|
||||
|
||||
var truncateMiddle = addTextView();
|
||||
truncateMiddle.attributedString = {
|
||||
string: "truncate-middle: " + loremIpsum,
|
||||
attributes: {
|
||||
// Required attributes
|
||||
pointSize: 20.0,
|
||||
color: { red: 1, blue: 1, green: 1 },
|
||||
breakMode: "truncate-middle"
|
||||
}
|
||||
};
|
||||
|
||||
containerView.subviews = exampleTextViews;
|
||||
controller.view = containerView;
|
45
app/js/storagetest.js
Normal file
45
app/js/storagetest.js
Normal file
@@ -0,0 +1,45 @@
|
||||
// /\_/|
|
||||
// { ' ' } JellyCAT
|
||||
// \____\
|
||||
|
||||
function printSessionStorage() {
|
||||
|
||||
var keys = ['test', 'exampleKey2', 'exampleKey3'];
|
||||
|
||||
keys.forEach(function(key) {
|
||||
var value = atv.sessionStorage.getItem(key);
|
||||
if (value !== null) {
|
||||
console.log(key + ": " + value);
|
||||
} else {
|
||||
console.log("No value found for key: " + key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function printLocalStorage() {
|
||||
|
||||
var keys = ['test', 'exampleKey2', 'exampleKey3'];
|
||||
|
||||
keys.forEach(function(key) {
|
||||
var value = atv.localStorage.getItem(key);
|
||||
if (value !== null) {
|
||||
console.log(key + ": " + value);
|
||||
} else {
|
||||
console.log("No value found for key: " + key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setTestSessionStorageItem() {
|
||||
// Set an example item in sessionStorage
|
||||
atv.sessionStorage.setItem('test', 'exampleValueForSessionStorage');
|
||||
console.log('Test item set in sessionStorage.');
|
||||
}
|
||||
|
||||
function setTestLocalStorageItem() {
|
||||
// Set an example item in sessionStorage
|
||||
atv.localStorage.setItem('test', 'exampleValueForLocalStorage');
|
||||
console.log('Test item set in localStorage.');
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user