static

ipuUI.js 126KB

    (function () { "use strict"; function setup(jQuery, iScroll, Hammer, FastClick) { /** * @class ipuUI对象,通过此对象实例UI组件 * */ var ipuUI = { version: '0.2.2' }; // tap点击效果处理,只针对jquery上面的click事件,依赖touch事件 (function (ipuUI, $) { var active = {}; var defaultOption = { distanceAllow: 10, // 最大移动距离,超过移除效果 displayDelay: 100, // 延时显示时间,以防止是滚动操作 hideDelay: 120, // 隐藏延时时间 eventName: 'click', // 事件处理是click activeClass: 'ipu-active', // 激活时的class getHandleNode: function (node) { // 找到最先一级处理此click事件的元素 if (!node || !node.nodeType) return; var distNode = null; var nodeArray = []; // 还有其它情形考虑,如a标签的跳转,或在原生元素添加事件属性的 function findHander(inNode) { // 此方法适用于jquery, 1.12.4, 2.2.4, 3.2.1版本,_data方法以后可能会被移除。$.data是一些老版本写法 var eventHandlers = ($._data || $.data)(inNode, 'events'); if (eventHandlers) { eventHandlers = eventHandlers[option.eventName]; } if (!eventHandlers) { return; } var thisNode = false; $.each(eventHandlers, function (index, handler) { if (handler.selector) { var objs = $(handler.selector, inNode); $.each(nodeArray, function (tIndex, tNode) { if (objs.is(tNode)) { distNode = tNode; return false; } }); if (distNode) { return false; // } } else { thisNode = true; // 保存distNode,有可能有子节点满足条件,所以只保存此值为默认值 } }); if (thisNode && distNode == null) { // 如果没在子节点找到click事件,而当前节点又有click事件,就使用当前节点 distNode = inNode; } return distNode; } while (!("tagName" in node) || !findHander(node)) { if (!node.parentNode || node.parentNode.nodeType != 1) { break; } nodeArray.push(node); node = node.parentNode; } return distNode; } }; var option = defaultOption; function getOriginalEvent(e) { return e.originalEvent || e; } function getXY(e) { var x = e.touches ? e.touches[0].pageX : e.clientX; var y = e.touches ? e.touches[0].pageY : e.clientY; return [x, y]; } //根据不同浏览器获取不同原生事件event var hasTouch = "ontouchstart" in window, START_EVENT = hasTouch ? 'touchstart' : 'mousedown', MOVE_EVENT = hasTouch ? 'touchmove' : 'mousemove', END_EVENT = hasTouch ? 'touchend' : 'mouseup', CANCEL_EVENT = hasTouch ? 'touchcancel' : ''; $(function () { var startXY, tapEl, timeOutID; var dom = document.body; // force为false的时候,不用管timeOutID,在老的timeOutID未移除的情况下,有可能又产生了新的, // 导致else代码未被执行,导致老的点击元素class未被移除 function removeClass(dom, force) { if (force && timeOutID) { window.clearTimeout(timeOutID); } else { $(dom).removeClass(option.activeClass); } } function removeActive(force) { if (force) { removeClass(tapEl, force); } else { window.setTimeout(removeClass, option.hideDelay, tapEl, force); } startXY = null; tapEl = null; } $(dom).bind(START_EVENT, function (e) { if (tapEl) { // 多点接触时处理 removeActive(true); return; } e = getOriginalEvent(e); startXY = getXY(e); tapEl = option.getHandleNode(e.target); if (tapEl) { timeOutID = window.setTimeout(function (dom) { timeOutID = null; $(dom).addClass(option.activeClass); }, option.displayDelay, tapEl); } }); $(dom).bind(MOVE_EVENT, function (e) { if (!tapEl) { return; } e = getOriginalEvent(e); var xy = getXY(e); if (startXY && (Math.abs(xy[0] - startXY[0]) > option.distanceAllow || Math.abs(xy[1] - startXY[1]) > option.distanceAllow)) { removeActive(true); } }); $(dom).bind(END_EVENT, function (e) { if (tapEl) { removeActive(); } }); // 手机来电等非用户取消操作时触发事件 if (CANCEL_EVENT) { $(dom).bind(CANCEL_EVENT, function (e) { if (tapEl) { removeActive(); } }); } }); // 更新默认值 active.setOption = function (opts) { option = this.option = $.extend({}, defaultOption, opts); }; ipuUI.active = active; })(ipuUI || window, jQuery); (function (ipuUI, $, iscroll) { /** * @uses IScroll.js * @class 简单封装IScroll.js的snap功能,实现banner功能 * @private 可以使用hammerCarousel代替 * * @example * <!-- 组件html结构如下,li里的内容用户可自定义 --> * <div class="ipu-carousel"> * <ul class="ipu-carousel-wrapper"> * <li ><img src="../../biz/img/01.jpg" alt=""></li> * <li ><img src="../../biz/img/02.jpg" alt=""></li> * <li ><img src="../../biz/img/03.jpg" alt=""></li> * <li ><img src="../../biz/img/04.jpg" alt=""></li> * </ul> * </div> * * @constructor 不能直接访问该类,使用ipuUI.carousel(slt, option)生成实例 {@link ipuUI#carousel} * @param {Dom|JqueryObj|String} slt jquery对象或者jquery选择器或Dom元素 * @param {Object} option 组件配置参数,默认配置见 {@link #cfg-defaultOption} * */ function Carousel(slt, option) { this.option = option = $.extend({}, this.defaultOption, option); this.el = $(slt).eq(0); // 一次只能实例化一个 this.autoPlay = option.autoPlay; this.hasIndicator = option.indicator; this.callBack = option.callBack; this.currentIndex = null; this._init(); this.play(); } Carousel.prototype = { /** * 组件默认配置项 * * @cfg {Object} defaultOption * @cfg {Number} defaultOption.index 默认显示的项 * @cfg {Boolean} defaultOption.autoPlay 是否自动播放 * @cfg {Number} defaultOption.duration 自动播放间隔,单位ms * @cfg {Boolean} defaultOption.indicator 是否生成指示器 * @cfg {Function} defaultOption.callBack 切换显示时的回调函数 * @cfg {Number} defaultOption.callBack.index 当前显示项索引 * */ defaultOption: { index: null, // 默认显示索引,未设置时先查找对就active,未找到时是0 autoPlay: false, // 是否自动播放 duration: 3000, // 自动播放延时 indicator: false, // 是否生成指示器 callBack: null // 变更时回调函数 }, _init: function () { var wrapper = $(">.ipu-carousel-wrapper", this.el); var carouselItems = $(">li", wrapper); this.carouselItems = carouselItems; this.size = carouselItems.size(); var that = this; if (this.option.index == null) { var activeIndex = carouselItems.filter(".ipu-current").index(); this.option.index = activeIndex != -1 ? activeIndex : 0; } if (this.hasIndicator) { this._addIndicator(); } $(window).resize(function () { that.refresh(); that.show(that.currentIndex, 0); }); var scrollOpt = { snap: "li", // carousel效果 momentum: false, // 移除惯性处理 scrollX: true, // X轴移动 scrollY: false, hScrollbar: false, // 没有纵向滚动条 onScrollStart: function () { that._pause(); }, onTouchEnd: function () { }, onScrollEnd: function () { that._end(); } }; this.iscroll = new iscroll(this.el.get(0), scrollOpt); this.show(this.option.index, 0); }, /** * 停止自动播放 */ stop: function () { this._pause(); this.autoPlay = false; }, _pause: function () { if (this.autoPlay && this.timeoutId) { clearTimeout(this.timeoutId); this.timeoutId = null; } }, /** * 显示上一项 */ prev: function () { var index = this.currentIndex == 0 ? this.size - 1 : this.currentIndex - 1; this.show(index); }, /** * 显示下一项 */ next: function () { var index = this.currentIndex == this.size - 1 ? 0 : this.currentIndex + 1; this.show(index); }, /** * 显示索引index对应的索 * * @param {Number} index 显示项索引 */ show: function (index, time) { this._pause(); this.iscroll.scrollToPage(index, 0, time); }, /** * 开始自动播放 */ play: function () { this.autoPlay = true; this._play(); }, /** * 若窗口发生大小变更,调用此方法更新位移 */ refresh: function () { this.show(this.currentIndex); }, _play: function () { if (this.autoPlay && !this.timeoutId) { var that = this; this.timeoutId = setTimeout(function () { that.timeoutId = null; that.next(); }, that.option.duration); } }, _end: function () { var currentIndex = this.iscroll.currPageX; if (currentIndex != this.currentIndex) { if (this.callBack) { this.callBack(currentIndex, this.currentIndex); } this.currentIndex = currentIndex; if (this.hasIndicator) { this.indicatorIndexs.eq(currentIndex).addClass("ipu-current").siblings().removeClass("ipu-current"); } this.carouselItems.eq(currentIndex).addClass("ipu-current").siblings().removeClass("ipu-current"); } this._play(); }, _addIndicator: function () { var html = ""; for (var i = 0; i < this.size; i++) { html += "<li></li>"; } html = "<ul class='ipu-carousel-indicator'>" + html + "</ul>"; this.indicator = $(html).appendTo(this.el); this.indicatorIndexs = $("li", this.indicator); }, destroy: function () { // 自己怎么销毁,相关事件移除?? this.iscroll.destroy(); } }; /** * @member ipuUI * 生成Carousel实例,参数信息见{@link Carousel#method-constructor} * * @param {String} slt * @param {Object} option * @returns {Carousel} */ ipuUI.carousel = function (slt, option) { return new Carousel(slt, option); }; })(ipuUI || window, jQuery, iScroll); // todo:添加判断平台如mobile ,tablet, pc,参考其它类似功能库,添加webview判断 (function (ipuUI, $) { var device = {}; // Classes var classNames = []; 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]; } } // 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); } } } device.wx = /MicroMessenger/i.test(ua); // 是否微信 device.ipu = /ipumobile/i.test(ua); // 是否ipu环境运行 if (device.wx) { classNames.push('wx'); } if (device.ipu) { classNames.push('ipu'); } var classPrev = "ipu-"; // Add html classes if (classNames.length > 0) { $('html').addClass(classPrev + classNames.join(' ' + classPrev)); } ipuUI.device = device; })(ipuUI || window, jQuery); // dtPicker 此版本最大值与最小值,存在问题,当时间跨过一天时 // show方法调用时,若没有值,则为当前值,还是有值就不变动了,点了确认按钮后,就不再变动了 // 日期范围的选择处理 // 不选择字符串连接符,合并后占空间 (function (ipuUI, $) { var Picker = ipuUI.Picker; var defaultPickerDate = new Date(); // 有些时间不齐全。如time,需要一个默认日期来协助运算 /** * @class 日期选择器,替代默认的web日历选择,日期格式如下<br> * type=datetime:yyyy-mm-dd hh:mi<br> * type=date:yyyy-mm-dd<br> * type=time: hh:mi<br> * type=month: yyyy-mm<br> * type=hour: yyyy-mm-dd hh<br> * * @constructor 不能直接访问该,调用{@link ipuUI#dtPicker}生成实例 * @param {object} option 组件参数,默认配置见 {@link #cfg-defaultOption} */ function DtPicker(option) { this.option = $.extend({}, this.defaultOption, option); if (!Picker) { Picker = ipuUI.Picker; } this._init(); } /** * 组件默认配置项 * * @cfg {Object} defaultOption= * @cfg {String} defaultOption.template 组件模板html,不建议变更 * @cfg {[String]} defaultOption.buttons=['取消', '确认', '清除'] 按钮名称 * @cfg {[String]} defaultOption.labels=['年', '月', '日', '时', '分'] 年月日标签 * @cfg {datetime|time|date|hour|month} defaultOption.type='datetime' 日期类型 * @cfg {Boolean} defaultOption.hasClear=false 是否显示清除按钮 * @cfg {Date} defaultOption.beginDate=null 日期开始时间,默认设置为当时间前5年 * @cfg {Date} defaultOption.endDate=null 日期结束时间,默认设置为开始时间后10年 * @cfg {Function} defaultOption.callBack=null 点击按钮时的回调函数,回调的参数同{@link #show show()}法设置 的回调 */ DtPicker.prototype.defaultOption = { template: '' + '<div class="ipu-poppicker ipu-dtpicker">' + ' <div class="ipu-poppicker-header">' + ' <button class="ipu-btn ipu-btn-s ipu-poppicker-btn-cancel">取消</button>' + ' <button class="ipu-btn ipu-btn-s ipu-poppicker-btn-ok">确定</button>' + ' <button class="ipu-btn ipu-btn-s ipu-poppicker-btn-clear">清除</button>' + ' </div>' + ' <div class="ipu-poppicker-title">' + ' <label class="ipu-dtpicker-y"></label>' + ' <label class="ipu-dtpicker-m"></label>' + ' <label class="ipu-dtpicker-d"></label>' + ' <label class="ipu-dtpicker-h"></label>' + ' <label class="ipu-dtpicker-mi"></label>' + ' </div>' + ' <div>' + ' <div class="ipu-poppicker-body">' + ' <div class="ipu-picker" data-id="picker-y">' + ' <div class="ipu-picker-selectbox"></div>' + ' <ul></ul>' + ' </div>' + ' <div class="ipu-picker" data-id="picker-m">' + ' <div class="ipu-picker-selectbox"></div>' + ' <ul></ul>' + ' </div>' + ' <div class="ipu-picker" data-id="picker-d">' + ' <div class="ipu-picker-selectbox"></div>' + ' <ul></ul>' + ' </div>' + ' <div class="ipu-picker" data-id="picker-h">' + ' <div class="ipu-picker-selectbox"></div>' + ' <ul></ul>' + ' </div>' + ' <div class="ipu-picker" data-id="picker-mi">' + ' <div class="ipu-picker-selectbox"></div>' + ' <ul></ul>' + ' </div>' + ' </div>' + '</div>', buttons: ['取消', '确认', '清除'], labels: ['年', '月', '日', '时', '分'], type: 'datetime', customData: {}, hasClear: false, beginDate: null, endDate: null, callBack: null }; DtPicker.prototype._init = function () { var self = this; this.mask = this.createMask(); var _picker = this.holder = $(this.option.template).appendTo("body"); var ui = self.ui = { picker: this.holder, ok: $('.ipu-poppicker-btn-ok', _picker), cancel: $('.ipu-poppicker-btn-cancel', _picker), clear: $('.ipu-poppicker-btn-clear', _picker), buttons: $('.ipu-poppicker-header .ipu-btn', _picker), labels: $('.ipu-poppicker-title label', _picker) }; ui.i = new Picker($('[data-id="picker-mi"]', _picker), {listen: false}); // 分钟变更无需要处理 ui.h = new Picker($('[data-id="picker-h"]', _picker), { // 小时变更,有最小值或最大值,需要变更分钟 listen: false, onChange: function (item, index) { if (index !== null && (self.option.beginMonth || self.option.endMonth)) { self._createMinutes(); } } }); ui.d = new Picker($('[data-id="picker-d"]', _picker), { //仅提供了beginDate时,触发day,hours,minutes的change listen: false, onChange: function (item, index) { if (index !== null && (self.option.beginMonth || self.option.endMonth)) { self._createHours(); } } }); ui.m = new Picker($('[data-id="picker-m"]', _picker), { // 月变更时,总要变更day listen: false, onChange: function (item, index) { if (index !== null) { self._createDay(); } } }); ui.y = new Picker($('[data-id="picker-y"]', _picker), { // 年发生变更,如果没有结束月,此时有所有的月,是不需要变更月的,只需要变更day listen: false, onChange: function (item, index) { if (index != null) { if (self.option.beginMonth || self.option.endMonth) { self._createMonth(); } else { self._createDay(); } } } }); self._create(); //设定label self._setLabels(); self._setButtons(); //设定类型 ui.picker.attr('data-type', this.option.type); //设定默认值 self._setSelectedValue(this.option.value); //防止滚动穿透 TODO:待确认情况 /* self.ui.picker.addEventListener($.EVENT_START, function (event) { event.preventDefault(); }, false); self.ui.picker.addEventListener($.EVENT_MOVE, function (event) { event.preventDefault(); }, false);*/ }; /** * 返回当前选中的日期,只有在点确认时,返回的才是正确的值,在点清除、或取消后,调用此方法返回的值不可控 * * @return 选择的日期信息 * @return {datetime|time|date|hour|month} return.type 日期类型 * @return {String} return.text 日期文本(text字段)拼接,格式yyyy-mm-dd hh:mi 根据上面的日期类型返回对应字符串 * @return {String} return.value 日期项值(value字段)拼接 * @return {Object} return.y 选择的年项 * @return {Object} return.m 选择的月项 * @return {Object} return.d 选择的日项 * @return {Object} return.h 选择的时项 * @return {Object} return.i 选择的分项 * @return {Function} return.toString 返回value字段的值 */ DtPicker.prototype.getSelected = function () { var self = this; var ui = self.ui; var type = self.option.type; var selected = { type: type, y: ui.y.getSelectedItem(), m: ui.m.getSelectedItem(), d: ui.d.getSelectedItem(), h: ui.h.getSelectedItem(), i: ui.i.getSelectedItem(), toString: function () { return this.value; } }; switch (type) { case 'datetime': selected.value = selected.y.value + '-' + selected.m.value + '-' + selected.d.value + ' ' + selected.h.value + ':' + selected.i.value; selected.text = selected.y.text + '-' + selected.m.text + '-' + selected.d.text + ' ' + selected.h.text + ':' + selected.i.text; break; case 'date': selected.value = selected.y.value + '-' + selected.m.value + '-' + selected.d.value; selected.text = selected.y.text + '-' + selected.m.text + '-' + selected.d.text; break; case 'time': selected.value = selected.h.value + ':' + selected.i.value; selected.text = selected.h.text + ':' + selected.i.text; break; case 'month': selected.value = selected.y.value + '-' + selected.m.value; selected.text = selected.y.text + '-' + selected.m.text; break; case 'hour': selected.value = selected.y.value + '-' + selected.m.value + '-' + selected.d.value + ' ' + selected.h.value; selected.text = selected.y.text + '-' + selected.m.text + '-' + selected.d.text + ' ' + selected.h.text; break; } return selected; }; DtPicker.prototype._setSelectedValue = function (value) { var self = this; var ui = self.ui; if (!value) { if (this.option.type == 'time') { value = '00:00'; } else { value = defaultPickerDate.getFullYear() + '-' + (defaultPickerDate.getMonth() + 1) + '-' + defaultPickerDate.getDate() + ' ' + defaultPickerDate.getHours() + ':' + defaultPickerDate.getMinutes(); } } var parsedValue = self._parseSetValue(value); ui.y.setListen(true); ui.m.setListen(false); ui.d.setListen(false); ui.h.setListen(false); ui.i.setListen(false); ui.y.setSelectedValue(parsedValue.y); ui.m.setListen(true); ui.m.setSelectedValue(parsedValue.m); ui.d.setListen(true); ui.d.setSelectedValue(parsedValue.d); ui.h.setListen(true); ui.h.setSelectedValue(parsedValue.h); ui.i.setListen(true); ui.i.setSelectedValue(parsedValue.i); this.value = this.getSelected().value; }; /** * 设置日期值,value为字符串时,格式请参照 yyyy-mm-dd hh:mi具体格式与配置项type相关 * * @param {String|Date} value */ DtPicker.prototype.setSelectedValue = function (value) { this._setSelectedValue(value); }; /** * 是否润年 * * @param {Number} year 年份 * @returns {Boolean} */ DtPicker.prototype.isLeapYear = function (year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }; DtPicker.prototype._inArray = function (array, item) { for (var index in array) { var _item = array[index]; if (_item === item) return true; } return false; }; DtPicker.prototype.getDayNum = function (year, month) { var self = this; if (self._inArray([1, 3, 5, 7, 8, 10, 12], month)) { return 31; } else if (self._inArray([4, 6, 9, 11], month)) { return 30; } else if (self.isLeapYear(year)) { return 29; } else { return 28; } }; DtPicker.prototype._fill = function (num) { num = num.toString(); if (num.length < 2) { num = 0 + num; } return num; }; DtPicker.prototype._isBeginYear = function () { return this.option.beginYear === parseInt(this.ui.y.getSelectedValue()); }; DtPicker.prototype._isBeginMonth = function () { return this.option.beginMonth && this._isBeginYear() && this.option.beginMonth === parseInt(this.ui.m.getSelectedValue()); }; DtPicker.prototype._isBeginDay = function () { return this._isBeginMonth() && this.option.beginDay === parseInt(this.ui.d.getSelectedValue()); }; DtPicker.prototype._isBeginHours = function () { return this._isBeginDay() && this.option.beginHours === parseInt(this.ui.h.getSelectedValue()); }; DtPicker.prototype._isEndYear = function () { return this.option.endYear === parseInt(this.ui.y.getSelectedValue()); }; DtPicker.prototype._isEndMonth = function () { return this.option.endMonth && this._isEndYear() && this.option.endMonth === parseInt(this.ui.m.getSelectedValue()); }; DtPicker.prototype._isEndDay = function () { return this._isEndMonth() && this.option.endDay === parseInt(this.ui.d.getSelectedValue()); }; DtPicker.prototype._isEndHours = function () { return this._isEndDay() && this.option.endHours === parseInt(this.ui.h.getSelectedValue()); }; DtPicker.prototype._createYear = function () { var self = this; var option = self.option; var ui = self.ui; //生成年列表 var yArray = []; if (option.customData.y) { yArray = option.customData.y; } else { var yBegin = option.beginYear; var yEnd = option.endYear; for (var y = yBegin; y <= yEnd; y++) { yArray.push({ text: y + '', value: y }); } } ui.y.setItems(yArray); }; DtPicker.prototype._createMonth = function () { var self = this; var option = self.option; var ui = self.ui; //生成月列表 var mArray = []; if (option.customData.m) { mArray = option.customData.m; } else { var m = option.beginMonth && self._isBeginYear() ? option.beginMonth : 1; var maxMonth = option.endMonth && self._isEndYear() ? option.endMonth : 12; for (; m <= maxMonth; m++) { var val = self._fill(m); mArray.push({ text: val, value: m }); } } ui.m.setItems(mArray); }; DtPicker.prototype._createDay = function () { var self = this; var option = self.option; var ui = self.ui; //生成日列表 var dArray = []; if (option.customData.d) { dArray = option.customData.d; } else { var d = self._isBeginMonth() ? option.beginDay : 1; var maxDay = self._isEndMonth() ? option.endDay : self.getDayNum(parseInt(this.ui.y.getSelectedValue()), parseInt(this.ui.m.getSelectedValue())); for (; d <= maxDay; d++) { var val = self._fill(d); dArray.push({ text: val, value: d }); } } ui.d.setItems(dArray); //current = current || ui.d.getSelectedValue(); //ui.d.setSelectedValue(current); }; DtPicker.prototype._createHours = function () { var self = this; var option = self.option; var ui = self.ui; //生成时列表 var hArray = []; if (option.customData.h) { hArray = option.customData.h; } else { var h = self._isBeginDay() ? option.beginHours : 0; var maxHours = self._isEndDay() ? option.endHours : 23; for (; h <= maxHours; h++) { var val = self._fill(h); hArray.push({ text: val, value: h }); } } ui.h.setItems(hArray); //ui.h.setSelectedValue(current); }; DtPicker.prototype._createMinutes = function () { var self = this; var option = self.option; var ui = self.ui; //生成分列表 var iArray = []; if (option.customData.i) { iArray = option.customData.i; } else { var i = self._isBeginHours() ? option.beginMinutes : 0; var maxMinutes = self._isEndHours() ? option.endMinutes : 59; for (; i <= maxMinutes; i++) { var val = self._fill(i); iArray.push({ text: val, value: i }); } } ui.i.setItems(iArray); //ui.i.setSelectedValue(current); }; DtPicker.prototype._setLabels = function () { var self = this; var option = self.option; var ui = self.ui; ui.labels.each(function (i, label) { label.innerText = option.labels[i]; }); }; DtPicker.prototype._setButtons = function () { var self = this; var option = self.option; var ui = self.ui; ui.cancel.text(option.buttons[0]); ui.ok.text(option.buttons[1]); if (option.hasClear) { ui.clear.text(option.buttons[2]) } else { ui.clear.hide(); } ui.buttons.each(function (index) { $(this).click(function () { self.clickCall(index); }) }) }; // 解析设置的值,目前是字符串,完整日期格式 2012-12-12 12:21 // 对于time类型时,或未完整的时间值,使用defaultPickerDate来填充 DtPicker.prototype._parseSetValue = function (value) { var now = defaultPickerDate; var type = this.option.type; var rs = { y: now.getFullYear(), m: now.getMonth() + 1, d: now.getDate(), h: now.getHours(), i: now.getMinutes() }; if (value instanceof Date) { if (type == 'time') { value = value.getHours() + ":" + value.getMinutes(); } else { value = value.getFullYear() + '-' + (value.getMonth() + 1) + '-' + value.getDate() + ' ' + value.getHours() + ":" + value.getMinutes(); } } var parts = value.replace(":", "-").replace(" ", "-").split("-"); for (var i = 0, j = parts.length; i < j; i++) { parts[i] = parseInt(parts[i]); } if (type == 'datetime') { rs.y = parts[0]; rs.m = parts[1]; rs.d = parts[2]; // rs.h = parts[3]; // rs.i = parts[4]; } else if (type == 'date') { rs.y = parts[0]; rs.m = parts[1]; rs.d = parts[2]; // rs.h = 0; // rs.i = 0; } else if (type == 'time') { rs.h = parts[0]; // rs.i = parts[1]; } else if (type == 'hour') { rs.y = parts[0]; rs.m = parts[1]; rs.d = parts[2]; // rs.h = parts[3]; // rs.i = 0; } else if (type == 'month') { rs.y = parts[0]; rs.m = parts[1]; rs.d = 1; // rs.h = 0; // rs.i = 0; } return rs; }; // 生成日期数据 DtPicker.prototype._create = function () { var self = this; var option = this.option; var now = defaultPickerDate; var beginDate = option.beginDate; if (beginDate) { // 若有设置开始日期 beginDate = this._parseSetValue(beginDate); option.beginYear = beginDate.y; option.beginMonth = beginDate.m; option.beginDay = beginDate.d; option.beginHours = beginDate.h; option.beginMinutes = beginDate.i; } else if (option.type == 'time') { // 未设置开始日期,但日期格式是time option.beginYear = now.getFullYear(); option.beginMonth = now.getMonth() + 1; option.beginDay = now.getDate(); option.beginHours = 0; option.beginMinutes = 0; } else { option.beginYear = now.getFullYear() - 5; // 其它,未设置开始日期,type也不为time,设置默认起始时间 } var endDate = option.endDate; if (endDate) { //设定了结束日期 endDate = this._parseSetValue(endDate); option.endYear = endDate.y; option.endMonth = endDate.m; option.endDay = endDate.d; option.endHours = endDate.h; option.endMinutes = endDate.i; } else if (option.type == 'time') { option.endYear = now.getFullYear(); option.endMonth = now.getMonth() + 1; option.endDay = now.getDate(); option.endHours = 23; option.endMinutes = 59; } else { option.endYear = option.beginYear + 10; } //生成 self._createYear(); self._createMonth(); self._createDay(); self._createHours(); self._createMinutes(); }; /** * 设置组件日期范围 * * @param {String|Date} beginDate 开始时间 * @param {Stirng|Date} endDate 结束时间 */ DtPicker.prototype.setDateRange = function (beginDate, endDate) { this.option.beginDate = beginDate; this.option.endDate = endDate; this._create(); }; /** * 设置开始组件的开始据时间 * @param {String|Date} date */ DtPicker.prototype.setBeginDate = function (date) { this.option.beginDate = date; this._create(); }; /** * 设置组件的结束时间 * * @param {String|Date} date */ DtPicker.prototype.setEndDate = function (date) { this.option.endDate = date; this._create(); }; DtPicker.prototype.dispose = function () { var self = this; self.hide(); setTimeout(function () { self.ui.picker.parentNode.removeChild(self.ui.picker); for (var name in self) { self[name] = null; delete self[name]; } self.disposed = true; }, 300); }; /** * 显示组件 * * @param {Function} callBack 点击按钮时的回调函数,设置此参数会覆盖初始化时的回调函数 * @param {Object} callBack.sltDate 当前选中的日期信息,具体格式,见方法{@link #getSelected getSelected()}的返回 * @param {Number} callBack.index 被点击的按钮索引,0取消,1确认,2清除 */ DtPicker.prototype.show = function (callBack) { if (callBack) { this.option.callBack = callBack; } this.mask.show(); this.setSelectedValue(this.value); this.holder.addClass("ipu-current"); }; DtPicker.prototype.clickCall = function (index) { var self = this; var sltDate = self.getSelected(); var rs = self.option.callBack.call(this, sltDate, index); if (rs !== false) { if (index == 1) { // 假定确认按钮在第二个位置,传回true则存储当前值 self.value = sltDate.value; } else if (index == 2) { self.value = null; } self.hide(); } }; /** * 隐藏组件 */ DtPicker.prototype.hide = function () { this.mask.close(); this.holder.removeClass("ipu-current"); }; // 应该移除callback参数,提取出业成一个工具方法 DtPicker.prototype.createMask = function (callback) { var self = this; var element = document.createElement('div'); element.classList.add("ipu-picker-backup"); //element.addEventListener($.EVENT_MOVE, $.preventDefault); element.addEventListener('click', function () { self.clickCall(0); }); var mask = [element]; mask._show = false; mask.show = function () { mask._show = true; element.setAttribute('style', 'opacity:1'); document.body.appendChild(element); return mask; }; mask._remove = function () { if (mask._show) { mask._show = false; element.setAttribute('style', 'opacity:0'); setTimeout(function () { var body = document.body; element.parentNode === body && body.removeChild(element); }, 350); } return mask; }; mask.close = function () { if (mask._show) { if (callback) { if (callback() !== false) { mask._remove(); } } else { mask._remove(); } } }; return mask; }; /** * @member ipuUI * 生成DtPicker实例,参数信息见{@link DtPicker#method-constructor} * * @param {Object} option * @returns {DtPicker} */ ipuUI.dtPicker = function (option) { return new DtPicker(option); }; })(ipuUI || window, jQuery); // 更新方法和属性命名 // 不能支持元素隐藏时,使用百比分处理移动距离。。。? // 支持两个以内容同时显示 // 支持类似snap实现 // 上下移动? // 理想是移除carousel.js的实现,用hammerCarousel.js实现所有相关功能 // indicatorPosition: 'center', // left|right|center;暂不支持,不知道怎么支持在中间显示,用全宽度,配合point-event:none,可能ok,参考humUI和mui (function (ipuUI, $, Hammer) { /** * @class * @uses Hammer.js * 通过hammer.js实现的banner功能组件, * 因为实现轮播,显示第一项后,再显示第一项,所以第一项有被复制到添加到最后 * * @example * <!-- 组件html结构如下,li里的内容用户可自定义 --> * <div class="ipu-carousel ipu-hammer-carousel"> * <ul class="ipu-carousel-wrapper"> * <li ><img src="../../biz/img/01.jpg" alt=""></li> * <li ><img src="../../biz/img/02.jpg" alt=""></li> * <li ><img src="../../biz/img/03.jpg" alt=""></li> * <li ><img src="../../biz/img/04.jpg" alt=""></li> * </ul> * </div> * * @constructor 不能直接访问该类,调用 {@link ipu#hammerCarousel}生成实例 * @param {String|JqueryObj} slt * jquery选择器字符串或jquery对象,用来查找要被组件初始化化的dom * @param {Object} option 组件配置参数,默认配置见 {@link #cfg-defaultOption} */ function HammerCarousel(slt, option) { this.option = $.extend({}, this.defaultOption, option); this.el = $(slt).get(0); this._init(); } $.extend(HammerCarousel.prototype, { /** * 组件默认配置项 * * @cfg {Object} defaultOption * @cfg {Number} defaultOption.index 初始化时显示第几项,用户未指定时,会查找子项内容上有ipu-current的项显示,默认显示第一项 * @cfg {Boolean} defaultOption.loop 是否循环切换,只有轮播切换时,才能自动轮播 * @cfg {Boolean} defaultOption.autoPlay 是否自动轮播 * @cfg {Number} defaultOption.duration 自动轮播时的间隔时间,单位ms * @cfg {Boolean} defaultOption.indicator 是否生成banner提示器,true右下角出现小点 * @cfg {Function} defaultOption.callBack 轮播显示某项时的回调函数 * @cfg {Number} defaultOption.callBack.index 当前显示的项索引 * @cfg {Function} defaultOption.clickBack * 切换项时被点击时的回调函数,此处主要是为了处理复制项与第一项的点击事件进行处理, * 让用户不关注点击的是第一项或是复制项,回调作用域为组件对象 * @cfg {Number} defaultOption.clickBack.index 点击的项索引 */ defaultOption: { index: null, loop: true, autoPlay: false, duration: 3000, indicator: false, callBack: null, clickBack: null }, _init: function () { this.wrapper = $(">.ipu-carousel-wrapper", this.el); this.carouselItems = $(">li", this.wrapper); this.itemSize = this.carouselItems.size(); // 子项数量 this.showItemSize = 1; // 假设一屏默认显示1个,所以做循环显示只需要复制一个子项 this.carouselItemWides = []; // 子项宽度尺寸 /** @property {Number} 当前显示子项索引,从0开始 */ this.currentIndex = 0; // 当前显示子项索引 this.moveLen = 0; // 当前滚动移动距离 /** @type {Boolean} 循环展示时,第一项会被复制,显示项是第一项时,是否为第一项的复制项 */ this.cloneItem = false; // index是0的时候,有可能显示的是第一项,也有可能显示的是复制项,这个参数用来标记是否复制项 if (this.option.indicator) { this._addIndicator(); } // 如果做循环展示,则要复制起始展示项到最后面 if (this.option.loop) { this.carouselItems.slice(0, this.showItemSize).clone().appendTo(this.wrapper); // 这里假设每个元素宽度都是相等的 } var that = this; if (this.option.clickBack) { $(">li", this.wrapper).each(function (i) { $(this).click(function () { that.option.clickBack.call(this, i % that.size); }); }) } this.hammer = new Hammer.Manager(this.el); this.hammer.add(new Hammer.Pan({direction: Hammer.DIRECTION_HORIZONTAL, threshold: 10})); this.hammer.on("panstart panmove panend pancancel", Hammer.bindFn(this._onPan, this)); this._sizeCount(); $(window).resize(function () { // 在窗口尺寸变化时,更新尺寸信息 that.refresh(); }); if (this.option.index == null) { var activeIndex = this.carouselItems.filter(".ipu-current").index(); this.currentIndex = activeIndex != -1 ? activeIndex : 0; } this.show(this.currentIndex, false); }, /** * 停止自动滚动 */ stop: function () { this._pause(); this.option.autoPlay = false; }, _pause: function () { if (this.timeoutId) { clearTimeout(this.timeoutId); this.timeoutId = null; } }, /** * 切换到上一项 */ prev: function () { var index; if (this.option.loop) { index = this.currentIndex == 0 ? this.itemSize - 1 : this.currentIndex - 1; if (index == this.itemSize - 1) { this._show(this.itemSize, false); this.wrapper.width(); } } else { index = (this.currentIndex - 1 + this.itemSize) % this.itemSize; } this._show(index); }, /** * 切换到下一项 */ next: function () {//下一张 var index if (this.option.loop) { index = this.currentIndex == this.itemSize ? 1 : this.currentIndex + 1; if (index == 1) { this._show(0, false); this.wrapper.width(); } } else { index = (this.currentIndex + 1) % this.itemSize; } this._show(index); }, /** * 切换显示指定项 * * @param {Number} index 要切换到的项索引 * */ show: function (index) {//跳到指定索引处 var index = index % this.itemSize; if (index < 0) { index = this.itemSize + index; } this._show(index); // 默认追加动画 }, /** * 自动轮播 */ play: function () { this.option.autoPlay = true; this._play(); }, _play: function () { if (this.option.autoPlay && this.option.loop && !this.timeoutId) { var that = this; this.timeoutId = setTimeout(function () { that.timeoutId = null;//清空这个timeoutId,代表该次处理已经执行了 that.next(); }, that.option.duration); } }, _addIndicator: function () { var html = ""; for (var i = 0; i < this.itemSize; i++) { html += "<li></li>"; } html = "<ul class='ipu-carousel-indicator'>" + html + "</ul>"; this.indicator = $(html).appendTo(this.el); this.indicatorIndexs = $("li", this.indicator); }, _sizeCount: function () { this.wrapperWidth = this.wrapper.outerWidth(true); this.itemWidth = this.carouselItems.eq(0).outerWidth(true); this.mostSize = this.itemSize * this.itemWidth; // 宽度*数量 $(this.wrapper).removeClass("ipu-carousel-animate").width(); this.carouselItemWides = []; var that = this; $(">li", this.wrapper).each(function (index, dom) { // 此处要注意,最后一个子项是后加进入的,要重新使用jquery处理一下,不能直接使用this.xx来处理 that.carouselItemWides[index] = $(this).position().left; }); }, /** * 宽度信息或尺寸信息发生变更时,进行刷新计算 * 判断是否需要重新计算尺寸,若宽度尺寸发生变化,进行重新尺寸计算 */ refresh: function () { if (this.wrapperWidth != this.wrapper.outerWidth(true)) { this._sizeCount(); this._show(this.currentIndex, false); //新的位置 } }, _move: function (moveLen) { // 拖动时的处理 this._pause(); $(this.wrapper).removeClass("ipu-carousel-animate"); if (this.option.loop) { var move = (this.moveLen - moveLen) % this.mostSize; move = (move + this.mostSize) % this.mostSize; } else { var move = this.moveLen - moveLen; if (move < 0) { move = move / 2; } else if (move > this.mostSize) { move = this.mostSize + (move - this.mostSize) / 2; } } this.displayMoveLen = move; move = -move + "px"; $(this.wrapper).css("transform", "translate3d(" + move + ", 0, 0)"); }, _show: function (index, animate) { // 知道最终移动到的项时,调用 if (animate !== false) { // 默认值为true animate = true; } this._pause(); $(this.wrapper).toggleClass("ipu-carousel-animate", animate); this.currentIndex = index % this.itemSize; this.cloneItem = index == this.itemSize; this.moveLen = this.carouselItemWides[index]; var move = -this.moveLen + "px"; $(this.wrapper).css("transform", "translate3d(" + move + ", 0, 0)"); var currentIndex = this.currentIndex; if (animate && this.option.callBack) { this.option.callBack(currentIndex, this.cloneItem);//返回当前索引,以及是滞最后一项参数 } if (this.indicator) { this.indicatorIndexs.eq(currentIndex).addClass("ipu-current").siblings().removeClass("ipu-current"); } this._play();//处理自动播放 }, _onPan: function (ev) { var delta = ev.deltaX; // 内容往左,deltaX为正值 // pancancel与panend,有效的pan事件结束与无效的pan事件结束? if (ev.type == 'panend' || ev.type == 'pancancel') { var value = delta / this.itemWidth; var intValue = parseInt(Math.abs(value)); // 取整数 var decimal = Math.abs(value) % 1; // 取小数 if (decimal > 0.2) { // 滑动超过页面宽20%; intValue = intValue + 1; } if (delta > 0) { intValue = -intValue; } var index; if (this.option.loop) { index = (this.currentIndex + intValue) % this.itemSize; index = (index + this.itemSize) % this.itemSize; // 因为可能是个负值,转换成正值 // 当前位移大于一个项的长度,这由move方法导致的,所以此时只能是最后一项在显示,所以要显示最后一项 if (index == 0 && this.displayMoveLen > this.itemWidth) { index = this.itemSize; } } else { // 非循环时 index = this.currentIndex + intValue; if (index < 0) { index = 0; } else if (index > this.itemSize - 1) { index = this.itemSize - 1; } } this._show(index); } else if (ev.type == 'panmove') { this._move(delta); } } }); /** * @member ipuUI * 生成HammerCarousel实例,参数信息见{@link HammerCarousel#method-constructor} * * @param {String} slt * @param {Object} option * @returns {HammerCarousel} */ ipuUI.hammerCarousel = function (slt, option) { return new HammerCarousel(slt, option); }; })(ipuUI || window, jQuery, Hammer); // 添加一些jquery扩展 (function (ipuUI, $) { // 在android部分手机上,部分窗体,在jqurey的ready函数执行时,宽度值还未确认,会导致部分UI或依赖宽度计算的代码出现问题 // 皮函数用来处理此问题,等宽度明确或等待最多6s后,执行相关函数 var readyBacks = []; var isSizeReady = false; // 需要记录状态 $.extend({ // 扩展jquery工具方法 sizeReady: function (callBack) { if (isSizeReady) { callBack(); } else { readyBacks.push(callBack); } } }); $(function () { // 添加监听页面ready函数 var count = 0; var delayTime = 40; // 间隔时间ms var totalTime = 6000; // 最高等待6s=6000ms function checkSizeReady() { if (window.innerHeight != 0 || delayTime * count >= totalTime) { isSizeReady = true; for (var i = 0, j = readyBacks.length; i < j; i++) { readyBacks[i](); } } else { count++; setTimeout(checkSizeReady, delayTime); } } checkSizeReady(); }); })(ipuUI || window, jQuery); (function (ipuUI, $) { /** * @class modal,模拟框实现对象,所有方法可直接通过ipuUI调用 * */ var modal = {}; function __dealCssEvent(eventNameArr, callback) { var events = eventNameArr, i, dom = this;// jshint ignore:line 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); } } } $.fn.transitionEnd = function (callback) { __dealCssEvent.call(this, ['webkitTransitionEnd', 'transitionend'], callback); return this; }; var _modalTemplateTempDiv = document.createElement('div'); var defaults = { modalTitle: '', modalStack: true, modalButtonOk: '确定', modalButtonCancel: '取消', modalPreloaderTitle: '加载中', modalContainer: document.body ? document.body : 'body' }; modal.modalStack = []; modal.modalStackClearQueue = function () { if (ipuUI.modalStack.length) { (ipu.modalStack.shift())(); } }; modal.modal = function (params) { params = params || {}; var buttonsHTML = ''; if (params.buttons && params.buttons.length > 0) { for (var i = 0; i < params.buttons.length; i++) { buttonsHTML += '<span class="ipu-modal-button' + (params.buttons[i].bold ? ' ipu-modal-button-bold' : '') + '">' + params.buttons[i].text + '</span>'; } } var extraClass = params.extraClass || ''; var titleHTML = params.title ? '<div class="ipu-modal-title">' + params.title + '</div>' : ''; var textHTML = params.text ? '<div class="ipu-modal-text">' + params.text + '</div>' : ''; var afterTextHTML = params.afterText ? params.afterText : ''; var noButtons = !params.buttons || params.buttons.length === 0 ? 'ipu-modal-no-buttons' : ''; var verticalButtons = params.verticalButtons ? 'ipu-modal-buttons-vertical' : ''; var modalHTML = '<div class="ipu-modal ' + extraClass + ' ' + noButtons + '"><div class="ipu-modal-inner">' + (titleHTML + textHTML + afterTextHTML) + '</div><div class="ipu-modal-buttons ' + verticalButtons + '">' + buttonsHTML + '</div></div>'; _modalTemplateTempDiv.innerHTML = modalHTML; var modalObj = $(_modalTemplateTempDiv).children(); $(defaults.modalContainer).append(modalObj[0]); // Add events on buttons modalObj.find('.ipu-modal-button').each(function (index, el) { $(el).on('click', function (e) { if (params.buttons[index].close !== false) modal.closeModal(modalObj); if (params.buttons[index].onClick) params.buttons[index].onClick(modalObj, e); if (params.onClick) params.onClick(modalObj, index); }); }); modal.openModal(modalObj); return modalObj[0]; }; /** * @member modal * 弹出警告消息 * * @param {String} text 警句文本 * @param {String} title 警告标题,可选参数 * @param {Function} callbackOk 用户确认后的回调函数,可选参数 */ modal.alert = function (text, title, callbackOk) { if (typeof title === 'function') { callbackOk = arguments[1]; title = undefined; } return modal.modal({ text: text || '', title: typeof title === 'undefined' ? defaults.modalTitle : title, buttons: [{text: defaults.modalButtonOk, bold: true, onClick: callbackOk}] }); }; /** * @member modal * 弹出确认消息 * * @param {String} text 确认文本 * @param {String} title 确认标题,可选参数 * @param {Function} callbackOk 用户确认后的回调函数,可选参数 * @param {Function} callbackCancel 用户确认后的回调函数,可选参数 */ modal.confirm = function (text, title, callbackOk, callbackCancel) { if (typeof title === 'function') { callbackCancel = arguments[2]; callbackOk = arguments[1]; title = undefined; } return modal.modal({ text: text || '', title: typeof title === 'undefined' ? defaults.modalTitle : title, buttons: [ {text: defaults.modalButtonCancel, bold: true, onClick: callbackCancel}, {text: defaults.modalButtonOk, bold: true, onClick: callbackOk} ] }); }; /** * @member modal * 弹出输入框 * * @param {String} text 输入提示文本 * @param {String} title 输入提示标题,可选参数 * @param {Function} callbackOk 用户确认后的回调函数,可选参数 * @param {Function} callbackCancel 用户确认后的回调函数,可选参数 */ modal.prompt = function (text, title, callbackOk, callbackCancel) { if (typeof title === 'function') { callbackCancel = arguments[2]; callbackOk = arguments[1]; title = undefined; } return modal.modal({ text: text || '', title: typeof title === 'undefined' ? defaults.modalTitle : title, afterText: '<input type="text" class="ipu-modal-text-input">', buttons: [ { text: defaults.modalButtonCancel }, { text: defaults.modalButtonOk, bold: true } ], onClick: function (modal, index) { if (index === 0 && callbackCancel) callbackCancel($(modal).find('.ipu-modal-text-input').val()); if (index === 1 && callbackOk) callbackOk($(modal).find('.ipu-modal-text-input').val()); } }); }; var minLoad = false; // 是否最小时间调用方式 var loadOverTime = false; // 是否超过最小调用时间 var loadEnd = false; // 是否调用结束 var loadTimeOut = null; // 延时调用ID /** * @member modal * 弹出加载消息提示 * * @param {String} title 加载提示文本 * @param {Number} minTime 消息最小显示时间,单位ms,可选参数 */ modal.showPreloader = function (title, minTime) { modal.hidePreloader(true); modal.showPreloader.preloaderModal = modal.modal({ title: title || defaults.modalPreloaderTitle, text: '<div class="ipu-preloader"></div>' }); if (minTime) { minLoad = true; loadTimeOut = setTimeout(function () { loadOverTime = true; if (loadEnd) { modal.hidePreloader(); } }, minTime); } return modal.showPreloader.preloaderModal; }; /** * @member modal * 隐藏加载消息提示 * * @param {Boolean} force 是否强制隐藏,不管最小提示时间,可选 */ modal.hidePreloader = function (force) { if (force || !minLoad || (minLoad && loadOverTime)) { if (force && loadTimeOut) { window.clearTimeout(loadTimeOut); } modal.showPreloader.preloaderModal && modal.closeModal(modal.showPreloader.preloaderModal); minLoad = false; // 重置各标志位 loadOverTime = false; loadEnd = false; loadTimeOut = null; } else { loadEnd = true; } }; /** * @member modal * 显示加载状态 */ modal.showIndicator = function () { if ($('.ipu-preloader-indicator-modal')[0]) return; $(defaults.modalContainer).append('<div class="ipu-preloader-indicator-overlay"></div><div class="ipu-preloader-indicator-modal"><span class="ipu-preloader ipu-preloader-white"></span></div>'); }; /** * @member modal * 隐藏加载状态 */ modal.hideIndicator = function () { $('.ipu-preloader-indicator-overlay, .ipu-preloader-indicator-modal').remove(); }; /** * @member modal * 显示操作选项 * * @param{[[Object]]} actions * @param {Object} actions.Object * @param {String} actions.Object.text 操作名称 * @param {Boolean} actions.Object.label 是否标签,非标签就是操作项,操作项有后续的配置,标签项无须后续配置项 * @param {String:warning} actions.Object.color 样式,可选 * @param {String:warning} actions.Object.bg 背景样式,可选 * @param {Function} actions.Object.onClick 点击时回调函数 */ modal.actions = function (params) { var modalObj, groupSelector, buttonSelector; params = params || []; if (params.length > 0 && !$.isArray(params[0])) { params = [params]; } var modalHTML; var buttonsHTML = ''; for (var i = 0; i < params.length; i++) { for (var j = 0; j < params[i].length; j++) { if (j === 0) buttonsHTML += '<div class="ipu-actions-modal-group">'; var button = params[i][j]; var buttonClass = button.label ? 'ipu-actions-modal-label' : 'ipu-actions-modal-button'; if (button.bold) buttonClass += ' ipu-actions-modal-button-bold'; if (button.color) buttonClass += ' ipu-color-' + button.color; if (button.bg) buttonClass += ' ipu-bg-' + button.bg; if (button.disabled) buttonClass += ' disabled'; buttonsHTML += '<span class="' + buttonClass + '">' + button.text + '</span>'; if (j === params[i].length - 1) buttonsHTML += '</div>'; } } modalHTML = '<div class="ipu-actions-modal">' + buttonsHTML + '</div>'; _modalTemplateTempDiv.innerHTML = modalHTML; modalObj = $(_modalTemplateTempDiv).children(); $(defaults.modalContainer).append(modalObj[0]); groupSelector = '.ipu-actions-modal-group'; buttonSelector = '.ipu-actions-modal-button'; var groups = modalObj.find(groupSelector); groups.each(function (index, el) { var groupIndex = index; $(el).children().each(function (index, el) { var buttonIndex = index; var buttonParams = params[groupIndex][buttonIndex]; var clickTarget; if ($(el).is(buttonSelector)) clickTarget = $(el); // if (toPopover && $(el).find(buttonSelector).length > 0) clickTarget = $(el).find(buttonSelector); if (clickTarget) { clickTarget.on('click', function (e) { if (buttonParams.close !== false) modal.closeModal(modalObj); if (buttonParams.onClick) buttonParams.onClick(modalObj, e); }); } }); }); modal.openModal(modalObj); return modalObj[0]; }; //显示一个消息,会在2秒钟后自动消失 /** * @member modal * 悬浮提示消息 * * @param {String} msg 消息文本 * @param {Number} duration=2000 消息显示时间,单位ms */ modal.toast = function (msg, duration, extraclass) { var $toast = $('<div class="ipu-modal ipu-toast ' + (extraclass || '') + '">' + msg + '</div>').appendTo(document.body); modal.openModal($toast, function () { setTimeout(function () { modal.closeModal($toast); }, duration || 2000); }); }; modal.openModal = function (modalObj, cb) { modalObj = $(modalObj); var isModal = modalObj.hasClass('ipu-modal'), isNotToast = !modalObj.hasClass('ipu-toast'); isNotToast = false; // 强制打开新窗口 if ($('.ipu-modal.ipu-modal-in:not(.ipu-modal-out)').length && defaults.modalStack && isModal && isNotToast) { modalObj.modalStack.push(function () { modal.openModal(modalObj, cb); }); return; } var isPopup = modalObj.hasClass('ipu-popup'); var isLoginScreen = modalObj.hasClass('ipu-login-screen'); var isPickerModal = modalObj.hasClass('ipu-picker-modal'); var isToast = modalObj.hasClass('ipu-toast'); if (isModal) { modalObj.show(); modalObj.css({ marginTop: -Math.round(modalObj.outerHeight() / 2) + 'px' }); } if (isToast) { modalObj.css({ marginLeft: -Math.round(modalObj.outerWidth() / 2) + 'px' //1.185 是初始化时候的放大效果 }); } var overlay; if (!isLoginScreen && !isPickerModal && !isToast) { if ($('.ipu-modal-overlay').length === 0 && !isPopup) { $(defaults.modalContainer).append('<div class="ipu-modal-overlay"></div>'); } if ($('.ipu-popup-overlay').length === 0 && isPopup) { $(defaults.modalContainer).append('<div class="ipu-popup-overlay"></div>'); } overlay = isPopup ? $('.ipu-popup-overlay') : $('.ipu-modal-overlay'); } //Make sure that styles are applied, trigger relayout; var clientLeft = modalObj[0].clientLeft; // Trugger open event modalObj.trigger('open'); // Picker modal body class if (isPickerModal) { $(defaults.modalContainer).addClass('ipu-with-picker-modal'); } // Classes for transition in if (!isLoginScreen && !isPickerModal && !isToast) { overlay.addClass('ipu-modal-overlay-visible'); } modalObj.removeClass('ipu-modal-out').addClass('ipu-modal-in').transitionEnd(function (e) { if (modalObj.hasClass('ipu-modal-out')) modalObj.trigger('closed'); else modalObj.trigger('opened'); }); // excute callback if (typeof cb === 'function') { cb.call(this); } return true; }; modal.closeModal = function (modalObj) { modalObj = $(modalObj || '.ipu-modal-in'); if (typeof modalObj === 'undefined' || modalObj == null || (typeof modalObj !== 'undefined' && modalObj.length === 0)) { return; } var isModal = modalObj.hasClass('ipu-modal'), isPopup = modalObj.hasClass('ipu-popup'), isToast = modalObj.hasClass('ipu-toast'), isLoginScreen = modalObj.hasClass('ipu-login-screen'), isPickerModal = modalObj.hasClass('ipu-picker-modal'), removeOnClose = modalObj.hasClass('ipu-remove-on-close'), overlay = isPopup ? $('.ipu-popup-overlay') : $('.ipu-modal-overlay'); if (isPopup) { if (modalObj.length === $('.ipu-popup.ipu-modal-in').length) { overlay.removeClass('ipu-modal-overlay-visible'); } } else if (!(isPickerModal || isToast)) { overlay.removeClass('ipu-modal-overlay-visible'); } modalObj.trigger('close'); // Picker modal body class if (isPickerModal) { $(defaults.modalContainer).removeClass('ipu-with-picker-modal'); $(defaults.modalContainer).addClass('ipu-picker-modal-closing'); } modalObj.removeClass('ipu-modal-in').addClass('ipu-modal-out').transitionEnd(function (e) { if (modalObj.hasClass('ipu-modal-out')) modalObj.trigger('closed'); else modalObj.trigger('opened'); if (isPickerModal) { $(defaults.modalContainer).removeClass('ipu-picker-modal-closing'); } if (isPopup || isLoginScreen || isPickerModal) { modalObj.removeClass('ipu-modal-out').hide(); if (removeOnClose && modalObj.length > 0) { modalObj.remove(); } } else { modalObj.remove(); } }); if (isModal && defaults.modalStack) { modal.modalStackClearQueue(); } return true; }; function handleClicks(e) { /*jshint validthis:true */ var clicked = $(this); var url = clicked.attr('href'); //Collect Clicked data- attributes /* var clickedData = clicked.dataset(); // Popup var popup; if (clicked.hasClass('ipu-open-popup')) { if (clickedData.popup) { popup = clickedData.popup; } else popup = '.ipu-popup'; ipu.popup(popup); } if (clicked.hasClass('ipu-close-popup')) { if (clickedData.popup) { popup = clickedData.popup; } else popup = '.ipu-popup.modal-in'; ipu.closeModal(popup); }*/ // Close Modal if (clicked.hasClass('ipu-modal-overlay')) { if ($('.ipu-modal.ipu-modal-in').length > 0 && defaults.modalCloseByOutside) modal.closeModal('.ipu-modal.ipu-modal-in'); if ($('.ipu-actions-modal.ipu-modal-in').length > 0 && defaults.actionsCloseByOutside) modal.closeModal('.ipu-actions-modal.ipu-modal-in'); } if (clicked.hasClass('ipu-popup-overlay')) { if ($('.ipu-popup.ipu-modal-in').length > 0 && defaults.popupCloseByOutside) modal.closeModal('.ipu-popup.modal-in'); } } $.extend(ipuUI, modal); $(document).on('click', ' .ipu-modal-overlay, .ipu-popup-overlay, .ipu-close-popup, .ipu-open-popup, .ipu-close-picker', handleClicks); })(ipuUI || window, jQuery); (function (ipuUI, $) { /** * @class 导航切换组件 * * @example * * <!-- 组件的html分成导航和内容两部分,一般与flex栅格配合布局--> * * <!-- 组件导航部分 --> * <nav class="ipu-navbar "> * <a class="ipu-navbar-item " href="javascript:;"> * <span class="ipu-icon fa fa-home"></span> * <span class="ipu-navbar-item-label">插件</span> * </a> * <a class="ipu-navbar-item " href="javascript:;"> * <span class="ipu-icon fa fa-dashcube"></span> * <span class="ipu-navbar-item-label">JS组件</span> * </a> * <a class="ipu-navbar-item ipu-current" href="javascript:;"> * <span class="ipu-icon fa fa-map"></span> * <span class="ipu-navbar-item-label">静态组件</span> * </a> * <a class="ipu-navbar-item" href="javascript:;"> * <span class="ipu-icon fa fa-mortar-board"></span> * <span class="ipu-navbar-item-label">更多</span> * </a> * </nav> * * <!-- 内容部分 --> * <div class="ipu-nav-content"> * <ul> * <li> * 自定义内容1 * </li> * <li> * 自定义内容 * </li> * <li> * 自定义内容 * </li> * <li> * 自定义内容 * </li> * </ul> * </div> * * * @constructor 不能直接访问该类,调用{@link ipu#navBar ipuUI.navBar(slt, option)}生成实例 * @param {String|jqueryObj} slt jquery选择器字符串或jquery对象,用来查找要被组件初始化化的dom * @param {Object} option 组件配置参数,默认配置见 {@link #cfg-defaultOption} */ function NavBar(slt, option) { this.option = $.extend({}, this.defaultOption, option); this.content = $(this.option.contentSlt); this.nav = $(slt); this.wrapper = $(">ul", this.content); this.contents = $(">li", this.wrapper); this.navs = $(">a", this.nav); var me = this; var activeIndex = this.navs.filter(".ipu-current").index(); // 查找默认有active的索引 if (activeIndex == -1) { activeIndex = this.contents.filter(".ipu-current").index(); // 查找默认有active的索引 } this.option.index = activeIndex != -1 ? activeIndex : 0; if (!this.option.animate) { this.wrapper.addClass("ipu-no-animation") } this.navs.each(function (index, i) { $(this).click(function () { me.show(index); }); }); this.lastIndex = null; this.currentIndex = null; me.show(this.option.index); } /** * 组件默认配置项 * * @cfg {Object} defaultOption * @cfg {Boolean} defaultOption.animate=false 切换时是否添加动画效果 * @cfg {Dom|String|JqueryObj} defaultOption.contentSlt='.ipu-nav-content' 内容dom选择器,页面有多个navBar组件时,需要设置此值 * @cfg {Function} defaultOption.callBack 切换时的回调函数 * @cfg {Number} defaultOption.callBack.index 当前显示项索引 */ NavBar.prototype.defaultOption = { animate: false, contentSlt: ".ipu-nav-content", callBack: function (currentIndex, lastIndex) { } }; /** * 显示第几项内容 * @param {Number} index 显示内容项索引 */ NavBar.prototype.show = function (index) { if (this.currentIndex != index) { $(this.contents[index]).addClass("ipu-show"); if (this.option.animate) { if (this.lastIndex != null && this.lastIndex != index) { $(this.contents[this.lastIndex]).removeClass("ipu-show"); // 隐藏上上个元素 } if (this.currentIndex != null) { // 非第一次需要动画效果 if (this.currentIndex < index) { // 需要内容为往左走,显示右边的内容 if (this.lastIndex != null && this.lastIndex < this.currentIndex) { // 内容已经左走过了,则需要移除动画复原位置,再通过width()方法强制生效 this.wrapper.addClass("ipu-no-animation").removeClass("ipu-nav-content-right").width(); // 可以强制刷新,默认jquery应该会将这些dom上的修改延时处理? } } else { if (this.lastIndex == null || this.lastIndex > this.currentIndex) { // 类似同上 this.wrapper.addClass("ipu-no-animation").addClass("ipu-nav-content-right").width(); // 可以强制刷新不? } } this.wrapper.removeClass("ipu-no-animation").toggleClass("ipu-nav-content-right"); } } else { $(this.contents[this.currentIndex]).removeClass("ipu-show"); } // 更新class,ipu-current状态 $(this.contents[index]).addClass("ipu-current").siblings(".ipu-current").removeClass("ipu-current"); $(this.navs[index]).addClass("ipu-current").siblings(".ipu-current").removeClass("ipu-current"); this.lastIndex = this.currentIndex; this.currentIndex = index; if (this.option.callBack) { this.option.callBack(this.currentIndex, this.lastIndex); } } }; /** * @member ipuUI * 生成NavBar实例,参数信息见{@link NavBar#method-constructor} * * @param {String} slt * @param {Object} option * @returns {NavBar} */ ipuUI.navBar = function (slt, option) { return new NavBar(slt, option); }; })(ipuUI || window, jQuery); (function (ipuUI, $) { function __dealCssEvent(eventNameArr, callback) { var events = eventNameArr, i, dom = this;// jshint ignore:line function fireCallBack(e) { 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); } } } $.fn.animationEnd = function (callback) { __dealCssEvent.call(this, ['webkitAnimationEnd', 'animationend'], callback); return this; }; function submitForm(doc, url, params) { var form = doc.createElement("form"); form.action = url; form.method = "post"; form.style.display = "none"; for (var x in params) { var ele = doc.createElement("input"); ele.type = "hidden"; ele.name = x; ele.value = params[x]; form.appendChild(ele); } doc.body.appendChild(form); form.submit(); } // 检查是否有ipu-pages的结构 function checkPages() { if (!hasPages) { pagesObj = $(".ipu-pages"); // pagesObj为空则进行jquery取值 if (pagesObj.size() == 0) { pagesObj = $("<div class='ipu-pages'><div class='ipu-page ipu-show " + zeroPageClass + "' id='" + pageIdPrefix + "0'></div>").appendTo("body"); } hasPages = true; } } // 站位页面 function isZeroPage(page) { return $(page).hasClass(zeroPageClass); } var page = {}; var hasPages = false; var maps = {}; var pageNo = 1; // 编号0留给主页面或当前页面,或没有 var pageIdPrefix = "ipuPage-"; var pagesObj = null; var animateInClass = "ipu-anim ipu-slideRightIn"; var animateOutClass = "ipu-anim ipu-slideRightOut"; var eventName = "ipuUIPageBack"; var zeroPageClass = 'ipu-page-zero'; // 占位页面,对于为当前页面 var zeroPagesClass = 'ipu-pages-zero'; // 占位页面的特殊class,作用已忘记,应该是用来标记显示用 /** * @private * @class page 单页面实现功能对象 * 以iframe加载子页面的方式,页面后退(后退时,后退到a页面,所有在a页面后打开的页面全都关闭) * ipu框架在浏览器运行时,使用此对象实现与客户端运行类似的效果 * 大致实现是当前页面进行处理,所有的后续页面加载都放在一个iframe中,所有页面按加载顺序排序,关闭或后退按页面打开的顺序处理 */ /** * 组件默认配置项 * * @cfg {Object} defaultOption page组件默认配置项 * @cfg {Window} defaultOption.target = window.parent 默认执行的窗口对象,子页面调用相关方法,默认都都是在parent窗口执行,需要指定此参数,如顶层父窗口 * @cfg {Number} defaultOption.backIndex=-1 回退索引,大于0时,正序计算,小于0时,倒序计算,-1即为当页面的上一个页面 * @cfg {Number} defaultOption.closeIndex 关闭页面索引,参数说明同上 * @cfg {Object} defaultOption.params Json格式参数,POST方式打开页面时,使用此参数传递参数,暂不支持数组格式参数 * @cfg {Boolean} defaultOption.animate=true 是否使用动画,打开或回退页面时有效参数 * @cfg {Boolean} defaultOption.showLoading=true 是否显示加载提示,打开或回退页面时有效参数 * @cfg {Boolean} defaultOption.loadingMessage='正在加载中' 是否显示加载提示,打开或回退页面时有效参数 * @cfg defaultOption.data=null 回退页面时,传递给回退到的页面的参数,回退到的页面有设置监听函数时,监听函数可以接收此参数 * @cfg {String} defaultOption.pageName='' 页面的名称,打开或回退页面时有效参数 * @cfg {Number} defaultOption.pageMax='' 保留的最大页面数,大于2 * @cfg {Function} defaultOption.callBack 方法执行结束时的回调函数 */ page.defaultOption = { // 那个窗口执行open,默认父窗口 target: window.parent, // 默认执行父窗口,方法:all backIndex: -1, // 默认回退一页 方法:back closeIndex: -1, // 默认关闭最近一个页面 方法:close params: {}, // post的传参 方法:post animate: true, // 是否动画效果 方法:open post showLoading: true, // 是否显示加载消息 方法:open post loadingMessage: '正在加载中', // 方法:open post method: null, // 请求方式,内置参数,方法自己设置,用户不需要设置 方法:无 minMessageTime: 500, // 最小显示加载时间,避免出现闪现的情况 方法:open post data: null, // 回退时,回传参数, 方法:back pageName: '', // 给打开的页面命名,以便根据此页面名称来切换页面 方法:open post back close pageMax: 8, // 允许的最大打开页面数 callBack: function () { // 事件回调 方法:open post close back } }; // 新增限制最大页面数 page.limitPages = function () { var pageMax = this.defaultOption.pageMax - 2; // $(".ipu-page.ipu-show").prevAll(".ipu-page:gt(" + pageMax + ")").remove(); }; // 当前页面加载,针对顶层父窗口 page.openPage = function (url, option) { var newPage = null; var nowPageNo = pageIdPrefix + (pageNo++); maps[nowPageNo] = url; checkPages(); if (option.showLoading) { ipuUI.showPreloader(option.loadingMessage, option.minMessageTime); } if (option.method == 'post') { newPage = $("<div class='ipu-page' id='" + nowPageNo + "' data-name='" + option.pageName + "'><iframe class='ipu-page-iframe'></iframe></div>"); } else { newPage = $("<div class='ipu-page' id='" + nowPageNo + "' data-name='" + option.pageName + "'><iframe class='ipu-page-iframe' src='" + url + "'></iframe></div>"); } var zeroPage = isZeroPage($(".ipu-page:last", pagesObj)); var animatePage = newPage; if (zeroPage) { animatePage = pagesObj.addClass(zeroPagesClass); } function end() { if (option.showLoading) { ipuUI.hidePreloader(); } if (option.animate) { animatePage.removeClass(animateInClass); } newPage.siblings(".ipu-show").removeClass('ipu-show'); if (option.callBack) { option.callBack(); } // 新增限制最大页面数 page.limitPages(); } $(".ipu-page-iframe", newPage).one('load', function () { newPage.addClass("ipu-show").width(); // 强制生效,否则可能出现页面闪现,无动画情况 if (zeroPage) { animatePage.removeClass(zeroPagesClass); } if (option.animate) { animatePage.addClass(animateInClass).animationEnd(end); } else { end(); } }); newPage.appendTo(pagesObj); if (option.method == 'post') { var pageDoc = $(".ipu-page-iframe", newPage)[0].contentDocument; submitForm(pageDoc, url, option.params); } }; // post方式加载页面 page.postPage = function (url, option) { option.method = 'post'; page.openPage(url, option); }; // 当前页面后退,针对顶层父窗口 page.backPage = function (option) { var backIndex = option.backIndex; var page = null; var nowPage = $(".ipu-page.ipu-show", pagesObj); if (option.pageName) { page = $(".ipu-page[data-name='" + option.pageName + "']:first", pagesObj); } else if (backIndex == 0) { page = $(".ipu-page:first", pagesObj); } else { // 越界的情况 var prevPage = nowPage.prevAll(".ipu-page"); if (backIndex < 0) { page = $(prevPage[-backIndex - 1]); } else { page = $(prevPage[prevPage.size() - backIndex]); } } var animatePage = nowPage; var zeroPage = isZeroPage(page); // 主页面模式时 if (zeroPage) { animatePage = pagesObj; } else { page.addClass("ipu-show"); //显示前一个 } function end() { $(this).removeClass(animateOutClass); page.nextAll(".ipu-page").remove(); var iframe = $(".ipu-page-iframe", page); var nowDoc; if (iframe.size() == 0) { // 找不到子窗口就当是返回了主页面,在当前窗口触发 nowDoc = window.document; } else { nowDoc = iframe[0].contentDocument; } if (zeroPage) { pagesObj.addClass(zeroPagesClass); } var evt = nowDoc.createEvent('Event'); evt.initEvent(eventName, true, true); if (option.data) { evt.data = option.data; } nowDoc.body.dispatchEvent(evt); if (option.callBack) { option.callBack(); } } if (option.animate) { animatePage.addClass(animateOutClass).animationEnd(end); } else { end(); } }; // 往前关闭窗口 page.closePage = function (option) { var closeIndex = option.closeIndex; var prevPage = $(".ipu-page.ipu-show", pagesObj).prevAll(".ipu-page"); if (option.pageName) { closeIndex = $(".ipu-page[data-name='" + option.pageName + "']:first", pagesObj).index(); } else if (closeIndex < 0) { closeIndex = -closeIndex - 1; } else { closeIndex = prevPage.size() - closeIndex; } $(prevPage[closeIndex]).remove(); if (option.callBack) { option.callBack(); } }; /** * get请求的方式加载页面 * * @param {String} url * @param {Object} option 回退参数,见{@link #cfg-defaultOption} */ page.open = function (url, option) { option = $.extend({}, this.defaultOption, option); option.target.ipuUI.page.openPage(url, option); }; /** * 使用post方式加载一个新页面 * * @param {String} url 要打开的页面地址 * @param {Object} option 回退参数,见{@link #cfg-defaultOption} */ page.post = function (url, option) { option = $.extend({}, this.defaultOption, option); option.method = 'post'; option.target.ipuUI.page.openPage(url, option); }; /** * 回退到某个历史页面,可以根据pageName回退,也可根据backIndex回退,默认回退上一个页面 * * @param {Object} option 回退参数,见{@link #cfg-defaultOption} */ page.back = function (option) { option = $.extend({}, this.defaultOption, option); option.target.ipuUI.page.backPage(option); }; /** * 回退到首页 * * @param {Object} option 回退参数,见{@link #cfg-defaultOption} */ page.backHome = function (option) { option = option || {}; option.backIndex = 0; page.back(option); }; // 子窗口,待确认 page.close = function (option) { option = $.extend({}, this.defaultOption, option); option.target.ipuUI.page.closePage(option); }; /** * 给页面增加一个监听,从其它页面回退到此页面,调用此函数,可以接收其它页面传来的数据 * * @param {Function} back 监听函数 * @param back.data 其它页面传过来的参数,推荐字符串或Json对象 */ page.onBack = function (back) { $("body").on(eventName, function (e) { var data = e.originalEvent.data; back(data); }); }; // 提供一个关闭一群窗口的方法 ipuUI.page = page; })(ipuUI || window, jQuery); // picker (function (ipuUI, $, Hammer) { var showItemSize = 9; // 显示的子项数量, var r = 90; // 计算旋转的圆半径,结果应该缩小,是为了r不要距离容器太近,是否不应该设置px,使用rem var itemAngle = 180 / showItemSize; // 每项对应的角度是 180/9 = 20 var maxExceed = itemAngle; // 滚动时允许超出边界的最大角度,允许最多翻过一项 // itemHeight = 40px;每项数据的高度设置 // 需要给出r=89是怎么计算出来的,是根据 40/2/Math.tan(40/2/180*Math.PI)=113,直接太大不好看 function toRem(num) { return num / 100; } /** * @private * @class 选择器,被DtPicker和PopPicker使用,实现选择与滚动等基础功能 * * @constructor 初始化方法 * @param {String|DOM|JQueryObj} slt * @param {object} option 组件参数,默认配置见 {@link #cfg-defaultOption} */ function Picker(slt, option) { this.el = $(slt)[0]; this.option = $.extend({}, this.defaultOption, option); this._init(); } /** * 组件默认配置项 * * @cfg {Object} defaultOption= * @cfg {Boolean} defaultOption.listen=true 是否需要监听变化 * @cfg {[Object]} defaultOption.data=[] 可选择项数组,每个项须有text属性 * @cfg {String} defaultOption.data.text 子项展示文本 * @cfg {Function} defaultOption.onChange=null 选择变化时的回调函数 * @cfg {Object} defaultOption.onChange.sltItem 选中项 * @cfg {Number} defaultOption.onChange.newIndex 新的选中项索引 * @cfg {Number} defaultOption.onChange.oldIndex 旧的选中项索引 * @cfg {Boolean} defaultOption.onChange.newData 是否为调用setItem()方法触发 */ Picker.prototype.defaultOption = { listen: true, data: [], onChange: null }; Picker.prototype._init = function () { var self = this; this.wrap = $(">ul", this.el); this.index = null; // 选中项索引 this.listen = !!this.option.listen; this.beginAngle = 0; // 开始角度 this.beginExceed = this.beginAngle - maxExceed; // 最小角度值 this.stopInertiaMove = false; this.lastAngle = null; // 保存滑动前的角度 // 当前滚动的角度 // 如果是ios,则ul的旋转中心点,样式不同于android if (ipuUI.device.ios) { this.wrap.css("transform-origin", "center center " + toRem(r) + "rem"); //如果是ios,要变更旋转的中心点 } this.setItems(this.option.data); this.hammer = new Hammer.Manager(this.el); this.hammer.add(new Hammer.Pan({direction: Hammer.DIRECTION_VERTICAL, threshold: 5})); this.hammer.add(new Hammer.Press({threshold: 4})); // this.hammer.on("panstart panmove panend pancancel", Hammer.bindFn(this._onPan, this)); // 处理滚动中,用户点中某项,停止 this.hammer.on("press pressup", function (e) { // 如果用户点击了,是停止自动滚动 if (this.empty) { return; } self.stopInertiaMove = true; if (e.type == 'pressup') { self.endScroll(); } }); }; /** * 设置选择项 * * @param {[Object]} data 设置项数组 */ Picker.prototype.setItems = function (data, textName) { // textNam字体暂不支持 this.wrap.empty(); // 清空历史数据 this.data = data = data || []; this.empty = data.length == 0; // 数据是否为空 this.newData = true; // 是否为新设置数据标记 var self = this; var lis = ""; textName = textName || 'text'; for (var i = 0, j = data.length; i < j; i++) { lis = lis + "<li>" + data[i][textName] + "</li>"; } $(lis).appendTo(this.wrap); this.items = $(">li", this.wrap); this.itemsSize = this.items.size(); this.endAngle = (this.empty ? 0 : this.itemsSize - 1) * itemAngle; this.endExceed = this.endAngle + maxExceed; // 最大旋转角度值 // 初始化各子项角度 this.items.each(function (i) { $(this).css({ "transform": "translateZ(" + toRem(r) + "rem) rotateX(-" + (i * itemAngle) + "deg)", "transform-origin": "center center -" + toRem(r) + "rem" }); $(this).click(function () { self.stopInertiaMove = true; self.setAngle(i * itemAngle, true); }) }); var newAngle; if (this.empty || this.index == null) { newAngle = 0; } else { if (this.index > this.itemsSize - 1) { // 取最大值 newAngle = (this.itemsSize - 1) * itemAngle; } else { newAngle = this.index * itemAngle; } } this.setAngle(newAngle, true); }; Picker.prototype._onPan = function (ev) { if (this.empty) { return; } //console.log(ev.deltaX + "=="+ ev.deltaY); if (ev.type == 'panstart') { // 好像一定要移动才有startg事件 self.stopInertiaMove = true; this.lastAngle = this.angle; this.wrap.addClass("ipu-noanimate"); // 移除动画 this.stopInertiaMove = true; // 停止自动减速滚动 } else if (ev.type == 'panmove') { var moveAngle = this.calcAngle(ev.deltaY); var newAngle = this.lastAngle - moveAngle; //最新的角度 //console.log('=='+newAngle); // 一个可以转动的最小值和最大值过滤 if (newAngle < this.beginExceed) { newAngle = this.beginExceed; } if (newAngle > this.endExceed) { newAngle = this.endExceed; } this.setAngle(newAngle); } else { // end or cancel事件 // console.log('end or cancel:' + ev.type); var v = ev.overallVelocityY; // 滑动的速度 var dir = v > 0 ? -1 : 1; //加速度方向 var deceleration = dir * 0.0006 * -1; var duration = Math.abs(v / deceleration); // 速度消减至0所需时间 var dist = v * duration / 2; //最终移动多少 var startAngle = this.angle; var distAngle = -this.calcAngle(dist); // console.log("dist=" + dist + ", distAngle" + distAngle); //---- var srcDistAngle = distAngle; if (startAngle + distAngle < this.beginExceed) { distAngle = this.beginExceed - startAngle; duration = duration * (distAngle / srcDistAngle) * 0.6; } if (startAngle + distAngle > this.endExceed) { distAngle = this.endExceed - startAngle; duration = duration * (distAngle / srcDistAngle) * 0.6; } if (distAngle == 0) { this.endScroll(); return; } this.scrollDistAngle(startAngle, distAngle, duration); } }; // 计算移动的角度,转动的角度,就是移动的距离对应相关圆周 // 2*r*PI = 360, angle = 360*c/(2*r*PI) var ca = 360 / (2 * r * Math.PI); Picker.prototype.calcAngle = function (c) { return c * ca; }; /** * 为组件设置新的滚动角度 * * @param {Number} newAngle 新的滚动角度 * @param {Boolean} endScroll 是否为最终滚动角度,为最终滚动角度时,若索引更新可以触发onChange的回调 */ Picker.prototype.setAngle = function (newAngle, endScroll) { this.angle = newAngle; // 存储最新值 this.wrap.css("transform", "perspective(" + toRem(1000) + "rem) rotateY(0deg) rotateX(" + newAngle + "deg)"); this.calcItemVisable(newAngle); if (endScroll) { var index = newAngle / itemAngle; var oldIndex = this.index; this.index = this.empty ? null : index; // 这里可以做一个判断,如果是empty,则index值可以不改变 // 这个地方要判断下,数据更新或索引更新都要触发 if (oldIndex != index || this.newData) { if (this.option.onChange && this.listen) { this.option.onChange(this.getSelectedItem(), this.index, oldIndex, this.newData); } this.newData = false; } } }; /** * 计算各子项滚动角度与新的滚动角度的值差异来决定显示的情况 * 角度大于 90-(itemAngle/2)时,隐藏 * 角度小于itemAngle/2表示最中心的项,显示并高亮 * 其它值则表示此项为显示 * * @param {Number} angle 新的滚动角度 */ Picker.prototype.calcItemVisable = function (angle) { this.items.each(function (index) { var difference = Math.abs(index * itemAngle - angle); if (difference < itemAngle / 2) { $(this).addClass("ipu-highlight ipu-visible"); } else if (difference >= (90 - itemAngle / 2)) { // 距离不能超过90度 $(this).removeClass("ipu-highlight ipu-visible"); } else { $(this).addClass("ipu-visible").removeClass("ipu-highlight"); } }); }; // 设置最后回归位置 Picker.prototype.endScroll = function () { this.wrap.removeClass("ipu-noanimate"); var endAngle; if (this.angle < this.beginAngle) { endAngle = this.beginAngle; } else if (this.angle > this.endAngle) { endAngle = this.endAngle; } else { var index = parseInt((this.angle / itemAngle).toFixed(0)); endAngle = (itemAngle * index); } this.setAngle(endAngle, true); }; // 进行惯性滚动 Picker.prototype.scrollDistAngle = function (startAngle, distAngle, duration) { var self = this; var nowTime = new Date().getTime(); this.stopInertiaMove = false; duration = 1 * duration; // 滚动时长控制修改 // hammer调用的惯性函数 (function (nowTime, startAngle, distAngle, duration) { var frameInterval = 13; var stepCount = duration / frameInterval; var stepIndex = 0; (function inertiaMove() { if (self.stopInertiaMove) return; var newAngle = self.quartEaseOut(stepIndex, startAngle, distAngle, stepCount); self.setAngle(newAngle); stepIndex++; if (stepIndex > stepCount - 1 || newAngle < self.beginExceed || newAngle > self.endExceed) { self.endScroll(); return; } setTimeout(inertiaMove, frameInterval); })(); })(nowTime, startAngle, distAngle, duration); }; /** * 设置是否监听触发onChange回调 * * @param {Boolean} listen */ Picker.prototype.setListen = function (listen) { this.listen = !!listen; }; Picker.prototype.quartEaseOut = function (t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }; /** * 设置选中项,若子项的value属性为value,则设置该项为选中项 * * @param value */ Picker.prototype.setSelectedValue = function (value) { var self = this; for (var index in self.data) { var item = self.data[index]; if (item.value == value) { self.setAngle(index * itemAngle, true); return; } } }; /** * 获取选中的子项,若子项集为空时,返回空对象 * * @returns {Object} */ Picker.prototype.getSelectedItem = function () { return this.empty ? {} : this.data[this.index]; }; /** * 获取选中的子项的value属性 * @returns */ Picker.prototype.getSelectedValue = function () { return this.getSelectedItem().value; }; /** * 返回选中项的text属性 * @return {String} */ Picker.prototype.getSelectedText = function () { return this.getSelectedItem().text; }; /** * 获取选中项的索引,若子项集为空则返回null * @returns {Number} */ Picker.prototype.getSelectedIndex = function () { return this.index; }; ipuUI.Picker = Picker; })(ipuUI || window, jQuery, Hammer); // popPicker (function (ipuUI, $) { var Picker = ipuUI.Picker; /** * @class * 原生select的替代实现,适应数据较多或级联的情况 * * @example * // 配置项data的数据结构 * var data = [{text:'显示名称', value:''}...]; * * // layer=1的数据结构 * var data-1 = [{text:'湖南', value:'HN'}, {text:'湖北', value:'HB'}]; * * // layer=2时的数据结构,有额外data属性存放下一层级数据 * var data-1 = [{ * text:'湖南',value:'HN', data:[{text:'长沙', value:'CS'}, {text:'湘谭', value:'XT'}] * },{ * text:'湖北',value:'HB', data:[{text:'武汉', value:'WH'}, {text:'天门', value:'TM'}] * } * ]; * * @constructor 不能直接访问该类,调用{@link ipuUI.popPicker(slt, option)}生成实例 * @param {String|jqueryObj} slt * jquery选择器字符串或jquery对象,用来查找要被组件初始化化的dom * @param {Object} option 组件配置参数,默认配置见 {@link #cfg-defaultOption} */ function PopPicker(option) { this.option = $.extend({}, this.defaultOption, option); if (!Picker) { Picker = ipuUI.Picker; } this._init(); } PopPicker.prototype._init = function () { this.holder = $(this.option.template).appendTo("body"); var bodyHtml = $(".ipu-poppicker-body", this.holder); var layer = this.option.layer; var width = (100 / layer) + "%"; this.pickers = new Array(layer); var self = this; var pickerHtml; this.mask = this.createMask(); // 先初始化最底层picerk,再上面来 for (var i = layer - 1; i >= 0; i--) { pickerHtml = $(this.option.pickerTemplate).prependTo(bodyHtml).css({width: width}); this.pickers[i] = new Picker(pickerHtml, { onChange: (function (i) { return function (item) { // 更新底部的值 if (i != layer - 1) { self.pickers[i + 1].setItems(item.data); } }; })(i) }); } $(".ipu-poppicker-btn-ok", this.holder).click(function () { var rs = self.getSelectItems(); if (self.option.callBack(rs) !== false) { self.hide(); } }).text(this.option.btns[1]); $(".ipu-poppicker-btn-cancel", this.holder).click(function () { self.hide(); }).text(this.option.btns[0]); }; /** * 组件默认配置项 * * @cfg {Object} defaultOption * @cfg {String} defaultOption.template html结构 * @cfg {String} defaultOption.pickerTemplate 内容dom选择器 * @cfg {Object[]} defaultOption.data 选择项数据 * @cfg {String} defaultOption.data.text 子项展示文本 * @cfg {*} defaultOption.data.value 子项值 * @cfg {Object[]} defaultOption.data.data 有更多层级时,此属性存放下一层级的数据 * @cfg {Number} defaultOption.layer=1 数据层数 * @cfg {String[]} defaultOption.btns=['取消', '确认'] 按钮文本 * @cfg {Function} defaultOption.callBack=null 回调函数 */ PopPicker.prototype.defaultOption = { template: '<div class="ipu-poppicker">' + '<div class="ipu-poppicker-header">' + '<button class="ipu-btn ipu-btn-s ipu-poppicker-btn-cancel">取消</button>' + '<button class="ipu-btn ipu-btn-s ipu-poppicker-btn-ok">确定</button>' + '</div>' + '<div class="ipu-poppicker-body">' + '</div>' + '</div>', pickerTemplate: '<div class="ipu-picker">' + '<div class="ipu-picker-selectbox"></div>' + '<ul></ul>' + '</div>', data: [], // 数据 layer: 1, // 数据层级 btns: ['取消', '确认'], callBack: function () { // 选择数据时的回调函数 } }; /** * 设置选择项数据 * * @param{[Object]} data 选择项数组 * @param {String} data.text 每个选择项的文本 * @param {[Object]} data.data 如果有多层选择的话,应该有一个data属性 */ PopPicker.prototype.setData = function (data) { this.pickers[0].setItems(data); }; /** * 显示选择器 * * @param callBack */ PopPicker.prototype.show = function (callBack) { if (callBack) { this.option.callBack = callBack; } this.mask.show(); this.holder.addClass("ipu-current"); }; /** * 隐藏选择器 */ PopPicker.prototype.hide = function () { this.mask.close(); this.holder.removeClass("ipu-current"); }; /** * 获取用户选择的项,如果配置项layer为1,则直接返回选择项, * 否则返回一个数组返回每层选中的项 * */ PopPicker.prototype.getSelectItems = function () { if (this.option.layer == 1) { return this.pickers[0].getSelectedItem(); } else { var rs = []; for (var i = 0; i < this.option.layer; i++) { rs.push(this.pickers[i].getSelectedItem()); } return rs; } }; // 应该移除callback参数,提取出业成一个工具方法 PopPicker.prototype.createMask = function (callback) { var self = this; var element = document.createElement('div'); element.classList.add("ipu-picker-backup"); //element.addEventListener($.EVENT_MOVE, $.preventDefault); element.addEventListener('click', function () { self.hide(); }); var mask = [element]; mask._show = false; mask.show = function () { mask._show = true; element.setAttribute('style', 'opacity:1'); document.body.appendChild(element); return mask; }; mask._remove = function () { if (mask._show) { mask._show = false; element.setAttribute('style', 'opacity:0'); setTimeout(function () { var body = document.body; element.parentNode === body && body.removeChild(element); }, 350); } return mask; }; mask.close = function () { if (mask._show) { if (callback) { if (callback() !== false) { mask._remove(); } } else { mask._remove(); } } }; return mask; }; /** * @member ipuUI * 生成PopPicker实例,参数信息见{@link PopPicker#method-constructor} * * @param {String} slt * @param {Object} option * @returns {PopPicker} */ ipuUI.popPicker = function (option) { return new PopPicker(option); }; })(ipuUI || window, jQuery); (function (ipuUI, $) { /** * @class * 进度条 * * @example * <!-- 组件html --> * <div class="ipu-progress "> * <span class="ipu-progressbar"></span> * </div> * * * @constructor 不能直接访问该类,调用 {@link ipuUI#progressBar}生成实例 * @param {String|jqueryObj} slt * jquery选择器字符串或jquery对象,用来查找要被组件初始化化的dom * @param {Object} option 组件配置参数,默认配置见 {@link #cfg-defaultOption} */ function ProgressBar(id, option) { this.id = id; this.level = option.level; this.progress = option.progress; this.progressBar = $(id).eq(0); if (option.progress != null) { this.setProgress(this.progress); } if (option.level != null) { this.setLevel(this.level); } } /** * @cfg defaultOption 刷新组件默认配置 * @cfg {default|warning|highlight|success} defaultOption.level='default' 级别,显示颜色 * @cfg {Number} defaultOption.progress=null 当前进度百分比 * */ /** * 设置百分进度 * * @param {Number} pro */ ProgressBar.prototype.setProgress = function (pro) { if (pro < 0 || pro > 100) return; $(this.progressBar.find(".ipu-progressbar")).css("transform", "translate3d(" + (-(100 - pro)) + "%, 0px, 0px)"); this.progress = pro; }; /** * 获取百分进度 * * @returns {Number|*} */ ProgressBar.prototype.getProgress = function () { return this.progress; }; /** * 设置进度条级别 * * @param {default | success | highlight | warning} level */ ProgressBar.prototype.setLevel = function (level) { if (level == "default") { $(this.progressBar).removeClass("ipu-progressbar-success ipu-progressbar-hightlight ipu-progressbar-warning"); $(this.progressBar).addClass("ipu-progress"); } else if (level == "success") { $(this.progressBar).removeClass("ipu-progressbar-highlight ipu-progressbar-warning"); $(this.progressBar).addClass("ipu-progressbar-success"); } else if (level == "highlight") { $(this.progressBar).removeClass("ipu-progressbar-success ipu-progressbar-warning"); $(this.progressBar).addClass("ipu-progressbar-highlight"); } else if (level == "warning") { $(this.progressBar).removeClass("ipu-progressbar-success ipu-progressbar-highlight"); $(this.progressBar).addClass("ipu-progressbar-warning"); } }; /** * @member ipuUI * 生成PopPicker实例,参数信息见{@link ProgressBar#method-constructor} * * @param {String} slt * @param {Object} option * @returns {ProgressBar} */ ipuUI.progressBar = function (slt, option) { return new ProgressBar(slt, option); }; })(ipuUI || window, jQuery); // 设置上下条件长度,或计算函数 // 处理resize的问题,用户主动调用refresh?? // 底部启用或停用时,应该刷新组件iscroll高度 // 顶部正在加载时,自动停止底端加载状态,停用底部加载,停用底部加载时,可以不隐藏,变性成显示不见,或者隐藏,然后修改iscroll参数 (function (ipuUI, $, iScroll) { /** * @class * 通过IScroll.js实现上拉下拉加载 * * @example * <!-- 组件html结构,最外层div应有一个固定的高度,会在此元素上初始化iScroll --> * <div> * <div class="ipu-refresh-wrapper"> * <!-- 此处组件初始化后,会添加上拉html --> * <div class="refresh-content"> * 内容区... * </div> * <!-- 此处组件初始化后,会添加下拉html --> * </div> * </div> * * @uses IScroll.js * * @constructor 不能直接访问该类,调用ipuUI.refresh(slt, option)生成实例 * @param {String|JqueryObj|Dom} slt * jquery选择器字符串或jquery对象,用来查找要被组件初始化化的dom * @param {Object} option * 组件参数 */ function Refresh(slt, option) { this.option = $.extend({}, this.defaultOption, option); this.el = $(slt).get(0); this._initBottomAndTop(); var me = this; this.iScrollOption = { onScrollMove: function (e) { if (me.topEnable && !me.topLoading) { // 顶部是松手才加载 if (this.y >= me.topPullOffset && !me.topEl.hasClass('ipu-refresh-toload')) { // 达到刷新距离,更新显示状态 me.topEl.addClass('ipu-refresh-toload'); } else if (this.y < me.topPullOffset && me.topEl.hasClass('ipu-refresh-toload')) { // 从达到刷新距离更新为未达到距离,更新显示状态 me.topEl.removeClass('ipu-refresh-toload'); } } me._checkBottomLoading(); // 底部加载条件和顶部条件不一样,只要滚动离底部一定高度就开始加载 me.goTop = this.y > me.topPullOffset; // 记录是否位于顶部位置,以便刷新后可以回到此位置 }, onBeforeScrollEnd: function () { // 一定是用户拖动触发,在滚动结束前应该触发 me._checkTopLoading(); me._checkBottomLoading(); }, onScrollEnd: function () { // 这个事件可能由非用户拖动时触发,可能是拖动惯性导致,所有顶部不应该处理,但顶部不管是否惯性,位置条件满足即触发 if (me.topLoading && this.y < this.minScrollY && me.goTop) { me.iScroll.scrollTo(0, this.minScrollY, 0); } me._checkBottomLoading(); // 在beforend执行还不够,还在要end执行 }, onRefresh: function () { // 刷新时,若顶部加载还在进行,且当前显示的顶部加载,则继续显示,否则刷新后会消失顶部加载,这里代码没有考虑重用了,应该可以做一步提取 if (me.topLoading) { // 如果顶部在加载,则刷新的时候,设置最小顶部距离,显示顶部加载状态 this.minScrollY = this.minScrollY + me.topPullOffset; } } }; this.iScrollOption = $.extend({}, this.option.iScrollOption, this.iScrollOption); this.iScroll = new iScroll(this.el, this.iScrollOption); this._checkContentLoading(); } /** * @cfg defaultOption 刷新组件默认配置 * @cfg {Function} defaultOption.bottomLoadFun=null 上拉时,触发底加载的响应函数 * @cfg {Function} defaultOption.topLoadFun=null 下拉时,触发顶部加载的响应函数 * @cfg {Boolean} defaultOption.initEnableTop=true 初始化时,是否启用顶部加载功能 * @cfg {Boolean} defaultOption.initEnableBottom=true 初始化时,是否启用底部加载功能 * @cfg {String} defaultOption.bottomLoadHtml=... 底部加载时显示的html片段,不建议变动 * @cfg {String} defaultOption.topLoadHtml=... 顶部加载时显示的html片段,不建议变动 * @cfg {Number} defaultOption.bottomAddLen=0 距离底部多远时,触发底部加载 * */ Refresh.prototype.defaultOption = { bottomLoadFun: null, // 底部加载处理函数 topLoadFun: null, // 顶部加载处理函数 initEnableTop: true, // 初始时启用刷新,有时用户并不想启用 initEnableBottom: true, // 初始时启用加载更多,用时用户并不想启用 bottomLoadHtml: '<div class="ipu-refresh-bottom"><span class="ipu-refresh-loading"></span></div>', // 默认底部加载显示内容 topLoadHtml: '<div class="ipu-refresh-top"><span class="ipu-refresh-loading"></span><div class="ipu-refresh-arrow"></div></div>', // 默认顶部加载显示内容,最上层节点class有下面三个阶段变化 // 默认阶段,不是顶部加载状态时,且拖动时未达到加载距离,无特殊class,移除ipu-refresh-top-loading // 拖动达到加载距离,则增加class:ipu-refresh-toload // 加载中,则增加class:ipu-refresh-top-loading,移除class:ipu-refresh-toload bottomAddLen: 0, // 底部提前加载距离,单位px iScrollOption: {} // 主要是用来接收外面一些函数,不能传递回调的相关函数如refresh,也可在本地函数调用完后,再调用参数的函数,不推荐 }; Refresh.prototype._initBottomAndTop = function () { this.scrollEl = $(">.ipu-refresh-wrapper", this.el); this.bottomEl = $(this.option.bottomLoadHtml).appendTo(this.scrollEl); this.topEl = $(this.option.topLoadHtml).prependTo(this.scrollEl); this.topPullOffset = this.topEl.outerHeight(); this.bottomPullOffset = this.bottomEl.outerHeight() + this.option.bottomAddLen; // 增加100;最好配一个额外参数 /** @property {Boolean} 顶部是否加载中 */ this.topLoading = false; // 顶部正在载加载 /** @property {Boolean} 底部是否加载中 */ this.bottomLoading = false; // 底部正在加载 /** @property {Boolean} 底部是否可加载 */ this.bottomEnable = this.option.initEnableBottom && !!this.option.bottomLoadFun; /** @property {Boolean} 顶部是否可加载 */ this.topEnable = this.option.initEnableTop && !!this.option.topLoadFun; this.goTop = false; // 用来处理,因为iScroll使用momentum(惯性), 导致有时顶部显示不正确问题,true表示顶部显示加载条 this.enableBottom(this.bottomEnable); this.enableTop(this.topEnable); }; // 检查是否需要底部加载 Refresh.prototype._checkBottomLoading = function () { if (this.bottomEnable) { if (this.iScroll.y < this.iScroll.maxScrollY + this.bottomPullOffset) { this._startBottomLoading(); } } }; Refresh.prototype._checkTopLoading = function () { if (this.topEnable) { if (this.topEl.hasClass('ipu-refresh-toload')) { this._startTopLoading(); } } }; // 检查内容是否超出容器高度,未超出时,自动调用底部加载 Refresh.prototype._checkContentLoading = function () { if (this.bottomEnable) { if (this.iScroll.maxScrollY >= -this.bottomPullOffset) { // 此处要计算底端的高度 this._startBottomLoading(); } } }; // 开始底部加载 Refresh.prototype._startBottomLoading = function () { if (!this.bottomLoading) { this.bottomLoading = true; this.option.bottomLoadFun(); // 刷新当前索引加载更多的数据 } }; // 开始顶部加载 Refresh.prototype._startTopLoading = function () { if (!this.topLoading) { this.topLoading = true; this.topEl.removeClass('ipu-refresh-toload').addClass('ipu-refresh-top-loading'); this.iScroll.minScrollY = this.iScroll.minScrollY + this.topPullOffset; this.option.topLoadFun(); // 刷新当前索引加载更多的数据 } }; /** * 结束底部加载,defaultOption.bottomLoadFun中处理完加载后,最后调用此方法 */ Refresh.prototype.endBottomLoading = function () { this.bottomLoading = false; this.refresh(); }; /** * 结束顶部加载,defaultOption.topLoadFun中处理完加载后,最后调用此方法 */ Refresh.prototype.endTopLoading = function () { this.topEl.removeClass('ipu-refresh-top-loading'); this.topLoading = false; // this.iScroll.scrollTo(0, 0); // 刷新加载则应该回到顶部,待测试确认 this.refresh(); }; /** * 是否启动顶部加载功能 * * @param {Boolean} enable */ Refresh.prototype.enableTop = function (enable) { this.topEnable = enable; if (enable) { this.topEl.show(); } else { this.topEl.hide(); } }; /** * 是否启用底部加载功能 * * @param {Boolean} enable */ Refresh.prototype.enableBottom = function (enable) { this.bottomEnable = enable; if (enable) { this.bottomEl.show(); } else { this.bottomEl.hide(); } }; /** * 在内容发生变化时,但是又不是因为顶部加载或底部加载导致的,此时调用此方法刷新IScroll */ Refresh.prototype.refresh = function () { this.iScroll.refresh(); this._checkContentLoading(); }; /** * @member ipuUI * 生成PopPicker实例,参数信息见{@link Refresh#method-constructor} * * @param {String} slt * @param {Object} option * @returns {Refresh} */ ipuUI.refresh = function (slt, optoins) { return new Refresh(slt, optoins); }; })(ipuUI || window, jQuery, iScroll); // Tab (function (ipuUI, $) { /** * @class * tab切换组件功能实现 * * @example * <!-- html结构 --> * <div class="ipu-tab"> <!-- 如果class中添加 ipu-tab-fixed,则可固定头部,此时需要父元素的高度是确定的 --> * <ul class="ipu-tab-title ipu-tab-title-link"> <!-- 页头有 ipu-tab-title-link 和 ipu-tab-title-button两种样式 --> * <li>热门推荐</li> * <li class="ipu-current">全部表情</li> <!-- class为ipu-current为默认选中项 --> * <li>表情</li> * <li>表情</li> * </ul> * <div class="ipu-tab-body"> * <ul class="ipu-tab-body-wrapper"> * <li>自定义内容</li> * <li class="">选项2内容</li> * <li class="">选项3内容</li> * <li class="">选项4内容</li> * </ul> * </div> * </div> * * * @constructor 不能直接访问该类,调用ipuUI.tab(slt, option)生成实例 * @param {string|jqueryObj} slt * jquery选择器字符串或jquery对象,用来查找要被组件初始化化的dom * @param {object} option * 组件参数 */ function Tab(slt, option) { this.el = $(slt).get(0); this.titleItems = $(".ipu-tab-title:first>li", this.el); this.bodyWrapper = $(".ipu-tab-body-wrapper:first", this.el); this.contentItems = $(">li", this.bodyWrapper); this.option = $.extend({}, this.defaultOption, option); this.itemSize = this.contentItems.size(); this.fixed = $(this.el).is(".ipu-tab-fixed"); // 是否为固定高度的 var that = this; this.titleItems.each(function (index) { $(this).click(function () { that.show(index); }); }); var index = this.titleItems.filter(".ipu-current").index(); if (index == -1) { index = 0; } this.show(index); } /** * 默认配置项 * @cfg defaultOption = { callBack: function (index) {}}; * @cfg defaultOption.callBack 切换tab项时的回调函数,参数为显示的项索引 */ Tab.prototype.defaultOption = { callBack: function (index) { } }; /** * 显示第几项内容 * @param {number} index 要显示的项索引 */ Tab.prototype.show = function (index) { if (this.fixed) { var move = -index * 100 + "%"; this.bodyWrapper.css("transform", "translate3d(" + move + ", 0, 0)"); } this.contentItems.eq(index).addClass("ipu-current").siblings().removeClass("ipu-current"); this.titleItems.eq(index).addClass("ipu-current").siblings().removeClass("ipu-current"); this._end(index); }; Tab.prototype._end = function (index) { this.lastIndex = this.currentIndex; this.currentIndex = index; if (this.option.callBack) { this.option.callBack(index, this.lastIndex); } }; ipuUI.tab = function (slt, option) { return new Tab(slt, option); }; })(ipuUI || window, jQuery); // 初始化代码 jQuery(function () { // 添加一个touchstart空函数,让:active样式可以在ios上生效 // 新版默认不需要事件好像也可生效 jQuery("body").on("touchstart", function (e) { }); // 处理ios点击延迟问题 FastClick.attach(document.body); }); return ipuUI; } // todo:可以添加一个和其它库的适配处理, // 这里假设第三方库,jquery,iScroll,Hammer的史称已经固定 if (typeof define === "function" && define.amd) { define(['jquery', 'iScroll', 'Hammer', 'FastClick'], function (jQuery, iScroll, Hammer, FastClick) { return window.ipuUI = setup(jQuery, iScroll, Hammer, FastClick); }); } else { window.ipuUI = setup(window.jQuery, window.iScroll, window.Hammer, window.FastClick); } })();