Skip to content

Instantly share code, notes, and snippets.

@glutanimate
Forked from redoPop/anki_mobile_card.html
Created November 12, 2016 14:04
Show Gist options
  • Save glutanimate/b2199552b24da7d3f7318875723e6397 to your computer and use it in GitHub Desktop.
Save glutanimate/b2199552b24da7d3f7318875723e6397 to your computer and use it in GitHub Desktop.
HTML used by AnkiMobile to render card templates; a reference for creating advanced Anki card templates with special HTML/CSS. When used by AnkiMobile, the card's HTML replaces the `<!-- (Card contents) -->` comment in this gist, with the shared styles dropped inline inside a `<style>` tag.
<!doctype html>
<html class=" webkit safari mobile iphone js">
<head>
<meta name="viewport" content="width=device-width;">
<style id="ss"></style>
<style>
body {
text-align: center;
font-size: 1em;
-webkit-transform: translate3d(0,0,0);
-webkit-font-smoothing:antialiased;
}
img {
max-width: 95%;
}
#typeans {
width: 95%;
margin-top: 5px;
}
.invis { visibility:hidden; }
.card { margin: 20px; }
</style>
<script type="text/javascript">
var typeans;
function setupType() {
typeans = document.getElementById("typeans");
if (typeans) {
typeans.focus();
}
$('.typeans').focus();
}
function runScripts() {
var d = document.getElementById('qa').getElementsByTagName("script");
var t = d.length;
for (var x = 0; x < t; x++) {
var newScript = document.createElement('script');
newScript.type = "text/javascript";
newScript.text = d[x].text;
document.getElementById('qa').appendChild(newScript);
}
}
// hack for ios7 to try make sure it refreshes the screen
function _refresh() {
setTimeout(function() {
var e = document.createElement('iframe');
e.setAttribute('src', 'ankiinternal://foo');
e.className = "invis";
document.documentElement.appendChild(e);
e.parentNode.removeChild(e);
e = null;
//console.log('update');}
}, 0);
}
function _getTypedText() {
if (typeans) {
return typeans.value;
}
}
function _resizeImages(shrink) {
var elems = document.getElementsByTagName("img");
for (var i=0; i<elems.length; i++) {
var e = elems[i];
var w = e.style.maxWidth;
if (!w) {
// assume it's default 95%
w = "95%";
}
w = parseInt(w, 10);
w += 30 * (shrink ? -1 : 1);
w = Math.max(30, w);
e.style.maxWidth = w+"%";
}
}
// make console.log work
var console = {
log: function(msg) {
var iframe = document.createElement('IFRAME');
iframe.setAttribute('src', 'ankilog://dummy/' + encodeURIComponent(msg));
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
};
// tap handling
//////////////////////////////////////////////////////////
var _startX, _startY, _winX, _winY;
var _lastTap = 0;
var _tapStart;
var _tapReady = function(threshold) {
var now = new Date().getTime();
var elapsed = now - _lastTap;
if (elapsed > threshold) {
_lastTap = now;
return true;
}
return false;
}
document.ontouchstart = function (e) {
_startX = e.touches[0].clientX;
_startY = e.touches[0].clientY;
_winX = _startX - window.pageXOffset;
_winY = _startY - window.pageYOffset;
_tapStart = new Date().getTime();
};
// this is run after the event bubbles up to the document
document.ontouchend = function (e) {
if (!_tapReady(300)) {
return;
} else if (_isLink(e)) {
return;
} else if ((new Date().getTime() - _tapStart) > 500) {
// long tap (eg copy), so ignore
return;
}
// if tap has moved, treat it as scrolling
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
var deltaX = Math.abs(x-_startX);
var deltaY = Math.abs(y-_startY);
var threshold = 30;
if (deltaX > threshold || deltaY > threshold) {
// if it was scrolling, we shouldn't use a touch delay
_lastTap = 0;
return;
}
// determine the section we're in
var tap;
var h = window.innerHeight / 3.0;
var w = window.innerWidth / 3.0;
x = Math.floor(x / w);
y = Math.floor(y / h);
if (x < 0 || x > 2 || y < 0 || y > 2) {
return;
}
x = ["Left", "Center", "Right"][x];
y = ["top", "mid", "bottom"][y];
tap = y+x;
// send tap to js
var iframe = document.createElement('IFRAME');
iframe.setAttribute('src', 'ankitap://' + tap);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
};
// this is run before any click events have a chance to fire
var _onclick = function (e) {
// if the target is not clickable, don't debounce
if (!_isLink(e)) {
return;
}
if (!_tapReady(1000)) {
e.stopPropagation();
e.preventDefault();
}
}
document.addEventListener("click", _onclick, true);
var _isLink = function(e) {
// we need to look at the source element and all parent elements
// except the document
var node = e.srcElement;
while (node && node != document) {
var res = (node.nodeName == "A" || node.onclick);
if (res) {
return true;
}
node = node.parentNode;
}
return res;
};
// JS callback to indicate page fully loaded
var script = document.createElement('script');
script.type = 'text/javascript';
script.text = function DOMReady() {
var iframe = document.createElement('IFRAME');
iframe.setAttribute('src', 'ankiinternal://DOMIsReady');
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
};
function addScript() {
document.getElementsByTagName('head')[0].appendChild(script);
document.addEventListener('DOMContentLoaded', DOMReady, false);
}
addScript();
/* browsersel.js 0.4.0 */
function css_browser_selector(u) {
var ua = u.toLowerCase(),
is = function(t) {
return ua.indexOf(t) > -1
},
g = 'gecko',
w = 'webkit',
s = 'safari',
o = 'opera',
m = 'mobile',
h = document.documentElement,
b = [(!(/opera|webtv/i.test(ua)) && /msie\s(\d)/.test(ua)) ? ('ie ie' + RegExp.$1) : is('firefox/2') ? g + ' ff2' : is('firefox/3.5') ? g + ' ff3 ff3_5' : is('firefox/3.6') ? g + ' ff3 ff3_6' : is('firefox/3') ? g + ' ff3' : is('gecko/') ? g : is('opera') ? o + (/version\/(\d+)/.test(ua) ? ' ' + o + RegExp.$1 : (/opera(\s|\/)(\d+)/.test(ua) ? ' ' + o + RegExp.$2 : '')) : is('konqueror') ? 'konqueror' : is('blackberry') ? m + ' blackberry' : is('android') ? m + ' android' : is('chrome') ? w + ' chrome' : is('iron') ? w + ' iron' : is('applewebkit/') ? w + ' ' + s + (/version\/(\d+)/.test(ua) ? ' ' + s + RegExp.$1 : '') : is('mozilla/') ? g : '', is('j2me') ? m + ' j2me' : is('iphone') ? m + ' iphone' : is('ipod') ? m + ' ipod' : is('ipad') ? m + ' ipad' : is('mac') ? 'mac' : is('darwin') ? 'mac' : is('webtv') ? 'webtv' : is('win') ? 'win' + (is('windows nt 6.0') ? ' vista' : '') : is('freebsd') ? 'freebsd' : (is('x11') || is('linux')) ? 'linux' : '', 'js'];
c = b.join(' ');
h.className += ' ' + c;
return c;
};
css_browser_selector(navigator.userAgent);
/* fastclick 0.5.0 */
function FastClick(e) {
"use strict";
var t, n = this;
this.trackingClick = false;
this.trackingClickStart = 0;
this.targetElement = null;
this.touchStartX = 0;
this.touchStartY = 0;
this.lastTouchIdentifier = 0;
this.layer = e;
if (!e || !e.nodeType) {
throw new TypeError("Layer must be a document node")
}
this.onClick = function() {
FastClick.prototype.onClick.apply(n, arguments)
};
this.onTouchStart = function() {
FastClick.prototype.onTouchStart.apply(n, arguments)
};
this.onTouchMove = function() {
FastClick.prototype.onTouchMove.apply(n, arguments)
};
this.onTouchEnd = function() {
FastClick.prototype.onTouchEnd.apply(n, arguments)
};
this.onTouchCancel = function() {
FastClick.prototype.onTouchCancel.apply(n, arguments)
};
if (typeof window.ontouchstart === "undefined") {
return
}
e.addEventListener("click", this.onClick, true);
e.addEventListener("touchstart", this.onTouchStart, false);
e.addEventListener("touchmove", this.onTouchMove, false);
e.addEventListener("touchend", this.onTouchEnd, false);
e.addEventListener("touchcancel", this.onTouchCancel, false);
if (!Event.prototype.stopImmediatePropagation) {
e.removeEventListener = function(t, n, r) {
var i = Node.prototype.removeEventListener;
if (t === "click") {
i.call(e, t, n.hijacked || n, r)
} else {
i.call(e, t, n, r)
}
};
e.addEventListener = function(t, n, r) {
var i = Node.prototype.addEventListener;
if (t === "click") {
i.call(e, t, n.hijacked || (n.hijacked = function(e) {
if (!e.propagationStopped) {
n(e)
}
}), r)
} else {
i.call(e, t, n, r)
}
}
}
if (typeof e.onclick === "function") {
t = e.onclick;
e.addEventListener("click", function(e) {
t(e)
}, false);
e.onclick = null
}
}
FastClick.prototype.deviceIsAndroid = navigator.userAgent.indexOf("Android") > 0;
FastClick.prototype.deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent);
FastClick.prototype.deviceIsIOS4 = FastClick.prototype.deviceIsIOS && /OS 4_\d(_\d)?/.test(navigator.userAgent);
FastClick.prototype.needsClick = function(e) {
"use strict";
switch (e.nodeName.toLowerCase()) {
case "label":
case "video":
return true;
default:
return /\bneedsclick\b/.test(e.className)
}
};
FastClick.prototype.needsFocus = function(e) {
"use strict";
switch (e.nodeName.toLowerCase()) {
case "textarea":
case "select":
return true;
case "input":
switch (e.type) {
case "button":
case "checkbox":
case "file":
case "image":
case "radio":
case "submit":
return false
}
return true;
default:
return /\bneedsfocus\b/.test(e.className)
}
};
FastClick.prototype.sendClick = function(e, t) {
"use strict";
var n, r;
if (document.activeElement && document.activeElement !== e) {
document.activeElement.blur()
}
r = t.changedTouches[0];
n = document.createEvent("MouseEvents");
n.initMouseEvent("click", true, true, window, 1, r.screenX, r.screenY, r.clientX, r.clientY, false, false, false, false, 0, null);
n.forwardedTouchEvent = true;
e.dispatchEvent(n)
};
FastClick.prototype.focus = function(e) {
"use strict";
var t;
if (this.deviceIsIOS && e.setSelectionRange) {
t = e.value.length;
e.setSelectionRange(t, t)
} else {
e.focus()
}
};
FastClick.prototype.updateScrollParent = function(e) {
"use strict";
var t, n;
t = e.fastClickScrollParent;
if (!t || !t.contains(e)) {
n = e;
do {
if (n.scrollHeight > n.offsetHeight) {
t = n;
e.fastClickScrollParent = n;
break
}
n = n.parentElement
} while (n)
}
if (t) {
t.fastClickLastScrollTop = t.scrollTop
}
};
FastClick.prototype.onTouchStart = function(e) {
"use strict";
var t, n;
t = e.target;
n = e.targetTouches[0];
if (this.deviceIsIOS) {
if (window.getSelection().rangeCount) {
return true
}
if (!this.deviceIsIOS4) {
if (n.identifier === this.lastTouchIdentifier) {
e.preventDefault();
return false
}
this.lastTouchIdentifier = n.identifier;
this.updateScrollParent(t)
}
}
this.trackingClick = true;
this.trackingClickStart = e.timeStamp;
this.targetElement = t;
this.touchStartX = n.pageX;
this.touchStartY = n.pageY;
if (e.timeStamp - this.lastClickTime < 200) {
e.preventDefault()
}
return true
};
FastClick.prototype.touchHasMoved = function(e) {
"use strict";
var t = e.targetTouches[0];
if (Math.abs(t.pageX - this.touchStartX) > 10 || Math.abs(t.pageY - this.touchStartY) > 10) {
return true
}
return false
};
FastClick.prototype.onTouchMove = function(e) {
"use strict";
if (!this.trackingClick) {
return true
}
if (this.targetElement !== e.target || this.touchHasMoved(e)) {
this.trackingClick = false;
this.targetElement = null
}
return true
};
FastClick.prototype.findControl = function(e) {
"use strict";
if (e.control !== undefined) {
return e.control
}
if (e.htmlFor) {
return document.getElementById(e.htmlFor)
}
return e.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")
};
FastClick.prototype.onTouchEnd = function(e) {
"use strict";
var t, n, r, i, s = this.targetElement;
if (!this.trackingClick) {
return true
}
if (e.timeStamp - this.lastClickTime < 200) {
this.cancelNextClick = true;
return true
}
this.lastClickTime = e.timeStamp;
n = this.trackingClickStart;
this.trackingClick = false;
this.trackingClickStart = 0;
r = s.tagName.toLowerCase();
if (r === "label") {
t = this.findControl(s);
if (t) {
this.focus(s);
if (this.deviceIsAndroid) {
return false
}
s = t
}
} else if (this.needsFocus(s)) {
if (e.timeStamp - n > 100 || this.deviceIsIOS && window.top !== window && r === "input") {
this.targetElement = null;
return false
}
this.focus(s);
if (!this.deviceIsIOS4 || r !== "select") {
this.targetElement = null;
e.preventDefault()
}
return false
}
if (this.deviceIsIOS && !this.deviceIsIOS4) {
i = s.fastClickScrollParent;
if (i && i.fastClickLastScrollTop !== i.scrollTop) {
return true
}
}
if (!this.needsClick(s)) {
e.preventDefault();
this.sendClick(s, e)
}
return false
};
FastClick.prototype.onTouchCancel = function() {
"use strict";
this.trackingClick = false;
this.targetElement = null
};
FastClick.prototype.onClick = function(e) {
"use strict";
var t;
if (!this.targetElement) {
return true
}
if (e.forwardedTouchEvent) {
return true
}
t = this.targetElement;
this.targetElement = null;
if (this.trackingClick) {
this.trackingClick = false;
return true
}
if (!e.cancelable) {
return true
}
if (e.target.type === "submit" && e.detail === 0) {
return true
}
if (!this.needsClick(t) || this.cancelNextClick) {
this.cancelNextClick = false;
if (e.stopImmediatePropagation) {
e.stopImmediatePropagation()
} else {
e.propagationStopped = true
}
e.stopPropagation();
e.preventDefault();
return false
}
return true
};
FastClick.prototype.destroy = function() {
"use strict";
var e = this.layer;
e.removeEventListener("click", this.onClick, true);
e.removeEventListener("touchstart", this.onTouchStart, false);
e.removeEventListener("touchmove", this.onTouchMove, false);
e.removeEventListener("touchend", this.onTouchEnd, false);
e.removeEventListener("touchcancel", this.onTouchCancel, false)
};
if (typeof define !== "undefined" && define.amd) {
define(function() {
"use strict";
return FastClick
})
}
if (typeof module !== "undefined" && module.exports) {
module.exports = function(e) {
"use strict";
return new FastClick(e)
};
module.exports.FastClick = FastClick
}
window.addEventListener('load', function() {
new FastClick(document.body);
}, false);
</script>
<script type="text/javascript">
function DOMReady() {
var iframe = document.createElement('IFRAME');
iframe.setAttribute('src', 'ankiinternal://DOMIsReady');
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
</script>
<title></title>
</head>
<body class="card card1">
<div id="qa">
<!--
(Card contents)
-->
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment