yzt
2023-05-08 24e1c6a1c3d5331b5a4f1111dcbae3ef148eda1a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/**
 * code 
 * Code 预览组件
 */
 
layui.define(['lay', 'util', 'element', 'form'], function(exports){
  "use strict";
  
  var $ = layui.$;
  var util = layui.util;
  var element = layui.element;
  var form = layui.form;
 
  // 常量
  var CONST = {
    ELEM_VIEW: 'layui-code-view',
    ELEM_TAB: 'layui-tab',
    ELEM_TITLE: 'layui-code-title',
    ELEM_FULL: 'layui-code-full',
    ELEM_PREVIEW: 'layui-code-preview',
    ELEM_ITEM: 'layui-code-item',
    ELEM_SHOW: 'layui-show'
  };
 
  // 默认参数项
  var config = {
    elem: '.layui-code', // 元素选择器
    title: '</>', // 代码栏标题
    about: '', // 代码栏右上角信息
    ln: true, // 代码区域是否显示行号
    header: false, // 是否显示代码栏头部区域
    // 默认文本
    text: {
      code: util.escape('</>'),
      preview: 'Preview'
    }
  };
 
  var trim = function(str){
    return $.trim(str).replace(/^\n|\n$/, '');
  };
  
  // export api
  exports('code', function(options){
    var opts = options = $.extend(true, {}, config, options);
 
    // 目标元素是否存在
    options.elem = $(options.elem);
    if(!options.elem[0]) return;
 
    // 从内至外渲染
    layui.each(options.elem.get().reverse(), function(index, item){
      var othis = $(item);
 
      // 合并属性上的参数,并兼容旧版本属性写法 lay-*
      var options = $.extend(true, {}, opts, lay.options(item), function(obj){
        var attrs = ['title', 'height', 'encode', 'skin', 'about'];
        layui.each(attrs, function(i, attr){
          var value = othis.attr('lay-'+ attr);
          if(typeof value === 'string'){
            obj[attr] = value;
          }  
        })
        return obj;
      }({}));
 
      // 获得代码
      var codes = othis.data('code') || function(){
        var arr = [];
        var textarea = othis.children('textarea');
        
        // 若内容放置在 textarea 中
        textarea.each(function(){
          arr.push(trim(this.value));
        });
        
        // 内容直接放置在元素外层
        if(arr.length === 0){
          arr.push(trim(othis.html()));
        }
        
        return arr;
      }();
 
      othis.data('code', codes);
 
      // 是否开启预览
      if(options.preview){
        var FILTER_VALUE = 'LAY-CODE-DF-'+ index;
        var layout = options.layout || ['code', 'preview'];
        var isIframePreview = options.preview === 'iframe';
        
        // 追加 Tab 组件
        var elemView = $('<div class="'+ CONST.ELEM_PREVIEW +'">');
        var elemTabView = $('<div class="layui-tab layui-tab-brief">');
        var elemHeaderView = $('<div class="layui-tab-title">');
        var elemPreviewView = $('<div class="'+ [
          CONST.ELEM_ITEM, 
          CONST.ELEM_ITEM +'-preview',
          'layui-border'
        ].join(' ') +'">');
        var elemToolbar = $('<div class="layui-code-tools"></div>');
        var elemViewHas = othis.parent('.' + CONST.ELEM_PREVIEW);
        var elemTabHas = othis.prev('.'+ CONST.ELEM_TAB);
        var elemPreviewViewHas = othis.next('.' + CONST.ELEM_ITEM +'-preview');
 
        if(options.id) elemView.attr('id', options.id);
        elemView.addClass(options.className);
        elemTabView.attr('lay-filter', FILTER_VALUE);
 
        // 标签头
        layui.each(layout, function(i, v){
          var li = $('<li lay-id="'+ v +'">');
          if(i === 0) li.addClass('layui-this');
          li.html(options.text[v]);
          elemHeaderView.append(li);
        });
 
        // 工具栏
        var tools = {
          'full': {
            className: 'screen-full',
            title: ['最大化显示', '还原显示'],
            event: function(el, type){
              var elemView = el.closest('.'+ CONST.ELEM_PREVIEW);
              var classNameFull = 'layui-icon-'+ this.className;
              var classNameRestore = 'layui-icon-screen-restore';
              var title = this.title;
              var html = $('html,body');
              var ELEM_SCOLLBAR_HIDE = 'layui-scollbar-hide';
 
              if(el.hasClass(classNameFull)){
                elemView.addClass(CONST.ELEM_FULL);
                el.removeClass(classNameFull).addClass(classNameRestore);
                el.attr('title', title[1]);
                html.addClass(ELEM_SCOLLBAR_HIDE);
              } else {
                elemView.removeClass(CONST.ELEM_FULL);
                el.removeClass(classNameRestore).addClass(classNameFull);
                el.attr('title', title[0]);
                html.removeClass(ELEM_SCOLLBAR_HIDE);
              }
            }
          },
          'window': {
            className: 'release',
            title: ['在新窗口预览'],
            event: function(el, type){
              util.openWin({
                content: codes.join('')
              });
            }
          }
        };
        elemToolbar.on('click', '>i', function(){ // 工具栏事件
          var oi = $(this);
          var type = oi.data('type');
          typeof tools[type].event === 'function' && tools[type].event(oi, type);
          typeof options.toolsEvent === 'function' && options.toolsEvent(oi, type);
        });
        layui.each(options.tools, function(i, v){
          var className = (tools[v] && tools[v].className) || v;
          var title = tools[v].title || [''];
          elemToolbar.append(
            '<i class="layui-icon layui-icon-'+ className +'" data-type="'+ v +'" title="'+ title[0] +'"></i>'
          );
        });
 
        // 移除旧结构
        if(elemTabHas[0]) elemTabHas.remove(); // 移除 tab
        if(elemPreviewViewHas[0]) elemPreviewViewHas.remove(); // 移除预览区域
        if(elemViewHas[0]) othis.unwrap(); // 移除外层容器
 
        elemTabView.append(elemHeaderView); // 追加标签头
        options.tools && elemTabView.append(elemToolbar); // 追加工具栏
        othis.wrap(elemView).addClass(CONST.ELEM_ITEM).before(elemTabView); // 追加标签结构
 
        
        // 追加预览
        if(isIframePreview){
          elemPreviewView.html('<iframe></iframe>');
        }
 
        // 执行预览
        var run = function(thisItemBody){
          var iframe = thisItemBody.children('iframe')[0];
          if(isIframePreview && iframe){
            iframe.srcdoc = codes.join('');
          } else {
            thisItemBody.html(codes.join(''));
          }
          // 回调的返回参数
          var params = {
            container: thisItemBody,
            render: function(){
              form.render(thisItemBody.find('.layui-form'));
              element.render();
            }
          };
          // 当前实例预览完毕后的回调
          setTimeout(function(){
            typeof options.done === 'function' && options.done(params);
          },3);
        };
 
        if(layout[0] === 'preview'){
          elemPreviewView.addClass(CONST.ELEM_SHOW);
          othis.before(elemPreviewView);
          run(elemPreviewView);
        } else {
          othis.addClass(CONST.ELEM_SHOW).after(elemPreviewView);
        }
 
        // 内容项初始化样式
        options.codeStyle = [options.style, options.codeStyle].join('');
        options.previewStyle = [options.style, options.previewStyle].join('');
        // othis.attr('style', options.codeStyle);
        elemPreviewView.attr('style', options.previewStyle);
 
        // tab change
        element.on('tab('+ FILTER_VALUE +')', function(data){
          var $this = $(this);
          var thisElem = $(data.elem).closest('.'+ CONST.ELEM_PREVIEW);
          var elemItemBody = thisElem.find('.'+ CONST.ELEM_ITEM);
          var thisItemBody = elemItemBody.eq(data.index);
          
          elemItemBody.removeClass(CONST.ELEM_SHOW);
          thisItemBody.addClass(CONST.ELEM_SHOW);
 
          if($this.attr('lay-id') === 'preview'){
            run(thisItemBody);
          }
        });
      }
 
 
      // 有序或无序列表
      var listTag = options.ln ? 'ol' : 'ul';
      var listElem = $('<'+ listTag +' class="layui-code-'+ listTag +'">');
 
      // header
      var headerElem = $('<div class="'+ CONST.ELEM_TITLE +'">');
 
      // 添加组件 clasName
      othis.addClass('layui-code-view layui-box');
 
      // 自定义风格
      if(options.skin){
        if(options.skin === 'notepad') options.skin = 'dark';
        othis.removeClass('layui-code-dark layui-code-light');
        othis.addClass('layui-code-'+ options.skin);
      } 
 
      
      // code
      var html = util.escape(codes.join(''));
 
      // 转义 HTML 标签
      if(options.encode) html = util.escape(html);
      html = html.replace(/[\r\t\n]+/g, '</li><li>'); // 转义换行符
      
      // 生成列表
      othis.html(listElem.html('<li>' + html + '</li>'));
      
      // 创建 header
      if(options.header && !othis.children('.'+ CONST.ELEM_TITLE)[0]){
        headerElem.html(options.title + (
          options.about 
            ? '<div class="layui-code-about">' + options.about + '</div>'
          : ''
        ));
        othis.prepend(headerElem);
      }
 
      // 所有实例渲染完毕后的回调
      if(options.elem.length === index + 1){
        typeof options.allDone === 'function' && options.allDone();
      }
 
      // 按行数适配左边距
      (function(autoIncNums){
        if(autoIncNums > 0){
          listElem.css('margin-left', autoIncNums + 'px');
        }
      })(Math.floor(listElem.find('li').length/100));
 
      // 限制 Code 最大高度
      if(options.height){ // 兼容旧版本
        listElem.css('max-height', options.height);
      }
      // Code 内容区域样式
      listElem.attr('style', options.codeStyle);
 
    });
    
  });
});
 
// 若为源码版,则自动加载该组件依赖的 css 文件
if(!layui['layui.all']){
  layui.addcss('modules/code.css?v=3', 'skincodecss');
}