Skip to content

Instantly share code, notes, and snippets.

@litenjacob
Created January 28, 2010 11:04
Show Gist options
  • Save litenjacob/288624 to your computer and use it in GitHub Desktop.
Save litenjacob/288624 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html manifest="txtv.manifest">
<head>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="width=320, user-scalable=no" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>txtv</title>
<link rel="apple-touch-icon" href="pics/apple-touch-icon.png" />
<link href="css/txtv.css" rel="stylesheet" type="text/css" />
<link href="css/svt.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="js/jquery-1.4.min.js"></script>
</head>
<body>
<div id="txtv">
<div id="pages"></div>
<input id="numpad" type="tel" />
<div id="install">
<div id="installMessage">
<p>
För att installera txtv, tryck på <strong>+</strong>
-knappen nedan och välj <strong>Lägg till på hemskärmen</strong>.
</p>
<p>
Kör den sedan som vilken app som helst!
</p>
</div>
</div>
</div>
<script type="text/javascript" src="js/txtv.js" charset="utf-8"></script>
</body>
</html>
html, body, #txtv, #pages > div, .page, #install {
width: 320px;
height: 416px;
background: #000;
}
html, body, #txtv {
position: relative;
overflow: hidden;
}
* {
margin: 0;
padding: 0;
-webkit-user-select: none; /* prevent copy paste for all elements */
}
a, input {
-webkit-user-select: auto;
}
#pages > div {
position: absolute;
top: 0;
left: 0;
display: none;
}
.page {
position: absolute;
}
.anim {
-webkit-transition-property: -webkit-transform;
-webkit-transition-duration: 0.5s;
}
.loader {
background: #000 url(../pics/ajax-loader.gif) no-repeat scroll center center;
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
opacity: 0.6;
z-index:1337;
}
.loader span {
font-family: Helvetica, sans-serif;
position: absolute;
color: #fff;
font-size: 800%;
text-align: center;
bottom: 0px;
width: 100%;
}
#numpad {
position: absolute;
top: 0;
left: -10px;
width: 80px;
height: 80px;
opacity: 0.2;
z-index: 1337;
background: none;
-webkit-appearance: none;
border: none;
line-height: 0;
font-size: 0;
color: #fff;
}
#install {
display: none;
}
#install.show {
display: table-cell;
vertical-align: middle
}
#installMessage {
color: yellow;
font-family: "Courier New", "Lucida Console", monospace;
margin: 2em;
}
#installMessage strong {
color: blue;
}
// What events to use?
touchstart = 'ontouchstart' in document.documentElement ? 'touchstart' : 'mousedown';
touchmove = 'ontouchmove' in document.documentElement ? 'touchmove' : 'mousemove';
touchend = 'ontouchend' in document.documentElement ? 'touchend' : 'mouseup';
iphone = navigator.userAgent.match(/iPhone/i);
// The singleton
var txtv = {
height: 416, // Lazily hardcoded
width: 320,
dom: {
body: $('body'),
pages: $('#pages'),
document: $(document),
numpad: $('#numpad'),
},
init: function(){
document.preventScroll = true; // Let me handle my own scrolling, please
// Quick fix to remap the iPhone's touch events
if(iphone){
txtv.touch.normalize = function(func){
return function(e){
if (!e.normalized) {
var _e = e.originalEvent.changedTouches[0];
e.pageX = _e.pageX;
e.pageY = _e.pageY;
e._normalized = true;
}
return func(e);
}
}
} else {
txtv.touch.normalize = function(func){
return function(e){
return func(e);
}
}
}
// 100 is the start page
txtv.page.create(100);
// Kill movements
document.addEventListener('touchmove', function(e){
e.preventDefault();
return false;
}, false);
// Bind numpad events
txtv.dom.numpad.keyup(txtv.pagenum.change);
txtv.dom.numpad.blur(txtv.pagenum.blur);
},
pagenum: {
update: function(num){
var num = num + ''; // Needs to be string
var headline = txtv.touch.vars.elem.find('pre').contents().get(0);
var numArr = num.split("");
// Fetch the textnode and replace the page number in it with our input
var cur = headline.textContent;
var s = cur.replace(/([\d\-]{3})/, function(all,sub){
var arr = sub.split("");
for(var j = 0; j < arr.length; j++){
if(numArr[j] != undefined){
arr[j] = numArr[j];
} else {
arr[j] = '-';
}
}
return arr.join("");
});
headline.textContent = s;
},
change: function(e){
var input = $(e.currentTarget);
var num = input.val();
txtv.pagenum.update(num);
if(num.length == 3){
input.blur();
}
},
blur: function(e){
var input = $(e.currentTarget).show();
var num = input.val();
// Reset the page number of the page we're on
txtv.pagenum.update.call(txtv.touch.vars.elem.data('num'));
// All page numbers consists of three digits, so move to page if we've got exactly three digits
if(num.length == 3){
txtv.page.create(num);
}
input.val('');
}
},
touch: {
normalize: null,
vars: {
h: 0
},
start: function(e){
var vars = txtv.touch.vars;
vars.at = {x: e.pageX, y: e.pageY }; // Touchdown!
vars.touching = true;
},
move: function(e){
if (txtv.touch.vars.touching) {
var vars = txtv.touch.vars;
var at = vars.at;
var dir = vars.dir;
if (!dir) {
if (Math.abs(e.pageY - at.y) > 10) { // Are we scrolling vertically?
vars.dir = 'v';
} else {
if (Math.abs(e.pageX - at.x) > 10) { // Or are we scrolling horizontally?
vars.dir = 'h';
}
}
} else {
if(dir == 'v'){ // Perform vertical scroll
var diffY = e.pageY - at.y;
vars.prev.css('webkitTransform', 'translate3d(0px, ' + (diffY - txtv.height) + 'px, 0px)');
vars.elem.css('webkitTransform', 'translate3d(0px, ' + (diffY) + 'px, 0px)');
vars.next.css('webkitTransform', 'translate3d(0px, ' + (diffY + txtv.height) + 'px, 0px)');
} else {
if(dir == 'h'){ // Perform horizontal subpage scroll
vars.elem.css('webkitTransform', 'translate3d(' + (-vars.h * txtv.width + e.pageX - at.x) + 'px, 0px, 0px)');
}
}
}
}
return false;
},
end: function(e){
var vars = txtv.touch.vars;
if (vars.touching) {
vars.touching = false;
if (vars.dir == 'v') {
var diffY = e.pageY - vars.at.y;
var prevPos, elemPos, nextPos;
if (diffY > 50) { // Going up
prevPos = 0;
elemPos = txtv.height;
nextPos = txtv.height * 2;
vars.activeElem = vars.prev;
}
else
if (diffY < -50) { // Going down
prevPos = -txtv.height * 2;
elemPos = -txtv.height;
nextPos = 0;
vars.activeElem = vars.next;
}
else { // Staying
prevPos = -txtv.height;
elemPos = 0;
nextPos = txtv.height;
vars.activeElem = vars.elem;
}
vars.prev.addClass('anim');
vars.elem.addClass('anim');
vars.next.addClass('anim');
vars.prev.css('webkitTransform', 'translate3d(0px, ' + (prevPos) + 'px, 0px)');
vars.elem.css('webkitTransform', 'translate3d(0px, ' + (elemPos) + 'px, 0px)');
vars.next.css('webkitTransform', 'translate3d(0px, ' + (nextPos) + 'px, 0px)');
// Let the animation start before we prefetch nearby pages - otherwise it might stutter
setTimeout(function(){
txtv.page.getNearby(vars.activeElem);
}, 100);
vars.h = 0; // Reset horizontal scroll
} else if (vars.dir == 'h') {
var h = vars.h;
var children = txtv.touch.vars.elem.children().filter('div').length;
var diffX = e.pageX - vars.at.x;
if (diffX < 50 && h + 1 <= children - 1) { // Going right, if possible
h++;
}
else
if (diffX > -50 && h - 1 >= 0) { // Going left, if possible
h--;
}
vars.elem.addClass('anim');
vars.elem.css('webkitTransform', 'translate3d(' + (-h * txtv.width) + 'px, 0px, 0px)');
vars.h = h;
}
vars.dir = null; // Reset direction
}
},
animEnd: function(e){
$(e.currentTarget).removeClass('anim'); // Remove anim class to be able to drag page again
},
normalize: null
},
page: {
request: function(num, callback, pos){
// Create container
var num = parseInt(num);
var con = $('<div/>')
.attr('id', 'page' + num)
.append($('<div class="loader"><span>' + num + '</span></div>'))
.data('loaded', false)
.css('webkitTransform', 'translate3d(0px, ' + (pos) + 'px, 0px)');
// Putting all pages in order
var anchor;
$(txtv.dom.pages.children()).each(function(i){
if($(this).data('num') < num){
anchor = $(this);
}
})
if(anchor){
anchor.after(con);
} else {
txtv.dom.pages.append(con);
}
con.show()
// Make the request
$.ajax({
url: 'http://79.99.1.153/txtv/api/' + num + '.html', // Gateway for cross domain requests
dataType: 'jsonp',
success: function(data){
con
.bind(touchstart, txtv.touch.normalize(txtv.touch.start))
.bind(touchmove, txtv.touch.normalize(txtv.touch.move))
.bind(touchend, txtv.touch.normalize(txtv.touch.end))
.bind('webkitTransitionEnd', txtv.touch.animEnd);
// SVT:s counting is upside down
var previousPage = data.match(/nextPage = "(\d+).html"/)[1],
nextPage = data.match(/previousPage = "(\d+).html"/)[1],
rawString = data
.substring(data.indexOf('<pre'), data.lastIndexOf('</pre>') + 6) // Extract all pages
.replace(/background: url.*?\.gif\)/g, '') // Remove ugly background images
.replace(/(\d{3}).html/g, function(all, sub){
return 'javascript:txtv.page.create(' + sub + ');'; // Not pretty, but live click event on iPhone is slow...
}),
pages = $(rawString)
.filter(function(i,elem){ // Filter pages
return elem.nodeName == 'PRE';
})
.appendTo(con.empty())
.wrap('<div class="page"/>')
.each(function(i){
$(this).parent().css('left', txtv.width*i); // Position subpages
})
previousPage = (num == 100 && previousPage == 100 ? 897 : previousPage);
con
.data('loaded', true)
.data('num', num)
.data('prev', previousPage)
.data('next', nextPage)
.trigger('loaded')
if (callback) {
callback(con);
}
},
error: function(){
con.remove();
}
});
return con;
},
create: function(num, caching, pos){
var num = num || 100,
pos = pos || 0;
var page = $('#page' + num); // Try fetching page from DOM
if(!caching){
page.css('webkitTransform', 'translate3d(0px, ' + (pos) + 'px, 0px)');
if(txtv.touch.vars.elem){ // Get it out of the viewport if non-empty
txtv.touch.vars.elem.css('webkitTransform', 'translate3d(0px, ' + (txtv.height) + 'px, 0px)');
}
}
if (!page.length) { // Didn we not find the node? If not, fetch it!
return txtv.page.request(num, function(con){
if (!caching) {
txtv.page.getNearby(con); // Prefetching
}
}, pos);
} else {
if (!caching) {
txtv.page.getNearby(page); // Prefetching
}
return page;
}
},
fetchNearby: function(elem){
txtv.touch.vars.prev = txtv.page.create(elem.data('prev'), true, -txtv.height);
txtv.touch.vars.next = txtv.page.create(elem.data('next'), true, txtv.height);
},
getNearby: function(elem){
txtv.touch.vars.elem = elem;
// The calling elem needs to be properly loaded to know which its nearby pages are
if(!elem.data('loaded')){
elem.one('loaded',function(){
txtv.page.fetchNearby(elem);
});
} else {
txtv.page.fetchNearby(elem);
}
}
}
}
// Install as webapp if on iPhone
if(iphone && !navigator.standalone){
$('#install').addClass('show');
} else {
// A timeout seems to make the iPhone display the app before everything is loaded
//setTimeout(txtv.init 2);
txtv.init();
}
// Titanium sniffs files for modules, and needs a dummy reference to the UI module to build properly
if (window.Titanium) {
var version = Titanium.version;
var UI = Titanium.UI;
}
CACHE MANIFEST
# txtv manifest, version 0.2.5
# HTML
index.html
# CSS
css/svt.css
css/txtv.css
# JS
js/jquery-1.4.min.js
js/txtv.js
# PICS
pics/ajax-loader.gif
NETWORK:
http://79.99.1.153/txtv/api/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment