├── img ├── .DS_Store └── globe.jpg ├── index.html ├── css ├── main.css └── main.less └── js ├── main.js └── zepto.js /img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yyx990803/Puzzle-Demo/HEAD/img/.DS_Store -------------------------------------------------------------------------------- /img/globe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yyx990803/Puzzle-Demo/HEAD/img/globe.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Puzzle 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | *{-webkit-tap-highlight-color:transparent;} 2 | html,body{margin:0;padding:0;background-color:#ddd;-webkit-user-select:none;} 3 | #board{width:288px;height:288px;margin:9px auto;background-color:#ccc;border-radius:2px;padding:2px;-webkit-box-shadow:inset 0 1px 8px rgba(0, 0, 0, 0.2),0 1px 1px rgba(255, 255, 255, 0.5);-moz-box-shadow:inset 0 1px 8px rgba(0, 0, 0, 0.2),0 1px 1px rgba(255, 255, 255, 0.5);box-shadow:inset 0 1px 8px rgba(0, 0, 0, 0.2),0 1px 1px rgba(255, 255, 255, 0.5);}#board .tile{display:block;width:70px;height:70px;position:absolute;overflow:hidden;border:1px solid #eeeeee;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.3);box-shadow:0 1px 4px rgba(0, 0, 0, 0.3);-webkit-transition:all 0.15s ease;-moz-transition:all 0.15s ease;-ms-transition:all 0.15s ease;-o-transition:all 0.15s ease;transition:all 0.15s ease;-webkit-backface-visibility:hidden;}#board .tile img{width:288px;height:288px;position:relative;} 4 | #board .tile.drag{-webkit-transition:all 0 cubic-bezier(0, 0, 0.2, 1) !important;-moz-transition:all 0 cubic-bezier(0, 0, 0.2, 1) !important;-ms-transition:all 0 cubic-bezier(0, 0, 0.2, 1) !important;-o-transition:all 0 cubic-bezier(0, 0, 0.2, 1) !important;transition:all 0 cubic-bezier(0, 0, 0.2, 1) !important;} 5 | #shuffle{display:block;color:#666;cursor:pointer;font-family:Helvetica,Arial,sans-serif;font-weight:bold;text-align:center;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);width:290px;margin:9px auto;padding:7px 0;border:1px solid #aaa;border-top-color:#b9b9b9;border-radius:2px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.15),0 1px 1px rgba(255, 255, 255, 0.5);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.15),0 1px 1px rgba(255, 255, 255, 0.5);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.15),0 1px 1px rgba(255, 255, 255, 0.5);background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #f3f3f3), color-stop(100%, #d9d9d9));background:-webkit-linear-gradient(top, #f3f3f3 0%, #d9d9d9 100%);background:-moz-linear-gradient(top, #f3f3f3 0%, #d9d9d9 100%);background:-ms-linear-gradient(top, #f3f3f3 0%, #d9d9d9 100%);background:-o-linear-gradient(top, #f3f3f3 0%, #d9d9d9 100%);background:linear-gradient(top, #f3f3f3 0%, #d9d9d9 100%);}#shuffle:active{background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #dddddd), color-stop(100%, #cccccc));background:-webkit-linear-gradient(top, #dddddd 0%, #cccccc 100%);background:-moz-linear-gradient(top, #dddddd 0%, #cccccc 100%);background:-ms-linear-gradient(top, #dddddd 0%, #cccccc 100%);background:-o-linear-gradient(top, #dddddd 0%, #cccccc 100%);background:linear-gradient(top, #dddddd 0%, #cccccc 100%);} 6 | -------------------------------------------------------------------------------- /css/main.less: -------------------------------------------------------------------------------- 1 | //Variables 2 | @tileSize: 70px; 3 | @tileBorder: 1px; 4 | @boardSize: (@tileSize + @tileBorder * 2) * 4; 5 | @boardPadding: 2px; 6 | 7 | //Mixins 8 | .transition(@args) { 9 | -webkit-transition: all @args; 10 | -moz-transition: all @args; 11 | -ms-transition: all @args; 12 | -o-transition: all @args; 13 | transition: all @args; 14 | } 15 | 16 | .gradient(@begin, @end) { 17 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,@begin), color-stop(100%,@end)); //iOS 4.3- 18 | background: -webkit-linear-gradient(top, @begin 0%, @end 100%); 19 | background: -moz-linear-gradient(top, @begin 0%, @end 100%); 20 | background: -ms-linear-gradient(top, @begin 0%, @end 100%); 21 | background: -o-linear-gradient(top, @begin 0%, @end 100%); 22 | background: linear-gradient(top, @begin 0%, @end 100%); 23 | } 24 | 25 | //Main 26 | * { 27 | -webkit-tap-highlight-color: transparent; 28 | } 29 | 30 | html, body { 31 | margin: 0; 32 | padding: 0; 33 | background-color: #ddd; 34 | -webkit-user-select: none; 35 | } 36 | 37 | #board { 38 | width: @boardSize; 39 | height: @boardSize; 40 | position: relative; 41 | margin: 9px auto; 42 | background-color: #ccc; 43 | border-radius: 2px; 44 | padding: @boardPadding; 45 | -webkit-box-shadow: inset 0 1px 8px rgba(0, 0, 0, .2), 0 1px 1px rgba(255, 255, 255, .5); 46 | -moz-box-shadow: inset 0 1px 8px rgba(0, 0, 0, .2), 0 1px 1px rgba(255, 255, 255, .5); 47 | box-shadow: inset 0 1px 8px rgba(0, 0, 0, .2), 0 1px 1px rgba(255, 255, 255, .5); 48 | .tile { 49 | display: block; 50 | width: @tileSize; 51 | height: @tileSize; 52 | position: absolute; 53 | overflow: hidden; 54 | border: @tileBorder solid #eee; 55 | -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, .3); 56 | -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, .3); 57 | box-shadow: 0 1px 4px rgba(0, 0, 0, .3); 58 | .transition(.15s ease); 59 | -webkit-backface-visibility: hidden; 60 | img { 61 | width: @boardSize; 62 | height: @boardSize; 63 | position: relative; 64 | } 65 | &.drag { 66 | .transition(0 cubic-bezier(0,0,0.2,1) ~"!important"); 67 | } 68 | } 69 | } 70 | 71 | #shuffle { 72 | display: block; 73 | color: #666; 74 | cursor: pointer; 75 | font-family: Helvetica, Arial, sans-serif; 76 | font-weight: bold; 77 | text-align: center; 78 | text-shadow: 0 1px 0 rgba(255, 255, 255, .5); 79 | width: @boardSize + 2px; 80 | margin: 9px auto; 81 | padding: 7px 0; 82 | border: 1px solid #aaa; 83 | border-top-color: #b9b9b9; 84 | border-radius: 2px; 85 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .15), 0 1px 1px rgba(255, 255, 255, .5); 86 | -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .15), 0 1px 1px rgba(255, 255, 255, .5); 87 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, .15), 0 1px 1px rgba(255, 255, 255, .5); 88 | .gradient(#f3f3f3, #d9d9d9); 89 | &:active { 90 | .gradient(#ddd, #ccc); 91 | } 92 | } -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | // 2 | // Slider Puzzle for mobile browser (webkit optimized) 3 | // Evan You 4 | // 02-24-2012 5 | // 6 | // Built with Zepto.js 7 | // 8 | 9 | var PUZZLE = (function ($, window, undefined) { 10 | "use strict"; 11 | 12 | var animating = false, 13 | tileSize = 72, 14 | animationSpeed = 150, 15 | setTimeout = window.setTimeout, 16 | 17 | // Templates 18 | templates = { 19 | board: function () { 20 | return '
'; 21 | }, 22 | tile: function (id) { 23 | var offsetTop = ~~(id / 4) * -tileSize, 24 | offsetLeft = id % 4 * -tileSize; 25 | return '
' + 26 | '' + 27 | '
'; 28 | }, 29 | shuffleButton: function () { 30 | return 'Shuffle'; 31 | } 32 | }, 33 | 34 | // Utility function for vendor prefixes 35 | cssTransform = function (x, y) { 36 | var translation = 'translate(' + x + 'px,' + y + 'px)'; 37 | return { 38 | '-webkit-transform': translation, 39 | '-moz-transform': translation, 40 | '-ms-transform': translation, 41 | '-o-transform': translation, 42 | 'transform': translation 43 | }; 44 | }; 45 | 46 | // The tile object 47 | var Tile = function (id, board) { 48 | this.id = id; // This also stands for the correct position 49 | this.board = board; 50 | this.render(); 51 | }; 52 | 53 | Tile.prototype = { 54 | 55 | // Render dom element wrapped in Zepto selector 56 | render: function () { 57 | this.el = $(templates.tile(this.id)); 58 | }, 59 | 60 | // Update and animate to new position 61 | update: function (position) { 62 | this.position = position; 63 | this.x = (position % 4) * tileSize; 64 | this.y = ~~(position / 4) * tileSize; 65 | this.el.data('position', position).css(cssTransform(this.x, this.y)); 66 | }, 67 | 68 | // Determine the legit move direction 69 | // 0-top, 1-right, 2-bottom, 3-left, -1 if cannot move 70 | getMovableDirection: function () { 71 | var row = ~~(this.position / 4), 72 | col = this.position % 4, 73 | slot = this.board.openSlot; 74 | if (row === slot.row) { 75 | return col < slot.col ? 1 : 3; 76 | } else if (col === slot.col) { 77 | return row < slot.row ? 2 : 0; 78 | } else { 79 | return -1; 80 | } 81 | }, 82 | 83 | // Get neighbor with given direction 84 | // returns a tile object if there is a nieghbor 85 | // returns null if the cell in that direction is empty 86 | // implicitly returns undefined if out of bound 87 | getNeighbor: function (direction) { 88 | var n; 89 | switch(direction) { 90 | case 0: 91 | n = this.position - 4; 92 | break; 93 | case 1: 94 | n = this.position + 1; 95 | break; 96 | case 2: 97 | n = this.position + 4; 98 | break; 99 | case 3: 100 | n = this.position - 1; 101 | break; 102 | default: 103 | break; 104 | } 105 | if (n >= 0 && n < 16) { 106 | return this.board.tiles[n]; 107 | } 108 | } 109 | }; 110 | 111 | // The board object 112 | var Board = function () { 113 | this.buildTiles(); 114 | this.shuffle(); 115 | this.render(); 116 | this.update(); 117 | }; 118 | 119 | Board.prototype = { 120 | 121 | // Populate the tiles 122 | buildTiles: function () { 123 | // Array holding the tile objects 124 | // The index here determines the actual position on the board 125 | // The empty slot is null 126 | this.tiles = []; 127 | for (var i = 0; i < 15; i++) { 128 | this.tiles.push(new Tile(i, this)); 129 | } 130 | this.tiles.push(null); 131 | this.setOpenSlot(15); 132 | }, 133 | 134 | // Shuffle the tiles 135 | shuffle: function () { 136 | var i, j, temp; 137 | for (i = this.tiles.length - 1; i > 0; i--) { 138 | j = ~~(Math.random() * (i + 1)); 139 | temp = this.tiles[i]; 140 | this.tiles[i] = this.tiles[j]; 141 | this.tiles[j] = temp; 142 | } 143 | }, 144 | 145 | // Render dom element wrapped in Zepto selector 146 | render: function () { 147 | this.el = $(templates.board()); 148 | for (var i = 0, j = this.tiles.length; i < j; i++) { 149 | if (this.tiles[i]) { 150 | this.el.append(this.tiles[i].el); 151 | } 152 | } 153 | this.initEvents(); 154 | }, 155 | 156 | // Delegate touch events on tiles 157 | initEvents: function () { 158 | var board = this, 159 | touch = {}; // Object for holding variables during a touch events cycle 160 | board.el 161 | .delegate('.tile', 'touchstart mousedown', function (e) { 162 | if (!animating && (!e.touches || e.touches.length === 1)) { 163 | touch.tile = board.tiles[$(this).data('position')]; 164 | touch.direction = touch.tile.getMovableDirection(); 165 | if (touch.direction !== -1) { 166 | touch.legit = true; 167 | touch.axis = touch.direction % 2 === 0; // true:y, false:x 168 | 169 | // Record original position 170 | touch.op = touch.axis ? (e.pageY || e.touches[0].pageY) : (e.pageX || e.touches[0].pageX); 171 | 172 | // Determine bounds and trigger point 173 | switch (touch.direction) { 174 | case 0: 175 | case 3: 176 | touch.lowerBound = -tileSize; 177 | touch.upperBound = 0; 178 | break; 179 | case 1: 180 | case 2: 181 | touch.lowerBound = 0; 182 | touch.upperBound = tileSize; 183 | break; 184 | default: 185 | break; 186 | } 187 | touch.triggerPoint = (touch.upperBound + touch.lowerBound) / 2; 188 | 189 | // Look for other affected tiles 190 | touch.group = []; 191 | var t = touch.tile; 192 | while (t !== null && t !== undefined) { 193 | touch.group.push(t); 194 | t = t.getNeighbor(touch.direction); 195 | } 196 | } 197 | } 198 | }) 199 | .delegate('.tile', 'touchmove mousemove', function (e) { 200 | var i, j, dx, dy; 201 | if ((!e.touches || e.touches.length === 1) && touch.legit) { 202 | if (!touch.dragging) { 203 | touch.dragging = true; 204 | for (i = 0, j = touch.group.length; i < j; i++) { 205 | touch.group[i].el.addClass('drag'); 206 | } 207 | } 208 | 209 | // Record displacement and limit it to the bounds 210 | var d = (touch.axis ? (e.pageY || e.touches[0].pageY) : (e.pageX || e.touches[0].pageX)) - touch.op; 211 | touch.dp = Math.max(Math.min(d, touch.upperBound), touch.lowerBound); 212 | 213 | // Drag animation 214 | for (i = 0, j = touch.group.length; i < j; i++) { 215 | dx = touch.group[i].x + (touch.axis ? 0 : touch.dp); 216 | dy = touch.group[i].y + (touch.axis ? touch.dp : 0); 217 | touch.group[i].el.css(cssTransform(dx, dy)); 218 | } 219 | } 220 | }) 221 | .delegate('.tile', 'touchend mouseup', function (e) { 222 | var i, j; 223 | if (touch.legit) { 224 | if (touch.dragging && Math.abs(touch.dp) < Math.abs(touch.triggerPoint)) { 225 | // Dragged but cancelled 226 | // return all tiles to original position 227 | for (i = 0, j = touch.group.length; i < j; i++) { 228 | touch.group[i].el 229 | .removeClass('drag') 230 | .css(cssTransform(touch.group[i].x, touch.group[i].y)); 231 | } 232 | board.lock(); 233 | } else { 234 | // Triggered a successful move 235 | // move the tile objects to new positions in array 236 | board.tiles[board.openSlot.position] = touch.group[touch.group.length - 1]; 237 | for (i = touch.group.length - 1; i >= 0; i--) { 238 | board.tiles[touch.group[i].position] = (i === 0 ? null : touch.group[i-1]); 239 | } 240 | // Animatie 241 | if (touch.dragging) { 242 | for (i = 0, j = touch.group.length; i < j; i++) { 243 | touch.group[i].el.removeClass('drag'); 244 | } 245 | } 246 | board.update(); 247 | } 248 | } 249 | // Reset 250 | touch = {}; 251 | }); 252 | }, 253 | 254 | // Update tiles/slots according to current positions 255 | update: function () { 256 | this.lock(); 257 | for (var i = 0, j = this.tiles.length; i < j; i++) { 258 | if (this.tiles[i]) { 259 | this.tiles[i].update(i); 260 | } else { 261 | this.setOpenSlot(i); 262 | } 263 | } 264 | if (this.gameComplete()) { 265 | var board = this; 266 | setTimeout( function () { 267 | window.alert('Wow you are damn good at slider puzzles... gonna shuffle that so that you can play some more!'); 268 | board.shuffle(); 269 | board.update(); 270 | }, animationSpeed); 271 | } 272 | }, 273 | 274 | // Utility function for setting the empty slot 275 | setOpenSlot: function (i) { 276 | this.openSlot = { 277 | position: i, 278 | row: ~~(i / 4), 279 | col: i % 4 280 | }; 281 | }, 282 | 283 | // Check if all tiles are in correct position 284 | gameComplete: function () { 285 | for (var i = 0, j = this.tiles.length; i < j; i++) { 286 | if (this.tiles[i] && this.tiles[i].id !== i) { 287 | return false; 288 | } 289 | } 290 | return true; 291 | }, 292 | 293 | // Prevent all touch events for a given duration 294 | // Used during animation 295 | lock: function () { 296 | animating = true; 297 | setTimeout(function () { 298 | animating = false; 299 | }, animationSpeed); 300 | } 301 | }; 302 | 303 | // Expose initialization function 304 | return { 305 | init: function (container) { 306 | 307 | // Prevent mobile browser viewport movement 308 | $(document.body).bind('touchmove', function (e) { 309 | e.preventDefault(); 310 | }); 311 | 312 | // Create puzzle 313 | var board = new Board(), 314 | shuffleButton = $(templates.shuffleButton()).bind('click', function () { 315 | if (!animating) { 316 | board.shuffle(); 317 | board.update(); 318 | } 319 | }); 320 | 321 | // Append elements to container 322 | $(container).append(board.el).append(shuffleButton); 323 | } 324 | }; 325 | 326 | }(Zepto, window)); -------------------------------------------------------------------------------- /js/zepto.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010, 2011 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | (function(undefined){ 6 | if (String.prototype.trim === undefined) // fix for iOS 3.2 7 | String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') }; 8 | 9 | // For iOS 3.x 10 | // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce 11 | if (Array.prototype.reduce === undefined) 12 | Array.prototype.reduce = function(fun){ 13 | if(this === void 0 || this === null) throw new TypeError(); 14 | var t = Object(this), len = t.length >>> 0, k = 0, accumulator; 15 | if(typeof fun != 'function') throw new TypeError(); 16 | if(len == 0 && arguments.length == 1) throw new TypeError(); 17 | 18 | if(arguments.length >= 2) 19 | accumulator = arguments[1]; 20 | else 21 | do{ 22 | if(k in t){ 23 | accumulator = t[k++]; 24 | break; 25 | } 26 | if(++k >= len) throw new TypeError(); 27 | } while (true); 28 | 29 | while (k < len){ 30 | if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t); 31 | k++; 32 | } 33 | return accumulator; 34 | }; 35 | 36 | })(); 37 | // Zepto.js 38 | // (c) 2010, 2011 Thomas Fuchs 39 | // Zepto.js may be freely distributed under the MIT license. 40 | 41 | var Zepto = (function() { 42 | var undefined, key, $$, classList, emptyArray = [], slice = emptyArray.slice, 43 | document = window.document, 44 | elementDisplay = {}, classCache = {}, 45 | getComputedStyle = document.defaultView.getComputedStyle, 46 | cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, 47 | fragmentRE = /^\s*<(\w+)[^>]*>/, 48 | elementTypes = [1, 9, 11], 49 | adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], 50 | table = document.createElement('table'), 51 | tableRow = document.createElement('tr'), 52 | containers = { 53 | 'tr': document.createElement('tbody'), 54 | 'tbody': table, 'thead': table, 'tfoot': table, 55 | 'td': tableRow, 'th': tableRow, 56 | '*': document.createElement('div') 57 | }, 58 | readyRE = /complete|loaded|interactive/, 59 | classSelectorRE = /^\.([\w-]+)$/, 60 | idSelectorRE = /^#([\w-]+)$/, 61 | tagSelectorRE = /^[\w-]+$/; 62 | 63 | function isF(value) { return ({}).toString.call(value) == "[object Function]" } 64 | function isO(value) { return value instanceof Object } 65 | function isA(value) { return value instanceof Array } 66 | function likeArray(obj) { return typeof obj.length == 'number' } 67 | 68 | function compact(array) { return array.filter(function(item){ return item !== undefined && item !== null }) } 69 | function flatten(array) { return array.length > 0 ? [].concat.apply([], array) : array } 70 | function camelize(str) { return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } 71 | function dasherize(str){ 72 | return str.replace(/::/g, '/') 73 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') 74 | .replace(/([a-z\d])([A-Z])/g, '$1_$2') 75 | .replace(/_/g, '-') 76 | .toLowerCase(); 77 | } 78 | function uniq(array) { return array.filter(function(item,index,array){ return array.indexOf(item) == index }) } 79 | 80 | function classRE(name){ 81 | return name in classCache ? 82 | classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')); 83 | } 84 | 85 | function maybeAddPx(name, value) { return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value; } 86 | 87 | function defaultDisplay(nodeName) { 88 | var element, display; 89 | if (!elementDisplay[nodeName]) { 90 | element = document.createElement(nodeName); 91 | document.body.appendChild(element); 92 | display = getComputedStyle(element, '').getPropertyValue("display"); 93 | element.parentNode.removeChild(element); 94 | display == "none" && (display = "block"); 95 | elementDisplay[nodeName] = display; 96 | } 97 | return elementDisplay[nodeName]; 98 | } 99 | 100 | function fragment(html, name) { 101 | if (name === undefined) fragmentRE.test(html) && RegExp.$1; 102 | if (!(name in containers)) name = '*'; 103 | var container = containers[name]; 104 | container.innerHTML = '' + html; 105 | return slice.call(container.childNodes); 106 | } 107 | 108 | function Z(dom, selector){ 109 | dom = dom || emptyArray; 110 | dom.__proto__ = Z.prototype; 111 | dom.selector = selector || ''; 112 | return dom; 113 | } 114 | 115 | function $(selector, context){ 116 | if (!selector) return Z(); 117 | if (context !== undefined) return $(context).find(selector); 118 | else if (isF(selector)) return $(document).ready(selector); 119 | else if (selector instanceof Z) return selector; 120 | else { 121 | var dom; 122 | if (isA(selector)) dom = compact(selector); 123 | else if (elementTypes.indexOf(selector.nodeType) >= 0 || selector === window) 124 | dom = [selector], selector = null; 125 | else if (fragmentRE.test(selector)) 126 | dom = fragment(selector.trim(), RegExp.$1), selector = null; 127 | else if (selector.nodeType && selector.nodeType == 3) dom = [selector]; 128 | else dom = $$(document, selector); 129 | return Z(dom, selector); 130 | } 131 | } 132 | 133 | $.extend = function(target){ 134 | slice.call(arguments, 1).forEach(function(source) { 135 | for (key in source) target[key] = source[key]; 136 | }) 137 | return target; 138 | } 139 | 140 | $.qsa = $$ = function(element, selector){ 141 | var found; 142 | return (element === document && idSelectorRE.test(selector)) ? 143 | ( (found = element.getElementById(RegExp.$1)) ? [found] : emptyArray ) : 144 | slice.call( 145 | classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) : 146 | tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) : 147 | element.querySelectorAll(selector) 148 | ); 149 | } 150 | 151 | function filtered(nodes, selector){ 152 | return selector === undefined ? $(nodes) : $(nodes).filter(selector); 153 | } 154 | 155 | function funcArg(context, arg, idx, payload){ 156 | return isF(arg) ? arg.call(context, idx, payload) : arg; 157 | } 158 | 159 | $.isFunction = isF; 160 | $.isObject = isO; 161 | $.isArray = isA; 162 | 163 | $.map = function(elements, callback) { 164 | var value, values = [], i, key; 165 | if (likeArray(elements)) 166 | for (i = 0; i < elements.length; i++) { 167 | value = callback(elements[i], i); 168 | if (value != null) values.push(value); 169 | } 170 | else 171 | for (key in elements) { 172 | value = callback(elements[key], key); 173 | if (value != null) values.push(value); 174 | } 175 | return flatten(values); 176 | } 177 | 178 | $.each = function(elements, callback) { 179 | var i, key; 180 | if (likeArray(elements)) 181 | for(i = 0; i < elements.length; i++) { 182 | if(callback(i, elements[i]) === false) return elements; 183 | } 184 | else 185 | for(key in elements) { 186 | if(callback(key, elements[key]) === false) return elements; 187 | } 188 | return elements; 189 | } 190 | 191 | $.fn = { 192 | forEach: emptyArray.forEach, 193 | reduce: emptyArray.reduce, 194 | push: emptyArray.push, 195 | indexOf: emptyArray.indexOf, 196 | concat: emptyArray.concat, 197 | map: function(fn){ 198 | return $.map(this, function(el, i){ return fn.call(el, i, el) }); 199 | }, 200 | slice: function(){ 201 | return $(slice.apply(this, arguments)); 202 | }, 203 | ready: function(callback){ 204 | if (readyRE.test(document.readyState)) callback($); 205 | else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false); 206 | return this; 207 | }, 208 | get: function(idx){ return idx === undefined ? this : this[idx] }, 209 | size: function(){ return this.length }, 210 | remove: function () { 211 | return this.each(function () { 212 | if (this.parentNode != null) { 213 | this.parentNode.removeChild(this); 214 | } 215 | }); 216 | }, 217 | each: function(callback){ 218 | this.forEach(function(el, idx){ callback.call(el, idx, el) }); 219 | return this; 220 | }, 221 | filter: function(selector){ 222 | return $([].filter.call(this, function(element){ 223 | return element.parentNode && $$(element.parentNode, selector).indexOf(element) >= 0; 224 | })); 225 | }, 226 | end: function(){ 227 | return this.prevObject || $(); 228 | }, 229 | andSelf:function(){ 230 | return this.add(this.prevObject || $()) 231 | }, 232 | add:function(selector,context){ 233 | return $(uniq(this.concat($(selector,context)))); 234 | }, 235 | is: function(selector){ 236 | return this.length > 0 && $(this[0]).filter(selector).length > 0; 237 | }, 238 | not: function(selector){ 239 | var nodes=[]; 240 | if (isF(selector) && selector.call !== undefined) 241 | this.each(function(idx){ 242 | if (!selector.call(this,idx)) nodes.push(this); 243 | }); 244 | else { 245 | var excludes = typeof selector == 'string' ? this.filter(selector) : 246 | (likeArray(selector) && isF(selector.item)) ? slice.call(selector) : $(selector); 247 | this.forEach(function(el){ 248 | if (excludes.indexOf(el) < 0) nodes.push(el); 249 | }); 250 | } 251 | return $(nodes); 252 | }, 253 | eq: function(idx){ 254 | return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1); 255 | }, 256 | first: function(){ var el = this[0]; return el && !isO(el) ? el : $(el) }, 257 | last: function(){ var el = this[this.length - 1]; return el && !isO(el) ? el : $(el) }, 258 | find: function(selector){ 259 | var result; 260 | if (this.length == 1) result = $$(this[0], selector); 261 | else result = this.map(function(){ return $$(this, selector) }); 262 | return $(result); 263 | }, 264 | closest: function(selector, context){ 265 | var node = this[0], candidates = $$(context || document, selector); 266 | if (!candidates.length) node = null; 267 | while (node && candidates.indexOf(node) < 0) 268 | node = node !== context && node !== document && node.parentNode; 269 | return $(node); 270 | }, 271 | parents: function(selector){ 272 | var ancestors = [], nodes = this; 273 | while (nodes.length > 0) 274 | nodes = $.map(nodes, function(node){ 275 | if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) { 276 | ancestors.push(node); 277 | return node; 278 | } 279 | }); 280 | return filtered(ancestors, selector); 281 | }, 282 | parent: function(selector){ 283 | return filtered(uniq(this.pluck('parentNode')), selector); 284 | }, 285 | children: function(selector){ 286 | return filtered(this.map(function(){ return slice.call(this.children) }), selector); 287 | }, 288 | siblings: function(selector){ 289 | return filtered(this.map(function(i, el){ 290 | return slice.call(el.parentNode.children).filter(function(child){ return child!==el }); 291 | }), selector); 292 | }, 293 | empty: function(){ return this.each(function(){ this.innerHTML = '' }) }, 294 | pluck: function(property){ return this.map(function(){ return this[property] }) }, 295 | show: function(){ 296 | return this.each(function() { 297 | this.style.display == "none" && (this.style.display = null); 298 | if (getComputedStyle(this, '').getPropertyValue("display") == "none") { 299 | this.style.display = defaultDisplay(this.nodeName) 300 | } 301 | }) 302 | }, 303 | replaceWith: function(newContent) { 304 | return this.each(function() { 305 | $(this).before(newContent).remove(); 306 | }); 307 | }, 308 | wrap: function(newContent) { 309 | return this.each(function() { 310 | $(this).wrapAll($(newContent)[0].cloneNode(false)); 311 | }); 312 | }, 313 | wrapAll: function(newContent) { 314 | if (this[0]) { 315 | $(this[0]).before(newContent = $(newContent)); 316 | newContent.append(this); 317 | } 318 | return this; 319 | }, 320 | unwrap: function(){ 321 | this.parent().each(function(){ 322 | $(this).replaceWith($(this).children()); 323 | }); 324 | return this; 325 | }, 326 | hide: function(){ 327 | return this.css("display", "none") 328 | }, 329 | toggle: function(setting){ 330 | return (setting === undefined ? this.css("display") == "none" : setting) ? this.show() : this.hide(); 331 | }, 332 | prev: function(){ return $(this.pluck('previousElementSibling')) }, 333 | next: function(){ return $(this.pluck('nextElementSibling')) }, 334 | html: function(html){ 335 | return html === undefined ? 336 | (this.length > 0 ? this[0].innerHTML : null) : 337 | this.each(function (idx) { 338 | var originHtml = this.innerHTML; 339 | $(this).empty().append( funcArg(this, html, idx, originHtml) ); 340 | }); 341 | }, 342 | text: function(text){ 343 | return text === undefined ? 344 | (this.length > 0 ? this[0].textContent : null) : 345 | this.each(function(){ this.textContent = text }); 346 | }, 347 | attr: function(name, value){ 348 | var res; 349 | return (typeof name == 'string' && value === undefined) ? 350 | (this.length == 0 ? undefined : 351 | (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() : 352 | (!(res = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : res 353 | ) : 354 | this.each(function(idx){ 355 | if (isO(name)) for (key in name) this.setAttribute(key, name[key]) 356 | else this.setAttribute(name, funcArg(this, value, idx, this.getAttribute(name))); 357 | }); 358 | }, 359 | removeAttr: function(name) { 360 | return this.each(function() { this.removeAttribute(name); }); 361 | }, 362 | data: function(name, value){ 363 | return this.attr('data-' + name, value); 364 | }, 365 | val: function(value){ 366 | return (value === undefined) ? 367 | (this.length > 0 ? this[0].value : null) : 368 | this.each(function(idx){ 369 | this.value = funcArg(this, value, idx, this.value); 370 | }); 371 | }, 372 | offset: function(){ 373 | if(this.length==0) return null; 374 | var obj = this[0].getBoundingClientRect(); 375 | return { 376 | left: obj.left + window.pageXOffset, 377 | top: obj.top + window.pageYOffset, 378 | width: obj.width, 379 | height: obj.height 380 | }; 381 | }, 382 | css: function(property, value){ 383 | if (value === undefined && typeof property == 'string') { 384 | return( 385 | this.length == 0 386 | ? undefined 387 | : this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property) 388 | ); 389 | } 390 | var css = ''; 391 | for (key in property) css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'; 392 | if (typeof property == 'string') css = dasherize(property) + ":" + maybeAddPx(property, value); 393 | return this.each(function() { this.style.cssText += ';' + css }); 394 | }, 395 | index: function(element){ 396 | return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]); 397 | }, 398 | hasClass: function(name){ 399 | if (this.length < 1) return false; 400 | else return classRE(name).test(this[0].className); 401 | }, 402 | addClass: function(name){ 403 | return this.each(function(idx) { 404 | classList = []; 405 | var cls = this.className, newName = funcArg(this, name, idx, cls); 406 | newName.split(/\s+/g).forEach(function(klass) { 407 | if (!$(this).hasClass(klass)) { 408 | classList.push(klass) 409 | } 410 | }, this); 411 | classList.length && (this.className += (cls ? " " : "") + classList.join(" ")) 412 | }); 413 | }, 414 | removeClass: function(name){ 415 | return this.each(function(idx) { 416 | if(name === undefined) 417 | return this.className = ''; 418 | classList = this.className; 419 | funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass) { 420 | classList = classList.replace(classRE(klass), " ") 421 | }); 422 | this.className = classList.trim() 423 | }); 424 | }, 425 | toggleClass: function(name, when){ 426 | return this.each(function(idx){ 427 | var newName = funcArg(this, name, idx, this.className); 428 | (when === undefined ? !$(this).hasClass(newName) : when) ? 429 | $(this).addClass(newName) : $(this).removeClass(newName); 430 | }); 431 | } 432 | }; 433 | 434 | 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){ 435 | var fn = $.fn[property]; 436 | $.fn[property] = function() { 437 | var ret = fn.apply(this, arguments); 438 | ret.prevObject = this; 439 | return ret; 440 | } 441 | }); 442 | 443 | ['width', 'height'].forEach(function(dimension){ 444 | $.fn[dimension] = function(value) { 445 | var offset, Dimension = dimension.replace(/./, function(m) { return m[0].toUpperCase() }); 446 | if (value === undefined) return this[0] == window ? window['inner' + Dimension] : 447 | this[0] == document ? document.documentElement['offset' + Dimension] : 448 | (offset = this.offset()) && offset[dimension]; 449 | else return this.each(function(idx){ 450 | var el = $(this); 451 | el.css(dimension, funcArg(this, value, idx, el[dimension]())); 452 | }); 453 | } 454 | }); 455 | 456 | function insert(operator, target, node) { 457 | var parent = (operator % 2) ? target : target.parentNode; 458 | parent && parent.insertBefore(node, 459 | !operator ? target.nextSibling : // after 460 | operator == 1 ? parent.firstChild : // prepend 461 | operator == 2 ? target : // before 462 | null); // append 463 | } 464 | 465 | function traverseNode (node, fun) { 466 | fun(node); 467 | for (var key in node.childNodes) { 468 | traverseNode(node.childNodes[key], fun); 469 | } 470 | } 471 | 472 | adjacencyOperators.forEach(function(key, operator) { 473 | $.fn[key] = function(html){ 474 | var nodes = isO(html) ? html : fragment(html); 475 | if (!('length' in nodes) || nodes.nodeType) nodes = [nodes]; 476 | if (nodes.length < 1) return this; 477 | var size = this.length, copyByClone = size > 1, inReverse = operator < 2; 478 | 479 | return this.each(function(index, target){ 480 | for (var i = 0; i < nodes.length; i++) { 481 | var node = nodes[inReverse ? nodes.length-i-1 : i]; 482 | traverseNode(node, function (node) { 483 | if (node.nodeName != null && node.nodeName.toUpperCase() === 'SCRIPT' && (!node.type || node.type === 'text/javascript')) { 484 | window['eval'].call(window, node.innerHTML); 485 | } 486 | }); 487 | if (copyByClone && index < size - 1) node = node.cloneNode(true); 488 | insert(operator, target, node); 489 | } 490 | }); 491 | }; 492 | 493 | var reverseKey = (operator % 2) ? key+'To' : 'insert'+(operator ? 'Before' : 'After'); 494 | $.fn[reverseKey] = function(html) { 495 | $(html)[key](this); 496 | return this; 497 | }; 498 | }); 499 | 500 | Z.prototype = $.fn; 501 | 502 | return $; 503 | })(); 504 | 505 | window.Zepto = Zepto; 506 | '$' in window || (window.$ = Zepto); 507 | // Zepto.js 508 | // (c) 2010, 2011 Thomas Fuchs 509 | // Zepto.js may be freely distributed under the MIT license. 510 | 511 | (function($){ 512 | var $$ = $.qsa, handlers = {}, _zid = 1, specialEvents={}; 513 | 514 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'; 515 | 516 | function zid(element) { 517 | return element._zid || (element._zid = _zid++); 518 | } 519 | function findHandlers(element, event, fn, selector) { 520 | event = parse(event); 521 | if (event.ns) var matcher = matcherFor(event.ns); 522 | return (handlers[zid(element)] || []).filter(function(handler) { 523 | return handler 524 | && (!event.e || handler.e == event.e) 525 | && (!event.ns || matcher.test(handler.ns)) 526 | && (!fn || handler.fn == fn) 527 | && (!selector || handler.sel == selector); 528 | }); 529 | } 530 | function parse(event) { 531 | var parts = ('' + event).split('.'); 532 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')}; 533 | } 534 | function matcherFor(ns) { 535 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)'); 536 | } 537 | 538 | function eachEvent(events, fn, iterator){ 539 | if ($.isObject(events)) $.each(events, iterator); 540 | else events.split(/\s/).forEach(function(type){ iterator(type, fn) }); 541 | } 542 | 543 | function add(element, events, fn, selector, getDelegate){ 544 | var id = zid(element), set = (handlers[id] || (handlers[id] = [])); 545 | eachEvent(events, fn, function(event, fn){ 546 | var delegate = getDelegate && getDelegate(fn, event), 547 | callback = delegate || fn; 548 | var proxyfn = function (event) { 549 | var result = callback.apply(element, [event].concat(event.data)); 550 | if (result === false) event.preventDefault(); 551 | return result; 552 | }; 553 | var handler = $.extend(parse(event), {fn: fn, proxy: proxyfn, sel: selector, del: delegate, i: set.length}); 554 | set.push(handler); 555 | element.addEventListener(handler.e, proxyfn, false); 556 | }); 557 | } 558 | function remove(element, events, fn, selector){ 559 | var id = zid(element); 560 | eachEvent(events || '', fn, function(event, fn){ 561 | findHandlers(element, event, fn, selector).forEach(function(handler){ 562 | delete handlers[id][handler.i]; 563 | element.removeEventListener(handler.e, handler.proxy, false); 564 | }); 565 | }); 566 | } 567 | 568 | $.event = { add: add, remove: remove } 569 | 570 | $.fn.bind = function(event, callback){ 571 | return this.each(function(){ 572 | add(this, event, callback); 573 | }); 574 | }; 575 | $.fn.unbind = function(event, callback){ 576 | return this.each(function(){ 577 | remove(this, event, callback); 578 | }); 579 | }; 580 | $.fn.one = function(event, callback){ 581 | return this.each(function(i, element){ 582 | add(this, event, callback, null, function(fn, type){ 583 | return function(){ 584 | var result = fn.apply(element, arguments); 585 | remove(element, type, fn); 586 | return result; 587 | } 588 | }); 589 | }); 590 | }; 591 | 592 | var returnTrue = function(){return true}, 593 | returnFalse = function(){return false}, 594 | eventMethods = { 595 | preventDefault: 'isDefaultPrevented', 596 | stopImmediatePropagation: 'isImmediatePropagationStopped', 597 | stopPropagation: 'isPropagationStopped' 598 | }; 599 | function createProxy(event) { 600 | var proxy = $.extend({originalEvent: event}, event); 601 | $.each(eventMethods, function(name, predicate) { 602 | proxy[name] = function(){ 603 | this[predicate] = returnTrue; 604 | return event[name].apply(event, arguments); 605 | }; 606 | proxy[predicate] = returnFalse; 607 | }) 608 | return proxy; 609 | } 610 | 611 | // emulates the 'defaultPrevented' property for browsers that have none 612 | function fix(event) { 613 | if (!('defaultPrevented' in event)) { 614 | event.defaultPrevented = false; 615 | var prevent = event.preventDefault; 616 | event.preventDefault = function() { 617 | this.defaultPrevented = true; 618 | prevent.call(this); 619 | } 620 | } 621 | } 622 | 623 | $.fn.delegate = function(selector, event, callback){ 624 | return this.each(function(i, element){ 625 | add(element, event, callback, selector, function(fn){ 626 | return function(e){ 627 | var evt, match = $(e.target).closest(selector, element).get(0); 628 | if (match) { 629 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}); 630 | return fn.apply(match, [evt].concat([].slice.call(arguments, 1))); 631 | } 632 | } 633 | }); 634 | }); 635 | }; 636 | $.fn.undelegate = function(selector, event, callback){ 637 | return this.each(function(){ 638 | remove(this, event, callback, selector); 639 | }); 640 | } 641 | 642 | $.fn.live = function(event, callback){ 643 | $(document.body).delegate(this.selector, event, callback); 644 | return this; 645 | }; 646 | $.fn.die = function(event, callback){ 647 | $(document.body).undelegate(this.selector, event, callback); 648 | return this; 649 | }; 650 | 651 | $.fn.on = function(event, selector, callback){ 652 | return selector === undefined || $.isFunction(selector) ? 653 | this.bind(event, selector) : this.delegate(selector, event, callback); 654 | }; 655 | $.fn.off = function(event, selector, callback){ 656 | return selector === undefined || $.isFunction(selector) ? 657 | this.unbind(event, selector) : this.undelegate(selector, event, callback); 658 | }; 659 | 660 | $.fn.trigger = function(event, data){ 661 | if (typeof event == 'string') event = $.Event(event); 662 | fix(event); 663 | event.data = data; 664 | return this.each(function(){ this.dispatchEvent(event) }); 665 | }; 666 | 667 | // triggers event handlers on current element just as if an event occurred, 668 | // doesn't trigger an actual event, doesn't bubble 669 | $.fn.triggerHandler = function(event, data){ 670 | var e, result; 671 | this.each(function(i, element){ 672 | e = createProxy(typeof event == 'string' ? $.Event(event) : event); 673 | e.data = data; e.target = element; 674 | $.each(findHandlers(element, event.type || event), function(i, handler){ 675 | result = handler.proxy(e); 676 | if (e.isImmediatePropagationStopped()) return false; 677 | }); 678 | }); 679 | return result; 680 | }; 681 | 682 | // shortcut methods for `.bind(event, fn)` for each event type 683 | ('focusin focusout load resize scroll unload click dblclick '+ 684 | 'mousedown mouseup mousemove mouseover mouseout '+ 685 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) { 686 | $.fn[event] = function(callback){ return this.bind(event, callback) }; 687 | }); 688 | 689 | ['focus', 'blur'].forEach(function(name) { 690 | $.fn[name] = function(callback) { 691 | if (callback) this.bind(name, callback); 692 | else if (this.length) try { this.get(0)[name]() } catch(e){}; 693 | return this; 694 | }; 695 | }); 696 | 697 | $.Event = function(type, props) { 698 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true; 699 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]); 700 | event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null); 701 | return event; 702 | }; 703 | 704 | })(Zepto); 705 | // Zepto.js 706 | // (c) 2010, 2011 Thomas Fuchs 707 | // Zepto.js may be freely distributed under the MIT license. 708 | 709 | (function($){ 710 | function detect(ua){ 711 | var os = (this.os = {}), browser = (this.browser = {}), 712 | webkit = ua.match(/WebKit\/([\d.]+)/), 713 | android = ua.match(/(Android)\s+([\d.]+)/), 714 | ipad = ua.match(/(iPad).*OS\s([\d_]+)/), 715 | iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/), 716 | webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/), 717 | touchpad = webos && ua.match(/TouchPad/), 718 | blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/); 719 | 720 | if (webkit) browser.version = webkit[1]; 721 | browser.webkit = !!webkit; 722 | 723 | if (android) os.android = true, os.version = android[2]; 724 | if (iphone) os.ios = true, os.version = iphone[2].replace(/_/g, '.'), os.iphone = true; 725 | if (ipad) os.ios = true, os.version = ipad[2].replace(/_/g, '.'), os.ipad = true; 726 | if (webos) os.webos = true, os.version = webos[2]; 727 | if (touchpad) os.touchpad = true; 728 | if (blackberry) os.blackberry = true, os.version = blackberry[2]; 729 | } 730 | 731 | // ### $.os 732 | // 733 | // Object containing information about browser platform 734 | // 735 | // *Example:* 736 | // 737 | // $.os.ios // => true if running on Apple iOS 738 | // $.os.android // => true if running on Android 739 | // $.os.webos // => true if running on HP/Palm WebOS 740 | // $.os.touchpad // => true if running on a HP TouchPad 741 | // $.os.version // => string with a version number, e.g. 742 | // "4.0", "3.1.1", "2.1", etc. 743 | // $.os.iphone // => true if running on iPhone 744 | // $.os.ipad // => true if running on iPad 745 | // $.os.blackberry // => true if running on BlackBerry 746 | // 747 | // ### $.browser 748 | // 749 | // *Example:* 750 | // 751 | // $.browser.webkit // => true if the browser is WebKit-based 752 | // $.browser.version // => WebKit version string 753 | detect.call($, navigator.userAgent); 754 | 755 | // make available to unit tests 756 | $.__detect = detect; 757 | 758 | })(Zepto); 759 | // Zepto.js 760 | // (c) 2010, 2011 Thomas Fuchs 761 | // Zepto.js may be freely distributed under the MIT license. 762 | 763 | (function($, undefined){ 764 | var prefix = '', eventPrefix, endEventName, endAnimationName, 765 | vendors = {Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS'}, 766 | document = window.document, testEl = document.createElement('div'), 767 | supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i; 768 | 769 | function downcase(str) { return str.toLowerCase() } 770 | function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) }; 771 | 772 | $.each(vendors, function(vendor, event){ 773 | if (testEl.style[vendor + 'TransitionProperty'] !== undefined) { 774 | prefix = '-' + downcase(vendor) + '-'; 775 | eventPrefix = event; 776 | return false; 777 | } 778 | }); 779 | 780 | $.fx = { 781 | off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), 782 | cssPrefix: prefix, 783 | transitionEnd: normalizeEvent('TransitionEnd'), 784 | animationEnd: normalizeEvent('AnimationEnd') 785 | }; 786 | 787 | $.fn.animate = function(properties, duration, ease, callback){ 788 | if ($.isObject(duration)) 789 | ease = duration.easing, callback = duration.complete, duration = duration.duration; 790 | if (duration) duration = duration / 1000; 791 | return this.anim(properties, duration, ease, callback); 792 | }; 793 | 794 | $.fn.anim = function(properties, duration, ease, callback){ 795 | var transforms, cssProperties = {}, key, that = this, wrappedCallback, endEvent = $.fx.transitionEnd; 796 | if (duration === undefined) duration = 0.4; 797 | if ($.fx.off) duration = 0; 798 | 799 | if (typeof properties == 'string') { 800 | // keyframe animation 801 | cssProperties[prefix + 'animation-name'] = properties; 802 | cssProperties[prefix + 'animation-duration'] = duration + 's'; 803 | endEvent = $.fx.animationEnd; 804 | } else { 805 | // CSS transitions 806 | for (key in properties) 807 | if (supportedTransforms.test(key)) { 808 | transforms || (transforms = []); 809 | transforms.push(key + '(' + properties[key] + ')'); 810 | } 811 | else cssProperties[key] = properties[key]; 812 | 813 | if (transforms) cssProperties[prefix + 'transform'] = transforms.join(' '); 814 | if (!$.fx.off) cssProperties[prefix + 'transition'] = 'all ' + duration + 's ' + (ease || ''); 815 | } 816 | 817 | wrappedCallback = function(){ 818 | var props = {}; 819 | props[prefix + 'transition'] = props[prefix + 'animation-name'] = 'none'; 820 | $(this).css(props); 821 | callback && callback.call(this); 822 | } 823 | if (duration > 0) this.one(endEvent, wrappedCallback); 824 | 825 | setTimeout(function() { 826 | that.css(cssProperties); 827 | if (duration <= 0) setTimeout(function() { 828 | that.each(function(){ wrappedCallback.call(this) }); 829 | }, 0); 830 | }, 0); 831 | 832 | return this; 833 | }; 834 | 835 | testEl = null; 836 | })(Zepto); 837 | // Zepto.js 838 | // (c) 2010, 2011 Thomas Fuchs 839 | // Zepto.js may be freely distributed under the MIT license. 840 | 841 | (function($){ 842 | var jsonpID = 0, 843 | isObject = $.isObject, 844 | document = window.document, 845 | key, 846 | name; 847 | 848 | // trigger a custom event and return false if it was cancelled 849 | function triggerAndReturn(context, eventName, data) { 850 | var event = $.Event(eventName); 851 | $(context).trigger(event, data); 852 | return !event.defaultPrevented; 853 | } 854 | 855 | // trigger an Ajax "global" event 856 | function triggerGlobal(settings, context, eventName, data) { 857 | if (settings.global) return triggerAndReturn(context || document, eventName, data); 858 | } 859 | 860 | // Number of active Ajax requests 861 | $.active = 0; 862 | 863 | function ajaxStart(settings) { 864 | if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart'); 865 | } 866 | function ajaxStop(settings) { 867 | if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop'); 868 | } 869 | 870 | // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable 871 | function ajaxBeforeSend(xhr, settings) { 872 | var context = settings.context; 873 | if (settings.beforeSend.call(context, xhr, settings) === false || 874 | triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) 875 | return false; 876 | 877 | triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]); 878 | } 879 | function ajaxSuccess(data, xhr, settings) { 880 | var context = settings.context, status = 'success'; 881 | settings.success.call(context, data, status, xhr); 882 | triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]); 883 | ajaxComplete(status, xhr, settings); 884 | } 885 | // type: "timeout", "error", "abort", "parsererror" 886 | function ajaxError(error, type, xhr, settings) { 887 | var context = settings.context; 888 | settings.error.call(context, xhr, type, error); 889 | triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error]); 890 | ajaxComplete(type, xhr, settings); 891 | } 892 | // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" 893 | function ajaxComplete(status, xhr, settings) { 894 | var context = settings.context; 895 | settings.complete.call(context, xhr, status); 896 | triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]); 897 | ajaxStop(settings); 898 | } 899 | 900 | // Empty function, used as default callback 901 | function empty() {} 902 | 903 | // ### $.ajaxJSONP 904 | // 905 | // Load JSON from a server in a different domain (JSONP) 906 | // 907 | // *Arguments:* 908 | // 909 | // options — object that configure the request, 910 | // see avaliable options below 911 | // 912 | // *Avaliable options:* 913 | // 914 | // url — url to which the request is sent 915 | // success — callback that is executed if the request succeeds 916 | // error — callback that is executed if the server drops error 917 | // context — in which context to execute the callbacks in 918 | // 919 | // *Example:* 920 | // 921 | // $.ajaxJSONP({ 922 | // url: 'http://example.com/projects?callback=?', 923 | // success: function (data) { 924 | // projects.push(json); 925 | // } 926 | // }); 927 | // 928 | $.ajaxJSONP = function(options){ 929 | var callbackName = 'jsonp' + (++jsonpID), 930 | script = document.createElement('script'), 931 | abort = function(){ 932 | $(script).remove(); 933 | if (callbackName in window) window[callbackName] = empty; 934 | ajaxComplete(xhr, options, 'abort'); 935 | }, 936 | xhr = { abort: abort }, abortTimeout; 937 | 938 | window[callbackName] = function(data){ 939 | clearTimeout(abortTimeout); 940 | $(script).remove(); 941 | delete window[callbackName]; 942 | ajaxSuccess(data, xhr, options); 943 | }; 944 | 945 | script.src = options.url.replace(/=\?/, '=' + callbackName); 946 | $('head').append(script); 947 | 948 | if (options.timeout > 0) abortTimeout = setTimeout(function(){ 949 | xhr.abort(); 950 | ajaxComplete(xhr, options, 'timeout'); 951 | }, options.timeout); 952 | 953 | return xhr; 954 | }; 955 | 956 | // ### $.ajaxSettings 957 | // 958 | // AJAX settings 959 | // 960 | $.ajaxSettings = { 961 | // Default type of request 962 | type: 'GET', 963 | // Callback that is executed before request 964 | beforeSend: empty, 965 | // Callback that is executed if the request succeeds 966 | success: empty, 967 | // Callback that is executed the the server drops error 968 | error: empty, 969 | // Callback that is executed on request complete (both: error and success) 970 | complete: empty, 971 | // The context for the callbacks 972 | context: null, 973 | // Whether to trigger "global" Ajax events 974 | global: true, 975 | // Transport 976 | xhr: function () { 977 | return new window.XMLHttpRequest(); 978 | }, 979 | // MIME types mapping 980 | accepts: { 981 | script: 'text/javascript, application/javascript', 982 | json: 'application/json', 983 | xml: 'application/xml, text/xml', 984 | html: 'text/html', 985 | text: 'text/plain' 986 | }, 987 | // Whether the request is to another domain 988 | crossDomain: false, 989 | // Default timeout 990 | timeout: 0 991 | }; 992 | 993 | // ### $.ajax 994 | // 995 | // Perform AJAX request 996 | // 997 | // *Arguments:* 998 | // 999 | // options — object that configure the request, 1000 | // see avaliable options below 1001 | // 1002 | // *Avaliable options:* 1003 | // 1004 | // type ('GET') — type of request GET / POST 1005 | // url (window.location) — url to which the request is sent 1006 | // data — data to send to server, 1007 | // can be string or object 1008 | // dataType ('json') — what response type you accept from 1009 | // the server: 1010 | // 'json', 'xml', 'html', or 'text' 1011 | // timeout (0) — request timeout 1012 | // beforeSend — callback that is executed before 1013 | // request send 1014 | // complete — callback that is executed on request 1015 | // complete (both: error and success) 1016 | // success — callback that is executed if 1017 | // the request succeeds 1018 | // error — callback that is executed if 1019 | // the server drops error 1020 | // context — in which context to execute the 1021 | // callbacks in 1022 | // 1023 | // *Example:* 1024 | // 1025 | // $.ajax({ 1026 | // type: 'POST', 1027 | // url: '/projects', 1028 | // data: { name: 'Zepto.js' }, 1029 | // dataType: 'html', 1030 | // timeout: 100, 1031 | // context: $('body'), 1032 | // success: function (data) { 1033 | // this.append(data); 1034 | // }, 1035 | // error: function (xhr, type) { 1036 | // alert('Error!'); 1037 | // } 1038 | // }); 1039 | // 1040 | $.ajax = function(options){ 1041 | var settings = $.extend({}, options || {}); 1042 | for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]; 1043 | 1044 | ajaxStart(settings); 1045 | 1046 | if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && 1047 | RegExp.$2 != window.location.host; 1048 | 1049 | if (/=\?/.test(settings.url)) return $.ajaxJSONP(settings); 1050 | 1051 | if (!settings.url) settings.url = window.location.toString(); 1052 | if (settings.data && !settings.contentType) settings.contentType = 'application/x-www-form-urlencoded'; 1053 | if (isObject(settings.data)) settings.data = $.param(settings.data); 1054 | 1055 | if (settings.type.match(/get/i) && settings.data) { 1056 | var queryString = settings.data; 1057 | if (settings.url.match(/\?.*=/)) { 1058 | queryString = '&' + queryString; 1059 | } else if (queryString[0] != '?') { 1060 | queryString = '?' + queryString; 1061 | } 1062 | settings.url += queryString; 1063 | } 1064 | 1065 | var mime = settings.accepts[settings.dataType], 1066 | baseHeaders = { }, 1067 | protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, 1068 | xhr = $.ajaxSettings.xhr(), abortTimeout; 1069 | 1070 | if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest'; 1071 | if (mime) baseHeaders['Accept'] = mime; 1072 | settings.headers = $.extend(baseHeaders, settings.headers || {}); 1073 | 1074 | xhr.onreadystatechange = function(){ 1075 | if (xhr.readyState == 4) { 1076 | clearTimeout(abortTimeout); 1077 | var result, error = false; 1078 | if ((xhr.status >= 200 && xhr.status < 300) || (xhr.status == 0 && protocol == 'file:')) { 1079 | if (mime == 'application/json' && !(/^\s*$/.test(xhr.responseText))) { 1080 | try { result = JSON.parse(xhr.responseText); } 1081 | catch (e) { error = e; } 1082 | } 1083 | else result = xhr.responseText; 1084 | if (error) ajaxError(error, 'parsererror', xhr, settings); 1085 | else ajaxSuccess(result, xhr, settings); 1086 | } else { 1087 | ajaxError(null, 'error', xhr, settings); 1088 | } 1089 | } 1090 | }; 1091 | 1092 | xhr.open(settings.type, settings.url, true); 1093 | 1094 | if (settings.contentType) settings.headers['Content-Type'] = settings.contentType; 1095 | for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name]); 1096 | 1097 | if (ajaxBeforeSend(xhr, settings) === false) { 1098 | xhr.abort(); 1099 | return false; 1100 | } 1101 | 1102 | if (settings.timeout > 0) abortTimeout = setTimeout(function(){ 1103 | xhr.onreadystatechange = empty; 1104 | xhr.abort(); 1105 | ajaxError(null, 'timeout', xhr, settings); 1106 | }, settings.timeout); 1107 | 1108 | xhr.send(settings.data); 1109 | return xhr; 1110 | }; 1111 | 1112 | // ### $.get 1113 | // 1114 | // Load data from the server using a GET request 1115 | // 1116 | // *Arguments:* 1117 | // 1118 | // url — url to which the request is sent 1119 | // success — callback that is executed if the request succeeds 1120 | // 1121 | // *Example:* 1122 | // 1123 | // $.get( 1124 | // '/projects/42', 1125 | // function (data) { 1126 | // $('body').append(data); 1127 | // } 1128 | // ); 1129 | // 1130 | $.get = function(url, success){ return $.ajax({ url: url, success: success }) }; 1131 | 1132 | // ### $.post 1133 | // 1134 | // Load data from the server using POST request 1135 | // 1136 | // *Arguments:* 1137 | // 1138 | // url — url to which the request is sent 1139 | // [data] — data to send to server, can be string or object 1140 | // [success] — callback that is executed if the request succeeds 1141 | // [dataType] — type of expected response 1142 | // 'json', 'xml', 'html', or 'text' 1143 | // 1144 | // *Example:* 1145 | // 1146 | // $.post( 1147 | // '/projects', 1148 | // { name: 'Zepto.js' }, 1149 | // function (data) { 1150 | // $('body').append(data); 1151 | // }, 1152 | // 'html' 1153 | // ); 1154 | // 1155 | $.post = function(url, data, success, dataType){ 1156 | if ($.isFunction(data)) dataType = dataType || success, success = data, data = null; 1157 | return $.ajax({ type: 'POST', url: url, data: data, success: success, dataType: dataType }); 1158 | }; 1159 | 1160 | // ### $.getJSON 1161 | // 1162 | // Load JSON from the server using GET request 1163 | // 1164 | // *Arguments:* 1165 | // 1166 | // url — url to which the request is sent 1167 | // success — callback that is executed if the request succeeds 1168 | // 1169 | // *Example:* 1170 | // 1171 | // $.getJSON( 1172 | // '/projects/42', 1173 | // function (json) { 1174 | // projects.push(json); 1175 | // } 1176 | // ); 1177 | // 1178 | $.getJSON = function(url, success){ 1179 | return $.ajax({ url: url, success: success, dataType: 'json' }); 1180 | }; 1181 | 1182 | // ### $.fn.load 1183 | // 1184 | // Load data from the server into an element 1185 | // 1186 | // *Arguments:* 1187 | // 1188 | // url — url to which the request is sent 1189 | // [success] — callback that is executed if the request succeeds 1190 | // 1191 | // *Examples:* 1192 | // 1193 | // $('#project_container').get( 1194 | // '/projects/42', 1195 | // function () { 1196 | // alert('Project was successfully loaded'); 1197 | // } 1198 | // ); 1199 | // 1200 | // $('#project_comments').get( 1201 | // '/projects/42 #comments', 1202 | // function () { 1203 | // alert('Comments was successfully loaded'); 1204 | // } 1205 | // ); 1206 | // 1207 | $.fn.load = function(url, success){ 1208 | if (!this.length) return this; 1209 | var self = this, parts = url.split(/\s/), selector; 1210 | if (parts.length > 1) url = parts[0], selector = parts[1]; 1211 | $.get(url, function(response){ 1212 | self.html(selector ? 1213 | $(document.createElement('div')).html(response).find(selector).html() 1214 | : response); 1215 | success && success.call(self); 1216 | }); 1217 | return this; 1218 | }; 1219 | 1220 | var escape = encodeURIComponent; 1221 | 1222 | function serialize(params, obj, traditional, scope){ 1223 | var array = $.isArray(obj); 1224 | $.each(obj, function(key, value) { 1225 | if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']'; 1226 | // handle data in serializeArray() format 1227 | if (!scope && array) params.add(value.name, value.value); 1228 | // recurse into nested objects 1229 | else if (traditional ? $.isArray(value) : isObject(value)) 1230 | serialize(params, value, traditional, key); 1231 | else params.add(key, value); 1232 | }); 1233 | } 1234 | 1235 | // ### $.param 1236 | // 1237 | // Encode object as a string of URL-encoded key-value pairs 1238 | // 1239 | // *Arguments:* 1240 | // 1241 | // obj — object to serialize 1242 | // [traditional] — perform shallow serialization 1243 | // 1244 | // *Example:* 1245 | // 1246 | // $.param( { name: 'Zepto.js', version: '0.6' } ); 1247 | // 1248 | $.param = function(obj, traditional){ 1249 | var params = []; 1250 | params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) }; 1251 | serialize(params, obj, traditional); 1252 | return params.join('&').replace('%20', '+'); 1253 | }; 1254 | })(Zepto); 1255 | // Zepto.js 1256 | // (c) 2010, 2011 Thomas Fuchs 1257 | // Zepto.js may be freely distributed under the MIT license. 1258 | 1259 | (function ($) { 1260 | 1261 | // ### $.fn.serializeArray 1262 | // 1263 | // Encode a set of form elements as an array of names and values 1264 | // 1265 | // *Example:* 1266 | // 1267 | // $('#login_form').serializeArray(); 1268 | // 1269 | // returns 1270 | // 1271 | // [ 1272 | // { 1273 | // name: 'email', 1274 | // value: 'koss@nocorp.me' 1275 | // }, 1276 | // { 1277 | // name: 'password', 1278 | // value: '123456' 1279 | // } 1280 | // ] 1281 | // 1282 | $.fn.serializeArray = function () { 1283 | var result = [], el; 1284 | $( Array.prototype.slice.call(this.get(0).elements) ).each(function () { 1285 | el = $(this); 1286 | var type = el.attr('type'); 1287 | if ( 1288 | !this.disabled && type != 'submit' && type != 'reset' && type != 'button' && 1289 | ((type != 'radio' && type != 'checkbox') || this.checked) 1290 | ) { 1291 | result.push({ 1292 | name: el.attr('name'), 1293 | value: el.val() 1294 | }); 1295 | } 1296 | }); 1297 | return result; 1298 | }; 1299 | 1300 | // ### $.fn.serialize 1301 | // 1302 | // 1303 | // Encode a set of form elements as a string for submission 1304 | // 1305 | // *Example:* 1306 | // 1307 | // $('#login_form').serialize(); 1308 | // 1309 | // returns 1310 | // 1311 | // "email=koss%40nocorp.me&password=123456" 1312 | // 1313 | $.fn.serialize = function () { 1314 | var result = []; 1315 | this.serializeArray().forEach(function (elm) { 1316 | result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) ); 1317 | }); 1318 | return result.join('&'); 1319 | }; 1320 | 1321 | // ### $.fn.submit 1322 | // 1323 | // Bind or trigger the submit event for a form 1324 | // 1325 | // *Examples:* 1326 | // 1327 | // To bind a handler for the submit event: 1328 | // 1329 | // $('#login_form').submit(function (e) { 1330 | // alert('Form was submitted!'); 1331 | // e.preventDefault(); 1332 | // }); 1333 | // 1334 | // To trigger form submit: 1335 | // 1336 | // $('#login_form').submit(); 1337 | // 1338 | $.fn.submit = function (callback) { 1339 | if (callback) this.bind('submit', callback) 1340 | else if (this.length) { 1341 | var event = $.Event('submit'); 1342 | this.eq(0).trigger(event); 1343 | if (!event.defaultPrevented) this.get(0).submit() 1344 | } 1345 | return this; 1346 | } 1347 | 1348 | })(Zepto); 1349 | // Zepto.js 1350 | // (c) 2010, 2011 Thomas Fuchs 1351 | // Zepto.js may be freely distributed under the MIT license. 1352 | 1353 | (function($){ 1354 | var touch = {}, touchTimeout; 1355 | 1356 | function parentIfText(node){ 1357 | return 'tagName' in node ? node : node.parentNode; 1358 | } 1359 | 1360 | function swipeDirection(x1, x2, y1, y2){ 1361 | var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2); 1362 | if (xDelta >= yDelta) { 1363 | return (x1 - x2 > 0 ? 'Left' : 'Right'); 1364 | } else { 1365 | return (y1 - y2 > 0 ? 'Up' : 'Down'); 1366 | } 1367 | } 1368 | 1369 | var longTapDelay = 750; 1370 | function longTap(){ 1371 | if (touch.last && (Date.now() - touch.last >= longTapDelay)) { 1372 | $(touch.target).trigger('longTap'); 1373 | touch = {}; 1374 | } 1375 | } 1376 | 1377 | $(document).ready(function(){ 1378 | $(document.body).bind('touchstart', function(e){ 1379 | var now = Date.now(), delta = now - (touch.last || now); 1380 | touch.target = parentIfText(e.touches[0].target); 1381 | touchTimeout && clearTimeout(touchTimeout); 1382 | touch.x1 = e.touches[0].pageX; 1383 | touch.y1 = e.touches[0].pageY; 1384 | if (delta > 0 && delta <= 250) touch.isDoubleTap = true; 1385 | touch.last = now; 1386 | setTimeout(longTap, longTapDelay); 1387 | }).bind('touchmove', function(e){ 1388 | touch.x2 = e.touches[0].pageX; 1389 | touch.y2 = e.touches[0].pageY; 1390 | }).bind('touchend', function(e){ 1391 | if (touch.isDoubleTap) { 1392 | $(touch.target).trigger('doubleTap'); 1393 | touch = {}; 1394 | } else if (touch.x2 > 0 || touch.y2 > 0) { 1395 | (Math.abs(touch.x1 - touch.x2) > 30 || Math.abs(touch.y1 - touch.y2) > 30) && 1396 | $(touch.target).trigger('swipe') && 1397 | $(touch.target).trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))); 1398 | touch.x1 = touch.x2 = touch.y1 = touch.y2 = touch.last = 0; 1399 | } else if ('last' in touch) { 1400 | touchTimeout = setTimeout(function(){ 1401 | touchTimeout = null; 1402 | $(touch.target).trigger('tap') 1403 | touch = {}; 1404 | }, 250); 1405 | } 1406 | }).bind('touchcancel', function(){ touch = {} }); 1407 | }); 1408 | 1409 | ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'longTap'].forEach(function(m){ 1410 | $.fn[m] = function(callback){ return this.bind(m, callback) } 1411 | }); 1412 | })(Zepto); 1413 | --------------------------------------------------------------------------------