swagger-ui-v1

swagger-ui-v1 添加查找接口交互

目前为 2018-01-03 提交的版本。查看 最新版本

// ==UserScript==
// @name         swagger-ui-v1
// @description  swagger-ui-v1 添加查找接口交互
// @namespace swagger
// @match *://*/*/swagger-ui.html*
// @match *://*/swagger-ui.html*
// @grant none
// @version 0.0.1.20180103031415
// ==/UserScript==

window.addEventListener('load', function () {
  /**
 * @file: EventEmitter
 * @author: Cuttle Cong
 * @date: 2017/11/1
 * @description:
 */
function assertType(type) {
    if (typeof type !== 'string') {
        throw new TypeError('type is not type of String!');
    }
}

function assertFn(fn) {
    if (typeof fn !== 'function') {
        throw new TypeError('fn is not type of Function!');
    }
}

function EventEmitter() {
    this._events = {};
}

function on(type, fn) {
    assertType(type);
    assertFn(fn);
    this._events[type] = this._events[type] || [];
    this._events[type].push({
        type: 'always',
        fn: fn
    });
}

function prepend(type, fn) {
    assertType(type);
    assertFn(fn);
    this._events[type] = this._events[type] || [];
    this._events[type].unshift({
        type: 'always',
        fn: fn
    });
}

function prependOnce(type, fn) {
    assertType(type);
    assertFn(fn);
    this._events[type] = this._events[type] || [];
    this._events[type].unshift({
        type: 'once',
        fn
    });
}

function once(type, fn) {
    assertType(type);
    assertFn(fn);
    this._events[type] = this._events[type] || [];
    this._events[type].push({
        type: 'once',
        fn
    });
}

function off(type, nullOrFn) {
    assertType(type);
    if (!this._events[type]) return;
    if (typeof nullOrFn === 'function') {
        var index = this._events[type].findIndex(function (event) {
            return event.fn === nullOrFn;
        });
        if (index >= 0) {
            this._events[type].splice(index, 1);
        }
    } else {
        delete this._events[type];
    }
}

function emit(type, /* arguments */) {
    assertType(type);
    var args = [].slice.call(arguments, 1);
    var self = this;
    if (this._events[type]) {
        this._events[type].forEach(function (event) {
            event.fn.apply(null, args);
            if (event.type === 'once') {
                self.off(type, event.fn);
            }
        })
    }
}

EventEmitter.prototype.on = EventEmitter.prototype.addListener = on;
EventEmitter.prototype.once = EventEmitter.prototype.addOnceListener = once;
EventEmitter.prototype.prepend = EventEmitter.prototype.prependListener = prepend;
EventEmitter.prototype.prependOnce = EventEmitter.prototype.prependOnceListener = prependOnce;
EventEmitter.prototype.off = EventEmitter.prototype.removeListener = off;
EventEmitter.prototype.emit = EventEmitter.prototype.trigger = emit;

if (typeof module !== 'undefined') {
    module.exports = EventEmitter;
}
  

function KeyExtra(opt) {
    this._init(opt);
}

KeyExtra.prototype = new EventEmitter();
KeyExtra.prototype.constructor = KeyExtra;

KeyExtra.prototype._init = function (opt) {
    var keyExtra = this;

    // double key press
    var doublePressTimeoutMs = 600;
    var lastKeypressTime = 0;
    var lastKeyChar = null;

    function doubleHandle(type) {
        return function (evt) {
            var thisCharCode = evt.key.toUpperCase();
            if (lastKeyChar === null) {
                lastKeyChar = thisCharCode;
                lastKeypressTime = new Date();
                return;
            }
            if (thisCharCode === lastKeyChar) {
                var thisKeypressTime = new Date();
                if (thisKeypressTime - lastKeypressTime <= doublePressTimeoutMs) {
                    keyExtra.emit('double-' + type, thisCharCode);
                }
            }
            lastKeyChar = null;
            lastKeypressTime = 0;
        }
    }
    document && document.addEventListener('keypress', doubleHandle('keypress'));
    document && document.addEventListener('keydown', doubleHandle('keydown'));
};
  
  
  setTimeout(
    function () {
      (function($) {
        if (typeof $ === 'undefined') {
          console.error('jQuery is not found, so we consider that the page isn\'t swagger-ui.');
          return;
        }
        if (typeof window.swaggerUi === 'undefined') {
          console.error('window.swaggerUi is not defined, so we consider that the page isn\'t swagger-ui.');
          return;
        }

        var dom = $('<div style="margin-top: 15px;"></div>');
        dom.attr('class', 'inject-dom-container');

        var btns = $('<div></div>');
        btns.attr('class', 'inject-btn-container');

        function listAll() {
          $('.collapseResource').click();
        }
        function hideAll() {
          $('.endpoints').css({display: 'none'});
        }
        function expendAll() {
          $('.expandResource').click();
        }

        btns.append(
          $('<button>List All</button>').on('click', listAll),
          $('<button>Hide All</button>').on('click', hideAll),
          $('<button>Expend All</button>').on('click', expendAll),
        );

        dom.append(btns);
        dom.append([
          '<div class="search-container" style="display: none;">',
            '<div class="search-main">',
              '<input class="search-input"/>',
              '<ul class="search-found-list">',
              '</ul>',
            '</div>',
          '</div>'
        ].join(''))
        
        var searchContainer = dom.find('.search-container')
        new KeyExtra()
          .on('double-keydown', function (charCode) {
            if (charCode === 'A' || charCode === 'SHIFT') {
              setTimeout(function () {
                $('body').css({overflow: 'hidden'});
                searchContainer.show();
                searchContainer.find('.search-input').focus().select();
              }, 0)
            }
          });
        
        function hideSearch() {
          $('body').css({overflow: ''});
            searchContainer.hide();
        }
        
        document.addEventListener('keydown', function (evt) {
          if (evt.key === 'Escape') {
            hideSearch();
          }
        })
        
        var COUNT = 20;
        function search(val) {
          val = typeof val !== 'string' ? '' : val.trim();
          
          if (!val) {
            foundListDom.empty();
            return;
          }

          var type = '';
          if (/^(p|s|m): ([^]+)$/.test(val)) {
            type = RegExp.$1;
            val = RegExp.$2;
          }

          var keywords = val.split(/[+ ]/);
          var foundList = [];

          list.some(function (entity) {
            if (foundList.length === 30) {
              return true;
            }
            var matched_types = [];
            var matched = keywords.every(function (keyword) {
              function find(type, keyword) {
                // console.log(entity);
                if (entity[type].toLowerCase().includes(keyword.toLowerCase())) {
                  if (!matched_types.includes(type)) {
                    matched_types.push(type)
                  }
                  return true;
                }
              }

              if (type) {
                return find(type, keyword);
              }
              else {
                return ['p', 's', 'm'].some(function (type) {
                  return find(type, keyword);
                })
              }
            });
            
            if (matched) {
              foundList.push({
                type: matched_types.join(' '),
                entity: entity
              });
            }
          });

          foundListDom.empty();

          function item(data, i) {
            var html = '<li class="search-item ' + (i === 0 ? 'active' : '') + '">'
              + '<span class="search-item-type">' + data.type + '</span>'
              + ': '
              + '<span class="search-item-method">' + data.entity.m.toUpperCase() + '</span>'
              + '  '
              + '<span class="search-item-path">' + data.entity.p + '</span>'
              + '<span class="search-item-summary">' + data.entity.s + '</span>'
              + '</li>';

            return $(html).on('click', function() {
              var href = '#' + data.entity.url;
              var link = $('.toggleOperation[href=' + JSON.stringify(href) + ']');
              link.parents('ul.endpoints').css({display: 'block'});
              link[0].scrollIntoView();
              var operation = link.parents('.operation');
              var content = operation.find('.content');
              content.css('display') === 'none' && link[0].click();
              hideSearch();
              foundListDom.empty();
            })
          }

          if (!foundList.length) {
            foundListDom.append(
              '<li class="search-item">' + 'Not Found :(' + '</li>'
            );
          }
          else {
            foundListDom.append(
              foundList.map(item)
            );

            var sumHeight = 1;
            var over = Array.from(foundListDom.children('.search-item')).some(function (dom, i) {
              if (i === COUNT) {
                return true;
              }
              sumHeight += $(dom).prop('clientHeight') + 1;
            });
            over && foundListDom.css({'max-height': sumHeight + 'px'})
          }
        }

        var foundListDom = dom.find('.search-found-list');
        dom.find('.search-input')
          .on('input', function (evt) { search(evt.target.value) })
          .on('focus', function (evt) { search(evt.target.value) })
          // .on('blur', function (evt) { setTimeout(function () {foundListDom.empty()}, 300) })
          .on('keydown', function (evt) {
            var activeIndex = null;
            var listDoms = foundListDom.find('.search-item');
            function findActive() {
              Array.from(listDoms).some(function (dom, i){
                if ($(dom).hasClass('active')) {
                  $(dom).removeClass('active');
                  activeIndex = i;
                }
              });
            }
            var crlKey = evt.metaKey || evt.ctrlKey;
            var offset = crlKey ? COUNT : 1;
            var isUp = null;
            var prevIndex = activeIndex;
            switch (evt.keyCode) {
              case 38: // UP
                findActive();
                activeIndex = (listDoms.length + activeIndex - offset) % listDoms.length;
                listDoms.eq(activeIndex).addClass('active');
                isUp = true;
                break;
              case 40: // DOWN
                findActive();
                activeIndex = (activeIndex + offset) % listDoms.length
                listDoms.eq(activeIndex).addClass('active');
                isUp = false;
                break;
              case 13: // ENTER
                findActive();
                listDoms[activeIndex] && listDoms[activeIndex].click();
                return;
            }
            if (isUp === null) {
              return;
            }
            evt.preventDefault();
            var rang = [foundListDom.prop('scrollTop'), foundListDom.prop('scrollTop') + foundListDom.prop('clientHeight') - 10]
            // console.log(rang, listDoms[activeIndex].offsetTop)
          // console.dir(foundListDom[0])
            // console.log('!', listDoms[activeIndex].offsetTop, rang);
            if (listDoms[activeIndex]) {
              if (!(listDoms[activeIndex].offsetTop >= rang[0] && listDoms[activeIndex].offsetTop <= rang[1])) {
                // debugger;
                if (activeIndex === 0) {
                  foundListDom[0].scrollTop = 0;
                } else if (activeIndex === listDoms.length - 1) {
                  foundListDom[0].scrollTop = foundListDom.prop('scrollHeight');
                } else {
                  foundListDom[0].scrollTop += isUp ? -foundListDom.prop('clientHeight') : foundListDom.prop('clientHeight');
                }
              }
            }
            
            //console.dir(foundListDom[0])
            //console.dir(listDoms[activeIndex]);
          })

        var list = [];
        var url = window.swaggerUi.api && window.swaggerUi.api.url;

        if (url) {
          $.getJSON(url, function(data) {
    //        console.log(data);
            $.each(data.paths, function (path, methodSet) {
              $.each(methodSet, function (method, methodEntity) {
                // @todo:: array ??
                methodEntity.tags.join(',')
                methodEntity.operationId
                methodEntity.summary

                list.push({
                  methodEntity: methodEntity,
                  url: '!/' + methodEntity.tags.join(',') + '/' + methodEntity.operationId,
                  s: methodEntity.summary,
                  m: method,
                  p: path
                })
              })
            })
          })
        }

        dom.insertAfter($('#header'));

        $('head').append(
          '<style type="text/css">'
          + '.inject-btn-container {'
          + 'text-align: center;'
          + '}'
          + '.inject-btn-container button {'
          + 'margin-left: 5px;'
          + 'margin-right: 5px;'
          + '}'
          + '.search-item-type{'
          + 'display: inline-block;'
          + 'min-width: 15px;'
          + '}'
          + '.search-item-method {'
          + 'display: inline-block;'
          + 'width: 65px;'
          + 'text-align: center'
          + '}'
          + '.search-item-summary {'
          + 'display: inline-block;'
          + 'width: auto;'
          + 'float: right;'
          + 'max-width: 200px;'
          + 'overflow: hidden;'
          + 'text-overflow: ellipsis;'
          + 'white-space: nowrap;'
          + 'text-align: right;'
          + '}'
          + '.search-main {'
          + 'position: static;'
          + 'margin: 40px auto 40px;'
          + 'width: 68%;'
          + 'min-width: 500px;'
          + '}'
          + '.search-container {'
          + 'overflow-y: auto;'
          + 'background-color: rgba(0, 0, 0, .3);'
          + 'position: fixed;'
          + 'left: 0;'
          + 'right: 0;'
          + 'top: 0;'
          + 'bottom: 0;'
          + '}'
          + '.search-input {'
          + 'display: block;'
          + 'margin: auto;'
          + 'width: 100%;'
          + 'border: none;'
          + 'border-bottom: 1px solid #89bf04;'
          + 'padding: 4px 10px 2px;'
          + 'box-sizing: border-box;'
          + '}'
          + '.search-input:focus {'
          + 'outline: none;'
          + '}'
          + '.search-found-list {'
          + 'position: static;'
          + 'left: 0;'
          + 'right: 0;'
          // + 'max-height: 200px;'
          + 'overflow: auto;'
          + ''
          + '}'
          + '.search-found-list {'
          + 'margin-top: 2px;'
          + 'list-style: none;'
          + '}'
          + '.search-item.active, .search-item:hover {'
          + 'background-color: #eee;'
          + '}'
          + '.search-item {'
          + 'cursor: pointer;'
          + 'background-color: #fff;'
          + 'padding: 7px 15px;'
          + 'border: 1px solid #333;'
          + 'border-bottom: none;'
          + '}'
          + '.search-item:last-child {'
          + 'border-bottom: 1px solid #333;'
          + '}'
          + '</style>'
        )

        // auto scrollIntoView by hash
        setTimeout(function () {
          var a = $('a[href="' + location.hash + '"]')[0];
          a && a.scrollIntoView();  
        }, 200);
        
        

      })(window.jQuery)
    },
    1000
  )
  
})

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址