/** * jQuery WeUI V1.2.1 * By 言川 * http://lihongxun945.github.io/jquery-weui/ */ /* global $:true */ /* global WebKitCSSMatrix:true */ (function($) { "use strict"; $.fn.transitionEnd = function(callback) { var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'], i, dom = this; function fireCallBack(e) { /*jshint validthis:true */ if (e.target !== this) return; callback.call(this, e); for (i = 0; i < events.length; i++) { dom.off(events[i], fireCallBack); } } if (callback) { for (i = 0; i < events.length; i++) { dom.on(events[i], fireCallBack); } } return this; }; $.support = (function() { var support = { touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) }; return support; })(); $.touchEvents = { start: $.support.touch ? 'touchstart' : 'mousedown', move: $.support.touch ? 'touchmove' : 'mousemove', end: $.support.touch ? 'touchend' : 'mouseup' }; $.getTouchPosition = function(e) { e = e.originalEvent || e; //jquery wrap the originevent if(e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend') { return { x: e.targetTouches[0].pageX, y: e.targetTouches[0].pageY }; } else { return { x: e.pageX, y: e.pageY }; } }; $.fn.scrollHeight = function() { return this[0].scrollHeight; }; $.fn.transform = function(transform) { for (var i = 0; i < this.length; i++) { var elStyle = this[i].style; elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform; } return this; }; $.fn.transition = function(duration) { if (typeof duration !== 'string') { duration = duration + 'ms'; } for (var i = 0; i < this.length; i++) { var elStyle = this[i].style; elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration; } return this; }; $.getTranslate = function (el, axis) { var matrix, curTransform, curStyle, transformMatrix; // automatic axis detection if (typeof axis === 'undefined') { axis = 'x'; } curStyle = window.getComputedStyle(el, null); if (window.WebKitCSSMatrix) { // Some old versions of Webkit choke when 'none' is passed; pass // empty string instead in this case transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform); } else { transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,'); matrix = transformMatrix.toString().split(','); } if (axis === 'x') { //Latest Chrome and webkits Fix if (window.WebKitCSSMatrix) curTransform = transformMatrix.m41; //Crazy IE10 Matrix else if (matrix.length === 16) curTransform = parseFloat(matrix[12]); //Normal Browsers else curTransform = parseFloat(matrix[4]); } if (axis === 'y') { //Latest Chrome and webkits Fix if (window.WebKitCSSMatrix) curTransform = transformMatrix.m42; //Crazy IE10 Matrix else if (matrix.length === 16) curTransform = parseFloat(matrix[13]); //Normal Browsers else curTransform = parseFloat(matrix[5]); } return curTransform || 0; }; $.requestAnimationFrame = function (callback) { if (window.requestAnimationFrame) return window.requestAnimationFrame(callback); else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback); else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback); else { return window.setTimeout(callback, 1000 / 60); } }; $.cancelAnimationFrame = function (id) { if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id); else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id); else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id); else { return window.clearTimeout(id); } }; $.fn.join = function(arg) { return this.toArray().join(arg); } })($); /*=========================== Template7 Template engine ===========================*/ /* global $:true */ /* jshint unused:false */ /* jshint forin:false */ +function ($) { "use strict"; $.Template7 = $.t7 = (function () { function isArray(arr) { return Object.prototype.toString.apply(arr) === '[object Array]'; } function isObject(obj) { return obj instanceof Object; } function isFunction(func) { return typeof func === 'function'; } var cache = {}; function helperToSlices(string) { var helperParts = string.replace(/[{}#}]/g, '').split(' '); var slices = []; var shiftIndex, i, j; for (i = 0; i < helperParts.length; i++) { var part = helperParts[i]; if (i === 0) slices.push(part); else { if (part.indexOf('"') === 0) { // Plain String if (part.match(/"/g).length === 2) { // One word string slices.push(part); } else { // Find closed Index shiftIndex = 0; for (j = i + 1; j < helperParts.length; j++) { part += ' ' + helperParts[j]; if (helperParts[j].indexOf('"') >= 0) { shiftIndex = j; slices.push(part); break; } } if (shiftIndex) i = shiftIndex; } } else { if (part.indexOf('=') > 0) { // Hash var hashParts = part.split('='); var hashName = hashParts[0]; var hashContent = hashParts[1]; if (hashContent.match(/"/g).length !== 2) { shiftIndex = 0; for (j = i + 1; j < helperParts.length; j++) { hashContent += ' ' + helperParts[j]; if (helperParts[j].indexOf('"') >= 0) { shiftIndex = j; break; } } if (shiftIndex) i = shiftIndex; } var hash = [hashName, hashContent.replace(/"/g,'')]; slices.push(hash); } else { // Plain variable slices.push(part); } } } } return slices; } function stringToBlocks(string) { var blocks = [], i, j, k; if (!string) return []; var _blocks = string.split(/({{[^{^}]*}})/); for (i = 0; i < _blocks.length; i++) { var block = _blocks[i]; if (block === '') continue; if (block.indexOf('{{') < 0) { blocks.push({ type: 'plain', content: block }); } else { if (block.indexOf('{/') >= 0) { continue; } if (block.indexOf('{#') < 0 && block.indexOf(' ') < 0 && block.indexOf('else') < 0) { // Simple variable blocks.push({ type: 'variable', contextName: block.replace(/[{}]/g, '') }); continue; } // Helpers var helperSlices = helperToSlices(block); var helperName = helperSlices[0]; var helperContext = []; var helperHash = {}; for (j = 1; j < helperSlices.length; j++) { var slice = helperSlices[j]; if (isArray(slice)) { // Hash helperHash[slice[0]] = slice[1] === 'false' ? false : slice[1]; } else { helperContext.push(slice); } } if (block.indexOf('{#') >= 0) { // Condition/Helper var helperStartIndex = i; var helperContent = ''; var elseContent = ''; var toSkip = 0; var shiftIndex; var foundClosed = false, foundElse = false, foundClosedElse = false, depth = 0; for (j = i + 1; j < _blocks.length; j++) { if (_blocks[j].indexOf('{{#') >= 0) { depth ++; } if (_blocks[j].indexOf('{{/') >= 0) { depth --; } if (_blocks[j].indexOf('{{#' + helperName) >= 0) { helperContent += _blocks[j]; if (foundElse) elseContent += _blocks[j]; toSkip ++; } else if (_blocks[j].indexOf('{{/' + helperName) >= 0) { if (toSkip > 0) { toSkip--; helperContent += _blocks[j]; if (foundElse) elseContent += _blocks[j]; } else { shiftIndex = j; foundClosed = true; break; } } else if (_blocks[j].indexOf('else') >= 0 && depth === 0) { foundElse = true; } else { if (!foundElse) helperContent += _blocks[j]; if (foundElse) elseContent += _blocks[j]; } } if (foundClosed) { if (shiftIndex) i = shiftIndex; blocks.push({ type: 'helper', helperName: helperName, contextName: helperContext, content: helperContent, inverseContent: elseContent, hash: helperHash }); } } else if (block.indexOf(' ') > 0) { blocks.push({ type: 'helper', helperName: helperName, contextName: helperContext, hash: helperHash }); } } } return blocks; } var Template7 = function (template) { var t = this; t.template = template; function getCompileFn(block, depth) { if (block.content) return compile(block.content, depth); else return function () {return ''; }; } function getCompileInverse(block, depth) { if (block.inverseContent) return compile(block.inverseContent, depth); else return function () {return ''; }; } function getCompileVar(name, ctx) { var variable, parts, levelsUp = 0, initialCtx = ctx; if (name.indexOf('../') === 0) { levelsUp = name.split('../').length - 1; var newDepth = ctx.split('_')[1] - levelsUp; ctx = 'ctx_' + (newDepth >= 1 ? newDepth : 1); parts = name.split('../')[levelsUp].split('.'); } else if (name.indexOf('@global') === 0) { ctx = '$.Template7.global'; parts = name.split('@global.')[1].split('.'); } else if (name.indexOf('@root') === 0) { ctx = 'ctx_1'; parts = name.split('@root.')[1].split('.'); } else { parts = name.split('.'); } variable = ctx; for (var i = 0; i < parts.length; i++) { var part = parts[i]; if (part.indexOf('@') === 0) { if (i > 0) { variable += '[(data && data.' + part.replace('@', '') + ')]'; } else { variable = '(data && data.' + name.replace('@', '') + ')'; } } else { if (isFinite(part)) { variable += '[' + part + ']'; } else { if (part.indexOf('this') === 0) { variable = part.replace('this', ctx); } else { variable += '.' + part; } } } } return variable; } function getCompiledArguments(contextArray, ctx) { var arr = []; for (var i = 0; i < contextArray.length; i++) { if (contextArray[i].indexOf('"') === 0) arr.push(contextArray[i]); else { arr.push(getCompileVar(contextArray[i], ctx)); } } return arr.join(', '); } function compile(template, depth) { depth = depth || 1; template = template || t.template; if (typeof template !== 'string') { throw new Error('Template7: Template must be a string'); } var blocks = stringToBlocks(template); if (blocks.length === 0) { return function () { return ''; }; } var ctx = 'ctx_' + depth; var resultString = '(function (' + ctx + ', data) {\n'; if (depth === 1) { resultString += 'function isArray(arr){return Object.prototype.toString.apply(arr) === \'[object Array]\';}\n'; resultString += 'function isFunction(func){return (typeof func === \'function\');}\n'; resultString += 'function c(val, ctx) {if (typeof val !== "undefined") {if (isFunction(val)) {return val.call(ctx);} else return val;} else return "";}\n'; } resultString += 'var r = \'\';\n'; var i, j, context; for (i = 0; i < blocks.length; i++) { var block = blocks[i]; // Plain block if (block.type === 'plain') { resultString += 'r +=\'' + (block.content).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/'/g, '\\' + '\'') + '\';'; continue; } var variable, compiledArguments; // Variable block if (block.type === 'variable') { variable = getCompileVar(block.contextName, ctx); resultString += 'r += c(' + variable + ', ' + ctx + ');'; } // Helpers block if (block.type === 'helper') { if (block.helperName in t.helpers) { compiledArguments = getCompiledArguments(block.contextName, ctx); 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});'; } else { if (block.contextName.length > 0) { throw new Error('Template7: Missing helper: "' + block.helperName + '"'); } else { variable = getCompileVar(block.helperName, ctx); resultString += 'if (' + variable + ') {'; resultString += 'if (isArray(' + variable + ')) {'; 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});'; resultString += '}else {'; 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});'; resultString += '}}'; } } } } resultString += '\nreturn r;})'; return eval.call(window, resultString); } t.compile = function (template) { if (!t.compiled) { t.compiled = compile(template); } return t.compiled; }; }; Template7.prototype = { options: {}, helpers: { 'if': function (context, options) { if (isFunction(context)) { context = context.call(this); } if (context) { return options.fn(this, options.data); } else { return options.inverse(this, options.data); } }, 'unless': function (context, options) { if (isFunction(context)) { context = context.call(this); } if (!context) { return options.fn(this, options.data); } else { return options.inverse(this, options.data); } }, 'each': function (context, options) { var ret = '', i = 0; if (isFunction(context)) { context = context.call(this); } if (isArray(context)) { if (options.hash.reverse) { context = context.reverse(); } for (i = 0; i < context.length; i++) { ret += options.fn(context[i], {first: i === 0, last: i === context.length - 1, index: i}); } if (options.hash.reverse) { context = context.reverse(); } } else { for (var key in context) { i++; ret += options.fn(context[key], {key: key}); } } if (i > 0) return ret; else return options.inverse(this); }, 'with': function (context, options) { if (isFunction(context)) { context = context.call(this); } return options.fn(context); }, 'join': function (context, options) { if (isFunction(context)) { context = context.call(this); } return context.join(options.hash.delimiter || options.hash.delimeter); }, 'js': function (expression, options) { var func; if (expression.indexOf('return')>=0) { func = '(function(){'+expression+'})'; } else { func = '(function(){return ('+expression+')})'; } return eval.call(this, func).call(this); }, 'js_compare': function (expression, options) { var func; if (expression.indexOf('return')>=0) { func = '(function(){'+expression+'})'; } else { func = '(function(){return ('+expression+')})'; } var condition = eval.call(this, func).call(this); if (condition) { return options.fn(this, options.data); } else { return options.inverse(this, options.data); } } } }; var t7 = function (template, data) { if (arguments.length === 2) { var instance = new Template7(template); var rendered = instance.compile()(data); instance = null; return (rendered); } else return new Template7(template); }; t7.registerHelper = function (name, fn) { Template7.prototype.helpers[name] = fn; }; t7.unregisterHelper = function (name) { Template7.prototype.helpers[name] = undefined; delete Template7.prototype.helpers[name]; }; t7.compile = function (template, options) { var instance = new Template7(template, options); return instance.compile(); }; t7.options = Template7.prototype.options; t7.helpers = Template7.prototype.helpers; return t7; })(); }($); /*! Hammer.JS - v2.0.8 - 2016-04-23 * http://hammerjs.github.io/ * * Copyright (c) 2016 Jorik Tangelder; * Licensed under the MIT license */ (function(window, document, exportName, undefined) { 'use strict'; var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; var TEST_ELEMENT = document.createElement('div'); var TYPE_FUNCTION = 'function'; var round = Math.round; var abs = Math.abs; var now = Date.now; /** * set a timeout with a given scope * @param {Function} fn * @param {Number} timeout * @param {Object} context * @returns {number} */ function setTimeoutContext(fn, timeout, context) { return setTimeout(bindFn(fn, context), timeout); } /** * if the argument is an array, we want to execute the fn on each entry * if it aint an array we don't want to do a thing. * this is used by all the methods that accept a single and array argument. * @param {*|Array} arg * @param {String} fn * @param {Object} [context] * @returns {Boolean} */ function invokeArrayArg(arg, fn, context) { if (Array.isArray(arg)) { each(arg, context[fn], context); return true; } return false; } /** * walk objects and arrays * @param {Object} obj * @param {Function} iterator * @param {Object} context */ function each(obj, iterator, context) { var i; if (!obj) { return; } if (obj.forEach) { obj.forEach(iterator, context); } else if (obj.length !== undefined) { i = 0; while (i < obj.length) { iterator.call(context, obj[i], i, obj); i++; } } else { for (i in obj) { obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); } } } /** * wrap a method with a deprecation warning and stack trace * @param {Function} method * @param {String} name * @param {String} message * @returns {Function} A new function wrapping the supplied method. */ function deprecate(method, name, message) { var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; return function() { var e = new Error('get-stack-trace'); var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') .replace(/^\s+at\s+/gm, '') .replace(/^Object.\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; var log = window.console && (window.console.warn || window.console.log); if (log) { log.call(window.console, deprecationMessage, stack); } return method.apply(this, arguments); }; } /** * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} target * @param {...Object} objects_to_assign * @returns {Object} target */ var assign; if (typeof Object.assign !== 'function') { assign = function assign(target) { if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var output = Object(target); for (var index = 1; index < arguments.length; index++) { var source = arguments[index]; if (source !== undefined && source !== null) { for (var nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; } else { assign = Object.assign; } /** * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} dest * @param {Object} src * @param {Boolean} [merge=false] * @returns {Object} dest */ var extend = deprecate(function extend(dest, src, merge) { var keys = Object.keys(src); var i = 0; while (i < keys.length) { if (!merge || (merge && dest[keys[i]] === undefined)) { dest[keys[i]] = src[keys[i]]; } i++; } return dest; }, 'extend', 'Use `assign`.'); /** * merge the values from src in the dest. * means that properties that exist in dest will not be overwritten by src * @param {Object} dest * @param {Object} src * @returns {Object} dest */ var merge = deprecate(function merge(dest, src) { return extend(dest, src, true); }, 'merge', 'Use `assign`.'); /** * simple class inheritance * @param {Function} child * @param {Function} base * @param {Object} [properties] */ function inherit(child, base, properties) { var baseP = base.prototype, childP; childP = child.prototype = Object.create(baseP); childP.constructor = child; childP._super = baseP; if (properties) { assign(childP, properties); } } /** * simple function bind * @param {Function} fn * @param {Object} context * @returns {Function} */ function bindFn(fn, context) { return function boundFn() { return fn.apply(context, arguments); }; } /** * let a boolean value also be a function that must return a boolean * this first item in args will be used as the context * @param {Boolean|Function} val * @param {Array} [args] * @returns {Boolean} */ function boolOrFn(val, args) { if (typeof val == TYPE_FUNCTION) { return val.apply(args ? args[0] || undefined : undefined, args); } return val; } /** * use the val2 when val1 is undefined * @param {*} val1 * @param {*} val2 * @returns {*} */ function ifUndefined(val1, val2) { return (val1 === undefined) ? val2 : val1; } /** * addEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function addEventListeners(target, types, handler) { each(splitStr(types), function(type) { target.addEventListener(type, handler, false); }); } /** * removeEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function removeEventListeners(target, types, handler) { each(splitStr(types), function(type) { target.removeEventListener(type, handler, false); }); } /** * find if a node is in the given parent * @method hasParent * @param {HTMLElement} node * @param {HTMLElement} parent * @return {Boolean} found */ function hasParent(node, parent) { while (node) { if (node == parent) { return true; } node = node.parentNode; } return false; } /** * small indexOf wrapper * @param {String} str * @param {String} find * @returns {Boolean} found */ function inStr(str, find) { return str.indexOf(find) > -1; } /** * split string on whitespace * @param {String} str * @returns {Array} words */ function splitStr(str) { return str.trim().split(/\s+/g); } /** * find if a array contains the object using indexOf or a simple polyFill * @param {Array} src * @param {String} find * @param {String} [findByKey] * @return {Boolean|Number} false when not found, or the index */ function inArray(src, find, findByKey) { if (src.indexOf && !findByKey) { return src.indexOf(find); } else { var i = 0; while (i < src.length) { if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { return i; } i++; } return -1; } } /** * convert array-like objects to real arrays * @param {Object} obj * @returns {Array} */ function toArray(obj) { return Array.prototype.slice.call(obj, 0); } /** * unique array with objects based on a key (like 'id') or just by the array's value * @param {Array} src [{id:1},{id:2},{id:1}] * @param {String} [key] * @param {Boolean} [sort=False] * @returns {Array} [{id:1},{id:2}] */ function uniqueArray(src, key, sort) { var results = []; var values = []; var i = 0; while (i < src.length) { var val = key ? src[i][key] : src[i]; if (inArray(values, val) < 0) { results.push(src[i]); } values[i] = val; i++; } if (sort) { if (!key) { results = results.sort(); } else { results = results.sort(function sortUniqueArray(a, b) { return a[key] > b[key]; }); } } return results; } /** * get the prefixed property * @param {Object} obj * @param {String} property * @returns {String|Undefined} prefixed */ function prefixed(obj, property) { var prefix, prop; var camelProp = property[0].toUpperCase() + property.slice(1); var i = 0; while (i < VENDOR_PREFIXES.length) { prefix = VENDOR_PREFIXES[i]; prop = (prefix) ? prefix + camelProp : property; if (prop in obj) { return prop; } i++; } return undefined; } /** * get a unique id * @returns {number} uniqueId */ var _uniqueId = 1; function uniqueId() { return _uniqueId++; } /** * get the window object of an element * @param {HTMLElement} element * @returns {DocumentView|Window} */ function getWindowForElement(element) { var doc = element.ownerDocument || element; return (doc.defaultView || doc.parentWindow || window); } var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; var SUPPORT_TOUCH = ('ontouchstart' in window); var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); var INPUT_TYPE_TOUCH = 'touch'; var INPUT_TYPE_PEN = 'pen'; var INPUT_TYPE_MOUSE = 'mouse'; var INPUT_TYPE_KINECT = 'kinect'; var COMPUTE_INTERVAL = 25; var INPUT_START = 1; var INPUT_MOVE = 2; var INPUT_END = 4; var INPUT_CANCEL = 8; var DIRECTION_NONE = 1; var DIRECTION_LEFT = 2; var DIRECTION_RIGHT = 4; var DIRECTION_UP = 8; var DIRECTION_DOWN = 16; var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; var PROPS_XY = ['x', 'y']; var PROPS_CLIENT_XY = ['clientX', 'clientY']; /** * create new input type manager * @param {Manager} manager * @param {Function} callback * @returns {Input} * @constructor */ function Input(manager, callback) { var self = this; this.manager = manager; this.callback = callback; this.element = manager.element; this.target = manager.options.inputTarget; // smaller wrapper around the handler, for the scope and the enabled state of the manager, // so when disabled the input events are completely bypassed. this.domHandler = function(ev) { if (boolOrFn(manager.options.enable, [manager])) { self.handler(ev); } }; this.init(); } Input.prototype = { /** * should handle the inputEvent data and trigger the callback * @virtual */ handler: function() { }, /** * bind the events */ init: function() { this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); }, /** * unbind the events */ destroy: function() { this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); } }; /** * create new input type manager * called by the Manager constructor * @param {Hammer} manager * @returns {Input} */ function createInputInstance(manager) { var Type; var inputClass = manager.options.inputClass; if (inputClass) { Type = inputClass; } else if (SUPPORT_POINTER_EVENTS) { Type = PointerEventInput; } else if (SUPPORT_ONLY_TOUCH) { Type = TouchInput; } else if (!SUPPORT_TOUCH) { Type = MouseInput; } else { Type = TouchMouseInput; } return new (Type)(manager, inputHandler); } /** * handle input events * @param {Manager} manager * @param {String} eventType * @param {Object} input */ function inputHandler(manager, eventType, input) { var pointersLen = input.pointers.length; var changedPointersLen = input.changedPointers.length; var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); input.isFirst = !!isFirst; input.isFinal = !!isFinal; if (isFirst) { manager.session = {}; } // source event is the normalized value of the domEvents // like 'touchstart, mouseup, pointerdown' input.eventType = eventType; // compute scale, rotation etc computeInputData(manager, input); // emit secret event manager.emit('hammer.input', input); manager.recognize(input); manager.session.prevInput = input; } /** * extend the data with some usable properties like scale, rotate, velocity etc * @param {Object} manager * @param {Object} input */ function computeInputData(manager, input) { var session = manager.session; var pointers = input.pointers; var pointersLength = pointers.length; // store the first input to calculate the distance and direction if (!session.firstInput) { session.firstInput = simpleCloneInputData(input); } // to compute scale and rotation we need to store the multiple touches if (pointersLength > 1 && !session.firstMultiple) { session.firstMultiple = simpleCloneInputData(input); } else if (pointersLength === 1) { session.firstMultiple = false; } var firstInput = session.firstInput; var firstMultiple = session.firstMultiple; var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; var center = input.center = getCenter(pointers); input.timeStamp = now(); input.deltaTime = input.timeStamp - firstInput.timeStamp; input.angle = getAngle(offsetCenter, center); input.distance = getDistance(offsetCenter, center); computeDeltaXY(session, input); input.offsetDirection = getDirection(input.deltaX, input.deltaY); var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); input.overallVelocityX = overallVelocity.x; input.overallVelocityY = overallVelocity.y; input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); computeIntervalInputData(session, input); // find the correct target var target = manager.element; if (hasParent(input.srcEvent.target, target)) { target = input.srcEvent.target; } input.target = target; } function computeDeltaXY(session, input) { var center = input.center; var offset = session.offsetDelta || {}; var prevDelta = session.prevDelta || {}; var prevInput = session.prevInput || {}; if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { prevDelta = session.prevDelta = { x: prevInput.deltaX || 0, y: prevInput.deltaY || 0 }; offset = session.offsetDelta = { x: center.x, y: center.y }; } input.deltaX = prevDelta.x + (center.x - offset.x); input.deltaY = prevDelta.y + (center.y - offset.y); } /** * velocity is calculated every x ms * @param {Object} session * @param {Object} input */ function computeIntervalInputData(session, input) { var last = session.lastInterval || input, deltaTime = input.timeStamp - last.timeStamp, velocity, velocityX, velocityY, direction; if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { var deltaX = input.deltaX - last.deltaX; var deltaY = input.deltaY - last.deltaY; var v = getVelocity(deltaTime, deltaX, deltaY); velocityX = v.x; velocityY = v.y; velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; direction = getDirection(deltaX, deltaY); session.lastInterval = input; } else { // use latest velocity info if it doesn't overtake a minimum period velocity = last.velocity; velocityX = last.velocityX; velocityY = last.velocityY; direction = last.direction; } input.velocity = velocity; input.velocityX = velocityX; input.velocityY = velocityY; input.direction = direction; } /** * create a simple clone from the input used for storage of firstInput and firstMultiple * @param {Object} input * @returns {Object} clonedInputData */ function simpleCloneInputData(input) { // make a simple copy of the pointers because we will get a reference if we don't // we only need clientXY for the calculations var pointers = []; var i = 0; while (i < input.pointers.length) { pointers[i] = { clientX: round(input.pointers[i].clientX), clientY: round(input.pointers[i].clientY) }; i++; } return { timeStamp: now(), pointers: pointers, center: getCenter(pointers), deltaX: input.deltaX, deltaY: input.deltaY }; } /** * get the center of all the pointers * @param {Array} pointers * @return {Object} center contains `x` and `y` properties */ function getCenter(pointers) { var pointersLength = pointers.length; // no need to loop when only one touch if (pointersLength === 1) { return { x: round(pointers[0].clientX), y: round(pointers[0].clientY) }; } var x = 0, y = 0, i = 0; while (i < pointersLength) { x += pointers[i].clientX; y += pointers[i].clientY; i++; } return { x: round(x / pointersLength), y: round(y / pointersLength) }; } /** * calculate the velocity between two points. unit is in px per ms. * @param {Number} deltaTime * @param {Number} x * @param {Number} y * @return {Object} velocity `x` and `y` */ function getVelocity(deltaTime, x, y) { return { x: x / deltaTime || 0, y: y / deltaTime || 0 }; } /** * get the direction between two points * @param {Number} x * @param {Number} y * @return {Number} direction */ function getDirection(x, y) { if (x === y) { return DIRECTION_NONE; } if (abs(x) >= abs(y)) { return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; } return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; } /** * calculate the absolute distance between two points * @param {Object} p1 {x, y} * @param {Object} p2 {x, y} * @param {Array} [props] containing x and y keys * @return {Number} distance */ function getDistance(p1, p2, props) { if (!props) { props = PROPS_XY; } var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]]; return Math.sqrt((x * x) + (y * y)); } /** * calculate the angle between two coordinates * @param {Object} p1 * @param {Object} p2 * @param {Array} [props] containing x and y keys * @return {Number} angle */ function getAngle(p1, p2, props) { if (!props) { props = PROPS_XY; } var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]]; return Math.atan2(y, x) * 180 / Math.PI; } /** * calculate the rotation degrees between two pointersets * @param {Array} start array of pointers * @param {Array} end array of pointers * @return {Number} rotation */ function getRotation(start, end) { return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); } /** * calculate the scale factor between two pointersets * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out * @param {Array} start array of pointers * @param {Array} end array of pointers * @return {Number} scale */ function getScale(start, end) { return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); } var MOUSE_INPUT_MAP = { mousedown: INPUT_START, mousemove: INPUT_MOVE, mouseup: INPUT_END }; var MOUSE_ELEMENT_EVENTS = 'mousedown'; var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; /** * Mouse events input * @constructor * @extends Input */ function MouseInput() { this.evEl = MOUSE_ELEMENT_EVENTS; this.evWin = MOUSE_WINDOW_EVENTS; this.pressed = false; // mousedown state Input.apply(this, arguments); } inherit(MouseInput, Input, { /** * handle mouse events * @param {Object} ev */ handler: function MEhandler(ev) { var eventType = MOUSE_INPUT_MAP[ev.type]; // on start we want to have the left mouse button down if (eventType & INPUT_START && ev.button === 0) { this.pressed = true; } if (eventType & INPUT_MOVE && ev.which !== 1) { eventType = INPUT_END; } // mouse must be down if (!this.pressed) { return; } if (eventType & INPUT_END) { this.pressed = false; } this.callback(this.manager, eventType, { pointers: [ev], changedPointers: [ev], pointerType: INPUT_TYPE_MOUSE, srcEvent: ev }); } }); var POINTER_INPUT_MAP = { pointerdown: INPUT_START, pointermove: INPUT_MOVE, pointerup: INPUT_END, pointercancel: INPUT_CANCEL, pointerout: INPUT_CANCEL }; // in IE10 the pointer types is defined as an enum var IE10_POINTER_TYPE_ENUM = { 2: INPUT_TYPE_TOUCH, 3: INPUT_TYPE_PEN, 4: INPUT_TYPE_MOUSE, 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 }; var POINTER_ELEMENT_EVENTS = 'pointerdown'; var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; // IE10 has prefixed support, and case-sensitive if (window.MSPointerEvent && !window.PointerEvent) { POINTER_ELEMENT_EVENTS = 'MSPointerDown'; POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; } /** * Pointer events input * @constructor * @extends Input */ function PointerEventInput() { this.evEl = POINTER_ELEMENT_EVENTS; this.evWin = POINTER_WINDOW_EVENTS; Input.apply(this, arguments); this.store = (this.manager.session.pointerEvents = []); } inherit(PointerEventInput, Input, { /** * handle mouse events * @param {Object} ev */ handler: function PEhandler(ev) { var store = this.store; var removePointer = false; var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; var isTouch = (pointerType == INPUT_TYPE_TOUCH); // get index of the event in the store var storeIndex = inArray(store, ev.pointerId, 'pointerId'); // start and mouse must be down if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { if (storeIndex < 0) { store.push(ev); storeIndex = store.length - 1; } } else if (eventType & (INPUT_END | INPUT_CANCEL)) { removePointer = true; } // it not found, so the pointer hasn't been down (so it's probably a hover) if (storeIndex < 0) { return; } // update the event in the store store[storeIndex] = ev; this.callback(this.manager, eventType, { pointers: store, changedPointers: [ev], pointerType: pointerType, srcEvent: ev }); if (removePointer) { // remove from the store store.splice(storeIndex, 1); } } }); var SINGLE_TOUCH_INPUT_MAP = { touchstart: INPUT_START, touchmove: INPUT_MOVE, touchend: INPUT_END, touchcancel: INPUT_CANCEL }; var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; /** * Touch events input * @constructor * @extends Input */ function SingleTouchInput() { this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; this.started = false; Input.apply(this, arguments); } inherit(SingleTouchInput, Input, { handler: function TEhandler(ev) { var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; // should we handle the touch events? if (type === INPUT_START) { this.started = true; } if (!this.started) { return; } var touches = normalizeSingleTouches.call(this, ev, type); // when done, reset the started state if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { this.started = false; } this.callback(this.manager, type, { pointers: touches[0], changedPointers: touches[1], pointerType: INPUT_TYPE_TOUCH, srcEvent: ev }); } }); /** * @this {TouchInput} * @param {Object} ev * @param {Number} type flag * @returns {undefined|Array} [all, changed] */ function normalizeSingleTouches(ev, type) { var all = toArray(ev.touches); var changed = toArray(ev.changedTouches); if (type & (INPUT_END | INPUT_CANCEL)) { all = uniqueArray(all.concat(changed), 'identifier', true); } return [all, changed]; } var TOUCH_INPUT_MAP = { touchstart: INPUT_START, touchmove: INPUT_MOVE, touchend: INPUT_END, touchcancel: INPUT_CANCEL }; var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; /** * Multi-user touch events input * @constructor * @extends Input */ function TouchInput() { this.evTarget = TOUCH_TARGET_EVENTS; this.targetIds = {}; Input.apply(this, arguments); } inherit(TouchInput, Input, { handler: function MTEhandler(ev) { var type = TOUCH_INPUT_MAP[ev.type]; var touches = getTouches.call(this, ev, type); if (!touches) { return; } this.callback(this.manager, type, { pointers: touches[0], changedPointers: touches[1], pointerType: INPUT_TYPE_TOUCH, srcEvent: ev }); } }); /** * @this {TouchInput} * @param {Object} ev * @param {Number} type flag * @returns {undefined|Array} [all, changed] */ function getTouches(ev, type) { var allTouches = toArray(ev.touches); var targetIds = this.targetIds; // when there is only one touch, the process can be simplified if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { targetIds[allTouches[0].identifier] = true; return [allTouches, allTouches]; } var i, targetTouches, changedTouches = toArray(ev.changedTouches), changedTargetTouches = [], target = this.target; // get target touches from touches targetTouches = allTouches.filter(function(touch) { return hasParent(touch.target, target); }); // collect touches if (type === INPUT_START) { i = 0; while (i < targetTouches.length) { targetIds[targetTouches[i].identifier] = true; i++; } } // filter changed touches to only contain touches that exist in the collected target ids i = 0; while (i < changedTouches.length) { if (targetIds[changedTouches[i].identifier]) { changedTargetTouches.push(changedTouches[i]); } // cleanup removed touches if (type & (INPUT_END | INPUT_CANCEL)) { delete targetIds[changedTouches[i].identifier]; } i++; } if (!changedTargetTouches.length) { return; } return [ // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), changedTargetTouches ]; } /** * Combined touch and mouse input * * Touch has a higher priority then mouse, and while touching no mouse events are allowed. * This because touch devices also emit mouse events while doing a touch. * * @constructor * @extends Input */ var DEDUP_TIMEOUT = 2500; var DEDUP_DISTANCE = 25; function TouchMouseInput() { Input.apply(this, arguments); var handler = bindFn(this.handler, this); this.touch = new TouchInput(this.manager, handler); this.mouse = new MouseInput(this.manager, handler); this.primaryTouch = null; this.lastTouches = []; } inherit(TouchMouseInput, Input, { /** * handle mouse and touch events * @param {Hammer} manager * @param {String} inputEvent * @param {Object} inputData */ handler: function TMEhandler(manager, inputEvent, inputData) { var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { return; } // when we're in a touch event, record touches to de-dupe synthetic mouse event if (isTouch) { recordTouches.call(this, inputEvent, inputData); } else if (isMouse && isSyntheticEvent.call(this, inputData)) { return; } this.callback(manager, inputEvent, inputData); }, /** * remove the event listeners */ destroy: function destroy() { this.touch.destroy(); this.mouse.destroy(); } }); function recordTouches(eventType, eventData) { if (eventType & INPUT_START) { this.primaryTouch = eventData.changedPointers[0].identifier; setLastTouch.call(this, eventData); } else if (eventType & (INPUT_END | INPUT_CANCEL)) { setLastTouch.call(this, eventData); } } function setLastTouch(eventData) { var touch = eventData.changedPointers[0]; if (touch.identifier === this.primaryTouch) { var lastTouch = {x: touch.clientX, y: touch.clientY}; this.lastTouches.push(lastTouch); var lts = this.lastTouches; var removeLastTouch = function() { var i = lts.indexOf(lastTouch); if (i > -1) { lts.splice(i, 1); } }; setTimeout(removeLastTouch, DEDUP_TIMEOUT); } } function isSyntheticEvent(eventData) { var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; for (var i = 0; i < this.lastTouches.length; i++) { var t = this.lastTouches[i]; var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { return true; } } return false; } var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; // magical touchAction value var TOUCH_ACTION_COMPUTE = 'compute'; var TOUCH_ACTION_AUTO = 'auto'; var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented var TOUCH_ACTION_NONE = 'none'; var TOUCH_ACTION_PAN_X = 'pan-x'; var TOUCH_ACTION_PAN_Y = 'pan-y'; var TOUCH_ACTION_MAP = getTouchActionProps(); /** * Touch Action * sets the touchAction property or uses the js alternative * @param {Manager} manager * @param {String} value * @constructor */ function TouchAction(manager, value) { this.manager = manager; this.set(value); } TouchAction.prototype = { /** * set the touchAction value on the element or enable the polyfill * @param {String} value */ set: function(value) { // find out the touch-action by the event handlers if (value == TOUCH_ACTION_COMPUTE) { value = this.compute(); } if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; } this.actions = value.toLowerCase().trim(); }, /** * just re-set the touchAction value */ update: function() { this.set(this.manager.options.touchAction); }, /** * compute the value for the touchAction property based on the recognizer's settings * @returns {String} value */ compute: function() { var actions = []; each(this.manager.recognizers, function(recognizer) { if (boolOrFn(recognizer.options.enable, [recognizer])) { actions = actions.concat(recognizer.getTouchAction()); } }); return cleanTouchActions(actions.join(' ')); }, /** * this method is called on each input cycle and provides the preventing of the browser behavior * @param {Object} input */ preventDefaults: function(input) { var srcEvent = input.srcEvent; var direction = input.offsetDirection; // if the touch action did prevented once this session if (this.manager.session.prevented) { srcEvent.preventDefault(); return; } var actions = this.actions; var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; if (hasNone) { //do not prevent defaults if this is a tap gesture var isTapPointer = input.pointers.length === 1; var isTapMovement = input.distance < 2; var isTapTouchTime = input.deltaTime < 250; if (isTapPointer && isTapMovement && isTapTouchTime) { return; } } if (hasPanX && hasPanY) { // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent return; } if (hasNone || (hasPanY && direction & DIRECTION_HORIZONTAL) || (hasPanX && direction & DIRECTION_VERTICAL)) { return this.preventSrc(srcEvent); } }, /** * call preventDefault to prevent the browser's default behavior (scrolling in most cases) * @param {Object} srcEvent */ preventSrc: function(srcEvent) { this.manager.session.prevented = true; srcEvent.preventDefault(); } }; /** * when the touchActions are collected they are not a valid value, so we need to clean things up. * * @param {String} actions * @returns {*} */ function cleanTouchActions(actions) { // none if (inStr(actions, TOUCH_ACTION_NONE)) { return TOUCH_ACTION_NONE; } var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); // if both pan-x and pan-y are set (different recognizers // for different directions, e.g. horizontal pan but vertical swipe?) // we need none (as otherwise with pan-x pan-y combined none of these // recognizers will work, since the browser would handle all panning if (hasPanX && hasPanY) { return TOUCH_ACTION_NONE; } // pan-x OR pan-y if (hasPanX || hasPanY) { return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; } // manipulation if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { return TOUCH_ACTION_MANIPULATION; } return TOUCH_ACTION_AUTO; } function getTouchActionProps() { if (!NATIVE_TOUCH_ACTION) { return false; } var touchMap = {}; var cssSupports = window.CSS && window.CSS.supports; ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { // If css.supports is not supported but there is native touch-action assume it supports // all values. This is the case for IE 10 and 11. touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; }); return touchMap; } /** * Recognizer flow explained; * * All recognizers have the initial state of POSSIBLE when a input session starts. * The definition of a input session is from the first input until the last input, with all it's movement in it. * * Example session for mouse-input: mousedown -> mousemove -> mouseup * * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed * which determines with state it should be. * * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to * POSSIBLE to give it another change on the next cycle. * * Possible * | * +-----+---------------+ * | | * +-----+-----+ | * | | | * Failed Cancelled | * +-------+------+ * | | * Recognized Began * | * Changed * | * Ended/Recognized */ var STATE_POSSIBLE = 1; var STATE_BEGAN = 2; var STATE_CHANGED = 4; var STATE_ENDED = 8; var STATE_RECOGNIZED = STATE_ENDED; var STATE_CANCELLED = 16; var STATE_FAILED = 32; /** * Recognizer * Every recognizer needs to extend from this class. * @constructor * @param {Object} options */ function Recognizer(options) { this.options = assign({}, this.defaults, options || {}); this.id = uniqueId(); this.manager = null; // default is enable true this.options.enable = ifUndefined(this.options.enable, true); this.state = STATE_POSSIBLE; this.simultaneous = {}; this.requireFail = []; } Recognizer.prototype = { /** * @virtual * @type {Object} */ defaults: {}, /** * set options * @param {Object} options * @return {Recognizer} */ set: function(options) { assign(this.options, options); // also update the touchAction, in case something changed about the directions/enabled state this.manager && this.manager.touchAction.update(); return this; }, /** * recognize simultaneous with an other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ recognizeWith: function(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { return this; } var simultaneous = this.simultaneous; otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); if (!simultaneous[otherRecognizer.id]) { simultaneous[otherRecognizer.id] = otherRecognizer; otherRecognizer.recognizeWith(this); } return this; }, /** * drop the simultaneous link. it doesnt remove the link on the other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ dropRecognizeWith: function(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { return this; } otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); delete this.simultaneous[otherRecognizer.id]; return this; }, /** * recognizer can only run when an other is failing * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ requireFailure: function(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { return this; } var requireFail = this.requireFail; otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); if (inArray(requireFail, otherRecognizer) === -1) { requireFail.push(otherRecognizer); otherRecognizer.requireFailure(this); } return this; }, /** * drop the requireFailure link. it does not remove the link on the other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ dropRequireFailure: function(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { return this; } otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); var index = inArray(this.requireFail, otherRecognizer); if (index > -1) { this.requireFail.splice(index, 1); } return this; }, /** * has require failures boolean * @returns {boolean} */ hasRequireFailures: function() { return this.requireFail.length > 0; }, /** * if the recognizer can recognize simultaneous with an other recognizer * @param {Recognizer} otherRecognizer * @returns {Boolean} */ canRecognizeWith: function(otherRecognizer) { return !!this.simultaneous[otherRecognizer.id]; }, /** * You should use `tryEmit` instead of `emit` directly to check * that all the needed recognizers has failed before emitting. * @param {Object} input */ emit: function(input) { var self = this; var state = this.state; function emit(event) { self.manager.emit(event, input); } // 'panstart' and 'panmove' if (state < STATE_ENDED) { emit(self.options.event + stateStr(state)); } emit(self.options.event); // simple 'eventName' events if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) emit(input.additionalEvent); } // panend and pancancel if (state >= STATE_ENDED) { emit(self.options.event + stateStr(state)); } }, /** * Check that all the require failure recognizers has failed, * if true, it emits a gesture event, * otherwise, setup the state to FAILED. * @param {Object} input */ tryEmit: function(input) { if (this.canEmit()) { return this.emit(input); } // it's failing anyway this.state = STATE_FAILED; }, /** * can we emit? * @returns {boolean} */ canEmit: function() { var i = 0; while (i < this.requireFail.length) { if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { return false; } i++; } return true; }, /** * update the recognizer * @param {Object} inputData */ recognize: function(inputData) { // make a new copy of the inputData // so we can change the inputData without messing up the other recognizers var inputDataClone = assign({}, inputData); // is is enabled and allow recognizing? if (!boolOrFn(this.options.enable, [this, inputDataClone])) { this.reset(); this.state = STATE_FAILED; return; } // reset when we've reached the end if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { this.state = STATE_POSSIBLE; } this.state = this.process(inputDataClone); // the recognizer has recognized a gesture // so trigger an event if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { this.tryEmit(inputDataClone); } }, /** * return the state of the recognizer * the actual recognizing happens in this method * @virtual * @param {Object} inputData * @returns {Const} STATE */ process: function(inputData) { }, // jshint ignore:line /** * return the preferred touch-action * @virtual * @returns {Array} */ getTouchAction: function() { }, /** * called when the gesture isn't allowed to recognize * like when another is being recognized or it is disabled * @virtual */ reset: function() { } }; /** * get a usable string, used as event postfix * @param {Const} state * @returns {String} state */ function stateStr(state) { if (state & STATE_CANCELLED) { return 'cancel'; } else if (state & STATE_ENDED) { return 'end'; } else if (state & STATE_CHANGED) { return 'move'; } else if (state & STATE_BEGAN) { return 'start'; } return ''; } /** * direction cons to string * @param {Const} direction * @returns {String} */ function directionStr(direction) { if (direction == DIRECTION_DOWN) { return 'down'; } else if (direction == DIRECTION_UP) { return 'up'; } else if (direction == DIRECTION_LEFT) { return 'left'; } else if (direction == DIRECTION_RIGHT) { return 'right'; } return ''; } /** * get a recognizer by name if it is bound to a manager * @param {Recognizer|String} otherRecognizer * @param {Recognizer} recognizer * @returns {Recognizer} */ function getRecognizerByNameIfManager(otherRecognizer, recognizer) { var manager = recognizer.manager; if (manager) { return manager.get(otherRecognizer); } return otherRecognizer; } /** * This recognizer is just used as a base for the simple attribute recognizers. * @constructor * @extends Recognizer */ function AttrRecognizer() { Recognizer.apply(this, arguments); } inherit(AttrRecognizer, Recognizer, { /** * @namespace * @memberof AttrRecognizer */ defaults: { /** * @type {Number} * @default 1 */ pointers: 1 }, /** * Used to check if it the recognizer receives valid input, like input.distance > 10. * @memberof AttrRecognizer * @param {Object} input * @returns {Boolean} recognized */ attrTest: function(input) { var optionPointers = this.options.pointers; return optionPointers === 0 || input.pointers.length === optionPointers; }, /** * Process the input and return the state for the recognizer * @memberof AttrRecognizer * @param {Object} input * @returns {*} State */ process: function(input) { var state = this.state; var eventType = input.eventType; var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); var isValid = this.attrTest(input); // on cancel input and we've recognized before, return STATE_CANCELLED if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { return state | STATE_CANCELLED; } else if (isRecognized || isValid) { if (eventType & INPUT_END) { return state | STATE_ENDED; } else if (!(state & STATE_BEGAN)) { return STATE_BEGAN; } return state | STATE_CHANGED; } return STATE_FAILED; } }); /** * Pan * Recognized when the pointer is down and moved in the allowed direction. * @constructor * @extends AttrRecognizer */ function PanRecognizer() { AttrRecognizer.apply(this, arguments); this.pX = null; this.pY = null; } inherit(PanRecognizer, AttrRecognizer, { /** * @namespace * @memberof PanRecognizer */ defaults: { event: 'pan', threshold: 10, pointers: 1, direction: DIRECTION_ALL }, getTouchAction: function() { var direction = this.options.direction; var actions = []; if (direction & DIRECTION_HORIZONTAL) { actions.push(TOUCH_ACTION_PAN_Y); } if (direction & DIRECTION_VERTICAL) { actions.push(TOUCH_ACTION_PAN_X); } return actions; }, directionTest: function(input) { var options = this.options; var hasMoved = true; var distance = input.distance; var direction = input.direction; var x = input.deltaX; var y = input.deltaY; // lock to axis? if (!(direction & options.direction)) { if (options.direction & DIRECTION_HORIZONTAL) { direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; hasMoved = x != this.pX; distance = Math.abs(input.deltaX); } else { direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; hasMoved = y != this.pY; distance = Math.abs(input.deltaY); } } input.direction = direction; return hasMoved && distance > options.threshold && direction & options.direction; }, attrTest: function(input) { return AttrRecognizer.prototype.attrTest.call(this, input) && (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); }, emit: function(input) { this.pX = input.deltaX; this.pY = input.deltaY; var direction = directionStr(input.direction); if (direction) { input.additionalEvent = this.options.event + direction; } this._super.emit.call(this, input); } }); /** * Pinch * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). * @constructor * @extends AttrRecognizer */ function PinchRecognizer() { AttrRecognizer.apply(this, arguments); } inherit(PinchRecognizer, AttrRecognizer, { /** * @namespace * @memberof PinchRecognizer */ defaults: { event: 'pinch', threshold: 0, pointers: 2 }, getTouchAction: function() { return [TOUCH_ACTION_NONE]; }, attrTest: function(input) { return this._super.attrTest.call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); }, emit: function(input) { if (input.scale !== 1) { var inOut = input.scale < 1 ? 'in' : 'out'; input.additionalEvent = this.options.event + inOut; } this._super.emit.call(this, input); } }); /** * Press * Recognized when the pointer is down for x ms without any movement. * @constructor * @extends Recognizer */ function PressRecognizer() { Recognizer.apply(this, arguments); this._timer = null; this._input = null; } inherit(PressRecognizer, Recognizer, { /** * @namespace * @memberof PressRecognizer */ defaults: { event: 'press', pointers: 1, time: 251, // minimal time of the pointer to be pressed threshold: 9 // a minimal movement is ok, but keep it low }, getTouchAction: function() { return [TOUCH_ACTION_AUTO]; }, process: function(input) { var options = this.options; var validPointers = input.pointers.length === options.pointers; var validMovement = input.distance < options.threshold; var validTime = input.deltaTime > options.time; this._input = input; // we only allow little movement // and we've reached an end event, so a tap is possible if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { this.reset(); } else if (input.eventType & INPUT_START) { this.reset(); this._timer = setTimeoutContext(function() { this.state = STATE_RECOGNIZED; this.tryEmit(); }, options.time, this); } else if (input.eventType & INPUT_END) { return STATE_RECOGNIZED; } return STATE_FAILED; }, reset: function() { clearTimeout(this._timer); }, emit: function(input) { if (this.state !== STATE_RECOGNIZED) { return; } if (input && (input.eventType & INPUT_END)) { this.manager.emit(this.options.event + 'up', input); } else { this._input.timeStamp = now(); this.manager.emit(this.options.event, this._input); } } }); /** * Rotate * Recognized when two or more pointer are moving in a circular motion. * @constructor * @extends AttrRecognizer */ function RotateRecognizer() { AttrRecognizer.apply(this, arguments); } inherit(RotateRecognizer, AttrRecognizer, { /** * @namespace * @memberof RotateRecognizer */ defaults: { event: 'rotate', threshold: 0, pointers: 2 }, getTouchAction: function() { return [TOUCH_ACTION_NONE]; }, attrTest: function(input) { return this._super.attrTest.call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); } }); /** * Swipe * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. * @constructor * @extends AttrRecognizer */ function SwipeRecognizer() { AttrRecognizer.apply(this, arguments); } inherit(SwipeRecognizer, AttrRecognizer, { /** * @namespace * @memberof SwipeRecognizer */ defaults: { event: 'swipe', threshold: 10, velocity: 0.3, direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, pointers: 1 }, getTouchAction: function() { return PanRecognizer.prototype.getTouchAction.call(this); }, attrTest: function(input) { var direction = this.options.direction; var velocity; if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { velocity = input.overallVelocity; } else if (direction & DIRECTION_HORIZONTAL) { velocity = input.overallVelocityX; } else if (direction & DIRECTION_VERTICAL) { velocity = input.overallVelocityY; } return this._super.attrTest.call(this, input) && direction & input.offsetDirection && input.distance > this.options.threshold && input.maxPointers == this.options.pointers && abs(velocity) > this.options.velocity && input.eventType & INPUT_END; }, emit: function(input) { var direction = directionStr(input.offsetDirection); if (direction) { this.manager.emit(this.options.event + direction, input); } this.manager.emit(this.options.event, input); } }); /** * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur * between the given interval and position. The delay option can be used to recognize multi-taps without firing * a single tap. * * The eventData from the emitted event contains the property `tapCount`, which contains the amount of * multi-taps being recognized. * @constructor * @extends Recognizer */ function TapRecognizer() { Recognizer.apply(this, arguments); // previous time and center, // used for tap counting this.pTime = false; this.pCenter = false; this._timer = null; this._input = null; this.count = 0; } inherit(TapRecognizer, Recognizer, { /** * @namespace * @memberof PinchRecognizer */ defaults: { event: 'tap', pointers: 1, taps: 1, interval: 300, // max time between the multi-tap taps time: 250, // max time of the pointer to be down (like finger on the screen) threshold: 9, // a minimal movement is ok, but keep it low posThreshold: 10 // a multi-tap can be a bit off the initial position }, getTouchAction: function() { return [TOUCH_ACTION_MANIPULATION]; }, process: function(input) { var options = this.options; var validPointers = input.pointers.length === options.pointers; var validMovement = input.distance < options.threshold; var validTouchTime = input.deltaTime < options.time; this.reset(); if ((input.eventType & INPUT_START) && (this.count === 0)) { return this.failTimeout(); } // we only allow little movement // and we've reached an end event, so a tap is possible if (validMovement && validTouchTime && validPointers) { if (input.eventType != INPUT_END) { return this.failTimeout(); } var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; this.pTime = input.timeStamp; this.pCenter = input.center; if (!validMultiTap || !validInterval) { this.count = 1; } else { this.count += 1; } this._input = input; // if tap count matches we have recognized it, // else it has began recognizing... var tapCount = this.count % options.taps; if (tapCount === 0) { // no failing requirements, immediately trigger the tap event // or wait as long as the multitap interval to trigger if (!this.hasRequireFailures()) { return STATE_RECOGNIZED; } else { this._timer = setTimeoutContext(function() { this.state = STATE_RECOGNIZED; this.tryEmit(); }, options.interval, this); return STATE_BEGAN; } } } return STATE_FAILED; }, failTimeout: function() { this._timer = setTimeoutContext(function() { this.state = STATE_FAILED; }, this.options.interval, this); return STATE_FAILED; }, reset: function() { clearTimeout(this._timer); }, emit: function() { if (this.state == STATE_RECOGNIZED) { this._input.tapCount = this.count; this.manager.emit(this.options.event, this._input); } } }); /** * Simple way to create a manager with a default set of recognizers. * @param {HTMLElement} element * @param {Object} [options] * @constructor */ function Hammer(element, options) { options = options || {}; options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); return new Manager(element, options); } /** * @const {string} */ Hammer.VERSION = '2.0.8'; /** * default settings * @namespace */ Hammer.defaults = { /** * set if DOM events are being triggered. * But this is slower and unused by simple implementations, so disabled by default. * @type {Boolean} * @default false */ domEvents: false, /** * The value for the touchAction property/fallback. * When set to `compute` it will magically set the correct value based on the added recognizers. * @type {String} * @default compute */ touchAction: TOUCH_ACTION_COMPUTE, /** * @type {Boolean} * @default true */ enable: true, /** * EXPERIMENTAL FEATURE -- can be removed/changed * Change the parent input target element. * If Null, then it is being set the to main element. * @type {Null|EventTarget} * @default null */ inputTarget: null, /** * force an input class * @type {Null|Function} * @default null */ inputClass: null, /** * Default recognizer setup when calling `Hammer()` * When creating a new Manager these will be skipped. * @type {Array} */ preset: [ // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] [RotateRecognizer, {enable: false}], [PinchRecognizer, {enable: false}, ['rotate']], [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], [TapRecognizer], [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], [PressRecognizer] ], /** * Some CSS properties can be used to improve the working of Hammer. * Add them to this method and they will be set when creating a new Manager. * @namespace */ cssProps: { /** * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. * @type {String} * @default 'none' */ userSelect: 'none', /** * Disable the Windows Phone grippers when pressing an element. * @type {String} * @default 'none' */ touchSelect: 'none', /** * Disables the default callout shown when you touch and hold a touch target. * On iOS, when you touch and hold a touch target such as a link, Safari displays * a callout containing information about the link. This property allows you to disable that callout. * @type {String} * @default 'none' */ touchCallout: 'none', /** * Specifies whether zooming is enabled. Used by IE10> * @type {String} * @default 'none' */ contentZooming: 'none', /** * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. * @type {String} * @default 'none' */ userDrag: 'none', /** * Overrides the highlight color shown when the user taps a link or a JavaScript * clickable element in iOS. This property obeys the alpha value, if specified. * @type {String} * @default 'rgba(0,0,0,0)' */ tapHighlightColor: 'rgba(0,0,0,0)' } }; var STOP = 1; var FORCED_STOP = 2; /** * Manager * @param {HTMLElement} element * @param {Object} [options] * @constructor */ function Manager(element, options) { this.options = assign({}, Hammer.defaults, options || {}); this.options.inputTarget = this.options.inputTarget || element; this.handlers = {}; this.session = {}; this.recognizers = []; this.oldCssProps = {}; this.element = element; this.input = createInputInstance(this); this.touchAction = new TouchAction(this, this.options.touchAction); toggleCssProps(this, true); each(this.options.recognizers, function(item) { var recognizer = this.add(new (item[0])(item[1])); item[2] && recognizer.recognizeWith(item[2]); item[3] && recognizer.requireFailure(item[3]); }, this); } Manager.prototype = { /** * set options * @param {Object} options * @returns {Manager} */ set: function(options) { assign(this.options, options); // Options that need a little more setup if (options.touchAction) { this.touchAction.update(); } if (options.inputTarget) { // Clean up existing event listeners and reinitialize this.input.destroy(); this.input.target = options.inputTarget; this.input.init(); } return this; }, /** * stop recognizing for this session. * This session will be discarded, when a new [input]start event is fired. * When forced, the recognizer cycle is stopped immediately. * @param {Boolean} [force] */ stop: function(force) { this.session.stopped = force ? FORCED_STOP : STOP; }, /** * run the recognizers! * called by the inputHandler function on every movement of the pointers (touches) * it walks through all the recognizers and tries to detect the gesture that is being made * @param {Object} inputData */ recognize: function(inputData) { var session = this.session; if (session.stopped) { return; } // run the touch-action polyfill this.touchAction.preventDefaults(inputData); var recognizer; var recognizers = this.recognizers; // this holds the recognizer that is being recognized. // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED // if no recognizer is detecting a thing, it is set to `null` var curRecognizer = session.curRecognizer; // reset when the last recognizer is recognized // or when we're in a new session if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { curRecognizer = session.curRecognizer = null; } var i = 0; while (i < recognizers.length) { recognizer = recognizers[i]; // find out if we are allowed try to recognize the input for this one. // 1. allow if the session is NOT forced stopped (see the .stop() method) // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one // that is being recognized. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. // this can be setup with the `recognizeWith()` method on the recognizer. if (session.stopped !== FORCED_STOP && ( // 1 !curRecognizer || recognizer == curRecognizer || // 2 recognizer.canRecognizeWith(curRecognizer))) { // 3 recognizer.recognize(inputData); } else { recognizer.reset(); } // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the // current active recognizer. but only if we don't already have an active recognizer if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { curRecognizer = session.curRecognizer = recognizer; } i++; } }, /** * get a recognizer by its event name. * @param {Recognizer|String} recognizer * @returns {Recognizer|Null} */ get: function(recognizer) { if (recognizer instanceof Recognizer) { return recognizer; } var recognizers = this.recognizers; for (var i = 0; i < recognizers.length; i++) { if (recognizers[i].options.event == recognizer) { return recognizers[i]; } } return null; }, /** * add a recognizer to the manager * existing recognizers with the same event name will be removed * @param {Recognizer} recognizer * @returns {Recognizer|Manager} */ add: function(recognizer) { if (invokeArrayArg(recognizer, 'add', this)) { return this; } // remove existing var existing = this.get(recognizer.options.event); if (existing) { this.remove(existing); } this.recognizers.push(recognizer); recognizer.manager = this; this.touchAction.update(); return recognizer; }, /** * remove a recognizer by name or instance * @param {Recognizer|String} recognizer * @returns {Manager} */ remove: function(recognizer) { if (invokeArrayArg(recognizer, 'remove', this)) { return this; } recognizer = this.get(recognizer); // let's make sure this recognizer exists if (recognizer) { var recognizers = this.recognizers; var index = inArray(recognizers, recognizer); if (index !== -1) { recognizers.splice(index, 1); this.touchAction.update(); } } return this; }, /** * bind event * @param {String} events * @param {Function} handler * @returns {EventEmitter} this */ on: function(events, handler) { if (events === undefined) { return; } if (handler === undefined) { return; } var handlers = this.handlers; each(splitStr(events), function(event) { handlers[event] = handlers[event] || []; handlers[event].push(handler); }); return this; }, /** * unbind event, leave emit blank to remove all handlers * @param {String} events * @param {Function} [handler] * @returns {EventEmitter} this */ off: function(events, handler) { if (events === undefined) { return; } var handlers = this.handlers; each(splitStr(events), function(event) { if (!handler) { delete handlers[event]; } else { handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); } }); return this; }, /** * emit event to the listeners * @param {String} event * @param {Object} data */ emit: function(event, data) { // we also want to trigger dom events if (this.options.domEvents) { triggerDomEvent(event, data); } // no handlers, so skip it all var handlers = this.handlers[event] && this.handlers[event].slice(); if (!handlers || !handlers.length) { return; } data.type = event; data.preventDefault = function() { data.srcEvent.preventDefault(); }; var i = 0; while (i < handlers.length) { handlers[i](data); i++; } }, /** * destroy the manager and unbinds all events * it doesn't unbind dom events, that is the user own responsibility */ destroy: function() { this.element && toggleCssProps(this, false); this.handlers = {}; this.session = {}; this.input.destroy(); this.element = null; } }; /** * add/remove the css properties as defined in manager.options.cssProps * @param {Manager} manager * @param {Boolean} add */ function toggleCssProps(manager, add) { var element = manager.element; if (!element.style) { return; } var prop; each(manager.options.cssProps, function(value, name) { prop = prefixed(element.style, name); if (add) { manager.oldCssProps[prop] = element.style[prop]; element.style[prop] = value; } else { element.style[prop] = manager.oldCssProps[prop] || ''; } }); if (!add) { manager.oldCssProps = {}; } } /** * trigger dom event * @param {String} event * @param {Object} data */ function triggerDomEvent(event, data) { var gestureEvent = document.createEvent('Event'); gestureEvent.initEvent(event, true, true); gestureEvent.gesture = data; data.target.dispatchEvent(gestureEvent); } assign(Hammer, { INPUT_START: INPUT_START, INPUT_MOVE: INPUT_MOVE, INPUT_END: INPUT_END, INPUT_CANCEL: INPUT_CANCEL, STATE_POSSIBLE: STATE_POSSIBLE, STATE_BEGAN: STATE_BEGAN, STATE_CHANGED: STATE_CHANGED, STATE_ENDED: STATE_ENDED, STATE_RECOGNIZED: STATE_RECOGNIZED, STATE_CANCELLED: STATE_CANCELLED, STATE_FAILED: STATE_FAILED, DIRECTION_NONE: DIRECTION_NONE, DIRECTION_LEFT: DIRECTION_LEFT, DIRECTION_RIGHT: DIRECTION_RIGHT, DIRECTION_UP: DIRECTION_UP, DIRECTION_DOWN: DIRECTION_DOWN, DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, DIRECTION_VERTICAL: DIRECTION_VERTICAL, DIRECTION_ALL: DIRECTION_ALL, Manager: Manager, Input: Input, TouchAction: TouchAction, TouchInput: TouchInput, MouseInput: MouseInput, PointerEventInput: PointerEventInput, TouchMouseInput: TouchMouseInput, SingleTouchInput: SingleTouchInput, Recognizer: Recognizer, AttrRecognizer: AttrRecognizer, Tap: TapRecognizer, Pan: PanRecognizer, Swipe: SwipeRecognizer, Pinch: PinchRecognizer, Rotate: RotateRecognizer, Press: PressRecognizer, on: addEventListeners, off: removeEventListeners, each: each, merge: merge, extend: extend, assign: assign, inherit: inherit, bindFn: bindFn, prefixed: prefixed }); // this prevents errors when Hammer is loaded in the presence of an AMD // style loader but by script tag, not by the loader. var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line freeGlobal.Hammer = Hammer; if (typeof define === 'function' && define.amd) { define(function() { return Hammer; }); } else if (typeof module != 'undefined' && module.exports) { module.exports = Hammer; } else { window[exportName] = Hammer; } })(window, document, 'Hammer'); + function($) { "use strict"; var defaults; $.modal = function(params, onOpen) { params = $.extend({}, defaults, params); var buttons = params.buttons; var buttonsHtml = buttons.map(function(d, i) { return '' + d.text + ''; }).join(""); var tpl = '
' + '
' + params.title + '
' + ( params.text ? '
'+params.text+'
' : '')+ '
' + buttonsHtml + '
' + '
'; var dialog = $.openModal(tpl, onOpen); dialog.find(".weui-dialog__btn").each(function(i, e) { var el = $(e); el.click(function() { //先关闭对话框,再调用回调函数 if(params.autoClose) $.closeModal(); if(buttons[i].onClick) { buttons[i].onClick.call(dialog); } }); }); return dialog; }; $.openModal = function(tpl, onOpen) { var mask = $("
").appendTo(document.body); mask.show(); var dialog = $(tpl).appendTo(document.body); if (onOpen) { dialog.transitionEnd(function () { onOpen.call(dialog); }); } dialog.show(); mask.addClass("weui-mask--visible"); dialog.addClass("weui-dialog--visible"); return dialog; } $.closeModal = function() { $(".weui-mask--visible").removeClass("weui-mask--visible").transitionEnd(function() { $(this).remove(); }); $(".weui-dialog--visible").removeClass("weui-dialog--visible").transitionEnd(function() { $(this).remove(); }); }; $.alert = function(text, title, onOK) { var config; if (typeof text === 'object') { config = text; } else { if (typeof title === 'function') { onOK = arguments[1]; title = undefined; } config = { text: text, title: title, onOK: onOK } } return $.modal({ text: config.text, title: config.title, buttons: [{ text: defaults.buttonOK, className: "primary", onClick: config.onOK }] }); } $.confirm = function(text, title, onOK, onCancel) { var config; if (typeof text === 'object') { config = text } else { if (typeof title === 'function') { onCancel = arguments[2]; onOK = arguments[1]; title = undefined; } config = { text: text, title: title, onOK: onOK, onCancel: onCancel } } return $.modal({ text: config.text, title: config.title, buttons: [ { text: defaults.buttonCancel, className: "default", onClick: config.onCancel }, { text: defaults.buttonOK, className: "primary", onClick: config.onOK }] }); }; //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。 $.prompt = function(text, title, onOK, onCancel, input) { var config; if (typeof text === 'object') { config = text; } else { if (typeof title === 'function') { input = arguments[3]; onCancel = arguments[2]; onOK = arguments[1]; title = undefined; } config = { text: text, title: title, input: input, onOK: onOK, onCancel: onCancel, empty: false //allow empty } } var modal = $.modal({ text: '

'+(config.text || '')+'

', title: config.title, autoClose: false, buttons: [ { text: defaults.buttonCancel, className: "default", onClick: function () { $.closeModal(); config.onCancel && config.onCancel.call(modal); } }, { text: defaults.buttonOK, className: "primary", onClick: function() { var input = $("#weui-prompt-input").val(); if (!config.empty && (input === "" || input === null)) { modal.find('.weui-prompt-input').focus()[0].select(); return false; } $.closeModal(); config.onOK && config.onOK.call(modal, input); } }] }, function () { this.find('.weui-prompt-input').focus()[0].select(); }); return modal; }; //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。 $.login = function(text, title, onOK, onCancel, username, password) { var config; if (typeof text === 'object') { config = text; } else { if (typeof title === 'function') { password = arguments[4]; username = arguments[3]; onCancel = arguments[2]; onOK = arguments[1]; title = undefined; } config = { text: text, title: title, username: username, password: password, onOK: onOK, onCancel: onCancel } } var modal = $.modal({ text: '

'+(config.text || '')+'

' + '' + '', title: config.title, autoClose: false, buttons: [ { text: defaults.buttonCancel, className: "default", onClick: function () { $.closeModal(); config.onCancel && config.onCancel.call(modal); } }, { text: defaults.buttonOK, className: "primary", onClick: function() { var username = $("#weui-prompt-username").val(); var password = $("#weui-prompt-password").val(); if (!config.empty && (username === "" || username === null)) { modal.find('#weui-prompt-username').focus()[0].select(); return false; } if (!config.empty && (password === "" || password === null)) { modal.find('#weui-prompt-password').focus()[0].select(); return false; } $.closeModal(); config.onOK && config.onOK.call(modal, username, password); } }] }, function () { this.find('#weui-prompt-username').focus()[0].select(); }); return modal; }; defaults = $.modal.prototype.defaults = { title: "提示", text: undefined, buttonOK: "确定", buttonCancel: "取消", buttons: [{ text: "确定", className: "primary" }], autoClose: true //点击按钮自动关闭对话框,如果你不希望点击按钮就关闭对话框,可以把这个设置为false }; }($); + function($) { "use strict"; var defaults; var show = function(html, className) { className = className || ""; var mask = $("
").appendTo(document.body); var tpl = '
' + html + '
'; var dialog = $(tpl).appendTo(document.body); dialog.addClass("weui-toast--visible"); dialog.show(); }; var hide = function(callback) { $(".weui-mask_transparent").remove(); var done = false; var $el = $(".weui-toast--visible").removeClass("weui-toast--visible").transitionEnd(function() { var $this = $(this); $this.remove(); callback && callback(); done = true }); setTimeout(function () { if (!done) { $el.remove() callback && callback(); } }, 1000) } $.toast = function(text, style, callback) { if(typeof style === "function") { callback = style; } var className, iconClassName = 'weui-icon-success-no-circle'; var duration = toastDefaults.duration; if(style == "cancel") { className = "weui-toast_cancel"; iconClassName = 'weui-icon-cancel' } else if(style == "forbidden") { className = "weui-toast--forbidden"; iconClassName = 'weui-icon-warn' } else if(style == "text") { className = "weui-toast--text"; } else if(typeof style === typeof 1) { duration = style } show('

' + (text || "已经完成") + '

', className); setTimeout(function() { hide(callback); }, duration); } $.showLoading = function(text) { var html = '
'; html += ''; html += '
'; html += '

' + (text || "数据加载中") + '

'; show(html, 'weui_loading_toast'); } $.hideLoading = function() { hide(); } var toastDefaults = $.toast.prototype.defaults = { duration: 2500 } }($); + function($) { "use strict"; var defaults; var show = function(params) { var mask = $("
").appendTo(document.body); var actions = params.actions || []; var actionsHtml = actions.map(function(d, i) { return '
' + d.text + '
'; }).join(""); var titleHtml = ""; if (params.title) { titleHtml = '

' + params.title + '

'; } var tpl = '
'+ titleHtml + '
'+ actionsHtml + '
'+ '
'+ '
取消
'+ '
'+ '
'; var dialog = $(tpl).appendTo(document.body); dialog.find(".weui-actionsheet__menu .weui-actionsheet__cell, .weui-actionsheet__action .weui-actionsheet__cell").each(function(i, e) { $(e).click(function() { $.closeActions(); params.onClose && params.onClose(); if(actions[i] && actions[i].onClick) { actions[i].onClick(); } }) }); mask.show(); dialog.show(); mask.addClass("weui-mask--visible"); dialog.addClass("weui-actionsheet_toggle"); }; var hide = function() { $(".weui-mask").removeClass("weui-mask--visible").transitionEnd(function() { $(this).remove(); }); $(".weui-actionsheet").removeClass("weui-actionsheet_toggle").transitionEnd(function() { $(this).remove(); }); } $.actions = function(params) { params = $.extend({}, defaults, params); show(params); } $.closeActions = function() { hide(); } $(document).on("click", ".weui-actions_mask", function() { $.closeActions(); }); var defaults = $.actions.prototype.defaults = { title: undefined, onClose: undefined, /*actions: [{ text: "菜单", className: "color-danger", onClick: function() { console.log(1); } },{ text: "菜单2", className: "color-success", onClick: function() { console.log(2); } }]*/ } }($); /* =============================================================================== ************ Pull to refreh ************ =============================================================================== */ /* global $:true */ +function ($) { "use strict"; var PTR = function(el, opt) { if (typeof opt === typeof function () {}) { opt = { onRefresh: opt } } if (typeof opt === typeof 'a') { opt = undefined } this.opt = $.extend(PTR.defaults, opt || {}); this.container = $(el); this.attachEvents(); } PTR.defaults = { distance: 50, onRefresh: undefined, onPull: undefined } PTR.prototype.touchStart = function(e) { if(this.container.hasClass("refreshing")) return; var p = $.getTouchPosition(e); this.start = p; this.diffX = this.diffY = 0; }; PTR.prototype.touchMove= function(e) { if(this.container.hasClass("refreshing")) return; if(!this.start) return false; if(this.container.scrollTop() > 0) return; var p = $.getTouchPosition(e); this.diffX = p.x - this.start.x; this.diffY = p.y - this.start.y; if (Math.abs(this.diffX) > Math.abs(this.diffY)) return true; // 说明是左右方向的拖动 if(this.diffY < 0) return; this.container.addClass("touching"); e.preventDefault(); e.stopPropagation(); this.diffY = Math.pow(this.diffY, 0.75); this.container.css("transform", "translate3d(0, "+this.diffY+"px, 0)"); this.triggerPull(this.diffY) }; PTR.prototype.touchEnd = function() { this.start = false; if(this.diffY <= 0 || this.container.hasClass("refreshing")) return; this.container.removeClass("touching"); this.container.removeClass("pull-down pull-up"); this.container.css("transform", ""); if(Math.abs(this.diffY) <= this.opt.distance) { } else { this.triggerPullToRefresh(); } }; PTR.prototype.triggerPullToRefresh = function() { this.triggerPull(this.opt.distance) this.container.removeClass('pull-up').addClass("refreshing"); if (this.opt.onRefresh) { this.opt.onRefresh.call(this) } this.container.trigger("pull-to-refresh"); } PTR.prototype.triggerPull = function(diffY) { if(diffY < this.opt.distance) { this.container.removeClass("pull-up").addClass("pull-down"); } else { this.container.removeClass("pull-down").addClass("pull-up"); } if (this.opt.onPull) { this.opt.onPull.call(this, Math.floor(diffY / this.opt.distance * 100)) } this.container.trigger("pull"); } PTR.prototype.pullToRefreshDone = function() { this.container.removeClass("refreshing"); } PTR.prototype.attachEvents = function() { var el = this.container; el.addClass("weui-pull-to-refresh"); el.on($.touchEvents.start, $.proxy(this.touchStart, this)); el.on($.touchEvents.move, $.proxy(this.touchMove, this)); el.on($.touchEvents.end, $.proxy(this.touchEnd, this)); }; var pullToRefreshDone = function(el) { $(el).removeClass("refreshing"); } $.fn.pullToRefresh = function(opt) { return this.each(function() { var $this = $(this) var ptr = $this.data('ptr') if (!ptr) $this.data('ptr', ptr = new PTR(this, opt)) if (typeof opt === typeof 'a') { ptr[opt].call(ptr) } }); } $.fn.pullToRefreshDone = function() { return this.each(function() { pullToRefreshDone(this); }); } }($); /* =============================================================================== ************ Infinite ************ =============================================================================== */ /* global $:true */ +function ($) { "use strict"; // fix https://github.com/lihongxun945/jquery-weui/issues/442 // chrome will always return 0, when use document.body.scrollTop // https://stackoverflow.com/questions/43717316/google-chrome-document-body-scrolltop-always-returns-0 var getOffset = function (container) { var tagName = container[0].tagName.toUpperCase() var scrollTop if (tagName === 'BODY' || tagName === 'HTML') { scrollTop = container.scrollTop() || $(window).scrollTop() } else { scrollTop = container.scrollTop() } var offset = container.scrollHeight() - ($(window).height() + scrollTop) console.log(offset) return offset } var Infinite = function(el, distance) { this.container = $(el); this.container.data("infinite", this); this.distance = distance || 50; this.attachEvents(); } Infinite.prototype.scroll = function() { var container = this.container; this._check(); } Infinite.prototype.attachEvents = function(off) { var el = this.container; var scrollContainer = (el[0].tagName.toUpperCase() === "BODY" ? $(document) : el); scrollContainer[off ? "off" : "on"]("scroll", $.proxy(this.scroll, this)); }; Infinite.prototype.detachEvents = function(off) { this.attachEvents(true); } Infinite.prototype._check = function() { var offset = getOffset(this.container); if(Math.abs(offset) <= this.distance) { this.container.trigger("infinite"); } } var infinite = function(el) { attachEvents(el); } $.fn.infinite = function(distance) { return this.each(function() { new Infinite(this, distance); }); } $.fn.destroyInfinite = function() { return this.each(function() { var infinite = $(this).data("infinite"); if(infinite && infinite.detachEvents) infinite.detachEvents(); }); } }($); /* global $:true */ +function ($) { "use strict"; var ITEM_ON = "weui-bar__item--on"; var showTab = function(a) { var $a = $(a); if($a.hasClass(ITEM_ON)) return; var href = $a.attr("href"); if(!/^#/.test(href)) return ; $a.parent().find("."+ITEM_ON).removeClass(ITEM_ON); $a.addClass(ITEM_ON); var content_box = $a.parents(".weui-tab").data('content'); var bd = content_box == undefined?$a.parents(".weui-tab").find(".weui-tab__bd"):$('#'+content_box); bd.find(".weui-tab__bd-item--active").removeClass("weui-tab__bd-item--active"); $(href).addClass("weui-tab__bd-item--active"); } $.showTab = showTab; $(document).on("click", ".weui-navbar__item, .weui-tabbar__item", function(e) { var $a = $(e.currentTarget); var href = $a.attr("href"); if($a.hasClass(ITEM_ON)) return; if(!/^#/.test(href)) return; e.preventDefault(); showTab($a); }); }($); /* global $:true */ + function($) { "use strict"; $(document).on("click touchstart", ".weui-search-bar__label", function(e) { $(e.target).parents(".weui-search-bar").addClass("weui-search-bar_focusing").find('input').focus(); }) /* .on("blur", ".weui-search-bar__input", function(e) { var $input = $(e.target); if(!$input.val()) $input.parents(".weui-search-bar").removeClass("weui-search-bar_focusing"); }) */ .on("click", ".weui-search-bar__cancel-btn", function(e) { var $input = $(e.target).parents(".weui-search-bar").removeClass("weui-search-bar_focusing").find(".weui-search-bar__input").val("").blur(); var callfn = $(e.target).data('callfn'); if (typeof callfn != 'undefined') { eval(callfn); } }) .on("click", ".weui-icon-clear", function(e) { var $input = $(e.target).parents(".weui-search-bar").find(".weui-search-bar__input").val("").focus(); }); }($); + function($) { "use strict"; $(document).on("click touchstart", ".mc-search-bar .search-icon", function(e) { $(e.target).parents(".mc-search-bar").addClass("active").find('input').focus(); }) /* .on("blur", ".weui-search-bar__input", function(e) { var $input = $(e.target); if(!$input.val()) $input.parents(".weui-search-bar").removeClass("weui-search-bar_focusing"); }) */ .on("input", ".mc-search-bar .keywords", function(e) { var $input = $(e.target); if($input.val()!='') { $input.parents(".mc-search-bar").find(".del-icon").show(); }else{ $input.parents(".mc-search-bar").find(".del-icon").hide(); } }) .on("click", ".mc-search-bar .clear-icon", function(e) { var $input = $(e.target).parents(".mc-search-bar").removeClass("active").find("input").val("").blur(); var $input = $(e.target).parents(".mc-search-bar").find(".del-icon").hide(); var callfn = $(e.target).data('callfn'); if (typeof callfn != 'undefined') { eval(callfn); } }) .on("click", ".mc-search-bar .del-icon", function(e) { var $input = $(e.target).parents(".mc-search-bar").find("input").val("").blur(); var callfn = $(e.target).data('callfn'); if (typeof callfn != 'undefined') { eval(callfn); } }); }($); /*=========================== Device/OS Detection ===========================*/ /* global $:true */ ;(function ($) { "use strict"; var device = {}; var ua = navigator.userAgent; var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/); device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false; // Android if (android) { device.os = 'android'; device.osVersion = android[2]; device.android = true; device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0; } if (ipad || iphone || ipod) { device.os = 'ios'; device.ios = true; } // iOS if (iphone && !ipod) { device.osVersion = iphone[2].replace(/_/g, '.'); device.iphone = true; } if (ipad) { device.osVersion = ipad[2].replace(/_/g, '.'); device.ipad = true; } if (ipod) { device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null; device.iphone = true; } // iOS 8+ changed UA if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) { if (device.osVersion.split('.')[0] === '10') { device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0]; } } // Webview device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i); // Minimal UI if (device.os && device.os === 'ios') { var osVersionArr = device.osVersion.split('.'); device.minimalUi = !device.webView && (ipod || iphone) && (osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) && $('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0; } // Check for status bar and fullscreen app mode var windowWidth = $(window).width(); var windowHeight = $(window).height(); device.statusBar = false; if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) { device.statusBar = true; } else { device.statusBar = false; } // Classes var classNames = []; // Pixel Ratio device.pixelRatio = window.devicePixelRatio || 1; classNames.push('pixel-ratio-' + Math.floor(device.pixelRatio)); if (device.pixelRatio >= 2) { classNames.push('retina'); } // OS classes if (device.os) { classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-')); if (device.os === 'ios') { var major = parseInt(device.osVersion.split('.')[0], 10); for (var i = major - 1; i >= 6; i--) { classNames.push('ios-gt-' + i); } } } // Status bar classes if (device.statusBar) { classNames.push('with-statusbar-overlay'); } else { $('html').removeClass('with-statusbar-overlay'); } // Add html classes if (classNames.length > 0) $('html').addClass(classNames.join(' ')); $.device = device; })($); /*====================================================== ************ Picker ************ ======================================================*/ /* global $:true */ /* jshint unused:false */ /* jshint multistr:true */ + function($) { "use strict"; var Picker = function (params) { var p = this; var defaults = { updateValuesOnMomentum: false, updateValuesOnTouchmove: true, rotateEffect: false, momentumRatio: 7, freeMode: false, // Common settings scrollToInput: true, inputReadOnly: true, toolbar: true, toolbarCloseText: '完成', title: '请选择', toolbarTemplate: '
\
\ {{closeText}}\

{{title}}

\
\
', }; params = params || {}; for (var def in defaults) { if (typeof params[def] === 'undefined') { params[def] = defaults[def]; } } p.params = params; p.cols = []; p.initialized = false; // Inline flag p.inline = p.params.container ? true : false; // 3D Transforms origin bug, only on safari var originBug = $.device.ios || (navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && navigator.userAgent.toLowerCase().indexOf('chrome') < 0) && !$.device.android; // Should be converted to popover function isPopover() { var toPopover = false; if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover; if (!p.inline && p.params.input) { if (p.params.onlyInPopover) toPopover = true; else { if ($.device.ios) { toPopover = $.device.ipad ? true : false; } else { if ($(window).width() >= 768) toPopover = true; } } } return toPopover; } function inPopover() { if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true; else return false; } // Value p.setValue = function (arrValues, transition) { var valueIndex = 0; for (var i = 0; i < p.cols.length; i++) { if (p.cols[i] && !p.cols[i].divider) { p.cols[i].setValue(arrValues[valueIndex], transition); valueIndex++; } } }; p.updateValue = function () { var newValue = []; var newDisplayValue = []; for (var i = 0; i < p.cols.length; i++) { if (!p.cols[i].divider) { newValue.push(p.cols[i].value); newDisplayValue.push(p.cols[i].displayValue); } } if (newValue.indexOf(undefined) >= 0) { return; } p.value = newValue; p.displayValue = newDisplayValue; if (p.params.onChange) { p.params.onChange(p, p.value, p.displayValue); } if (p.input && p.input.length > 0) { $(p.input).val(p.params.formatValue ? p.params.formatValue(p, p.value, p.displayValue) : p.value.join(' ')); $(p.input).trigger('change'); } }; // Columns Handlers p.initPickerCol = function (colElement, updateItems) { var colContainer = $(colElement); var colIndex = colContainer.index(); var col = p.cols[colIndex]; if (col.divider) return; col.container = colContainer; col.wrapper = col.container.find('.picker-items-col-wrapper'); col.items = col.wrapper.find('.picker-item'); var i, j; var wrapperHeight, itemHeight, itemsHeight, minTranslate, maxTranslate; col.replaceValues = function (values, displayValues) { col.destroyEvents(); col.values = values; col.displayValues = displayValues; var newItemsHTML = p.columnHTML(col, true); col.wrapper.html(newItemsHTML); col.items = col.wrapper.find('.picker-item'); col.calcSize(); col.setValue(col.values[0] || '', 0, true); col.initEvents(); }; col.calcSize = function () { if (!col.values.length) return; if (p.params.rotateEffect) { col.container.removeClass('picker-items-col-absolute'); if (!col.width) col.container.css({width:''}); } var colWidth, colHeight; colWidth = 0; colHeight = col.container[0].offsetHeight; wrapperHeight = col.wrapper[0].offsetHeight; itemHeight = col.items[0].offsetHeight; itemsHeight = itemHeight * col.items.length; minTranslate = colHeight / 2 - itemsHeight + itemHeight / 2; maxTranslate = colHeight / 2 - itemHeight / 2; if (col.width) { colWidth = col.width; if (parseInt(colWidth, 10) === colWidth) colWidth = colWidth + 'px'; col.container.css({width: colWidth}); } if (p.params.rotateEffect) { if (!col.width) { col.items.each(function () { var item = $(this); item.css({width:'auto'}); colWidth = Math.max(colWidth, item[0].offsetWidth); item.css({width:''}); }); col.container.css({width: (colWidth + 2) + 'px'}); } col.container.addClass('picker-items-col-absolute'); } }; col.calcSize(); col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)').transition(0); var activeIndex = 0; var animationFrameId; // Set Value Function col.setValue = function (newValue, transition, valueCallbacks) { if (typeof transition === 'undefined') transition = ''; var newActiveIndex = col.wrapper.find('.picker-item[data-picker-value="' + newValue + '"]').index(); if(typeof newActiveIndex === 'undefined' || newActiveIndex === -1) { col.value = col.displayValue = newValue; return; } var newTranslate = -newActiveIndex * itemHeight + maxTranslate; // Update wrapper col.wrapper.transition(transition); col.wrapper.transform('translate3d(0,' + (newTranslate) + 'px,0)'); // Watch items if (p.params.updateValuesOnMomentum && col.activeIndex && col.activeIndex !== newActiveIndex ) { $.cancelAnimationFrame(animationFrameId); col.wrapper.transitionEnd(function(){ $.cancelAnimationFrame(animationFrameId); }); updateDuringScroll(); } // Update items col.updateItems(newActiveIndex, newTranslate, transition, valueCallbacks); }; col.updateItems = function (activeIndex, translate, transition, valueCallbacks) { if (typeof translate === 'undefined') { translate = $.getTranslate(col.wrapper[0], 'y'); } if(typeof activeIndex === 'undefined') activeIndex = -Math.round((translate - maxTranslate)/itemHeight); if (activeIndex < 0) activeIndex = 0; if (activeIndex >= col.items.length) activeIndex = col.items.length - 1; var previousActiveIndex = col.activeIndex; col.activeIndex = activeIndex; /* col.wrapper.find('.picker-selected, .picker-after-selected, .picker-before-selected').removeClass('picker-selected picker-after-selected picker-before-selected'); col.items.transition(transition); var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform(''); var prevItems = selectedItem.prevAll().addClass('picker-before-selected'); var nextItems = selectedItem.nextAll().addClass('picker-after-selected'); */ //去掉 .picker-after-selected, .picker-before-selected 以提高性能 col.wrapper.find('.picker-selected').removeClass('picker-selected'); if (p.params.rotateEffect) { col.items.transition(transition); } var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform(''); if (valueCallbacks || typeof valueCallbacks === 'undefined') { // Update values col.value = selectedItem.attr('data-picker-value'); col.displayValue = col.displayValues ? col.displayValues[activeIndex] : col.value; // On change callback if (previousActiveIndex !== activeIndex) { if (col.onChange) { col.onChange(p, col.value, col.displayValue); } p.updateValue(); } } // Set 3D rotate effect if (!p.params.rotateEffect) { return; } var percentage = (translate - (Math.floor((translate - maxTranslate)/itemHeight) * itemHeight + maxTranslate)) / itemHeight; col.items.each(function () { var item = $(this); var itemOffsetTop = item.index() * itemHeight; var translateOffset = maxTranslate - translate; var itemOffset = itemOffsetTop - translateOffset; var percentage = itemOffset / itemHeight; var itemsFit = Math.ceil(col.height / itemHeight / 2) + 1; var angle = (-18*percentage); if (angle > 180) angle = 180; if (angle < -180) angle = -180; // Far class if (Math.abs(percentage) > itemsFit) item.addClass('picker-item-far'); else item.removeClass('picker-item-far'); // Set transform item.transform('translate3d(0, ' + (-translate + maxTranslate) + 'px, ' + (originBug ? -110 : 0) + 'px) rotateX(' + angle + 'deg)'); }); }; function updateDuringScroll() { animationFrameId = $.requestAnimationFrame(function () { col.updateItems(undefined, undefined, 0); updateDuringScroll(); }); } // Update items on init if (updateItems) col.updateItems(0, maxTranslate, 0); var allowItemClick = true; var isTouched, isMoved, touchStartY, touchCurrentY, touchStartTime, touchEndTime, startTranslate, returnTo, currentTranslate, prevTranslate, velocityTranslate, velocityTime; function handleTouchStart (e) { if (isMoved || isTouched) return; e.preventDefault(); isTouched = true; var position = $.getTouchPosition(e); touchStartY = touchCurrentY = position.y; touchStartTime = (new Date()).getTime(); allowItemClick = true; startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y'); } function handleTouchMove (e) { if (!isTouched) return; e.preventDefault(); allowItemClick = false; var position = $.getTouchPosition(e); touchCurrentY = position.y; if (!isMoved) { // First move $.cancelAnimationFrame(animationFrameId); isMoved = true; startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y'); col.wrapper.transition(0); } e.preventDefault(); var diff = touchCurrentY - touchStartY; currentTranslate = startTranslate + diff; returnTo = undefined; // Normalize translate if (currentTranslate < minTranslate) { currentTranslate = minTranslate - Math.pow(minTranslate - currentTranslate, 0.8); returnTo = 'min'; } if (currentTranslate > maxTranslate) { currentTranslate = maxTranslate + Math.pow(currentTranslate - maxTranslate, 0.8); returnTo = 'max'; } // Transform wrapper col.wrapper.transform('translate3d(0,' + currentTranslate + 'px,0)'); // Update items col.updateItems(undefined, currentTranslate, 0, p.params.updateValuesOnTouchmove); // Calc velocity velocityTranslate = currentTranslate - prevTranslate || currentTranslate; velocityTime = (new Date()).getTime(); prevTranslate = currentTranslate; } function handleTouchEnd (e) { if (!isTouched || !isMoved) { isTouched = isMoved = false; return; } isTouched = isMoved = false; col.wrapper.transition(''); if (returnTo) { if (returnTo === 'min') { col.wrapper.transform('translate3d(0,' + minTranslate + 'px,0)'); } else col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)'); } touchEndTime = new Date().getTime(); var velocity, newTranslate; if (touchEndTime - touchStartTime > 300) { newTranslate = currentTranslate; } else { velocity = Math.abs(velocityTranslate / (touchEndTime - velocityTime)); newTranslate = currentTranslate + velocityTranslate * p.params.momentumRatio; } newTranslate = Math.max(Math.min(newTranslate, maxTranslate), minTranslate); // Active Index var activeIndex = -Math.floor((newTranslate - maxTranslate)/itemHeight); // Normalize translate if (!p.params.freeMode) newTranslate = -activeIndex * itemHeight + maxTranslate; // Transform wrapper col.wrapper.transform('translate3d(0,' + (parseInt(newTranslate,10)) + 'px,0)'); // Update items col.updateItems(activeIndex, newTranslate, '', true); // Watch items if (p.params.updateValuesOnMomentum) { updateDuringScroll(); col.wrapper.transitionEnd(function(){ $.cancelAnimationFrame(animationFrameId); }); } // Allow click setTimeout(function () { allowItemClick = true; }, 100); } function handleClick(e) { if (!allowItemClick) return; $.cancelAnimationFrame(animationFrameId); /*jshint validthis:true */ var value = $(this).attr('data-picker-value'); col.setValue(value); } col.initEvents = function (detach) { var method = detach ? 'off' : 'on'; col.container[method]($.touchEvents.start, handleTouchStart); col.container[method]($.touchEvents.move, handleTouchMove); col.container[method]($.touchEvents.end, handleTouchEnd); col.items[method]('click', handleClick); }; col.destroyEvents = function () { col.initEvents(true); }; col.container[0].f7DestroyPickerCol = function () { col.destroyEvents(); }; col.initEvents(); }; p.destroyPickerCol = function (colContainer) { colContainer = $(colContainer); if ('f7DestroyPickerCol' in colContainer[0]) colContainer[0].f7DestroyPickerCol(); }; // Resize cols function resizeCols() { if (!p.opened) return; for (var i = 0; i < p.cols.length; i++) { if (!p.cols[i].divider) { p.cols[i].calcSize(); p.cols[i].setValue(p.cols[i].value, 0, false); } } } $(window).on('resize', resizeCols); // HTML Layout p.columnHTML = function (col, onlyItems) { var columnItemsHTML = ''; var columnHTML = ''; if (col.divider) { columnHTML += '
' + col.content + '
'; } else { for (var j = 0; j < col.values.length; j++) { columnItemsHTML += '
' + (col.displayValues ? col.displayValues[j] : col.values[j]) + '
'; } columnHTML += '
' + columnItemsHTML + '
'; } return onlyItems ? columnItemsHTML : columnHTML; }; p.layout = function () { var pickerHTML = ''; var pickerClass = ''; var i; p.cols = []; var colsHTML = ''; for (i = 0; i < p.params.cols.length; i++) { var col = p.params.cols[i]; colsHTML += p.columnHTML(p.params.cols[i]); p.cols.push(col); } pickerClass = 'weui-picker-modal picker-columns ' + (p.params.cssClass || '') + (p.params.rotateEffect ? ' picker-3d' : '') + (p.params.cols.length === 1 ? ' picker-columns-single' : ''); pickerHTML = '
' + (p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText).replace(/{{title}}/g, p.params.title) : '') + '
' + colsHTML + '
' + '
' + '
'; p.pickerHTML = pickerHTML; }; // Input Events function openOnInput(e) { e.preventDefault(); if (p.opened) return; p.open(); if (p.params.scrollToInput && !isPopover()) { var pageContent = p.input.parents('.content'); if (pageContent.length === 0) return; var paddingTop = parseInt(pageContent.css('padding-top'), 10), paddingBottom = parseInt(pageContent.css('padding-bottom'), 10), pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(), pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(), newPaddingBottom; var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight; if (inputTop > pageHeight) { var scrollTop = pageContent.scrollTop() + inputTop - pageHeight; if (scrollTop + pageHeight > pageScrollHeight) { newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom; if (pageHeight === pageScrollHeight) { newPaddingBottom = p.container.height(); } pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'}); } pageContent.scrollTop(scrollTop, 300); } } } function closeOnHTMLClick(e) { if (inPopover()) return; if (p.input && p.input.length > 0) { if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close(); } else { if ($(e.target).parents('.weui-picker-modal').length === 0) p.close(); } } if (p.params.input) { p.input = $(p.params.input); if (p.input.length > 0) { if (p.params.inputReadOnly) p.input.prop('readOnly', true); if (!p.inline) { p.input.on('click', openOnInput); } if (p.params.inputReadOnly) { p.input.on('focus mousedown', function (e) { e.preventDefault(); }); } } } if (!p.inline) $('html').on('click', closeOnHTMLClick); // Open function onPickerClose() { p.opened = false; if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''}); if (p.params.onClose) p.params.onClose(p); // Destroy events p.container.find('.picker-items-col').each(function () { p.destroyPickerCol(this); }); } p.opened = false; p.open = function () { var toPopover = isPopover(); if (!p.opened) { // Layout p.layout(); // Append if (toPopover) { p.pickerHTML = '
' + p.pickerHTML + '
'; p.popover = $.popover(p.pickerHTML, p.params.input, true); p.container = $(p.popover).find('.weui-picker-modal'); $(p.popover).on('close', function () { onPickerClose(); }); } else if (p.inline) { p.container = $(p.pickerHTML); p.container.addClass('picker-modal-inline'); $(p.params.container).append(p.container); } else { p.container = $($.openPicker(p.pickerHTML)); $(p.container) .on('close', function () { onPickerClose(); }); } // Store picker instance p.container[0].f7Picker = p; // Init Events p.container.find('.picker-items-col').each(function () { var updateItems = true; if ((!p.initialized && p.params.value) || (p.initialized && p.value)) updateItems = false; p.initPickerCol(this, updateItems); }); // Set value if (!p.initialized) { if (p.params.value) { p.setValue(p.params.value, 0); } } else { if (p.value) p.setValue(p.value, 0); } } // Set flag p.opened = true; p.initialized = true; if (p.params.onOpen) p.params.onOpen(p); }; // Close p.close = function (force) { if (!p.opened || p.inline) return; if (inPopover()) { $.closePicker(p.popover); return; } else { $.closePicker(p.container); return; } }; // Destroy p.destroy = function () { p.close(); if (p.params.input && p.input.length > 0) { p.input.off('click focus', openOnInput); $(p.input).data('picker', null); } $('html').off('click', closeOnHTMLClick); $(window).off('resize', resizeCols); }; if (p.inline) { p.open(); } return p; }; $(document).on("click", ".close-picker", function() { var pickerToClose = $('.weui-picker-modal.weui-picker-modal-visible'); if (pickerToClose.length > 0) { $.closePicker(pickerToClose); } }); //修复picker会滚动页面的bug $(document).on($.touchEvents.move, ".picker-modal-inner", function(e) { e.preventDefault(); }); $.openPicker = function(tpl, className, callback) { if(typeof className === "function") { callback = className; className = undefined; } $.closePicker(); var container = $("
").appendTo(document.body); container.show(); container.addClass("weui-picker-container-visible"); //关于布局的问题,如果直接放在body上,则做动画的时候会撑开body高度而导致滚动条变化。 var dialog = $(tpl).appendTo(container); dialog.width(); //通过取一次CSS值,强制浏览器不能把上下两行代码合并执行,因为合并之后会导致无法出现动画。 dialog.addClass("weui-picker-modal-visible"); callback && container.on("close", callback); return dialog; } $.updatePicker = function(tpl) { var container = $(".weui-picker-container-visible"); if(!container[0]) return false; container.html(""); var dialog = $(tpl).appendTo(container); dialog.addClass("weui-picker-modal-visible"); return dialog; } $.closePicker = function(container, callback) { if(typeof container === "function") callback = container; $(".weui-picker-modal-visible").removeClass("weui-picker-modal-visible").transitionEnd(function() { $(this).parent().remove(); callback && callback(); }).trigger("close"); }; $.fn.picker = function(params) { var args = arguments; return this.each(function() { if(!this) return; var $this = $(this); var picker = $this.data("picker"); if(!picker) { params = $.extend({ input: this }, params || {}) // https://github.com/lihongxun945/jquery-weui/issues/432 var inputValue = $this.val(); if(params.value === undefined && inputValue !== "") { params.value = (params.cols && params.cols.length > 1) ? inputValue.split(" ") : [inputValue]; } var p = $.extend({input: this}, params); picker = new Picker(p); $this.data("picker", picker); } if(typeof params === typeof "a") { picker[params].apply(picker, Array.prototype.slice.call(args, 1)); } }); }; }($); /* global $:true */ + function($) { "use strict"; var defaults; var selects = []; var Select = function(input, config) { var self = this; this.config = config; //init empty data this.data = { values: '', titles: '', origins: [], length: 0 }; this.$input = $(input); this.$input.prop("readOnly", true); this.initConfig(); config = this.config; this.$input.click($.proxy(this.open, this)); selects.push(this) } Select.prototype.initConfig = function() { this.config = $.extend({}, defaults, this.config); var config = this.config; if(!config.items || !config.items.length) return; config.items = config.items.map(function(d, i) { if(typeof d == typeof "a") { return { title: d, value: d }; }else{ return { title: d[config.showValue], value: d[config.value] }; } //return d; }); this.tpl = $.t7.compile("
" + config.toolbarTemplate + (config.multi ? config.checkboxTemplate : config.radioTemplate) + "
"); if(config.input !== undefined) this.$input.val(config.input); this.parseInitValue(); this._init = true; } Select.prototype.updateInputValue = function(values, titles) { var v, t; if(this.config.multi) { v = values.join(this.config.split); t = titles.join(this.config.split); } else { v = values[0]; t = titles[0]; } //caculate origin data var origins = []; this.config.items.forEach(function(d) { values.each(function(i, dd) { if(d.value == dd) origins.push(d); }); }); this.$input.val(t).data("values", v); this.$input.attr("value", t).attr("data-values", v); var data = { values: v, titles: t, valuesArray: values, titlesArray: titles, origins: origins, length: origins.length }; this.data = data; this.$input.trigger("change", data); this.config.onChange && this.config.onChange.call(this, data); } Select.prototype.parseInitValue = function() { var value = this.$input.val(); var items = this.config.items; //如果input为空,只有在第一次初始化的时候才保留默认选择。因为后来就是用户自己取消了全部选择,不能再为他选中默认值。 if( !this._init && (value === undefined || value == null || value === "")) return; var titles = this.config.multi ? value.split(this.config.split) : [value]; for(var i=0;i this.config.max) { $.toast("最多只能选择"+this.config.max+"个", "text"); return false } } } $.closePicker(function() { self.onClose(); callback && callback(); }); return true } Select.prototype.onClose = function() { this._open = false; if(this.config.onClose) this.config.onClose(this); } Select.prototype.getHTML = function(callback) { var config = this.config; if(config.items.length==0){ utils.error('没有可选项!'); return false; } return this.tpl({ items: config.items, title: config.title, closeText: config.closeText }) } $.fn.select = function(params, args) { return this.each(function() { var $this = $(this); if(!$this.data("weui-select")) $this.data("weui-select", new Select(this, params)); var select = $this.data("weui-select"); if(typeof params === typeof "a") select[params].call(select, args); return select; }); } defaults = $.fn.select.prototype.defaults = { items: [], input: undefined, //输入框的初始值 title: "请选择", multi: false, closeText: "确定", autoClose: true, //是否选择完成后自动关闭,只有单选模式下才有效 onChange: undefined, //function beforeClose: undefined, // function 关闭之前,如果返回false则阻止关闭 onClose: undefined, //function onOpen: undefined, //function split: ",", //多选模式下的分隔符 min: undefined, //多选模式下可用,最少选择数 max: undefined, //单选模式下可用,最多选择数 showValue:'title', //显示的字段 value:'value', //选定的值 toolbarTemplate: '
\
\ {{closeText}}\

{{title}}

\
\
', radioTemplate: '
\ {{#items}}\ \ {{/items}}\
', checkboxTemplate: '
\ {{#items}}\ \ {{/items}}\
', } }($); /*====================================================== ************ Calendar ************ ======================================================*/ /* global $:true */ /*jshint unused: false*/ +function ($) { "use strict"; var rtl = false; var defaults; var isSameDate = function (a, b) { var a = new Date(a), b = new Date(b); return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate() } var Calendar = function (params) { var p = this; params = params || {}; for (var def in defaults) { if (typeof params[def] === 'undefined') { params[def] = defaults[def]; } } p.params = params; p.initialized = false; // Inline flag p.inline = p.params.container ? true : false; // Is horizontal p.isH = p.params.direction === 'horizontal'; // RTL inverter var inverter = p.isH ? (rtl ? -1 : 1) : 1; // Animating flag p.animating = false; // Should be converted to popover function isPopover() { var toPopover = false; if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover; if (!p.inline && p.params.input) { if (p.params.onlyInPopover) toPopover = true; else { if ($.device.ios) { toPopover = $.device.ipad ? true : false; } else { if ($(window).width() >= 768) toPopover = true; } } } return toPopover; } function inPopover() { if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true; else return false; } // Format date function formatDate(date) { date = new Date(date); var year = date.getFullYear(); var month = date.getMonth(); var month1 = month + 1; var day = date.getDate(); var weekDay = date.getDay(); return p.params.dateFormat .replace(/yyyy/g, year) .replace(/yy/g, (year + '').substring(2)) .replace(/mm/g, month1 < 10 ? '0' + month1 : month1) .replace(/m/g, month1) .replace(/MM/g, p.params.monthNames[month]) .replace(/M/g, p.params.monthNamesShort[month]) .replace(/dd/g, day < 10 ? '0' + day : day) .replace(/d/g, day) .replace(/DD/g, p.params.dayNames[weekDay]) .replace(/D/g, p.params.dayNamesShort[weekDay]); } // Value p.addValue = function (value) { if (p.params.multiple) { if (!p.value) p.value = []; var inValuesIndex; for (var i = 0; i < p.value.length; i++) { if (isSameDate(value, p.value[i])) { inValuesIndex = i; } } if (typeof inValuesIndex === 'undefined') { p.value.push(value); } else { p.value.splice(inValuesIndex, 1); } p.updateValue(); } else { p.value = [value]; p.updateValue(); } }; p.setValue = function (arrValues) { var date = new Date(arrValues[0]); p.setYearMonth(date.getFullYear(), date.getMonth()); p.addValue(+ date); }; p.updateValue = function () { p.wrapper.find('.picker-calendar-day-selected').removeClass('picker-calendar-day-selected'); var i, inputValue; for (i = 0; i < p.value.length; i++) { var valueDate = new Date(p.value[i]); p.wrapper.find('.picker-calendar-day[data-date="' + valueDate.getFullYear() + '-' + valueDate.getMonth() + '-' + valueDate.getDate() + '"]').addClass('picker-calendar-day-selected'); } if (p.params.onChange) { p.params.onChange(p, p.value.map(formatDate), p.value.map(function (d) { return + new Date(typeof d === typeof 'a' ? d.split(/\D/).filter(function (a) { return !!a; }).join("-") : d); })); } if (p.input && p.input.length > 0) { if (p.params.formatValue) inputValue = p.params.formatValue(p, p.value); else { inputValue = []; for (i = 0; i < p.value.length; i++) { inputValue.push(formatDate(p.value[i])); } inputValue = inputValue.join(', '); } $(p.input).val(inputValue); $(p.input).trigger('change'); } }; // Columns Handlers p.initCalendarEvents = function () { var col; var allowItemClick = true; var isTouched, isMoved, touchStartX, touchStartY, touchCurrentX, touchCurrentY, touchStartTime, touchEndTime, startTranslate, currentTranslate, wrapperWidth, wrapperHeight, percentage, touchesDiff, isScrolling; function handleTouchStart (e) { if (isMoved || isTouched) return; // e.preventDefault(); isTouched = true; var position = $.getTouchPosition(e); touchStartX = touchCurrentY = position.x; touchStartY = touchCurrentY = position.y; touchStartTime = (new Date()).getTime(); percentage = 0; allowItemClick = true; isScrolling = undefined; startTranslate = currentTranslate = p.monthsTranslate; } function handleTouchMove (e) { if (!isTouched) return; var position = $.getTouchPosition(e); touchCurrentX = position.x; touchCurrentY = position.y; if (typeof isScrolling === 'undefined') { isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX)); } if (p.isH && isScrolling) { isTouched = false; return; } e.preventDefault(); if (p.animating) { isTouched = false; return; } allowItemClick = false; if (!isMoved) { // First move isMoved = true; wrapperWidth = p.wrapper[0].offsetWidth; wrapperHeight = p.wrapper[0].offsetHeight; p.wrapper.transition(0); } e.preventDefault(); touchesDiff = p.isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY; percentage = touchesDiff/(p.isH ? wrapperWidth : wrapperHeight); currentTranslate = (p.monthsTranslate * inverter + percentage) * 100; // Transform wrapper p.wrapper.transform('translate3d(' + (p.isH ? currentTranslate : 0) + '%, ' + (p.isH ? 0 : currentTranslate) + '%, 0)'); } function handleTouchEnd (e) { if (!isTouched || !isMoved) { isTouched = isMoved = false; return; } isTouched = isMoved = false; touchEndTime = new Date().getTime(); if (touchEndTime - touchStartTime < 300) { if (Math.abs(touchesDiff) < 10) { p.resetMonth(); } else if (touchesDiff >= 10) { if (rtl) p.nextMonth(); else p.prevMonth(); } else { if (rtl) p.prevMonth(); else p.nextMonth(); } } else { if (percentage <= -0.5) { if (rtl) p.prevMonth(); else p.nextMonth(); } else if (percentage >= 0.5) { if (rtl) p.nextMonth(); else p.prevMonth(); } else { p.resetMonth(); } } // Allow click setTimeout(function () { allowItemClick = true; }, 100); } function handleDayClick(e) { if (!allowItemClick) return; var day = $(e.target).parents('.picker-calendar-day'); if (day.length === 0 && $(e.target).hasClass('picker-calendar-day')) { day = $(e.target); } if (day.length === 0) return; // if (day.hasClass('picker-calendar-day-selected') && !p.params.multiple) return; if (day.hasClass('picker-calendar-day-disabled')) return; if (day.hasClass('picker-calendar-day-next')) p.nextMonth(); if (day.hasClass('picker-calendar-day-prev')) p.prevMonth(); var dateYear = day.attr('data-year'); var dateMonth = day.attr('data-month'); var dateDay = day.attr('data-day'); if (p.params.onDayClick) { p.params.onDayClick(p, day[0], dateYear, dateMonth, dateDay); } p.addValue(new Date(dateYear, dateMonth, dateDay).getTime()); if (p.params.closeOnSelect && !p.params.multiple) p.close(); } p.container.find('.picker-calendar-prev-month').on('click', p.prevMonth); p.container.find('.picker-calendar-next-month').on('click', p.nextMonth); p.container.find('.picker-calendar-prev-year').on('click', p.prevYear); p.container.find('.picker-calendar-next-year').on('click', p.nextYear); p.wrapper.on('click', handleDayClick); if (p.params.touchMove) { p.wrapper.on($.touchEvents.start, handleTouchStart); p.wrapper.on($.touchEvents.move, handleTouchMove); p.wrapper.on($.touchEvents.end, handleTouchEnd); } p.container[0].f7DestroyCalendarEvents = function () { p.container.find('.picker-calendar-prev-month').off('click', p.prevMonth); p.container.find('.picker-calendar-next-month').off('click', p.nextMonth); p.container.find('.picker-calendar-prev-year').off('click', p.prevYear); p.container.find('.picker-calendar-next-year').off('click', p.nextYear); p.wrapper.off('click', handleDayClick); if (p.params.touchMove) { p.wrapper.off($.touchEvents.start, handleTouchStart); p.wrapper.off($.touchEvents.move, handleTouchMove); p.wrapper.off($.touchEvents.end, handleTouchEnd); } }; }; p.destroyCalendarEvents = function (colContainer) { if ('f7DestroyCalendarEvents' in p.container[0]) p.container[0].f7DestroyCalendarEvents(); }; // Calendar Methods p.daysInMonth = function (date) { var d = new Date(date); return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); }; p.monthHTML = function (date, offset) { date = new Date(date); var year = date.getFullYear(), month = date.getMonth(), day = date.getDate(); if (offset === 'next') { if (month === 11) date = new Date(year + 1, 0); else date = new Date(year, month + 1, 1); } if (offset === 'prev') { if (month === 0) date = new Date(year - 1, 11); else date = new Date(year, month - 1, 1); } if (offset === 'next' || offset === 'prev') { month = date.getMonth(); year = date.getFullYear(); } var daysInPrevMonth = p.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000), daysInMonth = p.daysInMonth(date), firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay(); if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7; var dayDate, currentValues = [], i, j, rows = 6, cols = 7, monthHTML = '', dayIndex = 0 + (p.params.firstDay - 1), today = new Date().setHours(0,0,0,0), minDate = p.params.minDate ? new Date(p.params.minDate).getTime() : null, maxDate = p.params.maxDate ? new Date(p.params.maxDate).getTime() : null; if (p.value && p.value.length) { for (i = 0; i < p.value.length; i++) { currentValues.push(new Date(p.value[i]).setHours(0,0,0,0)); } } for (i = 1; i <= rows; i++) { var rowHTML = ''; var row = i; for (j = 1; j <= cols; j++) { var col = j; dayIndex ++; var dayNumber = dayIndex - firstDayOfMonthIndex; var addClass = ''; if (dayNumber < 0) { dayNumber = daysInPrevMonth + dayNumber + 1; addClass += ' picker-calendar-day-prev'; dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime(); } else { dayNumber = dayNumber + 1; if (dayNumber > daysInMonth) { dayNumber = dayNumber - daysInMonth; addClass += ' picker-calendar-day-next'; dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime(); } else { dayDate = new Date(year, month, dayNumber).getTime(); } } // Today if (dayDate === today) addClass += ' picker-calendar-day-today'; // Selected if (currentValues.indexOf(dayDate) >= 0) addClass += ' picker-calendar-day-selected'; // Weekend if (p.params.weekendDays.indexOf(col - 1) >= 0) { addClass += ' picker-calendar-day-weekend'; } // Disabled if ((minDate && dayDate < minDate) || (maxDate && dayDate > maxDate)) { addClass += ' picker-calendar-day-disabled'; } dayDate = new Date(dayDate); var dayYear = dayDate.getFullYear(); var dayMonth = dayDate.getMonth(); rowHTML += '
'+dayNumber+'
'; } monthHTML += '
' + rowHTML + '
'; } monthHTML = '
' + monthHTML + '
'; return monthHTML; }; p.animating = false; p.updateCurrentMonthYear = function (dir) { if (typeof dir === 'undefined') { p.currentMonth = parseInt(p.months.eq(1).attr('data-month'), 10); p.currentYear = parseInt(p.months.eq(1).attr('data-year'), 10); } else { p.currentMonth = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-month'), 10); p.currentYear = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-year'), 10); } p.container.find('.current-month-value').text(p.params.monthNames[p.currentMonth]); p.container.find('.current-year-value').text(p.currentYear); }; p.onMonthChangeStart = function (dir) { p.updateCurrentMonthYear(dir); p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'); var currentIndex = dir === 'next' ? p.months.length - 1 : 0; p.months.eq(currentIndex).addClass('picker-calendar-month-current'); p.months.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1).addClass(dir === 'next' ? 'picker-calendar-month-prev' : 'picker-calendar-month-next'); if (p.params.onMonthYearChangeStart) { p.params.onMonthYearChangeStart(p, p.currentYear, p.currentMonth); } }; p.onMonthChangeEnd = function (dir, rebuildBoth) { p.animating = false; var nextMonthHTML, prevMonthHTML, newMonthHTML; p.wrapper.find('.picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)').remove(); if (typeof dir === 'undefined') { dir = 'next'; rebuildBoth = true; } if (!rebuildBoth) { newMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), dir); } else { p.wrapper.find('.picker-calendar-month-next, .picker-calendar-month-prev').remove(); prevMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'prev'); nextMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'next'); } if (dir === 'next' || rebuildBoth) { p.wrapper.append(newMonthHTML || nextMonthHTML); } if (dir === 'prev' || rebuildBoth) { p.wrapper.prepend(newMonthHTML || prevMonthHTML); } p.months = p.wrapper.find('.picker-calendar-month'); p.setMonthsTranslate(p.monthsTranslate); if (p.params.onMonthAdd) { p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]); } if (p.params.onMonthYearChangeEnd) { p.params.onMonthYearChangeEnd(p, p.currentYear, p.currentMonth); } }; p.setMonthsTranslate = function (translate) { translate = translate || p.monthsTranslate || 0; if (typeof p.monthsTranslate === 'undefined') p.monthsTranslate = translate; p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'); var prevMonthTranslate = -(translate + 1) * 100 * inverter; var currentMonthTranslate = -translate * 100 * inverter; var nextMonthTranslate = -(translate - 1) * 100 * inverter; p.months.eq(0).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); p.months.eq(1).transform('translate3d(' + (p.isH ? currentMonthTranslate : 0) + '%, ' + (p.isH ? 0 : currentMonthTranslate) + '%, 0)').addClass('picker-calendar-month-current'); p.months.eq(2).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); }; p.nextMonth = function (transition) { if (typeof transition === 'undefined' || typeof transition === 'object') { transition = ''; if (!p.params.animate) transition = 0; } var nextMonth = parseInt(p.months.eq(p.months.length - 1).attr('data-month'), 10); var nextYear = parseInt(p.months.eq(p.months.length - 1).attr('data-year'), 10); var nextDate = new Date(nextYear, nextMonth); var nextDateTime = nextDate.getTime(); var transitionEndCallback = p.animating ? false : true; if (p.params.maxDate) { if (nextDateTime > new Date(p.params.maxDate).getTime()) { return p.resetMonth(); } } p.monthsTranslate --; if (nextMonth === p.currentMonth) { var nextMonthTranslate = -(p.monthsTranslate) * 100 * inverter; var nextMonthHTML = $(p.monthHTML(nextDateTime, 'next')).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); p.wrapper.append(nextMonthHTML[0]); p.months = p.wrapper.find('.picker-calendar-month'); if (p.params.onMonthAdd) { p.params.onMonthAdd(p, p.months.eq(p.months.length - 1)[0]); } } p.animating = true; p.onMonthChangeStart('next'); var translate = (p.monthsTranslate * 100) * inverter; p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); if (transitionEndCallback) { p.wrapper.transitionEnd(function () { p.onMonthChangeEnd('next'); }); } if (!p.params.animate) { p.onMonthChangeEnd('next'); } }; p.prevMonth = function (transition) { if (typeof transition === 'undefined' || typeof transition === 'object') { transition = ''; if (!p.params.animate) transition = 0; } var prevMonth = parseInt(p.months.eq(0).attr('data-month'), 10); var prevYear = parseInt(p.months.eq(0).attr('data-year'), 10); var prevDate = new Date(prevYear, prevMonth + 1, -1); var prevDateTime = prevDate.getTime(); var transitionEndCallback = p.animating ? false : true; if (p.params.minDate) { if (prevDateTime < new Date(p.params.minDate).getTime()) { return p.resetMonth(); } } p.monthsTranslate ++; if (prevMonth === p.currentMonth) { var prevMonthTranslate = -(p.monthsTranslate) * 100 * inverter; var prevMonthHTML = $(p.monthHTML(prevDateTime, 'prev')).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); p.wrapper.prepend(prevMonthHTML[0]); p.months = p.wrapper.find('.picker-calendar-month'); if (p.params.onMonthAdd) { p.params.onMonthAdd(p, p.months.eq(0)[0]); } } p.animating = true; p.onMonthChangeStart('prev'); var translate = (p.monthsTranslate * 100) * inverter; p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); if (transitionEndCallback) { p.wrapper.transitionEnd(function () { p.onMonthChangeEnd('prev'); }); } if (!p.params.animate) { p.onMonthChangeEnd('prev'); } }; p.resetMonth = function (transition) { if (typeof transition === 'undefined') transition = ''; var translate = (p.monthsTranslate * 100) * inverter; p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); }; p.setYearMonth = function (year, month, transition) { if (typeof year === 'undefined') year = p.currentYear; if (typeof month === 'undefined') month = p.currentMonth; if (typeof transition === 'undefined' || typeof transition === 'object') { transition = ''; if (!p.params.animate) transition = 0; } var targetDate; if (year < p.currentYear) { targetDate = new Date(year, month + 1, -1).getTime(); } else { targetDate = new Date(year, month).getTime(); } if (p.params.maxDate && targetDate > new Date(p.params.maxDate).getTime()) { return false; } if (p.params.minDate && targetDate < new Date(p.params.minDate).getTime()) { return false; } var currentDate = new Date(p.currentYear, p.currentMonth).getTime(); var dir = targetDate > currentDate ? 'next' : 'prev'; var newMonthHTML = p.monthHTML(new Date(year, month)); p.monthsTranslate = p.monthsTranslate || 0; var prevTranslate = p.monthsTranslate; var monthTranslate, wrapperTranslate; var transitionEndCallback = p.animating ? false : true; if (targetDate > currentDate) { // To next p.monthsTranslate --; if (!p.animating) p.months.eq(p.months.length - 1).remove(); p.wrapper.append(newMonthHTML); p.months = p.wrapper.find('.picker-calendar-month'); monthTranslate = -(prevTranslate - 1) * 100 * inverter; p.months.eq(p.months.length - 1).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); } else { // To prev p.monthsTranslate ++; if (!p.animating) p.months.eq(0).remove(); p.wrapper.prepend(newMonthHTML); p.months = p.wrapper.find('.picker-calendar-month'); monthTranslate = -(prevTranslate + 1) * 100 * inverter; p.months.eq(0).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); } if (p.params.onMonthAdd) { p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]); } p.animating = true; p.onMonthChangeStart(dir); wrapperTranslate = (p.monthsTranslate * 100) * inverter; p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? wrapperTranslate : 0) + '%, ' + (p.isH ? 0 : wrapperTranslate) + '%, 0)'); if (transitionEndCallback) { p.wrapper.transitionEnd(function () { p.onMonthChangeEnd(dir, true); }); } if (!p.params.animate) { p.onMonthChangeEnd(dir); } }; p.nextYear = function () { p.setYearMonth(p.currentYear + 1); }; p.prevYear = function () { p.setYearMonth(p.currentYear - 1); }; // HTML Layout p.layout = function () { var pickerHTML = ''; var pickerClass = ''; var i; var layoutDate = p.value && p.value.length ? p.value[0] : new Date().setHours(0,0,0,0); var prevMonthHTML = p.monthHTML(layoutDate, 'prev'); var currentMonthHTML = p.monthHTML(layoutDate); var nextMonthHTML = p.monthHTML(layoutDate, 'next'); var monthsHTML = '
' + (prevMonthHTML + currentMonthHTML + nextMonthHTML) + '
'; // Week days header var weekHeaderHTML = ''; if (p.params.weekHeader) { for (i = 0; i < 7; i++) { var weekDayIndex = (i + p.params.firstDay > 6) ? (i - 7 + p.params.firstDay) : (i + p.params.firstDay); var dayName = p.params.dayNamesShort[weekDayIndex]; weekHeaderHTML += '
' + dayName + '
'; } weekHeaderHTML = '
' + weekHeaderHTML + '
'; } pickerClass = 'weui-picker-calendar ' + (p.params.cssClass || ''); if(!p.inline) pickerClass = 'weui-picker-modal ' + pickerClass; var toolbarHTML = p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText) : ''; if (p.params.toolbar) { toolbarHTML = p.params.toolbarTemplate .replace(/{{closeText}}/g, p.params.toolbarCloseText) .replace(/{{monthPicker}}/g, (p.params.monthPicker ? p.params.monthPickerTemplate : '')) .replace(/{{yearPicker}}/g, (p.params.yearPicker ? p.params.yearPickerTemplate : '')); } pickerHTML = '
' + toolbarHTML + '
' + weekHeaderHTML + monthsHTML + '
' + '
'; p.pickerHTML = pickerHTML; }; // Input Events function openOnInput(e) { e.preventDefault(); if (p.opened) return; p.open(); if (p.params.scrollToInput && !isPopover()) { var pageContent = p.input.parents('.page-content'); if (pageContent.length === 0) return; var paddingTop = parseInt(pageContent.css('padding-top'), 10), paddingBottom = parseInt(pageContent.css('padding-bottom'), 10), pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(), pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(), newPaddingBottom; var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight; if (inputTop > pageHeight) { var scrollTop = pageContent.scrollTop() + inputTop - pageHeight; if (scrollTop + pageHeight > pageScrollHeight) { newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom; if (pageHeight === pageScrollHeight) { newPaddingBottom = p.container.height(); } pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'}); } pageContent.scrollTop(scrollTop, 300); } } } function closeOnHTMLClick(e) { if (inPopover()) return; if (p.input && p.input.length > 0) { if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close(); } else { if ($(e.target).parents('.weui-picker-modal').length === 0) p.close(); } } if (p.params.input) { p.input = $(p.params.input); if (p.input.length > 0) { if (p.params.inputReadOnly) p.input.prop('readOnly', true); if (!p.inline) { p.input.on('click', openOnInput); } if (p.params.inputReadOnly) { p.input.on('focus mousedown', function (e) { e.preventDefault(); }); } } } //iphone 上无法正确触发 click,会导致点击外面无法关闭 if (!p.inline) $(document).on('click touchend', closeOnHTMLClick); // Open function onPickerClose() { p.opened = false; if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''}); if (p.params.onClose) p.params.onClose(p); // Destroy events p.destroyCalendarEvents(); } p.opened = false; p.open = function () { var toPopover = isPopover() && false; var updateValue = false; if (!p.opened) { // Set date value if (!p.value) { if (p.params.value) { p.value = p.params.value; updateValue = true; } } // Layout p.layout(); // Append if (toPopover) { p.pickerHTML = '
' + p.pickerHTML + '
'; p.popover = $.popover(p.pickerHTML, p.params.input, true); p.container = $(p.popover).find('.weui-picker-modal'); $(p.popover).on('close', function () { onPickerClose(); }); } else if (p.inline) { p.container = $(p.pickerHTML); p.container.addClass('picker-modal-inline'); $(p.params.container).append(p.container); } else { p.container = $($.openPicker(p.pickerHTML)); $(p.container) .on('close', function () { onPickerClose(); }); } // Store calendar instance p.container[0].f7Calendar = p; p.wrapper = p.container.find('.picker-calendar-months-wrapper'); // Months p.months = p.wrapper.find('.picker-calendar-month'); // Update current month and year p.updateCurrentMonthYear(); // Set initial translate p.monthsTranslate = 0; p.setMonthsTranslate(); // Init events p.initCalendarEvents(); // Update input value if (updateValue) p.updateValue(); } // Set flag p.opened = true; p.initialized = true; if (p.params.onMonthAdd) { p.months.each(function () { p.params.onMonthAdd(p, this); }); } if (p.params.onOpen) p.params.onOpen(p); }; // Close p.close = function () { if (!p.opened || p.inline) return; p.animating = false; //有可能还有动画没做完,因此animating设置还没改。 if (inPopover()) { $.closePicker(p.popover); return; } else { $.closePicker(p.container); return; } }; // Destroy p.destroy = function () { p.close(); if (p.params.input && p.input.length > 0) { p.input.off('click focus', openOnInput); p.input.data("calendar", null); } $('html').off('click', closeOnHTMLClick); }; if (p.inline) { p.open(); } return p; }; var format = function(d) { return d < 10 ? "0"+d : d; } $.fn.calendar = function (params, args) { params = params || {}; return this.each(function() { var $this = $(this); if(!$this[0]) return; var p = {}; if($this[0].tagName.toUpperCase() === "INPUT") { p.input = $this; } else { p.container = $this; } var calendar = $this.data("calendar"); if(!calendar) { if(typeof params === typeof "a") { } else { if(!params.value && $this.val()) params.value = [$this.val()]; //默认显示今天 if(!params.value) { var today = new Date(); params.value = [today.getFullYear() + "/" + format(today.getMonth() + 1) + "/" + format(today.getDate())]; } calendar = $this.data("calendar", new Calendar($.extend(p, params))); } } if(typeof params === typeof "a") { calendar[params].call(calendar, args); } }); }; defaults = $.fn.calendar.prototype.defaults = { value: undefined, // 通过JS赋值,注意是数组 monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], firstDay: 1, // First day of the week, Monday weekendDays: [0, 6], // Sunday and Saturday multiple: false, dateFormat: 'yyyy/mm/dd', direction: 'horizontal', // or 'vertical' minDate: null, maxDate: null, touchMove: true, animate: true, closeOnSelect: true, monthPicker: true, monthPickerTemplate: '
' + '' + '
' + '' + '
', yearPicker: true, yearPickerTemplate: '
' + '' + '' + '' + '
', weekHeader: true, // Common settings scrollToInput: true, inputReadOnly: true, convertToPopover: true, onlyInPopover: false, toolbar: true, toolbarCloseText: 'Done', toolbarTemplate: '
' + '
' + '{{yearPicker}}' + '{{monthPicker}}' + // '{{closeText}}' + '
' + '
', /* Callbacks onMonthAdd onChange onOpen onClose onDayClick onMonthYearChangeStart onMonthYearChangeEnd */ }; }($); /* global $:true */ /* jshint unused:false*/ + function($) { "use strict"; var defaults; var formatNumber = function (n) { return n < 10 ? "0" + n : n; } var Datetime = function(input, params) { this.input = $(input); this.params = params || {}; this.initMonthes = params.monthes this.initYears = params.years var p = $.extend({}, params, this.getConfig()); $(this.input).picker(p); } Datetime.prototype = { getDays : function(max) { var days = []; for(var i=1; i<= (max||31);i++) { days.push(i < 10 ? "0"+i : i); } return days; }, getDaysByMonthAndYear : function(month, year) { var int_d = new Date(year, parseInt(month)+1-1, 1); var d = new Date(int_d - 1); return this.getDays(d.getDate()); }, getConfig: function() { var today = new Date(), params = this.params, self = this, lastValidValues; var config = { rotateEffect: false, //为了性能 cssClass: 'datetime-picker', value: [today.getFullYear(), formatNumber(today.getMonth()+1), formatNumber(today.getDate()), formatNumber(today.getHours()), (formatNumber(today.getMinutes()))], onChange: function (picker, values, displayValues) { var cols = picker.cols; var days = self.getDaysByMonthAndYear(values[1], values[0]); var currentValue = values[2]; if(currentValue > days.length) currentValue = days.length; picker.cols[4].setValue(currentValue); //check min and max var current = new Date(values[0]+'-'+values[1]+'-'+values[2]); var valid = true; if(params.min) { var min = new Date(typeof params.min === "function" ? params.min() : params.min); if(current < +min) { picker.setValue(lastValidValues); valid = false; } } if(params.max) { var max = new Date(typeof params.max === "function" ? params.max() : params.max); if(current > +max) { picker.setValue(lastValidValues); valid = false; } } valid && (lastValidValues = values); if (self.params.onChange) { self.params.onChange.apply(this, arguments); } }, formatValue: function (p, values, displayValues) { return self.params.format(p, values, displayValues); }, cols: [ { values: this.initYears }, { divider: true, // 这是一个分隔符 content: params.yearSplit }, { values: this.initMonthes }, { divider: true, // 这是一个分隔符 content: params.monthSplit }, { values: (function () { var dates = []; for (var i=1; i<=31; i++) dates.push(formatNumber(i)); return dates; })() }, ] } if (params.dateSplit) { config.cols.push({ divider: true, content: params.dateSplit }) } config.cols.push({ divider: true, content: params.datetimeSplit }) var times = self.params.times(); if (times && times.length) { config.cols = config.cols.concat(times); } var inputValue = this.input.val(); if(inputValue) config.value = params.parse(inputValue); if(this.params.value) { this.input.val(this.params.value); config.value = params.parse(this.params.value); } return config; } } $.fn.datetimePicker = function(params) { params = $.extend({}, defaults, params); return this.each(function() { if(!this) return; var $this = $(this); var datetime = $this.data("datetime"); if(!datetime) $this.data("datetime", new Datetime(this, params)); return datetime; }); }; defaults = $.fn.datetimePicker.prototype.defaults = { input: undefined, // 默认值 min: undefined, // YYYY-MM-DD 最大最小值只比较年月日,不比较时分秒 max: undefined, // YYYY-MM-DD yearSplit: '-', monthSplit: '-', dateSplit: '', // 默认为空 datetimeSplit: ' ', // 日期和时间之间的分隔符,不可为空 monthes: ('01 02 03 04 05 06 07 08 09 10 11 12').split(' '), years: (function () { var arr = []; for (var i = 1950; i <= 2030; i++) { arr.push(i); } return arr; })(), times: function () { return [ // 自定义的时间 { values: (function () { var hours = []; for (var i=0; i<24; i++) hours.push(formatNumber(i)); return hours; })() }, { divider: true, // 这是一个分隔符 content: ':' }, { values: (function () { var minutes = []; for (var i=0; i<60; i++) minutes.push(formatNumber(i)); return minutes; })() } ]; }, format: function (p, values) { // 数组转换成字符串 return p.cols.map(function (col) { return col.value || col.content; }).join(''); }, parse: function (str) { // 把字符串转换成数组,用来解析初始值 // 如果你的定制的初始值格式无法被这个默认函数解析,请自定义这个函数。比如你的时间是 '子时' 那么默认情况这个'时'会被当做分隔符而导致错误,所以你需要自己定义parse函数 // 默认兼容的分隔符 var t = str.split(this.datetimeSplit); return t[0].split(/\D/).concat(t[1].split(/:|时|分|秒/)).filter(function (d) { return !!d; }) } } }($); /*====================================================== ************ Picker ************ ======================================================*/ /* global $:true */ + function($) { "use strict"; //Popup 和 picker 之类的不要共用一个弹出方法,因为这样会导致 在 popup 中再弹出 picker 的时候会有问题。 $.openPopup = function(popup, className) { $.closePopup(); popup = $(popup); popup.show(); popup.width(); popup.addClass("weui-popup__container--visible"); var modal = popup.find(".weui-popup__modal"); modal.width(); modal.transitionEnd(function() { modal.trigger("open"); }); } $.closePopup = function(container, remove) { container = $(container || ".weui-popup__container--visible"); container.find('.weui-popup__modal').transitionEnd(function() { var $this = $(this); $this.trigger("close"); container.hide(); remove && container.remove(); }) container.removeClass("weui-popup__container--visible") }; $(document).on("click", ".close-popup, .weui-popup__overlay", function() { $.closePopup(); }) .on("click", ".open-popup", function() { $($(this).data("target")).popup(); }) .on("click", ".weui-popup__container", function(e) { if($(e.target).hasClass("weui-popup__container")) $.closePopup(); }) $.fn.popup = function() { return this.each(function() { $.openPopup(this); }); }; }($); /* =============================================================================== ************ Notification ************ =============================================================================== */ /* global $:true */ +function ($) { "use strict"; var noti, defaults, timeout, start, diffX, diffY; var touchStart = function(e) { var p = $.getTouchPosition(e); start = p; diffX = diffY = 0; noti.addClass("touching"); }; var touchMove = function(e) { if(!start) return false; e.preventDefault(); e.stopPropagation(); var p = $.getTouchPosition(e); diffX = p.x - start.x; diffY = p.y - start.y; if(diffY > 0) { diffY = Math.sqrt(diffY); } noti.css("transform", "translate3d(0, "+diffY+"px, 0)"); }; var touchEnd = function() { noti.removeClass("touching"); noti.attr("style", ""); if(diffY < 0 && (Math.abs(diffY) > noti.height()*0.38)) { $.closeNotification(); } if(Math.abs(diffX) <= 1 && Math.abs(diffY) <= 1) { noti.trigger("noti-click"); } start = false; }; var attachEvents = function(el) { el.on($.touchEvents.start, touchStart); el.on($.touchEvents.move, touchMove); el.on($.touchEvents.end, touchEnd); }; $.notification = $.noti = function(params) { params = $.extend({}, defaults, params); noti = $(".weui-notification"); if(!noti[0]) { // create a new notification noti = $('
').appendTo(document.body); attachEvents(noti); } noti.off("noti-click"); //the click event is not correct sometime: it will trigger when user is draging. if(params.onClick) noti.on("noti-click", function() { params.onClick(params.data); }); noti.html($.t7.compile(params.tpl)(params)); noti.show(); noti.addClass("weui-notification--in"); noti.data("params", params); var startTimeout = function() { if(timeout) { clearTimeout(timeout); timeout = null; } timeout = setTimeout(function() { if(noti.hasClass("weui-notification--touching")) { startTimeout(); } else { $.closeNotification(); } }, params.time); }; startTimeout(); }; $.closeNotification = function() { timeout && clearTimeout(timeout); timeout = null; var noti = $(".weui-notification").removeClass("weui-notification--in").transitionEnd(function() { $(this).remove(); }); if(noti[0]) { var params = $(".weui-notification").data("params"); if(params && params.onClose) { params.onClose(params.data); } } }; defaults = $.noti.prototype.defaults = { title: undefined, text: undefined, media: undefined, time: 4000, onClick: undefined, onClose: undefined, data: undefined, tpl: '
' + '{{#if media}}
{{media}}
{{/if}}' + '
' + '{{#if title}}
{{title}}
{{/if}}' + '{{#if text}}
{{text}}
{{/if}}' + '
' + '
' + '
' }; }($); + function($) { "use strict"; var timeout; $.toptip = function(text, duration, type) { if(!text) return; if(typeof duration === typeof "a") { type = duration; duration = undefined; } duration = duration || 3000; var className = type ? 'bg-' + type : 'bg-danger'; var $t = $('.weui-toptips').remove(); $t = $('
').appendTo(document.body); $t.html(text); $t[0].className = 'weui-toptips ' + className clearTimeout(timeout); if(!$t.hasClass('weui-toptips_visible')) { $t.show().width(); $t.addClass('weui-toptips_visible'); } timeout = setTimeout(function() { $t.removeClass('weui-toptips_visible').transitionEnd(function() { $t.remove(); }); }, duration); } }($); /* global $:true */ + function($) { "use strict"; var Slider = function (container, arg) { this.container = $(container); this.handler = this.container.find('.weui-slider__handler') this.track = this.container.find('.weui-slider__track') this.value = this.container.find('.weui-slider-box__value') this.bind() if (typeof arg === 'function') { this.callback = arg } } Slider.prototype.bind = function () { this.container .on($.touchEvents.start, $.proxy(this.touchStart, this)) .on($.touchEvents.end, $.proxy(this.touchEnd, this)); $(document.body).on($.touchEvents.move, $.proxy(this.touchMove, this)) // move even outside container } Slider.prototype.touchStart = function (e) { e.preventDefault() this.start = $.getTouchPosition(e) this.width = this.container.find('.weui-slider__inner').width() this.left = parseInt(this.container.find('.weui-slider__handler').css('left')) this.touching = true } Slider.prototype.touchMove = function (e) { if (!this.touching) return true var p = $.getTouchPosition(e) var distance = p.x - this.start.x var left = distance + this.left var per = parseInt(left / this.width * 100) if (per < 0) per = 0 if (per > 100) per = 100 this.handler.css('left', per + '%') this.track.css('width', per + '%') this.value.text(per) this.callback && this.callback.call(this, per) this.container.trigger('change', per) } Slider.prototype.touchEnd = function (e) { this.touching = false } $.fn.slider = function (arg) { this.each(function () { var $this = $(this) var slider = $this.data('slider') if (slider) return slider; else $this.data('slider', new Slider(this, arg)) }) } }($); /* =============================================================================== ************ Swipeout ************ =============================================================================== */ /* global $:true */ +function ($) { "use strict"; var cache = []; var TOUCHING = 'swipeout-touching' var Swipeout = function(el) { this.container = $(el); this.mover = this.container.find('>.weui-cell__bd') this.attachEvents(); cache.push(this) } Swipeout.prototype.touchStart = function(e) { var p = $.getTouchPosition(e); this.container.addClass(TOUCHING); this.start = p; this.startX = 0; this.startTime = + new Date; var transform = this.mover.css('transform').match(/-?[\d\.]+/g) if (transform && transform.length) this.startX = parseInt(transform[4]) this.diffX = this.diffY = 0; this._closeOthers() this.limit = this.container.find('>.weui-cell__ft').width() || 68; // 因为有的时候初始化的时候元素是隐藏的(比如在对话框内),所以在touchstart的时候计算宽度而不是初始化的时候 }; Swipeout.prototype.touchMove= function(e) { if(!this.start) return true; var p = $.getTouchPosition(e); this.diffX = p.x - this.start.x; this.diffY = p.y - this.start.y; if (Math.abs(this.diffX) < Math.abs(this.diffY)) { // 说明是上下方向在拖动 this.close() this.start = false return true; } e.preventDefault(); e.stopPropagation(); var x = this.diffX + this.startX if (x > 0) x = 0; if (Math.abs(x) > this.limit) x = - (Math.pow(-(x+this.limit), .7) + this.limit) this.mover.css("transform", "translate3d("+x+"px, 0, 0)"); }; Swipeout.prototype.touchEnd = function() { if (!this.start) return true; this.start = false; var x = this.diffX + this.startX var t = new Date - this.startTime; if (this.diffX < -5 && t < 200) { // 向左快速滑动,则打开 this.open() } else if (this.diffX >= 0 && t < 200) { // 向右快速滑动,或者单击,则关闭 this.close() } else if (x > 0 || -x <= this.limit / 2) { this.close() } else { this.open() } }; Swipeout.prototype.close = function() { this.container.removeClass(TOUCHING); this.mover.css("transform", "translate3d(0, 0, 0)"); this.container.trigger('swipeout-close'); } Swipeout.prototype.open = function() { this.container.removeClass(TOUCHING); this._closeOthers() this.mover.css("transform", "translate3d(" + (-this.limit) + "px, 0, 0)"); this.container.trigger('swipeout-open'); } Swipeout.prototype.attachEvents = function() { var el = this.mover; el.on($.touchEvents.start, $.proxy(this.touchStart, this)); el.on($.touchEvents.move, $.proxy(this.touchMove, this)); el.on($.touchEvents.end, $.proxy(this.touchEnd, this)); } Swipeout.prototype._closeOthers = function() { //close others var self = this cache.forEach(function (s) { if (s !== self) s.close() }) } var swipeout = function(el) { return new Swipeout(el); }; $.fn.swipeout = function (arg) { return this.each(function() { var $this = $(this) var s = $this.data('swipeout') || swipeout(this); $this.data('swipeout', s); if (typeof arg === typeof 'a') { s[arg]() } }); } $('.weui-cell_swiped').swipeout() // auto init }($); /*圆形进度条*/ +function( $ ) { $.fn.circliful = function(options) { var settings = $.extend({ // These are the defaults. foregroundColor: "#556b2f", backgroundColor: "#eee", fillColor: false, width: 15, dimension: 200, size: 15, percent: 50, animationStep: 1.0 }, options ); return this.each(function() { var dimension = ''; var text = ''; var info = ''; var width = ''; var size = 0; var percent = 0; var endPercent = 100; var fgcolor = ''; var bgcolor = ''; var icon = ''; var animationstep = 0.0; if($(this).hasClass('circliful')){ return ; } $(this).addClass('circliful'); if($(this).data('dimension') != undefined) { dimension = $(this).data('dimension'); } else { dimension = settings.dimension; } if($(this).data('width') != undefined) { width = $(this).data('width'); } else { width = settings.width; } if($(this).data('fontsize') != undefined) { size = $(this).data('fontsize'); } else { size = settings.size; } if($(this).data('percent') != undefined) { percent = $(this).data('percent') / 100; endPercent = $(this).data('percent'); } else { percent = settings.percent / 100; } if($(this).data('fgcolor') != undefined) { fgcolor = $(this).data('fgcolor'); } else { fgcolor = settings.foregroundColor; } if($(this).data('bgcolor') != undefined) { bgcolor = $(this).data('bgcolor'); } else { bgcolor = settings.backgroundColor; } if($(this).data('animation-step') != undefined) { animationstep = parseFloat($(this).data('animation-step')); } else { animationstep = settings.animationStep; } if($(this).data('text') != undefined) { text = $(this).data('text'); if($(this).data('icon') != undefined) { icon = ''; } if($(this).data('type') != undefined) { type = $(this).data('type'); if(type == 'half') { $(this).append('' + icon + text + ''); $(this).find('.circle-text-half').css({'line-height': (dimension / 1.45) + 'px', 'font-size' : size + 'px' }); } else { $(this).append('' + icon + text + ''); $(this).find('.circle-text').css({'line-height': dimension + 'px', 'font-size' : size + 'px' }); } } else { $(this).append('' + icon + text + ''); $(this).find('.circle-text').css({'line-height': dimension + 'px', 'font-size' : size + 'px' }); } } else if($(this).data('icon') != undefined) { } if($(this).data('info') != undefined) { info = $(this).data('info'); if($(this).data('type') != undefined) { type = $(this).data('type'); if(type == 'half') { $(this).append('' + info + ''); $(this).find('.circle-info-half').css({'line-height': (dimension * 0.9) + 'px', }); } else { $(this).append('' + info + ''); $(this).find('.circle-info').css({'line-height': (dimension * 1.25) + 'px', }); } } else { $(this).append('' + info + ''); $(this).find('.circle-info').css({'line-height': (dimension * 1.25) + 'px', }); } } $(this).width(dimension + 'px'); var canvas = $('').attr({ width: dimension, height: dimension }).appendTo($(this)).get(0); var context = canvas.getContext('2d'); var x = canvas.width / 2; var y = canvas.height / 2; var degrees = percent * 360.0; var radians = degrees * (Math.PI / 180); var radius = canvas.width / 2.5; var startAngle = 2.3 * Math.PI; var endAngle = 0; var counterClockwise = false; var curPerc = animationstep === 0.0 ? endPercent : 0.0; var curStep = Math.max(animationstep, 0.0); var circ = Math.PI * 2; var quart = Math.PI / 2; var type = ''; var fill = false; if($(this).data('type') != undefined) { type = $(this).data('type'); if(type == 'half') { var startAngle = 2.0 * Math.PI; var endAngle = 3.13; var circ = Math.PI * 1.0; var quart = Math.PI / 0.996; } } if($(this).data('fill') != undefined) { fill = $(this).data('fill'); } else { fill = settings.fillColor; } //animate foreground circle function animate(current) { context.clearRect(0, 0, canvas.width, canvas.height); context.beginPath(); context.arc(x, y, radius, endAngle, startAngle, false); context.lineWidth = width - 1; // line color context.strokeStyle = bgcolor; context.stroke(); if(fill) { context.fillStyle = fill; context.fill(); } context.beginPath(); context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false); context.lineWidth = width; // line color context.strokeStyle = fgcolor; context.stroke(); if (curPerc < endPercent) { curPerc += curStep; requestAnimationFrame(function () { animate(Math.min(curPerc, endPercent) / 100); }); } } animate(curPerc / 100); }); }; $('.circle-speed').circliful(); }( jQuery ); /*下拉菜单*/ /* ======================================================================== * Bootstrap: dropdown.js v3.3.7 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.7' function getParent($this) { var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = selector && $(selector) return $parent && $parent.length ? $parent : $this.parent() } function clearMenus(e) { if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { var $this = $(this) var $parent = getParent($this) var relatedTarget = { relatedTarget: this } if (!$parent.hasClass('open')) return if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) }) } Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $(document.createElement('div')) .addClass('dropdown-backdrop') .insertAfter($(this)) .on('click', clearMenus) } var relatedTarget = { relatedTarget: this } $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this .trigger('focus') .attr('aria-expanded', 'true') $parent .toggleClass('open') .trigger($.Event('shown.bs.dropdown', relatedTarget)) } return false } Dropdown.prototype.keydown = function (e) { if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return var $this = $(this) e.preventDefault() e.stopPropagation() if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') if (!isActive && e.which != 27 || isActive && e.which == 27) { if (e.which == 27) $parent.find(toggle).trigger('focus') return $this.trigger('click') } var desc = ' li:not(.disabled):visible a' var $items = $parent.find('.dropdown-menu' + desc) if (!$items.length) return var index = $items.index(e.target) if (e.which == 38 && index > 0) index-- // up if (e.which == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items.eq(index).trigger('focus') } // DROPDOWN PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.dropdown') if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.dropdown $.fn.dropdown = Plugin $.fn.dropdown.Constructor = Dropdown // DROPDOWN NO CONFLICT // ==================== $.fn.dropdown.noConflict = function () { $.fn.dropdown = old return this } // APPLY TO STANDARD DROPDOWN ELEMENTS // =================================== $(document) .on('click.bs.dropdown.data-api', clearMenus) .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) }(jQuery); /*灯箱预览*/ /** * LC Lightbox - LITE * yet.. another jQuery lightbox.. or not? * * @version : 1.1 * @copyright : Luca Montanari aka LCweb * @website : https://lcweb.it * @requires : jQuery v1.7 or later * Released under the MIT license */ (function ($) { lcl_objs = []; // array containing all initialized objects - useful for deeplinks lcl_shown = false; // know whether lightbox is shown lcl_is_active = false; // true when lightbox systems are acting (disable triggers) lcl_slideshow = undefined; // lightbox slideshow - setInterval object lcl_on_mobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent); // static vars avoiding useless parameters usage - related to currently opened lightbox - otherwise they are empty lcl_curr_obj = false; // store currently active object lcl_curr_opts = false; // currently active instance settings lcl_curr_vars = false; // currently active instance settings lcl_deeplink_tracked= false; // flag to track url changes and initial reading once lcl_hashless_url = false; // page URL without eventual hashes lcl_url_hash = ''; // URL hashtag // fixed values lcl_vid_instance_num = 0; // video.js instances number // lightbox structure var lb_code = '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'; //////////////////////////////////////////////////////////////////// // initialization // obj can be an array and overrides elements / [src: url/selector (only required data), title: (string), txt: (string), author: (string), ajax: bool, type: image/frame/text] lc_lightbox = function(obj, lcl_settings) { if(typeof(obj) != 'string' && (typeof(obj) != 'object' || !obj.length)) {return false;} // check among already initialized var already_init = false; $.each(lcl_objs, function(i, v) { if(JSON.stringify(v) == JSON.stringify(obj)) { already_init = v; return false; } }); if(already_init === false) { var instance = new lcl(obj, lcl_settings); lcl_objs.push(instance); return instance; } return already_init; }; // destruct method lcl_destroy = function(instance) { var index = $.inArray(instance, lcl_objs); if(index !== -1) { lcl_objs.splice(index, 1); } }; //////////////////////////////////////////////////////////////////// /* initialize */ var lcl = function(obj, settings) { var lcl_settings = $.extend({ gallery : true, // whether to display a single element or compose a gallery gallery_hook : 'rel', // attribute grouping elements - use false to create a gallery with all fetched elements live_elements : true, // if a selector is found, set true to handle automatically DOM changes preload_all : false, // whether to preload all images on document ready global_type : 'image', // force elements type src_attr : 'href', // attribute containing element's source title_attr : 'title', // attribute containing the title - is possible to specify a selector with this syntax: "> .selector" or "> span" txt_attr : 'data-lcl-txt', // attribute containing the description - is possible to specify a selector with this syntax: "> .selector" or "> span" author_attr : 'data-lcl-author', // attribute containing the author - is possible to specify a selector with this syntax: "> .selector" or "> span" slideshow : true, // whether to enable slideshow open_close_time : 500, // animation duration for lightbox opening and closing / 1000 = 1sec ol_time_diff : 100, // overlay's animation advance (on opening) and delay (on close) to window / 1000 = sec fading_time : 150, // elements fading animation duration in millisecods / 1000 = 1sec animation_time : 300, // sizing animation duration in millisecods / 1000 = 1sec slideshow_time : 6000, // slideshow interval duration in milliseconds / 1000 = 1sec autoplay : false, // autoplay slideshow - bool counter : false, // whether to display elements counter progressbar : true, // whether to display a progressbar when slideshow runs carousel : true, // whether to create a non-stop pagination cycling elements max_width : '93%', // Lightbox maximum width. Use a responsive percent value or an integer for static pixel value max_height : '93%', // Lightbox maximum height. Use a responsive percent value or an integer for static pixel value ol_opacity : 0.7, // overlay opacity / value between 0 and 1 ol_color : '#111', // background color of the overlay ol_pattern : false, // overlay patterns - insert the pattern name or false border_w : 3, // width of the lightbox border in pixels border_col : '#ddd', // color of the lightbox border padding : 10, // width of the lightbox padding in pixels radius : 4, // lightbox border radius in pixels shadow : true, // whether to apply a shadow around lightbox window remove_scrollbar: true, // whether to hide page's vertical scroller wrap_class : '', // custom classes added to wrapper - for custom styling/tracking skin : 'light', // light / dark / custom data_position : 'over', // over / under / lside / rside cmd_position : 'inner', // inner / outer ins_close_pos : 'normal', // set closing button position for inner commands - normal/corner nav_btn_pos : 'normal', // set arrows and play/pause position - normal/middle txt_hidden : 500, // whether to hide texts on lightbox opening - bool or int (related to browser's smaller side) show_title : true, // bool / whether to display titles show_descr : true, // bool / whether to display descriptions show_author : true, // bool / whether to display authors thumbs_nav : true, // enables thumbnails navigation (requires elements poster or images) tn_icons : true, // print type icons on thumbs if types are mixed tn_hidden : 500, // whether to hide thumbs nav on lightbox opening - bool or int (related to browser's smaller side) thumbs_w : 110, // width of the thumbs for the standard lightbox thumbs_h : 110, // height of the thumbs for the standard lightbox thumb_attr : false, // attribute containing thumb URL to use or false to use thumbs maker thumbs_maker_url: false, // script baseurl to create thumbnails (use src=%URL% w=%W% h=%H%) fullscreen : true, // Allow the user to expand a resized image. true/false fs_img_behavior : 'fit', // resize mode of the fullscreen image - smart/fit/fill fs_only : 500, // when directly open in fullscreen mode - bool or int (related to browser's smaller side) browser_fs_mode : true, // whether to trigger or nor browser fullscreen mode socials : true, // bool txt_toggle_cmd : true, // bool / allow text hiding download : true, // bool / whether to add download button touchswipe : true, // bool / Allow touch interactions for mobile (requires AlloyFinger) mousewheel : true, // bool / Allow elements navigation with mousewheel modal : false, // enable modal mode (no closing on overlay click) rclick_prevent : false, // whether to avoid right click on lightbox elems_parsed : function() {}, html_is_ready : function() {}, on_open : function() {}, on_elem_switch : function() {}, slideshow_start : function() {}, slideshow_end : function() {}, on_fs_enter : function() {}, on_fs_exit : function() {}, on_close : function() {}, }, settings); // Variables accessible globally var lcl_vars = { elems : [], // elements object / src: url/text (only required data), title: (string), descr: (string), author: (string), type: image/iframe/text is_arr_instance : (typeof(obj) != 'string' && typeof(obj[0].childNodes) == 'undefined') ? true : false, // true if lightbox is initialized usign direct array immission elems_count : (typeof(obj) != 'string' && typeof(obj[0].childNodes) == 'undefined') ? obj.length : $(obj).length, // elements count at the moment of lb initialization elems_selector : (typeof(obj) == 'string') ? obj : false, // elements selector - used for dynamic elements fetching elem_index : false, // current element index gallery_hook_val: false, // gallery hook value - to discard other ones preload_all_used: false, // flag to know when complete preload on document's ready has been triggered img_sizes_cache : [], // store image sizes after their preload - index is images index inner_cmd_w : false, // store inner commands width for inner->outer switch txt_exists : false, // any text exists in current element? txt_und_sizes : false, // custom lb sizes after text under calculation force_fullscreen: false, // flag to know whether to simulate "always fs" for small screens html_style : '', // html tag style (for scrollbar hiding) body_style : '', // body tag style (for scrollbar hiding) }; // textal element selector has been used? setup the real obj if(typeof(obj) == 'string') { obj = $(obj); } // .data() system to avoid issues on multi instances var lcl_ai_opts = $.data(obj, 'lcl_settings', lcl_settings); var lcl_ai_vars = $.data(obj, 'lcl_vars', lcl_vars); ///////////////////////////////////////////////////////////// /* given a string - returns an unique numerical hash */ var get_hash = function(str) { if(typeof(str) != 'string') { return str; } var hash = 0, i = 0, len = str.toString().length; while (i < len) { hash = ((hash << 5) - hash + str.charCodeAt(i++)) << 0; } return (hash < 0) ? hash * -1 : hash; }; /* element already elaborated? check through hash - returns false or elem object */ var obj_already_man = function(hash) { var found = false; $.each(lcl_ai_vars.elems, function(i, v) { if(v.hash == hash) { found = v; return false; } }); return found; }; /* revert HTML entitles that might have been used in attrs (and trim) */ var revert_html_entit = function(str) { if(!str) {return str;} str = str.replace(//g, '>') .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, "'"); return $.trim(str); }; /* returns title/text/author detecting whether to get an attribute or selector */ var attr_or_selector_data = function($elem, subj_key) { var o = lcl_ai_opts; var subj = o[subj_key]; if(subj.indexOf('> ') !== -1) { return ($elem.find( subj.replace('> ', '') ).length) ? $.trim( $elem.find( subj.replace('> ', '') ).html()) : ''; } else { return (typeof($elem.attr( subj )) != 'undefined') ? revert_html_entit( $elem.attr( subj )) : ''; } }; /* elaborate binded elements */ var setup_elems_obj = function($subj) { var o = lcl_ai_opts; // if single opening - load just that index if(!o.gallery) { $subj = $subj.eq( lcl_ai_vars.elem_index ); } // [src: url/selector (only required data), title: (string), descr: (string), author: (string), ajax: bool, type: image/frame/text] var new_elems = []; $subj.each(function() { var $e = $(this); var src = $e.attr( o.src_attr ); var hash = get_hash(src); // check against gallery hook if(lcl_ai_vars.gallery_hook_val && $e.attr(o.gallery_hook) != lcl_ai_vars.gallery_hook_val) { return true; } var already_man = obj_already_man(hash); if(already_man) { var el = already_man; } else { var type = el_type_finder(src, $e.data('lcl-type')); // compose if(type != 'unknown') { var el = { src : src, type : type, hash : (o.deeplink) ? get_hash(src) : false, title : (o.show_title) ? attr_or_selector_data($e, 'title_attr') : '', txt : (o.show_descr) ? attr_or_selector_data($e, 'txt_attr') : '', author : (o.show_author) ? attr_or_selector_data($e, 'author_attr') : '', thumb : (o.thumb_attr && typeof(o.thumb_attr) != 'undefined') ? $e.attr(o.thumb_attr) : '', download: ((type == 'image') && typeof($e.data('lcl-path')) != 'undefined') ? $e.data('lcl-path') : false, force_outer_cmd : (typeof($e.data('lcl-outer-cmd')) != 'undefined') ? $e.data('lcl-outer-cmd') : '', canonical_url : (typeof($e.data('lcl-canonical-url')) != 'undefined') ? $e.data('lcl-canonical-url') : '', }; } else { var el = { src : src, type : type, hash : (o.deeplink) ? get_hash(src) : false }; } } new_elems.push(el); }); // if only one element - remove nav arrows and thumbs nav if(new_elems.length < 2) { $('.lcl_prev, .lcl_next, #lcl_thumb_nav').remove(); } if(!new_elems.length) { return false; } // setup lcl_ai_vars.elems = new_elems; return true; }; /* given element source - return its type | accepts type forcing */ var el_type_finder = function(src, forced_type) { if(typeof(forced_type) == 'undefined') { forced_type = lcl_ai_opts.global_type; } if($.inArray(forced_type, ['image']) !== -1) { return forced_type; } src = src.toLowerCase(); var img_regex = /^https?:\/\/(?:[a-z\-]+\.)+[a-z]{2,6}(?:\/[^\/#?]+)+\.(?:jpe?g|gif|png)$/; if(img_regex.test(src)) { // image matching return 'image'; } return 'unknown'; }; ///////////// /* smart images preload */ var close_img_preload = function() { if(lcl_ai_vars.elems.length < 2 || !lcl_ai_opts.gallery) {return false;} if(lcl_ai_vars.elem_index > 0) { // prev maybe_preload(false, (lcl_ai_vars.elem_index - 1)); } if(lcl_ai_vars.elem_index != (lcl_ai_vars.elems.length - 1)) { // next maybe_preload(false, (lcl_ai_vars.elem_index + 1)); } }; /* preload images and eventually trigger showing function - if index not specified, loads current index */ var maybe_preload = function(show_when_ready, el_index) { var v = lcl_ai_vars; // if forced index is missing - use current one if(typeof(el_index) == 'undefined') { el_index = v.elem_index; } if(typeof(el_index) == 'undefined') { // if lightbox has alraedy been closed return false; } // is a preloadable element? if(v.elems[el_index].type == 'image') { var to_preload = (v.elems[el_index].type == 'image') ? v.elems[el_index].src : v.elems[el_index].poster; } else {var to_preload = '';} if(to_preload && typeof(v.img_sizes_cache[to_preload]) == 'undefined') { $('').bind("load", function(){ v.img_sizes_cache[to_preload] = { w : this.width, h : this.height }; // if sizes are zero, recalculate if(show_when_ready && el_index == v.elem_index) { show_element(); } }).attr('src', to_preload); } else { if(show_when_ready) { show_element(); } } }; ///////////// /* elements parsing */ var elems_parsing = function(inst_obj, $clicked_obj) { var o = $.data(inst_obj, 'lcl_settings'); var vars = $.data(inst_obj, 'lcl_vars'); // direct array initialization - validate and setup hashes if(vars.is_arr_instance) { var elems = []; $.each(inst_obj, function(i,v) { var el = {}; var el_type = (typeof(v.type) == 'undefined' && o.global_type) ? o.global_type : false; if(typeof(v.type) != 'undefined') {el_type = v.type;} if(el_type && $.inArray(el_type, ['image']) !== -1) { if(typeof(v.src) != 'undefined' && v.src) { el.src = v.src; el.type = el_type; el.hash = get_hash(v.src); el.title = (typeof(v.title) == 'undefined') ? '' : revert_html_entit(v.title); el.txt = (typeof(v.txt) == 'undefined') ? '' : revert_html_entit(v.txt); el.author = (typeof(v.author) == 'undefined') ? '' : revert_html_entit(v.author); el.force_outer_cmd = (typeof(v.force_outer_cmd) == 'undefined') ? false : v.force_outer_cmd; el.canonical_url = (typeof(v.canonical_url) == 'undefined') ? false : v.canonical_url; el.thumb = (typeof(v.thumb) == 'undefined') ? false : v.thumb; el.download = ((el_type != 'image') || typeof(v.download) == 'undefined') ? false : el.download; elems.push(el); } } else { var el = { src : el.src, type : 'unknown', hash : (o.deeplink) ? get_hash(el.src) : false }; elems.push(el); } }); vars.elems = elems; } // if is from DOM object - prepare elements object else { var $subj = inst_obj; // can fetch elements in real-time? save selector if(o.live_elements && vars.elems_selector) { var consider_group = ($clicked_obj && o.gallery && o.gallery_hook && typeof($(obj[0]).attr(o.gallery_hook)) != 'undefined') ? true : false; var sel = (consider_group) ? vars.elems_selector +'['+ o.gallery_hook +'='+ $clicked_obj.attr( o.gallery_hook ) +']' : vars.elems_selector; $subj = $(sel); } if(!setup_elems_obj($subj)) { if(!o.live_elements || (o.live_elements && !vars.elems_selector)) { console.error('LC Lightbox - no valid elements found'); } return false; } } // if preload every image on document's ready if(o.preload_all && !vars.preload_all_used) { vars.preload_all_used = true; $(document).ready(function(e) { $.each(vars.elems, function(i, v) { maybe_preload(false, i); }); }); } ///// // elements parsed - throw callback if(typeof(o.elems_parsed) == 'function') { o.elems_parsed.call({opts : lcl_ai_opts, vars: lcl_ai_vars}); } // elements parsed | args: elements array if(!vars.is_arr_instance) { var $subj = (vars.elems_selector) ? $(vars.elems_selector) : inst_obj; $subj.first().trigger('lcl_elems_parsed', [vars.elems]); } return true; }; elems_parsing(obj); // parsing on lightbox INIT //////////////////////////////////////////////////////////// /* open lightbox */ var open_lb = function(inst_obj, $clicked_obj) { if(lcl_shown || lcl_is_active) {return false;} lcl_shown = true; lcl_is_active = true; // setup static globals lcl_curr_obj = inst_obj; lcl_ai_opts = $.data(inst_obj, 'lcl_settings'); lcl_ai_vars = $.data(inst_obj, 'lcl_vars'); lcl_curr_opts = lcl_ai_opts; lcl_curr_vars = lcl_ai_vars; var o = lcl_ai_opts; var v = lcl_ai_vars; var $co = (typeof($clicked_obj) != 'undefined') ? $clicked_obj : false; // check instance existence if(!lcl_ai_vars) { console.error('LC Lightbox - cannot open. Object not initialized'); return false; } // set gallery hook value v.gallery_hook_val = ($co && o.gallery && o.gallery_hook && typeof($co.attr(o.gallery_hook)) != 'undefined') ? $co.attr(o.gallery_hook) : false; // parse elements if(!elems_parsing(inst_obj, $clicked_obj)) { return false; } // if there is a clicked element - set selected index if($co) { $.each(v.elems, function(i, e) { if( e.src == $co.attr(o.src_attr) ) { v.elem_index = i; return false; } }); } // array or deeplink initialization - check index existence else { if(parseInt(v.elem_index, 10) >= v.elems_count) { console.error('LC Lightbox - selected index does not exist'); return false; } } // try recalling cached images to already shape lightbox maybe_preload(false); // setup lightbox code setup_code(); touch_events(); // directly fullscreen? if(v.force_fullscreen) { enter_fullscreen(true, true); } // prepare thumbs nav if($('#lcl_thumbs_nav').length) { setup_thumbs_nav(); } // prepare first element and show maybe_preload(true); close_img_preload(); }; /* remove lightbox pre-show classes */ var rm_pre_show_classes = function() { // show window and overlay $('#lcl_wrap').removeClass('lcl_pre_show').addClass('lcl_shown'); //setTimeout(function() { $('#lcl_loader').removeClass('lcl_loader_pre_first_el'); //}, (lcl_ai_opts.ol_time_diff)); }; /* setup lightbox code */ var setup_code = function() { var o = lcl_ai_opts; var v = lcl_ai_vars; var wrap_classes = []; var css = ''; // add class if IE <= 11 and for commands positions if(typeof(document.documentMode) == 'number') { $('body').addClass('lcl_old_ie'); // actually disable middle nav if(o.cmd_position != 'outer') {o.nav_btn_pos = 'normal';} } if($('#lcl_wrap').length) {$('#lcl_wrap').remove();} $('body').append(lb_code); // command positions classes wrap_classes.push('lcl_'+o.ins_close_pos+'_close lcl_nav_btn_'+o.nav_btn_pos+' lcl_'+ o.ins_close_pos +'_close lcl_nav_btn_'+ o.nav_btn_pos); // hidden thumbs nav class if( o.tn_hidden === true || (typeof(o.tn_hidden) == 'number' && ($(window).width() < o.tn_hidden || $(window).height() < o.tn_hidden)) ) { wrap_classes.push('lcl_tn_hidden'); } // hide texts class if( o.txt_hidden === true || (typeof(o.txt_hidden) == 'number' && ($(window).width() < o.txt_hidden || $(window).height() < o.txt_hidden)) ) { wrap_classes.push('lcl_hidden_txt'); } // no carousel class if(!o.carousel) { wrap_classes.push('lcl_no_carousel'); } // mobile class if(lcl_on_mobile) {wrap_classes.push('lcl_on_mobile');} // custom classes if(o.wrap_class) {wrap_classes.push(o.wrap_class);} // manage elements wrap_classes.push('lcl_'+ o.cmd_position +'_cmd'); if(o.cmd_position != 'inner') { var nav = $('#lcl_nav_cmd').detach(); $('#lcl_wrap').prepend(nav); } if(!o.slideshow) {$('.lcl_play').remove();} if(!o.txt_toggle_cmd) {$('.lcl_txt_toggle').remove();} if(!o.socials) {$('.lcl_socials').remove();} if(!o.download) {$('.lcl_download').remove();} if(!o.counter || v.elems.length < 2) {$('.lcl_counter').remove();} if(!o.img_zoom) {$('.lcl_zoom_icon').remove();} // fullscreen v.force_fullscreen = false; if(!o.fullscreen) { $('.lcl_fullscreen').remove(); } else if(o.fs_only === true || (typeof(o.fs_only) == 'number' && ($(window).width() < o.fs_only || $(window).height() < o.fs_only))) { $('.lcl_fullscreen').remove(); lcl_ai_vars.force_fullscreen = true; } // prev/next buttons if(v.elems.length < 2) { $('.lcl_prev, .lcl_play, .lcl_next').remove(); } else { if(o.nav_btn_pos == 'middle') { css += '.lcl_prev, .lcl_next {margin: '+ o.padding +'px;}'; } } // thumbs nav if(!o.thumbs_nav || lcl_ai_vars.elems.length < 2) { $('#lcl_thumbs_nav, .lcl_thumbs_toggle').remove(); } else { $('#lcl_thumbs_nav').css('height', o.thumbs_h); // use JS to pick outerHeight after var th_margins = $('#lcl_thumbs_nav').outerHeight(true) - o.thumbs_h; css += '#lcl_window {margin-top: '+ ((o.thumbs_h - th_margins ) * -1) +'px;}'; } ////// // apply skin and layout wrap_classes.push('lcl_txt_'+ o.data_position +' lcl_'+ o.skin); css += set_wrap_padding(); css += '#lcl_overlay {background-color: '+ o.thumbs_h +'px; opacity: '+ o.ol_opacity +';}'; if(o.ol_pattern) {$('#lcl_overlay').addClass('lcl_pattern_'+ o.ol_pattern);} if(o.modal) {$('#lcl_overlay').addClass('lcl_modal');} if(o.border_w) {css += '#lcl_window {border: '+ o.border_w +'px solid '+ o.border_col +';}';} if(o.padding) {css += '#lcl_subj, #lcl_txt, #lcl_nav_cmd {margin: '+ o.padding +'px;}';} if(o.radius) {css += '#lcl_window, #lcl_contents_wrap {border-radius: '+ o.radius +'px;}';} if(o.shadow) {css += '#lcl_window {box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);}';} if(o.cmd_position == 'inner' && o.ins_close_pos == 'corner') { css += '#lcl_corner_close {'+ 'top: '+ ((o.border_w + Math.ceil($('#lcl_corner_close').outerWidth() / 2)) * -1) +'px;'+ 'right: '+ ((o.border_w + Math.ceil($('#lcl_corner_close').outerHeight() / 2)) * -1) +';'+ '}'; } // custom CSS if($('#lcl_inline_style').length) {$('#lcl_inline_style').remove();} $('head').append( ''); ////// // backup html/body inline CSS if(o.remove_scrollbar) { lcl_ai_vars.html_style = (typeof(jQuery('html').attr('style')) != 'undefined') ? jQuery('html').attr('style') : ''; lcl_ai_vars.body_style = (typeof(jQuery('body').attr('style')) != 'undefined') ? jQuery('body').attr('style') : ''; // avoid page scrolling and maintain contents position var orig_page_w = $(window).width(); $('html').css('overflow', 'hidden'); $('html').css({ 'margin-right' : ($(window).width() - orig_page_w), 'touch-action' : 'none' }); $('body').css({ 'overflow' : 'visible', 'touch-action' : 'none' }); } // opening element could already be shaped? var el = lcl_ai_vars.elems[v.elem_index]; if(el.type != 'image' || (el.type == 'image' && typeof(v.img_sizes_cache[el.src]) != 'undefined')) { wrap_classes.push('lcl_show_already_shaped'); } else { rm_pre_show_classes(); } // apply wrap classes $('#lcl_wrap').addClass( wrap_classes.join(' ') ); ////// // html is appended and ready - callback if(typeof(o.html_is_ready) == 'function') { o.html_is_ready.call({opts : lcl_ai_opts, vars: lcl_ai_vars}); } // lightbox html has been appended and managed if(!lcl_ai_vars.is_arr_instance) { var $subj = (lcl_ai_vars.elems_selector) ? $(lcl_ai_vars.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_html_is_ready', [lcl_ai_opts, lcl_ai_vars]); } }; /* returns wrapper padding css */ var set_wrap_padding = function(entering_fullscreen) { if(typeof(entering_fullscreen) == 'undefined') { var padd_horiz = (100 - parseInt(lcl_ai_opts.max_width, 10)) / 2; var padd_vert = (100 - parseInt(lcl_ai_opts.max_height, 10)) / 2; return '#lcl_wrap {padding: '+ padd_vert+'vh '+padd_horiz+'vw;}'; } else { return '#lcl_wrap {padding: 0;}'; } }; // prevent page touch scroll while moving a specific element var no_body_touch_scroll = function(selector) { var _overlay = $(selector)[0]; var _clientY = null; // remember Y position on touch start _overlay.addEventListener('touchstart', function (event) { if (event.targetTouches.length === 1) { // detect single touch _clientY = event.targetTouches[0].clientY; } }, false); _overlay.addEventListener('touchmove', function (event) { if (event.targetTouches.length === 1) { // detect single touch disableRubberBand(event); } }, false); function disableRubberBand(event) { var clientY = event.targetTouches[0].clientY - _clientY; if (_overlay.scrollTop === 0 && clientY > 0) { // element is at the top of its scroll event.preventDefault(); } if (isOverlayTotallyScrolled() && clientY < 0) { //element is at the top of its scroll event.preventDefault(); } } function isOverlayTotallyScrolled() { // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions return _overlay.scrollHeight - _overlay.scrollTop <= _overlay.clientHeight; } }; /* show element in lightbox */ var show_element = function() { if(!lcl_shown) {return false;} var v = lcl_ai_vars; var el = v.elems[v.elem_index]; $('#lcl_wrap').attr('lc-lelem', v.elem_index); // if not carousel - set classes if(!lcl_ai_opts.carousel) { $('#lcl_wrap').removeClass('lcl_first_elem lcl_last_elem'); if(!v.elem_index) { $('#lcl_wrap').addClass('lcl_first_elem'); } else if(v.elem_index == (v.elems.length - 1)) { $('#lcl_wrap').addClass('lcl_last_elem'); } } // global trigger - before element population | args: element object, element index $(document).trigger('lcl_before_populate_global', [el, v.elem_index]); // populate populate_lb(el); ////// // trigger right before EVERY element showing | args: element index, element object if(!v.is_arr_instance) { var $subj = (v.elems_selector) ? $(v.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_before_show', [el, v.elem_index]); } // global trigger - before EVERY element showing | args: element object, element index $(document).trigger('lcl_before_show_global', [el, v.elem_index]); ////// // actions on first opening if($('#lcl_wrap').hasClass('lcl_pre_first_el')) { // first element show - callback if(typeof(lcl_ai_opts.on_open) == 'function') { lcl_ai_opts.on_open.call({opts : lcl_ai_opts, vars: lcl_ai_vars}); } // first element show | args: element if(!v.is_arr_instance) { var $subj = (v.elems_selector) ? $(v.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_on_open', [el, v.elem_index]); } } ////// // set sizes and display size_elem(el); ////// $('#lcl_subj').removeClass('lcl_switching_el'); }; /* element has text ? */ var elem_has_txt = function(el) { return (el.title || el.txt || el.author) ? true : false; }; /* populate lightbox */ var populate_lb = function(el){ var el_index = lcl_ai_vars.elem_index; // reset $('#lcl_elem_wrap').removeAttr('style').removeAttr('class').empty(); // set classes/atts $('#lcl_wrap').attr('lcl-type', el.type); $('#lcl_elem_wrap').addClass('lcl_'+ el.type +'_elem'); // setup subect switch(el.type) { case 'image' : $('#lcl_elem_wrap').html(''); $('#lcl_elem_wrap').css('background-image', 'url(\''+ el.src +'\')'); break; default : // error message size $('#lcl_elem_wrap').html('


Error loading the resource ..
'); break; } if(el.download) { $('.lcl_download').show(); var arr = el.download.split('/'); var filename = arr[ (arr.length -1) ]; $('.lcl_download').html(''); } else { $('.lcl_download').hide(); } // counter $('.lcl_counter').html( (el_index+1) +' / '+ lcl_ai_vars.elems.length ); // texts if(elem_has_txt(el) && el.type != 'unknown') { $('#lcl_wrap').removeClass('lcl_no_txt'); $('.lcl_txt_toggle').show(); if(el.title) {$('#lcl_txt').append('

'+ el.title +'

');} if(el.author) {$('#lcl_txt').append('
by '+ el.author +'
');} if(el.txt) {$('#lcl_txt').append('
'+ el.txt +'
');} // set class for bottom border if(el.txt) { if(el.title && el.author) { $('#lcl_txt h5').addClass('lcl_txt_border'); } else { if($('#lcl_txt h3').length) { $('#lcl_txt h3').addClass('lcl_txt_border'); } else { $('#lcl_txt h5').addClass('lcl_txt_border'); } } } } else { $('.lcl_txt_toggle').hide(); $('#lcl_wrap').addClass('lcl_no_txt'); } // prevent body scroll moving text no_body_touch_scroll('#lcl_txt'); }; /* set element sizes */ var size_elem = function(el, flags, txt_und_sizes) { // flags: no_txt_under, inner_cmd_checked var o = lcl_ai_opts; var w, h; if(typeof(flags) == 'undefined') {flags = {};} var fs_mode = ($('.lcl_fullscreen_mode').length) ? true : false; // calculate padding and borders var add_space = (fs_mode) ? 0 : ((parseInt(o.border_w, 10) * 2) + (parseInt(o.padding, 10) * 2)); // is side-text layout? remove forced on hover if(typeof(flags.side_txt_checked) == 'undefined') { $('#lcl_wrap').removeClass('lcl_force_txt_over'); } var side_txt = (!$('.lcl_force_txt_over').length && !$('.lcl_hidden_txt').length && $.inArray(o.data_position, ['rside', 'lside']) !== -1 && elem_has_txt(el)) ? $('#lcl_txt').outerWidth() : 0; // has thumbs nav? var thumbs_nav = (!fs_mode && $('#lcl_thumbs_nav').length && !$('.lcl_tn_hidden').length) ? $('#lcl_thumbs_nav').outerHeight(true) : 0; // outer commands? var cmd_h = (!fs_mode && $('.lcl_outer_cmd').length) ? $('#lcl_nav_cmd').outerHeight() : 0; // calculate max sizes var max_w = (fs_mode) ? $(window).width() : Math.floor($('#lcl_wrap').width()) - add_space - side_txt; var max_h = (fs_mode) ? $(window).height() : Math.floor($('#lcl_wrap').height()) - add_space - thumbs_nav - cmd_h; ///////// // sizes already calculated by text under processor if(typeof(lcl_ai_vars.txt_und_sizes) == 'object') { w = lcl_ai_vars.txt_und_sizes.w; h = lcl_ai_vars.txt_und_sizes.h; } // normal processing else { switch(el.type) { case 'image' : // discard forced sizes $('#lcl_elem_wrap').css('bottom', 0); var img_sizes = lcl_ai_vars.img_sizes_cache[ el.src ]; $('#lcl_elem_wrap img').css({ 'maxWidth' : (img_sizes.w < max_w) ? img_sizes.w : max_w, 'maxHeight': (img_sizes.h < max_h) ? img_sizes.h : max_h }); w = $('#lcl_elem_wrap img').width(); h = $('#lcl_elem_wrap img').height(); // is zero? then recurse waiting a bit if(!w || !h) { setTimeout(function() { size_elem(el, flags, txt_und_sizes); }, 30); return false; } // calculate text under if(elem_has_txt(el) && !$('.lcl_hidden_txt').length && o.data_position == 'under' && typeof(flags.no_txt_under) == 'undefined') { txt_under_h(w, h, max_h); $(document).off('lcl_txt_und_calc').on('lcl_txt_und_calc', function() { if(lcl_ai_vars.txt_und_sizes) { if(lcl_ai_vars.txt_und_sizes == 'no_under') { flags.no_txt_under = true; } return size_elem(el, flags); } }); return false; } else { $('#lcl_subj').css('maxHeight', 'none'); } break; default : // error message size w = 280; h = 125; break; } } // text on side - turn into text over if small screen or tiny lb if( (o.data_position == 'rside' || o.data_position == 'lside') && !$('.lcl_no_txt').length && typeof(flags.side_txt_checked) == 'undefined' ) { var sto_w = w + add_space; var sto_h = h + add_space; var img_sizes = (el.type == 'image') ? lcl_ai_vars.img_sizes_cache[ el.src ] : ''; if( // if image size or custom size is already tiny - skip (img_sizes && img_sizes.w > 400 && img_sizes.h > 400) || (typeof(cust_w) != 'undefined' && (!cust_w || cust_w > 400)) || (typeof(cust_h) != 'undefined' && (!cust_h || cust_h > 400)) ) { if(!side_to_over_txt(sto_w, sto_h, side_txt)) { flags.side_txt_checked = true; return size_elem(el, flags); } } } // reset text under var lcl_ai_vars.txt_und_sizes = false; // force outer commands? if( typeof(flags.inner_cmd_checked) == 'undefined' && (o.cmd_position == 'inner' || el.force_outer_cmd) && inner_to_outer_cmd(el, w) ) { flags.inner_cmd_checked = true; return size_elem(el, flags); } // set lb window sizes $('#lcl_wrap').removeClass('lcl_pre_first_el'); $('#lcl_window').css({ 'width' : (fs_mode) ? '100%' : w + add_space + side_txt, 'height': (fs_mode) ? '100%' : h + add_space }); // if has to be shown already shaped if($('.lcl_show_already_shaped').length) { setTimeout(function() { // allow CSS propagation $('#lcl_wrap').removeClass('lcl_show_already_shaped'); rm_pre_show_classes(); }, 10); } // check thumbs nav arrows visibility thumbs_nav_arrows_vis(); if(typeof(lcl_size_n_show_timeout) != 'undefined') {clearTimeout(lcl_size_n_show_timeout);} var timing = ($('.lcl_first_sizing').length) ? o.open_close_time : o.animation_time; if($('.lcl_browser_resize').length) {timing = 0;} lcl_size_n_show_timeout = setTimeout(function() { if(lcl_is_active) {lcl_is_active = false;} // autoplay if first opening if($('.lcl_first_sizing').length) { if( o.autoplay && lcl_ai_vars.elems.length > 1 && (o.carousel || lcl_ai_vars.elem_index < (lcl_ai_vars.elems.length - 1)) ) { lcl_start_slideshow(); } } // fullscreen - image rendering manag if(el.type == 'image') { if($('.lcl_fullscreen_mode').length) { fs_img_manag(); } else { $('.lcl_image_elem').css('background-size', 'cover'); } } $('#lcl_wrap').removeClass('lcl_first_sizing lcl_switching_elem lcl_is_resizing lcl_browser_resize'); $(document).trigger('lcl_resized_window'); }, timing); }; /* track window size changes */ $(window).resize(function() { if(!lcl_shown || obj != lcl_curr_obj || $('.lcl_toggling_fs').length) {return false;} $('#lcl_wrap').addClass('lcl_browser_resize'); if(typeof(lcl_rs_defer) != 'undefined') {clearTimeout(lcl_rs_defer);} lcl_rs_defer = setTimeout(function() { lcl_resize(); }, 50); }); /* calculate text under size - return new element's width and height in an object */ var txt_under_h = function(curr_w, curr_h, max_height, recursive_count) { var rc = (typeof(recursive_count) == 'undefined') ? 1 : recursive_count; var fs_mode = $('.lcl_fullscreen_mode').length; var old_txt_h = Math.ceil( $('#lcl_txt').outerHeight() ); var w_ratio = curr_w / curr_h; // fullscreen mode and thumbs - text always over if(fs_mode && $('#lcl_thumbs_nav').length) { $('#lcl_wrap').addClass('lcl_force_txt_over'); $('#lcl_subj').css('maxHeight', 'none'); $('#lcl_txt').css({ 'right' : 0, 'width' : 'auto' }); lcl_ai_vars.txt_und_sizes = 'no_under'; $(document).trigger('lcl_txt_und_calc'); return false; } // reset $('#lcl_wrap').removeClass('lcl_force_txt_over').addClass('lcl_txt_under_calc'); if(!fs_mode) { $('#lcl_txt').css({ 'right' : 'auto', 'width' : curr_w }); } else { $('#lcl_txt').css({ 'right' : 0, 'width' : 'auto' }); } // wait for CSS to be rendered if(typeof(lcl_txt_under_calc) != 'undefined') {clearInterval(lcl_txt_under_calc);} lcl_txt_under_calc = setTimeout(function() { var txt_h = Math.ceil( $('#lcl_txt').outerHeight() ); var overflow = (curr_h + txt_h) - max_height; // fullscreen mode (no thumbs) - just set max height if(fs_mode) { $('#lcl_wrap').removeClass('lcl_txt_under_calc'); $('#lcl_subj').css('maxHeight', (curr_h - txt_h)); lcl_ai_vars.txt_und_sizes = {w: curr_w, h: curr_h}; $(document).trigger('lcl_txt_und_calc'); return false; } // there's overflow - recurse if(overflow > 0 && ( typeof(recursive_count) == 'undefined' || recursive_count < 10)) { var new_h = curr_h - overflow; var new_w = Math.floor(new_h * w_ratio); if(new_w < 200 || new_h < 200) { $('#lcl_wrap').removeClass('lcl_txt_under_calc').addClass('lcl_force_txt_over'); // screen too small or image excessively tall - switch to text over $('#lcl_subj').css('maxHeight', 'none'); $('#lcl_txt').css({ 'right' : 0, 'width' : 'auto' }); lcl_ai_vars.txt_und_sizes = 'no_under'; $(document).trigger('lcl_txt_und_calc'); return true; } return txt_under_h(new_w, new_h, max_height, (rc + 1)); } // no overflow - ok else { /*$('#lcl_txt').css({ 'right' : 0, 'width' : 'auto' });*/ $('#lcl_wrap').removeClass('lcl_txt_under_calc'); $('#lcl_subj').css('maxHeight', (curr_h + lcl_ai_opts.padding)); lcl_ai_vars.txt_und_sizes = { w: curr_w, h: (curr_h + txt_h) }; $(document).trigger('lcl_txt_und_calc'); return true; } }, 80); // min val to let CSS propagate }; /* is lightbox too small to show contents with side text? turn into over txt */ var side_to_over_txt = function(w, h, side_txt_w) { var already_forced = $('.lcl_force_txt_over').length; if(w < 400 || h < 400) { if(already_forced) {return true;} $('#lcl_wrap').addClass('lcl_force_txt_over'); } else { if(!already_forced) {return true;} $('#lcl_wrap').removeClass('lcl_force_txt_over'); } return false; }; /* are inner commands too wide for lb window? move to outer */ var inner_to_outer_cmd = function(el, window_width) { var o = lcl_ai_opts; var fs_mode = ($('.lcl_fullscreen_mode').length) ? true : false; // if already acted - reset if($('.lcl_forced_outer_cmd').length) { $('#lcl_wrap').removeClass('lcl_forced_outer_cmd'); $('#lcl_wrap').removeClass('lcl_outer_cmd').addClass('lcl_inner_cmd'); var nav = $('#lcl_nav_cmd').detach(); $('#lcl_window').prepend(nav); } // calculate if(!fs_mode && lcl_ai_vars.inner_cmd_w === false) { lcl_ai_vars.inner_cmd_w = 0; jQuery('#lcl_nav_cmd .lcl_icon').each(function() { if(($(this).hasClass('lcl_prev') || $(this).hasClass('lcl_next')) && o.nav_btn_pos == 'middle') { return true; } lcl_ai_vars.inner_cmd_w = lcl_ai_vars.inner_cmd_w + $(this).outerWidth(true); }); } // is wider? if(fs_mode || el.force_outer_cmd || window_width <= lcl_ai_vars.inner_cmd_w) { $('#lcl_wrap').addClass('lcl_forced_outer_cmd'); $('#lcl_wrap').removeClass('lcl_inner_cmd').addClass('lcl_outer_cmd'); var nav = $('#lcl_nav_cmd').detach(); $('#lcl_wrap').prepend(nav); return true; } else { return false; } }; ////////////////////////////////////////////////////////////// /* switch element - new_el could be "next", "prev" or element index */ var switch_elem = function(new_el, slideshow_switch) { var v = lcl_ai_vars; var carousel = lcl_ai_opts.carousel; if(lcl_is_active || v.elems.length < 2 || $('.lcl_switching_elem').length) {return false;} // find and sanitize new index if(new_el == 'next'){ if(v.elem_index == (v.elems.length - 1)) { if(!carousel) {return false;} new_el = 0; } else { new_el = v.elem_index + 1; } } else if(new_el == 'prev') { if(!v.elem_index) { if(!carousel) {return false;} new_el = (v.elems.length - 1); } else { new_el = v.elem_index - 1; } } else { new_el = parseInt(new_el, 10); if(new_el < 0 || new_el >= v.elems.length || new_el == v.elem_index) { return false; } } // if slideshow is active if(typeof(lcl_slideshow) != 'undefined') { // if isn't a slideshow switch and it is active || if isn't carousel and index is latest one - stop ss if(typeof(slideshow_switch) == 'undefined' || (!carousel && new_el == (v.elems.length - 1))) { lcl_stop_slideshow(); } } // hide current element and set a new one lcl_is_active = true; thumbs_nav_scroll_to_item(new_el); $('#lcl_wrap').addClass('lcl_switching_elem'); setTimeout(function() { $('#lcl_wrap').removeClass('lcl_playing_video'); // switching element - callback if(typeof(lcl_ai_opts.on_elem_switch) == 'function') { lcl_ai_opts.on_elem_switch.call({opts : lcl_ai_opts, vars: lcl_ai_vars, new_el : new_el}); } // switching | args: old_elem_id, new_elem_id if(!v.is_arr_instance && lcl_curr_obj) { var $subj = (v.elems_selector) ? $(v.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_on_elem_switch', [v.elem_index, new_el]); } ////// $('#lcl_wrap').removeClass('lcl_no_txt lcl_loading_iframe'); $('#lcl_txt').empty(); v.elem_index = new_el; maybe_preload(true); close_img_preload(); }, lcl_ai_opts.fading_time); }; /* temporary stop slideshow (to wait a preloader for example) */ var temp_slideshow_stop = function() { if(typeof(lcl_slideshow) == 'undefined') {return false;} clearInterval(lcl_slideshow); }; /* progressbar animation management */ var progbar_animate = function(first_run) { var o = lcl_ai_opts; if(!o.progressbar) {return false;} var delay = (first_run) ? 0 : (o.animation_time + o.fading_time); var time = o.slideshow_time + o.animation_time - delay; if(!$('#lcl_progressbar').length) { $('#lcl_wrap').append('
'); } if(typeof(lcl_pb_timeout) != 'undefined') {clearTimeout(lcl_pb_timeout);} lcl_pb_timeout = setTimeout(function() { $('#lcl_progressbar').stop(true).removeAttr('style').css('width', 0).animate({width: '100%'}, time, 'linear', function() { $('#lcl_progressbar').fadeTo(0, 0); // duration through CSS }); }, delay); }; /* close lightbox */ var close_lb = function() { if(!lcl_shown) {return false;} // lightbox is about to be closed - callback if(typeof(lcl_ai_opts.on_close) == 'function') { lcl_ai_opts.on_close.call({opts : lcl_ai_opts, vars: lcl_ai_vars}); } // event on lightbox closing if(!lcl_ai_vars.is_arr_instance) { var $subj = (lcl_ai_vars.elems_selector) ? $(lcl_ai_vars.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_on_close'); } // global trigger - on lightbox closing $(document).trigger('lcl_on_close_global'); ////// $('#lcl_wrap').removeClass('lcl_shown').addClass('lcl_is_closing lcl_tn_hidden'); lcl_stop_slideshow(); // exit fullscreen if($('.lcl_fullscreen_mode').length) { exit_browser_fs(); } // remove lb - wait for animations setTimeout(function() { $('#lcl_wrap, #lcl_inline_style').remove(); // restore html/body inline CSS if(lcl_ai_opts.remove_scrollbar) { jQuery('html').attr('style', lcl_ai_vars.html_style); jQuery('body').attr('style', lcl_ai_vars.body_style); } // global trigger - lightybox has been closed and code removed $(document).trigger('lcl_closed_global'); lcl_curr_obj = false; lcl_curr_opts = false; lcl_curr_vars = false; lcl_shown = false; lcl_is_active = false; }, (lcl_ai_opts.open_close_time + 80)); if(typeof(lcl_size_check) != 'undefined') {clearTimeout(lcl_size_check);} }; ////////////////////////////////////////////////////////////// /* Setup fullscreen mode */ var enter_fullscreen = function(set_browser_status, on_opening) { if(typeof(on_opening) == 'undefined') {on_opening = false;} if(!lcl_shown || !lcl_ai_opts.fullscreen || (!on_opening && lcl_is_active)) {return false;} var o = lcl_ai_opts; var v = lcl_ai_vars; // hide window elements $('#lcl_wrap').addClass('lcl_toggling_fs'); // enbale browser's fs if(o.browser_fs_mode && typeof(set_browser_status) != 'undefined') { if (document.documentElement.requestFullscreen) { document.documentElement.requestFullscreen(); } else if (document.documentElement.msRequestFullscreen) { document.documentElement.msRequestFullscreen(); } else if (document.documentElement.mozRequestFullScreen) { document.documentElement.mozRequestFullScreen(); } else if (document.documentElement.webkitRequestFullscreen) { document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); } } // set wrap class - recalculate sizes - show var timing = (on_opening) ? o.open_close_time : o.fading_time; setTimeout(function() { $('#lcl_wrap').addClass('lcl_fullscreen_mode'); set_wrap_padding(true); if(!on_opening) { size_elem( v.elems[v.elem_index] ); } // disable fs toogle class when has been sized $(document).on('lcl_resized_window', function() { $(document).off('lcl_resized_window'); // text under or on opening - recalculate if(on_opening || (lcl_curr_opts.data_position == 'under' && !$('.lcl_force_txt_over').length)) { size_elem( lcl_curr_vars.elems[lcl_curr_vars.elem_index] ); } setTimeout(function() { $('#lcl_wrap').removeClass('lcl_toggling_fs'); }, 150 + 50); // 150 is forced sizing timing for fs switch }); }, timing); ////// // entering fullscreen - callback if(typeof(o.on_fs_enter) == 'function') { o.on_fs_enter.call({opts : o, vars: v}); } // entering fullscreen - action if(!lcl_ai_vars.is_arr_instance) { lcl_curr_obj.first().trigger('lcl_on_fs_enter'); } }; /* fullscreen image rendering manag - smart/fit/fill */ var fs_img_manag = function() { var behav = lcl_ai_opts.fs_img_behavior; if(lcl_ai_vars.elems[lcl_ai_vars.elem_index].type != 'image') {return false;} var img_sizes = lcl_ai_vars.img_sizes_cache[ lcl_ai_vars.elems[lcl_ai_vars.elem_index].src ]; // if image is smaller than screen - bg size = auto if($('.lcl_fullscreen_mode').length && img_sizes.w <= $('#lcl_subj').width() && img_sizes.h <= $('#lcl_subj').height()) { $('.lcl_image_elem').css('background-size', 'auto'); return false; } // fit into screen if(behav == 'fit') { $('.lcl_image_elem').css('background-size', 'contain'); } // fill screen else if(behav == 'fill') { $('.lcl_image_elem').css('background-size', 'cover'); } // smart - fill only if is bigger than screen or same aspect ratio else { if(typeof(img_sizes) == 'undefined') { $('.lcl_image_elem').css('background-size', 'cover'); return false; } var ratio_diff = ($(window).width() / $(window).height()) - (img_sizes.w / img_sizes.h); var w_diff = $(window).width() - img_sizes.w; var h_diff = $(window).height() - img_sizes.h; if( (ratio_diff <= 1.15 && ratio_diff >= -1.15) && (w_diff <= 350 && h_diff <= 350) ) { // fill $('.lcl_image_elem').css('background-size', 'cover'); } else { // fit $('.lcl_image_elem').css('background-size', 'contain'); } } }; /* exit fullscreen */ var exit_fullscreen = function(set_browser_status) { if(!lcl_shown || !lcl_ai_opts.fullscreen || lcl_is_active) {return false;} var o = lcl_ai_opts; // hide window elements $('#lcl_wrap').addClass('lcl_toggling_fs'); // set wrap class - recalculate sizes - show setTimeout(function() { // disable browser's fs if(o.browser_fs_mode && typeof(set_browser_status) != 'undefined') { exit_browser_fs(); } set_wrap_padding(); $('#lcl_wrap').removeClass('lcl_fullscreen_mode'); // resize after a little while setTimeout(function() { size_elem( lcl_ai_vars.elems[lcl_ai_vars.elem_index] ); // disable fs toogle class setTimeout(function() { $('#lcl_wrap').removeClass('lcl_toggling_fs'); }, o.animation_time); // 150 is forced sizing timing for fs switch }, 550); // 550 - wait browser to exit fs }, o.fading_time); ////// // exiting fullscreen - callback if(typeof(o.on_fs_exit) == 'function') { o.on_fs_exit.call({opts : lcl_ai_opts, vars: lcl_ai_vars}); } // exiting fullscreen - action if(!lcl_ai_vars.is_arr_instance) { var $subj = (lcl_ai_vars.elems_selector) ? $(lcl_ai_vars.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_on_fs_exit'); } }; /* trigger browser instruction to exit fullscreen mode */ var exit_browser_fs = function() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } }; ////////////////////////////////////////////////////////////// /* setup thumbnails navigator */ var setup_thumbs_nav = function() { var mixed_types = false; var tracked_type = false; var uniq_id = Date.now(); $('#lcl_thumbs_nav').append(''); $('#lcl_thumbs_nav').attr('rel', uniq_id); $.each(lcl_ai_vars.elems, function(i, v) { if(v.type != 'unknown') { if(!mixed_types) { if(!tracked_type || tracked_type == v.type) { tracked_type = v.type; } else { mixed_types = true; } } var bg = '', bg_img = ''; tpc = ''; // thumbs preload class // has got a specific thumbnail? if(v.thumb) { bg_img = v.thumb; bg = 'style="background-image: url(\''+ v.thumb +'\');"'; } else { // find thumbnail for each source switch(v.type) { case 'image' : bg_img = v.src; break; } if(bg_img) { // has thumbs maker? if(lcl_ai_opts.thumbs_maker_url && v.poster) { var base = lcl_ai_opts.thumbs_maker_url; bg_img = base.replace('%URL%', encodeURIComponent(bg_img)).replace('%W%', lcl_ai_opts.thumbs_w).replace('%H%', lcl_ai_opts.thumbs_h); } bg = 'style="background-image: url(\''+ bg_img +'\');"'; } } // if iframe and html and no poster - skip if((v.type == 'html' || v.type == 'iframe') && !bg) {return true;} // video preview var vp = (v.type == 'video' && !bg) ? '' : ''; // thumbs preload class //if(!tpc && bg && typeof(lcl_ai_vars.img_sizes_cache[ bg_img ]) == 'undefined') {tpc = 'lcl_tn_preload';} tpc = 'lcl_tn_preload'; // append $('.lcl_tn_inner').append('
  • '+ vp +'
  • '); // thumbs image preload if(tpc) { thumbs_nav_img_preload(bg_img, i, uniq_id); } } }); // be sure at least 2 elements are left if($('.lcl_tn_inner > li').length < 2) { $('#lcl_thumbs_nav').remove(); return false; } $('.lcl_tn_inner > li').css('width', lcl_ai_opts.thumbs_w); if(!lcl_on_mobile) { $('.lcl_tn_inner').lcl_smoothscroll(0.3, 400, false, true); } // mixed type class if(mixed_types && lcl_ai_opts.tn_icons) { $('.lcl_tn_inner').addClass('lcl_tn_mixed_types'); } // elem offset thumbs_nav_scroll_to_item(lcl_ai_vars.elem_index); }; /* thumbs image preload */ var thumbs_nav_img_preload = function(img_url, el_index, uniq_id) { $('').bind("load", function(){ if(!lcl_ai_vars) {return false;} lcl_ai_vars.img_sizes_cache[ img_url ] = { w : this.width, h : this.height }; $('#lcl_thumbs_nav[rel='+ uniq_id +'] li[rel='+ el_index +']').removeClass('lcl_tn_preload'); setTimeout(function() { thumbs_nav_arrows_vis(); thumbs_nav_arrows_opacity(); }, 500); }).attr('src', img_url); }; /* thumbs navigator - thumbs total width */ var thumbs_nav_elems_w = function() { var thumbs_w = 0; $('.lcl_tn_inner > li').each(function() {thumbs_w = thumbs_w + $(this).outerWidth(true);}); return thumbs_w; }; /* thumbs navigator - arrows visibility */ var thumbs_nav_arrows_vis = function() { if(!$('#lcl_thumbs_nav').length) {return false;} if(thumbs_nav_elems_w() > $('.lcl_tn_inner').width()) { $('#lcl_thumbs_nav').addClass('lcl_tn_has_arr'); } else { $('#lcl_thumbs_nav').removeClass('lcl_tn_has_arr'); } }; /* thumbs navigator - arrows opacity */ var thumbs_nav_arrows_opacity = function() { var sl = $('.lcl_tn_inner').scrollLeft(); if(!sl) { $('.lcl_tn_prev').addClass('lcl_tn_disabled_arr').stop(true).fadeTo(150, 0.5); } else { $('.lcl_tn_prev').removeClass('lcl_tn_disabled_arr').stop(true).fadeTo(150, 1); } if(sl >= (thumbs_nav_elems_w() - $('.lcl_tn_inner').width())) { $('.lcl_tn_next').addClass('lcl_tn_disabled_arr').stop(true).fadeTo(150, 0.5); } else { $('.lcl_tn_next').removeClass('lcl_tn_disabled_arr').stop(true).fadeTo(150, 1); } }; $(document).on('lcl_smoothscroll_end', '.lcl_tn_inner', function(e) { if(obj != lcl_curr_obj) {return true;} thumbs_nav_arrows_opacity(); }); /* thumbs navigator - scroll to shown element - centering it */ var thumbs_nav_scroll_to_item = function(elem_id) { var $subj = $('.lcl_tn_inner > li[rel='+ elem_id +']'); if(!$subj.length) {return false;} var id = 0; $('.lcl_tn_inner > li').each(function(i,v) { if($(this).attr('rel') == elem_id) { id = i; return false; } }); // center thumb with scroll var elem_w = $('.lcl_tn_inner > li').last().outerWidth(); var margin = parseInt($('.lcl_tn_inner > li').last().css('margin-left'), 10); var wrap_w = $('.lcl_tn_inner').width(); var to_center = Math.floor( ($('.lcl_tn_inner').width() - elem_w - margin) / 2 ); var new_offset = ((elem_w * id) + margin * (id - 1)) + Math.floor(margin / 2) - to_center; $('.lcl_tn_inner').stop(true).animate({"scrollLeft" : new_offset}, 500, function() { $('.lcl_tn_inner').trigger('lcl_smoothscroll_end'); }); // set selected nav thumb class $('.lcl_tn_inner > li').removeClass('lcl_sel_thumb'); $subj.addClass('lcl_sel_thumb'); }; /* lc smooth scroll system */ // suggested ratio = 0.3 // suggested duration = 400 $.fn.lcl_smoothscroll = function(ratio, duration, ignoreX, ignoreY) { if(lcl_on_mobile) {return false;} this.off("mousemove mousedown mouseup mouseenter mouseleave"); var $subj = this, trackX = (typeof(ignoreX) == 'undefined' || !ignoreX) ? true : false, trackY = (typeof(ignoreY) == 'undefined' || !ignoreY) ? true : false, mouseout_timeout = false, curDown = false, curYPos = 0, curXPos = 0, startScrollY = 0, startScrollX = 0, scrollDif = 0; $subj.mousemove(function(m){ if(curDown === true) { $subj.stop(true); if(trackX) { $subj.scrollLeft(startScrollX + (curXPos - m.pageX)); } if(trackY) { $subj.scrollTop(startScrollY + (curYPos - m.pageY)); } } }); $subj.mouseover(function() { if(mouseout_timeout) { clearTimeout(mouseout_timeout); } }); $subj.mouseout(function() { mouseout_timeout = setTimeout(function() { curDown = false; mouseout_timeout = false; }, 500); }); $subj.mousedown(function(m){ if(typeof(lc_sms_timeout) != 'undefined') {clearTimeout(lc_sms_timeout);} curDown = true; startScrollY = $subj.scrollTop(); startScrollX = $subj.scrollLeft(); curYPos = m.pageY; curXPos = m.pageX; }); $subj.mouseup(function(m){ curDown = false; // smooth scroll var currScrollY = $subj.scrollTop(); var scrollDiffY = (startScrollY - currScrollY) * -1; var newScrollY = currScrollY + ( scrollDiffY * ratio); var currScrollX = $subj.scrollLeft(); var scrollDiffX = (startScrollX - currScrollX) * -1; var newScrollX = currScrollX + ( scrollDiffX * ratio); // thumbs nav - if is tiny movement, simulate a true click on element if( (scrollDiffY < 3 && scrollDiffY > -3) && (scrollDiffX < 3 && scrollDiffX > -3) ) { $(m.target).trigger('lcl_tn_elem_click'); return false; } // animate (only if movement was wide enough) if(scrollDiffY > 20 || scrollDiffX > 20) { var anim_obj = {}; if(trackY) { anim_obj["scrollTop"] = newScrollY; } if(trackX) { anim_obj["scrollLeft"] = newScrollX; } $subj.stop(true).animate(anim_obj, duration, 'linear', function() { $subj.trigger('lcl_smoothscroll_end'); }); } }); }; ////////////////////////////////////////////////////////////// /* setup elements - click handlers */ if(!lcl_vars.is_arr_instance) { if(lcl_settings.live_elements && lcl_vars.elems_selector) { // switch between static and dynamic elements retrieval $(document).off('click', lcl_vars.elems_selector) .on('click', lcl_vars.elems_selector, function(e) { e.preventDefault(); // update elements count - live var vars = $.data(obj, 'lcl_vars'); vars.elems_count = $(lcl_vars.elems_selector).length; // open lightbox open_lb(obj, $(this)); // binded element click - lb should open | args: clicked element obj.first().trigger('lcl_clicked_elem', [$(this)]); }); } else { obj.off('click'); obj.on('click', function(e) { e.preventDefault(); open_lb(obj, $(this)); // binded element click - lb should open obj.first().trigger('lcl_clicked_elem', [$(this)]); }); } } /* close clicking overlay or button */ $(document).on('click', '#lcl_overlay:not(.lcl_modal), .lcl_close, #lcl_corner_close', function(e) { if(obj != lcl_curr_obj) {return true;} close_lb(); }); /* navigation button - prev */ $(document).on('click', '.lcl_prev', function(e) { if(obj != lcl_curr_obj) {return true;} switch_elem('prev'); }); /* navigation button - next */ $(document).on('click', '.lcl_next', function(e) { if(obj != lcl_curr_obj) {return true;} switch_elem('next'); }); /* Keyboard events */ $(document).bind('keydown',function(e){ if(lcl_shown) { if(obj != lcl_curr_obj) {return true;} // next if (e.keyCode == 39) { e.preventDefault(); switch_elem('next'); } // prev else if (e.keyCode == 37) { e.preventDefault(); switch_elem('prev'); } // close else if (e.keyCode == 27) { e.preventDefault(); close_lb(); } // fullscreen else if(e.keyCode == 122 && lcl_ai_opts.fullscreen) { if(typeof(lcl_fs_key_timeout) != 'undefined') {clearTimeout(lcl_fs_key_timeout);} lcl_fs_key_timeout = setTimeout(function() { if($('.lcl_fullscreen_mode').length) { exit_fullscreen(); } else { enter_fullscreen(); } }, 50); } } }); /* elems navigation with mousewheel */ $(document).on('wheel', '#lcl_overlay, #lcl_window, #lcl_thumbs_nav:not(.lcl_tn_has_arr)', function(e) { if(obj != lcl_curr_obj) {return true;} // if has scrollers - avoid if( lcl_curr_opts.mousewheel && e.target.scrollHeight <= $(e.target).height()) { e.preventDefault(); var delta = e.originalEvent.deltaY; if(delta > 0) {switch_elem('next');} else {switch_elem('prev');} } }); /* next element clicking on image or zoom (where available with doubleclick) */ $(document).on('click', '.lcl_image_elem', function(e) { if(obj != lcl_curr_obj) {return true;} lcl_img_click_track = setTimeout(function() { if(!$('.lcl_zoom_wrap').length) { switch_elem('next'); } }, 250); }); /* toggle text */ $(document).on('click', '.lcl_txt_toggle', function(e) { if(obj != lcl_curr_obj) {return true;} var o = lcl_ai_opts; // class lcl_toggling_txt enables window sizing animations if(!lcl_is_active && !$('.lcl_no_txt').length && !$('.lcl_toggling_txt').length) { if(o.data_position != 'over') { var txt_on_side = (o.data_position == 'rside' || o.data_position == 'lside') ? true : false; var forced_over = $('.lcl_force_txt_over').length; var timing = (o.animation_time < 150) ? o.animation_time : 150; var classes_delay = 0; // if text on side - hide subject if(txt_on_side && !forced_over) { $('#lcl_subj').fadeTo(timing, 0); } // text under - hide else { if(!forced_over) { $('#lcl_contents_wrap').fadeTo(timing, 0); classes_delay = timing; } } setTimeout(function() { $('#lcl_wrap').toggleClass('lcl_hidden_txt'); }, classes_delay); if(!forced_over) { lcl_is_active = true; $('#lcl_wrap').addClass('lcl_toggling_txt'); // wait until text is hidden setTimeout(function() { lcl_is_active = false; lcl_resize(); }, o.animation_time); // after sizing - disable animations again setTimeout(function() { $('#lcl_wrap').removeClass('lcl_toggling_txt'); if(txt_on_side && !forced_over) { $('#lcl_subj').fadeTo(timing, 1); } else { if(!forced_over) { $('#lcl_contents_wrap').fadeTo(timing, 1); } } }, (o.animation_time * 2) + 50); } } // text over - just hide else { $('#lcl_wrap').toggleClass('lcl_hidden_txt'); } } }); /* start/end slideshow */ $(document).on('click', '.lcl_play', function(e) { if(obj != lcl_curr_obj) {return true;} if($('.lcl_is_playing').length) { lcl_stop_slideshow(); } else { lcl_start_slideshow(); } }); /* toggle socials */ $(document).on('click', '.lcl_socials', function(e) { if(obj != lcl_curr_obj) {return true;} // show if(!$('.lcl_socials > div').length) { var el = lcl_curr_vars.elems[ lcl_curr_vars.elem_index ]; var page_url = encodeURIComponent(window.location.href); var url = encodeURIComponent(el.src); var title = encodeURIComponent(el.title); var descr = encodeURIComponent(el.txt); // prepare and append code var code = '
    '+ ''+ ''; // on mobile - use Whatsapp if(lcl_on_mobile) { code += '
    '; } code += ''+ '
    '; $('.lcl_socials').addClass('lcl_socials_shown').html(code); setTimeout(function() { // delay to let CSS execute animation $('.lcl_socials_tt').addClass('lcl_show_tt'); }, 20); } // hide else { $('.lcl_socials_tt').removeClass('lcl_show_tt'); setTimeout(function() { $('.lcl_socials').removeClass('lcl_socials_shown').empty(); }, 260); } }); /* toggle fullscreen via button */ $(document).on('click', '.lcl_fullscreen', function(e) { if(obj != lcl_curr_obj) {return true;} if($('.lcl_fullscreen_mode').length) { exit_fullscreen(true); } else { enter_fullscreen(true); } }); /* thumbs navigator - toggle */ $(document).on('click', '.lcl_thumbs_toggle', function(e) { if(obj != lcl_curr_obj) {return true;} var fs_mode = $('.lcl_fullscreen_mode').length; $('#lcl_wrap').addClass('lcl_toggling_tn').toggleClass('lcl_tn_hidden'); // if not fullscreen - hide contents if(!fs_mode) { setTimeout(function() { lcl_resize(); }, 160); } setTimeout(function() { $('#lcl_wrap').removeClass('lcl_toggling_tn'); }, lcl_curr_opts.animation_time + 50); }); /* thumbs navigator - switch element */ var tn_track_touch = (lcl_on_mobile) ? ' click' : ''; $(document).on('lcl_tn_elem_click'+tn_track_touch, '.lcl_tn_inner > li', function(e) { if(obj != lcl_curr_obj) {return true;} var elem_id = $(this).attr('rel'); switch_elem( elem_id ); }); /* thumbs navigator - navigate with arrows click */ $(document).on('click', '.lcl_tn_prev:not(.lcl_tn_disabled_arr)', function(e) { if(obj != lcl_curr_obj) {return true;} $('.lcl_tn_inner').stop(true).animate({"scrollLeft" : ($('.lcl_tn_inner').scrollLeft() - lcl_curr_opts.thumbs_w - 10)}, 300, 'linear', function() { $('.lcl_tn_inner').trigger('lcl_smoothscroll_end'); }); }); $(document).on('click', '.lcl_tn_next:not(.lcl_tn_disabled_arr)', function(e) { if(obj != lcl_curr_obj) {return true;} $('.lcl_tn_inner').stop(true).animate({"scrollLeft" : ($('.lcl_tn_inner').scrollLeft() + lcl_curr_opts.thumbs_w + 10)}, 300, 'linear', function() { $('.lcl_tn_inner').trigger('lcl_smoothscroll_end'); }); }); /* thumbs navigator - navigate with mousewheel */ $(document).on('wheel', '#lcl_thumbs_nav.lcl_tn_has_arr', function(e) { if(obj != lcl_curr_obj) {return true;} e.preventDefault(); var delta = e.originalEvent.deltaY; if(delta > 0) {$('.lcl_tn_prev:not(.lcl_tn_disabled_arr)').trigger('click');} else {$('.lcl_tn_next:not(.lcl_tn_disabled_arr)').trigger('click');} }); /* right click prevent */ $(document).on("contextmenu", "#lcl_wrap *", function() { if(obj != lcl_curr_obj) {return true;} if(lcl_ai_opts.rclick_prevent) { return false; } }); /* avoid page scrolling on touch devices */ $(window).on('touchmove', function(e) { var $t = $(e.target); if(!lcl_shown || !lcl_on_mobile) {return true;} if(obj != lcl_curr_obj) {return true;} if(!$(e.target).parents('#lcl_window').length && !$(e.target).parents('#lcl_thumbs_nav').length) { e.preventDefault(); } }); ///////////////////////////////////////////////////////////// // touchswipe & zoom on pinch - requires alloy_finger.min.js var touch_events = function() { if(typeof(AlloyFinger) != 'function') {return false;} lcl_is_pinching = false; var el = document.querySelector('#lcl_wrap'); var af = new AlloyFinger(el, { singleTap: function(e) { // close lb tapping on overlay if($(e.target).attr('id') == 'lcl_overlay' && !lcl_ai_opts.modal) { lcl_close(); } }, doubleTap: function (e) { e.preventDefault(); zoom(true); }, pinch: function(e) { e.preventDefault(); lcl_is_pinching = true; if(typeof(lcl_swipe_delay) != 'undefined') {clearTimeout(lcl_swipe_delay);} if(typeof(lcl_pinch_delay) != 'undefined') {clearTimeout(lcl_pinch_delay);} lcl_pinch_delay = setTimeout(function() { if(e.scale > 1.2) { zoom(true); } else if(e.scale < 0.8) { zoom(false); } // avoid swipe if zoom-out setTimeout(function() { lcl_is_pinching = false; }, 300); }, 20); }, touchStart: function(e) { lcl_touchstartX = e.changedTouches[0].clientX; }, touchEnd: function(e) { // simulate swipe with treshold var diff = lcl_touchstartX - e.changedTouches[0].clientX; if((diff < -50 || diff > 50) && !lcl_is_pinching) { // ignore if consulting thumbs nav if($(e.target).parents('#lcl_thumbs_nav').length) { return false; } // do not swipe on zoomed if($(e.target).parents('.lcl_zoom_wrap').length) { return false; } var delay = ($(e.target).parents('.lcl_zoomable').length) ? 250 : 0; if(typeof(lcl_swipe_delay) != 'undefined') {clearTimeout(lcl_swipe_delay);} lcl_swipe_delay = setTimeout(function() { if(diff < -50) { switch_elem('prev'); } else { switch_elem('next'); } }, delay); } } }); }; ///////////////////////////////////////////////////////////// //// PUBLIC METHODS // set current settings and vars - for actions with lightbox opened - return false if object not initialized var set_curr_vars = function() { if(!lcl_curr_obj) {return false;} lcl_ai_vars = $.data(lcl_curr_obj, 'lcl_vars'); lcl_ai_opts = $.data(lcl_curr_obj, 'lcl_settings'); if(!lcl_ai_vars) { console.error('LC Lightbox. Object not initialized'); return false; } return true; }; // open lightbox lcl_open = function(obj, index) { lcl_ai_vars = $.data(obj, 'lcl_vars'); var v = lcl_ai_vars; // check instance existence if(!v) { console.error('LC Lightbox - cannot open. Object not initialized'); return false; } else if(typeof(v.elems[index]) == 'undefined') { console.error('LC Lightbox - cannot open. Unexisting index'); return false; } else { v.elem_index = index; $clicked_obj = (v.is_arr_instance) ? false : $(obj[index]); return open_lb(obj, $clicked_obj); } }; // resize lightbox lcl_resize = function() { if(!lcl_shown || lcl_is_active || !set_curr_vars()) {return false;} var v = lcl_ai_vars; if(typeof(lcl_size_check) != 'undefined') {clearTimeout(lcl_size_check);} lcl_size_check = setTimeout(function() { $('#lcl_wrap').addClass('lcl_is_resizing'); thumbs_nav_arrows_opacity(); var el = v.elems[ v.elem_index ]; return size_elem(el); }, 20); }; // close lightbox and destroy vars lcl_close = function() { if(!lcl_shown || lcl_is_active || !set_curr_vars()) {return false;} return close_lb(); }; // pagination (next/prev/index) lcl_switch = function(new_el) { if(!lcl_shown || lcl_is_active || !set_curr_vars()) {return false;} return switch_elem(new_el); }; // start slideshow lcl_start_slideshow = function(restart) { if(!lcl_shown || (typeof(restart) == 'undefined' && typeof(lcl_slideshow) != 'undefined') || !set_curr_vars()) {return false;} var o = lcl_ai_opts; // if is latest element and isn't carousel - return false if(!o.carousel && lcl_ai_vars.elem_index == (lcl_ai_vars.elems.length - 1)) { return false; } if(typeof(lcl_slideshow) != 'undefined') {clearInterval(lcl_slideshow);} // if reset timing $('#lcl_wrap').addClass('lcl_is_playing'); var time = o.animation_time + o.slideshow_time; // use progressbar? progbar_animate(true); // start lcl_slideshow = setInterval(function() { progbar_animate(false); switch_elem('next', true); }, time); ////// if(typeof(restart) == 'undefined') { // slideshow start - callback if(typeof(o.slideshow_start) == 'function') { o.slideshow_start.call({opts : o, vars: lcl_ai_vars}); } // slideshow start - hook | args: interval time if(!lcl_ai_vars.is_arr_instance) { var $subj = (lcl_ai_vars.elems_selector) ? $(lcl_ai_vars.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_slideshow_start', [time]); } } return true; }; // stop slideshow lcl_stop_slideshow = function() { if(!lcl_shown || typeof(lcl_slideshow) == 'undefined' || !set_curr_vars()) {return false;} var o = lcl_ai_opts; // check instance existence if(!o) { console.error('LC Lightbox. Object not initialized'); return false; } clearInterval(lcl_slideshow); lcl_slideshow = undefined; $('#lcl_wrap').removeClass('lcl_is_playing'); $('#lcl_progressbar').stop(true).animate({'marginTop' : ($('#lcl_progressbar').height() * -3)}, 300, function() { $(this).remove(); }); ////// // slideshow end - callback if(typeof(o.slideshow_end) == 'function') { o.slideshow_end.call({opts : lcl_ai_opts, vars: lcl_ai_vars}); } // slideshow end - hook if(!lcl_ai_vars.is_arr_instance) { var $subj = (lcl_ai_vars.elems_selector) ? $(lcl_ai_vars.elems_selector) : lcl_curr_obj; $subj.first().trigger('lcl_slideshow_end', []); } return true; }; return obj; }; })(jQuery); /* =============================================================================== ************ raty星星评分 ************ =============================================================================== */ +function($){ $.ratyli = function(el, options){ var base = this; base.$el = $(el); base.el = el; base.$el.data("ratyli", base); base.init = function(){ // extend with parameters base.options = $.extend({},$.ratyli.defaultOptions, options); // extend with html5 dataset base.options =$.extend({},base.options,base.$el.data()); // for the loop base.set(base.options.rate,true); // event listeners for rating signs base.$el.on("click","> *",function(e){ if(!base.options.disable){ var target= e.target; if(target.tagName!="SPAN") target=target.parentNode; base.options.onSignClick.call(base,target); var val=$(target).prevAll().length+1; base.set(val); } }); base.$el.on("mouseenter","> *",function(e){ var target= e.target; if(target.tagName!="SPAN") target=target.parentNode; if(!base.options.disable){ $(target).addClass("rate-active"); $(target).prevAll().addClass("rate-active"); } base.options.onSignEnter.apply(null,[base.options.rate,target]); }); base.$el.on("mouseleave","> *",function(e){ var target= e.target; if(target.tagName!="SPAN") target=target.parentNode; if(!base.options.disable){ $(target).removeClass("rate-active"); $(target).prevAll().removeClass("rate-active"); } base.options.onSignLeave.apply(null,[base.options.rate,target]); }); }; base.set=function(val,init){ if(val<0 || (val % 1 != 0) || val>base.options.ratemax) val=0; // reset to 0. if(val==1 && base.options.rate==1 && base.options.unrateable==true && !init){ val=0; } base.options.rate=val; // reset html base.$el.html(""); // set data-rate if (base.options.rate!=0) base.$el.attr("data-rate",base.options.rate); // set data-ratemax base.$el.attr("data-ratemax",base.options.ratemax); // generate signs var i=0; while (i < base.options.ratemax) { var tmp=""; if(i"+base.options[type]+""; }; base.init(); }; $.ratyli.defaultOptions = { disable: false, unrateable: false, full: "★", empty: "☆", cursor:"default", rate:0, ratemax:5, onSignEnter:function(){}, onSignLeave:function(){}, onSignClick:function(){}, onRated:function(){} }; $.fn.ratyli = function(options){ return this.each(function(){ (new $.ratyli(this, options)); }); }; }(jQuery); /*加减插件*/ /** * Spinner Plugin */ +function (window) { "use strict"; function Spinner(element, options) { this.$element = $(element); this.options = $.extend({}, Spinner.DEFAULTS, options || {}); console.log(options); this.init(); } Spinner.DEFAULTS = { input: '.J_Input', add: '.J_Add', minus: '.J_Del', unit:2, max: 0, min:-1, digit:0, longpress: false,//长按手势 //callback: null //回调方法 }; Spinner.prototype.init = function () { var _this = this, options = _this.options; _this.$input = $(options.input, _this.$element); _this.$add = $(options.add, _this.$element); _this.$minus = $(options.minus, _this.$element); _this.changeParameters(); _this.checkParameters(); _this.bindEvent(); }; Spinner.prototype.tapParams = {}; Spinner.prototype.isNumber = function (val) { //return /^([0]|[1-9]\d*)(\.\d{1,2})?$/.test(val); return /^[1-9]\d*.\d*|0.\d*[1-9]\d*$/.test(val); }; Spinner.prototype.FixNumber = function (val) { //return parseFloat(val); return parseFloat(val); }; Spinner.prototype.changeParameters = function () { var _this = this, options = _this.options; var params = [ {param: 'unit', default: 1}, {param: 'max', default: 0} ]; $.each(params, function (k, v) { var _val = options[v.param], _dataVal = _this.$input.data(v.param); if (!!_dataVal) { _val = _dataVal; if (!_this.isNumber(_dataVal)) { _val = options[v.param]; if (typeof _val == 'function') { _val = _val(); } } } else { if (typeof options[v.param] == 'function') { var _fnVal = options[v.param](); _val = _fnVal; if (!_this.isNumber(_fnVal)) { _val = options[v.param]; } } } if (!_this.isNumber(_val)) { _val = v.default; } options[v.param] = _this.FixNumber(_val); }); }; Spinner.prototype.checkParameters = function () { var _this = this, options = _this.options, value = _this.$input.val(); if (value) { _this.setValue(value); } else { if (options.max < options.min && options.max != 0) { options.max = options.min; } if (options.min < options.unit && options.min > 0) { options.min = options.unit; } if (options.min % options.unit != 0 && options.min > 0) { options.min = options.min - options.min % options.unit; } if (options.max < options.unit && options.max != 0) { options.max = options.unit; } if (options.max % options.unit != 0) { options.max = options.max - options.max % options.unit; } if (options.min < 0) { options.min = options.unit; } _this.setValue(options.min); } }; /*计算并更新数量*/ Spinner.prototype.calculation = function (type,value) { var _this = this, options = _this.options, max = parseFloat(options.max), unit = parseFloat(options.unit), min = parseFloat(options.min), $input = _this.$input, val = _this.FixNumber($input.val()); if (!!$input.attr('readonly') || !!$input.attr('disabled'))return; var newval; if (type == 'add') { newval = val + unit; if (max != 0 && newval > max)return; } else { newval = val - unit; if (newval < min)return; } /*前置方法*/ if(typeof options.before != 'undefined'){ var result = eval(options['before'] + '(newval,_this.$input)'); if(result===false) return; }; _this.setValue(newval); options.longpress && _this.longpressHandler(type); }; Spinner.prototype.longpressHandler = function (type) { var _this = this; var currentDate = new Date().getTime() / 1000, intervalTime = currentDate - _this.tapStartTime; if (intervalTime < 1) intervalTime = 0.5; var secondCount = intervalTime * 10; if (intervalTime == 30) secondCount = 50; if (intervalTime >= 40) secondCount = 100; _this.tapParams.timer = setTimeout(function () { _this.calculation(type); }, 1000 / secondCount); }; Spinner.prototype.setValue = function (val) { var _this = this, options = _this.options, digit = options.digit,//保留小数点位数 max = parseFloat(options.max), unit = parseFloat(options.unit), min = parseFloat(options.min) < 0 ? unit : parseFloat(options.min); if (!/^(([1-9]\d*(\.\d*)?$)|0)$/.test(val)) val = max; if (val > max && max != 0) val = max; if (val % unit > 0) { val = val - val % unit + unit; if (val > max && max != 0) val -= unit; } if (val < min) val = min - min % unit; /*小数位数*/ val = parseFloat(val).toFixed(digit); _this.$input.val(val); /*回调方法*/ if(typeof options.callback != 'undefined'){ eval(options['callback'] + '(val,_this.$input)'); }; }; Spinner.prototype.bindEvent = function () { var _this = this, options = _this.options, isMobile = helper.checkMobile(), mousedownEvent = 'mousedown.ydui.spinner', mouseupEvent = 'mouseup.ydui.spinner'; if (isMobile==1) { mousedownEvent = 'touchstart.ydui.spinner'; mouseupEvent = 'touchend.ydui.spinner'; } _this.$add.on(mousedownEvent, function (e) { if (options.longpress) { e.preventDefault(); e.stopPropagation(); _this.tapStartTime = new Date().getTime() / 1000; _this.$add.on(mouseupEvent, function () { _this.clearTapTimer(); }); } _this.calculation('add'); }); _this.$minus.on(mousedownEvent, function (e) { if (options.longpress) { e.preventDefault(); e.stopPropagation(); _this.tapStartTime = new Date().getTime() / 1000; _this.$minus.on(mouseupEvent, function () { _this.clearTapTimer(); }); } _this.calculation('minus'); }); _this.$input.on('change.ydui.spinner', function () { _this.setValue($(this).val()); }).on('keydown', function (event) { if (event.keyCode == 13) { _this.setValue($(this).val()); return false; } }); }; Spinner.prototype.clearTapTimer = function () { var _this = this; clearTimeout(_this.tapParams.timer); }; function Plugin(option) { var args = Array.prototype.slice.call(arguments, 1); return this.each(function () { var $this = $(this), spinner = $this.data('ydui.spinner'); if (!spinner) { $this.data('ydui.spinner', (spinner = new Spinner(this, option))); } if (typeof option == 'string') { spinner[option] && spinner[option].apply(spinner, args); } }); } $(window).on('load.ydui.spinner', function () { $('[data-ydui-spinner]').each(function () { var $this = $(this); $this.spinner(helper.parseOptions($this.data('ydui-spinner'))); }); }); $.fn.spinner = Plugin; }(window); /** * ScrollTab Plugin */ !function (window) { "use strict"; function ScrollTab (element, options) { this.$element = $(element); this.options = $.extend({}, ScrollTab.DEFAULTS, options || {}); this.init(); } ScrollTab.DEFAULTS = { navItem: '.scrolltab-item', content: '.scrolltab-content', contentItem: '.scrolltab-content-item', initIndex: 0 }; ScrollTab.prototype.init = function () { var _this = this, $element = _this.$element, options = _this.options; _this.$navItem = $element.find(options.navItem); _this.$content = $element.find(options.content); _this.$contentItem = $element.find(options.contentItem); _this.scrolling = false; _this.contentOffsetTop = _this.$content.offset().top; _this.bindEvent(); _this.movePosition(_this.options.initIndex, false); }; ScrollTab.prototype.bindEvent = function () { var _this = this; _this.$content.on('resize.ydui.scrolltab scroll.ydui.scrolltab', function () { _this.checkInView(); }); _this.$navItem.on('click.ydui.scrolltab', function () { _this.movePosition($(this).index(), true); }); }; ScrollTab.prototype.movePosition = function (index, animate) { var _this = this; if (_this.scrolling)return; _this.scrolling = true; _this.$navItem.removeClass('crt'); _this.$navItem.eq(index).addClass('crt'); var $item = _this.$contentItem.eq(index); if (!$item[0])return; var offset = $item.offset().top; var top = offset + _this.$content.scrollTop() - _this.contentOffsetTop + 1; _this.$content.stop().animate({scrollTop: top}, animate ? 200 : 0, function () { _this.scrolling = false; }); }; ScrollTab.prototype.checkInView = function () { var _this = this; if (_this.scrolling)return; if (_this.isScrollTop()) { _this.setClass(0); return; } if (_this.isScrollBottom()) { _this.setClass(_this.$navItem.length - 1); return; } _this.$contentItem.each(function () { var $this = $(this); if ($this.offset().top <= _this.contentOffsetTop) { _this.setClass($this.index()); } }); }; ScrollTab.prototype.setClass = function (index) { this.$navItem.removeClass('crt').eq(index).addClass('crt'); }; ScrollTab.prototype.isScrollTop = function () { return this.$content.scrollTop() == 0; }; ScrollTab.prototype.isScrollBottom = function () { var _this = this; return _this.$content.scrollTop() + 3 >= _this.$contentItem.height() * _this.$contentItem.length - _this.$content.height(); }; function Plugin (option) { var args = Array.prototype.slice.call(arguments, 1); return this.each(function () { var target = this, $this = $(target), scrollTab = $this.data('ydui.scrolltab'); if (!scrollTab) { $this.data('ydui.scrolltab', (scrollTab = new ScrollTab(target, option))); } if (typeof option == 'string') { scrollTab[option] && scrollTab[option].apply(scrollTab, args); } }); } $(window).on('load.ydui.scrolltab', function () { $('[data-ydui-scrolltab]').each(function () { var $this = $(this); $this.scrollTab(utils.parseOptions($this.data('ydui-scrolltab'))); }); }); $.fn.scrollTab = Plugin; }(window); /** * 倒计时插件 */ +function ($) { $.fn.yomi = function () { var emsg = ''; var etime = ""; var split = "text"; var _DOM = null; var TIMER; var callfn = null; var secfn = null; createdom = function (dom) { _DOM = dom; etime = $(dom).data("etime"); emsg = $(dom).data("emsg"); split = $(dom).data("split"); callfn = $(dom).data("callfn"); secfn = $(dom).data("secfn"); if(split!='text'){ $(_DOM).append("
    ::
    ") }else{ $(_DOM).append("
    ") } reflash(); }; reflash = function () { var nowtime = (new Date()).getTime() / 1000; if (nowtime > etime) { //已结束 clearInterval(TIMER); if(callfn!=null){ eval(callfn+'()'); }else{ $(_DOM).text(emsg); } return; } var range = etime - Math.round((new Date()).getTime() / 1000), secday = 86400, sechour = 3600, days = parseInt(range / secday), hours = parseInt((range % secday) / sechour), min = parseInt(((range % secday) % sechour) / 60), sec = ((range % secday) % sechour) % 60; if(days>0){ $(_DOM).find(".yomiday").html(nol(days)); }else{ $(_DOM).find(".yomiday").remove(); $(_DOM).find(".split").each(function () { var text=$(this).text(); text=='天'?$(this).remove():''; }); } $(_DOM).find(".yomihour").html(nol(hours)); $(_DOM).find(".yomimin").html(nol(min)); $(_DOM).find(".yomisec").html(nol(sec)); if(secfn!=null){ eval(secfn+'(range,TIMER)'); } }; TIMER = setInterval(reflash, 1000); nol = function (h) { return h > 9 ? h : '0' + h; } return this.each(function () { var $box = $(this); createdom($box); }); } $(".yomibox").each(function () { $(this).yomi(); }); }(jQuery);