UNPKG

lu2

Version:

Simple and flexible UI component library based on native HTML and JavaScript

2,161 lines (1,874 loc) 228 kB
/** * @Radio.js * @author xunxuzhang * @version * Created: 15-06-17 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { factory().init(); } }(this, function () { //require('plugin/jquery'); /** * 模拟单选效果 * 只针对IE8-浏览器进行处理 * 状态关键字为checked */ var CHECKED = 'checked'; /** * jQuery模式下的扩展方法, * 只针对IE8-浏览器进行处理 * 根据当前单复选框的选中态toggle类名checked * @example * $('[type=radio]').propMatch(); */ $.fn.propMatch = function() { if (!window.addEventListener) { var _match = function(element) { element = $(element); if (element.prop(CHECKED)) { element.attr(CHECKED, CHECKED); } else { element.removeAttr(CHECKED); } // 触发重绘,使相邻选择器渲染 element.parent().addClass('z').toggleClass('i_i'); }; if ($(this).length == 1 && $(this).attr('type') == 'radio') { // 单选框含有组的概念 // 也就是a选中的时候,b则变成非选中态 // 因此,类名切换不能只关注当前元素 var name = $(this).attr('name'); $('input[type=radio][name='+ name +']').each(function() { _match(this); }); } else { $(this).each(function() { _match(this); }); } return $(this); } }; return { // 使用面对对象模式暴露给其他方法使用,例如Checkbox.js // 当然,你也可以直接使用$().propMatch()方法 match: function(control) { control.propMatch(); }, init: function() { if (!window.addEventListener && !window.initedRadio) { // 压缩的时候少36字节 var selector = 'input[type=radio]'; // 全局委托 $(document.body).delegate(selector, 'click', function() { $(this).propMatch(); }); // 首次页面载入,对页面所有单选框(根据是否选中态)进行初始化 $(selector).propMatch(); // 防止多次初始化 window.initedRadio = true; } } }; })); /** * @Checkbox.js * @author xunxuzhang * @version * Created: 15-06-18 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { factory().init(); } }(this, function () { if (!$().propMatch && window.console) { console.error('need Radio.js'); } return { init: function() { if (!window.addEventListener && !window.initedCheckbox && $().propMatch) { var selector = 'input[type=checkbox]'; $(document.body).delegate(selector, 'click', function() { $(this).propMatch(); }); // 首次页面载入,对页面所有复选框(根据是否选中态)进行初始化 $(selector).propMatch(); // 防止多次初始化 window.initedCheckbox = true; } } }; })); /** * @Placeholder.js * @author xunxuzhang * @version * Created: 15-06-17 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Placeholder = factory(); } }(this, function () { var PLACEHOLDER = 'placeholder'; /** * 针对IE7-IE9浏览器的placeholder占位符效果 * 支持jQuery用法,以及模块化使用 * @使用示例 * $('input').placeholder(); * or * new Placeholder($('input')); */ $.fn[PLACEHOLDER] = function() { return $(this).each(function(index, element) { var placeholder = $(this).data(PLACEHOLDER); if (placeholder) { placeholder.visibility(); } else { new Placeholder($(this)); } }); }; var Placeholder = function(el) { // IE10+不处理 if (typeof history.pushState == 'function') return this; // 默认所有的占位符元素 if (!el) { el = $('['+ PLACEHOLDER +']').placeholder(); return this; } var self = this; // 暴露的元素 this.el = {}; this.el.target = el; // 创建 // 1. 得到对应的id, 没有则赋值 var attribute = el.attr(PLACEHOLDER), id = el.attr('id'); // 需要有placeholder属性值,以及不能重复实例化 if (!attribute || el.data(PLACEHOLDER)) return this; if (!id) { // 如果没有id, 设置随机id,使placeholder所在的label元素与之关联 id = PLACEHOLDER + (Math.random() + '').replace('.', ''); el.attr('id', id); } // 2. 对应的label元素 var elePlaceholder = $('<label class="ui-'+ PLACEHOLDER +'" for='+ id +'>'+ attribute +'</label>').hide(); // 插入 var isHide = el.is(':visible') == false; // 当input是内联时候,后置; 块状时候,下置 if (isHide == false) { if (el.css('display') != 'block') { elePlaceholder.insertAfter(el); } else { $('<div aria-hidden="true"></div>').append(elePlaceholder).insertAfter(el); } // 事件 // IE9 使用input事件 // IE7/IE8使用propertychange事件 if (window.addEventListener) { el.on('input', function() { self.visibility(); }); } else { el.get(0).attachEvent('onpropertychange', function(event) { if (event && event.propertyName == "value") { self.visibility(); } }); } // 元素上存储对应的实例对象 el.data(PLACEHOLDER, self); // 实例存储 this.el.placeholder = elePlaceholder; // 显示或隐藏 this.visibility(); } else { $(document.body).click(function() { // 全局委托 if (el.is(':visible') && !el.data(PLACEHOLDER)) { self = new Placeholder(el); } // 实时同步文本框的显隐状态 if (el.data(PLACEHOLDER)) { self.visibility(); } }); // 遇到DOM载入后fadeIn效果,导致初始化时候,元素还是隐藏的,加个定时器看看 setTimeout(function() { self.visibility(); }, 200); } }; Placeholder.prototype.position = function() { var target = this.el.target, label = this.el.placeholder; // 创建label元素 var mt = parseInt(target.css('marginTop')) || 0, mr = parseInt(target.css('marginRight')) || 0, mb = parseInt(target.css('marginBottom')) || 0, ml = parseInt(target.css('marginLeft')) || 0, width = target.width(), outerWidth = target.outerWidth(), outerHeight = target.outerHeight(); // 块状 if (label.parent().attr('aria-hidden')) { // 块状 label.css({ width: width, marginLeft: ml, marginTop: (outerHeight + mb) * -1 }); } else { // 内联 label.css({ width: width, marginTop: mt, marginLeft: (outerWidth + mr) * -1 }); } // if (target.attr('id') == 'spanSearch') location.href += '#' + label.get(0).outerHTML; return this; }; Placeholder.prototype.visibility = function() { var target = this.el.target; if (target.is(':visible') == false || $.trim(target.val())) { this.hide(); } else { this.show(); } return this; }; Placeholder.prototype.show = function() { if (this.el.placeholder) { this.el.placeholder.html(this.el.target.attr(PLACEHOLDER)).show(); // 显示同时重定位,各种复杂场景都能适应 this.position(); } return this; }; Placeholder.prototype.hide = function() { if (this.el.placeholder) this.el.placeholder.hide(); return this; }; return Placeholder; }));/** * @Follow.js * @author xunxuzhang * @version * Created: 15-06-25 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Follow = factory(); } }(this, function () { /** * 绝对定位元素的定位效果 * 针对所有浏览器 * 自动含边界判断 * 可用在DropDown, Tips等组件上 * 支持链式调用和模块化调用 * @example * $().follow(trigger, options); * new Follow(trigger, target, options); * 文档见:http://www.zhangxinxu.com/wordpress/?p=1328 position()方法 **/ $.fn.follow = function(trigger, options) { var defaults = { offsets: { x: 0, y: 0 }, position: "4-1", //trigger-target edgeAdjust: true //边缘位置自动调整 }; var params = $.extend({}, defaults, options || {}); return $(this).each(function() { var target = $(this); if (trigger.length == 0) { return; } var pos, tri_h = 0, tri_w = 0, tri_l, tri_t, tar_l, tar_t, cor_l, cor_t, tar_h = target.data("height"), tar_w = target.data("width"), st = $(window).scrollTop(), sl = $(window).scrollLeft(), off_x = parseInt(params.offsets.x, 10) || 0, off_y = parseInt(params.offsets.y, 10) || 0, mousePos = this.cacheData; //缓存目标对象高度,宽度,提高鼠标跟随时显示性能,元素隐藏时缓存清除 if (!tar_h) { tar_h = target.outerHeight(); } if (!tar_w) { tar_w = target.outerWidth(); } pos = trigger.offset(); tri_h = trigger.outerHeight(); tri_w = trigger.outerWidth(); tri_l = pos.left; tri_t = pos.top; var funMouseL = function() { if (tri_l < 0) { tri_l = 0; } else if (tri_l + tri_h > $(window).width()) { tri_l = $(window).width() - tri_w; } }, funMouseT = function() { if (tri_t < 0) { tri_t = 0; } else if (tri_t + tri_h > $(document).height()) { tri_t = $(document).height() - tri_h; } }; var arrLegalPos = ["4-1", "1-4", "5-7", "2-3", "2-1", "6-8", "3-4", "4-3", "8-6", "1-2", "7-5", "3-2"], align = params.position, alignMatch = false, strDirect; $.each(arrLegalPos, function(i, n) { if (n === align) { alignMatch = true; return; } }); // 如果没有匹配的对齐方式,使用默认的对齐方式 if (!alignMatch) { align = defaults.position; } var funDirect = function(a) { var dir = "bottom"; //确定方向 switch (a) { case "1-4": case "5-7": case "2-3": { dir = "top"; break; } case "2-1": case "6-8": case "3-4": { dir = "right"; break; } case "1-2": case "8-6": case "4-3": { dir = "left"; break; } case "4-1": case "7-5": case "3-2": { dir = "bottom"; break; } } return dir; }; //居中判断 var funCenterJudge = function(a) { if (a === "5-7" || a === "6-8" || a === "8-6" || a === "7-5") { return true; } return false; }; var funJudge = function(dir) { var totalHeight = 0, totalWidth = 0; if (dir === "right") { totalWidth = tri_l + tri_w + tar_w + off_x; if (totalWidth > $(window).width()) { return false; } } else if (dir === "bottom") { totalHeight = tri_t + tri_h + tar_h + off_y; if (totalHeight > st + $(window).height()) { return false; } } else if (dir === "top") { totalHeight = tar_h + off_y; if (totalHeight > tri_t - st) { return false; } } else if (dir === "left") { totalWidth = tar_w + off_x; if (totalWidth > tri_l) { return false; } } return true; }; //此时的方向 strDirect = funDirect(align); //边缘过界判断 if (params.edgeAdjust) { //根据位置是否溢出显示界面重新判定定位 if (funJudge(strDirect)) { //该方向不溢出 (function() { if (funCenterJudge(align)) { return; } var obj = { top: { right: "2-3", left: "1-4" }, right: { top: "2-1", bottom: "3-4" }, bottom: { right: "3-2", left: "4-1" }, left: { top: "1-2", bottom: "4-3" } }; var o = obj[strDirect], name; if (o) { for (name in o) { if (!funJudge(name)) { align = o[name]; } } } })(); } else { //该方向溢出 (function() { if (funCenterJudge(align)) { var center = { "5-7": "7-5", "7-5": "5-7", "6-8": "8-6", "8-6": "6-8" }; align = center[align]; } else { var obj = { top: { left: "3-2", right: "4-1" }, right: { bottom: "1-2", top: "4-3" }, bottom: { left: "2-3", right: "1-4" }, left: { bottom: "2-1", top: "3-4" } }; var o = obj[strDirect], arr = []; for (name in o) { arr.push(name); } if (funJudge(arr[0]) || !funJudge(arr[1])) { align = o[arr[0]]; } else { align = o[arr[1]]; } } })(); } } // 是否变换了方向 var strNewDirect = funDirect(align), strFirst = align.split("-")[0]; //确定left, top值 switch (strNewDirect) { case "top": { tar_t = tri_t - tar_h; if (strFirst == "1") { tar_l = tri_l; } else if (strFirst === "5") { tar_l = tri_l - (tar_w - tri_w) / 2; } else { tar_l = tri_l - (tar_w - tri_w); } break; } case "right": { tar_l = tri_l + tri_w ; if (strFirst == "2") { tar_t = tri_t; } else if (strFirst === "6") { tar_t = tri_t - (tar_h - tri_h) / 2; } else { tar_t = tri_t - (tar_h - tri_h); } break; } case "bottom": { tar_t = tri_t + tri_h; if (strFirst == "4") { tar_l = tri_l; } else if (strFirst === "7") { tar_l = tri_l - (tar_w - tri_w) / 2; } else { tar_l = tri_l - (tar_w - tri_w); } break; } case "left": { tar_l = tri_l - tar_w; if (strFirst == "2") { tar_t = tri_t; } else if (strFirst === "6") { tar_t = tri_t - (tar_w - tri_w) / 2; } else { tar_t = tri_t - (tar_h - tri_h); } break; } } if (params.edgeAdjust && funCenterJudge(align)) { var winWidth = $(window).width(), winHeight = $(window).height(); // 是居中定位 // 变更的不是方向,而是offset大小 // 偏移处理 if (align == '7-5' || align == '5-7') { // 左右是否超出 if (tar_l - sl < 0.5 * winWidth) { // 左半边,判断左边缘 if (tar_l - sl < 0) { tar_l = sl; } } else if (tar_l - sl + tar_w > winWidth) { tar_l = winWidth + sl - tar_w; } } else { // 上下是否超出 if (tar_t - st < 0.5 * winHeight) { // 左半边,判断左边缘 if (tar_t - st < 0) { tar_t = st; } } else if (tar_t - st + tar_h > winHeight) { tar_t = winHeight + st - tar_h; } } } if (strNewDirect == "top" || strNewDirect == "left") { tar_l = tar_l - off_x; tar_t = tar_t - off_y; } else { tar_l = tar_l + off_x; tar_t = tar_t + off_y; } //浮动框显示 target.css({ position: "absolute", left: Math.round(tar_l), top: Math.round(tar_t) }).attr('data-align', align); // 层级 // z-index自动最高 var zIndex = target.css('zIndex') * 1 || 19, maxIndex = zIndex; $('body').children().each(function() { var z, ele = this, el = $(ele); if (ele !== target[0] && el.css('display') !== 'none' && (z = el.css('zIndex') * 1)) { maxIndex = Math.max(z, maxIndex); } }); if (zIndex < maxIndex) { target.css('zIndex', maxIndex + 1); } }); }; var Follow = function(trigger, target, options) { target.follow(trigger, options); }; Follow.prototype.hide = function() { target.remove(); }; return Follow; }));/** * @Tab.js * @author xunxuzhang * @version * Created: 15-06-12 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Tab = factory(); } }(this, function () { /** * 定制级极简的选项卡 * 选项卡效果本质上就是单选框切换效果 * 因此,状态类名使用.checked * @使用示例 * new Tab($('#container > a'), { * callback: function() {} * }); * 或者包装器用法 * $('#container > a').tab({ * callback: function() {} * }); */ var STATE = 'checked'; $.fn.eqAttr = function(key) { key = key || "data-rel"; // 获得属性对应的值 var value = $(this).attr(key); if (!value) return $(); // 当作id来处理 var target = $("#" + value); if (target.length) return target; // 否则当作className处理 return $("." + value); }; // 根据属性获得对应的元素 $.fn.tab = function(options) { if (!$(this).data('tab')) { $(this).data('tab', new Tab($(this), options)); } }; $.queryString = function(key, value, str) { // 有则替换,无则加勉 var href = (str || location.href).split('#')[0], root = '', hash = location.hash; // 查询数组 var arr_query = href.split('?'), arr_map = []; if (arr_query.length > 1) { if (arr_query[1] != '') { arr_map = $.grep(arr_query[1].split('&'), function(query) { return query.split('=')[0] != key; }); } root = arr_query[0] + '?' + arr_map.join('&') + '&'; root = root.replace('?&', '?'); } else { root = href + '?'; } return root + key + '=' + value + hash; }; var Tab = function(el, options) { // 选项卡元素,点击主体 el = $(el); // 如果元素没有,打道回府 if (el.length == 0) return; // 下面3语句参数获取 options = options || {}; var defaults = { eventType: 'click', index: 'auto', // 'auto' 表示根据选项卡上的状态类名,如.checked获取index值 history: true, // 是否使用HTML5 history在URL上标记当前选项卡位置,for IE10+ callback: $.noop }; var params = $.extend({}, defaults, options); // 很重要的索引 var indexTab = params.index; // 首先,获得索引值 el.each(function(index) { if (typeof indexTab != 'number' && $(this).hasClass(STATE)) { indexTab = index; } $(this).data('index', index); }); if (typeof indexTab != 'number') { indexTab = 0; } // 获得选项卡对应的面板元素 // 两种情况 // 1. 直接切换 // 2. ajax并载入 // 目前先第一种 // 则直接就是页面元素 // 事件gogogo el.on(params.eventType, function(event) { if ($(this).hasClass(STATE)) { if (event.isTrigger) { // 让可能不匹配的content面板的checked状态同步 // 一般出现在选项卡查询关键字变化后 // 因为项目原因,内部面板默认display跟nav不匹配 // 后注释是因为发现体验比较差 /*el.each(function() { $(this).eqAttr().removeClass(STATE); }); $(this).eqAttr().addClass(STATE);*/ params.callback.call(this, this, $(), $(), $()); } return; } // 选项卡样式变变变 // 类名变化 var targetTab = $(this).addClass(STATE), // 要移除类名的选项卡 resetTab = el.eq(indexTab).removeClass(STATE); // 面板的变化 var targetPanel = targetTab.eqAttr().addClass(STATE), resetPanel = resetTab.eqAttr().removeClass(STATE); // 索引参数变化 indexTab = targetTab.data('index'); // 回调方法 params.callback.call(this, targetTab, resetTab, targetPanel, resetPanel); if (/^(:?javas|#)/.test(this.getAttribute('href'))) { var rel = targetTab.attr('data-rel'); if (params.history && history.replaceState) { history.replaceState(null, document.title, $.queryString('targetTab', rel)); } else if (params.history) { location.hash = '#targetTab=' + rel; } event.preventDefault(); } }); // 默认就来一发 $(function() { el.eq(indexTab).trigger(params.eventType); }); // 暴露的属性或方法,供外部调用 this.el = { tab: el }; this.params = params; }; return Tab; })); /** * @Select.js * @author xunxuzhang * @version * Created: 15-06-18 * **/ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { factory().init(); } }(this, function () { /** * 模拟下拉框效果 * 只针对all浏览器进行处理 * 基于原生的<select>元素生成 * */ /* * 支持jQuery API调用 * @example * $('select').selectMatch(); * 如果不依赖seajs,则载入改JS就会页面所有下拉UI初始化 */ $.fn.selectMatch = function(options) { // 常量变量 var SELECT = 'select', SELECTED = 'selected', DISABLED = 'disabled', ACTIVE = 'active', REVERSE = 'reverse'; // 默认参数 var defaults = { prefix: 'ui-', // 一些UI样式类名的前缀 trigger: ['change'] // 触发原始下拉框的事件,默认只触发change事件 }; // 参数合并 var params = $.extend({}, defaults, options || {}); // 通用前缀 var PREFIX = params.prefix + SELECT, joiner = params.prefix.replace(/[a-z]/gi, ''); // 根据下拉框获得相关数据的私有方法 var _get = function(el) { var selectedIndex = 0, htmlOptions = ''; // 遍历下拉框的options选项 el.find('option').each(function(index) { var arrCl = [PREFIX + joiner + 'datalist'+ joiner +'li', this.className]; if (this[SELECTED]) { selectedIndex = index; arrCl.push(SELECTED); } if (this[DISABLED]) { arrCl.push(DISABLED); } htmlOptions = htmlOptions + '<a href="javascript:" class="'+ arrCl.join(' ') +'" data-index='+ index +'>'+ this.innerHTML +'</a>'; }); return { index: selectedIndex, html: htmlOptions } } return $(this).each(function(index, element) { var sel = $(this).hide().data(SELECT); if (!sel) { // 如果没有关联的模拟下拉元素生成 // 创建新的模拟下拉元素 sel = $('<div></div>').on('click', 'a._', function() { if ($(element).prop(DISABLED)) return false; // 显示与隐藏 sel.toggleClass(ACTIVE); // 边界判断 if (sel.hasClass(ACTIVE)) { var ul = sel.children('div[id]'), overflow = ul.offset().top + ul.outerHeight() > Math.max($(document.body).height(), $(window).height()); sel[overflow? 'addClass': 'removeClass'](REVERSE); // 滚动与定位 var arrData = sel.data('scrollTop'), selected = ul.find('.' + SELECTED); // 严格验证 if (arrData && arrData[1] == selected.attr('data-index') && arrData[2] == selected.text()) { ul.scrollTop(arrData[0]); // 重置 sel.removeData('scrollTop'); } } else { sel.removeClass(REVERSE); } }).on('click', 'a[data-index]', function(event, istrigger) { var indexOption = $(this).attr('data-index'), scrollTop = $(this).parent().scrollTop(); // 下拉收起 sel.removeClass(ACTIVE); // 存储可能的滚动定位需要的数据 sel.data('scrollTop', [scrollTop, indexOption, $(this).text()]); // 修改下拉选中项 $(element).find('option').eq(indexOption).get(0)[SELECTED] = true; // 更新下拉框 $(element).selectMatch(params); // 回调处理 $.each(params.trigger, function(i, eventType) { $(element).trigger(eventType, [istrigger]); }); }); // 存储对象 $(this).data(SELECT, sel); // 载入元素 $(this).after(sel); // 点击页面空白要隐藏 $(document).mouseup(function(event) { var target = event.target; if (target && sel.hasClass(ACTIVE) && sel.get(0) !== target && sel.get(0).contains(target) == false) { sel.removeClass(ACTIVE).removeClass(REVERSE); } }); } // 根据当前下拉元素,重新刷新 // 0. 获得我们需要的一些数据 var data = _get($(this)), option = $(this).find('option').eq(data.index); // 1. 与select元素匹配的类名, 以及宽度 sel.attr('class', element.className + ' ' + PREFIX).width($(this).outerWidth()); // 2. 全新的按钮元素 var id = ('id_' + Math.random()).replace('0.', ''); var button = '<a href="javascript:" class="'+ PREFIX + joiner + 'button _" data-target="'+ id +'">'+ '<span class="'+ PREFIX + joiner + 'text">' + option.html() + '</span>'+ '<i class="'+ PREFIX + joiner + 'icon"></i></a>'; // 3. 全新的列表 var datalist = '<div id="'+ id +'" class="'+ PREFIX + joiner + 'datalist">'+ data.html +'</div>'; // 4. 刷新 sel.html(button + datalist); }); }; return { init: function(el, options) { el = el || $('select'); el.selectMatch(options); } }; })); /** * @Drop.js * @author xunxuzhang * @version * Created: 15-06-30 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Drop = factory(); } }(this, function (require, exports, module) { // require if (typeof require == 'function') { require('common/ui/Follow'); } else if (!$().follow) { if (window.console) { console.error('need Follow.js'); } return {}; } /* * 元素的下拉显示 */ /* * 支持jQuery API调用和模块化调用两种 * @example * $('trigger').drop(target, options); * or * new Drop(trigger, target, options) */ $.fn.drop = function(target, options) { return $(this).each(function() { var drop = $(this).data('drop'); if (!drop) { $(this).data('drop', new Drop($(this), target, options)); } }); }; // 实例方法 var Drop = function(trigger, target, options) { var defaults = { eventType: 'null', // 触发元素显示的事件,‘null’直接显示;‘hover’是hover方法;‘click’是点击显示, selector: '', // 新增,实现点击或hover事件的委托实现 offsets: { x: 0, y: 0 }, edgeAdjust: true, position: '7-5', onShow: $.noop, onHide: $.noop }; var params = $.extend({}, defaults, options || {}); // 元素暴露给实例 this.el = {}; this.el.trigger = trigger; this.el.target = target; // 偏移 this.offsets = params.offsets; // 回调 this.callback = { show: params.onShow, hide: params.onHide }; // 位置 this.position = params.position; // 边缘调整 this.edgeAdjust = params.edgeAdjust; // 实例的显示状态 this.display = false; var drop = this; switch (params.eventType) { case 'null': { this.show(); break; } case 'hover': { // hover处理需要增加延时 var timerHover; // 同时,从trigger移动到target也需要延时,因为两者可能有间隙,不能单纯移出就隐藏 var timerHold; trigger.delegate(params.selector, 'mouseenter', function() { drop.el.trigger = $(this); // 显示的定时器 timerHover = setTimeout(function() { drop.show(); }, 150); // 去除隐藏的定时器 clearTimeout(timerHold); }); trigger.delegate(params.selector, 'mouseleave', function() { // 清除显示的定时器 clearTimeout(timerHover); // 隐藏的定时器 timerHold = setTimeout(function() { drop.hide(); }, 200); }); if (!target.data('dropHover')) { target.hover(function() { // 去除隐藏的定时器 clearTimeout(timerHold); }, function() { // 隐藏 timerHold = setTimeout(function() { drop.hide(); }, 100); }); target.data('dropHover', true); } break; } case 'click': { trigger.delegate(params.selector, 'click', function(event) { drop.el.trigger = $(this); // 点击即显示 if (drop.display == false) { drop.show(); } else { drop.hide(); } event.preventDefault(); }); break; } } // 点击页面空白区域隐藏 if (params.eventType == 'null' || params.eventType == 'click') { $(document).mousedown(function(event) { var clicked = event && event.target; if (!clicked || !drop || drop.display != true) return; var tri = drop.el.trigger.get(0), tar = drop.el.target.get(0); if (clicked != tri && clicked != tar && tri.contains(clicked) == false && tar.contains(clicked) == false) { drop.hide(); } }); } // 窗体尺寸改变生活的重定位 $(window).resize(function() { drop.follow(); }); }; Drop.prototype.follow = function() { if (this.display = true && this.el.trigger.css('display') != 'none') { this.el.target.follow(this.el.trigger, { offsets: this.offsets, position: this.position, edgeAdjust: this.edgeAdjust }); } }; Drop.prototype.show = function() { // target需要在页面中 var target = this.el.target; if (target && $.contains(document.body, target.get(0)) == false) { $('body').append(target); } this.display = true; target.css('position', 'absolute').show(); // 定位 this.follow(); // onShow callback if ($.isFunction(this.callback.show)) { this.callback.show.call(this, this.el.trigger, this.el.target); } }; Drop.prototype.hide = function() { this.el.target.hide(); this.display = false; // onHide callback if ($.isFunction(this.callback.hide)) { this.callback.hide.call(this, this.el.trigger, this.el.target); } }; return Drop; })); /** * @DropList.js * @author xinxuzhang * @version * Created: 15-06-30 * 2017-03-13 使也支持委托 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.DropList = factory(); } }(this, function (require) { // require if (typeof require == 'function') { require('common/ui/Drop'); } else if (!$().drop) { if (window.console) { console.error('need Drop.js'); } return {}; } /* * 下拉列表 * @trigger 触发下拉的按钮元素 * @data 下拉列表数据,数组,例如: * [{ * id: 1, * value: '所有评论', * selected: true * }, { * id: 2, * value: '未审核评论', * disabled: true * }, { * id: 3, * value: '通过评论' * }] * 也可以是Function函数,表示数据是动态呈现的 */ var prefixDropList = 'ui-droplist-', SELECTED = 'selected', DISABLED = 'disabled'; var DropList = function(trigger, data, options) { var defaults = { eventType: 'click', // 触发元素显示的事件,‘null’直接显示;‘hover’是hover方法;‘click’是点击显示;其他为手动显示与隐藏 offsets: { x: 0, y: 0 }, selector: '', width: '', onShow: $.noop, onHide: $.noop, onSelect: $.noop // this为当前点击的列表元素,支持两个参数,第一个参数为列表元素对应的数据(纯对象),第二个是当前实例对象 }; var params = $.extend({}, defaults, options || {}); // 列表元素 var target = $('<div></div>').addClass(prefixDropList + 'x').css('width', params.width); // 下拉三角 var arrow = trigger.find('.' + prefixDropList + 'arrow'); // 下拉三角元素的HTML代码 var htmlArrow = arrow.length? arrow.get(0).outerHTML: ''; // trigger除去三角以外的HTML代码 var htmlLeft = $.trim(trigger.html().replace(htmlArrow, '')); // 创建列表 // 不管怎样,data需要是个有数据的数组 // 如果不符合条件,我们只能反馈没有数据 if ($.isArray(data) && data.length == 0) { data = [{ value: '没有数据', disabled: true }]; } else if ($.isArray(data) && params.selector == '') { // 1. 如果列表数据含有selected: true, 则自动重置trigger里面的内容 // 2. 如果列表数据没有selected: true, 则根据trigger的内容信息确定哪个数据是selected var hasSelected = false, matchIndex = -1; $.each(data, function(index, obj) { if (obj.selected) { trigger.html(obj.value + htmlArrow); hasSelected = true; } if ($.trim(obj.value) == htmlLeft) { matchIndex = index; } }); if (hasSelected == false && matchIndex != -1) { data[matchIndex].selected = true; } } // 根据data组装列表 var _get = function(arr) { var html = ''; $.each(arr, function(index, obj) { // 选中列表的类名 var clSelected = ''; if (obj[SELECTED]) clSelected = ' ' + SELECTED; // 禁用态和非禁用使用标签区分 if (obj[DISABLED] != true) { html = html + '<a href="javascript:;" class="'+ prefixDropList +'li'+ clSelected +'" data-index="'+ index +'">'+ obj.value +'</a>'; } else { html = html + '<span class="'+ prefixDropList +'li">'+ obj.value +'</span>'; } }); return html; }; // 下拉面板 trigger.drop(target, { eventType: params.eventType, offsets: params.offsets, selector: params.selector, onShow: function() { var _data = this.data; if ($.isFunction(data)) { _data = data(); // 更新 drop.data = _data; } target.html(_get(_data)); params.onShow.apply(this, [trigger, target]); }, onHide: params.onHide }); var drop = trigger.data('drop'); drop.data = data; // 列表元素内容,事件 target.delegate('a', 'click', function() { var index = $(this).attr('data-index') * 1; if ($(this).hasClass(SELECTED) == false) { // 除去所有的selected if ($.isArray(data)) { $.each(data, function(i, obj) { if (obj[SELECTED]) { obj[SELECTED] = null; } if (i == index) { obj[SELECTED] = true; } }); // 缓存新的数据 drop.data = data; trigger.html($(this).html() + htmlArrow); } params.onSelect.call(this, drop.data[index], drop); } drop.hide(); }); return drop; }; return DropList; })); /** * @DropPanel.js * @author xinxuzhang * @version * Created: 15-07-01 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.DropPanel = factory(); } }(this, function (require) { // require if (typeof require == 'function') { require('common/ui/Drop'); } else if (!$().drop) { if (window.console) { console.error('need Drop.js'); } return {}; } /* * 下拉面板 * @trigger 触发下拉的按钮元素 * @options 面板数据以及下拉参数,例如: *{ * title: '删除评论', * content: '删除后,您的粉丝在文章中将无法看到该评论。', * buttons: [{}, {}] * } */ var prefixGlobal = 'ui-', joiner = '-', prefixDropPanel = 'ui-dropanel-', SELECTED = 'selected', DISABLED = 'disabled'; // 关闭SVG var svg = window.addEventListener? '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><path d="M116.152,99.999l36.482-36.486c2.881-2.881,2.881-7.54,0-10.42 l-5.215-5.215c-2.871-2.881-7.539-2.881-10.42,0l-36.484,36.484L64.031,47.877c-2.881-2.881-7.549-2.881-10.43,0l-5.205,5.215 c-2.881,2.881-2.881,7.54,0,10.42l36.482,36.486l-36.482,36.482c-2.881,2.881-2.881,7.549,0,10.43l5.205,5.215 c2.881,2.871,7.549,2.871,10.43,0l36.484-36.488L137,152.126c2.881,2.871,7.549,2.871,10.42,0l5.215-5.215 c2.881-2.881,2.881-7.549,0-10.43L116.152,99.999z"/></svg>': ''; var DropPanel = function(trigger, options) { var defaults = { title: '', content: '', buttons: [{}, {}], width: 'auto', eventType: 'click', // 触发元素显示的事件,‘null’直接显示;‘hover’是hover方法;‘click’是点击显示;其他为手动显示与隐藏 offsets: { x: 0, y: 0 }, onShow: $.noop, onHide: $.noop }; var params = $.extend({}, defaults, options || {}); // 面板容器 var target = $('<div></div>').addClass(prefixDropPanel + 'x'); if (/\d/.test(params.width)) { target.width(params.width); } // 创建轻弹框面板的各个元素 var el = {}; // title el.title = $('<h5></h5>').addClass(prefixDropPanel + 'title').html(params.title); // close button el.close = $('<a></a>').attr({ href: 'javascript:;' }).html(svg).addClass(prefixDropPanel + 'close').click(function() { drop.hide(); }); el.content = $('<div></div>').addClass(prefixDropPanel + 'content').html(params.content); // footer el.footer = $('<div></div>').addClass(prefixDropPanel + 'footer'); // 按钮 $.each(params.buttons, function(i, btn) { // 避免btn为null等值报错 btn = btn || {}; // 按钮的默认参数 var type = i? (btn.type || ''): (btn.type || 'warning'), value = i? (btn.value || '取消'): (btn.value || '确定'), events = btn.events || { click: function() { drop.hide(); } }; // 按钮的类名值 var cl = prefixGlobal +'button'; if (type) cl = cl + ' ' + cl + joiner + type; el.footer.append(el['button' + i] = $('<a href="javascript:;" class="'+ cl +'">'+ value + '</a>').bind(events) ); }); // 组装 target.append(el.title).append(el.close).append(el.content).append(el.footer); // 基于drop的面板显隐效果 trigger.drop(target, { eventType: params.eventType, offsets: params.offsets, onShow: params.onShow, onHide: params.onHide }); var drop = trigger.data('drop'); for (key in el) { drop.el[key] = el[key]; } return drop; }; return DropPanel; })); /** * @Tips.js * @author xunxuzhang * @version * Created: 15-06-25 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Tips = factory(); } }(this, function (require) { if (typeof require == 'function') { require('common/ui/Follow'); } else if (!$().follow) { if (window.console) { console.error('need Follow.js'); } return {}; } /** * 黑色tips效果 * 支持jQuery包装器调用以及模块化调用 * @example * $().tips(options); * new Tips(el, options) **/ // 类名变量 var CL = 'ui-tips', prefixTips = CL + '-'; $.fn.tips = function(options) { return $(this).each(function() { if (!$(this).data('tips')) { $(this).data('tips', new Tips($(this), options)); } }); }; var Tips = function(el, options) { var defaults = { attribute: 'title', eventType: 'hover', // 'click', 'null' content: '', align: 'center', delay: 100, onShow: $.noop, onHide: $.noop }; if (typeof el == 'string') { el = $(el); } if (!el || !el.length) { return this; } var params = $.extend({}, defaults, options || {}); // 创建元素 var trigger = el, before, after; // 分两种情况 // 1. IE9+ 纯CSS(伪元素)实现,使用类名ui-tips; IE7-IE8 内入两元素,水平居中效果JS实现 // 2. 所有浏览器都绝对定位跟随, 手动绑定 // 对于第一种情况 if (trigger.hasClass(CL)) { // 如果元素含有title, 需要转换成data-title var title = trigger.attr('title') || trigger.attr('data-title'); if (title) { trigger.attr('data-title', title) // 原本的title移除 .removeAttr('title'); } // 对于IE7和IE8浏览器,我们需要插入两个元素 if (!window.addEventListener) { before = $('<span class="'+ prefixTips +'before"></span>').html(title); after = $('<i class="'+ prefixTips +'after"></i>'); // 前后插入 trigger.prepend(before); trigger.append(after); // 水平居中必须 before.css('margin-left', before.outerWidth() * -0.5); after.css('margin-left', after.outerWidth() * -0.5); // 其余交给CSS } trigger.data('tips', true); return; } var self = this, tips, timer; var _content = function() { var content = params.content; if (!content) { content = trigger.attr(params.attribute); // 如果是title, 移除避免重复交互 if (params.attribute == 'title') { content = content || trigger.data('title'); if (content) { trigger.data('title', content) } trigger.removeAttr('title'); } } return content; } // 暴露参数 this.el = { trigger: trigger, tips: tips }; this.callback = { show: params.onShow, hide: params.onHide }; this.align = params.align; // 事件走起 if (params.eventType == 'hover') { trigger.hover(function() { var content = _content(); timer = setTimeout(function() { self.show(content); }, params.delay); }, function() { clearTimeout(timer); self.hide(); }); } else if (params.eventType == "click") { trigger.click(function() { self.show(_content()); }); $(document).mouseup(function(event) { var target = event.target, dom = trigger.get(0); if (self.display == true && dom != target && dom.contains(target) == false && self.el.tips.get(0).contains(target) == false) { self.hide(); } }); } else { this.show(_content()); } return this; }; Tips.prototype.show = function(content) { if (!content) return this; var trigger = this.el.trigger, tips = this.el.tips, before, after; // 一些参数 if (tips) { tips.show(); before = tips.find('span').html(content); after = tips.find('i'); } else { tips = $('<div></div>').addClass(prefixTips + 'x'); // 两个元素 before = $('<span class="'+ prefixTips +'before"></span>').html(content); after = $('<i class="'+ prefixTips +'after"></i>'); $(document.body).append(tips.append(before).append(after)); } // 水平偏移大小 var offsetX = 0, position = '5-7'; if (this.align == 'left') { offsetX = -0.5 * before.width() + parseInt(before.css('padding-left')) || 0; } else if (this.align == 'right') { offsetX = 0.5 * before.width() - parseInt(before.css('padding-right')) || 0; } else if (this.align == 'rotate') { position = '6-8' } else if (typeof this.align == 'number') { offsetX = this.align; } tips.addClass(prefixTips + this.align); if (this.align != 'rotate') after.css({ // 尖角的位置永远对准元素 left: offsetX }); // 定位 tips.follow(trigger, { offsets: { x: offsetX, y: 0 }, position: position, // trigger-target edgeAdjust: false // 边界溢出不自动修正 }); // 显示的回调 this.callback.show.call(trigger, tips); this.el.tips = tips; this.display = true; return this; }; Tips.prototype.hide = function() { if (this.el.tips) { this.el.tips.hide(); // 移除回调 this.callback.hide.call(this.el.trigger, this.el.tips); } this.display = false; return this; }; Tips.prototype.init = function() { $('.' + CL).tips(); // 全局委托,因为上面的初始化对于动态创建的IE7,IE8浏览器无效 $(document).mouseover(function(event) { var target = event && event.target; if (target && $(target).hasClass(CL) && !$(target).data('tips')) { $(target).tips(); } }); return this; } return Tips; })); /** * @LightTip.js * @author xunxuzhang * @version * Created: 15-06-25 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.LightTip = factory(); } }(this, function () { /** * 顶部的请提示效果 * 支持jQuery $全局调用以及模块化调用 * @example * $.lightTip.success(message); * $.lightTip.error(message); * new LightTip().success(message); * new LightTip().error(message); **/ // 类名变量 var CL = 'ui-lightip', prefixTips = CL + '-'; $.lightTip = (function() { // 下面是两个私有方法 var _create = function(message) { return _position($('<div class="'+ CL +'"></div>') .html('<i class="'+ prefixTips +'icon">&nbsp;</i><span class="'+ prefixTips +'text">' + message + '</span>') .appendTo($(document.body))); }, _position = function(tips) { var zIndex = tips.css('z-index'), newZIndex = 0; $('body').children().each(function() { newZIndex = Math.max(zIndex, parseInt($(this).css('z-index')) || 0); }); if (zIndex != newZIndex) { tips.css('z-index', newZIndex); } return tips.css({ left: '50%', marginLeft: tips.outerWidth() * -0.5 }); }; return { success: function(message, time) { var lightTips = _create(message).addClass(prefixTips + 'success'); setTimeout(function() { lightTips.fadeOut(function() { lightTips.remove(); lightTips = null; }); }, time || 3000); return lightTips; }, error: function(message, time) { var lightTips = _create(message).addClass(prefixTips + 'error'); setTimeout(function() { lightTips.fadeOut(function() { lightTips.remove(); lightTips = null; }); }, time || 3000); return lightTips; } } })(); var LightTip = function() { this.el = {}; return this; }; LightTip.prototype.success = function(message) { this.el.container = $.lightTip.success(message); return this; }; LightTip.prototype.error = function(message) { this.el.container = $.lightTip.error(message); return this; }; return LightTip; })); /** * @ErrorTip.js * @author xunxuzhang * @version * Created: 15-07-01 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.ErrorTip = factory(); } }(this, function (require, exports, module) { if (typeof require == 'function') { require('common/ui/Follow'); } else if (!$().follow) { if (window.console) { console.error('need Follow.js'); } return {}; } /** * 红色的tips错误提示效果 * 支持jQuery包装器调用以及模块化调用 * @example * $().tips(options); * new Tips(el, options) **/ // 类名变量 var CL = 'ui-tips', prefixTips = CL + '-'; $.fn.errorTip = function(content, options) { var defaults = { unique: true, align: 'center', onShow: $.noop, onHide: $.noop }; var params = $.extend({}, defaults, options || {}); // 要显示的字符内容 if ($.isFunction(content)) content = content(); if (typeof content != 'string') return this; return $(this).each(function(index, element) { // 单实例模式下,我们只处理第一个包装器元素 if (params.unique == true && index > 0) { return; } // 一些元素 var trigger = $(this), tips, before, after; if (params.unique == true && window.errorTip) { tips = errorTip.data('trigger', trigger); } else if (params.unique == false && trigger.data('errorTip')) { tips = trigger.data('errorTip'); } else { tips = $('<div class="'+ prefixTips +'x '+ prefixTips +'error"></div>').html( '<span class="'+ prefixTips +'before"></span><i class="'+ prefixTips +'after"></i>' ); $(document.body).append(tips.append(before).append(after)); // 如果是唯一模式,全局存储 if (params.unique == true) { window.errorTip = tips.data('trigger', trigger); } else { trigger.data('errorTip', tips); } // 隐藏 var hide = function() { if (tips.css('display') != 'none') { tips.hide(); params.onHide.call((tips.data('trigger') || trigger).removeClass('error'), tips); } }; // 任何键盘操作,点击,或者拉伸都会隐藏错误提示框 $(document).bind({ keydown: function(event) { // ctrl/shift键等不隐藏 if (event.keyCode != 16 && event.keyCode != 17) { hide(); } }, mousedown: function(event) { var activeElement = document.activeElement, activeTrigger = tips.data('trigger'), activeTarget = event.target; if (activeElement && activeTrigger && activeElement == activeTarget && activeElement == activeTrigger.get(0) && // 这个与Datalist.js关联 activeTrigger.data('focus') == false ) { return; } hide(); } }); $(window).bind({ resize: hide }); } // 显示提示 tips.show(); // 两个元素 before = tips.find('span'); after = tips.find('i'); // 修改content内容 before.html(content); // 水平偏移大小 var offsetX = 0; if (params.align == 'left') { offsetX = -0.5 * before.width() + parseInt(before.css('padding-left')) || 0; } else if (params.align == 'right') { offsetX = 0.5 * before.width() - parseInt(before.css('padding-right')) || 0; } else if (typeof params.align == 'number') { offsetX = params.align; } after.css({ // 尖角的位置永远对准元素 left: offsetX }); // 定位 tips.follow(trigger, { align: params.align, position: "5-7", //trigger-target edgeAdjust: false // 边界溢出不自动修正 }); // 显示的回调 params.onShow.call(trigger.addClass('error valided'), tips); }); }; var ErrorTip = function(el, content, options) { el.errorTip(content, options); this.el = { trigger: el }; this.cl = CL; return this; }; return ErrorTip; })); /** * @Range.js * @author xunxuzhang * @version * Created: 15-07-20 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Range = factory(); } }(this, function (require) { if (typeof require == 'function') { require('common/ui/Tips'); } else if (!$().tips) { if (window.console) { console.error('need Tips.js'); } return {}; } /** * 基于HTML原生range范围选择框的模拟选择框效果 * 兼容IE7+ * min/max/step */ var CL = 'ui-range', rangePrefix = CL + '-'; $.fn.range = function(options) { return $(this).each(function() { if (!$(this).data('range')) { $(this).data('range', new Range($(this), options)); } }); }; var Range = function(el, options) { var self = this; // el就是type类型为range的input元素 var defaults = { tips: function(value) { return value; } }; var params = $.extend({}, defaults, options || {}); // 一些属性值获取 var min = el.attr('min') || 0, max = el.attr('max') || 100, step = el.attr('step') || 1; // 一些元素的创建 // 容器元素有一个类名直接取自input元素,宽度啊,margin什么的,直接就交给CSS了 var container = $('<div></div>').attr('class', el.attr('class')).addClass(CL); // 轨道元素 var track = $('<div></div>').addClass(rangePrefix + 'track'); // 中间的圈圈 var thumb = $('<a></a>').addClass(rangePrefix + 'thumb'); // 前置插入 el.before(container); // 如果元素没宽度,则使用el计算的宽度 if (container.width() == 0) { container.width(el.width()); } // 组装 track.append(thumb); container.append(track); // 事件 container.click(function(event) { var target = event && event.target; if (target && target != thumb.get(0)) { // 根据点击的位置在圈圈的左侧还是右侧判断选择值的增减 var distance = event.clientX - (thumb.offset().left - $(window).scrollLeft()) - thumb.width() / 2; // 根据点击的距离,判断值 self.value(el.val() * 1 + (max - min) * distance / $(this).width()); } }); // 拖动 var posThumb = {}; thumb.mousedown(function(event) { posThumb.x = event.clientX; posThumb.value = el.val() * 1; // 黑色提示 if ($.isFunction(params.tips)) { if (self.tips) { self.tips.show(params.tips.call(el, posThumb.value)); } else { thumb.tips({ eventType: 'null', content: params.tips(posThumb.value) }); self.tips = thumb.data('tips'); } } $(this).addClass('active'); }); $(document).mousemove(function(event) { if (typeof posThumb.x == 'number') { var distance = event.clientX - posThumb.x; // 根据移动的距离,判断值 self.value(posThumb.value + (max - min) * distance / container.width()); if (self.tips) { self.tips.show(params.tips.call(el, el.val())); } event.preventDefault(); } }); $(document).mouseup(function(event) { posThumb.x = null; posThumb.value = null; thumb.removeClass('active'); if (self.tips) { self.tips.hide(); } }); // 全局 this.num = { min: +min, max: +max, step: +step }; this.el = { input: el, container: container, track: track, thumb: thumb }; this.obj = {}; // 初始化 this.value(); return this; }; Range.prototype.value = function(value) { var input = this.el.input, oldvalue = input.val(); // 一些值 var max = this.num.max, min = this.num.min, step = this.num.step; if (!value) { oldvalue = value; value = $.trim(input.val()); } // 区域范围判断以及值是否合法的判断 if (value > max || (max - value) < step / 2) { value = max; } else if (value == '' || value < min || (value - min) < step / 2) { value = min; } else { // 寻找最近的合法value值 value = min + Math.round((value - min) / step) * step; } input.val(value); this.position(); if (value != oldvalue) { input.trigger('change'); } return this; }; Range.prototype.position = function() { var input = this.el.input, value = input.val(); // 一些值 var max = this.num.max, min = this.num.min, step = this.num.step; // 计算百分比 this.el.track.css('borderLeftWidth', this.el.container.width() * (value - min) / (max - min)); return this; }; return Range; })); /** * @Color.js * @author xunxuzhang