/** * * @authors yutent (yutent@doui.cc) * @date 2016-09-21 01:36:29 * */ "use strict"; define(['yua', 'lib/drag', 'css!./skin/def'], function(yua){ if(window.layer){ return window.layer } var layerDom = {}, layerObj = {}, unique = null, //储存当前打开的1/2/3类型的弹窗 lid = 0, defconf = { type: 1, // 弹窗类型 skin: 'def', //默认主题 icon: 1, //图标类型 background: '#fff', shade: true, //遮罩 shadeClose: false, //遮罩点击关闭弹窗 radius: '0px', //弹窗圆角半径 area: ['auto', 'auto'], title: '', //弹窗主标题(在工具栏上的) menubar: true, //是否显示菜单栏 content: '', // 弹窗的内容 fixed: false, //是否固定不可拖拽 offset: null, //弹窗出来时的坐标, 为数组,可有4个值,依次是 上右下左 btns: ['确定', '取消'], //弹窗的2个按钮的文字 yes: close, //确定按钮对应的回调 no: close, //取消按钮对应的回调 success: null //弹窗初始化完成时的回调 }; function uuid(){ return 'layer-' + (++lid) } function close(id){ if(typeof id !== 'string' && typeof id !== 'number'){ return console.error(new Error('要关闭的layer实例不存在')) } if(/^\$wrap\-/.test(id) || layerObj['$wrap-' + id]){ try { id = (layerObj['$wrap-' + id] ? '$wrap-' : '') + id; //未显示过,忽略 if(!layerObj[id].show){ return } layerObj[id].parentElem.replaceChild(layerObj[id].wrap, layerDom[id][1]) layerObj[id].show = false }catch(err){} }else{ try { document.body.removeChild(layerDom[id][1]) document.body.removeChild(layerDom[id][0]) unique = null }catch(err){} delete layerDom[id] delete yua.vmodels[id] } } function reapeat(str, num){ var idx = 0, result = '' while(idx < num){ result += str idx++ } return result } function fixOffset(val){ if(!val && val !== 0){ return 'auto' }else{ return val } } var __constructor = function(conf){ if(conf){ this.ready(conf).append().show() } }, __layer = { alert: function(msg, conf){ if(typeof conf === 'function'){ conf = {yes: conf, no: conf} }else if(typeof conf === 'object'){ conf = conf }else{ conf = {} } conf.icon = 6 conf.content = msg return __layer.open(conf) }, confirm: function(msg, conf){ if(typeof conf === 'function'){ conf = {yes: conf} }else if(typeof conf === 'object'){ conf = conf }else{ conf = {} } conf.type = 2 conf.icon = 8 conf.content = msg return __layer.open(conf) }, msg: function(msg, conf){ if(typeof conf !== 'object'){ var tmp = conf conf = {timeout: 2500} if(typeof tmp === 'number'){ conf.icon = tmp } } if(!conf.hasOwnProperty('timeout')){ conf.timeout = 2500 } conf.specialMode = true;//特殊模式 conf.content = '

' + msg + '

' conf.type = 7 conf.fixed = true conf.shade = false conf.menubar = false conf.radius = '5px' return __layer.open(conf) }, loading: function(style, time, cb){ style = style >>> 0 if(typeof time === 'function'){ cb = time; time = 0 } else{ time = time >>> 0 if(typeof cb !== 'function'){ cb = yua.noop } } return __layer.open({type: 6, load: style, yes: cb, timeout: time, menubar: false, background: 'none', fixed: true}) }, tips: function(msg, elem, conf){ if(!(elem instanceof HTMLElement)){ return console.error(new Error('tips类型必须指定一个目标容器')) } if(typeof conf !== 'object'){ var tmp = conf conf = {timeout: 2500} if(typeof tmp === 'number'){ conf.icon = tmp } } if(!conf.hasOwnProperty('timeout')){ conf.timeout = 2500 } if(!conf.background){ conf.background = 'rgba(0,0,0,.5)' } if(!conf.color){ conf.color = '#fff' } conf.$elem = elem conf.content = msg conf.type = 5; conf.icon = 0; conf.fixed = true; conf.shade = false; conf.menubar = false; return __layer.open(conf) }, prompt: function(msg, callback){ if(typeof callback !== 'function'){ return console.error('argument [callback] requires a function, but ' + (typeof callback) + ' given') } var conf = { type: 3, icon: 7, prompt: '', title: msg, content: '', yes: function(id){ callback(id, yua.vmodels[id].prompt) } } return __layer.open(conf) }, use: function(skin, callback){ require(['css!./skin/' + skin], callback) }, close: close, open: function(conf){ if(typeof conf === 'string'){ conf = '$wrap-' + conf if(!layerObj[conf]){ throw new Error('layer实例不存在') }else{ //只能显示一个实例 if(layerObj[conf].show){ return conf } layerObj[conf].show = true if(!yua.vmodels[conf]){ yua(layerObj[conf].obj.init) } yua.scan(layerDom[conf][1]) layerObj[conf].obj.show() layerObj[conf].parentElem.appendChild(layerDom[conf][1]) layerObj[conf].parentElem.replaceChild(layerDom[conf][1], layerObj[conf].wrap) return conf } }else{ return new __constructor(conf).init.$id } }, version: '0.0.2-base' }; /*type: { // 弹窗类型对应的id值 1: 'alert', 2: 'confirm', 3: 'prompt', 4: 'iframe', 5: 'tips', 6: 'loading', 7: 'msg', }*/ __constructor.prototype = { dot: { //loading的子元素数量 0: 4, 1: 9, 2: 2, 3: 3, 4: 2, 5: 5, 6: 5, 7: 5 }, timeout: null, create: function(){ var layBox = document.createElement('div'), coverBox = document.createElement('div'); coverBox.className = 'do-layer-cover type-' + this.init.type // 允许点击遮罩关闭弹层时, 添加控制器 if(this.init.shadeClose){ coverBox.setAttribute(':controller', this.cInit.$id) coverBox.setAttribute(':click', 'close(\'' + this.init.$id + '\')') } layBox.className = 'do-layer skin-' + this.init.skin + (this.init.type === 5 && ' active' || '') + ' type-' + ((!this.init.specialMode && this.init.type === 7) ? 'unspecial' : this.init.type); //暂时隐藏,避免修正定位时,能看到闪一下 layBox.style.visibility = 'hidden' layBox.style.borderRadius = this.init.radius layBox.setAttribute(':controller', this.init.$id) //没有菜单栏, 且未禁止拖拽,则加上可拖拽属性 if(!this.init.menubar && !this.init.fixed){ layBox.setAttribute(':drag', '') layBox.setAttribute('data-limit', 'window') } //弹窗的宽高 var boxcss = '' if(this.init.area[0] !== 'auto'){ boxcss += 'width: ' + this.init.area[0] + ';' } if(this.init.area[1] !== 'auto'){ boxcss += 'height: ' + this.init.area[1] + ';' } layBox.innerHTML = this.getMenubar() + '
' + this.getCont() + '
' + this.getBtns() + (this.init.type === 5 && '' || '') return [this.init.shade ? coverBox : null, layBox] }, getCont: function(){ if(this.init.type === 6){ return this.getLoading(this.init.load) }else{ return this.getIcon() + '
' } }, getLoading: function(style){ return '
' + '' + reapeat('', this.dot[style]) + '' + '
' }, //获取窗口导航条 getMenubar: function(){ var html = '' if(this.init.menubar){ html += '
' + '
' } return html }, //获取窗口内容的图标 getIcon: function(){ if(this.init.icon === 0){ return '' } if(this.init.type < 4 || this.init.type === 5 || this.init.specialMode){ return '' } return '' }, // 获取窗口按钮 getBtns: function(){ if(this.init.type > 3){ return '' }else{ var html = '
'; if(this.init.type > 1){ html += '' + '' }else{ html += '' } html += '
' return html } }, append: function(){ //如果有已经打开的弹窗,则关闭 if(unique){ __layer.close(unique) } if(this.init.type < 4){ unique = this.init.$id } layerDom[this.init.$id] = this.create() if(layerDom[this.init.$id][0]){ document.body.appendChild(layerDom[this.init.$id][0]) //仅在允许点击遮罩时,初始化控制器,减少资源消耗 if(this.init.shadeClose){ yua(this.cInit) yua.scan(layerDom[this.init.$id][0]) } } document.body.appendChild(layerDom[this.init.$id][1]) yua(this.init) yua.scan(layerDom[this.init.$id][1]) return this }, show: function(){ var _this = this; setTimeout(function(){ var style = {visibility: '', background: _this.init.background}, css = getComputedStyle(layerDom[_this.init.$id][1]); if(_this.init.type === 5){ style.color = _this.init.color var $elem = yua(_this.init.$elem), ew = $elem.innerWidth(), ol = $elem.offset().left - document.body.scrollLeft, ot = $elem.offset().top - document.body.scrollTop; style.left = ol + (ew * 0.7) style.top = ot - parseInt(css.height) - 8 }else{ if(_this.init.offset){ style.top = fixOffset(_this.init.offset[0]) style.right = fixOffset(_this.init.offset[1]) style.bottom = fixOffset(_this.init.offset[2]) style.left = fixOffset(_this.init.offset[3]) }else{ style = yua.mix(style, { marginLeft: -parseInt(css.width) / 2, marginTop: -parseInt(css.height) / 2, }) } } yua(layerDom[_this.init.$id][1]).css(style) }, 4) if(this.init.success && typeof this.init.success === 'function'){ //弹窗成功的回调 this.init.success(this.init.$id) } // loading类型,回调需要自动触发 if(this.init.type > 3) { //大于0自动触发超时关闭 if(this.init.timeout > 0){ clearTimeout(this.timeout) this.timeout = setTimeout(function(){ clearTimeout(_this.timeout) __layer.close(_this.init.$id) // 为loading类型时,自动关闭同时触发回调 if(_this.init.type === 6){ _this.init.yes(_this.init.$id) } }, this.init.timeout) } else if(this.init.type === 6) { // loading类型, 非自动关闭时, 主动触发回调 this.init.yes(this.init.$id) } } }, ready: function(conf){ this.init = yua.mix({}, defconf, conf) if(!this.init.$id){ this.init.$id = uuid(); } if(this.init.icon > 17){ this.icon.icon = 17 } //base版没有iframe类型 if(this.init.type === 4){ this.icon.type = 7 } this.cInit = { $id: this.init.$id + '-c', close: this.init.shadeClose ? close : yua.noop }; return this } } yua.directive('layer', { priority: 1400, init: function(binding){ if(!binding.param || binding.param !== 'tips'){ binding.param = '' //去掉param,保证之后的逻辑处理正常 // 去掉:layer属性,避免二次扫描 binding.element.removeAttribute(binding.name) binding.element.style.display = 'none' } }, update: function(val){ if(!val){ return console.error(new Error(':layer指令格式不正确或无效属性. [' + this.name + '="' + this.expr) + '"]') } var _this = this, init = Object.assign({}, this.element.dataset); if(init.hasOwnProperty('area')){ init.area = init.area.split(',') } if(init.hasOwnProperty('offset')){ init.offset = init.offset.split(',') } if(init.hasOwnProperty('btns')){ init.btns = init.btns.split(',') } if(!this.param){ init.type = 7; init.$id = '$wrap-' + val; if(!init.hasOwnProperty('menubar')){ init.menubar = false; } var tmp = new __constructor().ready(init), elem = this.element.cloneNode(true); // 去掉隐藏之后,再放入content中 elem.style.display = '' tmp.init.content = elem.outerHTML; layerObj[tmp.init.$id] = {obj: tmp, parentElem: this.element.parentNode, wrap: this.element, show: false}; layerDom[tmp.init.$id] = tmp.create(); }else if(this.param === 'tips'){ var $elem = yua(this.element), ew = $elem.innerWidth(), ol = $elem.offset().left - document.body.scrollLeft, ot = $elem.offset().top - document.body.scrollTop, tipsBox = document.createElement('div'), tipsArrow = document.createElement('i'), tipsCont = document.createElement('div'); tipsBox.className = 'do-layer skin-def type-5' tipsBox.style.left = ol + (ew * 0.7) + 'px' if(init.background){ tipsBox.style.background = init.background tipsArrow.style.borderTopColor = init.background } if(init.color){ tipsBox.style.color = init.color } tipsCont.className = 'layer-content' tipsCont.textContent = val tipsArrow.className = 'arrow' tipsBox.appendChild(tipsCont) tipsBox.appendChild(tipsArrow) yua(document).bind('scroll', function(){ ol = $elem.offset().left - document.body.scrollLeft; ot = $elem.offset().top - document.body.scrollTop; tipsBox.style.left = ol + (ew * 0.7) + 'px' tipsBox.style.top = (ot - tipsBox.offsetHeight - 8) + 'px' }) $elem.bind('mouseenter', function(ev){ _this.element.parentNode.appendChild(tipsBox) clearTimeout(_this.showTime) clearTimeout(_this.hideTime) _this.showTime = setTimeout(function(){ tipsBox.style.top = (ot - tipsBox.offsetHeight - 8) + 'px' tipsBox.classList.add('active') }, 4) }) $elem.bind('mouseleave', function(){ _this.hideTime = setTimeout(function(){ clearTimeout(_this.hideTime) try{ _this.element.parentNode.removeChild(tipsBox) }catch(err){} }, 150) }) } } }) window.layer = __layer return __layer })