skybreak / JIRA Quick Navigation Ext

// ==UserScript==
// @name JIRA Quick Navigation Ext
// @description A GD JIRA quick navigation extension. 
// @version 1.12
// @author Darren Teng
// @license MIT
// @include *
// @match https://pd/*
// @run-at document-end
// @grant none
// ==/UserScript==

(function () {
  //'use strict';
  console.log('Tampermonkey JIRA ext loaded...');
  String.prototype.replaceAll = function (search, replacement) {
    var target = this;
    return target.replace(new RegExp(search, 'g'), replacement);
  };
  window.extUtil = {
    isTop: true,
    pannelIsOpen: true,
    pageInfo: null,
    render() {
      this.id = Date.now();
      this['pannel' + this.id] = {};
      this.pageInfo = this.getPageInfo();
      this.isDivReady(0, 60, () => {
        this.renderPannel();
        this.renderRocket();
      });
    },
    handleDetailPage() {
      if (extUtil.pageInfo.page == 'story-detail') {
        extUtil.listenActivityClick();
      }
    },
    isDivReady(tryTime, maxTryTime, callback) {
      console.log('check isDivReady')
      tryTime++;
      if (tryTime > maxTryTime) {
        console.log('isDivReady exceed max try ' + maxTryTime);
        return;
      }
      if (this.pageInfo.scrollElement) {
        console.log('div is ready.');
        callback && callback();
      }
      else {
        setTimeout(() => {
          this.loadScollBox(this.pageInfo.scrollWinId);
          this.isDivReady(tryTime, maxTryTime, callback);
        }, 50);
      }
    },
    renderPannel() {
      var items = this.getPageHeaders();
      var renderFunc = () => {
        items = this.getPageHeaders();
        items = this.filterQuickLinks(items);
        var htmlContent = this.buildTab(items);
        //render pannel
        var pannelDiv = document.createElement('div');
        pannelDiv.innerHTML = htmlContent;
        document.getElementsByTagName('body')[0].appendChild(pannelDiv);
        this.pageInfo.onPannelReady && this.pageInfo.onPannelReady();
        $('.collspanClz').on('click', function () {
          extUtil.toggolePannel();
        });
        //default show the panel. so if it set to not display the panel, hide it.
        if (!this.pannelIsOpen) {
          extUtil.pannelIsOpen = true;
          extUtil.toggolePannel();
        }
      }
      if (!items || !items.length) {
        var firstRetryTime = 500;
        console.log('No items found, delay ' + firstRetryTime + ' ms and retry.')
        extUtil.delayDo(firstRetryTime, renderFunc);
      }
      else {
        renderFunc();
      }
    },
    changeRocketDirection(fire, rocket) {
      if (extUtil.isTop && (extUtil.direction == "up")) {
        extUtil.direction = "down";
        fire.classList.remove("burn");
        rocket.classList.add("rotate");
      }

      if (extUtil.isBottom && (extUtil.direction == "down" || !extUtil.direction)) {
        extUtil.direction = "up";
        fire.classList.remove("burn");
        rocket.classList.remove("rotate");
      }
    },
    updateRocketStatus(ele) {
      extUtil.isTop = ele.scrollTop <= 100;
      extUtil.isBottom = ele.clientHeight + ele.scrollTop >= ele.scrollHeight;
    },
    renderRocket() {
      //render rocket
      var rocket = document.createElement('div');
      rocket.setAttribute("class", "rocketOutbox rotate");
      rocket.innerHTML = this.buildRocket();
      document.getElementsByTagName('body')[0].appendChild(rocket);
      var fire = document.querySelector(".fire");
      document.getElementsByClassName('spaceship')[0].onclick = function () {
        fire.classList.add("burn");
        const pageInfo = extUtil.pageInfo;
        if (extUtil.direction == "up") {
          //go to top
          pageInfo.scrollElement.scrollTo(0, 0);
        }
        else {
          //go to bottom
          var ele = pageInfo.scrollElement;
          if (!ele) return;
          pageInfo.scrollElement.scrollTo(0, 100000);
        }
        extUtil.updateRocketStatus(extUtil.pageInfo.scrollElement);
        extUtil.changeRocketDirection(fire, rocket);
      };
      var scrollWindow = document.querySelector(this.pageInfo.scrollWinId);
      var d = extUtil.debounce(() => {
        console.log('isRocketStatusChanged?');
        extUtil.updateRocketStatus(scrollWindow);
        if (extUtil.isTop || extUtil.isBottom) {
          fire.classList.add("burn");
          setTimeout(() => {
            extUtil.changeRocketDirection(fire, rocket);
            console.log('isRocketStatusChanged:true');
          }, 300);
        }
      }, 100);
      scrollWindow.addEventListener('scroll', () => {
        d();
        console.log('scroll...');
      });
    },
    debounce(func, time, tag) {
      var store = {};
      return function () {
        var context = this;
        var args = arguments;
        tag = tag ? tag : func.name;
        var timeoutId = store[tag];
        if (timeoutId) {
          clearTimeout(timeoutId);
        }
        store[tag] = setTimeout(() => {
          func(args);
        }, time);
      }
    },
    filterQuickLinks(items) {
      if (items && this.pageInfo) {
        items.forEach(v => {
          if (this.pageInfo.list && this.pageInfo.list.length > 0) {
            var it = this.pageInfo.list.find(p => p.name == v.text.trim() || (p.id && ("#" + p.id) == v.link));
            if (it) {
              v.order = it.order;
              v.desc = it.desc;
              v.type = it.type;
              v.text = it.desc ? it.desc : v.text;
            };
          }
          else {
            v.order = 0;
            v.desc = v.text;
            v.type = "left";
          }
        })
      }
      return items;
    },
    loadScollBox(selecter) {
      this.pageInfo.scrollElement = document.querySelector(selecter);
    },
    getPageInfo() {
      var url = window.location.href;
      var isBacklogBoardViewPage = (url) => {
        return /pd\/secure\/RapidBoard\.jspa\?.*/.test(url) && url.indexOf('view=planning') < 0;;
      }
      var isStoryDetailPage = (url) => {
        return /pd\/browse\/.*/.test(url);
      }
      var isBacklogListViewPage = (url) => {
        return /pd\/secure\/RapidBoard\.jspa\?.*/.test(url) && url.indexOf('view=planning') > 0;
      }
      var isReportPage = (url) => {
        return url.indexOf('pd/projects/') > -1 && url.indexOf('selectedItem=com.atlassian.jira.jira-projects-plugin:report-page') >= 0;
      }
      var isReleasePage = (url) => {
        return url.indexOf('pd/projects/') > -1 && url.indexOf('selectedItem=com.atlassian.jira.jira-projects-plugin:release-page') >= 0;
      }
      //event listener for tab change between backlog list view and backlog board view.
      var onStateChange = () => {
        console.log('onStateChange...');
        if (!extUtil.hasAddedPushState) {
          var _wr = function (type) {
            var orig = history[type];
            return function () {
              var rv = orig.apply(this, arguments);
              var e = new Event(type);
              e.arguments = arguments;
              window.dispatchEvent(e);
              return rv;
            };
          };
          history.pushState = _wr('pushState');
          history.replaceState = _wr('replaceState');
          window.addEventListener('pushState', function (e) {
            var span = (Date.now() - extUtil.id) / 1000;
            //only re-render the panel if timespan larger than 1s.
            if (span > 1) {
              setTimeout(() => {
                console.log('re-render pannel...');
                var extPopupPage = document.querySelector('.extPopupPage');
                if (extPopupPage && extPopupPage.parentNode) {
                  extPopupPage.parentNode.remove();
                }
                var rocket = document.querySelector('.rocketOutbox');
                if (rocket) rocket.remove();
                setTimeout(() => extUtil.render(), 100);
              }, 200);
            }
            else {
              console.log('pannel' + extUtil.id + ' hasReRenderedPanel.');
            }
          });
          extUtil.hasAddedPushState = true;
        }
      }
      var map = [{
          page: "story-detail",
          isCurrentPage: isStoryDetailPage,
          scrollWinId: ".issue-view",
          listItemFilter: {
            itemTextFilter: "", // the filter to get the text content. if it's empty, just use the box as the text container.
            itemBoxFilter: ".mod-header" // the filter to navigate to 
          },
          buildFastLinkFunc: this.buildFastLinkForDetailPage,
          onPannelReady: this.handleDetailPage,
          list: [{
              type: "left",
              name: "Details",
              desc: "Details",
              order: 1
            },
            {
              type: "left",
              name: "Description",
              desc: "Description",
              order: 2
            },
            {
              type: "left",
              name: "Approvals",
              desc: "Approvals",
              order: 3
            },
            {
              type: "left",
              name: "Smart Checklist",
              desc: "Smart Checklist",
              order: 4
            },
            {
              type: "left",
              name: "Attachments",
              desc: "Attachments",
              order: 5,
              id: "attachmentmodule_heading"
            },
            {
              type: "left",
              name: "Issue Links",
              desc: "Issue Links",
              order: 6
            },
            {
              type: "left",
              name: "Sub-Tasks",
              desc: "Sub-Tasks",
              order: 7
            },
            {
              type: "left",
              name: "Activity",
              desc: "Activity",
              order: 8
            },
            {
              type: "right",
              name: "People",
              desc: "People",
              order: 1
            },
            {
              type: "right",
              name: "Dates",
              desc: "Dates",
              order: 2
            },
            {
              type: "fast",
              name: "Comments",
              desc: "Comments",
              order: 2,
              id: "comment-tabpanel"
            },
            {
              type: "fast",
              name: "History",
              desc: "History",
              order: 2,
              id: "changehistory-tabpanel"
            },
            {
              type: "fast",
              name: "Story Points",
              desc: "Story Points",
              order: 3,
              id: "customfield_10002",
              onclick: (id) => {
                extUtil.popupToEdit(id, "'Story point' not found, try again.");
              }
            },
            {
              type: "fast",
              name: "Dev Lead",
              desc: "Dev Lead",
              order: 4,
              id: "customfield_13211",
              onclick: (id) => {
                extUtil.popupToEdit(id, "'Development Lead' not found, try again.");
              }
            },
            {
              type: "fast",
              name: "QA Engineer",
              desc: "QA Engineer",
              order: 5,
              id: "customfield_13213",
              onclick: (id) => {
                extUtil.popupToEdit(id, "'QA Engineer' not found, try again.");
              }
            },
            {
              type: "fast",
              name: "Dev ETA",
              desc: "Dev ETA",
              order: 5,
              id: "customfield_13400",
              onclick: (id) => {
                extUtil.popupToEdit(id, "'Development ETA' not found, try again.");
              }
            },
          ]
        },
        {
          page: "backlog-list-view",
          isCurrentPage: isBacklogListViewPage,
          scrollWinId: "#ghx-backlog",
          listItemFilter: {
            itemTextFilter: ".ghx-name",
            itemBoxFilter: ".ghx-backlog-container"
          },
          additionalCss: `.extPopupPage{ overflow-y: scroll;}
        .rocketOutbox {right:10px !important;}
        .extContent li {
          width: 240px;
        }
        .extContent {
          width: 240px;
        }`,
          onPannelReady: onStateChange,
          list: []
        },
        {
          page: "backlog-board-view",
          isCurrentPage: isBacklogBoardViewPage,
          scrollWinId: "#ghx-pool-column", //"ghx-rabid",
          listItemFilter: {
            itemTextFilter: "span[role=button]",
            itemBoxFilter: ".ghx-swimlane-header"
          },
          additionalCss: `.extPopupPage{ overflow-y: scroll;}`,
          onPannelReady: onStateChange,
          list: []
        },
        {
          page: "report",
          isCurrentPage: isReportPage,
          scrollWinId: "report-page",
          list: []
        },
        {
          page: "release",
          isCurrentPage: isReleasePage,
          scrollWinId: "release-page",
          list: []
        },
      ];
      var pageInfo = null;
      map.forEach((p) => {
        if (p.isCurrentPage(url)) {
          pageInfo = p;
          pageInfo.scrollElement = document.querySelector(p.scrollWinId);
          return;
        }
      });
      return pageInfo;
    },
    getPageHeaders() {
      var headers = document.querySelectorAll(this.pageInfo.listItemFilter.itemBoxFilter);
      var items = [];
      const exculedeIds = ['dnd-metadata_heading'];
      var autoId = 100;
      headers.forEach(node => {
        if (exculedeIds.indexOf(node.id) > -1) return;
        if (!node.id) {
          node.id = "autoId" + autoId++;
          node.setAttribute('id', node.id);
        }
        var textEle = this.pageInfo.listItemFilter.itemTextFilter ? node.querySelector(this.pageInfo.listItemFilter.itemTextFilter) : node;
        items.push({
          link: '#' + node.id,
          text: textEle ? textEle.innerText : "-",
          anchor: '^'
        });
      });
      return items;
    },
    changeSelectedFastlinkTab(id) {
      document.querySelectorAll('.extFastLinks li').forEach(p => {
        p.setAttribute('class', p.getAttribute('class').replaceAll('fastlink_actived', ''));
      });
      var selectedItems = document.getElementsByClassName('fast_' + id);
      if (selectedItems && selectedItems[0]) {

        var selectedItemClzs = selectedItems[0].getAttribute("class");
        selectedItems[0].setAttribute("class", selectedItemClzs + ' fastlink_actived')
      }
    },
    delayDo(time, callback) {
      setTimeout(function () {
        callback && callback();
      }, time <= 0 ? 10 : time);
    },
    buildRocket() {
      //#region
      var style = `<style>
      .rotate{transform: rotate(180deg);}
      .rocketOutbox {
        /*border: 1px solid green;*/
        width: 90px;
        position: fixed;
        right: 30px;
        bottom: 200px;
        height: 90px;
        z-index: 999;
        zoom: 0.7;
      }
      .spaceship {
        zoom:0.3;
        width: 300px;
        height: 300px;
        display: flex;
        justify-content: center;
        align-items: center;
        position: absolute;
        transition: all 2s ease;
        cursor:pointer;
        z-index:999;
        animation: float 2s ease infinite alternate;
        }
        @keyframes float {
        0% {
          transform: translateY(10px);
        }
        100% {
          transform: translateY(0px);
        }
        }
        .spaceship.launch {
        bottom: 120%;
        animation: launch 3s ease;
        }
        @keyframes launch {
        0% {
          bottom: 130px;
          transform: translatex(2px);
        }
        10% {
          transform: translatex(-2px);
        }
        20% {
          transform: translatex(2px);
        }
        30% {
          transform: translatex(-2px);
        }
        60% {
          transform: translatex(0px);
          bottom: 150px;
        }
        100% {
          bottom: 120%;
        }
        }
        .spaceship.land {
        bottom: 130px;
        animation: land 3s ease;
        }
        @keyframes land {
        0% {
          bottom: 120%;
        }
        50% {
          bottom: 180px;
        }
        100% {
          bottom: 130px;
        }
        }
        .spaceship .spaceshipBody {
        width: 35%;
        height: 80%;
        background-color: white;
        border-bottom-left-radius: 100%;
        border-bottom-right-radius: 100%;
        border-top-left-radius: 100%;
        border-top-right-radius: 100%;
        display: flex;
        justify-content: center;
        position: relative;
        z-index: 1;
        box-shadow: inset 0px -173px 0px -80px white, inset 0px -213px 0px -60px #e4e4e4;
        }
        .spaceship .spaceshipBody:before {
        content: "";
        position: absolute;
        width: calc(100% - 36px);
        height: 42%;
        background-color: inherit;
        bottom: -30px;
        transform: perspective(10em) rotateX(-50deg);
        border-bottom-left-radius: 50px;
        border-bottom-right-radius: 50px;
        box-shadow: inset 0px -33px 0px 0px rgba(0, 0, 0, 0.1);
        }
        .spaceship .spaceshipBody:after {
        content: "";
        position: absolute;
        width: 45%;
        height: 40px;
        background-color: #f95959;
        bottom: -20px;
        transform: perspective(10em) rotateX(-50deg);
        border-bottom-left-radius: 50px;
        border-bottom-right-radius: 50px;
        z-index: -1;
        }
        .spaceship .spaceshipBody .spaceshipTop {
        width: 100%;
        height: 240px;
        border-radius: 100%;
        overflow: hidden;
        position: relative;
        box-shadow: inset -12px 17px 0px -7px rgba(0, 0, 0, 0.15);
        }
        .spaceship .spaceshipBody .spaceshipTop:before {
        content: "";
        background-color: #4ba3b7;
        position: absolute;
        width: 100%;
        height: 100px;
        left: calc(50% - 54%);
        border-radius: 100%;
        top: -55px;
        border: 2px solid white;
        box-shadow: inset -18px 56px 0px 3px rgba(0, 0, 0, 0.1), 0px 0px 0px 6px #f95959;
        }
        .spaceship .spaceshipBody .spaceshipWindows {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 300px;
        height: 300px;
        position: absolute;
        }
        .spaceship .spaceshipBody .spaceshipWindows span {
        background-color: #ace7ef;
        box-shadow: inset -4px 4px 0px 0px rgba(0, 0, 0, 0.3), inset 0px 0px 0px 2px white;
        border: 4px solid #f95959;
        z-index: 2;
        position: absolute;
        border-radius: 100%;
        overflow: hidden;
        }
        .spaceship .spaceshipBody .spaceshipWindows span:before {
        position: absolute;
        content: "";
        background-color: white;
        width: 200%;
        height: 100%;
        transform: rotate(45deg);
        opacity: 0.4;
        }
        .spaceship .spaceshipBody .spaceshipWindows span:nth-child(1) {
        width: 30px;
        height: 30px;
        top: 23%;
        }
        .spaceship .spaceshipBody .spaceshipWindows span:nth-child(1):before {
        top: 10px;
        right: 0px;
        }
        .spaceship .spaceshipBody .spaceshipWindows span:nth-child(2) {
        width: 45px;
        height: 45px;
        top: 40%;
        }
        .spaceship .spaceshipBody .spaceshipWindows span:nth-child(2):before {
        top: 12px;
        right: 0px;
        }
        .spaceship .spaceshipBottom {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 300px;
        height: 300px;
        position: absolute;
        }
        .spaceship .spaceshipBottom span {
        background-color: #4ba3b7;
        border-radius: 10px;
        position: absolute;
        overflow: hidden;
        }
        .spaceship .spaceshipBottom span:before {
        content: "";
        position: absolute;
        background-color: white;
        width: 2px;
        height: 120%;
        border-radius: 20px;
        }
        .spaceship .spaceshipBottom span:nth-child(1) {
        width: 15px;
        height: 80px;
        z-index: 2;
        bottom: 2%;
        box-shadow: inset -5px -3px 0px 0px rgba(0, 0, 0, 0.18);
        }
        .spaceship .spaceshipBottom span:nth-child(1):before {
        display: none;
        }
        .spaceship .spaceshipBottom span:nth-child(2) {
        width: 50px;
        height: 130px;
        left: 32%;
        bottom: 6%;
        transform: perspective(10em) rotateX(60deg) translateZ(-1px);
        box-shadow: inset -5px -3px 0px 0px rgba(0, 0, 0, 0.2);
        }
        .spaceship .spaceshipBottom span:nth-child(2):before {
        left: 0px;
        border-right: 2px solid #f95959;
        }
        .spaceship .spaceshipBottom span:nth-child(3) {
        width: 50px;
        height: 130px;
        right: 32%;
        bottom: 6%;
        transform: perspective(10em) rotateX(60deg) translateZ(-1px);
        box-shadow: inset -5px -3px 0px 0px rgba(0, 0, 0, 0.2);
        }
        .spaceship .spaceshipBottom span:nth-child(3):before {
        right: 0px;
        border-left: 2px solid #f95959;
        }
        .spaceship .fire {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 300px;
        height: 100px;
        position: absolute;
        bottom: -50px;
        }
        .spaceship .fire.burn span {
        border-radius: 50px;
        top: 0;
        position: absolute;
        background-color: #ffd460;
        height: inherit;
        animation: fire 0.8s ease infinite alternate;
        }
        .spaceship .fire.burn span:nth-child(1) {
        width: 6px;
        height: 40px;
        left: 44%;
        transform: translateY(27px);
        box-shadow: inset 0px -7px 10px #ea5455, inset 0px -19px 10px #ffc175, 0px -7px 10px #ea5455;
        animation-delay: 0.2s;
        }
        .spaceship .fire.burn span:nth-child(1):after {
        position: absolute;
        content: "";
        width: 4px;
        height: 60%;
        border-radius: 50px;
        background-color: #ffd460;
        bottom: 0;
        transform: translate(8px, 15px);
        box-shadow: inset 0px -5px 10px #ea5455, inset 0px -19px 10px #ffc175, 0px -7px 10px #ea5455;
        }
        .spaceship .fire.burn span:nth-child(2) {
        width: 10px;
        height: 60px;
        left: calc(50% - 8px);
        transform: translateY(35px);
        box-shadow: inset 0px -10px 10px #ea5455, inset 0px -30px 10px #ffc175, 0px -7px 10px #ea5455;
        }
        .spaceship .fire.burn span:nth-child(2):after {
        position: absolute;
        content: "";
        width: 10px;
        height: 100%;
        border-radius: 10px;
        background-color: #ffd460;
        top: 0;
        transform: translate(-6px, -25px);
        box-shadow: inset 0px -5px 10px #ea5455, inset 0px -15px 10px #ffc175, 0px -7px 10px #ea5455;
        }
        .spaceship .fire.burn span:nth-child(3) {
        width: 10px;
        height: 40px;
        right: 45%;
        transform: translateY(27px);
        box-shadow: inset 0px -5px 10px #ea5455, inset 0px -30px 10px #ffc175, 0px -7px 10px #ea5455;
        animation-delay: 0.4s;
        }
        .spaceship .fire.burn span:nth-child(3):after {
        position: absolute;
        content: "";
        width: 6px;
        height: 180%;
        border-radius: 10px;
        background-color: #ffd460;
        top: 0;
        transform: translate(-6px, -15px);
        box-shadow: inset 0px -5px 10px #ea5455, inset 0px -20px 10px #ffc175, 0px -7px 10px #ea5455;
        }
        @keyframes fire {
        0% {
          height: 10px;
          bottom: 0;
          50% {
          top: 0;
          }
          100% {
          height: 20px;
          bottom: 0;
          }
        }
        }
        .spaceship .fire.burn .glow {
        position: absolute;
        width: 0px;
        height: 0px;
        border-radius: 100%;
        box-shadow: 0px 0px 50px 20px #ea5455;
        opacity: 1;
        animation: glow 0.8s ease infinite alternate;
        }
        @keyframes glow {
        0% {
          box-shadow: 0px 0px 50px 20px #ea5455;
        }
        100% {
          box-shadow: 0px 0px 50px 25px #ea5455;
        }
        }
        .shadow {
        width: 50px;
        height: 10px;
        background-color: black;
        position: absolute;
        border-radius: 100%;
        opacity: 0.2;
        top: 96px;
        z-index: 99;
        left:25px;
        animation: shadow 2s ease infinite alternate;
        transition: all 0.5s ease;
        }
        @keyframes shadow {
        0% {
          width: 40px;
        }
        100% {
          width: 46px;
        }
        }
        ${navigator.userAgent.indexOf('Firefox') > -1 ? `.spaceship{
        -moz-transform: scale(0.3) !important;
        margin-left: -104px  !important;
        margin-top: -104px !important;
        }`: ''}
        </style>`;
      var html = `
      <div class="spaceship">
      <div class="spaceshipBody">
        <div class="spaceshipTop"></div>
        <div class="spaceshipWindows"> <span></span> <span></span> </div>
      </div>
      <div class="spaceshipBottom"> <span></span> <span></span> <span></span> </div>
      <div class="fire"> <span></span> <span></span> <span></span>
        <div class="glow"></div>
      </div>
      </div><div class="shadow" style="opacity: 0.2;"></div>`;
      //#endregion
      return style + html;
    },
    buildFastLinkForDetailPage(template, data) {
      if (data) {
        var content = data.map(d => `<li class='fast_${d.id}'><a href='javascript:void(0)' class='fastNavItem' onclick='extUtil.fastLinkClick("${d.id}")' fastid='"${d.id}"'>${d.desc}</a></li>`)
        return template.replace('$content', content.join(' '));
      }
      return template;
    },
    buildTab(items) {
      var templateCss = `<style>
      .extPopupPage {
        position: fixed;
        top: 200px;
        right: 20px;
        opacity: 0.8;
        background-color: #ece4f5;
        border-radius: 4px;
        width: 300px;
        height: 240px;
        z-index: 999;
      }

      .extTitle{
        font-weight:bold;
        margin:10px;
      }
  
      .extContent {
        width: 140px;
        float: left;
      }
      .ext-list-left{
        padding-left: 10px;  
      }
      .ext-list-right{
        width: 140px;
        padding-left: 10px;  
      }
      .extContent li {
        width: 140px;
        list-style-type: none;
      }
      .extFastLinks {
        width: 290px;
        float: left;
        background: #e2e0e0;
          padding: 4px;
      }
      .extFastLinks li{
        list-style-type: none;
        float: left;
        width: 80px;
      }
      .fastlink_actived a{
        color: green;
        font-weight:bold;
      }

      .left {
        width: 50%;
        float: left;
      }
      .collspanClz{
        background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTcuNyAzLjdMMy40IDhsNC4zIDQuM2MuNC40LjQgMSAwIDEuNC0uNC40LTEgLjQtMS40IDBsLTUtNWMtLjQtLjQtLjQtMSAwLTEuNGw1LTVjLjQtLjQgMS0uNCAxLjQgMCAuNC40LjQgMSAwIDEuNHptNyAwTDEwLjQgOGw0LjMgNC4zYy40LjQuNCAxIDAgMS40LS40LjQtMSAuNC0xLjQgMGwtNS01Yy0uNC0uNC0uNC0xIDAtMS40bDUtNWMuNC0uNCAxLS40IDEuNCAwIC40LjQuNCAxIDAgMS40eiIgZmlsbD0iIzUwNWY3OSIvPgo8L3N2Zz4K);
        width: 24px;
        position: absolute;
        height: 24px;
        cursor: pointer;
        font-weight: bold;
        text-align: center;
        color: green;
        top: 206px;
        right: 36px;
                z-index: 1000;
                line-height:36px;
      }
      .collspanOpen{
        transform: rotate(0deg);
      }
      .collspanClose{
        transform: rotate(180deg);
      }
      ${this.pageInfo.additionalCss ? this.pageInfo.additionalCss : ""}
      </style>`;
      var htmlContentTemplate = `
      <div class="extPopupPage">
        <div class="extPannel">
        <p class='extTitle'>Quick links</p>
          $content
          $fastContent
        </div>
      </div>
      <div class='collspanClz ${this.pannelIsOpen ? 'collspanClose' : 'collspanOpen'}' title="Click to show/hide the quick nav panel."></div>
      `;

      var htmlLeftListContentTemplate = `
      <ul class="extContent ext-list-left">
        $items
      </ul>`;

      var itemLeftItemContentTemplate = `
      <li class="extitem">
        <div class="">$item</div>
      </li>`;

      var htmlRightListContentTemplate = `
      <ul class="extContent ext-list-right">
        $items
      </ul>`;

      var itemRightItemContentTemplate = `
      <li class="extitem">
        <div class="">$item</div>
      </li>`;

      var htmlFastTemplate = `<ul class='extFastLinks'>$content</ul>`;

      var leftList = items.filter(p => p.type == "left").sort((a, b) => a - b);
      var rightList = items.filter(p => p.type == "right").sort((a, b) => a - b);
      var fastList = this.pageInfo.list.filter(p => p.type == "fast").sort((a, b) => a - b);
      var textLeft = leftList && leftList.map(i => {
        return itemLeftItemContentTemplate.replace('$item', `<a href='javascript:void(0);' class='link${i.anchor}' onclick='extUtil.anchorLinkClick("${i.link}")'>${i.text}</a>\r\n`);
      }).join('');
      var textRight = rightList && rightList.map(i => {
        return itemRightItemContentTemplate.replace('$item', `<a href='javascript:void(0);' class='link${i.anchor}' onclick='extUtil.anchorLinkClick("${i.link}")'>${i.text}</a>\r\n`);
      }).join('');

      htmlFastTemplate = this.pageInfo.buildFastLinkFunc ? this.pageInfo.buildFastLinkFunc(htmlFastTemplate, fastList) : "";
      this.injectFuncs = this.injectFuncs ? this.injectFuncs : [];
      this.injectFuncs.push({
        selector: '.extFastLinks li',
        func: function clickFastLinkItem() {
          var id = this.getAttribute('fastid')
          id && document.getElementById(id).click();
        }
      });

      var content = htmlLeftListContentTemplate.replace('$items', textLeft) + htmlRightListContentTemplate.replace('$items', textRight);
      text = templateCss + htmlContentTemplate.replace('$content', content).replace('$fastContent', htmlFastTemplate);
      return text;
    },
    popupToEdit(id, errorMsg) {
      var edit = document.getElementById('edit-issue');
      if (edit) {
        edit.click();
        setTimeout(function () {
          var ele = document.getElementById(id);
          if (!ele) {
            alert(errorMsg)
            return;
          }
          ele.scrollIntoView();
          ele.focus();
          extUtil.shrinkDiv(id);
        }, 1400);
      }
    },
    fastLinkClick(id) {
      id = id.replace("#", '');
      var curEle = document.getElementById(id);
      curEle && curEle.scrollIntoView();
      if (document.querySelector('#issue-tabs .active-tab').id == id) {
        console.log('same id, no action')
        return;
      }
      if (!document.getElementById(id) && !document.getElementById(id + '-val')) {
        //id == 'rowForcustomfield_10002' && !document.getElementById(id)
        var item = extUtil.pageInfo.list.find(i => i.id == id);
        if (item && item.onclick) {
          item.onclick(id);
        }
      }
      else {
        //check the actual id
        var ele = document.querySelector('#' + id + ' a');
        //check extend id 
        if (!ele) {
          ele = document.querySelector('#' + id + '-val');
        }
        ele && ele.click();
        extUtil.shrinkDiv(id);
      }
      console.log('click ' + id)
    },
    anchorLinkClick(anchor) {
      var id = anchor.replace('#', '');
      document.getElementById(id).scrollIntoView();
      this.shrinkDiv(id);
    },
    shrinkDiv(id) {
      var ele = document.getElementById(id);
      if (!ele) return;
      ele.style.border = '3px solid green';
      this.delayDo(200, function () {
        ele.style.border = '2px solid green';
        extUtil.delayDo(100, function () {
          ele.style.border = 'none';
        })
      });
    },
    toggolePannel() {
      var $elPannel = $(document.querySelector('.extPopupPage'));
      var $elCollspan = $(document.querySelector('.collspanClz'));
      if (this.pannelIsOpen) {
        $elPannel.hide();
        $elCollspan.removeClass('collspanClose');
        $elCollspan.addClass('collspanOpen');
      }
      else {
        $elPannel.show();
        $elCollspan.removeClass('collspanOpen');
        $elCollspan.addClass('collspanClose');
      }
      this.pannelIsOpen = !this.pannelIsOpen;
    },
    delayDo(time, callback) {
      setTimeout(function () {
        callback && callback();
      }, time <= 0 ? 10 : time);
    },
    listenActivityClick() {
      $('#activitymodule').on('click', function (e) {
        extUtil.changeSelectedFastlinkTab(e.srcElement.id)
        console.log('try to postMessage to content.js');
      })
    }
  };
  setTimeout(() => {
    extUtil.render();
  }, 100);
})();