qiaowei | f044a74 | 2019-07-10 16:04:20 +0800 | [diff] [blame^] | 1 | /** |
| 2 | * jQuery WeUI V1.2.1 |
| 3 | * By 言川 |
| 4 | * http://lihongxun945.github.io/jquery-weui/ |
| 5 | */ |
| 6 | /* global $:true */ |
| 7 | /* global WebKitCSSMatrix:true */ |
| 8 | |
| 9 | (function($) { |
| 10 | "use strict"; |
| 11 | |
| 12 | $.fn.transitionEnd = function(callback) { |
| 13 | var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'], |
| 14 | i, dom = this; |
| 15 | |
| 16 | function fireCallBack(e) { |
| 17 | /*jshint validthis:true */ |
| 18 | if (e.target !== this) return; |
| 19 | callback.call(this, e); |
| 20 | for (i = 0; i < events.length; i++) { |
| 21 | dom.off(events[i], fireCallBack); |
| 22 | } |
| 23 | } |
| 24 | if (callback) { |
| 25 | for (i = 0; i < events.length; i++) { |
| 26 | dom.on(events[i], fireCallBack); |
| 27 | } |
| 28 | } |
| 29 | return this; |
| 30 | }; |
| 31 | |
| 32 | $.support = (function() { |
| 33 | var support = { |
| 34 | touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) |
| 35 | }; |
| 36 | return support; |
| 37 | })(); |
| 38 | |
| 39 | $.touchEvents = { |
| 40 | start: $.support.touch ? 'touchstart' : 'mousedown', |
| 41 | move: $.support.touch ? 'touchmove' : 'mousemove', |
| 42 | end: $.support.touch ? 'touchend' : 'mouseup' |
| 43 | }; |
| 44 | |
| 45 | $.getTouchPosition = function(e) { |
| 46 | e = e.originalEvent || e; //jquery wrap the originevent |
| 47 | if(e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend') { |
| 48 | return { |
| 49 | x: e.targetTouches[0].pageX, |
| 50 | y: e.targetTouches[0].pageY |
| 51 | }; |
| 52 | } else { |
| 53 | return { |
| 54 | x: e.pageX, |
| 55 | y: e.pageY |
| 56 | }; |
| 57 | } |
| 58 | }; |
| 59 | |
| 60 | $.fn.scrollHeight = function() { |
| 61 | return this[0].scrollHeight; |
| 62 | }; |
| 63 | |
| 64 | $.fn.transform = function(transform) { |
| 65 | for (var i = 0; i < this.length; i++) { |
| 66 | var elStyle = this[i].style; |
| 67 | elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform; |
| 68 | } |
| 69 | return this; |
| 70 | }; |
| 71 | $.fn.transition = function(duration) { |
| 72 | if (typeof duration !== 'string') { |
| 73 | duration = duration + 'ms'; |
| 74 | } |
| 75 | for (var i = 0; i < this.length; i++) { |
| 76 | var elStyle = this[i].style; |
| 77 | elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration; |
| 78 | } |
| 79 | return this; |
| 80 | }; |
| 81 | |
| 82 | $.getTranslate = function (el, axis) { |
| 83 | var matrix, curTransform, curStyle, transformMatrix; |
| 84 | |
| 85 | // automatic axis detection |
| 86 | if (typeof axis === 'undefined') { |
| 87 | axis = 'x'; |
| 88 | } |
| 89 | |
| 90 | curStyle = window.getComputedStyle(el, null); |
| 91 | if (window.WebKitCSSMatrix) { |
| 92 | // Some old versions of Webkit choke when 'none' is passed; pass |
| 93 | // empty string instead in this case |
| 94 | transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform); |
| 95 | } |
| 96 | else { |
| 97 | transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,'); |
| 98 | matrix = transformMatrix.toString().split(','); |
| 99 | } |
| 100 | |
| 101 | if (axis === 'x') { |
| 102 | //Latest Chrome and webkits Fix |
| 103 | if (window.WebKitCSSMatrix) |
| 104 | curTransform = transformMatrix.m41; |
| 105 | //Crazy IE10 Matrix |
| 106 | else if (matrix.length === 16) |
| 107 | curTransform = parseFloat(matrix[12]); |
| 108 | //Normal Browsers |
| 109 | else |
| 110 | curTransform = parseFloat(matrix[4]); |
| 111 | } |
| 112 | if (axis === 'y') { |
| 113 | //Latest Chrome and webkits Fix |
| 114 | if (window.WebKitCSSMatrix) |
| 115 | curTransform = transformMatrix.m42; |
| 116 | //Crazy IE10 Matrix |
| 117 | else if (matrix.length === 16) |
| 118 | curTransform = parseFloat(matrix[13]); |
| 119 | //Normal Browsers |
| 120 | else |
| 121 | curTransform = parseFloat(matrix[5]); |
| 122 | } |
| 123 | |
| 124 | return curTransform || 0; |
| 125 | }; |
| 126 | $.requestAnimationFrame = function (callback) { |
| 127 | if (window.requestAnimationFrame) return window.requestAnimationFrame(callback); |
| 128 | else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback); |
| 129 | else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback); |
| 130 | else { |
| 131 | return window.setTimeout(callback, 1000 / 60); |
| 132 | } |
| 133 | }; |
| 134 | |
| 135 | $.cancelAnimationFrame = function (id) { |
| 136 | if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id); |
| 137 | else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id); |
| 138 | else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id); |
| 139 | else { |
| 140 | return window.clearTimeout(id); |
| 141 | } |
| 142 | }; |
| 143 | |
| 144 | $.fn.join = function(arg) { |
| 145 | return this.toArray().join(arg); |
| 146 | } |
| 147 | })($); |
| 148 | |
| 149 | /*=========================== |
| 150 | Template7 Template engine |
| 151 | ===========================*/ |
| 152 | /* global $:true */ |
| 153 | /* jshint unused:false */ |
| 154 | /* jshint forin:false */ |
| 155 | +function ($) { |
| 156 | "use strict"; |
| 157 | $.Template7 = $.t7 = (function () { |
| 158 | function isArray(arr) { |
| 159 | return Object.prototype.toString.apply(arr) === '[object Array]'; |
| 160 | } |
| 161 | function isObject(obj) { |
| 162 | return obj instanceof Object; |
| 163 | } |
| 164 | function isFunction(func) { |
| 165 | return typeof func === 'function'; |
| 166 | } |
| 167 | var cache = {}; |
| 168 | function helperToSlices(string) { |
| 169 | var helperParts = string.replace(/[{}#}]/g, '').split(' '); |
| 170 | var slices = []; |
| 171 | var shiftIndex, i, j; |
| 172 | for (i = 0; i < helperParts.length; i++) { |
| 173 | var part = helperParts[i]; |
| 174 | if (i === 0) slices.push(part); |
| 175 | else { |
| 176 | if (part.indexOf('"') === 0) { |
| 177 | // Plain String |
| 178 | if (part.match(/"/g).length === 2) { |
| 179 | // One word string |
| 180 | slices.push(part); |
| 181 | } |
| 182 | else { |
| 183 | // Find closed Index |
| 184 | shiftIndex = 0; |
| 185 | for (j = i + 1; j < helperParts.length; j++) { |
| 186 | part += ' ' + helperParts[j]; |
| 187 | if (helperParts[j].indexOf('"') >= 0) { |
| 188 | shiftIndex = j; |
| 189 | slices.push(part); |
| 190 | break; |
| 191 | } |
| 192 | } |
| 193 | if (shiftIndex) i = shiftIndex; |
| 194 | } |
| 195 | } |
| 196 | else { |
| 197 | if (part.indexOf('=') > 0) { |
| 198 | // Hash |
| 199 | var hashParts = part.split('='); |
| 200 | var hashName = hashParts[0]; |
| 201 | var hashContent = hashParts[1]; |
| 202 | if (hashContent.match(/"/g).length !== 2) { |
| 203 | shiftIndex = 0; |
| 204 | for (j = i + 1; j < helperParts.length; j++) { |
| 205 | hashContent += ' ' + helperParts[j]; |
| 206 | if (helperParts[j].indexOf('"') >= 0) { |
| 207 | shiftIndex = j; |
| 208 | break; |
| 209 | } |
| 210 | } |
| 211 | if (shiftIndex) i = shiftIndex; |
| 212 | } |
| 213 | var hash = [hashName, hashContent.replace(/"/g,'')]; |
| 214 | slices.push(hash); |
| 215 | } |
| 216 | else { |
| 217 | // Plain variable |
| 218 | slices.push(part); |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | return slices; |
| 224 | } |
| 225 | function stringToBlocks(string) { |
| 226 | var blocks = [], i, j, k; |
| 227 | if (!string) return []; |
| 228 | var _blocks = string.split(/({{[^{^}]*}})/); |
| 229 | for (i = 0; i < _blocks.length; i++) { |
| 230 | var block = _blocks[i]; |
| 231 | if (block === '') continue; |
| 232 | if (block.indexOf('{{') < 0) { |
| 233 | blocks.push({ |
| 234 | type: 'plain', |
| 235 | content: block |
| 236 | }); |
| 237 | } |
| 238 | else { |
| 239 | if (block.indexOf('{/') >= 0) { |
| 240 | continue; |
| 241 | } |
| 242 | if (block.indexOf('{#') < 0 && block.indexOf(' ') < 0 && block.indexOf('else') < 0) { |
| 243 | // Simple variable |
| 244 | blocks.push({ |
| 245 | type: 'variable', |
| 246 | contextName: block.replace(/[{}]/g, '') |
| 247 | }); |
| 248 | continue; |
| 249 | } |
| 250 | // Helpers |
| 251 | var helperSlices = helperToSlices(block); |
| 252 | var helperName = helperSlices[0]; |
| 253 | var helperContext = []; |
| 254 | var helperHash = {}; |
| 255 | for (j = 1; j < helperSlices.length; j++) { |
| 256 | var slice = helperSlices[j]; |
| 257 | if (isArray(slice)) { |
| 258 | // Hash |
| 259 | helperHash[slice[0]] = slice[1] === 'false' ? false : slice[1]; |
| 260 | } |
| 261 | else { |
| 262 | helperContext.push(slice); |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | if (block.indexOf('{#') >= 0) { |
| 267 | // Condition/Helper |
| 268 | var helperStartIndex = i; |
| 269 | var helperContent = ''; |
| 270 | var elseContent = ''; |
| 271 | var toSkip = 0; |
| 272 | var shiftIndex; |
| 273 | var foundClosed = false, foundElse = false, foundClosedElse = false, depth = 0; |
| 274 | for (j = i + 1; j < _blocks.length; j++) { |
| 275 | if (_blocks[j].indexOf('{{#') >= 0) { |
| 276 | depth ++; |
| 277 | } |
| 278 | if (_blocks[j].indexOf('{{/') >= 0) { |
| 279 | depth --; |
| 280 | } |
| 281 | if (_blocks[j].indexOf('{{#' + helperName) >= 0) { |
| 282 | helperContent += _blocks[j]; |
| 283 | if (foundElse) elseContent += _blocks[j]; |
| 284 | toSkip ++; |
| 285 | } |
| 286 | else if (_blocks[j].indexOf('{{/' + helperName) >= 0) { |
| 287 | if (toSkip > 0) { |
| 288 | toSkip--; |
| 289 | helperContent += _blocks[j]; |
| 290 | if (foundElse) elseContent += _blocks[j]; |
| 291 | } |
| 292 | else { |
| 293 | shiftIndex = j; |
| 294 | foundClosed = true; |
| 295 | break; |
| 296 | } |
| 297 | } |
| 298 | else if (_blocks[j].indexOf('else') >= 0 && depth === 0) { |
| 299 | foundElse = true; |
| 300 | } |
| 301 | else { |
| 302 | if (!foundElse) helperContent += _blocks[j]; |
| 303 | if (foundElse) elseContent += _blocks[j]; |
| 304 | } |
| 305 | |
| 306 | } |
| 307 | if (foundClosed) { |
| 308 | if (shiftIndex) i = shiftIndex; |
| 309 | blocks.push({ |
| 310 | type: 'helper', |
| 311 | helperName: helperName, |
| 312 | contextName: helperContext, |
| 313 | content: helperContent, |
| 314 | inverseContent: elseContent, |
| 315 | hash: helperHash |
| 316 | }); |
| 317 | } |
| 318 | } |
| 319 | else if (block.indexOf(' ') > 0) { |
| 320 | blocks.push({ |
| 321 | type: 'helper', |
| 322 | helperName: helperName, |
| 323 | contextName: helperContext, |
| 324 | hash: helperHash |
| 325 | }); |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | return blocks; |
| 330 | } |
| 331 | var Template7 = function (template) { |
| 332 | var t = this; |
| 333 | t.template = template; |
| 334 | |
| 335 | function getCompileFn(block, depth) { |
| 336 | if (block.content) return compile(block.content, depth); |
| 337 | else return function () {return ''; }; |
| 338 | } |
| 339 | function getCompileInverse(block, depth) { |
| 340 | if (block.inverseContent) return compile(block.inverseContent, depth); |
| 341 | else return function () {return ''; }; |
| 342 | } |
| 343 | function getCompileVar(name, ctx) { |
| 344 | var variable, parts, levelsUp = 0, initialCtx = ctx; |
| 345 | if (name.indexOf('../') === 0) { |
| 346 | levelsUp = name.split('../').length - 1; |
| 347 | var newDepth = ctx.split('_')[1] - levelsUp; |
| 348 | ctx = 'ctx_' + (newDepth >= 1 ? newDepth : 1); |
| 349 | parts = name.split('../')[levelsUp].split('.'); |
| 350 | } |
| 351 | else if (name.indexOf('@global') === 0) { |
| 352 | ctx = '$.Template7.global'; |
| 353 | parts = name.split('@global.')[1].split('.'); |
| 354 | } |
| 355 | else if (name.indexOf('@root') === 0) { |
| 356 | ctx = 'ctx_1'; |
| 357 | parts = name.split('@root.')[1].split('.'); |
| 358 | } |
| 359 | else { |
| 360 | parts = name.split('.'); |
| 361 | } |
| 362 | variable = ctx; |
| 363 | for (var i = 0; i < parts.length; i++) { |
| 364 | var part = parts[i]; |
| 365 | if (part.indexOf('@') === 0) { |
| 366 | if (i > 0) { |
| 367 | variable += '[(data && data.' + part.replace('@', '') + ')]'; |
| 368 | } |
| 369 | else { |
| 370 | variable = '(data && data.' + name.replace('@', '') + ')'; |
| 371 | } |
| 372 | } |
| 373 | else { |
| 374 | if (isFinite(part)) { |
| 375 | variable += '[' + part + ']'; |
| 376 | } |
| 377 | else { |
| 378 | if (part.indexOf('this') === 0) { |
| 379 | variable = part.replace('this', ctx); |
| 380 | } |
| 381 | else { |
| 382 | variable += '.' + part; |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | return variable; |
| 389 | } |
| 390 | function getCompiledArguments(contextArray, ctx) { |
| 391 | var arr = []; |
| 392 | for (var i = 0; i < contextArray.length; i++) { |
| 393 | if (contextArray[i].indexOf('"') === 0) arr.push(contextArray[i]); |
| 394 | else { |
| 395 | arr.push(getCompileVar(contextArray[i], ctx)); |
| 396 | } |
| 397 | } |
| 398 | return arr.join(', '); |
| 399 | } |
| 400 | function compile(template, depth) { |
| 401 | depth = depth || 1; |
| 402 | template = template || t.template; |
| 403 | if (typeof template !== 'string') { |
| 404 | throw new Error('Template7: Template must be a string'); |
| 405 | } |
| 406 | var blocks = stringToBlocks(template); |
| 407 | if (blocks.length === 0) { |
| 408 | return function () { return ''; }; |
| 409 | } |
| 410 | var ctx = 'ctx_' + depth; |
| 411 | var resultString = '(function (' + ctx + ', data) {\n'; |
| 412 | if (depth === 1) { |
| 413 | resultString += 'function isArray(arr){return Object.prototype.toString.apply(arr) === \'[object Array]\';}\n'; |
| 414 | resultString += 'function isFunction(func){return (typeof func === \'function\');}\n'; |
| 415 | resultString += 'function c(val, ctx) {if (typeof val !== "undefined") {if (isFunction(val)) {return val.call(ctx);} else return val;} else return "";}\n'; |
| 416 | } |
| 417 | resultString += 'var r = \'\';\n'; |
| 418 | var i, j, context; |
| 419 | for (i = 0; i < blocks.length; i++) { |
| 420 | var block = blocks[i]; |
| 421 | // Plain block |
| 422 | if (block.type === 'plain') { |
| 423 | resultString += 'r +=\'' + (block.content).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/'/g, '\\' + '\'') + '\';'; |
| 424 | continue; |
| 425 | } |
| 426 | var variable, compiledArguments; |
| 427 | // Variable block |
| 428 | if (block.type === 'variable') { |
| 429 | variable = getCompileVar(block.contextName, ctx); |
| 430 | resultString += 'r += c(' + variable + ', ' + ctx + ');'; |
| 431 | } |
| 432 | // Helpers block |
| 433 | if (block.type === 'helper') { |
| 434 | if (block.helperName in t.helpers) { |
| 435 | compiledArguments = getCompiledArguments(block.contextName, ctx); |
| 436 | resultString += 'r += ($.Template7.helpers.' + block.helperName + ').call(' + ctx + ', ' + (compiledArguments && (compiledArguments + ', ')) +'{hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});'; |
| 437 | } |
| 438 | else { |
| 439 | if (block.contextName.length > 0) { |
| 440 | throw new Error('Template7: Missing helper: "' + block.helperName + '"'); |
| 441 | } |
| 442 | else { |
| 443 | variable = getCompileVar(block.helperName, ctx); |
| 444 | resultString += 'if (' + variable + ') {'; |
| 445 | resultString += 'if (isArray(' + variable + ')) {'; |
| 446 | resultString += 'r += ($.Template7.helpers.each).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});'; |
| 447 | resultString += '}else {'; |
| 448 | resultString += 'r += ($.Template7.helpers.with).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});'; |
| 449 | resultString += '}}'; |
| 450 | } |
| 451 | } |
| 452 | } |
| 453 | } |
| 454 | resultString += '\nreturn r;})'; |
| 455 | return eval.call(window, resultString); |
| 456 | } |
| 457 | t.compile = function (template) { |
| 458 | if (!t.compiled) { |
| 459 | t.compiled = compile(template); |
| 460 | } |
| 461 | return t.compiled; |
| 462 | }; |
| 463 | }; |
| 464 | Template7.prototype = { |
| 465 | options: {}, |
| 466 | helpers: { |
| 467 | 'if': function (context, options) { |
| 468 | if (isFunction(context)) { context = context.call(this); } |
| 469 | if (context) { |
| 470 | return options.fn(this, options.data); |
| 471 | } |
| 472 | else { |
| 473 | return options.inverse(this, options.data); |
| 474 | } |
| 475 | }, |
| 476 | 'unless': function (context, options) { |
| 477 | if (isFunction(context)) { context = context.call(this); } |
| 478 | if (!context) { |
| 479 | return options.fn(this, options.data); |
| 480 | } |
| 481 | else { |
| 482 | return options.inverse(this, options.data); |
| 483 | } |
| 484 | }, |
| 485 | 'each': function (context, options) { |
| 486 | var ret = '', i = 0; |
| 487 | if (isFunction(context)) { context = context.call(this); } |
| 488 | if (isArray(context)) { |
| 489 | if (options.hash.reverse) { |
| 490 | context = context.reverse(); |
| 491 | } |
| 492 | for (i = 0; i < context.length; i++) { |
| 493 | ret += options.fn(context[i], {first: i === 0, last: i === context.length - 1, index: i}); |
| 494 | } |
| 495 | if (options.hash.reverse) { |
| 496 | context = context.reverse(); |
| 497 | } |
| 498 | } |
| 499 | else { |
| 500 | for (var key in context) { |
| 501 | i++; |
| 502 | ret += options.fn(context[key], {key: key}); |
| 503 | } |
| 504 | } |
| 505 | if (i > 0) return ret; |
| 506 | else return options.inverse(this); |
| 507 | }, |
| 508 | 'with': function (context, options) { |
| 509 | if (isFunction(context)) { context = context.call(this); } |
| 510 | return options.fn(context); |
| 511 | }, |
| 512 | 'join': function (context, options) { |
| 513 | if (isFunction(context)) { context = context.call(this); } |
| 514 | return context.join(options.hash.delimiter || options.hash.delimeter); |
| 515 | }, |
| 516 | 'js': function (expression, options) { |
| 517 | var func; |
| 518 | if (expression.indexOf('return')>=0) { |
| 519 | func = '(function(){'+expression+'})'; |
| 520 | } |
| 521 | else { |
| 522 | func = '(function(){return ('+expression+')})'; |
| 523 | } |
| 524 | return eval.call(this, func).call(this); |
| 525 | }, |
| 526 | 'js_compare': function (expression, options) { |
| 527 | var func; |
| 528 | if (expression.indexOf('return')>=0) { |
| 529 | func = '(function(){'+expression+'})'; |
| 530 | } |
| 531 | else { |
| 532 | func = '(function(){return ('+expression+')})'; |
| 533 | } |
| 534 | var condition = eval.call(this, func).call(this); |
| 535 | if (condition) { |
| 536 | return options.fn(this, options.data); |
| 537 | } |
| 538 | else { |
| 539 | return options.inverse(this, options.data); |
| 540 | } |
| 541 | } |
| 542 | } |
| 543 | }; |
| 544 | var t7 = function (template, data) { |
| 545 | if (arguments.length === 2) { |
| 546 | var instance = new Template7(template); |
| 547 | var rendered = instance.compile()(data); |
| 548 | instance = null; |
| 549 | return (rendered); |
| 550 | } |
| 551 | else return new Template7(template); |
| 552 | }; |
| 553 | t7.registerHelper = function (name, fn) { |
| 554 | Template7.prototype.helpers[name] = fn; |
| 555 | }; |
| 556 | t7.unregisterHelper = function (name) { |
| 557 | Template7.prototype.helpers[name] = undefined; |
| 558 | delete Template7.prototype.helpers[name]; |
| 559 | }; |
| 560 | |
| 561 | t7.compile = function (template, options) { |
| 562 | var instance = new Template7(template, options); |
| 563 | return instance.compile(); |
| 564 | }; |
| 565 | |
| 566 | t7.options = Template7.prototype.options; |
| 567 | t7.helpers = Template7.prototype.helpers; |
| 568 | return t7; |
| 569 | })(); |
| 570 | }($); |
| 571 | |
| 572 | /*! Hammer.JS - v2.0.8 - 2016-04-23 |
| 573 | * http://hammerjs.github.io/ |
| 574 | * |
| 575 | * Copyright (c) 2016 Jorik Tangelder; |
| 576 | * Licensed under the MIT license */ |
| 577 | (function(window, document, exportName, undefined) { |
| 578 | 'use strict'; |
| 579 | |
| 580 | var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; |
| 581 | var TEST_ELEMENT = document.createElement('div'); |
| 582 | |
| 583 | var TYPE_FUNCTION = 'function'; |
| 584 | |
| 585 | var round = Math.round; |
| 586 | var abs = Math.abs; |
| 587 | var now = Date.now; |
| 588 | |
| 589 | /** |
| 590 | * set a timeout with a given scope |
| 591 | * @param {Function} fn |
| 592 | * @param {Number} timeout |
| 593 | * @param {Object} context |
| 594 | * @returns {number} |
| 595 | */ |
| 596 | function setTimeoutContext(fn, timeout, context) { |
| 597 | return setTimeout(bindFn(fn, context), timeout); |
| 598 | } |
| 599 | |
| 600 | /** |
| 601 | * if the argument is an array, we want to execute the fn on each entry |
| 602 | * if it aint an array we don't want to do a thing. |
| 603 | * this is used by all the methods that accept a single and array argument. |
| 604 | * @param {*|Array} arg |
| 605 | * @param {String} fn |
| 606 | * @param {Object} [context] |
| 607 | * @returns {Boolean} |
| 608 | */ |
| 609 | function invokeArrayArg(arg, fn, context) { |
| 610 | if (Array.isArray(arg)) { |
| 611 | each(arg, context[fn], context); |
| 612 | return true; |
| 613 | } |
| 614 | return false; |
| 615 | } |
| 616 | |
| 617 | /** |
| 618 | * walk objects and arrays |
| 619 | * @param {Object} obj |
| 620 | * @param {Function} iterator |
| 621 | * @param {Object} context |
| 622 | */ |
| 623 | function each(obj, iterator, context) { |
| 624 | var i; |
| 625 | |
| 626 | if (!obj) { |
| 627 | return; |
| 628 | } |
| 629 | |
| 630 | if (obj.forEach) { |
| 631 | obj.forEach(iterator, context); |
| 632 | } else if (obj.length !== undefined) { |
| 633 | i = 0; |
| 634 | while (i < obj.length) { |
| 635 | iterator.call(context, obj[i], i, obj); |
| 636 | i++; |
| 637 | } |
| 638 | } else { |
| 639 | for (i in obj) { |
| 640 | obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); |
| 641 | } |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | /** |
| 646 | * wrap a method with a deprecation warning and stack trace |
| 647 | * @param {Function} method |
| 648 | * @param {String} name |
| 649 | * @param {String} message |
| 650 | * @returns {Function} A new function wrapping the supplied method. |
| 651 | */ |
| 652 | function deprecate(method, name, message) { |
| 653 | var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; |
| 654 | return function() { |
| 655 | var e = new Error('get-stack-trace'); |
| 656 | var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') |
| 657 | .replace(/^\s+at\s+/gm, '') |
| 658 | .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; |
| 659 | |
| 660 | var log = window.console && (window.console.warn || window.console.log); |
| 661 | if (log) { |
| 662 | log.call(window.console, deprecationMessage, stack); |
| 663 | } |
| 664 | return method.apply(this, arguments); |
| 665 | }; |
| 666 | } |
| 667 | |
| 668 | /** |
| 669 | * extend object. |
| 670 | * means that properties in dest will be overwritten by the ones in src. |
| 671 | * @param {Object} target |
| 672 | * @param {...Object} objects_to_assign |
| 673 | * @returns {Object} target |
| 674 | */ |
| 675 | var assign; |
| 676 | if (typeof Object.assign !== 'function') { |
| 677 | assign = function assign(target) { |
| 678 | if (target === undefined || target === null) { |
| 679 | throw new TypeError('Cannot convert undefined or null to object'); |
| 680 | } |
| 681 | |
| 682 | var output = Object(target); |
| 683 | for (var index = 1; index < arguments.length; index++) { |
| 684 | var source = arguments[index]; |
| 685 | if (source !== undefined && source !== null) { |
| 686 | for (var nextKey in source) { |
| 687 | if (source.hasOwnProperty(nextKey)) { |
| 688 | output[nextKey] = source[nextKey]; |
| 689 | } |
| 690 | } |
| 691 | } |
| 692 | } |
| 693 | return output; |
| 694 | }; |
| 695 | } else { |
| 696 | assign = Object.assign; |
| 697 | } |
| 698 | |
| 699 | /** |
| 700 | * extend object. |
| 701 | * means that properties in dest will be overwritten by the ones in src. |
| 702 | * @param {Object} dest |
| 703 | * @param {Object} src |
| 704 | * @param {Boolean} [merge=false] |
| 705 | * @returns {Object} dest |
| 706 | */ |
| 707 | var extend = deprecate(function extend(dest, src, merge) { |
| 708 | var keys = Object.keys(src); |
| 709 | var i = 0; |
| 710 | while (i < keys.length) { |
| 711 | if (!merge || (merge && dest[keys[i]] === undefined)) { |
| 712 | dest[keys[i]] = src[keys[i]]; |
| 713 | } |
| 714 | i++; |
| 715 | } |
| 716 | return dest; |
| 717 | }, 'extend', 'Use `assign`.'); |
| 718 | |
| 719 | /** |
| 720 | * merge the values from src in the dest. |
| 721 | * means that properties that exist in dest will not be overwritten by src |
| 722 | * @param {Object} dest |
| 723 | * @param {Object} src |
| 724 | * @returns {Object} dest |
| 725 | */ |
| 726 | var merge = deprecate(function merge(dest, src) { |
| 727 | return extend(dest, src, true); |
| 728 | }, 'merge', 'Use `assign`.'); |
| 729 | |
| 730 | /** |
| 731 | * simple class inheritance |
| 732 | * @param {Function} child |
| 733 | * @param {Function} base |
| 734 | * @param {Object} [properties] |
| 735 | */ |
| 736 | function inherit(child, base, properties) { |
| 737 | var baseP = base.prototype, |
| 738 | childP; |
| 739 | |
| 740 | childP = child.prototype = Object.create(baseP); |
| 741 | childP.constructor = child; |
| 742 | childP._super = baseP; |
| 743 | |
| 744 | if (properties) { |
| 745 | assign(childP, properties); |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | /** |
| 750 | * simple function bind |
| 751 | * @param {Function} fn |
| 752 | * @param {Object} context |
| 753 | * @returns {Function} |
| 754 | */ |
| 755 | function bindFn(fn, context) { |
| 756 | return function boundFn() { |
| 757 | return fn.apply(context, arguments); |
| 758 | }; |
| 759 | } |
| 760 | |
| 761 | /** |
| 762 | * let a boolean value also be a function that must return a boolean |
| 763 | * this first item in args will be used as the context |
| 764 | * @param {Boolean|Function} val |
| 765 | * @param {Array} [args] |
| 766 | * @returns {Boolean} |
| 767 | */ |
| 768 | function boolOrFn(val, args) { |
| 769 | if (typeof val == TYPE_FUNCTION) { |
| 770 | return val.apply(args ? args[0] || undefined : undefined, args); |
| 771 | } |
| 772 | return val; |
| 773 | } |
| 774 | |
| 775 | /** |
| 776 | * use the val2 when val1 is undefined |
| 777 | * @param {*} val1 |
| 778 | * @param {*} val2 |
| 779 | * @returns {*} |
| 780 | */ |
| 781 | function ifUndefined(val1, val2) { |
| 782 | return (val1 === undefined) ? val2 : val1; |
| 783 | } |
| 784 | |
| 785 | /** |
| 786 | * addEventListener with multiple events at once |
| 787 | * @param {EventTarget} target |
| 788 | * @param {String} types |
| 789 | * @param {Function} handler |
| 790 | */ |
| 791 | function addEventListeners(target, types, handler) { |
| 792 | each(splitStr(types), function(type) { |
| 793 | target.addEventListener(type, handler, false); |
| 794 | }); |
| 795 | } |
| 796 | |
| 797 | /** |
| 798 | * removeEventListener with multiple events at once |
| 799 | * @param {EventTarget} target |
| 800 | * @param {String} types |
| 801 | * @param {Function} handler |
| 802 | */ |
| 803 | function removeEventListeners(target, types, handler) { |
| 804 | each(splitStr(types), function(type) { |
| 805 | target.removeEventListener(type, handler, false); |
| 806 | }); |
| 807 | } |
| 808 | |
| 809 | /** |
| 810 | * find if a node is in the given parent |
| 811 | * @method hasParent |
| 812 | * @param {HTMLElement} node |
| 813 | * @param {HTMLElement} parent |
| 814 | * @return {Boolean} found |
| 815 | */ |
| 816 | function hasParent(node, parent) { |
| 817 | while (node) { |
| 818 | if (node == parent) { |
| 819 | return true; |
| 820 | } |
| 821 | node = node.parentNode; |
| 822 | } |
| 823 | return false; |
| 824 | } |
| 825 | |
| 826 | /** |
| 827 | * small indexOf wrapper |
| 828 | * @param {String} str |
| 829 | * @param {String} find |
| 830 | * @returns {Boolean} found |
| 831 | */ |
| 832 | function inStr(str, find) { |
| 833 | return str.indexOf(find) > -1; |
| 834 | } |
| 835 | |
| 836 | /** |
| 837 | * split string on whitespace |
| 838 | * @param {String} str |
| 839 | * @returns {Array} words |
| 840 | */ |
| 841 | function splitStr(str) { |
| 842 | return str.trim().split(/\s+/g); |
| 843 | } |
| 844 | |
| 845 | /** |
| 846 | * find if a array contains the object using indexOf or a simple polyFill |
| 847 | * @param {Array} src |
| 848 | * @param {String} find |
| 849 | * @param {String} [findByKey] |
| 850 | * @return {Boolean|Number} false when not found, or the index |
| 851 | */ |
| 852 | function inArray(src, find, findByKey) { |
| 853 | if (src.indexOf && !findByKey) { |
| 854 | return src.indexOf(find); |
| 855 | } else { |
| 856 | var i = 0; |
| 857 | while (i < src.length) { |
| 858 | if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { |
| 859 | return i; |
| 860 | } |
| 861 | i++; |
| 862 | } |
| 863 | return -1; |
| 864 | } |
| 865 | } |
| 866 | |
| 867 | /** |
| 868 | * convert array-like objects to real arrays |
| 869 | * @param {Object} obj |
| 870 | * @returns {Array} |
| 871 | */ |
| 872 | function toArray(obj) { |
| 873 | return Array.prototype.slice.call(obj, 0); |
| 874 | } |
| 875 | |
| 876 | /** |
| 877 | * unique array with objects based on a key (like 'id') or just by the array's value |
| 878 | * @param {Array} src [{id:1},{id:2},{id:1}] |
| 879 | * @param {String} [key] |
| 880 | * @param {Boolean} [sort=False] |
| 881 | * @returns {Array} [{id:1},{id:2}] |
| 882 | */ |
| 883 | function uniqueArray(src, key, sort) { |
| 884 | var results = []; |
| 885 | var values = []; |
| 886 | var i = 0; |
| 887 | |
| 888 | while (i < src.length) { |
| 889 | var val = key ? src[i][key] : src[i]; |
| 890 | if (inArray(values, val) < 0) { |
| 891 | results.push(src[i]); |
| 892 | } |
| 893 | values[i] = val; |
| 894 | i++; |
| 895 | } |
| 896 | |
| 897 | if (sort) { |
| 898 | if (!key) { |
| 899 | results = results.sort(); |
| 900 | } else { |
| 901 | results = results.sort(function sortUniqueArray(a, b) { |
| 902 | return a[key] > b[key]; |
| 903 | }); |
| 904 | } |
| 905 | } |
| 906 | |
| 907 | return results; |
| 908 | } |
| 909 | |
| 910 | /** |
| 911 | * get the prefixed property |
| 912 | * @param {Object} obj |
| 913 | * @param {String} property |
| 914 | * @returns {String|Undefined} prefixed |
| 915 | */ |
| 916 | function prefixed(obj, property) { |
| 917 | var prefix, prop; |
| 918 | var camelProp = property[0].toUpperCase() + property.slice(1); |
| 919 | |
| 920 | var i = 0; |
| 921 | while (i < VENDOR_PREFIXES.length) { |
| 922 | prefix = VENDOR_PREFIXES[i]; |
| 923 | prop = (prefix) ? prefix + camelProp : property; |
| 924 | |
| 925 | if (prop in obj) { |
| 926 | return prop; |
| 927 | } |
| 928 | i++; |
| 929 | } |
| 930 | return undefined; |
| 931 | } |
| 932 | |
| 933 | /** |
| 934 | * get a unique id |
| 935 | * @returns {number} uniqueId |
| 936 | */ |
| 937 | var _uniqueId = 1; |
| 938 | function uniqueId() { |
| 939 | return _uniqueId++; |
| 940 | } |
| 941 | |
| 942 | /** |
| 943 | * get the window object of an element |
| 944 | * @param {HTMLElement} element |
| 945 | * @returns {DocumentView|Window} |
| 946 | */ |
| 947 | function getWindowForElement(element) { |
| 948 | var doc = element.ownerDocument || element; |
| 949 | return (doc.defaultView || doc.parentWindow || window); |
| 950 | } |
| 951 | |
| 952 | var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; |
| 953 | |
| 954 | var SUPPORT_TOUCH = ('ontouchstart' in window); |
| 955 | var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; |
| 956 | var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); |
| 957 | |
| 958 | var INPUT_TYPE_TOUCH = 'touch'; |
| 959 | var INPUT_TYPE_PEN = 'pen'; |
| 960 | var INPUT_TYPE_MOUSE = 'mouse'; |
| 961 | var INPUT_TYPE_KINECT = 'kinect'; |
| 962 | |
| 963 | var COMPUTE_INTERVAL = 25; |
| 964 | |
| 965 | var INPUT_START = 1; |
| 966 | var INPUT_MOVE = 2; |
| 967 | var INPUT_END = 4; |
| 968 | var INPUT_CANCEL = 8; |
| 969 | |
| 970 | var DIRECTION_NONE = 1; |
| 971 | var DIRECTION_LEFT = 2; |
| 972 | var DIRECTION_RIGHT = 4; |
| 973 | var DIRECTION_UP = 8; |
| 974 | var DIRECTION_DOWN = 16; |
| 975 | |
| 976 | var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; |
| 977 | var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; |
| 978 | var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; |
| 979 | |
| 980 | var PROPS_XY = ['x', 'y']; |
| 981 | var PROPS_CLIENT_XY = ['clientX', 'clientY']; |
| 982 | |
| 983 | /** |
| 984 | * create new input type manager |
| 985 | * @param {Manager} manager |
| 986 | * @param {Function} callback |
| 987 | * @returns {Input} |
| 988 | * @constructor |
| 989 | */ |
| 990 | function Input(manager, callback) { |
| 991 | var self = this; |
| 992 | this.manager = manager; |
| 993 | this.callback = callback; |
| 994 | this.element = manager.element; |
| 995 | this.target = manager.options.inputTarget; |
| 996 | |
| 997 | // smaller wrapper around the handler, for the scope and the enabled state of the manager, |
| 998 | // so when disabled the input events are completely bypassed. |
| 999 | this.domHandler = function(ev) { |
| 1000 | if (boolOrFn(manager.options.enable, [manager])) { |
| 1001 | self.handler(ev); |
| 1002 | } |
| 1003 | }; |
| 1004 | |
| 1005 | this.init(); |
| 1006 | |
| 1007 | } |
| 1008 | |
| 1009 | Input.prototype = { |
| 1010 | /** |
| 1011 | * should handle the inputEvent data and trigger the callback |
| 1012 | * @virtual |
| 1013 | */ |
| 1014 | handler: function() { }, |
| 1015 | |
| 1016 | /** |
| 1017 | * bind the events |
| 1018 | */ |
| 1019 | init: function() { |
| 1020 | this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); |
| 1021 | this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); |
| 1022 | this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); |
| 1023 | }, |
| 1024 | |
| 1025 | /** |
| 1026 | * unbind the events |
| 1027 | */ |
| 1028 | destroy: function() { |
| 1029 | this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); |
| 1030 | this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); |
| 1031 | this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); |
| 1032 | } |
| 1033 | }; |
| 1034 | |
| 1035 | /** |
| 1036 | * create new input type manager |
| 1037 | * called by the Manager constructor |
| 1038 | * @param {Hammer} manager |
| 1039 | * @returns {Input} |
| 1040 | */ |
| 1041 | function createInputInstance(manager) { |
| 1042 | var Type; |
| 1043 | var inputClass = manager.options.inputClass; |
| 1044 | |
| 1045 | if (inputClass) { |
| 1046 | Type = inputClass; |
| 1047 | } else if (SUPPORT_POINTER_EVENTS) { |
| 1048 | Type = PointerEventInput; |
| 1049 | } else if (SUPPORT_ONLY_TOUCH) { |
| 1050 | Type = TouchInput; |
| 1051 | } else if (!SUPPORT_TOUCH) { |
| 1052 | Type = MouseInput; |
| 1053 | } else { |
| 1054 | Type = TouchMouseInput; |
| 1055 | } |
| 1056 | return new (Type)(manager, inputHandler); |
| 1057 | } |
| 1058 | |
| 1059 | /** |
| 1060 | * handle input events |
| 1061 | * @param {Manager} manager |
| 1062 | * @param {String} eventType |
| 1063 | * @param {Object} input |
| 1064 | */ |
| 1065 | function inputHandler(manager, eventType, input) { |
| 1066 | var pointersLen = input.pointers.length; |
| 1067 | var changedPointersLen = input.changedPointers.length; |
| 1068 | var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); |
| 1069 | var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); |
| 1070 | |
| 1071 | input.isFirst = !!isFirst; |
| 1072 | input.isFinal = !!isFinal; |
| 1073 | |
| 1074 | if (isFirst) { |
| 1075 | manager.session = {}; |
| 1076 | } |
| 1077 | |
| 1078 | // source event is the normalized value of the domEvents |
| 1079 | // like 'touchstart, mouseup, pointerdown' |
| 1080 | input.eventType = eventType; |
| 1081 | |
| 1082 | // compute scale, rotation etc |
| 1083 | computeInputData(manager, input); |
| 1084 | |
| 1085 | // emit secret event |
| 1086 | manager.emit('hammer.input', input); |
| 1087 | |
| 1088 | manager.recognize(input); |
| 1089 | manager.session.prevInput = input; |
| 1090 | } |
| 1091 | |
| 1092 | /** |
| 1093 | * extend the data with some usable properties like scale, rotate, velocity etc |
| 1094 | * @param {Object} manager |
| 1095 | * @param {Object} input |
| 1096 | */ |
| 1097 | function computeInputData(manager, input) { |
| 1098 | var session = manager.session; |
| 1099 | var pointers = input.pointers; |
| 1100 | var pointersLength = pointers.length; |
| 1101 | |
| 1102 | // store the first input to calculate the distance and direction |
| 1103 | if (!session.firstInput) { |
| 1104 | session.firstInput = simpleCloneInputData(input); |
| 1105 | } |
| 1106 | |
| 1107 | // to compute scale and rotation we need to store the multiple touches |
| 1108 | if (pointersLength > 1 && !session.firstMultiple) { |
| 1109 | session.firstMultiple = simpleCloneInputData(input); |
| 1110 | } else if (pointersLength === 1) { |
| 1111 | session.firstMultiple = false; |
| 1112 | } |
| 1113 | |
| 1114 | var firstInput = session.firstInput; |
| 1115 | var firstMultiple = session.firstMultiple; |
| 1116 | var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; |
| 1117 | |
| 1118 | var center = input.center = getCenter(pointers); |
| 1119 | input.timeStamp = now(); |
| 1120 | input.deltaTime = input.timeStamp - firstInput.timeStamp; |
| 1121 | |
| 1122 | input.angle = getAngle(offsetCenter, center); |
| 1123 | input.distance = getDistance(offsetCenter, center); |
| 1124 | |
| 1125 | computeDeltaXY(session, input); |
| 1126 | input.offsetDirection = getDirection(input.deltaX, input.deltaY); |
| 1127 | |
| 1128 | var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); |
| 1129 | input.overallVelocityX = overallVelocity.x; |
| 1130 | input.overallVelocityY = overallVelocity.y; |
| 1131 | input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; |
| 1132 | |
| 1133 | input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; |
| 1134 | input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; |
| 1135 | |
| 1136 | input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > |
| 1137 | session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); |
| 1138 | |
| 1139 | computeIntervalInputData(session, input); |
| 1140 | |
| 1141 | // find the correct target |
| 1142 | var target = manager.element; |
| 1143 | if (hasParent(input.srcEvent.target, target)) { |
| 1144 | target = input.srcEvent.target; |
| 1145 | } |
| 1146 | input.target = target; |
| 1147 | } |
| 1148 | |
| 1149 | function computeDeltaXY(session, input) { |
| 1150 | var center = input.center; |
| 1151 | var offset = session.offsetDelta || {}; |
| 1152 | var prevDelta = session.prevDelta || {}; |
| 1153 | var prevInput = session.prevInput || {}; |
| 1154 | |
| 1155 | if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { |
| 1156 | prevDelta = session.prevDelta = { |
| 1157 | x: prevInput.deltaX || 0, |
| 1158 | y: prevInput.deltaY || 0 |
| 1159 | }; |
| 1160 | |
| 1161 | offset = session.offsetDelta = { |
| 1162 | x: center.x, |
| 1163 | y: center.y |
| 1164 | }; |
| 1165 | } |
| 1166 | |
| 1167 | input.deltaX = prevDelta.x + (center.x - offset.x); |
| 1168 | input.deltaY = prevDelta.y + (center.y - offset.y); |
| 1169 | } |
| 1170 | |
| 1171 | /** |
| 1172 | * velocity is calculated every x ms |
| 1173 | * @param {Object} session |
| 1174 | * @param {Object} input |
| 1175 | */ |
| 1176 | function computeIntervalInputData(session, input) { |
| 1177 | var last = session.lastInterval || input, |
| 1178 | deltaTime = input.timeStamp - last.timeStamp, |
| 1179 | velocity, velocityX, velocityY, direction; |
| 1180 | |
| 1181 | if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { |
| 1182 | var deltaX = input.deltaX - last.deltaX; |
| 1183 | var deltaY = input.deltaY - last.deltaY; |
| 1184 | |
| 1185 | var v = getVelocity(deltaTime, deltaX, deltaY); |
| 1186 | velocityX = v.x; |
| 1187 | velocityY = v.y; |
| 1188 | velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; |
| 1189 | direction = getDirection(deltaX, deltaY); |
| 1190 | |
| 1191 | session.lastInterval = input; |
| 1192 | } else { |
| 1193 | // use latest velocity info if it doesn't overtake a minimum period |
| 1194 | velocity = last.velocity; |
| 1195 | velocityX = last.velocityX; |
| 1196 | velocityY = last.velocityY; |
| 1197 | direction = last.direction; |
| 1198 | } |
| 1199 | |
| 1200 | input.velocity = velocity; |
| 1201 | input.velocityX = velocityX; |
| 1202 | input.velocityY = velocityY; |
| 1203 | input.direction = direction; |
| 1204 | } |
| 1205 | |
| 1206 | /** |
| 1207 | * create a simple clone from the input used for storage of firstInput and firstMultiple |
| 1208 | * @param {Object} input |
| 1209 | * @returns {Object} clonedInputData |
| 1210 | */ |
| 1211 | function simpleCloneInputData(input) { |
| 1212 | // make a simple copy of the pointers because we will get a reference if we don't |
| 1213 | // we only need clientXY for the calculations |
| 1214 | var pointers = []; |
| 1215 | var i = 0; |
| 1216 | while (i < input.pointers.length) { |
| 1217 | pointers[i] = { |
| 1218 | clientX: round(input.pointers[i].clientX), |
| 1219 | clientY: round(input.pointers[i].clientY) |
| 1220 | }; |
| 1221 | i++; |
| 1222 | } |
| 1223 | |
| 1224 | return { |
| 1225 | timeStamp: now(), |
| 1226 | pointers: pointers, |
| 1227 | center: getCenter(pointers), |
| 1228 | deltaX: input.deltaX, |
| 1229 | deltaY: input.deltaY |
| 1230 | }; |
| 1231 | } |
| 1232 | |
| 1233 | /** |
| 1234 | * get the center of all the pointers |
| 1235 | * @param {Array} pointers |
| 1236 | * @return {Object} center contains `x` and `y` properties |
| 1237 | */ |
| 1238 | function getCenter(pointers) { |
| 1239 | var pointersLength = pointers.length; |
| 1240 | |
| 1241 | // no need to loop when only one touch |
| 1242 | if (pointersLength === 1) { |
| 1243 | return { |
| 1244 | x: round(pointers[0].clientX), |
| 1245 | y: round(pointers[0].clientY) |
| 1246 | }; |
| 1247 | } |
| 1248 | |
| 1249 | var x = 0, y = 0, i = 0; |
| 1250 | while (i < pointersLength) { |
| 1251 | x += pointers[i].clientX; |
| 1252 | y += pointers[i].clientY; |
| 1253 | i++; |
| 1254 | } |
| 1255 | |
| 1256 | return { |
| 1257 | x: round(x / pointersLength), |
| 1258 | y: round(y / pointersLength) |
| 1259 | }; |
| 1260 | } |
| 1261 | |
| 1262 | /** |
| 1263 | * calculate the velocity between two points. unit is in px per ms. |
| 1264 | * @param {Number} deltaTime |
| 1265 | * @param {Number} x |
| 1266 | * @param {Number} y |
| 1267 | * @return {Object} velocity `x` and `y` |
| 1268 | */ |
| 1269 | function getVelocity(deltaTime, x, y) { |
| 1270 | return { |
| 1271 | x: x / deltaTime || 0, |
| 1272 | y: y / deltaTime || 0 |
| 1273 | }; |
| 1274 | } |
| 1275 | |
| 1276 | /** |
| 1277 | * get the direction between two points |
| 1278 | * @param {Number} x |
| 1279 | * @param {Number} y |
| 1280 | * @return {Number} direction |
| 1281 | */ |
| 1282 | function getDirection(x, y) { |
| 1283 | if (x === y) { |
| 1284 | return DIRECTION_NONE; |
| 1285 | } |
| 1286 | |
| 1287 | if (abs(x) >= abs(y)) { |
| 1288 | return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; |
| 1289 | } |
| 1290 | return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; |
| 1291 | } |
| 1292 | |
| 1293 | /** |
| 1294 | * calculate the absolute distance between two points |
| 1295 | * @param {Object} p1 {x, y} |
| 1296 | * @param {Object} p2 {x, y} |
| 1297 | * @param {Array} [props] containing x and y keys |
| 1298 | * @return {Number} distance |
| 1299 | */ |
| 1300 | function getDistance(p1, p2, props) { |
| 1301 | if (!props) { |
| 1302 | props = PROPS_XY; |
| 1303 | } |
| 1304 | var x = p2[props[0]] - p1[props[0]], |
| 1305 | y = p2[props[1]] - p1[props[1]]; |
| 1306 | |
| 1307 | return Math.sqrt((x * x) + (y * y)); |
| 1308 | } |
| 1309 | |
| 1310 | /** |
| 1311 | * calculate the angle between two coordinates |
| 1312 | * @param {Object} p1 |
| 1313 | * @param {Object} p2 |
| 1314 | * @param {Array} [props] containing x and y keys |
| 1315 | * @return {Number} angle |
| 1316 | */ |
| 1317 | function getAngle(p1, p2, props) { |
| 1318 | if (!props) { |
| 1319 | props = PROPS_XY; |
| 1320 | } |
| 1321 | var x = p2[props[0]] - p1[props[0]], |
| 1322 | y = p2[props[1]] - p1[props[1]]; |
| 1323 | return Math.atan2(y, x) * 180 / Math.PI; |
| 1324 | } |
| 1325 | |
| 1326 | /** |
| 1327 | * calculate the rotation degrees between two pointersets |
| 1328 | * @param {Array} start array of pointers |
| 1329 | * @param {Array} end array of pointers |
| 1330 | * @return {Number} rotation |
| 1331 | */ |
| 1332 | function getRotation(start, end) { |
| 1333 | return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); |
| 1334 | } |
| 1335 | |
| 1336 | /** |
| 1337 | * calculate the scale factor between two pointersets |
| 1338 | * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out |
| 1339 | * @param {Array} start array of pointers |
| 1340 | * @param {Array} end array of pointers |
| 1341 | * @return {Number} scale |
| 1342 | */ |
| 1343 | function getScale(start, end) { |
| 1344 | return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); |
| 1345 | } |
| 1346 | |
| 1347 | var MOUSE_INPUT_MAP = { |
| 1348 | mousedown: INPUT_START, |
| 1349 | mousemove: INPUT_MOVE, |
| 1350 | mouseup: INPUT_END |
| 1351 | }; |
| 1352 | |
| 1353 | var MOUSE_ELEMENT_EVENTS = 'mousedown'; |
| 1354 | var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; |
| 1355 | |
| 1356 | /** |
| 1357 | * Mouse events input |
| 1358 | * @constructor |
| 1359 | * @extends Input |
| 1360 | */ |
| 1361 | function MouseInput() { |
| 1362 | this.evEl = MOUSE_ELEMENT_EVENTS; |
| 1363 | this.evWin = MOUSE_WINDOW_EVENTS; |
| 1364 | |
| 1365 | this.pressed = false; // mousedown state |
| 1366 | |
| 1367 | Input.apply(this, arguments); |
| 1368 | } |
| 1369 | |
| 1370 | inherit(MouseInput, Input, { |
| 1371 | /** |
| 1372 | * handle mouse events |
| 1373 | * @param {Object} ev |
| 1374 | */ |
| 1375 | handler: function MEhandler(ev) { |
| 1376 | var eventType = MOUSE_INPUT_MAP[ev.type]; |
| 1377 | |
| 1378 | // on start we want to have the left mouse button down |
| 1379 | if (eventType & INPUT_START && ev.button === 0) { |
| 1380 | this.pressed = true; |
| 1381 | } |
| 1382 | |
| 1383 | if (eventType & INPUT_MOVE && ev.which !== 1) { |
| 1384 | eventType = INPUT_END; |
| 1385 | } |
| 1386 | |
| 1387 | // mouse must be down |
| 1388 | if (!this.pressed) { |
| 1389 | return; |
| 1390 | } |
| 1391 | |
| 1392 | if (eventType & INPUT_END) { |
| 1393 | this.pressed = false; |
| 1394 | } |
| 1395 | |
| 1396 | this.callback(this.manager, eventType, { |
| 1397 | pointers: [ev], |
| 1398 | changedPointers: [ev], |
| 1399 | pointerType: INPUT_TYPE_MOUSE, |
| 1400 | srcEvent: ev |
| 1401 | }); |
| 1402 | } |
| 1403 | }); |
| 1404 | |
| 1405 | var POINTER_INPUT_MAP = { |
| 1406 | pointerdown: INPUT_START, |
| 1407 | pointermove: INPUT_MOVE, |
| 1408 | pointerup: INPUT_END, |
| 1409 | pointercancel: INPUT_CANCEL, |
| 1410 | pointerout: INPUT_CANCEL |
| 1411 | }; |
| 1412 | |
| 1413 | // in IE10 the pointer types is defined as an enum |
| 1414 | var IE10_POINTER_TYPE_ENUM = { |
| 1415 | 2: INPUT_TYPE_TOUCH, |
| 1416 | 3: INPUT_TYPE_PEN, |
| 1417 | 4: INPUT_TYPE_MOUSE, |
| 1418 | 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 |
| 1419 | }; |
| 1420 | |
| 1421 | var POINTER_ELEMENT_EVENTS = 'pointerdown'; |
| 1422 | var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; |
| 1423 | |
| 1424 | // IE10 has prefixed support, and case-sensitive |
| 1425 | if (window.MSPointerEvent && !window.PointerEvent) { |
| 1426 | POINTER_ELEMENT_EVENTS = 'MSPointerDown'; |
| 1427 | POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; |
| 1428 | } |
| 1429 | |
| 1430 | /** |
| 1431 | * Pointer events input |
| 1432 | * @constructor |
| 1433 | * @extends Input |
| 1434 | */ |
| 1435 | function PointerEventInput() { |
| 1436 | this.evEl = POINTER_ELEMENT_EVENTS; |
| 1437 | this.evWin = POINTER_WINDOW_EVENTS; |
| 1438 | |
| 1439 | Input.apply(this, arguments); |
| 1440 | |
| 1441 | this.store = (this.manager.session.pointerEvents = []); |
| 1442 | } |
| 1443 | |
| 1444 | inherit(PointerEventInput, Input, { |
| 1445 | /** |
| 1446 | * handle mouse events |
| 1447 | * @param {Object} ev |
| 1448 | */ |
| 1449 | handler: function PEhandler(ev) { |
| 1450 | var store = this.store; |
| 1451 | var removePointer = false; |
| 1452 | |
| 1453 | var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); |
| 1454 | var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; |
| 1455 | var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; |
| 1456 | |
| 1457 | var isTouch = (pointerType == INPUT_TYPE_TOUCH); |
| 1458 | |
| 1459 | // get index of the event in the store |
| 1460 | var storeIndex = inArray(store, ev.pointerId, 'pointerId'); |
| 1461 | |
| 1462 | // start and mouse must be down |
| 1463 | if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { |
| 1464 | if (storeIndex < 0) { |
| 1465 | store.push(ev); |
| 1466 | storeIndex = store.length - 1; |
| 1467 | } |
| 1468 | } else if (eventType & (INPUT_END | INPUT_CANCEL)) { |
| 1469 | removePointer = true; |
| 1470 | } |
| 1471 | |
| 1472 | // it not found, so the pointer hasn't been down (so it's probably a hover) |
| 1473 | if (storeIndex < 0) { |
| 1474 | return; |
| 1475 | } |
| 1476 | |
| 1477 | // update the event in the store |
| 1478 | store[storeIndex] = ev; |
| 1479 | |
| 1480 | this.callback(this.manager, eventType, { |
| 1481 | pointers: store, |
| 1482 | changedPointers: [ev], |
| 1483 | pointerType: pointerType, |
| 1484 | srcEvent: ev |
| 1485 | }); |
| 1486 | |
| 1487 | if (removePointer) { |
| 1488 | // remove from the store |
| 1489 | store.splice(storeIndex, 1); |
| 1490 | } |
| 1491 | } |
| 1492 | }); |
| 1493 | |
| 1494 | var SINGLE_TOUCH_INPUT_MAP = { |
| 1495 | touchstart: INPUT_START, |
| 1496 | touchmove: INPUT_MOVE, |
| 1497 | touchend: INPUT_END, |
| 1498 | touchcancel: INPUT_CANCEL |
| 1499 | }; |
| 1500 | |
| 1501 | var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; |
| 1502 | var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; |
| 1503 | |
| 1504 | /** |
| 1505 | * Touch events input |
| 1506 | * @constructor |
| 1507 | * @extends Input |
| 1508 | */ |
| 1509 | function SingleTouchInput() { |
| 1510 | this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; |
| 1511 | this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; |
| 1512 | this.started = false; |
| 1513 | |
| 1514 | Input.apply(this, arguments); |
| 1515 | } |
| 1516 | |
| 1517 | inherit(SingleTouchInput, Input, { |
| 1518 | handler: function TEhandler(ev) { |
| 1519 | var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; |
| 1520 | |
| 1521 | // should we handle the touch events? |
| 1522 | if (type === INPUT_START) { |
| 1523 | this.started = true; |
| 1524 | } |
| 1525 | |
| 1526 | if (!this.started) { |
| 1527 | return; |
| 1528 | } |
| 1529 | |
| 1530 | var touches = normalizeSingleTouches.call(this, ev, type); |
| 1531 | |
| 1532 | // when done, reset the started state |
| 1533 | if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { |
| 1534 | this.started = false; |
| 1535 | } |
| 1536 | |
| 1537 | this.callback(this.manager, type, { |
| 1538 | pointers: touches[0], |
| 1539 | changedPointers: touches[1], |
| 1540 | pointerType: INPUT_TYPE_TOUCH, |
| 1541 | srcEvent: ev |
| 1542 | }); |
| 1543 | } |
| 1544 | }); |
| 1545 | |
| 1546 | /** |
| 1547 | * @this {TouchInput} |
| 1548 | * @param {Object} ev |
| 1549 | * @param {Number} type flag |
| 1550 | * @returns {undefined|Array} [all, changed] |
| 1551 | */ |
| 1552 | function normalizeSingleTouches(ev, type) { |
| 1553 | var all = toArray(ev.touches); |
| 1554 | var changed = toArray(ev.changedTouches); |
| 1555 | |
| 1556 | if (type & (INPUT_END | INPUT_CANCEL)) { |
| 1557 | all = uniqueArray(all.concat(changed), 'identifier', true); |
| 1558 | } |
| 1559 | |
| 1560 | return [all, changed]; |
| 1561 | } |
| 1562 | |
| 1563 | var TOUCH_INPUT_MAP = { |
| 1564 | touchstart: INPUT_START, |
| 1565 | touchmove: INPUT_MOVE, |
| 1566 | touchend: INPUT_END, |
| 1567 | touchcancel: INPUT_CANCEL |
| 1568 | }; |
| 1569 | |
| 1570 | var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; |
| 1571 | |
| 1572 | /** |
| 1573 | * Multi-user touch events input |
| 1574 | * @constructor |
| 1575 | * @extends Input |
| 1576 | */ |
| 1577 | function TouchInput() { |
| 1578 | this.evTarget = TOUCH_TARGET_EVENTS; |
| 1579 | this.targetIds = {}; |
| 1580 | |
| 1581 | Input.apply(this, arguments); |
| 1582 | } |
| 1583 | |
| 1584 | inherit(TouchInput, Input, { |
| 1585 | handler: function MTEhandler(ev) { |
| 1586 | var type = TOUCH_INPUT_MAP[ev.type]; |
| 1587 | var touches = getTouches.call(this, ev, type); |
| 1588 | if (!touches) { |
| 1589 | return; |
| 1590 | } |
| 1591 | |
| 1592 | this.callback(this.manager, type, { |
| 1593 | pointers: touches[0], |
| 1594 | changedPointers: touches[1], |
| 1595 | pointerType: INPUT_TYPE_TOUCH, |
| 1596 | srcEvent: ev |
| 1597 | }); |
| 1598 | } |
| 1599 | }); |
| 1600 | |
| 1601 | /** |
| 1602 | * @this {TouchInput} |
| 1603 | * @param {Object} ev |
| 1604 | * @param {Number} type flag |
| 1605 | * @returns {undefined|Array} [all, changed] |
| 1606 | */ |
| 1607 | function getTouches(ev, type) { |
| 1608 | var allTouches = toArray(ev.touches); |
| 1609 | var targetIds = this.targetIds; |
| 1610 | |
| 1611 | // when there is only one touch, the process can be simplified |
| 1612 | if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { |
| 1613 | targetIds[allTouches[0].identifier] = true; |
| 1614 | return [allTouches, allTouches]; |
| 1615 | } |
| 1616 | |
| 1617 | var i, |
| 1618 | targetTouches, |
| 1619 | changedTouches = toArray(ev.changedTouches), |
| 1620 | changedTargetTouches = [], |
| 1621 | target = this.target; |
| 1622 | |
| 1623 | // get target touches from touches |
| 1624 | targetTouches = allTouches.filter(function(touch) { |
| 1625 | return hasParent(touch.target, target); |
| 1626 | }); |
| 1627 | |
| 1628 | // collect touches |
| 1629 | if (type === INPUT_START) { |
| 1630 | i = 0; |
| 1631 | while (i < targetTouches.length) { |
| 1632 | targetIds[targetTouches[i].identifier] = true; |
| 1633 | i++; |
| 1634 | } |
| 1635 | } |
| 1636 | |
| 1637 | // filter changed touches to only contain touches that exist in the collected target ids |
| 1638 | i = 0; |
| 1639 | while (i < changedTouches.length) { |
| 1640 | if (targetIds[changedTouches[i].identifier]) { |
| 1641 | changedTargetTouches.push(changedTouches[i]); |
| 1642 | } |
| 1643 | |
| 1644 | // cleanup removed touches |
| 1645 | if (type & (INPUT_END | INPUT_CANCEL)) { |
| 1646 | delete targetIds[changedTouches[i].identifier]; |
| 1647 | } |
| 1648 | i++; |
| 1649 | } |
| 1650 | |
| 1651 | if (!changedTargetTouches.length) { |
| 1652 | return; |
| 1653 | } |
| 1654 | |
| 1655 | return [ |
| 1656 | // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' |
| 1657 | uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), |
| 1658 | changedTargetTouches |
| 1659 | ]; |
| 1660 | } |
| 1661 | |
| 1662 | /** |
| 1663 | * Combined touch and mouse input |
| 1664 | * |
| 1665 | * Touch has a higher priority then mouse, and while touching no mouse events are allowed. |
| 1666 | * This because touch devices also emit mouse events while doing a touch. |
| 1667 | * |
| 1668 | * @constructor |
| 1669 | * @extends Input |
| 1670 | */ |
| 1671 | |
| 1672 | var DEDUP_TIMEOUT = 2500; |
| 1673 | var DEDUP_DISTANCE = 25; |
| 1674 | |
| 1675 | function TouchMouseInput() { |
| 1676 | Input.apply(this, arguments); |
| 1677 | |
| 1678 | var handler = bindFn(this.handler, this); |
| 1679 | this.touch = new TouchInput(this.manager, handler); |
| 1680 | this.mouse = new MouseInput(this.manager, handler); |
| 1681 | |
| 1682 | this.primaryTouch = null; |
| 1683 | this.lastTouches = []; |
| 1684 | } |
| 1685 | |
| 1686 | inherit(TouchMouseInput, Input, { |
| 1687 | /** |
| 1688 | * handle mouse and touch events |
| 1689 | * @param {Hammer} manager |
| 1690 | * @param {String} inputEvent |
| 1691 | * @param {Object} inputData |
| 1692 | */ |
| 1693 | handler: function TMEhandler(manager, inputEvent, inputData) { |
| 1694 | var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), |
| 1695 | isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); |
| 1696 | |
| 1697 | if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { |
| 1698 | return; |
| 1699 | } |
| 1700 | |
| 1701 | // when we're in a touch event, record touches to de-dupe synthetic mouse event |
| 1702 | if (isTouch) { |
| 1703 | recordTouches.call(this, inputEvent, inputData); |
| 1704 | } else if (isMouse && isSyntheticEvent.call(this, inputData)) { |
| 1705 | return; |
| 1706 | } |
| 1707 | |
| 1708 | this.callback(manager, inputEvent, inputData); |
| 1709 | }, |
| 1710 | |
| 1711 | /** |
| 1712 | * remove the event listeners |
| 1713 | */ |
| 1714 | destroy: function destroy() { |
| 1715 | this.touch.destroy(); |
| 1716 | this.mouse.destroy(); |
| 1717 | } |
| 1718 | }); |
| 1719 | |
| 1720 | function recordTouches(eventType, eventData) { |
| 1721 | if (eventType & INPUT_START) { |
| 1722 | this.primaryTouch = eventData.changedPointers[0].identifier; |
| 1723 | setLastTouch.call(this, eventData); |
| 1724 | } else if (eventType & (INPUT_END | INPUT_CANCEL)) { |
| 1725 | setLastTouch.call(this, eventData); |
| 1726 | } |
| 1727 | } |
| 1728 | |
| 1729 | function setLastTouch(eventData) { |
| 1730 | var touch = eventData.changedPointers[0]; |
| 1731 | |
| 1732 | if (touch.identifier === this.primaryTouch) { |
| 1733 | var lastTouch = {x: touch.clientX, y: touch.clientY}; |
| 1734 | this.lastTouches.push(lastTouch); |
| 1735 | var lts = this.lastTouches; |
| 1736 | var removeLastTouch = function() { |
| 1737 | var i = lts.indexOf(lastTouch); |
| 1738 | if (i > -1) { |
| 1739 | lts.splice(i, 1); |
| 1740 | } |
| 1741 | }; |
| 1742 | setTimeout(removeLastTouch, DEDUP_TIMEOUT); |
| 1743 | } |
| 1744 | } |
| 1745 | |
| 1746 | function isSyntheticEvent(eventData) { |
| 1747 | var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; |
| 1748 | for (var i = 0; i < this.lastTouches.length; i++) { |
| 1749 | var t = this.lastTouches[i]; |
| 1750 | var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); |
| 1751 | if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { |
| 1752 | return true; |
| 1753 | } |
| 1754 | } |
| 1755 | return false; |
| 1756 | } |
| 1757 | |
| 1758 | var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); |
| 1759 | var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; |
| 1760 | |
| 1761 | // magical touchAction value |
| 1762 | var TOUCH_ACTION_COMPUTE = 'compute'; |
| 1763 | var TOUCH_ACTION_AUTO = 'auto'; |
| 1764 | var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented |
| 1765 | var TOUCH_ACTION_NONE = 'none'; |
| 1766 | var TOUCH_ACTION_PAN_X = 'pan-x'; |
| 1767 | var TOUCH_ACTION_PAN_Y = 'pan-y'; |
| 1768 | var TOUCH_ACTION_MAP = getTouchActionProps(); |
| 1769 | |
| 1770 | /** |
| 1771 | * Touch Action |
| 1772 | * sets the touchAction property or uses the js alternative |
| 1773 | * @param {Manager} manager |
| 1774 | * @param {String} value |
| 1775 | * @constructor |
| 1776 | */ |
| 1777 | function TouchAction(manager, value) { |
| 1778 | this.manager = manager; |
| 1779 | this.set(value); |
| 1780 | } |
| 1781 | |
| 1782 | TouchAction.prototype = { |
| 1783 | /** |
| 1784 | * set the touchAction value on the element or enable the polyfill |
| 1785 | * @param {String} value |
| 1786 | */ |
| 1787 | set: function(value) { |
| 1788 | // find out the touch-action by the event handlers |
| 1789 | if (value == TOUCH_ACTION_COMPUTE) { |
| 1790 | value = this.compute(); |
| 1791 | } |
| 1792 | |
| 1793 | if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { |
| 1794 | this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; |
| 1795 | } |
| 1796 | this.actions = value.toLowerCase().trim(); |
| 1797 | }, |
| 1798 | |
| 1799 | /** |
| 1800 | * just re-set the touchAction value |
| 1801 | */ |
| 1802 | update: function() { |
| 1803 | this.set(this.manager.options.touchAction); |
| 1804 | }, |
| 1805 | |
| 1806 | /** |
| 1807 | * compute the value for the touchAction property based on the recognizer's settings |
| 1808 | * @returns {String} value |
| 1809 | */ |
| 1810 | compute: function() { |
| 1811 | var actions = []; |
| 1812 | each(this.manager.recognizers, function(recognizer) { |
| 1813 | if (boolOrFn(recognizer.options.enable, [recognizer])) { |
| 1814 | actions = actions.concat(recognizer.getTouchAction()); |
| 1815 | } |
| 1816 | }); |
| 1817 | return cleanTouchActions(actions.join(' ')); |
| 1818 | }, |
| 1819 | |
| 1820 | /** |
| 1821 | * this method is called on each input cycle and provides the preventing of the browser behavior |
| 1822 | * @param {Object} input |
| 1823 | */ |
| 1824 | preventDefaults: function(input) { |
| 1825 | var srcEvent = input.srcEvent; |
| 1826 | var direction = input.offsetDirection; |
| 1827 | |
| 1828 | // if the touch action did prevented once this session |
| 1829 | if (this.manager.session.prevented) { |
| 1830 | srcEvent.preventDefault(); |
| 1831 | return; |
| 1832 | } |
| 1833 | |
| 1834 | var actions = this.actions; |
| 1835 | var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; |
| 1836 | var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; |
| 1837 | var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; |
| 1838 | |
| 1839 | if (hasNone) { |
| 1840 | //do not prevent defaults if this is a tap gesture |
| 1841 | |
| 1842 | var isTapPointer = input.pointers.length === 1; |
| 1843 | var isTapMovement = input.distance < 2; |
| 1844 | var isTapTouchTime = input.deltaTime < 250; |
| 1845 | |
| 1846 | if (isTapPointer && isTapMovement && isTapTouchTime) { |
| 1847 | return; |
| 1848 | } |
| 1849 | } |
| 1850 | |
| 1851 | if (hasPanX && hasPanY) { |
| 1852 | // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent |
| 1853 | return; |
| 1854 | } |
| 1855 | |
| 1856 | if (hasNone || |
| 1857 | (hasPanY && direction & DIRECTION_HORIZONTAL) || |
| 1858 | (hasPanX && direction & DIRECTION_VERTICAL)) { |
| 1859 | return this.preventSrc(srcEvent); |
| 1860 | } |
| 1861 | }, |
| 1862 | |
| 1863 | /** |
| 1864 | * call preventDefault to prevent the browser's default behavior (scrolling in most cases) |
| 1865 | * @param {Object} srcEvent |
| 1866 | */ |
| 1867 | preventSrc: function(srcEvent) { |
| 1868 | this.manager.session.prevented = true; |
| 1869 | srcEvent.preventDefault(); |
| 1870 | } |
| 1871 | }; |
| 1872 | |
| 1873 | /** |
| 1874 | * when the touchActions are collected they are not a valid value, so we need to clean things up. * |
| 1875 | * @param {String} actions |
| 1876 | * @returns {*} |
| 1877 | */ |
| 1878 | function cleanTouchActions(actions) { |
| 1879 | // none |
| 1880 | if (inStr(actions, TOUCH_ACTION_NONE)) { |
| 1881 | return TOUCH_ACTION_NONE; |
| 1882 | } |
| 1883 | |
| 1884 | var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); |
| 1885 | var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); |
| 1886 | |
| 1887 | // if both pan-x and pan-y are set (different recognizers |
| 1888 | // for different directions, e.g. horizontal pan but vertical swipe?) |
| 1889 | // we need none (as otherwise with pan-x pan-y combined none of these |
| 1890 | // recognizers will work, since the browser would handle all panning |
| 1891 | if (hasPanX && hasPanY) { |
| 1892 | return TOUCH_ACTION_NONE; |
| 1893 | } |
| 1894 | |
| 1895 | // pan-x OR pan-y |
| 1896 | if (hasPanX || hasPanY) { |
| 1897 | return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; |
| 1898 | } |
| 1899 | |
| 1900 | // manipulation |
| 1901 | if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { |
| 1902 | return TOUCH_ACTION_MANIPULATION; |
| 1903 | } |
| 1904 | |
| 1905 | return TOUCH_ACTION_AUTO; |
| 1906 | } |
| 1907 | |
| 1908 | function getTouchActionProps() { |
| 1909 | if (!NATIVE_TOUCH_ACTION) { |
| 1910 | return false; |
| 1911 | } |
| 1912 | var touchMap = {}; |
| 1913 | var cssSupports = window.CSS && window.CSS.supports; |
| 1914 | ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { |
| 1915 | |
| 1916 | // If css.supports is not supported but there is native touch-action assume it supports |
| 1917 | // all values. This is the case for IE 10 and 11. |
| 1918 | touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; |
| 1919 | }); |
| 1920 | return touchMap; |
| 1921 | } |
| 1922 | |
| 1923 | /** |
| 1924 | * Recognizer flow explained; * |
| 1925 | * All recognizers have the initial state of POSSIBLE when a input session starts. |
| 1926 | * The definition of a input session is from the first input until the last input, with all it's movement in it. * |
| 1927 | * Example session for mouse-input: mousedown -> mousemove -> mouseup |
| 1928 | * |
| 1929 | * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed |
| 1930 | * which determines with state it should be. |
| 1931 | * |
| 1932 | * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to |
| 1933 | * POSSIBLE to give it another change on the next cycle. |
| 1934 | * |
| 1935 | * Possible |
| 1936 | * | |
| 1937 | * +-----+---------------+ |
| 1938 | * | | |
| 1939 | * +-----+-----+ | |
| 1940 | * | | | |
| 1941 | * Failed Cancelled | |
| 1942 | * +-------+------+ |
| 1943 | * | | |
| 1944 | * Recognized Began |
| 1945 | * | |
| 1946 | * Changed |
| 1947 | * | |
| 1948 | * Ended/Recognized |
| 1949 | */ |
| 1950 | var STATE_POSSIBLE = 1; |
| 1951 | var STATE_BEGAN = 2; |
| 1952 | var STATE_CHANGED = 4; |
| 1953 | var STATE_ENDED = 8; |
| 1954 | var STATE_RECOGNIZED = STATE_ENDED; |
| 1955 | var STATE_CANCELLED = 16; |
| 1956 | var STATE_FAILED = 32; |
| 1957 | |
| 1958 | /** |
| 1959 | * Recognizer |
| 1960 | * Every recognizer needs to extend from this class. |
| 1961 | * @constructor |
| 1962 | * @param {Object} options |
| 1963 | */ |
| 1964 | function Recognizer(options) { |
| 1965 | this.options = assign({}, this.defaults, options || {}); |
| 1966 | |
| 1967 | this.id = uniqueId(); |
| 1968 | |
| 1969 | this.manager = null; |
| 1970 | |
| 1971 | // default is enable true |
| 1972 | this.options.enable = ifUndefined(this.options.enable, true); |
| 1973 | |
| 1974 | this.state = STATE_POSSIBLE; |
| 1975 | |
| 1976 | this.simultaneous = {}; |
| 1977 | this.requireFail = []; |
| 1978 | } |
| 1979 | |
| 1980 | Recognizer.prototype = { |
| 1981 | /** |
| 1982 | * @virtual |
| 1983 | * @type {Object} |
| 1984 | */ |
| 1985 | defaults: {}, |
| 1986 | |
| 1987 | /** |
| 1988 | * set options |
| 1989 | * @param {Object} options |
| 1990 | * @return {Recognizer} |
| 1991 | */ |
| 1992 | set: function(options) { |
| 1993 | assign(this.options, options); |
| 1994 | |
| 1995 | // also update the touchAction, in case something changed about the directions/enabled state |
| 1996 | this.manager && this.manager.touchAction.update(); |
| 1997 | return this; |
| 1998 | }, |
| 1999 | |
| 2000 | /** |
| 2001 | * recognize simultaneous with an other recognizer. |
| 2002 | * @param {Recognizer} otherRecognizer |
| 2003 | * @returns {Recognizer} this |
| 2004 | */ |
| 2005 | recognizeWith: function(otherRecognizer) { |
| 2006 | if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { |
| 2007 | return this; |
| 2008 | } |
| 2009 | |
| 2010 | var simultaneous = this.simultaneous; |
| 2011 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); |
| 2012 | if (!simultaneous[otherRecognizer.id]) { |
| 2013 | simultaneous[otherRecognizer.id] = otherRecognizer; |
| 2014 | otherRecognizer.recognizeWith(this); |
| 2015 | } |
| 2016 | return this; |
| 2017 | }, |
| 2018 | |
| 2019 | /** |
| 2020 | * drop the simultaneous link. it doesnt remove the link on the other recognizer. |
| 2021 | * @param {Recognizer} otherRecognizer |
| 2022 | * @returns {Recognizer} this |
| 2023 | */ |
| 2024 | dropRecognizeWith: function(otherRecognizer) { |
| 2025 | if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { |
| 2026 | return this; |
| 2027 | } |
| 2028 | |
| 2029 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); |
| 2030 | delete this.simultaneous[otherRecognizer.id]; |
| 2031 | return this; |
| 2032 | }, |
| 2033 | |
| 2034 | /** |
| 2035 | * recognizer can only run when an other is failing |
| 2036 | * @param {Recognizer} otherRecognizer |
| 2037 | * @returns {Recognizer} this |
| 2038 | */ |
| 2039 | requireFailure: function(otherRecognizer) { |
| 2040 | if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { |
| 2041 | return this; |
| 2042 | } |
| 2043 | |
| 2044 | var requireFail = this.requireFail; |
| 2045 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); |
| 2046 | if (inArray(requireFail, otherRecognizer) === -1) { |
| 2047 | requireFail.push(otherRecognizer); |
| 2048 | otherRecognizer.requireFailure(this); |
| 2049 | } |
| 2050 | return this; |
| 2051 | }, |
| 2052 | |
| 2053 | /** |
| 2054 | * drop the requireFailure link. it does not remove the link on the other recognizer. |
| 2055 | * @param {Recognizer} otherRecognizer |
| 2056 | * @returns {Recognizer} this |
| 2057 | */ |
| 2058 | dropRequireFailure: function(otherRecognizer) { |
| 2059 | if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { |
| 2060 | return this; |
| 2061 | } |
| 2062 | |
| 2063 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); |
| 2064 | var index = inArray(this.requireFail, otherRecognizer); |
| 2065 | if (index > -1) { |
| 2066 | this.requireFail.splice(index, 1); |
| 2067 | } |
| 2068 | return this; |
| 2069 | }, |
| 2070 | |
| 2071 | /** |
| 2072 | * has require failures boolean |
| 2073 | * @returns {boolean} |
| 2074 | */ |
| 2075 | hasRequireFailures: function() { |
| 2076 | return this.requireFail.length > 0; |
| 2077 | }, |
| 2078 | |
| 2079 | /** |
| 2080 | * if the recognizer can recognize simultaneous with an other recognizer |
| 2081 | * @param {Recognizer} otherRecognizer |
| 2082 | * @returns {Boolean} |
| 2083 | */ |
| 2084 | canRecognizeWith: function(otherRecognizer) { |
| 2085 | return !!this.simultaneous[otherRecognizer.id]; |
| 2086 | }, |
| 2087 | |
| 2088 | /** |
| 2089 | * You should use `tryEmit` instead of `emit` directly to check |
| 2090 | * that all the needed recognizers has failed before emitting. |
| 2091 | * @param {Object} input |
| 2092 | */ |
| 2093 | emit: function(input) { |
| 2094 | var self = this; |
| 2095 | var state = this.state; |
| 2096 | |
| 2097 | function emit(event) { |
| 2098 | self.manager.emit(event, input); |
| 2099 | } |
| 2100 | |
| 2101 | // 'panstart' and 'panmove' |
| 2102 | if (state < STATE_ENDED) { |
| 2103 | emit(self.options.event + stateStr(state)); |
| 2104 | } |
| 2105 | |
| 2106 | emit(self.options.event); // simple 'eventName' events |
| 2107 | |
| 2108 | if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) |
| 2109 | emit(input.additionalEvent); |
| 2110 | } |
| 2111 | |
| 2112 | // panend and pancancel |
| 2113 | if (state >= STATE_ENDED) { |
| 2114 | emit(self.options.event + stateStr(state)); |
| 2115 | } |
| 2116 | }, |
| 2117 | |
| 2118 | /** |
| 2119 | * Check that all the require failure recognizers has failed, |
| 2120 | * if true, it emits a gesture event, |
| 2121 | * otherwise, setup the state to FAILED. |
| 2122 | * @param {Object} input |
| 2123 | */ |
| 2124 | tryEmit: function(input) { |
| 2125 | if (this.canEmit()) { |
| 2126 | return this.emit(input); |
| 2127 | } |
| 2128 | // it's failing anyway |
| 2129 | this.state = STATE_FAILED; |
| 2130 | }, |
| 2131 | |
| 2132 | /** |
| 2133 | * can we emit? |
| 2134 | * @returns {boolean} |
| 2135 | */ |
| 2136 | canEmit: function() { |
| 2137 | var i = 0; |
| 2138 | while (i < this.requireFail.length) { |
| 2139 | if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { |
| 2140 | return false; |
| 2141 | } |
| 2142 | i++; |
| 2143 | } |
| 2144 | return true; |
| 2145 | }, |
| 2146 | |
| 2147 | /** |
| 2148 | * update the recognizer |
| 2149 | * @param {Object} inputData |
| 2150 | */ |
| 2151 | recognize: function(inputData) { |
| 2152 | // make a new copy of the inputData |
| 2153 | // so we can change the inputData without messing up the other recognizers |
| 2154 | var inputDataClone = assign({}, inputData); |
| 2155 | |
| 2156 | // is is enabled and allow recognizing? |
| 2157 | if (!boolOrFn(this.options.enable, [this, inputDataClone])) { |
| 2158 | this.reset(); |
| 2159 | this.state = STATE_FAILED; |
| 2160 | return; |
| 2161 | } |
| 2162 | |
| 2163 | // reset when we've reached the end |
| 2164 | if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { |
| 2165 | this.state = STATE_POSSIBLE; |
| 2166 | } |
| 2167 | |
| 2168 | this.state = this.process(inputDataClone); |
| 2169 | |
| 2170 | // the recognizer has recognized a gesture |
| 2171 | // so trigger an event |
| 2172 | if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { |
| 2173 | this.tryEmit(inputDataClone); |
| 2174 | } |
| 2175 | }, |
| 2176 | |
| 2177 | /** |
| 2178 | * return the state of the recognizer |
| 2179 | * the actual recognizing happens in this method |
| 2180 | * @virtual |
| 2181 | * @param {Object} inputData |
| 2182 | * @returns {Const} STATE |
| 2183 | */ |
| 2184 | process: function(inputData) { }, // jshint ignore:line |
| 2185 | |
| 2186 | /** |
| 2187 | * return the preferred touch-action |
| 2188 | * @virtual |
| 2189 | * @returns {Array} |
| 2190 | */ |
| 2191 | getTouchAction: function() { }, |
| 2192 | |
| 2193 | /** |
| 2194 | * called when the gesture isn't allowed to recognize |
| 2195 | * like when another is being recognized or it is disabled |
| 2196 | * @virtual |
| 2197 | */ |
| 2198 | reset: function() { } |
| 2199 | }; |
| 2200 | |
| 2201 | /** |
| 2202 | * get a usable string, used as event postfix |
| 2203 | * @param {Const} state |
| 2204 | * @returns {String} state |
| 2205 | */ |
| 2206 | function stateStr(state) { |
| 2207 | if (state & STATE_CANCELLED) { |
| 2208 | return 'cancel'; |
| 2209 | } else if (state & STATE_ENDED) { |
| 2210 | return 'end'; |
| 2211 | } else if (state & STATE_CHANGED) { |
| 2212 | return 'move'; |
| 2213 | } else if (state & STATE_BEGAN) { |
| 2214 | return 'start'; |
| 2215 | } |
| 2216 | return ''; |
| 2217 | } |
| 2218 | |
| 2219 | /** |
| 2220 | * direction cons to string |
| 2221 | * @param {Const} direction |
| 2222 | * @returns {String} |
| 2223 | */ |
| 2224 | function directionStr(direction) { |
| 2225 | if (direction == DIRECTION_DOWN) { |
| 2226 | return 'down'; |
| 2227 | } else if (direction == DIRECTION_UP) { |
| 2228 | return 'up'; |
| 2229 | } else if (direction == DIRECTION_LEFT) { |
| 2230 | return 'left'; |
| 2231 | } else if (direction == DIRECTION_RIGHT) { |
| 2232 | return 'right'; |
| 2233 | } |
| 2234 | return ''; |
| 2235 | } |
| 2236 | |
| 2237 | /** |
| 2238 | * get a recognizer by name if it is bound to a manager |
| 2239 | * @param {Recognizer|String} otherRecognizer |
| 2240 | * @param {Recognizer} recognizer |
| 2241 | * @returns {Recognizer} |
| 2242 | */ |
| 2243 | function getRecognizerByNameIfManager(otherRecognizer, recognizer) { |
| 2244 | var manager = recognizer.manager; |
| 2245 | if (manager) { |
| 2246 | return manager.get(otherRecognizer); |
| 2247 | } |
| 2248 | return otherRecognizer; |
| 2249 | } |
| 2250 | |
| 2251 | /** |
| 2252 | * This recognizer is just used as a base for the simple attribute recognizers. |
| 2253 | * @constructor |
| 2254 | * @extends Recognizer |
| 2255 | */ |
| 2256 | function AttrRecognizer() { |
| 2257 | Recognizer.apply(this, arguments); |
| 2258 | } |
| 2259 | |
| 2260 | inherit(AttrRecognizer, Recognizer, { |
| 2261 | /** |
| 2262 | * @namespace |
| 2263 | * @memberof AttrRecognizer |
| 2264 | */ |
| 2265 | defaults: { |
| 2266 | /** |
| 2267 | * @type {Number} |
| 2268 | * @default 1 |
| 2269 | */ |
| 2270 | pointers: 1 |
| 2271 | }, |
| 2272 | |
| 2273 | /** |
| 2274 | * Used to check if it the recognizer receives valid input, like input.distance > 10. |
| 2275 | * @memberof AttrRecognizer |
| 2276 | * @param {Object} input |
| 2277 | * @returns {Boolean} recognized |
| 2278 | */ |
| 2279 | attrTest: function(input) { |
| 2280 | var optionPointers = this.options.pointers; |
| 2281 | return optionPointers === 0 || input.pointers.length === optionPointers; |
| 2282 | }, |
| 2283 | |
| 2284 | /** |
| 2285 | * Process the input and return the state for the recognizer |
| 2286 | * @memberof AttrRecognizer |
| 2287 | * @param {Object} input |
| 2288 | * @returns {*} State |
| 2289 | */ |
| 2290 | process: function(input) { |
| 2291 | var state = this.state; |
| 2292 | var eventType = input.eventType; |
| 2293 | |
| 2294 | var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); |
| 2295 | var isValid = this.attrTest(input); |
| 2296 | |
| 2297 | // on cancel input and we've recognized before, return STATE_CANCELLED |
| 2298 | if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { |
| 2299 | return state | STATE_CANCELLED; |
| 2300 | } else if (isRecognized || isValid) { |
| 2301 | if (eventType & INPUT_END) { |
| 2302 | return state | STATE_ENDED; |
| 2303 | } else if (!(state & STATE_BEGAN)) { |
| 2304 | return STATE_BEGAN; |
| 2305 | } |
| 2306 | return state | STATE_CHANGED; |
| 2307 | } |
| 2308 | return STATE_FAILED; |
| 2309 | } |
| 2310 | }); |
| 2311 | |
| 2312 | /** |
| 2313 | * Pan |
| 2314 | * Recognized when the pointer is down and moved in the allowed direction. |
| 2315 | * @constructor |
| 2316 | * @extends AttrRecognizer |
| 2317 | */ |
| 2318 | function PanRecognizer() { |
| 2319 | AttrRecognizer.apply(this, arguments); |
| 2320 | |
| 2321 | this.pX = null; |
| 2322 | this.pY = null; |
| 2323 | } |
| 2324 | |
| 2325 | inherit(PanRecognizer, AttrRecognizer, { |
| 2326 | /** |
| 2327 | * @namespace |
| 2328 | * @memberof PanRecognizer |
| 2329 | */ |
| 2330 | defaults: { |
| 2331 | event: 'pan', |
| 2332 | threshold: 10, |
| 2333 | pointers: 1, |
| 2334 | direction: DIRECTION_ALL |
| 2335 | }, |
| 2336 | |
| 2337 | getTouchAction: function() { |
| 2338 | var direction = this.options.direction; |
| 2339 | var actions = []; |
| 2340 | if (direction & DIRECTION_HORIZONTAL) { |
| 2341 | actions.push(TOUCH_ACTION_PAN_Y); |
| 2342 | } |
| 2343 | if (direction & DIRECTION_VERTICAL) { |
| 2344 | actions.push(TOUCH_ACTION_PAN_X); |
| 2345 | } |
| 2346 | return actions; |
| 2347 | }, |
| 2348 | |
| 2349 | directionTest: function(input) { |
| 2350 | var options = this.options; |
| 2351 | var hasMoved = true; |
| 2352 | var distance = input.distance; |
| 2353 | var direction = input.direction; |
| 2354 | var x = input.deltaX; |
| 2355 | var y = input.deltaY; |
| 2356 | |
| 2357 | // lock to axis? |
| 2358 | if (!(direction & options.direction)) { |
| 2359 | if (options.direction & DIRECTION_HORIZONTAL) { |
| 2360 | direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; |
| 2361 | hasMoved = x != this.pX; |
| 2362 | distance = Math.abs(input.deltaX); |
| 2363 | } else { |
| 2364 | direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; |
| 2365 | hasMoved = y != this.pY; |
| 2366 | distance = Math.abs(input.deltaY); |
| 2367 | } |
| 2368 | } |
| 2369 | input.direction = direction; |
| 2370 | return hasMoved && distance > options.threshold && direction & options.direction; |
| 2371 | }, |
| 2372 | |
| 2373 | attrTest: function(input) { |
| 2374 | return AttrRecognizer.prototype.attrTest.call(this, input) && |
| 2375 | (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); |
| 2376 | }, |
| 2377 | |
| 2378 | emit: function(input) { |
| 2379 | |
| 2380 | this.pX = input.deltaX; |
| 2381 | this.pY = input.deltaY; |
| 2382 | |
| 2383 | var direction = directionStr(input.direction); |
| 2384 | |
| 2385 | if (direction) { |
| 2386 | input.additionalEvent = this.options.event + direction; |
| 2387 | } |
| 2388 | this._super.emit.call(this, input); |
| 2389 | } |
| 2390 | }); |
| 2391 | |
| 2392 | /** |
| 2393 | * Pinch |
| 2394 | * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). |
| 2395 | * @constructor |
| 2396 | * @extends AttrRecognizer |
| 2397 | */ |
| 2398 | function PinchRecognizer() { |
| 2399 | AttrRecognizer.apply(this, arguments); |
| 2400 | } |
| 2401 | |
| 2402 | inherit(PinchRecognizer, AttrRecognizer, { |
| 2403 | /** |
| 2404 | * @namespace |
| 2405 | * @memberof PinchRecognizer |
| 2406 | */ |
| 2407 | defaults: { |
| 2408 | event: 'pinch', |
| 2409 | threshold: 0, |
| 2410 | pointers: 2 |
| 2411 | }, |
| 2412 | |
| 2413 | getTouchAction: function() { |
| 2414 | return [TOUCH_ACTION_NONE]; |
| 2415 | }, |
| 2416 | |
| 2417 | attrTest: function(input) { |
| 2418 | return this._super.attrTest.call(this, input) && |
| 2419 | (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); |
| 2420 | }, |
| 2421 | |
| 2422 | emit: function(input) { |
| 2423 | if (input.scale !== 1) { |
| 2424 | var inOut = input.scale < 1 ? 'in' : 'out'; |
| 2425 | input.additionalEvent = this.options.event + inOut; |
| 2426 | } |
| 2427 | this._super.emit.call(this, input); |
| 2428 | } |
| 2429 | }); |
| 2430 | |
| 2431 | /** |
| 2432 | * Press |
| 2433 | * Recognized when the pointer is down for x ms without any movement. |
| 2434 | * @constructor |
| 2435 | * @extends Recognizer |
| 2436 | */ |
| 2437 | function PressRecognizer() { |
| 2438 | Recognizer.apply(this, arguments); |
| 2439 | |
| 2440 | this._timer = null; |
| 2441 | this._input = null; |
| 2442 | } |
| 2443 | |
| 2444 | inherit(PressRecognizer, Recognizer, { |
| 2445 | /** |
| 2446 | * @namespace |
| 2447 | * @memberof PressRecognizer |
| 2448 | */ |
| 2449 | defaults: { |
| 2450 | event: 'press', |
| 2451 | pointers: 1, |
| 2452 | time: 251, // minimal time of the pointer to be pressed |
| 2453 | threshold: 9 // a minimal movement is ok, but keep it low |
| 2454 | }, |
| 2455 | |
| 2456 | getTouchAction: function() { |
| 2457 | return [TOUCH_ACTION_AUTO]; |
| 2458 | }, |
| 2459 | |
| 2460 | process: function(input) { |
| 2461 | var options = this.options; |
| 2462 | var validPointers = input.pointers.length === options.pointers; |
| 2463 | var validMovement = input.distance < options.threshold; |
| 2464 | var validTime = input.deltaTime > options.time; |
| 2465 | |
| 2466 | this._input = input; |
| 2467 | |
| 2468 | // we only allow little movement |
| 2469 | // and we've reached an end event, so a tap is possible |
| 2470 | if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { |
| 2471 | this.reset(); |
| 2472 | } else if (input.eventType & INPUT_START) { |
| 2473 | this.reset(); |
| 2474 | this._timer = setTimeoutContext(function() { |
| 2475 | this.state = STATE_RECOGNIZED; |
| 2476 | this.tryEmit(); |
| 2477 | }, options.time, this); |
| 2478 | } else if (input.eventType & INPUT_END) { |
| 2479 | return STATE_RECOGNIZED; |
| 2480 | } |
| 2481 | return STATE_FAILED; |
| 2482 | }, |
| 2483 | |
| 2484 | reset: function() { |
| 2485 | clearTimeout(this._timer); |
| 2486 | }, |
| 2487 | |
| 2488 | emit: function(input) { |
| 2489 | if (this.state !== STATE_RECOGNIZED) { |
| 2490 | return; |
| 2491 | } |
| 2492 | |
| 2493 | if (input && (input.eventType & INPUT_END)) { |
| 2494 | this.manager.emit(this.options.event + 'up', input); |
| 2495 | } else { |
| 2496 | this._input.timeStamp = now(); |
| 2497 | this.manager.emit(this.options.event, this._input); |
| 2498 | } |
| 2499 | } |
| 2500 | }); |
| 2501 | |
| 2502 | /** |
| 2503 | * Rotate |
| 2504 | * Recognized when two or more pointer are moving in a circular motion. |
| 2505 | * @constructor |
| 2506 | * @extends AttrRecognizer |
| 2507 | */ |
| 2508 | function RotateRecognizer() { |
| 2509 | AttrRecognizer.apply(this, arguments); |
| 2510 | } |
| 2511 | |
| 2512 | inherit(RotateRecognizer, AttrRecognizer, { |
| 2513 | /** |
| 2514 | * @namespace |
| 2515 | * @memberof RotateRecognizer |
| 2516 | */ |
| 2517 | defaults: { |
| 2518 | event: 'rotate', |
| 2519 | threshold: 0, |
| 2520 | pointers: 2 |
| 2521 | }, |
| 2522 | |
| 2523 | getTouchAction: function() { |
| 2524 | return [TOUCH_ACTION_NONE]; |
| 2525 | }, |
| 2526 | |
| 2527 | attrTest: function(input) { |
| 2528 | return this._super.attrTest.call(this, input) && |
| 2529 | (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); |
| 2530 | } |
| 2531 | }); |
| 2532 | |
| 2533 | /** |
| 2534 | * Swipe |
| 2535 | * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. |
| 2536 | * @constructor |
| 2537 | * @extends AttrRecognizer |
| 2538 | */ |
| 2539 | function SwipeRecognizer() { |
| 2540 | AttrRecognizer.apply(this, arguments); |
| 2541 | } |
| 2542 | |
| 2543 | inherit(SwipeRecognizer, AttrRecognizer, { |
| 2544 | /** |
| 2545 | * @namespace |
| 2546 | * @memberof SwipeRecognizer |
| 2547 | */ |
| 2548 | defaults: { |
| 2549 | event: 'swipe', |
| 2550 | threshold: 10, |
| 2551 | velocity: 0.3, |
| 2552 | direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, |
| 2553 | pointers: 1 |
| 2554 | }, |
| 2555 | |
| 2556 | getTouchAction: function() { |
| 2557 | return PanRecognizer.prototype.getTouchAction.call(this); |
| 2558 | }, |
| 2559 | |
| 2560 | attrTest: function(input) { |
| 2561 | var direction = this.options.direction; |
| 2562 | var velocity; |
| 2563 | |
| 2564 | if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { |
| 2565 | velocity = input.overallVelocity; |
| 2566 | } else if (direction & DIRECTION_HORIZONTAL) { |
| 2567 | velocity = input.overallVelocityX; |
| 2568 | } else if (direction & DIRECTION_VERTICAL) { |
| 2569 | velocity = input.overallVelocityY; |
| 2570 | } |
| 2571 | |
| 2572 | return this._super.attrTest.call(this, input) && |
| 2573 | direction & input.offsetDirection && |
| 2574 | input.distance > this.options.threshold && |
| 2575 | input.maxPointers == this.options.pointers && |
| 2576 | abs(velocity) > this.options.velocity && input.eventType & INPUT_END; |
| 2577 | }, |
| 2578 | |
| 2579 | emit: function(input) { |
| 2580 | var direction = directionStr(input.offsetDirection); |
| 2581 | if (direction) { |
| 2582 | this.manager.emit(this.options.event + direction, input); |
| 2583 | } |
| 2584 | |
| 2585 | this.manager.emit(this.options.event, input); |
| 2586 | } |
| 2587 | }); |
| 2588 | |
| 2589 | /** |
| 2590 | * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur |
| 2591 | * between the given interval and position. The delay option can be used to recognize multi-taps without firing |
| 2592 | * a single tap. |
| 2593 | * |
| 2594 | * The eventData from the emitted event contains the property `tapCount`, which contains the amount of |
| 2595 | * multi-taps being recognized. |
| 2596 | * @constructor |
| 2597 | * @extends Recognizer |
| 2598 | */ |
| 2599 | function TapRecognizer() { |
| 2600 | Recognizer.apply(this, arguments); |
| 2601 | |
| 2602 | // previous time and center, |
| 2603 | // used for tap counting |
| 2604 | this.pTime = false; |
| 2605 | this.pCenter = false; |
| 2606 | |
| 2607 | this._timer = null; |
| 2608 | this._input = null; |
| 2609 | this.count = 0; |
| 2610 | } |
| 2611 | |
| 2612 | inherit(TapRecognizer, Recognizer, { |
| 2613 | /** |
| 2614 | * @namespace |
| 2615 | * @memberof PinchRecognizer |
| 2616 | */ |
| 2617 | defaults: { |
| 2618 | event: 'tap', |
| 2619 | pointers: 1, |
| 2620 | taps: 1, |
| 2621 | interval: 300, // max time between the multi-tap taps |
| 2622 | time: 250, // max time of the pointer to be down (like finger on the screen) |
| 2623 | threshold: 9, // a minimal movement is ok, but keep it low |
| 2624 | posThreshold: 10 // a multi-tap can be a bit off the initial position |
| 2625 | }, |
| 2626 | |
| 2627 | getTouchAction: function() { |
| 2628 | return [TOUCH_ACTION_MANIPULATION]; |
| 2629 | }, |
| 2630 | |
| 2631 | process: function(input) { |
| 2632 | var options = this.options; |
| 2633 | |
| 2634 | var validPointers = input.pointers.length === options.pointers; |
| 2635 | var validMovement = input.distance < options.threshold; |
| 2636 | var validTouchTime = input.deltaTime < options.time; |
| 2637 | |
| 2638 | this.reset(); |
| 2639 | |
| 2640 | if ((input.eventType & INPUT_START) && (this.count === 0)) { |
| 2641 | return this.failTimeout(); |
| 2642 | } |
| 2643 | |
| 2644 | // we only allow little movement |
| 2645 | // and we've reached an end event, so a tap is possible |
| 2646 | if (validMovement && validTouchTime && validPointers) { |
| 2647 | if (input.eventType != INPUT_END) { |
| 2648 | return this.failTimeout(); |
| 2649 | } |
| 2650 | |
| 2651 | var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; |
| 2652 | var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; |
| 2653 | |
| 2654 | this.pTime = input.timeStamp; |
| 2655 | this.pCenter = input.center; |
| 2656 | |
| 2657 | if (!validMultiTap || !validInterval) { |
| 2658 | this.count = 1; |
| 2659 | } else { |
| 2660 | this.count += 1; |
| 2661 | } |
| 2662 | |
| 2663 | this._input = input; |
| 2664 | |
| 2665 | // if tap count matches we have recognized it, |
| 2666 | // else it has began recognizing... |
| 2667 | var tapCount = this.count % options.taps; |
| 2668 | if (tapCount === 0) { |
| 2669 | // no failing requirements, immediately trigger the tap event |
| 2670 | // or wait as long as the multitap interval to trigger |
| 2671 | if (!this.hasRequireFailures()) { |
| 2672 | return STATE_RECOGNIZED; |
| 2673 | } else { |
| 2674 | this._timer = setTimeoutContext(function() { |
| 2675 | this.state = STATE_RECOGNIZED; |
| 2676 | this.tryEmit(); |
| 2677 | }, options.interval, this); |
| 2678 | return STATE_BEGAN; |
| 2679 | } |
| 2680 | } |
| 2681 | } |
| 2682 | return STATE_FAILED; |
| 2683 | }, |
| 2684 | |
| 2685 | failTimeout: function() { |
| 2686 | this._timer = setTimeoutContext(function() { |
| 2687 | this.state = STATE_FAILED; |
| 2688 | }, this.options.interval, this); |
| 2689 | return STATE_FAILED; |
| 2690 | }, |
| 2691 | |
| 2692 | reset: function() { |
| 2693 | clearTimeout(this._timer); |
| 2694 | }, |
| 2695 | |
| 2696 | emit: function() { |
| 2697 | if (this.state == STATE_RECOGNIZED) { |
| 2698 | this._input.tapCount = this.count; |
| 2699 | this.manager.emit(this.options.event, this._input); |
| 2700 | } |
| 2701 | } |
| 2702 | }); |
| 2703 | |
| 2704 | /** |
| 2705 | * Simple way to create a manager with a default set of recognizers. |
| 2706 | * @param {HTMLElement} element |
| 2707 | * @param {Object} [options] |
| 2708 | * @constructor |
| 2709 | */ |
| 2710 | function Hammer(element, options) { |
| 2711 | options = options || {}; |
| 2712 | options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); |
| 2713 | return new Manager(element, options); |
| 2714 | } |
| 2715 | |
| 2716 | /** |
| 2717 | * @const {string} |
| 2718 | */ |
| 2719 | Hammer.VERSION = '2.0.8'; |
| 2720 | |
| 2721 | /** |
| 2722 | * default settings |
| 2723 | * @namespace |
| 2724 | */ |
| 2725 | Hammer.defaults = { |
| 2726 | /** |
| 2727 | * set if DOM events are being triggered. |
| 2728 | * But this is slower and unused by simple implementations, so disabled by default. |
| 2729 | * @type {Boolean} |
| 2730 | * @default false |
| 2731 | */ |
| 2732 | domEvents: false, |
| 2733 | |
| 2734 | /** |
| 2735 | * The value for the touchAction property/fallback. |
| 2736 | * When set to `compute` it will magically set the correct value based on the added recognizers. |
| 2737 | * @type {String} |
| 2738 | * @default compute |
| 2739 | */ |
| 2740 | touchAction: TOUCH_ACTION_COMPUTE, |
| 2741 | |
| 2742 | /** |
| 2743 | * @type {Boolean} |
| 2744 | * @default true |
| 2745 | */ |
| 2746 | enable: true, |
| 2747 | |
| 2748 | /** |
| 2749 | * EXPERIMENTAL FEATURE -- can be removed/changed |
| 2750 | * Change the parent input target element. |
| 2751 | * If Null, then it is being set the to main element. |
| 2752 | * @type {Null|EventTarget} |
| 2753 | * @default null |
| 2754 | */ |
| 2755 | inputTarget: null, |
| 2756 | |
| 2757 | /** |
| 2758 | * force an input class |
| 2759 | * @type {Null|Function} |
| 2760 | * @default null |
| 2761 | */ |
| 2762 | inputClass: null, |
| 2763 | |
| 2764 | /** |
| 2765 | * Default recognizer setup when calling `Hammer()` |
| 2766 | * When creating a new Manager these will be skipped. |
| 2767 | * @type {Array} |
| 2768 | */ |
| 2769 | preset: [ |
| 2770 | // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] |
| 2771 | [RotateRecognizer, {enable: false}], |
| 2772 | [PinchRecognizer, {enable: false}, ['rotate']], |
| 2773 | [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], |
| 2774 | [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], |
| 2775 | [TapRecognizer], |
| 2776 | [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], |
| 2777 | [PressRecognizer] |
| 2778 | ], |
| 2779 | |
| 2780 | /** |
| 2781 | * Some CSS properties can be used to improve the working of Hammer. |
| 2782 | * Add them to this method and they will be set when creating a new Manager. |
| 2783 | * @namespace |
| 2784 | */ |
| 2785 | cssProps: { |
| 2786 | /** |
| 2787 | * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. |
| 2788 | * @type {String} |
| 2789 | * @default 'none' |
| 2790 | */ |
| 2791 | userSelect: 'none', |
| 2792 | |
| 2793 | /** |
| 2794 | * Disable the Windows Phone grippers when pressing an element. |
| 2795 | * @type {String} |
| 2796 | * @default 'none' |
| 2797 | */ |
| 2798 | touchSelect: 'none', |
| 2799 | |
| 2800 | /** |
| 2801 | * Disables the default callout shown when you touch and hold a touch target. |
| 2802 | * On iOS, when you touch and hold a touch target such as a link, Safari displays |
| 2803 | * a callout containing information about the link. This property allows you to disable that callout. |
| 2804 | * @type {String} |
| 2805 | * @default 'none' |
| 2806 | */ |
| 2807 | touchCallout: 'none', |
| 2808 | |
| 2809 | /** |
| 2810 | * Specifies whether zooming is enabled. Used by IE10> |
| 2811 | * @type {String} |
| 2812 | * @default 'none' |
| 2813 | */ |
| 2814 | contentZooming: 'none', |
| 2815 | |
| 2816 | /** |
| 2817 | * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. |
| 2818 | * @type {String} |
| 2819 | * @default 'none' |
| 2820 | */ |
| 2821 | userDrag: 'none', |
| 2822 | |
| 2823 | /** |
| 2824 | * Overrides the highlight color shown when the user taps a link or a JavaScript |
| 2825 | * clickable element in iOS. This property obeys the alpha value, if specified. |
| 2826 | * @type {String} |
| 2827 | * @default 'rgba(0,0,0,0)' |
| 2828 | */ |
| 2829 | tapHighlightColor: 'rgba(0,0,0,0)' |
| 2830 | } |
| 2831 | }; |
| 2832 | |
| 2833 | var STOP = 1; |
| 2834 | var FORCED_STOP = 2; |
| 2835 | |
| 2836 | /** |
| 2837 | * Manager |
| 2838 | * @param {HTMLElement} element |
| 2839 | * @param {Object} [options] |
| 2840 | * @constructor |
| 2841 | */ |
| 2842 | function Manager(element, options) { |
| 2843 | this.options = assign({}, Hammer.defaults, options || {}); |
| 2844 | |
| 2845 | this.options.inputTarget = this.options.inputTarget || element; |
| 2846 | |
| 2847 | this.handlers = {}; |
| 2848 | this.session = {}; |
| 2849 | this.recognizers = []; |
| 2850 | this.oldCssProps = {}; |
| 2851 | |
| 2852 | this.element = element; |
| 2853 | this.input = createInputInstance(this); |
| 2854 | this.touchAction = new TouchAction(this, this.options.touchAction); |
| 2855 | |
| 2856 | toggleCssProps(this, true); |
| 2857 | |
| 2858 | each(this.options.recognizers, function(item) { |
| 2859 | var recognizer = this.add(new (item[0])(item[1])); |
| 2860 | item[2] && recognizer.recognizeWith(item[2]); |
| 2861 | item[3] && recognizer.requireFailure(item[3]); |
| 2862 | }, this); |
| 2863 | } |
| 2864 | |
| 2865 | Manager.prototype = { |
| 2866 | /** |
| 2867 | * set options |
| 2868 | * @param {Object} options |
| 2869 | * @returns {Manager} |
| 2870 | */ |
| 2871 | set: function(options) { |
| 2872 | assign(this.options, options); |
| 2873 | |
| 2874 | // Options that need a little more setup |
| 2875 | if (options.touchAction) { |
| 2876 | this.touchAction.update(); |
| 2877 | } |
| 2878 | if (options.inputTarget) { |
| 2879 | // Clean up existing event listeners and reinitialize |
| 2880 | this.input.destroy(); |
| 2881 | this.input.target = options.inputTarget; |
| 2882 | this.input.init(); |
| 2883 | } |
| 2884 | return this; |
| 2885 | }, |
| 2886 | |
| 2887 | /** |
| 2888 | * stop recognizing for this session. |
| 2889 | * This session will be discarded, when a new [input]start event is fired. |
| 2890 | * When forced, the recognizer cycle is stopped immediately. |
| 2891 | * @param {Boolean} [force] |
| 2892 | */ |
| 2893 | stop: function(force) { |
| 2894 | this.session.stopped = force ? FORCED_STOP : STOP; |
| 2895 | }, |
| 2896 | |
| 2897 | /** |
| 2898 | * run the recognizers! |
| 2899 | * called by the inputHandler function on every movement of the pointers (touches) |
| 2900 | * it walks through all the recognizers and tries to detect the gesture that is being made |
| 2901 | * @param {Object} inputData |
| 2902 | */ |
| 2903 | recognize: function(inputData) { |
| 2904 | var session = this.session; |
| 2905 | if (session.stopped) { |
| 2906 | return; |
| 2907 | } |
| 2908 | |
| 2909 | // run the touch-action polyfill |
| 2910 | this.touchAction.preventDefaults(inputData); |
| 2911 | |
| 2912 | var recognizer; |
| 2913 | var recognizers = this.recognizers; |
| 2914 | |
| 2915 | // this holds the recognizer that is being recognized. |
| 2916 | // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED |
| 2917 | // if no recognizer is detecting a thing, it is set to `null` |
| 2918 | var curRecognizer = session.curRecognizer; |
| 2919 | |
| 2920 | // reset when the last recognizer is recognized |
| 2921 | // or when we're in a new session |
| 2922 | if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { |
| 2923 | curRecognizer = session.curRecognizer = null; |
| 2924 | } |
| 2925 | |
| 2926 | var i = 0; |
| 2927 | while (i < recognizers.length) { |
| 2928 | recognizer = recognizers[i]; |
| 2929 | |
| 2930 | // find out if we are allowed try to recognize the input for this one. |
| 2931 | // 1. allow if the session is NOT forced stopped (see the .stop() method) |
| 2932 | // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one |
| 2933 | // that is being recognized. |
| 2934 | // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. |
| 2935 | // this can be setup with the `recognizeWith()` method on the recognizer. |
| 2936 | if (session.stopped !== FORCED_STOP && ( // 1 |
| 2937 | !curRecognizer || recognizer == curRecognizer || // 2 |
| 2938 | recognizer.canRecognizeWith(curRecognizer))) { // 3 |
| 2939 | recognizer.recognize(inputData); |
| 2940 | } else { |
| 2941 | recognizer.reset(); |
| 2942 | } |
| 2943 | |
| 2944 | // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the |
| 2945 | // current active recognizer. but only if we don't already have an active recognizer |
| 2946 | if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { |
| 2947 | curRecognizer = session.curRecognizer = recognizer; |
| 2948 | } |
| 2949 | i++; |
| 2950 | } |
| 2951 | }, |
| 2952 | |
| 2953 | /** |
| 2954 | * get a recognizer by its event name. |
| 2955 | * @param {Recognizer|String} recognizer |
| 2956 | * @returns {Recognizer|Null} |
| 2957 | */ |
| 2958 | get: function(recognizer) { |
| 2959 | if (recognizer instanceof Recognizer) { |
| 2960 | return recognizer; |
| 2961 | } |
| 2962 | |
| 2963 | var recognizers = this.recognizers; |
| 2964 | for (var i = 0; i < recognizers.length; i++) { |
| 2965 | if (recognizers[i].options.event == recognizer) { |
| 2966 | return recognizers[i]; |
| 2967 | } |
| 2968 | } |
| 2969 | return null; |
| 2970 | }, |
| 2971 | |
| 2972 | /** |
| 2973 | * add a recognizer to the manager |
| 2974 | * existing recognizers with the same event name will be removed |
| 2975 | * @param {Recognizer} recognizer |
| 2976 | * @returns {Recognizer|Manager} |
| 2977 | */ |
| 2978 | add: function(recognizer) { |
| 2979 | if (invokeArrayArg(recognizer, 'add', this)) { |
| 2980 | return this; |
| 2981 | } |
| 2982 | |
| 2983 | // remove existing |
| 2984 | var existing = this.get(recognizer.options.event); |
| 2985 | if (existing) { |
| 2986 | this.remove(existing); |
| 2987 | } |
| 2988 | |
| 2989 | this.recognizers.push(recognizer); |
| 2990 | recognizer.manager = this; |
| 2991 | |
| 2992 | this.touchAction.update(); |
| 2993 | return recognizer; |
| 2994 | }, |
| 2995 | |
| 2996 | /** |
| 2997 | * remove a recognizer by name or instance |
| 2998 | * @param {Recognizer|String} recognizer |
| 2999 | * @returns {Manager} |
| 3000 | */ |
| 3001 | remove: function(recognizer) { |
| 3002 | if (invokeArrayArg(recognizer, 'remove', this)) { |
| 3003 | return this; |
| 3004 | } |
| 3005 | |
| 3006 | recognizer = this.get(recognizer); |
| 3007 | |
| 3008 | // let's make sure this recognizer exists |
| 3009 | if (recognizer) { |
| 3010 | var recognizers = this.recognizers; |
| 3011 | var index = inArray(recognizers, recognizer); |
| 3012 | |
| 3013 | if (index !== -1) { |
| 3014 | recognizers.splice(index, 1); |
| 3015 | this.touchAction.update(); |
| 3016 | } |
| 3017 | } |
| 3018 | |
| 3019 | return this; |
| 3020 | }, |
| 3021 | |
| 3022 | /** |
| 3023 | * bind event |
| 3024 | * @param {String} events |
| 3025 | * @param {Function} handler |
| 3026 | * @returns {EventEmitter} this |
| 3027 | */ |
| 3028 | on: function(events, handler) { |
| 3029 | if (events === undefined) { |
| 3030 | return; |
| 3031 | } |
| 3032 | if (handler === undefined) { |
| 3033 | return; |
| 3034 | } |
| 3035 | |
| 3036 | var handlers = this.handlers; |
| 3037 | each(splitStr(events), function(event) { |
| 3038 | handlers[event] = handlers[event] || []; |
| 3039 | handlers[event].push(handler); |
| 3040 | }); |
| 3041 | return this; |
| 3042 | }, |
| 3043 | |
| 3044 | /** |
| 3045 | * unbind event, leave emit blank to remove all handlers |
| 3046 | * @param {String} events |
| 3047 | * @param {Function} [handler] |
| 3048 | * @returns {EventEmitter} this |
| 3049 | */ |
| 3050 | off: function(events, handler) { |
| 3051 | if (events === undefined) { |
| 3052 | return; |
| 3053 | } |
| 3054 | |
| 3055 | var handlers = this.handlers; |
| 3056 | each(splitStr(events), function(event) { |
| 3057 | if (!handler) { |
| 3058 | delete handlers[event]; |
| 3059 | } else { |
| 3060 | handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); |
| 3061 | } |
| 3062 | }); |
| 3063 | return this; |
| 3064 | }, |
| 3065 | |
| 3066 | /** |
| 3067 | * emit event to the listeners |
| 3068 | * @param {String} event |
| 3069 | * @param {Object} data |
| 3070 | */ |
| 3071 | emit: function(event, data) { |
| 3072 | // we also want to trigger dom events |
| 3073 | if (this.options.domEvents) { |
| 3074 | triggerDomEvent(event, data); |
| 3075 | } |
| 3076 | |
| 3077 | // no handlers, so skip it all |
| 3078 | var handlers = this.handlers[event] && this.handlers[event].slice(); |
| 3079 | if (!handlers || !handlers.length) { |
| 3080 | return; |
| 3081 | } |
| 3082 | |
| 3083 | data.type = event; |
| 3084 | data.preventDefault = function() { |
| 3085 | data.srcEvent.preventDefault(); |
| 3086 | }; |
| 3087 | |
| 3088 | var i = 0; |
| 3089 | while (i < handlers.length) { |
| 3090 | handlers[i](data); |
| 3091 | i++; |
| 3092 | } |
| 3093 | }, |
| 3094 | |
| 3095 | /** |
| 3096 | * destroy the manager and unbinds all events |
| 3097 | * it doesn't unbind dom events, that is the user own responsibility |
| 3098 | */ |
| 3099 | destroy: function() { |
| 3100 | this.element && toggleCssProps(this, false); |
| 3101 | |
| 3102 | this.handlers = {}; |
| 3103 | this.session = {}; |
| 3104 | this.input.destroy(); |
| 3105 | this.element = null; |
| 3106 | } |
| 3107 | }; |
| 3108 | |
| 3109 | /** |
| 3110 | * add/remove the css properties as defined in manager.options.cssProps |
| 3111 | * @param {Manager} manager |
| 3112 | * @param {Boolean} add |
| 3113 | */ |
| 3114 | function toggleCssProps(manager, add) { |
| 3115 | var element = manager.element; |
| 3116 | if (!element.style) { |
| 3117 | return; |
| 3118 | } |
| 3119 | var prop; |
| 3120 | each(manager.options.cssProps, function(value, name) { |
| 3121 | prop = prefixed(element.style, name); |
| 3122 | if (add) { |
| 3123 | manager.oldCssProps[prop] = element.style[prop]; |
| 3124 | element.style[prop] = value; |
| 3125 | } else { |
| 3126 | element.style[prop] = manager.oldCssProps[prop] || ''; |
| 3127 | } |
| 3128 | }); |
| 3129 | if (!add) { |
| 3130 | manager.oldCssProps = {}; |
| 3131 | } |
| 3132 | } |
| 3133 | |
| 3134 | /** |
| 3135 | * trigger dom event |
| 3136 | * @param {String} event |
| 3137 | * @param {Object} data |
| 3138 | */ |
| 3139 | function triggerDomEvent(event, data) { |
| 3140 | var gestureEvent = document.createEvent('Event'); |
| 3141 | gestureEvent.initEvent(event, true, true); |
| 3142 | gestureEvent.gesture = data; |
| 3143 | data.target.dispatchEvent(gestureEvent); |
| 3144 | } |
| 3145 | |
| 3146 | assign(Hammer, { |
| 3147 | INPUT_START: INPUT_START, |
| 3148 | INPUT_MOVE: INPUT_MOVE, |
| 3149 | INPUT_END: INPUT_END, |
| 3150 | INPUT_CANCEL: INPUT_CANCEL, |
| 3151 | |
| 3152 | STATE_POSSIBLE: STATE_POSSIBLE, |
| 3153 | STATE_BEGAN: STATE_BEGAN, |
| 3154 | STATE_CHANGED: STATE_CHANGED, |
| 3155 | STATE_ENDED: STATE_ENDED, |
| 3156 | STATE_RECOGNIZED: STATE_RECOGNIZED, |
| 3157 | STATE_CANCELLED: STATE_CANCELLED, |
| 3158 | STATE_FAILED: STATE_FAILED, |
| 3159 | |
| 3160 | DIRECTION_NONE: DIRECTION_NONE, |
| 3161 | DIRECTION_LEFT: DIRECTION_LEFT, |
| 3162 | DIRECTION_RIGHT: DIRECTION_RIGHT, |
| 3163 | DIRECTION_UP: DIRECTION_UP, |
| 3164 | DIRECTION_DOWN: DIRECTION_DOWN, |
| 3165 | DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, |
| 3166 | DIRECTION_VERTICAL: DIRECTION_VERTICAL, |
| 3167 | DIRECTION_ALL: DIRECTION_ALL, |
| 3168 | |
| 3169 | Manager: Manager, |
| 3170 | Input: Input, |
| 3171 | TouchAction: TouchAction, |
| 3172 | |
| 3173 | TouchInput: TouchInput, |
| 3174 | MouseInput: MouseInput, |
| 3175 | PointerEventInput: PointerEventInput, |
| 3176 | TouchMouseInput: TouchMouseInput, |
| 3177 | SingleTouchInput: SingleTouchInput, |
| 3178 | |
| 3179 | Recognizer: Recognizer, |
| 3180 | AttrRecognizer: AttrRecognizer, |
| 3181 | Tap: TapRecognizer, |
| 3182 | Pan: PanRecognizer, |
| 3183 | Swipe: SwipeRecognizer, |
| 3184 | Pinch: PinchRecognizer, |
| 3185 | Rotate: RotateRecognizer, |
| 3186 | Press: PressRecognizer, |
| 3187 | |
| 3188 | on: addEventListeners, |
| 3189 | off: removeEventListeners, |
| 3190 | each: each, |
| 3191 | merge: merge, |
| 3192 | extend: extend, |
| 3193 | assign: assign, |
| 3194 | inherit: inherit, |
| 3195 | bindFn: bindFn, |
| 3196 | prefixed: prefixed |
| 3197 | }); |
| 3198 | |
| 3199 | // this prevents errors when Hammer is loaded in the presence of an AMD |
| 3200 | // style loader but by script tag, not by the loader. |
| 3201 | var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line |
| 3202 | freeGlobal.Hammer = Hammer; |
| 3203 | |
| 3204 | if (typeof define === 'function' && define.amd) { |
| 3205 | define(function() { |
| 3206 | return Hammer; |
| 3207 | }); |
| 3208 | } else if (typeof module != 'undefined' && module.exports) { |
| 3209 | module.exports = Hammer; |
| 3210 | } else { |
| 3211 | window[exportName] = Hammer; |
| 3212 | } |
| 3213 | |
| 3214 | })(window, document, 'Hammer'); |
| 3215 | |
| 3216 | + function($) { |
| 3217 | "use strict"; |
| 3218 | |
| 3219 | var defaults; |
| 3220 | |
| 3221 | $.modal = function(params, onOpen) { |
| 3222 | params = $.extend({}, defaults, params); |
| 3223 | |
| 3224 | |
| 3225 | var buttons = params.buttons; |
| 3226 | |
| 3227 | var buttonsHtml = buttons.map(function(d, i) { |
| 3228 | return '<a href="javascript:;" class="weui-dialog__btn ' + (d.className || "") + '">' + d.text + '</a>'; |
| 3229 | }).join(""); |
| 3230 | |
| 3231 | var tpl = '<div class="weui-dialog">' + |
| 3232 | '<div class="weui-dialog__hd"><strong class="weui-dialog__title">' + params.title + '</strong></div>' + |
| 3233 | ( params.text ? '<div class="weui-dialog__bd">'+params.text+'</div>' : '')+ |
| 3234 | '<div class="weui-dialog__ft">' + buttonsHtml + '</div>' + |
| 3235 | '</div>'; |
| 3236 | |
| 3237 | var dialog = $.openModal(tpl, onOpen); |
| 3238 | |
| 3239 | dialog.find(".weui-dialog__btn").each(function(i, e) { |
| 3240 | var el = $(e); |
| 3241 | el.click(function() { |
| 3242 | //先关闭对话框,再调用回调函数 |
| 3243 | if(params.autoClose) $.closeModal(); |
| 3244 | |
| 3245 | if(buttons[i].onClick) { |
| 3246 | buttons[i].onClick.call(dialog); |
| 3247 | } |
| 3248 | }); |
| 3249 | }); |
| 3250 | |
| 3251 | return dialog; |
| 3252 | }; |
| 3253 | |
| 3254 | $.openModal = function(tpl, onOpen) { |
| 3255 | var mask = $("<div class='weui-mask'></div>").appendTo(document.body); |
| 3256 | mask.show(); |
| 3257 | |
| 3258 | var dialog = $(tpl).appendTo(document.body); |
| 3259 | |
| 3260 | if (onOpen) { |
| 3261 | dialog.transitionEnd(function () { |
| 3262 | onOpen.call(dialog); |
| 3263 | }); |
| 3264 | } |
| 3265 | |
| 3266 | dialog.show(); |
| 3267 | mask.addClass("weui-mask--visible"); |
| 3268 | dialog.addClass("weui-dialog--visible"); |
| 3269 | |
| 3270 | |
| 3271 | return dialog; |
| 3272 | } |
| 3273 | |
| 3274 | $.closeModal = function() { |
| 3275 | $(".weui-mask--visible").removeClass("weui-mask--visible").transitionEnd(function() { |
| 3276 | $(this).remove(); |
| 3277 | }); |
| 3278 | $(".weui-dialog--visible").removeClass("weui-dialog--visible").transitionEnd(function() { |
| 3279 | $(this).remove(); |
| 3280 | }); |
| 3281 | }; |
| 3282 | |
| 3283 | $.alert = function(text, title, onOK) { |
| 3284 | var config; |
| 3285 | if (typeof text === 'object') { |
| 3286 | config = text; |
| 3287 | } else { |
| 3288 | if (typeof title === 'function') { |
| 3289 | onOK = arguments[1]; |
| 3290 | title = undefined; |
| 3291 | } |
| 3292 | |
| 3293 | config = { |
| 3294 | text: text, |
| 3295 | title: title, |
| 3296 | onOK: onOK |
| 3297 | } |
| 3298 | } |
| 3299 | return $.modal({ |
| 3300 | text: config.text, |
| 3301 | title: config.title, |
| 3302 | buttons: [{ |
| 3303 | text: defaults.buttonOK, |
| 3304 | className: "primary", |
| 3305 | onClick: config.onOK |
| 3306 | }] |
| 3307 | }); |
| 3308 | } |
| 3309 | |
| 3310 | $.confirm = function(text, title, onOK, onCancel) { |
| 3311 | var config; |
| 3312 | if (typeof text === 'object') { |
| 3313 | config = text |
| 3314 | } else { |
| 3315 | if (typeof title === 'function') { |
| 3316 | onCancel = arguments[2]; |
| 3317 | onOK = arguments[1]; |
| 3318 | title = undefined; |
| 3319 | } |
| 3320 | |
| 3321 | config = { |
| 3322 | text: text, |
| 3323 | title: title, |
| 3324 | onOK: onOK, |
| 3325 | onCancel: onCancel |
| 3326 | } |
| 3327 | } |
| 3328 | return $.modal({ |
| 3329 | text: config.text, |
| 3330 | title: config.title, |
| 3331 | buttons: [ |
| 3332 | { |
| 3333 | text: defaults.buttonCancel, |
| 3334 | className: "default", |
| 3335 | onClick: config.onCancel |
| 3336 | }, |
| 3337 | { |
| 3338 | text: defaults.buttonOK, |
| 3339 | className: "primary", |
| 3340 | onClick: config.onOK |
| 3341 | }] |
| 3342 | }); |
| 3343 | }; |
| 3344 | |
| 3345 | //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。 |
| 3346 | $.prompt = function(text, title, onOK, onCancel, input) { |
| 3347 | var config; |
| 3348 | if (typeof text === 'object') { |
| 3349 | config = text; |
| 3350 | } else { |
| 3351 | if (typeof title === 'function') { |
| 3352 | input = arguments[3]; |
| 3353 | onCancel = arguments[2]; |
| 3354 | onOK = arguments[1]; |
| 3355 | title = undefined; |
| 3356 | } |
| 3357 | config = { |
| 3358 | text: text, |
| 3359 | title: title, |
| 3360 | input: input, |
| 3361 | onOK: onOK, |
| 3362 | onCancel: onCancel, |
| 3363 | empty: false //allow empty |
| 3364 | } |
| 3365 | } |
| 3366 | |
| 3367 | var modal = $.modal({ |
| 3368 | text: '<p class="weui-prompt-text">'+(config.text || '')+'</p><input type="text" class="weui-input weui-prompt-input" id="weui-prompt-input" value="' + (config.input || '') + '" />', |
| 3369 | title: config.title, |
| 3370 | autoClose: false, |
| 3371 | buttons: [ |
| 3372 | { |
| 3373 | text: defaults.buttonCancel, |
| 3374 | className: "default", |
| 3375 | onClick: function () { |
| 3376 | $.closeModal(); |
| 3377 | config.onCancel && config.onCancel.call(modal); |
| 3378 | } |
| 3379 | }, |
| 3380 | { |
| 3381 | text: defaults.buttonOK, |
| 3382 | className: "primary", |
| 3383 | onClick: function() { |
| 3384 | var input = $("#weui-prompt-input").val(); |
| 3385 | if (!config.empty && (input === "" || input === null)) { |
| 3386 | modal.find('.weui-prompt-input').focus()[0].select(); |
| 3387 | return false; |
| 3388 | } |
| 3389 | $.closeModal(); |
| 3390 | config.onOK && config.onOK.call(modal, input); |
| 3391 | } |
| 3392 | }] |
| 3393 | }, function () { |
| 3394 | this.find('.weui-prompt-input').focus()[0].select(); |
| 3395 | }); |
| 3396 | |
| 3397 | return modal; |
| 3398 | }; |
| 3399 | |
| 3400 | //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。 |
| 3401 | $.login = function(text, title, onOK, onCancel, username, password) { |
| 3402 | var config; |
| 3403 | if (typeof text === 'object') { |
| 3404 | config = text; |
| 3405 | } else { |
| 3406 | if (typeof title === 'function') { |
| 3407 | password = arguments[4]; |
| 3408 | username = arguments[3]; |
| 3409 | onCancel = arguments[2]; |
| 3410 | onOK = arguments[1]; |
| 3411 | title = undefined; |
| 3412 | } |
| 3413 | config = { |
| 3414 | text: text, |
| 3415 | title: title, |
| 3416 | username: username, |
| 3417 | password: password, |
| 3418 | onOK: onOK, |
| 3419 | onCancel: onCancel |
| 3420 | } |
| 3421 | } |
| 3422 | |
| 3423 | var modal = $.modal({ |
| 3424 | text: '<p class="weui-prompt-text">'+(config.text || '')+'</p>' + |
| 3425 | '<input type="text" class="weui-input weui-prompt-input" id="weui-prompt-username" value="' + (config.username || '') + '" placeholder="输入用户名" />' + |
| 3426 | '<input type="password" class="weui-input weui-prompt-input" id="weui-prompt-password" value="' + (config.password || '') + '" placeholder="输入密码" />', |
| 3427 | title: config.title, |
| 3428 | autoClose: false, |
| 3429 | buttons: [ |
| 3430 | { |
| 3431 | text: defaults.buttonCancel, |
| 3432 | className: "default", |
| 3433 | onClick: function () { |
| 3434 | $.closeModal(); |
| 3435 | config.onCancel && config.onCancel.call(modal); |
| 3436 | } |
| 3437 | }, { |
| 3438 | text: defaults.buttonOK, |
| 3439 | className: "primary", |
| 3440 | onClick: function() { |
| 3441 | var username = $("#weui-prompt-username").val(); |
| 3442 | var password = $("#weui-prompt-password").val(); |
| 3443 | if (!config.empty && (username === "" || username === null)) { |
| 3444 | modal.find('#weui-prompt-username').focus()[0].select(); |
| 3445 | return false; |
| 3446 | } |
| 3447 | if (!config.empty && (password === "" || password === null)) { |
| 3448 | modal.find('#weui-prompt-password').focus()[0].select(); |
| 3449 | return false; |
| 3450 | } |
| 3451 | $.closeModal(); |
| 3452 | config.onOK && config.onOK.call(modal, username, password); |
| 3453 | } |
| 3454 | }] |
| 3455 | }, function () { |
| 3456 | this.find('#weui-prompt-username').focus()[0].select(); |
| 3457 | }); |
| 3458 | |
| 3459 | return modal; |
| 3460 | }; |
| 3461 | |
| 3462 | defaults = $.modal.prototype.defaults = { |
| 3463 | title: "提示", |
| 3464 | text: undefined, |
| 3465 | buttonOK: "确定", |
| 3466 | buttonCancel: "取消", |
| 3467 | buttons: [{ |
| 3468 | text: "确定", |
| 3469 | className: "primary" |
| 3470 | }], |
| 3471 | autoClose: true //点击按钮自动关闭对话框,如果你不希望点击按钮就关闭对话框,可以把这个设置为false |
| 3472 | }; |
| 3473 | |
| 3474 | }($); |
| 3475 | |
| 3476 | + function($) { |
| 3477 | "use strict"; |
| 3478 | |
| 3479 | var defaults; |
| 3480 | |
| 3481 | var show = function(html, className) { |
| 3482 | className = className || ""; |
| 3483 | var mask = $("<div class='weui-mask_transparent'></div>").appendTo(document.body); |
| 3484 | |
| 3485 | var tpl = '<div class="weui-toast ' + className + '">' + html + '</div>'; |
| 3486 | var dialog = $(tpl).appendTo(document.body); |
| 3487 | |
| 3488 | dialog.addClass("weui-toast--visible"); |
| 3489 | dialog.show(); |
| 3490 | }; |
| 3491 | |
| 3492 | var hide = function(callback) { |
| 3493 | $(".weui-mask_transparent").remove(); |
| 3494 | var done = false; |
| 3495 | var $el = $(".weui-toast--visible").removeClass("weui-toast--visible").transitionEnd(function() { |
| 3496 | var $this = $(this); |
| 3497 | $this.remove(); |
| 3498 | callback && callback(); |
| 3499 | done = true |
| 3500 | }); |
| 3501 | |
| 3502 | setTimeout(function () { |
| 3503 | if (!done) { |
| 3504 | $el.remove() |
| 3505 | callback && callback(); |
| 3506 | } |
| 3507 | }, 1000) |
| 3508 | } |
| 3509 | |
| 3510 | $.toast = function(text, style, callback) { |
| 3511 | if(typeof style === "function") { |
| 3512 | callback = style; |
| 3513 | } |
| 3514 | var className, iconClassName = 'weui-icon-success-no-circle'; |
| 3515 | var duration = toastDefaults.duration; |
| 3516 | if(style == "cancel") { |
| 3517 | className = "weui-toast_cancel"; |
| 3518 | iconClassName = 'weui-icon-cancel' |
| 3519 | } else if(style == "forbidden") { |
| 3520 | className = "weui-toast--forbidden"; |
| 3521 | iconClassName = 'weui-icon-warn' |
| 3522 | } else if(style == "text") { |
| 3523 | className = "weui-toast--text"; |
| 3524 | } else if(typeof style === typeof 1) { |
| 3525 | duration = style |
| 3526 | } |
| 3527 | show('<i class="' + iconClassName + ' weui-icon_toast"></i><p class="weui-toast_content">' + (text || "已经完成") + '</p>', className); |
| 3528 | |
| 3529 | setTimeout(function() { |
| 3530 | hide(callback); |
| 3531 | }, duration); |
| 3532 | } |
| 3533 | |
| 3534 | $.showLoading = function(text) { |
| 3535 | var html = '<div class="weui_loading">'; |
| 3536 | html += '<i class="weui-loading weui-icon_toast"></i>'; |
| 3537 | html += '</div>'; |
| 3538 | html += '<p class="weui-toast_content">' + (text || "数据加载中") + '</p>'; |
| 3539 | show(html, 'weui_loading_toast'); |
| 3540 | } |
| 3541 | |
| 3542 | $.hideLoading = function() { |
| 3543 | hide(); |
| 3544 | } |
| 3545 | |
| 3546 | var toastDefaults = $.toast.prototype.defaults = { |
| 3547 | duration: 2500 |
| 3548 | } |
| 3549 | |
| 3550 | }($); |
| 3551 | |
| 3552 | + function($) { |
| 3553 | "use strict"; |
| 3554 | |
| 3555 | var defaults; |
| 3556 | |
| 3557 | var show = function(params) { |
| 3558 | |
| 3559 | var mask = $("<div class='weui-mask weui-actions_mask'></div>").appendTo(document.body); |
| 3560 | |
| 3561 | var actions = params.actions || []; |
| 3562 | |
| 3563 | var actionsHtml = actions.map(function(d, i) { |
| 3564 | return '<div class="weui-actionsheet__cell ' + (d.className || "") + '">' + d.text + '</div>'; |
| 3565 | }).join(""); |
| 3566 | |
| 3567 | var titleHtml = ""; |
| 3568 | |
| 3569 | if (params.title) { |
| 3570 | titleHtml = '<div class="weui-actionsheet__title"><p class="weui-actionsheet__title-text">' + params.title + '</p></div>'; |
| 3571 | } |
| 3572 | |
| 3573 | var tpl = '<div class="weui-actionsheet " id="weui-actionsheet">'+ |
| 3574 | titleHtml + |
| 3575 | '<div class="weui-actionsheet__menu">'+ |
| 3576 | actionsHtml + |
| 3577 | '</div>'+ |
| 3578 | '<div class="weui-actionsheet__action">'+ |
| 3579 | '<div class="weui-actionsheet__cell weui-actionsheet_cancel">取消</div>'+ |
| 3580 | '</div>'+ |
| 3581 | '</div>'; |
| 3582 | var dialog = $(tpl).appendTo(document.body); |
| 3583 | |
| 3584 | dialog.find(".weui-actionsheet__menu .weui-actionsheet__cell, .weui-actionsheet__action .weui-actionsheet__cell").each(function(i, e) { |
| 3585 | $(e).click(function() { |
| 3586 | $.closeActions(); |
| 3587 | params.onClose && params.onClose(); |
| 3588 | if(actions[i] && actions[i].onClick) { |
| 3589 | actions[i].onClick(); |
| 3590 | } |
| 3591 | }) |
| 3592 | }); |
| 3593 | |
| 3594 | mask.show(); |
| 3595 | dialog.show(); |
| 3596 | mask.addClass("weui-mask--visible"); |
| 3597 | dialog.addClass("weui-actionsheet_toggle"); |
| 3598 | }; |
| 3599 | |
| 3600 | var hide = function() { |
| 3601 | $(".weui-mask").removeClass("weui-mask--visible").transitionEnd(function() { |
| 3602 | $(this).remove(); |
| 3603 | }); |
| 3604 | $(".weui-actionsheet").removeClass("weui-actionsheet_toggle").transitionEnd(function() { |
| 3605 | $(this).remove(); |
| 3606 | }); |
| 3607 | } |
| 3608 | |
| 3609 | $.actions = function(params) { |
| 3610 | params = $.extend({}, defaults, params); |
| 3611 | show(params); |
| 3612 | } |
| 3613 | |
| 3614 | $.closeActions = function() { |
| 3615 | hide(); |
| 3616 | } |
| 3617 | |
| 3618 | $(document).on("click", ".weui-actions_mask", function() { |
| 3619 | $.closeActions(); |
| 3620 | }); |
| 3621 | |
| 3622 | var defaults = $.actions.prototype.defaults = { |
| 3623 | title: undefined, |
| 3624 | onClose: undefined, |
| 3625 | /*actions: [{ |
| 3626 | text: "菜单", |
| 3627 | className: "color-danger", |
| 3628 | onClick: function() { |
| 3629 | console.log(1); |
| 3630 | } |
| 3631 | },{ |
| 3632 | text: "菜单2", |
| 3633 | className: "color-success", |
| 3634 | onClick: function() { |
| 3635 | console.log(2); |
| 3636 | } |
| 3637 | }]*/ |
| 3638 | } |
| 3639 | |
| 3640 | }($); |
| 3641 | |
| 3642 | /* =============================================================================== |
| 3643 | ************ Pull to refreh ************ |
| 3644 | =============================================================================== */ |
| 3645 | /* global $:true */ |
| 3646 | |
| 3647 | +function ($) { |
| 3648 | "use strict"; |
| 3649 | |
| 3650 | var PTR = function(el, opt) { |
| 3651 | if (typeof opt === typeof function () {}) { |
| 3652 | opt = { |
| 3653 | onRefresh: opt |
| 3654 | } |
| 3655 | } |
| 3656 | if (typeof opt === typeof 'a') { |
| 3657 | opt = undefined |
| 3658 | } |
| 3659 | this.opt = $.extend(PTR.defaults, opt || {}); |
| 3660 | this.container = $(el); |
| 3661 | this.attachEvents(); |
| 3662 | } |
| 3663 | |
| 3664 | PTR.defaults = { |
| 3665 | distance: 50, |
| 3666 | onRefresh: undefined, |
| 3667 | onPull: undefined |
| 3668 | } |
| 3669 | |
| 3670 | PTR.prototype.touchStart = function(e) { |
| 3671 | if(this.container.hasClass("refreshing")) return; |
| 3672 | var p = $.getTouchPosition(e); |
| 3673 | this.start = p; |
| 3674 | this.diffX = this.diffY = 0; |
| 3675 | }; |
| 3676 | |
| 3677 | PTR.prototype.touchMove= function(e) { |
| 3678 | if(this.container.hasClass("refreshing")) return; |
| 3679 | if(!this.start) return false; |
| 3680 | if(this.container.scrollTop() > 0) return; |
| 3681 | var p = $.getTouchPosition(e); |
| 3682 | this.diffX = p.x - this.start.x; |
| 3683 | this.diffY = p.y - this.start.y; |
| 3684 | if (Math.abs(this.diffX) > Math.abs(this.diffY)) return true; // 说明是左右方向的拖动 |
| 3685 | if(this.diffY < 0) return; |
| 3686 | this.container.addClass("touching"); |
| 3687 | e.preventDefault(); |
| 3688 | e.stopPropagation(); |
| 3689 | this.diffY = Math.pow(this.diffY, 0.75); |
| 3690 | this.container.css("transform", "translate3d(0, "+this.diffY+"px, 0)"); |
| 3691 | this.triggerPull(this.diffY) |
| 3692 | }; |
| 3693 | PTR.prototype.touchEnd = function() { |
| 3694 | this.start = false; |
| 3695 | if(this.diffY <= 0 || this.container.hasClass("refreshing")) return; |
| 3696 | this.container.removeClass("touching"); |
| 3697 | this.container.removeClass("pull-down pull-up"); |
| 3698 | this.container.css("transform", ""); |
| 3699 | if(Math.abs(this.diffY) <= this.opt.distance) { |
| 3700 | } else { |
| 3701 | this.triggerPullToRefresh(); |
| 3702 | } |
| 3703 | }; |
| 3704 | |
| 3705 | PTR.prototype.triggerPullToRefresh = function() { |
| 3706 | this.triggerPull(this.opt.distance) |
| 3707 | this.container.removeClass('pull-up').addClass("refreshing"); |
| 3708 | if (this.opt.onRefresh) { |
| 3709 | this.opt.onRefresh.call(this) |
| 3710 | } |
| 3711 | this.container.trigger("pull-to-refresh"); |
| 3712 | } |
| 3713 | |
| 3714 | PTR.prototype.triggerPull = function(diffY) { |
| 3715 | |
| 3716 | if(diffY < this.opt.distance) { |
| 3717 | this.container.removeClass("pull-up").addClass("pull-down"); |
| 3718 | } else { |
| 3719 | this.container.removeClass("pull-down").addClass("pull-up"); |
| 3720 | } |
| 3721 | |
| 3722 | if (this.opt.onPull) { |
| 3723 | this.opt.onPull.call(this, Math.floor(diffY / this.opt.distance * 100)) |
| 3724 | } |
| 3725 | this.container.trigger("pull"); |
| 3726 | } |
| 3727 | |
| 3728 | PTR.prototype.pullToRefreshDone = function() { |
| 3729 | this.container.removeClass("refreshing"); |
| 3730 | } |
| 3731 | |
| 3732 | PTR.prototype.attachEvents = function() { |
| 3733 | var el = this.container; |
| 3734 | el.addClass("weui-pull-to-refresh"); |
| 3735 | el.on($.touchEvents.start, $.proxy(this.touchStart, this)); |
| 3736 | el.on($.touchEvents.move, $.proxy(this.touchMove, this)); |
| 3737 | el.on($.touchEvents.end, $.proxy(this.touchEnd, this)); |
| 3738 | }; |
| 3739 | |
| 3740 | var pullToRefreshDone = function(el) { |
| 3741 | $(el).removeClass("refreshing"); |
| 3742 | } |
| 3743 | |
| 3744 | $.fn.pullToRefresh = function(opt) { |
| 3745 | return this.each(function() { |
| 3746 | var $this = $(this) |
| 3747 | var ptr = $this.data('ptr') |
| 3748 | if (!ptr) $this.data('ptr', ptr = new PTR(this, opt)) |
| 3749 | if (typeof opt === typeof 'a') { |
| 3750 | ptr[opt].call(ptr) |
| 3751 | } |
| 3752 | }); |
| 3753 | } |
| 3754 | |
| 3755 | $.fn.pullToRefreshDone = function() { |
| 3756 | return this.each(function() { |
| 3757 | pullToRefreshDone(this); |
| 3758 | }); |
| 3759 | } |
| 3760 | |
| 3761 | }($); |
| 3762 | |
| 3763 | /* =============================================================================== |
| 3764 | ************ Infinite ************ |
| 3765 | =============================================================================== */ |
| 3766 | /* global $:true */ |
| 3767 | +function ($) { |
| 3768 | "use strict"; |
| 3769 | |
| 3770 | // fix https://github.com/lihongxun945/jquery-weui/issues/442 |
| 3771 | // chrome will always return 0, when use document.body.scrollTop |
| 3772 | // https://stackoverflow.com/questions/43717316/google-chrome-document-body-scrolltop-always-returns-0 |
| 3773 | var getOffset = function (container) { |
| 3774 | var tagName = container[0].tagName.toUpperCase() |
| 3775 | var scrollTop |
| 3776 | if (tagName === 'BODY' || tagName === 'HTML') { |
| 3777 | scrollTop = container.scrollTop() || $(window).scrollTop() |
| 3778 | } else { |
| 3779 | scrollTop = container.scrollTop() |
| 3780 | } |
| 3781 | var offset = container.scrollHeight() - ($(window).height() + scrollTop) |
| 3782 | console.log(offset) |
| 3783 | return offset |
| 3784 | } |
| 3785 | |
| 3786 | var Infinite = function(el, distance) { |
| 3787 | this.container = $(el); |
| 3788 | this.container.data("infinite", this); |
| 3789 | this.distance = distance || 50; |
| 3790 | this.attachEvents(); |
| 3791 | } |
| 3792 | |
| 3793 | Infinite.prototype.scroll = function() { |
| 3794 | var container = this.container; |
| 3795 | this._check(); |
| 3796 | } |
| 3797 | |
| 3798 | Infinite.prototype.attachEvents = function(off) { |
| 3799 | var el = this.container; |
| 3800 | var scrollContainer = (el[0].tagName.toUpperCase() === "BODY" ? $(document) : el); |
| 3801 | scrollContainer[off ? "off" : "on"]("scroll", $.proxy(this.scroll, this)); |
| 3802 | }; |
| 3803 | Infinite.prototype.detachEvents = function(off) { |
| 3804 | this.attachEvents(true); |
| 3805 | } |
| 3806 | Infinite.prototype._check = function() { |
| 3807 | var offset = getOffset(this.container); |
| 3808 | if(Math.abs(offset) <= this.distance) { |
| 3809 | this.container.trigger("infinite"); |
| 3810 | } |
| 3811 | } |
| 3812 | |
| 3813 | var infinite = function(el) { |
| 3814 | attachEvents(el); |
| 3815 | } |
| 3816 | |
| 3817 | $.fn.infinite = function(distance) { |
| 3818 | return this.each(function() { |
| 3819 | new Infinite(this, distance); |
| 3820 | }); |
| 3821 | } |
| 3822 | $.fn.destroyInfinite = function() { |
| 3823 | return this.each(function() { |
| 3824 | var infinite = $(this).data("infinite"); |
| 3825 | if(infinite && infinite.detachEvents) infinite.detachEvents(); |
| 3826 | }); |
| 3827 | } |
| 3828 | |
| 3829 | }($); |
| 3830 | |
| 3831 | /* global $:true */ |
| 3832 | +function ($) { |
| 3833 | "use strict"; |
| 3834 | |
| 3835 | var ITEM_ON = "weui-bar__item--on"; |
| 3836 | |
| 3837 | var showTab = function(a) { |
| 3838 | var $a = $(a); |
| 3839 | if($a.hasClass(ITEM_ON)) return; |
| 3840 | var href = $a.attr("href"); |
| 3841 | |
| 3842 | if(!/^#/.test(href)) return ; |
| 3843 | |
| 3844 | $a.parent().find("."+ITEM_ON).removeClass(ITEM_ON); |
| 3845 | $a.addClass(ITEM_ON); |
| 3846 | |
| 3847 | var bd = $a.parents(".weui-tab").find(".weui-tab__bd"); |
| 3848 | |
| 3849 | bd.find(".weui-tab__bd-item--active").removeClass("weui-tab__bd-item--active"); |
| 3850 | |
| 3851 | $(href).addClass("weui-tab__bd-item--active"); |
| 3852 | } |
| 3853 | |
| 3854 | $.showTab = showTab; |
| 3855 | |
| 3856 | $(document).on("click", ".weui-navbar__item, .weui-tabbar__item", function(e) { |
| 3857 | var $a = $(e.currentTarget); |
| 3858 | var href = $a.attr("href"); |
| 3859 | if($a.hasClass(ITEM_ON)) return; |
| 3860 | if(!/^#/.test(href)) return; |
| 3861 | |
| 3862 | e.preventDefault(); |
| 3863 | |
| 3864 | showTab($a); |
| 3865 | }); |
| 3866 | |
| 3867 | }($); |
| 3868 | |
| 3869 | /* global $:true */ |
| 3870 | + function($) { |
| 3871 | "use strict"; |
| 3872 | |
| 3873 | $(document).on("click touchstart", ".weui-search-bar__label", function(e) { |
| 3874 | $(e.target).parents(".weui-search-bar").addClass("weui-search-bar_focusing").find('input').focus(); |
| 3875 | }) |
| 3876 | /* |
| 3877 | .on("blur", ".weui-search-bar__input", function(e) { |
| 3878 | var $input = $(e.target); |
| 3879 | if(!$input.val()) $input.parents(".weui-search-bar").removeClass("weui-search-bar_focusing"); |
| 3880 | }) |
| 3881 | */ |
| 3882 | .on("click", ".weui-search-bar__cancel-btn", function(e) { |
| 3883 | var $input = $(e.target).parents(".weui-search-bar").removeClass("weui-search-bar_focusing").find(".weui-search-bar__input").val("").blur(); |
| 3884 | }) |
| 3885 | .on("click", ".weui-icon-clear", function(e) { |
| 3886 | var $input = $(e.target).parents(".weui-search-bar").find(".weui-search-bar__input").val("").focus(); |
| 3887 | }); |
| 3888 | |
| 3889 | }($); |
| 3890 | |
| 3891 | /*=========================== |
| 3892 | Device/OS Detection |
| 3893 | ===========================*/ |
| 3894 | /* global $:true */ |
| 3895 | ;(function ($) { |
| 3896 | "use strict"; |
| 3897 | var device = {}; |
| 3898 | var ua = navigator.userAgent; |
| 3899 | |
| 3900 | var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); |
| 3901 | var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); |
| 3902 | var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); |
| 3903 | var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/); |
| 3904 | |
| 3905 | device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false; |
| 3906 | |
| 3907 | // Android |
| 3908 | if (android) { |
| 3909 | device.os = 'android'; |
| 3910 | device.osVersion = android[2]; |
| 3911 | device.android = true; |
| 3912 | device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0; |
| 3913 | } |
| 3914 | if (ipad || iphone || ipod) { |
| 3915 | device.os = 'ios'; |
| 3916 | device.ios = true; |
| 3917 | } |
| 3918 | // iOS |
| 3919 | if (iphone && !ipod) { |
| 3920 | device.osVersion = iphone[2].replace(/_/g, '.'); |
| 3921 | device.iphone = true; |
| 3922 | } |
| 3923 | if (ipad) { |
| 3924 | device.osVersion = ipad[2].replace(/_/g, '.'); |
| 3925 | device.ipad = true; |
| 3926 | } |
| 3927 | if (ipod) { |
| 3928 | device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null; |
| 3929 | device.iphone = true; |
| 3930 | } |
| 3931 | // iOS 8+ changed UA |
| 3932 | if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) { |
| 3933 | if (device.osVersion.split('.')[0] === '10') { |
| 3934 | device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0]; |
| 3935 | } |
| 3936 | } |
| 3937 | |
| 3938 | // Webview |
| 3939 | device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i); |
| 3940 | |
| 3941 | // Minimal UI |
| 3942 | if (device.os && device.os === 'ios') { |
| 3943 | var osVersionArr = device.osVersion.split('.'); |
| 3944 | device.minimalUi = !device.webView && |
| 3945 | (ipod || iphone) && |
| 3946 | (osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) && |
| 3947 | $('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0; |
| 3948 | } |
| 3949 | |
| 3950 | // Check for status bar and fullscreen app mode |
| 3951 | var windowWidth = $(window).width(); |
| 3952 | var windowHeight = $(window).height(); |
| 3953 | device.statusBar = false; |
| 3954 | if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) { |
| 3955 | device.statusBar = true; |
| 3956 | } |
| 3957 | else { |
| 3958 | device.statusBar = false; |
| 3959 | } |
| 3960 | |
| 3961 | // Classes |
| 3962 | var classNames = []; |
| 3963 | |
| 3964 | // Pixel Ratio |
| 3965 | device.pixelRatio = window.devicePixelRatio || 1; |
| 3966 | classNames.push('pixel-ratio-' + Math.floor(device.pixelRatio)); |
| 3967 | if (device.pixelRatio >= 2) { |
| 3968 | classNames.push('retina'); |
| 3969 | } |
| 3970 | |
| 3971 | // OS classes |
| 3972 | if (device.os) { |
| 3973 | classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-')); |
| 3974 | if (device.os === 'ios') { |
| 3975 | var major = parseInt(device.osVersion.split('.')[0], 10); |
| 3976 | for (var i = major - 1; i >= 6; i--) { |
| 3977 | classNames.push('ios-gt-' + i); |
| 3978 | } |
| 3979 | } |
| 3980 | |
| 3981 | } |
| 3982 | // Status bar classes |
| 3983 | if (device.statusBar) { |
| 3984 | classNames.push('with-statusbar-overlay'); |
| 3985 | } |
| 3986 | else { |
| 3987 | $('html').removeClass('with-statusbar-overlay'); |
| 3988 | } |
| 3989 | |
| 3990 | // Add html classes |
| 3991 | if (classNames.length > 0) $('html').addClass(classNames.join(' ')); |
| 3992 | |
| 3993 | $.device = device; |
| 3994 | })($); |
| 3995 | |
| 3996 | /*====================================================== |
| 3997 | ************ Picker ************ |
| 3998 | ======================================================*/ |
| 3999 | /* global $:true */ |
| 4000 | /* jshint unused:false */ |
| 4001 | /* jshint multistr:true */ |
| 4002 | + function($) { |
| 4003 | "use strict"; |
| 4004 | var Picker = function (params) { |
| 4005 | var p = this; |
| 4006 | var defaults = { |
| 4007 | updateValuesOnMomentum: false, |
| 4008 | updateValuesOnTouchmove: true, |
| 4009 | rotateEffect: false, |
| 4010 | momentumRatio: 7, |
| 4011 | freeMode: false, |
| 4012 | // Common settings |
| 4013 | scrollToInput: true, |
| 4014 | inputReadOnly: true, |
| 4015 | toolbar: true, |
| 4016 | toolbarCloseText: '完成', |
| 4017 | title: '请选择', |
| 4018 | toolbarTemplate: '<div class="toolbar">\ |
| 4019 | <div class="toolbar-inner">\ |
| 4020 | <a href="javascript:;" class="picker-button close-picker">{{closeText}}</a>\ |
| 4021 | <h1 class="title">{{title}}</h1>\ |
| 4022 | </div>\ |
| 4023 | </div>', |
| 4024 | }; |
| 4025 | params = params || {}; |
| 4026 | for (var def in defaults) { |
| 4027 | if (typeof params[def] === 'undefined') { |
| 4028 | params[def] = defaults[def]; |
| 4029 | } |
| 4030 | } |
| 4031 | p.params = params; |
| 4032 | p.cols = []; |
| 4033 | p.initialized = false; |
| 4034 | |
| 4035 | // Inline flag |
| 4036 | p.inline = p.params.container ? true : false; |
| 4037 | |
| 4038 | // 3D Transforms origin bug, only on safari |
| 4039 | var originBug = $.device.ios || (navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && navigator.userAgent.toLowerCase().indexOf('chrome') < 0) && !$.device.android; |
| 4040 | |
| 4041 | // Should be converted to popover |
| 4042 | function isPopover() { |
| 4043 | var toPopover = false; |
| 4044 | if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover; |
| 4045 | if (!p.inline && p.params.input) { |
| 4046 | if (p.params.onlyInPopover) toPopover = true; |
| 4047 | else { |
| 4048 | if ($.device.ios) { |
| 4049 | toPopover = $.device.ipad ? true : false; |
| 4050 | } |
| 4051 | else { |
| 4052 | if ($(window).width() >= 768) toPopover = true; |
| 4053 | } |
| 4054 | } |
| 4055 | } |
| 4056 | return toPopover; |
| 4057 | } |
| 4058 | function inPopover() { |
| 4059 | if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true; |
| 4060 | else return false; |
| 4061 | } |
| 4062 | |
| 4063 | // Value |
| 4064 | p.setValue = function (arrValues, transition) { |
| 4065 | var valueIndex = 0; |
| 4066 | for (var i = 0; i < p.cols.length; i++) { |
| 4067 | if (p.cols[i] && !p.cols[i].divider) { |
| 4068 | p.cols[i].setValue(arrValues[valueIndex], transition); |
| 4069 | valueIndex++; |
| 4070 | } |
| 4071 | } |
| 4072 | }; |
| 4073 | p.updateValue = function () { |
| 4074 | var newValue = []; |
| 4075 | var newDisplayValue = []; |
| 4076 | for (var i = 0; i < p.cols.length; i++) { |
| 4077 | if (!p.cols[i].divider) { |
| 4078 | newValue.push(p.cols[i].value); |
| 4079 | newDisplayValue.push(p.cols[i].displayValue); |
| 4080 | } |
| 4081 | } |
| 4082 | if (newValue.indexOf(undefined) >= 0) { |
| 4083 | return; |
| 4084 | } |
| 4085 | p.value = newValue; |
| 4086 | p.displayValue = newDisplayValue; |
| 4087 | if (p.params.onChange) { |
| 4088 | p.params.onChange(p, p.value, p.displayValue); |
| 4089 | } |
| 4090 | if (p.input && p.input.length > 0) { |
| 4091 | $(p.input).val(p.params.formatValue ? p.params.formatValue(p, p.value, p.displayValue) : p.value.join(' ')); |
| 4092 | $(p.input).trigger('change'); |
| 4093 | } |
| 4094 | }; |
| 4095 | |
| 4096 | // Columns Handlers |
| 4097 | p.initPickerCol = function (colElement, updateItems) { |
| 4098 | var colContainer = $(colElement); |
| 4099 | var colIndex = colContainer.index(); |
| 4100 | var col = p.cols[colIndex]; |
| 4101 | if (col.divider) return; |
| 4102 | col.container = colContainer; |
| 4103 | col.wrapper = col.container.find('.picker-items-col-wrapper'); |
| 4104 | col.items = col.wrapper.find('.picker-item'); |
| 4105 | |
| 4106 | var i, j; |
| 4107 | var wrapperHeight, itemHeight, itemsHeight, minTranslate, maxTranslate; |
| 4108 | col.replaceValues = function (values, displayValues) { |
| 4109 | col.destroyEvents(); |
| 4110 | col.values = values; |
| 4111 | col.displayValues = displayValues; |
| 4112 | var newItemsHTML = p.columnHTML(col, true); |
| 4113 | col.wrapper.html(newItemsHTML); |
| 4114 | col.items = col.wrapper.find('.picker-item'); |
| 4115 | col.calcSize(); |
| 4116 | col.setValue(col.values[0] || '', 0, true); |
| 4117 | col.initEvents(); |
| 4118 | }; |
| 4119 | col.calcSize = function () { |
| 4120 | if (!col.values.length) return; |
| 4121 | if (p.params.rotateEffect) { |
| 4122 | col.container.removeClass('picker-items-col-absolute'); |
| 4123 | if (!col.width) col.container.css({width:''}); |
| 4124 | } |
| 4125 | var colWidth, colHeight; |
| 4126 | colWidth = 0; |
| 4127 | colHeight = col.container[0].offsetHeight; |
| 4128 | wrapperHeight = col.wrapper[0].offsetHeight; |
| 4129 | itemHeight = col.items[0].offsetHeight; |
| 4130 | itemsHeight = itemHeight * col.items.length; |
| 4131 | minTranslate = colHeight / 2 - itemsHeight + itemHeight / 2; |
| 4132 | maxTranslate = colHeight / 2 - itemHeight / 2; |
| 4133 | if (col.width) { |
| 4134 | colWidth = col.width; |
| 4135 | if (parseInt(colWidth, 10) === colWidth) colWidth = colWidth + 'px'; |
| 4136 | col.container.css({width: colWidth}); |
| 4137 | } |
| 4138 | if (p.params.rotateEffect) { |
| 4139 | if (!col.width) { |
| 4140 | col.items.each(function () { |
| 4141 | var item = $(this); |
| 4142 | item.css({width:'auto'}); |
| 4143 | colWidth = Math.max(colWidth, item[0].offsetWidth); |
| 4144 | item.css({width:''}); |
| 4145 | }); |
| 4146 | col.container.css({width: (colWidth + 2) + 'px'}); |
| 4147 | } |
| 4148 | col.container.addClass('picker-items-col-absolute'); |
| 4149 | } |
| 4150 | }; |
| 4151 | col.calcSize(); |
| 4152 | |
| 4153 | col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)').transition(0); |
| 4154 | |
| 4155 | |
| 4156 | var activeIndex = 0; |
| 4157 | var animationFrameId; |
| 4158 | |
| 4159 | // Set Value Function |
| 4160 | col.setValue = function (newValue, transition, valueCallbacks) { |
| 4161 | if (typeof transition === 'undefined') transition = ''; |
| 4162 | var newActiveIndex = col.wrapper.find('.picker-item[data-picker-value="' + newValue + '"]').index(); |
| 4163 | if(typeof newActiveIndex === 'undefined' || newActiveIndex === -1) { |
| 4164 | col.value = col.displayValue = newValue; |
| 4165 | return; |
| 4166 | } |
| 4167 | var newTranslate = -newActiveIndex * itemHeight + maxTranslate; |
| 4168 | // Update wrapper |
| 4169 | col.wrapper.transition(transition); |
| 4170 | col.wrapper.transform('translate3d(0,' + (newTranslate) + 'px,0)'); |
| 4171 | |
| 4172 | // Watch items |
| 4173 | if (p.params.updateValuesOnMomentum && col.activeIndex && col.activeIndex !== newActiveIndex ) { |
| 4174 | $.cancelAnimationFrame(animationFrameId); |
| 4175 | col.wrapper.transitionEnd(function(){ |
| 4176 | $.cancelAnimationFrame(animationFrameId); |
| 4177 | }); |
| 4178 | updateDuringScroll(); |
| 4179 | } |
| 4180 | |
| 4181 | // Update items |
| 4182 | col.updateItems(newActiveIndex, newTranslate, transition, valueCallbacks); |
| 4183 | }; |
| 4184 | |
| 4185 | col.updateItems = function (activeIndex, translate, transition, valueCallbacks) { |
| 4186 | if (typeof translate === 'undefined') { |
| 4187 | translate = $.getTranslate(col.wrapper[0], 'y'); |
| 4188 | } |
| 4189 | if(typeof activeIndex === 'undefined') activeIndex = -Math.round((translate - maxTranslate)/itemHeight); |
| 4190 | if (activeIndex < 0) activeIndex = 0; |
| 4191 | if (activeIndex >= col.items.length) activeIndex = col.items.length - 1; |
| 4192 | var previousActiveIndex = col.activeIndex; |
| 4193 | col.activeIndex = activeIndex; |
| 4194 | /* |
| 4195 | col.wrapper.find('.picker-selected, .picker-after-selected, .picker-before-selected').removeClass('picker-selected picker-after-selected picker-before-selected'); |
| 4196 | |
| 4197 | col.items.transition(transition); |
| 4198 | var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform(''); |
| 4199 | var prevItems = selectedItem.prevAll().addClass('picker-before-selected'); |
| 4200 | var nextItems = selectedItem.nextAll().addClass('picker-after-selected'); |
| 4201 | */ |
| 4202 | //去掉 .picker-after-selected, .picker-before-selected 以提高性能 |
| 4203 | col.wrapper.find('.picker-selected').removeClass('picker-selected'); |
| 4204 | if (p.params.rotateEffect) { |
| 4205 | col.items.transition(transition); |
| 4206 | } |
| 4207 | var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform(''); |
| 4208 | |
| 4209 | if (valueCallbacks || typeof valueCallbacks === 'undefined') { |
| 4210 | // Update values |
| 4211 | col.value = selectedItem.attr('data-picker-value'); |
| 4212 | col.displayValue = col.displayValues ? col.displayValues[activeIndex] : col.value; |
| 4213 | // On change callback |
| 4214 | if (previousActiveIndex !== activeIndex) { |
| 4215 | if (col.onChange) { |
| 4216 | col.onChange(p, col.value, col.displayValue); |
| 4217 | } |
| 4218 | p.updateValue(); |
| 4219 | } |
| 4220 | } |
| 4221 | |
| 4222 | // Set 3D rotate effect |
| 4223 | if (!p.params.rotateEffect) { |
| 4224 | return; |
| 4225 | } |
| 4226 | var percentage = (translate - (Math.floor((translate - maxTranslate)/itemHeight) * itemHeight + maxTranslate)) / itemHeight; |
| 4227 | |
| 4228 | col.items.each(function () { |
| 4229 | var item = $(this); |
| 4230 | var itemOffsetTop = item.index() * itemHeight; |
| 4231 | var translateOffset = maxTranslate - translate; |
| 4232 | var itemOffset = itemOffsetTop - translateOffset; |
| 4233 | var percentage = itemOffset / itemHeight; |
| 4234 | |
| 4235 | var itemsFit = Math.ceil(col.height / itemHeight / 2) + 1; |
| 4236 | |
| 4237 | var angle = (-18*percentage); |
| 4238 | if (angle > 180) angle = 180; |
| 4239 | if (angle < -180) angle = -180; |
| 4240 | // Far class |
| 4241 | if (Math.abs(percentage) > itemsFit) item.addClass('picker-item-far'); |
| 4242 | else item.removeClass('picker-item-far'); |
| 4243 | // Set transform |
| 4244 | item.transform('translate3d(0, ' + (-translate + maxTranslate) + 'px, ' + (originBug ? -110 : 0) + 'px) rotateX(' + angle + 'deg)'); |
| 4245 | }); |
| 4246 | }; |
| 4247 | |
| 4248 | function updateDuringScroll() { |
| 4249 | animationFrameId = $.requestAnimationFrame(function () { |
| 4250 | col.updateItems(undefined, undefined, 0); |
| 4251 | updateDuringScroll(); |
| 4252 | }); |
| 4253 | } |
| 4254 | |
| 4255 | // Update items on init |
| 4256 | if (updateItems) col.updateItems(0, maxTranslate, 0); |
| 4257 | |
| 4258 | var allowItemClick = true; |
| 4259 | var isTouched, isMoved, touchStartY, touchCurrentY, touchStartTime, touchEndTime, startTranslate, returnTo, currentTranslate, prevTranslate, velocityTranslate, velocityTime; |
| 4260 | function handleTouchStart (e) { |
| 4261 | if (isMoved || isTouched) return; |
| 4262 | e.preventDefault(); |
| 4263 | isTouched = true; |
| 4264 | var position = $.getTouchPosition(e); |
| 4265 | touchStartY = touchCurrentY = position.y; |
| 4266 | touchStartTime = (new Date()).getTime(); |
| 4267 | |
| 4268 | allowItemClick = true; |
| 4269 | startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y'); |
| 4270 | } |
| 4271 | function handleTouchMove (e) { |
| 4272 | if (!isTouched) return; |
| 4273 | e.preventDefault(); |
| 4274 | allowItemClick = false; |
| 4275 | var position = $.getTouchPosition(e); |
| 4276 | touchCurrentY = position.y; |
| 4277 | if (!isMoved) { |
| 4278 | // First move |
| 4279 | $.cancelAnimationFrame(animationFrameId); |
| 4280 | isMoved = true; |
| 4281 | startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y'); |
| 4282 | col.wrapper.transition(0); |
| 4283 | } |
| 4284 | e.preventDefault(); |
| 4285 | |
| 4286 | var diff = touchCurrentY - touchStartY; |
| 4287 | currentTranslate = startTranslate + diff; |
| 4288 | returnTo = undefined; |
| 4289 | |
| 4290 | // Normalize translate |
| 4291 | if (currentTranslate < minTranslate) { |
| 4292 | currentTranslate = minTranslate - Math.pow(minTranslate - currentTranslate, 0.8); |
| 4293 | returnTo = 'min'; |
| 4294 | } |
| 4295 | if (currentTranslate > maxTranslate) { |
| 4296 | currentTranslate = maxTranslate + Math.pow(currentTranslate - maxTranslate, 0.8); |
| 4297 | returnTo = 'max'; |
| 4298 | } |
| 4299 | // Transform wrapper |
| 4300 | col.wrapper.transform('translate3d(0,' + currentTranslate + 'px,0)'); |
| 4301 | |
| 4302 | // Update items |
| 4303 | col.updateItems(undefined, currentTranslate, 0, p.params.updateValuesOnTouchmove); |
| 4304 | |
| 4305 | // Calc velocity |
| 4306 | velocityTranslate = currentTranslate - prevTranslate || currentTranslate; |
| 4307 | velocityTime = (new Date()).getTime(); |
| 4308 | prevTranslate = currentTranslate; |
| 4309 | } |
| 4310 | function handleTouchEnd (e) { |
| 4311 | if (!isTouched || !isMoved) { |
| 4312 | isTouched = isMoved = false; |
| 4313 | return; |
| 4314 | } |
| 4315 | isTouched = isMoved = false; |
| 4316 | col.wrapper.transition(''); |
| 4317 | if (returnTo) { |
| 4318 | if (returnTo === 'min') { |
| 4319 | col.wrapper.transform('translate3d(0,' + minTranslate + 'px,0)'); |
| 4320 | } |
| 4321 | else col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)'); |
| 4322 | } |
| 4323 | touchEndTime = new Date().getTime(); |
| 4324 | var velocity, newTranslate; |
| 4325 | if (touchEndTime - touchStartTime > 300) { |
| 4326 | newTranslate = currentTranslate; |
| 4327 | } |
| 4328 | else { |
| 4329 | velocity = Math.abs(velocityTranslate / (touchEndTime - velocityTime)); |
| 4330 | newTranslate = currentTranslate + velocityTranslate * p.params.momentumRatio; |
| 4331 | } |
| 4332 | |
| 4333 | newTranslate = Math.max(Math.min(newTranslate, maxTranslate), minTranslate); |
| 4334 | |
| 4335 | // Active Index |
| 4336 | var activeIndex = -Math.floor((newTranslate - maxTranslate)/itemHeight); |
| 4337 | |
| 4338 | // Normalize translate |
| 4339 | if (!p.params.freeMode) newTranslate = -activeIndex * itemHeight + maxTranslate; |
| 4340 | |
| 4341 | // Transform wrapper |
| 4342 | col.wrapper.transform('translate3d(0,' + (parseInt(newTranslate,10)) + 'px,0)'); |
| 4343 | |
| 4344 | // Update items |
| 4345 | col.updateItems(activeIndex, newTranslate, '', true); |
| 4346 | |
| 4347 | // Watch items |
| 4348 | if (p.params.updateValuesOnMomentum) { |
| 4349 | updateDuringScroll(); |
| 4350 | col.wrapper.transitionEnd(function(){ |
| 4351 | $.cancelAnimationFrame(animationFrameId); |
| 4352 | }); |
| 4353 | } |
| 4354 | |
| 4355 | // Allow click |
| 4356 | setTimeout(function () { |
| 4357 | allowItemClick = true; |
| 4358 | }, 100); |
| 4359 | } |
| 4360 | |
| 4361 | function handleClick(e) { |
| 4362 | if (!allowItemClick) return; |
| 4363 | $.cancelAnimationFrame(animationFrameId); |
| 4364 | /*jshint validthis:true */ |
| 4365 | var value = $(this).attr('data-picker-value'); |
| 4366 | col.setValue(value); |
| 4367 | } |
| 4368 | |
| 4369 | col.initEvents = function (detach) { |
| 4370 | var method = detach ? 'off' : 'on'; |
| 4371 | col.container[method]($.touchEvents.start, handleTouchStart); |
| 4372 | col.container[method]($.touchEvents.move, handleTouchMove); |
| 4373 | col.container[method]($.touchEvents.end, handleTouchEnd); |
| 4374 | col.items[method]('click', handleClick); |
| 4375 | }; |
| 4376 | col.destroyEvents = function () { |
| 4377 | col.initEvents(true); |
| 4378 | }; |
| 4379 | |
| 4380 | col.container[0].f7DestroyPickerCol = function () { |
| 4381 | col.destroyEvents(); |
| 4382 | }; |
| 4383 | |
| 4384 | col.initEvents(); |
| 4385 | |
| 4386 | }; |
| 4387 | p.destroyPickerCol = function (colContainer) { |
| 4388 | colContainer = $(colContainer); |
| 4389 | if ('f7DestroyPickerCol' in colContainer[0]) colContainer[0].f7DestroyPickerCol(); |
| 4390 | }; |
| 4391 | // Resize cols |
| 4392 | function resizeCols() { |
| 4393 | if (!p.opened) return; |
| 4394 | for (var i = 0; i < p.cols.length; i++) { |
| 4395 | if (!p.cols[i].divider) { |
| 4396 | p.cols[i].calcSize(); |
| 4397 | p.cols[i].setValue(p.cols[i].value, 0, false); |
| 4398 | } |
| 4399 | } |
| 4400 | } |
| 4401 | $(window).on('resize', resizeCols); |
| 4402 | |
| 4403 | // HTML Layout |
| 4404 | p.columnHTML = function (col, onlyItems) { |
| 4405 | var columnItemsHTML = ''; |
| 4406 | var columnHTML = ''; |
| 4407 | if (col.divider) { |
| 4408 | columnHTML += '<div class="picker-items-col picker-items-col-divider ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '">' + col.content + '</div>'; |
| 4409 | } |
| 4410 | else { |
| 4411 | for (var j = 0; j < col.values.length; j++) { |
| 4412 | columnItemsHTML += '<div class="picker-item" data-picker-value="' + col.values[j] + '">' + (col.displayValues ? col.displayValues[j] : col.values[j]) + '</div>'; |
| 4413 | } |
| 4414 | columnHTML += '<div class="picker-items-col ' + (col.textAlign ? 'picker-items-col-' + col.textAlign : '') + ' ' + (col.cssClass || '') + '"><div class="picker-items-col-wrapper">' + columnItemsHTML + '</div></div>'; |
| 4415 | } |
| 4416 | return onlyItems ? columnItemsHTML : columnHTML; |
| 4417 | }; |
| 4418 | p.layout = function () { |
| 4419 | var pickerHTML = ''; |
| 4420 | var pickerClass = ''; |
| 4421 | var i; |
| 4422 | p.cols = []; |
| 4423 | var colsHTML = ''; |
| 4424 | for (i = 0; i < p.params.cols.length; i++) { |
| 4425 | var col = p.params.cols[i]; |
| 4426 | colsHTML += p.columnHTML(p.params.cols[i]); |
| 4427 | p.cols.push(col); |
| 4428 | } |
| 4429 | pickerClass = 'weui-picker-modal picker-columns ' + (p.params.cssClass || '') + (p.params.rotateEffect ? ' picker-3d' : '') + (p.params.cols.length === 1 ? ' picker-columns-single' : ''); |
| 4430 | pickerHTML = |
| 4431 | '<div class="' + (pickerClass) + '">' + |
| 4432 | (p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText).replace(/{{title}}/g, p.params.title) : '') + |
| 4433 | '<div class="picker-modal-inner picker-items">' + |
| 4434 | colsHTML + |
| 4435 | '<div class="picker-center-highlight"></div>' + |
| 4436 | '</div>' + |
| 4437 | '</div>'; |
| 4438 | |
| 4439 | p.pickerHTML = pickerHTML; |
| 4440 | }; |
| 4441 | |
| 4442 | // Input Events |
| 4443 | function openOnInput(e) { |
| 4444 | e.preventDefault(); |
| 4445 | if (p.opened) return; |
| 4446 | p.open(); |
| 4447 | if (p.params.scrollToInput && !isPopover()) { |
| 4448 | var pageContent = p.input.parents('.content'); |
| 4449 | if (pageContent.length === 0) return; |
| 4450 | |
| 4451 | var paddingTop = parseInt(pageContent.css('padding-top'), 10), |
| 4452 | paddingBottom = parseInt(pageContent.css('padding-bottom'), 10), |
| 4453 | pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(), |
| 4454 | pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(), |
| 4455 | newPaddingBottom; |
| 4456 | var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight; |
| 4457 | if (inputTop > pageHeight) { |
| 4458 | var scrollTop = pageContent.scrollTop() + inputTop - pageHeight; |
| 4459 | if (scrollTop + pageHeight > pageScrollHeight) { |
| 4460 | newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom; |
| 4461 | if (pageHeight === pageScrollHeight) { |
| 4462 | newPaddingBottom = p.container.height(); |
| 4463 | } |
| 4464 | pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'}); |
| 4465 | } |
| 4466 | pageContent.scrollTop(scrollTop, 300); |
| 4467 | } |
| 4468 | } |
| 4469 | } |
| 4470 | function closeOnHTMLClick(e) { |
| 4471 | if (inPopover()) return; |
| 4472 | if (p.input && p.input.length > 0) { |
| 4473 | if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close(); |
| 4474 | } |
| 4475 | else { |
| 4476 | if ($(e.target).parents('.weui-picker-modal').length === 0) p.close(); |
| 4477 | } |
| 4478 | } |
| 4479 | |
| 4480 | if (p.params.input) { |
| 4481 | p.input = $(p.params.input); |
| 4482 | if (p.input.length > 0) { |
| 4483 | if (p.params.inputReadOnly) p.input.prop('readOnly', true); |
| 4484 | if (!p.inline) { |
| 4485 | p.input.on('click', openOnInput); |
| 4486 | } |
| 4487 | if (p.params.inputReadOnly) { |
| 4488 | p.input.on('focus mousedown', function (e) { |
| 4489 | e.preventDefault(); |
| 4490 | }); |
| 4491 | } |
| 4492 | } |
| 4493 | |
| 4494 | } |
| 4495 | |
| 4496 | if (!p.inline) $('html').on('click', closeOnHTMLClick); |
| 4497 | |
| 4498 | // Open |
| 4499 | function onPickerClose() { |
| 4500 | p.opened = false; |
| 4501 | if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''}); |
| 4502 | if (p.params.onClose) p.params.onClose(p); |
| 4503 | |
| 4504 | // Destroy events |
| 4505 | p.container.find('.picker-items-col').each(function () { |
| 4506 | p.destroyPickerCol(this); |
| 4507 | }); |
| 4508 | } |
| 4509 | |
| 4510 | p.opened = false; |
| 4511 | p.open = function () { |
| 4512 | var toPopover = isPopover(); |
| 4513 | |
| 4514 | if (!p.opened) { |
| 4515 | |
| 4516 | // Layout |
| 4517 | p.layout(); |
| 4518 | |
| 4519 | // Append |
| 4520 | if (toPopover) { |
| 4521 | p.pickerHTML = '<div class="popover popover-picker-columns"><div class="popover-inner">' + p.pickerHTML + '</div></div>'; |
| 4522 | p.popover = $.popover(p.pickerHTML, p.params.input, true); |
| 4523 | p.container = $(p.popover).find('.weui-picker-modal'); |
| 4524 | $(p.popover).on('close', function () { |
| 4525 | onPickerClose(); |
| 4526 | }); |
| 4527 | } |
| 4528 | else if (p.inline) { |
| 4529 | p.container = $(p.pickerHTML); |
| 4530 | p.container.addClass('picker-modal-inline'); |
| 4531 | $(p.params.container).append(p.container); |
| 4532 | } |
| 4533 | else { |
| 4534 | p.container = $($.openPicker(p.pickerHTML)); |
| 4535 | $(p.container) |
| 4536 | .on('close', function () { |
| 4537 | onPickerClose(); |
| 4538 | }); |
| 4539 | } |
| 4540 | |
| 4541 | // Store picker instance |
| 4542 | p.container[0].f7Picker = p; |
| 4543 | |
| 4544 | // Init Events |
| 4545 | p.container.find('.picker-items-col').each(function () { |
| 4546 | var updateItems = true; |
| 4547 | if ((!p.initialized && p.params.value) || (p.initialized && p.value)) updateItems = false; |
| 4548 | p.initPickerCol(this, updateItems); |
| 4549 | }); |
| 4550 | |
| 4551 | // Set value |
| 4552 | if (!p.initialized) { |
| 4553 | if (p.params.value) { |
| 4554 | p.setValue(p.params.value, 0); |
| 4555 | } |
| 4556 | } |
| 4557 | else { |
| 4558 | if (p.value) p.setValue(p.value, 0); |
| 4559 | } |
| 4560 | } |
| 4561 | |
| 4562 | // Set flag |
| 4563 | p.opened = true; |
| 4564 | p.initialized = true; |
| 4565 | |
| 4566 | if (p.params.onOpen) p.params.onOpen(p); |
| 4567 | }; |
| 4568 | |
| 4569 | // Close |
| 4570 | p.close = function (force) { |
| 4571 | if (!p.opened || p.inline) return; |
| 4572 | if (inPopover()) { |
| 4573 | $.closePicker(p.popover); |
| 4574 | return; |
| 4575 | } |
| 4576 | else { |
| 4577 | $.closePicker(p.container); |
| 4578 | return; |
| 4579 | } |
| 4580 | }; |
| 4581 | |
| 4582 | // Destroy |
| 4583 | p.destroy = function () { |
| 4584 | p.close(); |
| 4585 | if (p.params.input && p.input.length > 0) { |
| 4586 | p.input.off('click focus', openOnInput); |
| 4587 | $(p.input).data('picker', null); |
| 4588 | } |
| 4589 | $('html').off('click', closeOnHTMLClick); |
| 4590 | $(window).off('resize', resizeCols); |
| 4591 | }; |
| 4592 | |
| 4593 | if (p.inline) { |
| 4594 | p.open(); |
| 4595 | } |
| 4596 | |
| 4597 | return p; |
| 4598 | }; |
| 4599 | |
| 4600 | $(document).on("click", ".close-picker", function() { |
| 4601 | var pickerToClose = $('.weui-picker-modal.weui-picker-modal-visible'); |
| 4602 | if (pickerToClose.length > 0) { |
| 4603 | $.closePicker(pickerToClose); |
| 4604 | } |
| 4605 | }); |
| 4606 | |
| 4607 | //修复picker会滚动页面的bug |
| 4608 | $(document).on($.touchEvents.move, ".picker-modal-inner", function(e) { |
| 4609 | e.preventDefault(); |
| 4610 | }); |
| 4611 | |
| 4612 | |
| 4613 | $.openPicker = function(tpl, className, callback) { |
| 4614 | |
| 4615 | if(typeof className === "function") { |
| 4616 | callback = className; |
| 4617 | className = undefined; |
| 4618 | } |
| 4619 | |
| 4620 | $.closePicker(); |
| 4621 | |
| 4622 | var container = $("<div class='weui-picker-container "+ (className || "") + "'></div>").appendTo(document.body); |
| 4623 | container.show(); |
| 4624 | |
| 4625 | container.addClass("weui-picker-container-visible"); |
| 4626 | |
| 4627 | //关于布局的问题,如果直接放在body上,则做动画的时候会撑开body高度而导致滚动条变化。 |
| 4628 | var dialog = $(tpl).appendTo(container); |
| 4629 | |
| 4630 | dialog.width(); //通过取一次CSS值,强制浏览器不能把上下两行代码合并执行,因为合并之后会导致无法出现动画。 |
| 4631 | |
| 4632 | dialog.addClass("weui-picker-modal-visible"); |
| 4633 | |
| 4634 | callback && container.on("close", callback); |
| 4635 | |
| 4636 | return dialog; |
| 4637 | } |
| 4638 | |
| 4639 | $.updatePicker = function(tpl) { |
| 4640 | var container = $(".weui-picker-container-visible"); |
| 4641 | if(!container[0]) return false; |
| 4642 | |
| 4643 | container.html(""); |
| 4644 | |
| 4645 | var dialog = $(tpl).appendTo(container); |
| 4646 | |
| 4647 | dialog.addClass("weui-picker-modal-visible"); |
| 4648 | |
| 4649 | return dialog; |
| 4650 | } |
| 4651 | |
| 4652 | $.closePicker = function(container, callback) { |
| 4653 | if(typeof container === "function") callback = container; |
| 4654 | $(".weui-picker-modal-visible").removeClass("weui-picker-modal-visible").transitionEnd(function() { |
| 4655 | $(this).parent().remove(); |
| 4656 | callback && callback(); |
| 4657 | }).trigger("close"); |
| 4658 | }; |
| 4659 | |
| 4660 | $.fn.picker = function(params) { |
| 4661 | var args = arguments; |
| 4662 | return this.each(function() { |
| 4663 | if(!this) return; |
| 4664 | var $this = $(this); |
| 4665 | |
| 4666 | var picker = $this.data("picker"); |
| 4667 | if(!picker) { |
| 4668 | params = $.extend({ input: this }, params || {}) // https://github.com/lihongxun945/jquery-weui/issues/432 |
| 4669 | var inputValue = $this.val(); |
| 4670 | if(params.value === undefined && inputValue !== "") { |
| 4671 | params.value = (params.cols && params.cols.length > 1) ? inputValue.split(" ") : [inputValue]; |
| 4672 | } |
| 4673 | var p = $.extend({input: this}, params); |
| 4674 | picker = new Picker(p); |
| 4675 | $this.data("picker", picker); |
| 4676 | } |
| 4677 | if(typeof params === typeof "a") { |
| 4678 | picker[params].apply(picker, Array.prototype.slice.call(args, 1)); |
| 4679 | } |
| 4680 | }); |
| 4681 | }; |
| 4682 | }($); |
| 4683 | |
| 4684 | /* global $:true */ |
| 4685 | + function($) { |
| 4686 | "use strict"; |
| 4687 | |
| 4688 | var defaults; |
| 4689 | |
| 4690 | var selects = []; |
| 4691 | |
| 4692 | var Select = function(input, config) { |
| 4693 | |
| 4694 | var self = this; |
| 4695 | this.config = config; |
| 4696 | |
| 4697 | //init empty data |
| 4698 | this.data = { |
| 4699 | values: '', |
| 4700 | titles: '', |
| 4701 | origins: [], |
| 4702 | length: 0 |
| 4703 | }; |
| 4704 | |
| 4705 | this.$input = $(input); |
| 4706 | this.$input.prop("readOnly", true); |
| 4707 | |
| 4708 | this.initConfig(); |
| 4709 | |
| 4710 | config = this.config; |
| 4711 | |
| 4712 | this.$input.click($.proxy(this.open, this)); |
| 4713 | selects.push(this) |
| 4714 | } |
| 4715 | |
| 4716 | Select.prototype.initConfig = function() { |
| 4717 | this.config = $.extend({}, defaults, this.config); |
| 4718 | |
| 4719 | var config = this.config; |
| 4720 | |
| 4721 | if(!config.items || !config.items.length) return; |
| 4722 | |
| 4723 | config.items = config.items.map(function(d, i) { |
| 4724 | if(typeof d == typeof "a") { |
| 4725 | return { |
| 4726 | title: d, |
| 4727 | value: d |
| 4728 | }; |
| 4729 | } |
| 4730 | |
| 4731 | return d; |
| 4732 | }); |
| 4733 | |
| 4734 | |
| 4735 | this.tpl = $.t7.compile("<div class='weui-picker-modal weui-select-modal'>" + config.toolbarTemplate + (config.multi ? config.checkboxTemplate : config.radioTemplate) + "</div>"); |
| 4736 | |
| 4737 | if(config.input !== undefined) this.$input.val(config.input); |
| 4738 | |
| 4739 | this.parseInitValue(); |
| 4740 | |
| 4741 | this._init = true; |
| 4742 | } |
| 4743 | |
| 4744 | Select.prototype.updateInputValue = function(values, titles) { |
| 4745 | var v, t; |
| 4746 | if(this.config.multi) { |
| 4747 | v = values.join(this.config.split); |
| 4748 | t = titles.join(this.config.split); |
| 4749 | } else { |
| 4750 | v = values[0]; |
| 4751 | t = titles[0]; |
| 4752 | } |
| 4753 | |
| 4754 | //caculate origin data |
| 4755 | var origins = []; |
| 4756 | |
| 4757 | this.config.items.forEach(function(d) { |
| 4758 | values.each(function(i, dd) { |
| 4759 | if(d.value == dd) origins.push(d); |
| 4760 | }); |
| 4761 | }); |
| 4762 | |
| 4763 | this.$input.val(t).data("values", v); |
| 4764 | this.$input.attr("value", t).attr("data-values", v); |
| 4765 | |
| 4766 | var data = { |
| 4767 | values: v, |
| 4768 | titles: t, |
| 4769 | valuesArray: values, |
| 4770 | titlesArray: titles, |
| 4771 | origins: origins, |
| 4772 | length: origins.length |
| 4773 | }; |
| 4774 | this.data = data; |
| 4775 | this.$input.trigger("change", data); |
| 4776 | this.config.onChange && this.config.onChange.call(this, data); |
| 4777 | } |
| 4778 | |
| 4779 | Select.prototype.parseInitValue = function() { |
| 4780 | var value = this.$input.val(); |
| 4781 | var items = this.config.items; |
| 4782 | |
| 4783 | //如果input为空,只有在第一次初始化的时候才保留默认选择。因为后来就是用户自己取消了全部选择,不能再为他选中默认值。 |
| 4784 | if( !this._init && (value === undefined || value == null || value === "")) return; |
| 4785 | |
| 4786 | var titles = this.config.multi ? value.split(this.config.split) : [value]; |
| 4787 | for(var i=0;i<items.length;i++) { |
| 4788 | items[i].checked = false; |
| 4789 | for(var j=0;j<titles.length;j++) { |
| 4790 | if(items[i].title === titles[j]) { |
| 4791 | items[i].checked = true; |
| 4792 | } |
| 4793 | } |
| 4794 | } |
| 4795 | } |
| 4796 | |
| 4797 | Select.prototype._bind = function(dialog) { |
| 4798 | var self = this, |
| 4799 | config = this.config; |
| 4800 | dialog.on("change", function(e) { |
| 4801 | var checked = dialog.find("input:checked"); |
| 4802 | var values = checked.map(function() { |
| 4803 | return $(this).val(); |
| 4804 | }); |
| 4805 | var titles = checked.map(function() { |
| 4806 | return $(this).data("title"); |
| 4807 | }); |
| 4808 | self.updateInputValue(values, titles); |
| 4809 | |
| 4810 | if(config.autoClose && !config.multi) self.close(); |
| 4811 | }) |
| 4812 | .trigger('change') |
| 4813 | .on("click", ".close-select", function() { |
| 4814 | self.close(); |
| 4815 | }); |
| 4816 | } |
| 4817 | |
| 4818 | //更新数据 |
| 4819 | Select.prototype.update = function(config) { |
| 4820 | this.config = $.extend({}, this.config, config); |
| 4821 | this.initConfig(); |
| 4822 | if(this._open) { |
| 4823 | this._bind($.updatePicker(this.getHTML())); |
| 4824 | } |
| 4825 | } |
| 4826 | |
| 4827 | Select.prototype.open = function(values, titles) { |
| 4828 | |
| 4829 | if(this._open) return; |
| 4830 | |
| 4831 | // open picker 会默认关掉其他的,但是 onClose 不会被调用,所以这里先关掉其他select |
| 4832 | for (var i = 0; i < selects.length; i++ ) { |
| 4833 | var s = selects[i]; |
| 4834 | if (s === this) continue; |
| 4835 | if (s._open) { |
| 4836 | if(!s.close()) return false; // 其他的select由于某些条件限制关闭失败。 |
| 4837 | } |
| 4838 | } |
| 4839 | |
| 4840 | this.parseInitValue(); |
| 4841 | |
| 4842 | var config = this.config; |
| 4843 | |
| 4844 | var dialog = this.dialog = $.openPicker(this.getHTML()); |
| 4845 | |
| 4846 | this._bind(dialog); |
| 4847 | |
| 4848 | this._open = true; |
| 4849 | if(config.onOpen) config.onOpen(this); |
| 4850 | } |
| 4851 | |
| 4852 | Select.prototype.close = function(callback, force) { |
| 4853 | if (!this._open) return false; |
| 4854 | var self = this, |
| 4855 | beforeClose = this.config.beforeClose; |
| 4856 | |
| 4857 | if(typeof callback === typeof true) { |
| 4858 | force === callback; |
| 4859 | } |
| 4860 | if(!force) { |
| 4861 | if(beforeClose && typeof beforeClose === 'function' && beforeClose.call(this, this.data.values, this.data.titles) === false) { |
| 4862 | return false |
| 4863 | } |
| 4864 | if(this.config.multi) { |
| 4865 | if(this.config.min !== undefined && this.data.length < this.config.min) { |
| 4866 | $.toast("请至少选择"+this.config.min+"个", "text"); |
| 4867 | return false |
| 4868 | } |
| 4869 | if(this.config.max !== undefined && this.data.length > this.config.max) { |
| 4870 | $.toast("最多只能选择"+this.config.max+"个", "text"); |
| 4871 | return false |
| 4872 | } |
| 4873 | } |
| 4874 | } |
| 4875 | $.closePicker(function() { |
| 4876 | self.onClose(); |
| 4877 | callback && callback(); |
| 4878 | }); |
| 4879 | |
| 4880 | return true |
| 4881 | } |
| 4882 | |
| 4883 | Select.prototype.onClose = function() { |
| 4884 | this._open = false; |
| 4885 | if(this.config.onClose) this.config.onClose(this); |
| 4886 | } |
| 4887 | |
| 4888 | Select.prototype.getHTML = function(callback) { |
| 4889 | var config = this.config; |
| 4890 | return this.tpl({ |
| 4891 | items: config.items, |
| 4892 | title: config.title, |
| 4893 | closeText: config.closeText |
| 4894 | }) |
| 4895 | } |
| 4896 | |
| 4897 | |
| 4898 | $.fn.select = function(params, args) { |
| 4899 | |
| 4900 | return this.each(function() { |
| 4901 | var $this = $(this); |
| 4902 | if(!$this.data("weui-select")) $this.data("weui-select", new Select(this, params)); |
| 4903 | |
| 4904 | var select = $this.data("weui-select"); |
| 4905 | |
| 4906 | if(typeof params === typeof "a") select[params].call(select, args); |
| 4907 | |
| 4908 | return select; |
| 4909 | }); |
| 4910 | } |
| 4911 | |
| 4912 | defaults = $.fn.select.prototype.defaults = { |
| 4913 | items: [], |
| 4914 | input: undefined, //输入框的初始值 |
| 4915 | title: "请选择", |
| 4916 | multi: false, |
| 4917 | closeText: "确定", |
| 4918 | autoClose: true, //是否选择完成后自动关闭,只有单选模式下才有效 |
| 4919 | onChange: undefined, //function |
| 4920 | beforeClose: undefined, // function 关闭之前,如果返回false则阻止关闭 |
| 4921 | onClose: undefined, //function |
| 4922 | onOpen: undefined, //function |
| 4923 | split: ",", //多选模式下的分隔符 |
| 4924 | min: undefined, //多选模式下可用,最少选择数 |
| 4925 | max: undefined, //单选模式下可用,最多选择数 |
| 4926 | toolbarTemplate: '<div class="toolbar">\ |
| 4927 | <div class="toolbar-inner">\ |
| 4928 | <a href="javascript:;" class="picker-button close-select">{{closeText}}</a>\ |
| 4929 | <h1 class="title">{{title}}</h1>\ |
| 4930 | </div>\ |
| 4931 | </div>', |
| 4932 | radioTemplate: |
| 4933 | '<div class="weui-cells weui-cells_radio">\ |
| 4934 | {{#items}}\ |
| 4935 | <label class="weui-cell weui-check_label" for="weui-select-id-{{this.title}}">\ |
| 4936 | <div class="weui-cell__bd weui-cell_primary">\ |
| 4937 | <p>{{this.title}}</p>\ |
| 4938 | </div>\ |
| 4939 | <div class="weui-cell__ft">\ |
| 4940 | <input type="radio" class="weui-check" name="weui-select" id="weui-select-id-{{this.title}}" value="{{this.value}}" {{#if this.checked}}checked="checked"{{/if}} data-title="{{this.title}}">\ |
| 4941 | <span class="weui-icon-checked"></span>\ |
| 4942 | </div>\ |
| 4943 | </label>\ |
| 4944 | {{/items}}\ |
| 4945 | </div>', |
| 4946 | checkboxTemplate: |
| 4947 | '<div class="weui-cells weui-cells_checkbox">\ |
| 4948 | {{#items}}\ |
| 4949 | <label class="weui-cell weui-check_label" for="weui-select-id-{{this.title}}">\ |
| 4950 | <div class="weui-cell__bd weui-cell_primary">\ |
| 4951 | <p>{{this.title}}</p>\ |
| 4952 | </div>\ |
| 4953 | <div class="weui-cell__ft">\ |
| 4954 | <input type="checkbox" class="weui-check" name="weui-select" id="weui-select-id-{{this.title}}" value="{{this.value}}" {{#if this.checked}}checked="checked"{{/if}} data-title="{{this.title}}" >\ |
| 4955 | <span class="weui-icon-checked"></span>\ |
| 4956 | </div>\ |
| 4957 | </label>\ |
| 4958 | {{/items}}\ |
| 4959 | </div>', |
| 4960 | } |
| 4961 | |
| 4962 | }($); |
| 4963 | |
| 4964 | /*====================================================== |
| 4965 | ************ Calendar ************ |
| 4966 | ======================================================*/ |
| 4967 | /* global $:true */ |
| 4968 | /*jshint unused: false*/ |
| 4969 | +function ($) { |
| 4970 | "use strict"; |
| 4971 | var rtl = false; |
| 4972 | var defaults; |
| 4973 | var isSameDate = function (a, b) { |
| 4974 | var a = new Date(a), |
| 4975 | b = new Date(b); |
| 4976 | return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate() |
| 4977 | } |
| 4978 | var Calendar = function (params) { |
| 4979 | var p = this; |
| 4980 | params = params || {}; |
| 4981 | for (var def in defaults) { |
| 4982 | if (typeof params[def] === 'undefined') { |
| 4983 | params[def] = defaults[def]; |
| 4984 | } |
| 4985 | } |
| 4986 | p.params = params; |
| 4987 | p.initialized = false; |
| 4988 | |
| 4989 | // Inline flag |
| 4990 | p.inline = p.params.container ? true : false; |
| 4991 | |
| 4992 | // Is horizontal |
| 4993 | p.isH = p.params.direction === 'horizontal'; |
| 4994 | |
| 4995 | // RTL inverter |
| 4996 | var inverter = p.isH ? (rtl ? -1 : 1) : 1; |
| 4997 | |
| 4998 | // Animating flag |
| 4999 | p.animating = false; |
| 5000 | |
| 5001 | // Should be converted to popover |
| 5002 | function isPopover() { |
| 5003 | var toPopover = false; |
| 5004 | if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover; |
| 5005 | if (!p.inline && p.params.input) { |
| 5006 | if (p.params.onlyInPopover) toPopover = true; |
| 5007 | else { |
| 5008 | if ($.device.ios) { |
| 5009 | toPopover = $.device.ipad ? true : false; |
| 5010 | } |
| 5011 | else { |
| 5012 | if ($(window).width() >= 768) toPopover = true; |
| 5013 | } |
| 5014 | } |
| 5015 | } |
| 5016 | return toPopover; |
| 5017 | } |
| 5018 | function inPopover() { |
| 5019 | if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true; |
| 5020 | else return false; |
| 5021 | } |
| 5022 | |
| 5023 | // Format date |
| 5024 | function formatDate(date) { |
| 5025 | date = new Date(date); |
| 5026 | var year = date.getFullYear(); |
| 5027 | var month = date.getMonth(); |
| 5028 | var month1 = month + 1; |
| 5029 | var day = date.getDate(); |
| 5030 | var weekDay = date.getDay(); |
| 5031 | return p.params.dateFormat |
| 5032 | .replace(/yyyy/g, year) |
| 5033 | .replace(/yy/g, (year + '').substring(2)) |
| 5034 | .replace(/mm/g, month1 < 10 ? '0' + month1 : month1) |
| 5035 | .replace(/m/g, month1) |
| 5036 | .replace(/MM/g, p.params.monthNames[month]) |
| 5037 | .replace(/M/g, p.params.monthNamesShort[month]) |
| 5038 | .replace(/dd/g, day < 10 ? '0' + day : day) |
| 5039 | .replace(/d/g, day) |
| 5040 | .replace(/DD/g, p.params.dayNames[weekDay]) |
| 5041 | .replace(/D/g, p.params.dayNamesShort[weekDay]); |
| 5042 | } |
| 5043 | |
| 5044 | |
| 5045 | // Value |
| 5046 | p.addValue = function (value) { |
| 5047 | if (p.params.multiple) { |
| 5048 | if (!p.value) p.value = []; |
| 5049 | var inValuesIndex; |
| 5050 | for (var i = 0; i < p.value.length; i++) { |
| 5051 | if (isSameDate(value, p.value[i])) { |
| 5052 | inValuesIndex = i; |
| 5053 | } |
| 5054 | } |
| 5055 | if (typeof inValuesIndex === 'undefined') { |
| 5056 | p.value.push(value); |
| 5057 | } |
| 5058 | else { |
| 5059 | p.value.splice(inValuesIndex, 1); |
| 5060 | } |
| 5061 | p.updateValue(); |
| 5062 | } |
| 5063 | else { |
| 5064 | p.value = [value]; |
| 5065 | p.updateValue(); |
| 5066 | } |
| 5067 | }; |
| 5068 | p.setValue = function (arrValues) { |
| 5069 | var date = new Date(arrValues[0]); |
| 5070 | p.setYearMonth(date.getFullYear(), date.getMonth()); |
| 5071 | p.addValue(+ date); |
| 5072 | }; |
| 5073 | p.updateValue = function () { |
| 5074 | p.wrapper.find('.picker-calendar-day-selected').removeClass('picker-calendar-day-selected'); |
| 5075 | var i, inputValue; |
| 5076 | for (i = 0; i < p.value.length; i++) { |
| 5077 | var valueDate = new Date(p.value[i]); |
| 5078 | p.wrapper.find('.picker-calendar-day[data-date="' + valueDate.getFullYear() + '-' + valueDate.getMonth() + '-' + valueDate.getDate() + '"]').addClass('picker-calendar-day-selected'); |
| 5079 | } |
| 5080 | if (p.params.onChange) { |
| 5081 | p.params.onChange(p, p.value.map(formatDate), p.value.map(function (d) { |
| 5082 | return + new Date(typeof d === typeof 'a' ? d.split(/\D/).filter(function (a) { return !!a; }).join("-") : d); |
| 5083 | })); |
| 5084 | } |
| 5085 | if (p.input && p.input.length > 0) { |
| 5086 | if (p.params.formatValue) inputValue = p.params.formatValue(p, p.value); |
| 5087 | else { |
| 5088 | inputValue = []; |
| 5089 | for (i = 0; i < p.value.length; i++) { |
| 5090 | inputValue.push(formatDate(p.value[i])); |
| 5091 | } |
| 5092 | inputValue = inputValue.join(', '); |
| 5093 | } |
| 5094 | $(p.input).val(inputValue); |
| 5095 | $(p.input).trigger('change'); |
| 5096 | } |
| 5097 | }; |
| 5098 | |
| 5099 | // Columns Handlers |
| 5100 | p.initCalendarEvents = function () { |
| 5101 | var col; |
| 5102 | var allowItemClick = true; |
| 5103 | var isTouched, isMoved, touchStartX, touchStartY, touchCurrentX, touchCurrentY, touchStartTime, touchEndTime, startTranslate, currentTranslate, wrapperWidth, wrapperHeight, percentage, touchesDiff, isScrolling; |
| 5104 | function handleTouchStart (e) { |
| 5105 | if (isMoved || isTouched) return; |
| 5106 | // e.preventDefault(); |
| 5107 | isTouched = true; |
| 5108 | var position = $.getTouchPosition(e); |
| 5109 | touchStartX = touchCurrentY = position.x; |
| 5110 | touchStartY = touchCurrentY = position.y; |
| 5111 | touchStartTime = (new Date()).getTime(); |
| 5112 | percentage = 0; |
| 5113 | allowItemClick = true; |
| 5114 | isScrolling = undefined; |
| 5115 | startTranslate = currentTranslate = p.monthsTranslate; |
| 5116 | } |
| 5117 | function handleTouchMove (e) { |
| 5118 | if (!isTouched) return; |
| 5119 | var position = $.getTouchPosition(e); |
| 5120 | touchCurrentX = position.x; |
| 5121 | touchCurrentY = position.y; |
| 5122 | if (typeof isScrolling === 'undefined') { |
| 5123 | isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX)); |
| 5124 | } |
| 5125 | if (p.isH && isScrolling) { |
| 5126 | isTouched = false; |
| 5127 | return; |
| 5128 | } |
| 5129 | e.preventDefault(); |
| 5130 | if (p.animating) { |
| 5131 | isTouched = false; |
| 5132 | return; |
| 5133 | } |
| 5134 | allowItemClick = false; |
| 5135 | if (!isMoved) { |
| 5136 | // First move |
| 5137 | isMoved = true; |
| 5138 | wrapperWidth = p.wrapper[0].offsetWidth; |
| 5139 | wrapperHeight = p.wrapper[0].offsetHeight; |
| 5140 | p.wrapper.transition(0); |
| 5141 | } |
| 5142 | e.preventDefault(); |
| 5143 | |
| 5144 | touchesDiff = p.isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY; |
| 5145 | percentage = touchesDiff/(p.isH ? wrapperWidth : wrapperHeight); |
| 5146 | currentTranslate = (p.monthsTranslate * inverter + percentage) * 100; |
| 5147 | |
| 5148 | // Transform wrapper |
| 5149 | p.wrapper.transform('translate3d(' + (p.isH ? currentTranslate : 0) + '%, ' + (p.isH ? 0 : currentTranslate) + '%, 0)'); |
| 5150 | |
| 5151 | } |
| 5152 | function handleTouchEnd (e) { |
| 5153 | if (!isTouched || !isMoved) { |
| 5154 | isTouched = isMoved = false; |
| 5155 | return; |
| 5156 | } |
| 5157 | isTouched = isMoved = false; |
| 5158 | |
| 5159 | touchEndTime = new Date().getTime(); |
| 5160 | if (touchEndTime - touchStartTime < 300) { |
| 5161 | if (Math.abs(touchesDiff) < 10) { |
| 5162 | p.resetMonth(); |
| 5163 | } |
| 5164 | else if (touchesDiff >= 10) { |
| 5165 | if (rtl) p.nextMonth(); |
| 5166 | else p.prevMonth(); |
| 5167 | } |
| 5168 | else { |
| 5169 | if (rtl) p.prevMonth(); |
| 5170 | else p.nextMonth(); |
| 5171 | } |
| 5172 | } |
| 5173 | else { |
| 5174 | if (percentage <= -0.5) { |
| 5175 | if (rtl) p.prevMonth(); |
| 5176 | else p.nextMonth(); |
| 5177 | } |
| 5178 | else if (percentage >= 0.5) { |
| 5179 | if (rtl) p.nextMonth(); |
| 5180 | else p.prevMonth(); |
| 5181 | } |
| 5182 | else { |
| 5183 | p.resetMonth(); |
| 5184 | } |
| 5185 | } |
| 5186 | |
| 5187 | // Allow click |
| 5188 | setTimeout(function () { |
| 5189 | allowItemClick = true; |
| 5190 | }, 100); |
| 5191 | } |
| 5192 | |
| 5193 | function handleDayClick(e) { |
| 5194 | if (!allowItemClick) return; |
| 5195 | var day = $(e.target).parents('.picker-calendar-day'); |
| 5196 | if (day.length === 0 && $(e.target).hasClass('picker-calendar-day')) { |
| 5197 | day = $(e.target); |
| 5198 | } |
| 5199 | if (day.length === 0) return; |
| 5200 | // if (day.hasClass('picker-calendar-day-selected') && !p.params.multiple) return; |
| 5201 | if (day.hasClass('picker-calendar-day-disabled')) return; |
| 5202 | if (day.hasClass('picker-calendar-day-next')) p.nextMonth(); |
| 5203 | if (day.hasClass('picker-calendar-day-prev')) p.prevMonth(); |
| 5204 | var dateYear = day.attr('data-year'); |
| 5205 | var dateMonth = day.attr('data-month'); |
| 5206 | var dateDay = day.attr('data-day'); |
| 5207 | if (p.params.onDayClick) { |
| 5208 | p.params.onDayClick(p, day[0], dateYear, dateMonth, dateDay); |
| 5209 | } |
| 5210 | p.addValue(new Date(dateYear, dateMonth, dateDay).getTime()); |
| 5211 | if (p.params.closeOnSelect && !p.params.multiple) p.close(); |
| 5212 | } |
| 5213 | |
| 5214 | p.container.find('.picker-calendar-prev-month').on('click', p.prevMonth); |
| 5215 | p.container.find('.picker-calendar-next-month').on('click', p.nextMonth); |
| 5216 | p.container.find('.picker-calendar-prev-year').on('click', p.prevYear); |
| 5217 | p.container.find('.picker-calendar-next-year').on('click', p.nextYear); |
| 5218 | p.wrapper.on('click', handleDayClick); |
| 5219 | if (p.params.touchMove) { |
| 5220 | p.wrapper.on($.touchEvents.start, handleTouchStart); |
| 5221 | p.wrapper.on($.touchEvents.move, handleTouchMove); |
| 5222 | p.wrapper.on($.touchEvents.end, handleTouchEnd); |
| 5223 | } |
| 5224 | |
| 5225 | p.container[0].f7DestroyCalendarEvents = function () { |
| 5226 | p.container.find('.picker-calendar-prev-month').off('click', p.prevMonth); |
| 5227 | p.container.find('.picker-calendar-next-month').off('click', p.nextMonth); |
| 5228 | p.container.find('.picker-calendar-prev-year').off('click', p.prevYear); |
| 5229 | p.container.find('.picker-calendar-next-year').off('click', p.nextYear); |
| 5230 | p.wrapper.off('click', handleDayClick); |
| 5231 | if (p.params.touchMove) { |
| 5232 | p.wrapper.off($.touchEvents.start, handleTouchStart); |
| 5233 | p.wrapper.off($.touchEvents.move, handleTouchMove); |
| 5234 | p.wrapper.off($.touchEvents.end, handleTouchEnd); |
| 5235 | } |
| 5236 | }; |
| 5237 | |
| 5238 | |
| 5239 | }; |
| 5240 | p.destroyCalendarEvents = function (colContainer) { |
| 5241 | if ('f7DestroyCalendarEvents' in p.container[0]) p.container[0].f7DestroyCalendarEvents(); |
| 5242 | }; |
| 5243 | |
| 5244 | // Calendar Methods |
| 5245 | p.daysInMonth = function (date) { |
| 5246 | var d = new Date(date); |
| 5247 | return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); |
| 5248 | }; |
| 5249 | p.monthHTML = function (date, offset) { |
| 5250 | date = new Date(date); |
| 5251 | var year = date.getFullYear(), |
| 5252 | month = date.getMonth(), |
| 5253 | day = date.getDate(); |
| 5254 | if (offset === 'next') { |
| 5255 | if (month === 11) date = new Date(year + 1, 0); |
| 5256 | else date = new Date(year, month + 1, 1); |
| 5257 | } |
| 5258 | if (offset === 'prev') { |
| 5259 | if (month === 0) date = new Date(year - 1, 11); |
| 5260 | else date = new Date(year, month - 1, 1); |
| 5261 | } |
| 5262 | if (offset === 'next' || offset === 'prev') { |
| 5263 | month = date.getMonth(); |
| 5264 | year = date.getFullYear(); |
| 5265 | } |
| 5266 | var daysInPrevMonth = p.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000), |
| 5267 | daysInMonth = p.daysInMonth(date), |
| 5268 | firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay(); |
| 5269 | if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7; |
| 5270 | |
| 5271 | var dayDate, currentValues = [], i, j, |
| 5272 | rows = 6, cols = 7, |
| 5273 | monthHTML = '', |
| 5274 | dayIndex = 0 + (p.params.firstDay - 1), |
| 5275 | today = new Date().setHours(0,0,0,0), |
| 5276 | minDate = p.params.minDate ? new Date(p.params.minDate).getTime() : null, |
| 5277 | maxDate = p.params.maxDate ? new Date(p.params.maxDate).getTime() : null; |
| 5278 | |
| 5279 | if (p.value && p.value.length) { |
| 5280 | for (i = 0; i < p.value.length; i++) { |
| 5281 | currentValues.push(new Date(p.value[i]).setHours(0,0,0,0)); |
| 5282 | } |
| 5283 | } |
| 5284 | |
| 5285 | for (i = 1; i <= rows; i++) { |
| 5286 | var rowHTML = ''; |
| 5287 | var row = i; |
| 5288 | for (j = 1; j <= cols; j++) { |
| 5289 | var col = j; |
| 5290 | dayIndex ++; |
| 5291 | var dayNumber = dayIndex - firstDayOfMonthIndex; |
| 5292 | var addClass = ''; |
| 5293 | if (dayNumber < 0) { |
| 5294 | dayNumber = daysInPrevMonth + dayNumber + 1; |
| 5295 | addClass += ' picker-calendar-day-prev'; |
| 5296 | dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime(); |
| 5297 | } |
| 5298 | else { |
| 5299 | dayNumber = dayNumber + 1; |
| 5300 | if (dayNumber > daysInMonth) { |
| 5301 | dayNumber = dayNumber - daysInMonth; |
| 5302 | addClass += ' picker-calendar-day-next'; |
| 5303 | dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime(); |
| 5304 | } |
| 5305 | else { |
| 5306 | dayDate = new Date(year, month, dayNumber).getTime(); |
| 5307 | } |
| 5308 | } |
| 5309 | // Today |
| 5310 | if (dayDate === today) addClass += ' picker-calendar-day-today'; |
| 5311 | // Selected |
| 5312 | if (currentValues.indexOf(dayDate) >= 0) addClass += ' picker-calendar-day-selected'; |
| 5313 | // Weekend |
| 5314 | if (p.params.weekendDays.indexOf(col - 1) >= 0) { |
| 5315 | addClass += ' picker-calendar-day-weekend'; |
| 5316 | } |
| 5317 | // Disabled |
| 5318 | if ((minDate && dayDate < minDate) || (maxDate && dayDate > maxDate)) { |
| 5319 | addClass += ' picker-calendar-day-disabled'; |
| 5320 | } |
| 5321 | |
| 5322 | dayDate = new Date(dayDate); |
| 5323 | var dayYear = dayDate.getFullYear(); |
| 5324 | var dayMonth = dayDate.getMonth(); |
| 5325 | rowHTML += '<div data-year="' + dayYear + '" data-month="' + dayMonth + '" data-day="' + dayNumber + '" class="picker-calendar-day' + (addClass) + '" data-date="' + (dayYear + '-' + dayMonth + '-' + dayNumber) + '"><span>'+dayNumber+'</span></div>'; |
| 5326 | } |
| 5327 | monthHTML += '<div class="picker-calendar-row">' + rowHTML + '</div>'; |
| 5328 | } |
| 5329 | monthHTML = '<div class="picker-calendar-month" data-year="' + year + '" data-month="' + month + '">' + monthHTML + '</div>'; |
| 5330 | return monthHTML; |
| 5331 | }; |
| 5332 | p.animating = false; |
| 5333 | p.updateCurrentMonthYear = function (dir) { |
| 5334 | if (typeof dir === 'undefined') { |
| 5335 | p.currentMonth = parseInt(p.months.eq(1).attr('data-month'), 10); |
| 5336 | p.currentYear = parseInt(p.months.eq(1).attr('data-year'), 10); |
| 5337 | } |
| 5338 | else { |
| 5339 | p.currentMonth = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-month'), 10); |
| 5340 | p.currentYear = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-year'), 10); |
| 5341 | } |
| 5342 | p.container.find('.current-month-value').text(p.params.monthNames[p.currentMonth]); |
| 5343 | p.container.find('.current-year-value').text(p.currentYear); |
| 5344 | |
| 5345 | }; |
| 5346 | p.onMonthChangeStart = function (dir) { |
| 5347 | p.updateCurrentMonthYear(dir); |
| 5348 | p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'); |
| 5349 | var currentIndex = dir === 'next' ? p.months.length - 1 : 0; |
| 5350 | |
| 5351 | p.months.eq(currentIndex).addClass('picker-calendar-month-current'); |
| 5352 | p.months.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1).addClass(dir === 'next' ? 'picker-calendar-month-prev' : 'picker-calendar-month-next'); |
| 5353 | |
| 5354 | if (p.params.onMonthYearChangeStart) { |
| 5355 | p.params.onMonthYearChangeStart(p, p.currentYear, p.currentMonth); |
| 5356 | } |
| 5357 | }; |
| 5358 | p.onMonthChangeEnd = function (dir, rebuildBoth) { |
| 5359 | p.animating = false; |
| 5360 | var nextMonthHTML, prevMonthHTML, newMonthHTML; |
| 5361 | p.wrapper.find('.picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)').remove(); |
| 5362 | |
| 5363 | if (typeof dir === 'undefined') { |
| 5364 | dir = 'next'; |
| 5365 | rebuildBoth = true; |
| 5366 | } |
| 5367 | if (!rebuildBoth) { |
| 5368 | newMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), dir); |
| 5369 | } |
| 5370 | else { |
| 5371 | p.wrapper.find('.picker-calendar-month-next, .picker-calendar-month-prev').remove(); |
| 5372 | prevMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'prev'); |
| 5373 | nextMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'next'); |
| 5374 | } |
| 5375 | if (dir === 'next' || rebuildBoth) { |
| 5376 | p.wrapper.append(newMonthHTML || nextMonthHTML); |
| 5377 | } |
| 5378 | if (dir === 'prev' || rebuildBoth) { |
| 5379 | p.wrapper.prepend(newMonthHTML || prevMonthHTML); |
| 5380 | } |
| 5381 | p.months = p.wrapper.find('.picker-calendar-month'); |
| 5382 | p.setMonthsTranslate(p.monthsTranslate); |
| 5383 | if (p.params.onMonthAdd) { |
| 5384 | p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]); |
| 5385 | } |
| 5386 | if (p.params.onMonthYearChangeEnd) { |
| 5387 | p.params.onMonthYearChangeEnd(p, p.currentYear, p.currentMonth); |
| 5388 | } |
| 5389 | }; |
| 5390 | p.setMonthsTranslate = function (translate) { |
| 5391 | translate = translate || p.monthsTranslate || 0; |
| 5392 | if (typeof p.monthsTranslate === 'undefined') p.monthsTranslate = translate; |
| 5393 | p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'); |
| 5394 | var prevMonthTranslate = -(translate + 1) * 100 * inverter; |
| 5395 | var currentMonthTranslate = -translate * 100 * inverter; |
| 5396 | var nextMonthTranslate = -(translate - 1) * 100 * inverter; |
| 5397 | p.months.eq(0).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); |
| 5398 | p.months.eq(1).transform('translate3d(' + (p.isH ? currentMonthTranslate : 0) + '%, ' + (p.isH ? 0 : currentMonthTranslate) + '%, 0)').addClass('picker-calendar-month-current'); |
| 5399 | p.months.eq(2).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); |
| 5400 | }; |
| 5401 | p.nextMonth = function (transition) { |
| 5402 | if (typeof transition === 'undefined' || typeof transition === 'object') { |
| 5403 | transition = ''; |
| 5404 | if (!p.params.animate) transition = 0; |
| 5405 | } |
| 5406 | var nextMonth = parseInt(p.months.eq(p.months.length - 1).attr('data-month'), 10); |
| 5407 | var nextYear = parseInt(p.months.eq(p.months.length - 1).attr('data-year'), 10); |
| 5408 | var nextDate = new Date(nextYear, nextMonth); |
| 5409 | var nextDateTime = nextDate.getTime(); |
| 5410 | var transitionEndCallback = p.animating ? false : true; |
| 5411 | if (p.params.maxDate) { |
| 5412 | if (nextDateTime > new Date(p.params.maxDate).getTime()) { |
| 5413 | return p.resetMonth(); |
| 5414 | } |
| 5415 | } |
| 5416 | p.monthsTranslate --; |
| 5417 | if (nextMonth === p.currentMonth) { |
| 5418 | var nextMonthTranslate = -(p.monthsTranslate) * 100 * inverter; |
| 5419 | var nextMonthHTML = $(p.monthHTML(nextDateTime, 'next')).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); |
| 5420 | p.wrapper.append(nextMonthHTML[0]); |
| 5421 | p.months = p.wrapper.find('.picker-calendar-month'); |
| 5422 | if (p.params.onMonthAdd) { |
| 5423 | p.params.onMonthAdd(p, p.months.eq(p.months.length - 1)[0]); |
| 5424 | } |
| 5425 | } |
| 5426 | p.animating = true; |
| 5427 | p.onMonthChangeStart('next'); |
| 5428 | var translate = (p.monthsTranslate * 100) * inverter; |
| 5429 | |
| 5430 | p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); |
| 5431 | if (transitionEndCallback) { |
| 5432 | p.wrapper.transitionEnd(function () { |
| 5433 | p.onMonthChangeEnd('next'); |
| 5434 | }); |
| 5435 | } |
| 5436 | if (!p.params.animate) { |
| 5437 | p.onMonthChangeEnd('next'); |
| 5438 | } |
| 5439 | }; |
| 5440 | p.prevMonth = function (transition) { |
| 5441 | if (typeof transition === 'undefined' || typeof transition === 'object') { |
| 5442 | transition = ''; |
| 5443 | if (!p.params.animate) transition = 0; |
| 5444 | } |
| 5445 | var prevMonth = parseInt(p.months.eq(0).attr('data-month'), 10); |
| 5446 | var prevYear = parseInt(p.months.eq(0).attr('data-year'), 10); |
| 5447 | var prevDate = new Date(prevYear, prevMonth + 1, -1); |
| 5448 | var prevDateTime = prevDate.getTime(); |
| 5449 | var transitionEndCallback = p.animating ? false : true; |
| 5450 | if (p.params.minDate) { |
| 5451 | if (prevDateTime < new Date(p.params.minDate).getTime()) { |
| 5452 | return p.resetMonth(); |
| 5453 | } |
| 5454 | } |
| 5455 | p.monthsTranslate ++; |
| 5456 | if (prevMonth === p.currentMonth) { |
| 5457 | var prevMonthTranslate = -(p.monthsTranslate) * 100 * inverter; |
| 5458 | var prevMonthHTML = $(p.monthHTML(prevDateTime, 'prev')).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); |
| 5459 | p.wrapper.prepend(prevMonthHTML[0]); |
| 5460 | p.months = p.wrapper.find('.picker-calendar-month'); |
| 5461 | if (p.params.onMonthAdd) { |
| 5462 | p.params.onMonthAdd(p, p.months.eq(0)[0]); |
| 5463 | } |
| 5464 | } |
| 5465 | p.animating = true; |
| 5466 | p.onMonthChangeStart('prev'); |
| 5467 | var translate = (p.monthsTranslate * 100) * inverter; |
| 5468 | p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); |
| 5469 | if (transitionEndCallback) { |
| 5470 | p.wrapper.transitionEnd(function () { |
| 5471 | p.onMonthChangeEnd('prev'); |
| 5472 | }); |
| 5473 | } |
| 5474 | if (!p.params.animate) { |
| 5475 | p.onMonthChangeEnd('prev'); |
| 5476 | } |
| 5477 | }; |
| 5478 | p.resetMonth = function (transition) { |
| 5479 | if (typeof transition === 'undefined') transition = ''; |
| 5480 | var translate = (p.monthsTranslate * 100) * inverter; |
| 5481 | p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); |
| 5482 | }; |
| 5483 | p.setYearMonth = function (year, month, transition) { |
| 5484 | if (typeof year === 'undefined') year = p.currentYear; |
| 5485 | if (typeof month === 'undefined') month = p.currentMonth; |
| 5486 | if (typeof transition === 'undefined' || typeof transition === 'object') { |
| 5487 | transition = ''; |
| 5488 | if (!p.params.animate) transition = 0; |
| 5489 | } |
| 5490 | var targetDate; |
| 5491 | if (year < p.currentYear) { |
| 5492 | targetDate = new Date(year, month + 1, -1).getTime(); |
| 5493 | } |
| 5494 | else { |
| 5495 | targetDate = new Date(year, month).getTime(); |
| 5496 | } |
| 5497 | if (p.params.maxDate && targetDate > new Date(p.params.maxDate).getTime()) { |
| 5498 | return false; |
| 5499 | } |
| 5500 | if (p.params.minDate && targetDate < new Date(p.params.minDate).getTime()) { |
| 5501 | return false; |
| 5502 | } |
| 5503 | var currentDate = new Date(p.currentYear, p.currentMonth).getTime(); |
| 5504 | var dir = targetDate > currentDate ? 'next' : 'prev'; |
| 5505 | var newMonthHTML = p.monthHTML(new Date(year, month)); |
| 5506 | p.monthsTranslate = p.monthsTranslate || 0; |
| 5507 | var prevTranslate = p.monthsTranslate; |
| 5508 | var monthTranslate, wrapperTranslate; |
| 5509 | var transitionEndCallback = p.animating ? false : true; |
| 5510 | if (targetDate > currentDate) { |
| 5511 | // To next |
| 5512 | p.monthsTranslate --; |
| 5513 | if (!p.animating) p.months.eq(p.months.length - 1).remove(); |
| 5514 | p.wrapper.append(newMonthHTML); |
| 5515 | p.months = p.wrapper.find('.picker-calendar-month'); |
| 5516 | monthTranslate = -(prevTranslate - 1) * 100 * inverter; |
| 5517 | p.months.eq(p.months.length - 1).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); |
| 5518 | } |
| 5519 | else { |
| 5520 | // To prev |
| 5521 | p.monthsTranslate ++; |
| 5522 | if (!p.animating) p.months.eq(0).remove(); |
| 5523 | p.wrapper.prepend(newMonthHTML); |
| 5524 | p.months = p.wrapper.find('.picker-calendar-month'); |
| 5525 | monthTranslate = -(prevTranslate + 1) * 100 * inverter; |
| 5526 | p.months.eq(0).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); |
| 5527 | } |
| 5528 | if (p.params.onMonthAdd) { |
| 5529 | p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]); |
| 5530 | } |
| 5531 | p.animating = true; |
| 5532 | p.onMonthChangeStart(dir); |
| 5533 | wrapperTranslate = (p.monthsTranslate * 100) * inverter; |
| 5534 | p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? wrapperTranslate : 0) + '%, ' + (p.isH ? 0 : wrapperTranslate) + '%, 0)'); |
| 5535 | if (transitionEndCallback) { |
| 5536 | p.wrapper.transitionEnd(function () { |
| 5537 | p.onMonthChangeEnd(dir, true); |
| 5538 | }); |
| 5539 | } |
| 5540 | if (!p.params.animate) { |
| 5541 | p.onMonthChangeEnd(dir); |
| 5542 | } |
| 5543 | }; |
| 5544 | p.nextYear = function () { |
| 5545 | p.setYearMonth(p.currentYear + 1); |
| 5546 | }; |
| 5547 | p.prevYear = function () { |
| 5548 | p.setYearMonth(p.currentYear - 1); |
| 5549 | }; |
| 5550 | |
| 5551 | |
| 5552 | // HTML Layout |
| 5553 | p.layout = function () { |
| 5554 | var pickerHTML = ''; |
| 5555 | var pickerClass = ''; |
| 5556 | var i; |
| 5557 | |
| 5558 | var layoutDate = p.value && p.value.length ? p.value[0] : new Date().setHours(0,0,0,0); |
| 5559 | var prevMonthHTML = p.monthHTML(layoutDate, 'prev'); |
| 5560 | var currentMonthHTML = p.monthHTML(layoutDate); |
| 5561 | var nextMonthHTML = p.monthHTML(layoutDate, 'next'); |
| 5562 | var monthsHTML = '<div class="picker-calendar-months"><div class="picker-calendar-months-wrapper">' + (prevMonthHTML + currentMonthHTML + nextMonthHTML) + '</div></div>'; |
| 5563 | // Week days header |
| 5564 | var weekHeaderHTML = ''; |
| 5565 | if (p.params.weekHeader) { |
| 5566 | for (i = 0; i < 7; i++) { |
| 5567 | var weekDayIndex = (i + p.params.firstDay > 6) ? (i - 7 + p.params.firstDay) : (i + p.params.firstDay); |
| 5568 | var dayName = p.params.dayNamesShort[weekDayIndex]; |
| 5569 | weekHeaderHTML += '<div class="picker-calendar-week-day ' + ((p.params.weekendDays.indexOf(weekDayIndex) >= 0) ? 'picker-calendar-week-day-weekend' : '') + '"> ' + dayName + '</div>'; |
| 5570 | |
| 5571 | } |
| 5572 | weekHeaderHTML = '<div class="picker-calendar-week-days">' + weekHeaderHTML + '</div>'; |
| 5573 | } |
| 5574 | pickerClass = 'weui-picker-calendar ' + (p.params.cssClass || ''); |
| 5575 | if(!p.inline) pickerClass = 'weui-picker-modal ' + pickerClass; |
| 5576 | var toolbarHTML = p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText) : ''; |
| 5577 | if (p.params.toolbar) { |
| 5578 | toolbarHTML = p.params.toolbarTemplate |
| 5579 | .replace(/{{closeText}}/g, p.params.toolbarCloseText) |
| 5580 | .replace(/{{monthPicker}}/g, (p.params.monthPicker ? p.params.monthPickerTemplate : '')) |
| 5581 | .replace(/{{yearPicker}}/g, (p.params.yearPicker ? p.params.yearPickerTemplate : '')); |
| 5582 | } |
| 5583 | |
| 5584 | pickerHTML = |
| 5585 | '<div class="' + (pickerClass) + '">' + |
| 5586 | toolbarHTML + |
| 5587 | '<div class="picker-modal-inner">' + |
| 5588 | weekHeaderHTML + |
| 5589 | monthsHTML + |
| 5590 | '</div>' + |
| 5591 | '</div>'; |
| 5592 | |
| 5593 | |
| 5594 | p.pickerHTML = pickerHTML; |
| 5595 | }; |
| 5596 | |
| 5597 | // Input Events |
| 5598 | function openOnInput(e) { |
| 5599 | e.preventDefault(); |
| 5600 | if (p.opened) return; |
| 5601 | p.open(); |
| 5602 | if (p.params.scrollToInput && !isPopover()) { |
| 5603 | var pageContent = p.input.parents('.page-content'); |
| 5604 | if (pageContent.length === 0) return; |
| 5605 | |
| 5606 | var paddingTop = parseInt(pageContent.css('padding-top'), 10), |
| 5607 | paddingBottom = parseInt(pageContent.css('padding-bottom'), 10), |
| 5608 | pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(), |
| 5609 | pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(), |
| 5610 | newPaddingBottom; |
| 5611 | |
| 5612 | var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight; |
| 5613 | if (inputTop > pageHeight) { |
| 5614 | var scrollTop = pageContent.scrollTop() + inputTop - pageHeight; |
| 5615 | if (scrollTop + pageHeight > pageScrollHeight) { |
| 5616 | newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom; |
| 5617 | if (pageHeight === pageScrollHeight) { |
| 5618 | newPaddingBottom = p.container.height(); |
| 5619 | } |
| 5620 | pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'}); |
| 5621 | } |
| 5622 | pageContent.scrollTop(scrollTop, 300); |
| 5623 | } |
| 5624 | } |
| 5625 | } |
| 5626 | function closeOnHTMLClick(e) { |
| 5627 | if (inPopover()) return; |
| 5628 | if (p.input && p.input.length > 0) { |
| 5629 | if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close(); |
| 5630 | } |
| 5631 | else { |
| 5632 | if ($(e.target).parents('.weui-picker-modal').length === 0) p.close(); |
| 5633 | } |
| 5634 | } |
| 5635 | |
| 5636 | if (p.params.input) { |
| 5637 | p.input = $(p.params.input); |
| 5638 | if (p.input.length > 0) { |
| 5639 | if (p.params.inputReadOnly) p.input.prop('readOnly', true); |
| 5640 | if (!p.inline) { |
| 5641 | p.input.on('click', openOnInput); |
| 5642 | } |
| 5643 | if (p.params.inputReadOnly) { |
| 5644 | p.input.on('focus mousedown', function (e) { |
| 5645 | e.preventDefault(); |
| 5646 | }); |
| 5647 | } |
| 5648 | } |
| 5649 | |
| 5650 | } |
| 5651 | |
| 5652 | //iphone 上无法正确触发 click,会导致点击外面无法关闭 |
| 5653 | if (!p.inline) $(document).on('click touchend', closeOnHTMLClick); |
| 5654 | |
| 5655 | // Open |
| 5656 | function onPickerClose() { |
| 5657 | p.opened = false; |
| 5658 | if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''}); |
| 5659 | if (p.params.onClose) p.params.onClose(p); |
| 5660 | |
| 5661 | // Destroy events |
| 5662 | p.destroyCalendarEvents(); |
| 5663 | } |
| 5664 | |
| 5665 | p.opened = false; |
| 5666 | p.open = function () { |
| 5667 | var toPopover = isPopover() && false; |
| 5668 | var updateValue = false; |
| 5669 | if (!p.opened) { |
| 5670 | // Set date value |
| 5671 | if (!p.value) { |
| 5672 | if (p.params.value) { |
| 5673 | p.value = p.params.value; |
| 5674 | updateValue = true; |
| 5675 | } |
| 5676 | } |
| 5677 | |
| 5678 | // Layout |
| 5679 | p.layout(); |
| 5680 | |
| 5681 | // Append |
| 5682 | if (toPopover) { |
| 5683 | p.pickerHTML = '<div class="popover popover-picker-calendar"><div class="popover-inner">' + p.pickerHTML + '</div></div>'; |
| 5684 | p.popover = $.popover(p.pickerHTML, p.params.input, true); |
| 5685 | p.container = $(p.popover).find('.weui-picker-modal'); |
| 5686 | $(p.popover).on('close', function () { |
| 5687 | onPickerClose(); |
| 5688 | }); |
| 5689 | } |
| 5690 | else if (p.inline) { |
| 5691 | p.container = $(p.pickerHTML); |
| 5692 | p.container.addClass('picker-modal-inline'); |
| 5693 | $(p.params.container).append(p.container); |
| 5694 | } |
| 5695 | else { |
| 5696 | p.container = $($.openPicker(p.pickerHTML)); |
| 5697 | $(p.container) |
| 5698 | .on('close', function () { |
| 5699 | onPickerClose(); |
| 5700 | }); |
| 5701 | } |
| 5702 | |
| 5703 | // Store calendar instance |
| 5704 | p.container[0].f7Calendar = p; |
| 5705 | p.wrapper = p.container.find('.picker-calendar-months-wrapper'); |
| 5706 | |
| 5707 | // Months |
| 5708 | p.months = p.wrapper.find('.picker-calendar-month'); |
| 5709 | |
| 5710 | // Update current month and year |
| 5711 | p.updateCurrentMonthYear(); |
| 5712 | |
| 5713 | // Set initial translate |
| 5714 | p.monthsTranslate = 0; |
| 5715 | p.setMonthsTranslate(); |
| 5716 | |
| 5717 | // Init events |
| 5718 | p.initCalendarEvents(); |
| 5719 | |
| 5720 | // Update input value |
| 5721 | if (updateValue) p.updateValue(); |
| 5722 | |
| 5723 | } |
| 5724 | |
| 5725 | // Set flag |
| 5726 | p.opened = true; |
| 5727 | p.initialized = true; |
| 5728 | if (p.params.onMonthAdd) { |
| 5729 | p.months.each(function () { |
| 5730 | p.params.onMonthAdd(p, this); |
| 5731 | }); |
| 5732 | } |
| 5733 | if (p.params.onOpen) p.params.onOpen(p); |
| 5734 | }; |
| 5735 | |
| 5736 | // Close |
| 5737 | p.close = function () { |
| 5738 | if (!p.opened || p.inline) return; |
| 5739 | p.animating = false; //有可能还有动画没做完,因此animating设置还没改。 |
| 5740 | if (inPopover()) { |
| 5741 | $.closePicker(p.popover); |
| 5742 | return; |
| 5743 | } |
| 5744 | else { |
| 5745 | $.closePicker(p.container); |
| 5746 | return; |
| 5747 | } |
| 5748 | }; |
| 5749 | |
| 5750 | // Destroy |
| 5751 | p.destroy = function () { |
| 5752 | p.close(); |
| 5753 | if (p.params.input && p.input.length > 0) { |
| 5754 | p.input.off('click focus', openOnInput); |
| 5755 | p.input.data("calendar", null); |
| 5756 | } |
| 5757 | $('html').off('click', closeOnHTMLClick); |
| 5758 | }; |
| 5759 | |
| 5760 | if (p.inline) { |
| 5761 | p.open(); |
| 5762 | } |
| 5763 | |
| 5764 | return p; |
| 5765 | }; |
| 5766 | |
| 5767 | var format = function(d) { |
| 5768 | return d < 10 ? "0"+d : d; |
| 5769 | } |
| 5770 | |
| 5771 | |
| 5772 | $.fn.calendar = function (params, args) { |
| 5773 | params = params || {}; |
| 5774 | return this.each(function() { |
| 5775 | var $this = $(this); |
| 5776 | if(!$this[0]) return; |
| 5777 | var p = {}; |
| 5778 | if($this[0].tagName.toUpperCase() === "INPUT") { |
| 5779 | p.input = $this; |
| 5780 | } else { |
| 5781 | p.container = $this; |
| 5782 | } |
| 5783 | |
| 5784 | var calendar = $this.data("calendar"); |
| 5785 | |
| 5786 | if(!calendar) { |
| 5787 | if(typeof params === typeof "a") { |
| 5788 | } else { |
| 5789 | if(!params.value && $this.val()) params.value = [$this.val()]; |
| 5790 | //默认显示今天 |
| 5791 | if(!params.value) { |
| 5792 | var today = new Date(); |
| 5793 | params.value = [today.getFullYear() + "/" + format(today.getMonth() + 1) + "/" + format(today.getDate())]; |
| 5794 | } |
| 5795 | calendar = $this.data("calendar", new Calendar($.extend(p, params))); |
| 5796 | } |
| 5797 | } |
| 5798 | |
| 5799 | if(typeof params === typeof "a") { |
| 5800 | calendar[params].call(calendar, args); |
| 5801 | } |
| 5802 | }); |
| 5803 | }; |
| 5804 | |
| 5805 | defaults = $.fn.calendar.prototype.defaults = { |
| 5806 | value: undefined, // 通过JS赋值,注意是数组 |
| 5807 | monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], |
| 5808 | monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], |
| 5809 | dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], |
| 5810 | dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], |
| 5811 | firstDay: 1, // First day of the week, Monday |
| 5812 | weekendDays: [0, 6], // Sunday and Saturday |
| 5813 | multiple: false, |
| 5814 | dateFormat: 'yyyy/mm/dd', |
| 5815 | direction: 'horizontal', // or 'vertical' |
| 5816 | minDate: null, |
| 5817 | maxDate: null, |
| 5818 | touchMove: true, |
| 5819 | animate: true, |
| 5820 | closeOnSelect: true, |
| 5821 | monthPicker: true, |
| 5822 | monthPickerTemplate: |
| 5823 | '<div class="picker-calendar-month-picker">' + |
| 5824 | '<a href="javascript:;" class="link icon-only picker-calendar-prev-month"><i class="icon icon-prev"></i></a>' + |
| 5825 | '<div class="current-month-value"></div>' + |
| 5826 | '<a href="javascript:;" class="link icon-only picker-calendar-next-month"><i class="icon icon-next"></i></a>' + |
| 5827 | '</div>', |
| 5828 | yearPicker: true, |
| 5829 | yearPickerTemplate: |
| 5830 | '<div class="picker-calendar-year-picker">' + |
| 5831 | '<a href="javascript:;" class="link icon-only picker-calendar-prev-year"><i class="icon icon-prev"></i></a>' + |
| 5832 | '<span class="current-year-value"></span>' + |
| 5833 | '<a href="javascript:;" class="link icon-only picker-calendar-next-year"><i class="icon icon-next"></i></a>' + |
| 5834 | '</div>', |
| 5835 | weekHeader: true, |
| 5836 | // Common settings |
| 5837 | scrollToInput: true, |
| 5838 | inputReadOnly: true, |
| 5839 | convertToPopover: true, |
| 5840 | onlyInPopover: false, |
| 5841 | toolbar: true, |
| 5842 | toolbarCloseText: 'Done', |
| 5843 | toolbarTemplate: |
| 5844 | '<div class="toolbar">' + |
| 5845 | '<div class="toolbar-inner">' + |
| 5846 | '{{yearPicker}}' + |
| 5847 | '{{monthPicker}}' + |
| 5848 | // '<a href="#" class="link close-picker">{{closeText}}</a>' + |
| 5849 | '</div>' + |
| 5850 | '</div>', |
| 5851 | /* Callbacks |
| 5852 | onMonthAdd |
| 5853 | onChange |
| 5854 | onOpen |
| 5855 | onClose |
| 5856 | onDayClick |
| 5857 | onMonthYearChangeStart |
| 5858 | onMonthYearChangeEnd |
| 5859 | */ |
| 5860 | }; |
| 5861 | |
| 5862 | }($); |
| 5863 | |
| 5864 | /* global $:true */ |
| 5865 | /* jshint unused:false*/ |
| 5866 | |
| 5867 | + function($) { |
| 5868 | "use strict"; |
| 5869 | |
| 5870 | |
| 5871 | var defaults; |
| 5872 | |
| 5873 | var formatNumber = function (n) { |
| 5874 | return n < 10 ? "0" + n : n; |
| 5875 | } |
| 5876 | |
| 5877 | var Datetime = function(input, params) { |
| 5878 | this.input = $(input); |
| 5879 | this.params = params || {}; |
| 5880 | |
| 5881 | this.initMonthes = params.monthes |
| 5882 | |
| 5883 | this.initYears = params.years |
| 5884 | |
| 5885 | var p = $.extend({}, params, this.getConfig()); |
| 5886 | $(this.input).picker(p); |
| 5887 | } |
| 5888 | |
| 5889 | Datetime.prototype = { |
| 5890 | getDays : function(max) { |
| 5891 | var days = []; |
| 5892 | for(var i=1; i<= (max||31);i++) { |
| 5893 | days.push(i < 10 ? "0"+i : i); |
| 5894 | } |
| 5895 | return days; |
| 5896 | }, |
| 5897 | |
| 5898 | getDaysByMonthAndYear : function(month, year) { |
| 5899 | var int_d = new Date(year, parseInt(month)+1-1, 1); |
| 5900 | var d = new Date(int_d - 1); |
| 5901 | return this.getDays(d.getDate()); |
| 5902 | }, |
| 5903 | getConfig: function() { |
| 5904 | var today = new Date(), |
| 5905 | params = this.params, |
| 5906 | self = this, |
| 5907 | lastValidValues; |
| 5908 | |
| 5909 | var config = { |
| 5910 | rotateEffect: false, //为了性能 |
| 5911 | cssClass: 'datetime-picker', |
| 5912 | |
| 5913 | value: [today.getFullYear(), formatNumber(today.getMonth()+1), formatNumber(today.getDate()), formatNumber(today.getHours()), (formatNumber(today.getMinutes()))], |
| 5914 | |
| 5915 | onChange: function (picker, values, displayValues) { |
| 5916 | var cols = picker.cols; |
| 5917 | var days = self.getDaysByMonthAndYear(values[1], values[0]); |
| 5918 | var currentValue = values[2]; |
| 5919 | if(currentValue > days.length) currentValue = days.length; |
| 5920 | picker.cols[4].setValue(currentValue); |
| 5921 | |
| 5922 | //check min and max |
| 5923 | var current = new Date(values[0]+'-'+values[1]+'-'+values[2]); |
| 5924 | var valid = true; |
| 5925 | if(params.min) { |
| 5926 | var min = new Date(typeof params.min === "function" ? params.min() : params.min); |
| 5927 | |
| 5928 | if(current < +min) { |
| 5929 | picker.setValue(lastValidValues); |
| 5930 | valid = false; |
| 5931 | } |
| 5932 | } |
| 5933 | if(params.max) { |
| 5934 | var max = new Date(typeof params.max === "function" ? params.max() : params.max); |
| 5935 | if(current > +max) { |
| 5936 | picker.setValue(lastValidValues); |
| 5937 | valid = false; |
| 5938 | } |
| 5939 | } |
| 5940 | |
| 5941 | valid && (lastValidValues = values); |
| 5942 | |
| 5943 | if (self.params.onChange) { |
| 5944 | self.params.onChange.apply(this, arguments); |
| 5945 | } |
| 5946 | }, |
| 5947 | |
| 5948 | formatValue: function (p, values, displayValues) { |
| 5949 | return self.params.format(p, values, displayValues); |
| 5950 | }, |
| 5951 | |
| 5952 | cols: [ |
| 5953 | { |
| 5954 | values: this.initYears |
| 5955 | }, |
| 5956 | { |
| 5957 | divider: true, // 这是一个分隔符 |
| 5958 | content: params.yearSplit |
| 5959 | }, |
| 5960 | { |
| 5961 | values: this.initMonthes |
| 5962 | }, |
| 5963 | { |
| 5964 | divider: true, // 这是一个分隔符 |
| 5965 | content: params.monthSplit |
| 5966 | }, |
| 5967 | { |
| 5968 | values: (function () { |
| 5969 | var dates = []; |
| 5970 | for (var i=1; i<=31; i++) dates.push(formatNumber(i)); |
| 5971 | return dates; |
| 5972 | })() |
| 5973 | }, |
| 5974 | |
| 5975 | ] |
| 5976 | } |
| 5977 | |
| 5978 | if (params.dateSplit) { |
| 5979 | config.cols.push({ |
| 5980 | divider: true, |
| 5981 | content: params.dateSplit |
| 5982 | }) |
| 5983 | } |
| 5984 | |
| 5985 | config.cols.push({ |
| 5986 | divider: true, |
| 5987 | content: params.datetimeSplit |
| 5988 | }) |
| 5989 | |
| 5990 | var times = self.params.times(); |
| 5991 | if (times && times.length) { |
| 5992 | config.cols = config.cols.concat(times); |
| 5993 | } |
| 5994 | |
| 5995 | var inputValue = this.input.val(); |
| 5996 | if(inputValue) config.value = params.parse(inputValue); |
| 5997 | if(this.params.value) { |
| 5998 | this.input.val(this.params.value); |
| 5999 | config.value = params.parse(this.params.value); |
| 6000 | } |
| 6001 | |
| 6002 | return config; |
| 6003 | } |
| 6004 | } |
| 6005 | |
| 6006 | $.fn.datetimePicker = function(params) { |
| 6007 | params = $.extend({}, defaults, params); |
| 6008 | return this.each(function() { |
| 6009 | if(!this) return; |
| 6010 | var $this = $(this); |
| 6011 | var datetime = $this.data("datetime"); |
| 6012 | if(!datetime) $this.data("datetime", new Datetime(this, params)); |
| 6013 | return datetime; |
| 6014 | }); |
| 6015 | }; |
| 6016 | |
| 6017 | defaults = $.fn.datetimePicker.prototype.defaults = { |
| 6018 | input: undefined, // 默认值 |
| 6019 | min: undefined, // YYYY-MM-DD 最大最小值只比较年月日,不比较时分秒 |
| 6020 | max: undefined, // YYYY-MM-DD |
| 6021 | yearSplit: '-', |
| 6022 | monthSplit: '-', |
| 6023 | dateSplit: '', // 默认为空 |
| 6024 | datetimeSplit: ' ', // 日期和时间之间的分隔符,不可为空 |
| 6025 | monthes: ('01 02 03 04 05 06 07 08 09 10 11 12').split(' '), |
| 6026 | years: (function () { |
| 6027 | var arr = []; |
| 6028 | for (var i = 1950; i <= 2030; i++) { arr.push(i); } |
| 6029 | return arr; |
| 6030 | })(), |
| 6031 | times: function () { |
| 6032 | return [ // 自定义的时间 |
| 6033 | { |
| 6034 | values: (function () { |
| 6035 | var hours = []; |
| 6036 | for (var i=0; i<24; i++) hours.push(formatNumber(i)); |
| 6037 | return hours; |
| 6038 | })() |
| 6039 | }, |
| 6040 | { |
| 6041 | divider: true, // 这是一个分隔符 |
| 6042 | content: ':' |
| 6043 | }, |
| 6044 | { |
| 6045 | values: (function () { |
| 6046 | var minutes = []; |
| 6047 | for (var i=0; i<60; i++) minutes.push(formatNumber(i)); |
| 6048 | return minutes; |
| 6049 | })() |
| 6050 | } |
| 6051 | ]; |
| 6052 | }, |
| 6053 | format: function (p, values) { // 数组转换成字符串 |
| 6054 | return p.cols.map(function (col) { |
| 6055 | return col.value || col.content; |
| 6056 | }).join(''); |
| 6057 | }, |
| 6058 | parse: function (str) { |
| 6059 | // 把字符串转换成数组,用来解析初始值 |
| 6060 | // 如果你的定制的初始值格式无法被这个默认函数解析,请自定义这个函数。比如你的时间是 '子时' 那么默认情况这个'时'会被当做分隔符而导致错误,所以你需要自己定义parse函数 |
| 6061 | // 默认兼容的分隔符 |
| 6062 | var t = str.split(this.datetimeSplit); |
| 6063 | return t[0].split(/\D/).concat(t[1].split(/:|时|分|秒/)).filter(function (d) { |
| 6064 | return !!d; |
| 6065 | }) |
| 6066 | } |
| 6067 | } |
| 6068 | |
| 6069 | }($); |
| 6070 | |
| 6071 | /*====================================================== |
| 6072 | ************ Picker ************ |
| 6073 | ======================================================*/ |
| 6074 | /* global $:true */ |
| 6075 | |
| 6076 | + function($) { |
| 6077 | "use strict"; |
| 6078 | |
| 6079 | |
| 6080 | //Popup 和 picker 之类的不要共用一个弹出方法,因为这样会导致 在 popup 中再弹出 picker 的时候会有问题。 |
| 6081 | |
| 6082 | $.openPopup = function(popup, className) { |
| 6083 | |
| 6084 | $.closePopup(); |
| 6085 | |
| 6086 | popup = $(popup); |
| 6087 | popup.show(); |
| 6088 | popup.width(); |
| 6089 | popup.addClass("weui-popup__container--visible"); |
| 6090 | var modal = popup.find(".weui-popup__modal"); |
| 6091 | modal.width(); |
| 6092 | modal.transitionEnd(function() { |
| 6093 | modal.trigger("open"); |
| 6094 | }); |
| 6095 | } |
| 6096 | |
| 6097 | |
| 6098 | $.closePopup = function(container, remove) { |
| 6099 | container = $(container || ".weui-popup__container--visible"); |
| 6100 | container.find('.weui-popup__modal').transitionEnd(function() { |
| 6101 | var $this = $(this); |
| 6102 | $this.trigger("close"); |
| 6103 | container.hide(); |
| 6104 | remove && container.remove(); |
| 6105 | }) |
| 6106 | container.removeClass("weui-popup__container--visible") |
| 6107 | }; |
| 6108 | |
| 6109 | |
| 6110 | $(document).on("click", ".close-popup, .weui-popup__overlay", function() { |
| 6111 | $.closePopup(); |
| 6112 | }) |
| 6113 | .on("click", ".open-popup", function() { |
| 6114 | $($(this).data("target")).popup(); |
| 6115 | }) |
| 6116 | .on("click", ".weui-popup__container", function(e) { |
| 6117 | if($(e.target).hasClass("weui-popup__container")) $.closePopup(); |
| 6118 | }) |
| 6119 | |
| 6120 | $.fn.popup = function() { |
| 6121 | return this.each(function() { |
| 6122 | $.openPopup(this); |
| 6123 | }); |
| 6124 | }; |
| 6125 | |
| 6126 | }($); |
| 6127 | |
| 6128 | /* =============================================================================== |
| 6129 | ************ Notification ************ |
| 6130 | =============================================================================== */ |
| 6131 | /* global $:true */ |
| 6132 | +function ($) { |
| 6133 | "use strict"; |
| 6134 | |
| 6135 | var noti, defaults, timeout, start, diffX, diffY; |
| 6136 | |
| 6137 | var touchStart = function(e) { |
| 6138 | var p = $.getTouchPosition(e); |
| 6139 | start = p; |
| 6140 | diffX = diffY = 0; |
| 6141 | noti.addClass("touching"); |
| 6142 | }; |
| 6143 | var touchMove = function(e) { |
| 6144 | if(!start) return false; |
| 6145 | e.preventDefault(); |
| 6146 | e.stopPropagation(); |
| 6147 | var p = $.getTouchPosition(e); |
| 6148 | diffX = p.x - start.x; |
| 6149 | diffY = p.y - start.y; |
| 6150 | if(diffY > 0) { |
| 6151 | diffY = Math.sqrt(diffY); |
| 6152 | } |
| 6153 | |
| 6154 | noti.css("transform", "translate3d(0, "+diffY+"px, 0)"); |
| 6155 | }; |
| 6156 | var touchEnd = function() { |
| 6157 | noti.removeClass("touching"); |
| 6158 | noti.attr("style", ""); |
| 6159 | if(diffY < 0 && (Math.abs(diffY) > noti.height()*0.38)) { |
| 6160 | $.closeNotification(); |
| 6161 | } |
| 6162 | |
| 6163 | if(Math.abs(diffX) <= 1 && Math.abs(diffY) <= 1) { |
| 6164 | noti.trigger("noti-click"); |
| 6165 | } |
| 6166 | |
| 6167 | start = false; |
| 6168 | }; |
| 6169 | |
| 6170 | var attachEvents = function(el) { |
| 6171 | el.on($.touchEvents.start, touchStart); |
| 6172 | el.on($.touchEvents.move, touchMove); |
| 6173 | el.on($.touchEvents.end, touchEnd); |
| 6174 | }; |
| 6175 | |
| 6176 | $.notification = $.noti = function(params) { |
| 6177 | params = $.extend({}, defaults, params); |
| 6178 | noti = $(".weui-notification"); |
| 6179 | if(!noti[0]) { // create a new notification |
| 6180 | noti = $('<div class="weui-notification"></div>').appendTo(document.body); |
| 6181 | attachEvents(noti); |
| 6182 | } |
| 6183 | |
| 6184 | noti.off("noti-click"); //the click event is not correct sometime: it will trigger when user is draging. |
| 6185 | if(params.onClick) noti.on("noti-click", function() { |
| 6186 | params.onClick(params.data); |
| 6187 | }); |
| 6188 | |
| 6189 | noti.html($.t7.compile(params.tpl)(params)); |
| 6190 | |
| 6191 | noti.show(); |
| 6192 | |
| 6193 | noti.addClass("weui-notification--in"); |
| 6194 | noti.data("params", params); |
| 6195 | |
| 6196 | var startTimeout = function() { |
| 6197 | if(timeout) { |
| 6198 | clearTimeout(timeout); |
| 6199 | timeout = null; |
| 6200 | } |
| 6201 | |
| 6202 | timeout = setTimeout(function() { |
| 6203 | if(noti.hasClass("weui-notification--touching")) { |
| 6204 | startTimeout(); |
| 6205 | } else { |
| 6206 | $.closeNotification(); |
| 6207 | } |
| 6208 | }, params.time); |
| 6209 | }; |
| 6210 | |
| 6211 | startTimeout(); |
| 6212 | |
| 6213 | }; |
| 6214 | |
| 6215 | $.closeNotification = function() { |
| 6216 | timeout && clearTimeout(timeout); |
| 6217 | timeout = null; |
| 6218 | var noti = $(".weui-notification").removeClass("weui-notification--in").transitionEnd(function() { |
| 6219 | $(this).remove(); |
| 6220 | }); |
| 6221 | |
| 6222 | if(noti[0]) { |
| 6223 | var params = $(".weui-notification").data("params"); |
| 6224 | if(params && params.onClose) { |
| 6225 | params.onClose(params.data); |
| 6226 | } |
| 6227 | } |
| 6228 | }; |
| 6229 | |
| 6230 | defaults = $.noti.prototype.defaults = { |
| 6231 | title: undefined, |
| 6232 | text: undefined, |
| 6233 | media: undefined, |
| 6234 | time: 4000, |
| 6235 | onClick: undefined, |
| 6236 | onClose: undefined, |
| 6237 | data: undefined, |
| 6238 | tpl: '<div class="weui-notification__inner">' + |
| 6239 | '{{#if media}}<div class="weui-notification__media">{{media}}</div>{{/if}}' + |
| 6240 | '<div class="weui-notification__content">' + |
| 6241 | '{{#if title}}<div class="weui-notification__title">{{title}}</div>{{/if}}' + |
| 6242 | '{{#if text}}<div class="weui-notification__text">{{text}}</div>{{/if}}' + |
| 6243 | '</div>' + |
| 6244 | '<div class="weui-notification__handle-bar"></div>' + |
| 6245 | '</div>' |
| 6246 | }; |
| 6247 | |
| 6248 | }($); |
| 6249 | |
| 6250 | + function($) { |
| 6251 | "use strict"; |
| 6252 | |
| 6253 | var timeout; |
| 6254 | |
| 6255 | $.toptip = function(text, duration, type) { |
| 6256 | if(!text) return; |
| 6257 | if(typeof duration === typeof "a") { |
| 6258 | type = duration; |
| 6259 | duration = undefined; |
| 6260 | } |
| 6261 | duration = duration || 3000; |
| 6262 | var className = type ? 'bg-' + type : 'bg-danger'; |
| 6263 | var $t = $('.weui-toptips').remove(); |
| 6264 | $t = $('<div class="weui-toptips"></div>').appendTo(document.body); |
| 6265 | $t.html(text); |
| 6266 | $t[0].className = 'weui-toptips ' + className |
| 6267 | |
| 6268 | clearTimeout(timeout); |
| 6269 | |
| 6270 | if(!$t.hasClass('weui-toptips_visible')) { |
| 6271 | $t.show().width(); |
| 6272 | $t.addClass('weui-toptips_visible'); |
| 6273 | } |
| 6274 | |
| 6275 | timeout = setTimeout(function() { |
| 6276 | $t.removeClass('weui-toptips_visible').transitionEnd(function() { |
| 6277 | $t.remove(); |
| 6278 | }); |
| 6279 | }, duration); |
| 6280 | } |
| 6281 | }($); |
| 6282 | |
| 6283 | /* global $:true */ |
| 6284 | + function($) { |
| 6285 | "use strict"; |
| 6286 | var Slider = function (container, arg) { |
| 6287 | this.container = $(container); |
| 6288 | this.handler = this.container.find('.weui-slider__handler') |
| 6289 | this.track = this.container.find('.weui-slider__track') |
| 6290 | this.value = this.container.find('.weui-slider-box__value') |
| 6291 | this.bind() |
| 6292 | if (typeof arg === 'function') { |
| 6293 | this.callback = arg |
| 6294 | } |
| 6295 | } |
| 6296 | |
| 6297 | Slider.prototype.bind = function () { |
| 6298 | this.container |
| 6299 | .on($.touchEvents.start, $.proxy(this.touchStart, this)) |
| 6300 | .on($.touchEvents.end, $.proxy(this.touchEnd, this)); |
| 6301 | $(document.body).on($.touchEvents.move, $.proxy(this.touchMove, this)) // move even outside container |
| 6302 | } |
| 6303 | |
| 6304 | Slider.prototype.touchStart = function (e) { |
| 6305 | e.preventDefault() |
| 6306 | this.start = $.getTouchPosition(e) |
| 6307 | this.width = this.container.find('.weui-slider__inner').width() |
| 6308 | this.left = parseInt(this.container.find('.weui-slider__handler').css('left')) |
| 6309 | this.touching = true |
| 6310 | } |
| 6311 | |
| 6312 | Slider.prototype.touchMove = function (e) { |
| 6313 | if (!this.touching) return true |
| 6314 | var p = $.getTouchPosition(e) |
| 6315 | var distance = p.x - this.start.x |
| 6316 | var left = distance + this.left |
| 6317 | var per = parseInt(left / this.width * 100) |
| 6318 | if (per < 0) per = 0 |
| 6319 | if (per > 100) per = 100 |
| 6320 | this.handler.css('left', per + '%') |
| 6321 | this.track.css('width', per + '%') |
| 6322 | this.value.text(per) |
| 6323 | this.callback && this.callback.call(this, per) |
| 6324 | this.container.trigger('change', per) |
| 6325 | } |
| 6326 | |
| 6327 | Slider.prototype.touchEnd = function (e) { |
| 6328 | this.touching = false |
| 6329 | } |
| 6330 | |
| 6331 | $.fn.slider = function (arg) { |
| 6332 | this.each(function () { |
| 6333 | var $this = $(this) |
| 6334 | var slider = $this.data('slider') |
| 6335 | if (slider) return slider; |
| 6336 | else $this.data('slider', new Slider(this, arg)) |
| 6337 | }) |
| 6338 | } |
| 6339 | }($); |
| 6340 | |
| 6341 | /* =============================================================================== |
| 6342 | ************ Swipeout ************ |
| 6343 | =============================================================================== */ |
| 6344 | /* global $:true */ |
| 6345 | |
| 6346 | +function ($) { |
| 6347 | "use strict"; |
| 6348 | |
| 6349 | var cache = []; |
| 6350 | var TOUCHING = 'swipeout-touching' |
| 6351 | |
| 6352 | var Swipeout = function(el) { |
| 6353 | this.container = $(el); |
| 6354 | this.mover = this.container.find('>.weui-cell__bd') |
| 6355 | this.attachEvents(); |
| 6356 | cache.push(this) |
| 6357 | } |
| 6358 | |
| 6359 | Swipeout.prototype.touchStart = function(e) { |
| 6360 | var p = $.getTouchPosition(e); |
| 6361 | this.container.addClass(TOUCHING); |
| 6362 | this.start = p; |
| 6363 | this.startX = 0; |
| 6364 | this.startTime = + new Date; |
| 6365 | var transform = this.mover.css('transform').match(/-?[\d\.]+/g) |
| 6366 | if (transform && transform.length) this.startX = parseInt(transform[4]) |
| 6367 | this.diffX = this.diffY = 0; |
| 6368 | this._closeOthers() |
| 6369 | this.limit = this.container.find('>.weui-cell__ft').width() || 68; // 因为有的时候初始化的时候元素是隐藏的(比如在对话框内),所以在touchstart的时候计算宽度而不是初始化的时候 |
| 6370 | }; |
| 6371 | |
| 6372 | Swipeout.prototype.touchMove= function(e) { |
| 6373 | if(!this.start) return true; |
| 6374 | var p = $.getTouchPosition(e); |
| 6375 | this.diffX = p.x - this.start.x; |
| 6376 | this.diffY = p.y - this.start.y; |
| 6377 | if (Math.abs(this.diffX) < Math.abs(this.diffY)) { // 说明是上下方向在拖动 |
| 6378 | this.close() |
| 6379 | this.start = false |
| 6380 | return true; |
| 6381 | } |
| 6382 | e.preventDefault(); |
| 6383 | e.stopPropagation(); |
| 6384 | var x = this.diffX + this.startX |
| 6385 | if (x > 0) x = 0; |
| 6386 | if (Math.abs(x) > this.limit) x = - (Math.pow(-(x+this.limit), .7) + this.limit) |
| 6387 | this.mover.css("transform", "translate3d("+x+"px, 0, 0)"); |
| 6388 | }; |
| 6389 | Swipeout.prototype.touchEnd = function() { |
| 6390 | if (!this.start) return true; |
| 6391 | this.start = false; |
| 6392 | var x = this.diffX + this.startX |
| 6393 | var t = new Date - this.startTime; |
| 6394 | if (this.diffX < -5 && t < 200) { // 向左快速滑动,则打开 |
| 6395 | this.open() |
| 6396 | } else if (this.diffX >= 0 && t < 200) { // 向右快速滑动,或者单击,则关闭 |
| 6397 | this.close() |
| 6398 | } else if (x > 0 || -x <= this.limit / 2) { |
| 6399 | this.close() |
| 6400 | } else { |
| 6401 | this.open() |
| 6402 | } |
| 6403 | }; |
| 6404 | |
| 6405 | |
| 6406 | Swipeout.prototype.close = function() { |
| 6407 | this.container.removeClass(TOUCHING); |
| 6408 | this.mover.css("transform", "translate3d(0, 0, 0)"); |
| 6409 | this.container.trigger('swipeout-close'); |
| 6410 | } |
| 6411 | |
| 6412 | Swipeout.prototype.open = function() { |
| 6413 | this.container.removeClass(TOUCHING); |
| 6414 | this._closeOthers() |
| 6415 | this.mover.css("transform", "translate3d(" + (-this.limit) + "px, 0, 0)"); |
| 6416 | this.container.trigger('swipeout-open'); |
| 6417 | } |
| 6418 | |
| 6419 | Swipeout.prototype.attachEvents = function() { |
| 6420 | var el = this.mover; |
| 6421 | el.on($.touchEvents.start, $.proxy(this.touchStart, this)); |
| 6422 | el.on($.touchEvents.move, $.proxy(this.touchMove, this)); |
| 6423 | el.on($.touchEvents.end, $.proxy(this.touchEnd, this)); |
| 6424 | } |
| 6425 | Swipeout.prototype._closeOthers = function() { |
| 6426 | //close others |
| 6427 | var self = this |
| 6428 | cache.forEach(function (s) { |
| 6429 | if (s !== self) s.close() |
| 6430 | }) |
| 6431 | } |
| 6432 | |
| 6433 | var swipeout = function(el) { |
| 6434 | return new Swipeout(el); |
| 6435 | }; |
| 6436 | |
| 6437 | $.fn.swipeout = function (arg) { |
| 6438 | return this.each(function() { |
| 6439 | var $this = $(this) |
| 6440 | var s = $this.data('swipeout') || swipeout(this); |
| 6441 | $this.data('swipeout', s); |
| 6442 | |
| 6443 | if (typeof arg === typeof 'a') { |
| 6444 | s[arg]() |
| 6445 | } |
| 6446 | }); |
| 6447 | } |
| 6448 | |
| 6449 | $('.weui-cell_swiped').swipeout() // auto init |
| 6450 | }($); |