Commit 8ad01803 authored by allen.zhang's avatar allen.zhang

Initial commit

parents
Pipeline #76 failed with stages
.idea
node_modules
# N年前写的年会抽奖程序
[demo](https://fouber.github.io/lottery/)
\ No newline at end of file
* {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
cursor: pointer;
}
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre,
form, fieldset, input, textarea, p, blockquote, th, td {
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
word-wrap: break-word;
word-break: break-all;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset, img {
border: 0;
}
address, caption, cite, code, dfn, em, strong, th, var {
font-weight: normal;
font-style: normal;
}
ol, ul {
list-style: none;
}
caption, th {
text-align: left;
}
h1, h2, h3, h4, h5, h6, i {
font-weight: normal;
font-style: normal;
font-size: 100%;
}
q:before, q:after {
content:'';
}
abbr, acronym {
border: 0;
}
body {
font-family: "Hiragino Sans GB","DroidSansFallback","Microsoft YaHei","微软雅黑",arial,simsun;
color: #333;
line-height: 22px;
font-size: 16px;
}
\ No newline at end of file
html, body {width: 100%; height: 100%;}
.wall {width: 100%; height: 100%;
background-image: url(../img/icon-wall.jpg);
overflow: hidden;
background-color: #121936;
background-size: 100% 100% ;
background-position: center center;
background-repeat: no-repeat;}
.photos-wall {width:100%; height:100%;}
.messages {width: 30%; float: left; position: relative; top: 52px; left: 0}
/*照片墙*/
.wall .photos {width: 75%; height: 50%; float: left; position: relative; top: 270px; left: 100px; padding-left: 130px;}
.wall .photo-title {
position: absolute;
top: -280px; left: 50%;
background: url(../img/title.png) no-repeat 0 0;
background-size: 100% auto;
width: 800px; height: 350px;
margin-left: -350px;
}
/*
.wall .photo-title{position: absolute; top:-137px ;left:50%; background: url(../img/title.png) no-repeat 0 0;
background-size:380px 107px; width:380px; height: 107px; margin-left:-190px;}*/
.wall .photo-content{width:100%;height: 100%; margin:0 auto; position: relative;}
.wall .photo {position: absolute;}
.wall .photo .photo-inner{position: relative; width:100%;height: 100%;}
.wall .photo .photo-inner .img-wrap{position: absolute; top:0; left:0;}
.wall .photo .photo-inner .old{z-index: 10; opacity: 1; -webkit-transform-origin: center center; /*-webkit-transition:all 2s linear;*/}
.wall .photo .photo-inner .new{z-index: 11; opacity: 0; -webkit-transform-origin: center center; /*-webkit-transition:all 2s linear;*/}
.wall .photo .photo-inner .show{-webkit-animation: show_photo 2s ease-in-out;}
.wall .photo .photo-inner .hide{-webkit-animation: hide_photo 2s ease-in-out;}
@-webkit-keyframes show_photo{
0% {opacity:0;-webkit-transform:scale(0, 0);}
100% { opacity:1;-webkit-transform:scale(1, 1);}
}
@-webkit-keyframes hide_photo{
0% {opacity:1;}
100% { opacity:0;}
}
/*.wall .photo .photo-inner .old{z-index: 10; opacity: 1; -webkit-transition:all 2s linear;}*/
/*.wall .photo .photo-inner .new{z-index: 11; opacity: 0; -webkit-transition:all 2s linear;}*/
/*.wall .photo .photo-inner .show{opacity: 1;}*/
/*.wall .photo .photo-inner .hide{opacity: 0;}*/
.wall .photo img {
width: 100%; height: 100%;
box-shadow: 0 5px 8px rgba(0, 0, 0, 0.8);
}
.wall .pos-0 {top:10px; left:0;width:65px; height:65px;}
.wall .pos-1 {top:30px; left:70px;width:70px; height:70px;}
.wall .pos-2 {top:0; left:145px;width:100px;height:100px;}
.wall .pos-3 {top:30px; left:250px;width:70px; height:70px;}
.wall .pos-4 {top:55px; left:325px;width:100px;height:100px;}
.wall .pos-5 {top:70px; left:430px;width:85px;height:85px;}
.wall .pos-6{top:15px; left:520px;width:65px;height:65px;}
.wall .pos-7{top:-15px; left:595px;width:80px;height:80px;}
.wall .pos-8{top:105px; left:40px;width:80px;height:80px;}
.wall .pos-9{top:105px; left:125px;width:80px;height:80px;}
.wall .pos-10{top:105px; left:210px;width:110px;height:110px;}
.wall .pos-11{top:160px; left:325px;width:85px;height:85px;}
.wall .pos-12{top:160px; left:415px;width:80px;height:80px;}
.wall .pos-13{top:160px; left:500px;width:100px;height:100px;}
.wall .pos-14{top:85px; left:520px;width:70px;height:70px;}
.wall .pos-15{top:265px; left:-55px;width:80px;height:80px;}
.wall .pos-16{top:190px; left:30px;width:70px;height:70px;}
.wall .pos-17{top:190px; left:105px;width:100px;height:100px;}
.wall .pos-18{top:220px; left:210px;width:110px;height:110px;}
.wall .pos-19{top:250px; left:325px;width:85px;height:85px;}
.wall .pos-20{top:245px; left:415px;width:75px;height:75px;}
.wall .pos-21{top:265px; left:500px;width:85px;height:85px;}
.wall .pos-22{top:70px; left:595px;width:85px;height:85px;}
.wall .pos-23{top:160px; left:605px;width:75px;height:75px;}
.wall .pos-24{top:240px; left:605px;width:65px;height:65px;}
.wall .pos-25{top:310px; left:650px;width:60px;height:60px;}
.wall .pos-26{top:265px; left:37px;width:65px;height:65px;}
.wall .pos-27{
top: 15px;
left: 685px;
width: 65px;
height: 65px;}
.wall .pos-28{top: -67px;
left: 685px;
width: 75px;
height: 75px;}
.wall .pos-29{top: 103px;
left: -44px;
width: 75px;
height: 75px;}
.wall .pos-30{top: -45px;
left: -81px;
width: 75px;
height: 75px;}
.wall .pos-31 {
top: 295px;
left: 105px;
width: 60px;
height: 60px;
}
.wall .pos-32 {
top: 240px;
left: 675px;
width: 65px;
height: 65px;
}
.wall .messages {width: 25%; float: right; position: relative; top: 77px; left: 0;}
/*.wall .messages {width: 25%;float: right;position: relative;top: 80px;left: 0;}*/
/*.wall .message { margin: 25px 35px 25px 10px;}*/
.wall .message {margin: 25px 35px 25px 50px;}
.wall .message span {line-height: 25px;color: #000000;font-size: 18px;display: inline-block;padding: 5px;margin: 0;}
.wall .message{height: auto;overflow: hidden;opacity: 1;}
.wall .message.newMsg {-webkit-animation: change_height .7s linear;}
@-webkit-keyframes change_height{
0% {opacity:0;height: 0;}
100% { opacity:1;height:32px;}
}
.wall .message .message-inner{position: relative; float:right; display: inline-block; background: #ffffff; border: 1px solid #ffffff; border-radius: 4px;margin-right: 20px;}
.wall .message .message-inner:before {content: '';width: 0;height: 0;border-width: 5px 11px 0px 11px;position: absolute;border-style: solid;border-color: transparent transparent transparent #ffffff;right: -19px;bottom: -1px;}
#qrcode{width:105px;height:105px;position: absolute;top:20px;left:20px;}
#qrcode img{width:100%;}
#qrcode {background: white; padding: 5px;}
.logo{color:#D13C3F;font-family: "Microsoft YaHei"; font-weight: bold; font-size: 13px; position: absolute;left:20px; bottom:20px;}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta name="screen-orientation" content="portrait">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
<title>量康年会抽奖小程序</title>
<link rel="stylesheet" type="text/css" href="css/reset.css">
<link rel="stylesheet" type="text/css" href="css/wall.css">
<style type="text/css">
body,
html {
width: 100%;
height: 100%;
}
.logo {
position: absolute;
left: 20px;
top: 20px;
color: #F5B809;
}
.logo img {
vertical-align: middle;
}
.result {
position: absolute;
height: 320px;
width: 100%;
left: 0;
top: 50%;
margin-top: -160px;
text-align: center;
padding: 10px;
display: none;
}
.result span {
display: inline-block;
font-size: 25px;
width: 150px;
background: #fff;
line-height: 30px;
color: #000;
margin: 5px;
border-radius: 10px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.8);
padding: 10px 0;
}
button,
input,
optgroup,
select,
textarea {
color: inherit;
font: inherit;
margin: 0;
border: none;
}
button {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html input[type=button],
input[type=reset],
input[type=submit] {
-webkit-appearance: button;
cursor: pointer;
}
.pure-button {
display: inline-block;
zoom: 1;
line-height: normal;
white-space: nowrap;
vertical-align: middle;
text-align: center;
cursor: pointer;
-webkit-user-drag: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.pure-button {
font-family: inherit;
font-size: 100%;
padding: .5em 1em;
color: #444;
color: rgba(0, 0, 0, .8);
border: 0 rgba(0, 0, 0, 0);
background-color: #E6E6E6;
text-decoration: none;
border-radius: 2px;
}
.pure-button:focus {
outline: 0
}
.pure-button-hover,
.pure-button:hover,
.pure-button:focus {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0, 0, 0, .05)), to(rgba(0, 0, 0, .1)));
background-image: -webkit-linear-gradient(transparent, rgba(0, 0, 0, .05) 40%, rgba(0, 0, 0, .1));
background-image: -moz-linear-gradient(top, rgba(0, 0, 0, .05) 0, rgba(0, 0, 0, .1));
background-image: -o-linear-gradient(transparent, rgba(0, 0, 0, .05) 40%, rgba(0, 0, 0, .1));
background-image: linear-gradient(transparent, rgba(0, 0, 0, .05) 40%, rgba(0, 0, 0, .1));
}
.button-success,
.button-error,
.button-warning,
.button-secondary {
color: white;
border-radius: 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.button-success {
background: rgb(28, 184, 65);
}
.button-error {
background: rgb(202, 60, 60);
}
.button-warning {
background: rgb(223, 117, 20);
}
.button-secondary {
background: rgb(66, 184, 221);
}
.tools {
position: absolute;
bottom: 20px;
right: 20px;
text-align: center;
}
.tools .pure-button {
display: inline-block;
margin: 5px;
padding: 10px 0;
text-align: center;
width: 50px;
}
.mask {
-webkit-filter: blur(5px);
}
#main {
-webkit-transition: all 1s;
transition: all 1s;
}
</style>
</head>
<body>
<div class="logo">
<img src="https://www.quantumhealth.cn/image/logo-orange.png" height="42px"></img>
</div>
<div id="main" class="wall"></div>
<div id="result" class="result"></div>
<div id="tools" class="tools">
<button v-on="click: onClick($value)" class="pure-button" v-class="button-error: selected == $value"
v-repeat="btns">{{$value}}</button>
<button class="pure-button" v-on="click: toggle" v-class="
button-secondary: !running,
button-success: running
">{{running?'停!':'开始'}}</button>
<button class="pure-button button-warning" v-on="click: reset">重置</button>
</div>
<script type="text/javascript" src="js/zepto.js"></script>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript" src="js/tagcanvas.js"></script>
<script type="text/javascript" src="js/member.js"></script>
<script type="text/javascript">
(function () {
var choosed = JSON.parse(localStorage.getItem('choosed')) || {};
console.log(choosed);
var speed = function () {
return [0.1 * Math.random() + 0.01, -(0.1 * Math.random() + 0.01)];
};
var getKey = function (item) {
return item.name + '-' + item.phone;
};
var createHTML = function () {
var html = ['<ul>'];
member.forEach(function (item, index) {
item.index = index;
var key = getKey(item);
var color = choosed[key] ? 'yellow' : 'white';
html.push('<li><a href="#" style="color: ' + color + ';">' + item.name +
'</a></li>');
});
html.push('</ul>');
return html.join('');
};
var lottery = function (count) {
var total = member.length;
var ret = [];
var list = canvas.getElementsByTagName('a');
var color = '#' + ('00000' + Math.floor(Math.random() * 0xffffff)).slice(-6);
var color = 'yellow';
for (var i = 0; i < count; i++) {
do {
var id = Math.ceil(Math.random() * total);
if (member[id]) {
var key = getKey(member[id]);
}
} while (choosed[key]);
choosed[key] = 1;
ret.push(member[id].name);
list[id].style.color = color;
}
localStorage.setItem('choosed', JSON.stringify(choosed));
return ret;
};
var canvas = document.createElement('canvas');
canvas.id = 'myCanvas';
canvas.width = document.body.offsetWidth;
canvas.height = document.body.offsetHeight;
document.getElementById('main').appendChild(canvas);
new Vue({
el: '#tools',
data: {
selected: 3,
running: false,
btns: [
3, 2, 1
]
},
ready: function () {
canvas.innerHTML = createHTML();
TagCanvas.Start('myCanvas', '', {
textColour: null,
initial: speed(),
dragControl: 1,
textHeight: 14
});
},
methods: {
reset: function () {
if (confirm('确定要重置么?所有之前的抽奖历史将被清除!')) {
localStorage.clear();
location.reload(true);
}
},
onClick: function (num) {
$('#result').css('display', 'none');
$('#main').removeClass('mask');
this.selected = num;
},
toggle: function () {
if (this.running) {
TagCanvas.SetSpeed('myCanvas', speed());
var ret = lottery(this.selected);
$('#result').css('display', 'block').html('<span>' + ret.join(
'</span><span>') + '</span>');
TagCanvas.Reload('myCanvas');
setTimeout(function () {
localStorage.setItem(new Date().toString(), JSON.stringify(
ret));
$('#main').addClass('mask');
}, 300);
console.log(ret);
} else {
$('#result').css('display', 'none');
$('#main').removeClass('mask');
TagCanvas.SetSpeed('myCanvas', [5, 1]);
}
this.running = !this.running;
}
}
});
})();
</script>
</body>
</html>
\ No newline at end of file
// 余国良 余国安 汪路 田玲玲 夏芬美 赵雅君 高美平 鄢婳 臧叶子 陈韬 陈蕾蕾 黄杰 朱相平 李玲玲 杨心竹 王爽 王斗斗 姜中华 郑建生
var member = [
{
"phone": "余国良",
"name": "余国良"
},
{
'phone':'余国安',
"name":"余国安"
},
{
'phone':'田玲玲',
"name":"田玲玲"
},
{
'phone':'汪路',
"name":"汪路"
},
{
'phone':'夏芬美',
"name":"夏芬美"
},
{
'phone':'赵雅君',
"name":"赵雅君"
},
{
'phone':'高美平',
"name":"高美平"
},
{
'phone':'鄢婳',
"name":"鄢婳"
},
{
'phone':'臧叶子',
"name":"臧叶子"
},
{
'phone':'陈韬',
"name":"陈韬"
},
{
'phone':'陈蕾蕾',
"name":"陈蕾蕾"
},
{
'phone':'黄杰',
"name":"黄杰"
},
{
'phone':'朱相平',
"name":"朱相平"
},
// 李玲玲 杨心竹 王爽 王斗斗 姜中华 郑建生
{
'phone':'李玲玲',
"name":"李玲玲"
},
{
'phone':'杨心竹',
"name":"杨心竹"
},
{
'phone':'王爽',
"name":"王爽"
},
{
'phone':'王斗斗',
"name":"王斗斗"
},
{
'phone':'姜中华',
"name":"姜中华"
},
{
'phone':'郑建生',
"name":"郑建生"
}
];
\ No newline at end of file
/**
* Copyright (C) 2010-2015 Graham Breach
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* TagCanvas 2.7
* For more information, please contact <graham@goat1000.com>
*/
(function(){
"use strict";
var i, j, abs = Math.abs, sin = Math.sin, cos = Math.cos, max = Math.max,
min = Math.min, ceil = Math.ceil, sqrt = Math.sqrt, pow = Math.pow,
hexlookup3 = {}, hexlookup2 = {}, hexlookup1 = {
0:"0,", 1:"17,", 2:"34,", 3:"51,", 4:"68,", 5:"85,",
6:"102,", 7:"119,", 8:"136,", 9:"153,", a:"170,", A:"170,",
b:"187,", B:"187,", c:"204,", C:"204,", d:"221,", D:"221,",
e:"238,", E:"238,", f:"255,", F:"255,"
}, Oproto, Tproto, TCproto, Mproto, Vproto, TSproto, TCVproto,
doc = document, ocanvas, handlers = {};
for(i = 0; i < 256; ++i) {
j = i.toString(16);
if(i < 16)
j = '0' + j;
hexlookup2[j] = hexlookup2[j.toUpperCase()] = i.toString() + ',';
}
function Defined(d) {
return typeof d != 'undefined';
}
function IsObject(o) {
return typeof o == 'object' && o != null;
}
function Clamp(v, mn, mx) {
return isNaN(v) ? mx : min(mx, max(mn, v));
}
function Nop() {
return false;
}
function TimeNow() {
return new Date().valueOf();
}
function SortList(l, f) {
var nl = [], tl = l.length, i;
for(i = 0; i < tl; ++i)
nl.push(l[i]);
nl.sort(f);
return nl;
}
function Shuffle(a) {
var i = a.length-1, t, p;
while(i) {
p = ~~(Math.random()*i);
t = a[i];
a[i] = a[p];
a[p] = t;
--i;
}
}
function Vector(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Vproto = Vector.prototype;
Vproto.length = function() {
return sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
};
Vproto.dot = function(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
};
Vproto.cross = function(v) {
var x = this.y * v.z - this.z * v.y,
y = this.z * v.x - this.x * v.z,
z = this.x * v.y - this.y * v.x;
return new Vector(x, y, z);
};
Vproto.angle = function(v) {
var dot = this.dot(v), ac;
if(dot == 0)
return Math.PI / 2.0;
ac = dot / (this.length() * v.length());
if(ac >= 1)
return 0;
if(ac <= -1)
return Math.PI;
return Math.acos(ac);
};
Vproto.unit = function() {
var l = this.length();
return new Vector(this.x / l, this.y / l, this.z / l);
};
function MakeVector(lg, lt) {
lt = lt * Math.PI / 180;
lg = lg * Math.PI / 180;
var x = sin(lg) * cos(lt), y = -sin(lt), z = -cos(lg) * cos(lt);
return new Vector(x, y, z);
}
function Matrix(a) {
this[1] = {1: a[0], 2: a[1], 3: a[2]};
this[2] = {1: a[3], 2: a[4], 3: a[5]};
this[3] = {1: a[6], 2: a[7], 3: a[8]};
}
Mproto = Matrix.prototype;
Matrix.Identity = function() {
return new Matrix([1,0,0, 0,1,0, 0,0,1]);
};
Matrix.Rotation = function(angle, u) {
var sina = sin(angle), cosa = cos(angle), mcos = 1 - cosa;
return new Matrix([
cosa + pow(u.x, 2) * mcos, u.x * u.y * mcos - u.z * sina, u.x * u.z * mcos + u.y * sina,
u.y * u.x * mcos + u.z * sina, cosa + pow(u.y, 2) * mcos, u.y * u.z * mcos - u.x * sina,
u.z * u.x * mcos - u.y * sina, u.z * u.y * mcos + u.x * sina, cosa + pow(u.z, 2) * mcos
]);
}
Mproto.mul = function(m) {
var a = [], i, j, mmatrix = (m.xform ? 1 : 0);
for(i = 1; i <= 3; ++i)
for(j = 1; j <= 3; ++j) {
if(mmatrix)
a.push(this[i][1] * m[1][j] +
this[i][2] * m[2][j] +
this[i][3] * m[3][j]);
else
a.push(this[i][j] * m);
}
return new Matrix(a);
};
Mproto.xform = function(p) {
var a = {}, x = p.x, y = p.y, z = p.z;
a.x = x * this[1][1] + y * this[2][1] + z * this[3][1];
a.y = x * this[1][2] + y * this[2][2] + z * this[3][2];
a.z = x * this[1][3] + y * this[2][3] + z * this[3][3];
return a;
};
function PointsOnSphere(n,xr,yr,zr) {
var i, y, r, phi, pts = [], inc = Math.PI * (3-sqrt(5)), off = 2/n;
for(i = 0; i < n; ++i) {
y = i * off - 1 + (off / 2);
r = sqrt(1 - y*y);
phi = i * inc;
pts.push([cos(phi) * r * xr, y * yr, sin(phi) * r * zr]);
}
return pts;
}
function Cylinder(n,o,xr,yr,zr) {
var phi, pts = [], inc = Math.PI * (3-sqrt(5)), off = 2/n, i, j, k, l;
for(i = 0; i < n; ++i) {
j = i * off - 1 + (off / 2);
phi = i * inc;
k = cos(phi);
l = sin(phi);
pts.push(o ? [j * xr, k * yr, l * zr] : [k * xr, j * yr, l * zr]);
}
return pts;
}
function Ring(o, n, xr, yr, zr, j) {
var phi, pts = [], inc = Math.PI * 2 / n, i, k, l;
for(i = 0; i < n; ++i) {
phi = i * inc;
k = cos(phi);
l = sin(phi);
pts.push(o ? [j * xr, k * yr, l * zr] : [k * xr, j * yr, l * zr]);
}
return pts;
}
function PointsOnCylinderV(n,xr,yr,zr) { return Cylinder(n, 0, xr, yr, zr) }
function PointsOnCylinderH(n,xr,yr,zr) { return Cylinder(n, 1, xr, yr, zr) }
function PointsOnRingV(n, xr, yr, zr, offset) {
offset = isNaN(offset) ? 0 : offset * 1;
return Ring(0, n, xr, yr, zr, offset);
}
function PointsOnRingH(n, xr, yr, zr, offset) {
offset = isNaN(offset) ? 0 : offset * 1;
return Ring(1, n, xr, yr, zr, offset);
}
function CentreImage(t) {
var i = new Image;
i.onload = function() {
var dx = i.width / 2, dy = i.height / 2;
t.centreFunc = function(c, w, h, cx, cy) {
c.setTransform(1, 0, 0, 1, 0, 0);
c.globalAlpha = 1;
c.drawImage(i, cx - dx, cy - dy);
};
};
i.src = t.centreImage;
}
function SetAlpha(c,a) {
var d = c, p1, p2, ae = (a*1).toPrecision(3) + ')';
if(c[0] === '#') {
if(!hexlookup3[c])
if(c.length === 4)
hexlookup3[c] = 'rgba(' + hexlookup1[c[1]] + hexlookup1[c[2]] + hexlookup1[c[3]];
else
hexlookup3[c] = 'rgba(' + hexlookup2[c.substr(1,2)] + hexlookup2[c.substr(3,2)] + hexlookup2[c.substr(5,2)];
d = hexlookup3[c] + ae;
} else if(c.substr(0,4) === 'rgb(' || c.substr(0,4) === 'hsl(') {
d = (c.replace('(','a(').replace(')', ',' + ae));
} else if(c.substr(0,5) === 'rgba(' || c.substr(0,5) === 'hsla(') {
p1 = c.lastIndexOf(',') + 1, p2 = c.indexOf(')');
a *= parseFloat(c.substring(p1,p2));
d = c.substr(0,p1) + a.toPrecision(3) + ')';
}
return d;
}
function NewCanvas(w,h) {
// if using excanvas, give up now
if(window.G_vmlCanvasManager)
return null;
var c = doc.createElement('canvas');
c.width = w;
c.height = h;
return c;
}
// I think all browsers pass this test now...
function ShadowAlphaBroken() {
var cv = NewCanvas(3,3), c, i;
if(!cv)
return false;
c = cv.getContext('2d');
c.strokeStyle = '#000';
c.shadowColor = '#fff';
c.shadowBlur = 3;
c.globalAlpha = 0;
c.strokeRect(2,2,2,2);
c.globalAlpha = 1;
i = c.getImageData(2,2,1,1);
cv = null;
return (i.data[0] > 0);
}
function SetGradient(c, l, o, g) {
var gd = c.createLinearGradient(0, 0, l, 0), i;
for(i in g)
gd.addColorStop(1 - i, g[i]);
c.fillStyle = gd;
c.fillRect(0, o, l, 1);
}
function FindGradientColour(tc, p, r) {
var l = 1024, h = 1, gl = tc.weightGradient, cv, c, i, d;
if(tc.gCanvas) {
c = tc.gCanvas.getContext('2d');
h = tc.gCanvas.height;
} else {
if(IsObject(gl[0]))
h = gl.length;
else
gl = [gl];
tc.gCanvas = cv = NewCanvas(l, h);
if(!cv)
return null;
c = cv.getContext('2d');
for(i = 0; i < h; ++i)
SetGradient(c, l, i, gl[i]);
}
r = max(min(r || 0, h - 1), 0);
d = c.getImageData(~~((l - 1) * p), r, 1, 1).data;
return 'rgba(' + d[0] + ',' + d[1] + ',' + d[2] + ',' + (d[3]/255) + ')';
}
function TextSet(ctxt, font, colour, strings, padx, pady, shadowColour,
shadowBlur, shadowOffsets, maxWidth, widths, align) {
var xo = padx + (shadowBlur || 0) +
(shadowOffsets.length && shadowOffsets[0] < 0 ? abs(shadowOffsets[0]) : 0),
yo = pady + (shadowBlur || 0) +
(shadowOffsets.length && shadowOffsets[1] < 0 ? abs(shadowOffsets[1]) : 0), i, xc;
ctxt.font = font;
ctxt.textBaseline = 'top';
ctxt.fillStyle = colour;
shadowColour && (ctxt.shadowColor = shadowColour);
shadowBlur && (ctxt.shadowBlur = shadowBlur);
shadowOffsets.length && (ctxt.shadowOffsetX = shadowOffsets[0],
ctxt.shadowOffsetY = shadowOffsets[1]);
for(i = 0; i < strings.length; ++i) {
xc = 0;
if(widths) {
if('right' == align) {
xc = maxWidth - widths[i];
} else if('centre' == align) {
xc = (maxWidth - widths[i]) / 2;
}
}
ctxt.fillText(strings[i], xo + xc, yo);
yo += parseInt(font);
}
}
function RRect(c, x, y, w, h, r, s) {
if(r) {
c.beginPath();
c.moveTo(x, y + h - r);
c.arcTo(x, y, x + r, y, r);
c.arcTo(x + w, y, x + w, y + r, r);
c.arcTo(x + w, y + h, x + w - r, y + h, r);
c.arcTo(x, y + h, x, y + h - r, r);
c.closePath();
c[s ? 'stroke' : 'fill']();
} else {
c[s ? 'strokeRect' : 'fillRect'](x, y, w, h);
}
}
function TextCanvas(strings, font, w, h, maxWidth, stringWidths, align, valign,
scale) {
this.strings = strings;
this.font = font;
this.width = w;
this.height = h;
this.maxWidth = maxWidth;
this.stringWidths = stringWidths;
this.align = align;
this.valign = valign;
this.scale = scale;
}
TCVproto = TextCanvas.prototype;
TCVproto.SetImage = function(image, w, h, position, padding, align, valign,
scale) {
this.image = image;
this.iwidth = w * this.scale;
this.iheight = h * this.scale;
this.ipos = position;
this.ipad = padding * this.scale;
this.iscale = scale;
this.ialign = align;
this.ivalign = valign;
};
TCVproto.Align = function(size, space, a) {
var pos = 0;
if(a == 'right' || a == 'bottom')
pos = space - size;
else if(a != 'left' && a != 'top')
pos = (space - size) / 2;
return pos;
};
TCVproto.Create = function(colour, bgColour, bgOutline, bgOutlineThickness,
shadowColour, shadowBlur, shadowOffsets, padding, radius) {
var cv, cw, ch, c, x1, x2, y1, y2, offx, offy, ix, iy, iw, ih,
sox = abs(shadowOffsets[0]), soy = abs(shadowOffsets[1]), shadowcv, shadowc;
padding = max(padding, sox + shadowBlur, soy + shadowBlur);
x1 = 2 * (padding + bgOutlineThickness);
y1 = 2 * (padding + bgOutlineThickness);
cw = this.width + x1;
ch = this.height + y1;
offx = offy = padding + bgOutlineThickness;
if(this.image) {
ix = iy = padding + bgOutlineThickness;
iw = this.iwidth;
ih = this.iheight;
if(this.ipos == 'top' || this.ipos == 'bottom') {
if(iw < this.width)
ix += this.Align(iw, this.width, this.ialign);
else
offx += this.Align(this.width, iw, this.align);
if(this.ipos == 'top')
offy += ih + this.ipad;
else
iy += this.height + this.ipad;
cw = max(cw, iw + x1);
ch += ih + this.ipad;
} else {
if(ih < this.height)
iy += this.Align(ih, this.height, this.ivalign);
else
offy += this.Align(this.height, ih, this.valign);
if(this.ipos == 'right')
ix += this.width + this.ipad;
else
offx += iw + this.ipad;
cw += iw + this.ipad;
ch = max(ch, ih + y1);
}
}
cv = NewCanvas(cw, ch);
if(!cv)
return null;
x1 = y1 = bgOutlineThickness / 2;
x2 = cw - bgOutlineThickness;
y2 = ch - bgOutlineThickness;
c = cv.getContext('2d');
if(bgColour) {
c.fillStyle = bgColour;
RRect(c, x1, y1, x2, y2, radius);
}
if(bgOutlineThickness) {
c.strokeStyle = bgOutline;
c.lineWidth = bgOutlineThickness;
RRect(c, x1, y1, x2, y2, radius, true);
}
if(shadowBlur || sox || soy) {
// use a transparent canvas to draw on
shadowcv = NewCanvas(cw, ch);
if(shadowcv) {
shadowc = c;
c = shadowcv.getContext('2d');
}
}
// don't use TextSet shadow support because it adds space for shadow
TextSet(c, this.font, colour, this.strings, offx, offy, 0, 0, [],
this.maxWidth, this.stringWidths, this.align);
if(this.image)
c.drawImage(this.image, ix, iy, iw, ih);
if(shadowc) {
// draw the text and image with the added shadow
c = shadowc;
shadowColour && (c.shadowColor = shadowColour);
shadowBlur && (c.shadowBlur = shadowBlur);
c.shadowOffsetX = shadowOffsets[0];
c.shadowOffsetY = shadowOffsets[1];
c.drawImage(shadowcv, 0, 0);
}
return cv;
};
function ExpandImage(i, w, h) {
var cv = NewCanvas(w, h), c;
if(!cv)
return null;
c = cv.getContext('2d');
c.drawImage(i, (w - i.width) / 2, (h - i.height) / 2);
return cv;
}
function ScaleImage(i, w, h) {
var cv = NewCanvas(w, h), c;
if(!cv)
return null;
c = cv.getContext('2d');
c.drawImage(i, 0, 0, w, h);
return cv;
}
function AddBackgroundToImage(i, w, h, scale, colour, othickness, ocolour,
padding, radius, ofill) {
var cw = w + ((2 * padding) + othickness) * scale,
ch = h + ((2 * padding) + othickness) * scale,
cv = NewCanvas(cw, ch), c, x1, y1, x2, y2, ocanvas, cc;
if(!cv)
return null;
othickness *= scale;
radius *= scale;
x1 = y1 = othickness / 2;
x2 = cw - othickness;
y2 = ch - othickness;
padding = (padding * scale) + x1; // add space for outline
c = cv.getContext('2d');
if(colour) {
c.fillStyle = colour;
RRect(c, x1, y1, x2, y2, radius);
}
if(othickness) {
c.strokeStyle = ocolour;
c.lineWidth = othickness;
RRect(c, x1, y1, x2, y2, radius, true);
}
if(ofill) {
// use compositing to colour in the image and border
ocanvas = NewCanvas(cw, ch);
cc = ocanvas.getContext('2d');
cc.drawImage(i, padding, padding, w, h);
cc.globalCompositeOperation = 'source-in';
cc.fillStyle = ocolour;
cc.fillRect(0, 0, cw, ch);
cc.globalCompositeOperation = 'destination-over';
cc.drawImage(cv, 0, 0);
cc.globalCompositeOperation = 'source-over';
c.drawImage(ocanvas, 0, 0);
} else {
c.drawImage(i, padding, padding, i.width, i.height);
}
return {image: cv, width: cw / scale, height: ch / scale};
}
/**
* Creates a new canvas containing the image and its shadow
* Returns an object containing the image and its dimensions at z=0
*/
function AddShadowToImage(i, w, h, scale, sc, sb, so) {
var sw = abs(so[0]), sh = abs(so[1]),
cw = w + (sw > sb ? sw + sb : sb * 2) * scale,
ch = h + (sh > sb ? sh + sb : sb * 2) * scale,
xo = scale * ((sb || 0) + (so[0] < 0 ? sw : 0)),
yo = scale * ((sb || 0) + (so[1] < 0 ? sh : 0)), cv, c;
cv = NewCanvas(cw, ch);
if(!cv)
return null;
c = cv.getContext('2d');
sc && (c.shadowColor = sc);
sb && (c.shadowBlur = sb * scale);
so && (c.shadowOffsetX = so[0] * scale, c.shadowOffsetY = so[1] * scale);
c.drawImage(i, xo, yo, w, h);
return {image: cv, width: cw / scale, height: ch / scale};
}
function FindTextBoundingBox(s,f,ht) {
var w = parseInt(s.toString().length * ht), h = parseInt(ht * 2 * s.length),
cv = NewCanvas(w,h), c, idata, w1, h1, x, y, i, ex;
if(!cv)
return null;
c = cv.getContext('2d');
c.fillStyle = '#000';
c.fillRect(0,0,w,h);
TextSet(c,ht + 'px ' + f,'#fff',s,0,0,0,0,[],'centre')
idata = c.getImageData(0,0,w,h);
w1 = idata.width; h1 = idata.height;
ex = {
min: { x: w1, y: h1 },
max: { x: -1, y: -1 }
};
for(y = 0; y < h1; ++y) {
for(x = 0; x < w1; ++x) {
i = (y * w1 + x) * 4;
if(idata.data[i+1] > 0) {
if(x < ex.min.x) ex.min.x = x;
if(x > ex.max.x) ex.max.x = x;
if(y < ex.min.y) ex.min.y = y;
if(y > ex.max.y) ex.max.y = y;
}
}
}
// device pixels might not be css pixels
if(w1 != w) {
ex.min.x *= (w / w1);
ex.max.x *= (w / w1);
}
if(h1 != h) {
ex.min.y *= (w / h1);
ex.max.y *= (w / h1);
}
cv = null;
return ex;
}
function FixFont(f) {
return "'" + f.replace(/(\'|\")/g,'').replace(/\s*,\s*/g, "', '") + "'";
}
function AddHandler(h,f,e) {
e = e || doc;
if(e.addEventListener)
e.addEventListener(h,f,false);
else
e.attachEvent('on' + h, f);
}
function RemoveHandler(h,f,e) {
e = e || doc;
if(e.removeEventListener)
e.removeEventListener(h, f);
else
e.detachEvent('on' + h, f);
}
function AddImage(i, o, t, tc) {
var s = tc.imageScale, mscale, ic, bc, oc, iw, ih;
// image not loaded, wait for image onload
if(!o.complete)
return AddHandler('load',function() { AddImage(i,o,t,tc); }, o);
if(!i.complete)
return AddHandler('load',function() { AddImage(i,o,t,tc); }, i);
// Yes, this does look like nonsense, but it makes sure that both the
// width and height are actually set and not just calculated. This is
// required to keep proportional sizes when the images are hidden, so
// the images can be used again for another cloud.
o.width = o.width;
o.height = o.height;
if(s) {
i.width = o.width * s;
i.height = o.height * s;
}
// the standard width of the image, with imageScale applied
t.iw = i.width;
t.ih = i.height;
if(tc.txtOpt) {
ic = i;
mscale = tc.zoomMax * tc.txtScale;
iw = t.iw * mscale;
ih = t.ih * mscale;
if(iw < o.naturalWidth || ih < o.naturalHeight) {
ic = ScaleImage(i, iw, ih);
if(ic)
t.fimage = ic;
} else {
iw = t.iw;
ih = t.ih;
mscale = 1;
}
if(!t.HasText()) {
if(tc.shadow) {
ic = AddShadowToImage(t.image, iw, ih, mscale, tc.shadow, tc.shadowBlur,
tc.shadowOffset);
if(ic) {
t.fimage = ic.image;
t.w = ic.width;
t.h = ic.height;
}
}
if(tc.bgColour || tc.bgOutlineThickness) {
bc = tc.bgColour == 'tag' ? GetProperty(t.a, 'background-color') :
tc.bgColour;
oc = tc.bgOutline == 'tag' ? GetProperty(t.a, 'color') :
(tc.bgOutline || tc.textColour);
iw = t.fimage.width;
ih = t.fimage.height;
if(tc.outlineMethod == 'colour') {
// create the outline version first, using the current image state
ic = AddBackgroundToImage(t.fimage, iw, ih, mscale, bc,
tc.bgOutlineThickness, tc.outlineColour, tc.padding, tc.bgRadius, 1);
if(ic)
t.oimage = ic.image;
}
ic = AddBackgroundToImage(t.fimage, iw, ih, mscale, bc,
tc.bgOutlineThickness, oc, tc.padding, tc.bgRadius);
if(ic) {
t.fimage = ic.image;
t.w = ic.width;
t.h = ic.height;
}
}
if(tc.outlineMethod == 'size') {
if(tc.outlineIncrease > 0) {
t.iw += 2 * tc.outlineIncrease;
t.ih += 2 * tc.outlineIncrease;
iw = mscale * t.iw;
ih = mscale * t.ih;
ic = ScaleImage(t.fimage, iw, ih);
t.oimage = ic;
t.fimage = ExpandImage(t.fimage, t.oimage.width, t.oimage.height);
} else {
iw = mscale * (t.iw + (2 * tc.outlineIncrease));
ih = mscale * (t.ih + (2 * tc.outlineIncrease));
ic = ScaleImage(t.fimage, iw, ih);
t.oimage = ExpandImage(ic, t.fimage.width, t.fimage.height);
}
}
}
}
t.Init();
}
function GetProperty(e,p) {
var dv = doc.defaultView, pc = p.replace(/\-([a-z])/g,function(a){return a.charAt(1).toUpperCase()});
return (dv && dv.getComputedStyle && dv.getComputedStyle(e,null).getPropertyValue(p)) ||
(e.currentStyle && e.currentStyle[pc]);
}
function FindWeight(a, wFrom, tHeight) {
var w = 1, p;
if(wFrom) {
w = 1 * (a.getAttribute(wFrom) || tHeight);
} else if(p = GetProperty(a,'font-size')) {
w = (p.indexOf('px') > -1 && p.replace('px','') * 1) ||
(p.indexOf('pt') > -1 && p.replace('pt','') * 1.25) ||
p * 3.3;
}
return w;
}
function EventToCanvasId(e) {
return e.target && Defined(e.target.id) ? e.target.id :
e.srcElement.parentNode.id;
}
function EventXY(e, c) {
var xy, p, xmul = parseInt(GetProperty(c, 'width')) / c.width,
ymul = parseInt(GetProperty(c, 'height')) / c.height;
if(Defined(e.offsetX)) {
xy = {x: e.offsetX, y: e.offsetY};
} else {
p = AbsPos(c.id);
if(Defined(e.changedTouches))
e = e.changedTouches[0];
if(e.pageX)
xy = {x: e.pageX - p.x, y: e.pageY - p.y};
}
if(xy && xmul && ymul) {
xy.x /= xmul;
xy.y /= ymul;
}
return xy;
}
function MouseOut(e) {
var cv = e.target || e.fromElement.parentNode, tc = TagCanvas.tc[cv.id];
if(tc) {
tc.mx = tc.my = -1;
tc.UnFreeze();
tc.EndDrag();
}
}
function MouseMove(e) {
return;
var i, t = TagCanvas, tc, p, tg = EventToCanvasId(e);
for(i in t.tc) {
tc = t.tc[i];
if(tc.tttimer) {
clearTimeout(tc.tttimer);
tc.tttimer = null;
}
}
if(tg && t.tc[tg]) {
tc = t.tc[tg];
if(p = EventXY(e, tc.canvas)) {
tc.mx = p.x;
tc.my = p.y;
tc.Drag(e, p);
}
tc.drawn = 0;
}
}
function MouseDown(e) {
var t = TagCanvas, cb = doc.addEventListener ? 0 : 1,
tg = EventToCanvasId(e);
if(tg && e.button == cb && t.tc[tg]) {
t.tc[tg].BeginDrag(e);
}
}
function MouseUp(e) {
var t = TagCanvas, cb = doc.addEventListener ? 0 : 1,
tg = EventToCanvasId(e), tc;
if(tg && e.button == cb && t.tc[tg]) {
tc = t.tc[tg];
MouseMove(e);
if(!tc.EndDrag() && !tc.touchState)
tc.Clicked(e);
}
}
function TouchDown(e) {
var tg = EventToCanvasId(e), tc = (tg && TagCanvas.tc[tg]), p;
if(tc && e.changedTouches) {
if(e.touches.length == 1 && tc.touchState == 0) {
tc.touchState = 1;
tc.BeginDrag(e);
if(p = EventXY(e, tc.canvas)) {
tc.mx = p.x;
tc.my = p.y;
tc.drawn = 0;
}
} else if(e.targetTouches.length == 2 && tc.pinchZoom) {
tc.touchState = 3;
tc.EndDrag();
tc.BeginPinch(e);
} else {
tc.EndDrag();
tc.EndPinch();
tc.touchState = 0;
}
}
}
function TouchUp(e) {
var tg = EventToCanvasId(e), tc = (tg && TagCanvas.tc[tg]);
if(tc && e.changedTouches) {
switch(tc.touchState) {
case 1:
tc.Draw();
tc.Clicked();
break;
case 2:
tc.EndDrag();
break;
case 3:
tc.EndPinch();
}
tc.touchState = 0;
}
}
function TouchMove(e) {
var i, t = TagCanvas, tc, p, tg = EventToCanvasId(e);
for(i in t.tc) {
tc = t.tc[i];
if(tc.tttimer) {
clearTimeout(tc.tttimer);
tc.tttimer = null;
}
}
tc = (tg && t.tc[tg]);
if(tc && e.changedTouches && tc.touchState) {
switch(tc.touchState) {
case 1:
case 2:
if(p = EventXY(e, tc.canvas)) {
tc.mx = p.x;
tc.my = p.y;
if(tc.Drag(e, p))
tc.touchState = 2;
}
break;
case 3:
tc.Pinch(e);
}
tc.drawn = 0;
}
}
function MouseWheel(e) {
var t = TagCanvas, tg = EventToCanvasId(e);
if(tg && t.tc[tg]) {
e.cancelBubble = true;
e.returnValue = false;
e.preventDefault && e.preventDefault();
t.tc[tg].Wheel((e.wheelDelta || e.detail) > 0);
}
}
function DrawCanvas() {
DrawCanvasRAF(TimeNow());
}
function DrawCanvasRAF(t) {
var tc = TagCanvas.tc, i;
TagCanvas.NextFrame(TagCanvas.interval);
t = t || TimeNow();
for(i in tc)
tc[i].Draw(t);
}
function AbsPos(id) {
var e = doc.getElementById(id), r = e.getBoundingClientRect(),
dd = doc.documentElement, b = doc.body, w = window,
xs = w.pageXOffset || dd.scrollLeft,
ys = w.pageYOffset || dd.scrollTop,
xo = dd.clientLeft || b.clientLeft,
yo = dd.clientTop || b.clientTop;
return { x: r.left + xs - xo, y: r.top + ys - yo };
}
function Project(tc,p1,sx,sy) {
var m = tc.radius * tc.z1 / (tc.z1 + tc.z2 + p1.z);
return {
x: p1.x * m * sx,
y: p1.y * m * sy,
z: p1.z,
w: (tc.z1 - p1.z) / tc.z2
};
}
/**
* @constructor
* for recursively splitting tag contents on <br> tags
*/
function TextSplitter(e) {
this.e = e;
this.br = 0;
this.line = [];
this.text = [];
this.original = e.innerText || e.textContent;
}
TSproto = TextSplitter.prototype;
TSproto.Empty = function() {
for(var i = 0; i < this.text.length; ++i)
if(this.text[i].length)
return false;
return true;
};
TSproto.Lines = function(e) {
var r = e ? 1 : 0, cn, cl, i;
e = e || this.e;
cn = e.childNodes;
cl = cn.length;
for(i = 0; i < cl; ++i) {
if(cn[i].nodeName == 'BR') {
this.text.push(this.line.join(' '));
this.br = 1;
} else if(cn[i].nodeType == 3) {
if(this.br) {
this.line = [cn[i].nodeValue];
this.br = 0;
} else {
this.line.push(cn[i].nodeValue);
}
} else {
this.Lines(cn[i]);
}
}
r || this.br || this.text.push(this.line.join(' '));
return this.text;
};
TSproto.SplitWidth = function(w, c, f, h) {
var i, j, words, text = [];
c.font = h + 'px ' + f;
for(i = 0; i < this.text.length; ++i) {
words = this.text[i].split(/\s+/);
this.line = [words[0]];
for(j = 1; j < words.length; ++j) {
if(c.measureText(this.line.join(' ') + ' ' + words[j]).width > w) {
text.push(this.line.join(' '));
this.line = [words[j]];
} else {
this.line.push(words[j]);
}
}
text.push(this.line.join(' '));
}
return this.text = text;
};
/**
* @constructor
*/
function Outline(tc,t) {
this.ts = TimeNow();
this.tc = tc;
this.tag = t;
this.x = this.y = this.w = this.h = this.sc = 1;
this.z = 0;
this.Draw = tc.pulsateTo < 1 && tc.outlineMethod != 'colour' ?
this.DrawPulsate : this.DrawSimple;
this.radius = tc.outlineRadius | 0;
this.SetMethod(tc.outlineMethod);
}
Oproto = Outline.prototype;
Oproto.SetMethod = function(om) {
var methods = {
block: ['PreDraw','DrawBlock'],
colour: ['PreDraw','DrawColour'],
outline: ['PostDraw','DrawOutline'],
classic: ['LastDraw','DrawOutline'],
size: ['PreDraw','DrawColour'],
none: ['LastDraw']
}, funcs = methods[om] || methods.outline;
if(om == 'none') {
this.Draw = function() { return 1; }
} else {
this.drawFunc = this[funcs[1]];
}
this[funcs[0]] = this.Draw;
};
Oproto.Update = function(x,y,w,h,sc,z,xo,yo) {
var o = this.tc.outlineOffset, o2 = 2 * o;
this.x = sc * x + xo - o;
this.y = sc * y + yo - o;
this.w = sc * w + o2;
this.h = sc * h + o2;
this.sc = sc; // used to determine frontmost
this.z = z;
};
Oproto.DrawOutline = function(c,x,y,w,h,colour) {
c.strokeStyle = colour;
RRect(c, x, y, w, h, this.radius, true);
};
Oproto.DrawColour = function(c,x,y,w,h,colour,tag,x1,y1) {
if(tag.oimage) {
tag.alpha = 1;
tag.Draw(c, x1, y1, tag.oimage);
return 1;
}
return this[tag.image ? 'DrawColourImage' : 'DrawColourText'](c,x,y,w,h,colour,tag,x1,y1);
};
Oproto.DrawColourText = function(c,x,y,w,h,colour,tag,x1,y1) {
var normal = tag.colour;
tag.colour = colour;
tag.alpha = 1;
tag.Draw(c,x1,y1);
tag.colour = normal;
return 1;
};
Oproto.DrawColourImage = function(c,x,y,w,h,colour,tag,x1,y1) {
var ccanvas = c.canvas, fx = ~~max(x,0), fy = ~~max(y,0),
fw = min(ccanvas.width - fx, w) + .5|0, fh = min(ccanvas.height - fy,h) + .5|0, cc;
if(ocanvas)
ocanvas.width = fw, ocanvas.height = fh;
else
ocanvas = NewCanvas(fw, fh);
if(!ocanvas)
return this.SetMethod('outline'); // if using IE and images, give up!
cc = ocanvas.getContext('2d');
cc.drawImage(ccanvas,fx,fy,fw,fh,0,0,fw,fh);
c.clearRect(fx,fy,fw,fh);
tag.alpha = 1;
tag.Draw(c,x1,y1);
c.setTransform(1,0,0,1,0,0);
c.save();
c.beginPath();
c.rect(fx,fy,fw,fh);
c.clip();
c.globalCompositeOperation = 'source-in';
c.fillStyle = colour;
c.fillRect(fx,fy,fw,fh);
c.restore();
c.globalCompositeOperation = 'destination-over';
c.drawImage(ocanvas,0,0,fw,fh,fx,fy,fw,fh);
c.globalCompositeOperation = 'source-over';
return 1;
};
Oproto.DrawBlock = function(c,x,y,w,h,colour) {
c.fillStyle = colour;
RRect(c, x, y, w, h, this.radius);
};
Oproto.DrawSimple = function(c, tag, x1, y1) {
var t = this.tc;
c.setTransform(1,0,0,1,0,0);
c.strokeStyle = t.outlineColour;
c.lineWidth = t.outlineThickness;
c.shadowBlur = c.shadowOffsetX = c.shadowOffsetY = 0;
c.globalAlpha = 1;
return this.drawFunc(c,this.x,this.y,this.w,this.h,t.outlineColour,tag,x1,y1);
};
Oproto.DrawPulsate = function(c, tag, x1, y1) {
var diff = TimeNow() - this.ts, t = this.tc;
c.setTransform(1,0,0,1,0,0);
c.strokeStyle = t.outlineColour;
c.lineWidth = t.outlineThickness;
c.shadowBlur = c.shadowOffsetX = c.shadowOffsetY = 0;
c.globalAlpha = t.pulsateTo + ((1 - t.pulsateTo) *
(0.5 + (cos(2 * Math.PI * diff / (1000 * t.pulsateTime)) / 2)));
return this.drawFunc(c,this.x,this.y,this.w,this.h,t.outlineColour,tag,x1,y1);
};
Oproto.Active = function(c,x,y) {
return (x >= this.x && y >= this.y &&
x <= this.x + this.w && y <= this.y + this.h);
};
Oproto.PreDraw = Oproto.PostDraw = Oproto.LastDraw = Nop;
/**
* @constructor
*/
function Tag(tc, text, a, v, w, h, col, bcol, bradius, boutline, bothickness,
font, padding, original) {
this.tc = tc;
this.image = null;
this.text = text;
this.text_original = original;
this.line_widths = [];
this.title = a.title || null;
this.a = a;
this.position = new Vector(v[0], v[1], v[2]);
this.x = this.y = this.z = 0;
this.w = w;
this.h = h;
this.colour = col || tc.textColour;
this.bgColour = bcol || tc.bgColour;
this.bgRadius = bradius | 0;
this.bgOutline = boutline || this.colour;
this.bgOutlineThickness = bothickness | 0;
this.textFont = font || tc.textFont;
this.padding = padding | 0;
this.sc = this.alpha = 1;
this.weighted = !tc.weight;
}
Tproto = Tag.prototype;
Tproto.Init = function(e) {
var tc = this.tc;
this.outline = new Outline(tc,this);
this.textHeight = tc.textHeight;
if(this.HasText()) {
this.Measure(tc.ctxt,tc);
} else {
this.w = this.iw;
this.h = this.ih;
}
this.SetShadowColour = tc.shadowAlpha ? this.SetShadowColourAlpha : this.SetShadowColourFixed;
this.SetDraw(tc);
};
Tproto.Draw = Nop;
Tproto.HasText = function() {
return this.text && this.text[0].length > 0;
};
Tproto.EqualTo = function(e) {
var i = e.getElementsByTagName('img');
if(this.a.href != e.href)
return 0;
if(i.length)
return this.image.src == i[0].src;
return (e.innerText || e.textContent) == this.text_original;
};
Tproto.SetImage = function(i) {
this.image = this.fimage = i;
};
Tproto.SetDraw = function(t) {
this.Draw = this.fimage ? (t.ie > 7 ? this.DrawImageIE : this.DrawImage) : this.DrawText;
t.noSelect && (this.CheckActive = Nop);
};
Tproto.MeasureText = function(c) {
var i, l = this.text.length, w = 0, wl;
for(i = 0; i < l; ++i) {
this.line_widths[i] = wl = c.measureText(this.text[i]).width;
w = max(w, wl);
}
return w;
};
Tproto.Measure = function(c,t) {
var extents = FindTextBoundingBox(this.text, this.textFont, this.textHeight),
s, th, f, soff, cw, twidth, theight, img, tcv;
// add the gap at the top to the height to make equal gap at bottom
theight = extents ? extents.max.y + extents.min.y : this.textHeight;
c.font = this.font = this.textHeight + 'px ' + this.textFont;
twidth = this.MeasureText(c);
if(t.txtOpt) {
s = t.txtScale;
th = s * this.textHeight;
f = th + 'px ' + this.textFont;
soff = [s * t.shadowOffset[0], s * t.shadowOffset[1]];
c.font = f;
cw = this.MeasureText(c);
tcv = new TextCanvas(this.text, f, cw + s, (s * theight) + s, cw,
this.line_widths, t.textAlign, t.textVAlign, s);
if(this.image)
tcv.SetImage(this.image, this.iw, this.ih, t.imagePosition, t.imagePadding,
t.imageAlign, t.imageVAlign, t.imageScale);
img = tcv.Create(this.colour, this.bgColour, this.bgOutline,
s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
s * this.padding, s * this.bgRadius);
// add outline image using highlight colour
if(t.outlineMethod == 'colour') {
this.oimage = tcv.Create(t.outlineColour, this.bgColour, t.outlineColour,
s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
s * this.padding, s * this.bgRadius);
} else if(t.outlineMethod == 'size') {
extents = FindTextBoundingBox(this.text, this.textFont,
this.textHeight + t.outlineIncrease);
th = extents.max.y + extents.min.y;
f = (s * (this.textHeight + t.outlineIncrease)) + 'px ' + this.textFont;
c.font = f;
cw = this.MeasureText(c);
tcv = new TextCanvas(this.text, f, cw + s, (s * th) + s, cw,
this.line_widths, t.textAlign, t.textVAlign, s);
if(this.image)
tcv.SetImage(this.image, this.iw + t.outlineIncrease,
this.ih + t.outlineIncrease, t.imagePosition, t.imagePadding,
t.imageAlign, t.imageVAlign, t.imageScale);
this.oimage = tcv.Create(this.colour, this.bgColour, this.bgOutline,
s * this.bgOutlineThickness, t.shadow, s * t.shadowBlur, soff,
s * this.padding, s * this.bgRadius);
if(t.outlineIncrease > 0)
img = ExpandImage(img, this.oimage.width, this.oimage.height);
else
this.oimage = ExpandImage(this.oimage, img.width, img.height);
}
if(img) {
this.fimage = img;
twidth = this.fimage.width / s;
theight = this.fimage.height / s;
}
this.SetDraw(t);
t.txtOpt = !!this.fimage;
}
this.h = theight;
this.w = twidth;
};
Tproto.SetFont = function(f, c, bc, boc) {
this.textFont = f;
this.colour = c;
this.bgColour = bc;
this.bgOutline = boc;
this.Measure(this.tc.ctxt, this.tc);
};
Tproto.SetWeight = function(w) {
var tc = this.tc, modes = tc.weightMode.split(/[, ]/), m, s, wl = w.length;
if(!this.HasText())
return;
this.weighted = true;
for(s = 0; s < wl; ++s) {
m = modes[s] || 'size';
if('both' == m) {
this.Weight(w[s], tc.ctxt, tc, 'size', tc.min_weight[s],
tc.max_weight[s], s);
this.Weight(w[s], tc.ctxt, tc, 'colour', tc.min_weight[s],
tc.max_weight[s], s);
} else {
this.Weight(w[s], tc.ctxt, tc, m, tc.min_weight[s], tc.max_weight[s], s);
}
}
this.Measure(tc.ctxt, tc);
};
Tproto.Weight = function(w, c, t, m, wmin, wmax, wnum) {
w = isNaN(w) ? 1 : w;
var nweight = (w - wmin) / (wmax - wmin);
if('colour' == m)
this.colour = FindGradientColour(t, nweight, wnum);
else if('bgcolour' == m)
this.bgColour = FindGradientColour(t, nweight, wnum);
else if('bgoutline' == m)
this.bgOutline = FindGradientColour(t, nweight, wnum);
else if('size' == m) {
if(t.weightSizeMin > 0 && t.weightSizeMax > t.weightSizeMin) {
this.textHeight = t.weightSize *
(t.weightSizeMin + (t.weightSizeMax - t.weightSizeMin) * nweight);
} else {
// min textHeight of 1
this.textHeight = max(1, w * t.weightSize);
}
}
};
Tproto.SetShadowColourFixed = function(c,s,a) {
c.shadowColor = s;
};
Tproto.SetShadowColourAlpha = function(c,s,a) {
c.shadowColor = SetAlpha(s, a);
};
Tproto.DrawText = function(c,xoff,yoff) {
var t = this.tc, x = this.x, y = this.y, s = this.sc, i, xl;
c.globalAlpha = this.alpha;
c.fillStyle = this.colour;
t.shadow && this.SetShadowColour(c,t.shadow,this.alpha);
c.font = this.font;
x += xoff / s;
y += (yoff / s) - (this.h / 2);
for(i = 0; i < this.text.length; ++i) {
xl = x;
if('right' == t.textAlign) {
xl += this.w / 2 - this.line_widths[i];
} else if('centre' == t.textAlign) {
xl -= this.line_widths[i] / 2;
} else {
xl -= this.w / 2;
}
c.setTransform(s, 0, 0, s, s * xl, s * y);
c.fillText(this.text[i], 0, 0);
y += this.textHeight;
}
};
Tproto.DrawImage = function(c,xoff,yoff,im) {
var x = this.x, y = this.y, s = this.sc,
i = im || this.fimage, w = this.w, h = this.h, a = this.alpha,
shadow = this.shadow;
c.globalAlpha = a;
shadow && this.SetShadowColour(c,shadow,a);
x += (xoff / s) - (w / 2);
y += (yoff / s) - (h / 2);
c.setTransform(s, 0, 0, s, s * x, s * y);
c.drawImage(i, 0, 0, w, h);
};
Tproto.DrawImageIE = function(c,xoff,yoff) {
var i = this.fimage, s = this.sc,
w = i.width = this.w*s, h = i.height = this.h * s,
x = (this.x*s) + xoff - (w/2), y = (this.y*s) + yoff - (h/2);
c.setTransform(1,0,0,1,0,0);
c.globalAlpha = this.alpha;
c.drawImage(i, x, y);
};
Tproto.Calc = function(m,a) {
var pp, t = this.tc, mnb = t.minBrightness,
mxb = t.maxBrightness, r = t.max_radius;
pp = m.xform(this.position);
this.xformed = pp;
pp = Project(t, pp, t.stretchX, t.stretchY);
this.x = pp.x;
this.y = pp.y;
this.z = pp.z;
this.sc = pp.w;
this.alpha = a * Clamp(mnb + (mxb - mnb) * (r - this.z) / (2 * r), 0, 1);
};
Tproto.UpdateActive = function(c, xoff, yoff) {
var o = this.outline, w = this.w, h = this.h,
x = this.x - w/2, y = this.y - h/2;
o.Update(x, y, w, h, this.sc, this.z, xoff, yoff);
return o;
};
Tproto.CheckActive = function(c,xoff,yoff) {
var t = this.tc, o = this.UpdateActive(c, xoff, yoff);
return o.Active(c, t.mx, t.my) ? o : null;
};
Tproto.Clicked = function(e) {
var a = this.a, t = a.target, h = a.href, evt;
if(t != '' && t != '_self') {
if(self.frames[t]) {
self.frames[t].document.location = h;
} else{
try {
if(top.frames[t]) {
top.frames[t].document.location = h;
return;
}
} catch(err) {
// different domain/port/protocol?
}
window.open(h, t);
}
return;
}
if(doc.createEvent) {
evt = doc.createEvent('MouseEvents');
evt.initMouseEvent('click', 1, 1, window, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
if(!a.dispatchEvent(evt))
return;
} else if(a.fireEvent) {
if(!a.fireEvent('onclick'))
return;
}
doc.location = h;
};
/**
* @constructor
*/
function TagCanvas(cid,lctr,opt) {
var i, p, c = doc.getElementById(cid), cp = ['id','class','innerHTML'], raf;
if(!c) throw 0;
if(Defined(window.G_vmlCanvasManager)) {
c = window.G_vmlCanvasManager.initElement(c);
this.ie = parseFloat(navigator.appVersion.split('MSIE')[1]);
}
if(c && (!c.getContext || !c.getContext('2d').fillText)) {
p = doc.createElement('DIV');
for(i = 0; i < cp.length; ++i)
p[cp[i]] = c[cp[i]];
c.parentNode.insertBefore(p,c);
c.parentNode.removeChild(c);
throw 0;
}
for(i in TagCanvas.options)
this[i] = opt && Defined(opt[i]) ? opt[i] :
(Defined(TagCanvas[i]) ? TagCanvas[i] : TagCanvas.options[i]);
this.canvas = c;
this.ctxt = c.getContext('2d');
this.z1 = 250 / max(this.depth, 0.001);
this.z2 = this.z1 / this.zoom;
this.radius = min(c.height, c.width) * 0.0075; // fits radius of 100 in canvas
this.max_radius = 100;
this.max_weight = [];
this.min_weight = [];
this.textFont = this.textFont && FixFont(this.textFont);
this.textHeight *= 1;
this.pulsateTo = Clamp(this.pulsateTo, 0, 1);
this.minBrightness = Clamp(this.minBrightness, 0, 1);
this.maxBrightness = Clamp(this.maxBrightness, this.minBrightness, 1);
this.ctxt.textBaseline = 'top';
this.lx = (this.lock + '').indexOf('x') + 1;
this.ly = (this.lock + '').indexOf('y') + 1;
this.frozen = this.dx = this.dy = this.fixedAnim = this.touchState = 0;
this.fixedAlpha = 1;
this.source = lctr || cid;
this.repeatTags = min(64, ~~this.repeatTags);
this.minTags = min(200, ~~this.minTags);
if(this.minTags > 0 && this.repeatTags < 1 && (i = this.GetTags().length))
this.repeatTags = ceil(this.minTags / i) - 1;
this.transform = Matrix.Identity();
this.startTime = this.time = TimeNow();
this.mx = this.my = -1;
this.centreImage && CentreImage(this);
this.Animate = this.dragControl ? this.AnimateDrag : this.AnimatePosition;
this.animTiming = (typeof TagCanvas[this.animTiming] == 'function' ?
TagCanvas[this.animTiming] : TagCanvas.Smooth);
if(this.shadowBlur || this.shadowOffset[0] || this.shadowOffset[1]) {
// let the browser translate "red" into "#ff0000"
this.ctxt.shadowColor = this.shadow;
this.shadow = this.ctxt.shadowColor;
this.shadowAlpha = ShadowAlphaBroken();
} else {
delete this.shadow;
}
this.Load();
if(lctr && this.hideTags) {
(function(t) {
if(TagCanvas.loaded)
t.HideTags();
else
AddHandler('load', function() { t.HideTags(); }, window);
})(this);
}
this.yaw = this.initial ? this.initial[0] * this.maxSpeed : 0;
this.pitch = this.initial ? this.initial[1] * this.maxSpeed : 0;
if(this.tooltip) {
this.ctitle = c.title;
c.title = '';
if(this.tooltip == 'native') {
this.Tooltip = this.TooltipNative;
} else {
this.Tooltip = this.TooltipDiv;
if(!this.ttdiv) {
this.ttdiv = doc.createElement('div');
this.ttdiv.className = this.tooltipClass;
this.ttdiv.style.position = 'absolute';
this.ttdiv.style.zIndex = c.style.zIndex + 1;
AddHandler('mouseover',function(e){e.target.style.display='none';},this.ttdiv);
doc.body.appendChild(this.ttdiv);
}
}
} else {
this.Tooltip = this.TooltipNone;
}
if(!this.noMouse && !handlers[cid]) {
handlers[cid] = [
['mousemove', MouseMove],
['mouseout', MouseOut],
['mouseup', MouseUp],
['touchstart', TouchDown],
['touchend', TouchUp],
['touchcancel', TouchUp],
['touchmove', TouchMove]
];
debugger;
if(this.dragControl) {
handlers[cid].push(['mousedown', MouseDown]);
handlers[cid].push(['selectstart', Nop]);
}
if(this.wheelZoom) {
handlers[cid].push(['mousewheel', MouseWheel]);
handlers[cid].push(['DOMMouseScroll', MouseWheel]);
}
for(i = 0; i < handlers[cid].length; ++i)
AddHandler(handlers[cid][i][0], handlers[cid][i][1], c);
}
if(!TagCanvas.started) {
raf = window.requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
TagCanvas.NextFrame = raf ? TagCanvas.NextFrameRAF :
TagCanvas.NextFrameTimeout;
TagCanvas.interval = this.interval;
TagCanvas.NextFrame(this.interval);
TagCanvas.started = 1;
}
}
TCproto = TagCanvas.prototype;
TCproto.SourceElements = function() {
if(doc.querySelectorAll)
return doc.querySelectorAll('#' + this.source);
return [doc.getElementById(this.source)];
};
TCproto.HideTags = function() {
var el = this.SourceElements(), i;
for(i = 0; i < el.length; ++i)
el[i].style.display = 'none';
};
TCproto.GetTags = function() {
var el = this.SourceElements(), etl, tl = [], i, j, k;
for(k = 0; k <= this.repeatTags; ++k) {
for(i = 0; i < el.length; ++i) {
etl = el[i].getElementsByTagName('a');
for(j = 0; j < etl.length; ++j) {
tl.push(etl[j]);
}
}
}
return tl;
};
TCproto.Message = function(text) {
var tl = [], i, p, tc = text.split(''), a, t, x, z;
for(i = 0; i < tc.length; ++i) {
if(tc[i] != ' ') {
p = i - tc.length / 2;
a = doc.createElement('A');
a.href = '#';
a.innerText = tc[i];
x = 100 * sin(p / 9);
z = -100 * cos(p / 9);
t = new Tag(this, tc[i], a, [x,0,z], 2, 18, '#000', '#fff', 0, 0, 0,
'monospace', 2, tc[i]);
t.Init();
tl.push(t);
}
}
return tl;
};
TCproto.CreateTag = function(e) {
var im, i, t, txt, ts, font, bc, boc, p = [0, 0, 0];
if('text' != this.imageMode) {
im = e.getElementsByTagName('img');
if(im.length) {
i = new Image;
i.src = im[0].src;
if(!this.imageMode) {
t = new Tag(this, "", e, p, 0, 0);
t.SetImage(i);
//t.Init();
AddImage(i, im[0], t, this);
return t;
}
}
}
if('image' != this.imageMode) {
ts = new TextSplitter(e);
txt = ts.Lines();
if(!ts.Empty()) {
font = this.textFont || FixFont(GetProperty(e,'font-family'));
if(this.splitWidth)
txt = ts.SplitWidth(this.splitWidth, this.ctxt, font, this.textHeight);
bc = this.bgColour == 'tag' ? GetProperty(e, 'background-color') :
this.bgColour;
boc = this.bgOutline == 'tag' ? GetProperty(e, 'color') : this.bgOutline;
} else {
ts = null;
}
}
if(ts || i) {
t = new Tag(this, txt, e, p, 2, this.textHeight + 2,
this.textColour || GetProperty(e,'color'), bc, this.bgRadius,
boc, this.bgOutlineThickness, font, this.padding, ts && ts.original);
if(i) {
t.SetImage(i);
AddImage(i, im[0], t, this);
} else {
t.Init();
}
return t;
}
};
TCproto.UpdateTag = function(t, a) {
var colour = this.textColour || GetProperty(a, 'color'),
font = this.textFont || FixFont(GetProperty(a, 'font-family')),
bc = this.bgColour == 'tag' ? GetProperty(a, 'background-color') :
this.bgColour, boc = this.bgOutline == 'tag' ? GetProperty(a, 'color') :
this.bgOutline;
t.a = a;
t.title = a.title;
if(t.colour != colour || t.textFont != font || t.bgColour != bc ||
t.bgOutline != boc)
t.SetFont(font, colour, bc, boc);
};
TCproto.Weight = function(tl) {
var ll = tl.length, w, i, s, weights = [], valid,
wfrom = this.weightFrom ? this.weightFrom.split(/[, ]/) : [null],
wl = wfrom.length;
for(i = 0; i < ll; ++i) {
weights[i] = [];
for(s = 0; s < wl; ++s) {
w = FindWeight(tl[i].a, wfrom[s], this.textHeight);
if(!this.max_weight[s] || w > this.max_weight[s])
this.max_weight[s] = w;
if(!this.min_weight[s] || w < this.min_weight[s])
this.min_weight[s] = w;
weights[i][s] = w;
}
}
for(s = 0; s < wl; ++s) {
if(this.max_weight[s] > this.min_weight[s])
valid = 1;
}
if(valid) {
for(i = 0; i < ll; ++i) {
tl[i].SetWeight(weights[i]);
}
}
};
TCproto.Load = function() {
var tl = this.GetTags(), taglist = [], shape, t,
shapeArgs, rx, ry, rz, vl, i, tagmap = [], pfuncs = {
sphere: PointsOnSphere,
vcylinder: PointsOnCylinderV,
hcylinder: PointsOnCylinderH,
vring: PointsOnRingV,
hring: PointsOnRingH
};
if(tl.length) {
tagmap.length = tl.length;
for(i = 0; i < tl.length; ++i)
tagmap[i] = i;
this.shuffleTags && Shuffle(tagmap);
rx = 100 * this.radiusX;
ry = 100 * this.radiusY;
rz = 100 * this.radiusZ;
this.max_radius = max(rx, max(ry, rz));
for(i = 0; i < tl.length; ++i) {
t = this.CreateTag(tl[tagmap[i]]);
if(t)
taglist.push(t);
}
this.weight && this.Weight(taglist, true);
if(this.shapeArgs) {
this.shapeArgs[0] = taglist.length;
} else {
shapeArgs = this.shape.toString().split(/[(),]/);
shape = shapeArgs.shift();
if(typeof window[shape] === 'function')
this.shape = window[shape];
else
this.shape = pfuncs[shape] || pfuncs.sphere;
this.shapeArgs = [taglist.length, rx, ry, rz].concat(shapeArgs);
}
vl = this.shape.apply(this, this.shapeArgs);
this.listLength = taglist.length;
for(i = 0; i < taglist.length; ++i)
taglist[i].position = new Vector(vl[i][0], vl[i][1], vl[i][2]);
}
if(this.noTagsMessage && !taglist.length)
taglist = this.Message('No tags');
this.taglist = taglist;
};
TCproto.Update = function() {
var tl = this.GetTags(), newlist = [],
taglist = this.taglist, found,
added = [], removed = [], vl, ol, nl, i, j;
if(!this.shapeArgs)
return this.Load();
if(tl.length) {
nl = this.listLength = tl.length;
ol = taglist.length;
// copy existing list, populate "removed"
for(i = 0; i < ol; ++i) {
newlist.push(taglist[i]);
removed.push(i);
}
// find added and removed tags
for(i = 0; i < nl; ++i) {
for(j = 0, found = 0; j < ol; ++j) {
if(taglist[j].EqualTo(tl[i])) {
this.UpdateTag(newlist[j], tl[i]);
found = removed[j] = -1;
}
}
if(!found)
added.push(i);
}
// clean out found tags from removed list
for(i = 0, j = 0; i < ol; ++i) {
if(removed[j] == -1)
removed.splice(j,1);
else
++j;
}
// insert new tags in gaps where old tags removed
if(removed.length) {
Shuffle(removed);
while(removed.length && added.length) {
i = removed.shift();
j = added.shift();
newlist[i] = this.CreateTag(tl[j]);
}
// remove any more (in reverse order)
removed.sort(function(a,b) {return a-b});
while(removed.length) {
newlist.splice(removed.pop(), 1);
}
}
// add any extra tags
j = newlist.length / (added.length + 1);
i = 0;
while(added.length) {
newlist.splice(ceil(++i * j), 0, this.CreateTag(tl[added.shift()]));
}
// assign correct positions to tags
this.shapeArgs[0] = nl = newlist.length;
vl = this.shape.apply(this, this.shapeArgs);
for(i = 0; i < nl; ++i)
newlist[i].position = new Vector(vl[i][0], vl[i][1], vl[i][2]);
// reweight tags
this.weight && this.Weight(newlist);
}
this.taglist = newlist;
};
TCproto.SetShadow = function(c) {
c.shadowBlur = this.shadowBlur;
c.shadowOffsetX = this.shadowOffset[0];
c.shadowOffsetY = this.shadowOffset[1];
};
TCproto.Draw = function(t) {
if(this.paused)
return;
var cv = this.canvas, cw = cv.width, ch = cv.height, max_sc = 0,
tdelta = (t - this.time) * TagCanvas.interval / 1000,
x = cw / 2 + this.offsetX, y = ch / 2 + this.offsetY, c = this.ctxt,
active, a, i, aindex = -1, tl = this.taglist, l = tl.length,
frontsel = this.frontSelect, centreDrawn = (this.centreFunc == Nop), fixed;
this.time = t;
if(this.frozen && this.drawn)
return this.Animate(cw,ch,tdelta);
fixed = this.AnimateFixed();
c.setTransform(1,0,0,1,0,0);
for(i = 0; i < l; ++i)
tl[i].Calc(this.transform, this.fixedAlpha);
tl = SortList(tl, function(a,b) {return b.z-a.z});
if(fixed && this.fixedAnim.active) {
active = this.fixedAnim.tag.UpdateActive(c, x, y);
} else {
this.active = null;
for(i = 0; i < l; ++i) {
a = this.mx >= 0 && this.my >= 0 && this.taglist[i].CheckActive(c, x, y);
if(a && a.sc > max_sc && (!frontsel || a.z <= 0)) {
active = a;
aindex = i;
active.tag = this.taglist[i];
max_sc = a.sc;
}
}
this.active = active;
}
this.txtOpt || (this.shadow && this.SetShadow(c));
c.clearRect(0,0,cw,ch);
for(i = 0; i < l; ++i) {
if(!centreDrawn && tl[i].z <= 0) {
// run the centreFunc if the next tag is at the front
try { this.centreFunc(c, cw, ch, x, y); }
catch(e) {
alert(e);
// don't run it again
this.centreFunc = Nop;
}
centreDrawn = true;
}
if(!(active && active.tag == tl[i] && active.PreDraw(c, tl[i], x, y)))
tl[i].Draw(c, x, y);
active && active.tag == tl[i] && active.PostDraw(c);
}
if(this.freezeActive && active) {
this.Freeze();
} else {
this.UnFreeze();
this.drawn = (l == this.listLength);
}
if(this.fixedCallback) {
this.fixedCallback(this,this.fixedCallbackTag);
this.fixedCallback = null;
}
fixed || this.Animate(cw, ch, tdelta);
active && active.LastDraw(c);
cv.style.cursor = active ? this.activeCursor : '';
this.Tooltip(active,this.taglist[aindex]);
};
TCproto.TooltipNone = function() { };
TCproto.TooltipNative = function(active,tag) {
if(active)
this.canvas.title = tag && tag.title ? tag.title : '';
else
this.canvas.title = this.ctitle;
};
TCproto.SetTTDiv = function(title, tag) {
var tc = this, s = tc.ttdiv.style;
if(title != tc.ttdiv.innerHTML)
s.display = 'none';
tc.ttdiv.innerHTML = title;
tag && (tag.title = tc.ttdiv.innerHTML);
if(s.display == 'none' && ! tc.tttimer) {
tc.tttimer = setTimeout(function() {
var p = AbsPos(tc.canvas.id);
s.display = 'block';
s.left = p.x + tc.mx + 'px';
s.top = p.y + tc.my + 24 + 'px';
tc.tttimer = null;
}, tc.tooltipDelay);
}
};
TCproto.TooltipDiv = function(active,tag) {
if(active && tag && tag.title) {
this.SetTTDiv(tag.title, tag);
} else if(!active && this.mx != -1 && this.my != -1 && this.ctitle.length) {
this.SetTTDiv(this.ctitle);
} else {
this.ttdiv.style.display = 'none';
}
};
TCproto.Transform = function(tc, p, y) {
if(p || y) {
var sp = sin(p), cp = cos(p), sy = sin(y), cy = cos(y),
ym = new Matrix([cy,0,sy, 0,1,0, -sy,0,cy]),
pm = new Matrix([1,0,0, 0,cp,-sp, 0,sp,cp]);
tc.transform = tc.transform.mul(ym.mul(pm));
}
};
TCproto.AnimateFixed = function() {
var fa, t1, angle, m, d;
if(this.fadeIn) {
t1 = TimeNow() - this.startTime;
if(t1 >= this.fadeIn) {
this.fadeIn = 0;
this.fixedAlpha = 1;
} else {
this.fixedAlpha = t1 / this.fadeIn;
}
}
if(this.fixedAnim) {
if(!this.fixedAnim.transform)
this.fixedAnim.transform = this.transform;
fa = this.fixedAnim, t1 = TimeNow() - fa.t0, angle = fa.angle,
m, d = this.animTiming(fa.t, t1);
this.transform = fa.transform;
if(t1 >= fa.t) {
this.fixedCallbackTag = fa.tag;
this.fixedCallback = fa.cb;
this.fixedAnim = this.yaw = this.pitch = 0;
} else {
angle *= d;
}
m = Matrix.Rotation(angle, fa.axis);
this.transform = this.transform.mul(m);
return (this.fixedAnim != 0);
}
return false;
};
TCproto.AnimatePosition = function(w, h, t) {
var tc = this, x = tc.mx, y = tc.my, s, r;
if(!tc.frozen && x >= 0 && y >= 0 && x < w && y < h) {
s = tc.maxSpeed, r = tc.reverse ? -1 : 1;
tc.lx || (tc.yaw = ((x * 2 * s / w) - s) * r * t);
tc.ly || (tc.pitch = ((y * 2 * s / h) - s) * -r * t);
tc.initial = null;
} else if(!tc.initial) {
if(tc.frozen && !tc.freezeDecel)
tc.yaw = tc.pitch = 0;
else
tc.Decel(tc);
}
this.Transform(tc, tc.pitch, tc.yaw);
};
TCproto.AnimateDrag = function(w, h, t) {
var tc = this, rs = 100 * t * tc.maxSpeed / tc.max_radius / tc.zoom;
if(tc.dx || tc.dy) {
tc.lx || (tc.yaw = tc.dx * rs / tc.stretchX);
tc.ly || (tc.pitch = tc.dy * -rs / tc.stretchY);
tc.dx = tc.dy = 0;
tc.initial = null;
} else if(!tc.initial) {
tc.Decel(tc);
}
this.Transform(tc, tc.pitch, tc.yaw);
};
TCproto.Freeze = function() {
if(!this.frozen) {
this.preFreeze = [this.yaw, this.pitch];
this.frozen = 1;
this.drawn = 0;
}
};
TCproto.UnFreeze = function() {
if(this.frozen) {
this.yaw = this.preFreeze[0];
this.pitch = this.preFreeze[1];
this.frozen = 0;
}
};
TCproto.Decel = function(tc) {
var s = tc.minSpeed, ay = abs(tc.yaw), ap = abs(tc.pitch);
if(!tc.lx && ay > s)
tc.yaw = ay > tc.z0 ? tc.yaw * tc.decel : 0;
if(!tc.ly && ap > s)
tc.pitch = ap > tc.z0 ? tc.pitch * tc.decel : 0;
};
TCproto.Zoom = function(r) {
this.z2 = this.z1 * (1/r);
this.drawn = 0;
};
TCproto.Clicked = function(e) {
var a = this.active;
try {
if(a && a.tag)
if(this.clickToFront === false || this.clickToFront === null)
a.tag.Clicked(e);
else
this.TagToFront(a.tag, this.clickToFront, function() {
a.tag.Clicked(e);
}, true);
} catch(ex) {
}
};
TCproto.Wheel = function(i) {
var z = this.zoom + this.zoomStep * (i ? 1 : -1);
this.zoom = min(this.zoomMax,max(this.zoomMin,z));
this.Zoom(this.zoom);
};
TCproto.BeginDrag = function(e) {
this.down = EventXY(e, this.canvas);
e.cancelBubble = true;
e.returnValue = false;
e.preventDefault && e.preventDefault();
};
TCproto.Drag = function(e, p) {
if(this.dragControl && this.down) {
var t2 = this.dragThreshold * this.dragThreshold,
dx = p.x - this.down.x, dy = p.y - this.down.y;
if(this.dragging || dx * dx + dy * dy > t2) {
this.dx = dx;
this.dy = dy;
this.dragging = 1;
this.down = p;
}
}
return this.dragging;
};
TCproto.EndDrag = function() {
var res = this.dragging;
this.dragging = this.down = null;
return res;
};
function PinchDistance(e) {
var t1 = e.targetTouches[0], t2 = e.targetTouches[1];
return sqrt(pow(t2.pageX - t1.pageX, 2) + pow(t2.pageY - t1.pageY, 2));
}
TCproto.BeginPinch = function(e) {
this.pinched = [PinchDistance(e), this.zoom];
e.preventDefault && e.preventDefault();
};
TCproto.Pinch = function(e) {
var z, d, p = this.pinched;
if(!p)
return;
d = PinchDistance(e);
z = p[1] * d / p[0];
this.zoom = min(this.zoomMax,max(this.zoomMin,z));
this.Zoom(this.zoom);
};
TCproto.EndPinch = function(e) {
this.pinched = null;
};
TCproto.Pause = function() { this.paused = true; };
TCproto.Resume = function() { this.paused = false; };
TCproto.SetSpeed = function(i) {
this.initial = i;
this.yaw = i[0] * this.maxSpeed;
this.pitch = i[1] * this.maxSpeed;
};
TCproto.FindTag = function(t) {
if(!Defined(t))
return null;
Defined(t.index) && (t = t.index);
if(!IsObject(t))
return this.taglist[t];
var srch, tgt, i;
if(Defined(t.id))
srch = 'id', tgt = t.id;
else if(Defined(t.text))
srch = 'innerText', tgt = t.text;
for(i = 0; i < this.taglist.length; ++i)
if(this.taglist[i].a[srch] == tgt)
return this.taglist[i];
};
TCproto.RotateTag = function(tag, lt, lg, time, callback, active) {
var t = tag.xformed, v1 = new Vector(t.x, t.y, t.z),
v2 = MakeVector(lg, lt), angle = v1.angle(v2), u = v1.cross(v2).unit();
if(angle == 0) {
this.fixedCallbackTag = tag;
this.fixedCallback = callback;
} else {
this.fixedAnim = {
angle: -angle,
axis: u,
t: time,
t0: TimeNow(),
cb: callback,
tag: tag,
active: active
};
}
};
TCproto.TagToFront = function(tag, time, callback, active) {
this.RotateTag(tag, 0, 0, time, callback, active);
};
TagCanvas.Start = function(id,l,o) {
TagCanvas.Delete(id);
TagCanvas.tc[id] = new TagCanvas(id,l,o);
};
function tccall(f,id) {
TagCanvas.tc[id] && TagCanvas.tc[id][f]();
}
TagCanvas.Linear = function(t, t0) { return t0 / t; }
TagCanvas.Smooth = function(t, t0) { return 0.5 - cos(t0 * Math.PI / t) / 2; }
TagCanvas.Pause = function(id) { tccall('Pause',id); };
TagCanvas.Resume = function(id) { tccall('Resume',id); };
TagCanvas.Reload = function(id) { tccall('Load',id); };
TagCanvas.Update = function(id) { tccall('Update',id); };
TagCanvas.SetSpeed = function(id, speed) {
if(IsObject(speed) && TagCanvas.tc[id] &&
!isNaN(speed[0]) && !isNaN(speed[1])) {
TagCanvas.tc[id].SetSpeed(speed);
return true;
}
return false;
};
TagCanvas.TagToFront = function(id, options) {
if(!IsObject(options))
return false;
options.lat = options.lng = 0;
return TagCanvas.RotateTag(id, options);
};
TagCanvas.RotateTag = function(id, options) {
if(IsObject(options) && TagCanvas.tc[id]) {
if(isNaN(options.time))
options.time = 500;
var tt = TagCanvas.tc[id].FindTag(options);
if(tt) {
TagCanvas.tc[id].RotateTag(tt, options.lat, options.lng,
options.time, options.callback, options.active);
return true;
}
}
return false;
};
TagCanvas.Delete = function(id) {
var i, c;
if(handlers[id]) {
c = doc.getElementById(id);
if(c) {
for(i = 0; i < handlers[id].length; ++i)
RemoveHandler(handlers[id][i][0], handlers[id][i][1], c);
}
}
delete handlers[id];
delete TagCanvas.tc[id];
};
TagCanvas.NextFrameRAF = function() {
requestAnimationFrame(DrawCanvasRAF);
};
TagCanvas.NextFrameTimeout = function(iv) {
setTimeout(DrawCanvas, iv);
};
TagCanvas.tc = {};
TagCanvas.options = {
z1: 20000,
z2: 20000,
z0: 0.0002,
freezeActive: false,
freezeDecel: false,
activeCursor: 'pointer',
pulsateTo: 1,
pulsateTime: 3,
reverse: false,
depth: 0.5,
maxSpeed: 0.05,
minSpeed: 0,
decel: 0.95,
interval: 20,
minBrightness: 0.1,
maxBrightness: 1,
outlineColour: '#ffff99',
outlineThickness: 2,
outlineOffset: 5,
outlineMethod: 'outline',
outlineRadius: 0,
textColour: '#ff99ff',
textHeight: 15,
textFont: 'Helvetica, Arial, sans-serif',
shadow: '#000',
shadowBlur: 0,
shadowOffset: [0,0],
initial: null,
hideTags: true,
zoom: 1,
weight: false,
weightMode: 'size',
weightFrom: null,
weightSize: 1,
weightSizeMin: null,
weightSizeMax: null,
weightGradient: {0:'#f00', 0.33:'#ff0', 0.66:'#0f0', 1:'#00f'},
txtOpt: true,
txtScale: 2,
frontSelect: false,
wheelZoom: true,
zoomMin: 0.3,
zoomMax: 3,
zoomStep: 0.05,
shape: 'sphere',
lock: null,
tooltip: null,
tooltipDelay: 300,
tooltipClass: 'tctooltip',
radiusX: 1,
radiusY: 1,
radiusZ: 1,
stretchX: 1,
stretchY: 1,
offsetX: 0,
offsetY: 0,
shuffleTags: false,
noSelect: false,
noMouse: false,
imageScale: 1,
paused: false,
dragControl: false,
dragThreshold: 4,
centreFunc: Nop,
splitWidth: 0,
animTiming: 'Smooth',
clickToFront: false,
fadeIn: 0,
padding: 0,
bgColour: null,
bgRadius: 0,
bgOutline: null,
bgOutlineThickness: 0,
outlineIncrease: 4,
textAlign: 'centre',
textVAlign: 'middle',
imageMode: null,
imagePosition: null,
imagePadding: 2,
imageAlign: 'centre',
imageVAlign: 'middle',
noTagsMessage: true,
centreImage: null,
pinchZoom: false,
repeatTags: 0,
minTags: 0
};
for(i in TagCanvas.options) TagCanvas[i] = TagCanvas.options[i];
window.TagCanvas = TagCanvas;
// set a flag for when the window has loaded
AddHandler('load',function(){TagCanvas.loaded=1},window);
})();
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Zepto v1.1.2-5-g4c456f6 - zepto ajax event fx fx_methods selector touch - zeptojs.com/license */
var Zepto = (function() {
var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
document = window.document,
elementDisplay = {}, classCache = {},
cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
fragmentRE = /^\s*<(\w+|!)[^>]*>/,
singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
rootNodeRE = /^(?:body|html)$/i,
capitalRE = /([A-Z])/g,
// special attributes that should be get/set via method calls
methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
table = document.createElement('table'),
tableRow = document.createElement('tr'),
containers = {
'tr': document.createElement('tbody'),
'tbody': table, 'thead': table, 'tfoot': table,
'td': tableRow, 'th': tableRow,
'*': document.createElement('div')
},
readyRE = /complete|loaded|interactive/,
classSelectorRE = /^\.([\w-]+)$/,
idSelectorRE = /^#([\w-]*)$/,
simpleSelectorRE = /^[\w-]*$/,
class2type = {},
toString = class2type.toString,
zepto = {},
camelize, uniq,
tempParent = document.createElement('div'),
propMap = {
'tabindex': 'tabIndex',
'readonly': 'readOnly',
'for': 'htmlFor',
'class': 'className',
'maxlength': 'maxLength',
'cellspacing': 'cellSpacing',
'cellpadding': 'cellPadding',
'rowspan': 'rowSpan',
'colspan': 'colSpan',
'usemap': 'useMap',
'frameborder': 'frameBorder',
'contenteditable': 'contentEditable'
}
zepto.matches = function(element, selector) {
if (!selector || !element || element.nodeType !== 1) return false
var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
element.oMatchesSelector || element.matchesSelector
if (matchesSelector) return matchesSelector.call(element, selector)
// fall back to performing a selector:
var match, parent = element.parentNode, temp = !parent
if (temp) (parent = tempParent).appendChild(element)
match = ~zepto.qsa(parent, selector).indexOf(element)
temp && tempParent.removeChild(element)
return match
}
function type(obj) {
return obj == null ? String(obj) :
class2type[toString.call(obj)] || "object"
}
function isFunction(value) { return type(value) == "function" }
function isWindow(obj) { return obj != null && obj == obj.window }
function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }
function isObject(obj) { return type(obj) == "object" }
function isPlainObject(obj) {
return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype
}
function isArray(value) { return value instanceof Array }
function likeArray(obj) { return typeof obj.length == 'number' }
function compact(array) { return filter.call(array, function(item){ return item != null }) }
function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }
camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
function dasherize(str) {
return str.replace(/::/g, '/')
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
.replace(/([a-z\d])([A-Z])/g, '$1_$2')
.replace(/_/g, '-')
.toLowerCase()
}
uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) }
function classRE(name) {
return name in classCache ?
classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))
}
function maybeAddPx(name, value) {
return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
}
function defaultDisplay(nodeName) {
var element, display
if (!elementDisplay[nodeName]) {
element = document.createElement(nodeName)
document.body.appendChild(element)
display = getComputedStyle(element, '').getPropertyValue("display")
element.parentNode.removeChild(element)
display == "none" && (display = "block")
elementDisplay[nodeName] = display
}
return elementDisplay[nodeName]
}
function children(element) {
return 'children' in element ?
slice.call(element.children) :
$.map(element.childNodes, function(node){ if (node.nodeType == 1) return node })
}
// `$.zepto.fragment` takes a html string and an optional tag name
// to generate DOM nodes nodes from the given html string.
// The generated DOM nodes are returned as an array.
// This function can be overriden in plugins for example to make
// it compatible with browsers that don't support the DOM fully.
zepto.fragment = function(html, name, properties) {
var dom, nodes, container
// A special case optimization for a single tag
if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1))
if (!dom) {
if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
if (!(name in containers)) name = '*'
container = containers[name]
container.innerHTML = '' + html
dom = $.each(slice.call(container.childNodes), function(){
container.removeChild(this)
})
}
if (isPlainObject(properties)) {
nodes = $(dom)
$.each(properties, function(key, value) {
if (methodAttributes.indexOf(key) > -1) nodes[key](value)
else nodes.attr(key, value)
})
}
return dom
}
// `$.zepto.Z` swaps out the prototype of the given `dom` array
// of nodes with `$.fn` and thus supplying all the Zepto functions
// to the array. Note that `__proto__` is not supported on Internet
// Explorer. This method can be overriden in plugins.
zepto.Z = function(dom, selector) {
dom = dom || []
dom.__proto__ = $.fn
dom.selector = selector || ''
return dom
}
// `$.zepto.isZ` should return `true` if the given object is a Zepto
// collection. This method can be overriden in plugins.
zepto.isZ = function(object) {
return object instanceof zepto.Z
}
// `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
// takes a CSS selector and an optional context (and handles various
// special cases).
// This method can be overriden in plugins.
zepto.init = function(selector, context) {
var dom
// If nothing given, return an empty Zepto collection
if (!selector) return zepto.Z()
// Optimize for string selectors
else if (typeof selector == 'string') {
selector = selector.trim()
// If it's a html fragment, create nodes from it
// Note: In both Chrome 21 and Firefox 15, DOM error 12
// is thrown if the fragment doesn't begin with <
if (selector[0] == '<' && fragmentRE.test(selector))
dom = zepto.fragment(selector, RegExp.$1, context), selector = null
// If there's a context, create a collection on that context first, and select
// nodes from there
else if (context !== undefined) return $(context).find(selector)
// If it's a CSS selector, use it to select nodes.
else dom = zepto.qsa(document, selector)
}
// If a function is given, call it when the DOM is ready
else if (isFunction(selector)) return $(document).ready(selector)
// If a Zepto collection is given, just return it
else if (zepto.isZ(selector)) return selector
else {
// normalize array if an array of nodes is given
if (isArray(selector)) dom = compact(selector)
// Wrap DOM nodes.
else if (isObject(selector))
dom = [selector], selector = null
// If it's a html fragment, create nodes from it
else if (fragmentRE.test(selector))
dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
// If there's a context, create a collection on that context first, and select
// nodes from there
else if (context !== undefined) return $(context).find(selector)
// And last but no least, if it's a CSS selector, use it to select nodes.
else dom = zepto.qsa(document, selector)
}
// create a new Zepto collection from the nodes found
return zepto.Z(dom, selector)
}
// `$` will be the base `Zepto` object. When calling this
// function just call `$.zepto.init, which makes the implementation
// details of selecting nodes and creating Zepto collections
// patchable in plugins.
$ = function(selector, context){
return zepto.init(selector, context)
}
function extend(target, source, deep) {
for (key in source)
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
if (isPlainObject(source[key]) && !isPlainObject(target[key]))
target[key] = {}
if (isArray(source[key]) && !isArray(target[key]))
target[key] = []
extend(target[key], source[key], deep)
}
else if (source[key] !== undefined) target[key] = source[key]
}
// Copy all but undefined properties from one or more
// objects to the `target` object.
$.extend = function(target){
var deep, args = slice.call(arguments, 1)
if (typeof target == 'boolean') {
deep = target
target = args.shift()
}
args.forEach(function(arg){ extend(target, arg, deep) })
return target
}
// `$.zepto.qsa` is Zepto's CSS selector implementation which
// uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
// This method can be overriden in plugins.
zepto.qsa = function(element, selector){
var found,
maybeID = selector[0] == '#',
maybeClass = !maybeID && selector[0] == '.',
nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked
isSimple = simpleSelectorRE.test(nameOnly)
return (isDocument(element) && isSimple && maybeID) ?
( (found = element.getElementById(nameOnly)) ? [found] : [] ) :
(element.nodeType !== 1 && element.nodeType !== 9) ? [] :
slice.call(
isSimple && !maybeID ?
maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class
element.getElementsByTagName(selector) : // Or a tag
element.querySelectorAll(selector) // Or it's not simple, and we need to query all
)
}
function filtered(nodes, selector) {
return selector == null ? $(nodes) : $(nodes).filter(selector)
}
$.contains = function(parent, node) {
return parent !== node && parent.contains(node)
}
function funcArg(context, arg, idx, payload) {
return isFunction(arg) ? arg.call(context, idx, payload) : arg
}
function setAttribute(node, name, value) {
value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
}
// access className property while respecting SVGAnimatedString
function className(node, value){
var klass = node.className,
svg = klass && klass.baseVal !== undefined
if (value === undefined) return svg ? klass.baseVal : klass
svg ? (klass.baseVal = value) : (node.className = value)
}
// "true" => true
// "false" => false
// "null" => null
// "42" => 42
// "42.5" => 42.5
// "08" => "08"
// JSON => parse if valid
// String => self
function deserializeValue(value) {
var num
try {
return value ?
value == "true" ||
( value == "false" ? false :
value == "null" ? null :
!/^0/.test(value) && !isNaN(num = Number(value)) ? num :
/^[\[\{]/.test(value) ? $.parseJSON(value) :
value )
: value
} catch(e) {
return value
}
}
$.type = type
$.isFunction = isFunction
$.isWindow = isWindow
$.isArray = isArray
$.isPlainObject = isPlainObject
$.isEmptyObject = function(obj) {
var name
for (name in obj) return false
return true
}
$.inArray = function(elem, array, i){
return emptyArray.indexOf.call(array, elem, i)
}
$.camelCase = camelize
$.trim = function(str) {
return str == null ? "" : String.prototype.trim.call(str)
}
// plugin compatibility
$.uuid = 0
$.support = { }
$.expr = { }
$.map = function(elements, callback){
var value, values = [], i, key
if (likeArray(elements))
for (i = 0; i < elements.length; i++) {
value = callback(elements[i], i)
if (value != null) values.push(value)
}
else
for (key in elements) {
value = callback(elements[key], key)
if (value != null) values.push(value)
}
return flatten(values)
}
$.each = function(elements, callback){
var i, key
if (likeArray(elements)) {
for (i = 0; i < elements.length; i++)
if (callback.call(elements[i], i, elements[i]) === false) return elements
} else {
for (key in elements)
if (callback.call(elements[key], key, elements[key]) === false) return elements
}
return elements
}
$.grep = function(elements, callback){
return filter.call(elements, callback)
}
if (window.JSON) $.parseJSON = JSON.parse
// Populate the class2type map
$.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase()
})
// Define methods that will be available on all
// Zepto collections
$.fn = {
// Because a collection acts like an array
// copy over these useful array functions.
forEach: emptyArray.forEach,
reduce: emptyArray.reduce,
push: emptyArray.push,
sort: emptyArray.sort,
indexOf: emptyArray.indexOf,
concat: emptyArray.concat,
// `map` and `slice` in the jQuery API work differently
// from their array counterparts
map: function(fn){
return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
},
slice: function(){
return $(slice.apply(this, arguments))
},
ready: function(callback){
// need to check if document.body exists for IE as that browser reports
// document ready when it hasn't yet created the body element
if (readyRE.test(document.readyState) && document.body) callback($)
else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
return this
},
get: function(idx){
return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
},
toArray: function(){ return this.get() },
size: function(){
return this.length
},
remove: function(){
return this.each(function(){
if (this.parentNode != null)
this.parentNode.removeChild(this)
})
},
each: function(callback){
emptyArray.every.call(this, function(el, idx){
return callback.call(el, idx, el) !== false
})
return this
},
filter: function(selector){
if (isFunction(selector)) return this.not(this.not(selector))
return $(filter.call(this, function(element){
return zepto.matches(element, selector)
}))
},
add: function(selector,context){
return $(uniq(this.concat($(selector,context))))
},
is: function(selector){
return this.length > 0 && zepto.matches(this[0], selector)
},
not: function(selector){
var nodes=[]
if (isFunction(selector) && selector.call !== undefined)
this.each(function(idx){
if (!selector.call(this,idx)) nodes.push(this)
})
else {
var excludes = typeof selector == 'string' ? this.filter(selector) :
(likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
this.forEach(function(el){
if (excludes.indexOf(el) < 0) nodes.push(el)
})
}
return $(nodes)
},
has: function(selector){
return this.filter(function(){
return isObject(selector) ?
$.contains(this, selector) :
$(this).find(selector).size()
})
},
eq: function(idx){
return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
},
first: function(){
var el = this[0]
return el && !isObject(el) ? el : $(el)
},
last: function(){
var el = this[this.length - 1]
return el && !isObject(el) ? el : $(el)
},
find: function(selector){
var result, $this = this
if (typeof selector == 'object')
result = $(selector).filter(function(){
var node = this
return emptyArray.some.call($this, function(parent){
return $.contains(parent, node)
})
})
else if (this.length == 1) result = $(zepto.qsa(this[0], selector))
else result = this.map(function(){ return zepto.qsa(this, selector) })
return result
},
closest: function(selector, context){
var node = this[0], collection = false
if (typeof selector == 'object') collection = $(selector)
while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector)))
node = node !== context && !isDocument(node) && node.parentNode
return $(node)
},
parents: function(selector){
var ancestors = [], nodes = this
while (nodes.length > 0)
nodes = $.map(nodes, function(node){
if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) {
ancestors.push(node)
return node
}
})
return filtered(ancestors, selector)
},
parent: function(selector){
return filtered(uniq(this.pluck('parentNode')), selector)
},
children: function(selector){
return filtered(this.map(function(){ return children(this) }), selector)
},
contents: function() {
return this.map(function() { return slice.call(this.childNodes) })
},
siblings: function(selector){
return filtered(this.map(function(i, el){
return filter.call(children(el.parentNode), function(child){ return child!==el })
}), selector)
},
empty: function(){
return this.each(function(){ this.innerHTML = '' })
},
// `pluck` is borrowed from Prototype.js
pluck: function(property){
return $.map(this, function(el){ return el[property] })
},
show: function(){
return this.each(function(){
this.style.display == "none" && (this.style.display = '')
if (getComputedStyle(this, '').getPropertyValue("display") == "none")
this.style.display = defaultDisplay(this.nodeName)
})
},
replaceWith: function(newContent){
return this.before(newContent).remove()
},
wrap: function(structure){
var func = isFunction(structure)
if (this[0] && !func)
var dom = $(structure).get(0),
clone = dom.parentNode || this.length > 1
return this.each(function(index){
$(this).wrapAll(
func ? structure.call(this, index) :
clone ? dom.cloneNode(true) : dom
)
})
},
wrapAll: function(structure){
if (this[0]) {
$(this[0]).before(structure = $(structure))
var children
// drill down to the inmost element
while ((children = structure.children()).length) structure = children.first()
$(structure).append(this)
}
return this
},
wrapInner: function(structure){
var func = isFunction(structure)
return this.each(function(index){
var self = $(this), contents = self.contents(),
dom = func ? structure.call(this, index) : structure
contents.length ? contents.wrapAll(dom) : self.append(dom)
})
},
unwrap: function(){
this.parent().each(function(){
$(this).replaceWith($(this).children())
})
return this
},
clone: function(){
return this.map(function(){ return this.cloneNode(true) })
},
hide: function(){
return this.css("display", "none")
},
toggle: function(setting){
return this.each(function(){
var el = $(this)
;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
})
},
prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },
next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },
html: function(html){
return arguments.length === 0 ?
(this.length > 0 ? this[0].innerHTML : null) :
this.each(function(idx){
var originHtml = this.innerHTML
$(this).empty().append( funcArg(this, html, idx, originHtml) )
})
},
text: function(text){
return arguments.length === 0 ?
(this.length > 0 ? this[0].textContent : null) :
this.each(function(){ this.textContent = (text === undefined) ? '' : ''+text })
},
attr: function(name, value){
var result
return (typeof name == 'string' && value === undefined) ?
(this.length == 0 || this[0].nodeType !== 1 ? undefined :
(name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
(!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
) :
this.each(function(idx){
if (this.nodeType !== 1) return
if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
})
},
removeAttr: function(name){
return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) })
},
prop: function(name, value){
name = propMap[name] || name
return (value === undefined) ?
(this[0] && this[0][name]) :
this.each(function(idx){
this[name] = funcArg(this, value, idx, this[name])
})
},
data: function(name, value){
var data = this.attr('data-' + name.replace(capitalRE, '-$1').toLowerCase(), value)
return data !== null ? deserializeValue(data) : undefined
},
val: function(value){
return arguments.length === 0 ?
(this[0] && (this[0].multiple ?
$(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') :
this[0].value)
) :
this.each(function(idx){
this.value = funcArg(this, value, idx, this.value)
})
},
offset: function(coordinates){
if (coordinates) return this.each(function(index){
var $this = $(this),
coords = funcArg(this, coordinates, index, $this.offset()),
parentOffset = $this.offsetParent().offset(),
props = {
top: coords.top - parentOffset.top,
left: coords.left - parentOffset.left
}
if ($this.css('position') == 'static') props['position'] = 'relative'
$this.css(props)
})
if (this.length==0) return null
var obj = this[0].getBoundingClientRect()
return {
left: obj.left + window.pageXOffset,
top: obj.top + window.pageYOffset,
width: Math.round(obj.width),
height: Math.round(obj.height)
}
},
css: function(property, value){
if (arguments.length < 2) {
var element = this[0], computedStyle = getComputedStyle(element, '')
if(!element) return
if (typeof property == 'string')
return element.style[camelize(property)] || computedStyle.getPropertyValue(property)
else if (isArray(property)) {
var props = {}
$.each(isArray(property) ? property: [property], function(_, prop){
props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))
})
return props
}
}
var css = ''
if (type(property) == 'string') {
if (!value && value !== 0)
this.each(function(){ this.style.removeProperty(dasherize(property)) })
else
css = dasherize(property) + ":" + maybeAddPx(property, value)
} else {
for (key in property)
if (!property[key] && property[key] !== 0)
this.each(function(){ this.style.removeProperty(dasherize(key)) })
else
css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
}
return this.each(function(){ this.style.cssText += ';' + css })
},
index: function(element){
return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
},
hasClass: function(name){
if (!name) return false
return emptyArray.some.call(this, function(el){
return this.test(className(el))
}, classRE(name))
},
addClass: function(name){
if (!name) return this
return this.each(function(idx){
classList = []
var cls = className(this), newName = funcArg(this, name, idx, cls)
newName.split(/\s+/g).forEach(function(klass){
if (!$(this).hasClass(klass)) classList.push(klass)
}, this)
classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
})
},
removeClass: function(name){
return this.each(function(idx){
if (name === undefined) return className(this, '')
classList = className(this)
funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){
classList = classList.replace(classRE(klass), " ")
})
className(this, classList.trim())
})
},
toggleClass: function(name, when){
if (!name) return this
return this.each(function(idx){
var $this = $(this), names = funcArg(this, name, idx, className(this))
names.split(/\s+/g).forEach(function(klass){
(when === undefined ? !$this.hasClass(klass) : when) ?
$this.addClass(klass) : $this.removeClass(klass)
})
})
},
scrollTop: function(value){
if (!this.length) return
var hasScrollTop = 'scrollTop' in this[0]
if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
return this.each(hasScrollTop ?
function(){ this.scrollTop = value } :
function(){ this.scrollTo(this.scrollX, value) })
},
scrollLeft: function(value){
if (!this.length) return
var hasScrollLeft = 'scrollLeft' in this[0]
if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset
return this.each(hasScrollLeft ?
function(){ this.scrollLeft = value } :
function(){ this.scrollTo(value, this.scrollY) })
},
position: function() {
if (!this.length) return
var elem = this[0],
// Get *real* offsetParent
offsetParent = this.offsetParent(),
// Get correct offsets
offset = this.offset(),
parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
// Subtract element margins
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
offset.top -= parseFloat( $(elem).css('margin-top') ) || 0
offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
// Add offsetParent borders
parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
// Subtract the two offsets
return {
top: offset.top - parentOffset.top,
left: offset.left - parentOffset.left
}
},
offsetParent: function() {
return this.map(function(){
var parent = this.offsetParent || document.body
while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
parent = parent.offsetParent
return parent
})
}
}
// for now
$.fn.detach = $.fn.remove
// Generate the `width` and `height` functions
;['width', 'height'].forEach(function(dimension){
var dimensionProperty =
dimension.replace(/./, function(m){ return m[0].toUpperCase() })
$.fn[dimension] = function(value){
var offset, el = this[0]
if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] :
isDocument(el) ? el.documentElement['scroll' + dimensionProperty] :
(offset = this.offset()) && offset[dimension]
else return this.each(function(idx){
el = $(this)
el.css(dimension, funcArg(this, value, idx, el[dimension]()))
})
}
})
function traverseNode(node, fun) {
fun(node)
for (var key in node.childNodes) traverseNode(node.childNodes[key], fun)
}
// Generate the `after`, `prepend`, `before`, `append`,
// `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
adjacencyOperators.forEach(function(operator, operatorIndex) {
var inside = operatorIndex % 2 //=> prepend, append
$.fn[operator] = function(){
// arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
var argType, nodes = $.map(arguments, function(arg) {
argType = type(arg)
return argType == "object" || argType == "array" || arg == null ?
arg : zepto.fragment(arg)
}),
parent, copyByClone = this.length > 1
if (nodes.length < 1) return this
return this.each(function(_, target){
parent = inside ? target : target.parentNode
// convert all methods to a "before" operation
target = operatorIndex == 0 ? target.nextSibling :
operatorIndex == 1 ? target.firstChild :
operatorIndex == 2 ? target :
null
nodes.forEach(function(node){
if (copyByClone) node = node.cloneNode(true)
else if (!parent) return $(node).remove()
traverseNode(parent.insertBefore(node, target), function(el){
if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
(!el.type || el.type === 'text/javascript') && !el.src)
window['eval'].call(window, el.innerHTML)
})
})
})
}
// after => insertAfter
// prepend => prependTo
// before => insertBefore
// append => appendTo
$.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
$(html)[operator](this)
return this
}
})
zepto.Z.prototype = $.fn
// Export internal API functions in the `$.zepto` namespace
zepto.uniq = uniq
zepto.deserializeValue = deserializeValue
$.zepto = zepto
return $
})()
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)
;(function($){
var jsonpID = 0,
document = window.document,
key,
name,
rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
scriptTypeRE = /^(?:text|application)\/javascript/i,
xmlTypeRE = /^(?:text|application)\/xml/i,
jsonType = 'application/json',
htmlType = 'text/html',
blankRE = /^\s*$/
// trigger a custom event and return false if it was cancelled
function triggerAndReturn(context, eventName, data) {
var event = $.Event(eventName)
$(context).trigger(event, data)
return !event.isDefaultPrevented()
}
// trigger an Ajax "global" event
function triggerGlobal(settings, context, eventName, data) {
if (settings.global) return triggerAndReturn(context || document, eventName, data)
}
// Number of active Ajax requests
$.active = 0
function ajaxStart(settings) {
if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart')
}
function ajaxStop(settings) {
if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop')
}
// triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
function ajaxBeforeSend(xhr, settings) {
var context = settings.context
if (settings.beforeSend.call(context, xhr, settings) === false ||
triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
return false
triggerGlobal(settings, context, 'ajaxSend', [xhr, settings])
}
function ajaxSuccess(data, xhr, settings, deferred) {
var context = settings.context, status = 'success'
settings.success.call(context, data, status, xhr)
if (deferred) deferred.resolveWith(context, [data, status, xhr])
triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data])
ajaxComplete(status, xhr, settings)
}
// type: "timeout", "error", "abort", "parsererror"
function ajaxError(error, type, xhr, settings, deferred) {
var context = settings.context
settings.error.call(context, xhr, type, error)
if (deferred) deferred.rejectWith(context, [xhr, type, error])
triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type])
ajaxComplete(type, xhr, settings)
}
// status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
function ajaxComplete(status, xhr, settings) {
var context = settings.context
settings.complete.call(context, xhr, status)
triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings])
ajaxStop(settings)
}
// Empty function, used as default callback
function empty() {}
$.ajaxJSONP = function(options, deferred){
if (!('type' in options)) return $.ajax(options)
var _callbackName = options.jsonpCallback,
callbackName = ($.isFunction(_callbackName) ?
_callbackName() : _callbackName) || ('jsonp' + (++jsonpID)),
script = document.createElement('script'),
originalCallback = window[callbackName],
responseData,
abort = function(errorType) {
$(script).triggerHandler('error', errorType || 'abort')
},
xhr = { abort: abort }, abortTimeout
if (deferred) deferred.promise(xhr)
$(script).on('load error', function(e, errorType){
clearTimeout(abortTimeout)
$(script).off().remove()
if (e.type == 'error' || !responseData) {
ajaxError(null, errorType || 'error', xhr, options, deferred)
} else {
ajaxSuccess(responseData[0], xhr, options, deferred)
}
window[callbackName] = originalCallback
if (responseData && $.isFunction(originalCallback))
originalCallback(responseData[0])
originalCallback = responseData = undefined
})
if (ajaxBeforeSend(xhr, options) === false) {
abort('abort')
return xhr
}
window[callbackName] = function(){
responseData = arguments
}
script.src = options.url.replace(/=\?/, '=' + callbackName)
document.head.appendChild(script)
if (options.timeout > 0) abortTimeout = setTimeout(function(){
abort('timeout')
}, options.timeout)
return xhr
}
$.ajaxSettings = {
// Default type of request
type: 'GET',
// Callback that is executed before request
beforeSend: empty,
// Callback that is executed if the request succeeds
success: empty,
// Callback that is executed the the server drops error
error: empty,
// Callback that is executed on request complete (both: error and success)
complete: empty,
// The context for the callbacks
context: null,
// Whether to trigger "global" Ajax events
global: true,
// Transport
xhr: function () {
return new window.XMLHttpRequest()
},
// MIME types mapping
// IIS returns Javascript as "application/x-javascript"
accepts: {
script: 'text/javascript, application/javascript, application/x-javascript',
json: jsonType,
xml: 'application/xml, text/xml',
html: htmlType,
text: 'text/plain'
},
// Whether the request is to another domain
crossDomain: false,
// Default timeout
timeout: 0,
// Whether data should be serialized to string
processData: true,
// Whether the browser should be allowed to cache GET responses
cache: true
}
function mimeToDataType(mime) {
if (mime) mime = mime.split(';', 2)[0]
return mime && ( mime == htmlType ? 'html' :
mime == jsonType ? 'json' :
scriptTypeRE.test(mime) ? 'script' :
xmlTypeRE.test(mime) && 'xml' ) || 'text'
}
function appendQuery(url, query) {
if (query == '') return url
return (url + '&' + query).replace(/[&?]{1,2}/, '?')
}
// serialize payload and append it to the URL for GET requests
function serializeData(options) {
if (options.processData && options.data && $.type(options.data) != "string")
options.data = $.param(options.data, options.traditional)
if (options.data && (!options.type || options.type.toUpperCase() == 'GET'))
options.url = appendQuery(options.url, options.data), options.data = undefined
}
$.ajax = function(options){
var settings = $.extend({}, options || {}),
deferred = $.Deferred && $.Deferred()
for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
ajaxStart(settings)
if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
RegExp.$2 != window.location.host
if (!settings.url) settings.url = window.location.toString()
serializeData(settings)
if (settings.cache === false) settings.url = appendQuery(settings.url, '_=' + Date.now())
var dataType = settings.dataType, hasPlaceholder = /=\?/.test(settings.url)
if (dataType == 'jsonp' || hasPlaceholder) {
if (!hasPlaceholder)
settings.url = appendQuery(settings.url,
settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
return $.ajaxJSONP(settings, deferred)
}
var mime = settings.accepts[dataType],
headers = { },
setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] },
protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
xhr = settings.xhr(),
nativeSetHeader = xhr.setRequestHeader,
abortTimeout
if (deferred) deferred.promise(xhr)
if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest')
setHeader('Accept', mime || '*/*')
if (mime = settings.mimeType || mime) {
if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0]
xhr.overrideMimeType && xhr.overrideMimeType(mime)
}
if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET'))
setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded')
if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name])
xhr.setRequestHeader = setHeader
xhr.onreadystatechange = function(){
if (xhr.readyState == 4) {
xhr.onreadystatechange = empty
clearTimeout(abortTimeout)
var result, error = false
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))
result = xhr.responseText
try {
// http://perfectionkills.com/global-eval-what-are-the-options/
if (dataType == 'script') (1,eval)(result)
else if (dataType == 'xml') result = xhr.responseXML
else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
} catch (e) { error = e }
if (error) ajaxError(error, 'parsererror', xhr, settings, deferred)
else ajaxSuccess(result, xhr, settings, deferred)
} else {
ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
}
}
}
if (ajaxBeforeSend(xhr, settings) === false) {
xhr.abort()
ajaxError(null, 'abort', xhr, settings, deferred)
return xhr
}
if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name]
var async = 'async' in settings ? settings.async : true
xhr.open(settings.type, settings.url, async, settings.username, settings.password)
for (name in headers) nativeSetHeader.apply(xhr, headers[name])
if (settings.timeout > 0) abortTimeout = setTimeout(function(){
xhr.onreadystatechange = empty
xhr.abort()
ajaxError(null, 'timeout', xhr, settings, deferred)
}, settings.timeout)
// avoid sending empty string (#319)
xhr.send(settings.data ? settings.data : null)
return xhr
}
// handle optional data/success arguments
function parseArguments(url, data, success, dataType) {
var hasData = !$.isFunction(data)
return {
url: url,
data: hasData ? data : undefined,
success: !hasData ? data : $.isFunction(success) ? success : undefined,
dataType: hasData ? dataType || success : success
}
}
$.get = function(url, data, success, dataType){
return $.ajax(parseArguments.apply(null, arguments))
}
$.post = function(url, data, success, dataType){
var options = parseArguments.apply(null, arguments)
options.type = 'POST'
return $.ajax(options)
}
$.getJSON = function(url, data, success){
var options = parseArguments.apply(null, arguments)
options.dataType = 'json'
return $.ajax(options)
}
$.fn.load = function(url, data, success){
if (!this.length) return this
var self = this, parts = url.split(/\s/), selector,
options = parseArguments(url, data, success),
callback = options.success
if (parts.length > 1) options.url = parts[0], selector = parts[1]
options.success = function(response){
self.html(selector ?
$('<div>').html(response.replace(rscript, "")).find(selector)
: response)
callback && callback.apply(self, arguments)
}
$.ajax(options)
return this
}
var escape = encodeURIComponent
function serialize(params, obj, traditional, scope){
var type, array = $.isArray(obj), hash = $.isPlainObject(obj)
$.each(obj, function(key, value) {
type = $.type(value)
if (scope) key = traditional ? scope :
scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']'
// handle data in serializeArray() format
if (!scope && array) params.add(value.name, value.value)
// recurse into nested objects
else if (type == "array" || (!traditional && type == "object"))
serialize(params, value, traditional, key)
else params.add(key, value)
})
}
$.param = function(obj, traditional){
var params = []
params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) }
serialize(params, obj, traditional)
return params.join('&').replace(/%20/g, '+')
}
})(Zepto)
;(function($){
var $$ = $.zepto.qsa, _zid = 1, undefined,
slice = Array.prototype.slice,
isFunction = $.isFunction,
isString = function(obj){ return typeof obj == 'string' },
handlers = {},
specialEvents={},
focusinSupported = 'onfocusin' in window,
focus = { focus: 'focusin', blur: 'focusout' },
hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
function zid(element) {
return element._zid || (element._zid = _zid++)
}
function findHandlers(element, event, fn, selector) {
event = parse(event)
if (event.ns) var matcher = matcherFor(event.ns)
return (handlers[zid(element)] || []).filter(function(handler) {
return handler
&& (!event.e || handler.e == event.e)
&& (!event.ns || matcher.test(handler.ns))
&& (!fn || zid(handler.fn) === zid(fn))
&& (!selector || handler.sel == selector)
})
}
function parse(event) {
var parts = ('' + event).split('.')
return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
}
function matcherFor(ns) {
return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
}
function eventCapture(handler, captureSetting) {
return handler.del &&
(!focusinSupported && (handler.e in focus)) ||
!!captureSetting
}
function realEvent(type) {
return hover[type] || (focusinSupported && focus[type]) || type
}
function add(element, events, fn, data, selector, delegator, capture){
var id = zid(element), set = (handlers[id] || (handlers[id] = []))
events.split(/\s/).forEach(function(event){
if (event == 'ready') return $(document).ready(fn)
var handler = parse(event)
handler.fn = fn
handler.sel = selector
// emulate mouseenter, mouseleave
if (handler.e in hover) fn = function(e){
var related = e.relatedTarget
if (!related || (related !== this && !$.contains(this, related)))
return handler.fn.apply(this, arguments)
}
handler.del = delegator
var callback = delegator || fn
handler.proxy = function(e){
e = compatible(e)
if (e.isImmediatePropagationStopped()) return
e.data = data
var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
if (result === false) e.preventDefault(), e.stopPropagation()
return result
}
handler.i = set.length
set.push(handler)
if ('addEventListener' in element)
element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
})
}
function remove(element, events, fn, selector, capture){
var id = zid(element)
;(events || '').split(/\s/).forEach(function(event){
findHandlers(element, event, fn, selector).forEach(function(handler){
delete handlers[id][handler.i]
if ('removeEventListener' in element)
element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
})
})
}
$.event = { add: add, remove: remove }
$.proxy = function(fn, context) {
if (isFunction(fn)) {
var proxyFn = function(){ return fn.apply(context, arguments) }
proxyFn._zid = zid(fn)
return proxyFn
} else if (isString(context)) {
return $.proxy(fn[context], fn)
} else {
throw new TypeError("expected function")
}
}
$.fn.bind = function(event, data, callback){
return this.on(event, data, callback)
}
$.fn.unbind = function(event, callback){
return this.off(event, callback)
}
$.fn.one = function(event, selector, data, callback){
return this.on(event, selector, data, callback, 1)
}
var returnTrue = function(){return true},
returnFalse = function(){return false},
ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/,
eventMethods = {
preventDefault: 'isDefaultPrevented',
stopImmediatePropagation: 'isImmediatePropagationStopped',
stopPropagation: 'isPropagationStopped'
}
function compatible(event, source) {
if (source || !event.isDefaultPrevented) {
source || (source = event)
$.each(eventMethods, function(name, predicate) {
var sourceMethod = source[name]
event[name] = function(){
this[predicate] = returnTrue
return sourceMethod && sourceMethod.apply(source, arguments)
}
event[predicate] = returnFalse
})
if (source.defaultPrevented !== undefined ? source.defaultPrevented :
'returnValue' in source ? source.returnValue === false :
source.getPreventDefault && source.getPreventDefault())
event.isDefaultPrevented = returnTrue
}
return event
}
function createProxy(event) {
var key, proxy = { originalEvent: event }
for (key in event)
if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
return compatible(proxy, event)
}
$.fn.delegate = function(selector, event, callback){
return this.on(event, selector, callback)
}
$.fn.undelegate = function(selector, event, callback){
return this.off(event, selector, callback)
}
$.fn.live = function(event, callback){
$(document.body).delegate(this.selector, event, callback)
return this
}
$.fn.die = function(event, callback){
$(document.body).undelegate(this.selector, event, callback)
return this
}
$.fn.on = function(event, selector, data, callback, one){
var autoRemove, delegator, $this = this
if (event && !isString(event)) {
$.each(event, function(type, fn){
$this.on(type, selector, data, fn, one)
})
return $this
}
if (!isString(selector) && !isFunction(callback) && callback !== false)
callback = data, data = selector, selector = undefined
if (isFunction(data) || data === false)
callback = data, data = undefined
if (callback === false) callback = returnFalse
return $this.each(function(_, element){
if (one) autoRemove = function(e){
remove(element, e.type, callback)
return callback.apply(this, arguments)
}
if (selector) delegator = function(e){
var evt, match = $(e.target).closest(selector, element).get(0)
if (match && match !== element) {
evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
}
}
add(element, event, callback, data, selector, delegator || autoRemove)
})
}
$.fn.off = function(event, selector, callback){
var $this = this
if (event && !isString(event)) {
$.each(event, function(type, fn){
$this.off(type, selector, fn)
})
return $this
}
if (!isString(selector) && !isFunction(callback) && callback !== false)
callback = selector, selector = undefined
if (callback === false) callback = returnFalse
return $this.each(function(){
remove(this, event, callback, selector)
})
}
$.fn.trigger = function(event, args){
event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)
event._args = args
return this.each(function(){
// items in the collection might not be DOM elements
if('dispatchEvent' in this) this.dispatchEvent(event)
else $(this).triggerHandler(event, args)
})
}
// triggers event handlers on current element just as if an event occurred,
// doesn't trigger an actual event, doesn't bubble
$.fn.triggerHandler = function(event, args){
var e, result
this.each(function(i, element){
e = createProxy(isString(event) ? $.Event(event) : event)
e._args = args
e.target = element
$.each(findHandlers(element, event.type || event), function(i, handler){
result = handler.proxy(e)
if (e.isImmediatePropagationStopped()) return false
})
})
return result
}
// shortcut methods for `.bind(event, fn)` for each event type
;('focusin focusout load resize scroll unload click dblclick '+
'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
'change select keydown keypress keyup error').split(' ').forEach(function(event) {
$.fn[event] = function(callback) {
return callback ?
this.bind(event, callback) :
this.trigger(event)
}
})
;['focus', 'blur'].forEach(function(name) {
$.fn[name] = function(callback) {
if (callback) this.bind(name, callback)
else this.each(function(){
try { this[name]() }
catch(e) {}
})
return this
}
})
$.Event = function(type, props) {
if (!isString(type)) props = type, type = props.type
var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
event.initEvent(type, bubbles, true)
return compatible(event)
}
})(Zepto)
;(function($, undefined){
var prefix = '', eventPrefix, endEventName, endAnimationName,
vendors = { Webkit: 'webkit', Moz: '', O: 'o' },
document = window.document, testEl = document.createElement('div'),
supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
transform,
transitionProperty, transitionDuration, transitionTiming, transitionDelay,
animationName, animationDuration, animationTiming, animationDelay,
cssReset = {}
function dasherize(str) { return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase() }
function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase() }
$.each(vendors, function(vendor, event){
if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
prefix = '-' + vendor.toLowerCase() + '-'
eventPrefix = event
return false
}
})
transform = prefix + 'transform'
cssReset[transitionProperty = prefix + 'transition-property'] =
cssReset[transitionDuration = prefix + 'transition-duration'] =
cssReset[transitionDelay = prefix + 'transition-delay'] =
cssReset[transitionTiming = prefix + 'transition-timing-function'] =
cssReset[animationName = prefix + 'animation-name'] =
cssReset[animationDuration = prefix + 'animation-duration'] =
cssReset[animationDelay = prefix + 'animation-delay'] =
cssReset[animationTiming = prefix + 'animation-timing-function'] = ''
$.fx = {
off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
speeds: { _default: 400, fast: 200, slow: 600 },
cssPrefix: prefix,
transitionEnd: normalizeEvent('TransitionEnd'),
animationEnd: normalizeEvent('AnimationEnd')
}
$.fn.animate = function(properties, duration, ease, callback, delay){
if ($.isFunction(duration))
callback = duration, ease = undefined, duration = undefined
if ($.isFunction(ease))
callback = ease, ease = undefined
if ($.isPlainObject(duration))
ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration
if (duration) duration = (typeof duration == 'number' ? duration :
($.fx.speeds[duration] || $.fx.speeds._default)) / 1000
if (delay) delay = parseFloat(delay) / 1000
return this.anim(properties, duration, ease, callback, delay)
}
$.fn.anim = function(properties, duration, ease, callback, delay){
var key, cssValues = {}, cssProperties, transforms = '',
that = this, wrappedCallback, endEvent = $.fx.transitionEnd,
fired = false
if (duration === undefined) duration = $.fx.speeds._default / 1000
if (delay === undefined) delay = 0
if ($.fx.off) duration = 0
if (typeof properties == 'string') {
// keyframe animation
cssValues[animationName] = properties
cssValues[animationDuration] = duration + 's'
cssValues[animationDelay] = delay + 's'
cssValues[animationTiming] = (ease || 'linear')
endEvent = $.fx.animationEnd
} else {
cssProperties = []
// CSS transitions
for (key in properties)
if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '
else cssValues[key] = properties[key], cssProperties.push(dasherize(key))
if (transforms) cssValues[transform] = transforms, cssProperties.push(transform)
if (duration > 0 && typeof properties === 'object') {
cssValues[transitionProperty] = cssProperties.join(', ')
cssValues[transitionDuration] = duration + 's'
cssValues[transitionDelay] = delay + 's'
cssValues[transitionTiming] = (ease || 'linear')
}
}
wrappedCallback = function(event){
if (typeof event !== 'undefined') {
if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below"
$(event.target).unbind(endEvent, wrappedCallback)
} else
$(this).unbind(endEvent, wrappedCallback) // triggered by setTimeout
fired = true
$(this).css(cssReset)
callback && callback.call(this)
}
if (duration > 0){
this.bind(endEvent, wrappedCallback)
// transitionEnd is not always firing on older Android phones
// so make sure it gets fired
setTimeout(function(){
if (fired) return
wrappedCallback.call(that)
}, (duration * 1000) + 25)
}
// trigger page reflow so new elements can animate
this.size() && this.get(0).clientLeft
this.css(cssValues)
if (duration <= 0) setTimeout(function() {
that.each(function(){ wrappedCallback.call(this) })
}, 0)
return this
}
testEl = null
})(Zepto)
;(function($, undefined){
var document = window.document, docElem = document.documentElement,
origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle
function anim(el, speed, opacity, scale, callback) {
if (typeof speed == 'function' && !callback) callback = speed, speed = undefined
var props = { opacity: opacity }
if (scale) {
props.scale = scale
el.css($.fx.cssPrefix + 'transform-origin', '0 0')
}
return el.animate(props, speed, null, callback)
}
function hide(el, speed, scale, callback) {
return anim(el, speed, 0, scale, function(){
origHide.call($(this))
callback && callback.call(this)
})
}
$.fn.show = function(speed, callback) {
origShow.call(this)
if (speed === undefined) speed = 0
else this.css('opacity', 0)
return anim(this, speed, 1, '1,1', callback)
}
$.fn.hide = function(speed, callback) {
if (speed === undefined) return origHide.call(this)
else return hide(this, speed, '0,0', callback)
}
$.fn.toggle = function(speed, callback) {
if (speed === undefined || typeof speed == 'boolean')
return origToggle.call(this, speed)
else return this.each(function(){
var el = $(this)
el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback)
})
}
$.fn.fadeTo = function(speed, opacity, callback) {
return anim(this, speed, opacity, null, callback)
}
$.fn.fadeIn = function(speed, callback) {
var target = this.css('opacity')
if (target > 0) this.css('opacity', 0)
else target = 1
return origShow.call(this).fadeTo(speed, target, callback)
}
$.fn.fadeOut = function(speed, callback) {
return hide(this, speed, null, callback)
}
$.fn.fadeToggle = function(speed, callback) {
return this.each(function(){
var el = $(this)
el[
(el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut'
](speed, callback)
})
}
})(Zepto)
;(function($){
var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches
function visible(elem){
elem = $(elem)
return !!(elem.width() || elem.height()) && elem.css("display") !== "none"
}
// Implements a subset from:
// http://api.jquery.com/category/selectors/jquery-selector-extensions/
//
// Each filter function receives the current index, all nodes in the
// considered set, and a value if there were parentheses. The value
// of `this` is the node currently being considered. The function returns the
// resulting node(s), null, or undefined.
//
// Complex selectors are not supported:
// li:has(label:contains("foo")) + li:has(label:contains("bar"))
// ul.inner:first > li
var filters = $.expr[':'] = {
visible: function(){ if (visible(this)) return this },
hidden: function(){ if (!visible(this)) return this },
selected: function(){ if (this.selected) return this },
checked: function(){ if (this.checked) return this },
parent: function(){ return this.parentNode },
first: function(idx){ if (idx === 0) return this },
last: function(idx, nodes){ if (idx === nodes.length - 1) return this },
eq: function(idx, _, value){ if (idx === value) return this },
contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this },
has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this }
}
var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'),
childRe = /^\s*>/,
classTag = 'Zepto' + (+new Date())
function process(sel, fn) {
// quote the hash in `a[href^=#]` expression
sel = sel.replace(/=#\]/g, '="#"]')
var filter, arg, match = filterRe.exec(sel)
if (match && match[2] in filters) {
filter = filters[match[2]], arg = match[3]
sel = match[1]
if (arg) {
var num = Number(arg)
if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '')
else arg = num
}
}
return fn(sel, filter, arg)
}
zepto.qsa = function(node, selector) {
return process(selector, function(sel, filter, arg){
try {
var taggedParent
if (!sel && filter) sel = '*'
else if (childRe.test(sel))
// support "> *" child queries by tagging the parent node with a
// unique class and prepending that classname onto the selector
taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel
var nodes = oldQsa(node, sel)
} catch(e) {
console.error('error performing selector: %o', selector)
throw e
} finally {
if (taggedParent) taggedParent.removeClass(classTag)
}
return !filter ? nodes :
zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) }))
})
}
zepto.matches = function(node, selector){
return process(selector, function(sel, filter, arg){
return (!sel || oldMatches(node, sel)) &&
(!filter || filter.call(node, null, arg) === node)
})
}
})(Zepto)
;(function($){
var touch = {},
touchTimeout, tapTimeout, swipeTimeout, longTapTimeout,
longTapDelay = 750,
gesture
function swipeDirection(x1, x2, y1, y2) {
return Math.abs(x1 - x2) >=
Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}
function longTap() {
longTapTimeout = null
if (touch.last) {
touch.el.trigger('longTap')
touch = {}
}
}
function cancelLongTap() {
if (longTapTimeout) clearTimeout(longTapTimeout)
longTapTimeout = null
}
function cancelAll() {
if (touchTimeout) clearTimeout(touchTimeout)
if (tapTimeout) clearTimeout(tapTimeout)
if (swipeTimeout) clearTimeout(swipeTimeout)
if (longTapTimeout) clearTimeout(longTapTimeout)
touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null
touch = {}
}
function isPrimaryTouch(event){
return (event.pointerType == 'touch' ||
event.pointerType == event.MSPOINTER_TYPE_TOUCH)
&& event.isPrimary
}
function isPointerEventType(e, type){
return (e.type == 'pointer'+type ||
e.type.toLowerCase() == 'mspointer'+type)
}
$(document).ready(function(){
var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType
if ('MSGesture' in window) {
gesture = new MSGesture()
gesture.target = document.body
}
$(document)
.bind('MSGestureEnd', function(e){
var swipeDirectionFromVelocity =
e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null;
if (swipeDirectionFromVelocity) {
touch.el.trigger('swipe')
touch.el.trigger('swipe'+ swipeDirectionFromVelocity)
}
})
.on('touchstart MSPointerDown pointerdown', function(e){
if((_isPointerType = isPointerEventType(e, 'down')) &&
!isPrimaryTouch(e)) return
firstTouch = _isPointerType ? e : e.touches[0]
if (e.touches && e.touches.length === 1 && touch.x2) {
// Clear out touch movement data if we have it sticking around
// This can occur if touchcancel doesn't fire due to preventDefault, etc.
touch.x2 = undefined
touch.y2 = undefined
}
now = Date.now()
delta = now - (touch.last || now)
touch.el = $('tagName' in firstTouch.target ?
firstTouch.target : firstTouch.target.parentNode)
touchTimeout && clearTimeout(touchTimeout)
touch.x1 = firstTouch.pageX
touch.y1 = firstTouch.pageY
if (delta > 0 && delta <= 250) touch.isDoubleTap = true
touch.last = now
longTapTimeout = setTimeout(longTap, longTapDelay)
// adds the current touch contact for IE gesture recognition
if (gesture && _isPointerType) gesture.addPointer(e.pointerId);
})
.on('touchmove MSPointerMove pointermove', function(e){
if((_isPointerType = isPointerEventType(e, 'move')) &&
!isPrimaryTouch(e)) return
firstTouch = _isPointerType ? e : e.touches[0]
cancelLongTap()
touch.x2 = firstTouch.pageX
touch.y2 = firstTouch.pageY
deltaX += Math.abs(touch.x1 - touch.x2)
deltaY += Math.abs(touch.y1 - touch.y2)
})
.on('touchend MSPointerUp pointerup', function(e){
if((_isPointerType = isPointerEventType(e, 'up')) &&
!isPrimaryTouch(e)) return
cancelLongTap()
// swipe
if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
(touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))
swipeTimeout = setTimeout(function() {
touch.el.trigger('swipe')
touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
touch = {}
}, 0)
// normal tap
else if ('last' in touch)
// don't fire tap when delta position changed by more than 30 pixels,
// for instance when moving to a point and back to origin
if (deltaX < 30 && deltaY < 30) {
// delay by one tick so we can cancel the 'tap' event if 'scroll' fires
// ('tap' fires before 'scroll')
tapTimeout = setTimeout(function() {
// trigger universal 'tap' with the option to cancelTouch()
// (cancelTouch cancels processing of single vs double taps for faster 'tap' response)
var event = $.Event('tap')
event.cancelTouch = cancelAll
touch.el.trigger(event)
// trigger double tap immediately
if (touch.isDoubleTap) {
if (touch.el) touch.el.trigger('doubleTap')
touch = {}
}
// trigger single tap after 250ms of inactivity
else {
touchTimeout = setTimeout(function(){
touchTimeout = null
if (touch.el) touch.el.trigger('singleTap')
touch = {}
}, 250)
}
}, 0)
} else {
touch = {}
}
deltaX = deltaY = 0
})
// when the browser window loses focus,
// for example when a modal dialog is shown,
// cancel all ongoing events
.on('touchcancel MSPointerCancel pointercancel', cancelAll)
// scrolling the window indicates intention of the user
// to scroll, not tap or swipe, so cancel all ongoing events
$(window).on('scroll', cancelAll)
})
;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown',
'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(eventName){
$.fn[eventName] = function(callback){ return this.on(eventName, callback) }
})
})(Zepto)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment