diff --git a/css/color-picker.css b/css/color-picker.css
new file mode 100644
index 0000000..cfcbba5
--- /dev/null
+++ b/css/color-picker.css
@@ -0,0 +1,90 @@
+/* Purty Picker Copyright 2013 Jayden Seric (MIT license): https://github.com/jaydenseric/Purty-Picker */
+/* Core: No touchy! */
+.color-picker .spectrum {
+ position: relative;
+ /* To position pin, luminosity filter */
+ background: linear-gradient(gray, transparent), linear-gradient(90deg, red, #ff2b00, #ff5500, #ff8000, #ffaa00, #ffd500, yellow, #d4ff00, #aaff00, #80ff00, #55ff00, #2bff00, lime, #00ff2b, #00ff55, #00ff80, #00ffaa, #00ffd5, cyan, #00d4ff, #00aaff, #007fff, #0055ff, #002bff, blue, #2a00ff, #5500ff, #7f00ff, #aa00ff, #d400ff, magenta, #ff00d4, #ff00aa, #ff0080, #ff0055, #ff002b, red);
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ /* Prevent pin interaction causing content selection */
+ cursor: crosshair;
+}
+.color-picker .spectrum.active {
+ cursor: none;
+}
+.color-picker .spectrum.active .pin {
+ cursor: none;
+}
+.color-picker .spectrum > div {
+ /* Luminosity filter */
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
+.color-picker .spectrum .pin {
+ position: absolute;
+ cursor: move;
+}
+
+/* Customization: Default skin */
+.color-picker {
+ margin: 20px;
+ padding: 11px;
+ border: 1px solid #e3e3e3;
+ border-radius: 4px;
+ background-color: #f5f5f5;
+}
+.color-picker .color,
+.color-picker .luminosity {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ display: block;
+ width: 100%;
+}
+.color-picker .format {
+ display: block;
+ margin: 0 auto 10px auto;
+}
+.color-picker .color {
+ -webkit-appearance: none;
+ border: 0;
+ border-radius: 2px;
+ padding: 10px;
+ text-align: center;
+ font-size: 11px;
+ letter-spacing: 1px;
+ font-family: Consolas, Monaco, 'Andale Mono', monospace;
+ color: rgba(0, 0, 0, 0.6);
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
+ transition: color 0.2s;
+}
+.color-picker .color.dark {
+ color: rgba(255, 255, 255, 0.7);
+}
+.color-picker .spectrum {
+ height: 150px;
+ /* Arbitary but required */
+ overflow: hidden;
+ /* Prevent pin overflowing container */
+ border-radius: 2px;
+ margin: 10px 0;
+}
+.color-picker .spectrum > div {
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
+}
+.color-picker .spectrum .pin {
+ margin-left: -4px;
+ margin-top: -4px;
+ width: 4px;
+ height: 4px;
+ border: 2px solid white;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
+ border-radius: 100%;
+}
+.color-picker .luminosity {
+ margin: 0;
+}
diff --git a/css/imgs/clear.png b/css/imgs/clear.png
new file mode 100644
index 0000000..cfcff2e
Binary files /dev/null and b/css/imgs/clear.png differ
diff --git a/css/imgs/redo.png b/css/imgs/redo.png
new file mode 100644
index 0000000..d2864fb
Binary files /dev/null and b/css/imgs/redo.png differ
diff --git a/css/imgs/undo.png b/css/imgs/undo.png
new file mode 100644
index 0000000..6117001
Binary files /dev/null and b/css/imgs/undo.png differ
diff --git a/css/main.less b/css/main.less
index 75e4be2..4cf4ec5 100644
--- a/css/main.less
+++ b/css/main.less
@@ -65,7 +65,25 @@ button {
border: none;
}
+.picker {
+ width: 30rem;
+ height: 30rem;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ margin-top: -15rem;
+ margin-left: -15rem;
+ #closePicker {
+ width: 2rem;
+ height: 2rem;
+ border: 1px solid rgb(227, 227, 227);
+ border-radius: 50%;
+ position: absolute;
+ top: 1rem;
+ left: 27rem;
+ }
+}
header {
@@ -117,12 +135,14 @@ header {
color: white;
font-family: 'MozTT-Light';
font-size: 8pt;
+ transition: left 0.2s ease-out;
&.pulled {
left: 0;
+ transition: left 0.2s ease-out;
}
- button[id^='set'], p {
+ button[id^='set'], p, .bottom button {
background: none;
display: block;
width: 75%;
@@ -132,6 +152,7 @@ header {
font-family: 'MozTT-Light';
font-size: 8pt;
padding: 0 0.6rem;
+ cursor: pointer;
}
span {
float: right;
@@ -150,13 +171,22 @@ header {
}
}
}
- .icon-settings {
+ hr {
+ clear: both;
+ padding: 0.7rem 0;
+ margin-bottom: 0.7rem;
+ border: none;
+ border-bottom: 1px solid rgba(255,255,255,0.3);
+ }
+ *[class^='icon'] {
display: block;
margin: 1rem 0.5rem;
+ &:nth-of-type(2) {
+ padding-top: 0.5rem;
+ }
&:before {
content: '';
- background: url('imgs/settings.png') center center no-repeat;
background-size: 2rem;
width: 2rem;
height: 2rem;
@@ -166,10 +196,30 @@ header {
}
}
+ .icon-settings:before {
+ background-image: url('imgs/settings.png');
+ }
+ .icon-clear:before {
+ background-image: url('imgs/clear.png');
+ }
+ .icon-undo:before {
+ background-image: url('imgs/undo.png');
+ }
+ .icon-redo:before {
+ background-image: url('imgs/redo.png');
+ }
+
.options {
display: block;
margin-top: 1rem;
}
+ .bottom {
+ position: absolute;
+ bottom: 5rem;
+ button {
+ margin-left: 3.5rem;
+ }
+ }
}
diff --git a/css/value_selector.css b/css/value_selector.css
index aebc7a7..0a3493c 100644
--- a/css/value_selector.css
+++ b/css/value_selector.css
@@ -114,7 +114,7 @@ form[role="dialog"][data-type="value-selector"] [role="listbox"] li:active label
}
/* Checked status */
-form[role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] span {
+form[role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"]:not([data-input]) span {
padding-right: 2.6rem;
margin-right: 1.2rem;
color: #00abcd;
diff --git a/index.html b/index.html
index ba294b7..34be387 100644
--- a/index.html
+++ b/index.html
@@ -8,6 +8,7 @@
+
@@ -36,24 +37,59 @@
Line Width 2
-
+
+
+
+
Sketch
+
Connet Telorance40
+
+
+
+
+
Fur
+
Connet Telorance40
+
+
Fur Length20
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
diff --git a/js/events.js b/js/events.js
index 0326d4b..f910013 100644
--- a/js/events.js
+++ b/js/events.js
@@ -1,22 +1,56 @@
+"use strict";
+
+function save() {
+ switch(save.background) {
+ case 'white': {
+ var cache = {
+ color: c.color,
+ composite: c.globalCompositeOperation
+ }
+ c.fillStyle = 'white';
+ c.globalCompositeOperation = 'destination-over';
+ c.fillRect(0, 0, $c.width(), $c.height());
+ c.fillStyle = cache.fillStyle;
+ c.globalCompositeOperation = cache.composite;
+ break;
+ }
+ case 'current color': {
+ var cache = {
+ fillStyle: c.color,
+ composite: c.globalCompositeOperation
+ }
+ c.fillStyle = settings.strokeStyle;
+ c.globalCompositeOperation = 'destination-over';
+ c.fillRect(0, 0, $c.width(), $c.height());
+ c.fillStyle = cache.fillStyle;
+ c.globalCompositeOperation = cache.composite;
+ break;
+ }
+ }
+ var data = $c[0].toDataURL();
+ window.open(data, save['file name']);
+
+ c.putImageData(window.points.history[window.points.history.length-1].data, 0, 0);
+
+}
+
$('.menu').click(function() {
$('#menu').toggleClass('pulled');
})
$('.save').click(function() {
- var data = $c[0].toDataURL();
- var img = $('');
- $('body').append(img);
+ $('#save').removeClass('hidden');
})
- $c.last().bind('mousedown', function(e) {
+ $c.last().on('mousedown', function(e) {
e.preventDefault();
var xy = relative(e.pageX, e.pageY);
startPoint(xy.x, xy.y);
window.active = true;
- }).bind('mousemove', function(e) {
+ }).on('mousemove', function(e) {
e.preventDefault();
if (!window.active || settings.type == 'line') return;
var xy = relative(e.pageX, e.pageY);
drawPoint(xy.x, xy.y);
- }).bind('mouseup mouseout', function(e) {
+ }).on('mouseup', function(e) {
e.preventDefault();
window.active = false;
@@ -33,32 +67,68 @@
// Value Selector
- var $selector = $('form[data-type="value-selector"]');
+ // Single
+
+ var $single = $('form[data-type="value-selector"].single');
- $selector.find('li').on('mouseup', function(e) {
- alert(e.type);
- $(this).parent().find('li').removeAttr('aria-selected');
+ $single.find('li').click(function(e) {
+ $(this).parent().find('li[aria-selected]').removeAttr('aria-selected');
$(this).attr('aria-selected', 'true');
- var prop = $(this).parents('form').attr('id'),
- key = $(this).find('label span').html().toLowerCase();
- window.settings[prop] = key;
+ var key = $(this).parents('form').attr('id'),
+ value = $(this).find('label span').html().toLowerCase();
+ window.settings[key] = value;
- $('button[id="set' + prop + '"] span').html(key[0].toUpperCase() + key.substr(1));
+ $('button[id="set' + key + '"] span').html(value[0].toUpperCase() + value.substr(1));
+ $('#menu div.options > div').addClass('hidden');
+ $('#menu div.options > .general, #menu div.options > .'+value).removeClass('hidden');
$(this).parents('form').addClass('hidden');
})
-
- $selector.submit(function(e) {
+ $single.submit(function(e) {
e.preventDefault();
$(this).addClass('hidden');
})
+ // Confirm
+
+ var $confirm = $('form[data-type="value-selector"].confirm');
+
+ $confirm.find('li').click(function(e) {
+ $(this).parent().find('li[aria-selected]').removeAttr('aria-selected');
+ $(this).attr('aria-selected', 'true');
+ })
+ $confirm.find('button').last().click(function(e) {
+ e.preventDefault();
+ var v = $(this).parents('form').attr('id');
+ $(this).parents('form').find('h1').each(function(i) {
+ if( i > 0 ) {
+ var key = $(this).html().toLowerCase();
+ var value = $(this).parent().find('ol:nth-of-type('+i+') li[aria-selected] span').html();
+ if( key !== 'file name' ) value = key.toLowerCase();
+
+ window[v][key] = value;
+ }
+ })
+ $(this).parents('form').addClass('hidden');
+ window[v]();
+ })
+ $confirm.find('button').first().click(function(e) {
+ e.preventDefault();
+ $(this).parents('form').addClass('hidden');
+ })
+
// Value Selector Callers
var $btn = $('button[id^="set"]');
$btn.each(function() {
var target = /set(.*)/.exec($(this).attr('id'))[1];
- $(this).on('mouseup', function(e) {
+ // Exception for Color
+ if( target == 'color' ) {
+ return $(this).click(function() {
+ $('.picker').removeClass('hidden');
+ })
+ }
+ $(this).click(function(e) {
e.preventDefault();
$('form[id="' + target + '"]').removeClass('hidden');
})
@@ -66,19 +136,50 @@
// Seekbar
- var sliderLeft = $('div[role="slider"] button').offset().left;
+ var sliderLeft;
$('div[role="slider"] button').on('mousedown', function() {
$(this).attr('data-moving','true');
+ if( !sliderLeft ) sliderLeft = $('div[role="slider"] button').offset().left;
}).on('mousemove', function(e) {
if( $(this).attr('data-moving') ) {
- var x = e.pageX - sliderLeft - 15;
- if( x <= 100 && x > 0 ) {
+ var x = parseInt(e.pageX - sliderLeft - 15);
+ var progress = $(this).parent().children().first();
+ var max = +progress.attr('max');
+ var min = +progress.attr('min');
+ if( x <= max && x >= min ) {
$(this).css('left', x+'%');
$(this).parent().find('progress').attr('value', x);
- settings.lineWidth = x / 10;
- $('#lineWidth span').html(x);
+ var key = $(this).parents('div[role="slider"]').attr('class');
+ settings[key] = x;
+ $('#'+ key +' span').html(x);
}
}
}).on('mouseup mouseleave', function() {
$(this).removeAttr('data-moving');
})
+
+
+ // Color Picker
+
+ $('#closePicker').click(function() {
+ $('.picker').addClass('hidden');
+ })
+
+ // Bottom
+
+ $('#clear').click(function() {
+ c.clearRect(0, 0, $c.width(), $c.height());
+ if(window.points.history.last < window.points.history.length-1) {
+ window.points.history.splice(window.points.history.last+1);
+ }
+
+ window.points.history.push({
+ data: c.getImageData(0, 0, $c.width(), $c.height()),
+ points: []
+ })
+ window.points.history.last = window.points.history.length-1;
+ })
+
+ $('#undo').click(undo);
+ $('#redo').click(redo);
+
diff --git a/js/functions.js b/js/functions.js
index dfebb71..4c48306 100644
--- a/js/functions.js
+++ b/js/functions.js
@@ -6,16 +6,20 @@ function sizeAndPos() {
var data = c.getImageData(0,0, $c.width(), $c.height());
var w = $(window).width(),
h = $(window).height();
- $c.attr('width', w);
- $c.attr('height',h - 53);
+ $c.attr('width', w * window.devicePixelRatio);
+ $c.attr('height',h * window.devicePixelRatio - 53 * window.devicePixelRatio);
+ $c.css({
+ 'width' : w,
+ 'height' : h - 53
+ });
c.clearRect(0,0, $c.width(), $c.height());
c.putImageData(data, 0, 0);
}
function relative(x,y) {
return {
- x : x - $c.offset().left,
- y : y - $c.offset().top
+ x : x*window.devicePixelRatio,
+ y : y*window.devicePixelRatio - 53 * window.devicePixelRatio
}
}
@@ -32,11 +36,13 @@ function line(x1, y1, x2, y2, opts, overlay) {
c.beginPath();
c.lineCap = opts.lineCap || settings.lineCap;
c.lineJoin = opts.lineJoin || settings.lineJoin;
- c.strokeStyle = opts.strokeStyle || settings.strokeStyle;
- c.lineWidth = opts.lineWidth || settings.lineWidth;
+ c.strokeStyle = opts.color || settings.color;
+ c.fillStyle = opts.color || settings.color;
+ c.lineWidth = ( opts.lineWidth || settings.lineWidth ) / 10;
c.moveTo(x1, y1);
c.lineTo(x2, y2);
- c.stroke();
+ if( !opts.noStroke ) c.stroke();
+ if( opts.fill ) c.fill();
}
function erase(x1, y1, x2, y2, overlay) {
@@ -73,6 +79,13 @@ function redo() {
}
}
+function dataToBlob(data) {
+ var binary = atob(data.split(',')[1]), array = [];
+ var type = data.split(',')[0].split(':')[1].split(';')[0];
+ for(var i = 0; i < binary.length; i++) array.push(binary.charCodeAt(i));
+ return new Blob([new Uint8Array(array)], {type: type});
+}
+
/*** END ***/
@@ -90,7 +103,10 @@ function startPoint(x, y) {
type : settings.type
}
if( old.type !== 'line' && current.type == 'line' ) {
- line(x,y,x,y, {lineWidth: 5, strokeStyle: 'red'}, true);
+ window.o.beginPath();
+ window.o.fillStyle = 'red';
+ window.o.arc(x,y, 3, 0, 2*Math.PI);
+ window.o.fill();
}
if( old.type == 'line' ) {
@@ -135,11 +151,11 @@ function drawPoint(x,y) {
points.push(current);
for( var i = 0, len = points.length-1; i < len; i++ ) {
- if(threshold(points[i].x, points[i].y, current.x, current.y, 40)) {
+ if(threshold(points[i].x, points[i].y, current.x, current.y, settings.connectTelorance)) {
var x = points[i].x - current.x,
y = points[i].y - current.y;
- line(points[i].x - x*0.2, points[i].y - y*0.2, current.x + x*0.2, current.y + y*0.2, {strokeStyle: 'rgba(0,0,0,0.4)', lineWidth: settings.lineWidth/2})
+ line(points[i].x - x*0.2, points[i].y - y*0.2, current.x + x*0.2, current.y + y*0.2, {strokeStyle: 'rgba(0,0,0,0.4)', lineWidth: settings.lineWidth/20})
}
}
break;
@@ -155,10 +171,10 @@ function drawPoint(x,y) {
points.push(current);
for( var i = 0, len = points.length-1; i < len; i++ ) {
- if(threshold(points[i].x, points[i].y, current.x, current.y, settings.lineWidth*20)) {
+ if(threshold(points[i].x, points[i].y, current.x, current.y, settings.connectTelorance)) {
var x = points[i].x - current.x,
y = points[i].y - current.y;
- var l = settings.furLength || 0.2;
+ var l = settings.furLength / 100 || 0.2;
line(points[i].x + x*l, points[i].y + y*l, current.x - x*l, current.y - y*l, {strokeStyle: 'rgba(0,0,0,0.4)', lineWidth: settings.lineWidth/2})
}
}
diff --git a/js/libs/color-picker-touch.js b/js/libs/color-picker-touch.js
new file mode 100644
index 0000000..85acba3
--- /dev/null
+++ b/js/libs/color-picker-touch.js
@@ -0,0 +1,290 @@
+/*
+
+Purty Picker Copyright 2013 Jayden Seric (MIT license).
+
+A super lightweight visual HSL, RGB and hex color picker with a responsive, touch-friendly and customizable UI.
+
+Requires jQuery or Zepto with core and event modules.
+
+https://github.com/jaydenseric/Purty-Picker
+
+*/
+
+// DOM ready
+$(function() {
+ 'use strict';
+
+//-------------------------------------------- Color conversions
+
+//---------------------- Convert HSL to RGB
+
+// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
+
+ function HSLToRGB(h, s, l) {
+ h /= 360;
+ s /= 100;
+ l /= 100;
+ var r, g, b;
+ if(s == 0) {
+ r = g = b = l; // Achromatic
+ } else {
+ var hueToRGB = function(p, q, t) {
+ if(t < 0) t += 1;
+ if(t > 1) t -= 1;
+ if(t < 1/6) return p + (q - p) * 6 * t;
+ if(t < 1/2) return q;
+ if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ };
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s,
+ p = 2 * l - q;
+ r = hueToRGB(p, q, h + 1/3);
+ g = hueToRGB(p, q, h);
+ b = hueToRGB(p, q, h - 1/3);
+ }
+ return {
+ red: Math.round(r * 255),
+ green: Math.round(g * 255),
+ blue: Math.round(b * 255)
+ };
+ }
+
+//---------------------- Convert RGB to HSL
+
+// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
+
+ function RGBToHSL(r, g, b) {
+ r /= 255,
+ g /= 255,
+ b /= 255;
+ var max = Math.max(r, g, b),
+ min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2;
+ if (max == min) {
+ h = s = 0; // Achromatic
+ } else {
+ var d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch(max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+ return {
+ hue: Math.round(h * 360),
+ saturation: Math.round(s * 100),
+ luminosity: Math.round(l * 100)
+ };
+ }
+
+//---------------------- Convert RGB to Hex
+
+// Source: http://stackoverflow.com/a/5624139
+
+ function RGBToHex(r, g, b) {
+ return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
+ }
+
+//---------------------- Convert hex to RGB
+
+// Source: http://stackoverflow.com/a/11508164
+
+ function hexToRGB(hex) {
+ var bigInt = parseInt(hex.replace('#', ''), 16),
+ r = (bigInt >> 16) & 255,
+ g = (bigInt >> 8) & 255,
+ b = bigInt & 255;
+ return {
+ red: r,
+ green: g,
+ blue: b
+ };
+ }
+
+//---------------------- Convert hex to HSL
+
+ function hexToHSL(hex) {
+ var RGB = hexToRGB(hex);
+ return RGBToHSL(RGB.red, RGB.green, RGB.blue);
+ }
+
+//---------------------- Convert HSL to hex
+
+ function HSLToHex(h, s, l) {
+ var RGB = HSLToRGB(h, s, l);
+ return RGBToHex(RGB.red, RGB.green, RGB.blue);
+ }
+
+//-------------------------------------------- Setup each color picker
+
+ $.each($('.color-picker'), function() {
+
+//---------------------- Find componenets
+
+ var picker = $(this),
+ formatInput = picker.find('.format'),
+ colorInput = picker.find('.color'),
+ luminosityInput = picker.find('input[type=range]'),
+ spectrum = picker.find('.spectrum'),
+ pin = picker.find('.pin');
+
+//---------------------- Get current color in HSL
+
+ function getHSL() {
+ var position = picker.find('.pin').position(),
+ width = spectrum.width(),
+ height = spectrum.height();
+ return {
+ hue: Math.round(position.left / width * 360),
+ saturation: Math.round(position.top / height * 100),
+ luminosity: luminosityInput.val()
+ };
+ }
+
+//---------------------- Output color in desired format
+
+ function updateColorInput() {
+ var HSL = getHSL();
+ switch (formatInput.val()) {
+ case 'HSL':
+ colorInput.val('hsl(' + HSL.hue + ', ' + HSL.saturation + '%, ' + HSL.luminosity + '%)');
+ break;
+ case 'RGB':
+ var RGB = HSLToRGB(HSL.hue, HSL.saturation, HSL.luminosity);
+ colorInput.val('rgb(' + RGB.red + ', ' + RGB.green + ', ' + RGB.blue + ')');
+ break;
+ case 'Hex':
+ colorInput.val(HSLToHex(HSL.hue, HSL.saturation, HSL.luminosity));
+ break;
+ }
+ // Trigger color picker change event for custom callbacks
+ picker.trigger('change');
+ }
+
+//---------------------- Set color format
+
+ formatInput.on('change', function() {
+ updateColorInput();
+ });
+
+//---------------------- Set color
+
+ colorInput.on('change', function() {
+ // Get the color values in HSL format
+ var HSL;
+ switch (formatInput.val()) {
+ case 'HSL':
+ var values = $(this).val().match(/\d+/g);
+ HSL = {
+ hue: values[0],
+ saturation: values[1],
+ luminosity: values[2]
+ };
+ break;
+ case 'RGB':
+ var values = $(this).val().match(/\d+/g);
+ HSL = RGBToHSL(values[0], values[1], values[2]);
+ break;
+ case 'Hex':
+ HSL = hexToHSL($(this).val());
+ break;
+ }
+ // Set the luminosity
+ luminosityInput.val(HSL.luminosity);
+ setLuminosity(HSL.luminosity);
+ // Place the pin
+ pin.css({
+ left: HSL.hue / 360 * 100 + '%',
+ top: HSL.saturation + '%'
+ });
+ // Trigger color picker change event for custom callbacks
+ picker.trigger('change');
+ });
+
+//---------------------- Set luminosity
+
+//---------- Set the luminosity spectrum overlay
+
+ function setLuminosity(luminosity) {
+ var color,
+ alpha;
+ if (luminosity <= 50) {
+ color = '0, 0, 0';
+ alpha = 1 - luminosity / 100 * 2;
+ } else {
+ color = '255, 255, 255';
+ alpha = luminosity / 100 * 2 - 1;
+ }
+ // Apply luminosity to the spectrum
+ spectrum.children().css('background-color', 'rgba(' + color + ', ' + alpha + ')');
+ }
+
+//---------- Luminosity input interaction
+
+ luminosityInput.on('change', function() {
+ setLuminosity($(this).val());
+ updateColorInput();
+ });
+
+//---------------------- Set hue, saturation via pin
+
+//---------- Move the pin
+
+ var movePin = function(event) {
+ var offset = spectrum.offset(),
+ width = spectrum.width(),
+ height = spectrum.height(),
+ x = event.changedTouches[0].clientX - offset.left,
+ y = event.changedTouches[0].clientY - offset.top;
+ // Account for pin being dragged outside the spectrum area
+ // Sanatize x
+ if (x < 0) {
+ x = 0;
+ } else if (x >= width) {
+ x = width;
+ }
+ // Sanatize y
+ if (y < 0) {
+ y = 0;
+ } else if (y >= height) {
+ y = height;
+ }
+ // Place the pin
+ pin.css({
+ left: x / width * 100 + '%',
+ top: y / height * 100 + '%'
+ });
+ // Output new color value
+ updateColorInput();
+ };
+
+//---------- Pin interaction
+
+ spectrum.on('touchstart', function(event) {
+ event.preventDefault();
+ movePin(event);
+ spectrum.addClass('active');
+ $(document).on('touchmove', movePin);
+ });
+
+ $(document).on('touchend', function() {
+ spectrum.removeClass('active');
+ $(document).off('touchmove', movePin);
+ });
+
+ spectrum.on('touchmove touchstart', movePin);
+
+//---------------------- Output color preview
+
+ picker.on('change', function() {
+ colorInput.css('background-color', colorInput.val()).toggleClass('dark', luminosityInput.val() <= 50);
+ });
+
+//---------------------- Initialize this color picker
+
+ colorInput.trigger('change');
+
+ });
+});
diff --git a/js/libs/color-picker.js b/js/libs/color-picker.js
new file mode 100644
index 0000000..7c211f9
--- /dev/null
+++ b/js/libs/color-picker.js
@@ -0,0 +1,289 @@
+/*
+
+Purty Picker Copyright 2013 Jayden Seric (MIT license).
+
+A super lightweight visual HSL, RGB and hex color picker with a responsive, touch-friendly and customizable UI.
+
+Requires jQuery or Zepto with core and event modules.
+
+https://github.com/jaydenseric/Purty-Picker
+
+*/
+
+// DOM ready
+$(function() {
+ 'use strict';
+
+//-------------------------------------------- Color conversions
+
+//---------------------- Convert HSL to RGB
+
+// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
+
+ function HSLToRGB(h, s, l) {
+ h /= 360;
+ s /= 100;
+ l /= 100;
+ var r, g, b;
+ if(s == 0) {
+ r = g = b = l; // Achromatic
+ } else {
+ var hueToRGB = function(p, q, t) {
+ if(t < 0) t += 1;
+ if(t > 1) t -= 1;
+ if(t < 1/6) return p + (q - p) * 6 * t;
+ if(t < 1/2) return q;
+ if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ };
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s,
+ p = 2 * l - q;
+ r = hueToRGB(p, q, h + 1/3);
+ g = hueToRGB(p, q, h);
+ b = hueToRGB(p, q, h - 1/3);
+ }
+ return {
+ red: Math.round(r * 255),
+ green: Math.round(g * 255),
+ blue: Math.round(b * 255)
+ };
+ }
+
+//---------------------- Convert RGB to HSL
+
+// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
+
+ function RGBToHSL(r, g, b) {
+ r /= 255,
+ g /= 255,
+ b /= 255;
+ var max = Math.max(r, g, b),
+ min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2;
+ if (max == min) {
+ h = s = 0; // Achromatic
+ } else {
+ var d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch(max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+ return {
+ hue: Math.round(h * 360),
+ saturation: Math.round(s * 100),
+ luminosity: Math.round(l * 100)
+ };
+ }
+
+//---------------------- Convert RGB to Hex
+
+// Source: http://stackoverflow.com/a/5624139
+
+ function RGBToHex(r, g, b) {
+ return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
+ }
+
+//---------------------- Convert hex to RGB
+
+// Source: http://stackoverflow.com/a/11508164
+
+ function hexToRGB(hex) {
+ var bigInt = parseInt(hex.replace('#', ''), 16),
+ r = (bigInt >> 16) & 255,
+ g = (bigInt >> 8) & 255,
+ b = bigInt & 255;
+ return {
+ red: r,
+ green: g,
+ blue: b
+ };
+ }
+
+//---------------------- Convert hex to HSL
+
+ function hexToHSL(hex) {
+ var RGB = hexToRGB(hex);
+ return RGBToHSL(RGB.red, RGB.green, RGB.blue);
+ }
+
+//---------------------- Convert HSL to hex
+
+ function HSLToHex(h, s, l) {
+ var RGB = HSLToRGB(h, s, l);
+ return RGBToHex(RGB.red, RGB.green, RGB.blue);
+ }
+
+//-------------------------------------------- Setup each color picker
+
+ $.each($('.color-picker'), function() {
+
+//---------------------- Find componenets
+
+ var picker = $(this),
+ formatInput = picker.find('.format'),
+ colorInput = picker.find('.color'),
+ luminosityInput = picker.find('input[type=range]'),
+ spectrum = picker.find('.spectrum'),
+ pin = picker.find('.pin');
+
+//---------------------- Get current color in HSL
+
+ function getHSL() {
+ var position = picker.find('.pin').position(),
+ width = spectrum.width(),
+ height = spectrum.height();
+ return {
+ hue: Math.round(position.left / width * 360),
+ saturation: Math.round(position.top / height * 100),
+ luminosity: luminosityInput.val()
+ };
+ }
+
+//---------------------- Output color in desired format
+
+ function updateColorInput() {
+ var HSL = getHSL();
+ switch (formatInput.val()) {
+ case 'HSL':
+ colorInput.val('hsl(' + HSL.hue + ', ' + HSL.saturation + '%, ' + HSL.luminosity + '%)');
+ break;
+ case 'RGB':
+ var RGB = HSLToRGB(HSL.hue, HSL.saturation, HSL.luminosity);
+ colorInput.val('rgb(' + RGB.red + ', ' + RGB.green + ', ' + RGB.blue + ')');
+ break;
+ case 'Hex':
+ colorInput.val(HSLToHex(HSL.hue, HSL.saturation, HSL.luminosity));
+ break;
+ }
+ // Trigger color picker change event for custom callbacks
+ picker.trigger('change');
+ }
+
+//---------------------- Set color format
+
+ formatInput.on('change', function() {
+ updateColorInput();
+ });
+
+//---------------------- Set color
+
+ colorInput.on('change', function() {
+ // Get the color values in HSL format
+ var HSL;
+ switch (formatInput.val()) {
+ case 'HSL':
+ var values = $(this).val().match(/\d+/g);
+ HSL = {
+ hue: values[0],
+ saturation: values[1],
+ luminosity: values[2]
+ };
+ break;
+ case 'RGB':
+ var values = $(this).val().match(/\d+/g);
+ HSL = RGBToHSL(values[0], values[1], values[2]);
+ break;
+ case 'Hex':
+ HSL = hexToHSL($(this).val());
+ break;
+ }
+ // Set the luminosity
+ luminosityInput.val(HSL.luminosity);
+ setLuminosity(HSL.luminosity);
+ // Place the pin
+ pin.css({
+ left: HSL.hue / 360 * 100 + '%',
+ top: HSL.saturation + '%'
+ });
+ // Trigger color picker change event for custom callbacks
+ picker.trigger('change');
+ });
+
+//---------------------- Set luminosity
+
+//---------- Set the luminosity spectrum overlay
+
+ function setLuminosity(luminosity) {
+ var color,
+ alpha;
+ if (luminosity <= 50) {
+ color = '0, 0, 0';
+ alpha = 1 - luminosity / 100 * 2;
+ } else {
+ color = '255, 255, 255';
+ alpha = luminosity / 100 * 2 - 1;
+ }
+ // Apply luminosity to the spectrum
+ spectrum.children().css('background-color', 'rgba(' + color + ', ' + alpha + ')');
+ }
+
+//---------- Luminosity input interaction
+
+ luminosityInput.on('change', function() {
+ setLuminosity($(this).val());
+ updateColorInput();
+ });
+
+//---------------------- Set hue, saturation via pin
+
+//---------- Move the pin
+
+ var movePin = function(event) {
+ var offset = spectrum.offset(),
+ width = spectrum.width(),
+ height = spectrum.height(),
+ x = event.clientX - offset.left,
+ y = event.clientY - offset.top;
+ // Account for pin being dragged outside the spectrum area
+ // Sanatize x
+ if (x < 0) {
+ x = 0;
+ } else if (x >= width) {
+ x = width;
+ }
+ // Sanatize y
+ if (y < 0) {
+ y = 0;
+ } else if (y >= height) {
+ y = height;
+ }
+ // Place the pin
+ pin.css({
+ left: x / width * 100 + '%',
+ top: y / height * 100 + '%'
+ });
+ // Output new color value
+ updateColorInput();
+ };
+
+//---------- Pin interaction
+
+ spectrum.on('mousedown', function(event) {
+ event.preventDefault();
+ movePin(event);
+ spectrum.addClass('active');
+ $(document).on('mousemove', movePin);
+ });
+
+ $(document).on('mouseup', function() {
+ spectrum.removeClass('active');
+ $(document).off('mousemove', movePin);
+ });
+
+ spectrum.on('touchmove touchstart', movePin);
+
+//---------------------- Output color preview
+
+ picker.on('change', function() {
+ colorInput.css('background-color', colorInput.val()).toggleClass('dark', luminosityInput.val() <= 50);
+ });
+
+//---------------------- Initialize this color picker
+
+ colorInput.trigger('change');
+ });
+});
diff --git a/js/libs/stack.js b/js/libs/stack.js
new file mode 100644
index 0000000..cbec614
--- /dev/null
+++ b/js/libs/stack.js
@@ -0,0 +1,22 @@
+// Zepto.js
+// (c) 2010-2014 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(function($){
+ $.fn.end = function(){
+ return this.prevObject || $()
+ }
+
+ $.fn.andSelf = function(){
+ return this.add(this.prevObject || $())
+ }
+
+ 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){
+ var fn = $.fn[property]
+ $.fn[property] = function(){
+ var ret = fn.apply(this, arguments)
+ ret.prevObject = this
+ return ret
+ }
+ })
+})(Zepto)
diff --git a/js/libs/touch.js b/js/libs/touch.js
new file mode 100644
index 0000000..c449212
--- /dev/null
+++ b/js/libs/touch.js
@@ -0,0 +1,165 @@
+// Zepto.js
+// (c) 2010-2014 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+;(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)
diff --git a/js/libs/zepto.min.js b/js/libs/zepto.min.js
index 112a1da..f64fc04 100644
--- a/js/libs/zepto.min.js
+++ b/js/libs/zepto.min.js
@@ -1,2 +1,2 @@
-/* Zepto v1.0-1-ga3cab6c - polyfill zepto detect event ajax form fx - zeptojs.com/license */
-(function(a){String.prototype.trim===a&&(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),Array.prototype.reduce===a&&(Array.prototype.reduce=function(b){if(this===void 0||this===null)throw new TypeError;var c=Object(this),d=c.length>>>0,e=0,f;if(typeof b!="function")throw new TypeError;if(d==0&&arguments.length==1)throw new TypeError;if(arguments.length>=2)f=arguments[1];else do{if(e in c){f=c[e++];break}if(++e>=d)throw new TypeError}while(!0);while(e0?c.fn.concat.apply([],a):a}function O(a){return a.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function P(a){return a in j?j[a]:j[a]=new RegExp("(^|\\s)"+a+"(\\s|$)")}function Q(a,b){return typeof b=="number"&&!l[O(a)]?b+"px":b}function R(a){var b,c;return i[a]||(b=h.createElement(a),h.body.appendChild(b),c=k(b,"").getPropertyValue("display"),b.parentNode.removeChild(b),c=="none"&&(c="block"),i[a]=c),i[a]}function S(a){return"children"in a?f.call(a.children):c.map(a.childNodes,function(a){if(a.nodeType==1)return a})}function T(c,d,e){for(b in d)e&&(J(d[b])||K(d[b]))?(J(d[b])&&!J(c[b])&&(c[b]={}),K(d[b])&&!K(c[b])&&(c[b]=[]),T(c[b],d[b],e)):d[b]!==a&&(c[b]=d[b])}function U(b,d){return d===a?c(b):c(b).filter(d)}function V(a,b,c,d){return F(b)?b.call(a,c,d):b}function W(a,b,c){c==null?a.removeAttribute(b):a.setAttribute(b,c)}function X(b,c){var d=b.className,e=d&&d.baseVal!==a;if(c===a)return e?d.baseVal:d;e?d.baseVal=c:b.className=c}function Y(a){var b;try{return a?a=="true"||(a=="false"?!1:a=="null"?null:isNaN(b=Number(a))?/^[\[\{]/.test(a)?c.parseJSON(a):a:b):a}catch(d){return a}}function Z(a,b){b(a);for(var c in a.childNodes)Z(a.childNodes[c],b)}var a,b,c,d,e=[],f=e.slice,g=e.filter,h=window.document,i={},j={},k=h.defaultView.getComputedStyle,l={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},m=/^\s*<(\w+|!)[^>]*>/,n=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,o=/^(?:body|html)$/i,p=["val","css","html","text","data","width","height","offset"],q=["after","prepend","before","append"],r=h.createElement("table"),s=h.createElement("tr"),t={tr:h.createElement("tbody"),tbody:r,thead:r,tfoot:r,td:s,th:s,"*":h.createElement("div")},u=/complete|loaded|interactive/,v=/^\.([\w-]+)$/,w=/^#([\w-]*)$/,x=/^[\w-]+$/,y={},z=y.toString,A={},B,C,D=h.createElement("div");return A.matches=function(a,b){if(!a||a.nodeType!==1)return!1;var c=a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.matchesSelector;if(c)return c.call(a,b);var d,e=a.parentNode,f=!e;return f&&(e=D).appendChild(a),d=~A.qsa(e,b).indexOf(a),f&&D.removeChild(a),d},B=function(a){return a.replace(/-+(.)?/g,function(a,b){return b?b.toUpperCase():""})},C=function(a){return g.call(a,function(b,c){return a.indexOf(b)==c})},A.fragment=function(b,d,e){b.replace&&(b=b.replace(n,"<$1>$2>")),d===a&&(d=m.test(b)&&RegExp.$1),d in t||(d="*");var g,h,i=t[d];return i.innerHTML=""+b,h=c.each(f.call(i.childNodes),function(){i.removeChild(this)}),J(e)&&(g=c(h),c.each(e,function(a,b){p.indexOf(a)>-1?g[a](b):g.attr(a,b)})),h},A.Z=function(a,b){return a=a||[],a.__proto__=c.fn,a.selector=b||"",a},A.isZ=function(a){return a instanceof A.Z},A.init=function(b,d){if(!b)return A.Z();if(F(b))return c(h).ready(b);if(A.isZ(b))return b;var e;if(K(b))e=M(b);else if(I(b))e=[J(b)?c.extend({},b):b],b=null;else if(m.test(b))e=A.fragment(b.trim(),RegExp.$1,d),b=null;else{if(d!==a)return c(d).find(b);e=A.qsa(h,b)}return A.Z(e,b)},c=function(a,b){return A.init(a,b)},c.extend=function(a){var b,c=f.call(arguments,1);return typeof a=="boolean"&&(b=a,a=c.shift()),c.forEach(function(c){T(a,c,b)}),a},A.qsa=function(a,b){var c;return H(a)&&w.test(b)?(c=a.getElementById(RegExp.$1))?[c]:[]:a.nodeType!==1&&a.nodeType!==9?[]:f.call(v.test(b)?a.getElementsByClassName(RegExp.$1):x.test(b)?a.getElementsByTagName(b):a.querySelectorAll(b))},c.contains=function(a,b){return a!==b&&a.contains(b)},c.type=E,c.isFunction=F,c.isWindow=G,c.isArray=K,c.isPlainObject=J,c.isEmptyObject=function(a){var b;for(b in a)return!1;return!0},c.inArray=function(a,b,c){return e.indexOf.call(b,a,c)},c.camelCase=B,c.trim=function(a){return a.trim()},c.uuid=0,c.support={},c.expr={},c.map=function(a,b){var c,d=[],e,f;if(L(a))for(e=0;e=0?b:b+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){this.parentNode!=null&&this.parentNode.removeChild(this)})},each:function(a){return e.every.call(this,function(b,c){return a.call(b,c,b)!==!1}),this},filter:function(a){return F(a)?this.not(this.not(a)):c(g.call(this,function(b){return A.matches(b,a)}))},add:function(a,b){return c(C(this.concat(c(a,b))))},is:function(a){return this.length>0&&A.matches(this[0],a)},not:function(b){var d=[];if(F(b)&&b.call!==a)this.each(function(a){b.call(this,a)||d.push(this)});else{var e=typeof b=="string"?this.filter(b):L(b)&&F(b.item)?f.call(b):c(b);this.forEach(function(a){e.indexOf(a)<0&&d.push(a)})}return c(d)},has:function(a){return this.filter(function(){return I(a)?c.contains(this,a):c(this).find(a).size()})},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){var a=this[0];return a&&!I(a)?a:c(a)},last:function(){var a=this[this.length-1];return a&&!I(a)?a:c(a)},find:function(a){var b,d=this;return typeof a=="object"?b=c(a).filter(function(){var a=this;return e.some.call(d,function(b){return c.contains(b,a)})}):this.length==1?b=c(A.qsa(this[0],a)):b=this.map(function(){return A.qsa(this,a)}),b},closest:function(a,b){var d=this[0],e=!1;typeof a=="object"&&(e=c(a));while(d&&!(e?e.indexOf(d)>=0:A.matches(d,a)))d=d!==b&&!H(d)&&d.parentNode;return c(d)},parents:function(a){var b=[],d=this;while(d.length>0)d=c.map(d,function(a){if((a=a.parentNode)&&!H(a)&&b.indexOf(a)<0)return b.push(a),a});return U(b,a)},parent:function(a){return U(C(this.pluck("parentNode")),a)},children:function(a){return U(this.map(function(){return S(this)}),a)},contents:function(){return this.map(function(){return f.call(this.childNodes)})},siblings:function(a){return U(this.map(function(a,b){return g.call(S(b.parentNode),function(a){return a!==b})}),a)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(a){return c.map(this,function(b){return b[a]})},show:function(){return this.each(function(){this.style.display=="none"&&(this.style.display=null),k(this,"").getPropertyValue("display")=="none"&&(this.style.display=R(this.nodeName))})},replaceWith:function(a){return this.before(a).remove()},wrap:function(a){var b=F(a);if(this[0]&&!b)var d=c(a).get(0),e=d.parentNode||this.length>1;return this.each(function(f){c(this).wrapAll(b?a.call(this,f):e?d.cloneNode(!0):d)})},wrapAll:function(a){if(this[0]){c(this[0]).before(a=c(a));var b;while((b=a.children()).length)a=b.first();c(a).append(this)}return this},wrapInner:function(a){var b=F(a);return this.each(function(d){var e=c(this),f=e.contents(),g=b?a.call(this,d):a;f.length?f.wrapAll(g):e.append(g)})},unwrap:function(){return this.parent().each(function(){c(this).replaceWith(c(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(b){return this.each(function(){var d=c(this);(b===a?d.css("display")=="none":b)?d.show():d.hide()})},prev:function(a){return c(this.pluck("previousElementSibling")).filter(a||"*")},next:function(a){return c(this.pluck("nextElementSibling")).filter(a||"*")},html:function(b){return b===a?this.length>0?this[0].innerHTML:null:this.each(function(a){var d=this.innerHTML;c(this).empty().append(V(this,b,a,d))})},text:function(b){return b===a?this.length>0?this[0].textContent:null:this.each(function(){this.textContent=b})},attr:function(c,d){var e;return typeof c=="string"&&d===a?this.length==0||this[0].nodeType!==1?a:c=="value"&&this[0].nodeName=="INPUT"?this.val():!(e=this[0].getAttribute(c))&&c in this[0]?this[0][c]:e:this.each(function(a){if(this.nodeType!==1)return;if(I(c))for(b in c)W(this,b,c[b]);else W(this,c,V(this,d,a,this.getAttribute(c)))})},removeAttr:function(a){return this.each(function(){this.nodeType===1&&W(this,a)})},prop:function(b,c){return c===a?this[0]&&this[0][b]:this.each(function(a){this[b]=V(this,c,a,this[b])})},data:function(b,c){var d=this.attr("data-"+O(b),c);return d!==null?Y(d):a},val:function(b){return b===a?this[0]&&(this[0].multiple?c(this[0]).find("option").filter(function(a){return this.selected}).pluck("value"):this[0].value):this.each(function(a){this.value=V(this,b,a,this.value)})},offset:function(a){if(a)return this.each(function(b){var d=c(this),e=V(this,a,b,d.offset()),f=d.offsetParent().offset(),g={top:e.top-f.top,left:e.left-f.left};d.css("position")=="static"&&(g.position="relative"),d.css(g)});if(this.length==0)return null;var b=this[0].getBoundingClientRect();return{left:b.left+window.pageXOffset,top:b.top+window.pageYOffset,width:Math.round(b.width),height:Math.round(b.height)}},css:function(a,c){if(arguments.length<2&&typeof a=="string")return this[0]&&(this[0].style[B(a)]||k(this[0],"").getPropertyValue(a));var d="";if(E(a)=="string")!c&&c!==0?this.each(function(){this.style.removeProperty(O(a))}):d=O(a)+":"+Q(a,c);else for(b in a)!a[b]&&a[b]!==0?this.each(function(){this.style.removeProperty(O(b))}):d+=O(b)+":"+Q(b,a[b])+";";return this.each(function(){this.style.cssText+=";"+d})},index:function(a){return a?this.indexOf(c(a)[0]):this.parent().children().indexOf(this[0])},hasClass:function(a){return e.some.call(this,function(a){return this.test(X(a))},P(a))},addClass:function(a){return this.each(function(b){d=[];var e=X(this),f=V(this,a,b,e);f.split(/\s+/g).forEach(function(a){c(this).hasClass(a)||d.push(a)},this),d.length&&X(this,e+(e?" ":"")+d.join(" "))})},removeClass:function(b){return this.each(function(c){if(b===a)return X(this,"");d=X(this),V(this,b,c,d).split(/\s+/g).forEach(function(a){d=d.replace(P(a)," ")}),X(this,d.trim())})},toggleClass:function(b,d){return this.each(function(e){var f=c(this),g=V(this,b,e,X(this));g.split(/\s+/g).forEach(function(b){(d===a?!f.hasClass(b):d)?f.addClass(b):f.removeClass(b)})})},scrollTop:function(){if(!this.length)return;return"scrollTop"in this[0]?this[0].scrollTop:this[0].scrollY},position:function(){if(!this.length)return;var a=this[0],b=this.offsetParent(),d=this.offset(),e=o.test(b[0].nodeName)?{top:0,left:0}:b.offset();return d.top-=parseFloat(c(a).css("margin-top"))||0,d.left-=parseFloat(c(a).css("margin-left"))||0,e.top+=parseFloat(c(b[0]).css("border-top-width"))||0,e.left+=parseFloat(c(b[0]).css("border-left-width"))||0,{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||h.body;while(a&&!o.test(a.nodeName)&&c(a).css("position")=="static")a=a.offsetParent;return a})}},c.fn.detach=c.fn.remove,["width","height"].forEach(function(b){c.fn[b]=function(d){var e,f=this[0],g=b.replace(/./,function(a){return a[0].toUpperCase()});return d===a?G(f)?f["inner"+g]:H(f)?f.documentElement["offset"+g]:(e=this.offset())&&e[b]:this.each(function(a){f=c(this),f.css(b,V(this,d,a,f[b]()))})}}),q.forEach(function(a,b){var d=b%2;c.fn[a]=function(){var a,e=c.map(arguments,function(b){return a=E(b),a=="object"||a=="array"||b==null?b:A.fragment(b)}),f,g=this.length>1;return e.length<1?this:this.each(function(a,h){f=d?h:h.parentNode,h=b==0?h.nextSibling:b==1?h.firstChild:b==2?h:null,e.forEach(function(a){if(g)a=a.cloneNode(!0);else if(!f)return c(a).remove();Z(f.insertBefore(a,h),function(a){a.nodeName!=null&&a.nodeName.toUpperCase()==="SCRIPT"&&(!a.type||a.type==="text/javascript")&&!a.src&&window.eval.call(window,a.innerHTML)})})})},c.fn[d?a+"To":"insert"+(b?"Before":"After")]=function(b){return c(b)[a](this),this}}),A.Z.prototype=c.fn,A.uniq=C,A.deserializeValue=Y,c.zepto=A,c}();window.Zepto=Zepto,"$"in window||(window.$=Zepto),function(a){function b(a){var b=this.os={},c=this.browser={},d=a.match(/WebKit\/([\d.]+)/),e=a.match(/(Android)\s+([\d.]+)/),f=a.match(/(iPad).*OS\s([\d_]+)/),g=!f&&a.match(/(iPhone\sOS)\s([\d_]+)/),h=a.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),i=h&&a.match(/TouchPad/),j=a.match(/Kindle\/([\d.]+)/),k=a.match(/Silk\/([\d._]+)/),l=a.match(/(BlackBerry).*Version\/([\d.]+)/),m=a.match(/(BB10).*Version\/([\d.]+)/),n=a.match(/(RIM\sTablet\sOS)\s([\d.]+)/),o=a.match(/PlayBook/),p=a.match(/Chrome\/([\d.]+)/)||a.match(/CriOS\/([\d.]+)/),q=a.match(/Firefox\/([\d.]+)/);if(c.webkit=!!d)c.version=d[1];e&&(b.android=!0,b.version=e[2]),g&&(b.ios=b.iphone=!0,b.version=g[2].replace(/_/g,".")),f&&(b.ios=b.ipad=!0,b.version=f[2].replace(/_/g,".")),h&&(b.webos=!0,b.version=h[2]),i&&(b.touchpad=!0),l&&(b.blackberry=!0,b.version=l[2]),m&&(b.bb10=!0,b.version=m[2]),n&&(b.rimtabletos=!0,b.version=n[2]),o&&(c.playbook=!0),j&&(b.kindle=!0,b.version=j[1]),k&&(c.silk=!0,c.version=k[1]),!k&&b.android&&a.match(/Kindle Fire/)&&(c.silk=!0),p&&(c.chrome=!0,c.version=p[1]),q&&(c.firefox=!0,c.version=q[1]),b.tablet=!!(f||o||e&&!a.match(/Mobile/)||q&&a.match(/Tablet/)),b.phone=!b.tablet&&!!(e||g||h||l||m||p&&a.match(/Android/)||p&&a.match(/CriOS\/([\d.]+)/)||q&&a.match(/Mobile/))}b.call(a,navigator.userAgent),a.__detect=b}(Zepto),function(a){function g(a){return a._zid||(a._zid=d++)}function h(a,b,d,e){b=i(b);if(b.ns)var f=j(b.ns);return(c[g(a)]||[]).filter(function(a){return a&&(!b.e||a.e==b.e)&&(!b.ns||f.test(a.ns))&&(!d||g(a.fn)===g(d))&&(!e||a.sel==e)})}function i(a){var b=(""+a).split(".");return{e:b[0],ns:b.slice(1).sort().join(" ")}}function j(a){return new RegExp("(?:^| )"+a.replace(" "," .* ?")+"(?: |$)")}function k(b,c,d){a.type(b)!="string"?a.each(b,d):b.split(/\s/).forEach(function(a){d(a,c)})}function l(a,b){return a.del&&(a.e=="focus"||a.e=="blur")||!!b}function m(a){return f[a]||a}function n(b,d,e,h,j,n){var o=g(b),p=c[o]||(c[o]=[]);k(d,e,function(c,d){var e=i(c);e.fn=d,e.sel=h,e.e in f&&(d=function(b){var c=b.relatedTarget;if(!c||c!==this&&!a.contains(this,c))return e.fn.apply(this,arguments)}),e.del=j&&j(d,c);var g=e.del||d;e.proxy=function(a){var c=g.apply(b,[a].concat(a.data));return c===!1&&(a.preventDefault(),a.stopPropagation()),c},e.i=p.length,p.push(e),b.addEventListener(m(e.e),e.proxy,l(e,n))})}function o(a,b,d,e,f){var i=g(a);k(b||"",d,function(b,d){h(a,b,d,e).forEach(function(b){delete c[i][b.i],a.removeEventListener(m(b.e),b.proxy,l(b,f))})})}function t(b){var c,d={originalEvent:b};for(c in b)!r.test(c)&&b[c]!==undefined&&(d[c]=b[c]);return a.each(s,function(a,c){d[a]=function(){return this[c]=p,b[a].apply(b,arguments)},d[c]=q}),d}function u(a){if(!("defaultPrevented"in a)){a.defaultPrevented=!1;var b=a.preventDefault;a.preventDefault=function(){this.defaultPrevented=!0,b.call(this)}}}var b=a.zepto.qsa,c={},d=1,e={},f={mouseenter:"mouseover",mouseleave:"mouseout"};e.click=e.mousedown=e.mouseup=e.mousemove="MouseEvents",a.event={add:n,remove:o},a.proxy=function(b,c){if(a.isFunction(b)){var d=function(){return b.apply(c,arguments)};return d._zid=g(b),d}if(typeof c=="string")return a.proxy(b[c],b);throw new TypeError("expected function")},a.fn.bind=function(a,b){return this.each(function(){n(this,a,b)})},a.fn.unbind=function(a,b){return this.each(function(){o(this,a,b)})},a.fn.one=function(a,b){return this.each(function(c,d){n(this,a,b,null,function(a,b){return function(){var c=a.apply(d,arguments);return o(d,b,a),c}})})};var p=function(){return!0},q=function(){return!1},r=/^([A-Z]|layer[XY]$)/,s={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};a.fn.delegate=function(b,c,d){return this.each(function(e,f){n(f,c,d,b,function(c){return function(d){var e,g=a(d.target).closest(b,f).get(0);if(g)return e=a.extend(t(d),{currentTarget:g,liveFired:f}),c.apply(g,[e].concat([].slice.call(arguments,1)))}})})},a.fn.undelegate=function(a,b,c){return this.each(function(){o(this,b,c,a)})},a.fn.live=function(b,c){return a(document.body).delegate(this.selector,b,c),this},a.fn.die=function(b,c){return a(document.body).undelegate(this.selector,b,c),this},a.fn.on=function(b,c,d){return!c||a.isFunction(c)?this.bind(b,c||d):this.delegate(c,b,d)},a.fn.off=function(b,c,d){return!c||a.isFunction(c)?this.unbind(b,c||d):this.undelegate(c,b,d)},a.fn.trigger=function(b,c){if(typeof b=="string"||a.isPlainObject(b))b=a.Event(b);return u(b),b.data=c,this.each(function(){"dispatchEvent"in this&&this.dispatchEvent(b)})},a.fn.triggerHandler=function(b,c){var d,e;return this.each(function(f,g){d=t(typeof b=="string"?a.Event(b):b),d.data=c,d.target=g,a.each(h(g,b.type||b),function(a,b){e=b.proxy(d);if(d.isImmediatePropagationStopped())return!1})}),e},"focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(b){a.fn[b]=function(a){return a?this.bind(b,a):this.trigger(b)}}),["focus","blur"].forEach(function(b){a.fn[b]=function(a){return a?this.bind(b,a):this.each(function(){try{this[b]()}catch(a){}}),this}}),a.Event=function(a,b){typeof a!="string"&&(b=a,a=b.type);var c=document.createEvent(e[a]||"Events"),d=!0;if(b)for(var f in b)f=="bubbles"?d=!!b[f]:c[f]=b[f];return c.initEvent(a,d,!0,null,null,null,null,null,null,null,null,null,null,null,null),c.isDefaultPrevented=function(){return this.defaultPrevented},c}}(Zepto),function($){function triggerAndReturn(a,b,c){var d=$.Event(b);return $(a).trigger(d,c),!d.defaultPrevented}function triggerGlobal(a,b,c,d){if(a.global)return triggerAndReturn(b||document,c,d)}function ajaxStart(a){a.global&&$.active++===0&&triggerGlobal(a,null,"ajaxStart")}function ajaxStop(a){a.global&&!--$.active&&triggerGlobal(a,null,"ajaxStop")}function ajaxBeforeSend(a,b){var c=b.context;if(b.beforeSend.call(c,a,b)===!1||triggerGlobal(b,c,"ajaxBeforeSend",[a,b])===!1)return!1;triggerGlobal(b,c,"ajaxSend",[a,b])}function ajaxSuccess(a,b,c){var d=c.context,e="success";c.success.call(d,a,e,b),triggerGlobal(c,d,"ajaxSuccess",[b,c,a]),ajaxComplete(e,b,c)}function ajaxError(a,b,c,d){var e=d.context;d.error.call(e,c,b,a),triggerGlobal(d,e,"ajaxError",[c,d,a]),ajaxComplete(b,c,d)}function ajaxComplete(a,b,c){var d=c.context;c.complete.call(d,b,a),triggerGlobal(c,d,"ajaxComplete",[b,c]),ajaxStop(c)}function empty(){}function mimeToDataType(a){return a&&(a=a.split(";",2)[0]),a&&(a==htmlType?"html":a==jsonType?"json":scriptTypeRE.test(a)?"script":xmlTypeRE.test(a)&&"xml")||"text"}function appendQuery(a,b){return(a+"&"+b).replace(/[&?]{1,2}/,"?")}function serializeData(a){a.processData&&a.data&&$.type(a.data)!="string"&&(a.data=$.param(a.data,a.traditional)),a.data&&(!a.type||a.type.toUpperCase()=="GET")&&(a.url=appendQuery(a.url,a.data))}function parseArguments(a,b,c,d){var e=!$.isFunction(b);return{url:a,data:e?b:undefined,success:e?$.isFunction(c)?c:undefined:b,dataType:e?d||c:c}}function serialize(a,b,c,d){var e,f=$.isArray(b);$.each(b,function(b,g){e=$.type(g),d&&(b=c?d:d+"["+(f?"":b)+"]"),!d&&f?a.add(g.name,g.value):e=="array"||!c&&e=="object"?serialize(a,g,c,b):a.add(b,g)})}var jsonpID=0,document=window.document,key,name,rscript=/