HEX
Server: nginx/1.28.1
System: Linux 10-41-63-61 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64
User: www (1001)
PHP: 7.4.33
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: //www/wwwroot/sr.dfhtcs.com/layui/modules/dropdown.js
/**
 
 @Name:dropdown 下拉菜单组件
 @License:MIT

 */

layui.define(['jquery', 'laytpl', 'lay'], function(exports){
  "use strict";
  
  var $ = layui.$
  ,laytpl = layui.laytpl
  ,hint = layui.hint()
  ,device = layui.device()
  ,clickOrMousedown = (device.mobile ? 'click' : 'mousedown')
  
  //模块名
  ,MOD_NAME = 'dropdown'
  ,MOD_INDEX = 'layui_'+ MOD_NAME +'_index' //模块索引名

  //外部接口
  ,dropdown = {
    config: {}
    ,index: layui[MOD_NAME] ? (layui[MOD_NAME].index + 10000) : 0

    //设置全局项
    ,set: function(options){
      var that = this;
      that.config = $.extend({}, that.config, options);
      return that;
    }
    
    //事件
    ,on: function(events, callback){
      return layui.onevent.call(this, MOD_NAME, events, callback);
    }
  }

  //操作当前实例
  ,thisModule = function(){
    var that = this
    ,options = that.config
    ,id = options.id;

    thisModule.that[id] = that; //记录当前实例对象

    return {
      config: options
      //重置实例
      ,reload: function(options){
        that.reload.call(that, options);
      }
    }
  }

  //字符常量
  ,STR_ELEM = 'layui-dropdown', STR_HIDE = 'layui-hide', STR_DISABLED = 'layui-disabled', STR_NONE = 'layui-none'
  ,STR_ITEM_UP = 'layui-menu-item-up', STR_ITEM_DOWN = 'layui-menu-item-down', STR_MENU_TITLE = 'layui-menu-body-title', STR_ITEM_GROUP = 'layui-menu-item-group', STR_ITEM_PARENT = 'layui-menu-item-parent', STR_ITEM_DIV = 'layui-menu-item-divider', STR_ITEM_CHECKED = 'layui-menu-item-checked', STR_ITEM_CHECKED2 = 'layui-menu-item-checked2', STR_MENU_PANEL = 'layui-menu-body-panel', STR_MENU_PANEL_L = 'layui-menu-body-panel-left'
  
  ,STR_GROUP_TITLE = '.'+ STR_ITEM_GROUP + '>.'+ STR_MENU_TITLE

  //构造器
  ,Class = function(options){
    var that = this;
    that.index = ++dropdown.index;
    that.config = $.extend({}, that.config, dropdown.config, options);
    that.init();
  };

  //默认配置
  Class.prototype.config = {
    trigger: 'click' //事件类型
    ,content: '' //自定义菜单内容
    ,className: '' //自定义样式类名
    ,style: '' //设置面板 style 属性
    ,show: false //是否初始即显示菜单面板
    ,isAllowSpread: true //是否允许菜单组展开收缩
    ,isSpreadItem: true //是否初始展开子菜单
    ,data: [] //菜单数据结构
    ,delay: 300 //延迟关闭的毫秒数,若 trigger 为 hover 时才生效
  };
  
  //重载实例
  Class.prototype.reload = function(options){
    var that = this;
    that.config = $.extend({}, that.config, options);
    that.init(true);
  };

  //初始化准备
  Class.prototype.init = function(rerender){
    var that = this
    ,options = that.config
    ,elem = options.elem = $(options.elem);
    
    //若 elem 非唯一
    if(elem.length > 1){
      layui.each(elem, function(){
        dropdown.render($.extend({}, options, {
          elem: this
        }));
      });
      return that;
    }

    //若重复执行 render,则视为 reload 处理
    if(!rerender && elem[0] && elem.data(MOD_INDEX)){;
      var newThat = thisModule.getThis(elem.data(MOD_INDEX));
      if(!newThat) return;

      return newThat.reload(options);
    };
    
    //初始化 id 参数
    options.id = ('id' in options) ? options.id : that.index;
    
    if(options.show) that.render(rerender); //初始即显示
    that.events(); //事件
  };
  
  //渲染
  Class.prototype.render = function(rerender){
    var that = this
    ,options = that.config
    ,elemBody = $('body')
    
    //默认菜单内容
    ,getDefaultView = function(){
      var elemUl = $('<ul class="layui-menu layui-dropdown-menu"></ul>');
      if(options.data.length > 0 ){
        eachItemView(elemUl, options.data)
      } else {
        elemUl.html('<li class="layui-menu-item-none">no menu</li>');
      }
      return elemUl;
    }
    
    //遍历菜单项
    ,eachItemView = function(views, data){
      //var views = [];
      layui.each(data, function(index, item){
        //是否存在子级
        var isChild = item.child && item.child.length > 0
        ,isSpreadItem = ('isSpreadItem' in item) ? item.isSpreadItem : options.isSpreadItem
        ,title = item.templet 
          ? laytpl(item.templet).render(item) 
        : (options.templet ? laytpl(options.templet).render(item) : item.title)
        
        //初始类型
        ,type = function(){
          if(isChild){
            item.type = item.type || 'parent';
          }
          if(item.type){
            return ({
              group: 'group'
              ,parent: 'parent'
              ,'-': '-'
            })[item.type] || 'parent';
          }
          return '';
        }();

        if(type !== '-' && (!item.title && !item.id && !isChild)) return;
        
        //列表元素
        var viewLi = $(['<li'+ function(){
          var className = {
            group: 'layui-menu-item-group'+ (
              options.isAllowSpread ? (
                isSpreadItem ? ' layui-menu-item-down' : ' layui-menu-item-up'
              ) : ''
            )
            ,parent: STR_ITEM_PARENT
            ,'-': 'layui-menu-item-divider'
          };
          if(isChild || type){
            return ' class="'+ className[type] +'"';
          }
          return '';
        }() +'>'
        
          //标题区
          ,function(){
            //是否超文本
            var viewText = ('href' in item) ? (
              '<a href="'+ item.href +'" target="'+ (item.target || '_self') +'">'+ title +'</a>'
            ) : title;
            
            //是否存在子级
            if(isChild){
              return '<div class="'+ STR_MENU_TITLE +'">'+ viewText + function(){
                if(type === 'parent'){
                  return '<i class="layui-icon layui-icon-right"></i>';
                } else if(type === 'group' && options.isAllowSpread){
                  return '<i class="layui-icon layui-icon-'+ (isSpreadItem ? 'up' : 'down') +'"></i>';
                } else {
                  return '';
                }
              }() +'</div>'
              
            }
            return '<div class="'+ STR_MENU_TITLE +'">'+ viewText +'</div>';
          }()
        ,'</li>'].join(''));
        
        viewLi.data('item', item);
        
        //子级区
        if(isChild){
          var elemPanel = $('<div class="layui-panel layui-menu-body-panel"></div>')
          ,elemUl = $('<ul></ul>');

          if(type === 'parent'){
            elemPanel.append(eachItemView(elemUl, item.child));
            viewLi.append(elemPanel);
          } else {
            viewLi.append(eachItemView(elemUl, item.child));
          }
        }

        views.append(viewLi);
      });
      return views;
    }
    
    //主模板
    ,TPL_MAIN = ['<div class="layui-dropdown layui-border-box layui-panel layui-anim layui-anim-downbit">'
    ,'</div>'].join('');
    
    //如果是右键事件,则每次触发事件时,将允许重新渲染
    if(options.trigger === 'contextmenu' || lay.isTopElem(options.elem[0])) rerender = true;
    
    //判断是否已经打开了下拉菜单面板
    if(!rerender && options.elem.data(MOD_INDEX +'_opened')) return;

    //记录模板对象
    that.elemView = $(TPL_MAIN);
    that.elemView.append(options.content || getDefaultView());
    
    //初始化某些属性
    if(options.className) that.elemView.addClass(options.className);
    if(options.style) that.elemView.attr('style', options.style);
    
    
    //记录当前执行的实例索引
    dropdown.thisId = options.id;
    
    //插入视图
    that.remove(); //移除非当前绑定元素的面板
    elemBody.append(that.elemView);
    options.elem.data(MOD_INDEX +'_opened', true);
    
    //坐标定位
    that.position();
    thisModule.prevElem = that.elemView; //记录当前打开的元素,以便在下次关闭
    thisModule.prevElem.data('prevElem', options.elem); //将当前绑定的元素,记录在打开元素的 data 对象中
    
    //阻止全局事件
    that.elemView.find('.layui-menu').on(clickOrMousedown, function(e){
      layui.stope(e);
    });

    //触发菜单列表事件
    that.elemView.find('.layui-menu li').on('click', function(e){
      var othis = $(this)
      ,data = othis.data('item') || {}
      ,isChild = data.child && data.child.length > 0;
      
      if(!isChild && data.type !== '-'){
        that.remove();
        typeof options.click === 'function' && options.click(data, othis);
      }
    });
    
    //触发菜单组展开收缩
    that.elemView.find(STR_GROUP_TITLE).on('click', function(e){
      var othis = $(this)
      ,elemGroup = othis.parent()
      ,data = elemGroup.data('item') || {}
      
      if(data.type === 'group' && options.isAllowSpread){
        thisModule.spread(elemGroup);
      }
    });
    
    //如果是鼠标移入事件,则鼠标移出时自动关闭
    if(options.trigger === 'mouseenter'){
      that.elemView.on('mouseenter', function(){
        clearTimeout(thisModule.timer);
      }).on('mouseleave', function(){
        that.delayRemove();
      });
    }

  };
  
  //位置定位
  Class.prototype.position = function(obj){
    var that = this
    ,options = that.config;
    
    lay.position(options.elem[0], that.elemView[0], {
      position: options.position
      ,e: that.e
      ,clickType: options.trigger === 'contextmenu' ? 'right' : null
      ,align: options.align || null
    });
  };
  
  //删除视图
  Class.prototype.remove = function(){
    var that = this
    ,options = that.config
    ,elemPrev = thisModule.prevElem;
    
    //若存在已打开的面板元素,则移除
    if(elemPrev){
      elemPrev.data('prevElem') && (
        elemPrev.data('prevElem').data(MOD_INDEX +'_opened', false)
      );
      elemPrev.remove();
    }
  };
  
  //延迟删除视图
  Class.prototype.delayRemove = function(){
    var that = this
    ,options = that.config;
    clearTimeout(thisModule.timer);

    thisModule.timer = setTimeout(function(){
      that.remove();
    }, options.delay);
  };
  
  //事件
  Class.prototype.events = function(){
    var that = this
    ,options = that.config;
    
    //如果传入 hover,则解析为 mouseenter
    if(options.trigger === 'hover') options.trigger = 'mouseenter';

    //解除上一个事件
    if(that.prevElem) that.prevElem.off(options.trigger, that.prevElemCallback);
    
    //记录被绑定的元素及回调
    that.prevElem = options.elem;
    that.prevElemCallback = function(e){
      clearTimeout(thisModule.timer);
      that.e = e;
      that.render();
      e.preventDefault();
      
      //组件打开完毕的时间
      typeof options.ready === 'function' && options.ready(that.elemView, options.elem, that.e.target);
    };

    //触发元素事件
    options.elem.on(options.trigger, that.prevElemCallback);
    
    //如果是鼠标移入事件
    if(options.trigger === 'mouseenter'){
      //直行鼠标移出事件
      options.elem.on('mouseleave', function(){
        that.delayRemove();
      });
    }
  };
  
  //记录所有实例
  thisModule.that = {}; //记录所有实例对象
  
  //获取当前实例对象
  thisModule.getThis = function(id){
    var that = thisModule.that[id];
    if(!that) hint.error(id ? (MOD_NAME +' instance with ID \''+ id +'\' not found') : 'ID argument required');
    return that;
  };
  
  //设置菜单组展开和收缩状态
  thisModule.spread = function(othis){
    //菜单组展开和收缩
    var elemIcon = othis.children('.'+ STR_MENU_TITLE).find('.layui-icon');
    if(othis.hasClass(STR_ITEM_UP)){
      othis.removeClass(STR_ITEM_UP).addClass(STR_ITEM_DOWN);
      elemIcon.removeClass('layui-icon-down').addClass('layui-icon-up');
    } else {
      othis.removeClass(STR_ITEM_DOWN).addClass(STR_ITEM_UP);
      elemIcon.removeClass('layui-icon-up').addClass('layui-icon-down')
    }
  };
  
  //全局事件
  ;!function(){
    var _WIN = $(window)
    ,_DOC = $(document);
    
    //自适应定位
    _WIN.on('resize', function(){
      if(!dropdown.thisId) return;
      var that = thisModule.getThis(dropdown.thisId);
      if(!that) return;
      
      if(!that.elemView[0] || !$('.'+ STR_ELEM)[0]){
        return false;
      }
      
      var options = that.config;
      
      if(options.trigger === 'contextmenu'){
        that.remove();
      } else {
        that.position();
      }
    });
    
    
      
    //点击任意处关闭
    _DOC.on(clickOrMousedown, function(e){
      if(!dropdown.thisId) return;
      var that = thisModule.getThis(dropdown.thisId)
      if(!that) return;
      
      var options = that.config;
      
      //如果触发的是绑定的元素,或者属于绑定元素的子元素,则不关闭
      //满足条件:当前绑定的元素不是 body document,或者不是鼠标右键事件
      if(!(lay.isTopElem(options.elem[0]) || options.trigger === 'contextmenu')){
        if(
          e.target === options.elem[0] || 
          options.elem.find(e.target)[0] ||
          e.target === that.elemView[0] ||
          (that.elemView && that.elemView.find(e.target)[0])
        ) return;
      }
      
      that.remove();
    });
    
    //基础菜单的静态元素事件
    var ELEM_LI = '.layui-menu:not(.layui-dropdown-menu) li';
    _DOC.on('click', ELEM_LI, function(e){
      var othis = $(this)
      ,parent = othis.parents('.layui-menu').eq(0)
      ,isChild = othis.hasClass(STR_ITEM_GROUP) || othis.hasClass(STR_ITEM_PARENT)
      ,filter = parent.attr('lay-filter') || parent.attr('id')
      ,options = lay.options(this);
      
      //非触发元素
      if(othis.hasClass(STR_ITEM_DIV)) return;

      //非菜单组
      if(!isChild){
        //选中
        parent.find('.'+ STR_ITEM_CHECKED).removeClass(STR_ITEM_CHECKED); //清除选中样式
        parent.find('.'+ STR_ITEM_CHECKED2).removeClass(STR_ITEM_CHECKED2); //清除父级菜单选中样式
        othis.addClass(STR_ITEM_CHECKED); //添加选中样式
        othis.parents('.'+ STR_ITEM_PARENT).addClass(STR_ITEM_CHECKED2); //添加父级菜单选中样式
        
        //触发事件
        layui.event.call(this, MOD_NAME, 'click('+ filter +')', options);
      }
    });
    
    //基础菜单的展开收缩事件
    _DOC.on('click', (ELEM_LI + STR_GROUP_TITLE), function(e){
      var othis = $(this)
      ,elemGroup = othis.parents('.'+ STR_ITEM_GROUP +':eq(0)')
      ,options = lay.options(elemGroup[0]);

      if(('isAllowSpread' in options) ? options.isAllowSpread : true){
        thisModule.spread(elemGroup);
      };
    });
    
    //判断子级菜单是否超出屏幕
    var ELEM_LI_PAR = '.layui-menu .'+ STR_ITEM_PARENT
    _DOC.on('mouseenter', ELEM_LI_PAR, function(e){
      var othis = $(this)
      ,elemPanel = othis.find('.'+ STR_MENU_PANEL);

      if(!elemPanel[0]) return;
      var rect = elemPanel[0].getBoundingClientRect();
      
      //是否超出右侧屏幕
      if(rect.right > _WIN.width()){
        elemPanel.addClass(STR_MENU_PANEL_L);
        //不允许超出左侧屏幕
        rect = elemPanel[0].getBoundingClientRect();
        if(rect.left < 0){
          elemPanel.removeClass(STR_MENU_PANEL_L);
        }
      }
      
      //是否超出底部屏幕
      if(rect.bottom > _WIN.height()){
        elemPanel.eq(0).css('margin-top', -(rect.bottom - _WIN.height()));
      };
    }).on('mouseleave', ELEM_LI_PAR, function(e){
      var othis = $(this)
      ,elemPanel = othis.children('.'+ STR_MENU_PANEL);
      
      elemPanel.removeClass(STR_MENU_PANEL_L);
      elemPanel.css('margin-top', 0);
    });
    
  }();
  
  //重载实例
  dropdown.reload = function(id, options){
    var that = thisModule.getThis(id);
    if(!that) return this;

    that.reload(options);
    return thisModule.call(that);
  };

  //核心入口
  dropdown.render = function(options){
    var inst = new Class(options);
    return thisModule.call(inst);
  };

  exports(MOD_NAME, dropdown);
});