<commit_before>/**
 * node-archiver
 *
 * Copyright (c) 2012-2013 Chris Talkington, contributors.
 * Licensed under the MIT license.
 * https://github.com/ctalkington/node-archiver/blob/master/LICENSE-MIT
 */

require('../../compat/buffer');

var inherits = require('util').inherits;
var Transform = require('stream').Transform || require('readable-stream').Transform;
var PassThrough = require('stream').PassThrough || require('readable-stream').PassThrough;

var util = require('../../util');
var Queue = require('./queue');

var Archiver = module.exports = function(options) {
  options = this.options = util.defaults(options, {
    highWaterMark: 512 * 1024
  });

  Transform.call(this, options);

  this.catchEarlyExitAttached = false;
  this.moduleOutputPiped = false;

  this.archiver = {
    pointer: 0,
    file: {},
    files: [],
    module: false
  };

  this.queue = new Queue();
  this.queue.on('entry', this._onQueueEntry.bind(this));
  this.queue.on('end', this._onQueueEnd.bind(this));
};

inherits(Archiver, Transform);

Archiver.prototype._catchEarlyExit = function() {
  var earlyExitCheck = function() {
    if (this._readableState.endEmitted === false) {
      throw new Error('process exited before Archiver could finish emitting data');
    }
  }.bind(this);

  process.once('exit', earlyExitCheck);

  this.once('end', function() {
    process.removeListener('exit', earlyExitCheck);
  });

  this.catchEarlyExitAttached = true;
};

Archiver.prototype._emitErrorCallback = function(err, data) {
  if (err) {
    this.emit('error', err);
  }
};

Archiver.prototype._normalizeFileData = function(data) {
  data = util.defaults(data, {
    name: null,
    date: null
  });

  data.name = util.sanitizeFilePath(data.name);
  data.date = util.dateify(data.date);

  return data;
};

Archiver.prototype._normalizeSource = function(source) {
  if (typeof source === 'string') {
    return new Buffer(source);
  } else if (util.isStream(source) && !source._readableState) {
    var normalized = new PassThrough();
    source.pipe(normalized);

    return normalized;
  }

  return source;
};

Archiver.prototype._onQueueEnd = function() {
  if (typeof this.archiver.module.finalize === 'function') {
    this.archiver.module.finalize();
  } else if (typeof this.archiver.module.end === 'function') {
    this.archiver.module.end();
  } else {
    this.emit('error', new Error('format module missing finalize and end method'));
  }
};

Archiver.prototype._onQueueEntry = function(entry, queueCallback) {
  var nextCallback = function(err, file) {
    entry.callback(err);

    if (!err) {
      this.archiver.files.push(file);
      queueCallback();
    }
  }.bind(this);

  this.archiver.module.append(entry.source, entry.data, nextCallback);
};

Archiver.prototype._pipeModuleOutput = function() {
  this.archiver.module.pipe(this);

  this.moduleOutputPiped = true;
};

Archiver.prototype._processFile = function(source, data, callback) {
  callback(new Error('method not implemented'));
};

Archiver.prototype._transform = function(chunk, encoding, callback) {
  if (chunk) {
    this.archiver.pointer += chunk.length;
  }

  callback(null, chunk);
};

Archiver.prototype.append = function(source, data, callback) {
  data = this._normalizeFileData(data);

  if (!this.catchEarlyExitAttached) {
    this._catchEarlyExit();
  }

  if (typeof callback !== 'function') {
    callback = this._emitErrorCallback.bind(this);
  }

  if (!this.moduleOutputPiped) {
    if (!this.archiver.module) {
      callback(new Error('format module not registered'));
      return this;
    }

    this._pipeModuleOutput();
  }

  if (typeof data.name !== 'string' || data.name.length === 0) {
    callback(new Error('filename must be a non-empty string value'));
    return this;
  }

  source = this._normalizeSource(source);

  if (Buffer.isBuffer(source)) {
    data.sourceType = 'buffer';
  } else if (util.isStream(source)) {
    data.sourceType = 'stream';
  } else {
    callback(new Error('input source must be valid Stream or Buffer instance'));
    return this;
  }

  this.queue.add({
    data: data,
    source: source,
    callback: callback
  });

  return this;
};

Archiver.prototype.bulk = function(mappings) {
  if (!util.isArray(mappings)) {
    return this;
  }

  var self = this;
  var files = util.normalizeFilesArray(mappings);

  files.forEach(function(file){
    var isExpandedPair = file.orig.expand || false;
    var data = util.defaults({}, file.data);
    var src = file.src.filter(function(f) {
      return util.file.isFile(f);
    });

    src.forEach(function(filepath) {
      data.name = isExpandedPair ? file.dest : util.unixifyPath(file.dest || '', filepath);

      self.append(util.lazyReadStream(filepath), data);
    });
  });

  return this;
};

Archiver.prototype.file = function(filepath, data, callback) {
  filepath = String(filepath);

  if (typeof callback !== 'function') {
    callback = this._emitErrorCallback.bind(this);
  }

  if (util.file.exists(filepath)) {
    data = util.defaults(data, {
      name: util.sanitizeFilePath(filepath)
    });

    this.append(util.lazyReadStream(filepath), data, callback);
  } else {
    callback(new Error('invalid file: ' + filepath));
  }

  return this;
};

Archiver.prototype.finalize = function(callback) {
  if (typeof callback === 'function') {
    this.once('end', function() {
      callback(null, this.archiver.pointer);
    }.bind(this));
  }

  this.queue.close();

  return this;
};

Archiver.prototype.setModule = function(module) {
  this.archiver.module = module;
};<commit_msg>core: remove unused var.<commit_after>/**
 * node-archiver
 *
 * Copyright (c) 2012-2013 Chris Talkington, contributors.
 * Licensed under the MIT license.
 * https://github.com/ctalkington/node-archiver/blob/master/LICENSE-MIT
 */

require('../../compat/buffer');

var inherits = require('util').inherits;
var Transform = require('stream').Transform || require('readable-stream').Transform;
var PassThrough = require('stream').PassThrough || require('readable-stream').PassThrough;

var util = require('../../util');
var Queue = require('./queue');

var Archiver = module.exports = function(options) {
  options = this.options = util.defaults(options, {
    highWaterMark: 512 * 1024
  });

  Transform.call(this, options);

  this.catchEarlyExitAttached = false;
  this.moduleOutputPiped = false;

  this.archiver = {
    pointer: 0,
    files: [],
    module: false
  };

  this.queue = new Queue();
  this.queue.on('entry', this._onQueueEntry.bind(this));
  this.queue.on('end', this._onQueueEnd.bind(this));
};

inherits(Archiver, Transform);

Archiver.prototype._catchEarlyExit = function() {
  var earlyExitCheck = function() {
    if (this._readableState.endEmitted === false) {
      throw new Error('process exited before Archiver could finish emitting data');
    }
  }.bind(this);

  process.once('exit', earlyExitCheck);

  this.once('end', function() {
    process.removeListener('exit', earlyExitCheck);
  });

  this.catchEarlyExitAttached = true;
};

Archiver.prototype._emitErrorCallback = function(err, data) {
  if (err) {
    this.emit('error', err);
  }
};

Archiver.prototype._normalizeFileData = function(data) {
  data = util.defaults(data, {
    name: null,
    date: null
  });

  data.name = util.sanitizeFilePath(data.name);
  data.date = util.dateify(data.date);

  return data;
};

Archiver.prototype._normalizeSource = function(source) {
  if (typeof source === 'string') {
    return new Buffer(source);
  } else if (util.isStream(source) && !source._readableState) {
    var normalized = new PassThrough();
    source.pipe(normalized);

    return normalized;
  }

  return source;
};

Archiver.prototype._onQueueEnd = function() {
  if (typeof this.archiver.module.finalize === 'function') {
    this.archiver.module.finalize();
  } else if (typeof this.archiver.module.end === 'function') {
    this.archiver.module.end();
  } else {
    this.emit('error', new Error('format module missing finalize and end method'));
  }
};

Archiver.prototype._onQueueEntry = function(entry, queueCallback) {
  var nextCallback = function(err, file) {
    entry.callback(err);

    if (!err) {
      this.archiver.files.push(file);
      queueCallback();
    }
  }.bind(this);

  this.archiver.module.append(entry.source, entry.data, nextCallback);
};

Archiver.prototype._pipeModuleOutput = function() {
  this.archiver.module.pipe(this);

  this.moduleOutputPiped = true;
};

Archiver.prototype._processFile = function(source, data, callback) {
  callback(new Error('method not implemented'));
};

Archiver.prototype._transform = function(chunk, encoding, callback) {
  if (chunk) {
    this.archiver.pointer += chunk.length;
  }

  callback(null, chunk);
};

Archiver.prototype.append = function(source, data, callback) {
  data = this._normalizeFileData(data);

  if (!this.catchEarlyExitAttached) {
    this._catchEarlyExit();
  }

  if (typeof callback !== 'function') {
    callback = this._emitErrorCallback.bind(this);
  }

  if (!this.moduleOutputPiped) {
    if (!this.archiver.module) {
      callback(new Error('format module not registered'));
      return this;
    }

    this._pipeModuleOutput();
  }

  if (typeof data.name !== 'string' || data.name.length === 0) {
    callback(new Error('filename must be a non-empty string value'));
    return this;
  }

  source = this._normalizeSource(source);

  if (Buffer.isBuffer(source)) {
    data.sourceType = 'buffer';
  } else if (util.isStream(source)) {
    data.sourceType = 'stream';
  } else {
    callback(new Error('input source must be valid Stream or Buffer instance'));
    return this;
  }

  this.queue.add({
    data: data,
    source: source,
    callback: callback
  });

  return this;
};

Archiver.prototype.bulk = function(mappings) {
  if (!util.isArray(mappings)) {
    return this;
  }

  var self = this;
  var files = util.normalizeFilesArray(mappings);

  files.forEach(function(file){
    var isExpandedPair = file.orig.expand || false;
    var data = util.defaults({}, file.data);
    var src = file.src.filter(function(f) {
      return util.file.isFile(f);
    });

    src.forEach(function(filepath) {
      data.name = isExpandedPair ? file.dest : util.unixifyPath(file.dest || '', filepath);

      self.append(util.lazyReadStream(filepath), data);
    });
  });

  return this;
};

Archiver.prototype.file = function(filepath, data, callback) {
  filepath = String(filepath);

  if (typeof callback !== 'function') {
    callback = this._emitErrorCallback.bind(this);
  }

  if (util.file.exists(filepath)) {
    data = util.defaults(data, {
      name: util.sanitizeFilePath(filepath)
    });

    this.append(util.lazyReadStream(filepath), data, callback);
  } else {
    callback(new Error('invalid file: ' + filepath));
  }

  return this;
};

Archiver.prototype.finalize = function(callback) {
  if (typeof callback === 'function') {
    this.once('end', function() {
      callback(null, this.archiver.pointer);
    }.bind(this));
  }

  this.queue.close();

  return this;
};

Archiver.prototype.setModule = function(module) {
  this.archiver.module = module;
};<|endoftext|>


<commit_before>var fnObj = {};
var ACTIONS = axboot.actionExtend(fnObj, {
    PAGE_SEARCH: "PAGE_SEARCH",
    PAGE_SAVE: "PAGE_SAVE",
    TREEITEM_CLICK: "TREEITEM_CLICK",
    TREE_ROOTNODE_ADD: "TREE_ROOTNODE_ADD",
    dispatch: function (caller, act, data) {
        var _this = this;
        switch (act) {
            case ACTIONS.PAGE_SEARCH:
                var searchData = this.searchView.getData();
                axboot.ajax({
                    type: "GET",
                    url: "/api/v1/manual",
                    data: this.searchView.getData()
                }, function (res) {
                    _this.treeView01.setData(searchData, res.list);

                }, {
                    onError: function (err) {
                        console.log(err);
                    }
                });
                break;
            case ACTIONS.TREEITEM_CLICK:

                if (typeof data.manualId === "undefined") {
                    this.formView01.clear();
                    if(confirm("신규 생성된 목차는 저장 후 편집 할수 있습니다. 지금 저장 하시겠습니까? (저장 후에 다시 선택해주세요)")){
                        ACTIONS.dispatch(ACTIONS.PAGE_SAVE);
                    }
                    return;
                }

                axboot.ajax({
                    type: "GET",
                    url: "/api/v1/manual/" + data.manualId,
                    data: ""
                }, function (res) {
                    _this.formView01.setData(res);
                });

                break;
            case ACTIONS.TREEITEM_DESELECTE:
                this.formView01.clear();
                break;

            case ACTIONS.TREE_ROOTNODE_ADD:
                this.treeView01.addRootNode();
                break;
            case ACTIONS.PAGE_SAVE:

                var obj = {
                    list: this.treeView01.getData(),
                    deletedList: this.treeView01.getDeletedList()
                };

                axboot
                    .call({
                        type: "PUT",
                        url: "/api/v1/manual",
                        data: JSON.stringify(obj),
                        callback: function(){
                            _this.treeView01.clearDeletedList();
                            axToast.push("목차가 저장 되었습니다");
                        }
                    })
                    .done(function(){
                        var data = _this.formView01.getData();

                        if(data.manualId) {
                            axboot.ajax({
                                type: "PUT",
                                url: "/api/v1/manual/detail",
                                data: JSON.stringify(data)
                            }, function (res) {
                                axToast.push("매뉴얼 내용이 저장 되었습니다");
                                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
                            });
                        }else{
                            ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
                        }

                    });

                break;
            case ACTIONS.FORM_CLEAR:
                var _this = this;
                axDialog.confirm({
                    msg: "정말 양식을 초기화 하시겠습니까?"
                }, function () {
                    if (this.key == "ok") {
                        _this.formView01.clear();
                    }
                });
                break;
            default:
                return false;
        }
        return false;
    }
});
var CODE = {};

// fnObj 기본 함수 스타트와 리사이즈
fnObj.pageStart = function () {

    var _this = this;

    axboot
        .call({
            type: "GET", url: "/api/v1/commonCodes", data: {groupCd: "MANUAL_GROUP", useYn: "Y"},
            callback: function (res) {
                var manualGroup = [];
                res.list.forEach(function (n) {
                    manualGroup.push({
                        value: n.code, text: n.name + "(" + n.code + ")",
                        grpAuthCd: n.code, grpAuthNm: n.name,
                        data: n
                    });
                });
                this.authGroup = manualGroup;
            }
        })
        .done(function () {
            CODE = this; // this는 call을 통해 수집된 데이터들.

            _this.pageButtonView.initView();
            _this.searchView.initView();
            _this.treeView01.initView();
            _this.formView01.initView();

            ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
        });
};

fnObj.pageResize = function () {
    setTimeout(function(){
        fnObj.formView01.resize();
    }, 100);
};

fnObj.pageButtonView = axboot.viewExtend({
    initView: function () {
        var _this = this;
        $('[data-page-btn]').click(function () {
            _this.onClick(this.getAttribute("data-page-btn"));
        });
    },
    onClick: function (_act) {
        var _root = fnObj;
        switch (_act) {
            case "search":
                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
                break;
            case "save":
                ACTIONS.dispatch(ACTIONS.PAGE_SAVE);
                break;
            case "excel":
                break;
            case "fn1":
                break;
            case "fn2":
                break;
            case "fn3":
                break;
            case "fn4":
                break;
            case "fn5":
                break;
        }
    }
});

//== view 시작
/**
 * searchView
 */
fnObj.searchView = axboot.viewExtend(axboot.searchView, {
    initView: function () {
        this.target = $(document["searchView0"]);
        this.target.attr("onsubmit", "return ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);");
        this.manualGrpCd = $("#manualGrpCd");
    },
    getData: function () {
        return {
            pageNumber: 0,
            pageSize: 99999,
            manualGrpCd: this.manualGrpCd.val()
        }
    }
});

/**
 * treeView
 */
fnObj.treeView01 = axboot.viewExtend(axboot.treeView, {
    param: {},
    deletedList: [],
    newCount: 0,
    addRootNode: function () {
        var _this = this;
        var nodes = _this.target.zTree.getSelectedNodes();
        var treeNode = nodes[0];

        // root
        treeNode = _this.target.zTree.addNodes(null, {
            id: "_isnew_" + (++_this.newCount),
            pId: 0,
            name: "새 메뉴",
            __created__: true,
            manualGrpCd: _this.param.manualGrpCd
        });

        if (treeNode) {
            _this.target.zTree.editName(treeNode[0]);
        }
        fnObj.treeView01.deselectNode();
    },
    initView: function () {
        var _this = this;

        $('[data-tree-view-01-btn]').click(function () {
            var _act = this.getAttribute("data-tree-view-01-btn");
            switch (_act) {
                case "add":
                    ACTIONS.dispatch(ACTIONS.TREE_ROOTNODE_ADD);
                    break;
                case "delete":
                    //ACTIONS.dispatch(ACTIONS.ITEM_DEL);
                    break;
            }
        });

        this.target = axboot.treeBuilder($('[data-z-tree="tree-view-01"]'), {
            view: {
                dblClickExpand: false,
                addHoverDom: function (treeId, treeNode) {
                    var sObj = $("#" + treeNode.tId + "_span");
                    if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) return;
                    var addStr = "";
                    sObj.after(addStr);
                    var btn = $("#addBtn_" + treeNode.tId);
                    if (btn) {
                        btn.bind("click", function () {
                            _this.target.zTree.addNodes(
                                treeNode,
                                {
                                    id: "_isnew_" + (++_this.newCount),
                                    pId: treeNode.id,
                                    name: "새 메뉴",
                                    __created__: true,
                                    manualGrpCd: _this.param.manualGrpCd
                                }
                            );
                            _this.target.zTree.selectNode(treeNode.children[treeNode.children.length - 1]);
                            _this.target.editName();
                            fnObj.treeView01.deselectNode();
                            return false;
                        });
                    }
                },
                removeHoverDom: function (treeId, treeNode) {
                    $("#addBtn_" + treeNode.tId).unbind().remove();
                }
            },
            edit: {
                enable: true,
                editNameSelectAll: true
            },
            callback: {
                beforeDrag: function () {
                    //return false;
                },
                onClick: function (e, treeId, treeNode, isCancel) {
                    ACTIONS.dispatch(ACTIONS.TREEITEM_CLICK, treeNode);
                },
                onRename: function (e, treeId, treeNode, isCancel) {
                    treeNode.__modified__ = true;
                },
                onRemove: function (e, treeId, treeNode, isCancel) {
                    if (!treeNode.__created__) {
                        treeNode.__deleted__ = true;
                        _this.deletedList.push(treeNode);
                    }
                }
            }
        }, []);
    },
    setData: function (_searchData, _tree) {
        this.param = $.extend({}, _searchData);
        this.target.setData(_tree);
    },
    getData: function () {
        var _this = this;
        var tree = this.target.getData();

        var convertList = function (_tree) {
            var _newTree = [];
            _tree.forEach(function (n, nidx) {
                var item = {};
                if (n.__created__ || n.__modified__) {
                    item = {
                        __created__: n.__created__,
                        __modified__: n.__modified__,
                        manualId: n.manualId,
                        manualGrpCd: _this.param.manualGrpCd,
                        manualNm: n.name,
                        parentId: n.parentId,
                        sort: nidx,
                        progCd: n.progCd,
                        level: n.level
                    };
                } else {
                    item = {
                        manualId: n.manualId,
                        manualGrpCd: n.manualGrpCd,
                        manualNm: n.name,
                        parentId: n.parentId,
                        sort: nidx,
                        progCd: n.progCd,
                        level: n.level
                    };
                }
                if (n.children && n.children.length) {
                    item.children = convertList(n.children);
                }
                _newTree.push(item);
            });
            return _newTree;
        };
        var newTree = convertList(tree);
        return newTree;
    },
    getDeletedList: function () {
        return this.deletedList;
    },
    clearDeletedList: function () {
        this.deletedList = [];
        return true;
    },
    updateNode: function (data) {
        var treeNodes = this.target.getSelectedNodes();
        if (treeNodes[0]) {
            treeNodes[0].progCd = data.progCd;
        }
    },
    deselectNode: function () {
        ACTIONS.dispatch(ACTIONS.TREEITEM_DESELECTE);
    }
});

/**
 * formView01
 */
fnObj.formView01 = axboot.viewExtend(axboot.formView, {
    getDefaultData: function () {
        return $.extend({}, axboot.formView.defaultData, {});
    },
    initView: function () {
        var _this = this;

        this.mask = new ax5.ui.mask({
            theme: "form-mask",
            target: $('#split-panel-form'),
            content: '좌측 메뉴를 선택해주세요.'
        });
        this.mask.open();

        this.manualGroup = CODE.manualGroup;

        this.summernote = $('#summernote');
        this.summernote.summernote({
            height: ($('#summernote').height() - 50),                 // set editor height
            minHeight: null,             // set minimum height of editor
            maxHeight: null,             // set maximum height of editor
            focus: true,                  // set focus to editable area after initializing summernote,
            lang: 'ko-KR',
            airMode: false,
            toolbar: [
                ['style', ['style']],
                ['font', ['bold', 'italic', 'underline', 'clear']],
                ['fontname', ['fontname']],
                ['color', ['color']],
                ['para', ['ul', 'ol', 'paragraph']],
                ['height', ['height']],
                ['table', ['table']],
                ['insert', ['link', 'picture', 'hr']],
                ['view', ['codeview']],
                ['help', ['help']]
            ]
        });

        this.target = $("#formView01");
        this.model = new ax5.ui.binder();
        this.model.setModel(this.getDefaultData(), this.target);
        this.modelFormatter = new axboot.modelFormatter(this.model); // 모델 포메터 시작

        this.initEvent();

        $('[data-form-view-01-btn]').click(function () {
            var _root = fnObj;
            switch (this.getAttribute("data-form-view-01-btn")) {
                case "form-clear":
                    ACTIONS.dispatch(ACTIONS.FORM_CLEAR);
                    break;
            }
        });
    },
    initEvent: function () {
        var _this = this;
    },
    getData: function () {
        var data = this.modelFormatter.getClearData(this.model.get()); // 모델의 값을 포멧팅 전 값으로 치환.

        data.content = this.summernote.summernote("code");

        return data;
    },
    setData: function (data) {
        this.mask.close();
        $.extend(true, data, this.getDefaultData());
        this.model.setModel(data);
        console.log(this.summernote.summernote("code", data.content||""));
    },
    resize: function(){
        $('.note-editable.panel-body').height(($('#summernote').height() - 200));
    },
    clear: function () {
        this.mask.open();
        this.model.setModel(this.getDefaultData());
    }
});<commit_msg>매뉴얼 파일업로드 기능 추가<commit_after>var fnObj = {};
var ACTIONS = axboot.actionExtend(fnObj, {
    PAGE_SEARCH: "PAGE_SEARCH",
    PAGE_SAVE: "PAGE_SAVE",
    TREEITEM_CLICK: "TREEITEM_CLICK",
    TREE_ROOTNODE_ADD: "TREE_ROOTNODE_ADD",
    dispatch: function (caller, act, data) {
        var _this = this;
        switch (act) {
            case ACTIONS.PAGE_SEARCH:
                var searchData = this.searchView.getData();
                axboot.ajax({
                    type: "GET",
                    url: "/api/v1/manual",
                    data: this.searchView.getData()
                }, function (res) {
                    _this.treeView01.setData(searchData, res.list);

                }, {
                    onError: function (err) {
                        console.log(err);
                    }
                });
                break;
            case ACTIONS.TREEITEM_CLICK:

                if (typeof data.manualId === "undefined") {
                    this.formView01.clear();
                    if(confirm("신규 생성된 목차는 저장 후 편집 할수 있습니다. 지금 저장 하시겠습니까? (저장 후에 다시 선택해주세요)")){
                        ACTIONS.dispatch(ACTIONS.PAGE_SAVE);
                    }
                    return;
                }

                axboot.ajax({
                    type: "GET",
                    url: "/api/v1/manual/" + data.manualId,
                    data: ""
                }, function (res) {
                    _this.formView01.setData(res);
                });

                break;
            case ACTIONS.TREEITEM_DESELECTE:
                this.formView01.clear();
                break;

            case ACTIONS.TREE_ROOTNODE_ADD:
                this.treeView01.addRootNode();
                break;
            case ACTIONS.PAGE_SAVE:

                var obj = {
                    list: this.treeView01.getData(),
                    deletedList: this.treeView01.getDeletedList()
                };

                axboot
                    .call({
                        type: "PUT",
                        url: "/api/v1/manual",
                        data: JSON.stringify(obj),
                        callback: function(){
                            _this.treeView01.clearDeletedList();
                            axToast.push("목차가 저장 되었습니다");
                        }
                    })
                    .done(function(){
                        var data = _this.formView01.getData();

                        if(data.manualId) {
                            axboot.ajax({
                                type: "PUT",
                                url: "/api/v1/manual/detail",
                                data: JSON.stringify(data)
                            }, function (res) {
                                axToast.push("매뉴얼 내용이 저장 되었습니다");
                                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
                            });
                        }else{
                            ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
                        }

                    });

                break;
            case ACTIONS.FORM_CLEAR:
                var _this = this;
                axDialog.confirm({
                    msg: "정말 양식을 초기화 하시겠습니까?"
                }, function () {
                    if (this.key == "ok") {
                        _this.formView01.clear();
                    }
                });
                break;
            default:
                return false;
        }
        return false;
    }
});
var CODE = {};

// fnObj 기본 함수 스타트와 리사이즈
fnObj.pageStart = function () {

    var _this = this;

    axboot
        .call({
            type: "GET", url: "/api/v1/commonCodes", data: {groupCd: "MANUAL_GROUP", useYn: "Y"},
            callback: function (res) {
                var manualGroup = [];
                res.list.forEach(function (n) {
                    manualGroup.push({
                        value: n.code, text: n.name + "(" + n.code + ")",
                        grpAuthCd: n.code, grpAuthNm: n.name,
                        data: n
                    });
                });
                this.authGroup = manualGroup;
            }
        })
        .done(function () {
            CODE = this; // this는 call을 통해 수집된 데이터들.

            _this.pageButtonView.initView();
            _this.searchView.initView();
            _this.treeView01.initView();
            _this.formView01.initView();

            ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
        });
};

fnObj.pageResize = function () {
    setTimeout(function(){
        fnObj.formView01.resize();
    }, 100);
};

fnObj.pageButtonView = axboot.viewExtend({
    initView: function () {
        var _this = this;
        $('[data-page-btn]').click(function () {
            _this.onClick(this.getAttribute("data-page-btn"));
        });
    },
    onClick: function (_act) {
        var _root = fnObj;
        switch (_act) {
            case "search":
                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);
                break;
            case "save":
                ACTIONS.dispatch(ACTIONS.PAGE_SAVE);
                break;
            case "excel":
                break;
            case "fn1":
                break;
            case "fn2":
                break;
            case "fn3":
                break;
            case "fn4":
                break;
            case "fn5":
                break;
        }
    }
});

//== view 시작
/**
 * searchView
 */
fnObj.searchView = axboot.viewExtend(axboot.searchView, {
    initView: function () {
        this.target = $(document["searchView0"]);
        this.target.attr("onsubmit", "return ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);");
        this.manualGrpCd = $("#manualGrpCd");
    },
    getData: function () {
        return {
            pageNumber: 0,
            pageSize: 99999,
            manualGrpCd: this.manualGrpCd.val()
        }
    }
});

/**
 * treeView
 */
fnObj.treeView01 = axboot.viewExtend(axboot.treeView, {
    param: {},
    deletedList: [],
    newCount: 0,
    addRootNode: function () {
        var _this = this;
        var nodes = _this.target.zTree.getSelectedNodes();
        var treeNode = nodes[0];

        // root
        treeNode = _this.target.zTree.addNodes(null, {
            id: "_isnew_" + (++_this.newCount),
            pId: 0,
            name: "새 목차",
            __created__: true,
            manualGrpCd: _this.param.manualGrpCd
        });

        if (treeNode) {
            _this.target.zTree.editName(treeNode[0]);
        }
        fnObj.treeView01.deselectNode();
    },
    initView: function () {
        var _this = this;

        $('[data-tree-view-01-btn]').click(function () {
            var _act = this.getAttribute("data-tree-view-01-btn");
            switch (_act) {
                case "add":
                    ACTIONS.dispatch(ACTIONS.TREE_ROOTNODE_ADD);
                    break;
                case "delete":
                    //ACTIONS.dispatch(ACTIONS.ITEM_DEL);
                    break;
            }
        });

        this.target = axboot.treeBuilder($('[data-z-tree="tree-view-01"]'), {
            view: {
                dblClickExpand: false,
                addHoverDom: function (treeId, treeNode) {
                    var sObj = $("#" + treeNode.tId + "_span");
                    if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) return;
                    var addStr = "";
                    sObj.after(addStr);
                    var btn = $("#addBtn_" + treeNode.tId);
                    if (btn) {
                        btn.bind("click", function () {
                            _this.target.zTree.addNodes(
                                treeNode,
                                {
                                    id: "_isnew_" + (++_this.newCount),
                                    pId: treeNode.id,
                                    name: "새 목차",
                                    __created__: true,
                                    manualGrpCd: _this.param.manualGrpCd
                                }
                            );
                            _this.target.zTree.selectNode(treeNode.children[treeNode.children.length - 1]);
                            _this.target.editName();
                            fnObj.treeView01.deselectNode();
                            return false;
                        });
                    }
                },
                removeHoverDom: function (treeId, treeNode) {
                    $("#addBtn_" + treeNode.tId).unbind().remove();
                }
            },
            edit: {
                enable: true,
                editNameSelectAll: true
            },
            callback: {
                beforeDrag: function () {
                    //return false;
                },
                onClick: function (e, treeId, treeNode, isCancel) {
                    ACTIONS.dispatch(ACTIONS.TREEITEM_CLICK, treeNode);
                },
                onRename: function (e, treeId, treeNode, isCancel) {
                    treeNode.__modified__ = true;
                },
                onRemove: function (e, treeId, treeNode, isCancel) {
                    if (!treeNode.__created__) {
                        treeNode.__deleted__ = true;
                        _this.deletedList.push(treeNode);
                    }
                }
            }
        }, []);
    },
    setData: function (_searchData, _tree) {
        this.param = $.extend({}, _searchData);
        this.target.setData(_tree);
    },
    getData: function () {
        var _this = this;
        var tree = this.target.getData();

        var convertList = function (_tree) {
            var _newTree = [];
            _tree.forEach(function (n, nidx) {
                var item = {};
                if (n.__created__ || n.__modified__) {
                    item = {
                        __created__: n.__created__,
                        __modified__: n.__modified__,
                        manualId: n.manualId,
                        manualGrpCd: _this.param.manualGrpCd,
                        manualNm: n.name,
                        parentId: n.parentId,
                        sort: nidx,
                        progCd: n.progCd,
                        level: n.level
                    };
                } else {
                    item = {
                        manualId: n.manualId,
                        manualGrpCd: n.manualGrpCd,
                        manualNm: n.name,
                        parentId: n.parentId,
                        sort: nidx,
                        progCd: n.progCd,
                        level: n.level
                    };
                }
                if (n.children && n.children.length) {
                    item.children = convertList(n.children);
                }
                _newTree.push(item);
            });
            return _newTree;
        };
        var newTree = convertList(tree);
        return newTree;
    },
    getDeletedList: function () {
        return this.deletedList;
    },
    clearDeletedList: function () {
        this.deletedList = [];
        return true;
    },
    updateNode: function (data) {
        var treeNodes = this.target.getSelectedNodes();
        if (treeNodes[0]) {
            treeNodes[0].progCd = data.progCd;
        }
    },
    deselectNode: function () {
        ACTIONS.dispatch(ACTIONS.TREEITEM_DESELECTE);
    }
});

/**
 * formView01
 */
fnObj.formView01 = axboot.viewExtend(axboot.formView, {
    getDefaultData: function () {
        return $.extend({}, axboot.formView.defaultData, {});
    },
    initView: function () {
        var _this = this;

        this.mask = new ax5.ui.mask({
            theme: "form-mask",
            target: $('#split-panel-form'),
            content: '좌측 목차를 선택해주세요.'
        });
        this.mask.open();

        this.manualGroup = CODE.manualGroup;

        this.summernote = $('#summernote');
        this.summernote.summernote({
            height: ($('#summernote').height() - 50),                 // set editor height
            minHeight: null,             // set minimum height of editor
            maxHeight: null,             // set maximum height of editor
            focus: true,                  // set focus to editable area after initializing summernote,
            lang: 'ko-KR',
            airMode: false,
            toolbar: [
                ['style', ['style']],
                ['font', ['bold', 'italic', 'underline', 'clear']],
                ['fontname', ['fontname']],
                ['color', ['color']],
                ['para', ['ul', 'ol', 'paragraph']],
                ['height', ['height']],
                ['table', ['table']],
                ['insert', ['link', 'picture', 'hr']],
                ['view', ['codeview']],
                ['help', ['help']]
            ]
        });

        this.target = $("#formView01");
        this.model = new ax5.ui.binder();
        this.model.setModel(this.getDefaultData(), this.target);
        this.modelFormatter = new axboot.modelFormatter(this.model); // 모델 포메터 시작

        this.initEvent();

        $('[data-form-view-01-btn]').click(function () {
            var _root = fnObj;
            switch (this.getAttribute("data-form-view-01-btn")) {
                case "form-clear":
                    ACTIONS.dispatch(ACTIONS.FORM_CLEAR);
                    break;
            }
        });
    },
    initEvent: function () {
        var _this = this;
    },
    getData: function () {
        var data = this.modelFormatter.getClearData(this.model.get()); // 모델의 값을 포멧팅 전 값으로 치환.

        data.content = this.summernote.summernote("code");

        return data;
    },
    setData: function (data) {
        this.mask.close();
        $.extend(true, data, this.getDefaultData());
        this.model.setModel(data);
        console.log(this.summernote.summernote("code", data.content||""));
    },
    resize: function(){
        $('.note-editable.panel-body').height(($('#summernote').height() - 200));
    },
    clear: function () {
        this.mask.open();
        this.model.setModel(this.getDefaultData());
    }
});<|endoftext|>


<commit_before>/**
 *
 * VariableDraggableAttr
 */

/* eslint-disable react/no-find-dom-node */
import React from 'react';
import PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
import {
  DragSource,
  DropTarget,
} from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import { get, flow } from 'lodash';
import cn from 'classnames';
import ClickOverHint from 'components/ClickOverHint';
import DraggedRemovedIcon  from 'components/DraggedRemovedIcon';
import VariableEditIcon from 'components/VariableEditIcon';
import ItemTypes from 'utils/ItemTypes';
import Carret from './Carret';
import styles from './styles.scss';

const getBootstrapClass = attrType => {
  switch(attrType) {
    case 'checkbox':
    case 'boolean':
    case 'toggle':
    case 'date':
      return {
        bootstrap: 'col-md-4',
        wrapper: cn(styles.attrWrapper),
        withLongerHeight: false,
      };
    case 'json':
    case 'wysiwyg':
    case 'WYSIWYG':
      return {
        bootstrap: 'col-md-12',
        wrapper: cn(styles.attrWrapper, styles.customHeight),
        withLongerHeight: true,
      };
    case 'file':
      return {
        bootstrap: 'col-md-6',
        wrapper: cn(styles.attrWrapper, styles.customHeight),
        withLongerHeight: true,
      };
    default:
      return {
        bootstrap: 'col-md-6',
        wrapper: cn(styles.attrWrapper),
        withLongerHeight: false,
      };
  }
};
const variableDraggableAttrSource = {
  beginDrag: (props, monitor, component) => {
    props.beginMove(props.name, props.index, props.keys);

    return {
      component,
      id: props.id,
      index: props.index,
    };
  },
  endDrag: props => {
    props.endMove(props.keys);
    return {};
  },
};
const variableDraggableAttrTarget = {
  hover: (props, monitor, component) => {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Determine rectangle on screen
    const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; // NOTE: Change the divider for wysiwyg

    // Determine mouse position
    const clientOffset = monitor.getClientOffset();

    // Get pixels to the top
    const hoverClientY = clientOffset.y - hoverBoundingRect.top;

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return;
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return;
    }

    // Time to actually perform the action
    props.moveAttr(dragIndex, hoverIndex, props.keys);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  },
};

class VariableDraggableAttr extends React.Component {
  constructor(props) {
    super(props);
    const { data, layout, name } = this.props;
    const appearance = get(layout, [name, 'appearance'], '');
    const type = appearance !== '' ? appearance : data.type;
    let classNames = getBootstrapClass(type);
    let style = {};

    if (!type) {
      style = { backgroundColor: 'blue', opacity: 1, display: 'none' };
      classNames = {
        bootstrap: name.split('__')[1],
        wrapper: cn(styles.attrWrapper),
        withLongerHeight: false,
      };
    }
    this.state = {
      classNames,
      dragStart: false,
      isOver: false,
      style,
    };
  }

  componentDidMount() {
    // Use empty image as a drag preview so browsers don't draw it
    // and we can draw whatever we want on the custom drag layer instead.
    this.props.connectDragPreview(getEmptyImage(), {
    // IE fallback: specify that we'd rather screenshot the node
    // when it already knows it's being dragged so we can hide it with CSS.
    // Removginv the fallabck makes it handle variable height elements
      // captureDraggingState: true,
    });
  }

  componentDidUpdate(prevProps) {
    if (prevProps.isDragging !== this.props.isDragging) {
      this.handleDragEffect();
    }
  }

  handleClickEdit = () => {
    this.props.onClickEdit(this.props.index);
  }

  handleDragEffect = () => this.setState(prevState => ({ dragStart: !prevState.dragStart }));

  handleMouseEnter= () => {
    this.setState({ isOver: true });
  }

  handleMouseLeave = () => this.setState({ isOver: false });

  handleRemove = (e) => {
    e.preventDefault();
    e.stopPropagation();
    const { index, keys, onRemove } = this.props;
    onRemove(index, keys);
  }

  renderContent = () => {
    let { classNames, isOver, style, dragStart } = this.state;
    const { data, hoverIndex, index, isEditing, name } = this.props;
    const isFullSize = classNames.bootstrap === 'col-md-12';
    const showHint = data.type !== 'boolean';
    const showCarret = hoverIndex === index;
    const carretStyle = (() => {
      let style = { height: '30px' };

      if (classNames.withLongerHeight) {
        style = { height: '84px' };
      }

      if (isFullSize) {
        style = { width: '100%', height: '10px', marginBottom: '6px' };
      }

      return style;
    })();

    if (dragStart && isFullSize) {
      return <Carret style={carretStyle} />;
    }

    // if (hoverIndex === index) {
    //   style = { : 'red' };
    // }

    return (

        { showCarret && <Carret style={carretStyle} />}

            {name}

          {showHint && <ClickOverHint show={isOver} /> }
          {!isOver && get(data, 'name', '').toLowerCase() !== get(data, 'label', '').toLowerCase() && (

              {data.label}

          )}
          {isEditing && !isOver ? (
            <VariableEditIcon withLongerHeight={classNames.withLongerHeight} onClick={this.handleClickEdit} />
          ) : (
            <DraggedRemovedIcon withLongerHeight={classNames.withLongerHeight} onRemove={this.handleRemove} />
          )}

    );
  }

  render() {
    const { classNames } = this.state;
    const {
      connectDragSource,
      connectDropTarget,
    } = this.props;

    return (
      connectDragSource(
        connectDropTarget(

            {this.renderContent()}

        ),
      )
    );
  }
}

VariableDraggableAttr.defaultProps = {
  data: {
    type: 'text',
  },
  hoverIndex: -1,
  index: 0,
  isDragging: false,
  isEditing: false,
  keys: '',
  layout: {},
  name: '',
  onClickEdit: () => {},
  onRemove: () => {},
};

VariableDraggableAttr.propTypes = {
  connectDragPreview: PropTypes.func.isRequired,
  connectDragSource: PropTypes.func.isRequired,
  connectDropTarget: PropTypes.func.isRequired,
  data: PropTypes.object,
  hoverIndex: PropTypes.number,
  index: PropTypes.number,
  isDragging: PropTypes.bool,
  isEditing: PropTypes.bool,
  keys: PropTypes.string,
  layout: PropTypes.object,
  name: PropTypes.string,
  onClickEdit: PropTypes.func,
  onRemove: PropTypes.func,
};

const withDropTarget = DropTarget(ItemTypes.VARIABLE, variableDraggableAttrTarget, (connect) => ({
  connectDropTarget: connect.dropTarget(),
}));
const withDragSource = DragSource(ItemTypes.VARIABLE, variableDraggableAttrSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  connectDragPreview: connect.dragPreview(),
  isDragging: monitor.isDragging(),
}));

export default flow([withDropTarget, withDragSource])(VariableDraggableAttr);<commit_msg>Display hint for boolean<commit_after>/**
 *
 * VariableDraggableAttr
 */

/* eslint-disable react/no-find-dom-node */
import React from 'react';
import PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
import {
  DragSource,
  DropTarget,
} from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import { get, flow } from 'lodash';
import cn from 'classnames';
import ClickOverHint from 'components/ClickOverHint';
import DraggedRemovedIcon  from 'components/DraggedRemovedIcon';
import VariableEditIcon from 'components/VariableEditIcon';
import ItemTypes from 'utils/ItemTypes';
import Carret from './Carret';
import styles from './styles.scss';

const getBootstrapClass = attrType => {
  switch(attrType) {
    case 'checkbox':
    case 'boolean':
    case 'toggle':
    case 'date':
      return {
        bootstrap: 'col-md-4',
        wrapper: cn(styles.attrWrapper),
        withLongerHeight: false,
      };
    case 'json':
    case 'wysiwyg':
    case 'WYSIWYG':
      return {
        bootstrap: 'col-md-12',
        wrapper: cn(styles.attrWrapper, styles.customHeight),
        withLongerHeight: true,
      };
    case 'file':
      return {
        bootstrap: 'col-md-6',
        wrapper: cn(styles.attrWrapper, styles.customHeight),
        withLongerHeight: true,
      };
    default:
      return {
        bootstrap: 'col-md-6',
        wrapper: cn(styles.attrWrapper),
        withLongerHeight: false,
      };
  }
};
const variableDraggableAttrSource = {
  beginDrag: (props, monitor, component) => {
    props.beginMove(props.name, props.index, props.keys);

    return {
      component,
      id: props.id,
      index: props.index,
    };
  },
  endDrag: props => {
    props.endMove(props.keys);
    return {};
  },
};
const variableDraggableAttrTarget = {
  hover: (props, monitor, component) => {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Determine rectangle on screen
    const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; // NOTE: Change the divider for wysiwyg

    // Determine mouse position
    const clientOffset = monitor.getClientOffset();

    // Get pixels to the top
    const hoverClientY = clientOffset.y - hoverBoundingRect.top;

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return;
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return;
    }

    // Time to actually perform the action
    props.moveAttr(dragIndex, hoverIndex, props.keys);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  },
};

class VariableDraggableAttr extends React.Component {
  constructor(props) {
    super(props);
    const { data, layout, name } = this.props;
    const appearance = get(layout, [name, 'appearance'], '');
    const type = appearance !== '' ? appearance : data.type;
    let classNames = getBootstrapClass(type);
    let style = {};

    if (!type) {
      style = { display: 'none' };
      classNames = {
        bootstrap: name.split('__')[1],
        wrapper: cn(styles.attrWrapper),
        withLongerHeight: false,
      };
    }
    this.state = {
      classNames,
      dragStart: false,
      isOver: false,
      style,
    };
  }

  componentDidMount() {
    // Use empty image as a drag preview so browsers don't draw it
    // and we can draw whatever we want on the custom drag layer instead.
    this.props.connectDragPreview(getEmptyImage(), {
    // IE fallback: specify that we'd rather screenshot the node
    // when it already knows it's being dragged so we can hide it with CSS.
    // Removginv the fallabck makes it handle variable height elements
      // captureDraggingState: true,
    });
  }

  componentDidUpdate(prevProps) {
    if (prevProps.isDragging !== this.props.isDragging) {
      this.handleDragEffect();
    }
  }

  handleClickEdit = () => {
    this.props.onClickEdit(this.props.index);
  }

  handleDragEffect = () => this.setState(prevState => ({ dragStart: !prevState.dragStart }));

  handleMouseEnter= () => {
    this.setState({ isOver: true });
  }

  handleMouseLeave = () => this.setState({ isOver: false });

  handleRemove = (e) => {
    e.preventDefault();
    e.stopPropagation();
    const { index, keys, onRemove } = this.props;
    onRemove(index, keys);
  }

  renderContent = () => {
    let { classNames, isOver, style, dragStart } = this.state;
    const { data, hoverIndex, index, isEditing, name } = this.props;
    const isFullSize = classNames.bootstrap === 'col-md-12';
    const showCarret = hoverIndex === index;
    const carretStyle = (() => {
      let style = { height: '30px' };

      if (classNames.withLongerHeight) {
        style = { height: '84px' };
      }

      if (isFullSize) {
        style = { width: '100%', height: '10px', marginBottom: '6px' };
      }

      return style;
    })();

    if (dragStart && isFullSize) {
      return <Carret style={carretStyle} />;
    }

    return (

        { showCarret && <Carret style={carretStyle} />}

            {name}

          <ClickOverHint show={isOver} />
          {!isOver && get(data, 'name', '').toLowerCase() !== get(data, 'label', '').toLowerCase() && (

              {data.label}

          )}
          {isEditing && !isOver ? (
            <VariableEditIcon withLongerHeight={classNames.withLongerHeight} onClick={this.handleClickEdit} />
          ) : (
            <DraggedRemovedIcon withLongerHeight={classNames.withLongerHeight} onRemove={this.handleRemove} />
          )}

    );
  }

  render() {
    const { classNames } = this.state;
    const {
      connectDragSource,
      connectDropTarget,
    } = this.props;

    return (
      connectDragSource(
        connectDropTarget(

            {this.renderContent()}

        ),
      )
    );
  }
}

VariableDraggableAttr.defaultProps = {
  data: {
    type: 'text',
  },
  hoverIndex: -1,
  index: 0,
  isDragging: false,
  isEditing: false,
  keys: '',
  layout: {},
  name: '',
  onClickEdit: () => {},
  onRemove: () => {},
};

VariableDraggableAttr.propTypes = {
  connectDragPreview: PropTypes.func.isRequired,
  connectDragSource: PropTypes.func.isRequired,
  connectDropTarget: PropTypes.func.isRequired,
  data: PropTypes.object,
  hoverIndex: PropTypes.number,
  index: PropTypes.number,
  isDragging: PropTypes.bool,
  isEditing: PropTypes.bool,
  keys: PropTypes.string,
  layout: PropTypes.object,
  name: PropTypes.string,
  onClickEdit: PropTypes.func,
  onRemove: PropTypes.func,
};

const withDropTarget = DropTarget(ItemTypes.VARIABLE, variableDraggableAttrTarget, (connect) => ({
  connectDropTarget: connect.dropTarget(),
}));
const withDragSource = DragSource(ItemTypes.VARIABLE, variableDraggableAttrSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  connectDragPreview: connect.dragPreview(),
  isDragging: monitor.isDragging(),
}));

export default flow([withDropTarget, withDragSource])(VariableDraggableAttr);<|endoftext|>


<commit_before>(function (window, document, undefined) {
    "use strict";

    if (window.GG !== undefined) {
        return;
    }

    ////////////////////////////////////////////////////////////////
    // namespace
    window.GG = {};
    var GG = window.GG;

    GG.namespace = function () {
        var a = arguments, o = null, i, j, d;
        for (i = 0; i < a.length; i++) {
            d = a[i].split(".");
            o = window;
            for (j = 0; j < d.length; j++) {
                o[d[j]] = o[d[j]] || {};
                o = o[d[j]];
            }
        }
        return o;
    };

    ////////////////////////////////////////////////////////////////
    // Class
    var _initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
    // The base Class implementation (does nothing)
    var Class = function () { };
    // Create a new Class that inherits from this class
    Class.extend = function (prop) {
        var _super = this.prototype;

        // Instantiate a base class (but only create the instance,
        // don't run the init constructor)
        _initializing = true;
        var prototype = new this();
        _initializing = false;

        // Copy the properties over onto the new prototype
        for (var name in prop) {
            // Check if we're overwriting an existing function
            prototype[name] = typeof prop[name] === "function" &&
                typeof _super[name] === "function" && fnTest.test(prop[name]) ?
                (function (name, fn) {
                    return function () {
                        var tmp = this._super;

                        // Add a new ._super() method that is the same method
                        // but on the super-class
                        this._super = _super[name];

                        // The method only need to be bound temporarily, so we
                        // remove it when we're done executing
                        var ret = fn.apply(this, arguments);
                        this._super = tmp;

                        return ret;
                    };
                })(name, prop[name]) :
                prop[name];
        }

        // The dummy class constructor
        function NewClass() {
            // All construction is actually done in the init method
            if (!_initializing && this.init) {
                this.init.apply(this, arguments);
            }
        }

        // Populate our constructed prototype object
        NewClass.prototype = prototype;

        // Enforce the constructor to be what we expect
        NewClass.prototype.constructor = NewClass;

        // And make this class extendable
        NewClass.extend = Class.extend;

        return NewClass;
    };

    GG.Class = Class;

})(window, document);
<commit_msg>Update<commit_after>(function (window, document, undefined) {
    'use strict';

    if (window.PN !== undefined) {
        return;
    }

    ////////////////////////////////////////////////////////////////
    // namespace
    window.PN = {};
    var PN = window.PN;

    PN.namespace = function () {
        var a = arguments, o = null, i, j, d;
        for (i = 0; i < a.length; i++) {
            d = a[i].split('.');
            o = window;
            for (j = 0; j < d.length; j++) {
                o[d[j]] = o[d[j]] || {};
                o = o[d[j]];
            }
        }
        return o;
    };

    ////////////////////////////////////////////////////////////////
    // Class
    var _initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
    // The base Class implementation (does nothing)
    var Class = function () { };
    // Create a new Class that inherits from this class
    Class.extend = function (prop) {
        var _super = this.prototype;

        // Instantiate a base class (but only create the instance,
        // don't run the init constructor)
        _initializing = true;
        var prototype = new this();
        _initializing = false;

        // Copy the properties over onto the new prototype
        for (var name in prop) {
            // Check if we're overwriting an existing function
            prototype[name] = typeof prop[name] === 'function' &&
                typeof _super[name] === 'function' && fnTest.test(prop[name]) ?
                (function (name, fn) {
                    return function () {
                        var tmp = this._super;

                        // Add a new ._super() method that is the same method
                        // but on the super-class
                        this._super = _super[name];

                        // The method only need to be bound temporarily, so we
                        // remove it when we're done executing
                        var ret = fn.apply(this, arguments);
                        this._super = tmp;

                        return ret;
                    };
                })(name, prop[name]) :
                prop[name];
        }

        // The dummy class constructor
        function NewClass() {
            // All construction is actually done in the init method
            if (!_initializing && this.init) {
                this.init.apply(this, arguments);
            }
        }

        // Populate our constructed prototype object
        NewClass.prototype = prototype;

        // Enforce the constructor to be what we expect
        NewClass.prototype.constructor = NewClass;

        // And make this class extendable
        NewClass.extend = Class.extend;

        return NewClass;
    };

    PN.Class = Class;

})(window, document);
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Button, Input, Select, Card, Alert, Spin, Icon, message, Collapse } from 'antd'
import { autobind } from 'core-decorators';
import crossRequest from 'cross-request';
import { withRouter } from 'react-router';
// import axios from 'axios';
import URL from 'url';

// import {
// } from '../../../reducer/modules/group.js'

import './Run.scss'

const { TextArea } = Input;
const InputGroup = Input.Group;
const Option = Select.Option;
const Panel = Collapse.Panel;

@connect(
  state => ({
    currInterface: state.inter.curdata,
    currProject: state.project.currProject
    // reqParams: state.addInterface.reqParams,
    // method: state.addInterface.method,
    // url: state.addInterface.url,
    // seqGroup: state.addInterface.seqGroup,
    // interfaceName: state.addInterface.interfaceName,
  })
)
@withRouter
export default class Run extends Component {

  static propTypes = {
    match: PropTypes.object,
    currProject: PropTypes.object,
    currInterface: PropTypes.object,
    reqParams: PropTypes.string,
    // method: PropTypes.string,
    // url: PropTypes.string,
    interfaceName: PropTypes.string
    // seqGroup: PropTypes.array,
  }

  state = {
    res: '',
    method: 'GET',
    domains: [],
    pathname: '',
    query: {},
    params: {},
    paramsNotJson: false,
    headers: {},
    currDomain: '',
    paramsType: 'from'
  }

  constructor(props) {
    super(props)
  }

  componentWillMount() {
    this.getInterfaceState()
  }

  componentWillReceiveProps(nextProps) {
    this.getInterfaceState(nextProps)
  }

  @autobind
  getInterfaceState(nextProps) {
    const props = nextProps || this.props;
    const { currInterface, currProject } = props;
    console.log('currInterface', currInterface)
    console.log('currProject', currProject)
    const { method, path: url, req_headers } = currInterface;
    const { prd_host, basepath, protocol, env } = currProject;
    const pathname = (basepath + url).replace(/\/+/g, '/');

    const domains = {prd: protocol + '://' + prd_host};
    env.forEach(item => {
      domains[item.name] = item.domain;
    })

    const query = [];
    let params = [];
    let reqParams = this.props.reqParams ? this.props.reqParams : '{}';
    let paramsNotJson = false;
    try {
      reqParams = JSON.parse(reqParams);
      // paramsNotJson = false;
    } catch (e) {
      // paramsNotJson = true;
      reqParams = {};
      message.error('请求参数不是 JSON 格式');
    }
    if (method === 'GET') {
      Object.keys(reqParams).forEach(key => {
        const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
        query.push({key, value})
      })
    } else if (method === 'POST') {
      // params = reqParams;
      Object.keys(reqParams).forEach(key => {
        const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
        query.push({key, value, type: 'text'})
      })
    }

    const headers = []
    let contentTypeIndex = -1;
    req_headers.forEach((headerItem, index) => {
      if (headerItem.name) {
        // TODO 'Content-Type' 排除大小写不同格式影响
        if (headerItem.name === 'Content-Type'){
          contentTypeIndex = index;
          headerItem.value = headerItem.value || 'application/x-www-form-urlencoded';
        }
        headers.push({name: headerItem.name, value: headerItem.value});
      }
    })
    if (contentTypeIndex === -1) {
      headers.push({name: 'Content-Type', value: 'application/x-www-form-urlencoded'});
    }

    this.setState({
      method,
      domains,
      pathname,
      query,
      params,
      paramsNotJson,
      headers,
      currDomain: domains.prd,
      loading: false,
      paramsType: 'form'
    });
  }

  @autobind
  requestInterface() {
    const { headers, params, currDomain, method, pathname, query } = this.state;
    const urlObj = URL.parse(currDomain);

    const href = URL.format({
      protocol: urlObj.protocol || 'http',
      host: urlObj.host,
      pathname,
      query: this.getQueryObj(query)
    });

    this.setState({ loading: true })

    crossRequest({
      url: href,
      method,
      headers: this.getHeadersObj(headers),
      data: this.arrToObj(params),
      files: this.getFiles(params),
      success: (res) => {
        try {
          res = JSON.parse(res)
        } catch (e) {
          null
        }
        this.setState({res})
        this.setState({ loading: false })
      },
      error: (err) => {
        this.setState({res: err || '请求失败'})
        this.setState({ loading: false })
      }
    })
  }

  @autobind
  changeDomain(value) {
    this.setState({ currDomain: value });
  }

  @autobind
  selectDomain(value) {
    this.setState({ currDomain: value });
  }

  @autobind
  changeHeader(e, index, isName) {
    const headers = JSON.parse(JSON.stringify(this.state.headers));
    const v = e.target.value;
    if (isName) {
      headers[index].name = v;
    } else {
      headers[index].value = v;
    }
    this.setState({ headers });
  }
  @autobind
  addHeader() {
    const { headers } = this.state;
    this.setState({headers: headers.concat([{name: '', value: ''}])})
  }
  @autobind
  deleteHeader(index) {
    const { headers } = this.state;
    this.setState({headers: headers.filter((item, i) => +index !== +i)});
  }
  @autobind
  setContentType(type) {
    const headersObj = this.getHeadersObj(this.state.headers);
    headersObj['Content-Type'] = type;
    this.setState({headers: this.objToArr(headersObj, 'name')})
  }

  @autobind
  changeQuery(e, index, isKey) {
    const query = JSON.parse(JSON.stringify(this.state.query));
    const v = e.target.value;
    if (isKey) {
      query[index].key = v;
    } else {
      query[index].value = v;
    }
    this.setState({ query });
  }
  @autobind
  addQuery() {
    const { query } = this.state;
    this.setState({query: query.concat([{key: '', value: ''}])})
  }
  @autobind
  deleteQuery(index) {
    const { query } = this.state;
    this.setState({query: query.filter((item, i) => +index !== +i)});
  }

  @autobind
  changeParams(e, index, type) {
    const params = JSON.parse(JSON.stringify(this.state.params));
    switch (type) {
      case 'key':
        params[index].key = e.target.value
        break;
      case 'type':
        params[index].type = e
        break;
      case 'value':
        if (params[index].type === 'file') {
          params[index].value = e.target.id
        } else {
          params[index].value = e.target.value
        }
        break;
      default:
        break;
    }
    if (type === 'type' && e === 'file') {
      this.setContentType('multipart/form-data')
    }
    this.setState({ params });
  }
  @autobind
  addParams() {
    const { params } = this.state;
    this.setState({params: params.concat([{key: '', value: '', type: 'text'}])})
  }
  @autobind
  deleteParams(index) {
    const { params } = this.state;
    this.setState({params: params.filter((item, i) => +index !== +i)});
  }

  @autobind
  changeMethod(value) {
    this.setState({ method: value });
  }

  @autobind
  changePath(e) {
    const path = e.target.value;
    const urlObj = URL.parse(path, true);
    this.setState({
      query: this.objToArr(urlObj.query),
      pathname: urlObj.pathname
    })
  }

  @autobind
  changeParamsType(value) {
    this.setState({paramsType: value})
  }

  hasCrossRequestPlugin() {
    const dom = document.getElementById('y-request');
    return dom.getAttribute('key') === 'yapi';
  }

  objToArr(obj, key, value) {
    const keyName = key || 'key';
    const valueName = value || 'value';
    const arr = []
    Object.keys(obj).forEach((_key) => {
      if (_key) {
        arr.push({[keyName]: _key, [valueName]: obj[_key]});
      }
    })
    return arr;
  }
  arrToObj(arr) {
    const obj = {};
    arr.forEach(item => {
      if (item.key && item.type !== 'file') {
        obj[item.key] = item.value || '';
      }
    })
    return obj;
  }
  getFiles(params) {
    const files = {};
    params.forEach(item => {
      if (item.key && item.type === 'file') {
        files[item.key] = item.value
      }
    })
    return files;
  }
  getQueryObj(query) {
    const queryObj = {};
    query.forEach(item => {
      if (item.key) {
        queryObj[item.key] = item.value || '';
      }
    })
    return queryObj;
  }
  getHeadersObj(headers) {
    const headersObj = {};
    headers.forEach(item => {
      if (item.name && item.value) {
        headersObj[item.name] = item.value;
      }
    })
    return headersObj;
  }

  @autobind
  fileChange(e, index) {
    console.log(e)
    console.log(index)
  }

  render () {

    const { method, domains, pathname, query, headers, params, currDomain, paramsType } = this.state;
    const hasPlugin = this.hasCrossRequestPlugin();
    const search = decodeURIComponent(URL.format({query: this.getQueryObj(query)}));

    return (

          {
            hasPlugin ? '' : (
              <Alert
                message={

                    温馨提示：当前正在使用接口测试服务，请安装我们为您免费提供的
                    测试增强插件 [点击获取]！

                }
                type="warning"
              />
            )
          }

        {/*
{interfaceName}
*/}

        <Card title="请求部分" noHovering className="req-part">

            <InputGroup compact style={{display: 'flex'}}>
              <Select value={method} style={{flexBasis: 60}} onChange={this.changeMethod} >
                <Option value="GET">GET</Option>
                <Option value="POST">POST</Option>
              </Select>
              <Select value={currDomain} mode="combobox" filterOption={() => true} style={{flexBasis: 180, flexGrow: 1}} onChange={this.changeDomain} onSelect={this.selectDomain}>
                {
                  Object.keys(domains).map((key, index) => (<Option value={domains[key]} key={index}>{key + '：' + domains[key]}</Option>))
                }
              </Select>
              <Input value={pathname + search} onChange={this.changePath} spellCheck="false" style={{flexBasis: 180, flexGrow: 1}} />
            </InputGroup>
            <Button
              onClick={this.requestInterface}
              type="primary"
              style={{marginLeft: 10}}
              loading={this.state.loading}
            >发送</Button>

          <Collapse defaultActiveKey={['1', '2', '3']} bordered={true}>
            <Panel header="QUERY PARAMETERS" key="1">
              {
                query.map((item, index) => {
                  return (

                      <Input value={item.key} onChange={e => this.changeQuery(e, index, true)} className="key" />
                      =
                      <Input value={item.value} onChange={e => this.changeQuery(e, index)} className="value" />
                      <Icon type="delete" className="icon-btn" onClick={() => this.deleteQuery(index)} />

                  )
                })
              }
              <Button type="primary" icon="plus" onClick={this.addQuery}>Add query parameter</Button>
            </Panel>
            <Panel header="HEADERS" key="2" >
              {
                headers.map((item, index) => {
                  return (

                      <Input value={item.name} onChange={e => this.changeHeader(e, index, true)} className="key" />{' = '}
                      <Input value={item.value} onChange={e => this.changeHeader(e, index)} className="value" />
                      <Icon type="delete" className="icon-btn" onClick={() => this.deleteHeader(index)} />

                  )
                })
              }
              <Button type="primary" icon="plus" onClick={this.addHeader}>Add header</Button>
            </Panel>
            <Panel
              header={

BODY

 e.stopPropagation()} style={{marginRight: 5}}>
                    <Select defaultValue={paramsType} onChange={this.changeParamsType} className={method === 'POST' ? '' : 'hidden'}>
                      <Option value="text">Text</Option>
                      <Option value="file">File</Option>
                      <Option value="form">Form</Option>
                    </Select>

              }
              key="3"
            >
              { method === 'POST' && paramsType !== 'form' && paramsType !== 'file' &&

                  <TextArea
                    value={params}
                    style={{margin: 10}}
                    autosize={{ minRows: 2, maxRows: 6 }}
                  ></TextArea>

{paramsType}

              }
              {
                method === 'POST' && paramsType === 'form' && (

                    {
                      params.map((item, index) => {
                        return (

                            <Input value={item.key} onChange={e => this.changeParams(e, index, 'key')} style={{display: 'inline-block', width: 200, margin: 10}} />
                            [<Select value={item.type} onChange={e => this.changeParams(e, index, 'type')}>
                              <Option value="file">File</Option>
                              <Option value="text">Text</Option>
                            </Select>]{' = '}
                            {item.type === 'file' ?
                              <Input type="file" id={'file_' + index} onChange={e => this.changeParams(e, index, 'value')} multiple style={{display: 'inline-block', width: 200, margin: 10}} /> :
                              <Input value={item.value} onChange={e => this.changeParams(e, index, 'value')} style={{display: 'inline-block', width: 200, margin: 10}} />
                            }

                        )
                      })
                    }
                    <Button type="primary" icon="plus" onClick={this.addParams}>Add form parameter</Button>

                )
              }
              {
                method === 'POST' && paramsType === 'file' && (

                    <Input type="file"></Input>

                )
              }
              {
                method !== 'POST' && (

GET 请求没有 Body。

                )
              }
            </Panel>
          </Collapse>
        </Card>

        <Card title="返回结果" noHovering className="resp-part">
          <Spin spinning={this.state.loading}>

                <TextArea
                  value={typeof this.state.res === 'object' ? JSON.stringify(this.state.res, null, 2) : this.state.res.toString()}
                  autosize={{ minRows: 2, maxRows: 6 }}
                ></TextArea>

          </Spin>
        </Card>

    )
  }
}
<commit_msg>feat: run<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Button, Input, Select, Card, Alert, Spin, Icon, message, Collapse, Radio } from 'antd'
import { autobind } from 'core-decorators';
import crossRequest from 'cross-request';
import { withRouter } from 'react-router';
// import axios from 'axios';
import URL from 'url';

// import {
// } from '../../../reducer/modules/group.js'

import './Run.scss'

const { TextArea } = Input;
const InputGroup = Input.Group;
const Option = Select.Option;
const Panel = Collapse.Panel;
const RadioButton = Radio.Button;
const RadioGroup = Radio.Group;

@connect(
  state => ({
    currInterface: state.inter.curdata,
    currProject: state.project.currProject
    // reqParams: state.addInterface.reqParams,
    // method: state.addInterface.method,
    // url: state.addInterface.url,
    // seqGroup: state.addInterface.seqGroup,
    // interfaceName: state.addInterface.interfaceName,
  })
)
@withRouter
export default class Run extends Component {

  static propTypes = {
    match: PropTypes.object,
    currProject: PropTypes.object,
    currInterface: PropTypes.object,
    reqParams: PropTypes.string,
    // method: PropTypes.string,
    // url: PropTypes.string,
    interfaceName: PropTypes.string
    // seqGroup: PropTypes.array,
  }

  state = {
    res: '',
    method: 'GET',
    domains: [],
    pathname: '',
    query: {},
    params: {},
    paramsNotJson: false,
    headers: {},
    currDomain: '',
    paramsType: 'from'
  }

  constructor(props) {
    super(props)
  }

  componentWillMount() {
    this.getInterfaceState()
  }

  componentWillReceiveProps(nextProps) {
    this.getInterfaceState(nextProps)
  }

  @autobind
  getInterfaceState(nextProps) {
    const props = nextProps || this.props;
    const { currInterface, currProject } = props;
    console.log('currInterface', currInterface)
    console.log('currProject', currProject)
    const { method, path: url, req_headers } = currInterface;
    const { prd_host, basepath, protocol, env } = currProject;
    const pathname = (basepath + url).replace(/\/+/g, '/');

    const domains = {prd: protocol + '://' + prd_host};
    env.forEach(item => {
      domains[item.name] = item.domain;
    })

    const query = [];
    let params = [];
    let reqParams = this.props.reqParams ? this.props.reqParams : '{}';
    let paramsNotJson = false;
    try {
      reqParams = JSON.parse(reqParams);
      // paramsNotJson = false;
    } catch (e) {
      // paramsNotJson = true;
      reqParams = {};
      message.error('请求参数不是 JSON 格式');
    }
    if (method === 'GET') {
      Object.keys(reqParams).forEach(key => {
        const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
        query.push({key, value})
      })
    } else if (method === 'POST') {
      // params = reqParams;
      Object.keys(reqParams).forEach(key => {
        const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString();
        query.push({key, value, type: 'text'})
      })
    }

    const headers = []
    let contentTypeIndex = -1;
    req_headers.forEach((headerItem, index) => {
      if (headerItem.name) {
        // TODO 'Content-Type' 排除大小写不同格式影响
        if (headerItem.name === 'Content-Type'){
          contentTypeIndex = index;
          headerItem.value = headerItem.value || 'application/x-www-form-urlencoded';
        }
        headers.push({name: headerItem.name, value: headerItem.value});
      }
    })
    if (contentTypeIndex === -1) {
      headers.push({name: 'Content-Type', value: 'application/x-www-form-urlencoded'});
    }

    this.setState({
      method,
      domains,
      pathname,
      query,
      params,
      paramsNotJson,
      headers,
      currDomain: domains.prd,
      loading: false,
      paramsType: 'form'
    });
  }

  @autobind
  requestInterface() {
    const { headers, params, currDomain, method, pathname, query } = this.state;
    const urlObj = URL.parse(currDomain);

    const href = URL.format({
      protocol: urlObj.protocol || 'http',
      host: urlObj.host,
      pathname,
      query: this.getQueryObj(query)
    });

    this.setState({ loading: true })

    crossRequest({
      url: href,
      method,
      headers: this.getHeadersObj(headers),
      data: this.arrToObj(params),
      files: this.getFiles(params),
      success: (res) => {
        try {
          res = JSON.parse(res)
        } catch (e) {
          null
        }
        this.setState({res})
        this.setState({ loading: false })
      },
      error: (err) => {
        this.setState({res: err || '请求失败'})
        this.setState({ loading: false })
      }
    })
  }

  @autobind
  changeDomain(value) {
    this.setState({ currDomain: value });
  }

  @autobind
  selectDomain(value) {
    this.setState({ currDomain: value });
  }

  @autobind
  changeHeader(e, index, isName) {
    const headers = JSON.parse(JSON.stringify(this.state.headers));
    const v = e.target.value;
    if (isName) {
      headers[index].name = v;
    } else {
      headers[index].value = v;
    }
    this.setState({ headers });
  }
  @autobind
  addHeader() {
    const { headers } = this.state;
    this.setState({headers: headers.concat([{name: '', value: ''}])})
  }
  @autobind
  deleteHeader(index) {
    const { headers } = this.state;
    this.setState({headers: headers.filter((item, i) => +index !== +i)});
  }
  @autobind
  setContentType(type) {
    const headersObj = this.getHeadersObj(this.state.headers);
    headersObj['Content-Type'] = type;
    this.setState({headers: this.objToArr(headersObj, 'name')})
  }

  @autobind
  changeQuery(e, index, isKey) {
    const query = JSON.parse(JSON.stringify(this.state.query));
    const v = e.target.value;
    if (isKey) {
      query[index].key = v;
    } else {
      query[index].value = v;
    }
    this.setState({ query });
  }
  @autobind
  addQuery() {
    const { query } = this.state;
    this.setState({query: query.concat([{key: '', value: ''}])})
  }
  @autobind
  deleteQuery(index) {
    const { query } = this.state;
    this.setState({query: query.filter((item, i) => +index !== +i)});
  }

  @autobind
  changeParams(e, index, type) {
    const params = JSON.parse(JSON.stringify(this.state.params));
    switch (type) {
      case 'key':
        params[index].key = e.target.value
        break;
      case 'type':
        params[index].type = e
        break;
      case 'value':
        if (params[index].type === 'file') {
          params[index].value = e.target.id
        } else {
          params[index].value = e.target.value
        }
        break;
      default:
        break;
    }
    if (type === 'type' && e === 'file') {
      this.setContentType('multipart/form-data')
    }
    this.setState({ params });
  }
  @autobind
  addParams() {
    const { params } = this.state;
    this.setState({params: params.concat([{key: '', value: '', type: 'text'}])})
  }
  @autobind
  deleteParams(index) {
    const { params } = this.state;
    this.setState({params: params.filter((item, i) => +index !== +i)});
  }

  @autobind
  changeMethod(value) {
    this.setState({ method: value });
  }

  @autobind
  changePath(e) {
    const path = e.target.value;
    const urlObj = URL.parse(path, true);
    this.setState({
      query: this.objToArr(urlObj.query),
      pathname: urlObj.pathname
    })
  }

  @autobind
  changeParamsType(value) {
    this.setState({paramsType: value})
  }

  hasCrossRequestPlugin() {
    const dom = document.getElementById('y-request');
    return dom.getAttribute('key') === 'yapi';
  }

  objToArr(obj, key, value) {
    const keyName = key || 'key';
    const valueName = value || 'value';
    const arr = []
    Object.keys(obj).forEach((_key) => {
      if (_key) {
        arr.push({[keyName]: _key, [valueName]: obj[_key]});
      }
    })
    return arr;
  }
  arrToObj(arr) {
    const obj = {};
    arr.forEach(item => {
      if (item.key && item.type !== 'file') {
        obj[item.key] = item.value || '';
      }
    })
    return obj;
  }
  getFiles(params) {
    const files = {};
    params.forEach(item => {
      if (item.key && item.type === 'file') {
        files[item.key] = item.value
      }
    })
    return files;
  }
  getQueryObj(query) {
    const queryObj = {};
    query.forEach(item => {
      if (item.key) {
        queryObj[item.key] = item.value || '';
      }
    })
    return queryObj;
  }
  getHeadersObj(headers) {
    const headersObj = {};
    headers.forEach(item => {
      if (item.name && item.value) {
        headersObj[item.name] = item.value;
      }
    })
    return headersObj;
  }

  @autobind
  fileChange(e, index) {
    console.log(e)
    console.log(index)
  }

  render () {

    const { method, domains, pathname, query, headers, params, currDomain, paramsType } = this.state;
    const hasPlugin = this.hasCrossRequestPlugin();
    const search = decodeURIComponent(URL.format({query: this.getQueryObj(query)}));

    return (

          {
            hasPlugin ? '' : (
              <Alert
                message={

                    温馨提示：当前正在使用接口测试服务，请安装我们为您免费提供的
                    测试增强插件 [点击获取]！

                }
                type="warning"
              />
            )
          }

        <Card title="请求部分" noHovering className="req-part">

            <InputGroup compact style={{display: 'flex'}}>
              <Select value={method} style={{flexBasis: 60}} onChange={this.changeMethod} >
                <Option value="GET">GET</Option>
                <Option value="POST">POST</Option>
              </Select>
              <Select value={currDomain} mode="combobox" filterOption={() => true} style={{flexBasis: 180, flexGrow: 1}} onChange={this.changeDomain} onSelect={this.selectDomain}>
                {
                  Object.keys(domains).map((key, index) => (<Option value={domains[key]} key={index}>{key + '：' + domains[key]}</Option>))
                }
              </Select>
              <Input value={pathname + search} onChange={this.changePath} spellCheck="false" style={{flexBasis: 180, flexGrow: 1}} />
            </InputGroup>
            <Button
              onClick={this.requestInterface}
              type="primary"
              style={{marginLeft: 10}}
              loading={this.state.loading}
            >发送</Button>

          <Collapse defaultActiveKey={['1', '2', '3']} bordered={true}>
            <Panel header="QUERY PARAMETERS" key="1">
              {
                query.map((item, index) => {
                  return (

                      <Input value={item.key} onChange={e => this.changeQuery(e, index, true)} className="key" />
                      =
                      <Input value={item.value} onChange={e => this.changeQuery(e, index)} className="value" />
                      <Icon type="delete" className="icon-btn" onClick={() => this.deleteQuery(index)} />

                  )
                })
              }
              <Button type="primary" icon="plus" onClick={this.addQuery}>Add query parameter</Button>
            </Panel>
            <Panel header="HEADERS" key="2" >
              {
                headers.map((item, index) => {
                  return (

                      <Input value={item.name} onChange={e => this.changeHeader(e, index, true)} className="key" />
                      =
                      <Input value={item.value} onChange={e => this.changeHeader(e, index)} className="value" />
                      <Icon type="delete" className="icon-btn" onClick={() => this.deleteHeader(index)} />

                  )
                })
              }
              <Button type="primary" icon="plus" onClick={this.addHeader}>Add header</Button>
            </Panel>
            <Panel
              header={

BODY

 e.stopPropagation()} style={{marginRight: 5}}>
                    <Select defaultValue={paramsType} onChange={this.changeParamsType} className={method === 'POST' ? '' : 'hidden'}>
                      <Option value="text">Text</Option>
                      <Option value="file">File</Option>
                      <Option value="form">Form</Option>
                    </Select>

              }
              key="3"
            >
              { method === 'POST' && paramsType !== 'form' && paramsType !== 'file' &&

                  <RadioGroup defaultValue="json">
                    <RadioButton value="json">JSON</RadioButton>
                    <RadioButton value="text">TEXT</RadioButton>
                    <RadioButton value="xml">XML</RadioButton>
                    <RadioButton value="html">HTML</RadioButton>
                  </RadioGroup>
                  <TextArea
                    value={params}
                    style={{marginTop: 10}}
                    autosize={{ minRows: 2, maxRows: 6 }}
                  ></TextArea>

              }
              {
                method === 'POST' && paramsType === 'form' &&

                  {
                    params.map((item, index) => {
                      return (

                          <Input value={item.key} onChange={e => this.changeParams(e, index, 'key')} className="key" />
                          [
                          <Select value={item.type} onChange={e => this.changeParams(e, index, 'type')}>
                            <Option value="file">File</Option>
                            <Option value="text">Text</Option>
                          </Select>
                          ]
                          =
                          {
                            item.type === 'file' ? <Input type="file" id={'file_' + index} onChange={e => this.changeParams(e, index, 'value')} multiple className="value" /> :
                            <Input value={item.value} onChange={e => this.changeParams(e, index, 'value')} className="value" />
                          }
                          <Icon type="delete" className="icon-btn" onClick={() => this.deleteParams(index)} />

                      )
                    })
                  }
                  <Button type="primary" icon="plus" onClick={this.addParams}>Add form parameter</Button>

              }
              {
                method === 'POST' && paramsType === 'file' &&

                  <Input type="file"></Input>

              }
              {
                method !== 'POST' &&

GET 请求没有 BODY。

              }
            </Panel>
          </Collapse>
        </Card>

        <Card title="返回结果" noHovering className="resp-part">
          <Spin spinning={this.state.loading}>

                <TextArea
                  value={typeof this.state.res === 'object' ? JSON.stringify(this.state.res, null, 2) : this.state.res.toString()}
                  autosize={{ minRows: 2, maxRows: 6 }}
                ></TextArea>

          </Spin>
        </Card>

    )
  }
}
<|endoftext|>