diff options
Diffstat (limited to 'app')
627 files changed, 9964 insertions, 5895 deletions
diff --git a/app/assets/images/file_icons.svg b/app/assets/images/file_icons.svg new file mode 100644 index 00000000000..26ec1a6b388 --- /dev/null +++ b/app/assets/images/file_icons.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 24 24" id="actionscript" xmlns="http://www.w3.org/2000/svg"><text style="line-height:113.99999857%" x="5.605" y="15.892" transform="scale(.91325 1.095)" font-weight="400" font-size="42.822" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#f44336"/><path style="line-height:125%" d="M4.744 2.031c-1.157 0-1.994.31-2.51.93-.515.612-.771 1.678-.771 3.197v2.467c0 1.408-.402 2.111-1.201 2.111v2.035c.8 0 1.2.679 1.2 2.036v2.654c0 1.512.26 2.562.78 3.152.52.59 1.355.885 2.502.885V19.43c-.447 0-.77-.151-.97-.453-.195-.303-.292-.815-.292-1.538v-2.267c0-1.807-.404-2.937-1.214-3.395v-.045c.81-.464 1.214-1.581 1.214-3.351V6.025c0-1.283.42-1.925 1.262-1.925V2.03zm14.66 0V4.1c.842 0 1.262.642 1.262 1.925v2.268c0 1.843.402 2.996 1.207 3.46v.046c-.805.442-1.207 1.544-1.207 3.306v2.356c0 .715-.099 1.22-.299 1.516-.2.302-.52.453-.963.453v2.068c1.152 0 1.984-.295 2.494-.885.516-.59.772-1.663.772-3.218V14.84c0-1.379.404-2.069 1.209-2.069v-2.035c-.805 0-1.21-.696-1.21-2.09V6.113c0-1.49-.255-2.54-.77-3.152-.516-.62-1.348-.93-2.495-.93zm-3.054 4.46c-.455 0-.886.057-1.293.173a3.056 3.056 0 0 0-1.078.527c-.308.241-.551.549-.731.924-.18.37-.27.817-.27 1.336 0 .663.165 1.227.493 1.695.33.468.831.864 1.502 1.188.263.125.509.249.736.37.227.12.422.244.586.374.168.13.299.271.394.424a.963.963 0 0 1 .145.521c0 .144-.03.28-.09.405a.9.9 0 0 1-.275.318c-.12.088-.272.158-.455.21a2.34 2.34 0 0 1-.635.075c-.415 0-.825-.083-1.233-.25a3.644 3.644 0 0 1-1.13-.763v2.222a3.68 3.68 0 0 0 1.101.418c.427.093.875.139 1.346.139.459 0 .894-.05 1.305-.152a3.002 3.002 0 0 0 1.09-.5c.31-.237.556-.543.736-.918.183-.38.275-.849.275-1.405 0-.403-.052-.755-.156-1.056a2.542 2.542 0 0 0-.45-.813 3.295 3.295 0 0 0-.704-.633 6.754 6.754 0 0 0-.922-.535 12.4 12.4 0 0 1-.676-.348c-.2-.115-.37-.231-.51-.347a1.502 1.502 0 0 1-.322-.375.91.91 0 0 1-.115-.453c0-.153.033-.288.101-.408a.948.948 0 0 1 .29-.32c.123-.089.275-.156.454-.202a2.18 2.18 0 0 1 .598-.078c.16 0 .326.015.502.043.18.028.36.07.539.13.18.056.354.13.522.218.171.088.329.188.472.304V6.871a4.039 4.039 0 0 0-.957-.285 6.448 6.448 0 0 0-1.185-.096zm-8.774.165l-3.123 9.967h2.094l.605-2.217h3.053l.61 2.217h2.107L9.869 6.656H7.576zm1.072 1.78h.047c.028.347.077.646.145.896l.922 3.35H7.564l.934-3.377c.08-.288.13-.578.15-.87z" font-weight="400" font-size="51.019" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#f44336"/></symbol><symbol viewBox="0 0 24 24" id="android" xmlns="http://www.w3.org/2000/svg"><path d="M15 5h-1V4h1m-5 1H9V4h1m5.53-1.84L16.84.85c.19-.19.19-.51 0-.71a.513.513 0 0 0-.71 0l-1.48 1.48C13.85 1.23 12.95 1 12 1c-.96 0-1.86.23-2.66.63L7.85.14a.501.501 0 0 0-.7 0c-.2.2-.2.52 0 .71l1.31 1.31C6.97 3.26 6 5 6 7h12c0-2-1-3.75-2.47-4.84M20.5 8A1.5 1.5 0 0 0 19 9.5v7a1.5 1.5 0 0 0 1.5 1.5 1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 20.5 8m-17 0A1.5 1.5 0 0 0 2 9.5v7A1.5 1.5 0 0 0 3.5 18 1.5 1.5 0 0 0 5 16.5v-7A1.5 1.5 0 0 0 3.5 8M6 18a1 1 0 0 0 1 1h1v3.5A1.5 1.5 0 0 0 9.5 24a1.5 1.5 0 0 0 1.5-1.5V19h2v3.5a1.5 1.5 0 0 0 1.5 1.5 1.5 1.5 0 0 0 1.5-1.5V19h1a1 1 0 0 0 1-1V8H6v10z" fill="#c0ca33"/></symbol><symbol viewBox="0 0 24 24" id="angular" xmlns="http://www.w3.org/2000/svg"><path d="M12.102 2.625l8.84 3.15-1.34 11.7-7.5 4.15-7.5-4.15-1.34-11.7 8.84-3.15m0 2.1l-5.53 12.4h2.06l1.11-2.78h4.7l1.11 2.78h2.05l-5.5-12.4m1.62 7.9h-3.23l1.61-3.87z" fill="#e53935"/></symbol><symbol viewBox="0 0 24 24" id="angular-component" xmlns="http://www.w3.org/2000/svg"><path d="M12.102 2.625l8.84 3.15-1.34 11.7-7.5 4.15-7.5-4.15-1.34-11.7 8.84-3.15m0 2.1l-5.53 12.4h2.06l1.11-2.78h4.7l1.11 2.78h2.05l-5.5-12.4m1.62 7.9h-3.23l1.61-3.87z" fill="#0288d1"/></symbol><symbol viewBox="0 0 24 24" id="angular-directive" xmlns="http://www.w3.org/2000/svg"><path d="M12.102 2.625l8.84 3.15-1.34 11.7-7.5 4.15-7.5-4.15-1.34-11.7 8.84-3.15m0 2.1l-5.53 12.4h2.06l1.11-2.78h4.7l1.11 2.78h2.05l-5.5-12.4m1.62 7.9h-3.23l1.61-3.87z" fill="#ab47bc"/></symbol><symbol viewBox="0 0 24 24" id="angular-guard" xmlns="http://www.w3.org/2000/svg"><path d="M12.102 2.625l8.84 3.15-1.34 11.7-7.5 4.15-7.5-4.15-1.34-11.7 8.84-3.15m0 2.1l-5.53 12.4h2.06l1.11-2.78h4.7l1.11 2.78h2.05l-5.5-12.4m1.62 7.9h-3.23l1.61-3.87z" fill="#43a047"/></symbol><symbol viewBox="0 0 24 24" id="angular-pipe" xmlns="http://www.w3.org/2000/svg"><path d="M12.102 2.625l8.84 3.15-1.34 11.7-7.5 4.15-7.5-4.15-1.34-11.7 8.84-3.15m0 2.1l-5.53 12.4h2.06l1.11-2.78h4.7l1.11 2.78h2.05l-5.5-12.4m1.62 7.9h-3.23l1.61-3.87z" fill="#00897b"/></symbol><symbol viewBox="0 0 24 24" id="angular-resolver" xmlns="http://www.w3.org/2000/svg"><path d="M12.102 2.625l8.84 3.15-1.34 11.7-7.5 4.15-7.5-4.15-1.34-11.7 8.84-3.15m0 2.1l-5.53 12.4h2.06l1.11-2.78h4.7l1.11 2.78h2.05l-5.5-12.4m1.62 7.9h-3.23l1.61-3.87z" fill="#43a047"/></symbol><symbol viewBox="0 0 24 24" id="angular-routing" xmlns="http://www.w3.org/2000/svg"><path d="M11 10H5L3 8l2-2h6V3l1-1 1 1v1h6l2 2-2 2h-6v2h6l2 2-2 2h-6v6a2 2 0 0 1 2 2H9a2 2 0 0 1 2-2V10z" fill="#43a047"/></symbol><symbol viewBox="0 0 24 24" id="angular-service" xmlns="http://www.w3.org/2000/svg"><path d="M12.102 2.625l8.84 3.15-1.34 11.7-7.5 4.15-7.5-4.15-1.34-11.7 8.84-3.15m0 2.1l-5.53 12.4h2.06l1.11-2.78h4.7l1.11 2.78h2.05l-5.5-12.4m1.62 7.9h-3.23l1.61-3.87z" fill="#ffca28"/></symbol><symbol viewBox="0 0 100 100" id="apiblueprint" xmlns="http://www.w3.org/2000/svg"><title>api-blueprint</title><path d="M50.133 7.521A16.998 16.998 0 0 0 33.135 24.52a16.998 16.998 0 0 0 4.945 11.974L24.861 57.398a16.998 16.998 0 0 0-3.175-.308A16.998 16.998 0 0 0 4.688 74.088a16.998 16.998 0 0 0 16.998 16.998 16.998 16.998 0 0 0 16.998-16.998 16.998 16.998 0 0 0-7.063-13.773l12.576-19.89a16.998 16.998 0 0 0 5.936 1.093 16.998 16.998 0 0 0 6.154-1.155l12.537 19.83a16.998 16.998 0 0 0-7.244 13.895 16.998 16.998 0 0 0 16.998 17 16.998 16.998 0 0 0 16.998-17A16.998 16.998 0 0 0 78.578 57.09a16.998 16.998 0 0 0-2.95.262L62.337 36.327A16.998 16.998 0 0 0 67.13 24.52 16.998 16.998 0 0 0 50.132 7.522z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="applescript" xmlns="http://www.w3.org/2000/svg"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" fill="#78909c"/></symbol><symbol viewBox="0 0 24 24" id="appveyor" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c-.084 0-.165.008-.248.01a10 10 0 0 0-.266.01 9.952 9.952 0 0 0-.754.066 10 10 0 0 0-.148.018 9.855 9.855 0 0 0-.93.177 10 10 0 0 0-.07.02c-.196.049-.392.1-.584.16v.012a10 10 0 0 0-2 .875V3.34c-.02.012-.038.027-.059.039a10 10 0 0 0-.953.635c-.09.067-.172.142-.26.213a10 10 0 0 0-.628.546c-.109.104-.211.211-.315.319a10 10 0 0 0-.476.539c-.1.12-.201.237-.295.361a10 10 0 0 0-.52.766c-.088.143-.17.288-.252.435a10 10 0 0 0-.363.723c-.072.161-.136.327-.2.492a10 10 0 0 0-.269.778c-.02.067-.044.131-.062.199a10 10 0 0 0-.008.027c-.098.364-.166.728-.22 1.09-.012.077-.024.153-.034.23a9.85 9.85 0 0 0-.08 1.182c0 .03-.006.057-.006.086a10 10 0 0 0 .008.148c.001.094-.002.188.002.282l.011.004a10 10 0 0 0 .333 2.158l-.012-.004c.012.047.033.091.047.139a10 10 0 0 0 .322.955c.02.052.037.106.059.158a10 10 0 0 0 .503 1.035c.065.116.14.226.21.34a10 10 0 0 0 .423.64c.092.128.187.252.285.375a10 10 0 0 0 .448.52c.112.123.222.248.341.365a10 10 0 0 0 .803.719 10 10 0 0 0 .01.006c.099.078.207.146.309.22a10 10 0 0 0 .648.442c.138.085.28.163.424.242a10 10 0 0 0 .715.358c.114.051.226.106.343.154a10 10 0 0 0 1.133.389c.016.004.031.01.047.015a10 10 0 0 0 .461.098 10 10 0 0 0 .482.103 10 10 0 0 0 .418.051 10 10 0 0 0 .575.065 10 10 0 0 0 .144.005A10 10 0 0 0 12 22a10 10 0 0 0 .197-.01 10 10 0 0 0 .496-.025 10 10 0 0 0 .49-.043 10 10 0 0 0 .489-.074 10 10 0 0 0 .51-.098 10 10 0 0 0 .47-.12 10 10 0 0 0 .477-.14 10 10 0 0 0 .47-.172 10 10 0 0 0 .481-.197 10 10 0 0 0 .414-.201 10 10 0 0 0 .475-.252 10 10 0 0 0 .39-.238 10 10 0 0 0 .452-.301 10 10 0 0 0 .38-.291 10 10 0 0 0 .385-.315 10 10 0 0 0 .375-.347 10 10 0 0 0 .36-.363 10 10 0 0 0 .293-.334 10 10 0 0 0 .353-.434 10 10 0 0 0 .28-.393 10 10 0 0 0 .263-.4 10 10 0 0 0 .264-.461 10 10 0 0 0 .228-.436 10 10 0 0 0 .195-.437 10 10 0 0 0 .196-.48 10 10 0 0 0 .228-.69 10 10 0 0 0 .028-.094 10 10 0 0 0 .021-.066 10 10 0 0 0 .098-.461 10 10 0 0 0 .103-.482 10 10 0 0 0 .051-.418 10 10 0 0 0 .065-.575 10 10 0 0 0 .005-.144A10 10 0 0 0 22 12a10 10 0 0 0-.01-.197 10 10 0 0 0-.025-.496 10 10 0 0 0-.043-.49 10 10 0 0 0-.074-.489 10 10 0 0 0-.098-.51 10 10 0 0 0-.12-.47 10 10 0 0 0-.14-.477 10 10 0 0 0-.172-.47 10 10 0 0 0-.197-.481 10 10 0 0 0-.201-.414 10 10 0 0 0-.252-.475 10 10 0 0 0-.238-.39 10 10 0 0 0-.301-.452 10 10 0 0 0-.291-.38 10 10 0 0 0-.315-.385 10 10 0 0 0-.347-.375 10 10 0 0 0-.363-.36 10 10 0 0 0-.334-.293 10 10 0 0 0-.434-.353 10 10 0 0 0-.393-.28 10 10 0 0 0-.4-.263 10 10 0 0 0-.461-.264 10 10 0 0 0-.436-.228 10 10 0 0 0-.437-.196 10 10 0 0 0-.48-.195 10 10 0 0 0-.69-.228 10 10 0 0 0-.094-.028 10 10 0 0 0-.066-.021 10 10 0 0 0-.461-.098 10 10 0 0 0-.482-.103 10 10 0 0 0-.418-.051 10 10 0 0 0-.575-.065 10 10 0 0 0-.144-.005A10 10 0 0 0 12 2zm-.016 5.002a5 5 0 0 1 .262.01 5 5 0 0 1 .227.011 5 5 0 0 1 .341.05 5 5 0 0 1 .135.019 5 5 0 0 1 .014.004 5 5 0 0 1 .115.025 5 5 0 0 1 .303.076 5 5 0 0 1 .265.086 5 5 0 0 1 .2.074 5 5 0 0 1 .242.106 5 5 0 0 1 .228.11 5 5 0 0 1 .196.109 5 5 0 0 1 .244.15 5 5 0 0 1 .17.12 5 5 0 0 1 .224.171 5 5 0 0 1 .186.16 5 5 0 0 1 .176.164 5 5 0 0 1 .172.18 5 5 0 0 1 .177.203 5 5 0 0 1 .133.172 5 5 0 0 1 .16.223 5 5 0 0 1 .133.214 5 5 0 0 1 .12.21 5 5 0 0 1 .107.216 5 5 0 0 1 .109.24 5 5 0 0 1 .084.223 5 5 0 0 1 .08.242 5 5 0 0 1 .07.264 5 5 0 0 1 .047.207 5 5 0 0 1 .045.277 5 5 0 0 1 .028.227 5 5 0 0 1 .02.351 5 5 0 0 1 .003.079 5 5 0 0 1-.012.271 5 5 0 0 1-.011.227 5 5 0 0 1-.05.341 5 5 0 0 1-.019.135 5 5 0 0 1-.004.014 5 5 0 0 1-.025.115 5 5 0 0 1-.076.303 5 5 0 0 1-.086.265 5 5 0 0 1-.074.2 5 5 0 0 1-.106.242 5 5 0 0 1-.11.228 5 5 0 0 1-.109.196 5 5 0 0 1-.15.244 5 5 0 0 1-.12.17 5 5 0 0 1-.171.224 5 5 0 0 1-.16.186 5 5 0 0 1-.164.176 5 5 0 0 1-.18.172 5 5 0 0 1-.203.177l-.002.002c-.018.019-.028.035-.047.053l-3.959 5.09-3.05-.979a141.684 141.684 0 0 0 3.177-3.084 5 5 0 0 1-.103-.015 5 5 0 0 1-.149-.024 5 5 0 0 1-.115-.025 5 5 0 0 1-3.57-3.04 5.072 5.072 0 0 1-.206-.661 5 5 0 0 1-.033-.147c-.025-.118-.036-.24-.054-.36-.987.993-1.964 1.993-2.954 3.05l-.98-3.053 5.092-3.957c.043-.044.082-.07.125-.11a5 5 0 0 1 .71-.634c.18-.13.367-.25.561-.356a5 5 0 0 1 .16-.08 4.94 4.94 0 0 1 .516-.222 5 5 0 0 1 .147-.057c.211-.07.43-.123.654-.164a5 5 0 0 1 .172-.027c.236-.035.476-.058.722-.059zM12 9a3 3 0 0 0-.053.002 3 3 0 0 0-.166.01 3 3 0 0 0-.133.011 3 3 0 0 0-.17.026 3 3 0 0 0-.113.021 3 3 0 0 0-.19.05 3 3 0 0 0-.103.03 3 3 0 0 0-.16.057 3 3 0 0 0-.129.053 3 3 0 0 0-.146.072 3 3 0 0 0-.12.063 3 3 0 0 0-.132.082 3 3 0 0 0-.123.08 3 3 0 0 0-.116.088 3 3 0 0 0-.126.105 3 3 0 0 0-.1.094 3 3 0 0 0-.111.111 3 3 0 0 0-.096.107 3 3 0 0 0-.094.116 3 3 0 0 0-.098.136 3 3 0 0 0-.072.11 3 3 0 0 0-.076.133 3 3 0 0 0-.07.132 3 3 0 0 0-.063.14 3 3 0 0 0-.054.14 3 3 0 0 0-.077.228 3 3 0 0 0-.007.026 3 3 0 0 0-.03.138 3 3 0 0 0-.031.149 3 3 0 0 0-.014.11 3 3 0 0 0-.02.183 3 3 0 0 0-.001.052A3 3 0 0 0 9 12a3 3 0 0 0 .002.053 3 3 0 0 0 .01.166 3 3 0 0 0 .011.133 3 3 0 0 0 .026.17 3 3 0 0 0 .021.113 3 3 0 0 0 .05.19 3 3 0 0 0 .03.103 3 3 0 0 0 .057.16 3 3 0 0 0 .053.129 3 3 0 0 0 .072.146 3 3 0 0 0 .063.12 3 3 0 0 0 .082.132 3 3 0 0 0 .08.123 3 3 0 0 0 .088.116 3 3 0 0 0 .105.126 3 3 0 0 0 .094.1 3 3 0 0 0 .111.111 3 3 0 0 0 .107.096 3 3 0 0 0 .116.094 3 3 0 0 0 .136.098 3 3 0 0 0 .11.072 3 3 0 0 0 .133.076 3 3 0 0 0 .132.07 3 3 0 0 0 .135.06 3 3 0 0 0 .153.061 3 3 0 0 0 .216.07 3 3 0 0 0 .004.003 3 3 0 0 0 .026.007 3 3 0 0 0 .138.03 3 3 0 0 0 .149.031 3 3 0 0 0 .11.014 3 3 0 0 0 .183.02 3 3 0 0 0 .011.001 3 3 0 0 0 .041 0A3 3 0 0 0 12 15a3 3 0 0 0 .053-.002 3 3 0 0 0 .166-.01 3 3 0 0 0 .133-.011 3 3 0 0 0 .17-.026 3 3 0 0 0 .113-.021 3 3 0 0 0 .19-.05 3 3 0 0 0 .103-.03 3 3 0 0 0 .16-.057 3 3 0 0 0 .129-.053 3 3 0 0 0 .146-.072 3 3 0 0 0 .12-.063 3 3 0 0 0 .132-.082 3 3 0 0 0 .123-.08 3 3 0 0 0 .116-.088 3 3 0 0 0 .126-.105 3 3 0 0 0 .1-.094 3 3 0 0 0 .111-.111 3 3 0 0 0 .096-.107 3 3 0 0 0 .094-.116 3 3 0 0 0 .098-.136 3 3 0 0 0 .072-.11 3 3 0 0 0 .076-.133 3 3 0 0 0 .07-.132 3 3 0 0 0 .06-.135 3 3 0 0 0 .061-.153 3 3 0 0 0 .07-.216 3 3 0 0 0 .003-.004 3 3 0 0 0 .007-.026 3 3 0 0 0 .03-.138 3 3 0 0 0 .031-.149 3 3 0 0 0 .002-.008 3 3 0 0 0 .012-.101 3 3 0 0 0 .02-.184 3 3 0 0 0 .001-.011 3 3 0 0 0 0-.041A3 3 0 0 0 15 12a3 3 0 0 0-.002-.053 3 3 0 0 0-.01-.166 3 3 0 0 0-.011-.133 3 3 0 0 0-.026-.17 3 3 0 0 0-.021-.113 3 3 0 0 0-.05-.19 3 3 0 0 0-.03-.103 3 3 0 0 0-.057-.16 3 3 0 0 0-.053-.129 3 3 0 0 0-.072-.146 3 3 0 0 0-.063-.12 3 3 0 0 0-.082-.132 3 3 0 0 0-.08-.123 3 3 0 0 0-.088-.116 3 3 0 0 0-.105-.126 3 3 0 0 0-.094-.1 3 3 0 0 0-.111-.111 3 3 0 0 0-.107-.096 3 3 0 0 0-.116-.094 3 3 0 0 0-.136-.098 3 3 0 0 0-.11-.072 3 3 0 0 0-.133-.076 3 3 0 0 0-.132-.07 3 3 0 0 0-.14-.063 3 3 0 0 0-.14-.054 3 3 0 0 0-.228-.077 3 3 0 0 0-.026-.007 3 3 0 0 0-.138-.03 3 3 0 0 0-.149-.031 3 3 0 0 0-.008-.002 3 3 0 0 0-.101-.012 3 3 0 0 0-.184-.02 3 3 0 0 0-.011-.001 3 3 0 0 0-.041 0A3 3 0 0 0 12 9z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 720 720" id="arduino" xmlns="http://www.w3.org/2000/svg"><defs><symbol id="ana" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke-opacity="100%" stroke-width="60" stroke="#00979c" d="M174 30a10.5 10.1 0 0 0 0 280C364 320 344 30 544 30a10.5 10.1 0 0 1 0 280C354 320 374 30 174 30"/><path d="M528 205v-32.8h-32.5v-13.7H528V126h13.9v32.5h32.5v13.7h-32.5V205H528z" text-anchor="middle" fill="#00979c" stroke-width="20" stroke="#00979c" font-family="sans-serif" font-size="167"/><path fill="#00979c" stroke="#00979c" stroke-width="23.6" transform="matrix(1.56 0 0 .64 -366 .528)" d="M321 266v-17.4h53.3V266H321z"/></symbol></defs><title>Layer 1</title><use x="20.063" y="360.85" transform="matrix(.997 0 0 .997 -18.596 -159.19)" xlink:href="#ana"/></symbol><symbol viewBox="0 0 24 24" id="assembly" xmlns="http://www.w3.org/2000/svg"><path d="M1.746 1.566v20.905H5.13v-2.088H3.438V3.656h1.69v-2.09H1.747zm17.219 0v2.09h1.693v16.727h-1.693v2.09h3.383V1.566h-3.383zM15.196 3.988c-.5 0-.93.076-1.29.225-.359.15-.652.372-.877.671-.226.302-.39.673-.494 1.108a6.715 6.715 0 0 0-.155 1.54c0 .573.049 1.083.15 1.528.1.442.264.811.49 1.11.222.298.512.524.872.676.36.153.795.23 1.304.23.518 0 .954-.075 1.308-.224.353-.153.643-.376.869-.671.219-.29.38-.661.484-1.112.104-.454.156-.967.156-1.54 0-.573-.052-1.079-.152-1.515a2.92 2.92 0 0 0-.485-1.106 2.09 2.09 0 0 0-.868-.686c-.354-.155-.79-.234-1.312-.234zm-6.814.12a.941.941 0 0 1-.138.458.849.849 0 0 1-.356.296A1.71 1.71 0 0 1 7.385 5a5.244 5.244 0 0 1-.631.037v1.11H8.19v3.6H6.754v1.188h4.545V9.745H9.894V4.11H8.382zm6.814 1.138c.375 0 .643.176.805.527.161.348.241.933.241 1.756 0 .814-.082 1.399-.247 1.756-.164.356-.43.534-.799.534-.369 0-.636-.178-.8-.534-.165-.357-.248-.941-.248-1.749 0-.829.082-1.415.243-1.763.162-.35.43-.527.805-.527zm-6.33 7.64c-.5 0-.93.073-1.29.223-.359.15-.651.374-.877.673-.225.302-.39.67-.494 1.106a6.715 6.715 0 0 0-.155 1.54c0 .573.05 1.082.15 1.527.1.442.264.814.49 1.112.222.3.514.525.874.677.36.152.793.229 1.302.229.519 0 .954-.076 1.308-.225.354-.153.643-.376.869-.672.22-.29.38-.66.484-1.111.104-.455.156-.967.156-1.54 0-.573-.05-1.079-.15-1.515a2.923 2.923 0 0 0-.487-1.106 2.084 2.084 0 0 0-.867-.686c-.353-.156-.791-.232-1.313-.232zm5.846.119a.941.941 0 0 1-.138.457.85.85 0 0 1-.356.296 1.71 1.71 0 0 1-.503.137 5.245 5.245 0 0 1-.631.037v1.112h1.435v3.597h-1.435v1.189h4.545v-1.189h-1.405v-5.636h-1.512zm-5.846 1.137c.375 0 .643.176.805.527.162.347.241.933.241 1.756 0 .813-.08 1.399-.245 1.755-.164.357-.432.534-.8.534-.37 0-.637-.177-.802-.534-.164-.356-.245-.939-.245-1.746 0-.83.08-1.418.242-1.765.161-.35.43-.527.804-.527z" fill="#ff6e40"/></symbol><symbol viewBox="0 0 24 24" id="aurelia" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="api" x1="-31.824" x2="19.682" y1="-11.741" y2="35.548" gradientTransform="scale(.95818 1.0436)" gradientUnits="userSpaceOnUse" xlink:href="#apa"/><linearGradient id="apa" x1="-3.881" x2="2.377" y1="-1.442" y2="4.304"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="apj" x1="12.022" x2="-15.716" y1="13.922" y2="-23.952" gradientTransform="scale(.96226 1.0392)" gradientUnits="userSpaceOnUse" xlink:href="#apb"/><linearGradient id="apb" x1=".729" x2="-.971" y1=".844" y2="-1.477"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".29"/><stop stop-color="#CD0F7E" offset=".84"/><stop stop-color="#ED2C89" offset="1"/></linearGradient><linearGradient id="apk" x1="-23.39" x2="23.931" y1="-57.289" y2="8.573" gradientTransform="scale(1.0429 .95884)" gradientUnits="userSpaceOnUse" xlink:href="#apc"/><linearGradient id="apc" x1="-2.839" x2="2.875" y1="-6.936" y2="1.017"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="apl" x1="-53.331" x2="6.771" y1="-30.517" y2="18.785" gradientTransform="scale(.99898 1.001)" gradientUnits="userSpaceOnUse" xlink:href="#apd"/><linearGradient id="apd" x1="-8.212" x2="1.02" y1="-4.691" y2="2.882"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="apm" x1="-14.029" x2="41.998" y1="-23.111" y2="26.259" gradientTransform="scale(1.0003 .99965)" gradientUnits="userSpaceOnUse" xlink:href="#ape"/><linearGradient id="ape" x1="-1.404" x2="4.19" y1="-2.309" y2="2.62"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="apn" x1="31.177" x2="3.37" y1="41.442" y2="3.402" gradientTransform="scale(.96254 1.0389)" gradientUnits="userSpaceOnUse" xlink:href="#apf"/><linearGradient id="apf" x1="1.911" x2=".204" y1="2.539" y2=".204"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".29"/><stop stop-color="#CD0F7E" offset=".84"/><stop stop-color="#ED2C89" offset="1"/></linearGradient><linearGradient id="apo" x1="-31.905" x2="19.599" y1="-14.258" y2="42.767" gradientTransform="scale(.95823 1.0436)" gradientUnits="userSpaceOnUse" xlink:href="#apg"/><linearGradient id="apg" x1="-3.881" x2="2.377" y1="-1.738" y2="5.19"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="app" x1="4.301" x2="34.534" y1="34.41" y2="4.514" gradientTransform="scale(1.002 .99796)" gradientUnits="userSpaceOnUse" xlink:href="#aph"/><linearGradient id="aph" x1=".112" x2=".901" y1=".897" y2=".116"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".53"/><stop stop-color="#CD0F7E" offset=".79"/><stop stop-color="#ED2C89" offset="1"/></linearGradient></defs><g transform="rotate(11.282 -1.694 21.569) scale(.47102)" clip-rule="evenodd" fill="none" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M8.002 6.127L4.117 8.719.116 2.723 4 .13z" transform="rotate(-11.284 17.839 -78.732)" fill="url(#api)"/><path d="M9.179 1.887l6.637 9.946-7.906 5.276-6.637-9.946L.115 5.43 8.02.153z" transform="rotate(-11.284 129.49 -99.884)" fill="url(#apj)"/><path d="M7.3 1.88l1.462 2.189-6.018 4.015L.124 4.16l1.315-.877L6.143.144z" transform="rotate(-11.284 167.2 -62.32)" fill="url(#apk)"/><path d="M2.328 1.146L4.016.02l2.619 3.925L2.75 6.537 1.29 4.347l2.197-1.466zm-1.04 3.201L.132 2.612l2.197-1.466 1.158 1.735z" transform="rotate(-11.284 104.37 -149.22)" fill="url(#apl)"/><path d="M5.346 9.155l-1.315.877L.03 4.035 6.047.019l2.805 4.204L4.15 7.36l4.703-3.138 1.197 1.793z" transform="rotate(-11.284 81.819 7.645)" fill="url(#apm)"/><path d="M14.533 9.934l1.197 1.793-7.907 5.276-1.196-1.793L.052 5.358 7.958.082z" transform="rotate(-11.284 17.141 -7.825)" fill="url(#apn)"/><path d="M6.235 7.177L4.038 8.643 2.84 6.849.036 2.646 3.92.053 7.923 6.05z" transform="rotate(-11.284 18.188 -79.174)" fill="url(#apo)"/><path d="M18.955 35.925L17.48 34.45l3.998-3.998 1.475 1.475z" fill="#714896"/><path d="M33.33 21.55l-1.475-1.474 1.867-1.868 1.475 1.475z" fill="#6f4795"/><path d="M7.12 24.09l-1.525-1.525 3.998-3.998 1.525 1.525z" fill="#88519f"/><path d="M21.495 9.714L19.97 8.19l1.868-1.868 1.524 1.525z" fill="#85509e"/><path d="M31.418 23.462l-6.72 6.72-1.475-1.474 6.72-6.721z" fill="#8d166a"/><path d="M18.058 10.101l1.525 1.525-6.721 6.72-1.525-1.524z" fill="#a70d6f"/><path d="M2.375 11.769l1.9 1.9-1.9 1.901-1.901-1.9z" fill="#9e61ad"/><path d="M15.523 36.482l1.9 1.9-1.9 1.901-1.9-1.9z" fill="#8053a3"/><path d="M8.372 38.294L.017 29.876 29.749.08l8.636 8.201z" transform="translate(1.823 1.548)" fill="url(#app)"/></g></symbol><symbol viewBox="0 0 24 24" id="autohotkey" xmlns="http://www.w3.org/2000/svg"><path d="M5 3c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H5zm3.668 3.447a.9.9 0 0 1 .652.256.84.84 0 0 1 .262.625c0 .34-.014.852-.041 1.537-.022.68-.033 1.19-.033 1.53 0 .111-.016.326-.047.644a6.149 6.149 0 0 0-.033.68l2.578-.485c1.007-.179 1.874-.281 2.603-.308.018-.3.048-1.105.088-2.416.01-.345.115-.742.317-1.19.25-.55.533-.826.851-.826.237 0 .448.08.631.236.197.17.295.382.295.637a.775.775 0 0 1-.025.201c-.09.327-.135.612-.135.854 0 .125-.014.32-.041.584-.023.26-.033.453-.033.578 0 .425-.022 1.056-.067 1.893a38.963 38.963 0 0 0-.068 1.892c0 .327.025.816.074 1.465.05.649.074 1.136.074 1.463a.84.84 0 0 1-.261.625.893.893 0 0 1-.65.254 1 1 0 0 1-.686-.254.777.777 0 0 1-.29-.611c0-.327-.015-.818-.046-1.471a39.552 39.552 0 0 1-.041-1.47c0-.256.004-.482.013-.679-.702.032-1.57.142-2.603.33-.86.157-1.719.316-2.578.477-.01.304-.042.812-.096 1.523a22.354 22.354 0 0 0-.066 1.538.84.84 0 0 1-.262.625.893.893 0 0 1-.65.253.898.898 0 0 1-.653-.253.84.84 0 0 1-.262-.625c0-.452.038-1.128.114-2.028.08-.9.12-1.575.12-2.027 0-.573.015-1.436.042-2.586.027-1.155.04-2.017.04-2.59a.84.84 0 0 1 .263-.625.895.895 0 0 1 .65-.256z" fill="#4caf50"/></symbol><symbol viewBox="0 0 24 24" id="autoit" xmlns="http://www.w3.org/2000/svg"><defs id="ardefs8"><style id="arstyle4482">.cls-1{fill:#5d83ac}.cls-2{fill:#f0f0f0;fill-rule:evenodd}</style><style id="arstyle4510">.cls-1{fill:#5d83ac}.cls-2{fill:#f0f0f0;fill-rule:evenodd}</style></defs><g id="arg4522" transform="translate(-59.538 -26.404) scale(.0555)"><path d="M12.8 2.133A10.666 10.666 0 0 0 2.136 12.799 10.666 10.666 0 0 0 12.8 23.465a10.666 10.666 0 0 0 10.668-10.666A10.666 10.666 0 0 0 12.8 2.133zm.15 4.713c.456 0 .836.105 1.142.314.306.21.565.469.78.78l6.089 8.812H9.627l1.82-2.506h3.36c.315 0 .589.01.822.03a11.93 11.93 0 0 1-.473-.663 39.13 39.13 0 0 0-.517-.75l-1.748-2.578-4.577 6.467H4.746l6.25-8.813c.204-.281.46-.534.772-.757.31-.224.705-.336 1.181-.336z" transform="matrix(16.89188 0 0 16.89188 1072.761 475.745)" id="arcircle4514" fill="#1976d2" stroke-width=".026"/></g></symbol><symbol viewBox="0 0 213.33333 213.33333" id="babel" xmlns="http://www.w3.org/2000/svg"><path d="M50.22 199.659c-.875-.406-1.261-1.6-.857-2.652.404-1.053.12-1.914-.63-1.914s-1.615.748-1.92 1.663c-.328.983-1.27.302-2.304-1.667-.962-1.831-3.718-5.533-6.126-8.226-9.418-10.535-7.71-27.444 5.432-53.77 12.459-24.96 23.117-39.033 45.966-60.696 30.229-28.66 52.679-46.223 70.587-55.22 10.98-5.518 13.025-5.059 2.778.624-11.004 6.102-11.378 6.359-10.512 7.226.33.33 7.306-2.67 15.504-6.667 15.87-7.737 16.34-7.912 16.34-6.082 0 .652-4.95 3.738-11 6.858-13.062 6.736-12.722 6.48-10.472 7.872 1.117.69 5.428-.582 11.54-3.406 5.367-2.48 10.397-4.508 11.179-4.508 2.755 0-3.928 5.302-11.541 9.157-20.437 10.35-68.937 46.043-68.07 50.097.166.777-5.792 7.639-13.241 15.248-15.257 15.587-26.14 30.002-33.748 44.706-6.379 12.326-7.457 17.734-5.385 26.996 3.482 15.56 11.592 18.366 31.482 10.895 28.228-10.603 45.758-28.704 47.022-48.556.602-9.442-1.317-13.479-8.52-17.93-4.01-2.48-5.268-2.621-12.065-1.365-4.173.771-10.153 2.906-13.289 4.744s-6.455 3.34-7.377 3.34c-.922 0-3.216 1.336-5.096 2.968-1.88 1.633.48-1.13 5.247-6.14 6.82-7.167 7.956-8.9 5.333-8.132-5.208 1.525-10.194 4.33-15.649 8.803-2.76 2.264-.923.175 4.08-4.641 11.565-11.131 21.183-15.97 33.088-16.641 17.097-.966 27.254 5.805 31.964 21.31 2.435 8.017 2.609 10.24 1.353 17.37-1.65 9.361-7.034 21.553-15.593 35.307-4.398 7.067-8.434 11.427-15.588 16.844-9.166 6.94-15.654 11.02-15.654 9.845 0-.295 2.455-2.161 5.455-4.147 8.818-5.835 5.075-5.377-8.326 1.02-6.854 3.27-15.199 6.593-18.542 7.38-7.106 1.675-30.527 3.164-32.846 2.089zm-8.408-19.899c0-1.1-.6-2-1.333-2-.734 0-1.334.9-1.334 2s.6 2 1.333 2c.734 0 1.334-.9 1.334-2zm89.255-8.204c1.53-1.945 2.473-3.845 2.097-4.222-.377-.377-.836-.435-1.02-.13-.182.306-1.787 2.206-3.565 4.223-1.778 2.016-2.571 3.666-1.763 3.666s2.72-1.591 4.25-3.536zm-77.644-1.745c-.82-2.172-1.74-3.7-2.045-3.396-.951.952 1.088 7.345 2.343 7.345.656 0 .522-1.777-.298-3.95zm82.303-27.915c-.837-.837-3.217 2.55-3.184 4.53.012.734.896.178 1.965-1.235 1.07-1.413 1.618-2.896 1.219-3.295zm-66.238-36.904c-1.312-1.312-3.676.702-3.676 3.133 0 2.035.175 2.031 2.254-.047 1.24-1.24 1.88-2.628 1.422-3.086zm39.657.768c4.403-2.196 6.8-3.986 5.333-3.982-2.838.01-16.667 6.028-16.667 7.254 0 1.6 3.717.527 11.333-3.272zm16.667-5.333c0-.733-.9-1.333-2-1.333s-2 .6-2 1.333.9 1.333 2 1.333 2-.6 2-1.333zm-3.334-3.923l5.334-1.104-7.334-.133c-4.033-.073-8.233.45-9.333 1.16-2.539 1.64 3.572 1.682 11.333.077zm35.738-63.976c2.788-1.69 4.765-3.376 4.393-3.748-.947-.947-11.942 5.654-14.237 8.548-1.792 2.258-1.714 2.276 1.44.329a1452.76 1452.76 0 0 1 8.403-5.13z" fill="#ffca28" stroke-width="1.333"/></symbol><symbol viewBox="0 0 400 400" fill-opacity=".05" id="bithound" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.88 0 0 .88 24.121 2.895)" fill="#e53935" fill-opacity="1"><path d="M370.5 207c-1.5-14.8-4.8-29.9-9.5-44-13.5-40.3-38.6-81.6-70.3-110.1-1.4-1.2-6.7-4.4-8.7-3.3-5.2 2.9 4.6 22.8 5.8 26.4 7.4 22 12.1 45.3 6.8 68.3-7.1 30.4-30.4 51.7-61.5 54.3-17.1 1.4-34.3-.5-51.4 1.5-25.6 3-51.7 11.8-68 32.8-1.9 2.4-3.6 5.1-5.2 7.9h-.4c-6.3.7-12.6-2-15.7-3.7-.8-.5-1.6-.9-2.2-1.2-19-10.5-33-34-41.6-53.4-3.9-9-7.2-18.4-9.3-27.9-1-4.3-1.1-8.8-1.3-13.2-.1-2.7.3-6.5-1.2-8.9-3.3-5.2-7.5-.2-8.2 4-1.1 6.9-2.1 13.7-1.8 20.7.5 11.8 3.8 23.5 8 34.5 6.2 16.2 14.9 31.1 26.2 44.4 4.7 5.5 9.7 10.6 15.1 15.3 4.8 4.3 10.9 7.7 14.5 13.2 4.2 6.3 4.9 14.1 4.5 21.4-1 19.3-1.6 37.4 3.9 56.2 4.8 16.7 10.8 33.8 20.8 48.1 5 7.1 11.2 14.6 18 19.9 4.6 3.6 13.3 4 8.3-9.2-11.1-29.3-12.1-59.7 5.2-87.1 14.5-22.8 40.1-43.1 69-39.5 42.5 5.3 72.1 44.3 70 86-.6 11.7-1 21.7-4.7 32.7-1.5 4.4-2.6 10-1.5 14.6 1.8 7.8 10.5 4.9 14.3-.2 10.3-14 21.1-27.6 30.8-42 31.6-47.2 47-101.8 41.3-158.5z"/><path d="M132.4 92.1c.7 2.3 1.4 4.8 1.9 7.5.1 1.1.4 2.3 1 3.4 2.6 6.8 8.9 10.5 14.8 14 3.6 2.2 10.1 4.3 14.1 5.9 5.2 2.1 16.4-.6 21.7-1 12.2-1 23.5-5.3 34.7 1.2-57.4 67.3-3.2 82.3 38.8 49.9 48-37 2.8-124.3 2.8-124.3s-1-6.8-19.2-10.8c-1.7-.9-3.4-1.7-5.1-2.4-18-8.3-34.2 5.3-47.2 16.4-3.8 3.2-7.5 6.4-11.5 9.4-5.4 4-11.2 7.3-17.3 10.2-6.4 3-14 6.4-21.1 6.7-1 0-2.9.2-4.9.6-3.1.3-4.7 1.1-5.4 2.5-1.2 1-2 2.4-1.8 4.2.2 2.5 1.4 4.6 2.7 6.2.4.1.7.3 1 .4z"/></g></symbol><symbol viewBox="0 0 400.00001 399.99999" id="bower" xmlns="http://www.w3.org/2000/svg"><g transform="translate(12.061 33.203) scale(.81733)"><path d="M447.61 200.08c-23.139-22.234-138.85-36.114-175.36-40.154a107.137 107.137 0 0 0 4.517-12.944 146.107 146.107 0 0 1 15.905-5.901c.677 1.997 3.865 9.648 5.682 13.279 73.415 2.025 77.184-54.557 80.17-70.058 2.92-15.157 2.771-29.802 27.953-56.575-37.516-10.933-91.467 16.945-109.54 58.437-6.79-2.545-13.597-4.424-20.328-5.586-4.824-19.46-29.944-73.672-95.863-73.672-83.46 0-174.43 68.853-174.43 185.41 0 97.976 66.891 183.84 104.68 183.84 16.505 0 30.703-12.36 34.036-23.44 2.795 7.597 11.368 31.213 14.184 37.225 4.162 8.89 23.41 16.583 31.833 7.357 10.83 6.017 30.703 9.641 41.534-6.405 20.86 4.412 39.3-8.026 39.702-22.868 10.235-.546 15.256-14.918 13.021-26.363-1.647-8.426-19.248-38.66-26.113-49.098 13.59 11.054 48.013 14.183 52.194.007 21.911 17.198 56.057 8.171 58.765-5.815 26.624 6.917 57.16-8.276 52.146-26.676 42.771-2.958 37.296-48.464 25.296-59.996z" fill="#543729" stroke-width=".973"/><path d="M328.514 103.025c9.212-18.277 20.788-38.234 35.409-50.58-16.093 6.485-31.981 25.873-41.375 46.595a144.914 144.914 0 0 0-14.552-8.132c13.105-27.972 43.555-51.332 77.112-53.157-22.477 20.385-14.498 62.754-32.979 85.183-5.288-5.311-17.43-15.562-23.615-19.909zm-14.53 29.762c.01-.7.272-6.094.763-8.557-1.288-.304-9.3-1.87-13.476-1.772-.304 5.245 2.204 14.17 4.684 19.541 17.075-.358 29.408-5.471 36.667-10.172-6.18-2.88-16.726-5.442-24.745-6.974-.894 1.851-3.097 6.568-3.892 7.934z" fill="#00acee"/><g stroke-width=".973"><path d="M250.54 277.39c.004.024.015.057.018.082-2.165-4.657-4.463-10.314-7.208-17.708 10.688 15.557 44.184 7.533 42.427-6.407 16.395 12.336 50.143-2.055 42.471-19.353 16.423 7.653 35.168-7.745 30.964-14.455 28 5.4 54.832 10.783 63.256 12.938-5.595 9.123-18.339 15.566-37.549 11.089 10.38 14.14-9.773 31.105-37.844 21.76 6.18 13.883-18.814 26.38-47.22 11.91.361 13.889-35.24 15.488-49.315.143zm55.543-70.194c32.497 2.495 86.238 7.34 119.51 11.997-2.102-10.828-7.844-13.921-25.905-18.772-19.425 2.072-68.706 6.913-93.604 6.776z" fill="#2baf2b"/><path d="M285.78 253.36c16.395 12.336 50.143-2.055 42.471-19.353 16.423 7.653 35.168-7.745 30.964-14.455-33.103-6.383-67.84-12.788-75.719-13.908 4.78.254 12.702.797 22.59 1.556 24.899.137 74.18-4.704 93.604-6.775-31.452-7.975-95.666-19.613-140.01-22.48-2.055 3.003-5.833 8.097-12.413 13.51-19.403 41.053-54.557 68.34-93.454 68.34-11.335 0-24.018-1.912-38.233-6.456-8.865 9.497-46.661 16.694-77.329 1.641 24.326 56.961 80.74 94.984 143.19 94.984 52.591 0 75.912-53.704 70.808-67.914-1.238-3.45-6.145-14.889-8.891-22.283 10.689 15.556 44.185 7.532 42.429-6.408z" fill="#ffcc2f"/><path d="M253.91 145.27c4.644-2.526 20.69-12.253 35.981-15.908a67.843 67.843 0 0 1-.536-5.12c-10.032 2.403-28.945 10.51-39.784-.661 22.866 6.9 34.283-6.149 51.09-6.149 10.014 0 24.305 2.798 35.57 7.22-9.061-8.37-38.772-33.63-75.558-33.717-8.213 9.957-17.09 31.526-6.764 54.334z" fill="#cecece"/><path d="M115.58 253.33c14.215 4.544 26.898 6.457 38.233 6.457 38.896 0 74.05-27.29 93.454-68.341-14.351 11.978-39.291 22.228-78.241 22.228 34.694-7.866 64.56-25.156 79.753-50.427-10.68-16.998-22.263-54.603 7.07-84.33-4.512-14.497-26.475-52.766-75.095-52.766-84.85 0-155.17 71.001-155.17 166.15 0 22.525 4.547 43.65 12.67 62.664 30.666 15.054 68.462 7.858 77.327-1.64z" fill="#ef5734"/><path d="M141.03 108.45c0 21.644 17.546 39.191 39.19 39.191s39.192-17.548 39.192-39.191c0-21.644-17.548-39.191-39.192-39.191-21.644 0-39.19 17.547-39.19 39.191z" fill="#ffcc2f"/><path d="M156.76 108.45c0 12.958 10.507 23.463 23.463 23.463 12.96 0 23.464-10.506 23.464-23.463 0-12.959-10.504-23.464-23.464-23.464-12.957 0-23.463 10.506-23.463 23.464z" fill="#543729"/><ellipse cx="180.22" cy="98.044" rx="13.673" ry="8.501" fill="#fff"/></g></g></symbol><symbol viewBox="0 0 140 140" id="browserlist" xmlns="http://www.w3.org/2000/svg"><title>Browserslist logo</title><path d="M70.314 10.066a59.828 59.828 0 0 0-59.828 59.828 59.828 59.828 0 0 0 59.828 59.828 59.828 59.828 0 0 0 59.828-59.828 59.828 59.828 0 0 0-59.828-59.828zm-4.836 8.785c.496 4.043 1.352 7.322 2.572 10.223 4.779-4.287 10.265-7.546 16.041-9.02-.981 3.938-1.357 7.295-1.261 10.43 6.026-2.314 12.349-3.404 18.3-2.706-3.182 2.413-5.482 4.717-7.128 7.015-2.201 12.074 6.858 20.43 14.779 24.551a5.128 5.128 0 0 1 5.183-3.888 5.128 5.128 0 0 1 3.7 8.435v.002c-.487 1.055-2.002 2.343-3.497 3.219-4.075 2.39-11.172 5.736-20.914 7.39.045 1.214.077 2.453.077 3.747 0 4.817-.485 8.291-1.385 10.699-3.3 13.313-12.648 26.76-24.695 31.95.357-4.083.197-7.485-.402-10.591-5.582 3.218-11.646 5.278-17.623 5.52h-.002c1.785-3.662 2.855-6.878 3.412-9.976-6.347.996-12.727.742-18.377-1.17 2.93-2.732 5.054-5.314 6.673-7.96-6.292-1.344-12.169-3.87-16.766-7.686 3.822-1.544 6.795-3.239 9.3-5.197-5.426-3.517-10.034-7.998-12.972-13.23 4.012-.07 7.321-.568 10.3-1.453-3.786-5.215-6.468-11.032-7.333-16.951 3.861 1.405 7.196 2.133 10.36 2.355-1.662-6.22-2.081-12.605-.768-18.436 3.03 2.634 5.824 4.48 8.63 5.815.678-6.406 2.576-12.52 5.893-17.496 1.926 3.622 3.914 6.391 6.111 8.672 2.93-5.754 6.9-10.798 11.791-14.262zm26.465 19.557c-2.395 5.514-1.665 11.297-.555 18.732a2.138 2.138 0 0 0 .28-4.178 3.419 3.419 0 1 1 .092 6.704c.574 3.882 1.157 8.18 1.421 13.125a67.143 67.143 0 0 0 3.25-.649c6.616-1.487 12.258-3.801 16.871-6.506.45-.264.884-.563 1.276-.867.366-.557.333-.957.035-1.285-4.831-1.245-10.891-4.53-15.258-8.795-4.764-4.653-7.428-10.164-7.412-16.281z" fill="#ffca28" stroke-width=".855"/></symbol><symbol viewBox="0 0 140 140" id="browserlist_light" xmlns="http://www.w3.org/2000/svg"><title>Browserslist logo</title><g transform="translate(10.823 10.1)" stroke-width=".855"><circle cx="59.492" cy="59.795" r="59.828" fill="#ffca28"/><path d="M54.656 8.752c-4.89 3.464-8.862 8.508-11.791 14.262-2.198-2.28-4.185-5.05-6.111-8.672-3.318 4.976-5.216 11.09-5.893 17.496-2.807-1.335-5.6-3.18-8.63-5.814-1.314 5.83-.895 12.216.767 18.436-3.164-.223-6.498-.95-10.36-2.356.865 5.92 3.548 11.737 7.333 16.951-2.978.885-6.287 1.383-10.3 1.453 2.939 5.233 7.547 9.714 12.972 13.23-2.505 1.959-5.478 3.654-9.299 5.198 4.596 3.815 10.474 6.341 16.766 7.685-1.62 2.647-3.743 5.228-6.674 7.96 5.65 1.912 12.03 2.166 18.377 1.17-.556 3.098-1.626 6.314-3.412 9.975h.002c5.977-.24 12.042-2.3 17.623-5.52.6 3.108.76 6.51.402 10.593 12.047-5.19 21.395-18.638 24.695-31.951.9-2.408 1.385-5.881 1.385-10.7 0-1.293-.031-2.531-.076-3.745 9.742-1.655 16.839-5.001 20.914-7.39 1.494-.877 3.01-2.165 3.496-3.22v-.002a5.128 5.128 0 0 0-3.7-8.435 5.128 5.128 0 0 0-5.183 3.889c-7.92-4.122-16.98-12.477-14.779-24.551 1.646-2.299 3.947-4.603 7.13-7.016-5.952-.698-12.276.392-18.302 2.707-.095-3.135.28-6.492 1.262-10.43-5.776 1.473-11.262 4.733-16.041 9.02-1.22-2.902-2.076-6.18-2.572-10.223zm26.465 19.557c-.015 6.117 2.648 11.628 7.412 16.281 4.366 4.265 10.426 7.55 15.258 8.795.298.328.331.728-.035 1.285-.392.304-.825.603-1.275.867-4.613 2.704-10.256 5.019-16.871 6.506-1.071.24-2.154.458-3.25.649-.265-4.945-.848-9.243-1.422-13.125a3.419 3.419 0 1 0-.092-6.703 2.138 2.138 0 0 1-.28 4.177c-1.11-7.435-1.84-13.218.555-18.732z" fill="#37474f"/></g></symbol><symbol viewBox="0 0 24 24" id="bucklescript" xmlns="http://www.w3.org/2000/svg"><path d="M3 3v18h18V3H3zm14.1 8.858a5.5 5.5 0 0 1 1.26.145c.417.093.778.213 1.082.357v1.723h-.18a3.281 3.281 0 0 0-.959-.603 2.867 2.867 0 0 0-1.155-.247c-.14 0-.277.011-.416.035a1.4 1.4 0 0 0-.395.12.756.756 0 0 0-.291.231.54.54 0 0 0-.123.348c0 .198.065.35.196.456.13.104.376.2.738.288.237.057.466.11.683.164.22.054.455.128.706.222.496.188.86.444 1.095.77.238.32.357.738.357 1.253 0 .737-.271 1.336-.813 1.798-.538.46-1.27.689-2.197.689a5.447 5.447 0 0 1-1.402-.161 6.725 6.725 0 0 1-1.117-.416v-1.794h.183c.344.318.73.563 1.155.734.429.17.839.256 1.233.256.1 0 .235-.01.4-.03.166-.02.3-.055.403-.102a.97.97 0 0 0 .313-.225c.084-.09.127-.223.127-.4a.568.568 0 0 0-.183-.424c-.119-.12-.294-.213-.526-.276-.243-.067-.5-.128-.773-.185a5.523 5.523 0 0 1-.76-.227c-.544-.204-.936-.48-1.177-.828-.237-.351-.357-.786-.357-1.305 0-.697.27-1.265.81-1.703.54-.442 1.235-.663 2.083-.663zm-8.981.135h2.51c.521 0 .903.02 1.143.06.243.041.484.13.721.266.246.144.43.338.548.583.121.24.181.518.181.83 0 .36-.082.68-.247.959a1.697 1.697 0 0 1-.7.642v.04c.423.098.758.298 1.004.603.249.305.373.706.373 1.205 0 .361-.063.686-.19.97-.125.285-.296.52-.516.707a2.31 2.31 0 0 1-.845.472c-.304.094-.69.141-1.159.141H8.12v-7.478zm1.659 1.372v1.582h.262c.263 0 .486-.007.672-.017.185-.01.332-.043.44-.1.15-.077.248-.175.294-.295.046-.124.07-.266.07-.427a.91.91 0 0 0-.083-.371.518.518 0 0 0-.282-.277 1.187 1.187 0 0 0-.456-.086c-.18-.007-.433-.01-.76-.01h-.157zm0 2.873V18.1H9.9c.469 0 .804-.002 1.007-.006.202-.003.39-.046.56-.13a.712.712 0 0 0 .357-.33c.067-.142.099-.302.099-.483 0-.237-.04-.42-.121-.547-.078-.13-.214-.228-.405-.291a1.842 1.842 0 0 0-.538-.072 49.47 49.47 0 0 0-.716-.003h-.366z" fill="#26a69a" stroke-width="1.067"/></symbol><symbol viewBox="0 0 24 24" id="c" xmlns="http://www.w3.org/2000/svg"><path d="M15.45 15.97l.42 2.44c-.26.14-.68.27-1.24.39-.57.13-1.24.2-2.01.2-2.21-.04-3.87-.7-4.98-1.96-1.14-1.27-1.68-2.88-1.68-4.83C6 9.9 6.68 8.13 8 6.89 9.28 5.64 10.92 5 12.9 5c.75 0 1.4.07 1.94.19s.94.25 1.2.4l-.6 2.49-1.04-.34c-.4-.1-.87-.15-1.4-.15-1.15-.01-2.11.36-2.86 1.1-.76.73-1.14 1.85-1.18 3.34.01 1.36.37 2.42 1.08 3.2.71.77 1.7 1.17 2.99 1.18l1.33-.12c.43-.08.79-.19 1.09-.32z" fill="#0277bd"/></symbol><symbol viewBox="0 0 300 300" id="cabal" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -822.52)" fill-rule="evenodd" color="#000"><rect transform="matrix(-.98339 .18149 .60192 .79856 0 0)" x="405.55" y="967.22" width="107.25" height="156.59" rx="12.306" ry="12.31" fill="#2d9bbd"/><rect transform="matrix(-.98528 .17093 -.59175 .80612 0 0)" x="-1156.5" y="1461.9" width="108.34" height="123.15" rx="10.69" ry="12.31" fill="#4a4bcd"/><path d="M52.112 965.158c-1.343 3.515-26.292 23.248-25.744 27.277.548 4.03 29.812 16.023 32.04 19.027s10.545 41.668 13.603 42.5 18.828-31.274 21.548-32.932c2.72-1.658 32.808 2.503 34.15-1.01 1.343-3.515-18.174-35.352-18.721-39.381-.548-4.03 9.732-40.12 7.502-43.125-2.229-3.005-30.06 9.427-33.118 8.594-3.059-.833-26.793-27.3-29.514-25.643-2.72 1.657-.405 41.177-1.747 44.693z" fill="#2e5bc1"/></g></symbol><symbol viewBox="0 0 24 24" id="cake" xmlns="http://www.w3.org/2000/svg"><path d="M12.254 6.621a1.807 1.807 0 0 0 1.808-1.807c0-.344-.09-.66-.262-.932l-1.546-2.684-1.546 2.684a1.72 1.72 0 0 0-.262.932 1.808 1.808 0 0 0 1.808 1.807m4.158 9.04l-.967-.976-.976.976c-1.175 1.166-3.236 1.175-4.42 0l-.959-.976-.994.976a3.134 3.134 0 0 1-3.977.353v4.167a.904.904 0 0 0 .904.904h14.463a.904.904 0 0 0 .904-.904v-4.167a3.134 3.134 0 0 1-3.977-.353m1.265-6.328h-4.52V7.525H11.35v1.808H6.83a2.712 2.712 0 0 0-2.711 2.712v1.392c0 .977.795 1.772 1.771 1.772.489 0 .94-.18 1.248-.515l1.952-1.926 1.908 1.926c.669.669 1.835.669 2.504 0l1.916-1.926 1.944 1.926c.316.334.768.515 1.247.515.976 0 1.78-.795 1.78-1.772v-1.392a2.712 2.712 0 0 0-2.711-2.712z" fill="#ff7043" stroke-width=".904"/></symbol><symbol viewBox="0 0 24 24" id="certificate" xmlns="http://www.w3.org/2000/svg"><path d="M4 3c-1.11 0-2 .89-2 2v10a2 2 0 0 0 2 2h8v5l3-3 3 3v-5h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H4m8 2l3 2 3-2v3.5l3 1.5-3 1.5V15l-3-2-3 2v-3.5L9 10l3-1.5V5M4 5h5v2H4V5m0 4h3v2H4V9m0 4h5v2H4v-2z" fill="#ff5722"/></symbol><symbol viewBox="0 0 24 24" id="changelog" xmlns="http://www.w3.org/2000/svg"><path d="M11 7v5.11l4.71 2.79.79-1.28-4-2.37V7m0-5C8.97 2 5.91 3.92 4.27 6.77L2 4.5V11h6.5L5.75 8.25C6.96 5.73 9.5 4 12.5 4a7.5 7.5 0 0 1 7.5 7.5 7.5 7.5 0 0 1-7.5 7.5c-3.27 0-6.03-2.09-7.06-5h-2.1c1.1 4.03 4.77 7 9.16 7 5.24 0 9.5-4.25 9.5-9.5A9.5 9.5 0 0 0 12.5 2z" fill="#8bc34a"/></symbol><symbol viewBox="0 0 24 24" id="clojure" xmlns="http://www.w3.org/2000/svg"><path d="M3.355 1.78c-.845 0-1.525.68-1.525 1.525v17.441c0 .845.68 1.525 1.525 1.525h17.442c.845 0 1.525-.68 1.525-1.525V3.305c0-.845-.68-1.526-1.525-1.526H3.355zm6.168 2.572h1.963l6.368 14.931H15.93l-3.38-8.086-3.349 8.086H7.21l4.346-10.38-2.032-4.551z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="cmake" xmlns="http://www.w3.org/2000/svg"><path d="M11.99 2.965L2.977 20.999l9.874-8.47-.863-9.564z" fill="#1e88e5"/><path d="M12.007 2.963l.002.29 1.312 14.498-.001.006.023.26 7.362 2.979h.416l-.158-.311-.114-.228h-.002l-8.84-17.494z" fill="#e53935"/><path d="M8.607 16.11L2.98 20.995h17.743v-.016L8.607 16.11z" fill="#7cb342"/></symbol><symbol class="bfmain_logo__svg" viewBox="0 0 300 300.00001" id="code-climate" xmlns="http://www.w3.org/2000/svg"><path class="bfsymbol" d="M196.19 75.562l-51.846 51.561 30.766 30.766 21.08-21.08 59.252 59.537 30.481-30.766zm-61.246 60.961l-30.481-30.481-78.053 78.053-11.964 11.964 30.766 30.766 11.964-12.249 39.596-39.312 7.691-7.691 30.481 30.48 28.772 28.773 30.766-30.766-28.772-28.772z" fill="#eee" stroke-width="2.849"/></symbol><symbol class="bgmain_logo__svg" viewBox="0 0 300 300.00001" id="code-climate_light" xmlns="http://www.w3.org/2000/svg"><path class="bgsymbol" d="M196.19 75.562l-51.846 51.561 30.766 30.766 21.08-21.08 59.252 59.537 30.481-30.766zm-61.246 60.961l-30.481-30.481-78.053 78.053-11.964 11.964 30.766 30.766 11.964-12.249 39.596-39.312 7.691-7.691 30.481 30.48 28.772 28.773 30.766-30.766-28.772-28.772z" fill="#455a64" stroke-width="2.849"/></symbol><symbol viewBox="0 0 24 24" id="coffee" xmlns="http://www.w3.org/2000/svg"><path d="M2 21h18v-2H2M20 8h-2V5h2m0-2H4v10a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4v-3h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="coldfusion" xmlns="http://www.w3.org/2000/svg"><rect transform="rotate(90)" x="2.283" y="-21.86" width="19.487" height="19.487" ry="0" fill="#0d3858" stroke="#4dd0e1" stroke-width=".7"/><text x="6.653" y="16.426" fill="#4dd0e1" font-family="Calibri" font-size="29.001" font-weight="bold" letter-spacing="0" stroke-width=".725" word-spacing="0" style="line-height:1.25"><tspan x="6.653" y="16.426" font-family="'Segoe UI'" font-size="10.634" font-weight="normal">C<tspan font-size="11.844">f</tspan></tspan></text></symbol><symbol viewBox="0 0 24 24" id="conduct" xmlns="http://www.w3.org/2000/svg"><path d="M10 17l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9m-6-6a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z" fill="#cddc39"/></symbol><symbol viewBox="0 0 24 24" id="console" xmlns="http://www.w3.org/2000/svg"><path d="M20 19V7H4v12h16m0-16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16m-7 14v-2h5v2h-5m-3.42-4L5.57 9H8.4l3.3 3.3c.39.39.39 1.03 0 1.42L8.42 17H5.59z" fill="#ff7043"/></symbol><symbol viewBox="0 0 24 24" id="contributing" xmlns="http://www.w3.org/2000/svg"><path d="M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z" fill="#ffca28"/></symbol><symbol viewBox="0 0 24 24" id="cpp" xmlns="http://www.w3.org/2000/svg"><path d="M10.5 15.97l.41 2.44c-.26.14-.68.27-1.24.39-.57.13-1.24.2-2.01.2-2.21-.04-3.87-.7-4.98-1.96C1.56 15.77 1 14.16 1 12.21c.05-2.31.72-4.08 2-5.32C4.32 5.64 5.96 5 7.94 5c.75 0 1.4.07 1.94.19s.94.25 1.2.4l-.58 2.49-1.06-.34c-.4-.1-.86-.15-1.39-.15-1.16-.01-2.12.36-2.87 1.1-.76.73-1.15 1.85-1.18 3.34 0 1.36.37 2.42 1.08 3.2.71.77 1.71 1.17 2.99 1.18l1.33-.12c.43-.08.79-.19 1.1-.32M11 11h2V9h2v2h2v2h-2v2h-2v-2h-2v-2m7 0h2V9h2v2h2v2h-2v2h-2v-2h-2v-2z" fill="#0277bd"/></symbol><symbol viewBox="0 0 24 24" id="credits" xmlns="http://www.w3.org/2000/svg"><path d="M3 3h18v2H3V3m4 4h10v2H7V7m-4 4h18v2H3v-2m4 4h10v2H7v-2m-4 4h18v2H3v-2z" fill="#9ccc65"/></symbol><symbol viewBox="0 0 200 200" id="crystal" xmlns="http://www.w3.org/2000/svg"><style>.st0{fill:none}</style><path d="M179.363 121.67l-57.623 57.507c-.23.23-.576.346-.806.23l-78.713-21.09c-.346-.115-.577-.345-.577-.576L20.44 79.144c-.115-.345 0-.576.23-.806L78.294 20.83c.23-.23.576-.346.807-.23l78.713 21.09c.345.114.576.345.576.575l21.09 78.597c.23.346.115.577-.115.807zM102.148 59.09l-77.33 20.63c-.115 0-.23.23-.115.345l56.586 56.47c.115.115.346.115.346-.115l20.744-77.215c.115 0-.115-.23-.23-.116z" stroke-width="1.153" fill="#cfd8dc"/></symbol><symbol viewBox="0 0 200 200" id="crystal_light" xmlns="http://www.w3.org/2000/svg"><style>.st0{fill:none}</style><path d="M179.363 121.67l-57.623 57.507c-.23.23-.576.346-.806.23l-78.713-21.09c-.346-.115-.577-.345-.577-.576L20.44 79.144c-.115-.345 0-.576.23-.806L78.294 20.83c.23-.23.576-.346.807-.23l78.713 21.09c.345.114.576.345.576.575l21.09 78.597c.23.346.115.577-.115.807zM102.148 59.09l-77.33 20.63c-.115 0-.23.23-.115.345l56.586 56.47c.115.115.346.115.346-.115l20.744-77.215c.115 0-.115-.23-.23-.116z" fill="#37474f" stroke-width="1.153"/></symbol><symbol viewBox="0 0 24 24" id="csharp" xmlns="http://www.w3.org/2000/svg"><path d="M11.5 15.97l.41 2.44c-.26.14-.68.27-1.24.39-.57.13-1.24.2-2.01.2-2.21-.04-3.87-.7-4.98-1.96C2.56 15.77 2 14.16 2 12.21c.05-2.31.72-4.08 2-5.32C5.32 5.64 6.96 5 8.94 5c.75 0 1.4.07 1.94.19s.94.25 1.2.4l-.58 2.49-1.06-.34c-.4-.1-.86-.15-1.39-.15-1.16-.01-2.12.36-2.87 1.1-.76.73-1.15 1.85-1.18 3.34 0 1.36.37 2.42 1.08 3.2.71.77 1.71 1.17 2.99 1.18l1.33-.12c.43-.08.79-.19 1.1-.32M13.89 19l.61-4H13l.34-2h1.5l.32-2h-1.5L14 9h1.5l.61-4h2l-.61 4h1l.61-4h2l-.61 4H22l-.34 2h-1.5l-.32 2h1.5L21 15h-1.5l-.61 4h-2l.61-4h-1l-.61 4h-2m2.95-6h1l.32-2h-1l-.32 2z" fill="#0277bd"/></symbol><symbol viewBox="0 0 24 24" id="css" xmlns="http://www.w3.org/2000/svg"><path d="M5 3l-.65 3.34h13.59L17.5 8.5H3.92l-.66 3.33h13.59l-.76 3.81-5.48 1.81-4.75-1.81.33-1.64H2.85l-.79 4 7.85 3 9.05-3 1.2-6.03.24-1.21L21.94 3H5z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="css-map" xmlns="http://www.w3.org/2000/svg"><path d="M18 8v2h2v10H10v-2H8v4h14V8h-4z" fill="#42a5f5"/><path d="M4.676 3l-.488 2.51h10.211l-.33 1.623H3.864l-.496 2.502H13.58l-.57 2.863-4.119 1.36-3.569-1.36.248-1.232H3.06l-.593 3.005 5.898 2.254 6.8-2.254.902-4.53.18-.91L17.406 3H4.675z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 33 33" id="cucumber" xmlns="http://www.w3.org/2000/svg"><title>cucumber-mark-transparent-pips</title><g transform="translate(0 -5)" fill="none" fill-rule="evenodd"><path d="M-4-1h40v40H-4z"/><path d="M16.641 7.092c-7.028 0-12.714 5.686-12.714 12.714 0 6.187 4.435 11.327 10.288 12.471v3.64C21.824 34.77 28.561 28.73 29.063 20.8c.303-4.772-2.076-9.644-6.09-12.01a10.575 10.575 0 0 0-1.455-.728l-.243-.097c-.223-.082-.448-.175-.68-.242a12.614 12.614 0 0 0-3.954-.632zm2.62 4.707a1.387 1.387 0 0 0-1.213.485c-.233.31-.379.611-.534.923-.466 1.087-.31 2.251.388 3.105 1.087-.233 2.01-.927 2.475-2.014a2.45 2.45 0 0 0 .243-1.02c.048-.824-.634-1.404-1.359-1.479zm-5.654.073c-.708.068-1.382.63-1.382 1.407 0 .31.087.709.243 1.02.466 1.086 1.46 1.78 2.546 2.013.621-.854.782-2.018.316-3.105-.155-.311-.3-.617-.534-.85a1.364 1.364 0 0 0-1.188-.485zm-3.809 3.735c-1.224.063-1.77 1.602-.752 2.402.31.233.612.403.922.559 1.087.466 2.344.306 3.275-.316-.233-1.009-1.023-1.936-2.11-2.402-.388-.155-.703-.243-1.092-.243-.087-.009-.161-.004-.243 0zm11.961 4.708a3.551 3.551 0 0 0-2.013.582c.233 1.01 1.023 1.936 2.11 2.401.389.156.705.244 1.093.244 1.397.077 2.08-1.65.994-2.427-.31-.233-.611-.379-.922-.534a3.354 3.354 0 0 0-1.262-.266zm-10.603.072a3.376 3.376 0 0 0-1.261.267c-.389.155-.69.325-.923.558-1.009.854-.33 2.48 1.068 2.402.388 0 .782-.087 1.092-.243 1.087-.465 1.859-1.392 2.014-2.401a3.474 3.474 0 0 0-1.99-.582zm3.931 2.378c-1.087.233-2.009.927-2.475 2.014-.155.31-.243.684-.243.995-.077 1.32 1.724 2.028 2.5 1.02.233-.312.378-.613.534-.923.466-1.01.306-2.174-.316-3.106zm2.887.073c-.621.854-.781 2.019-.315 3.106.155.31.3.615.534.848.854.932 2.65.243 2.572-.921 0-.31-.088-.71-.243-1.02-.466-1.087-1.46-1.78-2.547-2.013z" fill="#4caf50" stroke-width=".776"/></g></symbol><symbol id="cuda" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><style>.bust0{fill:#76b900}</style><title>NVIDIA-Logo</title><path id="buEye_Mark" class="bust0" d="M76.362 75.199V64.116c1.095-.068 2.19-.137 3.284-.137 30.377-.958 50.286 26.135 50.286 26.135s-21.483 29.83-44.539 29.83c-3.079 0-6.089-.48-8.962-1.438v-33.66c11.836 1.436 14.23 6.636 21.277 18.471l15.804-13.273s-11.562-15.12-30.992-15.12c-2.053-.068-4.105.069-6.158.274m0-36.67v16.556l3.284-.205c42.213-1.437 69.784 34.618 69.784 34.618s-31.608 38.45-64.516 38.45c-2.873 0-5.678-.274-8.483-.753v10.262c2.326.274 4.72.48 7.046.48 30.65 0 52.817-15.668 74.3-34.14 3.558 2.874 18.13 9.784 21.14 12.794-20.388 17.104-67.937 30.856-94.893 30.856-2.6 0-5.062-.137-7.525-.41v14.436h116.44V38.532zm0 79.977v8.757C48.038 122.2 40.17 92.712 40.17 92.712s13.615-15.05 36.192-17.514v9.579h-.068c-11.836-1.437-21.14 9.646-21.14 9.646s5.268 18.678 21.209 24.082M26.077 91.481S42.839 66.714 76.43 64.115v-9.03C39.213 58.094 7.057 89.565 7.057 89.565s18.199 52.68 69.305 57.47v-9.579c-37.492-4.652-50.286-45.975-50.286-45.975z" fill="#8bc34a" stroke-width=".684"/></symbol><symbol viewBox="0 0 24 24" id="dart" xmlns="http://www.w3.org/2000/svg"><title>Dart</title><path d="M12.486 1.385a.978.978 0 0 0-.682.281l-.01.007-6.387 3.692 6.371 6.372v.004l7.659 7.659 1.46-2.63-5.265-12.64-2.456-2.457a.972.972 0 0 0-.69-.288z" fill="#00ca94"/><path d="M5.422 5.35L1.73 11.733l-.007.01a.967.967 0 0 0 .006 1.371l3.059 3.061 11.963 4.706 2.704-1.502-.073-.073-.018.002-7.5-7.512h-.01L5.423 5.35z" fill="#1565c0"/><path d="M5.405 5.353l6.518 6.525h.01l7.502 7.51 2.855-.544.005-8.449-3.016-2.955c-.66-.647-1.675-1.064-2.695-1.202l.002-.032-11.181-.853z" fill="#1565c0"/><path d="M5.414 5.361l6.521 6.522v.009l7.506 7.506-.546 2.855h-8.448l-2.954-3.017c-.647-.66-1.064-1.676-1.2-2.696l-.033.003L5.414 5.36z" fill="#00ee94"/></symbol><symbol viewBox="0 0 24 24" id="database" xmlns="http://www.w3.org/2000/svg"><path d="M12 3C7.58 3 4 4.79 4 7s3.58 4 8 4 8-1.79 8-4-3.58-4-8-4M4 9v3c0 2.21 3.58 4 8 4s8-1.79 8-4V9c0 2.21-3.58 4-8 4s-8-1.79-8-4m0 5v3c0 2.21 3.58 4 8 4s8-1.79 8-4v-3c0 2.21-3.58 4-8 4s-8-1.79-8-4z" fill="#ffca28"/></symbol><symbol viewBox="0 0 24 24" id="diff" xmlns="http://www.w3.org/2000/svg"><path d="M3 1c-1.11 0-2 .89-2 2v11c0 1.11.89 2 2 2h2v-2H3V3h11v2h2V3c0-1.11-.89-2-2-2H3m6 6c-1.11 0-2 .89-2 2v2h2V9h2V7H9m4 0v2h1v1h2V7h-3m5 0v2h2v11H9v-2H7v2c0 1.11.89 2 2 2h11c1.11 0 2-.89 2-2V9c0-1.11-.89-2-2-2h-2m-4 5v2h-2v2h2c1.11 0 2-.89 2-2v-2h-2m-7 1v3h3v-2H9v-1H7z" fill="#42a5f5"/></symbol><symbol id="docker" viewBox="0 0 41 34.5" xmlns="http://www.w3.org/2000/svg"><style id="bystyle2">.byst0{fill:#fff}.byst1{clip-path:url(#bySVGID_4_)}</style><g id="byg34" transform="translate(.292 1.9)" fill="#0087c9"><g id="byg32"><g id="byg30"><title id="bytitle4">Group 3</title><g id="byg28"><g id="byg26"><g id="byg9"><path id="bySVGID_1_" class="byst0" d="M8.7 24c-1.1 0-2.1-.9-2.1-2s.9-2 2.1-2c1.2 0 2.1.9 2.1 2s-1 2-2.1 2zm25.8-10.9c-.2-1.6-1.2-2.9-2.5-3.9l-.5-.4-.4.5c-.8.9-1.1 2.5-1 3.7.1.9.4 1.8.9 2.5-.4.2-.9.4-1.3.6-.9.3-1.8.4-2.7.4H1.1l-.1.6c-.2 1.9.1 3.9.9 5.7l.4.7v.1c2.4 4 6.7 5.8 11.4 5.8 9 0 16.4-3.9 19.9-12.3 2.3.1 4.6-.5 5.7-2.7l.3-.5-.5-.3c-1.3-.8-3.1-.9-4.6-.5zm-12.9-1.6h-3.9v3.9h3.9zm0-4.9h-3.9v3.9h3.9zm0-5h-3.9v3.9h3.9zm4.8 9.9h-3.9v3.9h3.9zm-14.5 0H8v3.9h3.9zm4.9 0h-3.9v3.9h3.9zm-9.7 0H3.2v3.9h3.9zm9.7-4.9h-3.9v3.9h3.9zm-4.9 0H8v3.9h3.9z"/></g><g id="byg24"><defs id="bydefs12"><path id="bySVGID_2_" d="M8.7 24c-1.1 0-2.1-.9-2.1-2s.9-2 2.1-2c1.2 0 2.1.9 2.1 2s-1 2-2.1 2zm25.8-10.9c-.2-1.6-1.2-2.9-2.5-3.9l-.5-.4-.4.5c-.8.9-1.1 2.5-1 3.7.1.9.4 1.8.9 2.5-.4.2-.9.4-1.3.6-.9.3-1.8.4-2.7.4H1.1l-.1.6c-.2 1.9.1 3.9.9 5.7l.4.7v.1c2.4 4 6.7 5.8 11.4 5.8 9 0 16.4-3.9 19.9-12.3 2.3.1 4.6-.5 5.7-2.7l.3-.5-.5-.3c-1.3-.8-3.1-.9-4.6-.5zm-12.9-1.6h-3.9v3.9h3.9zm0-4.9h-3.9v3.9h3.9zm0-5h-3.9v3.9h3.9zm4.8 9.9h-3.9v3.9h3.9zm-14.5 0H8v3.9h3.9zm4.9 0h-3.9v3.9h3.9zm-9.7 0H3.2v3.9h3.9zm9.7-4.9h-3.9v3.9h3.9zm-4.9 0H8v3.9h3.9z"/></defs><clipPath id="bySVGID_4_"><use xlink:href="#bySVGID_2_" id="byuse14" width="100%" height="100%" overflow="visible"/></clipPath><g class="byst1" clip-path="url(#bySVGID_4_)" id="byg22"><g id="byg20"><g id="byg18"><path id="bySVGID_3_" class="byst0" d="M-48.8-21H1226v151.4H-48.8z"/></g></g></g></g></g></g></g></g></g></symbol><symbol viewBox="0 0 24 24" id="document" xmlns="http://www.w3.org/2000/svg"><path d="M13 9h5.5L13 3.5V9M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m9 16v-2H6v2h9m3-4v-2H6v2h12z" fill="#42a5f5"/></symbol><symbol preserveAspectRatio="xMidYMid" viewBox="0 0 200 200" id="drone" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" transform="translate(9.063 22.346) scale(.71044)"><path d="M128.22.723C32.095.723.39 84.566.39 115.222h77.928S89.36 75.275 128.22 75.275s49.906 39.947 49.906 39.947h77.476c0-30.66-31.257-114.5-127.38-114.5m98.82 134.45h-48.914s-8.55 39.946-49.906 39.946c-41.355 0-49.902-39.948-49.902-39.948H30.255c0 10.25 37.727 82.708 98.443 82.708 60.714 0 98.344-59.604 98.344-82.708"/><circle cx="128" cy="126.08" r="32.768"/></g></symbol><symbol preserveAspectRatio="xMidYMid" viewBox="0 0 200 200" id="drone_light" xmlns="http://www.w3.org/2000/svg"><g fill="#424242" transform="translate(9.063 22.346) scale(.71044)"><path d="M128.22.723C32.095.723.39 84.566.39 115.222h77.928S89.36 75.275 128.22 75.275s49.906 39.947 49.906 39.947h77.476c0-30.66-31.257-114.5-127.38-114.5m98.82 134.45h-48.914s-8.55 39.946-49.906 39.946c-41.355 0-49.902-39.948-49.902-39.948H30.255c0 10.25 37.727 82.708 98.443 82.708 60.714 0 98.344-59.604 98.344-82.708"/><circle cx="128" cy="126.08" r="32.768"/></g></symbol><symbol viewBox="0 0 3473 3473" id="editorconfig" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" xmlns="http://www.w3.org/2000/svg"><defs id="ccdefs4"><style id="ccstyle2">.ccfil2{fill:#020202}.ccfil0{fill:#e3e3f8}.ccfil5{fill:#efefef}.ccfil6{fill:#faf1f1}.ccfil3{fill:#fdf2f2}.ccfil1{fill:#fdfdfd}.ccfil4{fill:#fef3f3}</style></defs><g id="ccLayer_x0020_1" transform="matrix(.8945 0 0 .8945 138.649 275.985)"><g id="cc_631799120"><g id="ccg11"><path class="ccfil0" d="M967 1895c46-30 84-105 61-158-63 27-60 89-61 158z" id="ccpath7" fill="#e3e3f8"/><path class="ccfil0" d="M1679 2067c50-16 98-72 71-130-39 27-64 64-71 130z" id="ccpath9" fill="#e3e3f8"/></g><g id="ccg21"><path class="ccfil1" d="M280 2895c0 63 16 131 60 155 162 91 730 20 923-23 101-23 183-98 278-139 214-93 369-168 540-293 124-91 321-347 342-500l-169-38c-4 172-43 211-196 251-103 28-304 34-409 16-139-23-202-96-265-179-122-162 27-275-166-286-203 249-561 70-718 45-67 97-224 727-222 871 97-33 158 3 245 37 308 119 39 224-84 193-84-20-110-75-159-110z" id="ccpath13" fill="#fdfdfd"/><path class="ccfil1" d="M683 1458c125 24 236 76 342 129 173 86 204 74 220 194 2 22-2 34 61 54 106 33-61-26 223-25 169 1 556 69 681 148 52 33 42 75 218 70-2-207-57-516-138-706-99-230-230-265-497-351-156-50-614-105-756-17-133 83-158 182-282 356-36 51-49 90-72 148z" id="ccpath15" fill="#fdfdfd"/><path class="ccfil1" d="M1784 1883c100 41-5 306-144 242-45-127 62-199 91-256-60-9-231-36-282-17-66 25-81 166-47 232 160 314 867 247 792 3-30-99-58-115-159-149-81-27-162-55-251-55z" id="ccpath17" fill="#fdfdfd"/><path class="ccfil1" d="M527 1848c80 77 261 89 378 95 15-155 28-271 152-262 61 83 29 181-35 244 109-1 172-83 156-202-92-66-371-198-511-217-39 42-135 272-140 342z" id="ccpath19" fill="#fdfdfd"/></g><path class="ccfil2" d="M339 2838c66-6 238 44 252 100-107 13-243 3-252-100zm-59 57c49 35 75 90 159 110 123 31 392-74 84-193-87-34-148-70-245-37-2-144 155-774 222-871 157 25 515 204 718-45 193 11 44 124 166 286 63 83 126 156 265 179 105 18 306 12 409-16 153-40 192-79 196-251l169 38c-21 153-218 409-342 500-171 125-326 200-540 293-95 41-177 116-278 139-193 43-761 114-923 23-44-24-60-92-60-155zm1399-828c7-66 32-103 71-130 27 58-21 114-71 130zm105-184c89 0 170 28 251 55 101 34 129 50 159 149 75 244-632 311-792-3-34-66-19-207 47-232 51-19 222 8 282 17-29 57-136 129-91 256 139 64 244-201 144-242zm-817 12c1-69-2-131 61-158 23 53-15 128-61 158zm-440-47c5-70 101-300 140-342 140 19 419 151 511 217 16 119-47 201-156 202 64-63 96-161 35-244-124-9-137 107-152 262-117-6-298-18-378-95zm-100-80c-37-102-37-261 120-274l-80 223c-21 48-21 37-40 51zm256-310c23-58 36-97 72-148 124-174 149-273 282-356 142-88 600-33 756 17 267 86 398 121 497 351 81 190 136 499 138 706-176 5-166-37-218-70-125-79-512-147-681-148-284-1-117 58-223 25-63-20-59-32-61-54-16-120-47-108-220-194-106-53-217-105-342-129zm1770-49c-19-63 16-59 77-102 35-25 63-51 106-75 161-90 461-105 589 2 52 43 137 127 124 237-27 219-177 339-300 439-125 102-333 207-548 137-18-44-4-323-25-426-19-92-9-102 44-157 156-162 494-280 686-141 81 60 58 83 100 129 52-56-45-244-403-232-243 8-348 198-450 189zM997 840c5-139 133-427 261-527 155-120 317-233 555-98 59 33 56 50 62 132 5 79-2 108-22 172-158 510-290 217-796 338 19-166 163-314 243-391 137-133 236-219 442-191 57 95 63 155-6 266-92 148-115 139-101 240 72-18 94-88 127-158 201-420-91-471-270-394-120 51-334 287-404 429-14 28-29 64-42 95zm792 21c21-125 145-156 145-541 0-166-204-315-471-204-229 94-264 166-386 350-115 174-111 365-210 526-29 46-55 62-87 108-23 34-40 77-63 117-47 77-95 133-133 225-120 3-221 5-233 129-16 170 64 212 64 276-1 69-281 765-203 1180 22 114 97 115 217 129 289 35 664 23 923-81l470-225c119-67 319-194 408-287 63-65 96-120 150-197 74-108 76-106 92-253 98 18 281 61 342 114-7 69-41 36-41 98 39 1 104-48 120-102-41-60-84-50-143-98 47-37 132-54 197-81 140-58 379-234 438-394 47-129 12-344-64-428-80-88-266-133-418-133-181 0-368 130-514 186-56-49-60-105-101-159-47-64-353-224-499-255z" id="ccpath23" fill="#020202"/><path class="ccfil3" d="M2453 1409c102 9 207-181 450-189 358-12 455 176 403 232-42-46-19-69-100-129-192-139-530-21-686 141-53 55-63 65-44 157 21 103 7 382 25 426 215 70 423-35 548-137 123-100 273-220 300-439 13-110-72-194-124-237-128-107-428-92-589-2-43 24-71 50-106 75-61 43-96 39-77 102z" id="ccpath25" fill="#fdf2f2"/><path class="ccfil4" d="M997 840l49-87c13-31 28-67 42-95 70-142 284-378 404-429 179-77 471-26 270 394-33 70-55 140-127 158-14-101 9-92 101-240 69-111 63-171 6-266-206-28-305 58-442 191-80 77-224 225-243 391 506-121 638 172 796-338 20-64 27-93 22-172-6-82-3-99-62-132-238-135-400-22-555 98-128 100-256 388-261 527z" id="ccpath27" fill="#fef3f3"/><path class="ccfil5" d="M427 1768c19-14 19-3 40-51l80-223c-157 13-157 172-120 274z" id="ccpath29" fill="#efefef"/><path class="ccfil6" d="M591 2938c-14-56-186-106-252-100 9 103 145 113 252 100z" id="ccpath31" fill="#faf1f1"/></g></g></symbol><symbol viewBox="0 0 24 24" id="elixir" xmlns="http://www.w3.org/2000/svg"><path d="M12.431 22.383c-3.86 0-6.99-3.64-6.99-8.13 0-3.678 2.774-8.172 4.916-10.91 1.014-1.295 2.931-2.321 2.931-2.321s-.982 5.238 1.683 7.318c2.365 1.847 4.105 4.25 4.105 6.363 0 4.232-2.784 7.68-6.645 7.68z" fill="#9575cd" stroke-width="1.256"/></symbol><symbol viewBox="0 0 323.00001 322.99999" id="elm" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.8053 0 0 .8053 30.106 31.524)"><path fill="#f0ad00" d="M160.8 153.865l68.028-68.03H92.77z"/><path fill="#7fd13b" d="M160.983 5.098H12.033l68.524 68.525H229.51z"/><path fill="#7fd13b" stroke-width=".974" d="M243.906 88.021l74.136 74.137-74.474 74.475-74.137-74.137z"/><path fill="#60b5cc" d="M318.2 145.045V5.098H178.252z"/><path fill="#5a6378" d="M152.164 162.499L3.4 13.733v297.533z"/><path fill="#f0ad00" d="M252.205 245.27l65.995 65.996v-131.99z"/><path fill="#60b5cc" d="M160.8 171.134L12.034 319.899h297.53z"/></g></symbol><symbol viewBox="0 0 24 24" id="email" xmlns="http://www.w3.org/2000/svg"><path d="M20 8l-8 5-8-5V6l8 5 8-5m0-2H4c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 30 30" id="erlang" xmlns="http://www.w3.org/2000/svg"><path style="line-height:1.25;-inkscape-font-specification:'Wide Latin'" d="M5.217 4.367c-.048.052-.097.1-.144.153C2.697 7.182 1.51 10.798 1.51 15.366c0 4.418 1.156 7.862 3.46 10.34h19.414c2.553-1.152 4.127-3.43 4.127-3.43l-3.147-2.52-1.454 1.381c-.866.773-.845.931-2.314 1.78-1.496.674-3.04.966-4.634.966-2.516 0-4.423-.909-5.723-2.059-1.286-1.15-1.985-4.511-2.097-6.68l17.458.067-.182-1.472s-.847-7.129-2.542-9.372zm8.76.846c1.565 0 3.22.535 3.96 1.471.742.937.932 1.667.974 3.524H9.12c.111-1.955.436-2.81 1.372-3.697.937-.888 2.03-1.298 3.484-1.298z" font-weight="400" font-size="48" font-family="Wide Latin" letter-spacing="0" word-spacing="0" fill="#f44336" stroke-width=".97"/></symbol><symbol viewBox="0 0 299.99999 300.00001" id="eslint" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-2.88 18.438) scale(1.0344)"><path d="M97.021 99.016l48.432-27.962c1.212-.7 2.706-.7 3.918 0l48.433 27.962a3.92 3.92 0 0 1 1.959 3.393v55.924a3.924 3.924 0 0 1-1.959 3.394l-48.433 27.962c-1.212.7-2.706.7-3.918 0l-48.432-27.962a3.92 3.92 0 0 1-1.959-3.394v-55.924a3.922 3.922 0 0 1 1.959-3.393" fill="#7986cb"/><path d="M273.34 124.49L215.473 23.82c-2.102-3.64-5.985-6.325-10.188-6.325H89.545c-4.204 0-8.088 2.685-10.19 6.325L21.488 124.27c-2.102 3.641-2.102 8.236 0 11.877l57.867 99.847c2.102 3.64 5.986 5.501 10.19 5.501h115.74c4.203 0 8.087-1.805 10.188-5.446l57.867-100.01c2.104-3.639 2.104-7.907.001-11.547m-47.917 48.41c0 1.48-.891 2.849-2.174 3.59l-73.71 42.527a4.194 4.194 0 0 1-4.17 0l-73.767-42.527c-1.282-.741-2.179-2.109-2.179-3.59V87.847c0-1.481.884-2.849 2.167-3.59l73.707-42.527a4.185 4.185 0 0 1 4.168 0l73.772 42.527c1.283.741 2.186 2.109 2.186 3.59z" fill="#3f51b5"/></g></symbol><symbol viewBox="0 0 24 24" id="exe" xmlns="http://www.w3.org/2000/svg"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h14m0 14V8H5v10h14z" fill="#e64a19"/></symbol><symbol viewBox="0 0 24 24" id="favicon" xmlns="http://www.w3.org/2000/svg"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.62L12 2 9.19 8.62 2 9.24l5.45 4.73L5.82 21 12 17.27z" fill="#ffd54f"/></symbol><symbol viewBox="0 0 24 24" id="file" xmlns="http://www.w3.org/2000/svg"><path d="M13 9h5.5L13 3.5V9M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m5 2H6v16h12v-9h-7V4z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 400 400" id="firebase" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 103)"><path d="M72.55 208.77l44.456-292.29 56.209 90.445L195.49-37.57 330.6 209.28z" fill="#ffa712"/><path d="M195.7 276.73l134.9-67.45-46.5-224.83L72.55 208.77z" fill="#fcca3f"/><path d="M173.22 6.932L72.56 208.772l136.35-144.58z" fill="#f6820c"/></g></symbol><symbol viewBox="0 0 24 24" id="flash" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="cma"><stop offset="0" stop-color="#d92f3c"/><stop offset="1" stop-color="#791223"/></linearGradient><linearGradient xlink:href="#cma" id="cmb" x1="2.373" y1="12.027" x2="21.86" y2="12.027" gradientUnits="userSpaceOnUse" gradientTransform="translate(-.09 -24.144)"/></defs><rect width="19.487" height="19.487" x="2.283" y="-21.86" transform="rotate(90)" ry="0" fill="url(#cmb)"/><path style="line-height:125%" d="M16.802 5.768l-.013.002a6.43 6.43 0 0 0-1.182.192 5.062 5.062 0 0 0-1.494.718c-.428.323-.817.72-1.17 1.191-.34.48-.682 1.032-1.022 1.66-.12.228-.233.424-.35.636v.002h-.004l-1.34 2.394-.005-.002c-.238.443-.461.847-.665 1.198a4.358 4.358 0 0 1-.716.94 2.79 2.79 0 0 1-.907.594c-.072.027-.161.042-.242.063h-.989v2.414h.989v-.002a6.427 6.427 0 0 0 1.185-.192 5.062 5.062 0 0 0 1.494-.718 5.94 5.94 0 0 0 1.171-1.191c.34-.48.681-1.033 1.021-1.66.12-.228.235-.425.353-.637l.006.002.003-.005.037-.066h2.53v.002h1.124v-2.408h-.33v-.001h-1.98c.22-.407.432-.789.621-1.115.214-.37.452-.682.717-.94a2.79 2.79 0 0 1 .906-.594c.07-.027.16-.041.239-.061h.992V8.18h-.002V5.77h-.977v-.002z" font-weight="400" font-size="40" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#fff"/></symbol><symbol class="cnflow-logo" viewBox="0 0 299.99999 300" id="flow" xmlns="http://www.w3.org/2000/svg"><title>Flow logo</title><path d="M38.75 33.427l77.461 77.47H54.436l61.145 61.16H38.437l93.462 93.478v-77.158l.01-.01v-77.47h-.01V66.982h46.691l20.394 20.393H153.57v76.531h22.05l24.474 24.473h-15.806l-.01-.01v.01h-31.665l-.01-.01v.01h-.313l.313.313v77.148h109.149l-39.2-39.2v-15.806l8.465 8.466v-77.37h-15.682l.017-38.191 30.09 30.086V56.362h-64.874l-22.94-22.934H113.71z" fill="#fbc02d" fill-opacity=".976" stroke-width=".955" class="cnflow-logo-mark"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-aurelia" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="coa" x1="-388.15%" x2="237.68%" y1="-144.18%" y2="430.41%"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cob" x1="72.945%" x2="-97.052%" y1="84.424%" y2="-147.7%"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".29"/><stop stop-color="#CD0F7E" offset=".84"/><stop stop-color="#ED2C89" offset="1"/></linearGradient><linearGradient id="coc" x1="-283.88%" x2="287.54%" y1="-693.6%" y2="101.71%"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cod" x1="-821.19%" x2="101.99%" y1="-469.05%" y2="288.24%"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="coe" x1="-140.36%" x2="419.01%" y1="-230.93%" y2="261.98%"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cof" x1="191.08%" x2="20.358%" y1="253.95%" y2="20.403%"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".29"/><stop stop-color="#CD0F7E" offset=".84"/><stop stop-color="#ED2C89" offset="1"/></linearGradient><linearGradient id="cog" x1="-388.09%" x2="237.67%" y1="-173.85%" y2="518.99%"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="coi" x1="-31.824" x2="19.682" y1="-11.741" y2="35.548" gradientTransform="scale(.95818 1.0436)" gradientUnits="userSpaceOnUse" xlink:href="#coa"/><linearGradient id="coj" x1="12.022" x2="-15.716" y1="13.922" y2="-23.952" gradientTransform="scale(.96226 1.0392)" gradientUnits="userSpaceOnUse" xlink:href="#cob"/><linearGradient id="cok" x1="-23.39" x2="23.931" y1="-57.289" y2="8.573" gradientTransform="scale(1.0429 .95884)" gradientUnits="userSpaceOnUse" xlink:href="#coc"/><linearGradient id="col" x1="-53.331" x2="6.771" y1="-30.517" y2="18.785" gradientTransform="scale(.99898 1.001)" gradientUnits="userSpaceOnUse" xlink:href="#cod"/><linearGradient id="com" x1="-14.029" x2="41.998" y1="-23.111" y2="26.259" gradientTransform="scale(1.0003 .99965)" gradientUnits="userSpaceOnUse" xlink:href="#coe"/><linearGradient id="con" x1="31.177" x2="3.37" y1="41.442" y2="3.402" gradientTransform="scale(.96254 1.0389)" gradientUnits="userSpaceOnUse" xlink:href="#cof"/><linearGradient id="coo" x1="-31.905" x2="19.599" y1="-14.258" y2="42.767" gradientTransform="scale(.95823 1.0436)" gradientUnits="userSpaceOnUse" xlink:href="#cog"/><linearGradient id="cop" x1="4.301" x2="34.534" y1="34.41" y2="4.514" gradientTransform="scale(1.002 .99796)" gradientUnits="userSpaceOnUse" xlink:href="#coh"/><linearGradient id="coh" x1=".112" x2=".901" y1=".897" y2=".116"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".53"/><stop stop-color="#CD0F7E" offset=".79"/><stop stop-color="#ED2C89" offset="1"/></linearGradient></defs><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#f06292" fill-rule="nonzero"/><g transform="matrix(.31022 .0619 -.0619 .31022 11.807 7.546)" fill="none"><path d="M8.002 6.127L4.117 8.719.116 2.723 4 .13z" transform="rotate(-11.284 17.839 -78.732)" fill="url(#coi)"/><path d="M9.179 1.887l6.637 9.946-7.906 5.276-6.637-9.946L.115 5.43 8.02.153z" transform="rotate(-11.284 129.49 -99.884)" fill="url(#coj)"/><path d="M7.3 1.88l1.462 2.189-6.018 4.015L.124 4.16l1.315-.877L6.143.144z" transform="rotate(-11.284 167.2 -62.32)" fill="url(#cok)"/><path d="M2.328 1.146L4.016.02l2.619 3.925L2.75 6.537 1.29 4.347l2.197-1.466zm-1.04 3.201L.132 2.612l2.197-1.466 1.158 1.735z" transform="rotate(-11.284 104.37 -149.22)" fill="url(#col)"/><path d="M5.346 9.155l-1.315.877L.03 4.035 6.047.019l2.805 4.204L4.15 7.36l4.703-3.138 1.197 1.793z" transform="rotate(-11.284 81.819 7.645)" fill="url(#com)"/><path d="M14.533 9.934l1.197 1.793-7.907 5.276-1.196-1.793L.052 5.358 7.958.082z" transform="rotate(-11.284 17.141 -7.825)" fill="url(#con)"/><path d="M6.235 7.177L4.038 8.643 2.84 6.849.036 2.646 3.92.053 7.923 6.05z" transform="rotate(-11.284 18.188 -79.174)" fill="url(#coo)"/><path d="M18.955 35.925L17.48 34.45l3.998-3.998 1.475 1.475z" fill="#714896"/><path d="M33.33 21.55l-1.475-1.474 1.867-1.868 1.475 1.475z" fill="#6f4795"/><path d="M7.12 24.09l-1.525-1.525 3.998-3.998 1.525 1.525z" fill="#88519f"/><path d="M21.495 9.714L19.97 8.19l1.868-1.868 1.524 1.525z" fill="#85509e"/><path d="M31.418 23.462l-6.72 6.72-1.475-1.474 6.72-6.721z" fill="#8d166a"/><path d="M18.058 10.101l1.525 1.525-6.721 6.72-1.525-1.524z" fill="#a70d6f"/><path d="M2.375 11.769l1.9 1.9-1.9 1.901-1.901-1.9z" fill="#9e61ad"/><path d="M15.523 36.482l1.9 1.9-1.9 1.901-1.9-1.9z" fill="#8053a3"/><path d="M8.372 38.294L.017 29.876 29.749.08l8.636 8.201z" transform="translate(1.823 1.548)" fill="url(#cop)"/></g></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-aurelia-open" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="cpi" x1="-31.824" x2="19.682" y1="-11.741" y2="35.548" gradientTransform="scale(.95818 1.0436)" gradientUnits="userSpaceOnUse" xlink:href="#cpa"/><linearGradient id="cpa" x1="-3.881" x2="2.377" y1="-1.442" y2="4.304"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cpj" x1="12.022" x2="-15.716" y1="13.922" y2="-23.952" gradientTransform="scale(.96226 1.0392)" gradientUnits="userSpaceOnUse" xlink:href="#cpb"/><linearGradient id="cpb" x1=".729" x2="-.971" y1=".844" y2="-1.477"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".29"/><stop stop-color="#CD0F7E" offset=".84"/><stop stop-color="#ED2C89" offset="1"/></linearGradient><linearGradient id="cpk" x1="-23.39" x2="23.931" y1="-57.289" y2="8.573" gradientTransform="scale(1.0429 .95884)" gradientUnits="userSpaceOnUse" xlink:href="#cpc"/><linearGradient id="cpc" x1="-2.839" x2="2.875" y1="-6.936" y2="1.017"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cpl" x1="-53.331" x2="6.771" y1="-30.517" y2="18.785" gradientTransform="scale(.99898 1.001)" gradientUnits="userSpaceOnUse" xlink:href="#cpd"/><linearGradient id="cpd" x1="-8.212" x2="1.02" y1="-4.691" y2="2.882"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cpm" x1="-14.029" x2="41.998" y1="-23.111" y2="26.259" gradientTransform="scale(1.0003 .99965)" gradientUnits="userSpaceOnUse" xlink:href="#cpe"/><linearGradient id="cpe" x1="-1.404" x2="4.19" y1="-2.309" y2="2.62"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cpn" x1="31.177" x2="3.37" y1="41.442" y2="3.402" gradientTransform="scale(.96254 1.0389)" gradientUnits="userSpaceOnUse" xlink:href="#cpf"/><linearGradient id="cpf" x1="1.911" x2=".204" y1="2.539" y2=".204"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".29"/><stop stop-color="#CD0F7E" offset=".84"/><stop stop-color="#ED2C89" offset="1"/></linearGradient><linearGradient id="cpo" x1="-31.905" x2="19.599" y1="-14.258" y2="42.767" gradientTransform="scale(.95823 1.0436)" gradientUnits="userSpaceOnUse" xlink:href="#cpg"/><linearGradient id="cpg" x1="-3.881" x2="2.377" y1="-1.738" y2="5.19"><stop stop-color="#C06FBB" offset="0"/><stop stop-color="#6E4D9B" offset="1"/></linearGradient><linearGradient id="cpp" x1="4.301" x2="34.534" y1="34.41" y2="4.514" gradientTransform="scale(1.002 .99796)" gradientUnits="userSpaceOnUse" xlink:href="#cph"/><linearGradient id="cph" x1=".112" x2=".901" y1=".897" y2=".116"><stop stop-color="#6E4D9B" offset="0"/><stop stop-color="#77327A" offset=".14"/><stop stop-color="#B31777" offset=".53"/><stop stop-color="#CD0F7E" offset=".79"/><stop stop-color="#ED2C89" offset="1"/></linearGradient></defs><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#f06292" fill-rule="nonzero"/><g transform="matrix(.31022 .0619 -.0619 .31022 11.807 7.546)" fill="none"><path d="M8.002 6.127L4.117 8.719.116 2.723 4 .13z" transform="rotate(-11.284 17.839 -78.732)" fill="url(#cpi)"/><path d="M9.179 1.887l6.637 9.946-7.906 5.276-6.637-9.946L.115 5.43 8.02.153z" transform="rotate(-11.284 129.49 -99.884)" fill="url(#cpj)"/><path d="M7.3 1.88l1.462 2.189-6.018 4.015L.124 4.16l1.315-.877L6.143.144z" transform="rotate(-11.284 167.2 -62.32)" fill="url(#cpk)"/><path d="M2.328 1.146L4.016.02l2.619 3.925L2.75 6.537 1.29 4.347l2.197-1.466zm-1.04 3.201L.132 2.612l2.197-1.466 1.158 1.735z" transform="rotate(-11.284 104.37 -149.22)" fill="url(#cpl)"/><path d="M5.346 9.155l-1.315.877L.03 4.035 6.047.019l2.805 4.204L4.15 7.36l4.703-3.138 1.197 1.793z" transform="rotate(-11.284 81.819 7.645)" fill="url(#cpm)"/><path d="M14.533 9.934l1.197 1.793-7.907 5.276-1.196-1.793L.052 5.358 7.958.082z" transform="rotate(-11.284 17.141 -7.825)" fill="url(#cpn)"/><path d="M6.235 7.177L4.038 8.643 2.84 6.849.036 2.646 3.92.053 7.923 6.05z" transform="rotate(-11.284 18.188 -79.174)" fill="url(#cpo)"/><path d="M18.955 35.925L17.48 34.45l3.998-3.998 1.475 1.475z" fill="#714896"/><path d="M33.33 21.55l-1.475-1.474 1.867-1.868 1.475 1.475z" fill="#6f4795"/><path d="M7.12 24.09l-1.525-1.525 3.998-3.998 1.525 1.525z" fill="#88519f"/><path d="M21.495 9.714L19.97 8.19l1.868-1.868 1.524 1.525z" fill="#85509e"/><path d="M31.418 23.462l-6.72 6.72-1.475-1.474 6.72-6.721z" fill="#8d166a"/><path d="M18.058 10.101l1.525 1.525-6.721 6.72-1.525-1.524z" fill="#a70d6f"/><path d="M2.375 11.769l1.9 1.9-1.9 1.901-1.901-1.9z" fill="#9e61ad"/><path d="M15.523 36.482l1.9 1.9-1.9 1.901-1.9-1.9z" fill="#8053a3"/><path d="M8.372 38.294L.017 29.876 29.749.08l8.636 8.201z" transform="translate(1.823 1.548)" fill="url(#cpp)"/></g></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-components" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#cddc39" fill-rule="nonzero"/><path d="M11.185 9.613h5.346v2.9l3.782-3.775 3.775 3.775-3.775 3.782h2.9v5.346h-5.346v-5.346h2.446l-3.782-3.782v2.446h-5.346V9.613m0 6.682h5.346v5.346h-5.346z" fill="#f0f4c3" stroke-width=".668"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-components-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#cddc39"/><path d="M11.185 9.613h5.346v2.9l3.782-3.775 3.775 3.775-3.775 3.782h2.9v5.346h-5.346v-5.346h2.446l-3.782-3.782v2.446h-5.346V9.613m0 6.682h5.346v5.346h-5.346z" fill="#f0f4c3" stroke-width=".668"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-config" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#00acc1" fill-rule="nonzero"/><path d="M17.293 17.786a2.308 2.308 0 0 1-2.308-2.308 2.308 2.308 0 0 1 2.308-2.307 2.308 2.308 0 0 1 2.308 2.307 2.308 2.308 0 0 1-2.308 2.308m4.899-1.668c.026-.211.046-.422.046-.64 0-.217-.02-.435-.046-.659l1.391-1.075a.333.333 0 0 0 .08-.422l-1.32-2.28c-.079-.146-.257-.205-.402-.146l-1.641.66a4.779 4.779 0 0 0-1.115-.647l-.244-1.747a.333.333 0 0 0-.33-.277h-2.637a.333.333 0 0 0-.33.277l-.243 1.747a4.78 4.78 0 0 0-1.114.646l-1.642-.659a.324.324 0 0 0-.402.145l-1.319 2.281a.325.325 0 0 0 .08.422l1.39 1.075c-.026.224-.046.442-.046.66s.02.428.046.639l-1.39 1.094a.325.325 0 0 0-.08.422l1.319 2.282c.079.145.257.197.402.145l1.642-.666c.342.264.698.488 1.114.653l.244 1.747a.333.333 0 0 0 .33.277h2.637a.333.333 0 0 0 .33-.277l.243-1.747a4.802 4.802 0 0 0 1.115-.653l1.641.666c.145.052.323 0 .403-.145l1.318-2.282a.333.333 0 0 0-.079-.422z" fill="#80deea" stroke-width=".659"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-config-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#00acc1"/><path d="M17.293 17.786a2.308 2.308 0 0 1-2.308-2.308 2.308 2.308 0 0 1 2.308-2.307 2.308 2.308 0 0 1 2.308 2.307 2.308 2.308 0 0 1-2.308 2.308m4.899-1.668c.026-.211.046-.422.046-.64 0-.217-.02-.435-.046-.659l1.391-1.075a.333.333 0 0 0 .08-.422l-1.32-2.28c-.079-.146-.257-.205-.402-.146l-1.641.66a4.779 4.779 0 0 0-1.115-.647l-.244-1.747a.333.333 0 0 0-.33-.277h-2.637a.333.333 0 0 0-.33.277l-.243 1.747a4.78 4.78 0 0 0-1.114.646l-1.642-.659a.324.324 0 0 0-.402.145l-1.319 2.281a.325.325 0 0 0 .08.422l1.39 1.075c-.026.224-.046.442-.046.66s.02.428.046.639l-1.39 1.094a.325.325 0 0 0-.08.422l1.319 2.282c.079.145.257.197.402.145l1.642-.666c.342.264.698.488 1.114.653l.244 1.747a.333.333 0 0 0 .33.277h2.637a.333.333 0 0 0 .33-.277l.243-1.747a4.802 4.802 0 0 0 1.115-.653l1.641.666c.145.052.323 0 .403-.145l1.318-2.282a.333.333 0 0 0-.079-.422z" fill="#80deea" stroke-width=".659"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-css" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#42a5f5" fill-rule="nonzero"/><path d="M12.488 9.415l-.44 2.259h9.188l-.298 1.46h-9.18l-.447 2.251H20.5l-.514 2.576-3.705 1.224-3.211-1.224.223-1.109h-2.258l-.534 2.704 5.307 2.029 6.118-2.029.812-4.076.162-.818 1.041-5.247H12.488z" fill-rule="nonzero" fill="#bbdefb"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-css-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#42a5f5" fill-rule="nonzero"/><path d="M12.488 9.415l-.44 2.259h9.188l-.298 1.46h-9.18l-.447 2.251H20.5l-.514 2.576-3.705 1.224-3.211-1.224.223-1.109h-2.258l-.534 2.704 5.307 2.029 6.118-2.029.812-4.076.162-.818 1.041-5.247H12.488z" fill-rule="nonzero" fill="#bbdefb"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-dist" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#e57373" fill-rule="nonzero"/><path d="M18.575 11.113h-2.576V9.825h2.576m3.864 1.288h-2.576V9.825l-1.288-1.288h-2.576L14.71 9.825v1.288h-2.577c-.715 0-1.288.573-1.288 1.288v7.085a1.288 1.288 0 0 0 1.288 1.288H22.44a1.288 1.288 0 0 0 1.288-1.288V12.4c0-.715-.58-1.288-1.288-1.288z" fill="#ffcdd2" stroke-width=".644"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-dist-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#e57373"/><path d="M18.575 11.113h-2.576V9.825h2.576m3.864 1.288h-2.576V9.825l-1.288-1.288h-2.576L14.71 9.825v1.288h-2.577c-.715 0-1.288.573-1.288 1.288v7.085a1.288 1.288 0 0 0 1.288 1.288H22.44a1.288 1.288 0 0 0 1.288-1.288V12.4c0-.715-.58-1.288-1.288-1.288z" fill="#ffcdd2" fill-rule="evenodd" stroke-width=".644"/></symbol><symbol id="folder-docker" clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><defs id="cydefs10"><path id="cySVGID_2_" d="M8.7 24c-1.1 0-2.1-.9-2.1-2s.9-2 2.1-2 2.1.9 2.1 2-1 2-2.1 2zm25.8-10.9c-.2-1.6-1.2-2.9-2.5-3.9l-.5-.4-.4.5c-.8.9-1.1 2.5-1 3.7.1.9.4 1.8.9 2.5-.4.2-.9.4-1.3.6-.9.3-1.8.4-2.7.4H1.1l-.1.6c-.2 1.9.1 3.9.9 5.7l.4.7v.1c2.4 4 6.7 5.8 11.4 5.8 9 0 16.4-3.9 19.9-12.3 2.3.1 4.6-.5 5.7-2.7l.3-.5-.5-.3c-1.3-.8-3.1-.9-4.6-.5zm-12.9-1.6h-3.9v3.9h3.9zm0-4.9h-3.9v3.9h3.9zm0-5h-3.9v3.9h3.9zm4.8 9.9h-3.9v3.9h3.9zm-14.5 0H8v3.9h3.9zm4.9 0h-3.9v3.9h3.9zm-9.7 0H3.2v3.9h3.9zm9.7-4.9h-3.9v3.9h3.9zm-4.9 0H8v3.9h3.9z"/></defs><path id="cypath2" d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#039be5" fill-rule="nonzero"/><style id="cystyle2">.cyst0{fill:#fff}.cyst1{clip-path:url(#cySVGID_4_)}</style><g id="cyg34" transform="translate(8.319 9.626) scale(.39491)" fill="#b3e5fc"><g id="cyg32"><g id="cyg30"><title id="cytitle4">Group 3</title><g id="cyg28"><g id="cyg26"><g id="cyg9"><path id="cySVGID_1_" class="cyst0" d="M8.7 24c-1.1 0-2.1-.9-2.1-2s.9-2 2.1-2 2.1.9 2.1 2-1 2-2.1 2zm25.8-10.9c-.2-1.6-1.2-2.9-2.5-3.9l-.5-.4-.4.5c-.8.9-1.1 2.5-1 3.7.1.9.4 1.8.9 2.5-.4.2-.9.4-1.3.6-.9.3-1.8.4-2.7.4H1.1l-.1.6c-.2 1.9.1 3.9.9 5.7l.4.7v.1c2.4 4 6.7 5.8 11.4 5.8 9 0 16.4-3.9 19.9-12.3 2.3.1 4.6-.5 5.7-2.7l.3-.5-.5-.3c-1.3-.8-3.1-.9-4.6-.5zm-12.9-1.6h-3.9v3.9h3.9zm0-4.9h-3.9v3.9h3.9zm0-5h-3.9v3.9h3.9zm4.8 9.9h-3.9v3.9h3.9zm-14.5 0H8v3.9h3.9zm4.9 0h-3.9v3.9h3.9zm-9.7 0H3.2v3.9h3.9zm9.7-4.9h-3.9v3.9h3.9zm-4.9 0H8v3.9h3.9z"/></g><g id="cyg24"><clipPath id="cySVGID_4_"><use id="cyuse14" width="100%" height="100%" xlink:href="#cySVGID_2_"/></clipPath><g id="cyg22" class="cyst1" clip-path="url(#cySVGID_4_)"><g id="cyg20"><g id="cyg18"><path id="cySVGID_3_" class="cyst0" d="M-48.8-21H1226v151.4H-48.8z"/></g></g></g></g></g></g></g></g></g></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-docker-open" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="cza"><use width="100%" height="100%" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#SVGID_2_"/></clipPath></defs><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#039be5"/><g transform="matrix(.3949 0 0 .39489 8.319 9.626)" fill="#b3e5fc"><title>Group 3</title><path class="czst0" d="M8.7 24c-1.1 0-2.1-.9-2.1-2s.9-2 2.1-2 2.1.9 2.1 2-1 2-2.1 2zm25.8-10.9c-.2-1.6-1.2-2.9-2.5-3.9l-.5-.4-.4.5c-.8.9-1.1 2.5-1 3.7.1.9.4 1.8.9 2.5-.4.2-.9.4-1.3.6-.9.3-1.8.4-2.7.4H1.1l-.1.6c-.2 1.9.1 3.9.9 5.7l.4.7v.1c2.4 4 6.7 5.8 11.4 5.8 9 0 16.4-3.9 19.9-12.3 2.3.1 4.6-.5 5.7-2.7l.3-.5-.5-.3c-1.3-.8-3.1-.9-4.6-.5zm-12.9-1.6h-3.9v3.9h3.9zm0-4.9h-3.9v3.9h3.9zm0-5h-3.9v3.9h3.9zm4.8 9.9h-3.9v3.9h3.9zm-14.5 0H8v3.9h3.9zm4.9 0h-3.9v3.9h3.9zm-9.7 0H3.2v3.9h3.9zm9.7-4.9h-3.9v3.9h3.9zm-4.9 0H8v3.9h3.9z"/><g class="czst1" clip-path="url(#cza)"><path class="czst0" d="M-48.8-21H1226v151.4H-48.8z"/></g></g></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-docs" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#0277bd" fill-rule="nonzero"/><path d="M18.575 12.859h3.713l-3.713-3.713v3.713M13.85 8.134h5.4l4.05 4.05v8.1c0 .74-.61 1.35-1.35 1.35h-8.1a1.35 1.35 0 0 1-1.35-1.35v-10.8c0-.75.6-1.35 1.35-1.35m6.075 10.8v-1.35H13.85v1.35h6.075m2.025-2.7v-1.35h-8.1v1.35h8.1z" fill-rule="nonzero" fill="#b3e5fc"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-docs-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#0277bd" fill-rule="nonzero"/><path d="M18.575 12.859h3.713l-3.713-3.713v3.713M13.85 8.134h5.4l4.05 4.05v8.1c0 .74-.61 1.35-1.35 1.35h-8.1a1.35 1.35 0 0 1-1.35-1.35v-10.8c0-.75.6-1.35 1.35-1.35m6.075 10.8v-1.35H13.85v1.35h6.075m2.025-2.7v-1.35h-8.1v1.35h8.1z" fill-rule="nonzero" fill="#b3e5fc"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-expo" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#01579b" fill-rule="nonzero"/><style>.dcst0{fill:#1173b6}.st1{fill:#585d67}</style><path class="dcst0" d="M18.575 9.82c-.489-.745-.605-.844-1.6-.844h-.024c-.996 0-1.106.099-1.601.844-.46.699-5.024 9.058-5.024 9.291 0 .338.087.658.402 1.112.32.46.873.716 1.275.309.273-.274 3.201-5.321 4.616-7.23a.425.425 0 0 1 .693 0c1.414 1.909 4.343 6.956 4.616 7.23.402.407.955.15 1.275-.309.314-.454.402-.774.402-1.112-.006-.233-4.57-8.598-5.03-9.291z" fill="#1173b6" stroke-width=".058"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-expo-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#01579b"/><path class="ddst0" d="M18.575 9.82c-.489-.745-.605-.844-1.6-.844h-.024c-.996 0-1.106.099-1.601.844-.46.699-5.024 9.058-5.024 9.291 0 .338.087.658.402 1.112.32.46.873.716 1.275.309.273-.274 3.201-5.321 4.616-7.23a.425.425 0 0 1 .693 0c1.414 1.909 4.343 6.956 4.616 7.23.402.407.955.15 1.275-.309.314-.454.402-.774.402-1.112-.006-.233-4.57-8.598-5.03-9.291z" fill="#1173b6" stroke-width=".058" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-font" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ef9a9a" fill-rule="nonzero"/><path d="M14.62 17.403l2.38-6.33 2.37 6.33m-3.37-9l-5.5 14h2.25l1.12-3h6.25l1.13 3h2.25l-5.5-14h-2z" fill="#f44336" fill-rule="nonzero"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-font-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ef9a9a" fill-rule="nonzero"/><path d="M14.62 17.403l2.38-6.33 2.37 6.33m-3.37-9l-5.5 14h2.25l1.12-3h6.25l1.13 3h2.25l-5.5-14h-2z" fill="#f44336" fill-rule="nonzero"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-git" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ff8a65" fill-rule="nonzero"/><path d="M10.43 14.14l4.044-4.052 1.183 1.19a1.387 1.387 0 0 0 .65 1.56v3.877c-.42.238-.699.693-.699 1.21 0 .768.632 1.4 1.4 1.4.767 0 1.4-.632 1.4-1.4 0-.517-.28-.972-.7-1.21v-3.4l1.448 1.462c-.05.105-.05.224-.05.35 0 .767.633 1.399 1.4 1.399.768 0 1.4-.632 1.4-1.4 0-.767-.632-1.4-1.4-1.4-.126 0-.245 0-.35.05l-1.798-1.799a1.385 1.385 0 0 0-.805-1.637c-.3-.112-.615-.14-.895-.063l-1.19-1.183.553-.545a1.381 1.381 0 0 1 1.973 0l5.591 5.59a1.381 1.381 0 0 1 0 1.974l-5.59 5.591a1.381 1.381 0 0 1-1.974 0l-5.591-5.59a1.381 1.381 0 0 1 0-1.974z" fill="#e64a19" fill-rule="nonzero"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-git-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ff8a65" fill-rule="nonzero"/><path d="M10.43 14.14l4.044-4.052 1.183 1.19a1.387 1.387 0 0 0 .65 1.56v3.877c-.42.238-.699.693-.699 1.21 0 .768.632 1.4 1.4 1.4.767 0 1.4-.632 1.4-1.4 0-.517-.28-.972-.7-1.21v-3.4l1.448 1.462c-.05.105-.05.224-.05.35 0 .767.633 1.399 1.4 1.399.768 0 1.4-.632 1.4-1.4 0-.767-.632-1.4-1.4-1.4-.126 0-.245 0-.35.05l-1.798-1.799a1.385 1.385 0 0 0-.805-1.637c-.3-.112-.615-.14-.895-.063l-1.19-1.183.553-.545a1.381 1.381 0 0 1 1.973 0l5.591 5.59a1.381 1.381 0 0 1 0 1.974l-5.59 5.591a1.381 1.381 0 0 1-1.974 0l-5.591-5.59a1.381 1.381 0 0 1 0-1.974z" fill="#e64a19" fill-rule="nonzero"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-global" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#5c6bc0" fill-rule="nonzero"/><path d="M21.132 18.585a1.22 1.22 0 0 0-1.156-.846h-.609v-1.825a.608.608 0 0 0-.608-.609h-3.65v-1.217h1.216a.608.608 0 0 0 .609-.608v-1.217h1.217a1.217 1.217 0 0 0 1.216-1.217v-.25a4.858 4.858 0 0 1 1.765 7.79m-4.198 1.545a4.86 4.86 0 0 1-4.26-4.826c0-.377.049-.742.128-1.089l2.915 2.915v.608a1.217 1.217 0 0 0 1.217 1.217m.608-9.735a6.085 6.085 0 0 0-6.085 6.084 6.085 6.085 0 0 0 6.085 6.085 6.085 6.085 0 0 0 6.085-6.085 6.085 6.085 0 0 0-6.085-6.084z" fill="#c5cae9" stroke-width=".608"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-global-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#5c6bc0"/><path d="M21.133 18.585a1.22 1.22 0 0 0-1.156-.846h-.609v-1.825a.608.608 0 0 0-.608-.609h-3.65v-1.217h1.216a.608.608 0 0 0 .609-.608v-1.217h1.217a1.217 1.217 0 0 0 1.216-1.217v-.25a4.858 4.858 0 0 1 1.765 7.79m-4.198 1.545a4.86 4.86 0 0 1-4.26-4.826c0-.377.049-.742.128-1.089l2.915 2.915v.608a1.217 1.217 0 0 0 1.216 1.217m.609-9.735a6.085 6.085 0 0 0-6.085 6.084 6.085 6.085 0 0 0 6.085 6.085 6.085 6.085 0 0 0 6.085-6.085 6.085 6.085 0 0 0-6.085-6.084z" fill="#c5cae9" stroke-width=".608"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-i18n" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#5c6bc0" fill-rule="nonzero"/><path d="M17.293 17.786l-1.53-1.512.018-.018a10.555 10.555 0 0 0 2.235-3.934h1.765v-1.205h-4.217V9.912h-1.205v1.205h-4.217v1.205h6.73a9.5 9.5 0 0 1-1.91 3.223 9.424 9.424 0 0 1-1.392-2.018h-1.205c.44.982 1.042 1.91 1.795 2.747l-3.067 3.024.856.856 3.012-3.013 1.874 1.874.458-1.229m3.392-3.054H19.48l-2.711 7.23h1.205l.674-1.808h2.862l.68 1.807h1.206l-2.711-7.23m-1.579 4.218l.976-2.609.976 2.609z" fill="#c5cae9" stroke-width=".602"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-i18n-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#5c6bc0"/><path d="M17.293 17.786l-1.53-1.512.018-.018a10.555 10.555 0 0 0 2.235-3.934h1.765v-1.205h-4.217V9.912h-1.205v1.205h-4.217v1.205h6.73a9.5 9.5 0 0 1-1.91 3.223 9.424 9.424 0 0 1-1.392-2.018h-1.205c.44.982 1.042 1.91 1.795 2.747l-3.067 3.024.856.856 3.012-3.013 1.874 1.874.458-1.229m3.392-3.054H19.48l-2.711 7.23h1.205l.674-1.808h2.862l.68 1.807h1.206l-2.711-7.23m-1.579 4.218l.976-2.609.976 2.609z" fill="#c5cae9" stroke-width=".602"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-images" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#009688" fill-rule="nonzero"/><path d="M18.575 12.859h3.713l-3.713-3.713v3.713M13.85 8.134h5.4l4.05 4.05v8.1c0 .74-.61 1.35-1.35 1.35h-8.1a1.35 1.35 0 0 1-1.35-1.35v-10.8c0-.75.6-1.35 1.35-1.35m0 12.15h8.1v-5.4l-2.7 2.7-1.35-1.35-4.05 4.05m1.35-7.425c-.74 0-1.35.61-1.35 1.35s.61 1.35 1.35 1.35 1.35-.61 1.35-1.35-.61-1.35-1.35-1.35z" fill-rule="nonzero" fill="#b2dfdb"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-images-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#009688" fill-rule="nonzero"/><path d="M18.575 12.859h3.713l-3.713-3.713v3.713M13.85 8.134h5.4l4.05 4.05v8.1c0 .74-.61 1.35-1.35 1.35h-8.1a1.35 1.35 0 0 1-1.35-1.35v-10.8c0-.75.6-1.35 1.35-1.35m0 12.15h8.1v-5.4l-2.7 2.7-1.35-1.35-4.05 4.05m1.35-7.425c-.74 0-1.35.61-1.35 1.35s.61 1.35 1.35 1.35 1.35-.61 1.35-1.35-.61-1.35-1.35-1.35z" fill-rule="nonzero" fill="#b2dfdb"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-include" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#039be5" fill-rule="nonzero"/><path d="M20.788 15.981h-2.434v2.434h-1.217V15.98h-2.434v-1.217h2.434V12.33h1.217v2.434h2.434m-3.042-5.476a6.085 6.085 0 0 0-6.085 6.084 6.085 6.085 0 0 0 6.085 6.085 6.085 6.085 0 0 0 6.084-6.085 6.085 6.085 0 0 0-6.084-6.084z" fill="#b3e5fc" stroke-width=".608"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-include-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#039be5"/><path d="M20.788 15.981h-2.434v2.434h-1.217V15.98h-2.434v-1.217h2.434V12.33h1.217v2.434h2.434m-3.042-5.476a6.085 6.085 0 0 0-6.085 6.084 6.085 6.085 0 0 0 6.085 6.085 6.085 6.085 0 0 0 6.084-6.085 6.085 6.085 0 0 0-6.084-6.084z" fill="#b3e5fc" stroke-width=".608"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-javascript" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ffca28" fill-rule="nonzero"/><path d="M17.935 18.374a2.18 2.18 0 0 0 1.972 1.213c.829 0 1.354-.415 1.354-.987 0-.682-.542-.927-1.452-1.324l-.502-.216c-1.435-.613-2.404-1.378-2.404-3.005 0-1.5 1.167-2.638 2.917-2.638a2.957 2.957 0 0 1 2.842 1.599l-1.552.999a1.362 1.362 0 0 0-1.29-.858.873.873 0 0 0-.957.858c0 .583.374.84 1.226 1.213l.502.216c1.697.733 2.654 1.47 2.654 3.139 0 1.798-1.411 2.783-3.308 2.783a3.839 3.839 0 0 1-3.618-2.046zm-7.048.175c.315.583.583 1.027 1.283 1.027s1.066-.256 1.066-1.255v-6.774h1.998v6.804c0 2.064-1.214 3.01-2.982 3.01a3.104 3.104 0 0 1-2.993-1.826z" fill-rule="nonzero" fill="#ffecb3"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-javascript-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ffca28"/><path d="M17.935 18.374a2.18 2.18 0 0 0 1.972 1.213c.829 0 1.354-.415 1.354-.987 0-.682-.542-.927-1.452-1.324l-.502-.216c-1.435-.613-2.404-1.378-2.404-3.005 0-1.5 1.167-2.638 2.917-2.638a2.957 2.957 0 0 1 2.842 1.599l-1.552.999a1.362 1.362 0 0 0-1.29-.858.873.873 0 0 0-.957.858c0 .583.374.84 1.226 1.213l.502.216c1.697.733 2.654 1.47 2.654 3.139 0 1.798-1.412 2.783-3.308 2.783a3.839 3.839 0 0 1-3.618-2.046zm-7.048.175c.315.583.583 1.027 1.283 1.027s1.066-.256 1.066-1.255v-6.774h1.998v6.804c0 2.064-1.214 3.01-2.982 3.01a3.104 3.104 0 0 1-2.993-1.826z" fill="#ffecb3"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-lib" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#c0ca33" fill-rule="nonzero"/><path d="M17.39 12.544a2.05 2.05 0 0 0 2.05-2.05 2.05 2.05 0 0 0-2.05-2.052 2.05 2.05 0 0 0-2.05 2.051 2.05 2.05 0 0 0 2.05 2.051m0 2.42a8.992 8.992 0 0 0-6.152-2.42v7.52c2.392 0 4.539.923 6.152 2.42a8.992 8.992 0 0 1 6.152-2.42v-7.52c-2.392 0-4.539.923-6.152 2.42z" fill="#f0f4c3" stroke-width=".684"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-lib-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#c0ca33"/><path d="M17.391 12.543a2.05 2.05 0 0 0 2.05-2.05 2.05 2.05 0 0 0-2.05-2.052 2.05 2.05 0 0 0-2.05 2.051 2.05 2.05 0 0 0 2.05 2.051m0 2.42a8.992 8.992 0 0 0-6.152-2.42v7.52c2.392 0 4.539.923 6.152 2.42a8.992 8.992 0 0 1 6.152-2.42v-7.52c-2.392 0-4.539.923-6.152 2.42z" fill="#f0f4c3" stroke-width=".684"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-actions" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ab47bc" fill-rule="nonzero"/><path d="M17.655 8.39l-6.152 2.193.933 8.142 5.219 2.888 5.219-2.888.932-8.142zm-1.278 2.067c.234-.004.487.07.768.223.124.066.498.16.83.21 1.183.17 2.586 1.073 3.03 1.95.306.602.243.927-.225 1.169-.404.209-1.23.108-2.43-.297l-1.012-.342-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.518 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.004-.363-.046 0-.04.164-.243.363-.451.748-.781 1.004-1.365 1.083-2.474l.055-.767.176.365c.194.401.23.98.091 1.478-.115.416-.038.462.173.104.261-.443.345-.373.299.251-.05.678-.283 1.187-.808 1.762-.429.468-.377.552.141.233.5-.308.567-.26.31.224-.487.914-1.516 1.69-2.585 1.948-.647.158-1.106.187-1.7.11-1.55-.204-3.018-1.249-3.718-2.648a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.141.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.108.282-.199.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#e1bee7" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-actions-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ab47bc"/><path d="M17.655 8.39l-6.152 2.192.933 8.143 5.219 2.888 5.219-2.888.932-8.143zm-1.278 2.067c.234-.004.487.07.768.222.124.067.498.162.83.21 1.183.171 2.586 1.074 3.03 1.95.306.603.243.928-.225 1.17-.404.208-1.23.107-2.43-.298l-1.012-.341-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.517 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.005-.363-.046 0-.04.164-.243.363-.452.748-.78 1.004-1.364 1.083-2.474l.055-.766.176.364c.194.402.23.981.091 1.479-.115.416-.038.462.173.103.261-.442.345-.372.299.252-.05.678-.283 1.186-.808 1.761-.429.47-.377.553.141.234.5-.308.567-.26.31.224-.487.914-1.516 1.689-2.585 1.948-.647.158-1.106.187-1.7.109-1.55-.203-3.018-1.248-3.718-2.647a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.142.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.109.282-.2.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#e1bee7" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-effects" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#00bcd4" fill-rule="nonzero"/><path d="M17.655 8.39l-6.152 2.193.933 8.142 5.219 2.888 5.219-2.888.932-8.142zm-1.278 2.067c.234-.004.487.07.768.223.124.066.498.16.83.21 1.183.17 2.586 1.073 3.03 1.95.306.602.243.927-.225 1.169-.404.209-1.23.108-2.43-.297l-1.012-.342-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.518 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.004-.363-.046 0-.04.164-.243.363-.451.748-.781 1.004-1.365 1.083-2.474l.055-.767.176.365c.194.401.23.98.091 1.478-.115.416-.038.462.173.104.261-.443.345-.373.299.251-.05.678-.283 1.187-.808 1.762-.429.468-.377.552.141.233.5-.308.567-.26.31.224-.487.914-1.516 1.69-2.585 1.948-.647.158-1.106.187-1.7.11-1.55-.204-3.018-1.249-3.718-2.648a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.141.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.108.282-.199.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#b2ebf2" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-effects-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#00bcd4"/><path d="M17.655 8.39l-6.152 2.192.933 8.143 5.219 2.888 5.219-2.888.932-8.143zm-1.278 2.067c.234-.004.487.07.768.222.124.067.498.162.83.21 1.183.171 2.586 1.074 3.03 1.95.306.603.243.928-.225 1.17-.404.208-1.23.107-2.43-.298l-1.012-.341-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.517 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.005-.363-.046 0-.04.164-.243.363-.452.748-.78 1.004-1.364 1.083-2.474l.055-.766.176.364c.194.402.23.981.091 1.479-.115.416-.038.462.173.103.261-.442.345-.372.299.252-.05.678-.283 1.186-.808 1.761-.429.47-.377.553.141.234.5-.308.567-.26.31.224-.487.914-1.516 1.689-2.585 1.948-.647.158-1.106.187-1.7.109-1.55-.203-3.018-1.248-3.718-2.647a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.142.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.109.282-.2.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#b2ebf2" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-reducer" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ef5350" fill-rule="nonzero"/><path d="M17.655 8.39l-6.152 2.193.933 8.142 5.219 2.888 5.219-2.888.932-8.142zm-1.278 2.067c.234-.004.487.07.768.223.124.066.498.16.83.21 1.183.17 2.586 1.073 3.03 1.95.306.602.243.927-.225 1.169-.404.209-1.23.108-2.43-.297l-1.012-.342-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.518 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.004-.363-.046 0-.04.164-.243.363-.451.748-.781 1.004-1.365 1.083-2.474l.055-.767.176.365c.194.401.23.98.091 1.478-.115.416-.038.462.173.104.261-.443.345-.373.299.251-.05.678-.283 1.187-.808 1.762-.429.468-.377.552.141.233.5-.308.567-.26.31.224-.487.914-1.516 1.69-2.585 1.948-.647.158-1.106.187-1.7.11-1.55-.204-3.018-1.249-3.718-2.648a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.141.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.108.282-.199.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#ffcdd2" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-reducer-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ef5350"/><path d="M17.655 8.39l-6.152 2.192.933 8.143 5.219 2.888 5.219-2.888.932-8.143zm-1.278 2.067c.234-.004.487.07.768.222.124.067.498.162.83.21 1.183.171 2.586 1.074 3.03 1.95.306.603.243.928-.225 1.17-.404.208-1.23.107-2.43-.298l-1.012-.341-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.517 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.005-.363-.046 0-.04.164-.243.363-.452.748-.78 1.004-1.364 1.083-2.474l.055-.766.176.364c.194.402.23.981.091 1.479-.115.416-.038.462.173.103.261-.442.345-.372.299.252-.05.678-.283 1.186-.808 1.761-.429.47-.377.553.141.234.5-.308.567-.26.31.224-.487.914-1.516 1.689-2.585 1.948-.647.158-1.106.187-1.7.109-1.55-.203-3.018-1.248-3.718-2.647a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.142.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.109.282-.2.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#ffcdd2" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-state" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#8bc34a" fill-rule="nonzero"/><path d="M17.655 8.39l-6.152 2.193.933 8.142 5.219 2.888 5.219-2.888.932-8.142zm-1.278 2.067c.234-.004.487.07.768.223.124.066.498.16.83.21 1.183.17 2.586 1.073 3.03 1.95.306.602.243.927-.225 1.169-.404.209-1.23.108-2.43-.297l-1.012-.342-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.518 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.004-.363-.046 0-.04.164-.243.363-.451.748-.781 1.004-1.365 1.083-2.474l.055-.767.176.365c.194.401.23.98.091 1.478-.115.416-.038.462.173.104.261-.443.345-.373.299.251-.05.678-.283 1.187-.808 1.762-.429.468-.377.552.141.233.5-.308.567-.26.31.224-.487.914-1.516 1.69-2.585 1.948-.647.158-1.106.187-1.7.11-1.55-.204-3.018-1.249-3.718-2.648a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.141.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.108.282-.199.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#dcedc8" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-ngrx-state-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#8bc34a"/><path d="M17.655 8.39l-6.152 2.192.933 8.143 5.219 2.888 5.219-2.888.932-8.143zm-1.278 2.067c.234-.004.487.07.768.222.124.067.498.162.83.21 1.183.171 2.586 1.074 3.03 1.95.306.603.243.928-.225 1.17-.404.208-1.23.107-2.43-.298l-1.012-.341-.36.137c-.522.2-1.044.694-1.258 1.19-.154.359-.177.527-.149 1.116.028.59.071.761.28 1.132.239.422.786.96.88.866.026-.026-.03-.197-.124-.38-.093-.183-.148-.368-.122-.41.026-.042.273.114.548.347.611.517 1.326.848 1.981.917.538.056.661-.044.258-.211a1.238 1.238 0 0 1-.374-.25c-.157-.173-.166-.168.504-.318.417-.094 1.24-.531 1.29-.685.016-.05-.118-.07-.338-.053-.2.016-.363-.005-.363-.046 0-.04.164-.243.363-.452.748-.78 1.004-1.364 1.083-2.474l.055-.766.176.364c.194.402.23.981.091 1.479-.115.416-.038.462.173.103.261-.442.345-.372.299.252-.05.678-.283 1.186-.808 1.761-.429.47-.377.553.141.234.5-.308.567-.26.31.224-.487.914-1.516 1.689-2.585 1.948-.647.158-1.106.187-1.7.109-1.55-.203-3.018-1.248-3.718-2.647a8.736 8.736 0 0 0-.572-.989c-.275-.373-.298-.54-.113-.823.093-.142.114-.286.076-.502-.176-.999-.17-1.03.23-1.437.35-.353.371-.4.371-.813 0-.358.036-.475.198-.637.109-.109.282-.2.384-.2.296-.003.807-.277 1.11-.595.252-.265.52-.4.822-.404z" fill="#dcedc8" stroke-width=".696"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-node" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#8bc34a" fill-rule="nonzero"/><path d="M17.25 8.403c-.188 0-.382.048-.542.139l-5.166 2.986a1.096 1.096 0 0 0-.542.944v5.959c0 .388.208.75.542.944l1.354.778c.66.32.882.326 1.187.326.973 0 1.535-.59 1.535-1.618V12.98a.154.154 0 0 0-.153-.153h-.646c-.09 0-.16.07-.16.153v5.882c0 .458-.472.91-1.228.528l-1.424-.813a.181.181 0 0 1-.076-.145v-5.959c0-.062.027-.118.076-.146l5.167-2.979a.15.15 0 0 1 .152 0l5.167 2.98a.164.164 0 0 1 .076.145v5.959a.181.181 0 0 1-.076.145l-5.167 2.98c-.041.027-.11.027-.16 0l-1.305-.792c-.055-.021-.111-.028-.146-.007-.368.208-.437.25-.778.354-.083.028-.215.076.05.222l1.721 1.021c.167.097.348.146.542.146s.375-.049.542-.146l5.166-2.979c.334-.194.542-.556.542-.944v-5.959c0-.389-.208-.75-.542-.944l-5.166-2.986a1.103 1.103 0 0 0-.542-.14m1.389 4.272c-1.472 0-2.354.618-2.354 1.66 0 1.117.875 1.444 2.291 1.583 1.688.166 1.82.416 1.82.75 0 .576-.465.82-1.549.82-1.375 0-1.666-.341-1.77-1.022a.157.157 0 0 0-.153-.125h-.667c-.083 0-.146.063-.146.153 0 .861.472 1.903 2.736 1.903 1.632 0 2.57-.646 2.57-1.771 0-1.118-.75-1.41-2.34-1.625-1.605-.208-1.765-.32-1.765-.694 0-.313.14-.73 1.327-.73 1.042 0 1.451.23 1.611.945.014.07.076.118.146.118h.673a.134.134 0 0 0 .105-.049c.027-.028.048-.07.034-.11-.097-1.237-.916-1.806-2.57-1.806z" fill-rule="nonzero" fill="#f1f8e9"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-node-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#8bc34a" fill-rule="nonzero"/><path d="M17.25 8.403c-.188 0-.382.048-.542.139l-5.166 2.986a1.096 1.096 0 0 0-.542.944v5.959c0 .388.208.75.542.944l1.354.778c.66.32.882.326 1.187.326.973 0 1.535-.59 1.535-1.618V12.98a.154.154 0 0 0-.153-.153h-.646c-.09 0-.16.07-.16.153v5.882c0 .458-.472.91-1.228.528l-1.424-.813a.181.181 0 0 1-.076-.145v-5.959c0-.062.027-.118.076-.146l5.167-2.979a.15.15 0 0 1 .152 0l5.167 2.98a.164.164 0 0 1 .076.145v5.959a.181.181 0 0 1-.076.145l-5.167 2.98c-.041.027-.11.027-.16 0l-1.305-.792c-.055-.021-.111-.028-.146-.007-.368.208-.437.25-.778.354-.083.028-.215.076.05.222l1.721 1.021c.167.097.348.146.542.146s.375-.049.542-.146l5.166-2.979c.334-.194.542-.556.542-.944v-5.959c0-.389-.208-.75-.542-.944l-5.166-2.986a1.103 1.103 0 0 0-.542-.14m1.389 4.272c-1.472 0-2.354.618-2.354 1.66 0 1.117.875 1.444 2.291 1.583 1.688.166 1.82.416 1.82.75 0 .576-.465.82-1.549.82-1.375 0-1.666-.341-1.77-1.022a.157.157 0 0 0-.153-.125h-.667c-.083 0-.146.063-.146.153 0 .861.472 1.903 2.736 1.903 1.632 0 2.57-.646 2.57-1.771 0-1.118-.75-1.41-2.34-1.625-1.605-.208-1.765-.32-1.765-.694 0-.313.14-.73 1.327-.73 1.042 0 1.451.23 1.611.945.014.07.076.118.146.118h.673a.134.134 0 0 0 .105-.049c.027-.028.048-.07.034-.11-.097-1.237-.916-1.806-2.57-1.806z" fill-rule="nonzero" fill="#f1f8e9"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-public" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#039be5" fill-rule="nonzero"/><path d="M20.036 16.746c.05-.408.087-.817.087-1.237s-.037-.83-.087-1.238h2.091c.099.396.16.81.16 1.238a5.1 5.1 0 0 1-.16 1.237m-3.186 3.44c.371-.687.656-1.43.854-2.203h1.825a4.968 4.968 0 0 1-2.679 2.203m-.155-3.44h-2.895c-.062-.408-.099-.817-.099-1.237s.037-.835.1-1.238h2.894c.056.403.1.817.1 1.238s-.044.829-.1 1.237m-1.447 3.687a8.39 8.39 0 0 1-1.182-2.45h2.363a8.39 8.39 0 0 1-1.181 2.45m-2.475-7.399h-1.806a4.902 4.902 0 0 1 2.672-2.202c-.37.686-.65 1.429-.866 2.202m-1.806 4.95h1.806c.217.773.495 1.515.866 2.202a4.954 4.954 0 0 1-2.672-2.203m-.508-1.237a5.099 5.099 0 0 1-.16-1.237 5.1 5.1 0 0 1 .16-1.238h2.091c-.049.409-.086.817-.086 1.238s.037.829.086 1.237m2.698-6.168a8.425 8.425 0 0 1 1.181 2.456h-2.363a8.426 8.426 0 0 1 1.182-2.456m4.28 2.456h-1.824a9.682 9.682 0 0 0-.854-2.202 4.94 4.94 0 0 1 2.679 2.202m-4.281-3.712a6.193 6.193 0 0 0-6.187 6.187 6.186 6.186 0 0 0 6.187 6.186 6.186 6.186 0 0 0 6.186-6.186 6.186 6.186 0 0 0-6.186-6.187z" fill="#b3e5fc" stroke-width=".619"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-public-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#039be5"/><path d="M20.037 16.746c.05-.408.087-.817.087-1.237s-.037-.83-.087-1.238h2.091c.099.396.16.81.16 1.238a5.1 5.1 0 0 1-.16 1.237m-3.186 3.44c.371-.687.656-1.43.854-2.203h1.825a4.967 4.967 0 0 1-2.68 2.203m-.154-3.44h-2.895c-.062-.408-.099-.817-.099-1.237s.037-.835.099-1.238h2.895c.056.403.1.817.1 1.238s-.044.829-.1 1.237m-1.447 3.687a8.39 8.39 0 0 1-1.182-2.45h2.363a8.39 8.39 0 0 1-1.181 2.45m-2.475-7.399H13.06a4.902 4.902 0 0 1 2.672-2.202c-.371.686-.65 1.429-.866 2.202m-1.806 4.95h1.806c.217.773.495 1.515.866 2.202a4.954 4.954 0 0 1-2.672-2.203m-.508-1.237a5.099 5.099 0 0 1-.16-1.237 5.1 5.1 0 0 1 .16-1.238h2.091c-.05.409-.086.817-.086 1.238s.037.829.086 1.237m2.698-6.168a8.425 8.425 0 0 1 1.181 2.456h-2.363a8.426 8.426 0 0 1 1.182-2.456m4.28 2.456h-1.824a9.682 9.682 0 0 0-.854-2.202 4.941 4.941 0 0 1 2.679 2.202M17.34 9.322a6.193 6.193 0 0 0-6.187 6.187 6.186 6.186 0 0 0 6.187 6.186 6.186 6.186 0 0 0 6.186-6.186 6.186 6.186 0 0 0-6.186-6.187z" fill="#b3e5fc" stroke-width=".619"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-react-components" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#00bcd4" fill-rule="nonzero"/><path d="M16.473 13.927c.723 0 1.313.59 1.313 1.327 0 .703-.59 1.3-1.313 1.3a1.318 1.318 0 0 1-1.313-1.3c0-.737.59-1.327 1.313-1.327m-3.252 6.946c.443.267 1.412-.14 2.529-1.194a17.015 17.015 0 0 1-1.06-1.335 15.945 15.945 0 0 1-1.686-.252c-.358 1.502-.225 2.535.217 2.78m.499-4.03l-.204-.359a5.558 5.558 0 0 0-.203.604c.19.042.4.078.618.113l-.211-.359m4.593-.533l.569-1.054-.569-1.053c-.21-.372-.435-.702-.639-1.032-.38-.022-.78-.022-1.2-.022-.422 0-.823 0-1.202.022-.204.33-.428.66-.639 1.032l-.569 1.053.57 1.054c.21.372.434.702.638 1.032.38.021.78.021 1.201.021.421 0 .822 0 1.201-.02.204-.331.428-.661.639-1.033m-1.84-4.72c-.133.155-.274.316-.414.506h.828c-.14-.19-.28-.351-.414-.506m0 7.332c.133-.154.274-.316.414-.505h-.828c.14.19.28.35.414.505m3.245-9.284c-.436-.267-1.405.14-2.522 1.194.366.414.724.864 1.06 1.334.577.057 1.146.14 1.686.253.359-1.503.225-2.535-.224-2.78m-.492 4.03l.204.358c.077-.203.154-.407.203-.604-.19-.042-.4-.077-.618-.112l.211.358m1.018-4.95c1.033.589 1.145 2.141.71 3.953 1.784.527 3.069 1.398 3.069 2.584 0 1.187-1.285 2.058-3.07 2.585.436 1.812.324 3.364-.709 3.954-1.025.59-2.423-.085-3.77-1.37-1.35 1.285-2.747 1.96-3.78 1.37-1.025-.59-1.137-2.142-.702-3.954-1.783-.527-3.069-1.398-3.069-2.585s1.286-2.057 3.07-2.584c-.436-1.812-.324-3.364.702-3.954 1.032-.59 2.43.084 3.778 1.37 1.348-1.286 2.746-1.96 3.771-1.37m-.203 6.538c.239.527.45 1.054.625 1.588 1.475-.443 2.303-1.075 2.303-1.588 0-.512-.828-1.144-2.303-1.587a15.81 15.81 0 0 1-.625 1.587m-7.136 0a15.806 15.806 0 0 1-.625-1.587c-1.474.443-2.303 1.075-2.303 1.587 0 .513.829 1.145 2.303 1.588.176-.534.387-1.06.625-1.588m6.321 1.588l-.21.358c.217-.035.428-.07.617-.113-.049-.196-.126-.4-.203-.604l-.204.359m-2.03 2.837c1.117 1.053 2.086 1.46 2.522 1.194.45-.246.583-1.278.224-2.781-.54.112-1.11.196-1.685.253-.337.47-.695.92-1.06 1.334m-3.477-6.012l.21-.358c-.217.035-.428.07-.617.113.049.196.126.4.203.604l.204-.359m2.03-2.837c-1.117-1.053-2.086-1.46-2.529-1.194-.442.246-.576 1.278-.217 2.781.54-.112 1.11-.196 1.685-.253.337-.47.695-.92 1.06-1.334z" fill="#b2ebf2" stroke-width=".702"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-react-components-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#00bcd4"/><path d="M16.473 13.928c.723 0 1.313.59 1.313 1.327 0 .703-.59 1.3-1.313 1.3a1.318 1.318 0 0 1-1.313-1.3c0-.737.59-1.327 1.313-1.327m-3.252 6.946c.443.267 1.412-.14 2.529-1.194a16.997 16.997 0 0 1-1.06-1.335 15.945 15.945 0 0 1-1.686-.252c-.358 1.502-.225 2.535.217 2.78m.499-4.03l-.204-.359c-.077.204-.154.408-.203.604.19.042.4.078.618.113l-.211-.359m4.593-.533l.569-1.054-.57-1.053c-.21-.372-.434-.702-.638-1.032-.38-.022-.78-.022-1.201-.022-.421 0-.822 0-1.2.022-.205.33-.43.66-.64 1.032l-.569 1.053.569 1.054c.21.372.435.702.64 1.032.378.021.779.021 1.2.021.421 0 .822 0 1.2-.02.205-.33.43-.661.64-1.033m-1.84-4.72c-.133.155-.274.316-.414.506h.828c-.14-.19-.28-.351-.414-.506m0 7.332c.133-.154.274-.316.414-.505h-.828c.14.19.28.35.414.505m3.244-9.284c-.435-.267-1.404.14-2.52 1.194.364.414.723.864 1.06 1.334.575.057 1.144.14 1.685.253.358-1.503.225-2.535-.225-2.78m-.491 4.03l.203.358c.078-.203.155-.407.204-.604-.19-.042-.4-.077-.618-.112l.21.358m1.02-4.95c1.032.589 1.144 2.141.708 3.953 1.784.527 3.07 1.398 3.07 2.584 0 1.187-1.286 2.058-3.07 2.585.436 1.812.323 3.364-.709 3.954-1.025.59-2.423-.085-3.771-1.37-1.348 1.285-2.746 1.96-3.778 1.37-1.026-.59-1.138-2.142-.703-3.954-1.783-.527-3.069-1.398-3.069-2.585s1.286-2.057 3.07-2.584c-.436-1.812-.324-3.364.702-3.954 1.032-.59 2.43.084 3.778 1.37 1.348-1.286 2.746-1.96 3.771-1.37m-.204 6.538c.24.527.45 1.054.625 1.588 1.475-.443 2.304-1.075 2.304-1.588 0-.512-.829-1.144-2.304-1.587a15.81 15.81 0 0 1-.625 1.587m-7.135 0a15.808 15.808 0 0 1-.625-1.587c-1.475.443-2.303 1.075-2.303 1.587 0 .513.828 1.145 2.303 1.588.176-.534.386-1.06.625-1.588m6.32 1.588l-.21.358c.218-.035.428-.07.618-.113a5.56 5.56 0 0 0-.204-.604l-.203.359m-2.03 2.837c1.117 1.053 2.086 1.46 2.521 1.194.45-.246.583-1.278.225-2.781-.54.112-1.11.196-1.685.253-.338.47-.696.92-1.06 1.334m-3.477-6.012l.21-.358c-.217.035-.428.07-.617.112.049.197.126.4.203.604l.204-.358m2.03-2.837c-1.117-1.053-2.086-1.46-2.529-1.194-.442.246-.576 1.278-.217 2.781.54-.112 1.11-.196 1.685-.253.337-.47.695-.92 1.06-1.334z" fill="#b2ebf2" stroke-width=".702"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-redux-actions" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ab47bc" fill-rule="nonzero"/><g transform="translate(8.378 6.436) scale(.17228)" fill="#e1bee7" stroke="#e1bee7" stroke-miterlimit="4" stroke-width="1.702"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8S68 54.2 65 54.2h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-redux-actions-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ab47bc"/><g transform="translate(8.378 6.436) scale(.17228)" fill="#e1bee7" stroke="#e1bee7" stroke-miterlimit="4" stroke-width="1.702"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8S68 54.2 65 54.2h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-redux-reducer" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ef5350" fill-rule="nonzero"/><g transform="translate(8.378 6.436) scale(.17228)" fill="#ffcdd2" stroke="#ffcdd2" stroke-miterlimit="4" stroke-width="1.702"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8S68 54.2 65 54.2h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-redux-reducer-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ef5350"/><g transform="translate(8.378 6.436) scale(.17228)" fill="#ffcdd2" stroke="#ffcdd2" stroke-miterlimit="4" stroke-width="1.702"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8S68 54.2 65 54.2h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-redux-store" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#8bc34a" fill-rule="nonzero"/><g transform="translate(8.378 6.436) scale(.17228)" fill="#dcedc8" stroke="#dcedc8" stroke-miterlimit="4" stroke-width="1.702"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8S68 54.2 65 54.2h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-redux-store-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#8bc34a"/><g transform="translate(8.378 6.436) scale(.17228)" fill="#dcedc8" stroke="#dcedc8" stroke-miterlimit="4" stroke-width="1.702"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8S68 54.2 65 54.2h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-resource" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#fbc02d" fill-rule="nonzero"/><path d="M21.598 12.059h-6.085v-1.217h6.085m-2.434 6.085h-3.65V15.71h3.65m2.434-1.217h-6.085v-1.217h6.085m.608-4.26h-7.301a1.217 1.217 0 0 0-1.217 1.218v7.301a1.217 1.217 0 0 0 1.217 1.217h7.301a1.217 1.217 0 0 0 1.217-1.217v-7.301a1.217 1.217 0 0 0-1.217-1.217m-9.735 2.434h-1.217v8.518a1.217 1.217 0 0 0 1.217 1.217h8.519v-1.217H12.47z" fill="#fff9c4" stroke-width=".608"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-resource-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#fbc02d"/><path d="M21.598 12.059h-6.085v-1.217h6.085m-2.434 6.085h-3.65V15.71h3.65m2.434-1.217h-6.085v-1.217h6.085m.608-4.26h-7.301a1.217 1.217 0 0 0-1.217 1.218v7.301a1.217 1.217 0 0 0 1.217 1.217h7.301a1.217 1.217 0 0 0 1.217-1.217v-7.301a1.217 1.217 0 0 0-1.217-1.217m-9.735 2.433h-1.217v8.519a1.217 1.217 0 0 0 1.217 1.217h8.519v-1.217H12.47z" fill="#fff9c4" stroke-width=".608"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" id="folder-sass" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#f8bbd0" fill-rule="nonzero"/><path d="M23.36 10.506c-.39-1.527-2.922-2.03-5.319-1.178-1.426.507-2.97 1.302-4.08 2.34-1.32 1.235-1.53 2.31-1.444 2.759.306 1.584 2.477 2.62 3.37 3.388v.005c-.264.13-2.19 1.104-2.64 2.1-.476 1.052.075 1.806.44 1.908 1.131.315 2.292-.251 2.916-1.182.602-.897.551-2.056.29-2.633.36-.095.781-.138 1.316-.076 1.508.177 1.804 1.118 1.748 1.513-.057.394-.373.61-.48.676-.105.065-.137.088-.129.137.013.07.062.068.152.053.125-.021.792-.321.821-1.048.037-.924-.849-1.958-2.416-1.93-.646.01-1.052.072-1.345.181-.022-.024-.044-.05-.067-.073-.969-1.034-2.76-1.765-2.684-3.156.027-.505.203-1.835 3.442-3.45 2.653-1.322 4.777-.957 5.145-.151.524 1.152-1.136 3.293-3.891 3.601-1.05.118-1.603-.289-1.74-.44-.145-.16-.166-.167-.22-.137-.088.049-.033.19 0 .274.082.214.42.594.995.782.506.166 1.739.258 3.23-.319 1.669-.646 2.972-2.443 2.59-3.944zm-7.103 7.783a2.2 2.2 0 0 1-.065 1.413 2.405 2.405 0 0 1-.453.704c-.5.546-1.198.752-1.497.579-.323-.188-.161-.956.418-1.568.623-.66 1.52-1.083 1.52-1.083l-.002-.002.079-.043z" fill="#ec407a" fill-rule="nonzero" stroke="#ec407a" stroke-width=".5199012000000001"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" id="folder-sass-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#f8bbd0" fill-rule="nonzero"/><path d="M23.36 10.506c-.39-1.527-2.922-2.03-5.319-1.178-1.426.507-2.97 1.302-4.08 2.34-1.32 1.235-1.53 2.31-1.444 2.759.306 1.584 2.477 2.62 3.37 3.388v.005c-.264.13-2.19 1.104-2.64 2.1-.476 1.052.075 1.806.44 1.908 1.131.315 2.292-.251 2.916-1.182.602-.897.551-2.056.29-2.633.36-.095.781-.138 1.316-.076 1.508.177 1.804 1.118 1.748 1.513-.057.394-.373.61-.48.676-.105.065-.137.088-.129.137.013.07.062.068.152.053.125-.021.792-.321.821-1.048.037-.924-.849-1.958-2.416-1.93-.646.01-1.052.072-1.345.181-.022-.024-.044-.05-.067-.073-.969-1.034-2.76-1.765-2.684-3.156.027-.505.203-1.835 3.442-3.45 2.653-1.322 4.777-.957 5.145-.151.524 1.152-1.136 3.293-3.891 3.601-1.05.118-1.603-.289-1.74-.44-.145-.16-.166-.167-.22-.137-.088.049-.033.19 0 .274.082.214.42.594.995.782.506.166 1.739.258 3.23-.319 1.669-.646 2.972-2.443 2.59-3.944zm-7.103 7.783a2.2 2.2 0 0 1-.065 1.413 2.405 2.405 0 0 1-.453.704c-.5.546-1.198.752-1.497.579-.323-.188-.161-.956.418-1.568.623-.66 1.52-1.083 1.52-1.083l-.002-.002.079-.043z" fill="#ec407a" fill-rule="nonzero" stroke="#ec407a" stroke-width=".5199012000000001"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-scripts" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#546e7a" fill-rule="nonzero"/><path d="M18.466 20.241c.69 0 1.259-.568 1.259-1.258v-8.18H15.32a.632.632 0 0 0-.63.63v6.292h-1.887v-6.922c0-1.036.852-1.888 1.888-1.888h6.921c1.036 0 1.888.852 1.888 1.888v.63h-2.517v8.18a1.896 1.896 0 0 1-1.888 1.887h-6.292a1.896 1.896 0 0 1-1.888-1.888v-.629h6.293c0 .69.568 1.258 1.258 1.258z" fill-rule="nonzero" fill="#cfd8dc"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-scripts-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#546e7a" fill-rule="nonzero"/><path d="M18.466 20.241c.69 0 1.259-.568 1.259-1.258v-8.18H15.32a.632.632 0 0 0-.63.63v6.292h-1.887v-6.922c0-1.036.852-1.888 1.888-1.888h6.921c1.036 0 1.888.852 1.888 1.888v.63h-2.517v8.18a1.896 1.896 0 0 1-1.888 1.887h-6.292a1.896 1.896 0 0 1-1.888-1.888v-.629h6.293c0 .69.568 1.258 1.258 1.258z" fill-rule="nonzero" fill="#cfd8dc"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-src" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#4caf50" fill-rule="nonzero"/><g fill="#c8e6c9" transform="translate(2.065 -.225) scale(.70678)"><path d="M19.146 30.989a.902.902 0 0 1-.207-.025 1.045 1.045 0 0 1-.726-1.213l2.709-14.431c.049-.279.209-.525.444-.683a.891.891 0 0 1 .7-.122c.519.152.837.684.727 1.213L20.077 30.16a1.032 1.032 0 0 1-.442.681.895.895 0 0 1-.489.148zM24.578 28.944h-.068a.932.932 0 0 1-.668-.377 1.104 1.104 0 0 1 .1-1.419l4.658-4.553-4.638-4.239a1.105 1.105 0 0 1-.141-1.416.938.938 0 0 1 .661-.4.9.9 0 0 1 .709.237l5.47 5c.386.372.448.974.144 1.416a1.05 1.05 0 0 1-.142.163l-5.447 5.324a.913.913 0 0 1-.638.264zM16.423 28.947a.917.917 0 0 1-.639-.267l-5.452-5.327a.874.874 0 0 1-.132-.153 1.097 1.097 0 0 1 .141-1.414l5.471-5a.882.882 0 0 1 .7-.238.939.939 0 0 1 .665.4 1.104 1.104 0 0 1-.14 1.417L12.4 22.6l4.659 4.551c.377.382.42.988.1 1.419a.928.928 0 0 1-.669.377z" fill-rule="nonzero"/></g></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-src-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#4caf50"/><g fill="#c8e6c9" fill-rule="evenodd" transform="translate(2.064 -.224) scale(.70678)"><path d="M19.146 30.989a.902.902 0 0 1-.207-.025 1.045 1.045 0 0 1-.726-1.213l2.709-14.431c.049-.279.209-.525.444-.683a.891.891 0 0 1 .7-.122c.519.152.837.684.727 1.213L20.077 30.16a1.032 1.032 0 0 1-.442.681.895.895 0 0 1-.489.148zM24.578 28.944h-.068a.932.932 0 0 1-.668-.377 1.104 1.104 0 0 1 .1-1.419l4.658-4.553-4.638-4.239a1.105 1.105 0 0 1-.141-1.416.938.938 0 0 1 .661-.4.9.9 0 0 1 .709.237l5.47 5c.386.372.448.974.144 1.416a1.05 1.05 0 0 1-.142.163l-5.447 5.324a.913.913 0 0 1-.638.264zM16.423 28.947a.917.917 0 0 1-.639-.267l-5.452-5.327a.874.874 0 0 1-.132-.153 1.097 1.097 0 0 1 .141-1.414l5.471-5a.882.882 0 0 1 .7-.238.939.939 0 0 1 .665.4 1.104 1.104 0 0 1-.14 1.417L12.4 22.6l4.659 4.551c.377.382.42.988.1 1.419a.928.928 0 0 1-.669.377z" fill-rule="nonzero"/></g></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-test" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#1de9b6" fill-rule="nonzero"/><path d="M14 8.097v1.39h.695v9.732A2.794 2.794 0 0 0 17.475 22a2.794 2.794 0 0 0 2.781-2.78V9.486h.695v-1.39H14m2.78 9.732c-.417 0-.695-.278-.695-.695 0-.417.278-.695.696-.695.417 0 .695.278.695.695 0 .417-.278.695-.695.695m1.39-2.78c-.417 0-.695-.278-.695-.696 0-.417.278-.695.695-.695.417 0 .695.278.695.695 0 .418-.278.696-.695.696m.695-3.476h-2.78V9.487h2.78v2.086z" fill="#00897b" fill-rule="nonzero"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-test-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#1de9b6" fill-rule="nonzero"/><path d="M14 8.097v1.39h.695v9.732A2.794 2.794 0 0 0 17.475 22a2.794 2.794 0 0 0 2.781-2.78V9.486h.695v-1.39H14m2.78 9.732c-.417 0-.695-.278-.695-.695 0-.417.278-.695.696-.695.417 0 .695.278.695.695 0 .417-.278.695-.695.695m1.39-2.78c-.417 0-.695-.278-.695-.696 0-.417.278-.695.695-.695.417 0 .695.278.695.695 0 .418-.278.696-.695.696m.695-3.476h-2.78V9.487h2.78v2.086z" fill="#00897b" fill-rule="nonzero"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-tools" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#1e88e5" fill-rule="nonzero"/><path d="M21.043 15.266a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m-2.141-2.855a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m-3.569 0a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m-2.141 2.855a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m3.925-6.424a6.424 6.424 0 0 0-6.423 6.424 6.424 6.424 0 0 0 6.423 6.424 1.07 1.07 0 0 0 1.071-1.07c0-.28-.107-.53-.278-.715a1.105 1.105 0 0 1-.271-.713 1.07 1.07 0 0 1 1.07-1.071h1.263a3.569 3.569 0 0 0 3.57-3.569c0-3.154-2.877-5.71-6.425-5.71z" fill="#bbdefb" stroke-width=".714"/></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-tools-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#1e88e5"/><path d="M21.043 15.266a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m-2.141-2.855a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m-3.569 0a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m-2.141 2.855a1.07 1.07 0 0 1-1.07-1.07 1.07 1.07 0 0 1 1.07-1.071 1.07 1.07 0 0 1 1.07 1.07 1.07 1.07 0 0 1-1.07 1.071m3.925-6.424a6.424 6.424 0 0 0-6.423 6.424 6.424 6.424 0 0 0 6.423 6.424 1.07 1.07 0 0 0 1.071-1.07c0-.28-.107-.53-.278-.715a1.105 1.105 0 0 1-.271-.713 1.07 1.07 0 0 1 1.07-1.071h1.263a3.569 3.569 0 0 0 3.57-3.569c0-3.154-2.877-5.71-6.425-5.71z" fill="#bbdefb" stroke-width=".714"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-views" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#ff8a65" fill-rule="nonzero"/><path d="M12.487 21.868L11.384 9.5H23.5l-1.104 12.366-4.961 1.375-4.948-1.373zm4.464-3.2l-3.926-2.36v-.855l3.926-2.361v1.323l-2.504 1.465 2.504 1.465v1.323zm.982-.001v-1.323l2.522-1.464-2.522-1.464v-1.323l3.926 2.35v.874l-3.926 2.35z" fill="#e44d26"/></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="folder-views-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#ff8a65" fill-rule="nonzero"/><path d="M12.487 21.868L11.384 9.5H23.5l-1.104 12.366-4.961 1.375-4.948-1.373zm4.464-3.2l-3.926-2.36v-.855l3.926-2.361v1.323l-2.504 1.465 2.504 1.465v1.323zm.982-.001v-1.323l2.522-1.464-2.522-1.464v-1.323l3.926 2.35v.874l-3.926 2.35z" fill="#e44d26"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-vscode" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#42a5f5" fill-rule="nonzero"/><path d="M20.811 8.52l-5.988 5.506-3.346-2.522-1.383.805 3.298 3.03-3.298 3.032 1.383.807 3.346-2.522 5.988 5.503 2.921-1.419V9.94zm0 3.622v6.396l-4.245-3.198z" fill="#bbdefb" stroke-width=".974"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-vscode-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#42a5f5" fill-rule="nonzero"/><path d="M20.81 8.52l-5.988 5.506-3.346-2.522-1.384.805 3.3 3.03-3.3 3.032 1.384.807 3.346-2.522 5.988 5.503 2.921-1.419V9.94zm0 3.621v6.397l-4.245-3.198z" fill="#bbdefb" stroke-width=".974"/></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-vue" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#009688" fill-rule="nonzero"/><g transform="translate(8.459 6.362) scale(.69572)"><path d="M1.821 4.15l10.21 17.618L22.239 4.235v-.084h-7.692l-2.434 4.178-2.422-4.178z" fill="#41b883"/><path d="M5.937 4.15l6.152 10.616 6.18-10.617h-3.722l-2.434 4.179-2.422-4.179z" fill="#35495e"/></g></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-vue-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#009688"/><g transform="translate(8.458 6.362) scale(.69572)"><path d="M1.821 4.15l10.21 17.618L22.239 4.235v-.084h-7.692l-2.434 4.178-2.422-4.178z" fill="#41b883"/><path d="M5.937 4.15l6.152 10.616 6.18-10.617h-3.722l-2.434 4.179-2.422-4.179z" fill="#35495e"/></g></symbol><symbol clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-webpack" xmlns="http://www.w3.org/2000/svg"><path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.097.903 2 2 2h16c1.097 0 2-.903 2-2V8a2 2 0 0 0-2-2h-8l-2-2z" fill="#03a9f4" fill-rule="nonzero"/><g transform="translate(9.192 7.48) scale(.66328)"><path d="M19.376 15.988l-7.708 4.45-7.709-4.45v-8.9l7.709-4.451 7.708 4.45z" fill="#fff" fill-opacity=".785"/><path d="M12.286 1.98c-.21 0-.41.059-.57.179l-7.9 4.44c-.32.17-.53.5-.53.88v9c0 .38.21.711.53.881l7.9 4.44c.16.12.36.18.57.18s.41-.06.57-.18l7.9-4.44c.32-.17.53-.5.53-.88v-9c0-.38-.21-.712-.53-.882l-7.9-4.44a.945.945 0 0 0-.57-.179zm0 2.15l7 3.94v2.103h-.016v5.177h.016v.54l-7 3.939-7-3.94V8.07zm0 2.08l-4.9 2.83 4.9 2.83 4.9-2.83zm-5 5.08v3.58l4 2.309v-3.58zm10 0l-4 2.308v3.58l4-2.308z" fill="#8ed6fb"/><path d="M12.286 6.21l-4.9 2.83 4.9 2.83 4.9-2.83zm-5 5.08v3.58l4 2.309v-3.58zm10 0l-4 2.308v3.58l4-2.308z" fill="#1c78c0"/></g></symbol><symbol clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" viewBox="0 0 24 24" id="folder-webpack-open" xmlns="http://www.w3.org/2000/svg"><path d="M19 20H4a2 2 0 0 1-2-2V6c0-1.11.89-2 2-2h6l2 2h7c1.097 0 2 .903 2 2H4v10l2.14-8h17.07l-2.28 8.5c-.23.87-1.01 1.5-1.93 1.5z" fill="#03a9f4"/><g transform="translate(9.193 7.48) scale(.66328)"><path d="M19.376 15.988l-7.708 4.45-7.709-4.45v-8.9l7.709-4.451 7.708 4.45z" fill="#fff" fill-opacity=".785"/><path d="M12.286 1.98c-.21 0-.41.059-.57.179l-7.9 4.44c-.32.17-.53.5-.53.88v9c0 .38.21.711.53.881l7.9 4.44c.16.12.36.18.57.18s.41-.06.57-.18l7.9-4.44c.32-.17.53-.5.53-.88v-9c0-.38-.21-.712-.53-.882l-7.9-4.44a.945.945 0 0 0-.57-.179zm0 2.15l7 3.94v2.103h-.016v5.177h.016v.54l-7 3.939-7-3.94V8.07zm0 2.08l-4.9 2.83 4.9 2.83 4.9-2.83zm-5 5.08v3.58l4 2.309v-3.58zm10 0l-4 2.308v3.58l4-2.308z" fill="#8ed6fb"/><path d="M12.286 6.21l-4.9 2.83 4.9 2.83 4.9-2.83zm-5 5.08v3.58l4 2.309v-3.58zm10 0l-4 2.308v3.58l4-2.308z" fill="#1c78c0"/></g></symbol><symbol viewBox="0 0 24 24" id="font" xmlns="http://www.w3.org/2000/svg"><path d="M9.62 12L12 5.67 14.37 12M11 3L5.5 17h2.25l1.12-3h6.25l1.13 3h2.25L13 3h-2z" fill="#f44336"/></symbol><symbol viewBox="0 0 500 500" id="fsharp" xmlns="http://www.w3.org/2000/svg"><path d="M235.906 36.66L21.963 250.601l213.943 213.943v-84.36L106.209 250.487l129.697-129.696z" fill="#378bba" stroke-width="14.706"/><path d="M235.906 156.614l-93.622 93.62 93.622 93.622z" fill="#378bba" stroke-width="15.006"/><path d="M263.417 36.64L477.36 250.583 263.417 464.526v-84.36l129.696-129.697-129.696-129.696z" fill="#30b9db" stroke-width="14.706"/></symbol><symbol viewBox="0 0 152.99 160.01" id="fusebox" xmlns="http://www.w3.org/2000/svg"><defs id="fkdefs4"><style id="fkstyle2">.fkcls-1{fill:#fff}.fkcls-2{fill:#515151}.fkcls-3{fill:#1d79bf}.fkcls-4{fill:#383838}</style></defs><title id="fktitle6">Asset 3</title><g id="fkLayer_2" data-name="Layer 2" transform="matrix(.87285 0 0 .87285 10.17 10.175)"><g id="fkFuse_Box" data-name="Fuse Box"><g id="fkLOGO"><path class="fkcls-1" id="fkpolygon8" fill="#fff" d="M76.56 2.19l74.22 24.93-7.7 87.77-65.41 42.66-69.79-43.93-5.7-86.13z"/><path class="fkcls-2" d="M77.69 160L5.87 114.81 0 26 76.55 0 153 25.67l-7.94 90.4zM9.88 112.43l67.77 42.66 63.45-41.39 7.47-85.13-72-24.18L4.36 28.95z" id="fkpath10" fill="#515151"/><path class="fkcls-3" id="fkpolygon12" fill="#1d79bf" d="M76.4 148.8V61.68l66.93-29.82-5.99 78.77z"/><path id="fkF" class="fkcls-4" fill="#383838" d="M76.4 148.8l-60.35-37.39L9.63 31.8 76.4 61.68z"/><path class="fkcls-1" d="M25.58 52.73l.54 15.93 37.35 18.18.12 14.69-37-18.21 1.64 37.1-14.56-9-5.05-80.55 67.79 30.82v15.46z" id="fkpath15" fill="#fff"/><path class="fkcls-1" d="M135.91 90.77c-.08 13.12-6.33 26.59-16.77 33.12l-42.8 27.93V61.71l42.27-18.84c5.16-2.41 9.51-1.43 12.4 3.11 1.9 3 2.89 7.23 2.86 12.21A35.69 35.69 0 0 1 129.34 76c4.29 2 6.66 6.55 6.57 14.77zM123 63.76c0-4.64-2-6.93-4.92-5.45l-29 14.48L89 90l29.44-15.59c2.5-1.32 4.56-5.91 4.56-10.65zM125.15 96c0-5.71-2.42-8.24-6.55-5.93L89 106.64v19.58l29.34-17.46c4.43-2.64 6.79-7.27 6.81-12.76z" id="fkpath17" fill="#fff"/><path id="fkTOP" class="fkcls-4" fill="#383838" d="M76.4 8.82L9.71 31.77l109.77 2.38-84.02 9.21L76.4 61.68l20.76-9.25-27.73-1.37 49.78-8.46 24.12-10.74z"/></g></g></g></symbol><symbol viewBox="0 0 24 24" id="git" xmlns="http://www.w3.org/2000/svg"><path d="M2.6 10.59L8.38 4.8l1.69 1.7c-.24.85.15 1.78.93 2.23v5.54c-.6.34-1 .99-1 1.73a2 2 0 0 0 2 2 2 2 0 0 0 2-2c0-.74-.4-1.39-1-1.73V9.41l2.07 2.09c-.07.15-.07.32-.07.5a2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0-2-2c-.18 0-.35 0-.5.07L13.93 7.5a1.98 1.98 0 0 0-1.15-2.34c-.43-.16-.88-.2-1.28-.09L9.8 3.38l.79-.78c.78-.79 2.04-.79 2.82 0l7.99 7.99c.79.78.79 2.04 0 2.82l-7.99 7.99c-.78.79-2.04.79-2.82 0L2.6 13.41c-.79-.78-.79-2.04 0-2.82z" fill="#e64a19"/></symbol><symbol viewBox="0 0 494 455" id="gitlab" xmlns="http://www.w3.org/2000/svg"><title>logo</title><defs><path id="fma" d="M0 1173.3h2000V0H0v1173.3z"/></defs><g transform="matrix(.88256 0 0 -.88256 -286.767 742.766)" fill="none" fill-rule="evenodd"><mask id="fmb" fill="#fff"><use width="100%" height="100%" xlink:href="#fma"/></mask><g mask="url(#fmb)"><g transform="translate(358.67 358.67)"><path d="M492.532 195.445l-27.559 84.815-54.617 168.1c-2.81 8.648-15.045 8.648-17.856 0l-54.619-168.1h-181.37l-54.62 168.1c-2.81 8.648-15.045 8.648-17.856 0l-54.617-168.1-27.557-84.815a18.775 18.775 0 0 1 6.82-20.992l238.51-173.29 238.51 173.29a18.777 18.777 0 0 1 6.82 20.992" fill="#fc6d26"/><path d="M247.2 1.16l90.684 279.1h-181.37z" fill="#e24329"/><path d="M247.201 1.16l-90.684 279.09H29.427z" fill="#fc6d26"/><path d="M29.422 280.256L1.862 195.44a18.774 18.774 0 0 1 6.822-20.991L247.194 1.16z" fill="#fca326"/><path d="M29.422 280.26h127.09l-54.619 168.1c-2.81 8.65-15.047 8.65-17.856 0z" fill="#e24329"/><path d="M247.2 1.16l90.684 279.09h127.09z" fill="#fc6d26"/><path d="M464.98 280.256l27.559-84.815a18.774 18.774 0 0 0-6.821-20.991L247.208 1.16z" fill="#fca326"/><path d="M464.97 280.26H337.88l54.619 168.1c2.81 8.65 15.047 8.65 17.856 0z" fill="#e24329"/></g></g></g></symbol><symbol viewBox="0 0 24 24" id="go" xmlns="http://www.w3.org/2000/svg"><path d="M10.575 1.695c-2.634 0-4.756 2.453-4.756 5.502v4.6l-.027-.003v4.71c0 3.05 2.123 5.502 4.757 5.502h2.286c2.634 0 4.757-2.453 4.757-5.502v-4.6a5.1 5.1 0 0 0 .026.003v-4.71c0-3.049-2.122-5.502-4.756-5.502h-2.287z" fill="#73cddc"/><rect width="2.289" height="3.335" x="-1.178" y="6.092" ry="1.125" transform="matrix(.4849 -.87457 .85979 .51065 0 0)" fill="#73cddc"/><rect width="2.297" height="3.39" x="10.261" y="-15.076" ry="1.143" transform="matrix(.44646 .8948 -.89204 .45195 0 0)" fill="#73cddc"/><circle cx="9.267" cy="5.13" r="2.054" fill="#fff" stroke="#5e5d5b" stroke-width=".1"/><circle cx="14.214" cy="5.116" r="2.054" fill="#fff" stroke="#5e5d5b" stroke-width=".1"/><ellipse cx="8.039" cy="5.051" rx=".792" ry=".901" fill="#030d18"/><path d="M11.792 9.556l.763.138a.403.689 0 0 1 .008.138.403.689 0 0 1-.402.69.403.689 0 0 1-.404-.69.403.689 0 0 1 .035-.276z" fill="#fff" stroke="#fff" stroke-width=".155"/><ellipse cx="8.51" cy="5.365" rx=".138" ry=".166" fill="#fff"/><ellipse cx="12.945" cy="5.189" rx=".792" ry=".901" fill="#030d18"/><ellipse cx="13.414" cy="5.446" rx=".138" ry=".166" fill="#fff"/><ellipse cx="-12.982" cy="-3.409" rx=".708" ry="1.026" transform="rotate(-129.403)" fill="#f6d2a1" stroke-width=".4"/><path d="M11.772 9.553l-.757.135a.4.672 0 0 0-.008.135.4.672 0 0 0 .4.672.4.672 0 0 0 .4-.672.4.672 0 0 0-.035-.27z" fill="#fff" stroke="#fff" stroke-width=".153"/><ellipse cx="1.841" cy="-21.563" rx=".707" ry="1.026" transform="scale(1 -1) rotate(50.597)" fill="#f6d2a1" stroke-width=".4"/><ellipse cx="-17.281" cy="-21.784" rx=".864" ry="1.27" transform="matrix(.3054 -.95222 -.97065 -.2405 0 0)" fill="#f6d2a1" stroke-width=".4"/><ellipse cx="22.885" cy="2.587" rx=".864" ry="1.27" transform="matrix(.22652 .974 .95652 -.29167 0 0)" fill="#f6d2a1" stroke-width=".4"/><path d="M10.708 8.392a.594.594 0 0 0-.594.597v.115c0 .331.264.598.594.598h.386a.973.772 0 0 1 .697-.235.973.772 0 0 1 .698.235h.334c.33 0 .594-.267.594-.598V8.99a.595.595 0 0 0-.594-.597h-2.115z" fill="#f6d2a1" stroke="#657075" stroke-width=".1"/><ellipse cx="11.734" cy="8.203" rx="1.208" ry=".68" fill="#030d18" stroke="#fff" stroke-width=".162"/></symbol><symbol viewBox="0 0 24 24" id="gradle" xmlns="http://www.w3.org/2000/svg"><path d="M21.718 5.503c-.731-1.315-2.04-1.708-2.963-1.727-1.133-.023-2.065.605-1.888 1.017.037.088.25.55.38.741.19.275.527.064.646 0 .353-.187.73-.248 1.16-.198.409.048.954.3 1.319 1.001.859 1.652-1.794 5.05-5.114 2.697-3.32-2.353-6.548-1.574-8.01-1.1-1.462.475-2.135.952-1.556 2.055.785 1.498.524 1.038 1.285 2.28 1.21 1.97 3.856-.908 3.856-.908-1.972 2.906-3.662 2.204-4.31 1.188a15.864 15.864 0 0 1-1.038-1.97c-4.993 1.76-3.642 9.534-3.642 9.534h2.48c.632-2.862 2.892-2.757 3.28 0h1.892c1.673-5.59 5.914 0 5.914 0h2.466c-.69-3.812 1.388-5.01 2.697-7.246 1.31-2.235 2.551-4.969 1.146-7.364zm-6.362 7.362c-1.304-.426-.837-1.723-.837-1.723s1.139.368 2.68.87c-.09.403-.856 1.175-1.843.853z" fill="#0097a7" stroke-width=".47"/></symbol><symbol preserveAspectRatio="xMidYMid" viewBox="0 0 300 300" id="graphcool" xmlns="http://www.w3.org/2000/svg"><path d="M246.886 107.727c-12.237-6.892-27.616 2.1-30.081 3.646l-52.834 29.965c-7.8-6.196-18.914-5.933-26.412.625-7.499 6.558-9.24 17.537-4.14 26.094 5.102 8.556 15.588 12.246 24.923 8.768 9.335-3.478 14.852-13.129 13.111-22.937l52.688-29.9.321-.196c3.464-2.188 11.5-5.462 15.256-3.34 2.706 1.524 4.252 6.629 4.376 14.148h-.066v66.092a17.313 17.313 0 0 1-8.635 14.95l-75.739 43.755a17.312 17.312 0 0 1-17.261 0l-75.74-43.756a17.312 17.312 0 0 1-8.634-14.95V113.22c.01-6.165 3.3-11.86 8.634-14.95l68.549-39.562c6.522 7.482 17.451 9.25 26 4.206s12.283-15.468 8.886-24.794c-3.397-9.327-12.962-14.904-22.751-13.27-9.79 1.636-17.022 10.02-17.204 19.944L59.397 85.632a31.932 31.932 0 0 0-15.978 27.588v87.454a31.933 31.933 0 0 0 15.927 27.602l75.74 43.755a31.934 31.934 0 0 0 31.846 0l75.74-43.755a31.933 31.933 0 0 0 15.927-27.58V137.12h.05c.373-14.913-3.616-24.794-11.762-29.389z" fill="#27ae60" stroke="#27ae60" stroke-width="7.883622079999999"/></symbol><symbol viewBox="0 0 400 400" id="graphql" xmlns="http://www.w3.org/2000/svg"><path d="M67.008 293.022l-13.143-7.588L200.282 31.839l13.143 7.588z" fill="#ec407a" stroke-width="6.803" stroke="#ec407a"/><path d="M50.855 265.174H343.69v15.177H50.855z" fill="#ec407a" stroke-width="6.803" stroke="#ec407a"/><path d="M203.122 358.269L56.649 273.7l7.589-13.143 146.472 84.568zm127.24-220.407L183.889 53.293l7.589-13.143 146.472 84.568z" fill="#ec407a" stroke-width="6.803" stroke="#ec407a"/><path d="M64.278 137.803l-7.588-13.142 146.472-84.568 7.588 13.143z" fill="#ec407a" stroke-width="6.803" stroke="#ec407a"/><path d="M327.661 293.025L181.244 39.43l13.143-7.589 146.417 253.596zM62.466 114.597h15.176v169.136H62.466zm254.528 0h15.176v169.136h-15.176z" fill="#ec407a" stroke-width="6.803" stroke="#ec407a"/><path d="M200.538 351.845l-6.628-11.481L321.3 266.812l6.629 11.48z" fill="#ec407a" stroke-width="6.803" stroke="#ec407a"/><path d="M352.284 288.67c-8.777 15.268-28.342 20.48-43.61 11.703-15.268-8.777-20.48-28.342-11.703-43.61 8.777-15.268 28.342-20.48 43.61-11.703 15.36 8.869 20.57 28.342 11.703 43.61M97.574 141.567c-8.778 15.268-28.343 20.48-43.61 11.703-15.269-8.777-20.48-28.342-11.703-43.61 8.777-15.268 28.342-20.48 43.61-11.703 15.268 8.869 20.479 28.342 11.702 43.61M42.353 288.67c-8.777-15.268-3.566-34.741 11.702-43.61 15.268-8.776 34.741-3.565 43.61 11.703 8.776 15.268 3.565 34.741-11.703 43.61-15.36 8.776-34.833 3.565-43.61-11.703m254.71-147.103c-8.776-15.268-3.565-34.741 11.703-43.61 15.268-8.776 34.742-3.565 43.61 11.703 8.777 15.268 3.566 34.741-11.702 43.61-15.268 8.776-34.833 3.565-43.61-11.703m-99.745 236.608c-17.645 0-31.907-14.262-31.907-31.907s14.262-31.907 31.907-31.907 31.907 14.262 31.907 31.907c0 17.554-14.262 31.907-31.907 31.907m0-294.206c-17.645 0-31.907-14.262-31.907-31.907s14.262-31.907 31.907-31.907 31.907 14.262 31.907 31.907-14.262 31.907-31.907 31.907" fill="#ec407a" stroke-width="6.803" stroke="#ec407a"/></symbol><symbol viewBox="0 0 24 24" id="groovy" xmlns="http://www.w3.org/2000/svg"><path d="M12 1.982a10.119 10.119 0 0 0-10.12 10.12A10.119 10.119 0 0 0 12 22.22 10.119 10.119 0 0 0 22.12 12.1 10.119 10.119 0 0 0 12 1.983zm1.254 2.422c.91 0 1.647.261 2.213.78.571.518.857 1.188.857 2.013 0 .889-.319 1.673-.959 2.35-.64.677-1.376 1.015-2.207 1.015-.486 0-.89-.119-1.213-.357-.317-.238-.476-.532-.476-.88 0-.212.06-.4.181-.563.127-.164.274-.246.438-.246.159 0 .238.092.238.277 0 .164.06.29.182.38.121.09.261.136.42.136.423 0 .828-.29 1.215-.866.391-.582.587-1.202.587-1.863 0-.465-.151-.844-.453-1.135-.301-.296-.69-.445-1.166-.445-.714 0-1.406.318-2.078.953-.666.635-1.211 1.47-1.635 2.506-.417 1.031-.627 2.014-.627 2.945 0 .857.185 1.54.555 2.047.37.503.863.754 1.477.754 1.037 0 2.027-.734 2.974-2.2l1.493-.212c.185-.026.277.018.277.135 0 .053-.072.28-.215.681-.143.402-.337 1.074-.586 2.016.82-.476 1.455-1.003 1.904-1.58v.914c-.36.418-1.046.888-2.062 1.412-.212 1.407-.682 2.493-1.406 3.26-.725.772-1.54 1.16-2.444 1.16-.433 0-.775-.102-1.023-.303-.243-.2-.365-.477-.365-.832 0-.984.955-1.94 2.865-2.865.2-.714.395-1.356.586-1.928-.333.482-.817.907-1.451 1.278-.635.37-1.225.554-1.77.554-.889 0-1.628-.383-2.22-1.15-.588-.772-.881-1.748-.881-2.928 0-1.243.333-2.42 1-3.531a7.747 7.747 0 0 1 2.625-2.674c1.084-.672 2.134-1.008 3.15-1.008zM12.03 16.592c-1.375.687-2.062 1.365-2.062 2.031 0 .354.169.533.508.533.666 0 1.184-.856 1.554-2.564z" fill="#26c6da"/></symbol><symbol viewBox="0 0 24 24" id="gulp" xmlns="http://www.w3.org/2000/svg"><path d="M8.37 15.94a596.238 596.238 0 0 1-.482-4.982c.002-.042-.225-.077-.505-.077h-.508V8.95h3.966V5.198l1.871-1.124c1.14-.685 1.978-1.125 2.144-1.125.4 0 .866.506.866.939 0 .19-.057.422-.127.517-.07.095-.722.53-1.45.966l-1.321.792-.029 1.393-.028 1.393h3.972v1.932h-.98l-.495 4.983-.495 4.983H8.854l-.485-4.906z" fill="#e53935"/></symbol><symbol viewBox="0 0 24 24" id="h" xmlns="http://www.w3.org/2000/svg"><path d="M16.745 19.818h-3.007v-5.882q0-2.381-1.736-2.381-.869 0-1.438.663-.56.662-.56 1.718v5.882H6.988V4.533h3.016v6.508h.037q1.186-1.802 3.193-1.802 3.511 0 3.511 4.239z" stroke-width=".478" fill="#0277bd"/></symbol><symbol viewBox="0 0 253.6 253.6" id="hack" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-29.243 -29.515) scale(1.2301)"><path fill="#607d8b" d="M69.496 159.551v52.576l51.77-52.576zM123.507 41.523l-54.01 52.755v55.084l54.01-54.009z"/><path fill="#eceff1" d="M130.023 95.663v51.501l52.128-51.5z"/><path fill="#607d8b" d="M185.465 101.867l-55.442 55.174v55.083l55.442-55.262z"/><path fill="#ffa000" d="M73.068 154.283l50.427.09v-50.248z"/></g></symbol><symbol viewBox="0 0 300 300.00001" id="haml" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 165.6)"><path d="M78.42-132.307c-12.047-.302-26.924 5.998-26.924 5.998l49.195 99.791L74.605 85.005c23.81 20.134 50.07 10.504 50.07 10.504L136.76 9.212c1.526 1.446 3.146 2.77 4.777 3.995 5.244 3.714 10.925 6.553 16.606 8.738 5.68 2.185 11.583 3.933 17.482 5.244 3.933.874 7.645 1.53 11.578 1.967-1.748 3.933-2.84 8.083-2.621 12.672 0 .437.22.873.656 1.092h.217c4.152 2.185 8.521 3.934 13.328 5.027 4.589.874 9.615 1.312 14.422.656 5.026-.655 10.051-2.623 13.984-5.9 3.933-3.278 6.774-7.648 8.522-12.237l.219-.218v-.217l.656-5.899v-.22c2.185-1.311 4.37-2.621 6.555-4.37 2.622-2.184 5.025-4.589 6.773-7.648 1.748-3.059 2.84-6.774 2.621-10.488-.218-3.496-1.53-6.99-3.06-10.049-1.53-3.059-3.495-5.901-5.68-8.523-4.37-5.026-9.614-9.176-15.295-12.454-5.462-3.496-11.581-6.338-17.7-8.304l-2.404-.656-1.962-.655c-1.311-.437-2.406-1.092-3.498-1.53-2.185-1.31-3.717-2.622-4.809-4.37-2.185-3.278-2.403-8.301-1.31-13.545.218-1.311.656-2.623 1.093-3.934a96.064 96.064 0 0 0 1.31-4.152c.314-1.412.51-2.829.598-4.402l29.203-25.553c-2.275-8.404-27.488-17.158-27.488-17.158l-74.931 63.726-43.243-81.584c-1.553-.35-3.218-.527-4.94-.57zm107.682 73.14c-.449 2.336-.647 4.795-.647 7.258.219 3.715 1.311 7.87 3.715 11.366 2.403 3.496 5.68 6.117 8.957 7.646a29.663 29.663 0 0 0 5.027 1.967l2.623.654 2.184.438c5.68 1.53 11.142 3.714 16.168 6.554 5.025 2.84 9.833 6.337 13.766 10.27s6.992 8.959 7.43 13.984c.218 3.496-.22 6.118-1.313 8.303-1.093 2.404-2.84 4.588-4.807 6.555-.874.874-1.966 1.747-2.84 2.402a27.11 27.11 0 0 0-.654-5.898c-.219-1.093-.438-1.966-.875-3.059-.437-.874-.872-1.966-1.965-2.621-.218 0-.44-.001-.44.217-1.31 3.277-3.494 6.12-5.898 8.086-2.403 1.966-5.462 2.84-8.521 3.058-3.06.219-6.338-.436-9.616-1.31-3.277-.874-6.552-1.968-9.83-3.06l-.439-.22c-.656-.218-1.526.002-1.963.44-1.748 2.185-3.06 4.149-4.59 6.334a58.435 58.435 0 0 0-2.84 5.027c-3.933-1.53-7.649-2.841-11.582-4.37-5.462-2.186-10.925-4.37-15.95-6.991-5.245-2.404-10.268-5.246-14.638-8.524-3.15-2.363-6.062-4.845-8.185-7.681l2.404-17.172z" fill="#f4511e" stroke-width="0" stroke-linejoin="round"/></g></symbol><symbol viewBox="0 0 24 24" id="handlebars" xmlns="http://www.w3.org/2000/svg"><path d="M8.55 10.32c-2.753 0-4.202 3.48-5.793 3.48-.98 0-1.126-.677-1.126-.915 0-.332.236-.706.564-.706.59 0 .414.77.414.77s.798-.555.272-1.298c-.42-.595-1.31-.623-1.92-.17-.617.458-1.057 1.146-.853 2.287.1.551.468 1.35 1.233 1.805.764.455 1.925.566 2.335.566 2.194 0 4.342-1.633 6.639-2.322a5.513 5.513 0 0 1 1.497-.222 6.19 6.19 0 0 1 1.92.226c2.296.689 4.444 2.323 6.638 2.323.41 0 1.57-.11 2.335-.566.765-.455 1.132-1.256 1.231-1.807.204-1.14-.235-1.829-.853-2.287-.61-.453-1.497-.423-1.918.172-.526.743.27 1.297.27 1.297s-.176-.77.414-.77c.329 0 .565.373.565.705 0 .238-.147.914-1.126.914-1.592 0-3.04-3.478-5.794-3.478-2.565 0-3.076 1.177-3.462 1.718-.004.005-.005.011-.008.016-.005-.006-.007-.013-.012-.02-.386-.54-.896-1.717-3.461-1.717z" fill="#ff7043" fill-rule="evenodd" stroke-width=".3"/></symbol><symbol viewBox="0 0 300.00001 300" id="haskell" xmlns="http://www.w3.org/2000/svg"><g stroke-width="2.422"><path d="M23.928 240.5l59.94-89.852-59.94-89.855h44.955l59.94 89.855-59.94 89.852z" fill="#ef5350"/><path d="M83.869 240.5l59.94-89.852-59.94-89.855h44.955l119.88 179.71h-44.95l-37.46-56.156-37.468 56.156z" fill="#ffa726"/><path d="M228.72 188.08l-19.98-29.953h69.93v29.956h-49.95zm-29.97-44.924l-19.98-29.953h99.901v29.953z" fill="#ffee58"/></g></symbol><symbol viewBox="0 0 210 210" id="haxe" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -87)"><path fill="#f68712" stroke-width=".221" d="M42.78 191.545l63.431-63.43 63.431 63.43-63.431 63.431z"/><path d="M42.8 191.592L31.193 148.28 19.59 104.97 62.9 116.575l43.311 11.605-31.706 31.706z" fill="#fab20b" stroke-width=".266"/><path d="M105.956 128.111l-43.19-11.544-43.177-11.597 22.927.185 23.228.294 20.264 11.36z" fill="#fbc707" stroke-width=".265"/><path d="M19.59 104.97l11.596 43.176 11.545 43.19-11.303-19.948-11.36-20.263-.294-23.228z" fill="#fff200" stroke-width=".265"/><path d="M106.23 128.133l43.312-11.605 43.311-11.605-11.605 43.31-11.605 43.312-31.706-31.706z" fill="#f47216" stroke-width=".266"/><path d="M169.711 191.289l11.545-43.19 11.597-43.176-.185 22.927-.294 23.228-11.36 20.263z" fill="#f1471d" stroke-width=".265"/><path d="M192.853 104.923l-43.176 11.597-43.19 11.544 19.947-11.303 20.264-11.36 23.228-.293z" fill="#fbc707" stroke-width=".265"/><path d="M169.643 191.545l11.605 43.31 11.605 43.312-43.311-11.605-43.311-11.606 31.706-31.705z" fill="#f25c19" stroke-width=".266"/><path d="M106.487 255.025l43.19 11.544 43.176 11.598-22.927-.185-23.228-.294-20.264-11.36z" fill="#f68712" stroke-width=".265"/><path d="M192.853 278.167l-11.597-43.176-11.545-43.19 11.303 19.947 11.36 20.264.294 23.228z" fill="#f1471d" stroke-width=".265"/><path d="M106.211 254.976l-43.31 11.605-43.312 11.605 11.605-43.31L42.8 191.563l31.706 31.706z" fill="#f89c0e" stroke-width=".266"/><path d="M42.731 191.82l-11.545 43.19-11.597 43.176.185-22.927.294-23.228 11.36-20.263z" fill="#fff200" stroke-width=".265"/><path d="M19.59 278.186l43.175-11.597 43.19-11.544-19.947 11.303-20.264 11.36-23.228.293z" fill="#f25c19" stroke-width=".265"/></g></symbol><symbol viewBox="0 0 144 152" id="heroku" xmlns="http://www.w3.org/2000/svg"><path d="M118.68 13.279H26.865c-6.337 0-11.476 5.139-11.476 11.476V129.32c0 6.338 5.139 11.477 11.476 11.477h91.813c6.338 0 11.477-5.14 11.477-11.477V24.755c0-6.337-5.139-11.476-11.477-11.476zM44.08 121.669V96.165l14.346 12.752zm44.632 0v-38.08c-.063-2.976-1.496-6.551-7.97-6.551-12.966 0-27.51 6.52-27.654 6.586l-9.008 4.08V32.407h12.752v36.201c6.366-2.072 15.266-4.321 23.91-4.321 7.882 0 12.6 3.099 15.17 5.698 5.484 5.547 5.56 12.613 5.551 13.43v38.255zm3.188-68.54H79.149c5.011-6.576 8.158-13.496 9.564-20.723h12.751c-.86 7.243-3.796 14.187-9.563 20.722z" fill="#6963b9"/></symbol><symbol viewBox="0 0 24 24" id="hpp" xmlns="http://www.w3.org/2000/svg"><path d="M9.757 19.818H6.751v-5.882q0-2.381-1.737-2.381-.868 0-1.438.663-.56.662-.56 1.718v5.882H0V4.533h3.016v6.508h.037Q4.24 9.239 6.247 9.239q3.51 0 3.51 4.239z" stroke-width=".478" fill="#0277bd"/><path d="M13.073 11.448v2h-2v2h2v2h2v-2h2v-2h-2v-2zm7 0v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#0277bd"/></symbol><symbol viewBox="0 0 24 24" id="html" xmlns="http://www.w3.org/2000/svg"><path d="M12 17.56l4.07-1.13.55-6.1H9.38L9.2 8.3h7.6l.2-1.99H7l.56 6.01h6.89l-.23 2.58-2.22.6-2.22-.6-.14-1.66h-2l.29 3.19L12 17.56M4.07 3h15.86L18.5 19.2 12 21l-6.5-1.8L4.07 3z" fill="#e44d26"/></symbol><symbol viewBox="0 0 24 24" id="http" xmlns="http://www.w3.org/2000/svg"><path d="M16.046 13.784c.074-.613.13-1.225.13-1.856s-.056-1.244-.13-1.856h3.137c.148.594.241 1.215.241 1.856a7.65 7.65 0 0 1-.241 1.856m-4.78 5.16c.557-1.03.984-2.144 1.281-3.304h2.738a7.452 7.452 0 0 1-4.019 3.304m-.232-5.16H9.828a12.314 12.314 0 0 1-.149-1.856c0-.631.056-1.253.149-1.856h4.343c.084.603.149 1.225.149 1.856 0 .63-.065 1.243-.149 1.856M12 19.315c-.77-1.113-1.393-2.348-1.773-3.675h3.545c-.38 1.327-1.002 2.562-1.773 3.675m-3.712-11.1h-2.71a7.353 7.353 0 0 1 4.01-3.304c-.557 1.03-.975 2.144-1.3 3.304m-2.71 7.425h2.71c.325 1.16.743 2.274 1.3 3.304a7.433 7.433 0 0 1-4.01-3.304m-.761-1.856a7.65 7.65 0 0 1-.241-1.856c0-.64.093-1.262.241-1.856h3.137c-.074.612-.13 1.225-.13 1.856 0 .63.056 1.243.13 1.856m4.046-9.253c.77 1.114 1.393 2.357 1.773 3.684h-3.545c.38-1.327 1.002-2.57 1.773-3.684m6.422 3.684h-2.738a14.523 14.523 0 0 0-1.28-3.304 7.412 7.412 0 0 1 4.018 3.304m-6.423-5.568c-5.132 0-9.28 4.176-9.28 9.28a9.28 9.28 0 0 0 9.28 9.282 9.28 9.28 0 0 0 9.281-9.281A9.28 9.28 0 0 0 12 2.647z" fill="#e53935" stroke-width=".928"/></symbol><symbol viewBox="0 0 24 24" id="image" xmlns="http://www.w3.org/2000/svg"><path d="M13.009 9.202h5.368l-5.368-5.368v5.368M6.177 2.37h7.808l5.856 5.856v11.711a1.952 1.952 0 0 1-1.952 1.952H6.178a1.951 1.951 0 0 1-1.952-1.952V4.322c0-1.083.868-1.952 1.952-1.952m0 17.567h11.71V12.13l-3.903 3.903-1.952-1.951-5.856 5.855M8.13 9.202a1.952 1.952 0 0 0-1.952 1.952 1.952 1.952 0 0 0 1.952 1.952 1.952 1.952 0 0 0 1.952-1.952A1.952 1.952 0 0 0 8.13 9.202z" fill="#26a69a" stroke-width=".976"/></symbol><symbol viewBox="0 0 512 512" id="ionic" xmlns="http://www.w3.org/2000/svg"><g fill="#4f8ff7"><path d="M423.592 132.804A31.855 31.855 0 0 0 429 115c0-17.675-14.33-32-32-32a31.853 31.853 0 0 0-17.805 5.409C344.709 63.015 302.11 48 256 48 141.125 48 48 141.125 48 256c0 114.877 93.125 208 208 208 114.873 0 208-93.123 208-208 0-46.111-15.016-88.71-40.408-123.196zM391.83 391.832c-17.646 17.646-38.191 31.499-61.064 41.174-23.672 10.012-48.826 15.089-74.766 15.089-25.94 0-51.095-5.077-74.767-15.089-22.873-9.675-43.417-23.527-61.064-41.174s-31.5-38.191-41.174-61.064C68.982 307.096 63.905 281.94 63.905 256c0-25.94 5.077-51.095 15.089-74.767 9.674-22.873 23.527-43.417 41.174-61.064s38.191-31.5 61.064-41.174c23.673-10.013 48.828-15.09 74.768-15.09 25.939 0 51.094 5.077 74.766 15.089a191.221 191.221 0 0 1 37.802 21.327A31.853 31.853 0 0 0 365 115c0 17.675 14.327 32 32 32 5.293 0 10.28-1.293 14.678-3.568a191.085 191.085 0 0 1 21.327 37.801c10.013 23.672 15.09 48.827 15.09 74.767 0 25.939-5.077 51.096-15.09 74.768-9.675 22.873-23.527 43.418-41.175 61.064z"/><circle cx="256.003" cy="256" r="96"/></g></symbol><symbol viewBox="0 0 24 24" id="java" xmlns="http://www.w3.org/2000/svg"><path d="M2 21h18v-2H2M20 8h-2V5h2m0-2H4v10a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4v-3h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z" fill="#f44336"/></symbol><symbol viewBox="0 0 24 24" id="javascript" xmlns="http://www.w3.org/2000/svg"><path d="M3 3h18v18H3V3m4.73 15.04c.4.85 1.19 1.55 2.54 1.55 1.5 0 2.53-.8 2.53-2.55v-5.78h-1.7V17c0 .86-.35 1.08-.9 1.08-.58 0-.82-.4-1.09-.87l-1.38.83m5.98-.18c.5.98 1.51 1.73 3.09 1.73 1.6 0 2.8-.83 2.8-2.36 0-1.41-.81-2.04-2.25-2.66l-.42-.18c-.73-.31-1.04-.52-1.04-1.02 0-.41.31-.73.81-.73.48 0 .8.21 1.09.73l1.31-.87c-.55-.96-1.33-1.33-2.4-1.33-1.51 0-2.48.96-2.48 2.23 0 1.38.81 2.03 2.03 2.55l.42.18c.78.34 1.24.55 1.24 1.13 0 .48-.45.83-1.15.83-.83 0-1.31-.43-1.67-1.03l-1.38.8z" fill="#ffca28"/></symbol><symbol viewBox="0 0 24 24" id="javascript-map" xmlns="http://www.w3.org/2000/svg"><path d="M18 8v2h2v10H10v-2H8v4h14V8h-4z" fill="#ffca28"/><path d="M2.444 2.506h14.135v14.136H2.444V2.506m3.714 11.811c.315.668.935 1.218 1.995 1.218 1.178 0 1.987-.629 1.987-2.003V8.993H8.805v4.508c0 .675-.275.848-.707.848-.455 0-.644-.314-.856-.683l-1.084.651m4.697-.14c.392.769 1.185 1.358 2.426 1.358 1.257 0 2.199-.652 2.199-1.854 0-1.107-.636-1.602-1.767-2.089l-.33-.141c-.573-.243-.816-.408-.816-.801 0-.322.243-.573.636-.573.377 0 .628.165.856.573l1.028-.683c-.432-.754-1.044-1.045-1.884-1.045-1.186 0-1.948.754-1.948 1.752 0 1.083.636 1.594 1.594 2.002l.33.141c.613.267.974.432.974.888 0 .377-.354.652-.903.652-.652 0-1.029-.338-1.312-.81l-1.083.63z" fill="#ffca28"/></symbol><symbol viewBox="0 0 180 180" id="jenkins" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="gia"><path transform="scale(1 -1)" fill="#37474f" d="M.899-144.42h144.42V0H.899z"/></clipPath></defs><g transform="matrix(1.0691 0 0 -1.0691 9.4 166.143)" clip-path="url(#gia)"><g fill-rule="evenodd"><path d="M107.96 30.661l-12.506-1.876-16.883-1.876-10.943-.312-10.629.312-8.13 2.502-7.19 7.815-5.628 15.945-1.25 3.44-7.504 2.5-4.377 7.191-3.126 10.317 3.44 9.067 8.128 2.814 6.565-3.127 3.127-6.878 3.752.626 1.25 1.563-1.25 7.19-.313 9.068 1.876 12.505-.074 7.143 5.701 9.114 10.005 7.19 17.508 7.504 19.383-2.814 16.883-12.193 7.817-12.505 5.002-9.067 1.25-22.51-3.752-19.384-6.877-17.195-6.566-9.066" fill="#f0d6b7"/><path d="M97.334-23.425l-44.709-1.876v-7.503l3.752-26.262-1.876-2.19-31.264 10.63-2.19 3.752-3.126 35.328-7.19 21.26-1.563 5.002 25.01 17.195 7.818 3.127 6.877-8.441 5.94-5.315 6.88-2.188 3.125-.938L68.57 1.899l2.814-3.44 7.19 2.502-5.002-9.693 27.2-12.818-3.439-1.876" fill="#335061"/><path d="M23.238 85.687l8.128 2.814 6.566-3.127 3.127-6.878 3.751.626.938 3.751-1.876 7.19 1.876 17.197-1.563 9.379 5.627 6.565 12.193 9.692-3.44 4.69-17.194-8.442-7.191-5.627-4.064-8.754-6.253-8.442-1.876-10.005 1.251-10.63" fill="#6d6b6d"/><path d="M36.055 115.07s4.69 11.567 23.448 17.195c18.759 5.628.938 4.065.938 4.065l-20.321-7.817-7.817-7.816-3.438-6.253 7.19.626M26.676 87.875s-6.566 21.886 18.446 25.012l-.938 3.752-17.195-4.065-5.003-16.257 1.251-10.63 3.439 2.188" fill="#dcd9d8"/></g><g fill="#f7e4cd"><path d="M36.681 58.799l4.094 3.966s1.847-.214 2.16-2.402c.312-2.19 1.25-21.886 14.693-32.516 1.227-.97-10.004 1.564-10.004 1.564L37.62 45.042M94.209 64.739s.729 9.477 3.28 8.748c2.553-.729 2.553-3.28 2.553-3.28s-6.198-4.01-5.833-5.468" fill-rule="evenodd"/><path d="M120.16 99.442s-5.153-1.088-5.628-5.628c-.474-4.54 5.628-.938 6.566-.625M82.327 99.129s-6.879-.938-6.879-5.314c0-4.378 7.817-4.065 10.005-2.19"/><g fill-rule="evenodd"><path d="M39.807 78.808s-11.881 7.191-13.131.312c-1.25-6.877-4.065-11.88 1.876-19.07l-4.064 1.25-3.752 9.691-1.25 9.38 7.19 7.504 8.129-.626 4.69-3.751.312-4.69M45.435 98.504s5.315 27.512 32.203 32.827c22.136 4.375 33.765-.938 38.142-5.94 0 0-19.696 23.447-38.455 16.257-18.759-7.191-32.514-20.322-32.202-28.762.532-14.377.313-14.382.313-14.382M117.97 122.27s-9.066.312-9.38-7.817c0 0 0-1.25.625-2.5 0 0 7.192 8.129 11.568 3.751"/><path d="M78.268 111.1s-1.56 12.477-12.199 5.223c-6.878-4.69-6.252-11.255-5.002-12.505s.91-3.77 1.862-2.04c.952 1.728.638 7.356 4.078 8.918 3.439 1.564 9.077 3.31 11.26.404"/></g></g><g fill="#49728b" fill-rule="evenodd"><path d="M48.874 26.597L19.486 13.466s12.193-48.46 5.94-63.467l-4.377 1.563-.313 18.446-8.128 35.015-3.44 9.692 30.639 20.633 9.067-8.753M51.896-.206l4.17-5.087v-18.76h-5.003s-.625 13.132-.625 14.696c0 1.563.624 7.19.624 7.19M52-26.866l-14.069-.625 4.065-2.813L52-31.868"/></g><g fill-rule="evenodd"><path d="M100.15-23.739l11.567.313 2.814-28.764-11.881-1.563-2.5 30.014" fill="#335061"/><path d="M103.27-23.739l17.508.938s7.19 18.133 7.19 19.07c0 .939 6.253 26.263 6.253 26.263l-14.069 14.694-2.813 2.501-7.504-7.503V3.148l-6.565-26.887" fill="#335061"/><path d="M111.09-21.55l-10.942-2.188 1.563-8.755c4.064-1.876 10.943 3.127 10.943 3.127M111.4 33.162l21.885-16.257.626 7.503-16.57 15.32-5.94-6.566" fill="#49728b"/><path d="M62.85-85.332l-6.473 26.266-3.22 19.38-.531 14.385 29.296 1.56 18.226.003-1.658-32.83 2.814-25.324-.312-4.69-23.76-1.876-14.382 3.126" fill="#fff"/><path d="M96.083-23.426s-1.563-32.515 3.127-55.65c0 0-9.38-5.94-23.136-7.503l26.262.938 3.126 1.875-3.752 51.273-.938 10.944" fill="#dcd9d8"/><path d="M115.06-49.691l12.193 3.44 23.135 1.25 3.44 10.629-6.254 18.446-7.19.938-10.005-3.127-9.599-4.686-5.095.935-3.972-1.56" fill="#fff"/><path d="M114.84-43.435s8.128 3.751 9.38 3.438L120.78-22.8l4.065 1.563s2.814-16.257 2.814-18.133c0 0 17.507-.938 19.07-.938 0 0 3.752 7.191 2.814 14.694l3.44-10.005.312-5.628-5.002-7.503-5.627-1.25-9.38.312-3.126 4.064-10.943-1.563-3.44-1.25" fill="#dcd9d8"/></g><path d="M102.56-21.241L95.682-3.733l-7.19 10.317s1.562 4.377 3.75 4.377h7.192l6.878-2.501-.625-11.568-3.127-18.134" fill="#fff"/><path d="M103.9-15.297S95.145 1.585 95.145 4.086c0 0 1.563 3.752 3.752 2.814 2.19-.938 6.879-3.439 6.879-3.439v5.94l-10.63 2.19-7.19-.939 12.193-28.763 2.5-.313" fill="#dcd9d8" fill-rule="evenodd"/><path d="M65.664 25.968l-8.661.942-8.13 2.501v-2.814l3.972-4.38 12.506-5.627" fill="#fff"/><path d="M51.689 25.031s9.693-4.065 12.819-3.127l.311-3.748-8.752 1.872-5.316 3.752.938 1.251" fill="#dcd9d8" fill-rule="evenodd"/><path d="M115.03 9.897c-5.305.156-10.098.786-14.294 1.97.285 1.72-.249 3.408.18 4.647 1.17.843 3.13.83 4.898 1.027-1.529.752-3.677 1.049-5.44.615-.042 1.194-.578 1.934-.902 2.868 2.982 1.064 10.024 8.044 13.984 5.732 1.887-1.099 2.689-7.377 2.835-10.43.122-2.533-.23-5.088-1.261-6.43" fill="#d33833" fill-rule="evenodd"/><path d="M115.03 9.897c-5.305.156-10.098.786-14.294 1.97.285 1.72-.249 3.408.18 4.647 1.17.843 3.13.83 4.898 1.027-1.529.752-3.677 1.049-5.44.615-.042 1.194-.578 1.934-.902 2.868 2.982 1.064 10.024 8.044 13.984 5.732 1.887-1.099 2.689-7.377 2.835-10.43.122-2.533-.23-5.088-1.261-6.43z" fill="none" stroke="#d33833" stroke-width="2"/><path d="M89.66 18.569c-.014-.401-.03-.806-.047-1.21-1.656-1.089-4.33-1.076-6.148-1.99 2.68-.117 4.79-.763 6.614-1.672l-.118-3.033c-3.036-2.078-5.81-5.173-9.384-7.122-1.69-.922-7.622-3.294-9.42-2.875-1.017.236-1.109 1.499-1.516 2.689-.866 2.548-2.861 6.605-3.035 10.44-.222 4.846-.71 12.967 4.51 11.969 4.213-.804 9.113-2.745 12.375-4.527 1.993-1.09 3.146-2.436 6.17-2.669" fill="#d33833" fill-rule="evenodd"/><path d="M89.66 18.569c-.014-.401-.03-.806-.047-1.21-1.656-1.089-4.33-1.076-6.148-1.99 2.68-.117 4.79-.763 6.614-1.672l-.118-3.033c-3.036-2.078-5.81-5.173-9.384-7.122-1.69-.922-7.622-3.294-9.42-2.875-1.017.236-1.109 1.499-1.516 2.689-.866 2.548-2.861 6.605-3.035 10.44-.222 4.846-.71 12.967 4.51 11.969 4.213-.804 9.113-2.745 12.375-4.527 1.993-1.09 3.146-2.436 6.17-2.669z" fill="none" stroke="#d33833" stroke-width="2"/><path d="M92.675 12.788c-.463 2.64-.999 3.393-.792 5.695 7.04 4.693 8.361-8.061.792-5.695" fill="#d33833" fill-rule="evenodd"/><path d="M92.675 12.788c-.463 2.64-.999 3.393-.792 5.695 7.04 4.693 8.361-8.061.792-5.695z" fill="none" stroke="#d33833" stroke-width="2"/><path d="M102.87 10.649s-2.19 3.127-.626 4.065c1.564.938 3.127 0 4.065 1.563s0 2.501.313 4.377 1.877 2.189 3.44 2.501c1.562.313 5.94.938 6.565-.625l-1.876 5.627-3.752 1.25-11.88-6.877-.626-3.44v-6.877M70.041.331c-.376 4.88-.773 9.752-1.215 14.626-.662 7.279 1.748 6.009 8.057 6.009.964 0 5.933-1.15 6.289-1.876 1.705-3.483-2.851-2.709 1.964-5.335 4.065-2.216 11.246 1.346 9.603 6.273-.919 1.095-4.789.341-6.176 1.06l-7.327 3.8c-3.108 1.612-10.29 3.962-13.603 1.709-8.395-5.71.53-19.974 3.524-25.93" fill="#ef3d3a" fill-rule="evenodd"/><g fill="#231f20" fill-rule="evenodd"><path d="M78.268 111.1c-8.521 1.985-12.755-3.566-15.338-9.323-2.306.559-1.389 3.695-.806 5.294 1.525 4.194 7.672 9.778 12.694 9.02 2.161-.325 5.086-2.301 3.45-4.99M119.79 101.4l.404-.016c1.926-4 3.593-8.238 6.022-11.769-1.628-3.79-12.322-7.144-12.157-.338 2.313 1.01 6.305.206 8.356 1.497-1.186 3.254-2.897 6.024-2.625 10.626M82.63 101.29c1.827-3.35 2.422-6.868 5.019-9.4 1.17-1.14 3.444-2.529 2.316-5.698-.263-.747-2.189-2.414-3.3-2.741-4.06-1.2-13.521-.248-10.317 4.814 3.358-.157 7.871-2.18 10.38.257-1.927 3.081-5.363 9.177-4.098 12.768M118.26 67.253c-6.113-3.927-12.93-8.197-22.947-7.207-2.14 1.86-2.956 6.002-.877 8.737 1.082-1.861.402-5.284 3.419-5.799 5.684-.972 12.299 3.477 16.387 5.032 2.535 4.275-.219 5.847-2.503 8.597-4.675 5.636-10.947 12.622-10.72 21.06 1.89 1.37 2.053-2.092 2.325-2.722 2.44-5.714 8.585-13.021 13.07-17.912 1.1-1.205 2.914-2.36 3.115-3.157.582-2.315-1.513-5.09-1.27-6.63M37.668 71.387c-1.916 1.094-2.372 5.91-4.622 6.048-3.215.195-2.629-6.25-2.616-10.018-2.213 2.009-2.602 8.194-.976 11.37-1.853.91-2.68-1.003-3.708-1.677 1.32 9.595 14.036 4.45 11.922-5.723M122.15 63.257c-2.846-5.417-6.871-11.382-15.222-11.555-.17 1.75-.3 4.411.009 5.464 6.384.614 10.325 3.863 15.212 6.091M82.149 59.745c5.326-2.8 15.114-3.102 22.353-2.89.388-1.586.379-3.545.394-5.48-9.305-.463-20.307 1.84-22.747 8.37M81.136 54.523c3.683-9.247 16.341-8.182 27.016-7.927-.47-1.2-1.489-2.62-2.755-3.132-3.42-1.392-12.855-2.448-17.604.074-3.011 1.601-4.946 5.219-6.596 7.34-.797 1.024-4.765 3.64-.06 3.645"/></g><path d="M117.82 3.516c-4.322-7.402-8.457-15.005-13.585-21.534 2.15 6.32 3.07 16.9 3.394 24.965 4.498 2.105 8.349-.474 10.191-3.43" fill="#81b0c4" fill-rule="evenodd"/><g fill="#231f20" fill-rule="evenodd"><path d="M141.07-23.089c-4.839-.969-8.239-5.671-12.959-5.37 2.594 3.658 7.14 5.2 12.959 5.37M143.21-30.661c-3.944-.417-8.576-1.055-12.577-.726 1.894 2.892 9.19 1.894 12.577.726M144.58-37.19c-4.433-.096-9.942-.008-14.155.346 2.492 2.677 11.28.993 14.155-.346"/></g><g fill-rule="evenodd"><path d="M109.48-55.057c.636-5.567 2.843-11.207 2.566-17.304-2.45-.827-3.858-1.55-7.142-1.545-.232 5.181-.925 13.102-.718 18.041 1.615-.107 3.997 1.154 5.294.808" fill="#dcd9d8"/><path d="M102.33 26.985c-2.226-1.453-4.121-3.267-6.259-4.818-4.74-.235-7.327.328-10.81 3.05.057.219.407.121.42.39 5.075-2.262 11.524.92 16.648 1.378" fill="#f0d6b7"/><path d="M75.694-7.603c1.394 6.04 6.857 9.17 11.817 12.497 5.12-6.498 8.234-14.855 11.663-22.92-8.102 2.443-16.38 6.406-23.481 10.423" fill="#81b0c4"/><path d="M104.18-55.865c-.207-4.94.486-12.86.718-18.041 3.283-.004 4.691.718 7.142 1.545.276 6.096-1.93 11.737-2.566 17.304-1.298.346-3.679-.914-5.294-.808zm-51.13 28.09c2.165-19.906 5.301-36.639 11.054-54.266 12.766-3.876 28.157-4.214 39.441-.716-2.072 9.948-1.167 22.06-2.378 32.677-.912 7.98-.447 16.009-1.698 24.15-13.673 2.844-33 .665-46.418-1.845zm49.651 1.72c-.115-8.549.383-16.982 1.036-25.542 3.282.493 5.51.822 8.56 1.49-.99 8.241-.869 17.514-2.886 24.804-2.332-.023-4.385.027-6.71-.752zm16.653 1.378c-1.558.357-3.372.014-4.86-.015.7-6.969 2.397-14.659 2.995-21.974 2.342-.073 3.593 1.032 5.52 1.403.102 6.421-.562 15.268-3.655 20.586zm25.215-23.038c4.882 1.186 7.952 7.165 6.586 13.305-.916 4.127-2.548 11.898-4.295 14.538-1.29 1.953-4.79 4.51-7.584 2.72-4.545-2.91-12.552-3.755-15.867-7.278 1.662-5.534 2.178-13.135 2.864-20.146 5.678-.354 12.665 1.562 17.387-.471-3.297-1.068-7.575-1.077-10.423-2.633 2.328-1.125 7.778-.897 11.332-.035zM99.17-18.025c-3.43 8.063-6.543 16.42-11.663 22.918-4.96-3.327-10.423-6.456-11.817-12.497 7.1-4.017 15.379-7.98 23.481-10.422zm8.453 24.971c-.325-8.065-1.245-18.644-3.395-24.965 5.128 6.53 9.263 14.132 13.585 21.534-1.842 2.957-5.693 5.536-10.19 3.431zm-9.582 3.405c-1.943.21-3.592-2.233-6.117-1.177-.58-.64-1.105-1.333-1.695-1.958 5.579-6.723 8.114-16.262 12.423-24.163 2.312 7.59 2.045 15.904 2.555 24.188-3.177-.201-4.94 2.873-7.166 3.11zm-6.161 8.132c-.208-2.303.328-3.056.791-5.695 7.57-2.367 6.248 10.388-.791 5.695zm-8.394 2.755c-3.261 1.782-8.161 3.723-12.374 4.527-5.222.999-4.732-7.123-4.51-11.968.173-3.836 2.168-7.893 3.035-10.441.406-1.19.498-2.453 1.515-2.69 1.798-.418 7.73 1.954 9.42 2.875 3.575 1.95 6.348 5.045 9.384 7.123.04 1.011.078 2.021.119 3.032-1.826.91-3.935 1.555-6.615 1.673 1.818.914 4.492.901 6.148 1.989.016.405.033.81.047 1.21-3.024.234-4.176 1.58-6.17 2.67zm-31.152 5.659c-2.707-2.748 7.592-6.494 10.871-6.696-.018 1.739.991 3.378.788 4.626-3.895.684-9.013.232-11.66 2.07zm33.345-1.29c-.013-.27-.363-.172-.42-.39 3.482-2.722 6.07-3.285 10.81-3.05 2.137 1.551 4.033 3.365 6.259 4.818-5.124-.458-11.574-3.64-16.648-1.379zm30.606-9.282c-.146 3.053-.948 9.332-2.835 10.431-3.961 2.312-11.002-4.668-13.984-5.732.324-.934.86-1.674.901-2.868 1.764.434 3.912.137 5.44-.615-1.767-.198-3.727-.185-4.897-1.027-.429-1.239.105-2.927-.18-4.647 4.196-1.184 8.989-1.814 14.294-1.97 1.032 1.341 1.383 3.896 1.261 6.429zM47.777 24.24c-.85.606-6.6 8.087-7.388 7.777-10.405-4.103-20.134-11.199-28.828-17.91 8.29-17.787 11.635-39.579 12.227-60.582 9.496-4.441 17.836-10.844 30.722-11.512-1.491 10.55-2.852 19.962-3.699 29.895-3.237 1.365-7.882-.062-10.913.423-.025 3.651 4.628 1.6 5.015 4.054.292 1.858-2.56 1.998-1.631 4.923 2.368-.861 3.612-2.763 6.138-3.477 2.309 5.05-.032 13.985.3 18.205.064.792.397 4.39 2.172 3.759 1.57-.559-.09-9.569.082-13.563.157-3.68-.444-7.242 1.046-9.552a355.817 355.817 0 0 0 38.576 3.16c-2.964 1.272-6.485 2.475-10.345 4.651-2.093 1.18-8.69 3.635-9.293 5.622-.964 3.167 2.528 4.855 3.125 7.57-6.285-3.428-7.511 3.286-8.998 8.042-1.347 4.308-2.114 7.526-2.445 10.01-5.414 2.581-11.203 5.195-15.863 8.505zm63.009 6.872c8.67 4.204 10.232-15.711 6.834-22.127.525-1.914 2.331-2.646 3.069-4.366-4.838-8.667-10.211-16.756-15.148-25.32 3.672 2.286 8.917.409 13.238 2.12 1.58.624 2.722 4.24 3.918 7.133 3.29 7.958 6.743 17.99 8.28 25.586.346 1.73 1.292 5.5 1.08 7.04-.378 2.758-4.12 4.803-6.022 6.508-3.506 3.15-5.714 5.921-9.371 8.866-1.483-2.189-4.666-3.66-5.878-5.44zM27.95 107.99c-4.13-4.545-3.266-13.062-2.766-19.121 7.467 4.697 17.377-.372 17.284-8.36 3.565.094 1.332 4.452.687 7.259-2.107 9.169 3.55 19.13.256 27.516-6.395-.485-11.649-3.097-15.46-7.294zm29.558 26.38c-9.352-2.65-21.337-9.446-25.18-17.847 2.976.432 5.041 1.933 7.977 2.119 1.11.072 2.563-.466 3.838-.148 2.54.63 4.685 6.327 6.602 8.447 1.868 2.07 4.114 2.954 5.651 4.841.988.477 2.448.444 2.504 1.927-.428.457-.879.806-1.392.66zm48.681-2.493c-9.707 5.477-26.136 9.596-36.462 4.449-8.331-4.155-19.593-11.027-23.433-19.737 3.587-8.405-1.062-16.106-1.36-24.64-.157-4.54 2.139-8.504 2.315-13.446-1.228-2.025-4.978-2.275-7.574-2.136-.873 4.372-2.403 9.287-6.906 9.78-6.371.697-11.03-4.576-11.319-10.085-.342-6.48 4.978-17.22 12.517-16.475 2.913.287 3.629 3.207 6.802 3.177 1.72-3.432-2.653-4.51-3.103-6.964-.117-.634.363-3.112.642-4.274 1.37-5.658 4.422-12.982 7.427-17.29 3.814-5.464 11.307-6.288 19.37-6.823 1.44 3.101 6.743 2.846 10.2 2.035-4.143 1.64-7.993 5.617-11.185 9.137-3.665 4.039-7.378 8.371-7.566 13.65 6.927-9.61 12.65-18.003 25.246-22.23 9.53-3.196 20.662 1.465 27.986 6.608 3.039 2.137 4.853 5.529 7.013 8.634 8.082 11.626 11.854 28.219 11.024 44.303-.342 6.633-.327 13.244-2.552 17.706-2.326 4.666-10.193 8.84-14.8 4.62-.853 4.537 3.83 7.344 9.331 5.71-3.922 5.063-8.039 11.145-13.614 14.29zm18.084-149.66c7.585 3.77 21.757 10.149 26.512-.014 1.755-3.746 3.814-10.079 4.723-13.946 1.284-5.456-1.392-16.923-7-18.754-4.953-1.617-10.733-1.518-16.7-.32-.702.585-1.484 1.603-2.03 2.665-4.261.165-8.25-.229-11.615-1.98.319-3.15-1.812-3.656-3.81-4.305-1.48-5.872 2.963-13.541 1.9-18.896-.76-3.815-5.453-4.405-8.902-5.118-.113-2.12.15-3.89.386-5.683-.789-2.907-4.327-4.561-7.679-4.967-11.029-1.326-27.775-1.922-38.384 1.893-2.96 7.261-5.292 16.093-7.758 24.384-10.346-1.105-18.715 4.464-26.603 8.113-2.731 1.266-6.51 1.964-7.53 4.138-.99 2.105-.584 6.14-.83 9.95-.625 9.733-1.16 19.12-3.73 29.086-1.154 4.472-3.165 8.418-4.568 12.727C9.358 5.184 7.092 10.12 6.5 14.1c-.877 5.903 4.681 6.232 8.235 8.79 5.494 3.954 9.806 6.142 15.756 9.711 1.762 1.057 7.077 3.733 7.681 4.966 1.202 2.443-2.062 5.888-2.935 7.803-1.38 3.03-2.1 5.602-2.298 8.59-4.992.789-8.775 3.76-11.06 7.109-3.781 5.543-6.403 15.798-3.132 23.599.257.614 1.536 1.822 1.725 2.765.372 1.858-.7 4.329-.768 6.305-.343 10.14 1.716 18.875 8.541 21.932 2.771 11.038 12.688 14.71 22.032 20.195 3.493 2.05 7.343 3.36 11.32 4.824 14.263 5.25 36.15 4.261 47.987-4.692 5.02-3.797 13.044-11.813 15.914-17.617 7.58-15.323 7.042-40.931 1.74-59.571-.712-2.503-1.746-6.181-3.19-9.187-1.006-2.1-4.134-6.3-3.754-8.153.391-1.916 7.132-7.034 8.577-8.428 2.603-2.51 7.548-5.843 7.948-9.012.43-3.372-1.485-7.984-2.456-11.238-3.245-10.858-6.412-20.895-10.091-30.576" fill="#231f20"/><path d="M73.674 57.38c.411.548 2.674 1.38 5.84-.144 0 0-3.752-.626-3.44-6.881l-1.564.313s-1.615 5.672-.836 6.712" fill="#f7e4cd"/><path d="M101.09 3.617a1.72 1.72 0 1 0-3.44.001 1.72 1.72 0 0 0 3.44-.001M102.81-4.355a1.72 1.72 0 1 0-3.44 0 1.72 1.72 0 0 0 3.44 0" fill="#1d1919"/></g><g><rect transform="matrix(.8 0 0 -.8 0 144)" x="16.854" y="177.38" width="70.412" height="4.12" rx=".983" ry=".983"/><rect transform="scale(1 -1)" x="78.502" y="-2.097" width="50.037" height="3.296" rx=".786" ry=".786"/><rect transform="scale(1 -1)" x="13.483" y="-3.697" width="54.831" height="3.296" rx=".786" ry=".786"/><rect transform="scale(1 -1)" x="83.296" y="-3.697" width="45.243" height="3.296" rx=".786" ry=".786"/></g></g></symbol><symbol viewBox="0 0 24 24" id="json" xmlns="http://www.w3.org/2000/svg"><path d="M5 3h2v2H5v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5h2v2H5c-1.07-.27-2-.9-2-2v-4a2 2 0 0 0-2-2H0v-2h1a2 2 0 0 0 2-2V5a2 2 0 0 1 2-2m14 0a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h1v2h-1a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-2v-2h2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5h-2V3h2m-7 12a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m-4 0a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m8 0a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1z" fill="#fbc02d"/></symbol><symbol viewBox="0 0 50 50" id="julia" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -247)" stroke-width="5.673"><circle cx="13.497" cy="281.632" r="9.555" fill="#bc342d"/><circle cx="36.081" cy="281.632" r="9.555" fill="#864e9f"/><circle cx="24.722" cy="262.389" r="9.555" fill="#328a22"/></g></symbol><symbol viewBox="0 0 64 64" id="karma" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -233)"><path d="M38.556 288.413l-20.29-26.687 9.532-7.246 20.29 26.686h-.001.002l5.527 7.247z" fill="#359b8b" stroke-width=".173"/><path d="M35.681 241.172L24.92 255.327v-14.13H12.947v13.817l7.84 33.235h4.132v-13.147l.003.003 20.29-26.686-.008-.006 5.504-7.24H35.84v.12z" fill="#3cbeae" stroke-width=".206"/></g></symbol><symbol viewBox="0 0 24 24" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7 14a2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2m5.65-4A5.99 5.99 0 0 0 7 6a6 6 0 0 0-6 6 6 6 0 0 0 6 6 5.99 5.99 0 0 0 5.65-4H17v4h4v-4h2v-4H12.65z" fill="#26a69a"/></symbol><symbol viewBox="0 0 24 24" id="kivy" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1.89 0 0 1.89 -12.157 -11.429)" fill="#90a4ae"><path d="M7.026 8.63v4.474l1.928-1.928a.437.437 0 0 0 0-.619zM9.38 16.072v-4.474l-1.927 1.927a.437.437 0 0 0 0 .62zM18.576 10.412l-5.346.564-.017.018 2.39 2.39zM9.922 8.502s.023 3.304-.003 4.452c-.02.856.371 1.114.746 1.507.538.564 1.599 1.57 1.599 1.57a.53.53 0 0 0 .75 0l1.843-1.844a.53.53 0 0 0 0-.75z"/></g></symbol><symbol viewBox="0 0 24 24" id="kl" xmlns="http://www.w3.org/2000/svg"><defs><style>.a{fill:#3aaae1}.b{fill:#fdfeff}</style></defs><title>kl</title><path d="M12.033 1.737c-.25-.003-.5.11-.729.337C8.225 5.15 5.15 8.227 2.078 11.31c-.144.144-.229.346-.341.521v.41c.16.223.294.474.485.666a3259.51 3259.51 0 0 0 8.936 8.937c.193.192.443.325.666.486h.41c.205-.142.436-.256.609-.428 3.046-3.041 6.09-6.083 9.133-9.127.47-.47.472-1.005.006-1.472l-9.218-9.217c-.23-.23-.48-.347-.731-.35zm-1.062 4.545l1.386.832c.702.422 1.403.846 2.109 1.262a.544.544 0 0 1 .04.026l.016.013.017.013c.061.056.089.123.088.224a510.281 510.281 0 0 0 0 3.794.463.463 0 0 1-.007.094c-.015.069-.054.103-.142.109a.464.464 0 0 1-.044.002c-.045-.002-.09-.002-.136-.003-.323-.006-.648-.001-.998-.001v-.527-1.34-.671-.003l.004-.668c0-.147-.039-.231-.17-.308-.893-.528-1.78-1.066-2.67-1.6-.051-.03-.101-.065-.173-.111l.001-.003h-.001zm.362 3.39c.068-.003.119.043.173.138.085.148.174.293.264.44l.015.025c.096.154.194.31.292.47l-1.915 1.176c-.337.207-.673.417-1.014.617-.113.067-.154.143-.154.277.01.977.01 1.955.014 2.932V16H7.7V16h-.002c-.004-.053-.014-.112-.014-.17-.005-1.25-.006-2.501-.015-3.751 0-.142.045-.222.164-.294a467.13 467.13 0 0 0 3.353-2.054l.016-.01a.606.606 0 0 1 .032-.017l.016-.008a.308.308 0 0 1 .033-.013l.012-.004a.157.157 0 0 1 .028-.005l.01-.001zm5.677 3.126l.314.54.346.594v.001c-.158.094-.298.178-.438.259l-3.097 1.798c-.106.062-.189.071-.3.01l-.893-.496-1.524-.843-.895-.493c-.035-.02-.068-.044-.129-.085h.001l.137-.25.495-.902 1.446.795c.442.243.886.483 1.323.734.121.07.212.072.334 0 .894-.525 1.792-1.043 2.689-1.563.057-.034.118-.061.191-.1z" fill="#29b6f6" stroke-width=".041"/></symbol><symbol viewBox="0 0 24 24" id="kotlin" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="gpb"><stop offset="0" stop-color="#cb55c0"/><stop offset="1" stop-color="#f28e0e"/></linearGradient><linearGradient id="gpa"><stop offset="0" stop-color="#0296d8"/><stop offset="1" stop-color="#8371d9"/></linearGradient><linearGradient xlink:href="#gpa" id="gpc" x1="1.725" y1="22.67" x2="22.185" y2="1.982" gradientUnits="userSpaceOnUse" gradientTransform="translate(1.638 1.155) scale(.89324)"/><linearGradient xlink:href="#gpb" id="gpd" x1="1.869" y1="22.382" x2="22.798" y2="3.377" gradientUnits="userSpaceOnUse" gradientTransform="translate(1.638 1.155) scale(.89324)"/></defs><path d="M3.307 3.003v18.048h18.05v-.03L16.88 16.51l-4.48-4.515 4.48-4.515 4.443-4.477H3.307z" fill="url(#gpc)"/><path d="M12.538 3.003l-9.23 9.23v8.818h.083l9.032-9.032-.025-.024 4.48-4.515 4.444-4.477h-8.784z" fill="url(#gpd)"/></symbol><symbol viewBox="0 0 240 240" id="laravel" xmlns="http://www.w3.org/2000/svg"><path d="M216.05 119.036c-1.433.343-24.945 6.673-24.945 6.673l-19.227-28.622c-.537-.828-.99-1.656.359-1.849 1.345-.196 23.195-4.477 24.182-4.723.99-.245 1.837-.536 3.053 1.267 1.21 1.8 17.836 24.626 18.464 25.506.627.877-.447 1.41-1.883 1.748m-4.101 49.326c.588 1.003 1.176 1.64-.67 2.367-1.843.73-62.243 22.847-63.418 23.39-1.173.546-2.092.73-3.607-1.637-1.51-2.362-21.16-39.264-21.16-39.264l64.03-18.075c1.876-.644 2.317-.405 3.103.822 1.074 1.68 21.143 31.403 21.726 32.4m-103.7-21.087c-.78.202-37.566 9.733-39.525 10.22-1.965.485-1.965.246-2.188-.49-.226-.727-43.728-98.053-44.333-99.271-.605-1.214-.574-2.177 0-2.177.571 0 34.734-3.313 35.944-3.383 1.207-.07 1.08.205 1.526 1.033l49.025 91.818c.84 1.58 1.239 1.81-.452 2.248m94.588-59.77c-3.5-4.58-5.2-3.751-7.357-3.41-2.154.336-27.277 4.915-30.194 5.449-2.918.536-4.758 1.803-2.963 4.53 1.597 2.422 18.113 27.824 21.751 33.42l-65.663 17.066L66.18 49.832c-2.075-3.342-2.507-4.514-7.236-4.28-4.735.23-40.969 3.495-43.55 3.731-2.58.233-5.416 1.479-2.835 8.09 2.583 6.612 43.734 102.82 44.88 105.62 1.149 2.803 4.128 7.345 11.11 5.527 7.157-1.871 31.969-8.894 45.52-12.742 7.163 14.07 21.77 42.619 24.473 46.707 3.607 5.459 6.089 4.56 11.626 2.738 4.325-1.42 67.65-26.129 70.502-27.4 2.855-1.273 4.613-2.184 2.685-5.275-1.419-2.28-18.124-26.558-26.876-39.26 5.993-1.733 27.305-7.888 29.575-8.557 2.646-.779 3.008-2.19 1.572-3.94-1.436-1.755-21.293-28.72-24.79-33.296z" fill="#ff5722" stroke="#ff5722" stroke-width="8.852" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 24 24" id="less" xmlns="http://www.w3.org/2000/svg"><path d="M13.696 2.999V5h2.002v5a2 2 0 0 0 1.999 2 2 2 0 0 0-2 2v5h-2v2h2a2 2 0 0 0 2-2v-4a2 2 0 0 1 2-2h1V11h-1a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2.001zm-.03 12.766v.47a1 1 0 0 0 .03-.236 1 1 0 0 0-.03-.234zM10.566 21v-2.001H8.565v-5a2 2 0 0 0-2-2 2 2 0 0 0 2-2V5h2.001v-2H8.565a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-.999V13h1a2 2 0 0 1 2 2v3.999A2 2 0 0 0 8.564 21zm.03-12.766v-.47a1 1 0 0 0-.03.236 1 1 0 0 0 .03.234z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="lib" xmlns="http://www.w3.org/2000/svg"><path d="M19 7H9V5h10m-4 10H9v-2h6m4-2H9V9h10m1-7H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2M4 6H2v14a2 2 0 0 0 2 2h14v-2H4V6z" fill="#8bc34a"/></symbol><symbol viewBox="0 0 40 40" id="livescript" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -257)" fill="#317eac"><path stroke-width="3.299" d="M5.419 260.18h3.685v34.207H5.419z"/><path stroke-width="3.299" d="M37.074 288.197v3.685H2.867v-3.685z"/><path stroke-width="2.894" d="M29.612 265.658l2.004 2.005L7.428 291.85l-2.004-2.005z"/><path stroke-width="2.325" d="M10.73 262.471h2.835v22.08H10.73z"/><path stroke-width="2.063" d="M15.36 262.519h2.835v17.382H15.36z"/><path stroke-width="1.77" d="M19.99 262.471h2.835v12.802H19.99z"/><path stroke-width="1.422" d="M24.526 262.491h2.835v8.254h-2.835z"/><path stroke-width="1.128" d="M28.783 262.463h2.835v5.197h-2.835z"/><path stroke-width="2.325" d="M34.801 286.545v-2.835h-22.08v2.835z"/><path stroke-width="2.063" d="M34.753 281.914v-2.835H17.371v2.835z"/><path stroke-width="1.77" d="M34.801 277.284v-2.835H21.999v2.835z"/><path stroke-width="1.422" d="M34.781 272.749v-2.835h-8.254v2.835z"/><path stroke-width="1.128" d="M34.809 268.492v-2.835h-5.197v2.835z"/></g></symbol><symbol viewBox="0 0 24 24" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M12 17a2 2 0 0 0 2-2 2 2 0 0 0-2-2 2 2 0 0 0-2 2 2 2 0 0 0 2 2m6-9a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2h1V6a5 5 0 0 1 5-5 5 5 0 0 1 5 5v2h1m-6-5a3 3 0 0 0-3 3v2h6V6a3 3 0 0 0-3-3z" fill="#ffd54f"/></symbol><symbol viewBox="0 0 24 24" id="lua" xmlns="http://www.w3.org/2000/svg"><circle cx="12.203" cy="12.102" r="10.322" fill="none" stroke="#42a5f5"/><path d="M12.33 5.746a6.483 6.381 0 0 0-6.482 6.381 6.483 6.381 0 0 0 6.482 6.38 6.483 6.381 0 0 0 6.484-6.38 6.483 6.381 0 0 0-6.484-6.38zm1.86 1.916a2.329 2.292 0 0 1 2.33 2.293 2.329 2.292 0 0 1-2.33 2.291 2.329 2.292 0 0 1-2.329-2.29 2.329 2.292 0 0 1 2.328-2.294z" fill="#42a5f5" fill-rule="evenodd"/><ellipse cy="4.615" cx="19.631" rx="2.329" ry="2.292" fill="#42a5f5" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 24 24" id="markdown" xmlns="http://www.w3.org/2000/svg"><path d="M2 16V8h2l3 3 3-3h2v8h-2v-5.17l-3 3-3-3V16H2m14-8h3v4h2.5l-4 4.5-4-4.5H16V8z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" preserveAspectRatio="xMidYMid" id="markojs" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -120.96)" stroke-width=".984"><path d="M4.002 126.482c-.655 1.07-1.32 2.14-1.976 3.21-.655 1.06-1.308 2.142-1.963 3.212l.002.002-.002.002c.655 1.07 1.308 2.15 1.963 3.211.655 1.07 1.32 2.141 1.976 3.211h3.33c-.664-1.07-1.318-2.14-1.974-3.21-.653-1.069-1.307-2.145-1.961-3.214.654-1.068 1.308-2.146 1.961-3.215a601.93 601.93 0 0 1 1.974-3.209z" fill="#2196f3"/><path d="M3.999 126.482l-.002.002c.655 1.07 1.31 2.15 1.964 3.212.655 1.07 1.32 2.14 1.974 3.21h3.331c-.664-1.07-1.319-2.14-1.974-3.21-.653-1.068-1.306-2.146-1.96-3.214z" fill="#26a69a"/><path d="M15.203 126.482l.002.002c-.655 1.07-1.31 2.15-1.965 3.212-.655 1.07-1.319 2.14-1.974 3.21h-3.33c.664-1.07 1.318-2.14 1.973-3.21.654-1.069 1.307-2.146 1.961-3.214z" fill="#8bc34a"/><path d="M11.874 126.484c.664 1.07 1.318 2.14 1.974 3.21.653 1.068 1.307 2.146 1.961 3.214-.654 1.069-1.308 2.145-1.961 3.213-.656 1.07-1.31 2.14-1.974 3.21h3.33c.655-1.07 1.319-2.14 1.974-3.21.655-1.06 1.31-2.14 1.966-3.21l-.002-.003.002-.002c-.656-1.07-1.311-2.152-1.966-3.213-.655-1.07-1.319-2.138-1.974-3.209z" fill="#ffc107"/><path d="M16.74 126.482c.665 1.07 1.319 2.14 1.974 3.21.654 1.068 1.306 2.146 1.96 3.214-.654 1.069-1.306 2.145-1.96 3.213-.655 1.07-1.31 2.141-1.974 3.211h3.33c.656-1.07 1.32-2.14 1.974-3.21.655-1.062 1.31-2.141 1.966-3.212l-.002-.002.002-.002c-.655-1.07-1.31-2.152-1.966-3.213-.655-1.07-1.318-2.138-1.973-3.209z" fill="#f44336"/></g></symbol><symbol viewBox="0 0 23 24" id="mathematica" xmlns="http://www.w3.org/2000/svg"><path d="M11.512 1.523l-.073.025-.46.794-.454.763-1.217 2.09H9.29L5.435 3.5l-.1-.047h-.018v.092l.025.163v.086l.132 1.226v.082l.032.252v.082l.22 2.137v.075l.018.082v.06l-2.348.507-.04.015-.457.1-.025.01h-.042l-1.096.244-.04.007-.17.036v.082l.018.01 1.859 2.086.053.052.114.132.804.909v.005l-.053.05-.22.257-2.564 2.875-.01.007v.082l.071.006.295.075 1.697.366v.006l2.139.472h.015v.047l-.036.252v.08l-.046.412v.082l-.036.244v.082l-.045.412v.08l-.05.41v.08l-.036.244v.082l-.046.412v.082l-.05.407v.082l-.032.248V20l-.05.407v.104h.037l3.642-1.6.294-.134h.018l.177.312.539.911.015.032.854 1.465.16.262.404.695.007.022h.092l.005-.022.017-.025.56-.947.014-.042.6-1.033.316-.539.644-1.091.05.013 3.906 1.721h.035v-.085l-.138-1.32v-.082l-.032-.244v-.082l-.035-.245v-.085l-.033-.244v-.081l-.032-.245v-.082l-.032-.244v-.085l-.035-.245v-.082l-.032-.245v-.082l-.033-.244v-.085l-.025-.17v-.053l1.632-.354.043-.008.458-.107h.028v-.01l.23-.05.03-.01h.042l.382-.09.025-.01h.043l.194-.05h.033l1.015-.23.07-.007v-.064l-.015-.013-1.19-1.342-.028-.028-.197-.22-1.428-1.604v-.006l.295-.323.4-.457 2.148-2.408.015-.01v-.065l-.035-.008-1.288-.28-.372-.084-.047-.01-2.481-.544v-.045l.432-4.265v-.02h-.042l-.302.135-.01.014h-.025l-3.307 1.45-.297.135h-.015l-2.028-3.483-.099-.145-.014-.045zm-.001 1.114l1.365 2.323.34.592-.008.025-1.18 1.511-.517.66-.012-.01-.258-.335-.04-.05-1.397-1.787.03-.063 1.378-2.365.287-.491zm4.908 2.039l-.007.025-.168.225-.538.066zm-9.817.004l.053.02.677.3h-.499l-.224-.3zM16.947 5l-.123 1.248-.113-.928.226-.307zm-9.26.156l.053.024.705.309-.757-.175zm7.388.116l.02.168-1.318.403.003-.003.16-.071 1.015-.444zM9.669 6.388l.944 1.204v.01L9.483 7.2zm3.55.172l.21.682-.234.084-.089.022-.702.255.008-.022.776-.982zm-5 .836l.986.356.898.312.048.02 1.054.373.011 3.086-.362-.117-.67-.224-.081-.038-.735-.245-.77-.256-.29-.1-.011-.255-.032-1.195-.01-.287-.015-.894-.013-.297zm6.583 0l-.011.227-.028.9-.008.303-.032 1.475-.01.262-.337.117-.734.245-.77.256-.712.245-.355.117.01-3.086 1.632-.578zm.585.437l.09.735.79-.097-.915 1.302-.018.006.01-.183.018-.877zm-9.451.536l.152.22 1.447 2.049-2.607.968-.05.015-1.972-2.214-.28-.312.003-.01.115-.018.424-.1.14-.021.337-.078.042-.01zm11.146.003l3.284.713.029.01-.022.025-1.954 2.192-.277.312-.092-.036-2.564-.95.475-.681.152-.216zM6.787 8.52h.86l.036 1.258-.013-.006-.763-1.078zm1.358 2.625l.152.06.77.252.712.245.746.247.49.167-.065.092-1.723 2.334-1.015-.302-.082-.017-.035-.015-1.902-.56.938-1.22.981-1.277zm6.73 0l.033.006 1.787 2.327.132.17-.128.036-.032.014-2.196.642-.105.032-.564.17-.018-.003-1.053-1.44-.174-.239-.547-.726-.007-.018.469-.16.769-.254.713-.245.77-.252zm-7.766.305l-.007.02-.405.523-.291-.291.657-.245zm8.802 0l.043.007.578.212.714.27-.661.394-.375-.479-.03-.042-.262-.342zm-10.843.75l-.67.668.355-.397.207-.23zm12.911.016l.068.025.045.042.554.627.042.043.204.228-.255.135zm-6.473.265l.022.015 1.38 1.872.032.05.343.465.008.031-.088.117-.422.629-.047.074-.245.343-.97 1.43-.013.007-1.18-1.72-.096-.16-.493-.708-.008-.037 1.618-2.191.007-.01zm7.827 1.194l.565.633.063.082-.272-.093-.037-.013zm-15.785.148l.297.299-.637.218-.152.05.038-.058zm13.224.47l-.855.448.346.66-.185-.058-.27-.088-1.092-.348.012-.01zm-9.687.255l1.222.356-.006.007-.458.145-.443.135-.032.01-.49.157zm-2.765.048l.318.32 2.007.517-.567.18-.055.004-2.103-.469-.744-.156.007-.006zm14.966.205l.548.188v.003l-.457.1-.043.014-1.069.23zm-10.23.507l.007.227.01.347.025 1.363.025.691-.007.255-.24.107-2.863 1.255.032-.372.033-.255.017-.227.031-.256.037-.407.045-.42.018-.23.032-.251.032-.412.05-.414.013-.14 1.455-.457.003-.014.301-.098zm4.908 0l1.245.39v.014l.312.1 1.146.362.022.23.03.255.043.408.04.42.017.23.033.251.032.412.042.325.078.848-.078-.04-3.025-1.322-.004-.305.06-2.368zm-4.295.617l.015.007.067.107.6.875-.64.531-.034-1.438zm3.671 0h.008l-.005.06-.02.678-.005.214-.479-.223zm-2.888 3.605l.763.915.001.37-.017-.006-.025-.05-.464-.791-.012-.018zm1.53.61l.184.083-.343.586-.018.007.002-.532z" fill="#f44336" fill-rule="evenodd" stroke="#f44336" stroke-width=".7747499999999999" stroke-linejoin="round"/></symbol><symbol viewBox="0 0 720 720" id="matlab" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><path d="M209.247 329.98L52.368 387.638l121.325 85.822 96.752-95.804-61.198-47.674z" fill="#4db6ac" fill-rule="evenodd" stroke-width=".3"/><path d="M480.193 71.446c-13.123 1.784-9.565 1.013-28.4 16.09-18.008 14.418-69.925 100.347-97.673 129.256-24.688 25.722-34.46 12.199-60.102 33.661-25.68 21.494-65.273 64.464-65.273 64.464l63.978 47.32L394.15 222.754c23.948-32.932 23.694-37.266 36.744-71.82 6.384-16.907 17.76-29.9 27.756-45.809 12.488-19.874 30.186-34.855 21.543-33.68z" fill="#00897b" fill-rule="evenodd" stroke-width=".3"/><path d="M478.206 69.796c-31.268-.189-62.068 137.245-115.56 242.691-54.543 107.519-162.235 176.82-162.235 176.82 18.156 8.243 34.681 4.91 54.236 23.394 13.375 16.164 52.09 95.976 75.174 146.117 0 0 18.964-10.297 42.994-27.695 24.03-17.397 53.124-41.896 73.384-70.3 26.883-37.692 47.897-61.043 65.703-75.271 17.806-14.23 32.404-19.336 46.458-20.54 50.238-4.305 124.582 85.792 124.582 85.792S527.267 70.09 478.206 69.796z" fill="#ffb74d" fill-rule="evenodd" stroke-width=".3"/></symbol><symbol viewBox="0 0 24 24" id="merlin" xmlns="http://www.w3.org/2000/svg"><text style="line-height:1.25;-inkscape-font-specification:'Century Gothic Bold'" x="1.953" y="21.178" transform="scale(.99582 1.0042)" font-weight="700" font-size="30.255" font-family="Century Gothic" letter-spacing="0" word-spacing="0" fill="#42a5f5" stroke-width=".756"><tspan x="1.953" y="21.178" style="-inkscape-font-specification:'Century Gothic Bold'" font-size="22.745">M</tspan></text></symbol><symbol viewBox="0 0 192 191.99999" id="mocha" xmlns="http://www.w3.org/2000/svg"><title>Mocha Logo</title><g transform="translate(-354.75 -262.42) scale(4.835)" fill="#a1887f"><path d="M103.6 69.6c0-.5-.4-1-1-1H83.8c-.5 0-1 .4-1 1 0 3.4.5 15.1 5.5 20.8.2.2.4.3.7.3h8.4c.3 0 .5-.1.7-.3 5-5.6 5.5-17.3 5.5-20.8zm-7.4 18.2h-5.9c-.3 0-.5-.1-.7-.3-3.4-4-3.8-12-3.9-14.8 0-.5.4-1 1-1h13.2c.5 0 1 .4 1 1 0 2.8-.5 10.7-3.9 14.8-.3.2-.5.3-.8.3zM95.1 66.6s3.6-2.1 1.4-5.9c-1.3-2-1.9-3.7-1.4-4.4-1.3 1.6-3.5 3.3-1.1 6.9.8.9 1.2 2.8 1.1 3.4zM91.1 66.9s2.4-1.4.9-4c-.9-1.3-1.3-2.5-.9-2.9-.9 1.1-2.3 2.2-.7 4.7.5.5.7 1.8.7 2.2z"/><path d="M99.3 78.5c-.4 2.7-1.2 5.8-2.9 7.8-.2.2-.4.3-.6.3h-5c-.2 0-.5-.1-.6-.3-1.2-1.5-2-3.5-2.5-5.6 0 0 5.8.8 9.1-.4 2.4-.9 2.5-1.8 2.5-1.8z"/></g></symbol><symbol viewBox="0 0 24 24" id="movie" xmlns="http://www.w3.org/2000/svg"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V4h-4z" fill="#ff9800"/></symbol><symbol viewBox="0 0 24 24" id="music" xmlns="http://www.w3.org/2000/svg"><path d="M16 9V7h-4v5.5c-.42-.31-.93-.5-1.5-.5A2.5 2.5 0 0 0 8 14.5a2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5V9h3m-4-7a10 10 0 0 1 10 10 10 10 0 0 1-10 10A10 10 0 0 1 2 12 10 10 0 0 1 12 2z" fill="#ef5350"/></symbol><symbol viewBox="0 0 24 24" id="mxml" xmlns="http://www.w3.org/2000/svg"><path d="M13 9h5.5L13 3.5V9M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m.12 13.5l3.74 3.74 1.42-1.41-2.33-2.33 2.33-2.33-1.42-1.41-3.74 3.74m11.16 0l-3.74-3.74-1.42 1.41 2.33 2.33-2.33 2.33 1.42 1.41 3.74-3.74z" fill="#ffa726"/></symbol><symbol viewBox="0 0 300 300" id="ngrx-actions" xmlns="http://www.w3.org/2000/svg"><path d="M150 27.324L35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z" fill="#ab47bc" stroke-width="12.914"/></symbol><symbol viewBox="0 0 300 300" id="ngrx-effects" xmlns="http://www.w3.org/2000/svg"><path d="M150 27.324L35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z" fill="#26c6da" stroke-width="12.914"/></symbol><symbol viewBox="0 0 300 300" id="ngrx-reducer" xmlns="http://www.w3.org/2000/svg"><path d="M150 27.324L35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z" fill="#e53935" stroke-width="12.914"/></symbol><symbol viewBox="0 0 300 300" id="ngrx-state" xmlns="http://www.w3.org/2000/svg"><path d="M150 27.324L35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z" fill="#9ccc65" stroke-width="12.914"/></symbol><symbol viewBox="0 0 24 24" id="nim" xmlns="http://www.w3.org/2000/svg"><path d="M4.464 15.75L2.288 3.78l5.985 7.617L12.08 3.78l3.809 7.617 5.985-7.617-2.177 11.97H4.464m15.234 3.264a1.088 1.088 0 0 1-1.088 1.088H5.553a1.088 1.088 0 0 1-1.089-1.088v-1.089h15.234z" stroke-width="1.088" fill="#ffca28"/></symbol><symbol viewBox="0 0 500 500" id="nix" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-1.965 36.302)" stroke-width=".395"><path d="M135.59 415.7c0-.295-2.752-5.283-6.116-11.084-3.364-5.801-6.116-10.776-6.116-11.055s9.514-16.889 21.143-36.912c11.629-20.022 21.323-36.798 21.542-37.279.346-.76-1.608-4.363-14.896-27.466-8.412-14.625-15.294-26.785-15.294-27.023 0-.5 24.46-43.501 25.206-44.31.414-.45.592-.384 1.078.395.32.513 16.876 29.256 36.791 63.87 62.62 108.85 74.852 130.01 75.41 130.46.3.242.544.554.544.694 0 .14-11.836.21-26.302.154-23.023-.09-26.313-.175-26.393-.694-.11-.714-27.662-48.825-28.86-50.392-.746-.978-.906-1.035-1.426-.51-.688.696-28.954 49.323-29.49 50.733l-.365.96h-13.229c-10.896 0-13.229-.095-13.229-.538zm167.58-125.61c-.134-.216 1.188-2.863 2.938-5.882 6.924-11.944 84.291-145.75 96.491-166.88 7.143-12.371 13.142-22.465 13.333-22.433.363.062 25.861 43.105 25.861 43.655 0 .174-6.761 11.952-15.026 26.173-8.46 14.557-14.932 26.104-14.81 26.421.185.483 4.564.564 30.213.564h29.996l.958 1.48c.526.814 3.296 5.547 6.155 10.518 2.859 4.971 5.45 9.29 5.756 9.597.706.705.704.724-.16 1.572-.395.388-3.36 5.323-6.587 10.965-3.228 5.643-6.056 10.387-6.285 10.543-.23.156-19.695.171-43.256.034l-42.84-.249-.804 1.15c-.441.632-7.504 12.736-15.696 26.897l-14.892 25.747H339.03c-8.517 0-20.015.116-25.55.259-6.55.168-10.15.121-10.309-.135zM169.42 132.23c-56.373-.055-102.5-.182-102.5-.282 0-.1 5.617-10.132 12.481-22.294l12.481-22.112h30.332c27.113 0 30.332-.065 30.332-.611 0-.336-6.659-12.228-14.797-26.427-8.139-14.199-14.797-25.917-14.797-26.04 0-.123 2.682-4.853 5.96-10.51s6.003-10.578 6.055-10.934c.086-.586 1.376-.648 13.572-.648 7.413 0 13.463.143 13.446.317-.017.174.222.707.531 1.184.31.476 9.763 16.937 21.007 36.578 11.244 19.64 20.71 36.022 21.036 36.4.554.647 2.549.691 31.428.691h30.837l12.896 22.145c7.093 12.18 12.8 22.301 12.682 22.492-.118.19-4.776.303-10.352.249-5.575-.054-56.26-.143-112.63-.198z" fill="#5075c1"/><path d="M25.289 203.14c-6.098 10.563-6.69 11.711-6.225 12.078.283.224 3.18 5.044 6.44 10.712 3.261 5.668 6.017 10.355 6.124 10.417.106.061 13.585.153 29.95.204 16.367.052 29.994.23 30.285.399.472.273-1.08 3.094-14.637 26.574L62.06 289.793l12.907 21.865c7.1 12.026 12.982 21.906 13.068 21.956.086.05 23.257-39.831 51.492-88.624 11.352-19.617 21.214-36.64 30.37-52.442 23.308-40.452 30.68-53.468 30.73-54.132-1.097-.11-6.141-.187-13.006-.216-3.945-.01-7.82-.02-12.75-.002l-25.341.092-15.42 26.706c-14.256 24.693-15.445 26.663-16.278 26.86l-.024.037c-.011.003-1.62-.001-1.825 0-4.29.062-20.453.063-40.226-.01-22.632-.082-41.615-.125-42.183-.096-.568.03-1.147-.03-1.29-.132-.142-.102-3.29 5.066-6.996 11.485zm205.16-190.3c-.123.149 5.62 10.392 12.761 22.763 12.199 21.131 89.393 155.03 96.276 167 1.502 2.613 2.92 4.803 3.443 5.348.9-1.249 3.531-5.63 7.954-13.219a1342.88 1342.88 0 0 1 10.049-17.76l6.606-11.443c.692-1.403.754-1.818.653-2.117-.162-.48-6.904-12.332-14.982-26.337-8.078-14.005-14.824-25.849-14.991-26.32a.73.73 0 0 1-.009-.366l-.426-.913L359.42 72.5c3.69-6.307 6.425-11.042 9.47-16.29 9.159-15.948 12.037-21.189 11.896-21.55-.126-.324-2.7-4.83-5.72-10.017-3.021-5.185-5.845-10.148-6.275-11.026-.483-.987-.734-1.364-1.1-1.456-.054.014-.083.018-.145.035-.42.112-5.454.195-11.189.185-5.734-.01-11.22.024-12.188.073l-1.76.089-14.997 25.978c-12.824 22.212-15.084 25.964-15.595 25.883-.024-.004-.15-.189-.235-.301-.109.066-.2.09-.272.05-.255-.148-7.143-11.902-15.306-26.119l-14.36-25.016c-.115-.186-.444-.744-.457-.752-.477-.275-50.502.287-50.737.57zm-18.646 283.09c-.047.109-.026.262.042.48.329 1.05 25.338 43.735 25.772 43.985.207.119 14.178.239 31.05.266 26.651.044 30.75.152 31.234.832.308.43 9.988 17.214 21.513 37.296s21.152 36.627 21.394 36.767c.242.14 5.927.243 12.633.23 6.706-.013 12.401.099 12.657.246.132.076.382-.141.852-.795l6.008-10.406c5.234-9.065 6.62-11.684 6.294-11.888-.575-.36-15.597-26.643-23.859-41.482-3.09-5.45-5.37-9.516-5.441-9.774-.195-.712-.065-.822 1.156-.98 1.956-.252 57.397-.057 58.07.205.238.092.79-.569 2.594-3.497 1.866-3.067 5.03-8.524 11-18.866 7.22-12.505 13.044-22.784 12.942-22.843-.102-.059-.771-.051-1.489.016l-.046.001c-4.452.204-33.918.203-149.74.025-38.96-.06-69.786-.09-71.912-.072-1.121.01-2.095.076-2.66.172a.25.25 0 0 0-.062.083z" fill="#7db7e1"/></g></symbol><symbol viewBox="0 0 24 24" id="nodejs" xmlns="http://www.w3.org/2000/svg"><path d="M12 1.85c-.27 0-.55.07-.78.2l-7.44 4.3c-.48.28-.78.8-.78 1.36v8.58c0 .56.3 1.08.78 1.36l1.95 1.12c.95.46 1.27.47 1.71.47 1.4 0 2.21-.85 2.21-2.33V8.44c0-.12-.1-.22-.22-.22H8.5c-.13 0-.23.1-.23.22v8.47c0 .66-.68 1.31-1.77.76L4.45 16.5a.26.26 0 0 1-.11-.21V7.71c0-.09.04-.17.11-.21l7.44-4.29c.06-.04.16-.04.22 0l7.44 4.29c.07.04.11.12.11.21v8.58c0 .08-.04.16-.11.21l-7.44 4.29c-.06.04-.16.04-.23 0L10 19.65c-.08-.03-.16-.04-.21-.01-.53.3-.63.36-1.12.51-.12.04-.31.11.07.32l2.48 1.47c.24.14.5.21.78.21s.54-.07.78-.21l7.44-4.29c.48-.28.78-.8.78-1.36V7.71c0-.56-.3-1.08-.78-1.36l-7.44-4.3c-.23-.13-.5-.2-.78-.2M14 8c-2.12 0-3.39.89-3.39 2.39 0 1.61 1.26 2.08 3.3 2.28 2.43.24 2.62.6 2.62 1.08 0 .83-.67 1.18-2.23 1.18-1.98 0-2.4-.49-2.55-1.47a.226.226 0 0 0-.22-.18h-.96c-.12 0-.21.09-.21.22 0 1.24.68 2.74 3.94 2.74 2.35 0 3.7-.93 3.7-2.55 0-1.61-1.08-2.03-3.37-2.34-2.31-.3-2.54-.46-2.54-1 0-.45.2-1.05 1.91-1.05 1.5 0 2.09.33 2.32 1.36.02.1.11.17.21.17h.97c.05 0 .11-.02.15-.07.04-.04.07-.1.05-.16C17.56 8.82 16.38 8 14 8z" fill="#8bc34a"/></symbol><symbol viewBox="0 0 300 300" id="nodemon" xmlns="http://www.w3.org/2000/svg"><title>nodemon</title><path d="M149.868 20.62c-2.124 0-4.25.55-6.154 1.648L41.899 81.083a12.306 12.306 0 0 0-6.15 10.652v117.633a12.29 12.29 0 0 0 6.152 10.646l101.815 58.766h.001a12.282 12.282 0 0 0 12.291 0l101.84-58.766a12.29 12.29 0 0 0 6.153-10.652V91.738a12.31 12.31 0 0 0-6.146-10.652L156.015 22.27a12.302 12.302 0 0 0-6.153-1.648zM83.303 70.93s11.789 33.031 35.477 31.934l27.74-15.961a7.348 7.348 0 0 1 3.414-.99h.641a7.233 7.233 0 0 1 3.404.99l27.738 15.961c23.69 1.094 35.475-31.934 35.475-31.934 5.233 23.154 1.06 38.641-5.924 48.942l4.541 2.614h.002c2.321 1.327 3.734 3.795 3.737 6.49l-.12 95.811a3.724 3.724 0 0 1-1.855 3.227 3.624 3.624 0 0 1-3.735 0L177.1 206.971c-2.311-1.363-3.742-3.818-3.742-6.48v-44.763a7.44 7.44 0 0 0-3.737-6.465l-15.642-9.01a7.28 7.28 0 0 0-3.715-1.01 7.378 7.378 0 0 0-3.742 1.01l-15.648 9.01c-2.316 1.323-3.729 3.798-3.729 6.467v44.762c0 2.663-1.413 5.1-3.738 6.48l-36.748 21.041a3.571 3.571 0 0 1-3.71 0c-1.173-.65-1.864-1.887-1.864-3.224l-.137-95.812a7.483 7.483 0 0 1 3.74-6.49l4.541-2.615c-6.982-10.302-11.16-25.79-5.925-48.942z" fill="#8bc34a"/></symbol><symbol viewBox="0 0 990 990" id="npm" xmlns="http://www.w3.org/2000/svg"><defs><style>.hncls-1{fill:#cb3837}.cls-2{fill:#fff}</style></defs><title>n</title><path class="hncls-1" d="M113.26 876.74V113.27h763.47v763.47zm143.59-620.4v476.18h240.61V355.63h140.21v376.96h95.457V256.34z" fill="#e53935" stroke-width=".771"/></symbol><symbol id="nunjucks" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.host0{fill:#388e3c}</style><path class="host0" d="M11.2 21.1H8.1l-2.3-7.9v7.9H2.7V2.9h3.1l2.3 7.4V2.9h3.1zM21.3 19.2c0 1-.8 1.9-1.9 1.9h-4.8c-1 0-1.9-.8-1.9-1.9v-3.8l3.2-.7V18h2.3V7.2h3.1v12z"/></symbol><symbol viewBox="0 0 150 150.00001" id="ocaml" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.76136 0 0 .76136 11.616 19.98)"><path d="M83.02 101.645l.023-.062c-.035-.159-.047-.195-.024.062z" fill="none" stroke-width="1.028"/><linearGradient id="hpa" gradientUnits="userSpaceOnUse" x1="-696.735" y1="97.7" x2="-696.735" y2="142.997" gradientTransform="matrix(1.02783 0 0 1.02783 776.895 2.337)"><stop offset="0" stop-color="#f29100"/><stop offset="1" stop-color="#ec670f"/></linearGradient><path d="M82.313 138.79c-.471-1.004-1.904-3.621-2.624-4.46-1.562-1.828-1.927-1.966-2.386-4.275-.799-4.02-2.913-11.31-5.405-16.341-1.286-2.596-3.426-4.777-5.385-6.66-1.71-1.652-5.565-4.431-6.237-4.294-6.296 1.257-8.249 7.432-11.21 12.323-1.638 2.705-3.374 5.007-4.665 7.885-1.192 2.646-1.087 5.577-3.128 7.849-2.093 2.333-3.454 4.814-4.48 7.829-.194.574-.747 6.596-1.348 8.015l9.357-.659c8.719.594 6.2 3.936 19.81 3.208l21.487-.665c-.666-1.97-1.584-4.25-1.938-4.991-.599-1.248-1.352-3.69-1.848-4.763z" fill="url(#hpa)" stroke-width="1.028"/><linearGradient id="hpb" gradientUnits="userSpaceOnUse" x1="-666.972" y1="142.12" x2="-666.972" y2="142.12" gradientTransform="matrix(1.02783 0 0 1.02783 776.895 2.337)"><stop offset="0" stop-color="#f29100"/><stop offset="1" stop-color="#ec670f"/></linearGradient><linearGradient id="hpc" gradientUnits="userSpaceOnUse" x1="-675.228" y1="-1.28" x2="-675.228" y2="142.967" gradientTransform="matrix(1.02783 0 0 1.02783 776.895 2.337)"><stop offset="0" stop-color="#f29100"/><stop offset="1" stop-color="#ec670f"/></linearGradient><path d="M109.553 94.296c-1.652 1.193-4.88 4.06-11.902 5.145-3.152.487-6.1.527-9.335.365-1.584-.076-3.077-.157-4.665-.177-.936-.008-4.074-.107-3.919.193l-.349.871c.054.287.169 1.004.2 1.177.129.704.165 1.265.192 1.912.048 1.331-.11 2.719-.043 4.062.141 2.787 1.175 5.326 1.306 8.137.143 3.13 1.69 6.442 3.188 8.998.569.973 1.434 1.084 1.811 2.283.442 1.373.024 2.83.239 4.293.842 5.675 2.477 11.606 5.032 16.728.018.043.038.09.06.128 3.156-.53 6.318-1.665 10.418-2.271 7.517-1.115 17.972-.54 24.688-1.17 16.993-1.597 26.216 6.97 41.478 3.459V22.459c0-11.84-9.594-21.438-21.435-21.438H19.239C7.4 1.021-2.197 10.62-2.197 22.458v46.774c3.067-1.11 7.479-7.635 8.861-9.222 2.419-2.775 2.858-6.315 4.062-8.544 2.743-5.078 3.215-8.57 9.451-8.57 2.907 0 4.061.67 6.027 3.31 1.368 1.834 3.731 5.224 4.837 7.49 1.277 2.615 3.357 6.153 4.272 6.867.677.53 1.35.928 1.976 1.163 1.012.38 1.848-.316 2.525-.855.863-.687 1.235-2.088 2.035-3.957 1.152-2.696 2.408-5.926 3.122-7.054 1.237-1.949 1.658-4.261 2.993-5.381 1.97-1.652 4.54-1.768 5.246-1.908 3.957-.781 5.755 1.906 7.704 3.645 1.276 1.138 3.019 3.432 4.256 6.507.967 2.4 2.199 4.622 2.714 6.008.497 1.339 1.725 3.484 2.453 6.055.661 2.336 2.43 4.125 3.102 5.235 0 0 1.029 2.882 7.285 5.516 1.357.572 4.1 1.501 5.736 2.096 2.718.988 5.351.86 8.704.458 2.391 0 3.686-3.462 4.772-6.234.643-1.639 1.259-6.334 1.678-7.667.406-1.297-.544-2.3.265-3.437.946-1.327 1.508-1.399 2.054-3.129 1.172-3.704 7.95-3.89 11.761-3.89 3.176 0 2.772 3.083 8.16 2.028 3.086-.605 6.059.398 9.335 1.265 2.758.732 5.352 1.566 6.906 3.385 1.005 1.178 3.5 7.08.958 7.331.244.3.423.84.88 1.135-.566 2.226-3.03.64-4.4.355-1.845-.383-3.147.057-4.952.856-3.085 1.374-7.598 1.214-10.286 3.452-2.281 1.898-2.277 6.133-3.34 8.507-.002-.001-2.955 7.6-9.402 12.248z" fill="url(#hpc)" stroke-width="1.028"/><linearGradient id="hpd" gradientUnits="userSpaceOnUse" x1="-735.137" y1="90.833" x2="-735.137" y2="141.967" gradientTransform="matrix(1.02783 0 0 1.02783 776.895 2.337)"><stop offset="0" stop-color="#f29100"/><stop offset="1" stop-color="#ec670f"/></linearGradient><path d="M38.247 105.09c-1.467-.15-2.83-.317-4.256-.605-2.662-.536-5.57-1.06-8.193-1.688-1.592-.385-6.895-2.263-8.048-2.792-2.702-1.246-4.496-4.63-6.609-4.282-1.348.22-2.662.682-3.5 2.042-.685 1.11-.917 3.016-1.391 4.294-.55 1.485-1.5 2.87-2.331 4.284-1.53 2.595-4.282 4.941-5.468 7.469-.239.52-.45 1.101-.649 1.708V144.415a48.57 48.57 0 0 1 4.45.96c11.955 3.19 14.872 3.46 26.598 2.119l1.1-.146c.897-1.867 1.59-8.227 2.171-10.195.454-1.51 1.077-2.712 1.313-4.253.223-1.463-.02-2.858-.146-4.188-.329-3.332 2.427-4.522 3.742-7.384 1.186-2.589 1.871-5.535 2.853-8.181.941-2.54 2.41-6.13 4.918-7.408-.305-.355-5.237-.518-6.554-.65z" fill="url(#hpd)" stroke-width="1.028"/></g></symbol><symbol viewBox="0 0 24 24" id="pdf" xmlns="http://www.w3.org/2000/svg"><path d="M14 9h5.5L14 3.5V9M7 2h8l6 6v12a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m4.93 10.44c.41.9.93 1.64 1.53 2.15l.41.32c-.87.16-2.07.44-3.34.93l-.11.04.5-1.04c.45-.87.78-1.66 1.01-2.4m6.48 3.81c.18-.18.27-.41.28-.66.03-.2-.02-.39-.12-.55-.29-.47-1.04-.69-2.28-.69l-1.29.07-.87-.58c-.63-.52-1.2-1.43-1.6-2.56l.04-.14c.33-1.33.64-2.94-.02-3.6a.853.853 0 0 0-.61-.24h-.24c-.37 0-.7.39-.79.77-.37 1.33-.15 2.06.22 3.27v.01c-.25.88-.57 1.9-1.08 2.93l-.96 1.8-.89.49c-1.2.75-1.77 1.59-1.88 2.12-.04.19-.02.36.05.54l.03.05.48.31.44.11c.81 0 1.73-.95 2.97-3.07l.18-.07c1.03-.33 2.31-.56 4.03-.75 1.03.51 2.24.74 3 .74.44 0 .74-.11.91-.3m-.41-.71l.09.11c-.01.1-.04.11-.09.13h-.04l-.19.02c-.46 0-1.17-.19-1.9-.51.09-.1.13-.1.23-.1 1.4 0 1.8.25 1.9.35M8.83 17c-.65 1.19-1.24 1.85-1.69 2 .05-.38.5-1.04 1.21-1.69l.48-.31m3.02-6.91c-.23-.9-.24-1.63-.07-2.05l.07-.12.15.05c.17.24.19.56.09 1.1l-.03.16-.16.82-.05.04z" fill="#f44336"/></symbol><symbol viewBox="0 0 24 24" id="perl" xmlns="http://www.w3.org/2000/svg"><path d="M12 14c-1 0-3 1-3 2 0 2 3 2 3 2v-1a1 1 0 0 1-1-1 1 1 0 0 1 1-1v-1m0 5s-4-.5-4-2.5c0-3 3-3.75 4-3.75V11.5c-1 0-5 1.5-5 4.5 0 4 5 4 5 4v-1M10.07 7.03l1.19.53c.43-2.44 1.58-4.06 1.58-4.06-.43 1.03-.71 1.88-.89 2.55C13.16 3.55 15.61 2 15.61 2a15.916 15.916 0 0 0-2.64 3.53c1.58-1.68 3.77-2.78 3.77-2.78-2.69 1.72-3.9 4.45-4.2 5.21l.55.08c0 .52 0 1 .25 1.38C14.1 11.31 18 11.47 18 16s-4.03 6-6.17 6C9.69 22 5 21.03 5 16s4.95-5.07 5.83-7.08c.12-.38-.76-1.89-.76-1.89z" fill="#9575cd"/></symbol><symbol viewBox="0 0 24 24" id="php" xmlns="http://www.w3.org/2000/svg"><path d="M12 18.08c-6.63 0-12-2.72-12-6.08s5.37-6.08 12-6.08S24 8.64 24 12s-5.37 6.08-12 6.08m-5.19-7.95c.54 0 .91.1 1.09.31.18.2.22.56.13 1.03-.1.53-.29.87-.58 1.09-.28.22-.71.33-1.29.33h-.87l.53-2.76h.99m-3.5 5.55h1.44l.34-1.75h1.23c.54 0 .98-.06 1.33-.17.35-.12.67-.31.96-.58.24-.22.43-.46.58-.73.15-.26.26-.56.31-.88.16-.78.05-1.39-.33-1.82-.39-.44-.99-.65-1.82-.65H4.59l-1.28 6.58m7.25-8.33l-1.28 6.58h1.42l.74-3.77h1.14c.36 0 .6.06.71.18.11.12.13.34.07.66l-.57 2.93h1.45l.59-3.07c.13-.62.03-1.07-.27-1.36-.3-.27-.85-.4-1.65-.4h-1.27L12 7.35h-1.44M18 10.13c.55 0 .91.1 1.09.31.18.2.22.56.13 1.03-.1.53-.29.87-.57 1.09-.29.22-.72.33-1.3.33h-.85l.5-2.76h1m-3.5 5.55h1.44l.34-1.75h1.22c.55 0 1-.06 1.35-.17.35-.12.65-.31.95-.58.24-.22.44-.46.58-.73.15-.26.26-.56.32-.88.15-.78.04-1.39-.34-1.82-.36-.44-.99-.65-1.82-.65h-2.75l-1.29 6.58z" fill="#1E88E5"/></symbol><symbol viewBox="0 0 79 78" id="postcss" xmlns="http://www.w3.org/2000/svg"><title>postcss-logo-symbol</title><g transform="translate(5.48 5.52) scale(.85425)" fill="#e53935" fill-rule="evenodd" stroke="#e53935" stroke-width="1.519"><path d="M15.447 32.623c.106.08.29.132.106.29-.132.184-.29.342-.395.553-.105.185-.184.237-.342.106.21-.343.42-.66.63-.95zM68.342 60.24c0 .078.026.13.026.21.053-.105.053-.158.08-.21zm0 .236v-.026zm-5.368 10.277l-4.58-25.402c-.078-.025-.183-.077-.368-.13.053.105.08.184.106.263.13-.026.184-.026.236-.052 0-.026 0-.052.027-.08l4.58 25.404zm-4.737-31.12c-.026.078-.026.158-.026.237 0-.08 0-.16.028-.238zm.026.526c-.026 0-.026 0-.052-.028v.026c.028.026.028.026.054 0zm-.052.21v-.185c-.077.026-.156.026-.262.053.132.05.264.078.264.13z"/><path d="M78.71 33.967c-.052-1.028-.078-2.056-.184-3.083-.184-1.397-.368-2.82-.684-4.19-.237-1.133-.63-2.214-1.026-3.294-.5-1.265-1-2.556-1.632-3.768-1.026-1.95-2.368-3.69-3.605-5.508-.818-1.16-1.87-2.108-2.66-3.294-.447-.685-1.105-1.264-1.763-1.79-1.053-.845-2.158-1.61-3.263-2.347a32.525 32.525 0 0 0-2.58-1.634c-.71-.397-1.473-.713-2.21-1.056-.842-.395-1.658-.87-2.605-1.054-.238-.05-.448-.13-.685-.21-.605-.21-1.184-.447-1.79-.632-.92-.29-1.815-.632-2.763-.87C50.342 1 49.394.843 48.446.71 47.394.555 46.316.5 45.262.397a26.83 26.83 0 0 0-2.026-.184C42.236.16 41.21.16 40.21.134c-.5-.027-1.026-.08-1.526-.053-.763.026-1.526.105-2.29.21-.736.08-1.473.21-2.183.317-.867.105-1.735.158-2.604.264-.816.106-1.658.264-2.473.396-.29.053-.58.158-.87.21-.63.132-1.288.185-1.92.396-1.13.344-2.263.74-3.368 1.16-1.027.422-2.027.87-3 1.397-1 .552-1.948 1.21-2.895 1.844a45.325 45.325 0 0 0-2.66 1.923c-.84.66-1.63 1.397-2.394 2.135-.42.42-.763.922-1.158 1.396-.657.765-1.315 1.502-1.947 2.293-.524.66-1 1.344-1.5 2.03-.893 1.21-1.656 2.502-2.366 3.794-.29.527-.553 1.054-.816 1.58-.395.79-.816 1.555-1.184 2.372-.264.554-.474 1.16-.632 1.766-.367 1.292-.736 2.61-1.078 3.9-.316 1.16-.395 2.372-.42 3.558-.027 1.054.078 2.082.183 3.136.027.264-.13.58.184.79-.105.29-.026.45.13.5-.182.29.08.476-.024.74-.027.052.08.157.13.236 0 .08-.025.185 0 .264.028.237.133.474.133.738 0 .184.157.395.21.58.026.078 0 .21-.053.263-.158.184-.132.342.105.448.133.342.08.5.054.66.052.236-.027.315 0 .368.21.422.29.896.315 1.37 0 .106.053.212.106.343.026 0 0 .5 0 .5.13-.078.237-.104.368-.157.08.342.158.66.263.95.132.21.132.314.08.34.105.474.157.922.34 1.37 0-.5-.05-1-.13-1.475.368.132.684.263.895.263.027-.08.053-.184.08-.237-.158-.157-.29-.394-.448-.552.053.21 0 .29 0 .37-.105-.054-.237-.107-.368-.16.105-.13.21-.263.368-.42 0-.238-.13-.45-.5-.423.158-.052.316-.13.5-.184.29-.157-.026-.447-.026-.816.026-.447-.237-.895-.316-1.37-.132-.737-.105-1.844-.184-2.582-.158-.132-.29.21-.316.237.08.632.158 1.264.21 1.897-.157-.527-.263-1.107-.394-1.74-.027.185-.053.264-.053.37-.13.13-.026.29.053.474-.184-.08-.395-.052-.395-.052v.738c-.262-.264-.34-.474-.473-.66-.052-.21-.08-.42-.13-.63.05-.133 0-.212 0-.29a15.968 15.968 0 0 1-.08-.634c.026-.026-.026-.42-.026-.42.21.025.343.05.474.05-.263-.34-.08-.552.027-.763.053-.106.237-.13.29-.238.21-.395.553-.71.553-1.212 0-.237.08-.5.105-.738.053-.448.105-.896.13-1.344.054-.58 0-1.16.133-1.713.212-.92.475-1.843.764-2.766.21-.66.448-1.29.71-1.95.395-1.028.764-2.056 1.264-3.03.71-1.424 1.526-2.794 2.316-4.19.5-.87 1.026-1.687 1.58-2.53.525-.817 1.05-1.66 1.657-2.425a21.452 21.452 0 0 1 2.79-2.978c1.053-.948 2.053-1.923 3.184-2.793a32.218 32.218 0 0 1 4.685-3.005c1.343-.71 2.737-1.266 4.132-1.793.895-.342 1.868-.5 2.79-.79 1.052-.343 2.105-.5 3.21-.527.71-.027 1.395-.106 2.105-.185.632-.05 1.263-.104 1.948-.183-.08.105-.106.158-.132.21-.288.422-.604.844-.894 1.265-.237.343-.5.712-.737 1.054-.422.555-.87 1.108-1.264 1.688-.605.87-1.158 1.766-1.79 2.635-.63.843-1.315 1.634-1.973 2.45-.868 1.134-1.684 2.293-2.552 3.426-.79 1.08-1.63 2.11-2.394 3.19-.684.947-1.29 1.95-1.948 2.923-.973 1.45-1.947 2.872-2.92 4.322a271.93 271.93 0 0 1-2.316 3.294c-.053.08-.132.104-.21.157-.21.342-.21.527-.29.685-.21.395-.42.79-.658 1.16-.132.21-.316.394-.474.605-.026-.316.42-.474.21-.87-.13.212-.263.396-.394.607l-.316.63c.105.08.29.133.105.29-.08.133-.158.29-.237.423a.954.954 0 0 0 .29-.264c0 .29-.158.526-.29.763-.105.21-.368.37-.552.527.026.027.21.106.237.132.237-.08.316-.21.343-.132.08-.105.158-.184.184-.263.104-.264.262-.474.525-.58.106-.053.184-.132.263-.21.79-.818 1.606-1.608 2.316-2.478 1.106-1.345 2.106-2.74 3.16-4.11.446-.58.973-1.16 1.446-1.714.078.606.026 1.185 0 1.74-.08.974-.132 1.95-.21 2.95-.027.395 0 .79-.027 1.186 0 .105-.08.184-.08.29 0 .263.08.553.08.817-.08.975-.186 1.923-.265 2.898-.027.21.078.422.13.607-.13 1.422.16 2.925-.078 4.427.184-.29.237-.474.237-.658.025-.158 0-.316 0-.5v-.264c.025-.475.13-.975.078-1.45-.053-.527-.053-1.027.053-1.528.053-.21-.026-.474.106-.738v.395c-.026 1.5.027 3.003-.183 4.505-.027.132.08.37-.21.343-.238.474.052.817-.21 1.08-.054.053.05.29.077.448-.106.317-.106.317.052.343.026.58.08 1.106.105 1.66.42-1 .21-2.03.396-3.058.026.422.053.844.026 1.29 0 .687-.026 1.345-.052 2.03 0 .132-.027.264-.053.396-.08.37-.105.738-.237 1.08-.105.264-.052.66-.052.975v1.003c.105.448-.027.685.052.948-.08.265-.105.344-.08.423l.08.395c.527-.053.29.343.5.553-.158.212-.105.29-.105.397 0 .237-.025.448-.052.685 0 .606-.026 1.212-.026 1.792 0 .08.026.157.026.236 0 .054-.026.74-.026.74.053.078 0 .157-.08.236-.025 0-.104-3.347-.104-3.347h-.395c-.052 1.58.08 3.003-.21 4.48-.316.025-.42.078-.764.078-.816 0-1.632 0-2.448.026-.974 0-1.92.026-2.895.026-.472 0-.972.054-1.446.054-.632 0-1.29-.08-1.92-.08-.975 0-1.922.08-2.896.106-.71.026-1.42.026-2.13.053-.475.025-.95.05-1.422.104-.21.026-.395.105-.658.184-.08 0-.263-.026-.42 0-.265.053-.5.21-.765.264-.395.08-.5.184-.448.58v.263c-.026.052.58-.08.58-.08-.054 0-.08.158-.16.29.212-.08.343-.132.475-.184.395.185.737.08 1.052.16 1.026.262 2.078.37 3.13.473.685.053 1.343.08 2.027.105.973.053 1.947.106 2.92.106.816 0 1.606-.08 2.42-.08 1.13 0 2.264.052 3.395.08.237 0 .5-.028.763-.028h1.92c1.712-.052 3.422-.08 5.133-.13.975-.028 1.975-.08 2.948-.107l3-.08c1.158-.026 2.316-.026 3.448-.05.868 0 1.71-.03 2.58-.055.972-.026 1.972-.105 2.946-.157.527-.027 1.054-.08 1.58-.132.632-.052 1.29-.13 1.92-.157.948-.054 1.922-.08 2.87-.133 1.184-.078 2.368-.183 3.578-.21 1.106-.052 2.237-.026 3.343-.052.974-.027 1.948-.08 2.948-.106l1.66-.08s1.104-.026 1.657-.08c.947-.052 1.894-.157 2.842-.183.604-.027 1.21 0 1.815-.027.973-.026 1.973-.08 2.947-.08.367 0 .762.054 1.236.08-.21.185-.342.29-.5.422.105.026.21.08.316.132a.71.71 0 0 1-.42.13c-.054.133-.107.186-.16.45h.474c-.184 0-.342.237-.526.395-.21-.054-.395 0-.5.29.184.104.158.183.132.29-.316.104-.553.21-.42.552-.107.052-.238.105-.37.184-.13.21-.368.263-.316.553.106.025.21.08.29.104-.132.053-.263.132-.395.184-.473.29-.262.422-.157.554-.08.053-.158.105-.237.132.052.237.13.29.157.29a9.3 9.3 0 0 0-.395.316c-.08.237-.185.342-.29.5s-.158.37-.29.527c-.552.607-.947 1.32-1.657 1.793-.264.185-.5.422-.737.66-.474.447-.895.948-1.395 1.37a29.595 29.595 0 0 1-2.052 1.554 151.56 151.56 0 0 1-2.604 1.792c-.474.315-1 .552-1.5.842s-.974.554-1.474.843c-.316.21-.606.5-.948.66-.868.37-1.79.685-2.684 1.028-.87.37-1.5.685-2.158.922-.605.21-1.237.37-1.868.5-.21.054-.448 0-.685.027-.448.08-.895.186-1.343.238-1.158.158-2.316.264-3.473.422-.685.08-1.343.21-2.027.29-.473.026-.973-.026-1.447-.026-.342 0-.71.08-1.053.027-.552-.08-1.105-.21-1.658-.316-.13-.026-.316-.08-.42-.026-.21.106-.396-.052-.607 0-.13.027-.262-.08-.394-.08-.106-.025-.238.028-.37 0-.29-.078-.552-.183-.87-.157-.313.026-.63-.132-.97-.21-.475-.106-.92-.21-1.396-.317a2.38 2.38 0 0 1-.525-.237c-.685 0-1.133-.026-1.554-.185-.368-.13-.71-.315-1.105-.262-.104.026-.183-.026-.29-.026-.08-.106-.157-.317-.235-.317-.526.027-.842-.42-1.29-.553-.236-.08-.42-.343-.657-.422-.58-.237-1.052-.737-1.71-.816-.21-.027-.42-.132-.658-.21.08.104.13.183.21.262-.763-.37-1.473-.79-2.184-1.186-.104-.026-.183-.13-.262-.184l-.71-.474c-.395.08-.553-.08-.66-.132-.71-.5-1.525-.817-2.21-1.37-.29-.238-.63-.396-.84-.686-.37-.448-.817-.764-1.317-1.027-.394-.21-.762-.448-1.13-.685-.185-.132-.37-.29-.37-.58 0-.185-.078-.37-.315-.264-.105-.158-.21-.342-.342-.395-.316-.13-.526-.37-.763-.58s-.42-.5-.71-.605c-.527-.21-.843-.658-1.158-1.027-.738-.87-1.396-1.82-2.08-2.74-.053-.08-.158-.133-.237-.212.105.29.237.527.368.79-.262-.105-.446-.29-.604-.474-.027.027 1.815 3.057 1.815 3.057.16.237.29.475.448.712a.813.813 0 0 1-.79-.422c-.236-.42-.5-.684-1.026-.63a4.588 4.588 0 0 1-.13-.58c-.107 0-.185 0-.37-.027.37.58.685 1.08 1.027 1.66-.133-.08-.21-.132-.265-.158.473.5.815 1.133 1.42 1.45.132.605.816.895.974 1.475-.13-.027-.238-.053-.37-.08-.21-.263-.447-.526-.683-.816.052.184.13.342.236.474.316.395.606.79.974 1.133.132.134.316.187.316.424.21.105.29.13.368.13.054.16-.025.397.29.344.21.395.42.395.71.264.343.343.528.37.764.16 0 .13.026.262.026.368.105-.053.08-.132.08-.264.13.105.21.158.262.21.263.37.5.712.868 1.002.5.422.948.87 1.42 1.265.922.765 1.95 1.398 2.975 1.977 1.264.712 2.475 1.476 3.764 2.16 1.552.818 3.21 1.372 4.92 1.767.632.132 1.237.263 1.87.42.55.16 1.104.397 1.657.528.842.185 1.71.343 2.552.5.183.027.37.054.58.08.235.053.524-.053.577.027.132.21.237.104.395.078.184-.053.395-.053.605-.053.737.026 1.447.184 2.184.132.16 0 .396-.133.528.13.236-.105.368-.105.473-.13.028.236 0 .236-.05.262-.054.026-.133.053-.238.132.947.184 1.842.21 2.63 0 1.37.105 2.554-.053 3.686-.448.105.132.184.316.342.053.052-.08.184-.107.29-.133.236-.053.526-.158.736-.08.238.08.317-.13.5-.13.317 0 .606-.027.896-.08.158-.026.316-.105.5-.158a1.285 1.285 0 0 0-.58-.133c.317-.158.606-.29.896-.42-.053.078-.106.183-.21.183h.367c-.08 0-.185.237-.316.395.946-.237 1.814-.448 2.657-.66-.29-.552.315-.367.526-.684-.263.08-.526.158-.79.21.895-.447 1.816-.842 2.71-1.237-.13.158-.29.237-.525.37.158.025.263.025.342.05.42.133.316-.262.447-.5.5 0 .71-.078.947-.158.263-.08.526-.158.79-.263.42-.184.815-.42 1.236-.63.08-.028.21 0 .316 0 .29-.186.394-.344.473-.318.37.053.63-.08.736-.42.184-.133.316-.238.447-.318.578-.316 1.13-.632 1.71-.948.21 0 .316 0 .368-.027.344-.16.66-.342.975-.527a2.258 2.258 0 0 1-.263-.13c.262-.054.34-.08.5-.133.63-.74 1.5-1.24 2.157-1.82.29-.026.29-.105.29-.157.104-.132.21-.29.34-.396.58-.527 1.21-.975 1.737-1.528a37.16 37.16 0 0 0 2.184-2.374c.63-.738 1.264-1.475 1.79-2.292.737-1.133 1.368-2.293 2.026-3.48.474-.842.895-1.685 1.37-2.528.05-.08.157-.185.236-.185.71-.08 1.422-.13 2.106-.21.158-.026.342-.13.5-.21-.08-.132-.132-.29-.21-.422-.106-.16-.264-.29-.37-.45-.104-.13-.183-.29-.262-.447-.08-.13-.158-.236-.237-.37a9.7 9.7 0 0 1-.45-.894c-.026-.08-.08-.21-.052-.29.474-1.027.658-2.134 1.105-3.162.447-1.054.58-2.24.79-3.373.184-1.08.29-2.16.42-3.24.08-.764.185-1.502.21-2.266.16-1.212.106-2.346.08-3.48-.026-1-.08-2.028-.13-3.03zM12.685 66.405c-.184-.21-.342-.448-.526-.658l.08-.08c.287.317.577.633.866.976-.158-.08-.342-.132-.42-.238zm.42.238c.08-.027.16-.027.238-.053.08.132.132.29.21.448-.368-.027-.552-.185-.447-.395zm27.37 10.883v-.08c.5-.052.973-.105 1.473-.157v.077c-.5.08-.973.13-1.473.158zm6.63-.685c-.367.08-.762.133-1.13.186-.132.026-.29.158-.342-.08-.053.027-.106.027-.158.054.13.394.447.078.71.236-.58.08-1.13.132-1.684.21v-.052c.16-.026.343-.053.5-.08v-.078a7.743 7.743 0 0 0-.79-.053c-.077 0-.183.106-.262.132-.105.026-.21.053-.342.053-.447.026-.894.026-1.316.052-.027 0-.08-.026-.106-.026v-.08c1.763-.236 3.5-.473 5.263-.71.027.052.027.105.053.157-.158 0-.263.055-.395.08zm.396-.262c.606-.08 1.16-.132 1.738-.21-1.21.342-1.605.394-1.737.21zM24.58 23.374c.84-1.16 1.71-2.32 2.552-3.505.263-.345.473-.714.736-1.056.08-.106.185-.158.316-.264l-.026-.05c.105-.133.21-.24.263-.344.134-.21.213-.448.318-.685a.385.385 0 0 1 .105-.103c.37.184.37-.21.5-.343.237-.264.474-.553.684-.817.158-.21.316-.395.448-.632.026-.08-.053-.21-.08-.317h-.078c.08-.052.158-.13.237-.184.026 0 .026 0 .052-.026.158-.238.316-.475.474-.686.315-.42.657-.842 1.025-1.21-.052.13-.105.263-.158.368.027 0 .027.027.053.027.316-.422.658-.817.974-1.24-.027-.025-.053-.052-.08-.052-.13.132-.236.264-.368.396-.026-.027-.052-.053-.08-.053.265-.343.528-.685.79-1.08.053.08.106.184.21.395.107-.263.212-.447.29-.632-.078.08-.183.158-.262.238l-.08-.08.474-.71c.5-.712 1-1.45 1.5-2.162.185-.263.42-.474.58-.738.5-1 1.29-1.792 1.894-2.714.132-.184.316-.342.474-.5.13-.16.237-.106.342.026.71.896 1.42 1.818 2.13 2.714.528.66 1.054 1.29 1.554 1.976.605.844 1.184 1.687 1.79 2.53.684.975 1.368 1.95 2.026 2.95 1 1.477 1.947 2.953 2.947 4.428.737 1.08 1.474 2.135 2.184 3.215h-1.344c-1.236-.025-2.5-.13-3.736-.078-1.684.08-3.394.264-5.078.396-2.132.185-4.29.21-6.42.21-.765 0-1.528.107-2.29.16-.922.052-1.817.105-2.738.13-1.08.054-2.13.08-3.21.107-.606.026-1.237 0-1.895 0zm30.183 12.12v.238c-.026 0-.052.027-.105.027-.105-.37-.21-.766-.342-1.135-.263-.765-.553-1.53-1.027-2.214-.528-.737-1-1.5-1.528-2.265-.13-.185-.316-.343-.474-.5-.553-.607-1.106-1.24-1.816-1.687a21.485 21.485 0 0 0-3.29-1.688 7.374 7.374 0 0 1-.92-.474h.63l4.5-.08c.974-.025 1.922-.025 2.895-.078.236 0 .368.08.5.29.236.395.473.79.736 1.186.027.052.08.13.08.21 0 .58 0 1.186.026 1.766.025.606.08 1.186.104 1.792 0 .606-.053 1.238-.026 1.87.027.897.053 1.82.053 2.74zM26.447 26.67c1.237-.053 2.42-.132 3.632-.185.945-.053 1.92-.08 2.866-.132.395-.025.764-.05 1.158 0-.42.212-.842.423-1.21.686-.474.316-.92.737-1.395 1.08-.475.342-.896.764-1.29 1.212-.5.605-1.053 1.132-1.58 1.712-.37.422-.79.817-1.105 1.265-.447.58-.842 1.21-1.263 1.87.132-2.504.29-4.98.184-7.51zm17.185 25.35c-.843.21-1.71.448-2.58.553-.736.106-1.5.08-2.263.08a25.42 25.42 0 0 1-2.028-.08c-.763-.078-1.526-.157-2.263-.5-.633-.29-1.29-.553-1.92-.87-.634-.316-1.265-.684-1.74-1.264-.34-.423-.815-.765-1.236-1.134.08.316.263.58.553.764-.132.158-.316.08-.58-.343-.078.053-.157.08-.21.106.08-.185.158-.37.237-.527-.105-.21-.237-.448-.342-.66-.21-.342-.42-.71-.605-1.053-.053-.08-.053-.158-.105-.237a5.893 5.893 0 0 1-.37-.475c-.21-.315-.394-.657-.657-.974 0 .08.027.158.027.264-.027 0-.053.026-.053.026l-.554-1.344c-.026 0-.026 0-.052.026l.473 1.74c-.026 0-.052.025-.08.025-.077-.104-.156-.21-.21-.34-.052-.212-.21-.212-.34-.133-.08.053-.133.237-.106.316.185.448.395.896.606 1.344.052.158.105.29.184.448.027.053.106.105.106.184.106.21.185.42.316.606.237.316.5.632.737.948.235.316.445.66.656.975.026.053.105.053.13.08.133.395.58.684.896.526.08.606.737.817 1 1.397a11.957 11.957 0 0 1-.763-.343c-.027.026-.027.052-.054.105.316.158.632.316.92.5.265.16.528.317.765.5.316.29.685.45 1.13.554a.282.282 0 0 0-.05-.107c.736.343 1.5.712 2.078 1-2.737.054-5.658.107-8.685.16 0-.5-.026-.975-.026-1.476 0-.21.052-.395.025-.606-.08-1.21-.08-2.424-.237-3.61-.157-1.264-.157-2.503-.13-3.77.025-.683-.027-1.394-.054-2.08 0-.922 0-1.82.028-2.74 0-.132.053-.237.106-.37h.08c.025.054 0 .133.05.16.08.08.212.21.265.184.157-.106.394-.21.447-.37.13-.315.184-.658.184-.974 0-.236.106-.394.21-.553.054-.08.08-.158.133-.263-.105-.08-.21-.132-.342-.237.106-.29.08-.633.475-.79.052-.027.052-.16.08-.238.025-.213.05-.45.078-.66.052.08.08.105.13.157a.42.42 0 0 1 .054-.08c0-.104-.026-.315 0-.315.316-.053.184-.395.342-.553.025-.028-.027-.107-.027-.16 0-.052 0-.13.026-.13.367-.08.315-.475.552-.66.08-.053.105-.13.21-.263.21.368-.158.553-.184.816.446-.263.578-.895.315-1.08.105-.08.21-.184.29-.29.29-.316.604-.606.868-.922.185-.236.29-.526.474-.763.106-.132.316-.237.474-.317.474-.262.92-.552 1.21-1 .053-.053.132-.105.21-.158.08-.053.238-.053.264-.132.027-.052-.052-.184-.105-.263.104-.053.21-.158.42-.264-.08.158-.105.264-.158.37l.13.13c.238-.184.606-.394.843-.552 0-.025-.132-.13-.132-.13-.157.08-.394.21-.63.316.05-.08.05-.132.08-.158.367-.237.735-.474 1.13-.66.92-.42 1.842-.842 2.763-1.237.158-.08.37-.026.553-.026.078 0 .13 0 .21-.026.42-.132.842-.264 1.263-.37.183-.052.393-.078.58-.078.787.025 1.577.025 2.366.078.342.026.658.105.974.21a9.88 9.88 0 0 1 1.184.5c.447.24.868.502 1.29.792.763.5 1.473 1.054 2.236 1.502.737.448 1.316 1.054 1.79 1.74.58.816 1.237 1.554 1.5 2.555l.394 1.74c.08.316.264.632.185 1-.133.66-.238 1.345-.343 2.004-.052.265-.105.53-.078.79.05.82-.265 1.53-.58 2.268-.106.237-.264.475-.395.738a.798.798 0 0 0 .21.106l.237-.474c.027 0 .027 0 .053.027-.132.368-.237.764-.37 1.133-.314.817-.63 1.66-1.025 2.45-.21.448-.58.817-.842 1.24-.262.368-.473.763-.736 1.106-.237.29-.473.58-.79.79-.71.527-1.447 1.054-2.21 1.476-.473.29-1.026.448-1.552.58zm-14.027-1.4l-.026.027c-.055-.026-.134-.052-.186-.105l-.632-.95c-.052-.078-.08-.157-.052-.262.29.448.58.87.895 1.29zm16.37 3.61c1.183-.5 2.157-1.21 3.05-2.028.133-.132.264-.263.422-.37 1.106-.684 1.92-1.633 2.658-2.687.842-1.212 1.395-2.582 2.08-3.873a2.73 2.73 0 0 1 .157-.29c-.053 3.004.29 5.955.684 8.933-2.973.105-6 .21-9.052.316zm26.683-.79c-.026.053-.08.106-.105.16-.027-.054-.027-.133-.053-.24-.158.423-.5.212-.737.212-1.42.027-2.868.027-4.29.027-1.368 0-2.762 0-4.13.024-.448 0-.922.105-1.37.132-1.078.052-2.157.08-3.236.105-.08 0-.158-.13-.29-.236a1.81 1.81 0 0 1-.158.237c-.028-.052-.08-.104-.133-.183-.026.08-.053.158-.08.21H58c-.053-.368-.158-.71-.158-1.08 0-.79.08-1.58.105-2.372.027-.368 0-.71 0-1.054.106.08.185.133.29.21.052-.103.105-.182.158-.26 0 0-.053-.028-.106-.08.05-.027.104-.08.104-.106.026-.08.08-.158.08-.21 0-.185-.054-.343-.08-.5.026 0 .052 0 .08-.028l.157.79h.08c-.106-.183.236-.342-.053-.552-.026-.027.026-.185.026-.264-.08-.157-.13-.315-.21-.526.026-.026.105-.053.184-.08-.105-.052-.184-.104-.263-.13.263-.238.263-.37.026-.633.054-.025.106-.025.106-.05 0-.238 0-.475-.052-.71-.053-.266.08-.58-.316-.74a.79.79 0 0 0 .105.21s-.08.027-.158.08c-.342-.317-.13-.74-.21-1.213.184.053.316.106.447.16-.053-.186-.184-.397-.263-.634h-.107v-1.74c0 .027.184.027.29.054 0-.027.025-.053.025-.08-.08-.105-.185-.21-.29-.342l.053-.053c-.21-.262-.105-.63-.105-.71V39.4c.264.264-.13.606.264.764v-.263h-.027c-.026-.395-.026-.79-.052-1.186h-.052c-.027.054-.027.08-.054.133h-.052l.158-6.298c.263.342.552.66.736 1 .606 1.108 1.395 2.057 2.132 3.058.632.87 1.21 1.818 1.79 2.714.71 1.08 1.394 2.16 2.105 3.24a81.41 81.41 0 0 0 1.63 2.426c.5.71 1.028 1.396 1.554 2.082.446.606.92 1.212 1.367 1.818.527.738 1.053 1.475 1.58 2.187.262.368.552.737.84 1.106.16.21.396.37.554.5-.025 0-.052 0-.104-.026.08.105.13.184.184.237.29.158.316.316.158.554zM74 46.854v-.185c0 .052.026.13 0 .184zm.895-11.62c-.027 0-.184-.16-.21-.186-.027.08 0 .158-.053.264-.027-.078-.21-.052-.21-.13-.027.368.157.737.13 1.106.08-.053.395-.08.474-.158.027.026.08.052.106.052-.527.396-.395.79-.158 1.24.052.104.21.315.052.526-.052.053.027.21.053.343h.077v.05l-.237.08c-.052-.08-.367-.236-.367-.37v1.346c.263.08.263.448.368.633a.768.768 0 0 0 .107-.21l.027.024c-.027.158-.053.316-.106.475-.052.236-.105.447-.13.684 0 .026.05.08.05.105-.288.66-.13 1.396-.235 2.08-.08.5 0 1.03-.053 1.556-.054.448-.16.922-.264 1.37-.027.08-.08.105-.21.158.052-.316.026-.527-.027-.817-.028 0-.37-.184-.397-.184 0 .37.21.87.29 1.29-.08-.026-.395-.21-.42-.21-.054.316-.054.738-.08 1.08-.027.264-.263.5-.29.79 0 .16.184.264.158.528h.21c0-.526.238-1 .238-1.554h.078c.027.053.106.106.08.132-.053.29-.16.606-.132.896 0 .158.13.316.08.5-.054.16-.08.317-.107.554-.027-.132-.053-.184-.053-.263-.026 0-.263-.027-.29-.027-.026.158.185.316.158.448-.026.026-.052.026-.105.053l-.868-1.266c-.686-1-1.37-2.003-2.054-3.03a6.312 6.312 0 0 1-.475-.79 37.09 37.09 0 0 0-2.71-4.033c-.762-.974-1.37-2.03-2.08-3.055-.656-.975-1.314-1.924-1.972-2.9-.237-.315-.526-.605-.737-.948-.683-1.08-1.29-2.187-1.972-3.267-.58-.897-1.21-1.767-1.816-2.636-.21-.29-.42-.607-.632-.923a.37.37 0 0 1-.052-.182c-.053-.58-.106-1.16-.132-1.713 0-.527.053-1.054.053-1.608v-.474c0-.132.025-.237.025-.37.025-.025.052-.078.078-.104-.763 0-1.553-.028-2.316 0-.5.025-.763-.186-1.105-.555-1-1.133-1.737-2.424-2.605-3.636a162.42 162.42 0 0 0-2.5-3.427c-.685-.922-1.37-1.818-2.053-2.74-.764-1.054-1.5-2.108-2.29-3.162a381.983 381.983 0 0 0-2.895-3.794c-.45-.58-.95-1.133-1.45-1.74.343.054.66.106.975.133l1.264.08c.947.077 1.894.13 2.84.26.79.107 1.58.265 2.396.396 1.738.29 3.448.765 5.106 1.318.974.316 1.92.738 2.87 1.133 2.13.87 4.157 1.924 6.157 3.03.63.343 1 .896 1.472 1.397.685.712 1.37 1.423 2.027 2.16.762.87 1.472 1.766 2.21 2.662.657.79 1.34 1.58 2 2.372.21.237.37.527.552.79.42.633.895 1.24 1.263 1.924.262.502.42 1.082.604 1.635.262.817.526 1.607.79 2.424.183.606.34 1.24.472 1.87.106.423.08.87.21 1.29.16.556 0 1.16.16 1.715.025.053.05.132.078.185.105.104.184.21.026.368-.025.026-.025.13 0 .21.054-.052.08-.105.133-.184 0 .053.025.08.025.105 0 .104-.027.21 0 .315 0 .052.052.13.078.184.053-.054.105-.08.21-.16.237.897.264 1.793.264 2.715 0 .87.157 1.74-.21 2.583.078-.29-.106-.555-.027-.818z"/><path d="M58.08 45.482c.025 0 .052.027.052.027l-.027-.03c0-.025 0-.025-.026 0zm4.157 26.036c-.29.21-.58.395-.948.474-.028-.026-.028-.053-.054-.08.29-.184.605-.368.895-.553.027.05.08.104.106.157zM12.895 35.81c.29-.367.58-.736.894-1.105.025.026.235.08.262.105-.29.37-.685.87-.974 1.265-.054-.053-.133-.237-.185-.264zM5.42 48.725c-.21-.448-.42-.923-.63-1.37a.91.91 0 0 1 .236-.106c.29.42.42.92.632 1.37 0 0-.21.105-.237.105zm6.712-12.65c-.158.238-.316.502-.474.74-.026-.028-.316.104-.342.078.158-.237.552-.66.71-.896.027.026.053.053.106.08zM59.422 72.6c.025 0 .025-.026.052-.026.184.026.394.052.605.052-.344.237-.555.21-.66-.026zm-47.24-35.418c.028-.08.08-.158.133-.237.052 0 .13-.027.13-.027.107-.184.107-.316.212-.474-.026-.026-.053-.026-.08-.053-.157.108-.315.24-.473.345.053.052.053.08.053.132-.21-.027-.29.08-.395.368-.026.08-.158.106-.29.21-.026.054-.052.186-.105.317l.027.028c-.053.053-.132.08-.132.08-.158.157-.342.29-.5.447-.026.08-.052.158-.052.237.185-.184.5-.527.737-.738l.027.027c.105-.158.184-.316.29-.474.025.026.025.052.052.08-.08.21-.158.446-.237.657-.055.026-.134.08-.134.053-.105.08-.184.184-.29.263l-.473.316c-.263.237-.526.447-.816.685-.184.29-.368.553-.58.896.317-.08.396.053.37.317.368.052.395-.237.5-.448.026-.054.053-.16.105-.186.237-.21.5-.394.763-.605.053-.053.053-.16.053-.238 0-.026-.133-.026-.212-.053.237-.264.58-.71.816-1 .132-.08.263-.186.263-.265-.026-.29.158-.368.37-.474-.106-.08-.133-.157-.133-.183z"/><path d="M12.71 36.892c-.105.184-.21.342-.315.527l-.158-.08c-.105.605-.474 1.132-.842 1.237.105.053.21.106.29.08.078-.027.13-.16.183-.238l.71-1.028.238-.396-.105-.105zM3.948 48.46c.132 0 .264.026.42.026 0-.105.133-.08.133-.184h.08c0 .132.026.237.026.37h-.552c-.027-.027-.132-.186-.106-.212zm-.21-1.212c-.08-.08-.21-.158-.21-.237-.027-.104.052-.235.13-.367.054.184.08.342.132.527-.027.025-.053.052-.053.078zm.658-1.687c.105.266.21.556.316.82a.798.798 0 0 0-.21.105c-.105-.264-.237-.554-.342-.817a.652.652 0 0 1 .237-.106zm58.58 25.194c.13-.052.288-.08.5-.13-.238.183-.422.315-.58.473-.027-.026-.053-.053-.08-.053.053-.105.106-.184.16-.29zM30.63 15.074c.157-.106.29-.185.447-.29l.052.052c-.16.21-.29.42-.475.685-.026-.183-.026-.29-.053-.42-.026 0 0 0 .027-.026zm7.71 13.333c.237-.106.474-.21.763-.343-.026.158-.026.264-.026.37a.927.927 0 0 0-.264-.054c-.158.027-.448.238-.58.264-.025 0 .106-.21.106-.237zm19.74 22.346c.052.263.552.395.052.658.08.055.157.08.236.134a.2.2 0 0 1-.052.106c-.053.025-.158.078-.21.05-.027 0-.08-.104-.08-.157 0-.237.027-.474.053-.79z"/></g></symbol><symbol viewBox="0 0 24 24" id="powerpoint" xmlns="http://www.w3.org/2000/svg"><path d="M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5L13 3.5M8 11v2h1v6H8v1h4v-1h-1v-2h2a3 3 0 0 0 3-3 3 3 0 0 0-3-3H8m5 2a1 1 0 0 1 1 1 1 1 0 0 1-1 1h-2v-2h2z" fill="#d14524"/></symbol><symbol viewBox="0 0 67.47 70" id="powershell" xmlns="http://www.w3.org/2000/svg"><path d="M18.545 12.4c-3.014 0-6.08 2.34-6.873 5.248L1.91 53.438c-.793 2.908.996 5.248 4.01 5.248h42.887c3.014 0 6.08-2.34 6.873-5.248l9.761-35.79c.794-2.908-.993-5.248-4.007-5.248h-42.89zm4.848 6.243c.652.04 1.29.33 1.76.86l7.96 9.013-3.957 3.246 3.957-3.244 4.832 5.47c.037.042.06.088.094.131.026.034.057.06.082.096.02.028.032.057.05.086.057.087.105.176.15.267.028.06.055.117.08.178a2.546 2.546 0 0 1 .171.764c.005.073.01.146.008.219-.002.09-.01.178-.021.267a2.53 2.53 0 0 1-.036.217 2.56 2.56 0 0 1-.07.252c-.024.076-.048.15-.08.224a2.547 2.547 0 0 1-.111.22 2.503 2.503 0 0 1-.133.218 2.546 2.546 0 0 1-.147.187c-.058.07-.118.137-.185.202-.027.026-.048.057-.076.082-.037.032-.077.054-.116.084-.038.03-.07.065-.11.093L16.8 52.271a2.552 2.552 0 0 1-3.563-.626 2.553 2.553 0 0 1 .63-3.563l18.349-12.853-3.06-3.467-7.839-8.873a2.549 2.549 0 0 1 .225-3.608 2.546 2.546 0 0 1 1.85-.638zm22.441 28.214c1.377 0 2.255 1.083 1.969 2.43-.287 1.347-1.627 2.433-3.004 2.434l-9.957.006c-1.378 0-2.256-1.083-1.969-2.43.287-1.347 1.626-2.433 3.004-2.434l9.957-.006z" fill="#03a9f4" stroke-width="5.342" stroke-linejoin="round"/></symbol><symbol viewBox="0 0 210 210" id="prettier" xmlns="http://www.w3.org/2000/svg"><title>prettier-icon-dark</title><g transform="matrix(.9 0 0 .9 10.5 10.5)" fill="none" fill-rule="evenodd"><rect fill="#56B3B4" x="165" y="40" width="20" height="10" rx="5"/><rect fill="#EA5E5E" x="15" y="200" width="60" height="10" rx="5"/><rect fill="#BF85BF" x="135" y="120" width="40" height="10" rx="5"/><rect fill="#EA5E5E" x="75" y="120" width="50" height="10" rx="5"/><rect fill="#56B3B4" x="15" y="120" width="50" height="10" rx="5"/><rect fill="#BF85BF" x="15" y="160" width="60" height="10" rx="5"/><rect fill="#BF85BF" x="15" y="80" width="60" height="10" rx="5"/><rect fill="#F7BA3E" x="65" y="20" width="110" height="10" rx="5"/><rect fill="#EA5E5E" x="15" y="20" width="40" height="10" rx="5"/><rect fill="#F7BA3E" x="55" y="180" width="20" height="10" rx="5"/><rect fill="#56B3B4" x="55" y="60" width="20" height="10" rx="5"/><rect fill="#56B3B4" x="15" y="180" width="30" height="10" rx="5"/><rect fill="#F7BA3E" x="15" y="60" width="30" height="10" rx="5"/><rect fill="#56B3B4" x="95" y="100" width="90" height="10" rx="5"/><rect fill="#F7BA3E" x="45" y="100" width="40" height="10" rx="5"/><rect fill="#EA5E5E" x="15" y="100" width="20" height="10" rx="5"/><rect fill="#BF85BF" x="105" y="40" width="50" height="10" rx="5"/><rect fill="#56B3B4" x="15" y="40" width="80" height="10" rx="5"/><rect fill="#F7BA3E" x="45" y="140" width="100" height="10" rx="5"/><rect fill="#BF85BF" x="15" y="140" width="20" height="10" rx="5"/><rect fill="#EA5E5E" x="135" y="60" width="60" height="10" rx="5"/><rect fill="#F7BA3E" x="135" y="80" width="60" height="10" rx="5"/><rect fill="#56B3B4" x="15" width="130" height="10" rx="5"/></g></symbol><symbol viewBox="0 0 80 80" id="protractor" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="hxa"><path transform="scale(1 -1)" fill="#564b55" stroke-width="27.224" d="M-2.983-69.251h69.412v67.108H-2.983z"/></clipPath></defs><g transform="matrix(1.13039 0 0 -1.13039 5.714 82.137)" clip-path="url(#hxa)"><g transform="scale(.1)"><path d="M1180.54 92.324c-5.53 0-9.93-1.797-13.23-5.39-3.29-3.614-5.22-8.594-5.81-14.97h36.02c0 6.583-1.47 11.622-4.4 15.126-2.93 3.496-7.12 5.234-12.58 5.234zm2.84-62.656c-10.19 0-18.22 3.086-24.11 9.297-5.88 6.21-8.83 14.824-8.83 25.84 0 11.101 2.73 19.922 8.21 26.464 5.45 6.524 12.81 9.805 22.02 9.805 8.63 0 15.46-2.851 20.48-8.523 5.03-5.676 7.55-13.157 7.55-22.461v-6.613h-47.45c.21-8.086 2.26-14.22 6.12-18.418 3.89-4.18 9.34-6.29 16.38-6.29 7.42 0 14.76 1.563 22 4.669V34.14c-3.68-1.602-7.18-2.746-10.48-3.438-3.28-.684-7.24-1.035-11.89-1.035M1272.34 30.918v44.57c0 5.606-1.28 9.805-3.82 12.559-2.56 2.773-6.56 4.16-12.02 4.16-7.2 0-12.49-1.953-15.84-5.851-3.34-3.895-5.03-10.32-5.03-19.286V30.918h-10.42v68.887h8.47l1.71-9.422h.5c2.14 3.387 5.14 6.023 8.99 7.887 3.85 1.867 8.15 2.804 12.88 2.804 8.29 0 14.54-2.011 18.73-6.015 4.19-3.985 6.28-10.391 6.28-19.192V30.918h-10.43M1328.96 38.406c7.1 0 12.27 1.938 15.48 5.813 3.22 3.879 4.81 10.129 4.81 18.758v2.199c0 9.765-1.62 16.726-4.87 20.898-3.25 4.18-8.44 6.25-15.56 6.25-6.11 0-10.79-2.383-14.04-7.129-3.26-4.746-4.88-11.472-4.88-20.136 0-8.797 1.61-15.45 4.84-19.93 3.23-4.484 7.97-6.723 14.22-6.723zm20.85 1.762h-.56c-4.83-7.004-12.02-10.5-21.62-10.5-9.01 0-16.03 3.066-21.04 9.238-5 6.153-7.5 14.922-7.5 26.27 0 11.355 2.51 20.176 7.54 26.465 5.03 6.289 12.03 9.433 21 9.433 9.34 0 16.5-3.398 21.49-10.195h.81l-.43 4.96-.25 4.845v28.039h10.43V30.918h-8.49l-1.38 9.25M1434.91 38.27c1.85 0 3.63.136 5.34.421 1.72.274 3.09.547 4.1.84v-7.976c-1.15-.559-2.81-.996-5.01-1.36-2.18-.351-4.17-.527-5.94-.527-13.32 0-19.97 7.012-19.97 21.055V91.71h-9.88v5.027l9.88 4.336 4.38 14.707h6.04V99.805h20V91.71h-20V51.16c0-4.15.98-7.333 2.96-9.56 1.97-2.206 4.67-3.331 8.1-3.331M1463.81 65.43c0-8.809 1.76-15.508 5.27-20.118 3.53-4.609 8.69-6.906 15.53-6.906s12.01 2.297 15.56 6.875c3.53 4.602 5.3 11.301 5.3 20.149 0 8.75-1.77 15.41-5.3 19.953-3.55 4.539-8.77 6.824-15.69 6.824-6.82 0-11.99-2.246-15.47-6.73-3.46-4.48-5.2-11.16-5.2-20.047zm52.47 0c0-11.23-2.83-20-8.48-26.309-5.66-6.309-13.47-9.453-23.44-9.453-6.17 0-11.64 1.445-16.42 4.336-4.78 2.89-8.46 7.031-11.06 12.45-2.59 5.401-3.88 11.73-3.88 18.976 0 11.23 2.8 19.968 8.41 26.242 5.61 6.258 13.4 9.402 23.38 9.402 9.64 0 17.3-3.222 22.97-9.62 5.69-6.415 8.52-15.087 8.52-26.024M1591.71 92.324c-5.54 0-9.94-1.797-13.23-5.39-3.3-3.614-5.24-8.594-5.81-14.97h36c0 6.583-1.46 11.622-4.39 15.126-2.93 3.496-7.13 5.234-12.57 5.234zm2.83-62.656c-10.19 0-18.22 3.086-24.11 9.297-5.89 6.21-8.83 14.824-8.83 25.84 0 11.101 2.74 19.922 8.2 26.464 5.46 6.524 12.81 9.805 22.04 9.805 8.62 0 15.45-2.851 20.48-8.523 5.03-5.676 7.54-13.157 7.54-22.461v-6.613h-47.45c.21-8.086 2.25-14.22 6.13-18.418 3.87-4.18 9.33-6.29 16.36-6.29 7.43 0 14.77 1.563 22.01 4.669V34.14c-3.69-1.602-7.17-2.746-10.46-3.438-3.3-.684-7.27-1.035-11.91-1.035M1683.5 30.918v44.57c0 5.606-1.27 9.805-3.83 12.559-2.55 2.773-6.55 4.16-12.01 4.16-7.2 0-12.48-1.953-15.83-5.851-3.35-3.895-5.03-10.32-5.03-19.286V30.918h-10.43v68.887h8.48l1.69-9.422h.51c2.14 3.387 5.14 6.023 8.99 7.887 3.84 1.867 8.15 2.804 12.88 2.804 8.3 0 14.54-2.011 18.74-6.015 4.19-3.985 6.29-10.391 6.29-19.192V30.918h-10.45M1740.11 38.406c7.12 0 12.28 1.938 15.49 5.813 3.21 3.879 4.81 10.129 4.81 18.758v2.199c0 9.765-1.62 16.726-4.87 20.898-3.25 4.18-8.43 6.25-15.56 6.25-6.12 0-10.8-2.383-14.05-7.129-3.24-4.746-4.88-11.472-4.88-20.136 0-8.797 1.64-15.45 4.85-19.93 3.22-4.484 7.96-6.723 14.21-6.723zm20.87 1.762h-.57c-4.82-7.004-12.03-10.5-21.62-10.5-9.01 0-16.02 3.066-21.03 9.238-5 6.153-7.52 14.922-7.52 26.27 0 11.355 2.52 20.176 7.55 26.465 5.02 6.289 12.02 9.433 21 9.433 9.34 0 16.5-3.398 21.48-10.195h.83l-.44 4.96-.25 4.845v28.039h10.43V30.918h-8.49l-1.37 9.25M1846.07 38.27c1.85 0 3.64.136 5.36.421 1.7.274 3.07.547 4.08.84v-7.976c-1.13-.559-2.8-.996-5-1.36-2.2-.351-4.18-.527-5.94-.527-13.33 0-19.99 7.012-19.99 21.055V91.71h-9.86v5.027l9.86 4.336 4.4 14.707h6.04V99.805H1855V91.71h-19.98V51.16c0-4.15.98-7.333 2.95-9.56 1.97-2.206 4.68-3.331 8.1-3.331M1894.26 92.324c-5.53 0-9.94-1.797-13.22-5.39-3.31-3.614-5.25-8.594-5.83-14.97h36.01c0 6.583-1.45 11.622-4.38 15.126-2.95 3.496-7.13 5.234-12.58 5.234zm2.83-62.656c-10.19 0-18.22 3.086-24.1 9.297-5.9 6.21-8.84 14.824-8.84 25.84 0 11.101 2.73 19.922 8.2 26.464 5.47 6.524 12.81 9.805 22.03 9.805 8.63 0 15.46-2.851 20.49-8.523 5.03-5.676 7.55-13.157 7.55-22.461v-6.613h-47.46c.22-8.086 2.26-14.22 6.13-18.418 3.87-4.18 9.33-6.29 16.37-6.29 7.42 0 14.75 1.563 22 4.669V34.14c-3.7-1.602-7.17-2.746-10.47-3.438-3.28-.684-7.25-1.035-11.9-1.035M1983.36 49.727c0-6.426-2.4-11.368-7.18-14.844-4.77-3.477-11.47-5.215-20.11-5.215-9.13 0-16.26 1.445-21.37 4.336v9.687a51.32 51.32 0 0 1 10.65-3.964c3.79-.977 7.45-1.457 10.97-1.457 5.46 0 9.64.87 12.57 2.609 2.95 1.738 4.41 4.394 4.41 7.95 0 2.694-1.17 4.98-3.5 6.894-2.32 1.914-6.85 4.152-13.6 6.757-6.41 2.383-10.97 4.473-13.67 6.25-2.71 1.778-4.72 3.81-6.04 6.067-1.31 2.254-1.98 4.96-1.98 8.113 0 5.606 2.29 10.04 6.86 13.281 4.57 3.25 10.84 4.883 18.79 4.883 7.42 0 14.66-1.515 21.74-4.531l-3.71-8.496c-6.9 2.851-13.17 4.277-18.79 4.277-4.94 0-8.67-.77-11.18-2.324-2.52-1.543-3.78-3.691-3.78-6.406 0-1.844.48-3.418 1.42-4.707.95-1.309 2.46-2.54 4.56-3.711 2.09-1.184 6.11-2.871 12.07-5.086 8.16-2.98 13.69-5.98 16.55-8.996 2.87-3.02 4.32-6.809 4.32-11.367M2021.28 38.27c1.85 0 3.64.136 5.35.421 1.71.274 3.09.547 4.09.84v-7.976c-1.14-.559-2.81-.996-5.01-1.36-2.18-.351-4.18-.527-5.93-.527-13.33 0-19.99 7.012-19.99 21.055V91.71h-9.87v5.027l9.87 4.336 4.4 14.707h6.02V99.805h20V91.71h-20V51.16c0-4.15 1-7.333 2.97-9.56 1.98-2.206 4.67-3.331 8.1-3.331M2053.61 30.918h-10.42v68.887h10.42zm-11.31 87.559c0 2.39.59 4.14 1.76 5.253 1.18 1.106 2.65 1.661 4.42 1.661 1.67 0 3.1-.567 4.32-1.7 1.22-1.132 1.82-2.871 1.82-5.214 0-2.344-.6-4.09-1.82-5.247-1.22-1.16-2.65-1.726-4.32-1.726-1.77 0-3.24.566-4.42 1.726-1.17 1.157-1.76 2.903-1.76 5.247M2121.59 30.918v44.57c0 5.606-1.27 9.805-3.83 12.559-2.55 2.773-6.55 4.16-12 4.16-7.21 0-12.49-1.953-15.84-5.851-3.35-3.895-5.03-10.32-5.03-19.286V30.918h-10.43v68.887h8.49l1.69-9.422h.5c2.15 3.387 5.14 6.023 8.99 7.887 3.85 1.867 8.16 2.804 12.88 2.804 8.3 0 14.54-2.011 18.74-6.015 4.19-3.985 6.29-10.391 6.29-19.192V30.918h-10.45M2159.29 77.742c0-4.812 1.35-8.465 4.08-10.926 2.72-2.48 6.51-3.71 11.37-3.71 10.19 0 15.28 4.953 15.28 14.831 0 10.344-5.16 15.532-15.47 15.532-4.9 0-8.67-1.32-11.31-3.965-2.63-2.649-3.95-6.555-3.95-11.762zm-5.67-58.387c0-3.73 1.58-6.55 4.72-8.488 3.14-1.922 7.65-2.879 13.52-2.879 8.75 0 15.24 1.309 19.45 3.926 4.21 2.617 6.31 6.172 6.31 10.652 0 3.723-1.15 6.32-3.45 7.754-2.31 1.457-6.65 2.168-13.01 2.168h-12.51c-4.74 0-8.43-1.12-11.06-3.386-2.65-2.266-3.97-5.508-3.97-9.747zm54.94 80.45v-6.582l-12.76-1.512c1.18-1.477 2.23-3.39 3.15-5.754.91-2.371 1.37-5.039 1.37-8.02 0-6.746-2.29-12.128-6.91-16.152-4.61-4.012-10.93-6.023-18.98-6.023-2.05 0-3.98.156-5.78.5-4.45-2.356-6.67-5.305-6.67-8.871 0-1.883.77-3.282 2.34-4.176 1.54-.902 4.21-1.36 7.97-1.36h12.2c7.46 0 13.19-1.574 17.19-4.707 4-3.144 6-7.714 6-13.71 0-7.618-3.06-13.426-9.17-17.43C2192.38 2.004 2183.46 0 2171.72 0c-9 0-15.95 1.68-20.82 5.027-4.88 3.352-7.34 8.079-7.34 14.211 0 4.18 1.35 7.813 4.03 10.88 2.68 3.046 6.45 5.116 11.32 6.21-1.77.8-3.24 2.031-4.44 3.711-1.19 1.68-1.78 3.633-1.78 5.84 0 2.52.66 4.707 2.01 6.602 1.34 1.882 3.44 3.71 6.34 5.468-3.56 1.465-6.46 3.953-8.71 7.48-2.23 3.516-3.35 7.54-3.35 12.06 0 7.55 2.26 13.37 6.79 17.452 4.52 4.082 10.93 6.133 19.22 6.133 3.6 0 6.86-.429 9.75-1.27h23.82M2284.61 91.71h-17.54V30.919h-10.43v60.793h-12.31v4.707l12.31 3.766v3.839c0 16.922 7.4 25.391 22.19 25.391 3.65 0 7.93-.73 12.82-2.195l-2.7-8.364c-4.03 1.301-7.46 1.946-10.31 1.946-3.93 0-6.85-1.309-8.73-3.926-1.89-2.617-2.84-6.816-2.84-12.598v-4.472h17.54V91.71M2302.87 65.43c0-8.809 1.76-15.508 5.28-20.118 3.52-4.609 8.7-6.906 15.52-6.906 6.84 0 12.02 2.297 15.57 6.875 3.54 4.602 5.3 11.301 5.3 20.149 0 8.75-1.76 15.41-5.3 19.953-3.55 4.539-8.78 6.824-15.69 6.824-6.83 0-11.99-2.246-15.46-6.73-3.48-4.48-5.22-11.16-5.22-20.047zm52.48 0c0-11.23-2.82-20-8.47-26.309-5.67-6.309-13.48-9.453-23.46-9.453-6.15 0-11.62 1.445-16.4 4.336-4.77 2.89-8.47 7.031-11.06 12.45-2.59 5.401-3.9 11.73-3.9 18.976 0 11.23 2.81 19.968 8.43 26.242 5.6 6.258 13.4 9.402 23.38 9.402 9.63 0 17.28-3.222 22.97-9.62 5.68-6.415 8.51-15.087 8.51-26.024M2403.79 101.074c3.07 0 5.8-.254 8.22-.761l-1.43-9.676c-2.86.633-5.37.933-7.55.933-5.58 0-10.33-2.261-14.3-6.785-3.95-4.531-5.94-10.156-5.94-16.902V30.918h-10.43v68.887h8.62l1.19-12.754h.5c2.56 4.48 5.63 7.949 9.23 10.37 3.61 2.423 7.56 3.653 11.89 3.653M2500.33 69.766l-10.68 28.476c-1.39 3.594-2.81 8.028-4.28 13.262-.93-4.024-2.24-8.438-3.96-13.262l-10.81-28.476zm14.77-38.848l-11.44 29.227h-36.83l-11.32-29.227h-10.81l36.34 92.273h8.98l36.13-92.273h-11.05M2583.07 30.918v44.57c0 5.606-1.27 9.805-3.83 12.559-2.55 2.773-6.55 4.16-12 4.16-7.21 0-12.49-1.953-15.84-5.851-3.35-3.895-5.03-10.32-5.03-19.286V30.918h-10.43v68.887h8.48l1.69-9.422h.51c2.14 3.387 5.14 6.023 8.99 7.887 3.84 1.867 8.15 2.804 12.88 2.804 8.3 0 14.54-2.011 18.74-6.015 4.19-3.985 6.29-10.391 6.29-19.192V30.918h-10.45M2620.76 77.742c0-4.812 1.36-8.465 4.08-10.926 2.73-2.48 6.53-3.71 11.37-3.71 10.2 0 15.28 4.953 15.28 14.831 0 10.344-5.15 15.532-15.45 15.532-4.91 0-8.68-1.32-11.32-3.965-2.64-2.649-3.96-6.555-3.96-11.762zm-5.66-58.387c0-3.73 1.57-6.55 4.71-8.488 3.15-1.922 7.65-2.879 13.53-2.879 8.75 0 15.23 1.309 19.44 3.926 4.21 2.617 6.31 6.172 6.31 10.652 0 3.723-1.14 6.32-3.45 7.754-2.31 1.457-6.64 2.168-13 2.168h-12.51c-4.74 0-8.43-1.12-11.07-3.386-2.63-2.266-3.96-5.508-3.96-9.747zm54.94 80.45v-6.582l-12.76-1.512c1.18-1.477 2.22-3.39 3.14-5.754.92-2.371 1.38-5.039 1.38-8.02 0-6.746-2.3-12.128-6.92-16.152-4.61-4.012-10.92-6.023-18.97-6.023-2.05 0-3.99.156-5.78.5-4.46-2.356-6.67-5.305-6.67-8.871 0-1.883.78-3.282 2.33-4.176 1.55-.902 4.21-1.36 7.98-1.36h12.2c7.46 0 13.18-1.574 17.18-4.707 4.01-3.144 6-7.714 6-13.71 0-7.618-3.06-13.426-9.17-17.43C2653.87 2.004 2644.94 0 2633.2 0c-9 0-15.95 1.68-20.83 5.027-4.88 3.352-7.33 8.079-7.33 14.211 0 4.18 1.35 7.813 4.02 10.88 2.69 3.046 6.47 5.116 11.32 6.21-1.77.8-3.23 2.031-4.43 3.711-1.19 1.68-1.79 3.633-1.79 5.84 0 2.52.66 4.707 2.01 6.602 1.35 1.882 3.45 3.71 6.35 5.468-3.56 1.465-6.47 3.953-8.71 7.48-2.23 3.516-3.35 7.54-3.35 12.06 0 7.55 2.25 13.37 6.79 17.452 4.52 4.082 10.92 6.133 19.21 6.133 3.62 0 6.86-.429 9.75-1.27h23.83M2692.7 99.805V55.117c0-5.605 1.27-9.805 3.83-12.566 2.56-2.766 6.57-4.145 12.01-4.145 7.2 0 12.47 1.965 15.81 5.903 3.33 3.945 4.99 10.379 4.99 19.304v36.192h10.44V30.918h-8.62l-1.5 9.25h-.58c-2.13-3.41-5.1-5.988-8.88-7.793-3.8-1.809-8.13-2.707-12.99-2.707-8.37 0-14.65 1.992-18.81 5.977-4.18 3.964-6.26 10.351-6.26 19.101v45.059h10.56M2760.61 30.918h10.43v97.805h-10.43zM2810.67 38.27c6.5 0 11.6 1.789 15.31 5.343 3.71 3.575 5.56 8.555 5.56 14.961v6.23l-10.44-.448c-8.3-.286-14.27-1.583-17.94-3.868-3.66-2.273-5.5-5.82-5.5-10.644 0-3.781 1.14-6.64 3.42-8.613 2.29-1.973 5.48-2.961 9.59-2.961zm23.57-7.352l-2.07 9.805h-.51c-3.44-4.305-6.86-7.227-10.27-8.77-3.42-1.523-7.68-2.285-12.8-2.285-6.83 0-12.17 1.758-16.05 5.273-3.87 3.528-5.81 8.536-5.81 15.032 0 13.906 11.12 21.199 33.37 21.875l11.7.359v4.277c0 5.418-1.17 9.395-3.5 11.985-2.32 2.566-6.03 3.855-11.15 3.855-5.74 0-12.24-1.758-19.49-5.273l-3.21 7.988c3.4 1.836 7.11 3.281 11.16 4.324a47.81 47.81 0 0 0 12.16 1.575c8.23 0 14.3-1.817 18.27-5.461 3.96-3.66 5.93-9.5 5.93-17.54V30.919h-7.73M2893.6 101.074c3.07 0 5.8-.254 8.25-.761l-1.46-9.676c-2.84.633-5.35.933-7.54.933-5.56 0-10.33-2.261-14.3-6.785-3.96-4.531-5.93-10.156-5.93-16.902V30.918h-10.44v68.887h8.61l1.19-12.754h.5c2.57 4.48 5.65 7.949 9.25 10.37 3.6 2.423 7.56 3.653 11.87 3.653M2901.63 6.727c-3.94 0-7.04.558-9.31 1.691v9.121c2.97-.84 6.08-1.25 9.31-1.25 4.14 0 7.3 1.25 9.45 3.77 2.16 2.507 3.24 6.132 3.24 10.859v91.895h10.69V31.797c0-7.95-2.01-14.121-6.04-18.496-4.02-4.383-9.8-6.574-17.34-6.574M2999.96 55.371c0-8.086-2.93-14.394-8.8-18.918-5.87-4.52-13.83-6.785-23.88-6.785-10.9 0-19.27 1.406-25.14 4.219v10.3c3.77-1.59 7.88-2.847 12.31-3.765 4.45-.93 8.85-1.399 13.21-1.399 7.12 0 12.49 1.36 16.09 4.063 3.59 2.695 5.4 6.465 5.4 11.277 0 3.196-.63 5.805-1.91 7.832-1.29 2.024-3.42 3.907-6.42 5.625-2.99 1.711-7.56 3.664-13.67 5.84-8.55 3.059-14.66 6.692-18.32 10.871-3.66 4.2-5.51 9.668-5.51 16.407 0 7.089 2.68 12.714 7.99 16.914 5.32 4.191 12.36 6.289 21.12 6.289 9.13 0 17.54-1.68 25.2-5.032l-3.32-9.304c-7.59 3.183-14.96 4.785-22.13 4.785-5.66 0-10.07-1.223-13.26-3.652-3.19-2.43-4.78-5.809-4.78-10.118 0-3.191.59-5.8 1.76-7.832 1.17-2.031 3.14-3.886 5.95-5.597 2.78-1.688 7.04-3.563 12.79-5.625 9.63-3.426 16.26-7.118 19.89-11.063 3.62-3.937 5.43-9.043 5.43-15.332M741.648 375.406h30c28.965 0 50.227 5.039 63.774 15.117 13.531 10.079 20.32 25.821 20.32 47.247 0 19.832-6.074 34.628-18.191 44.402-12.141 9.758-31.028 14.641-56.692 14.641h-39.211zm172.192 64.246c0-36.062-11.809-63.691-35.434-82.898-23.621-19.219-57.234-28.82-100.847-28.82h-35.911V198.73h-56.445v345.329h99.438c43.14 0 75.457-8.829 96.961-26.465 21.496-17.637 32.238-43.614 32.238-77.942M1099.26 464.691c11.17 0 20.39-.789 27.63-2.371l-5.43-51.718c-7.88 1.894-16.07 2.832-24.57 2.832-22.2 0-40.19-7.246-53.97-21.731-13.78-14.48-20.66-33.301-20.66-56.453V198.73h-55.514v261.227h43.464l7.32-46.055h2.83c8.66 15.594 19.96 27.95 33.9 37.09 13.93 9.141 28.93 13.699 45 13.699M1206.88 329.82c0-60.308 22.28-90.465 66.85-90.465 44.08 0 66.13 30.157 66.13 90.465 0 59.688-22.21 89.512-66.61 89.512-23.31 0-40.2-7.707-50.67-23.144-10.47-15.43-15.7-37.54-15.7-66.368zm190.13 0c0-42.672-10.95-75.972-32.83-99.898-21.89-23.945-52.35-35.918-91.41-35.918-24.41 0-45.97 5.508-64.7 16.543-18.75 11.016-33.16 26.836-43.23 47.48-10.08 20.625-15.11 44.551-15.11 71.793 0 42.364 10.86 75.43 32.58 99.2 21.73 23.777 52.36 35.671 91.89 35.671 37.79 0 67.7-12.156 89.75-36.492 22.05-24.328 33.06-57.121 33.06-98.379M1558.11 238.887c13.54 0 27.07 2.129 40.62 6.386v-41.816c-6.13-2.676-14.05-4.922-23.73-6.738-9.69-1.797-19.73-2.715-30.12-2.715-52.59 0-78.88 27.715-78.88 83.144v140.778h-35.68v24.558l38.26 20.325 18.9 55.261h34.26v-58.113h74.39v-42.031h-74.39v-139.84c0-13.379 3.34-23.242 10.03-29.629 6.69-6.387 15.48-9.57 26.34-9.57M1783.44 464.691c11.17 0 20.38-.789 27.62-2.371l-5.43-51.718c-7.88 1.894-16.06 2.832-24.56 2.832-22.2 0-40.2-7.246-53.97-21.731-13.78-14.48-20.66-33.301-20.66-56.453V198.73h-55.52v261.227h43.46l7.34-46.055h2.82c8.66 15.594 19.95 27.95 33.9 37.09 13.92 9.141 28.93 13.699 45 13.699M1925.05 236.523c20.15 0 36.32 5.625 48.52 16.895 12.21 11.25 18.31 27.051 18.31 47.344v22.676l-33.54-1.407c-26.13-.937-45.16-5.312-57.04-13.105-11.89-7.793-17.82-19.727-17.82-35.781 0-11.661 3.45-20.665 10.39-27.051 6.91-6.387 17.32-9.571 31.18-9.571zm82.66-37.793l-11.11 36.387h-1.87c-12.62-15.918-25.29-26.738-38.04-32.48-12.74-5.742-29.13-8.633-49.13-8.633-25.67 0-45.7 6.934-60.1 20.801-14.41 13.847-21.62 33.457-21.62 58.808 0 26.934 10 47.246 30 60.934 19.99 13.691 50.45 21.172 91.41 22.441l45.09 1.414v13.938c0 16.699-3.88 29.16-11.68 37.441-7.79 8.262-19.88 12.383-36.25 12.383-13.39 0-26.23-1.953-38.5-5.891a294.638 294.638 0 0 1-35.44-13.933l-17.94 39.668c14.17 7.41 29.68 13.035 46.52 16.894 16.85 3.868 32.77 5.789 47.72 5.789 33.22 0 58.31-7.246 75.22-21.726 16.94-14.492 25.4-37.246 25.4-68.262V198.73h-39.68M2220.04 194.004c-39.52 0-69.55 11.543-90.1 34.609-20.55 23.067-30.82 56.172-30.82 99.321 0 43.925 10.74 77.707 32.23 101.339 21.5 23.614 52.56 35.418 93.18 35.418 27.56 0 52.35-5.117 74.41-15.359l-16.78-44.641c-23.46 9.133-42.82 13.704-58.1 13.704-45.19 0-67.79-29.993-67.79-89.981 0-29.293 5.63-51.305 16.89-66.031 11.26-14.707 27.76-22.09 49.48-22.09 24.72 0 48.11 6.152 70.15 18.437v-48.417c-9.92-5.84-20.5-10-31.76-12.52-11.26-2.52-24.93-3.789-40.99-3.789M2451.52 238.887c13.54 0 27.08 2.129 40.63 6.386v-41.816c-6.15-2.676-14.05-4.922-23.73-6.738-9.69-1.797-19.73-2.715-30.12-2.715-52.6 0-78.9 27.715-78.9 83.144v140.778h-35.66v24.558l38.26 20.325 18.9 55.261h34.26v-58.113h74.39v-42.031h-74.39v-139.84c0-13.379 3.34-23.242 10.03-29.629 6.69-6.387 15.47-9.57 26.33-9.57M2585.92 329.82c0-60.308 22.28-90.465 66.84-90.465 44.09 0 66.15 30.157 66.15 90.465 0 59.688-22.22 89.512-66.62 89.512-23.31 0-40.2-7.707-50.67-23.144-10.47-15.43-15.7-37.54-15.7-66.368zm190.13 0c0-42.672-10.94-75.972-32.83-99.898-21.89-23.945-52.36-35.918-91.4-35.918-24.42 0-45.98 5.508-64.72 16.543-18.74 11.016-33.14 26.836-43.22 47.48-10.07 20.625-15.12 44.551-15.12 71.793 0 42.364 10.87 75.43 32.59 99.2 21.74 23.777 52.36 35.671 91.89 35.671 37.79 0 67.7-12.156 89.75-36.492 22.04-24.328 33.06-57.121 33.06-98.379M2972.33 464.691c11.18 0 20.38-.789 27.63-2.371l-5.43-51.718c-7.87 1.894-16.05 2.832-24.57 2.832-22.2 0-40.19-7.246-53.96-21.731-13.78-14.48-20.67-33.301-20.67-56.453V198.73h-55.51v261.227h43.46l7.33-46.055h2.83c8.66 15.594 19.96 27.95 33.89 37.09 13.94 9.141 28.94 13.699 45 13.699" fill="#100f0d"/><path d="M610.11 372.83c0-170.584-138.257-308.862-308.846-308.862-170.602 0-308.846 138.278-308.846 308.863 0 170.576 138.244 308.846 308.846 308.846 170.59 0 308.846-138.27 308.846-308.846" fill="#e53935" stroke-width="1.029"/><path d="M460.694 521.792l-105.04.958-61.415 61.415-72.096-47.883 12.445-12.438-29.207.26-99.129-166.817H67.357l24.39-24.402-24.57-41.363L294.66 64.049c2.192-.04 4.399-.08 6.603-.08 170.416 0 308.585 138.055 308.846 308.408L460.694 521.792" fill="#d51c2f" stroke-width="1.029"/><path d="M149.093 350.258c0 84.048 68.13 152.151 152.171 152.151 84.028 0 152.139-68.103 152.139-152.151zm342.063-7.017v14.046h44.015c-1.75 59.337-25.556 113.104-63.54 153.419L438.75 477.81l-9.925 9.94 32.875 32.887c-40.314 37.983-94.081 61.79-153.41 63.527l-.015-44.003h-14.035v44.003c-59.34-1.737-113.096-25.556-153.41-63.527l32.887-32.887-9.945-9.92-32.883 32.875c-37.975-40.315-61.781-94.082-63.53-153.419h44.002l-.008-14.034H67.176v-51.511h468.176v51.5h-44.196" fill="#f5f5f5" stroke-width="1.029"/></g></g></symbol><symbol id="pug" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><style>.st0{fill:#c1272d}.hyst1{fill:#efcca3}.st2{fill:#ed1c24}.hyst3{fill:#ccac8d}.hyst4{fill:#fff}.st5{fill:#ff931e}.st6{fill:#ffb81e}.hyst7{fill:#56332b}.hyst8{fill:#442823}.hyst9{fill:#7f4a41}.hyst10{fill:#331712}.st11{fill:#fc6}.st12{fill:#ccc}.st13{fill:#b3b3b3}.st14{fill:#989898}.st15{fill:#323232}.st16{fill:#1e1e1e}.st17{fill:#4c4c4c}.st18{fill:#e6e6e6}.st19{fill:#606060}</style><path class="hyst1" d="M107.4 50.9c-.2-4.4.4-8.3-1.6-11.6-4.8-8.2-16.8-13-40.8-13v.7h-.5.5v-.7c-24 0-36.6 4.8-41.4 13.1-1.9 3.4-1.7 7.2-2 11.6-.2 3.5-1.8 7.2-1.1 11.2.8 5.2 1.1 10.4 1.9 15.2.6 3.9 6 7.2 6.5 10.9 1.4 10.2 12 14.9 36 14.9v.8h-.6.7v-.8c24 0 34.2-4.7 35.5-14.9.5-3.8 5.5-7 6.1-10.9.8-4.8 1.1-10 1.9-15.2.7-4-.9-7.8-1.1-11.3z"/><path class="hyst3" d="M64.6 54.5c4.3.1 7.3 2.8 10.1 5.3 3.3 2.9 8.9 4.9 11.2 7.4 2.3 2.5 5.3 5 6.4 8.9 1.1 3.9 1.4 8.9 1.4 10.2 0 1.3.7 1 2.7 0 4.7-2.3 9.9-8.5 9.9-8.5-.6 3.9-5.7 7.4-6.2 11.1C98.9 99.1 89 104 64.5 104h-.1.6"/><path class="hyst3" d="M80.4 46.7c.9 3.1 4.1 13.6-2.1 10.1 0 0 2.6 1.5 4.2 7.2 1.7 5.7 5.8 6.4 5.8 6.4s6.7 1.3 11.7-3c4.2-3.6 4.9-10 3.1-14.9-1.8-4.8-5-6.3-9.7-7.3-4.7-1.1-14.1-2-13 1.5z"/><circle cx="92.3" cy="58.1" r="8.8"/><circle class="hyst4" cx="90" cy="54.2" r="2.3"/><path class="hyst1" d="M78.9 57.7s7.9 5.4 12.2 10.7c4.3 5.3 4.2 6.3 4.2 6.3l-3.1 1.4s-4.4-8.3-9.8-11.4c-5.5-3.1-6.1-5.7-6.1-5.7l2.6-1.3z"/><path class="hyst3" d="M64.9 54.5c-4.3.1-7.5 2.8-10.4 5.3-3.3 2.9-9.1 4.9-11.4 7.4-2.3 2.5-5.4 5-6.5 8.9-1.1 3.9-1.5 8.9-1.5 10.2 0 1.3.2 1.4-2.7 0-4.7-2.2-9.9-8.5-9.9-8.5.6 3.9 5.7 7.4 6.2 11.1C30.1 99.1 40 104 64.5 104h.5"/><path class="hyst7" d="M88.1 71.4C83.3 65.5 75.6 60 64.9 60h-.1c-10.7 0-18.4 5.5-23.2 11.4-5 6.1-4.6 8.5-4.6 14.3 0 21 7.4 15 12.3 17.6 5 2.5 10.2 1.7 15.5 1.7h.1c5.4 0 10.5.7 15.5-1.8 4.9-2.5 12.3 3.7 12.3-17.3.1-5.8.4-8.4-4.6-14.5z"/><path class="hyst8" d="M64.4 65.2s-.7 9.7-2.1 11.6l2.6-.6-.5-11z"/><path class="hyst8" d="M65.1 65.2s.7 9.7 2.1 11.6l-2.6-.6.5-11z"/><path class="hyst7" d="M56.7 62.9c-1-2.3 2.6-6 8.3-6.1 5.7 0 9.3 3.7 8.3 6.1-1 2.4-4.6 3.1-8.3 3.2-3.6-.1-7.3-.8-8.3-3.2z"/><path d="M65 65.2c0-.4 3.4-.5 5.2-1.7 0 0-3.7 1.2-4.5.7-.8-.4-1-1.6-1-1.6s-.3 1.2-.9 1.6c-.7.4-4.9-.7-4.9-.7s5.6 1.4 5.6 1.7c0 .3-.1 1.3-.1 2 0 2.5 0 8.7.4 9.2.6.9.4-6.7.4-9.2-.1-.8-.1-1.6-.2-2z"/><path class="hyst9" d="M65.2 78.6c1.7 0 4.7 1.2 7.4 3.1-2.6-2.9-5.7-4.9-7.4-4.9-1.8 0-5.6 2.2-8.3 5.4 2.8-2.2 6.4-3.6 8.3-3.6z"/><path class="hyst8" d="M64.5 96.3c-3.8 0-7.5-1.2-10.9-2.1-.7-.2-1.4.3-2.1.1-6.3-2-11.4-5.4-14.5-9.7v1c0 21 7.4 15.1 12.3 17.6 5 2.5 10.2 1.7 15.5 1.7h.1c5.4 0 10.5.7 15.5-1.8 4.9-2.5 12.3 3.6 12.3-17.4 0-.8 0-1.6.1-2.3-2.9 4.7-8.2 8.4-14.8 10.6-.6.2-2-.3-2.6-.2-3.6 1.2-6.8 2.5-10.9 2.5z"/><path class="hyst8" d="M55 85s-2.5 7.5-.8 10.8l-2.3-1s1.7-7.6 3.1-9.8zM74.8 85s2.5 7.5.8 10.8l2.3-1s-1.8-7.6-3.1-9.8z"/><path class="hyst3" d="M48.6 46.7c-.9 3.1-4.1 13.6 2.1 10.1 0 0-2.6 1.5-4.2 7.2s-5.8 6.4-5.8 6.4-6.7 1.3-11.7-3c-4.2-3.6-4.9-10-3.1-14.9s5-6.3 9.7-7.3c4.7-1.1 14-2 13 1.5z"/><path d="M64.9 76.8c2.7 0 11.1 5.8 11.2 12.9v-.4c0-7.4-6.8-13.3-11.2-13.3-4.4 0-11.2 6-11.2 13.3v.4c.1-7.1 8.5-12.9 11.2-12.9z"/><ellipse transform="rotate(-14.465 66.712 61.468)" class="hyst10" cx="66.7" cy="61.5" rx=".8" ry="1.5"/><ellipse transform="rotate(17.235 62.371 61.462)" class="hyst10" cx="62.4" cy="61.5" rx=".8" ry="1.5"/><circle cx="37.2" cy="58.1" r="8.8"/><circle class="hyst4" cx="39.5" cy="54.2" r="2.3"/><path class="hyst9" d="M67.5 58.2c0-.1-2.3 1-2.9 1.1-.6-.1-2.9-1.2-2.9-1.1h5.8z"/><path class="hyst1" d="M50 57.7s-7.9 5.4-12.2 10.7c-4.3 5.3-4.2 6.3-4.2 6.3l3.1 1.4s4.4-8.3 9.8-11.4 6.1-5.7 6.1-5.7L50 57.7z"/><path class="hyst3" d="M32.7 41.7S30 49.1 24 52.2c0 0 9.4-1.1 8.7-10.5zM95.8 41.7s2.7 7.4 8.7 10.5c0 0-9.4-1.1-8.7-10.5zM78.7 55.5s-5.9-6.2-13.8-6.4h.1.1c-8 .2-13.8 6.4-13.8 6.4 6.9-4.8 12.8-4.7 13.8-4.7-.1 0 6.7-.1 13.6 4.7zM71.8 42.5s-3-4.2-7-4.3h.2c-3 .1-6.9 4.3-6.9 4.3 3.4-3.3 6.9-3.2 6.9-3.2s3.3-.1 6.8 3.2zM37.2 73.2s-4.7 2.3-8.1.9H29c-3-1.7-4.5-6.8-4.5-6.8s3 9 12.7 5.9zM92 73.2s4.7 2.3 8.1.9c4-1.7 4.6-6.8 4.6-6.8s-3 9-12.7 5.9z"/><path class="hyst3" d="M42.6 41.2c2.6-.5 6.9-.6 10.3.5 4.3 1.5.8 7 1.7 7.3.9.3 2.1-3.8 10.1-3.4 8.1.4 9 4 10.1 3.4s-1.1-10 11-7.8c0 0-12.7-3.4-12.1 5.8 0 0-7.3-5.6-17.5-.6.1 0 2.7-8.6-13.6-5.2zM86.9 41.2c.2 0 .3.1.4.1.1 0-.1-.1-.4-.1zM86.9 41.2zM39.1 28.9S28.3 42.5 26.7 47.7c-1.6 5.3-2.8 27-4.2 30.1l-5-21.4 9.2-22.3 12.4-5.2zM89.9 28.9s10.8 13.6 12.4 18.8c1.6 5.3 2.8 27 4.2 30.1l5-21.4-9.2-22.3-12.4-5.2z"/><path class="hyst7" d="M89.4 28.9s11.6 9.7 15 20.9c3.4 11.2 2 24.8 4.6 26.5 3.7 2.4 7.9-11.9 9.3-13.4 2.2-2.4 9.5-8.5 10-9.6.5-1.1-14.8-17.8-21.5-21.1-8.1-3.8-18.1-4.1-17.4-3.3z"/><path class="hyst8" d="M99.3 34.9s13.7 17.5 13.5 39.3l5.5-11.2c-.1 0-4.9-14.3-19-28.1z"/><path class="hyst7" d="M39.1 28.9s-11.6 9.7-15 20.9-2 24.8-4.6 26.5c-3.7 2.4-7.9-11.9-9.3-13.4C8 60.5.7 54.4.2 53.3-.3 52.2 15 35.5 21.7 32.2c8.1-3.8 18.1-4.1 17.4-3.3z"/><path class="hyst8" d="M29.2 34.9S15.5 52.4 15.7 74.2L10.3 63s4.8-14.3 18.9-28.1z"/><path class="hyst3" d="M21.8 74.6s1 5.4 2.6 7.1.5-1.3.5-1.3-1.7-.9-1.4-7.8-1.7 2-1.7 2zM107.1 74.6s-1 5.4-2.6 7.1-.5-1.3-.5-1.3 1.7-.9 1.4-7.8 1.7 2 1.7 2z"/><g><circle class="hyst8" cx="54.5" cy="70.5" r=".8"/><circle class="hyst8" cx="49.9" cy="75.3" r=".8"/><circle class="hyst8" cx="48.4" cy="70.5" r=".8"/></g><g><circle class="hyst8" cx="74" cy="70.5" r=".8"/><circle class="hyst8" cx="78.6" cy="75.3" r=".8"/><circle class="hyst8" cx="80.1" cy="70.5" r=".8"/></g></symbol><symbol viewBox="0 0 50 50" id="puppet" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -247)" fill="#fbc02d"><path stroke-width=".283" d="M11.559 249.467h13.587v13.587H11.559zM27.435 265.056h13.587v13.587H27.435zM11.559 281.074h13.587v13.587H11.559z"/><path stroke-width=".256" d="M16.62 251.615l18.305 18.305-3.236 3.236-18.305-18.305z"/><path stroke-width=".256" d="M37.834 271.331L19.53 289.636l-3.237-3.237 18.305-18.304z"/></g></symbol><symbol viewBox="0 0 100 99.999997" id="purescript" xmlns="http://www.w3.org/2000/svg"><path clip-path="url(#SVGID_2_)" d="M98.079 38.548L79.22 19.68l-5.087 5.088L90.447 41.09 74.134 57.41l5.087 5.087 18.858-18.86a3.59 3.59 0 0 0 1.055-2.55 3.578 3.578 0 0 0-1.055-2.54M25.483 42.794l-5.09-5.089L1.53 56.568a3.566 3.566 0 0 0-1.05 2.545c0 .961.373 1.863 1.05 2.542L20.394 80.52l5.089-5.086L9.162 59.113z" fill="#42a5f5" stroke-width="1.192"/><path clip-path="url(#SVGID_2_)" transform="matrix(1.19175 0 0 1.19175 -306.84 -629.047)" fill="#42a5f5" d="M281.841 551.736l6.461 6.037h28.379l-6.461-6.037zM288.302 566.861l-6.463 6.035h28.381l6.463-6.035zM281.838 581.982l6.464 6.035h28.381l-6.463-6.035z"/></symbol><symbol viewBox="0 0 24 24" id="python" xmlns="http://www.w3.org/2000/svg"><path d="M19.14 7.5A2.86 2.86 0 0 1 22 10.36v3.78A2.86 2.86 0 0 1 19.14 17H12c0 .39.32.96.71.96H17v1.68a2.86 2.86 0 0 1-2.86 2.86H9.86A2.86 2.86 0 0 1 7 19.64v-3.75a2.85 2.85 0 0 1 2.86-2.85h5.25a2.85 2.85 0 0 0 2.85-2.86V7.5h1.18m-4.28 11.79c-.4 0-.72.3-.72.89 0 .59.32.71.72.71a.71.71 0 0 0 .71-.71c0-.59-.32-.89-.71-.89m-10-1.79A2.86 2.86 0 0 1 2 14.64v-3.78A2.86 2.86 0 0 1 4.86 8H12c0-.39-.32-.96-.71-.96H7V5.36A2.86 2.86 0 0 1 9.86 2.5h4.28A2.86 2.86 0 0 1 17 5.36v3.75a2.85 2.85 0 0 1-2.86 2.85H8.89a2.85 2.85 0 0 0-2.85 2.86v2.68H4.86M9.14 5.71c.4 0 .72-.3.72-.89 0-.59-.32-.71-.72-.71-.39 0-.71.12-.71.71s.32.89.71.89z"/><path d="M9.264 22.379c-.895-.24-1.581-.799-1.947-1.582-.228-.489-.237-.606-.238-2.957-.001-2.745.057-3.074.666-3.785.193-.226.568-.517.833-.648.47-.23.579-.239 3.839-.288 3.131-.048 3.386-.065 3.814-.264.626-.291 1.07-.687 1.4-1.247.27-.46.278-.522.311-2.29l.034-1.82.932.051c1.075.058 1.504.211 2.098.748.853.77.869.841.869 3.957 0 2.434-.02 2.783-.18 3.075a3.365 3.365 0 0 1-1.337 1.33l-.517.273-3.95.031-3.951.031.068.274c.037.151.164.377.282.503.209.224.262.229 2.433.229h2.22v1.05c0 1.653-.394 2.437-1.54 3.072l-.545.302-2.644.018c-1.455.01-2.782-.018-2.95-.063zm6.12-1.692c.22-.222.253-.325.206-.675-.07-.523-.278-.73-.732-.73-.467 0-.672.217-.735.78-.042.372-.012.496.163.672.3.3.77.28 1.097-.047z" fill="#fc0" stroke="#fc0" stroke-width=".102"/><path d="M9.349 22.38c-.911-.15-1.936-1.074-2.176-1.963-.073-.273-.101-1.279-.079-2.868.033-2.317.047-2.473.27-2.926.13-.263.401-.623.603-.8.674-.592.87-.63 3.484-.675 4.399-.076 4.927-.166 5.705-.967.642-.662.706-.9.774-2.883l.061-1.784.951.055c.523.031 1.11.122 1.304.204.54.225 1.358 1.042 1.472 1.47.153.572.243 3.18.16 4.617-.071 1.23-.093 1.327-.395 1.78-.193.288-.577.647-.966.903l-.647.425-3.922.008c-2.157.004-3.942.028-3.966.052-.115.115.354.82.587.883.14.038 1.181.073 2.314.079l2.06.01v.91c0 1.739-.326 2.446-1.454 3.162l-.631.4-2.543-.011c-1.398-.007-2.733-.043-2.966-.081zm5.98-1.718c.285-.256.313-.328.251-.658-.09-.483-.301-.682-.722-.682-.436 0-.625.193-.715.73-.065.384-.044.453.2.663.358.308.595.295.985-.053z" fill="#fdd835" stroke-width=".102"/><path d="M4.281 17.396c-.88-.215-1.714-.935-2.024-1.747-.149-.389-.168-.804-.142-3.041.027-2.26.054-2.638.215-2.962.259-.519.851-1.092 1.392-1.346.437-.206.632-.217 4.408-.245l3.95-.03-.067-.275a1.367 1.367 0 0 0-.282-.504c-.21-.224-.263-.23-2.433-.23h-2.22l.002-1.143c.003-1.338.157-1.795.84-2.493.746-.763 1.103-.838 4.025-.838 2.961 0 3.28.06 4.067.768.37.333.572.621.728 1.037.201.539.213.735.183 3.072-.035 2.777-.045 2.824-.78 3.598-.787.829-.76.824-4.59.883-3.812.06-3.797.057-4.61.806-.765.706-.917 1.2-.964 3.133l-.04 1.653-.677-.01c-.371-.007-.813-.045-.98-.086zM9.59 5.551c.237-.204.286-.326.286-.72 0-.547-.201-.763-.71-.763-.502 0-.765.248-.765.724 0 .492.141.782.439.902.345.14.444.12.75-.143z" fill="#3c78aa"/></symbol><symbol viewBox="0 0 24 24" id="r" xmlns="http://www.w3.org/2000/svg"><path d="M11.956 4.05c-5.694 0-10.354 3.106-10.354 6.947 0 3.396 3.686 6.212 8.531 6.813v2.205h3.53V17.82c.88-.093 1.699-.259 2.475-.497l1.43 2.692h3.996l-2.402-4.048c1.936-1.263 3.147-3.034 3.147-4.97 0-3.841-4.659-6.947-10.354-6.947m1.584 2.712c4.349 0 7.558 1.45 7.558 4.753 0 1.77-.952 3.013-2.505 3.779a1.081 1.081 0 0 1-.228-.156c-.373-.165-.994-.352-.994-.352s3.085-.227 3.085-3.302-3.23-3.127-3.23-3.127h-7.092v7.413c-2.64-.766-4.462-2.392-4.462-4.255 0-2.63 3.52-4.753 7.868-4.753m.156 4.12h2.143s.983-.05.983.974c0 1.004-.983 1.004-.983 1.004h-2.143v-1.977m-.031 4.566h.952c.186 0 .28.052.445.207.135.103.28.3.404.476-.57.073-1.17.104-1.801.104z" fill="#1976d2" stroke-width="1.035"/></symbol><symbol viewBox="0 0 24 24" id="raml" xmlns="http://www.w3.org/2000/svg"><path d="M5 3h2v2H5v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5h2v2H5c-1.07-.27-2-.9-2-2v-4a2 2 0 0 0-2-2H0v-2h1a2 2 0 0 0 2-2V5a2 2 0 0 1 2-2m14 0a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h1v2h-1a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-2v-2h2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5h-2V3h2m-7 12a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m-4 0a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m8 0a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="razor" xmlns="http://www.w3.org/2000/svg"><path d="M15.45 11.91c-.11-2.21-1.75-3.54-3.73-3.54h-.08c-2.29 0-3.55 1.8-3.55 3.84 0 2.29 1.53 3.74 3.54 3.74 2.25 0 3.72-1.65 3.83-3.59m-3.81-5.97c1.53 0 2.97.68 4.02 1.74 0-.51.33-.89.83-.89h.11c.74 0 .89.7.89.92v7.9c-.04.52.54.78.87.44 1.27-1.29 2.78-6.69-.79-9.81-3.33-2.92-7.8-2.44-10.18-.8-2.52 1.74-4.14 5.61-2.57 9.22 1.71 3.95 6.61 5.13 9.52 3.95 1.48-.59 2.15 1.4.65 2.05-2.34.99-8.77.89-11.78-4.32-2.03-3.52-1.93-9.71 3.46-12.92C10.81 1.42 16.24 2.1 19.5 5.5c3.45 3.6 3.25 10.3-.1 12.91-1.51 1.18-3.76.03-3.74-1.7l-.02-.56a5.611 5.611 0 0 1-3.99 1.66C8.63 17.81 6 15.15 6 12.13c0-3.05 2.63-5.74 5.65-5.74z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="react" xmlns="http://www.w3.org/2000/svg"><path d="M12 10.11c1.03 0 1.87.84 1.87 1.89 0 1-.84 1.85-1.87 1.85-1.03 0-1.87-.85-1.87-1.85 0-1.05.84-1.89 1.87-1.89M7.37 20c.63.38 2.01-.2 3.6-1.7-.52-.59-1.03-1.23-1.51-1.9a22.7 22.7 0 0 1-2.4-.36c-.51 2.14-.32 3.61.31 3.96m.71-5.74l-.29-.51c-.11.29-.22.58-.29.86.27.06.57.11.88.16l-.3-.51m6.54-.76l.81-1.5-.81-1.5c-.3-.53-.62-1-.91-1.47C13.17 9 12.6 9 12 9c-.6 0-1.17 0-1.71.03-.29.47-.61.94-.91 1.47L8.57 12l.81 1.5c.3.53.62 1 .91 1.47.54.03 1.11.03 1.71.03.6 0 1.17 0 1.71-.03.29-.47.61-.94.91-1.47M12 6.78c-.19.22-.39.45-.59.72h1.18c-.2-.27-.4-.5-.59-.72m0 10.44c.19-.22.39-.45.59-.72h-1.18c.2.27.4.5.59.72M16.62 4c-.62-.38-2 .2-3.59 1.7.52.59 1.03 1.23 1.51 1.9.82.08 1.63.2 2.4.36.51-2.14.32-3.61-.32-3.96m-.7 5.74l.29.51c.11-.29.22-.58.29-.86-.27-.06-.57-.11-.88-.16l.3.51m1.45-7.05c1.47.84 1.63 3.05 1.01 5.63 2.54.75 4.37 1.99 4.37 3.68 0 1.69-1.83 2.93-4.37 3.68.62 2.58.46 4.79-1.01 5.63-1.46.84-3.45-.12-5.37-1.95-1.92 1.83-3.91 2.79-5.38 1.95-1.46-.84-1.62-3.05-1-5.63-2.54-.75-4.37-1.99-4.37-3.68 0-1.69 1.83-2.93 4.37-3.68-.62-2.58-.46-4.79 1-5.63 1.47-.84 3.46.12 5.38 1.95 1.92-1.83 3.91-2.79 5.37-1.95M17.08 12c.34.75.64 1.5.89 2.26 2.1-.63 3.28-1.53 3.28-2.26 0-.73-1.18-1.63-3.28-2.26-.25.76-.55 1.51-.89 2.26M6.92 12c-.34-.75-.64-1.5-.89-2.26-2.1.63-3.28 1.53-3.28 2.26 0 .73 1.18 1.63 3.28 2.26.25-.76.55-1.51.89-2.26m9 2.26l-.3.51c.31-.05.61-.1.88-.16-.07-.28-.18-.57-.29-.86l-.29.51m-2.89 4.04c1.59 1.5 2.97 2.08 3.59 1.7.64-.35.83-1.82.32-3.96-.77.16-1.58.28-2.4.36-.48.67-.99 1.31-1.51 1.9M8.08 9.74l.3-.51c-.31.05-.61.1-.88.16.07.28.18.57.29.86l.29-.51m2.89-4.04C9.38 4.2 8 3.62 7.37 4c-.63.35-.82 1.82-.31 3.96a22.7 22.7 0 0 1 2.4-.36c.48-.67.99-1.31 1.51-1.9z" fill="#00bcd4"/></symbol><symbol viewBox="0 0 24 24" id="readme" xmlns="http://www.w3.org/2000/svg"><path d="M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="reason" xmlns="http://www.w3.org/2000/svg"><path d="M3 3v18h18V3H3zm5.119 8.993h2.798c.382 0 .71.025.985.075.275.05.534.159.774.326.244.168.435.386.577.654.145.265.218.598.218 1 0 .552-.112 1.001-.335 1.35-.22.348-.536.638-.947.87l2.16 3.203H12.31l-1.763-2.742h-.77v2.742H8.12v-7.478zm6.594 0h4.676v1.447h-3.018v1.29h2.802v1.447h-2.802v1.848h3.018v1.446h-4.676v-7.478zM9.778 13.37v2.014h.513c.266 0 .49-.014.67-.044.18-.03.329-.1.45-.207a.96.96 0 0 0 .253-.34c.055-.128.082-.297.082-.508 0-.187-.034-.35-.1-.483a.698.698 0 0 0-.343-.317 1.086 1.086 0 0 0-.395-.095 6.012 6.012 0 0 0-.526-.02h-.604z" fill="#f44336" stroke-width="1.067"/></symbol><symbol viewBox="0 0 172 193" id="restql" xmlns="http://www.w3.org/2000/svg"><title>Group</title><g transform="translate(14.767 16.713) scale(.82795)" fill="none"><path d="M171.39 55.799c-.975-6.147-4.673-11.642-10.15-14.805L96.381 3.546C93.217 1.72 89.615.756 85.964.756s-7.253.964-10.415 2.788L10.69 40.992A20.896 20.896 0 0 0 .272 59.035v74.89a20.894 20.894 0 0 0 10.416 18.042l64.859 37.446c3.165 1.827 6.767 2.791 10.417 2.791s7.252-.964 10.415-2.79l64.859-37.445c5.479-3.166 9.178-8.66 10.152-14.808zm-16.516 85.147L90.017 178.39a8.104 8.104 0 0 1-8.108 0l-64.857-37.444a8.109 8.109 0 0 1-4.053-7.021v-74.89a8.109 8.109 0 0 1 4.053-7.021l64.857-37.446c1.254-.725 2.654-1.086 4.054-1.086s2.8.361 4.054 1.086l64.857 37.446a8.106 8.106 0 0 1 4.053 7.021v74.89a8.109 8.109 0 0 1-4.053 7.021z" fill="#83e8c2"/><path d="M158.93 59.035a8.109 8.109 0 0 0-4.053-7.021L90.02 14.568c-1.254-.725-2.654-1.086-4.054-1.086s-2.8.361-4.054 1.086L17.055 52.014a8.106 8.106 0 0 0-4.053 7.021v74.89a8.109 8.109 0 0 0 4.053 7.021l64.857 37.444a8.104 8.104 0 0 0 8.108 0l64.857-37.444a8.109 8.109 0 0 0 4.053-7.021zm-46.766 31.681c.119-.069.242-.118.365-.149.044-.012.088-.01.131-.018.076-.012.152-.029.228-.029l.015.001c.02.001.038.005.059.006.093.005.184.019.273.04l.1.03c.077.025.15.057.223.095.028.014.057.027.084.043.094.057.184.122.263.199.007.008.013.017.021.024.07.071.133.15.188.235.018.029.033.059.05.09.04.072.072.148.099.229a1.512 1.512 0 0 1 .081.46v16.209l-3.278 1.893a1.548 1.548 0 0 0-.678.83 1.533 1.533 0 0 0-.098.514v3.785l-14.038 8.104-.01.004a1.55 1.55 0 0 1-.354.146c-.045.012-.09.011-.135.018-.074.012-.15.029-.225.029l-.014-.001c-.02-.001-.039-.005-.059-.006a1.463 1.463 0 0 1-.273-.041c-.034-.008-.066-.019-.1-.03a1.318 1.318 0 0 1-.223-.094c-.029-.015-.057-.027-.084-.044a1.45 1.45 0 0 1-.263-.198c-.009-.008-.015-.019-.023-.027a1.495 1.495 0 0 1-.185-.232c-.019-.029-.034-.06-.051-.09a1.422 1.422 0 0 1-.098-.229 1.702 1.702 0 0 1-.033-.101 1.487 1.487 0 0 1-.048-.358l-.001-.002v-20.053a1.446 1.446 0 0 1 .727-1.255zM85.24 31.369a1.449 1.449 0 0 1 1.452 0l45.741 26.41a1.45 1.45 0 0 1 0 2.512l-17.366 10.027a1.457 1.457 0 0 1-1.452 0l-15.49-8.943 1.727-.996a1.552 1.552 0 0 0 0-2.688l-13.111-7.57c-.239-.139-.508-.207-.775-.207s-.535.068-.775.207l-3.278 1.893-14.038-8.104a1.451 1.451 0 0 1 0-2.513zM57.59 47.558c.251 0 .501.065.726.194l15.489 8.942-1.727.997a1.552 1.552 0 0 0 0 2.688l1.727.996-15.488 8.943a1.457 1.457 0 0 1-1.452 0L39.499 60.291a1.45 1.45 0 0 1 0-2.512l17.366-10.027c.225-.129.475-.194.725-.194zm-9.56 92.328c-.241 0-.489-.062-.724-.196l-17.365-10.026a1.45 1.45 0 0 1-.726-1.256V75.59c0-.847.694-1.453 1.452-1.453.242 0 .49.062.724.197l17.366 10.025c.449.26.726.738.726 1.257v17.886l-1.727-.997a1.552 1.552 0 0 0-2.327 1.344v15.139c0 .555.295 1.067.775 1.344l3.278 1.894v16.209a1.45 1.45 0 0 1-1.452 1.451zm29.828 14.929a1.452 1.452 0 0 1-2.177 1.257l-17.365-10.026a1.452 1.452 0 0 1-.726-1.257v-17.885l1.726.996c.25.145.515.211.773.211.811 0 1.554-.648 1.554-1.555v-1.993l15.489 8.942c.449.26.726.738.726 1.257zm0-32.768c0 .127-.02.246-.049.36-.009.035-.021.067-.032.101-.026.08-.059.157-.099.229-.017.03-.032.061-.05.09a1.48 1.48 0 0 1-.188.235l-.021.025a1.51 1.51 0 0 1-.264.199c-.026.016-.055.028-.082.043a1.597 1.597 0 0 1-.324.124 1.362 1.362 0 0 1-.278.041c-.018.001-.036.006-.055.006l-.015.001c-.077 0-.155-.018-.233-.03-.043-.007-.084-.005-.125-.017a1.484 1.484 0 0 1-.366-.149l-14.035-8.104v-3.784a1.545 1.545 0 0 0-.776-1.343l-3.276-1.892V91.976c0-.127.02-.246.049-.361.009-.034.021-.066.032-.1a1.33 1.33 0 0 1 .099-.229c.017-.03.032-.062.051-.091.054-.084.116-.163.187-.234l.021-.025c.079-.076.168-.142.263-.199.027-.016.056-.029.084-.043a1.476 1.476 0 0 1 .601-.166c.019 0 .036-.005.055-.005l.015-.001c.078 0 .157.018.236.03.04.007.081.005.122.017.124.031.246.08.366.149l17.361 10.023a1.456 1.456 0 0 1 .726 1.259zm-9.984-45.373a1.448 1.448 0 0 1-.544-.55 1.466 1.466 0 0 1 0-1.413c.121-.219.303-.41.544-.55l14.038-8.104 3.277 1.892c.48.276 1.071.276 1.551 0l3.278-1.893 14.038 8.105a1.45 1.45 0 0 1 0 2.513L86.691 86.7a1.447 1.447 0 0 1-1.452 0zm74.842 51.733c0 .518-.276.997-.726 1.256l-45.741 26.409a1.452 1.452 0 0 1-2.177-1.257v-20.053c0-.519.277-.997.727-1.257l15.488-8.941v1.992c0 .906.743 1.555 1.553 1.555.26 0 .523-.066.774-.21l13.11-7.57a1.55 1.55 0 0 0 .776-1.344v-3.784l14.038-8.105a1.452 1.452 0 0 1 2.177 1.257v20.052zm0-32.764c0 .519-.276.997-.726 1.256l-15.489 8.943v-1.993c0-.906-.744-1.554-1.554-1.554a1.519 1.519 0 0 0-.773.21l-1.727.996V85.616c0-.519.277-.997.727-1.257l17.365-10.025c.234-.135.482-.197.724-.197.758 0 1.453.606 1.453 1.453z" fill="#111d5a"/><g fill="#83e8c2"><path d="M59.402 90.568zM94.485 123.06zM94.771 123.29zM77.775 122.51zM77.072 123.33zM77.418 123.09zM77.856 122.05zM76.749 123.45zM94.119 122.41zM77.131 133.51l-15.489-8.942v1.993c0 .906-.743 1.555-1.554 1.555a1.53 1.53 0 0 1-.773-.211l-1.726-.996v17.885c0 .519.276.997.726 1.257l17.365 10.026a1.452 1.452 0 0 0 2.177-1.257v-20.053a1.454 1.454 0 0 0-.726-1.257zM94.25 122.74zM110.28 111.42zM94.494 100.98c.088-.089.189-.168.303-.232l17.365-10.026-17.365 10.026a1.392 1.392 0 0 0-.303.232zM77.627 122.83zM58.027 90.936zM58.374 90.693zM59.044 90.521l-.015.001c.083-.001.167.015.251.029-.079-.012-.158-.03-.236-.03zM57.819 91.195zM58.696 90.568zM57.589 91.977zM76.043 123.46zM57.67 91.516zM75.677 123.31l-14.035-8.11zM76.401 123.5l.015-.001c-.082.001-.166-.016-.248-.029.078.012.156.03.233.03zM112.16 90.716zM77.662 101.27zM113.64 90.734zM96.237 123.31zM113.33 90.597zM112.89 90.52c-.075 0-.151.018-.228.029.081-.014.162-.029.242-.028l-.014-.001zM141.26 74.137c-.241 0-.489.062-.724.197l-17.365 10.025c-.449.26-.727.738-.727 1.257v17.885l1.727-.996c.25-.145.515-.211.773-.21.81 0 1.554.647 1.554 1.554v1.993l15.489-8.943a1.45 1.45 0 0 0 .726-1.256V75.59c0-.847-.695-1.453-1.453-1.453zM112.96 90.526zM95.523 123.5c.074 0 .15-.018.225-.029-.08.013-.159.028-.238.028l.013.001zM95.451 123.5zM85.238 86.7zM95.078 123.43zM141.26 106.9c-.241 0-.489.062-.724.196l-14.038 8.105v3.784c0 .555-.296 1.067-.776 1.344l-13.11 7.57c-.251.144-.515.21-.774.21-.81 0-1.553-.648-1.553-1.555v-1.992l-15.488 8.941c-.449.26-.727.738-.727 1.257v20.053a1.452 1.452 0 0 0 2.177 1.257l45.741-26.409a1.45 1.45 0 0 0 .726-1.256v-20.053a1.454 1.454 0 0 0-1.454-1.452zM67.871 41.396a1.451 1.451 0 0 0 0 2.513l14.038 8.104 3.278-1.893c.24-.139.508-.207.775-.207s.536.068.775.207l13.111 7.57a1.552 1.552 0 0 1 0 2.688l-1.727.996 15.49 8.943a1.457 1.457 0 0 0 1.452 0l17.366-10.027a1.45 1.45 0 0 0 0-2.512l-45.741-26.41a1.449 1.449 0 0 0-1.452 0zM39.497 57.779a1.45 1.45 0 0 0 0 2.512l17.366 10.027a1.457 1.457 0 0 0 1.452 0l15.488-8.943-1.727-.996a1.552 1.552 0 0 1 0-2.688l1.727-.997-15.489-8.942a1.458 1.458 0 0 0-1.451 0zM49.481 138.43v-16.209l-3.278-1.894a1.55 1.55 0 0 1-.775-1.344v-15.139c0-.906.743-1.555 1.554-1.554.259 0 .523.065.773.21l1.727.997V85.611a1.45 1.45 0 0 0-.726-1.257L31.39 74.33a1.436 1.436 0 0 0-.724-.197c-.758 0-1.452.606-1.452 1.453v52.817c0 .518.276.997.726 1.256l17.365 10.026a1.45 1.45 0 0 0 2.176-1.255zM114.34 108.18l-3.278 1.893 3.278-1.893V91.971zM114.11 91.193zM114.16 91.283z"/></g><g fill="#de5941"><path d="M94.494 100.98a1.45 1.45 0 0 0-.424 1.023v20.053l.001.002c0 .126.02.244.048.358.01.034.021.066.033.101.026.08.059.156.098.229.017.03.032.061.051.09.055.084.115.162.185.232.009.009.015.02.023.027.079.077.169.142.263.198.027.017.055.029.084.044a1.46 1.46 0 0 0 .596.165c.02.001.039.005.059.006.079 0 .158-.016.238-.028.045-.007.09-.006.135-.018.119-.031.238-.08.354-.146l.01-.004 14.038-8.104v-3.785c0-.18.04-.35.098-.514.122-.343.353-.643.678-.83l3.278-1.893V91.977c0-.127-.021-.246-.049-.361-.009-.033-.021-.065-.032-.099a1.266 1.266 0 0 0-.099-.229c-.017-.031-.032-.061-.05-.09a1.425 1.425 0 0 0-.188-.235l-.021-.024a1.41 1.41 0 0 0-.263-.199c-.027-.016-.056-.029-.084-.043a1.509 1.509 0 0 0-.323-.125 1.591 1.591 0 0 0-.273-.04c-.021-.001-.039-.005-.059-.006-.08-.001-.161.015-.242.028-.043.008-.087.006-.131.018-.123.031-.246.08-.365.149l-17.365 10.026a1.447 1.447 0 0 0-.302.233zM77.13 100.74L59.769 90.717a1.424 1.424 0 0 0-.366-.149c-.041-.012-.082-.01-.122-.017-.084-.015-.168-.03-.251-.029-.019 0-.036.005-.055.005-.095.005-.188.02-.278.041-.034.009-.065.02-.099.03a1.406 1.406 0 0 0-.224.095c-.028.014-.057.027-.084.043a1.515 1.515 0 0 0-.263.199l-.021.025c-.07.071-.133.15-.187.234-.019.029-.034.061-.051.091-.04.073-.072.149-.099.229a1.463 1.463 0 0 0-.081.461v16.206l3.276 1.892a1.547 1.547 0 0 1 .776 1.343v3.784l14.035 8.104c.119.068.242.117.366.149.041.012.082.01.125.017.082.014.166.03.248.029.019 0 .037-.005.055-.006.095-.004.188-.019.278-.041.034-.008.065-.019.099-.029.077-.025.152-.058.225-.095.027-.015.056-.027.082-.043.095-.058.185-.123.264-.199l.021-.025c.07-.071.133-.15.188-.235.018-.029.033-.06.05-.09.04-.072.072-.149.099-.229a1.448 1.448 0 0 0 .081-.461v-20.047a1.456 1.456 0 0 0-.726-1.259zM86.689 86.7l17.365-10.026a1.45 1.45 0 0 0 0-2.513l-14.038-8.105-3.278 1.893a1.556 1.556 0 0 1-1.551 0l-3.277-1.892-14.038 8.104c-.241.14-.423.331-.544.55a1.466 1.466 0 0 0 0 1.413c.121.218.303.41.544.55L85.238 86.7a1.447 1.447 0 0 0 1.451 0z"/></g></g></symbol><symbol viewBox="0 0 24 24" id="riot" xmlns="http://www.w3.org/2000/svg"><defs><path d="M13.26 3.04l.58.05.54.07.52.09.49.11.46.13.44.14.41.16.39.17.36.19.33.21.32.22.29.23.26.25.22.22.2.22.19.24.17.24.15.25.15.26.12.27.12.28.1.29.08.31.07.31.05.32.04.34.02.35.01.37v.05l-.02.51-.05.49-.09.48-.13.45-.15.43-.19.4-.22.39-.26.37-.28.34-.31.33-.33.3-.37.28-.39.27-.41.24-.44.22L21 21h-7.04l-3.48-5.14H9.17V21H3V3h9.01l.64.01.61.03zm-4.09 8.52h2.66l.99-.11.75-.35.47-.55.16-.74v-.05l-.17-.75-.47-.54-.74-.32-.96-.11H9.17v3.52z" id="ija"/></defs><use xlink:href="#ija" fill="#ff1744"/><use xlink:href="#ija" fill-opacity="0" stroke="#000" stroke-opacity="0"/></symbol><symbol viewBox="0 0 24 24" id="robot" xmlns="http://www.w3.org/2000/svg"><path d="M12.05 2.804a1.787 1.787 0 0 1 1.788 1.788c0 .661-.357 1.242-.893 1.546v1.135h.893a6.256 6.256 0 0 1 6.256 6.256h.894a.894.894 0 0 1 .893.893v2.681a.894.894 0 0 1-.893.894h-.894v.894a1.787 1.787 0 0 1-1.787 1.787H5.795a1.787 1.787 0 0 1-1.787-1.787v-.894h-.894a.894.894 0 0 1-.894-.894v-2.68a.894.894 0 0 1 .894-.894h.894a6.256 6.256 0 0 1 6.255-6.256h.894V6.138a1.773 1.773 0 0 1-.894-1.546 1.787 1.787 0 0 1 1.788-1.788m-4.022 9.83a2.234 2.234 0 0 0-2.234 2.235 2.234 2.234 0 0 0 2.234 2.234 2.234 2.234 0 0 0 2.234-2.234 2.234 2.234 0 0 0-2.234-2.234m8.043 0a2.234 2.234 0 0 0-2.234 2.234 2.234 2.234 0 0 0 2.234 2.234 2.234 2.234 0 0 0 2.235-2.234 2.234 2.234 0 0 0-2.235-2.234z" fill="#ff5722" stroke-width=".894"/></symbol><symbol viewBox="100 100 800 800" id="rollup" xmlns="http://www.w3.org/2000/svg"><style>.ilst0{fill:url(#ilXMLID_4_)}.ilst1{fill:url(#ilXMLID_5_)}.ilst2{fill:url(#ilXMLID_8_)}.ilst3{fill:url(#ilXMLID_9_)}.ilst4{fill:url(#ilXMLID_11_)}.ilst5{opacity:.3;fill:url(#ilXMLID_16_)}</style><g id="ilXMLID_14_" transform="translate(-54.117 -62.353) scale(1.1129)"><linearGradient id="ilXMLID_4_" x1="444.47" x2="598.47" y1="526.05" y2="562.05" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6533" offset="0"/><stop stop-color="#FF5633" offset=".157"/><stop stop-color="#FF4333" offset=".434"/><stop stop-color="#FF3733" offset=".714"/><stop stop-color="#F33" offset="1"/></linearGradient><path id="ilXMLID_15_" class="ilst0" d="M721 410c0-33.6-8.8-65.1-24.3-92.4-41.1-42.3-130.5-52.1-152.7-.2-22.8 53.2 38.3 112.4 65 107.7 34-6-6-84-6-84 52 98 40 68-54 158S359 779 345 787c-.6.4-1.2.7-1.9 1h368.7c6.5 0 10.7-6.9 7.8-12.7l-96.4-190.8c-2.1-4.1-.6-9.2 3.4-11.5C683 540.6 721 479.8 721 410z" fill="url(#ilXMLID_4_)"/></g><g id="ilXMLID_2_" transform="translate(-54.117 -62.353) scale(1.1129)"><linearGradient id="ilXMLID_5_" x1="420.38" x2="696.38" y1="475" y2="689" gradientUnits="userSpaceOnUse"><stop stop-color="#BF3338" offset="0"/><stop stop-color="#F33" offset="1"/></linearGradient><path id="ilXMLID_10_" class="ilst1" d="M721 410c0-33.6-8.8-65.1-24.3-92.4-41.1-42.3-130.5-52.1-152.7-.2-22.8 53.2 38.3 112.4 65 107.7 34-6-6-84-6-84 52 98 40 68-54 158S359 779 345 787c-.6.4-1.2.7-1.9 1h368.7c6.5 0 10.7-6.9 7.8-12.7l-96.4-190.8c-2.1-4.1-.6-9.2 3.4-11.5C683 540.6 721 479.8 721 410z" fill="url(#ilXMLID_5_)"/></g><linearGradient id="ilXMLID_8_" x1="429.39" x2="469.39" y1="517.16" y2="559.16" gradientTransform="translate(-54.117 -62.353) scale(1.1129)" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6533" offset="0"/><stop stop-color="#FF5633" offset=".157"/><stop stop-color="#FF4333" offset=".434"/><stop stop-color="#FF3733" offset=".714"/><stop stop-color="#F33" offset="1"/></linearGradient><path id="ilXMLID_3_" class="ilst2" d="M329.82 813.46c15.58-8.903 122.41-220.34 227.02-320.5s117.96-66.771 60.094-175.83c0 0-221.46 310.49-301.58 464.06" fill="url(#ilXMLID_8_)" stroke-width="1.113"/><g id="ilXMLID_7_" transform="translate(-54.117 -62.353) scale(1.1129)"><linearGradient id="ilXMLID_9_" x1="502.11" x2="490.11" y1="589.46" y2="417.46" gradientUnits="userSpaceOnUse"><stop stop-color="#FF6533" offset="0"/><stop stop-color="#FF5633" offset=".157"/><stop stop-color="#FF4333" offset=".434"/><stop stop-color="#FF3733" offset=".714"/><stop stop-color="#F33" offset="1"/></linearGradient><path id="ilXMLID_12_" class="ilst3" d="M373 537c134.4-247.1 152-272 222-272 36.8 0 73.9 16.6 97.9 46.1-32.7-52.7-90.6-88-156.9-89H307.7c-4.8 0-8.7 3.9-8.7 8.7V691c13.6-35.1 36.7-85.3 74-154z" fill="url(#ilXMLID_9_)"/></g><linearGradient id="ilXMLID_11_" x1="450.12" x2="506.94" y1="514.21" y2="552.85" gradientTransform="translate(-54.117 -62.353) scale(1.1129)" gradientUnits="userSpaceOnUse"><stop stop-color="#FBB040" offset="0"/><stop stop-color="#FB8840" offset="1"/></linearGradient><path id="ilXMLID_6_" class="ilst4" d="M556.84 492.96c-104.61 100.16-211.44 311.6-227.02 320.5s-41.732 10.016-55.643-5.564c-14.801-16.582-37.837-43.401 86.802-272.65 149.57-274.99 169.15-302.7 247.05-302.7 40.953 0 82.24 18.473 108.95 51.302 1.447 2.337 2.893 4.785 4.34 7.233-45.738-47.074-145.23-57.98-169.93-.222-25.373 59.204 42.622 125.08 72.335 119.85 37.837-6.677-6.677-93.48-6.677-93.48 57.757 108.95 44.403 75.563-60.205 175.72z" fill="url(#ilXMLID_11_)" stroke-width="1.113"/><linearGradient id="ilXMLID_16_" x1="508.33" x2="450.33" y1="295.76" y2="933.76" gradientTransform="translate(-54.117 -62.353) scale(1.1129)" gradientUnits="userSpaceOnUse"><stop stop-color="#FFF" offset="0"/><stop stop-color="#FFF" stop-opacity="0" offset="1"/></linearGradient><path id="ilXMLID_13_" class="ilst5" d="M373.22 547.49c149.57-274.99 169.15-302.7 247.05-302.7 33.719 0 67.661 12.575 93.48 35.277-26.708-30.492-66.326-47.519-105.72-47.519-77.9 0-97.486 27.71-247.05 302.7-124.64 229.25-101.6 256.07-86.802 272.65 2.114 2.337 4.563 4.34 7.122 6.01-13.02-18.919-18.807-62.877 91.922-266.42z" fill="url(#ilXMLID_16_)" opacity=".3" stroke-width="1.113"/></symbol><symbol viewBox="0 0 24 24" id="ruby" xmlns="http://www.w3.org/2000/svg"><path d="M16 9h3l-5 7m-4-7h4l-2 8M5 9h3l2 7m5-12h2l2 3h-3m-5-3h2l1 3h-4M7 4h2L8 7H5m1-5L2 8l10 14L22 8l-4-6H6z" fill="#f44336"/></symbol><symbol viewBox="0 0 144 144" id="rust" xmlns="http://www.w3.org/2000/svg"><path d="M68.252 26.206a3.561 3.561 0 0 1 7.123 0 3.561 3.561 0 0 1-7.123 0M25.766 58.451a3.561 3.561 0 0 1 7.123 0 3.561 3.561 0 0 1-7.123 0m84.97.166a3.561 3.561 0 0 1 7.123 0 3.561 3.561 0 0 1-7.123 0m-74.661 4.88a3.252 3.252 0 0 0 1.651-4.29l-1.58-3.574h6.214v28.01H29.823a43.847 43.847 0 0 1-1.42-16.738zm25.994.688v-8.256h14.798c.764 0 5.397.883 5.397 4.347 0 2.877-3.553 3.908-6.475 3.908zm-20.203 44.452a3.561 3.561 0 0 1 7.123 0 3.561 3.561 0 0 1-7.123 0m52.769.166a3.561 3.561 0 0 1 7.123 0 3.561 3.561 0 0 1-7.123 0m1.101-8.076a3.246 3.246 0 0 0-3.856 2.498l-1.787 8.342a43.847 43.847 0 0 1-36.566-.175l-1.787-8.342a3.246 3.246 0 0 0-3.854-2.497l-7.365 1.581a43.847 43.847 0 0 1-3.808-4.488h35.834c.406 0 .676-.074.676-.443V84.527c0-.369-.27-.442-.676-.442h-10.48V76.05h11.335c1.035 0 5.532.296 6.97 6.045.45 1.768 1.44 7.519 2.116 9.36.674 2.065 3.417 6.19 6.34 6.19h18.501a43.847 43.847 0 0 1-4.06 4.7zm19.898-33.468a43.847 43.847 0 0 1 .093 7.612h-4.499c-.45 0-.631.296-.631.737v2.066c0 4.863-2.742 5.92-5.145 6.19-2.288.258-4.825-.958-5.138-2.358-1.35-7.593-3.6-9.214-7.152-12.016 4.409-2.8 8.996-6.93 8.996-12.457 0-5.97-4.092-9.729-6.881-11.572-3.914-2.58-8.246-3.096-9.415-3.096H39.336A43.847 43.847 0 0 1 63.867 28.52l5.484 5.753a3.243 3.243 0 0 0 4.59.105l6.137-5.869a43.847 43.847 0 0 1 30.017 21.38l-4.201 9.487a3.256 3.256 0 0 0 1.652 4.29zm10.477.154l-.143-1.467 4.327-4.036c.88-.82.55-2.472-.574-2.891l-5.532-2.068-.433-1.428 3.45-4.792c.704-.974.058-2.53-1.127-2.724l-5.833-.949-.7-1.31 2.45-5.38c.502-1.095-.43-2.496-1.636-2.45l-5.92.206-.935-1.135 1.36-5.766c.275-1.17-.913-2.36-2.084-2.085l-5.765 1.359-1.136-.935.207-5.92c.046-1.198-1.357-2.135-2.45-1.637l-5.379 2.452-1.31-.703-.95-5.833c-.193-1.183-1.75-1.83-2.723-1.128l-4.796 3.45-1.425-.432-2.068-5.532c-.42-1.127-2.072-1.452-2.89-.576l-4.036 4.33-1.467-.143-3.117-5.036c-.63-1.02-2.318-1.02-2.946 0l-3.117 5.036-1.467.143-4.037-4.33c-.819-.876-2.47-.551-2.89.576l-2.069 5.532-1.426.432-4.795-3.45c-.974-.703-2.53-.055-2.723 1.128l-.951 5.833-1.31.703-5.379-2.452c-1.093-.5-2.496.439-2.45 1.637l.206 5.92-1.136.935-5.765-1.36c-1.171-.272-2.36.915-2.086 2.086l1.358 5.766-.933 1.135-5.92-.206c-1.193-.035-2.134 1.355-1.637 2.45l2.453 5.38-.703 1.31-5.832.949c-1.185.192-1.827 1.75-1.128 2.724l3.45 4.792-.433 1.428-5.532 2.068c-1.123.42-1.452 2.07-.574 2.891l4.328 4.036-.143 1.467-5.035 3.116c-1.02.63-1.02 2.318 0 2.946l5.035 3.117.143 1.467-4.328 4.037c-.878.818-.549 2.468.574 2.89l5.532 2.068.433 1.428-3.45 4.793c-.701.976-.056 2.532 1.129 2.723l5.831.948.703 1.312-2.453 5.378c-.5 1.093.444 2.5 1.638 2.451l5.917-.207.935 1.136-1.358 5.768c-.275 1.168.915 2.355 2.086 2.08l5.765-1.357 1.137.932-.207 5.921c-.046 1.199 1.357 2.136 2.45 1.636l5.379-2.45 1.31.702.95 5.83c.193 1.187 1.75 1.829 2.725 1.13l4.792-3.453 1.427.435 2.069 5.53c.42 1.123 2.072 1.454 2.89.574l4.037-4.328 1.467.146 3.117 5.035c.628 1.016 2.316 1.018 2.946 0l3.117-5.035 1.467-.146 4.036 4.328c.818.88 2.47.549 2.89-.574l2.068-5.53 1.428-.435 4.793 3.453c.974.699 2.53.055 2.722-1.13l.952-5.83 1.31-.703 5.378 2.451c1.093.5 2.493-.435 2.45-1.636l-.206-5.92 1.135-.933 5.765 1.357c1.171.275 2.36-.912 2.085-2.08l-1.358-5.768.932-1.136 5.92.207c1.194.048 2.138-1.358 1.636-2.451l-2.45-5.378.7-1.312 5.833-.948c1.187-.19 1.831-1.747 1.127-2.723l-3.45-4.793.433-1.428 5.532-2.068c1.125-.422 1.454-2.072.574-2.89l-4.327-4.037.143-1.467 5.035-3.117c1.02-.628 1.021-2.315.001-2.946z" fill="#ff7043" stroke-width="1.146"/></symbol><symbol viewBox="0 0 500 500" id="sass" xmlns="http://www.w3.org/2000/svg"><path d="M422.676 96.573c-12.192-47.839-91.508-63.557-166.575-36.892-44.68 15.877-93.029 40.786-127.81 73.311-41.349 38.675-47.943 72.328-45.216 86.395 9.583 49.622 77.585 82.069 105.535 106.126v.144c-8.246 4.05-68.565 34.584-82.684 65.799-14.893 32.932 2.372 56.556 13.804 59.742 35.424 9.859 71.764-7.866 91.311-37.01 18.853-28.12 17.28-64.422 9.086-82.487 11.3-2.976 24.476-4.314 41.218-2.36 47.248 5.52 56.517 35.017 54.747 47.366-1.77 12.35-11.681 19.14-14.998 21.186-3.317 2.045-4.326 2.766-4.05 4.287.405 2.215 1.94 2.137 4.758 1.652 3.894-.656 24.804-10.042 25.709-32.828 1.14-28.933-26.587-61.302-75.684-60.45-20.216.354-32.933 2.268-42.123 5.69-.681-.774-1.363-1.547-2.084-2.307-30.35-32.382-86.46-55.285-84.088-98.824.866-15.823 6.372-57.5 107.817-108.052 83.104-41.415 149.637-30.009 161.135-4.76 16.427 36.08-35.554 103.137-121.858 112.812-32.88 3.684-50.198-9.059-54.498-13.804-4.536-4.995-5.204-5.218-6.909-4.287-2.753 1.533-1.01 5.938 0 8.574 2.583 6.712 13.15 18.603 31.176 24.515 15.863 5.205 54.459 8.063 101.156-9.99 52.283-20.255 93.12-76.523 81.125-123.548zM200.213 340.34c3.92 14.5 3.487 28.016-.564 40.248a65.289 65.289 0 0 1-3.225 7.97c-3.12 6.477-7.316 12.534-12.442 18.132-15.653 17.069-37.507 23.532-46.88 18.092-10.122-5.874-5.048-29.944 13.083-49.11 19.52-20.636 47.602-33.903 47.602-33.903l-.039-.079 2.465-1.35z" fill="#ec407a" stroke="#ec407a" stroke-width="16.286552999999998"/></symbol><symbol viewBox="0 0 300 300" id="sbt" xmlns="http://www.w3.org/2000/svg"><path d="M105.46 209.517c-7.875 0-13.452-7.521-13.452-15.37v-.327c0-7.848 5.578-13.735 13.452-13.735h164.05c1.476-4.905 2.625-11.446 3.281-17.986h-137.81c-7.875 0-14.273-6.05-14.273-13.898s6.398-13.898 14.273-13.898h137.31c-.82-6.54-1.969-13.081-3.773-17.986h-104.01c-7.875 0-14.273-6.05-14.273-13.898s6.398-13.898 14.273-13.898h91.87c-21.327-37.607-60.864-61.315-106.14-61.315-67.918 0-123.04 54.448-123.04 122.3 0 67.856 55.122 123.28 123.04 123.28 46.59 0 87.112-25.507 107.95-63.114h-152.73z" fill="#0277bd" stroke-width="1.638"/></symbol><symbol viewBox="0 0 256 256" id="scala" xmlns="http://www.w3.org/2000/svg"><path fill="#f44336" fill-rule="evenodd" stroke-width=".3" d="M59.607 50.647l149.097-21.982v49.488L59.607 100.135zM59.593 114.08L208.69 92.098v49.488L59.593 163.568zM59.587 177.358l149.097-21.982v49.488L59.587 226.846z"/><path fill="#f44336" fill-rule="evenodd" stroke-width=".3" d="M62.425 91.414l95.605 30.923-2.832 8.757-95.605-30.922zM113.084 61.13l95.604 30.922-2.832 8.757-95.605-30.922zM62.425 154.79l95.605 30.922-2.833 8.758-95.604-30.923zM113.097 124.408l95.604 30.923-2.832 8.757-95.605-30.922z"/></symbol><symbol viewBox="0 0 24 24" id="settings" xmlns="http://www.w3.org/2000/svg"><path d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.64.07-.97 0-.33-.03-.66-.07-1l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65A.506.506 0 0 0 14 2h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 11c-.04.34-.07.67-.07 1 0 .33.03.65.07.97l-2.11 1.66c-.19.15-.25.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1.01c.52.4 1.06.74 1.69.99l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.26 1.17-.59 1.69-.99l2.49 1.01c.22.08.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.66z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="shaderlab" xmlns="http://www.w3.org/2000/svg"><path d="M9.11 17H6.5l-4.91-5L6.5 7h2.61l1.31-2.26L17.21 3l1.87 6.74L17.77 12l1.31 2.26L17.21 21l-6.79-1.74L9.11 17m.14-.25l5.13 1.38L11.42 13H5.5l3.75 3.75m6.87.38L17.5 12l-1.38-5.13L13.15 12l2.97 5.13M9.25 7.25L5.5 11h5.92l2.96-5.13-5.13 1.38z" fill="#1976d2"/></symbol><symbol viewBox="0 0 24 24" id="slim" xmlns="http://www.w3.org/2000/svg"><path d="M6.959 2.5a4.605 4.605 0 0 0-4.615 4.615v9.957a4.605 4.605 0 0 0 4.615 4.615h9.957a4.605 4.605 0 0 0 4.615-4.615V7.115A4.605 4.605 0 0 0 16.916 2.5zm4.938 2.691a6.811 6.811 0 0 1 6.81 6.813H13.43L9.938 7.287l.699 4.717H5.086a6.811 6.811 0 0 1 6.81-6.813z" fill="#f57f17"/></symbol><symbol id="smarty" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.iust0{fill:#ffce00}</style><path class="iust0" d="M9.14 20.606c0 .556.398.953.954.953h3.812c.556 0 .953-.397.953-.953v-.953H9.141zM12 2.5c-3.653 0-6.671 3.018-6.671 6.671 0 2.303 1.112 4.289 2.859 5.48v2.144c0 .556.397.953.953.953h5.718c.556 0 .953-.397.953-.953V14.65c1.747-1.191 2.86-3.177 2.86-5.48 0-3.653-3.019-6.671-6.672-6.671zm2.7 10.563l-.794.555v2.224h-3.812v-2.224l-.794-.555A4.712 4.712 0 0 1 7.235 9.17 4.78 4.78 0 0 1 12 4.405a4.78 4.78 0 0 1 4.765 4.765 4.712 4.712 0 0 1-2.065 3.892z"/></symbol><symbol viewBox="0 0 200 200" id="snyk" xmlns="http://www.w3.org/2000/svg"><title>Group 2</title><g transform="translate(15.255 18.22) scale(1.8477)" fill="none" fill-rule="evenodd"><path d="M65.161 24.997c-1.656 5.974-5.255 23.587-5.255 23.587s-6.618-2.464-14.148-2.476h-.055c-.413.002-.822.012-1.23.026v41.649h6.677v.003h5.815v-.003h20.858c.111-8.177-2.036-27.066-2.036-27.066-1.088-2.279.46-7.668.46-7.668-8.869-9.092-11.086-28.051-11.086-28.051zm-3.357 43.958c5.476 0 1.381 4.64.9 5.168H52.35c.944-1.18 4.504-5.168 9.453-5.168z" fill="#607d8b" stroke-width="1.6"/><path d="M26.366 24.995s-2.217 18.961-11.087 28.053c0 0 1.548 5.391.46 7.669 0 0-2.15 18.895-2.038 27.066h19.273v.003h7.079v-.003h5.744V46.107h-.025c-7.532.013-14.151 2.478-14.151 2.478s-3.6-17.615-5.255-23.59zm3.264 43.96c4.95 0 8.51 3.987 9.452 5.168H28.73c-.479-.528-4.573-5.168.9-5.168z" fill="#90a4ae" stroke-width="1.6"/><g transform="translate(23.76 77.45) scale(1.5998)"><g transform="translate(17.526)"><path d="M7.357.06H.177v.075C.177 2.64 2.345 4.67 4.89 4.67 7.431 4.67 9.6 2.64 9.6.135V.059z" fill="#455a64"/><path d="M1.972.06v.075a2.692 2.692 0 1 0 5.386 0V.059z" fill="#fff"/><path d="M5.496.06H4.234c-.012 0-.023.005-.034.007.157.033.243.388.21.624a.721.721 0 0 1-.71.617c.102.471.487.85.997.922a1.188 1.188 0 0 0 1.35-1.007C6.112.743 5.881.06 5.495.06z" fill="#37474f"/></g><path d="M7.552.06H.372v.075c0 2.505 2.17 4.535 4.712 4.535 2.544 0 4.712-2.03 4.712-4.535V.059z" fill="#455a64"/><path d="M2.168.06v.075a2.692 2.692 0 1 0 5.385 0V.059z" fill="#fff"/><path d="M5.692.06H4.428c-.01 0-.022.005-.032.007.156.033.242.388.21.624a.72.72 0 0 1-.712.617c.104.471.488.85.999.922A1.187 1.187 0 0 0 6.24 1.223C6.308.743 6.078.06 5.69.06z" fill="#37474f"/></g><path d="M25.514-.27l-4.202 7.697C19.838 10.17 6.858 34.465 6.858 43.243v.516L12.8 59.573c-.8 7.258-2.203 21.643-1.78 28.21h5.73c-.354-3.787.648-17.008 1.903-28.25l.076-.677-1.075-2.892c3.694-3.868 6.285-9.193 8.073-14.261l.174 1.235 5.869 9.629 2.291-.983c.058-.024 5.935-2.523 11.643-2.523 5.672 0 11.646 2.5 11.702 2.525l2.29.976 5.86-9.626.23-1.608c1.769 5.117 4.358 10.536 8.07 14.49l-1.127 3.035.076.678c1.259 11.286 2.266 24.564 1.916 28.252h5.677c.406-6.567-1.05-20.952-1.848-28.208l5.838-15.817v-.514c0-8.779-12.876-33.074-14.347-35.816L65.923-.27l-5.897 41.229-2.723 4.478c-2.628-.882-7.1-2.11-11.603-2.11-4.498 0-8.94 1.225-11.557 2.108l-2.722-4.476-2.07-14.452a.832.832 0 0 0 .006-.071l-.016-.004zm-3.166 18.39l1.206 8.407c-.46 3.143-2.561 15.47-8.198 23.24l-2.598-6.99c.325-4.554 5.067-15.462 9.59-24.656zm46.763 0c4.523 9.194 9.267 20.104 9.592 24.657L76.166 49.6c-6.09-8.553-8-22.459-8.166-23.73z" fill="#607d8b" stroke-width="1.6"/></g></symbol><symbol viewBox="0 0 24 24" id="solidity" xmlns="http://www.w3.org/2000/svg"><path d="M5.8 14.05l6.253 8.61 6.252-8.61-6.254 3.807z" fill="#0288d1" stroke-width="4.553" stroke-linejoin="round"/><path d="M12.051 1.347L5.8 11.833l6.252 3.807 6.254-3.807z" fill="#0288d1" stroke-width="5.025" stroke-linejoin="round"/></symbol><symbol viewBox="0 0 120 120" id="sonar" xmlns="http://www.w3.org/2000/svg"><style>.a,.b{fill:#fff}.b{stroke:#fff;stroke-miterlimit:10}</style><path d="M115.45 23.033S97.961 33.27 97.534 33.412c-.427.284-.852.57-1.137.854-1.422 1.421-1.848 3.41-1.422 5.26.285.852.711 1.849 1.422 2.56.711.71 1.564 1.137 2.559 1.422 1.848.426 3.84 0 5.262-1.422.426-.427.709-.853.851-1.28l.143-.427 2.56-4.692zm-39.102 9.242c-27.441 0-31.99 13.08-31.99 29.29 0 3.838.569 7.962-1.99 11.942-3.84 5.972-8.957 5.828-10.236 5.828-1.706 0-7.962-.993-8.246-2.841h.994c6.682 0 11.658-5.404 11.658-12.655v-2.56h-5.686c-4.123 0-7.82 1.849-10.238 5.12-2.417-3.271-6.113-5.12-10.236-5.12h-5.83v2.56c0 7.11 5.688 12.795 12.797 12.795h1.848c0 4.124 5.687 20.332 47.63 20.332 16.352 0 40.665-2.843 40.665-33.697 0-5.829-1.848-11.23-4.691-15.78-.996.284-1.992.568-3.13.568a8.92 8.92 0 0 1-8.956-8.957c0-.995.141-1.991.425-2.986-4.265-2.702-8.53-3.838-14.787-3.838z" fill="#1e88e5" stroke-width="1.422"/></symbol><symbol viewBox="0 0 412 395" id="stylelint" xmlns="http://www.w3.org/2000/svg"><title>stylelint-icon-white</title><g transform="translate(31.478 29.499) scale(.84775)" fill="#cfd8dc" fill-rule="evenodd"><path d="M208.8 393.05c45.057-161.12 43.75-161.85 76.32-276.73l7.832 4.523c4.255 2.458 7.738.448 7.738-4.455V61.602c8.643-30.27 15.416-53.66 17.4-60.693h35.287l58.618 54.304-38.498 33.27 29.11 31.473-191.86 273.09c-.938 1.542-2.244 1.19-1.947 0zm20.96-347.28c1.733 0 3.148.958 3.148 2.147v28.077c0 1.186-1.415 2.15-3.147 2.15h-47.396c-1.742 0-3.153-.96-3.153-2.15V47.917c0-1.185 1.41-2.147 3.153-2.147h47.396z"/><path d="M288.26 14.688l-52.14 30.1c.605.92.973 1.98.973 3.136v28.078c0 1.457-.565 2.77-1.496 3.83l52.663 30.402c3.59 2.073 6.535.377 6.535-3.764V18.456c0-4.145-2.944-5.836-6.535-3.768zM175.02 76V47.923c0-1.15.368-2.21.966-3.13l-52.14-30.105c-3.588-2.068-6.53-.376-6.53 3.768v88.013c0 4.14 2.938 5.84 6.53 3.76l52.66-30.405c-.926-1.06-1.487-2.37-1.487-3.827z"/><path d="M201.25 393.05h1.947c-45.05-161.12-43.753-161.85-76.32-276.73l-7.833 4.523c-4.253 2.458-7.737.448-7.737-4.455V61.602C102.662 31.332 95.892 7.942 93.902.909H58.619L.002 55.213l38.494 33.27-29.11 31.473z"/><circle cx="204.57" cy="122.54" r="14.231"/><circle cx="204.57" cy="207.16" r="14.231"/><circle cx="204.57" cy="291.78" r="14.23"/></g></symbol><symbol viewBox="0 0 412 395" id="stylelint_light" xmlns="http://www.w3.org/2000/svg"><title>stylelint-icon-black</title><g transform="translate(31.478 29.499) scale(.84775)" fill="#546e7a" fill-rule="evenodd"><path d="M208.8 393.05c45.057-161.12 43.75-161.85 76.32-276.73l7.832 4.523c4.255 2.458 7.738.448 7.738-4.455V61.602c8.643-30.27 15.416-53.66 17.4-60.693h35.287l58.618 54.304-38.498 33.27 29.11 31.473-191.86 273.09c-.938 1.542-2.244 1.19-1.947 0zm20.96-347.28c1.733 0 3.148.958 3.148 2.147v28.077c0 1.186-1.415 2.15-3.147 2.15h-47.396c-1.742 0-3.153-.96-3.153-2.15V47.917c0-1.185 1.41-2.147 3.153-2.147h47.396z"/><path d="M288.26 14.688l-52.14 30.1c.605.92.973 1.98.973 3.136v28.078c0 1.457-.565 2.77-1.496 3.83l52.663 30.402c3.59 2.073 6.535.377 6.535-3.764V18.456c0-4.145-2.944-5.836-6.535-3.768zM175.02 76V47.923c0-1.15.368-2.21.966-3.13l-52.14-30.105c-3.588-2.068-6.53-.376-6.53 3.768v88.013c0 4.14 2.938 5.84 6.53 3.76l52.66-30.405c-.926-1.06-1.487-2.37-1.487-3.827z"/><path d="M201.25 393.05h1.947c-45.05-161.12-43.753-161.85-76.32-276.73l-7.833 4.523c-4.253 2.458-7.737.448-7.737-4.455V61.602C102.662 31.332 95.892 7.942 93.902.909H58.619L.002 55.213l38.494 33.27-29.11 31.473z"/><circle cx="204.57" cy="122.54" r="14.231"/><circle cx="204.57" cy="207.16" r="14.231"/><circle cx="204.57" cy="291.78" r="14.23"/></g></symbol><symbol viewBox="0 0 200.00001 200.00001" id="stylus" xmlns="http://www.w3.org/2000/svg"><path d="M126.814 155.9c14.64-17.51 16.362-35.595 5.024-69.18-7.177-21.24-19.09-37.602-10.334-50.807 9.329-14.065 29.135-.43 12.63 18.371l3.301 2.297c19.806 2.296 29.566-24.83 14.783-32.58C113.179 3.621 79.02 42.803 94.09 88.156c6.458 19.232 15.5 39.613 8.18 55.83-6.314 13.923-18.514 22.103-26.695 22.39-17.079.862-5.74-38.32 13.922-48.08 1.722-.861 4.162-2.01 1.866-4.88-24.256-2.727-38.464 8.468-46.645 24.112-23.825 45.497 45.21 62.29 82.095 18.371z" fill="#c0ca33" stroke-width="1.435"/></symbol><symbol viewBox="0 0 24 24" id="swc" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="jba"><stop offset="0" stop-color="#791223"/><stop offset="1" stop-color="#d92f3c"/></linearGradient><linearGradient xlink:href="#jba" id="jbb" x1="12.356" y1="21.559" x2="12.356" y2="2.949" gradientUnits="userSpaceOnUse"/></defs><path d="M6 3c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6 3 6.5V19a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.5c0-.5-.17-.93-.46-1.27l-1.39-1.68C18.88 3.21 18.47 3 18 3H6zm-.07 1h12l.94 1H5.12l.81-1z" fill="url(#jbb)"/><path style="line-height:125%" d="M11.053 11.918h-.008c-.244.022-.475.054-.676.11a2.9 2.9 0 0 0-.856.412 3.399 3.399 0 0 0-.67.683 9.36 9.36 0 0 0-.586.95c-.07.131-.134.244-.201.365v.001h-.002l-.768 1.372-.003-.001c-.136.253-.264.485-.38.686-.123.212-.26.39-.411.539a1.599 1.599 0 0 1-.52.34c-.04.016-.092.024-.138.036h-.567v1.383H5.834v-.001c.245-.02.477-.053.679-.11a2.9 2.9 0 0 0 .856-.411c.245-.185.469-.413.67-.683.195-.275.39-.591.585-.95.07-.131.135-.244.202-.366l.004.001.002-.002.02-.038H10.948v-1.378h-.19v-.001H9.624c.125-.234.246-.452.355-.64.123-.21.259-.39.41-.538.152-.148.325-.26.52-.34.04-.015.091-.024.136-.035h.57V13.3h-.002v-1.381h-.56v-.001z" font-weight="400" font-size="40" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#fff"/></symbol><symbol viewBox="0 0 24 24" id="swift" xmlns="http://www.w3.org/2000/svg"><path d="M17.09 19.72c-2.36 1.36-5.59 1.5-8.86.1A13.807 13.807 0 0 1 2 14.5c.67.55 1.46 1 2.3 1.4 3.37 1.57 6.73 1.46 9.1 0-3.37-2.59-6.24-5.96-8.37-8.71-.45-.45-.78-1.01-1.12-1.51 8.28 6.05 7.92 7.59 2.41-1.01 4.89 4.94 9.43 7.74 9.43 7.74.16.09.25.16.36.22.1-.25.19-.51.26-.78.79-2.85-.11-6.12-2.08-8.81 4.55 2.75 7.25 7.91 6.12 12.24-.03.11-.06.22-.05.39 2.24 2.83 1.64 5.78 1.35 5.22-1.21-2.39-3.48-1.65-4.62-1.17z" fill="#fe5e2f"/></symbol><symbol viewBox="0 0 24 24" id="table" xmlns="http://www.w3.org/2000/svg"><path d="M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5L13 3.5m4 7.5h-4v2h1l-2 1.67L10 13h1v-2H7v2h1l3 2.5L8 18H7v2h4v-2h-1l2-1.67L14 18h-1v2h4v-2h-1l-3-2.5 3-2.5h1v-2z" fill="#8bc34a"/></symbol><symbol viewBox="0 0 200 200" id="terraform" xmlns="http://www.w3.org/2000/svg"><g transform="translate(177.03 -58.705) scale(.92881)" fill="#5c6bc0" stroke="#b0aff5" stroke-linejoin="round"><g stroke-width=".288"><path transform="skewY(26.439) scale(.89541 1)" d="M-203.8 170.95h64.714v51.88H-203.8zM-124.37 171.04h64.714v51.88h-64.714zM-124.37 236.09h64.714v51.88h-64.714z"/></g><path transform="skewY(-22.59) scale(-.92328 1)" stroke-width=".284" d="M-19.172 128.27h62.76v51.88h-62.76z"/></g></symbol><symbol viewBox="0 0 24 24" id="test-js" xmlns="http://www.w3.org/2000/svg"><path d="M5 19a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1c0-.21-.07-.41-.18-.57L13 8.35V4h-2v4.35L5.18 18.43c-.11.16-.18.36-.18.57m1 3a3 3 0 0 1-3-3c0-.6.18-1.16.5-1.63L9 7.81V6a1 1 0 0 1-1-1V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1v1.81l5.5 9.56c.32.47.5 1.03.5 1.63a3 3 0 0 1-3 3H6m7-6l1.34-1.34L16.27 18H7.73l2.66-4.61L13 16m-.5-4a.5.5 0 0 1 .5.5.5.5 0 0 1-.5.5.5.5 0 0 1-.5-.5.5.5 0 0 1 .5-.5z" fill="#ffca28"/></symbol><symbol viewBox="0 0 24 24" id="test-jsx" xmlns="http://www.w3.org/2000/svg"><path d="M5 19a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1c0-.21-.07-.41-.18-.57L13 8.35V4h-2v4.35L5.18 18.43c-.11.16-.18.36-.18.57m1 3a3 3 0 0 1-3-3c0-.6.18-1.16.5-1.63L9 7.81V6a1 1 0 0 1-1-1V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1v1.81l5.5 9.56c.32.47.5 1.03.5 1.63a3 3 0 0 1-3 3H6m7-6l1.34-1.34L16.27 18H7.73l2.66-4.61L13 16m-.5-4a.5.5 0 0 1 .5.5.5.5 0 0 1-.5.5.5.5 0 0 1-.5-.5.5.5 0 0 1 .5-.5z" fill="#00bcd4"/></symbol><symbol viewBox="0 0 24 24" id="test-ts" xmlns="http://www.w3.org/2000/svg"><path d="M5 19a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1c0-.21-.07-.41-.18-.57L13 8.35V4h-2v4.35L5.18 18.43c-.11.16-.18.36-.18.57m1 3a3 3 0 0 1-3-3c0-.6.18-1.16.5-1.63L9 7.81V6a1 1 0 0 1-1-1V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1v1.81l5.5 9.56c.32.47.5 1.03.5 1.63a3 3 0 0 1-3 3H6m7-6l1.34-1.34L16.27 18H7.73l2.66-4.61L13 16m-.5-4a.5.5 0 0 1 .5.5.5.5 0 0 1-.5.5.5.5 0 0 1-.5-.5.5.5 0 0 1 .5-.5z" fill="#0288d1"/></symbol><symbol viewBox="0 0 500 500" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="tex" xmlns="http://www.w3.org/2000/svg"><g font-weight="400" font-size="40" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#42a5f5" stroke-linejoin="miter"><text style="line-height:125%" x="9.914" y="364.919"><tspan x="9.914" y="364.919" font-size="287.5">T</tspan></text><text style="line-height:125%" x="136.374" y="435.558"><tspan x="136.374" y="435.558" font-size="287.5">E</tspan></text><text style="line-height:125%" x="307.819" y="361.201"><tspan x="307.819" y="361.201" font-size="287.5">X</tspan></text></g></symbol><symbol viewBox="0 0 24 24" id="todo" xmlns="http://www.w3.org/2000/svg"><path d="M3 5h6v6H3V5m2 2v2h2V7H5m6 0h10v2H11V7m0 8h10v2H11v-2m-6 5l-3.5-3.5 1.41-1.41L5 17.17l4.59-4.58L11 14l-6 6z" fill="#42a5f5"/></symbol><symbol id="travis" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><style id="jkstyle2">.jkst0{fill:#cb3349}.jkst1{fill:#f4edae}.jkst2{fill:#e6ccad}.jkst3{fill:#656c67}.jkst4{fill:#e5caa3}.jkst5{fill:#c7b39a}.jkst6{fill:#ebd599}.jkst7{fill:#2d3136}.jkst8{fill:#edf6fa}.jkst9{opacity:.8}.jkst10{opacity:.75;fill:#ebd599}</style><g id="jkg99" transform="translate(11.017 12.484) scale(.8858)"><g id="jkg10"><path class="jkst0" d="M47.781 86.572s-31.118 21.903-32.335 30.247l2.335-.48S55.045 91.64 84.584 88.628l.669-3.749z" id="jkpath4" fill="#cb3349"/><path class="jkst0" d="M96.629 83.442l-24.511 17.385 1.325 1.063c.999-.806 43.539-13.798 43.539-13.798l8.969-5.623c-6.018.749-29.322.973-29.322.973z" id="jkpath6" fill="#cb3349"/><path class="jkst0" d="M117.932 104.469c17.405 0 43.495-17.046 43.495-17.046l-8.434-1.605c-.417.417-13.6-.462-13.6-.462l-6.258-1.738-14.951 17.036-1.217 2.956c1.075-.437.965.859.965.859z" id="jkpath8" fill="#cb3349"/></g><path class="jkst0" d="M174.728 158.832l-5.377 1.514-24.843-.537-15.541-12.085-18.784 4.7-21.726-1.88-12.166 13.294-22.828 6.819-11.398-3.534-.574-.494 5.116 12.527s11.588 12.424 18.061 13.885c6.472 1.461 18.165-.105 26.935-1.463 8.769-1.357 15.764-4.489 18.582-9.603 2.818-5.117 3.236-6.578 3.236-6.578s8.353 11.797 15.556 13.155c7.203 1.357 28.605-5.952 28.605-5.952s13.051-3.549 15.346-8.038c2.297-4.489 8.353-19.209 8.353-19.209zM44.456 169.038l-.361-.166-2.013-1.736z" id="jkpath12" fill="#cb3349"/><g id="jkg97"><path class="jkst1" d="M195.832 70.085a48.125 48.125 0 0 0-.21-2.009 26.472 26.472 0 0 0-.215-1.424c-1.793-1.509-3.831-2.851-5.952-4.071-2.299-1.343-4.704-2.546-7.159-3.663-2.438-1.15-4.942-2.191-7.461-3.207a134.313 134.313 0 0 0-3.798-1.477c-1.269-.495-2.55-.956-3.835-1.424 2.697.447 5.366 1.059 8.015 1.741 1.723.446 3.437.945 5.14 1.477-12.112-31.655-41.07-52.27-72.687-52.27-31.622 0-60.577 20.615-72.686 52.27a109.044 109.044 0 0 1 5.137-1.477c2.653-.682 5.323-1.294 8.018-1.741-1.289.468-2.567.929-3.84 1.424-1.267.472-2.536.967-3.798 1.477-2.519 1.016-5.016 2.057-7.46 3.207-2.45 1.117-4.857 2.32-7.156 3.663-2.121 1.219-4.157 2.562-5.957 4.071-.075.457-.151.951-.21 1.424a51.768 51.768 0 0 0-.21 2.009 51.354 51.354 0 0 0-.177 4.061 59.216 59.216 0 0 0 .5 8.11c.37 2.692.864 5.366 1.595 7.951.36 1.295.768 2.572 1.24 3.808.237.617.495 1.225.764 1.816.134.294.274.585.413.864l.172.328c.199.101.408.204.607.3l1.204.575c.671.305 1.6.746 2.368 1.09.043-.037.086-.075.123-.114l-2.235-8.513c.474-.13 4.718-1.225 12.032-2.617a38.816 38.816 0 0 1-1.772-.381c-1.665-.414-3.309-.919-4.899-1.564a22.415 22.415 0 0 1-2.309-1.115c-.742-.426-1.472-.908-2.037-1.548 8.036 2.622 24.64 1.434 39.399-.091 13.499-1.391 27.029-2.293 40.63-2.32 13.602.027 27.137.929 40.63 2.32 14.766 1.525 31.37 2.713 39.405.091-.564.64-1.293 1.123-2.035 1.548a22.5 22.5 0 0 1-2.308 1.115c-1.592.645-3.234 1.15-4.899 1.564-.247.059-.496.113-.743.166 8.02 1.488 12.689 2.697 13.188 2.831l-2.138 8.11c.43-.194.864-.381 1.29-.574l1.202-.575c.2-.097.403-.199.607-.3l.166-.328c.146-.279.286-.57.419-.864.27-.591.528-1.199.764-1.816a42.235 42.235 0 0 0 1.241-3.808c.731-2.585 1.225-5.259 1.595-7.951.345-2.685.526-5.398.501-8.11a50.874 50.874 0 0 0-.179-4.059z" id="jkpath14" fill="#f4edae"/><path class="jkst2" d="M116.787 182.661c-1.064.16-2.128.295-3.186.375-.682.033-1.404.102-2.059.102l-.242.005c.822-1.837 1.446-3.26 1.919-4.339.963 1.08 2.188 2.417 3.568 3.857z" id="jkpath16" fill="#e6ccad"/><path class="jkst2" d="M119.101 185.018c3.304 3.272 7.398 5.146 11.904 5.479-7.569 3.074-14.702 4.26-20.197 4.63-5.478.367-11.032-.279-16.474-1.771.456-.082.79-.14 1.193-.189.447-.054 10.206-1.327 14.605-7.868l.413.009 1.08-.009c.731 0 1.395-.06 2.094-.087a43.69 43.69 0 0 0 4.878-.703c.167.171.333.338.504.509z" id="jkpath18" fill="#e6ccad"/><path class="jkst3" d="M128.464 87.071a98.82 98.82 0 0 1-1.048 1.343c-1.933 2.444-4.614 5.57-7.794 8.627a369.585 369.585 0 0 0-11.404-.177c-6.46 0-12.655.171-18.537.457 8.311-3.449 18.296-6.818 29.109-8.842a113.323 113.323 0 0 1 9.674-1.408z" id="jkpath20" fill="#656c67"/><path class="jkst3" d="M79.821 90.792c-2.966 2.084-6.317 4.744-9.566 7.971a360.155 360.155 0 0 0-21.567 2.81c9.207-4.232 19.713-8.127 31.133-10.781z" id="jkpath22" fill="#656c67"/><path class="jkst3" d="M181.48 107.969l-3.384 23.679-16.212 11.355-42.283-4.807-6.365-20.961a1.383 1.383 0 0 0-1.108-.971c-1.567-.253-2.953-.382-4.108-.382-1.16 0-2.541.129-4.115.382-.522.086-.95.461-1.106.971l-6.209 20.45-42.047 9.357-16.662-11.672-3.283-26.572c.715-.404 1.441-.806 2.176-1.209 1.031-.222 2.191-.457 3.475-.704l3.094 25.073c.048.392.264.741.586.967l11.462 8.032a1.425 1.425 0 0 0 1.101.213l34.57-7.692c.119-.027.237-.069.344-.124a1.39 1.39 0 0 0 .682-.827l6.225-20.498c1.67-.43 5.947-1.429 9.706-1.429 3.749 0 8.03.999 9.701 1.429l6.225 20.498c.161.532.624.912 1.176.977l34.57 3.927c.335.037.677-.05.952-.242l11.469-8.025c.31-.22.52-.566.573-.946l3.062-21.421c2.301.444 4.224.846 5.733 1.172z" id="jkpath24" fill="#656c67"/><path class="jkst3" d="M185.751 93.119l-2.976 11.29c-6.086-1.342-19.456-3.975-37.654-5.747 5.946-2.535 12-5.715 17.531-9.69 10.829 1.53 18.78 3.169 23.099 4.147z" id="jkpath26" fill="#656c67"/><g id="jkg32"><path class="jkst4" d="M63.841 128.441c2.357-1.274 5.021-1.085 9.19-1.079.447.011.908.005 1.39-.005.41-.005.822-.011 1.258-.022 4.296-.042 7.869.366 7.806-6.381-.065-6.746-3.062-12.198-7.354-12.155-4.297.037-8.454 5.564-8.197 12.306.07 1.756.328 3.023.742 3.937-3.745.938-4.777 3.254-4.835 3.399zm51.657-27.749a46.634 46.634 0 0 1-5.249 3.712l-6.097 3.68a52.065 52.065 0 0 0-7.331 1.467 1.216 1.216 0 0 0-.317.14 1.406 1.406 0 0 0-.629.794l-6.209 20.46-33.185 7.38-10.452-7.321-3.041-24.634c5.936-1.09 13.874-2.352 23.41-3.42a56.802 56.802 0 0 0-2.955 3.855l-5.677 8.149 8.266-5.511c.123-.086 5.387-3.549 13.998-7.761a377.407 377.407 0 0 1 35.468-.99z" id="jkpath28" fill="#e5caa3"/><path class="jkst4" d="M151.835 125.675c-.042-.16-.945-2.873-4.942-2.397.461-1.003.666-2.356.521-4.21-.528-6.731-4.443-12.08-8.735-11.931-4.292.152-7.042 5.731-6.805 12.478.236 6.741 3.84 6.694 8.132 6.543 5.77-.107 8.939-1.88 11.829-.483zm21.18-19.385l-2.992 20.944-10.539 7.379-33.141-3.766-6.183-20.363a1.41 1.41 0 0 0-.945-.934c-.205-.06-4.308-1.23-8.659-1.607l.795-.053c.687-.049 12.118-1.451 25.767-6.157 15.115 1.161 27.458 3.02 35.897 4.557z" id="jkpath30" fill="#e5caa3"/></g><g id="jkg38"><path class="jkst5" d="M63.841 128.441c2.357-1.274 5.021-1.085 9.19-1.079.447.011.908.005 1.39-.005.41-.005.822-.011 1.258-.022 4.296-.042 7.869.366 7.806-6.381-.065-6.746-3.062-12.198-7.354-12.155-4.297.037-8.454 5.564-8.197 12.306.07 1.756.328 3.023.742 3.937-3.745.938-4.777 3.254-4.835 3.399zm51.657-27.749a46.634 46.634 0 0 1-5.249 3.712l-6.097 3.68a52.065 52.065 0 0 0-7.331 1.467 1.216 1.216 0 0 0-.317.14 1.406 1.406 0 0 0-.629.794l-6.209 20.46-33.185 7.38-10.452-7.321-3.041-24.634c5.936-1.09 13.874-2.352 23.41-3.42a56.802 56.802 0 0 0-2.955 3.855l-5.677 8.149 8.266-5.511c.123-.086 5.387-3.549 13.998-7.761a377.407 377.407 0 0 1 35.468-.99z" id="jkpath34" fill="#c7b39a"/><path class="jkst5" d="M151.835 125.675c-.042-.16-.945-2.873-4.942-2.397.461-1.003.666-2.356.521-4.21-.528-6.731-4.443-12.08-8.735-11.931-4.292.152-7.042 5.731-6.805 12.478.236 6.741 3.84 6.694 8.132 6.543 5.77-.107 8.939-1.88 11.829-.483zm21.18-19.385l-2.992 20.944-10.539 7.379-33.141-3.766-6.183-20.363a1.41 1.41 0 0 0-.945-.934c-.205-.06-4.308-1.23-8.659-1.607l.795-.053c.687-.049 12.118-1.451 25.767-6.157 15.115 1.161 27.458 3.02 35.897 4.557z" id="jkpath36" fill="#c7b39a"/></g><path class="jkst2" d="M187.481 115.502c.508.419.911 1.504.456 6.558-.559 6.188-3.16 17.049-4.771 18.8-1.778.344-5.505-.064-7.778-.595.393-1.559.505-2.306.822-3.9l3.975-2.781c.317-.22.526-.566.58-.941l2.778-19.466c1.686.912 3.421 1.899 3.938 2.325z" id="jkpath40" fill="#e6ccad"/><path class="jkst2" d="M40.937 140.908c.199.704.408 1.407.624 2.1-2.139.628-6.495 1.23-8.465.886-1.633-1.645-4.679-12.966-5.345-18.978-.543-4.871-.162-5.924.333-6.334.575-.483 2.728-1.708 4.593-2.707l2.519 20.449c.048.393.257.741.586.967z" id="jkpath42" fill="#e6ccad"/><path class="jkst2" d="M121.347 141.194l-.151 1.305s-4.581 4.248-11.956 5.199c-7.375.95-13.171-3.582-13.171-3.582.242.788.586 2.567 2.256 4.086a53.184 53.184 0 0 0-6.313-.393c-.804 0-1.616.023-2.401.061-4.539.237-10.924 7.1-15.414 14.014-2.203.697-9.089 2.883-17.06 5.237-7.44-10.309-11.098-20.842-11.469-21.932l.005-.006c-.15-.419-.301-.839-.441-1.268l1.913 1.338v.005l4.726 3.309 1.58 1.101c.236.167.515.253.794.253.102 0 .204-.011.305-.031l43.435-9.67a1.385 1.385 0 0 0 1.025-.95l6.194-20.39c1.069-.145 2.008-.22 2.814-.22.801 0 1.746.075 2.815.22l6.374 20.997c.162.532.624.919 1.171.977z" id="jkpath44" fill="#e6ccad"/><path class="jkst2" d="M170.926 140.066l1.402-.984c-.232.973-.484 1.94-.747 2.896-1.949 6.248-4.25 11.774-6.805 16.656-.565.039-1.161.061-1.8.061-1.972 0-3.986-.167-6.215-.371-3.868-.355-10.007-1.058-11.946-1.283-1.67-1.332-7.385-5.873-12.14-9.615-.187-.151-.348-.291-.505-.42-.837-.708-1.789-1.513-3.717-1.513-1.751 0-4.308.638-10.489 2.508 3.212-2.401 3.233-5.5 3.233-5.5l.151-1.305 40.748 4.629a1.41 1.41 0 0 0 .955-.241l4.094-2.868z" id="jkpath46" fill="#e6ccad"/><path class="jkst6" d="M140.937 54.337c.124 3.625.033 10.194-1.655 16.345a1.335 1.335 0 0 0 0 .704 259.298 259.298 0 0 0-6.446-.591c2.412-5.054 2.938-10.436 3.052-12.332 1.852-1.317 3.696-2.896 5.049-4.126z" id="jkpath48" fill="#ebd599"/><path class="jkst6" d="M79.456 58.462c.112 1.896.638 7.267 3.046 12.317-2.149.171-4.297.37-6.441.596a1.328 1.328 0 0 0 0-.694c-1.686-6.139-1.772-12.714-1.654-16.345 1.353 1.231 3.19 2.81 5.049 4.126z" id="jkpath50" fill="#ebd599"/><path class="jkst7" d="M151.835 125.675c-2.89-1.396-6.059.377-11.828.484-4.292.151-7.896.198-8.132-6.543-.237-6.747 2.513-12.326 6.805-12.478 4.292-.15 8.207 5.2 8.735 11.931.145 1.854-.06 3.207-.521 4.21 3.996-.477 4.899 2.235 4.941 2.396zm-13.488-9.878a2.203 2.203 0 0 0 2.154-2.235 2.186 2.186 0 0 0-2.235-2.153 2.194 2.194 0 0 0 .081 4.388z" id="jkpath52" fill="#2d3136"/><circle transform="rotate(-1.049 138.093 113.428)" class="jkst8" cx="138.307" cy="113.602" id="jkellipse54" r="2.194" fill="#edf6fa"/><path class="jkst7" d="M83.484 120.953c.063 6.747-3.509 6.339-7.806 6.381-.435.011-.848.016-1.258.022-.482.011-.944.016-1.39.005-4.168-.005-6.833-.194-9.19 1.079.058-.145 1.09-2.461 4.835-3.4-.414-.914-.673-2.181-.742-3.937-.257-6.741 3.9-12.269 8.197-12.306 4.292-.042 7.289 5.411 7.354 12.156zm-6.634-3.529a2.195 2.195 0 1 0-.122-4.388 2.195 2.195 0 0 0 .122 4.388z" id="jkpath56" fill="#2d3136"/><circle transform="rotate(-1.473 76.78 115.216)" class="jkst8" cx="76.79" cy="115.23" id="jkellipse58" r="2.195" fill="#edf6fa"/><g class="jkst9" id="jkg64" opacity=".8"><path class="jkst6" d="M50.691 75.155s.667-8.692 2.03-12.023c.702-1.717 4.996-2.81 8.276-3.591 3.278-.78 8.508-2.342 9.524 2.264 1.015 4.606 2.653 7.963 3.746 9.446l-1.404-18.97-22.562 5.464-1.484 16.786.703 1.327 1.171-.703" id="jkpath60" fill="#ebd599"/><path class="jkst6" d="M164.855 75.155s-.666-8.692-2.029-12.023c-.703-1.717-4.997-2.81-8.275-3.591-3.28-.78-8.51-2.342-9.526 2.264-1.013 4.606-2.654 7.963-3.748 9.446l1.407-18.97 22.562 5.464 1.483 16.786-.703 1.327-1.171-.703" id="jkpath62" fill="#ebd599"/></g><path class="jkst10" d="M132.965 18.378s-.598 45.49-11.224 45.49h-14.875-12.752c-10.626 0-11.484-45.47-11.484-45.47l-5.22 15.438.085 21.183 3.707 2.947 1.685 9.096 2.357 5.307 45.482.084 2.105-3.791 1.769-6.4.254-4.043 5.023-14.341z" id="jkpath66" opacity=".75" fill="#ebd599"/><path class="jkst10" d="M166.429 60.794s2.187 15.692 7.974 18.522c5.788 2.829 0 0 0 0l-8.103-2.444z" id="jkpath68" opacity=".75" fill="#ebd599"/><path class="jkst10" d="M48.908 60.794s-2.187 15.692-7.975 18.522c-5.788 2.829 0 0 0 0l8.104-2.444z" id="jkpath70" opacity=".75" fill="#ebd599"/><path class="jkst7" d="M167.987 76.8c2.755.902 5.526 1.858 8.036 3.325-1.343-.532-2.729-.913-4.126-1.257a70.385 70.385 0 0 0-4.201-.924c-2.82-.531-5.65-.982-8.498-1.327-2.841-.37-5.687-.682-8.546-.924-2.858-.241-5.709-.483-8.573-.65-11.446-.704-22.924-.88-34.41-.892-11.483.006-22.962.221-34.409.897-2.862.166-5.715.409-8.572.651-2.857.241-5.71.548-8.546.923-2.847.345-5.678.796-8.498 1.327-1.407.264-2.81.57-4.206.919-1.391.344-2.783.725-4.126 1.257 2.509-1.466 5.28-2.427 8.041-3.331.232-.075.467-.139.703-.214-.015-.059-.032-.113-.043-.177-.048-.317-1.069-7.859.709-18.645.086-.516.456-.935.962-1.075l2.917-.831c.634-22.625 9.952-33.266 10.243-33.594-8.326 13.397-8.25 29.286-8.106 32.986l18.128-5.152c.016-.005.026-.005.042-.01.076-.016.151-.027.226-.032.021 0 .049-.006.075-.006a1.19 1.19 0 0 1 .297.027c.015 0 .031.011.053.016.075.016.145.042.224.075.033.016.054.033.086.049.058.033.119.07.177.112.016.011.034.016.049.033l.032.032c.016.016.037.027.054.044.012.016.494.493 1.262 1.209-.182-5.973.102-23.108 8.262-37.31-.172.498-6.646 19.428-4.415 40.645.724.58 1.486 1.149 2.229 1.649.359.247.58.655.585 1.09.006.07.161 6.833 3.148 12.586.042.086.074.177.102.268 7.429-.505 14.878-.709 22.312-.714 7.436.005 14.88.22 22.307.731.027-.097.06-.193.109-.285 2.986-5.753 3.142-12.516 3.142-12.586.01-.436.231-.843.591-1.09.741-.5 1.493-1.069 2.224-1.649 2.234-21.217-4.24-40.147-4.411-40.645 8.153 14.201 8.444 31.336 8.262 37.31a62.536 62.536 0 0 0 1.261-1.209c.016-.016.039-.027.053-.044.012-.01.018-.021.033-.032.016-.016.033-.022.049-.033.06-.042.119-.079.177-.118.028-.01.054-.027.081-.043.081-.033.155-.059.236-.08.016 0 .033-.011.049-.011.096-.021.2-.032.296-.027.027 0 .049.006.07.006.075.005.156.016.231.032.012.006.028.006.042.01l18.129 5.152c.146-3.7.221-19.59-8.104-32.986.289.328 9.609 10.969 10.237 33.594l2.922.831c.499.14.875.559.962 1.075 1.777 10.786.752 18.328.708 18.645-.01.065-.026.124-.042.182.239.07.47.139.707.215zm-3.297-.968c.14-1.207.789-7.809-.591-16.801l-20.52-5.833c.184 3.475.265 11.012-1.707 18.199a1.619 1.619 0 0 1-.101.258c.203.021.408.037.606.064 5.769.661 11.511 1.584 17.189 2.83 1.712.398 3.426.823 5.124 1.283zm-25.409-5.151c1.688-6.15 1.779-12.72 1.655-16.345-1.353 1.23-3.197 2.809-5.049 4.125-.114 1.896-.64 7.278-3.052 12.332 2.149.173 4.298.366 6.446.591a1.33 1.33 0 0 1 0-.703zm-56.78.098c-2.408-5.05-2.934-10.422-3.046-12.317-1.858-1.316-3.696-2.895-5.049-4.125-.119 3.631-.032 10.206 1.654 16.345.065.237.058.473 0 .694 2.145-.227 4.292-.425 6.441-.597zm-8.933.864a1.65 1.65 0 0 1-.098-.247c-1.975-7.187-1.889-14.723-1.712-18.199L51.244 59.03c-1.38 8.982-.736 15.583-.597 16.797 1.703-.462 3.411-.887 5.131-1.284 2.835-.628 5.693-1.154 8.556-1.638 2.869-.478 5.747-.843 8.626-1.192.205-.027.404-.042.608-.07z" id="jkpath72" fill="#2d3136"/><g id="jkXMLID_1_"><g id="jkg78"><path class="jkst7" d="M129.293 18.973v17.025h-12.068v-4.974h-2.72v22.981h4.109v12.85H97.505v-12.85h4.092v-22.98h-2.711v4.974h-12.06V18.973zm-3.626 13.408v-9.789H90.443v9.789h4.816v-4.974h9.964v30.225h-4.1v5.606h13.865v-5.606h-4.1V27.407h9.964v4.974z" id="jkpath74" fill="#2d3136"/><path class="jkst0" id="jkpolygon76" fill="#cb3349" d="M101.123 57.632h4.1V27.407h-9.964v4.974h-4.816v-9.79h35.224v9.79h-4.816v-4.974h-9.964v30.225h4.1v5.606h-13.864z"/></g></g><path class="jkst3" d="M30.694 93.119c1.759-.399 4.136-.907 7.051-1.47a104.37 104.37 0 0 0-6.222 4.597z" id="jkpath83" fill="#656c67"/><path class="jkst5" d="M95.111 139.78s.492 3.165-3.938 4.519c-4.428 1.355-32.482 9.716-35.682 9.263-3.199-.451-11.319-5.874-11.319-5.874l-1.969-7.004 12.016 7.492z" id="jkpath85" fill="#c7b39a"/><path class="jkst5" d="M120.242 139.167s-.354 3.182 4.131 4.345c4.484 1.161 32.875 8.295 36.05 7.704 3.176-.591 11.053-6.361 11.053-6.361l1.663-7.084-11.045 6.588z" id="jkpath87" fill="#c7b39a"/><path class="jkst5" d="M28.412 133.956s3.887 7.775 10.166 5.083l4.485 1.645-.448 3.29-9.419 1.195-2.541-1.494z" id="jkpath89" fill="#c7b39a"/><path class="jkst5" d="M187.551 131.822s-6.353 8.115-12.632 5.424l-2.019 1.302.448 3.289 9.419 1.196 2.54-1.495z" id="jkpath91" fill="#c7b39a"/><path class="jkst5" d="M89.279 192.904s23.03 11.611 49.106-4.188l-8.374-.571s-18.272 7.232-32.738 3.235z" id="jkpath93" fill="#c7b39a"/><path class="jkst7" d="M112.626 171.509l1.594 1.899c.036.046 3.577 4.26 7.906 8.552 2.879 2.853 6.357 4.297 10.343 4.297 1.361 0 2.791-.175 4.235-.523 1.34-.326 2.796-.673 4.287-1.03 5.384-1.287 11.482-2.749 14.438-3.577.585-.166 1.238-.315 1.925-.472 3.935-.909 9.329-2.163 12.187-7.889 2.149-4.297 5.047-9.874 7.197-13.961-1.863.859-3.816 1.79-5.203 2.52-2.138 1.123-4.938 1.667-8.558 1.667-2.152 0-4.266-.181-6.605-.389-4.675-.43-12.586-1.361-12.667-1.372l-.606-.067-.478-.383c-.071-.052-7.003-5.575-12.606-9.981-.227-.186-.434-.358-.621-.513-.59-.503-.59-.503-.942-.503-1.797 0-7.02 1.62-18.462 5.167l-.703.223-.689-.26c-.078-.026-7.585-2.81-16.581-2.81-.736 0-1.47.019-2.185.056-.901.046-5.958 2.448-12.425 12.68l-.419.657-.741.238c-.107.037-11.238 3.63-23.042 7.005l-.766.218-.725-.337c-.077-.031-4.696-2.174-9.091-4.194 2.397 3.541 5.462 7.958 8.159 11.422 4.711 6.067 10.649 11.674 22.034 11.674 1.428 0 2.945-.088 4.503-.265 11.581-1.309 14.563-1.837 16.168-2.117.543-.092.973-.171 1.522-.238.088-.011 9.571-1.237 12.232-7.206 2.744-6.134 3.298-7.595 3.319-7.651l.968-2.583s.12-.669.317-.877c0 .005 0 .005.005.005l.019.016c.305.219.757.902.757.902zM40.499 55.71c-2.516 1.014-5.016 2.06-7.46 3.209-2.449 1.119-4.856 2.32-7.155 3.66-2.121 1.222-4.157 2.563-5.954 4.076-.077.455-.149.952-.211 1.423a51.357 51.357 0 0 0-.388 6.068c-.026 2.713.16 5.426.502 8.112.372 2.692.864 5.369 1.594 7.952a41.963 41.963 0 0 0 1.243 3.804c.233.623.492 1.228.762 1.818.134.294.274.585.413.864l.172.326c.201.104.409.207.605.3l1.206.574c.673.311 1.6.751 2.366 1.093.046-.037.088-.078.124-.114l-2.231-8.511c.471-.129 4.717-1.227 12.032-2.619a33.744 33.744 0 0 1-1.775-.379 36.704 36.704 0 0 1-4.898-1.563 22.857 22.857 0 0 1-2.309-1.119c-.741-.425-1.471-.905-2.035-1.547 8.035 2.624 24.637 1.433 39.398-.088 13.501-1.393 27.028-2.293 40.628-2.325 13.6.031 27.138.931 40.63 2.325 14.77 1.522 31.374 2.713 39.406.088-.564.642-1.293 1.122-2.034 1.547-.739.42-1.522.782-2.309 1.119a36.965 36.965 0 0 1-4.903 1.563c-.244.056-.492.114-.741.166 8.02 1.486 12.689 2.697 13.186 2.832l-2.138 8.107c.43-.192.864-.377 1.288-.574l1.207-.574c.196-.094.404-.196.606-.3l.166-.326c.144-.279.284-.57.419-.864.27-.591.528-1.196.767-1.818.471-1.231.879-2.51 1.236-3.804.731-2.583 1.228-5.26 1.595-7.952.346-2.686.528-5.4.502-8.112a52.755 52.755 0 0 0-.176-4.059 51.573 51.573 0 0 0-.213-2.009 29.83 29.83 0 0 0-.213-1.423c-1.797-1.513-3.831-2.853-5.954-4.076-2.299-1.34-4.704-2.541-7.159-3.66-2.438-1.149-4.943-2.195-7.46-3.209a140.105 140.105 0 0 0-3.801-1.476c-1.267-.491-2.552-.956-3.835-1.423 2.696.445 5.369 1.06 8.013 1.739 1.724.446 3.444.948 5.141 1.481-12.11-31.658-41.07-52.272-72.685-52.272-31.622 0-60.576 20.614-72.684 52.272a107.832 107.832 0 0 1 5.135-1.481c2.651-.678 5.322-1.294 8.02-1.739-1.29.466-2.568.931-3.842 1.423-1.268.47-2.535.967-3.799 1.475zm159.43 18.316a53.972 53.972 0 0 1-.258 8.733 55.462 55.462 0 0 1-1.619 8.605c-.4 1.414-.86 2.811-1.404 4.198a38.295 38.295 0 0 1-.89 2.071c-.161.341-.331.678-.523 1.025l-.284.512a8.975 8.975 0 0 1-.348.574l-.294.457-.461.237c-.492.254-.895.445-1.342.653l-1.298.585a88.22 88.22 0 0 1-2.62 1.065c-.611.239-1.15.457-1.662.674l-1.444 5.487c-.036-.009-.471-.12-1.283-.315l-.078.574c1.594.833 4.726 2.522 5.793 3.403 2.148 1.775 2.299 4.587 1.823 9.841-.244 2.697-1.139 7.946-2.381 12.767-2.144 8.298-3.283 9.273-4.753 9.649-.746.192-1.894.383-3.008.383-2.266 0-5.353.063-7.429-.439-.533 1.888-2.055 6.812-5.068 12.962.151-.073.3-.135.435-.207 3.717-1.952 10.861-5.064 11.162-5.199l5.643-2.452-2.89 5.435c-.067.118-6.264 11.773-10.059 19.383-3.769 7.538-10.835 9.179-15.065 10.151-.637.151-1.241.291-1.733.425-3.035.854-9.18 2.319-14.599 3.623-.064.016-.13.033-.197.042a64.057 64.057 0 0 1-10.955 5.411c-14.568 5.518-29.923 5.208-43.844.092a647.05 647.05 0 0 1-9.193 1.097 45.12 45.12 0 0 1-4.985.291c-13.264 0-20.294-6.736-25.425-13.331-5.493-7.062-12.212-17.546-12.497-17.985L31 158.426l6.585 2.961c3.152 1.419 12.524 5.757 15.205 7 .217-.061.43-.124.642-.186-4.457-6.357-8.112-13.605-10.695-21.634-2.195.662-5.576 1.175-8.206 1.175-.961 0-1.822-.072-2.484-.228-1.471-.336-3.148-1.754-5.431-9.795-1.325-4.668-2.314-9.764-2.603-12.387-.57-5.121-.466-7.864 1.662-9.636 1.283-1.071 5.611-3.344 6.507-3.809l-.192-1.58c-13.75 8.08-21.991 15.22-22.157 15.366L0 134.302l7.005-11.047c5.544-8.755 11.948-15.832 17.84-21.284-.244-.098-.471-.196-.71-.294l-1.299-.585a34.907 34.907 0 0 1-1.34-.653l-.461-.237-.295-.457c-.166-.249-.238-.388-.347-.574l-.29-.512c-.181-.347-.358-.684-.518-1.025a30.878 30.878 0 0 1-.89-2.071 44.74 44.74 0 0 1-1.404-4.198 54.745 54.745 0 0 1-1.62-8.605 54.664 54.664 0 0 1-.259-8.733c.078-1.455.218-2.909.419-4.354.104-.725.213-1.45.358-2.17.15-.734.296-1.418.518-2.221l.155-.564.404-.317c2.294-1.802 4.768-3.163 7.284-4.369a78.87 78.87 0 0 1 6.311-2.616c5.943-16.493 16.162-31.118 29.591-41.311C74.337 5.57 90.664 0 107.671 0s33.334 5.57 47.218 16.106c13.43 10.193 23.649 24.819 29.588 41.307a78.282 78.282 0 0 1 6.316 2.62c2.515 1.206 4.99 2.567 7.283 4.369l.404.317.156.564c.227.803.372 1.487.517 2.221.146.72.26 1.445.357 2.17.203 1.443.348 2.897.419 4.352zm-11.995 48.031c.456-5.052.058-6.139-.455-6.554-.513-.43-2.247-1.412-3.935-2.329l-2.779 19.464a1.39 1.39 0 0 1-.58.942l-3.977 2.781c-.315 1.593-.429 2.345-.817 3.903 2.273.528 5.999.938 7.775.595 1.612-1.748 4.214-12.61 4.768-18.802zm-5.161-17.648l2.977-11.29c-4.318-.978-12.27-2.615-23.1-4.148-5.53 3.976-11.582 7.155-17.53 9.691 18.199 1.771 31.57 4.406 37.653 5.747zm-4.68 27.237l3.385-23.676a240.127 240.127 0 0 0-5.731-1.169l-3.059 21.422a1.415 1.415 0 0 1-.575.943l-11.472 8.023c-.27.192-.616.28-.947.243l-34.572-3.929a1.391 1.391 0 0 1-1.176-.973l-6.227-20.5c-1.668-.431-5.949-1.43-9.696-1.43-3.764 0-8.041.999-9.708 1.43l-6.228 20.5a1.388 1.388 0 0 1-1.025.947l-34.572 7.692a1.483 1.483 0 0 1-.306.033 1.36 1.36 0 0 1-.792-.25l-11.467-8.029a1.396 1.396 0 0 1-.585-.968l-3.091-25.072c-1.284.249-2.443.487-3.479.703-.734.405-1.46.809-2.174 1.213l3.281 26.568 16.666 11.675 42.047-9.354 6.207-20.449a1.389 1.389 0 0 1 1.108-.975c1.574-.253 2.95-.382 4.116-.382 1.153 0 2.536.129 4.105.382.528.083.957.461 1.108.975l6.366 20.956 42.282 4.808zm-8.07-4.411l2.992-20.948c-8.439-1.536-20.78-3.394-35.897-4.554-13.647 4.707-25.077 6.108-25.766 6.155l-.797.057c4.353.374 8.454 1.544 8.66 1.605.452.135.804.481.944.933l6.186 20.366 33.138 3.764zm2.303 11.845l-1.404.983-3.779 2.651-4.095 2.868c-.279.192-.621.28-.954.243l-40.746-4.633-2.966-.337a1.39 1.39 0 0 1-1.171-.977l-6.377-20.998c-1.066-.145-2.014-.219-2.81-.219-.809 0-1.751.073-2.817.219l-6.192 20.392a1.383 1.383 0 0 1-1.025.946l-43.435 9.672c-.103.02-.206.03-.305.03-.279 0-.559-.083-.798-.253l-1.578-1.098-4.726-3.307v-.011l-1.91-1.335c.135.43.289.85.441 1.268l-.006.006c.368 1.092 4.028 11.622 11.467 21.929a873.96 873.96 0 0 0 17.057-5.234c4.488-6.917 10.877-13.777 15.418-14.014a51.12 51.12 0 0 1 2.402-.061c2.221 0 4.344.16 6.31.393-1.671-1.517-2.013-3.298-2.256-4.085 0 0 5.793 4.53 13.17 3.584 7.378-.953 11.959-5.204 11.959-5.204s-.021 3.102-3.236 5.503c6.182-1.869 8.739-2.511 10.489-2.511 1.931 0 2.883.808 3.717 1.519.161.129.322.268.507.419a3519.302 3519.302 0 0 1 12.141 9.614c1.936.227 8.075.926 11.943 1.283 2.23.201 4.245.372 6.217.372.637 0 1.233-.026 1.797-.063 2.558-4.88 4.857-10.411 6.808-16.653.261-.96.516-1.928.743-2.901zm-15.034-51.593c-.01-.006-.02-.012-.031-.012a551.624 551.624 0 0 0-9.826-.651 905.6 905.6 0 0 0-13.667-.668 72.95 72.95 0 0 1-1.574 2.225c-2.479 3.355-7.398 9.51-13.704 14.729 8.926-1.6 24.409-5.56 37.803-14.905.336-.238.668-.486.999-.718zm-29.876.926c.377-.471.729-.926 1.044-1.34-3.281.331-6.512.808-9.67 1.408-10.814 2.024-20.801 5.389-29.11 8.837a383.259 383.259 0 0 1 18.54-.455c3.908 0 7.708.067 11.404.176 3.179-3.056 5.861-6.182 7.792-8.626zm3.587 102.085c-4.503-.332-8.598-2.205-11.903-5.477a271.86 271.86 0 0 0-.502-.512 44.25 44.25 0 0 1-4.881.704c-.698.026-1.361.087-2.091.087l-1.083.011-.413-.011c-4.396 6.539-14.159 7.813-14.605 7.87-.403.046-.734.103-1.191.186 5.442 1.491 10.996 2.138 16.474 1.77 5.492-.367 12.627-1.558 20.195-4.628zm-17.4-7.461a45.604 45.604 0 0 0 3.184-.378 138.958 138.958 0 0 1-3.568-3.857 398.441 398.441 0 0 1-1.92 4.339h.243c.658.001 1.378-.071 2.061-.104zm-3.354-78.632c1.827-1.103 3.582-2.366 5.249-3.712a422.33 422.33 0 0 0-7.278-.072c-10.137 0-19.606.415-28.189 1.061-8.61 4.209-13.875 7.672-13.998 7.76l-8.268 5.514 5.679-8.149a52.452 52.452 0 0 1 2.956-3.857c-9.536 1.066-17.477 2.329-23.41 3.422l3.038 24.632 10.453 7.321 33.184-7.378 6.212-20.464c.104-.337.331-.621.627-.793.098-.063.202-.109.315-.14.192-.052 3.51-.999 7.336-1.465zm3.816-18.788c-2.31-.036-4.623-.057-6.933-.062h-.005c-3.39.005-6.787.041-10.189.109l-6.269 2.971c-.005.005-.041.021-.088.048-.942.46-9.174 4.613-16.919 12.021 6.943-3.65 17.146-8.418 29.153-12.115a144.186 144.186 0 0 1 11.25-2.972zM70.251 98.761c3.251-3.225 6.605-5.886 9.567-7.967-11.415 2.651-21.923 6.543-31.128 10.778a360.846 360.846 0 0 1 21.561-2.811zm2.159-9.949a150.122 150.122 0 0 1 11.813-2.796c-5.798.212-11.6.481-17.393.808-3.366.186-6.715.414-10.065.667-1.678.129-3.345.263-5.007.445-.476.046-.942.098-1.418.16-4.369 2.614-21.127 13.134-32.631 26.889 11.179-7.769 30.654-19.443 54.701-26.173zm-30.85 54.197a68.861 68.861 0 0 1-.621-2.102l-5.162-3.612a1.391 1.391 0 0 1-.586-.969l-2.516-20.449c-1.864.999-4.017 2.225-4.592 2.707-.497.409-.875 1.46-.336 6.332.668 6.01 3.712 17.333 5.348 18.979 1.968.347 6.327-.258 8.465-.886zm-3.815-51.36a229.005 229.005 0 0 0-7.051 1.47l.829 3.127a103.93 103.93 0 0 1 6.222-4.597z" id="jkpath95" fill="#2d3136"/></g></g></symbol><symbol viewBox="0 0 24 24" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="tune" xmlns="http://www.w3.org/2000/svg"><path d="M6.85 2.852h-2v6h2v-6m12 0h-2v10h2v-10m-16 10h2v8h2v-8h2v-2h-6v2m12-6h-2v-4h-2v4h-2v2h6v-2m-4 14h2v-10h-2v10m4-6v2h2v4h2v-4h2v-2h-6z" fill="#fbc02d" fill-rule="nonzero"/></symbol><symbol viewBox="0 0 50 50" id="twig" xmlns="http://www.w3.org/2000/svg"><path d="M9.727 47.556c-.125-.223-.297-2.168-.183-2.087.034.025.171.267.304.537.132.27.282.487.332.482.123-.011.075-1.196-.1-2.454-.331-2.398-1.176-4.435-2.358-5.69-.2-.212-.344-.4-.319-.419.093-.067 1.327.843 1.842 1.359.293.293.735.825.981 1.181.328.474.465.618.51.534.078-.147-.21-9.903-.376-12.701-.074-1.255.063-1.023.61 1.035 1.064 4.006 1.858 7.922 2.342 11.55.086.637.173 1.172.195 1.19.022.016.092.001.157-.034.888-.483 1.524-.667 2.55-.736.727-.048.945.062.35.178-1.15.222-1.99 1.013-2.344 2.201-.315 1.061-.327 2.707-.024 3.434.152.366.037.426-1.067.56-.716.088-.977.096-1.202.037-.356-.092-1.118-.098-1.195-.008-.031.036-.243.066-.47.066-.38 0-.423-.017-.535-.215zm1.974-3.233c.152-.205.072-.41-.204-.522-.225-.09-.263-.088-.437.025-.21.137-.252.43-.08.554.18.13.607.096.72-.057zm1.248.086a.763.763 0 0 0 .214-.203c.241-.33-.352-.622-.745-.366-.406.265.08.785.531.569zm2.288 3.094c-.033-.039.117-.387.334-.775.216-.387.411-.665.433-.618.07.152-.201 1.28-.33 1.372-.15.108-.354.117-.437.02zM8.2 47.092c-.29-.343-.221-.434.14-.182.176.123.321.263.321.31 0 .165-.279.087-.46-.128zm8.649-.145c0-.053.102-.18.227-.282.25-.204.312-.113.143.207-.095.18-.37.236-.37.075zm8.065-.827c-.243-.025-.48-.088-.527-.141-.11-.125-.114-3.043-.004-3.043.045 0 .132.149.193.331.127.38.228.42.31.124.094-.337.065-3.472-.039-4.297-.449-3.55-1.865-6.124-4.342-7.89-1.086-.774-2.653-1.436-4.047-1.711-.764-.15-.522-.224.598-.182 2.364.089 4.167.706 5.847 2.001a11.046 11.046 0 0 1 2.32 2.502c.453.682.64.854.64.584 0-.07.063-.882.139-1.805.679-8.26 2.396-15.1 4.984-19.86 1.86-3.422 5.108-6.817 7.885-8.244 1.397-.718 2.539-.988 4.02-.952.933.023 1.01.036 1.77.307a6.822 6.822 0 0 1 1.363.662c.612.407 1.309 1.004 1.235 1.058-.026.018-.343-.165-.705-.407-2.657-1.771-5.062-1.52-7.12.742-1.108 1.22-2.651 3.53-3.634 5.443-2.828 5.503-4.541 11.464-5.291 18.413-.163 1.509-.282 3.76-.195 3.703.032-.022.266-.52.518-1.108 1.597-3.723 3.578-6.428 5.79-7.908.672-.449 1.612-.904 1.715-.83.022.016-.172.22-.432.454-1.957 1.754-3.248 3.76-4.232 6.572-.938 2.68-1.366 5.588-1.368 9.3-.002 1.741.188 4.385.366 5.101.125.505.08.546-.585.546-.55 0-2.306.138-3.416.27-.414.05-.817.04-1.609-.036-.58-.056-1.129-.119-1.218-.14-.165-.037-.18-.014-.2.302-.01.186-.098.203-.728.139zm2.507-6.725c.294-.11.375-.22.375-.517 0-.63-1.309-.706-1.524-.088-.074.211.13.51.42.616.297.108.413.106.73-.011zm2.369-.052c.277-.222.318-.364.174-.611-.4-.691-1.755-.307-1.428.404.121.266.299.35.738.354.227 0 .387-.045.516-.147zm3.011 6.681c-.027-.05.088-.268.256-.484.879-1.135 1.22-1.544 1.284-1.544.04 0 .056.037.036.082l-.423.964c-.212.485-.445.924-.519.977-.169.122-.57.125-.634.005zm2.446-.596c0-.121.853-.683.896-.59.018.04-.056.209-.166.376-.168.259-.238.305-.464.305-.164 0-.266-.035-.266-.091zm-13.04-.124c-.177-.159-.493-.656-.462-.725.018-.038.248.1.512.309.264.207.457.405.428.438-.075.088-.371.074-.478-.022z" fill="#9bb92f" stroke-width=".078"/></symbol><symbol viewBox="0 0 500 500" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="typescript" xmlns="http://www.w3.org/2000/svg"><path d="M49 51h408v408H49V51zm246.669 314.879l19.463-1.702c.922 7.8 3.067 14.199 6.435 19.198 3.368 4.998 8.597 9.04 15.688 12.124 7.09 3.085 15.067 4.627 23.93 4.627 7.87 0 14.819-1.17 20.845-3.51 6.027-2.34 10.512-5.548 13.455-9.625 2.942-4.077 4.413-8.526 4.413-13.348 0-4.892-1.418-9.164-4.254-12.816-2.836-3.651-7.516-6.718-14.039-9.2-4.183-1.63-13.436-4.165-27.759-7.604s-24.355-6.683-30.099-9.732c-7.445-3.899-12.993-8.739-16.644-14.517-3.652-5.779-5.478-12.249-5.478-19.41 0-7.871 2.234-15.227 6.701-22.069 4.467-6.842 10.99-12.036 19.569-15.581 8.58-3.546 18.116-5.318 28.61-5.318 11.557 0 21.75 1.861 30.577 5.584 8.828 3.722 15.617 9.199 20.368 16.432 4.75 7.232 7.303 15.421 7.657 24.568l-19.782 1.489c-1.064-9.856-4.662-17.301-10.795-22.335-6.133-5.034-15.191-7.551-27.174-7.551-12.479 0-21.573 2.286-27.281 6.86-5.707 4.573-8.561 10.086-8.561 16.538 0 5.602 2.021 10.21 6.062 13.826 3.971 3.617 14.34 7.321 31.109 11.115 16.769 3.793 28.273 7.108 34.513 9.944 9.076 4.183 15.776 9.483 20.101 15.9 4.325 6.417 6.488 13.809 6.488 22.175 0 8.296-2.375 16.113-7.126 23.452-4.751 7.338-11.575 13.046-20.474 17.123-8.898 4.077-18.913 6.116-30.045 6.116-14.11 0-25.933-2.056-35.47-6.169-9.537-4.112-17.017-10.299-22.441-18.559-5.424-8.26-8.278-17.602-8.562-28.025zm-65.728 50.094V278.454h51.583v-18.399H157.938v18.399h51.37v137.519h20.633z" fill="#0288d1"/></symbol><symbol viewBox="0 0 500 500" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414" id="typescript-def" xmlns="http://www.w3.org/2000/svg"><path d="M457 459H49V51h408v408zM69 71v368h368V71H69z" fill="#0288d1"/><text x="342.219" y="344.544" font-family="ArialMT" font-size="12" fill="#0288d1" transform="translate(-6058.94 -5838) scale(18.1514)"><tspan style="-inkscape-font-specification:sans-serif" font-family="sans-serif" font-weight="400">TS</tspan></text></symbol><symbol viewBox="0 0 24 24" id="url" xmlns="http://www.w3.org/2000/svg"><path d="M16 6h-3v1.9h3a4.1 4.1 0 0 1 4.1 4.1 4.1 4.1 0 0 1-4.1 4.1h-3V18h3a6 6 0 0 0 6-6c0-3.32-2.69-6-6-6M3.9 12A4.1 4.1 0 0 1 8 7.9h3V6H8a6 6 0 0 0-6 6 6 6 0 0 0 6 6h3v-1.9H8c-2.26 0-4.1-1.84-4.1-4.1M8 13h8v-2H8v2z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="verilog" xmlns="http://www.w3.org/2000/svg"><path d="M17.282 17.08H6.718V6.513h10.564m4.226 4.226V8.627h-2.113V6.514c0-1.173-.95-2.113-2.113-2.113H15.17V2.288h-2.113v2.113h-2.112V2.288H8.83v2.113H6.718c-1.173 0-2.113.94-2.113 2.113v2.113H2.492v2.113h2.113v2.113H2.492v2.113h2.113v2.113a2.113 2.113 0 0 0 2.113 2.113H8.83v2.113h2.113v-2.113h2.112v2.113h2.113v-2.113h2.113a2.113 2.113 0 0 0 2.113-2.113v-2.113h2.113v-2.113h-2.113V10.74m-6.339 2.113h-2.112V10.74h2.112m2.113-2.113H8.831v6.34h6.338z" fill="#ff7043" stroke-width="1.056"/></symbol><symbol viewBox="0 0 24 23.999999" id="vfl" xmlns="http://www.w3.org/2000/svg"><defs><style>.jra{fill:#f05223}.jrb{fill:url(#jra)}</style><radialGradient id="jra" cx="205.45" cy="208.29" r="225.35" gradientTransform="matrix(.04556 0 0 .0456 2.888 2.88)" gradientUnits="userSpaceOnUse"><stop stop-color="#ffd104" offset="0"/><stop stop-color="#faa60e" offset=".35"/><stop stop-color="#f05023" offset="1"/></radialGradient></defs><title>houdinibadge</title><g stroke-width=".046"><path class="jra" d="M19.97 3H4.03A1.03 1.031 0 0 0 3 4.031v4.135C4.548 6.977 6.563 6.21 8.948 6.21c5.107.003 8.35 3.574 8.348 8.081 0 3.13-1.46 5.485-3.746 6.71h6.42A1.03 1.031 0 0 0 21 19.968V4.031a1.03 1.031 0 0 0-1.03-1.03z" fill="#f4511e"/><path class="jrb" d="M3 17.722v2.247A1.03 1.031 0 0 0 4.03 21h1.837C4.474 20.21 3.49 19 3 17.722z" fill="url(#jra)"/><path class="jra" d="M8.948 8.231c-2.586-.09-4.598.86-5.948 2.264v3.163c.918-2.654 3.447-3.87 5.565-3.85 2.647.027 4.689 2.025 4.7 4.284.012 2.159-.892 3.748-3.33 4.14-1.33.213-3.411-.567-3.318-2.578.046-1.037.854-1.622 1.777-1.58-.905 1.213.293 2.102 1.139 1.921 1.048-.224 1.475-1.156 1.475-1.878 0-.762-.718-1.994-2.498-1.951-2.204.052-3.591 1.639-3.638 3.602-.056 2.468 2.253 4.091 4.622 4.121 3.48.046 5.543-2.24 5.539-5.586-.005-3.029-2.434-5.946-6.085-6.072z" fill="#f05223"/></g></symbol><symbol viewBox="0 0 24 24" id="virtual" xmlns="http://www.w3.org/2000/svg"><path d="M21 14H3V4h18m0-2H3c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h7l-2 3v1h8v-1l-2-3h7a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 281.25 281.25" id="visualstudio" xmlns="http://www.w3.org/2000/svg"><path d="M196.18 101.74l-52.778 42.444 52.778 40.889V101.74m-136.67 110l-30-18.889v-100L62.843 81.74l47.778 37 96.666-89.222 44.444 27.778v172.22l-55.555 22.222-85.111-81.555-51.555 41.555m3.333-48.889l20.667-19.111-20.667-19.778z" fill="#ab47bc" stroke-width="11.111"/></symbol><symbol viewBox="0 0 300 300" id="vscode" xmlns="http://www.w3.org/2000/svg"><defs><style>.icon-canvas-transparent{fill:#f6f6f6;opacity:0}.icon-white{fill:#fff}</style></defs><title>BrandVisualStudioCode</title><path d="M218.62 29.953l-105.41 96.92L54.301 82.47 29.955 96.64l58.068 53.359-58.068 53.359 24.346 14.212 58.909-44.402 105.41 96.878 51.424-24.976V54.93zm0 63.744v112.6l-74.719-56.302z" fill="#2196f3" stroke-width="17.15"/></symbol><symbol viewBox="0 0 24 24" id="vue" xmlns="http://www.w3.org/2000/svg"><path d="M1.821 4.15l10.21 17.618L22.24 4.235V4.15h-7.692L12.113 8.33 9.691 4.15H1.82z" fill="#41b883"/><path d="M5.937 4.15l6.152 10.616 6.18-10.617h-3.722l-2.434 4.179-2.422-4.179H5.937z" fill="#35495e"/></symbol><symbol viewBox="0 0 420 419" id="watchman" xmlns="http://www.w3.org/2000/svg"><g stroke="#fff" stroke-linecap="round" stroke-linejoin="bevel"><path d="M166.95 145.32a93.935 123.23 0 0 1 92.934 3.263" fill="none" stroke-width="18.467"/><path d="M162.92 137.96L44.63 256.25a174.07 173.93 0 0 0 5.705 16.486l123.68-123.68-11.096-11.096zM266.54 144.04l-11.096 11.096 117.16 117.16a174.07 173.93 0 0 0 5.691-16.5l-111.76-111.76zm170.65 170.65v22.193l17.1 17.1 11.096-11.098-28.195-28.195z" fill="#fff" stroke-width="1.963"/><path d="M167.52 273.36a93.935 123.23 0 0 1 92.934-3.263" fill="none" stroke-width="18.467"/><path d="M49.516 144.56a174.07 173.93 0 0 0-.809 2.213 174.07 173.93 0 0 0-4.757 14.344 174.07 173.93 0 0 0-.016.055l119.56 119.56 11.098-11.096-125.07-125.07zM454.87 64.703l-17.668 17.668v22.191l28.764-28.764-11.096-11.096zm-80.984 80.984l-117.86 117.86 11.098 11.096 112.18-112.18a174.07 173.93 0 0 0-5.416-16.777z" fill="#fff" stroke-width="1.963"/></g><image x="21.229" y="20.262" width="378" height="377.1" preserveAspectRatio="none" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaQAAAGjCAYAAABjSWGNAAAgAElEQVR4AeydB3hUVdrH/+fOpNMF JAFUmivFXtZuIBRRQUUTil1RV3et6Lr6rSu6rg3B1dXVtXeBCCioCASIimLDhkFsgAIJSAkhPZmZ 8z3vjReGMJlMueeWmfc88MzMLaf8zp3855zznvcV4MQEkoxA7sVr01PrUrv4pdZNBNA1AHQRkJ0g ZAcJrYOA7Aipv8/Q6JgUKdBkG0iZISDSDVwSaAfAY3z+/bVSAD56L+mfwA5INAqgSkrUCKA2IOQO IcQOIUU5pNwhNZQLia2Q2OyH3OJBxqaiwk4VzfLlj0wg4QmIhG8hNzDJCEgxJH9DjgfevgGBAyCx vxSyh5DoAWA/CHQH0MEFUOoBbJbAeiHEBiED6wMSv0DgF02In1J2Vq+ZP78fXcOJCSQMARakhOnK 5GrICaO3tE3P8B0kERgkgP6Qop8E+gqgL4Bdo5gEphKAwHpIrIHAjxL4DpCrAo1yVfHsHhsSuN3c tAQmwIKUwJ2bGE2TYsjY33qLQOBwCHk4JA6HhgE08kmM9ilpRQUgvwPE1xD4MiC0r2R67dfFz/eq U1IaZ8oETCLAgmQSSM7GHAIj8td38sN7HAROkpDHATgMTWs15hSQvLn4JLBaQHwqEPgQQiwvmpHz XfLi4JY7kQALkhN7JYnqlDd2w4GQnuMk5AkCOBHAQQD4ubTgGZDAFg1ieQCBj6Dhg7SKmhW8LmUB eC6iRQL8xW8RDZ8wn4AUQ8eVHib9YjCELj4nAOhqfjmcY4wE6iGxAkIsCwDvSel5v7iwa1WMefFt TCBqAixIUSPjG6IhkDtmQw/N4xkKTQ4TEkNZgKKhZ/u1DZD4CAKLIMSik/p3WzF5sgjYXiuuQMIS YEFK2K61p2GTJ0tt2Xebj5EyMArAab+vAdlTGS7VVAI0xSeAd6QQb2uBtIW8V8pUvJwZz9XzM2AG AdpoKqpTh3uEdlYA8gwBdDEjX87D0QQaALlMCLzha5Rz2NTc0X3lmsrxCMk1XeWsig6/YFOWr0Ge JoBzICWNhNo6q4ZcGwsJkFOKT4TUZiOgzSqate8aC8vmohKIAAtSAnWm6qaMHPljWl2bzBGapk2A lKMBZKguk/N3JYHPpMCrHuGbuWj6fqWubAFX2hYCLEi2YHdToVIMKSg7SQAXABgDoJObas91tZWA H5DvCeCV2rrUwg/ndqm0tTZcuOMJsCA5vovsqeDwszd19af4LwbEZQAOtKcWXGoCEaiCwAwB7emi Gd0+TqB2cVNMJMCCZCJMt2dFFnIflJQNh8BEgKbkRIrb28T1dySBlULIpzyBwCsLCntud2QNuVK2 EGBBsgW7swqlvUIer+cSQE7UPWI7q3pcm8QlUAtgtibFU4sKu70PCJm4TeWWRUKABSkSSgl5DTkt 3XSqJuVfJDAiRFyfhGw1N8qxBH4AxJP+hrpnit/otcOxteSKKSXAgqQUr/My18216wMXCOB6AH9w Xg25RklOoEoI8bzw+R5ZNKvnj0nOIumaz4KUJF0+YsyWbL/Xdx0gr5BAxyRpNjfTvQQCkPIdDdqD iwqz33NvM7jm0RBgQYqGlguvbfKmrd2MJrPtNBc2gauc5ASElJ8EIO4/eWD2m+xLL7EfBhakBO3f wfmlR2oCtwI4G4CWoM3kZiURASmwGpAPVLTPeXnFk6IxiZqeNE1lQUqwrs4bW3Y0AoF/QIjT2Vdh gnUuN8cgsFYK3LNPoPzFwsKBDcZBfnU/ARYk9/eh3oIh+WXHaELeIZs8bCdIq7gZTCAsgXUQuK9T oPw5FqawnFxzkgXJNV0VuqJD8zcdDCHvlvpG1tDX8FEmkOAE1gohJp/Yv9vLvMbk7p5mQXJp/w0Z u7mPkP7JACbwGpFLO5GrbS4BiRII/H3xzOw3eZOtuWityo0FySrSJpWTO760s8cv7wQEeVVINSlb zoYJJBAB+bGQ4uaiwpxlCdSopGgKC5JLuvm4/PUZmcJ7NSD/DqCDS6rN1WQCdhKY45f+vxYX9vzJ zkpw2ZETYEGKnJVNV1L4h9JxAuI+9jNnUxdwsW4mQFZ4//E31N/NLomc340sSA7uo2H5Gw7zC+3f AjjFwdXkqjEBNxDYKgVuPbl/9rNs+ODc7mJBcmDf5J61toMnNfVfgLgCgNeBVeQqMQF3EpD4XGja NRyTyZndx4LkqH6RYkj+pouEkFMAdHZU1bgyTCBxCEgIPFvvabx52av7lydOs9zfEhYkh/Th0HM2 95Ye//8ADHVIlbgaTCDRCWwWENcWzcyemegNdUv7WJBs7qncXOnVum66XoBMuZFpc3W4eCaQdAQE xFyfz//n4tk9NiRd4x3WYBYkGzuEjBak0J6WwJE2VoOLZgJMANgJIW49qX+3J9jowb7HgQXJBva0 pyhLeO+QkJPYaMGGDuAimUDLBJYJgSuKZuR81/IlfEYVARYkVWRbyHfouLJcBPCUhOzbwiV8mAkw AXsJ1EOKe3Z07HYvh7mwtiNYkCzinXvx2nRvTdq9EriOw0JYBJ2LYQLxEfgCUlywuDB7VXzZ8N2R EmBBipRUHNcNGVt6hJB4CcCAOLLhW5kAE7CeQB1tqF0yI/thdtiqHj4LkkLG+fnSUy7KbpWQ/wBE isKiOGsmwASUEpBLPBouXji9+3qlxSR55ixIih6A3DEbemhe7WV2+6MIMGfLBKwnsA3AxMUzc96w vujkKJEFSUE/D87fOFoT4lkA+yjInrNkAkzAVgLyv/7MhknFz/eqs7UaCVg4C5KJnTpy5I9p9W3b TBGQf2HDBRPBclZMwHkEVnr8KFg4K2e186rm3hqxIJnUd0PGlO4vPHgdAkeZlCVnwwSYgLMJVEHI yxfP6D7d2dV0T+1YkEzoq6H5ZadLIV/gKToTYHIWTMBlBIQQj6bsrLpp/vx+9S6ruuOqy4IUR5fo VnRa2Z1S4jaeoosDJN/KBNxP4DMhcW5RYc6v7m+KfS1gQYqRvR6zKCXtVQiMjDELvo0JMIEEIiCB LR4p8hcVZr+XQM2ytCmapaUlSGF5+WUDvKnpn7EYJUiHcjOYgAkEBNAlIAKLho7deI0J2SVlFjxC irLbh+SXni0EXgTQJspb+XImwASShICAeC6lsuoqXleKrsNZkKLglVdQeiuAf/F6URTQ+FImkLwE lnkatXMWzun2W/IiiK7lLEgR8KL9RQ1tsyia60URXM6XMAEmwAQMAmsDHjFq6WvZJcYBfm2ZAAtS y2z0M7njSzt7/JgD4MRWLuXTTIAJMIFQBCqkkAVLZnRfGOokH9tNgI0adrPY611u/vq+Xr9YzmK0 Fxo+wASYQOQE2gsp3h4ytnRi5Lck55UsSC30+5D8smM8wvMhB9JrARAfZgJMIBoCXiHxVN7YjXcC kmemWiDHYEKAyRu76QzIALkDyQpxmg8xASbABGImQBZ4vt+6XVFcLHwxZ5KgN7IgNevYvPyNl0EI MmDwNDvFH5kAE2AC5hCQmJ9Zh3PnzcupMSfDxMiFp+yC+nFIQdlNEOIpFqMgKPyWCTAB8wkIjKxJ xyLy+GJ+5u7NkUdIet9JkVdQRvuLaJ8RJybABJiAVQS+9kvvqcWFXTdZVaCTy0l6QZo8WWrLVpU9 JoE/ObmjuG5MgAkkJgEB8ZNPw7Di6dnrErOFkbcqqQWJvHVvF2UU2fXCyJHxlUyACTAB0wn86pf+ vOLCnj+ZnrOLMkzaNaTcXOndLja9zGLkoqeVq8oEEpfAfh7hfW/4OaUHJW4TW29ZUo6Q8vNLUreh 42tCYEzriPgKJsAEmIBlBDZDiiGLC7NXWVaigwpKOkEiMSoXnQol5GgH9QNXhQkwASagE6C4SprU 8ooKu61MNiRJNWVH03Q0MmIxSrbHnNvLBNxDgOIqSREoGjq2tL97am1OTZNmhEQGDNtE6asCosAc dJwLE2ACTEAlAVkGIXMXz+jxg8pSnJR3UoyQfreme57FyEmPHteFCTCB8ARENqS2mJw8h78ucc4m gSBJUS7KHgVwfuJ0G7eECTCBJCHQwyM8i3LHbOiRDO1NeEEaMrbsHt70mgyPMreRCSQsgQM8Hu3d kfllXRK2hb83LKEFaUjBxluExN8SvRO5fUyACSQ4AYGBjQjMG5q/vX0itzRhBSlvbOmVAuLeRO48 bhsTYALJQ0AK8UcpamfT1pVEbXVCCtLg/I2jIfEYgKSxIkzUB5TbxQSYQDABMWS76Phsogb5SzhB Gjxu0x81IV7jEBLBDzG/ZwJMIIEInJc3tuyhBGrPrqYklCCReaQIBOYByNzVQn7DBJgAE0g0AhLX Dc0vTbj18YSZ0iILlEaBjyRk0tjsJ9p3jNvDBJhAVAQkpBy/uLD7jKjucvDFCTFCokW+Bsg3WIwc /KRx1ZgAEzCbgIAQz9IyhdkZ25VfAgiSFPoin8DxdkHkcpkAE2ACNhHI1AKBuYmycdb1gjS0oOz/ AJxn08PAxTIBJsAE7CbQ1ePV3kmEPUquFqS8/LJzJXCX3U8Dl88EmAATsJnAwVLUv+R2c3CPzRBj Ln5o/qaDIeRbABJ2k1jMcPhGJsAEkpHAH3oPrNTWlkxb6tbGu9LKLu/sDfsgRfsEQB+3gud6MwEm wAQUEJBC4NyiGTmzFeStPEvXTdlRKAmkaLTxlcVI+ePBBTABJuAyAkJKvKDPILms4lRd1wlSudj0 LwDDXMiaq8wEmAATsIJAG6kFZrnRyMFVgkQ+6iTkX63oUS6DCTABJuBaAhL9Aqhznc871wjS0HM2 99aEeIEdprr2K8IVZwJMwEICQmBMXsGmGy0sMu6iXGHUkHvx2nRPddpHEDg87hZzBkyACTCBpCEg G4UUQ4oKc5a5ocmuGCF5a9IeYjFyw+PEdWQCTMBZBESKFGKGbpnsrIqFrI3jBWno2NIxHII8ZN/x QSbABJhABARkDlI8z7lh06yjBWnImNL9IfF0BMT5EibABJgAE2iRgByVl192bYunHXLCsWtItN9o O8reBztNdcijwtVgAkzA5QTqhSaPK5re/UuntsOxI6RyUXo7i5FTHxuuFxNgAi4kkBaQ4tVRo0od G8DUkYI0JL/sGAlBXrw5MQEmwASYgEkEhMRBtZnifpOyMz0bx03ZDb9gU5a/PvAFgANNby1nyASY ABNgAlIKeeqSGd0XOg2F40ZI/nr/AyxGTntMuD5MgAkkEAEhpHh2RP76Tk5rk6MEacjYjcMBcZXT IHF9mAATYAIJRqC7X3gedVqbHDNld8LoLW3T0xu/BbCf0yBxfZgAE2ACiUhASoxZUpgzxyltc8wI KT29cQqLkVMeC64HE2ACyUBACPnYiRN+6eiUtjpCkIbll50C4AqnQOF6MAEmwASSg4DITvN7pzml rbZP2ZFNfE0GvuGAe055JLgeTIAJJB0BiZGLC3Petbvdto+QajPEP1iM7H4MuHwmwASSmoDA407Y MGurIOWNX3+IRMBV8TqS+qHlxjMBJpCoBA6oyRB32N042wRp8mSpwe99AhApdkPg8pkAE2ACTEDe OHjshkPt5GCbIC0r2XQlII+zs/FcNhNgAkyACewi4NWkeEIfLOw6ZO0bWwRpZH5ZFynkPdY2lUtj AkyACTCB8ATEse9/V3Zp+GvUnbVFkBqaxKiDumZxzkyACTABJhATAYl7cs9aa8vfZ8sFadi40qMA 2KbAMXUQ38QEmAATSBICAuiipabfZUdzLRYkKQIB+R8AFpdrB1oukwkwASbgTgIC8uph+RsOs7r2 lgrDkPxNFwHiWKsbyeUxASbABJhAVAQ8AWgPRXWHCRdb5qnhd48MPwDobkK9OQsmwASYABNQTEAI eVbRjO5vKi5mV/aWjZBq0sVNLEa7uPMbJsAEmIDjCUgpHjjyCmnZXlFLBGnEmC3ZEPJmx9PnCjIB JsAEmEAwgQM7lJddHXxA5XtLBMmX0vhPAG1UNoTzZgJMgAkwAQUEBG63KkSFckEafk7pQZC4SAEm zpIJMAEmwATUE9gn1Z/yV/XFWGB+7feARkdeKxrDZTABJsAEmID5BITENXnjN+9rfs575qh0hPT7 Jthz9iySPzEBJsAEmIDLCGSJQODvquusVJACAZC/OstMy1XD4vyZABNgAslKQEp5xfD8Tb1Utl+Z IA0pKD0ZwDCVlee8mQATYAJMwDICqX7NTwFVlSVlgiQgbQ/2pIwaZ8wEmAATSEYCUpw/ZOzmPqqa rkSQ8saVngSIIaoqzfkyASbABJiALQS8Aj5la0lKBAkBOdkWVFwoE2ACTIAJqCUgxflDz9ncW0Uh pgvS0PzSE3l0pKKrOE8mwASYgCMIeOFRY3FnuiBB4FZHIONKMAEmwASYgBICEvK84eM29jQ7c1MF KW/8+kMkMNLsSnJ+TIAJMAEm4CgCqb6AuMHsGpkqSPB7yL0E7zsyu5c4PybABJiAwwgI4PIR+es7 mVkt0wQpd1zZAQDGmlk5zosJMAEmwAQcS6CNT3j/bGbtTBMkLYAb2WedmV3DeTEBJsAEnE5AXntc /voMs2ppitPT3LPWdhCQl5hVKc6HCdhNwOsVaN9WQ1amQFaGQEaGhox0gdQUAaEBaali19x0Q6PU j1OdG30S/gDQ0CDh9wPVtQFUVUlUVgdQXRNATa20u2lcPhMwk0DnLOE5H8BTZmRqiiBpqekTAcnx jszoEc7DUgIkLL3396JvrxT03T8FPXK8yNnXgy77eJTUo7pG4tdSH9Zv9OGXDT78vK4Rq39uRFV1 QEl5nCkTUE1AAtcD8mlAxP1rK24DhNxc6fV0LfsZwH6qG875M4F4CXTqoOHQAWk4dGAqBv0hFQf0 NOU3WbzV0gXqu58asXJ1A75YWY/NW/xx58kZMAGrCAjg1KKZOQviLS/ub6O3a9mZksUo3n7g+xUR SEkROKR/Kv54eJr+v3t23I+8kpr27O4F/R9+StN0/MZNPnzxTQM++7oeK76pR31D3D8+ldSbM2UC REBKXAUgbkGKe4SUl1+6FAK53C1MwCkEaK3n2CPSMfiEdBx/VLpTqhVXPZZ9WocPP6vDxyvq9fWo uDLjm5mA+QQCwu/pVzRr3zXxZB2XIOXllw2AkCXxVIDvZQJmEKCR0PFHpWHw8Rk48ZjEEKGWuJAw LXq/Fp98WY/GRh45tcSJj1tLQEDcXzQz+2/xlBqfIBVsfBQQptqhx9MYvjf5CPQ5IAUjB2fgrFOz kq/xAN4qqsE7i2vww5rGpGw/N9pRBLZ2kuXdCwsHNsRaq5gFadSo0syaDGwE0CHWwvk+JhALAbKM yzsxA6OGZ6Jfr5RYski4e0iQ5i2qwdIPa1FXz6OmhOtglzRISHleUWH3V2OtbsyCNLRg46US4plY C+b7mEC0BPbt4sGoYZkYdybvMAjHbtbb1Zi7qAYby3zhLuNzTMB8AhIfLC7MoWjhMaWYBElKeeCM udULXp1TeQDtq+DEBFQSGHRQKkYPy8SQE03bEK6yuo7J+73ldZi7sBpfr4p5BsUxbeGKuIOApkE+ PLnTjQMOSv93LDWOWpBIjAB8T4XNW1iDp1/bCRalWNDzPa0ROO7IdH1a7pjD0lq7lM+HIfDNqgZ9 xFT8UW2Yq/gUE4iPAE2fP35fZyOTvwsh/mV8iPQ1FkG6F8AuSwoWpUhR83WREsg9PkMfER0yIDXS W/i6CAjQxtt5C6t1Cz3JExsREONLIiXQe78UPDlllxgZt3mFEFHt8I5KkKSU5E9lr4lpFiWDP7/G Q+CUY2lElIXDBrIQxcMxknsfeqoCbxfVRHIpX8MEwhJoQYzontOEEPPD3tzsZLSCdCqAkAWQKP3v 5Z1s4dMMMH9snQBNyZHFHE3RcbKOwKofGnTLPNrTxIkJxEIgjBhRdjOFEFGFJIpWkF4EcEFLFSdR eviZipZO83EmsAeBA3unYPTwTJw6OHOP4/zBWgLkmohMxskbBCcmECkB8gP59INdWru8vRBiZ2sX GecjFiQpJf3VqDZubOmVRaklMnzcINCxvaabbp9zuns3s1IYCWMdJjNDoLau6XNKCnaFojDa65ZX GimRVd53P/ImW7f0mV31pC0Y40a30Wc2WqnDJUKI51u5ZtfpaASJhl7Td90Z5g2LUhg4SXxKCGD8 WW1w6bi2jqZAISHWl/qwcZMf5OR0yzY/tu8IoKIygIqdgV1CFK4RXg/QsYMHnTtq6NTRg25dPdi/ hxf75XhBZuxOTrSPqfCtamzdHtV6tJObxHUzkUAUYkSlLhJCDI+0+GgE6U0AoyPNmEUpUlLJcR15 Vrj1Guc59aA/urRP59vVDbr7nTW/+pT7hyNh7pHtxYADU3BQ31TdiKNnjvO8kD8/sxKvzK6KSICT 4ynmVkYpRgawbCHEJuNDuNeIBElK2RHA9nAZhTrHohSKSnIdoz+8Z52a6Shfcx9/UY9PvqjDFysb 9BGQE3qka2cPjjg4DUcflgayNnRSuu/RHSj6gA0fnNQndtQlRjGiql4jhHg0kjpHKkgUnvzZSDJs fg2LUnMiyfP5vDFtcMlYZ0zP0R/UpR/V4qtvGxwfW4g8lx91SBpOOCYNp+Y6w+CDnLjSd/nnX3h9 KXm+wbtb2r6thosL2kayZrT7pt3vioUQg3d/bPldpIL0NtmUt5xN+DMsSuH5JNpZp3hYIKuxhe/V 6kHu3BqmgcSJggtS4D4nxHZ66fUqvPh6JU/jJdqXNkx7sjIFJo5vF6sYGTnvK4T4zfjQ0murgiSl bA9gR0sZRHqcRSlSUu69zuMBLhvXDgWj7bOeW7fBh/lLavQpJjJASKTUrq2GYSdl4KqL2tnerH8+ VI73PmYzcds7QnEFTBIjquWVQognW6tuJII0HkDM7sSDK8CiFEwjsd4POzkDt/zZPqMFGg3NXViD L1bWJxbYEK0ho4hDB6TqXi3sXG+a/U41XplTpVsehqgmH3I5ARPFiEgsFEKMaA1JJII0E0B+axlF ep5FKVJS7riOwoVfPLYtzjnNnlHRzHnVmLeoGmWbk9NEOWdfjy5M+WfYw5+e0keeqdB/DLjjieVa RkKA9tZdPiHuabrmRXUQQoT1nNCqIAUCcqYQ5gkS1ZBFqXk/ufOznaOiFwsrMXt+DaqqE2taLtYn gf6AnD0yyzYjkjcX1GD2/GqOwRRrBzrsvusuax/vmlGoFh0phPgi1AnjWKuCNOutquvHnJ71kHGD Wa8sSmaRtD4fWiuiX0/n2vCr/IXCSsycW+14Sznre6WpRK9XYMxpmbjiPHvWmZ54aSdef6tVhy52 4eFyIyCgSIyoZE0IEdbPPHnvDpu2+f5y7uqfGo/PO8nc4Gh/6JOCju09+OTLxJ/zDwvYZSfJ0uvZ aV0w4EBrvQ2QEP3ffeX63iF/cs7ORfSkBAJAyfeN+tpOba3EkYdYG0vqqEPT0KG9B6Wb/dhZyaPX iDrNQRepEqM/3bK14sWnMqesWnVnfILUa9Ckh0s3+Tuv/qkRLEoOenJsqArtKbpuIhldWpdmzK3C P6aU47Ov6sFCFDl38rNX8kMjXn2jGo0+icMHWSdM9GPzrFOzUF0r2S9e5F1m+5WqxOiav28lLyjp dahatGbV1F/DNTTsCGnI2M19hJR3UQbk14tFKRzKxD3X94AUfVOclc5Q58yvxgOPV2Dph3U8PRfH o0UjppXfNWDGvGpoAjjYQj96Rx+ahvbtPPh5nU93PhtHM/hWxQRUidH1d2zDqh9+30wtsGltydQl 4ZoSVpB6DbxxrADOMDIgUfplgw+nHMfTdwaTRH+lX7p33dwR/XqnWNLU5Svq8PgLO/HGuzU85WMi cRpdfvltAxYva9o71L+fNVOuB/VNAVkA/rYtgJ/WsZcHE7vUtKyuubQ9Ro8w3yMIiRH5iAxKaWtL pj4d9Hmvt2GNGobkl84SAmOa30V7H26/gdzbmZvY0MFcnvHmRhswrTTnfvQ5EiJnLoiTFdthA9NA 01H7dfcip5sHbTI1tMnSdMwNjVIfBZBn8A2lPtAG3a9K6rH2170CLMfbLabcT/uYRg/PwinHWec3 j4wdyOiBk3MIkBidaY0YUaP9XunvuqCwZ4t+UVsUpPx86dkuyrYCCLnbUZUokfnof54Na6runN5M 0JrQegMFzjvpj9b8saJ1ohdmVoH+qDsppaUKDD4hA8NPzsAhA2IbUWwvD+CDT+vwzmJn+oEbNSzT 0nXBjz6v04MB0pogJ3sJqBKjG+7YhpV7jox2N1SK/MWF2a/vPrDnuxYFKS9/w3EQ2kd7Xr7nJxal PXkkwieaXrnyAmtMht9bTt4VqvXwD05iRzvUzz29DS44t42p1fr863p9Ayn9UXZS0jToa4QTzja3 veHa+NyMptAW4a7hc+oIqBKj2+7djk/D/dgQeHLxjJwrW2pZi2tIvQbedJEQGNLSjXSc1pNUrCnR vDMthn7KJuHh8Jt+jqboLjjXGu/cT76yU18r2rzFOTbcNCKiP8r33baP7prHbMA53bz6iOsPfVJR XRvAxjJntJ0s8mh9icJy0B6zvr3UrxfSKJy+4zSlSdF3OVlH4M8Xt1MSDqZVMWpqYoe1JVMfaam1 LY6QhuaXLpACEUX645FSS3jdcZz2FNHUDXleUJ30Hf3vVDsmDhG1lzaTjh1tvZcDGimR/z0aOTkp 0ZoC/YK2Kt37nx1YvIzjLVnBm8SIPHqYnSIUI71YKf09lhT23BiqDiFHSLm50ivaVP0XQEQT5zxS CoXWHcdGDc/EnZM6os/+6n8VP/x0BV6aVYXKKmdsmKTRwLgz22DaHftYuk/HeDIoSuzQkzJ0I4kd lQE4ZbT4/c+NmPVONVJThCUboGmt0uMR+KpkD4ssAxO/mkTACWJETdEgVqxZNW1lqGaFFKRex151 tJDy6lA3tHSMRaklMs49ftn4tpg4Qf16EVnO0eZWChXuhETesmmt7N93ddajtNpdpwN6pmBEbia6 dfVie7kfW7fbL9iNjdBHbr+W+nGyBRFsDx63vWsAACAASURBVOmfqntuIevE6hqewjP7mVQlRpOn lmP5iihH+EJsXVsy9a1QbQwpSH37T5oAgWGhbgh3jEUpHB3nnOvYXsOV57eDFRtdyWKSgrrV1tn/ R4aEiPZVPfqvziAXN05LfQ5IwWl5mejU0YMt2wIor7BfmNat9+mjWlpfG/SHiCZMYsZKJvW0zWB9 qR9ULidzCKgSI4qJ9cEnMRnoZK0tmfpYqNaFXEPKKyibC8hRoW6I5BivKUVCyZ5rjjksDffc2kl5 4bQ2MnNuFTY5wGiBhIj+0N9wuXXrImYApvW2eQur9T1NZuQXbx4nHJ2OO28yf/9hqHqRN/cXX68K dYqPRUHgTxeocYIcb4DGVCm6zi/M3tK8KSFHSL0GTnpYADGvfPFIqTlmZ3ymxcxbrwm5rczUCj79 WiWeea0SVQ6YeqHQ3/+7vwsorLrbElmbjh6RpW++JWG321np+tKm0VJ6qoaBikdLhw5MA0XI/ea7 BvicYYzotscHThUjAtkoRPG6kqk/Noe6lyANz9/USwp5W/MLo/3MohQtMbXXTxzfFpeOU2vSvWRZ LR5+Zifo1e405MQMkH8uFRZFVreN3PzQVGN6uoYNZfavsaxYWY9fNvpwyrFqrTIP6puqm+GvXN2I Tb+xKkXz3CkTo3+bE7peCPnz2pJpxc3btJcgHXDwDacC4tzmF8bymUUpFmrm3pOWJnD1Re2Vxy4i bwsPP70Tv2219w8HucK59rL2utFC1857Pd7mwrU4NxqV0BoLWb+tXe9DXb1963L03SaHrRlpAqr9 4tEot6IyALL+49Q6AVViNOVxMs+Pac0oRKVFw9qSqS81P7HXN7b3oJsmAji2+YWxfmZRipVc/PfR lM+F57bF6UPNd5wYXLsHn6jQg+YFH7P6PcVposXb8We1Qbcuez3WVldHaXmDDkpFwag2ujB9v6YR ZBFnRyKHrZ99XY/ynQEce4TaKdE/Hp6u7xejDbycWiYwcUJbFIw23+MGidGCYlNnPjpflP/gA8XF e8ZH2suoIa9g43JAmCZIBjo2dDBIWPOad2KG8vUicoY7Y16VrdMpZC1HfvdIkJI1vTK7Cq+9UWXr iClnXw/yz2ijIuz1Ht1Khh5Pv7rTEVabe1TMAR9IjGhfndlJgRjpVfT40X/hrJzVwfXd46ckOVSt FVX/BmD6LkkeKQVjV/ueHkrVgfTojyB5bq6qtmfaiJydXj6haR8VbTBN5kR7eMjlEfXEdz82gmIg WZ0qq6Ue/ZnqQF7RVSUa9Xfr6sGOioDt08Oq2hhLvm4TI72NQi5vvkF2D0HKOfjKgwBcHwuQSO5h UYqEUnzXkPHChflqjRfuf2yHvpM/vprGdnf/vim4dFw7XH1RO9CGUjsTTR9R8Luff/FhQ5kfASn1 zZ121YmE4PwxbdDogx5M0w5h+mZVA35c68OQE9QZPPTaLwWnDs7E9h0BikRqF27HlKtKjMizyjtL TJ2m24OZEFi7pmTaouCDe0zZ5Y0tPQ8SLwdfoOL9iNwM3HyV+ebHyR66QtUGOOMZoCm619+2xw9d v14p+nTQaUPUrocZbW3p1fDYTYEEySlp89Q2S9MNSM4bY/7USfOyWvtMDmxnvV1tS+j3lBShj2DH nBbz7pHWmqeff3lWFZ6fWRnRtYl4kSoxeuSZCt3PokpmUorFSwqzhwaXsccIqffASRcBOD74AhXv KaTx5q1+0EY7M1Oyegnvso8Hl09ohzNPVfflnzm3Gv99YaflfugO6OnVR3yTrmwPEiW7Erk9euqV nfr+KtqP01KimE7kk418wdEIhabT7EpHHpKGC85pq6+30FSelYnaTgYPZAlI9VCVaOqW9iuFDXmg qnCb8724oK0+VWt2NawQI6qzEGi/tmTqA8H133OEVFBKw6c9FCv4YrPf80gpfqL0B2/a5H3izyhM DnZEcqV1IfJArvoXdphm66dKvm/QfynG6o2a3DSRqfa4s+wfMVE/vrmgOuTIrjUO8Zy3IuAjjd4L 36pC6WZ7tx3Ewymae0mMzj/H/GfKKjEy2iok9i8qzPl112fjDb3mFWwsBUR28DHV71WJ0pz51Xjs +cQOl2yFJd3N/9ymx8pR/RwY+dOUF5luF4xWN9ozymrt9YH/7sDC98yZQ9+3iwdnjshCwSj72/XQ UxV4u6imteabep6CAF51oZrQB8EVjSYMQvB9bnqfKGKkM5cYubgw512D/64puxMn/NLRG/DcbZyw 6lXV9B1t1mvbRkOihkomx6g3XKHONxv5orvlX9vx68aWp6fMfkbox8kjd3dW7pamtXrTH2zyTk7P plmJPFiv+KYeRR/U6lN5FIPKrkRulAb8IVWfygs3/Whm/Wi9jb6LZHBxxMHqpvDyTsrAth0B/Jig xg6qxOh/L+3EnPnW/kjRny8hvlpbMnW58aztEqQDD/rrURC41Dhh5SuLUnS06aGk0BGqEnldePyF naD1EKsSmS3/5RJ1AhtJO2hK6//u367UcovMo8kwYulHTUYRqr0ctNTunH29GHx8Bsj4wMrNpt+u brJKpLJVJRJcenLJ4i+REhnKXKTAgpbEqPCtaptQiQ1rS6bONQrfJUi9Dp40EsAZxgmrX1mUIiNO bkFUrkfQw0lB9KxMNNq74jz1cZlaahO1+dZ7t+um0i1dY/ZxcpRKI4YPP6unxV0c2Nseg42DD0rV nbeSAYJViUZlbxXVIC1NA4WcUJHIBD4zQ8Pn31jXLhXtMPIkMbpkrPk/Qu0VI2qdrF9bMu0Zo527 BKn3oEkXmOkyyCggmlcWpfC0/nJJO6WL/HdOK8f8peasmYRvye6zfQ9IsSykwe5Sm96RR3ISom+/ t9YCLbgeFPPo4y/qdXGiUOoUE8nqRKO01FSBL1ZaN6Kg+FiffFkP8rWoKs4STYt2bO9ByQ+Nlo72 ze6/xBUjIiXarS2Zep/BbJcg9Rk46RoA/YwTdr2yKO1Nnhb6aUGYFsVVJLJQuu/RHVi52ro/SEY7 aPqxn8Wjg+dmkBCV61M6ofYSGXWz8pWixH74WR2+WtUACoZn9aZf8o9nbFy3st0kghSm5OjD1Kwr 0QisXRtND3hIG2ndllSJ0bPTKzFjrl3TdHv0QlrvQ/76xNpvH9Qrs0uQeg2c9A8Anfe41KYPLEq7 we/X3YsJZ6nzETb7nWrQ2okdsXbItFvFBund9PZ890JhJf7vvnJ9zcQOLwZ71ib0p81b/Hj/kzpQ yIWMdIH9e1jnFql9O49pVoWhWxf6KO2RIiexZDWqItEPnjOGZeplbCxzj1m4KjGiH2SvzrF2Wj5c v2oy8Maakmnr6RpdkJp82FVOBcQugQqXgRXnWJSAwwel4rF7OiubZ6fQ4k+/at8ud/rCDein3tqM /O7d/sB2fP51gy1eC2L5vlD8n/eW12HVj43IzBCwwl8f+Yhb9UOjLXt5SCjeXlyL9FSh7Hknwftt WwA/rbNvijbSZyH/jCxMnGD+uiqJEX0fHJWE9t6akqlfU510Aeox8KoDpMCNjqokoJvdqvDo4AaT 8NzjM3D3X9WFGievC9PftPfBJFdH7dtqyh676W9UYfK0cny8ot62EA3xNo42epJF3k/rfPo2hpxu akdM6ekCxcvNinkTXeuNdSUKRKgqIi15hSfvEbSu5NREYnTlBUkiRmTWAJSsLZm6lPpDf7r98PcF 9nDa4Ji+MmJwmD21Y0QSdeLmWfJQoNJb9z8p6qNNf3SMB8vrgbJf/TPnVYOmIrdud8/0jMGlpdeP Pq8D/acfKqOHZYJc5qhIJx6Trlv92bm29uTLO1G+w6/kjzIxu+L8droFnhN94CWbGFF/aEAf41nW BUlqYn9h3ZYTo+yIX5NJlFQ9kAbsa2/fhlU/WG+8YJRvvHbsYP7sMDkSJQ8dm7YkjhAZvIzX4o9q Qf8piuqo4Vkg7+dmp306emwXc9oXQ/14x40dzW6enh+53UlPE3oIFSUFxJCpqu8+OaB13DRdEB8p sb/xUf+r0HvQjWcC4hTjoBNfk2FNidZUVMwbU3+SJR25VdlQZp73gXieE/pjkD/KHF9cNBp64L8V IH9zZLGVDIlCXsxfUqOviZgdnHDRB7Uod4BFGnkJ+fDzemiK9mmRWTgZcnz6pf17lc4ckYmrLzZ/ Y7grvKELYG3J1Ifoe6uPkITUekp9b7Ozv8qJPFJS5RKEetSJfv0aTJjCp82VJLQ//2JCZs5+9Fus 3btLa7CguAan5WXihsvN/4PWYsEWnfh5XaPuZd7nB+iPttmJ8iTBe+H1Sj3on9n5R5IfRTy+5lLz +84VYtQEKCc3V3qLi4VPHyEdMOCma4RA70jg2X1NIo6UKKYJuc5RkWio/uQr9lnStdQm2ogZT7hl cm1EFkO0sZQTdN9t5GFjZ6XEMYfHt6eHhN4JIySjX/1+6KMYVcYOtFcpI03Tpwgrdlr7PJEYXXtZ UosRdbOW0rby6Z+/nbZTN3ESQvY0Ot8NrzRSomiGZicydCDLLyuT7groTDVi9NQrlfofbSvbw2XZ R4AMEd54txpTHt9hXyUUlkzGDuRdQ0UaNTwTz0ztAnKlZFVSJUZkPetEg41wXGUA+9F5w+a2R7iL nXhu3qIaUOwOs5OVokTid+4Zarwv/PupCpCTVE5MIJEIvPZGFR58wvzvvcHooTv3gdlrckbewa8q xcjOvYXBbYzmfUBqetgjbdSoUpqYNX9yNpraxHgthUhwqyjRnLFheh5j81u87Z8PlevOK1u8gE8w ARcToHUzitOlKt11c0cMO1mN1wiqM4VZUTFNRyMjN4oRMZEIdKNXrTrTY2lAPrMfIpWiRNNpZidy B3PdZe2VLNBSXW+6axve+9iejY1ms+L8mEBLBChkxgXX/qYbtbR0TTzHb/lzB9AoxuykKiCpm8WI GAsNXejVK4Xs7OQ9SJE8ECRKlMz+1WFMpz3xkjmRZ7MyBSaObwearzY7kbXZ7Hersd7CgHpmt4Hz YwLRECjb7Mdjz1cgINVY4NHfE/JcMdMkJ6QsRmF6V4ocOuvVAgFXj5CMJjpdlLp29mD8mWqcpJIY Pf3aTlBUUk5MIF4CNbXWWprFU18yB//PsxXw+SQorpbZieJ0kfd18vsYT1IlRq+/Ve3aabpmPHXH 3l4BdEmUP2NOFaVuXTwYO1qNGJFVFXnr5sQEzCIg3aNHu5r8+Is7dR91tLnc7ERRWj2aiNlyjdw9 me36jNpIYmTW7I3ZzKLPT+xL93ghRQe4fc4uqPVOE6Xe+6XgySlqonok1gMZ1In8lgnEQID2pdXU Slx+nvmRVcnVkNeLqEcjpxybjr9f1yGG1oS/JfG++1KHpEnR9CZ88911lkRJhfUdrSlFY+jQr5c6 MaJFzMT5deSu54tr61wCtNVh2pNqzMJpI3c0338So9tvMN8XX+KJkf486aA0IPEEiZpntyhRBM7H 71MzMiKXIG4173TunzKuWaIQeGdxDf4xpVxJcyL9UapKjMgNWIL+EG0aIQkI8yVcyaMQfaZ2iRIF 1vv3nftEX+EI7qBpCbftwo6gWXwJEzCVAIXquO4favYqkSiF8+hysqKRkRN9UprYaem5F69NJ08N 5jtSMrGW8WZltSgdc1gaptyuRoz+99JOR7uRj7ev+H4mYCaBku8bcOmNW5TsVaJN7aEcotL3/x8K pukSXIyaur06q4NXAubv/jTzqTIhLxIlSqr3KdHDeM+taqK8PvxMhZIvlgl4OQsm4FgCFMLi+cIm /3dm7/8zPIXTd5OSqu9/UogRgFQEMryQSHdosFhTH3LVovTFynplYnT/Yzuw6P1aU3lwZkwgWQiQ B+9HnlWzgdYQueUr6pR8/5NFjOhZDABtvBCIz1e9i55qlaJkeHUwG8fkqeVY9im7AjKbK+eXXATI EzptoA0EpOk+JEmUDGEyk+qbC2rw2PPJs8fQ70EmBehL6DWk5g+IKlFqXo4ZnynC66df2R/N0oy2 cB5MwAkE6A88xVdS9QPSrDaSGJGAJlMSgUAaCVLSjJCMznWDKN04eRu++a7BqDK/MgEmYBIBMpuu b5BQ4dXBjComoxgRN02KtmRll24GRLflQaLk1OHw9XewGLnteeL6uosAbZ+g/XxOS8kqRkY/0Agp aRMtGHo9wJUKwkzECvWKm7diza+Nsd7O9zEBJhAhAdrP1+iTuGSs+a6GIqzCHpeRk+Rkm6YLBiAR aJPUgkQwCt+q1pnYLUr0ML65sBrr1vuC+4jfMwEmoJDAK7Or0NAgbf9RSt9/w3xcYXMdnbUAPCRI zvh5YCMqu0WJHsbpc6uweYvfRgpcNBNITgL0/acwFuG8L6gkw2K0my4JEq0jJX2yS5ToYXx5dhW2 lbMYJf1DyABsI0DT936/NH3zfGsNYjHak1DST9kF47BalOhh5MB6wT3A75mAfQTI0Ims71TELgrV Khajvanw6KgZExIl8hmnOrEYqSbM+TOB6AksKK7FPx9S4yk8uDb0/f/fy+r/zgSX6Yb3JEgujA+p Fq1qUWIxUtt/nDsTiIfAex/XKRUl4/tfV58osbrjob3nvSRITZ4H9zye1J+yMgVy9lU3m0luRkYO yUxqxtx4JuBkAocMUOcvgL7/+WeYH2rdyTwjrZu6v7qR1sBh15EYTRzfTolvquCmUuRJEWR2Hnwu Gd5npFPrOTEB5xG47rL2yr//FBKdEsc2293/EvCzIO3mAavEyCiS9j4JAcyc17QXyjieDK8eXr1M hm52VRszMwQun6D+x6gBhUXJINH0KqBV0Z8FdiUNWC5GRldccX47x/rUMurIr0wg0Ql0bK9ZKkYG TxKliwuSfiuogQM0Qkp6d9JWj4x20f/9DbkuSfEKHr43BxPmcy0vCIehw6eiIdCtiwdjR7dRPk3X Up14pNREJiBkJQlScvk4b/ZU0DDdijWjZsXu9ZEeSq8XePpVtjHZC06IA7SJkRMTiJdA7/1S8OSU zvFmE/f99P0nv3rkyihZk9S0eg0yuUdIVs4Zt/agjTuzDSZO4OF7a5z4PBMwg0D/fs4QI6MtNFPi 1JAYRh1Vvnr8qNEgkncNyQprmmg7kESJLPA4MQG7CGRkJL4F5CEDUvGfu+0fGTXv42QWJQ2o0gSQ lNuFnShGxsNJ0SztcvRo1IFf3UugPs64jhr9VUjgdPxR6Zh2xz6ObWGyilIDtNqkXENyshgZ35Kz R2aB/jAkc3wUgwW/RkegsZHX11oiNuzkDNzy5w4tnXbMcRIlSkm1ppRVvUOTkOodNzmmmwFVYvT6 73GVzGzqmSMy9fqmpyX2L1YzmXFeTKAlAqOHZyoTI4pAa3YiUco/I8vsbJ2aX13x873qvIDY4dQa ml0vVWJ0273b8elXTdbzNN1mZiI3I5QK36pC6WYOUWEmW84reQgUjMoC7flTkcgZK/m/o2SMbMwq xwgcakQiMCtfB+aj65AmZHIIkhVi9MRLO6FipESi9OIjXXFQ3xQHPkdcJSbgbAIXntvGEjGi6bWX Z5lvtk2ilAQjJX2mToOQCT9CskKMjK8kiZIqV0CP/qszjj5MndNHow38ygQShQBto7gwX81Witsf 2D0yMniRbzoWJYNGNK9NAyNNAluiuc1t11opRgabJ1/eielvmv9LifK/99ZOGHJChlEUvzIBJtAC Ado+QdsoVKS/3bMdy1eE9rqmUpTMXhJQwSa2POVmuk8LaFpZbBk4/y47xMigQh4XVPxSovxvu7YD Rg3j8BUGa35lAs0JXHNpe6j64339Hdvw+dfhPa6pEiUSWTLOSMC0ldrkFVLobxKtgXaKkcGSHkpy B2L2Qiflf93E9qANjDPnJp+ncIMvvzKB5gQ6ddBwwTltlfmlu/KWrfh5XWPzYkN+pu8/JcNXXciL Yjh47WXt9bso5HrCJCFLqS3erBp/WU2CzQA5QYyMB4UWOn0+4PLzzJ/HvuK8dsjK0KDC5NSoP78y AbcQ+EOfFDx2jxrvCxTldfa71Vi/0RcVDhIl8lFp9tRhoomSDDQtHWnz5uWQzCaM1DpJjIwnd8bc KvzvJTUOMcj31VUXqjFnNerPr0zA6QTI+4JKMZo+typqMTKY0fS9ijVlEqVEmb4T0DYRLyNM2gYD nptfad7Y2LdjZjuC9xnFmi/tI3jkGTWO1c85PQvU9pQU3kAba//wfe4lcFpeJu66uaOSBtDI6OnX dmLzlvj2AKoUpTOGun9NSRMB3ZZBjxgrpVgvhDxQSY9alCn9QSbPBmanux4q37XpNd68ac63vkHi 5qvMd11CbScXZLPnV2N9aXTTCvG2i+9nAnYRoDhGKqbDqT1vLqjBo89VQJrkickILWP29N31l7fX 16oXFNfa1Q1xlys0/EqZ6CMkoQXWx52jjRmoEiPagf3+7zuwzWoePTQkcioSjQ6fe6gLDh/Ee5VU 8OU8nUWAjIVUidGc+dW6H0mzxMggp2qkRD9yR+S61hjA37ApRx8hNU3ZSbhWkFSKkeEOxHiYzHol kbv5n9vNym6vfKbc3glDT3Ltw7lXe/gAE2hO4OqL2imLHUTeVh57Xs2aL7WDREmFRxcXi1JpcbHQ p3V0QZJC6MOl5p3u9M9uFCOD6Zff1uNPt2zFO4vV2JP87S8dcM5p5vrVM+ruhFdaxD7qEB4JOqEv rKxD504e3eHwGEXP9qtzqkDeVlQnVW7GXClKQQMifQ0JkL8C7loQd7MYGQ/7T+sa8eLrVfD7ocQY 46qL2qFDew3PvGa+J2KjDXa9nnB0Ouh/8Ue1mLuoBt+sijMIkF0NMbFcjwf6jxBVTkRNrGpMWR06 IBVTFcYxou0TVoZ7MITP7A28JEqNPmDJMpesKQUNiHRB8gY8P/pFIKaHxI6bEkGMDG5bt/vx3xeb fpGpsBAcf1YbZKQLPPqc+l99RpusfM09PgP0f+F7tZi3sBrf/RTZpkUr66i6LCEAGjEksvk/rY/Q H1pVibZl2OFRW5Uo3XZNB/h9cpcXclXczMlXrjHy8dCbIwZOqawVlbcCQv9snHTiayKJkcE3EAA+ +bIebdto6N8v1Ths2utBfVPRsb0Hm7b4UbHTGT88AhIgsTQr9TkgBWT+26mjB1u2BVBe4Yx2mtW+ UPmQEJ11ahZ0p7uHmjd9+eaCauxwyHNC7R53Vhtcc0mTd4JQHOI99u+nKvDGu2qmziOp2+ff1CMz Q8OAA8397p9yXAZ+2eDT/0dSD7uuEZDPrymZ9iWVrwvQqlV3yt4DbzofgHPj+gL6XhsVpt3B8Uzs 6hQq97Ov1DyYlDftYj9zRBbW/OrDr1HuNlfBhKYUzj2jjel7pw7snaL7+WvXVmsS4MrEFKbTh2bi v/d2xjGHmydERj/TNHJdvUm2zkamMb6SN5ILzjXvh0vzakyeWo6iD+yf2iJRUvGD1BWipImpa7+d qtsx7BoR9Rk4aSSAfs07zCmfE3FkFIotPZgej8Ah/c39tWSURdNbldUSqx0wtdW/Xwp65vy+jGlU 0KRXGhWSAGdladj0mx87qxJDmIafkoHrr1DnXLeqOoDnZ6rxVB9N12ZmCPzpwvagTd+q0rW3b8OK b8I7SVVVdqh86QdpMoqSP+C9bd2qKfpDt0uQeg2adAyAY0OBsvtYsoiRwfmrkgb9F+qRiqzIjjks TRc9KsfOJCFw8rHpSqswoF+qPq2Vnq5hQ5kP1TXO+OUfbaPph8S1l7bX14q6dNr1tY02m1avX/5F vel771ottNkFBx+UiosK2mLkYPM3ulNR5H2BRkY0neW0pFKUflzr078DDmvzzqWF3f5u1GnXk917 0KT9AZxhnHDKa7KJkcG95IdG/LY1gOOPVvMHm0ZgNK21YmW9aTvRjbpH+kpThxeca77T2VDlD/xD qm6BlpoisHa9zzFTUqHqGnzsxGPS8eeLm+L67Ntl19c1+BJT39Pifumm+NzkxFMhcoMzeVJH9NpP TXRk8r7wyLMVqKl17g8TVaJEcdRoZmSjjf0b4tn4Zm3J1KeM47ue8D4DJmVA4FLjhBNek1WMDPZk Fk4PUJ6iTa40rUW/trduD2BbufVTWrQLnkYsVkbBHXRQKgpGNa1d/bCmEY0ONcr74xFpuPrC9nro gpx91UxrGs+Z8frdj436pk3js9Wv5HlBpck6bUb97wvusDZVJUr0t8RhorR4bcnUN4xnbZcg9Rt4 fVVAaLcYJ+x+/csl7fSpFrPr4RQDhkjbRb9mPvi0DgJCN0yI9L5Ir+vbKwW0QL55qx8/r7N+CoP+ CLZv58FBfdX8Im6JA00LkZUfrddRHWgvmBPSUYem4U8XtsPFBW3RI9saITLa/ez0Sqz5xfpngJwC k+cF+qGgKlGwzKddth+PREnFd8NJoiQhX1tbMu1Do993CdLPqx6q7T3wxisBYc0cilGDEK80RXH2 SPMXM+9/bAeWfBg67HCIajjm0I6KAFaubkCbTE2JKFFDaZMppa9t2GBK0Tc7tPMoa1u4jqSpSwrh QRM4q35oBJng25EOGZCKyye0xcQJ7ZQZeoRr18x51SicZ32wx4EHpuLisW11k/1w9Yvn3JMv78Qr c+w31IilDZ9+mdiipEnt32tWTf3RYLNLkOhArwE3jxQCvY2TdryqEqMpj+/QN0/a0SYzyiQzaZV7 laiOhw5M09eVSJSsHDHQ1N2n9GuwrfUjJaNvDhuYpk+PEWea0rBKmPr3TcGl49rpI4QDelo7SjTa To5En/h9c7ZxzIrXkUOawkb03l9du+/9zw68VWTfHiMzOKoUpa9WNcQdWiOeNgrg1jWrpu6Ky7OH IPUeNOkQAMfHU0A896oUIze7Zg9mSsN4r1fgYEVm4bSuRCOGb75r0PfxBJet+j198eoaJFRZF0ZS /yMObhKm2jqJ739uVGbw0Wf/FN2SjEIH0KZeuxK5ynnyFetdS106ri2uPF9tYMnrbt+m/4izi62Z 5aoSpRG5mfji2wb8ttWWOettiwtzCjVkfQAAIABJREFU/i+Y056CNHBSVwBjgi+w6j2LUeSkv/y2 AVWKjQGGn5Jpy36lku8b8frb1boQqBLdSEjTWs4F57RFTZ3U15giuSeSa2jf1QXntMHNV3cAbeK1 Ky1fUYfHX9iJtxU5922pXZ06aLj8vHYw239bcHlk1v23e7Y70cQ5uJpRv1clSqcOtkeUpBTL166a +mIwiD0Eqc+gSWRz9OfgC6x4z2IUPWVaiCevC7Q/RVWi/UoZGZrlmwdp2oxEd96iGgT8AFnG2ZVI mC48ty12VjWNmGKtR86+Hpx/Tlvcek0HJe6hIq0X+fp7bnqlbk1ntfkvmbBTmHHyGqIqGaEjGhqc a9YdT9tJlMgNmNkM7RAlTcjZa0qmLQrmsYcgHTngwfJaUXUTAHVPTHDppH6KDBhozShRpumaIdv1 kfbxqLTAo4Jo0blrZ3tMw8l9DU0nLHivVl/Tor1EdiVy0UPCtG1HAD+uidxWvMs+TUL09+s7mu6r LFoW0/5XgUeeqcDPNljSnX9OG9D0pMr0YmElnplu/fSjyjaFypvWkhNBlITEY2tWTVsZ3Ma9Yk7k FWxcDghLPDawGAV3RezvyckmsSRHmyoT/TGjMOx2pe7ZXowelqnUnUwkbaNRBnkWX/R+bYtrTB3b a/pGXHIManci56E0NWd29NNI2kWbry/KbwsVPiiDy3/46Qp9RB18LNHfX3dZeyVha668ZSt+Xhf5 j65YOQuBAUUzcr4Lvn9vQRpb+m9IXBd8kYr3LEbmU504vq3uGdn8nHfnSFMihsv83Uetfdeze5Mw qdgaEG1LSJRopEpGEB4N6NhBQ98DUkBTfXYnCjlCnrvtECJqO7mF+scNHZVj+L/7tieM8UK0sFSJ 0hU3b8WaX5WKUsVJA7I7TZ68Z9yjEIK0cRykeC1aMNFcz2IUDa3orqXF4j9doNZ6iUK70wjBbl94 ZKlGMaTI3Qyn3QTI/Y9hGLL7qLXvaGMvTdOpTGS88PLsKmwrt8VCTGXTosrbpaK0aPHMnOHNG7rH GhKd7HPQLTXQpLIREotR8y4w9zNt7ly3wQdyO68qHdDDC7LCa2gEvv3ePgetFPPo4y/q9bAdZApv p/m0KtbR5EuRgW+9dzu+/V7pL9uwVdqvu1f3MpE/Su30MW3kJTdANDJN9kTfQRWb5unH3rJP69XE FhPylbUl04qb991eIyS6YEhB6W8C6NL84ng/sxjFSzDy+7t38+Lc07OUzDEH14KcVU5/swpbttn/ K5W8HdAak0rLw+C2O+X9C4WVeO2Navh89v5xpj9g9GtddbIruqvqdsWTf1amwMTx7ZR835VM3wlt 1OIZ3d5q3ua9Rkh0Qe+BN51EMd2aXxzPZxajeOhFf29lVUCfV8/KND8SZXBtyAcdCd9v2wIgZ7B2 ps1b/Hj/kzqQp3QK206/1hM5kX82GhF9sbLBMs8SoXimpQndKSo5R1Wd/jGlPOGtZ2NhSE6CVY6U Fr5Xq+99jKVuoe5Jlbj+p1VT97KQakGQbuwJiGGhMorlGDlOHHOa+UP4ZDDtjoV38D3kJ67BB5AH ApXp+KPS9TDMX5XUg8KT25nKNvtRvLwOZA1Hgd5UBQG0q43kXeH2B7brU5VWungK1V4ajf7v/s6g uFMqE60X3fVQue7WSWU5bs5bpSileIW+FGBSPLHvFhbmTAnFOqQg9Rn0Vz8gJ4a6IdpjtMCuIuoj mbLOX2J/6OFoedhx/berG/S9J4MVbqKldg04MFXf/PnLRp8jgp9RXJ+lH9Xhp3U+PRJnTjd3j5im v1GFO6eVY/mKekeEzZg4oa3ug0/1M134VjUee36na4MrquYTnD+JEq0jZ2WY64iZNuKmmidKs9eW TN1ruo7aEVKQ9usyZZOWVTUJQFw/e0iMVLgIof0wtIufU+QE1pf68M6SWqSlqgljEVyTU47NAE3j kLcFJyRqe9EHtfi11I8O7TRYEejOzHbTAv49/9mBDz6pc0RgwWOPSMNVF7XHqbnqrRtpi8FLr7vT U7eZz0A0eTU0Sn00QwJipkcHs0RJSPlQ8w2xRvtCCtK6dXcG+gyYdAoE+hgXRvuqUozs3JwZLQcn XU9RMmmXN8WfoXhAKtOgP6Tqng0oTLRTQkWvW+/TvT5s2uJHp44ePTihSgbx5j3r7Wrc/98KFH9U 65jRAe11u3Zie0tiNd3yr+1Y/AHPgsTyHNHUGlnbOlGU/H55/brvpoWMlBhSkAjAAQMn7S8EhsQC g8UoFmrW3UMjF4oSe9xRasKjB7eEzM8zMzR8/Z29C+/BdSLXOfOX1FjGILjsSN6/8W41pjxeoY/q yDjFCYnWCGktmLxDq07U/kl3bQeNbDnFTkC1KJERRdQRlwXWLH29+90ttapFQeo98AYfhLispRtb Os5i1BIZZx3/cW2jbpGmKhJtcGv1taUxbWyLShtcl+D3xOClWbSxMoDjjlQvzsFlh3pPcXumPlGh W5FV7HSGENH+LgoaSBGcaSuB6vTcjErQfiq7jTVUt9Oq/FWKEu19ilqUBApbWj8iJi0K0lEDp5bV iqprAUT8TWUxsuoxM6ecHTubTMPT0zRY4biUotKSb7MNZT49tIU5rYg/F3KWSsK0s1KCnKhancik lox0SJBos69T0ojcDDx+b2dLng1qM5l0v2NxOAynsFZZDxKlX0t9GGNyFG5aU4pWlATEA2tKppa0 1N6QG2ONi/MKSmcDONv4HO6VxSgcHeefOy0vEzdeoX5To0Hi6dcqQVZjTkvkqJb2VV2p2P0StZvW huYuqsE3NoSND8edfAWeOTxTubNeow40RUcjI/rDyUkdgd77peDJKZ1NL4BM8p9+LSIryIBX+rss KOy5vaVKtDhCohv6DLipEwROb+lm4ziLkUHCva80fbXkwzp4NHMtc1oiQvuiaCqPDC2ctlZAZrOv zKlCQ4Oa/VsffV6nmzFPf7Pa1vDRofrmgnPb4I4bO4IiB1uR6IfJs9Mro1+LsKJyCVYGjb7JFRB5 1DAzGSOllasbQLHMwqTPFxX2eCTMeYQdIeXmr+/rEZ4fw2XAYhSOjjvP0Y57CmNuVZozv1p3Bkqe FpyWPB6A9m+dPjQzbstEmpKjX5M//2KvR4tQjIeckKH/oVJtfWmU7RQHvUZ9kulV5Ujp4WcqwqG8 Z/HMPUOWN784rCDRxXkFpd8DOLD5jfSZxSgUlcQ4RtE9J09SHzogmBYFWCPvzQHnLKMEVw8d2ms4 8uA0HH5wKg7okYIe2R60ydL2uMb4QKbltFb2/U+NesTdVT80wOc8vdVDZYwengmasrUq0b6qJ18O afVrVRWSvhw7RElInFRUmLMsHPxIBOlBALRJdo/EYrQHjoT8QPuVaDf+OQrcPoUDZncgwHB1a36O XBNlpGtITxfw+yRq6qTugbqx0dnrISSk487MwrgzrRsJE7up/yMPK7ypvflzZMfnfr1S8Ph9ataU QoyUtneS2V0LC0XYn2Vh15AIUu+BN9H8wkXBwFiMgmkk7nsaqZAvvPKdARx7RMTGlnED+eMR6Ti4 f6rulYCC3zk50Zw5hUCg/UJVNRL1DdKxIzziqGnAhLPb4P7/64RBijdHB/cbGS7c8eAOfPOdM7x3 BNctWd9v3xHAF9824NTB5o6OaU2JQqzTJvygNOutwnazgj6HfNuqIHU64cGN6XW7zb9ZjEJyTOiD P/zciIXv18LjESDv3lak7K5e5B6Xgb69UnQT8dLNYX9YWVElV5dB1oMU4v4/d3fG4YOsNW0n9z/P z6zi2EUOfIJ+2+q3RJSkEHevLZm6qjUErU7ZUQZDC0pnSiCfpm9UDPEp0Nbsd6pbqyufdwABKyLS hmqmU02kQ9XVScdIiGh96IbLrTPpN9pPBhxvLqwGuWzi5GwCNFr+9537mF5Jipf2n2d3NAqZ0aWo sFNYiwcqPCJByhu7cdzE8e1eUyFGHGzL9GdAeYb79/DirBHqg/+FasiyT+t0x7orvtljOiDUpUl9 jIRoxCmZuOkq64WIwD/1SiVmzHXePrOkfihaabwqUVr6Ue36ISdk7tdK8frpiASp9LfGU7O7eOdH kmE017AYRUPLeddSWJGrLmxnS8U+/6ZeN6H+8LM6W8p3aqFkpj5qWJbu6seOOvKoyA7q5pV5+KBU TLnd/JESgH5CiJ9aq2lEgiSlfA3AuNYyi+Y8i1E0tJx7bbcuHj3ECK1P2JHW/NqoC9O7xbVwumWb Sj5tszR9H9Gl49RHbW2pHbRW9PpbPPXeEh+3HD/msDTcc2sns6vbTQixubVMIxWkCwG80FpmkZ5n MYqUlHuus9r1UCgy09+swluLakB7gJIl0X4S2nk/api5llLR8CMLOgqi58SNzdG0g6/dTcBkUSoW QgzenXvL7yIVpA4AylvOJvIzLEaRs3LblakpAhcXtEXBaHtGSwavT76ox4L3an6PrOrs/UBGnaN5 peCHucel47QhmZY5Pm2pfryvqCUy7j9uoihdI4R4NBIiEQkSZSSlfBPA6EgybekaFqOWyCTW8aMO SdN/tZN3b7sTWfkUfVCD1T81QrpYm8hI4ZD+qcg7McNSrwot9R+NRskHnVO9arRUbz4eHQGTRClb CLEpkpKjEaSxAKZHkmmoa1iMQlFJ7GMFo7Jwxfn2GD2EIkvrGxQGfNWPDa4QJxKhAf1SccIx6SCW TkhLljV5KP92NW9wdUJ/WFEH+mF5500xuxFbJIQYHmk9oxEkmqSOacWSxSjS7ki869q20XDe2W10 wwcnta7og1p8+mU9Vqysh1OC4RGfrEyBwwam4Y9HpOlTck5i9uhzO0HrRZySj8Apx6bj9htiEqVL hBDPR0osYkGiDKWULwK4INLM6ToWo2hoJe61hw5IxejhWTjlOPun8ZpTXrfBh5XfNej/v1/TiNJN PstGUB3bayBXKxQgkTwoWOUJozmDcJ9fnVOFFwo5ims4RslwLkZRai+EiNiTbrSCdCqAiPcjsRgl w2MaXRuHnZyBW/5MNjLOTuRzjTwMbNzkQ+kmP0o3+0ARdmMZTZGT2k4dNHTu6EGPHA96ZnuxXw+v 7mm7a+dWvXfZBmreoqZwGWRaz4kJEIEoRalQCFEQDbloBYm+PRH5AWExiqYbkutaWhspGNUGl59n 356ZWIn7/UBFZUDf80SOVBt9TZYSXo8Atcv4nJmu6Y5MMzMFaI+QmxJZKc5dVA165cQEmhOIQpRO F0K80/z+cJ+jEiTKSEp5L4C/hcuUxSgcHT5nEKCRw4Sz2oCilHJyBoEHn6jAu0s5PIQzesO5tYhw psMrRPhwE81bGIsg/QHA6uYZGZ/Zh5VBgl8jJdC+rYb8M7Iw7iwWpkiZmX0dGSy8uaDasrUzs+vP +VlPYERuBm6+qsXp9/uEELdGW6uoBYkKkFJ+AODE5oU9N6MSr8xmh4rNufDnyAjQesroYZksTJHh MuUqms14/W0WIlNgJmEmYUTpICEERRuPKsUqSJcCeCa4JBajYBr8Ph4CZHlGUWp5xBQPxfD3khDN nl8NWhPjxATiIRBClJYJIU6KJc9YBYl26e0aCrEYxYKe72mNAIXZPuvUTN0dUWvX8vnICDz8dAXe XlzDHhYiw8VXRUigmSidJ4R4NcJb97gsJkGiHKSU1wJ4+LHnd2LOfN4stwdV/mAqAa9X4PS8DFxz qT2xfUxtjA2Zfb2qAXMXVuP9j+t4jcgG/slSZJssbdurj+57aVaWmBtrm2MWJCrw7oe3n7j0wzpa T+LEBJQTILPqIw9Jw+jhmTj+KOdtsFUOIMoCFhTXYt6iat2PX5S38uVMIGoCAuL+opnZYS2wW8s0 LkGizPPyS5dCILe1gvg8EzCTABlAnDE0ExPOZsu85lzJ6ek7S2qwoyLQ/BR/ZgKqCASE39OvaNa+ a+IpwARBKjsXQhbGUwm+lwnESsDrAY45PB3DT8nAicck76jpvY/r9FhQX5XU87RcrA8T3xczAQEx t2hm9pkxZ/D7jXELUm6u9Hq6lv0MIKKY6fFWmO9nAi0RoP1Mg0/I0GMFDTootaXLEub4N6sasPjD Wn1tqLKKR0MJ07EubIgATi2ambMg3qrHLUhUgSEFZTcJyCnxVobvZwJmEdinowcn/TEdJx+brscR Mitfu/MhH3sffV6H95bXYcs2ttm2uz+4fJ3AqsUzswcBIu6IY6YIUu5Zazt4UtPWA+AJfX5CHUeA zMePPCQVfzw8HUcflgba5+SmRNNxH6+ow8df1INHQm7queSoqwCuKJqZ85QZrTVFkKgiQwrKHhGQ 15hRKc6DCagk0LO7F4f2T8XB/VP10VOXfZzjcZsct67+sRG0FvTFtw1Y84u7I92q7EfO2xEEttZI /37LC3vWmlEb0wRpeP6mXn4R+AGA14yKcR5MwCoC7dtpOLB3CvockIJ+vVLQM8eD7vt6kZZm2tcj ZFPKNvvx60Yfftnow/c/NYBiMW36jafhQsLig84kIMUdiwuz7zKrcqZ+4/IKSl8GcJ5ZleN8mICd BMhIonMnD9q1FaBpP4p+2yZT6EKV4hWgDbspv//8CgRoszjg+X2wVVsndW8IdQ0S9fUSOysD+nTb zqqAbo69dbsfPtYeO7uXy46fQJVX+vdfUNhze/xZNeVg7mjG438Afs8EAKYKnVmN5XyYQDQEaPqM /nNiAkxgbwISeMpMMaISTF3dXfxaz29EFBFl924iH2ECTIAJMAEXEGjwavIhs+tpqiBR5aSG+8yu JOfHBJgAE2ACziEgIF5ZOL07WVabmkwXpMXTcz4A5BJTa8mZMQEmwASYgFMI+ODX7lZRGdMFiSop Ie5UUVnOkwkwASbABGwmIOTL8fqsa6kFSgRpycyc9wEsbalQPs4EmAATYAKuJODzBwL/UlVzJYJE lZXAZFWV5nyZABNgAkzAFgKvFBf2/ElVycoE6fdR0iJVFed8mQATYAJMwFICDR6pKV2OUSZIhEnT cFvTYMlSaFwYE2ACTIAJmE5APrWwsNta07MNylCpIC2anvM5gFlB5fFbJsAEmAATcB+BGq8vVdna kYFDqSBRIR4/bgfgMwrkVybABJgAE3AXASnwyILZXcpU11q5IC2clbMaAi+obgjnzwSYABNgAkoI bGvwND6gJOdmmSoXJCrP25hCo6TqZmXzRybABJgAE3A4ASlw97JX9y+3opqWCBIN9SQER5S1oke5 DCbABJiAWQQEfqxon/2YWdm1lo8lgkSVyKqlEOeitLUK8XkmwASYABNwBgEZwC0rnhSNVtXGMkGa Ny+nRorA361qGJfDBJgAE2ACsROQwHtLCnPmxJ5D9HdaJkhUtZP757wACTIF58QEmAATYALOJeD3 yMD1VlfP8kB6eWPLjoaUH5sdi8lqcFweE2ACTCBRCUghH18yo/vVVrfP0hESNW7xjOzPADxrdUO5 PCbABJgAE2idgAS2BOobyMuO5clyQaIWpkpBjd1heWu5QCbABJgAEwhPQOC24jd62fL32RZBml+Y vQVC/F94KnyWCTABJsAErCQgpPzk5P7Zts1g2SJIBPik/t2eAPS1JCt5c1lMgAkwASYQmoDPr8kr J08WgdCn1R+13KghuEmDx244VJMaWd15g4/zeybABJgAE7CYgJBTFs/o/leLS92jONtGSFSLpTN6 fA2IaXvUiD8wASbABJiA1QTWeVI9SmMdRdIgWwWJKphZKwmC0hgbkYDga5gAE2ACyUpACnH1wpe6 2e5v1HZBIg8Omha4nAP5JetXgdvNBJiAzQReXjIje77NddCLt12QqBaLpvdYDOBJJwDhOjABJsAE kojAJq/0X+eU9jpCkAhGXV3KzQB+dQoYrgcTYAJMINEJSImrFxT23O6UdjpGkD6c26VSCslTd055 MrgeTIAJJDgBMd1q56mtAXWMIFFFl8zovhCQj7dWaT7PBJgAE2AC8RAQpV7p+3M8Oai411GCRA30 pHnIDv4HFY3lPJkAE2ACTABSQF7qpKk6o08cJ0hkehjQtAsB+IxK8isTYAJMgAmYRUD+t2hmzgKz cjMzH8cJEjVu6fRun0DIe8xsKOfFBJgAE0h2AlJgdWatsNUbQ7g+cKQgUYX9m3P+CYmPwlWezzEB JsAEmEDEBBoAnEd7PyO+w+ILHStIxcXCJ/2YIIByi5lwcUyACTCBxCMg8dclM3K+cHLDHCtIBG3J 7JxfpBRXOBkg140JMAEm4AICby8uzH7E6fV0tCARvMWF2a8L4Amng+T6MQEmwAScSUCU+j24GBDS mfXbXSvHCxJVtVr6b4TEl7urze+YABNgAkwgAgI+aHJc8Ws5WyO41vZLXCFIywt71krNkw+gwnZi XAEmwASYgEsISIhbF0/P+cAl1YUrBIlgLpmx789CyIvYK7hbHi2uJxNgAjYTmLNkZrepNtchquJd I0jUqqIZ3d+EkA9G1UK+mAkwASaQbAQEfhQy/RI3rBsFd42rBIkq3imQc6uUgsJVcGICTIAJMIG9 CVSLgHZOUWEn1y1xuE6QCguFPw0YD4Ff9u4HPsIEmAATSGoCUgAXFRV2W+lGCq4TJII8vzB7SwCB MwE4dsexGx8GrjMTYALuJiCBfxXNzJnl1la4UpAI9tIZPb6WQlzMRg5uffS43kyACZhKQMq3Th6Q fYepeVqcmbC4PNOLyysoux2Qd5meMWfIBJgAE3APgZVCpp/kxnWjYMSuFyRAiryCspfIaWBww/g9 E2ACTCAZCEhgS8AXOKJ4do8Nbm+va6fsdoMXspMsvxSQH+8+xu+YABNgAklBoEZq2qhEECPqrQQY ITU9dCPzy7o0CnwkIfsmxWPIjWQCTCDZCUgJed6Smd1fSxQQCTBCauoKsrzzSd9IGr4mSudwO5gA E2ACLREQErclkhhROxNmhGR02uBxm/6oBQJLAGQax/iVCTABJpBIBKSQjy+Z0f3qRGoTtSVhRkhG x1D484CU4ynorHGMX5kAE2ACCUTglX0COdckUHt2NSXhBIlatrSw+1wJ/IX3KO3qZ37DBJhAQhCQ S8iIizzWJERzmjUi4absgts3NL/0b1Lg3uBj/J4JMAEm4FICnwqZPtzte43CsU/IEZLR4KLCnPsE xP3GZ35lAkyACbiUwKpUKc5IZDGifknoEVLTg6dvnH0cwJUufRC52kyACSQzAYFf/I2BExNlr1G4 rkzoEVJTw2njbPafAbwSDgSfYwJMgAk4kMAGf8A/NBnEiNgnwQip6RHLz5eebaL0VQFR4MCHjqvE BJgAE2hGQJZByNzFM3r80OxEwn5MGkGiHjzyCpnSvrxsuhAYk7A9yg1jAkzA9QRog78mcErRjJzv XN+YKBqQBFN2u2mseFI07oPy8QJi7u6j/I4JMAEm4BwCuhhJLS/ZxIh6IKkEiRpcWDiwoaPcni8l ZjvnEeSaMAEmwAR0ApulRwx2a8TXePswqabsgmHl5kqvZ99NL0PKscHH+T0TYAJMwB4CotTjl3kL Z+Wstqd8+0tNuhGSgby4WPg6BbpRDKWXjWP8ygSYABOwicB6v/SdksxiRNyTVpCo8eR+o5PMpjDo /7PpIeRimQATSHICAuInvyZOLi7s+VOSo0ges+/wHS3FkLFl9wiJv4W/js8yASbABEwkIMU3fnhG FBd23WRirq7NKqlHSLt7TcglM3JuFVL8lR2y7qbC75gAE1BIQOKj+pSGXBaj3YyT1qhhN4I93w0Z WzpRSDwBwLPnGf7EBJgAEzCNwLuZtThn3rycGtNyTICMWJBCdOKQgo2jBASFBc4KcZoPMQEmwARi JiAgnivv0O1K2hcZcyYJeiMLUgsdS5FnRSAwTwBdWriEDzMBJsAEoiQg/7l4Zs4dgJBR3pgUl7Mg henm3Pz1fb3CO19C9g1zGZ9iAkyACbRGwC+Aq4pm5jzV2oXJfJ4FqZXezx1f2tnjxxwAJ7ZyKZ9m AkyACYQiUCGAsUUzcxaEOsnHdhNgK7vdLEK+K34tZ2tqZfVQ3kAbEg8fZAJMIDyBtQGPOIHFKDwk 4yyPkAwSEbzmFZTeCuBfyRS2IwIsfAkTYAIhCYjlnkZx1sI53X4LeZoP7kWABWkvJOEPDMkvPVsI vAigTfgr+SwTYAJJS0DI51N31vxp/vx+9UnLIIaGsyDFAG342E2DAlLOYWOHGODxLUwgsQn4hJA3 Fs3o/p/Ebqaa1rEgxcj1xAm/dEzzpbwK4NQYs+DbmAATSCACehwjTRQUTc8uTqBmWdoUNmqIEfey V/cv7ySzzwBwL7sbihEi38YEEoWAxOfw4WgWo/g6lEdI8fHT7x6aX3a6FPIFAPuYkB1nwQSYgIsI CCEeTdlZdROvF8XfaSxI8TPUcxiev6mXH4GZEDjKpCw5GybABJxNoAoCf1o8I+cVZ1fTPbXjKTuT +mphYbe1qVXVJ9KvJZ7CMwkqZ8MEnEtgpcePo1mMzO0gHiGZy1PPbejYjWdKKZ7hKTwFcDlLJmAz ASnk44GMhhuLn+9VZ3NVEq54FiRFXZo7ZkMPzau9LIBTFBXB2TIBJmAtge1SYuKSwhxyJcZJAQGe slMAlbIsnt1jwz4yOw8Q/wAku5lXxJmzZQIWEVgqJA5nMVJLm0dIavnquQ8bV3pUIACywhtgQXFc BBNgAuYRqBPAbScOyH548mQRMC9bzikUARakUFQUHDsuf31Gpua5FxLXsi88BYA5SyZgNgGJLwFx /uLC7FVmZ835hSbAghSai7KjeQVlgwXw5P+3d26xVVRRGP7/PbW2oqYgSIGC8a6QoPLgJYKppYL1 Fi+p0AcUrw/6oDHxHhU1XuOLiYmJqVGjRvAIGjERQ9GCGNEgmGgUlYhSORRRqQhKy5lZZho1EUs9 9zMz5386ObPXWnutb03yZ86Zvbe2HSoZYgUWgUIJDBj48K8NjQ/rVNdCUebmL0HKjVdRrAeflliz ALBbANQUJaiCiIAIFEzAgA/DPS8CAAAGbElEQVRovF5PRQWjzCuABCkvbMVxap275RQL2AlgWnEi KooIiECeBHYCvGvG5Man9V9RngSL4CZBKgLEQkI0N1uNO7z3ZsIeAFBfSCz5ioAI5EOAS/2Mf0P4 Zmw+3vIpHgEJUvFYFhSp9bJtRwUueIa0mQUFkrMIiEC2BLbB7KYVqQmLsnWQXWkJSJBKyzfH6MaW Oen5ND4OYHSOzjIXARHIjoABfK7GMre+k5r4S3YusioHAQlSOSjnOMfs9p5RGboHAV6vlx5yhCdz ERiewDrS3di1qHHN8GYarQQBCVIlqGc55+BLDz6fBDEjSxeZiYAIDE3gJxrunj5lXKdeWhgaUBSu SpCi0IVhczC2XJ6eS/BRAJOGNdWgCIjAvgQGQDzl9/c/2P3GkX37Dup7tAhIkKLVj/1mE65dGgHv JiNuB9CwX0MNiIAI/E3gdd/827pTEzf+fUGf0SYgQYp2f/6TXXNHerTn2/0ArgN4wH8MdEEEqpwA zT6C5+7QceLxuxEkSPHr2WDGze09x3j0FgDoAKBd22PaR6VdVAJfkLina9G41wFaUSMrWFkISJDK grl0k8zs6JmKjHsI5AWlm0WRRSDCBIjvYbxvlDW+lErRj3CmSu1/CEiQ/gdQXIZb5/SebkFwL4i2 uOSsPEWgQAKbaXx0JH55NpWaMlBgLLlHgIAEKQJNKGYKLe1bTyXtXgDnFzOuYolAhAh8R8MjI7Hj eQlRhLpShFQkSEWAGMUQ4aGAvo87SVys/5ii2CHllAeBrwh7fEfD+Bd1LEQe9GLgIkGKQZMKSfHs 9i3HO8dbYZgHoLaQWPIVgQoR+JjEY9NPHPeGFrVWqANlmlaCVCbQlZ7mnLmbxwfm3QzjdVrHVOlu aP4sCAQElhn4xIpXx72Xhb1MEkBAgpSAJuZSwqx5vSP8Absy3OUYwHG5+MpWBMpAYDdgL4D25IpF TV+XYT5NESECEqQINaOcqSxYYG7lF73nerAbDZgNwCvn/JpLBP5FgPjGjM8EA3s6tcXPv8hU1RcJ UlW1e+hiW9vTkwLyKsKu1n55QzPS1ZIQ6AfwGh07uxY2rtRi1pIwjlVQCVKs2lXaZMOnplVf9s5G YNeSdqG2Jiot7yqO/hkMz9bAf1HnEVXxXTBE6RKkIaDoEjCzY9tY8zPzSV4Dw7FiIgIFEtgF8FWY 37ki1fRhgbHknlACEqSENrZ4ZYXHX2ydQeIKGi41YGTxYitSwgkEAFbC7OXa2rrU2y8ftjPh9aq8 AglIkAoEWE3ubW3fHDhwyIg2g3UQvBBAfTXVr1qzJGBYC+AV52UWLl84KZ2ll8xEABIk3QR5ETjz ou2H1B04cB7Jyww4D8CIvALJKQkEDLCPaG4JAre4a/HYb5NQlGooPwEJUvmZJ27G8PDAg+iFr45f YkAbgTGJK1IF7UsgA9gqI5cGe4PXupc0/bCvgb6LQK4EJEi5EpP9sATa28372UufxoAXwHg+aFOH ddBgnAj8BGAZzN4i6pd1pUb9GqfklWv0CUiQot+jWGfYfOkPTTU1nGVw5xhspp6eYtXO8EiHNQSW +84tH+2PXavzhmLVv9glK0GKXcvim3C4zmn1hvRJ8F2rITgL5HTtqxepfvowrIfDKgZ8NwNvZXfq 8F2RylDJJJqABCnR7Y12ceHPezu89FTzeRaIGQBOBzAh2lknKrvdMKwDsdrI9/v/qFn9wZtjfktU hSomVgQkSLFqV/KTbWnvmeCcdxrMzjDwVAAnAzg0+ZWXvMKMARsIfGLAGs+CNXu3T/i8u5uZks+s CUQgSwISpCxByaxSBIwtc348CgimATaNNihQk7Xn3rD92EmzL438FMT6gO7Tg3cHny1dOv73Yb00 KAIVJiBBqnADNH1+BMJ1ULUH+ZO9wJ9ixAk0d7yZHQ3iaAB1+UWNlZcBCF+13mjARho2mLPPXcAN Xanxm2NViZIVgb8ISJB0KySMgHHW3HSTH7hjBp+iDEcYrYmGpsHvxMSY/AQY7oS9zYAewLYQDD83 G7DJkRsz9f2bup8/ck/CmqdyqpyABKnKb4BqLL95/qa62j21Y3xzjQDHGvwxNDfKYA2ObDCzBpg1 GFy9ozWY8QA4Oxhm9QT/efraz75+vxMIxQTh9gUg+mDYS2CXGcKxPwJaH8k+GnfArC8g+hyw3Rx+ 9PZie+DqtmqNTzXemar5T7boKrYfCqI6AAAAAElFTkSuQmCC"/></symbol><symbol viewBox="0 0 24 24" id="webpack" xmlns="http://www.w3.org/2000/svg"><path d="M19.376 15.988l-7.709 4.45-7.708-4.45V7.087l7.708-4.45 7.709 4.45z" fill="#fff" fill-opacity=".785" stroke-width="0"/><path d="M12.286 1.98c-.21 0-.41.059-.57.179l-7.9 4.44c-.32.17-.53.5-.53.88v9c0 .38.21.711.53.881l7.9 4.44c.16.12.36.18.57.18.21 0 .41-.06.57-.18l7.9-4.44c.32-.17.53-.5.53-.88v-9c0-.38-.21-.712-.53-.882l-7.9-4.44a.945.945 0 0 0-.57-.179zm0 2.15l7 3.939v2.104h-.016v5.177h.016v.54l-7 3.939-7-3.94V8.07l7-3.94zm0 2.08l-4.9 2.83 4.9 2.83 4.9-2.83-4.9-2.83zm-5 5.08v3.58l4 2.308v-3.58l-4-2.308zm10 0l-4 2.308v3.58l4-2.308v-3.58z" fill="#8ed6fb"/><path d="M12.286 6.21l-4.9 2.83 4.9 2.83 4.9-2.83-4.9-2.83zm-5 5.08v3.58l4 2.308v-3.58l-4-2.308zm10 0l-4 2.308v3.58l4-2.308v-3.58z" fill="#1c78c0"/></symbol><symbol viewBox="0 0 24 24" id="wolframlanguage" xmlns="http://www.w3.org/2000/svg"><title>wolframLanguage</title><g transform="scale(.12121)" fill="none" fill-rule="evenodd"><circle cx="99.197" cy="98.946" r="83.28" fill="#212121" stroke-width=".841"/><path d="M182.529 98.828a83.406 83.406 0 0 1-39.14 70.721.064.064 0 0 1-.038.019l-28.62-35.665 23.71 2.612s11.385 1.177 13.978 0c2.373-.938 15.175-18.963 15.175-18.963s-36.75-23.23-49.312-36.032c1.434-21.575-1.656-50.269-1.656-50.03-9.251 9.234-10.429 10.669-19.68 19.203-4.028-13.04-5.923-17.547-9.95-30.588-12.104 9.95-21.337 26.799-27.977 46.48a78.68 78.68 0 0 0-4.23 5.094 109.774 109.774 0 0 0-2.667 3.66 114.558 114.558 0 0 0-5.132 8.002 172.555 172.555 0 0 0-3.403 6.051c-7.706 14.475-14.034 31.066-19.515 46.001a.858.858 0 0 1-.092-.184c-14.988-30.912-9.502-67.85 13.822-93.072 23.325-25.223 59.723-33.575 91.71-21.045 31.988 12.53 53.029 43.382 53.017 77.736z" fill="#e53935"/><path d="M101.452 69.178s-1.416-8.295-2.373-11.367c6.401-6.18 7.357-7.118 13.52-13.04.477 11.845.238 18.006-.479 32.481-3.55-3.568-10.668-8.074-10.668-8.074zm-27.737 40.778s-6.64-4.029-11.624-4.728c1.435-3.329 5.223-7.596 6.18-8.773-1.913.699-15.653 6.86-17.087 12.084a74.804 74.804 0 0 1 11.385 3.79 35.993 35.993 0 0 0-8.774 20.158s21.815-3.33 38.185-1.196c.283.168.609.251.938.24l8.534.239 27.111 45.136.221.35c-.037.018-.055.037-.073.037-51.133 18.485-88.085-15.543-95.976-27.443.034-.102.058-.206.074-.313 7.1-30.017 15.855-65.939 30-76.552 7.356-12.82 9.49-31.783 22.751-41.734 3.33 9.951 8.553 30.588 12.103 40.539 15.653 15.652 39.361 35.094 55.234 43.15 1.656.956 3.79 7.596 3.79 7.596l-6.401 8.056-68.276-6.879a54.462 54.462 0 0 0-4.58-.183 86.848 86.848 0 0 0-14.144 1.36c3.311-8.295 10.43-14.935 10.43-14.935zm22.054-8.774c3.789-.46 7.817.956 12.323 3.568 4.267-1.195 4.745-1.434 9.013-2.612-5.463-4.028-11.386-8.295-19.442-7.118a47.249 47.249 0 0 0-1.894 6.162z" fill="#fff" stroke-width=".936"/></g></symbol><symbol viewBox="0 0 24 24" id="word" xmlns="http://www.w3.org/2000/svg"><path d="M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5L13 3.5M7 13l1.5 7h2l1.5-3 1.5 3h2l1.5-7h1v-2h-4v2h1l-.9 4.2L13 15h-2l-1.1 2.2L9 13h1v-2H6v2h1z" fill="#01579b"/></symbol><symbol viewBox="0 0 24 24" id="xaml" xmlns="http://www.w3.org/2000/svg"><path d="M18.93 12l-3.47 6H8.54l-3.47-6 3.47-6h6.92l3.47 6m4.84 0l-4.04 7L18 18l3.46-6L18 6l1.73-1 4.04 7M.23 12l4.04-7L6 6l-3.46 6L6 18l-1.73 1-4.04-7z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 24 24" id="xml" xmlns="http://www.w3.org/2000/svg"><path d="M13 9h5.5L13 3.5V9M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m.12 13.5l3.74 3.74 1.42-1.41-2.33-2.33 2.33-2.33-1.42-1.41-3.74 3.74m11.16 0l-3.74-3.74-1.42 1.41 2.33 2.33-2.33 2.33 1.42 1.41 3.74-3.74z" fill="#8bc34a"/></symbol><symbol viewBox="0 0 24 24" id="yaml" xmlns="http://www.w3.org/2000/svg"><path d="M5 3h2v2H5v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5h2v2H5c-1.07-.27-2-.9-2-2v-4a2 2 0 0 0-2-2H0v-2h1a2 2 0 0 0 2-2V5a2 2 0 0 1 2-2m14 0a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h1v2h-1a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-2v-2h2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5h-2V3h2m-7 12a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m-4 0a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m8 0a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1z" fill="#f44336"/></symbol><symbol viewBox="0 0 24 24" id="yang" xmlns="http://www.w3.org/2000/svg"><path d="M12 2a10 10 0 0 1 10 10 10 10 0 0 1-10 10A10 10 0 0 1 2 12 10 10 0 0 1 12 2m0 2a8 8 0 0 0-8 8 8 8 0 0 0 8 8 4 4 0 0 1-4-4 4 4 0 0 1 4-4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 2.5A1.5 1.5 0 0 1 13.5 8 1.5 1.5 0 0 1 12 9.5 1.5 1.5 0 0 1 10.5 8 1.5 1.5 0 0 1 12 6.5m0 8a1.5 1.5 0 0 0-1.5 1.5 1.5 1.5 0 0 0 1.5 1.5 1.5 1.5 0 0 0 1.5-1.5 1.5 1.5 0 0 0-1.5-1.5z" fill="#42a5f5"/></symbol><symbol viewBox="0 0 289.99999 290.00001" id="yarn" xmlns="http://www.w3.org/2000/svg"><path d="M250.733 218.418c-12.39 2.943-18.661 5.653-33.993 15.641-24.004 15.487-50.176 22.688-50.176 22.688s-2.168 3.252-8.44 4.723c-10.84 2.633-51.647 4.878-55.364 4.956-9.988.077-16.105-2.555-17.809-6.66-5.188-12.388 7.434-17.809 7.434-17.809s-2.788-1.703-4.414-3.252c-1.471-1.47-3.02-4.413-3.484-3.33-1.936 4.724-2.943 16.261-8.13 21.45-7.125 7.2-20.598 4.8-28.573.619-8.75-4.646.62-15.564.62-15.564s-4.724 2.788-8.518-2.942c-3.407-5.266-6.582-14.248-5.73-25.32 1.084-12.777 15.176-25.011 15.176-25.011s-2.477-18.661 5.653-37.787c7.356-17.422 27.179-31.437 27.179-31.437s-16.648-18.352-10.454-35c4.027-10.84 5.653-10.763 6.97-11.227 4.645-1.781 9.136-3.717 12.466-7.356 16.648-17.964 37.864-14.557 37.864-14.557s9.911-30.431 19.203-24.469c2.865 1.859 13.163 24.778 13.163 24.778s10.996-6.426 12.235-4.026c6.659 12.931 7.433 37.632 4.49 52.654-4.955 24.778-17.344 38.096-22.3 46.459-1.161 1.936 13.319 8.053 22.456 33.373 8.44 23.152.929 42.587 2.245 44.756.232.387.31.542.31.542s9.679.774 29.114-11.228c10.376-6.427 22.688-13.628 36.703-13.783 13.55-.232 14.247 15.719 4.104 18.12z" fill="#2c8ebb" stroke-width=".774"/></symbol><symbol viewBox="0 0 24 24" id="zip" xmlns="http://www.w3.org/2000/svg"><path d="M14 17h-2v-2h-2v-2h2v2h2m0-6h-2v2h2v2h-2v-2h-2V9h2V7h-2V5h2v2h2m5-4H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z" fill="#afb42b"/></symbol></svg>
\ No newline at end of file diff --git a/app/assets/images/icon_image_comment.svg b/app/assets/images/icon_image_comment.svg deleted file mode 100644 index cf6cb972940..00000000000 --- a/app/assets/images/icon_image_comment.svg +++ /dev/null @@ -1 +0,0 @@ -<svg width="24" height="30" viewBox="0 0 24 30" xmlns="http://www.w3.org/2000/svg"><title>cursor</title><g fill="none" fill-rule="evenodd"><path d="M24 12.105c0 6.686-5.74 11.58-12 17.895C5.74 23.684 0 18.79 0 12.105 0 5.42 5.373 0 12 0s12 5.42 12 12.105z" fill="#1F78D1" fill-rule="nonzero"/><path d="M15.28 25.249c1.458-1.475 2.539-2.635 3.474-3.747 2.851-3.394 4.203-6.265 4.203-9.397 0-6.111-4.908-11.062-10.957-11.062-6.05 0-10.957 4.951-10.957 11.062 0 3.132 1.352 6.003 4.203 9.397.935 1.112 2.016 2.272 3.474 3.747.511.517 2.216 2.213 3.28 3.275 1.064-1.062 2.769-2.758 3.28-3.275z" fill="#FFF"/><path d="M14.551 8.256A6.874 6.874 0 0 0 12 7.787c-.91 0-1.763.156-2.558.469-.79.308-1.42.725-1.888 1.252-.465.527-.697 1.096-.697 1.708 0 .5.159.977.476 1.433.321.45.772.841 1.352 1.172l.583.334-.181.643c-.107.407-.263.79-.469 1.152a6.604 6.604 0 0 0 1.842-1.145l.288-.254.381.04c.309.035.599.053.871.053.91 0 1.761-.154 2.551-.462.795-.312 1.424-.732 1.889-1.259.468-.526.703-1.096.703-1.707 0-.612-.235-1.181-.703-1.708-.465-.527-1.094-.944-1.889-1.252zm2.645.81c.536.656.804 1.373.804 2.15 0 .776-.268 1.495-.804 2.156-.535.656-1.263 1.176-2.183 1.56-.92.38-1.924.57-3.013.57a9.16 9.16 0 0 1-.971-.054 7.32 7.32 0 0 1-3.08 1.62 5.044 5.044 0 0 1-.764.148h-.033a.26.26 0 0 1-.181-.074.324.324 0 0 1-.107-.18v-.007c-.014-.018-.016-.045-.007-.08.014-.037.018-.059.014-.068 0-.009.01-.031.033-.067a.645.645 0 0 0 .04-.06 1.73 1.73 0 0 0 .047-.054l.054-.06a53.034 53.034 0 0 1 .435-.489c.049-.049.118-.136.207-.26.094-.126.168-.24.221-.342.054-.103.114-.235.181-.395.067-.161.125-.33.174-.51-.7-.397-1.254-.888-1.66-1.473A3.261 3.261 0 0 1 6 11.216c0-.777.268-1.494.804-2.15.535-.66 1.263-1.18 2.183-1.56.92-.384 1.924-.576 3.013-.576 1.09 0 2.094.192 3.013.576.92.38 1.648.9 2.183 1.56z" fill="#1F78D1" fill-rule="nonzero"/></g></svg> diff --git a/app/assets/images/icon_image_comment@2x.svg b/app/assets/images/icon_image_comment@2x.svg deleted file mode 100644 index 83be91d3705..00000000000 --- a/app/assets/images/icon_image_comment@2x.svg +++ /dev/null @@ -1 +0,0 @@ -<svg width="48" height="60" viewBox="0 0 48 60" xmlns="http://www.w3.org/2000/svg"><title>cursor_2x</title><g fill="none" fill-rule="evenodd"><path d="M48 24.21C48 37.583 36.522 47.369 24 60 11.478 47.368 0 37.582 0 24.21 0 10.84 10.745 0 24 0s24 10.84 24 24.21z" fill="#1F78D1" fill-rule="nonzero"/><path d="M30.56 50.497c2.915-2.95 5.078-5.268 6.947-7.493 5.703-6.788 8.406-12.53 8.406-18.793 0-12.223-9.815-22.124-21.913-22.124S2.087 11.988 2.087 24.211c0 6.263 2.703 12.005 8.406 18.793 1.87 2.225 4.032 4.544 6.947 7.493 1.022 1.035 4.432 4.426 6.56 6.55 2.128-2.124 5.538-5.515 6.56-6.55z" fill="#FFF"/><path d="M29.103 16.512c-1.58-.625-3.282-.938-5.103-.938-1.821 0-3.527.313-5.116.938-1.58.616-2.84 1.45-3.777 2.504-.928 1.054-1.393 2.192-1.393 3.415 0 1 .317 1.956.951 2.866.643.902 1.545 1.684 2.706 2.344l1.165.67-.362 1.286a9.603 9.603 0 0 1-.937 2.303 13.208 13.208 0 0 0 3.683-2.29l.576-.509.763.08c.616.072 1.196.108 1.741.108 1.821 0 3.522-.308 5.103-.925 1.589-.625 2.848-1.464 3.776-2.517.938-1.054 1.407-2.192 1.407-3.416 0-1.223-.469-2.361-1.407-3.415-.928-1.053-2.187-1.888-3.776-2.504zm5.29 1.62c1.071 1.313 1.607 2.746 1.607 4.3 0 1.553-.536 2.99-1.607 4.312-1.072 1.312-2.527 2.353-4.366 3.12-1.84.76-3.848 1.139-6.027 1.139a18.32 18.32 0 0 1-1.942-.107c-1.768 1.562-3.821 2.643-6.16 3.24-.438.126-.947.224-1.527.295h-.067a.521.521 0 0 1-.362-.147.649.649 0 0 1-.214-.362v-.013c-.027-.036-.032-.09-.014-.16.027-.072.036-.117.027-.135 0-.017.022-.062.067-.133a1.29 1.29 0 0 0 .08-.121c.01-.009.04-.045.094-.107a106.068 106.068 0 0 1 .522-.59c.215-.232.367-.401.456-.508.098-.099.236-.273.415-.523.188-.25.335-.477.442-.683.107-.205.228-.468.362-.79.134-.321.25-.66.348-1.018-1.402-.794-2.51-1.777-3.322-2.946C12.402 25.025 12 23.77 12 22.43c0-1.553.536-2.986 1.607-4.299 1.072-1.321 2.527-2.361 4.366-3.12 1.84-.768 3.848-1.152 6.027-1.152 2.179 0 4.188.384 6.027 1.152 1.84.759 3.294 1.799 4.366 3.12z" fill="#1F78D1" fill-rule="nonzero"/></g></svg> diff --git a/app/assets/images/icons.json b/app/assets/images/icons.json index 4e967936ce0..296cb856734 100644 --- a/app/assets/images/icons.json +++ b/app/assets/images/icons.json @@ -1 +1 @@ -{"iconCount":180,"spriteSize":82176,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","import","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
\ No newline at end of file +{"iconCount":189,"spriteSize":85766,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o-open","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]}
\ No newline at end of file diff --git a/app/assets/images/icons.svg b/app/assets/images/icons.svg index 77ce6b2d89f..8d5426da19c 100644 --- a/app/assets/images/icons.svg +++ b/app/assets/images/icons.svg @@ -1 +1 @@ -<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.536 7.95L1.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 1 1-1.414-1.415L5.536 7.95zm7 0L8.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.414-1.415l4.243-4.242z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-down" xmlns="http://www.w3.org/2000/svg"><path d="M10.472 7.282a.862.862 0 0 1 1.26-.006c.357.364.357.958 0 1.285L8.627 11.73A.886.886 0 0 1 8 12a.849.849 0 0 1-.627-.27L4.275 8.561a.904.904 0 0 1-.013-1.285.861.861 0 0 1 1.26-.007l2.486 2.527z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4 12.5v-9A1.5 1.5 0 0 1 5.5 2h2.104c2.182 0 3.879.681 3.879 2.982 0 1.067-.517 2.227-1.374 2.595v.073C11.176 7.963 12 8.865 12 10.466 12 12.914 10.19 14 7.911 14H5.5A1.5 1.5 0 0 1 4 12.5zm2.376-5.696H7.49c1.164 0 1.665-.552 1.665-1.417 0-.94-.534-1.289-1.649-1.289h-1.13v2.706zm0 5.098h1.341c1.293 0 1.956-.515 1.956-1.62 0-1.049-.647-1.472-1.956-1.472H6.376v3.092z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="bullhorn" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.143 10H7V4H3a3 3 0 1 0 0 6h.143l.734 5.141a1 1 0 0 0 .99.859h1.556a.5.5 0 0 0 .495-.57L6.143 10zM8 4c1.034.02 2.039-.274 3.014-.883.727-.455 1.836-1.334 3.328-2.637A1 1 0 0 1 16 1.233v10.764a1 1 0 0 1-1.595.803c-1.658-1.227-2.788-1.992-3.392-2.294-.781-.39-1.785-.559-3.013-.506V4z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chart" xmlns="http://www.w3.org/2000/svg"><path d="M15 14a1 1 0 0 1 0 2H2a2 2 0 0 1-2-2V1a1 1 0 1 1 2 0v13h13zM3.142 8.735l2.502-2.561a.5.5 0 0 1 .714-.003L8 7.833l3.592-4.553a.5.5 0 0 1 .796.015l2.516 3.454a.5.5 0 0 1 .096.295V12.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5V9.085a.5.5 0 0 1 .142-.35z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 9 13" id="collapse"><path d="M.084.25C.01.18-.015.12.008.071.031.024.093 0 .194 0h8.521c.1 0 .162.024.185.072.023.048-.002.107-.075.177l-4.11 3.935a.372.372 0 0 1-.11.072h-.301a.508.508 0 0 1-.11-.072L.084.249zM.377 6.88a.364.364 0 0 1-.26-.105.334.334 0 0 1-.11-.25v-.709c0-.096.036-.179.11-.249a.364.364 0 0 1 .26-.105h8.15c.101 0 .188.035.261.105.074.07.11.153.11.25v.709c0 .096-.036.179-.11.249a.364.364 0 0 1-.26.105H.377zM.084 12.132c-.074.07-.099.129-.076.177.023.048.085.072.186.072h8.521c.1 0 .162-.024.185-.072.023-.048-.002-.107-.075-.177l-4.11-3.935a.372.372 0 0 0-.11-.072h-.301a.508.508 0 0 0-.11.072l-4.11 3.935z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="cut" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="2" y="7" fill-rule="evenodd" rx="1"/></symbol><symbol viewBox="0 0 16 16" id="dashboard" xmlns="http://www.w3.org/2000/svg"><path d="M7.709 10.021l.696-2.6a.5.5 0 0 1 .966.26l-.657 2.45A2 2 0 0 1 10 12H6a2 2 0 0 1 1.709-1.979zM0 8.9a8 8 0 0 1 15.998 0H16v3.6a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5V8.9zM14 9A6 6 0 1 0 2 9v3.5a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5V9zM3.5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm9 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-7-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm5 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 105 26" id="double-headed-arrow" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.018 11.089L15.138.614c1.23-.911 3.086-.795 4.147.26.461.46.715 1.045.715 1.651v20.95C20 24.869 18.684 26 17.06 26a3.238 3.238 0 0 1-1.921-.614L1.019 14.911C-.212 14-.347 12.405.714 11.35c.094-.094.195-.18.303-.261zm102.964 0c.108.08.21.167.303.26 1.061 1.056.925 2.65-.303 3.562l-14.12 10.475A3.238 3.238 0 0 1 87.94 26C86.316 26 85 24.87 85 23.475V2.525c0-.606.254-1.192.715-1.65 1.061-1.056 2.917-1.172 4.146-.26l14.12 10.474zM35 17a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 1600 1600" id="ellipsis_v" xmlns="http://www.w3.org/2000/svg"><path d="M1088 1248v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V224q0-40 28-68t68-28h192q40 0 68 28t28 68z"/></symbol><symbol viewBox="0 0 18 18" id="emoji_slightly_smiling_face" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369.721.721 0 0 1 .568.047.715.715 0 0 1 .37.445 2.91 2.91 0 0 0 1.084 1.518A2.93 2.93 0 0 0 9 12.75a2.93 2.93 0 0 0 1.775-.58 2.913 2.913 0 0 0 1.084-1.518.711.711 0 0 1 .375-.445.737.737 0 0 1 .575-.047c.195.063.34.186.433.37.094.183.11.372.047.568zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smile" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568zM14 6.37c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm-6.5 0c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm9 2.63a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smiley" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568h.001zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6c.92.397 1.91.6 2.912.598a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39c.397-.92.6-1.91.598-2.912zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z"/></symbol><symbol viewBox="0 0 16 16" id="epic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.985 8.044l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637A2 2 0 0 0 1.618 9h11.661a2 2 0 0 0 1.706-.956zm0 3l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637a2 2 0 0 0 .576.084h11.661a2 2 0 0 0 1.706-.956zM3.618 2h10.995a1 1 0 0 1 .948 1.316l-1.333 4a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l2-4A1 1 0 0 1 3.618 2zm-.382 4h9.322l.667-2H4.236l-1 2z"/></symbol><symbol viewBox="0 0 16 16" id="external-link" xmlns="http://www.w3.org/2000/svg"><path d="M13.121 4.177l-4.95 4.95a1 1 0 1 1-1.414-1.414l4.95-4.95-1.386-1.386a.5.5 0 0 1 .299-.85l4.709-.524a.5.5 0 0 1 .552.552l-.523 4.71a.5.5 0 0 1-.851.297l-1.386-1.385zM12 8.884a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-8a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-4z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-addition" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M7.228 5l-.475-1.335A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H7.228zM13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177a.505.505 0 0 1-.038.044l.038-.044zm-.787 0l.038.043a.5.5 0 0 1-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="hourglass" xmlns="http://www.w3.org/2000/svg"><path d="M10.331 4.889A2.988 2.988 0 0 0 11 3V2H5v1c0 .362.064.709.182 1.03l5.15.859zM3 14v-1c0-1.78.93-3.342 2.33-4.228.447-.327.67-.582.67-.764 0-.19-.242-.46-.725-.815A4.996 4.996 0 0 1 3 3V2H2a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2h-1v1a4.997 4.997 0 0 1-2.39 4.266c-.407.3-.61.545-.61.734 0 .19.203.434.61.734A4.997 4.997 0 0 1 13 13v1h1a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2h1zm8 0v-1a3 3 0 0 0-6 0v1h6z"/></symbol><symbol viewBox="0 0 24 30" id="image-comment-dark" xmlns="http://www.w3.org/2000/svg"><title>cursor_active</title><g fill="none" fill-rule="evenodd"><path d="M24 12.105c0 6.686-5.74 11.58-12 17.895C5.74 23.684 0 18.79 0 12.105 0 5.42 5.373 0 12 0s12 5.42 12 12.105z" fill="#FFF" fill-rule="nonzero"/><path d="M15.28 25.249c1.458-1.475 2.539-2.635 3.474-3.747 2.851-3.394 4.203-6.265 4.203-9.397 0-6.111-4.908-11.062-10.957-11.062-6.05 0-10.957 4.951-10.957 11.062 0 3.132 1.352 6.003 4.203 9.397.935 1.112 2.016 2.272 3.474 3.747.511.517 2.216 2.213 3.28 3.275 1.064-1.062 2.769-2.758 3.28-3.275z" fill="#1F78D1"/><path d="M14.551 8.256A6.874 6.874 0 0 0 12 7.787a6.92 6.92 0 0 0-2.558.469c-.79.308-1.42.725-1.888 1.252-.465.527-.697 1.096-.697 1.708 0 .5.159.977.476 1.433.321.45.772.841 1.352 1.172l.583.334-.181.643c-.107.407-.263.79-.469 1.152a6.604 6.604 0 0 0 1.842-1.145l.288-.254.381.04c.309.035.599.053.871.053.91 0 1.761-.154 2.551-.462.795-.312 1.424-.732 1.889-1.259.468-.526.703-1.096.703-1.707 0-.612-.235-1.181-.703-1.708-.465-.527-1.094-.944-1.889-1.252zm2.645.81c.536.656.804 1.373.804 2.15 0 .776-.268 1.495-.804 2.156-.535.656-1.263 1.176-2.183 1.56-.92.38-1.924.57-3.013.57a9.16 9.16 0 0 1-.971-.054 7.32 7.32 0 0 1-3.08 1.62 5.044 5.044 0 0 1-.764.148h-.033a.26.26 0 0 1-.181-.074.324.324 0 0 1-.107-.18v-.007c-.014-.018-.016-.045-.007-.08.014-.037.018-.059.014-.068a.19.19 0 0 1 .033-.067.645.645 0 0 0 .04-.06 1.73 1.73 0 0 0 .047-.054l.054-.06a53.034 53.034 0 0 1 .435-.489c.049-.049.118-.136.207-.26a2.57 2.57 0 0 0 .221-.342c.054-.103.114-.235.181-.395a4.18 4.18 0 0 0 .174-.51c-.7-.397-1.254-.888-1.66-1.473A3.261 3.261 0 0 1 6 11.216c0-.777.268-1.494.804-2.15.535-.66 1.263-1.18 2.183-1.56.92-.384 1.924-.576 3.013-.576 1.09 0 2.094.192 3.013.576.92.38 1.648.9 2.183 1.56z" fill="#FFF" fill-rule="nonzero"/></g></symbol><symbol viewBox="0 0 16 16" id="import" xmlns="http://www.w3.org/2000/svg"><path d="M9 8h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0L5.6 8.8A.5.5 0 0 1 6 8h1V1a1 1 0 1 1 2 0v7zM0 8a1 1 0 1 1 2 0 6 6 0 1 0 12 0 1 1 0 0 1 2 0A8 8 0 1 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="italic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.5 12l2-8H6a1 1 0 1 1 0-2h6a1 1 0 0 1 0 2h-1.5l-2 8H10a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1.5z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="menu" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.143 2h13.714C15.488 2 16 2.448 16 3s-.512 1-1.143 1H1.143C.512 4 0 3.552 0 3s.512-1 1.143-1zm0 5h13.714C15.488 7 16 7.448 16 8s-.512 1-1.143 1H1.143C.512 9 0 8.552 0 8s.512-1 1.143-1zm0 5h13.714c.631 0 1.143.448 1.143 1s-.512 1-1.143 1H1.143C.512 14 0 13.552 0 13s.512-1 1.143-1z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pencil-square" xmlns="http://www.w3.org/2000/svg"><path d="M12 9a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V9zm.778-7.179l1.414 1.415-6.476 6.476a1 1 0 0 1-.498.27l-1.51.325.323-1.512a1 1 0 0 1 .27-.497l6.477-6.477zM15.607.407a1 1 0 0 1 0 1.414l-.708.707-1.414-1.414.707-.707a1 1 0 0 1 1.415 0z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V1a1 1 0 1 1 2 0v6h6a1 1 0 0 1 0 2H9v6a1 1 0 0 1-2 0V9H1a1 1 0 1 1 0-2h6z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.666 4.423a5 5 0 1 1-.203 6.944 1 1 0 1 0-1.478 1.347 7 7 0 1 0 .12-9.556L1.842 2.137a.5.5 0 0 0-.815.385L1 7.26a.5.5 0 0 0 .607.492l4.629-1.013a.5.5 0 0 0 .207-.877L4.666 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.494 4.423a5 5 0 1 0 .203 6.944 1 1 0 1 1 1.478 1.347 7 7 0 1 1-.12-9.556l1.262-1.021a.5.5 0 0 1 .815.385l.028 4.738a.5.5 0 0 1-.607.492L9.924 6.739a.5.5 0 0 1-.207-.877l1.777-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.114 6.958a4 4 0 0 0 5.283 4.775 1 1 0 1 1 .712 1.87A6 6 0 0 1 2.182 6.44l-.741-.2a.5.5 0 0 1-.12-.915l2.195-1.268a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.712-1.87 6 6 0 0 1 7.927 7.162l.742.2a.5.5 0 0 1 .12.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 12 16" id="scroll_down" xmlns="http://www.w3.org/2000/svg"><path class="evfirst-triangle" d="M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043a.51.51 0 0 0 .321-.105c.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"/><path class="evsecond-triangle" d="M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"/><path class="evthird-triangle" d="M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91a.458.458 0 0 1-.136.09h-.37a.626.626 0 0 1-.136-.09"/></symbol><symbol viewBox="0 0 12 16" id="scroll_up" xmlns="http://www.w3.org/2000/svg"><path d="M1.048 1.845a.508.508 0 0 1-.32-.105c-.091-.07-.136-.154-.136-.25V.78c0-.095.045-.178.135-.249a.508.508 0 0 1 .321-.105h10.043a.51.51 0 0 1 .321.105c.09.07.136.154.136.25v.71c0 .095-.045.178-.136.249a.508.508 0 0 1-.32.105M.687 7.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 3.09A.458.458 0 0 0 6.257 3h-.37a.626.626 0 0 0-.136.09M.687 14.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 10.09a.458.458 0 0 0-.136-.09h-.37a.626.626 0 0 0-.136.09"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v7.186a3 3 0 0 1-1.426 2.554l-4 2.465a3 3 0 0 1-3.148 0l-4-2.465A3 3 0 0 1 1 10.186V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v7.186a1 1 0 0 0 .475.852l4 2.464a1 1 0 0 0 1.05 0l4-2.464a1 1 0 0 0 .475-.852V3a1 1 0 0 0-1-1H4zm0 1.5a.5.5 0 0 1 .5-.5h4v8.837a.5.5 0 0 1-.753.431l-3.5-2.052A.5.5 0 0 1 4 9.785V3.5z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 14 14" id="spinner" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="7" cy="7" r="6" stroke="#000" stroke-opacity=".1" stroke-width="2"/><path fill="#000" fill-opacity=".1" fill-rule="nonzero" d="M7 0a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5V0z"/></g></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 14 14" id="status_canceled" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M5.2 3.8l4.9 4.9c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L3.8 5.2c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0"/></g></symbol><symbol viewBox="0 0 22 22" id="status_canceled_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M8.171 5.971l7.7 7.7a.76.76 0 0 1 0 1.1l-1.1 1.1a.76.76 0 0 1-1.1 0l-7.7-7.7a.76.76 0 0 1 0-1.1l1.1-1.1a.76.76 0 0 1 1.1 0"/></symbol><symbol viewBox="0 0 16 16" id="status_closed" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83a1 1 0 0 1 1.414 1.416l-3.535 3.535a1 1 0 0 1-1.415.001l-2.12-2.12a1 1 0 1 1 1.413-1.415zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 14 14" id="status_created" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><circle cx="7" cy="7" r="3.25"/></g></symbol><symbol viewBox="0 0 22 22" id="status_created_borderless" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="5.107"/></symbol><symbol viewBox="0 0 14 14" id="status_failed" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 5.969L5.599 4.568a.29.29 0 0 0-.413.004l-.614.614a.294.294 0 0 0-.004.413L5.968 7l-1.4 1.401a.29.29 0 0 0 .004.413l.614.614c.113.114.3.117.413.004L7 8.032l1.401 1.4a.29.29 0 0 0 .413-.004l.614-.614a.294.294 0 0 0 .004-.413L8.032 7l1.4-1.401a.29.29 0 0 0-.004-.413l-.614-.614a.294.294 0 0 0-.413-.004L7 5.968z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_failed_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 9.38L8.798 7.178a.455.455 0 0 0-.65.006l-.964.965a.462.462 0 0 0-.006.65L9.38 11l-2.202 2.202a.455.455 0 0 0 .006.65l.965.964a.462.462 0 0 0 .65.006L11 12.62l2.202 2.202a.455.455 0 0 0 .65-.006l.964-.965a.462.462 0 0 0 .006-.65L12.62 11l2.202-2.202a.455.455 0 0 0-.006-.65l-.965-.964a.462.462 0 0 0-.65-.006L11 9.38z"/></symbol><symbol viewBox="0 0 14 14" id="status_manual" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M10.5 7.63V6.37l-.787-.13c-.044-.175-.132-.349-.263-.61l.481-.652-.918-.913-.657.478a2.346 2.346 0 0 0-.612-.26L7.656 3.5H6.388l-.132.783c-.219.043-.394.13-.612.26l-.657-.478-.918.913.437.652c-.131.218-.175.392-.262.61l-.744.086v1.261l.787.13c.044.218.132.392.263.61l-.438.651.92.913.655-.434c.175.086.394.173.613.26l.131.783h1.313l.131-.783c.219-.043.394-.13.613-.26l.656.478.918-.913-.48-.652c.13-.218.218-.435.262-.61l.656-.13zM7 8.283a1.285 1.285 0 0 1-1.313-1.305c0-.739.57-1.304 1.313-1.304.744 0 1.313.565 1.313 1.304 0 .74-.57 1.305-1.313 1.305z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_manual_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 11.99v-1.98l-1.238-.206c-.068-.273-.206-.546-.412-.956l.756-1.025-1.444-1.435-1.03.752a3.686 3.686 0 0 0-.963-.41L12.03 5.5h-1.994l-.206 1.23c-.343.068-.618.205-.962.41l-1.031-.752-1.444 1.435.687 1.025c-.206.341-.275.615-.412.956L5.5 9.941v1.981l1.237.205c.07.342.207.615.413.957l-.688 1.025 1.444 1.434 1.032-.683c.274.137.618.274.962.41l.206 1.23h2.063l.206-1.23c.344-.068.619-.205.963-.41l1.03.752 1.444-1.435-.756-1.025c.207-.341.344-.683.413-.956l1.031-.205zM11 13.017c-1.169 0-2.063-.889-2.063-2.05 0-1.162.894-2.05 2.063-2.05s2.063.888 2.063 2.05c0 1.161-.894 2.05-2.063 2.05z"/></symbol><symbol viewBox="0 0 22 22" id="status_notfound_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0v-1.43a5.9 5.9 0 0 0 .827-.492z"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></symbol><symbol viewBox="0 0 14 14" id="status_open" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M7 9.219a2.218 2.218 0 1 0 0-4.436A2.218 2.218 0 0 0 7 9.22zm0 1.12a3.338 3.338 0 1 1 0-6.676 3.338 3.338 0 0 1 0 6.676z"/></symbol><symbol viewBox="0 0 14 14" id="status_pending" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M4.7 5.3c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H5c-.2 0-.3-.1-.3-.3V5.3m3 0c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H8c-.2 0-.3-.1-.3-.3V5.3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_pending_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M7.386 8.329c0-.315.157-.472.471-.472h1.414c.315 0 .472.157.472.472v5.342c0 .315-.157.472-.472.472H7.857c-.314 0-.471-.157-.471-.472V8.33m4.714 0c0-.315.157-.472.471-.472h1.415c.314 0 .471.157.471.472v5.342c0 .315-.157.472-.471.472H12.57c-.314 0-.471-.157-.471-.472V8.33"/></symbol><symbol viewBox="0 0 14 14" id="status_running" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 3c2.2 0 4 1.8 4 4s-1.8 4-4 4c-1.3 0-2.5-.7-3.3-1.7L7 7V3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_running_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.714c3.457 0 6.286 2.829 6.286 6.286 0 3.457-2.829 6.286-6.286 6.286-2.043 0-3.929-1.1-5.186-2.672L11 11V4.714"/></symbol><symbol viewBox="0 0 14 14" id="status_skipped" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF"/><path d="M6.415 7.04L4.579 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L5.341 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L6.415 7.04zm2.54 0L7.119 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L7.881 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L8.955 7.04z"/></symbol><symbol viewBox="0 0 22 22" id="status_skipped_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M14.072 11.063l-2.82 2.82a.46.46 0 0 0-.001.652l.495.495a.457.457 0 0 0 .653-.001l3.7-3.7a.46.46 0 0 0 .001-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.479-3.479a.464.464 0 0 0-.654.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/><path d="M10.08 11.063l-2.819 2.82a.46.46 0 0 0-.002.652l.496.495a.457.457 0 0 0 .652-.001l3.7-3.7a.46.46 0 0 0 .002-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.48-3.479a.464.464 0 0 0-.653.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/></symbol><symbol viewBox="0 0 14 14" id="status_success" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_success_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.866 12.095l-1.95-1.95a.462.462 0 0 0-.647.01l-.964.964a.46.46 0 0 0-.01.646l3.013 3.014a.787.787 0 0 0 1.106.008l.425-.425 4.854-4.853a.462.462 0 0 0 .002-.659l-.964-.964a.468.468 0 0 0-.658.002l-4.207 4.207z"/></symbol><symbol viewBox="0 0 14 14" id="status_success_solid" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7zm6.278.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 14 14" id="status_warning" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6 3.5c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v4c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-4m0 6c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v1c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-1"/></g></symbol><symbol viewBox="0 0 22 22" id="status_warning_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.429 5.5c0-.471.314-.786.785-.786h1.572c.471 0 .785.315.785.786v6.286c0 .471-.314.785-.785.785h-1.572c-.471 0-.785-.314-.785-.785V5.5m0 9.429c0-.472.314-.786.785-.786h1.572c.471 0 .785.314.785.786V16.5c0 .471-.314.786-.785.786h-1.572c-.471 0-.785-.315-.785-.786v-1.571"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="terminal" xmlns="http://www.w3.org/2000/svg"><path d="M7 8a.997.997 0 0 1-.293.707l-1.414 1.414a1 1 0 1 1-1.414-1.414L4.586 8l-.707-.707a1 1 0 1 1 1.414-1.414l1.414 1.414A.997.997 0 0 1 7 8zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4zm5 7h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="thumbtack" xmlns="http://www.w3.org/2000/svg"><path d="M7.125 9h-2.19a.5.5 0 0 1-.417-.777L6 6V2L5.362.724A.5.5 0 0 1 5.809 0h4.382a.5.5 0 0 1 .447.724L10 2v4l1.482 2.223a.5.5 0 0 1-.416.777H8.875L8 16l-.875-7z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.34 10.479A3 3 0 0 1 12.756 15h-9.51A3 3 0 0 1 .66 10.479l4.755-8.083a3 3 0 0 1 5.172 0l4.755 8.083zm-6.478-7.07a1 1 0 0 0-1.724 0l-4.755 8.084A1 1 0 0 0 3.245 13h9.51a1 1 0 0 0 .862-1.507L8.862 3.41zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.536 7.95L1.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 1 1-1.414-1.415L5.536 7.95zm7 0L8.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.414-1.415l4.243-4.242z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-down" xmlns="http://www.w3.org/2000/svg"><path d="M10.472 7.282a.862.862 0 0 1 1.26-.006c.357.364.357.958 0 1.285L8.627 11.73A.886.886 0 0 1 8 12a.849.849 0 0 1-.627-.27L4.275 8.561a.904.904 0 0 1-.013-1.285.861.861 0 0 1 1.26-.007l2.486 2.527z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4 12.5v-9A1.5 1.5 0 0 1 5.5 2h2.104c2.182 0 3.879.681 3.879 2.982 0 1.067-.517 2.227-1.374 2.595v.073C11.176 7.963 12 8.865 12 10.466 12 12.914 10.19 14 7.911 14H5.5A1.5 1.5 0 0 1 4 12.5zm2.376-5.696H7.49c1.164 0 1.665-.552 1.665-1.417 0-.94-.534-1.289-1.649-1.289h-1.13v2.706zm0 5.098h1.341c1.293 0 1.956-.515 1.956-1.62 0-1.049-.647-1.472-1.956-1.472H6.376v3.092z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="bookmark" xmlns="http://www.w3.org/2000/svg"><path d="M6.746 10.505a2 2 0 0 1 2.508 0L11 11.911V3H5v8.91l1.746-1.405zM5 1h6a2 2 0 0 1 2 2v10.999a1 1 0 0 1-1.627.779L8 12.064l-3.373 2.714A1 1 0 0 1 3 13.998V3a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="bullhorn" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.143 10H7V4H3a3 3 0 1 0 0 6h.143l.734 5.141a1 1 0 0 0 .99.859h1.556a.5.5 0 0 0 .495-.57L6.143 10zM8 4c1.034.02 2.039-.274 3.014-.883.727-.455 1.836-1.334 3.328-2.637A1 1 0 0 1 16 1.233v10.764a1 1 0 0 1-1.595.803c-1.658-1.227-2.788-1.992-3.392-2.294-.781-.39-1.785-.559-3.013-.506V4z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chart" xmlns="http://www.w3.org/2000/svg"><path d="M15 14a1 1 0 0 1 0 2H2a2 2 0 0 1-2-2V1a1 1 0 1 1 2 0v13h13zM3.142 8.735l2.502-2.561a.5.5 0 0 1 .714-.003L8 7.833l3.592-4.553a.5.5 0 0 1 .796.015l2.516 3.454a.5.5 0 0 1 .096.295V12.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5V9.085a.5.5 0 0 1 .142-.35z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 9 13" id="collapse"><path d="M.084.25C.01.18-.015.12.008.071.031.024.093 0 .194 0h8.521c.1 0 .162.024.185.072.023.048-.002.107-.075.177l-4.11 3.935a.372.372 0 0 1-.11.072h-.301a.508.508 0 0 1-.11-.072L.084.249zM.377 6.88a.364.364 0 0 1-.26-.105.334.334 0 0 1-.11-.25v-.709c0-.096.036-.179.11-.249a.364.364 0 0 1 .26-.105h8.15c.101 0 .188.035.261.105.074.07.11.153.11.25v.709c0 .096-.036.179-.11.249a.364.364 0 0 1-.26.105H.377zM.084 12.132c-.074.07-.099.129-.076.177.023.048.085.072.186.072h8.521c.1 0 .162-.024.185-.072.023-.048-.002-.107-.075-.177l-4.11-3.935a.372.372 0 0 0-.11-.072h-.301a.508.508 0 0 0-.11.072l-4.11 3.935z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="cut" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="2" y="7" fill-rule="evenodd" rx="1"/></symbol><symbol viewBox="0 0 16 16" id="dashboard" xmlns="http://www.w3.org/2000/svg"><path d="M7.709 10.021l.696-2.6a.5.5 0 0 1 .966.26l-.657 2.45A2 2 0 0 1 10 12H6a2 2 0 0 1 1.709-1.979zM0 8.9a8 8 0 0 1 15.998 0H16v3.6a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5V8.9zM14 9A6 6 0 1 0 2 9v3.5a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5V9zM3.5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm9 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-7-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm5 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 105 26" id="double-headed-arrow" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.018 11.089L15.138.614c1.23-.911 3.086-.795 4.147.26.461.46.715 1.045.715 1.651v20.95C20 24.869 18.684 26 17.06 26a3.238 3.238 0 0 1-1.921-.614L1.019 14.911C-.212 14-.347 12.405.714 11.35c.094-.094.195-.18.303-.261zm102.964 0c.108.08.21.167.303.26 1.061 1.056.925 2.65-.303 3.562l-14.12 10.475A3.238 3.238 0 0 1 87.94 26C86.316 26 85 24.87 85 23.475V2.525c0-.606.254-1.192.715-1.65 1.061-1.056 2.917-1.172 4.146-.26l14.12 10.474zM35 17a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 1600 1600" id="ellipsis_v" xmlns="http://www.w3.org/2000/svg"><path d="M1088 1248v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V224q0-40 28-68t68-28h192q40 0 68 28t28 68z"/></symbol><symbol viewBox="0 0 18 18" id="emoji_slightly_smiling_face" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369.721.721 0 0 1 .568.047.715.715 0 0 1 .37.445 2.91 2.91 0 0 0 1.084 1.518A2.93 2.93 0 0 0 9 12.75a2.93 2.93 0 0 0 1.775-.58 2.913 2.913 0 0 0 1.084-1.518.711.711 0 0 1 .375-.445.737.737 0 0 1 .575-.047c.195.063.34.186.433.37.094.183.11.372.047.568zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smile" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568zM14 6.37c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm-6.5 0c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm9 2.63a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smiley" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568h.001zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6c.92.397 1.91.6 2.912.598a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39c.397-.92.6-1.91.598-2.912zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z"/></symbol><symbol viewBox="0 0 16 16" id="epic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.985 8.044l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637A2 2 0 0 0 1.618 9h11.661a2 2 0 0 0 1.706-.956zm0 3l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637a2 2 0 0 0 .576.084h11.661a2 2 0 0 0 1.706-.956zM3.618 2h10.995a1 1 0 0 1 .948 1.316l-1.333 4a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l2-4A1 1 0 0 1 3.618 2zm-.382 4h9.322l.667-2H4.236l-1 2z"/></symbol><symbol viewBox="0 0 16 16" id="external-link" xmlns="http://www.w3.org/2000/svg"><path d="M13.121 4.177l-4.95 4.95a1 1 0 1 1-1.414-1.414l4.95-4.95-1.386-1.386a.5.5 0 0 1 .299-.85l4.709-.524a.5.5 0 0 1 .552.552l-.523 4.71a.5.5 0 0 1-.851.297l-1.386-1.385zM12 8.884a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-8a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-4z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-addition" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-o" xmlns="http://www.w3.org/2000/svg"><path d="M13 5l-4.365-.005a2 2 0 0 1-1.882-1.33A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zm0-2a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-o-open" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.59 5.464a2.998 2.998 0 0 1 1.096 3.845l-1.666 3.436A4 4 0 0 1 10.46 15H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.558a2 2 0 0 1 1.898 1.368l.21.632h4.973a2 2 0 0 1 2 2 2 2 0 0 1-.027.329l-.023.135zM5.285 7a1 1 0 0 0-.9.564l-1.939 4a1 1 0 0 0 .9 1.436h7.074a2 2 0 0 0 1.8-1.128l1.665-3.436a1 1 0 0 0-.9-1.436h-7.7z"/></symbol><symbol viewBox="0 0 16 16" id="folder-open" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 6H5.333a2 2 0 0 0-1.664.89l-3.333 5a2 2 0 0 0-.285.662A3.017 3.017 0 0 1 0 12V3a2 2 0 0 1 2-2h2.838a3 3 0 0 1 2.828 2H11a3 3 0 0 1 3 3zM5.333 7h9.53a1 1 0 0 1 .875 1.486l-2.492 4.485A2 2 0 0 1 11.498 14H2a1 1 0 0 1-.832-1.555l3.333-5A1 1 0 0 1 5.333 7z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M9 13h3v-3H4v3h3v-1a1 1 0 0 1 2 0v1zm5-3v3.659c0 .729-.657 1.341-1.5 1.341h-9c-.843 0-1.5-.612-1.5-1.341V10h-.88C.502 10 0 9.486 0 8.853c0-.307.12-.601.333-.816l6.405-6.463a1.56 1.56 0 0 1 2.374-.052L15.66 8.03c.444.441.455 1.167.024 1.622a1.108 1.108 0 0 1-.804.348H14zM7.95 3.273l-4.595 4.64h9.264l-4.67-4.64z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="hourglass" xmlns="http://www.w3.org/2000/svg"><path d="M10.331 4.889A2.988 2.988 0 0 0 11 3V2H5v1c0 .362.064.709.182 1.03l5.15.859zM3 14v-1c0-1.78.93-3.342 2.33-4.228.447-.327.67-.582.67-.764 0-.19-.242-.46-.725-.815A4.996 4.996 0 0 1 3 3V2H2a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2h-1v1a4.997 4.997 0 0 1-2.39 4.266c-.407.3-.61.545-.61.734 0 .19.203.434.61.734A4.997 4.997 0 0 1 13 13v1h1a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2h1zm8 0v-1a3 3 0 0 0-6 0v1h6z"/></symbol><symbol viewBox="0 0 38 38" id="image-comment-dark" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#1F78D1"/><path fill="#FFF" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 38 38" id="image-comment-light" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 16 16" id="import" xmlns="http://www.w3.org/2000/svg"><path d="M9 8h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0L5.6 8.8A.5.5 0 0 1 6 8h1V1a1 1 0 1 1 2 0v7zM0 8a1 1 0 1 1 2 0 6 6 0 1 0 12 0 1 1 0 0 1 2 0A8 8 0 1 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-external" xmlns="http://www.w3.org/2000/svg"><path d="M11 4a5.99 5.99 0 0 0-2 .341V3a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2.528a6.003 6.003 0 0 0 2.705 1.736A2.99 2.99 0 0 1 8 16H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3v1zM8.212 8.97l-.568-.876A.25.25 0 0 1 7.66 7.8l.404-.5a.25.25 0 0 1 .284-.076l.938.36c.256-.182.543-.325.85-.42l.323-.988a.25.25 0 0 1 .237-.173h.643a.25.25 0 0 1 .238.173l.321.989c.308.094.595.237.852.418l.937-.359a.25.25 0 0 1 .284.076l.404.5a.25.25 0 0 1 .016.293l-.568.875c.113.297.18.616.192.95l.9.54a.25.25 0 0 1 .114.27l-.145.627a.25.25 0 0 1-.221.192l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.282a.25.25 0 0 1-.29-.051l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.905a.25.25 0 0 1-.29.05l-.577-.281a.25.25 0 0 1-.138-.26L9 12.254a3.015 3.015 0 0 1-.512-.607l-1.114-.098a.25.25 0 0 1-.222-.192l-.145-.627a.25.25 0 0 1 .115-.27l.899-.54c.012-.334.08-.653.192-.95zm2.806 2.034a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="italic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.5 12l2-8H6a1 1 0 1 1 0-2h6a1 1 0 0 1 0 2h-1.5l-2 8H10a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1.5z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="menu" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.143 2h13.714C15.488 2 16 2.448 16 3s-.512 1-1.143 1H1.143C.512 4 0 3.552 0 3s.512-1 1.143-1zm0 5h13.714C15.488 7 16 7.448 16 8s-.512 1-1.143 1H1.143C.512 9 0 8.552 0 8s.512-1 1.143-1zm0 5h13.714c.631 0 1.143.448 1.143 1s-.512 1-1.143 1H1.143C.512 14 0 13.552 0 13s.512-1 1.143-1z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pencil-square" xmlns="http://www.w3.org/2000/svg"><path d="M12 9a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V9zm.778-7.179l1.414 1.415-6.476 6.476a1 1 0 0 1-.498.27l-1.51.325.323-1.512a1 1 0 0 1 .27-.497l6.477-6.477zM15.607.407a1 1 0 0 1 0 1.414l-.708.707-1.414-1.414.707-.707a1 1 0 0 1 1.415 0z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7H2a1 1 0 1 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2H9V2a1 1 0 1 0-2 0v5z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="podcast" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862a1 1 0 0 1-.785 1.177A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-1-1 1 1 0 0 1 .02-.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 7.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4.464 2.464A1 1 0 0 1 5.88 3.88a3 3 0 0 0 0 4.242 1 1 0 0 1-1.415 1.415 5 5 0 0 1 0-7.072zm7.072 7.072A1 1 0 0 1 10.12 8.12a3 3 0 0 0 0-4.242 1 1 0 0 1 1.415-1.415 5 5 0 0 1 0 7.072zM2.343.343a1 1 0 1 1 1.414 1.414 6 6 0 0 0 0 8.486 1 1 0 1 1-1.414 1.414 8 8 0 0 1 0-11.314zm11.314 11.314a1 1 0 1 1-1.414-1.414 6 6 0 0 0 0-8.486A1 1 0 0 1 13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.625 4.423A4.897 4.897 0 0 1 8.079 3c2.73 0 4.944 2.239 4.944 5s-2.214 5-4.944 5c-1.41 0-2.723-.6-3.655-1.633a.98.98 0 0 0-1.397-.066 1.008 1.008 0 0 0-.064 1.413A6.87 6.87 0 0 0 8.079 15C11.9 15 15 11.866 15 8s-3.099-7-6.921-7A6.866 6.866 0 0 0 3.08 3.158L1.833 2.137a.49.49 0 0 0-.695.074.504.504 0 0 0-.11.311L1 7.26a.497.497 0 0 0 .6.492l4.576-1.013a.5.5 0 0 0 .206-.877L4.625 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.375 4.423A4.897 4.897 0 0 0 7.921 3c-2.73 0-4.944 2.239-4.944 5s2.214 5 4.944 5c1.41 0 2.723-.6 3.655-1.633a.98.98 0 0 1 1.397-.066c.403.373.432 1.005.064 1.413A6.87 6.87 0 0 1 7.921 15C4.1 15 1 11.866 1 8s3.099-7 6.921-7c1.915 0 3.706.792 4.999 2.158l1.247-1.021a.49.49 0 0 1 .695.074c.07.088.11.198.11.311L15 7.26a.497.497 0 0 1-.6.492L9.824 6.739a.5.5 0 0 1-.206-.877l1.757-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.114 6.958a4 4 0 0 0 5.283 4.775 1 1 0 1 1 .712 1.87A6 6 0 0 1 2.182 6.44l-.741-.2a.5.5 0 0 1-.12-.915l2.195-1.268a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.712-1.87 6 6 0 0 1 7.927 7.162l.742.2a.5.5 0 0 1 .12.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 12 16" id="scroll_down" xmlns="http://www.w3.org/2000/svg"><path class="fcfirst-triangle" d="M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043a.51.51 0 0 0 .321-.105c.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"/><path class="fcsecond-triangle" d="M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"/><path class="fcthird-triangle" d="M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91a.458.458 0 0 1-.136.09h-.37a.626.626 0 0 1-.136-.09"/></symbol><symbol viewBox="0 0 12 16" id="scroll_up" xmlns="http://www.w3.org/2000/svg"><path d="M1.048 1.845a.508.508 0 0 1-.32-.105c-.091-.07-.136-.154-.136-.25V.78c0-.095.045-.178.135-.249a.508.508 0 0 1 .321-.105h10.043a.51.51 0 0 1 .321.105c.09.07.136.154.136.25v.71c0 .095-.045.178-.136.249a.508.508 0 0 1-.32.105M.687 7.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 3.09A.458.458 0 0 0 6.257 3h-.37a.626.626 0 0 0-.136.09M.687 14.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 10.09a.458.458 0 0 0-.136-.09h-.37a.626.626 0 0 0-.136.09"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8c1.657 0 3 1.373 3 3.067v7.346c0 1.065-.54 2.053-1.426 2.611l-4 2.52a2.944 2.944 0 0 1-3.148 0l-4-2.52A3.083 3.083 0 0 1 1 10.414V3.066C1 1.373 2.343 0 4 0zm0 2.045c-.552 0-1 .457-1 1.022v7.346c0 .355.18.685.475.87l4 2.52a.981.981 0 0 0 1.05 0l4-2.52c.295-.185.475-.515.475-.87V3.067c0-.565-.448-1.022-1-1.022H4zm0 1.533c0-.282.224-.511.5-.511h4V12.1a.52.52 0 0 1-.069.258.494.494 0 0 1-.684.183l-3.5-2.098a.513.513 0 0 1-.247-.44V3.577z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 14 14" id="spinner" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="7" cy="7" r="6" stroke="#000" stroke-opacity=".1" stroke-width="2"/><path fill="#000" fill-opacity=".1" fill-rule="nonzero" d="M7 0a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5V0z"/></g></symbol><symbol viewBox="0 0 16 16" id="staged" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.828 3.536l1.415-1.415a1 1 0 1 1 1.414 1.415l-2.121 2.12a.997.997 0 0 1-1.415 0L9.707 4.244a1 1 0 0 1 1.414-1.415l.707.708zM2 3h5a1 1 0 1 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 14 14" id="status_canceled" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M5.2 3.8l4.9 4.9c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L3.8 5.2c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0"/></g></symbol><symbol viewBox="0 0 22 22" id="status_canceled_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M8.171 5.971l7.7 7.7a.76.76 0 0 1 0 1.1l-1.1 1.1a.76.76 0 0 1-1.1 0l-7.7-7.7a.76.76 0 0 1 0-1.1l1.1-1.1a.76.76 0 0 1 1.1 0"/></symbol><symbol viewBox="0 0 16 16" id="status_closed" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83a1 1 0 0 1 1.414 1.416l-3.535 3.535a1 1 0 0 1-1.415.001l-2.12-2.12a1 1 0 1 1 1.413-1.415zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 14 14" id="status_created" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><circle cx="7" cy="7" r="3.25"/></g></symbol><symbol viewBox="0 0 22 22" id="status_created_borderless" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="5.107"/></symbol><symbol viewBox="0 0 14 14" id="status_failed" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 5.969L5.599 4.568a.29.29 0 0 0-.413.004l-.614.614a.294.294 0 0 0-.004.413L5.968 7l-1.4 1.401a.29.29 0 0 0 .004.413l.614.614c.113.114.3.117.413.004L7 8.032l1.401 1.4a.29.29 0 0 0 .413-.004l.614-.614a.294.294 0 0 0 .004-.413L8.032 7l1.4-1.401a.29.29 0 0 0-.004-.413l-.614-.614a.294.294 0 0 0-.413-.004L7 5.968z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_failed_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 9.38L8.798 7.178a.455.455 0 0 0-.65.006l-.964.965a.462.462 0 0 0-.006.65L9.38 11l-2.202 2.202a.455.455 0 0 0 .006.65l.965.964a.462.462 0 0 0 .65.006L11 12.62l2.202 2.202a.455.455 0 0 0 .65-.006l.964-.965a.462.462 0 0 0 .006-.65L12.62 11l2.202-2.202a.455.455 0 0 0-.006-.65l-.965-.964a.462.462 0 0 0-.65-.006L11 9.38z"/></symbol><symbol viewBox="0 0 14 14" id="status_manual" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M10.5 7.63V6.37l-.787-.13c-.044-.175-.132-.349-.263-.61l.481-.652-.918-.913-.657.478a2.346 2.346 0 0 0-.612-.26L7.656 3.5H6.388l-.132.783c-.219.043-.394.13-.612.26l-.657-.478-.918.913.437.652c-.131.218-.175.392-.262.61l-.744.086v1.261l.787.13c.044.218.132.392.263.61l-.438.651.92.913.655-.434c.175.086.394.173.613.26l.131.783h1.313l.131-.783c.219-.043.394-.13.613-.26l.656.478.918-.913-.48-.652c.13-.218.218-.435.262-.61l.656-.13zM7 8.283a1.285 1.285 0 0 1-1.313-1.305c0-.739.57-1.304 1.313-1.304.744 0 1.313.565 1.313 1.304 0 .74-.57 1.305-1.313 1.305z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_manual_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 11.99v-1.98l-1.238-.206c-.068-.273-.206-.546-.412-.956l.756-1.025-1.444-1.435-1.03.752a3.686 3.686 0 0 0-.963-.41L12.03 5.5h-1.994l-.206 1.23c-.343.068-.618.205-.962.41l-1.031-.752-1.444 1.435.687 1.025c-.206.341-.275.615-.412.956L5.5 9.941v1.981l1.237.205c.07.342.207.615.413.957l-.688 1.025 1.444 1.434 1.032-.683c.274.137.618.274.962.41l.206 1.23h2.063l.206-1.23c.344-.068.619-.205.963-.41l1.03.752 1.444-1.435-.756-1.025c.207-.341.344-.683.413-.956l1.031-.205zM11 13.017c-1.169 0-2.063-.889-2.063-2.05 0-1.162.894-2.05 2.063-2.05s2.063.888 2.063 2.05c0 1.161-.894 2.05-2.063 2.05z"/></symbol><symbol viewBox="0 0 22 22" id="status_notfound_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0v-1.43a5.9 5.9 0 0 0 .827-.492z"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></symbol><symbol viewBox="0 0 14 14" id="status_open" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M7 9.219a2.218 2.218 0 1 0 0-4.436A2.218 2.218 0 0 0 7 9.22zm0 1.12a3.338 3.338 0 1 1 0-6.676 3.338 3.338 0 0 1 0 6.676z"/></symbol><symbol viewBox="0 0 14 14" id="status_pending" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M4.7 5.3c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H5c-.2 0-.3-.1-.3-.3V5.3m3 0c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H8c-.2 0-.3-.1-.3-.3V5.3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_pending_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M7.386 8.329c0-.315.157-.472.471-.472h1.414c.315 0 .472.157.472.472v5.342c0 .315-.157.472-.472.472H7.857c-.314 0-.471-.157-.471-.472V8.33m4.714 0c0-.315.157-.472.471-.472h1.415c.314 0 .471.157.471.472v5.342c0 .315-.157.472-.471.472H12.57c-.314 0-.471-.157-.471-.472V8.33"/></symbol><symbol viewBox="0 0 14 14" id="status_running" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 3c2.2 0 4 1.8 4 4s-1.8 4-4 4c-1.3 0-2.5-.7-3.3-1.7L7 7V3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_running_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.714c3.457 0 6.286 2.829 6.286 6.286 0 3.457-2.829 6.286-6.286 6.286-2.043 0-3.929-1.1-5.186-2.672L11 11V4.714"/></symbol><symbol viewBox="0 0 14 14" id="status_skipped" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF"/><path d="M6.415 7.04L4.579 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L5.341 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L6.415 7.04zm2.54 0L7.119 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L7.881 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L8.955 7.04z"/></symbol><symbol viewBox="0 0 22 22" id="status_skipped_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M14.072 11.063l-2.82 2.82a.46.46 0 0 0-.001.652l.495.495a.457.457 0 0 0 .653-.001l3.7-3.7a.46.46 0 0 0 .001-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.479-3.479a.464.464 0 0 0-.654.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/><path d="M10.08 11.063l-2.819 2.82a.46.46 0 0 0-.002.652l.496.495a.457.457 0 0 0 .652-.001l3.7-3.7a.46.46 0 0 0 .002-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.48-3.479a.464.464 0 0 0-.653.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/></symbol><symbol viewBox="0 0 14 14" id="status_success" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_success_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.866 12.095l-1.95-1.95a.462.462 0 0 0-.647.01l-.964.964a.46.46 0 0 0-.01.646l3.013 3.014a.787.787 0 0 0 1.106.008l.425-.425 4.854-4.853a.462.462 0 0 0 .002-.659l-.964-.964a.468.468 0 0 0-.658.002l-4.207 4.207z"/></symbol><symbol viewBox="0 0 14 14" id="status_success_solid" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7zm6.278.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 14 14" id="status_warning" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6 3.5c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v4c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-4m0 6c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v1c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-1"/></g></symbol><symbol viewBox="0 0 22 22" id="status_warning_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.429 5.5c0-.471.314-.786.785-.786h1.572c.471 0 .785.315.785.786v6.286c0 .471-.314.785-.785.785h-1.572c-.471 0-.785-.314-.785-.785V5.5m0 9.429c0-.472.314-.786.785-.786h1.572c.471 0 .785.314.785.786V16.5c0 .471-.314.786-.785.786h-1.572c-.471 0-.785-.315-.785-.786v-1.571"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="terminal" xmlns="http://www.w3.org/2000/svg"><path d="M7 8a.997.997 0 0 1-.293.707l-1.414 1.414a1 1 0 1 1-1.414-1.414L4.586 8l-.707-.707a1 1 0 1 1 1.414-1.414l1.414 1.414A.997.997 0 0 1 7 8zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4zm5 7h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="thumbtack" xmlns="http://www.w3.org/2000/svg"><path d="M7.125 9h-2.19a.5.5 0 0 1-.417-.777L6 6V2L5.362.724A.5.5 0 0 1 5.809 0h4.382a.5.5 0 0 1 .447.724L10 2v4l1.482 2.223a.5.5 0 0 1-.416.777H8.875L8 16l-.875-7z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="unstaged" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.572 10.506c.867 1.42.375 3.247-1.098 4.082a3.184 3.184 0 0 1-1.57.412h-9.81C1.387 15 0 13.665 0 12.018a2.9 2.9 0 0 1 .427-1.512L5.332 2.47C6.2 1.05 8.096.577 9.57 1.412c.453.257.831.622 1.098 1.059l4.905 8.035zM8.89 3.479a1.014 1.014 0 0 0-.366-.353 1.053 1.053 0 0 0-1.412.353l-4.905 8.035a.967.967 0 0 0-.143.504c0 .549.462.994 1.032.994h9.81c.184 0 .364-.048.523-.137a.974.974 0 0 0 .366-1.361L8.889 3.479zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/image_comment_light_cursor.svg b/app/assets/images/illustrations/image_comment_light_cursor.svg new file mode 100644 index 00000000000..ac712ea0c96 --- /dev/null +++ b/app/assets/images/illustrations/image_comment_light_cursor.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 38 38"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/image_comment_light_cursor@2x.svg b/app/assets/images/illustrations/image_comment_light_cursor@2x.svg new file mode 100644 index 00000000000..02943acd9d7 --- /dev/null +++ b/app/assets/images/illustrations/image_comment_light_cursor@2x.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 38 38"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/job_not_triggered.svg b/app/assets/images/illustrations/job_not_triggered.svg new file mode 100644 index 00000000000..e13c1cb0a7d --- /dev/null +++ b/app/assets/images/illustrations/job_not_triggered.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 310 141" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" fill-rule="evenodd"><g fill-rule="nonzero"><path fill="#e5e5e5" d="M48 69c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 48 69m14 0c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 62 69"/><g fill="#31af64"><path d="M19 88C8.507 88 0 79.493 0 69s8.507-19 19-19 19 8.507 19 19-8.507 19-19 19m0-4c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15-8.284 0-15 6.716-15 15 0 8.284 6.716 15 15 15"/><path d="M17.07 71.02l-2.829-2.828a1.995 1.995 0 0 0-2.828 0 1.997 1.997 0 0 0 0 2.83l4.243 4.243a1.993 1.993 0 0 0 2.823.005l7.79-7.79a1.998 1.998 0 0 0-.007-2.822 1.99 1.99 0 0 0-2.822-.006l-6.37 6.37v-.001"/></g></g><g transform="translate(187)"><rect width="116" height="134" y="7" fill="#f9f9f9" rx="10"/><rect width="116" height="134" x="5" y="2" fill="#fff" rx="10"/><path fill="#eee" fill-rule="nonzero" d="M15 4a8 8 0 0 0-8 8v114a8 8 0 0 0 8 8h96a8 8 0 0 0 8-8V12a8 8 0 0 0-8-8H15m0-4h96c6.627 0 12 5.373 12 12v114c0 6.627-5.373 12-12 12H15c-6.627 0-12-5.373-12-12V12C3 5.373 8.373 0 15 0"/><g transform="translate(23 25)"><g fill="#e1dbf1"><rect width="16" height="4" rx="2"/><rect width="16" height="4" x="32" y="12" rx="2"/></g><rect width="16" height="4" x="44" fill="#eee" rx="2"/><rect width="16" height="4" x="12" y="24" fill="#e1dbf1" rx="2"/><rect width="16" height="4" x="64" y="36" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="20" fill="#fee1d3" rx="2" id="a"/><rect width="8" height="4" x="32" y="36" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="52" y="12" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="64" fill="#fef0e8" rx="2" id="b"/><rect width="12" height="4" x="16" y="48" fill="#e1dbf1" rx="2"/><rect width="8" height="4" x="44" y="36" fill="#fc6d26" rx="2"/><g fill="#e1dbf1"><rect width="4" height="4" x="56" y="36" rx="2"/><rect width="4" height="4" x="64" y="60" rx="2"/></g><rect width="4" height="4" x="72" y="60" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="32" fill="#fc6d26" rx="2" id="c"/><g fill="#eee"><rect width="28" height="4" y="36" rx="2"/><rect width="28" height="4" x="44" y="48" rx="2"/></g><rect width="28" height="4" x="32" y="60" fill="#efedf8" rx="2"/><rect width="28" height="4" y="12" fill="#6b4fbb" rx="2"/><rect width="28" height="4" x="32" y="24" fill="#c3b8e3" rx="2"/><rect width="8" height="4" y="24" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="32" y="48" fill="#6b4fbb" rx="2"/><rect width="12" height="4" y="48" fill="#fc6d26" rx="2"/><g fill="#fef0e8"><rect width="12" height="4" y="60" rx="2"/><rect width="12" height="4" x="16" y="60" rx="2"/></g></g><g transform="translate(23 97)"><rect width="16" height="4" fill="#efedf8" rx="2"/><rect width="16" height="4" x="18" y="12" fill="#fc6d26" rx="2"/><rect width="16" height="4" x="44" fill="#6b4fbb" rx="2"/><use xlink:href="#a"/><rect width="8" height="4" x="38" y="12" fill="#fef0e8" rx="2"/><use xlink:href="#b"/><use xlink:href="#c"/><rect width="14" height="4" y="12" fill="#eee" rx="2"/></g></g><g fill-rule="nonzero"><path fill="#eee" d="M109 101a2 2 0 1 1 0-4c2.524 0 5-.346 7.379-1.02a2 2 0 0 1 1.091 3.849 31.007 31.007 0 0 1-8.47 1.172m18.09-5.825a31.174 31.174 0 0 0 6.187-5.899 2 2 0 1 0-3.131-2.489 27.133 27.133 0 0 1-5.393 5.142 2.001 2.001 0 0 0 2.337 3.247m11.297-15.288a30.923 30.923 0 0 0 1.576-8.407 2 2 0 1 0-3.996-.188 26.875 26.875 0 0 1-1.372 7.32 2 2 0 1 0 3.791 1.275m.283-18.89a30.855 30.855 0 0 0-3.593-7.763 2 2 0 1 0-3.362 2.166 26.905 26.905 0 0 1 3.128 6.757 2 2 0 0 0 3.828-1.16M127.875 45.41a30.973 30.973 0 0 0-7.435-4.228 2 2 0 0 0-1.477 3.717 26.936 26.936 0 0 1 6.474 3.682 2 2 0 0 0 2.438-3.172m-17.834-6.391a31.09 31.09 0 0 0-8.5.886 2 2 0 0 0 .959 3.883 27.06 27.06 0 0 1 7.408-.771 2 2 0 1 0 .132-3.998m-18.272 5.207a31.139 31.139 0 0 0-6.383 5.688 2 2 0 1 0 3.045 2.593 27.152 27.152 0 0 1 5.564-4.957 2 2 0 1 0-2.226-3.324M79.96 59.121a30.864 30.864 0 0 0-1.862 8.349 2 2 0 1 0 3.987.323c.203-2.506.75-4.946 1.62-7.268a2 2 0 1 0-3.746-1.404m-.923 18.873a30.827 30.827 0 0 0 3.327 7.881 2.001 2.001 0 0 0 3.435-2.051 26.785 26.785 0 0 1-2.895-6.859 2 2 0 0 0-3.865 1.029M89.301 93.94a31.008 31.008 0 0 0 7.286 4.476 2 2 0 1 0 1.603-3.665 26.983 26.983 0 0 1-6.346-3.899 2 2 0 0 0-2.543 3.087m17.61 6.991a2 2 0 0 1 .265-3.991c.601.04 1.205.06 1.812.06a1.999 1.999 0 1 1-.001 3.999c-.695 0-1.387-.023-2.076-.069"/><path fill="#fc0" d="M117.78 63.798c.241.268.288.563.14.884l-10.848 23.24c-.174.334-.455.502-.843.502-.054 0-.148-.014-.282-.04a.855.855 0 0 1-.512-.382.761.761 0 0 1-.09-.603l3.957-16.232-8.156 2.03a1.08 1.08 0 0 1-.241.02.93.93 0 0 1-.623-.222c-.24-.2-.328-.462-.26-.783l4.04-16.574a.858.858 0 0 1 .321-.462.917.917 0 0 1 .563-.18h6.59c.254 0 .468.083.642.25a.797.797 0 0 1 .261.593.818.818 0 0 1-.1.362l-3.435 9.301 7.955-1.969c.107-.027.187-.04.241-.04.254 0 .482.1.683.301"/><path fill="#e5e5e5" d="M148 69c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 148 69m14 0c0-1.105.887-2 1.998-2h4c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.992 1.992 0 0 1 162 69"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/manual_action.svg b/app/assets/images/illustrations/manual_action.svg new file mode 100644 index 00000000000..85735855b46 --- /dev/null +++ b/app/assets/images/illustrations/manual_action.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 398 151" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" fill-rule="evenodd"><path fill="#fef0e8" stroke="#fc6d26" stroke-width="4" d="M57.7 106.5h21.6a4.2 4.2 0 0 1 4.2 4.2v5.6a4.2 4.2 0 0 1-4.2 4.2H57.7a4.2 4.2 0 0 1-4.2-4.2v-5.6a4.2 4.2 0 0 1 4.2-4.2"/><g transform="translate(42 117)"><rect width="52" height="23" x=".5" y=".5" fill="#fff" stroke="#eee" stroke-width="4" rx="4.2"/><g fill="#fdc4a8"><rect width="11" height="2" x="8" y="8" rx="1"/><rect width="11" height="2" x="8" y="14" rx="1"/></g></g><g fill-rule="nonzero"><path fill="#e1dbf1" d="M96.31 132.32c1.048 0 1.648.007 4.319.042 11.523.153 18.377-.12 26.32-1.533 24.23-4.309 38.521-18.02 38.521-45.03 0-31.02 21.885-44.487 66.903-40.522l.351-3.985c-47.09-4.147-71.25 10.727-71.25 44.507 0 24.868-12.746 37.1-35.22 41.09-7.623 1.356-14.284 1.621-25.567 1.471a287.717 287.717 0 0 0-4.372-.042v4"/><path fill="#eee" d="M242 57.678c-6.29-1.373-11-6.976-11-13.678 0-6.702 4.71-12.304 11-13.678v4.136c-4.057 1.274-7 5.065-7 9.542 0 4.478 2.943 8.268 7 9.542v4.136"/></g><g transform="translate(242)"><rect width="116" height="134" y="7" fill="#f9f9f9" rx="10"/><rect width="116" height="134" x="5" y="2" fill="#fff" rx="10"/><path fill="#eee" fill-rule="nonzero" d="M15 4a8 8 0 0 0-8 8v114a8 8 0 0 0 8 8h96a8 8 0 0 0 8-8V12a8 8 0 0 0-8-8H15m0-4h96c6.627 0 12 5.373 12 12v114c0 6.627-5.373 12-12 12H15c-6.627 0-12-5.373-12-12V12C3 5.373 8.373 0 15 0"/><g transform="translate(23 25)"><g fill="#e1dbf1"><rect width="16" height="4" rx="2"/><rect width="16" height="4" x="32" y="12" rx="2"/></g><rect width="16" height="4" x="44" fill="#eee" rx="2"/><rect width="16" height="4" x="12" y="24" fill="#e1dbf1" rx="2"/><rect width="16" height="4" x="64" y="36" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="20" fill="#fee1d3" rx="2" id="a"/><rect width="8" height="4" x="32" y="36" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="52" y="12" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="64" fill="#fef0e8" rx="2" id="b"/><rect width="12" height="4" x="16" y="48" fill="#e1dbf1" rx="2"/><rect width="8" height="4" x="44" y="36" fill="#fc6d26" rx="2"/><g fill="#e1dbf1"><rect width="4" height="4" x="56" y="36" rx="2"/><rect width="4" height="4" x="64" y="60" rx="2"/></g><rect width="4" height="4" x="72" y="60" fill="#fc6d26" rx="2"/><rect width="8" height="4" x="32" fill="#fc6d26" rx="2" id="c"/><g fill="#eee"><rect width="28" height="4" y="36" rx="2"/><rect width="28" height="4" x="44" y="48" rx="2"/></g><rect width="28" height="4" x="32" y="60" fill="#efedf8" rx="2"/><rect width="28" height="4" y="12" fill="#6b4fbb" rx="2"/><rect width="28" height="4" x="32" y="24" fill="#c3b8e3" rx="2"/><rect width="8" height="4" y="24" fill="#fef0e8" rx="2"/><rect width="8" height="4" x="32" y="48" fill="#6b4fbb" rx="2"/><rect width="12" height="4" y="48" fill="#fc6d26" rx="2"/><g fill="#fef0e8"><rect width="12" height="4" y="60" rx="2"/><rect width="12" height="4" x="16" y="60" rx="2"/></g><g transform="translate(0 72)"><rect width="16" height="4" fill="#efedf8" rx="2"/><rect width="16" height="4" x="18" y="12" fill="#fc6d26" rx="2"/><rect width="16" height="4" x="44" fill="#6b4fbb" rx="2"/><use xlink:href="#a"/><rect width="8" height="4" x="38" y="12" fill="#fef0e8" rx="2"/><use xlink:href="#b"/><use xlink:href="#c"/><rect width="14" height="4" y="12" fill="#eee" rx="2"/></g></g></g><g transform="translate(330 83)"><circle cx="33" cy="33" r="33" fill="#fff"/><g fill-rule="nonzero"><path fill="#eee" d="M33 68C13.67 68-2 52.33-2 33S13.67-2 33-2s35 15.67 35 35-15.67 35-35 35m0-4c17.12 0 31-13.879 31-31C64 15.88 50.121 2 33 2 15.88 2 2 15.879 2 33c0 17.12 13.879 31 31 31"/><path fill="#6b4fbb" stroke="#6b4fbb" stroke-width=".968" d="M42.383 34.655v-3.308l-2.112-.343c-.116-.456-.351-.913-.703-1.598l1.29-1.711-2.463-2.398-1.76 1.256a6.347 6.347 0 0 0-1.642-.684l-.233-2.055h-3.401l-.352 2.055c-.586.114-1.055.342-1.642.684l-1.76-1.255-2.463 2.397 1.173 1.711c-.352.57-.469 1.027-.704 1.598l-1.995.228v3.31l2.112.342c.116.57.351 1.027.703 1.598l-1.172 1.712 2.463 2.397 1.759-1.141c.469.227 1.056.456 1.642.684l.352 2.055h3.518l.352-2.055c.586-.114 1.055-.342 1.642-.684l1.76 1.255 2.463-2.397-1.29-1.712a6.03 6.03 0 0 0 .703-1.598l1.76-.344M33 36.367c-1.994 0-3.519-1.484-3.519-3.424 0-1.941 1.525-3.424 3.519-3.424 1.994 0 3.519 1.483 3.519 3.424 0 1.94-1.525 3.424-3.519 3.424" stroke-linecap="round" stroke-linejoin="bevel"/><path fill="#e1dbf1" d="M33 53.563c-11.598 0-21-9.206-21-20.563s9.402-20.563 21-20.563S54 21.643 54 33s-9.402 20.563-21 20.563m0-4.375c9.13 0 16.532-7.248 16.532-16.188 0-8.94-7.402-16.188-16.532-16.188-9.13 0-16.532 7.248-16.532 16.188 0 8.94 7.402 16.188 16.532 16.188"/></g></g><path fill="#fff" d="M164 114c14.912 0 27-12.09 27-27 0-14.912-12.09-27-27-27-14.912 0-27 12.09-27 27 0 14.912 12.09 27 27 27"/><g fill-rule="nonzero"><path fill="#eee" d="M164 118c-17.12 0-31-13.879-31-31 0-17.12 13.879-31 31-31 17.12 0 31 13.879 31 31 0 17.12-13.879 31-31 31m0-4c14.912 0 27-12.09 27-27 0-14.912-12.09-27-27-27-14.912 0-27 12.09-27 27 0 14.912 12.09 27 27 27"/><path fill="#fc0" d="M172.78 80.798c.241.268.288.563.14.884l-10.848 23.24c-.174.334-.455.502-.843.502-.054 0-.148-.014-.282-.04a.855.855 0 0 1-.512-.382.761.761 0 0 1-.09-.603l3.957-16.232-8.156 2.03a1.08 1.08 0 0 1-.241.02.93.93 0 0 1-.623-.222c-.24-.2-.328-.462-.26-.783l4.04-16.574a.858.858 0 0 1 .321-.462.917.917 0 0 1 .563-.18h6.59c.254 0 .468.083.642.25a.797.797 0 0 1 .261.593.818.818 0 0 1-.1.362l-3.435 9.301 7.955-1.969c.107-.027.187-.04.241-.04.254 0 .482.1.683.301"/></g><g><path fill="#eee" fill-rule="nonzero" d="M37.801 99.01l5.355 2.648c2.271 1.122 4.643-.252 4.809-2.778l.487-7.546a27.675 27.675 0 0 0 2.87-4.076c7.594-13.152 3.088-29.972-10.07-37.565-13.153-7.594-29.971-3.087-37.566 10.07-7.594 13.154-3.087 29.973 10.07 37.565a27.46 27.46 0 0 0 24.05 1.687m.952-3.992a2.002 2.002 0 0 0-1.698-.035 23.454 23.454 0 0 1-21.299-1.124c-11.24-6.488-15.09-20.86-8.602-32.1 6.49-11.239 20.862-15.09 32.1-8.601 11.239 6.489 15.09 20.862 8.6 32.1a23.519 23.519 0 0 1-2.849 3.939 1.995 1.995 0 0 0-.504 1.204l-.466 7.229-5.285-2.613"/><path fill="#fdc4a8" d="M21.137 70.471A7.495 7.495 0 0 0 27.5 74c2.684 0 5.04-1.41 6.363-3.529C36.377 71.869 38 74.267 38 77.674c0 5.799-2.739 9.587-10.5 9.587S17 83.473 17 77.674c0-3.407 1.622-5.804 4.137-7.203M27.5 72a5.5 5.5 0 1 1 0-11 5.5 5.5 0 1 1 0 11"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/merge_request_changes_empty.svg b/app/assets/images/illustrations/merge_request_changes_empty.svg index 707efa736e4..40efeb2de57 100644 --- a/app/assets/images/illustrations/merge_request_changes_empty.svg +++ b/app/assets/images/illustrations/merge_request_changes_empty.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 374 268" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="d" width="230" height="176" rx="10" fill="#fff"/><rect id="e" width="14" rx="2" height="4"/><rect id="f" width="14" x="40" rx="2" height="4"/><rect id="g" width="14" x="40" y="24" rx="2" height="4"/><rect id="h" width="7" x="20" y="12" rx="2" height="4"/><rect id="i" width="7" y="24" rx="2" height="4"/><rect id="j" width="7" x="33" y="12" rx="2" height="4"/><circle id="l" cx="31" cy="31" r="31"/><circle id="c" cx="35" cy="35" r="35"/><circle id="a" cx="44" cy="44" r="44"/><circle id="b" cx="31" cy="31" r="31"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 94)"><circle cx="57" cy="57" r="44" fill="#f9f9f9"/><g transform="rotate(-7.999 120.507 -22.508)"><use fill="#fff" xlink:href="#a"/><circle cx="44" cy="44" r="42" stroke="#eee" stroke-width="4"/><path fill="#fee1d3" fill-rule="nonzero" d="M34.394 55.736A4 4 0 0 1 36.706 55H56a6 6 0 0 0 6-6V35a6 6 0 0 0-6-6H34a6 6 0 0 0-6 6v25.26l6.394-4.529m2.312 3.264l-7.972 5.647A3.001 3.001 0 0 1 24 62.194v-27.2c0-5.523 4.477-10 10-10h22c5.523 0 10 4.477 10 10v14c0 5.523-4.477 10-10 10H36.706"/><path fill="#fc6d26" d="M38 40a2 2 0 1 1 .001 3.999A2 2 0 0 1 38 40m7 0a2 2 0 1 1 .001 3.999A2 2 0 0 1 45 40m7 0a2 2 0 1 1 .001 3.999A2 2 0 0 1 52 40"/></g></g><g transform="translate(48)"><circle cx="41" cy="41" r="31" fill="#f9f9f9"/><g transform="rotate(-7.999 84.554 -15.551)"><use fill="#fff" xlink:href="#b"/><circle cx="31" cy="31" r="29" stroke="#eee" stroke-width="4"/><rect width="20" height="4" x="21" y="29" fill="#6b4fbb" rx="2"/></g></g><path fill="#f9f9f9" d="M235.58 229H102c-6.627 0-12-5.373-12-12V65c0-6.627 5.373-12 12-12h206c6.627 0 12 5.373 12 12v18.399A34.834 34.834 0 0 1 337 79c19.33 0 35 15.67 35 35s-15.67 35-35 35a34.831 34.831 0 0 1-17-4.399v72.4c0 6.627-5.373 12-12 12h-11.58c.381 1.941.58 3.947.58 6 0 17.12-13.879 31-31 31-17.12 0-31-13.879-31-31 0-2.053.2-4.059.58-6"/><g transform="translate(87 50)"><g transform="rotate(7.999 -44.933 1563.894)"><use fill="#fff" xlink:href="#c"/><circle cx="35" cy="35" r="33" stroke="#eee" stroke-width="4"/><g transform="translate(20 19)"><circle cx="15" cy="16" r="15" fill="#f4f1fa" stroke="#6b4fbb" stroke-width="3"/><g fill="#6b4fbb"><path d="M19.419 6.996h-.007L16.959 4l-2.454 2.997H14.5L12.046 4 9.591 6.998h-.003L7.133 4 4.677 6.999H2.001c2.605-4.204 7.231-7 12.502-7 5.269 0 9.892 2.793 12.498 6.994h-2.676l-2.452-2.994-2.453 2.996"/><circle cx="9.5" cy="17.5" r="1.5"/><circle cx="20.5" cy="17.5" r="1.5"/></g></g></g><use xlink:href="#d"/><rect width="226" height="172" x="2" y="2" stroke="#eee" stroke-width="4" rx="10"/><rect width="4" height="122" x="33" y="42" fill="#eee" rx="2"/><g transform="translate(13 59)"><rect width="10" height="4" fill="#fee1d3" rx="2"/><rect width="10" height="4" y="12" fill="#f0edf8" rx="2"/><rect width="10" height="4" y="24" fill="#fef0e9" rx="2"/><rect width="10" height="4" y="36" fill="#fee1d3" rx="2"/><rect width="10" height="4" y="48" fill="#e1dbf1" rx="2"/><rect width="10" height="4" y="60" fill="#f0edf8" rx="2"/><rect width="10" height="4" y="72" fill="#fef0e9" rx="2"/><rect width="10" height="4" y="84" fill="#fee1d3" rx="2"/></g><g transform="translate(55 59)"><use fill="#6b4fbb" xlink:href="#e"/><rect width="14" height="4" x="20" fill="#f0edf8" rx="2"/><use fill="#fef0e9" xlink:href="#f"/><rect width="14" height="4" y="12" fill="#f0edf8" rx="2"/><use fill="#fef0e9" xlink:href="#g"/><rect width="14" height="4" y="48" fill="#e1dbf1" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#fef0e9" rx="2"/><use fill="#fee1d3" xlink:href="#h"/><rect width="7" height="4" x="27" y="36" fill="#6b4fbb" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#fee1d3" rx="2"/><use fill="#fc6d26" xlink:href="#i"/><rect width="21" height="4" x="13" y="24" fill="#e1dbf1" rx="2"/><rect width="21" height="4" y="36" fill="#eee" rx="2"/><use fill="#6b4fbb" xlink:href="#j"/><g transform="translate(98)"><use fill="#fee1d3" xlink:href="#e"/><rect width="14" height="4" x="20" fill="#f0edf8" rx="2"/><use fill="#fc6d26" xlink:href="#f"/><rect width="14" height="4" y="12" fill="#fef0e9" rx="2" id="k"/><use fill="#e1dbf1" xlink:href="#g"/><rect width="14" height="4" y="48" fill="#f0edf8" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#fee1d3" rx="2"/><use fill="#fc6d26" xlink:href="#h"/><rect width="7" height="4" x="27" y="36" fill="#6b4fbb" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#fc6d26" rx="2"/><use fill="#6b4fbb" xlink:href="#i"/><rect width="21" height="4" x="13" y="24" fill="#fee1d3" rx="2"/><rect width="21" height="4" y="36" fill="#fef0e9" rx="2"/><use fill="#6b4fbb" xlink:href="#j"/></g><g transform="translate(0 60)"><use fill="#f0edf8" xlink:href="#e"/><rect width="14" height="4" x="20" fill="#6b4fbb" rx="2"/><use fill="#e1dbf1" xlink:href="#f"/><use xlink:href="#k"/><use fill="#fee1d3" xlink:href="#g"/><use fill="#eee" xlink:href="#h"/><use fill="#6b4fbb" xlink:href="#i"/><rect width="21" height="4" x="13" y="24" fill="#fef0e9" rx="2"/><use fill="#fc6d26" xlink:href="#j"/></g><rect width="4" height="63" x="74" y="13" fill="#eee" rx="2"/></g><rect width="230" height="4" y="27" fill="#eee" rx="2"/></g><g transform="rotate(7.999 -1289.786 1797.583)"><use fill="#fff" xlink:href="#l"/><circle cx="31" cy="31" r="29" stroke="#eee" stroke-width="4"/><path fill="#fc6d26" d="M29 29h-6a2 2 0 1 0 0 4h6v6a2 2 0 1 0 4 0v-6h6a2 2 0 1 0 0-4h-6v-6a2 2 0 1 0-4 0v6"/></g></g></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="374" height="268" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><circle id="a" cx="44" cy="44" r="44"/><circle id="b" cx="31" cy="31" r="31"/><circle id="c" cx="35" cy="35" r="35"/><rect id="d" width="230" height="176" rx="10"/><circle id="e" cx="31" cy="31" r="31"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(4 98)"><circle cx="53" cy="53" r="44" fill="#F9F9F9"/><g transform="translate(6 6)"><use fill="#FFF" xlink:href="#a"/><circle cx="44" cy="44" r="42" stroke="#EEE" stroke-width="4"/><path fill="#FEE1D3" fill-rule="nonzero" d="M34.394 55.736A4 4 0 0 1 36.706 55H56a6 6 0 0 0 6-6V35a6 6 0 0 0-6-6H34a6 6 0 0 0-6 6v25.265l6.394-4.53zM36.706 59l-7.972 5.647A3 3 0 0 1 24 62.199V35c0-5.523 4.477-10 10-10h22c5.523 0 10 4.477 10 10v14c0 5.523-4.477 10-10 10H36.706z"/><path fill="#FC6D26" d="M38 40a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm7 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm7 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4z"/></g></g><g transform="translate(50 2)"><circle cx="39" cy="39" r="31" fill="#F9F9F9"/><g transform="translate(5 5)"><use fill="#FFF" xlink:href="#b"/><circle cx="31" cy="31" r="29" stroke="#EEE" stroke-width="4"/><rect width="20" height="4" x="21" y="29" fill="#6B4FBB" rx="2"/></g></g><path fill="#F9F9F9" d="M235.58 229H102c-6.627 0-12-5.373-12-12V65c0-6.627 5.373-12 12-12h206c6.627 0 12 5.373 12 12v18.399A34.842 34.842 0 0 1 337 79c19.33 0 35 15.67 35 35s-15.67 35-35 35a34.842 34.842 0 0 1-17-4.399V217c0 6.627-5.373 12-12 12h-11.58c.38 1.941.58 3.947.58 6 0 17.12-13.88 31-31 31s-31-13.88-31-31c0-2.053.2-4.059.58-6z"/><g transform="translate(87 50)"><g transform="translate(212 26)"><use fill="#FFF" xlink:href="#c"/><circle cx="35" cy="35" r="33" stroke="#EEE" stroke-width="4"/><g transform="translate(20 19)"><circle cx="15" cy="16" r="15" fill="#F4F1FA" stroke="#6B4FBB" stroke-width="3"/><path fill="#6B4FBB" d="M19.419 6.996h-.007L16.959 4l-2.454 2.997h-.006L12.045 4 9.59 6.998h-.003L7.132 4 4.676 7H2c2.605-4.204 7.23-7 12.502-7C19.771 0 24.394 2.793 27 6.994h-2.676L21.872 4l-2.453 2.996z"/><circle cx="9.5" cy="17.5" r="1.5" fill="#6B4FBB"/><circle cx="20.5" cy="17.5" r="1.5" fill="#6B4FBB"/></g></g><use fill="#FFF" xlink:href="#d"/><rect width="226" height="172" x="2" y="2" stroke="#EEE" stroke-width="4" rx="10"/><rect width="4" height="122" x="33" y="42" fill="#EEE" rx="2"/><g transform="translate(13 59)"><rect width="10" height="4" fill="#FEE1D3" rx="2"/><rect width="10" height="4" y="12" fill="#F0EDF8" rx="2"/><rect width="10" height="4" y="24" fill="#FEF0E9" rx="2"/><rect width="10" height="4" y="36" fill="#FEE1D3" rx="2"/><rect width="10" height="4" y="48" fill="#E1DBF1" rx="2"/><rect width="10" height="4" y="60" fill="#F0EDF8" rx="2"/><rect width="10" height="4" y="72" fill="#FEF0E9" rx="2"/><rect width="10" height="4" y="84" fill="#FEE1D3" rx="2"/></g><g transform="translate(55 59)"><rect width="14" height="4" fill="#6B4FBB" rx="2"/><rect width="14" height="4" x="20" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" fill="#FEF0E9" rx="2"/><rect width="14" height="4" y="12" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" y="24" fill="#FEF0E9" rx="2"/><rect width="14" height="4" y="48" fill="#E1DBF1" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#FEF0E9" rx="2"/><rect width="7" height="4" x="20" y="12" fill="#FEE1D3" rx="2"/><rect width="7" height="4" x="27" y="36" fill="#6B4FBB" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#FEE1D3" rx="2"/><rect width="7" height="4" y="24" fill="#FC6D26" rx="2"/><rect width="21" height="4" x="13" y="24" fill="#E1DBF1" rx="2"/><rect width="21" height="4" y="36" fill="#EEE" rx="2"/><rect width="7" height="4" x="33" y="12" fill="#6B4FBB" rx="2"/><g transform="translate(98)"><rect width="14" height="4" fill="#FEE1D3" rx="2"/><rect width="14" height="4" x="20" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" fill="#FC6D26" rx="2"/><rect width="14" height="4" y="12" fill="#FEF0E9" rx="2"/><rect width="14" height="4" x="40" y="24" fill="#E1DBF1" rx="2"/><rect width="14" height="4" y="48" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="40" y="36" fill="#FEE1D3" rx="2"/><rect width="7" height="4" x="20" y="12" fill="#FC6D26" rx="2"/><rect width="7" height="4" x="27" y="36" fill="#6B4FBB" rx="2"/><rect width="7" height="4" x="20" y="48" fill="#FC6D26" rx="2"/><rect width="7" height="4" y="24" fill="#6B4FBB" rx="2"/><rect width="21" height="4" x="13" y="24" fill="#FEE1D3" rx="2"/><rect width="21" height="4" y="36" fill="#FEF0E9" rx="2"/><rect width="7" height="4" x="33" y="12" fill="#6B4FBB" rx="2"/></g><g transform="translate(0 60)"><rect width="14" height="4" fill="#F0EDF8" rx="2"/><rect width="14" height="4" x="20" fill="#6B4FBB" rx="2"/><rect width="14" height="4" x="40" fill="#E1DBF1" rx="2"/><rect width="14" height="4" y="12" fill="#FEF0E9" rx="2"/><rect width="14" height="4" x="40" y="24" fill="#FEE1D3" rx="2"/><rect width="7" height="4" x="20" y="12" fill="#EEE" rx="2"/><rect width="7" height="4" y="24" fill="#6B4FBB" rx="2"/><rect width="21" height="4" x="13" y="24" fill="#FEF0E9" rx="2"/><rect width="7" height="4" x="33" y="12" fill="#FC6D26" rx="2"/></g><rect width="4" height="63" x="74" y="13" fill="#EEE" rx="2"/></g><rect width="230" height="4" y="27" fill="#EEE" rx="2"/></g><g transform="translate(233 201)"><use fill="#FFF" xlink:href="#e"/><circle cx="31" cy="31" r="29" stroke="#EEE" stroke-width="4"/><path fill="#FC6D26" d="M29 29v-6a2 2 0 1 1 4 0v6h6a2 2 0 1 1 0 4h-6v6a2 2 0 1 1-4 0v-6h-6a2 2 0 1 1 0-4h6z"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/multi_file_editor_empty.svg b/app/assets/images/illustrations/multi_file_editor_empty.svg new file mode 100644 index 00000000000..bd376f0a050 --- /dev/null +++ b/app/assets/images/illustrations/multi_file_editor_empty.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="430" height="300"><g fill="none" fill-rule="evenodd" transform="translate(35 29)"><path fill="#EEE" fill-rule="nonzero" d="M90 23a2 2 0 1 1 0-4h10a2 2 0 0 1 0 4H90zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h10a2 2 0 0 1 0 4h-10zm20 0a2 2 0 0 1 0-4h1a11.98 11.98 0 0 1 9.457 4.612 2 2 0 0 1-3.151 2.464A7.981 7.981 0 0 0 331 23h-1zm9 11.39a2 2 0 0 1 4 0v10a2 2 0 0 1-4 0v-10zm0 180a2 2 0 1 1 4 0V223c0 .56-.038 1.114-.114 1.662a2 2 0 0 1-3.962-.55A8.21 8.21 0 0 0 339 223v-8.61zm-4.769 15.931a2 2 0 0 1 1.618 3.658A11.967 11.967 0 0 1 331 235h-5.782a2 2 0 0 1 0-4H331c1.13 0 2.224-.233 3.231-.679zm-19.013.679a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zm-20 0a2 2 0 1 1 0 4h-10a2 2 0 0 1 0-4h10zM115 231a2 2 0 0 1 0 4h-10a2 2 0 0 1 0-4h10zm-26.2 4c.131-.646.2-1.315.2-2v-2h4a2 2 0 0 1 0 4h-4.2z"/><path fill="#EEE" fill-rule="nonzero" d="M103 211h258a6 6 0 0 0 6-6V63a6 6 0 0 0-6-6H166a5 5 0 0 1-5-5v-8.5a5.5 5.5 0 0 0-5.5-5.5H109a6 6 0 0 0-6 6v167zm62-167.5V52a1 1 0 0 0 1 1h195c5.523 0 10 4.477 10 10v142c0 5.523-4.477 10-10 10H99V44c0-5.523 4.477-10 10-10h46.5a9.5 9.5 0 0 1 9.5 9.5z"/><rect width="40" height="4" x="118" y="78" fill="#6B4FBB" rx="2"/><rect width="30" height="4" x="118" y="90" fill="#EFEDF8" rx="2"/><rect width="30" height="4" x="153" y="90" fill="#E1DBF1" rx="2"/><rect width="150" height="4" x="118" y="102" fill="#EFEDF8" rx="2"/><rect width="90" height="4" x="118" y="114" fill="#E1DBF1" rx="2"/><rect width="60" height="4" x="118" y="138" fill="#EFEDF8" rx="2"/><rect width="20" height="4" x="118" y="150" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="144" y="150" fill="#C3B8E3" rx="2"/><rect width="20" height="4" x="170" y="150" fill="#E1DBF1" rx="2"/><rect width="130" height="4" x="118" y="162" fill="#EFEDF8" rx="2"/><rect width="30" height="4" x="118" y="174" fill="#C3B8E3" rx="2"/><rect width="30" height="4" x="154" y="174" fill="#EFEDF8" rx="2"/><rect width="30" height="4" x="190" y="174" fill="#EFEDF8" rx="2"/><rect width="40" height="4" x="118" y="186" fill="#E1DBF1" rx="2"/><path fill="#F9F9F9" d="M89 24.292l11.434 19.326v170.326L89 226.336V24.292z"/><path fill="#EEE" fill-rule="nonzero" d="M89 229.286v-5.9l9.434-10.223V44.165L89 28.22v-7.856l13.434 22.707v171.655L89 229.286zM10 4a6 6 0 0 0-6 6v223a6 6 0 0 0 6 6h69a6 6 0 0 0 6-6V10a6 6 0 0 0-6-6H10zm0-4h69c5.523 0 10 4.477 10 10v223c0 5.523-4.477 10-10 10H10c-5.523 0-10-4.477-10-10V10C0 4.477 4.477 0 10 0z"/><circle cx="25" cy="23" r="11" fill="#FEF0E8"/><path fill="#FEE1D3" d="M46 17h16a2 2 0 1 1 0 4H46a2 2 0 1 1 0-4zm0 8h27a2 2 0 1 1 0 4H46a2 2 0 1 1 0-4z"/><path fill="#EEE" d="M16 50h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2zm14 2h24a2 2 0 1 1 0 4H30a2 2 0 1 1 0-4zm-4 12h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2zm14 2h24a2 2 0 1 1 0 4H40a2 2 0 1 1 0-4zM26 78h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2zm14 2h24a2 2 0 1 1 0 4H40a2 2 0 1 1 0-4z"/><g transform="translate(14 110)"><rect width="8" height="8" fill="#FEE1D3" rx="2"/><rect width="28" height="4" x="14" y="2" fill="#FEF0E8" rx="2"/></g><path fill="#EEE" d="M16 140h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2zm14 2h24a2 2 0 1 1 0 4H30a2 2 0 1 1 0-4zm-14 14h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2zm14 2h24a2 2 0 1 1 0 4H30a2 2 0 1 1 0-4zm-14 14h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2zm14 2h24a2 2 0 1 1 0 4H30a2 2 0 1 1 0-4zm-14 14h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2zm14 2h24a2 2 0 1 1 0 4H30a2 2 0 1 1 0-4z"/><g transform="translate(24 124)"><rect width="8" height="8" fill="#FEE1D3" rx="2"/><rect width="28" height="4" x="14" y="2" fill="#FEF0E8" rx="2"/></g><g fill="#FC6D26" transform="translate(24 92)"><rect width="8" height="8" rx="2"/><rect width="28" height="4" x="14" y="2" rx="2"/></g><path fill="#FDC4A8" fill-rule="nonzero" d="M152 50.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9zm0-3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/service_desk_callout.svg b/app/assets/images/illustrations/service_desk_callout.svg new file mode 100644 index 00000000000..2886388279e --- /dev/null +++ b/app/assets/images/illustrations/service_desk_callout.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><rect width="7" height="1" x="59" y="38" fill="#E1DBF2" rx=".5"/><path fill="#6B4FBB" d="M60.5 42a3.5 3.5 0 0 0 0-7v7z"/><rect width="7" height="1" x="12" y="38" fill="#E1DBF2" transform="matrix(-1 0 0 1 31 0)" rx=".5"/><path fill="#6B4FBB" d="M17.5 42a3.5 3.5 0 0 1 0-7v7z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M39 58c10.493 0 19-8.507 19-19s-8.507-19-19-19-19 8.507-19 19 8.507 19 19 19zm0 4c-12.703 0-23-10.297-23-23s10.297-23 23-23 23 10.297 23 23-10.297 23-23 23z"/><path fill="#6B4FBB" d="M35 56a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M26.5 40c0 4.143 3.355 7.5 7.494 7.5h10.012A7.497 7.497 0 0 0 51.5 40c0-4.143-3.355-7.5-7.494-7.5H33.994A7.497 7.497 0 0 0 26.5 40zm-3 0c0-5.799 4.698-10.5 10.494-10.5h10.012C49.802 29.5 54.5 34.2 54.5 40c0 5.799-4.698 10.5-10.494 10.5H33.994C28.198 50.5 23.5 45.8 23.5 40z"/><path fill="#6B4FBB" fill-rule="nonzero" d="M35.255 42.406a1 1 0 1 1 1.872-.703 2.001 2.001 0 0 0 3.76-.038 1 1 0 1 1 1.886.665 4 4 0 0 1-7.518.076zM31.5 40a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm15 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/><path fill="#6B4FBB" d="M38 22h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2zm0 3h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2z" style="mix-blend-mode:multiply"/></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/service_desk_empty.svg b/app/assets/images/illustrations/service_desk_empty.svg new file mode 100644 index 00000000000..daaaeae6a17 --- /dev/null +++ b/app/assets/images/illustrations/service_desk_empty.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="226" height="178" viewBox="0 0 226 178"><g fill="none" fill-rule="evenodd"><path fill="#EEE" fill-rule="nonzero" d="M109.496 165.895a78.17 78.17 0 0 0 6.158.08 2 2 0 0 0-.11-4c-1.94.053-3.886.028-5.84-.074a2 2 0 0 0-2.1 1.893 1.996 1.996 0 0 0 1.89 2.102zm18.408-1.245a76 76 0 0 0 6-1.4 2 2 0 1 0-1.064-3.856c-1.875.52-3.772.96-5.686 1.327a2.001 2.001 0 0 0 .75 3.93zm17.572-5.636a76.28 76.28 0 0 0 5.486-2.803 2 2 0 1 0-1.962-3.485 72.42 72.42 0 0 1-5.2 2.656 2.003 2.003 0 0 0 1.676 3.635zm44.342-74.897a75.786 75.786 0 0 0-.674-6.127 2.002 2.002 0 0 0-3.956.598c.29 1.92.505 3.857.64 5.805a1.998 1.998 0 0 0 2.133 1.857 2 2 0 0 0 1.858-2.133zm-3.505-18.144a76.141 76.141 0 0 0-2.13-5.78 2.001 2.001 0 0 0-3.695 1.534 72.381 72.381 0 0 1 2.02 5.476 1.999 1.999 0 1 0 3.805-1.229zm-7.754-16.73a77.053 77.053 0 0 0-3.454-5.1 1.998 1.998 0 0 0-2.797-.423 1.998 1.998 0 0 0-.424 2.796 73.06 73.06 0 0 1 3.273 4.835c.58.94 1.814 1.23 2.753.647a2.001 2.001 0 0 0 .646-2.754zm-11.582-14.446a76.37 76.37 0 0 0-4.572-4.128 1.999 1.999 0 1 0-2.559 3.073 72.633 72.633 0 0 1 4.334 3.913 2.001 2.001 0 1 0 2.798-2.86zm-101.422-4.91a77.634 77.634 0 0 0-4.64 4.05 2.001 2.001 0 0 0 2.749 2.906 72.611 72.611 0 0 1 4.4-3.84 2 2 0 1 0-2.509-3.115zM52.7 43.062a75.962 75.962 0 0 0-3.546 5.04 2 2 0 1 0 3.363 2.168 72.314 72.314 0 0 1 3.36-4.777 2 2 0 0 0-3.177-2.432zm-9.373 15.924c-.82 1.882-1.56 3.8-2.226 5.745a2 2 0 1 0 3.787 1.294 72.253 72.253 0 0 1 2.108-5.443 1.998 1.998 0 0 0-1.036-2.63 2.001 2.001 0 0 0-2.633 1.036zm-5.26 17.74a76.33 76.33 0 0 0-.777 6.11 2 2 0 0 0 3.985.347c.17-1.947.415-3.88.737-5.793a2 2 0 0 0-3.945-.664zM74.87 155.55a76.028 76.028 0 0 0 5.437 2.897 2 2 0 1 0 1.737-3.603 71.34 71.34 0 0 1-5.152-2.745 1.998 1.998 0 0 0-2.737.714 2.002 2.002 0 0 0 .715 2.738zm16.97 7.34a76.606 76.606 0 0 0 5.975 1.498 2 2 0 1 0 .816-3.916 72.52 72.52 0 0 1-5.662-1.42 1.999 1.999 0 1 0-1.129 3.837z"/><path fill="#F9F9F9" d="M2.12 130c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3-1.527 19.032-17.455 34-36.88 34-19.425 0-35.353-14.968-36.88-34z"/><path fill="#EEE" fill-rule="nonzero" d="M39 166c-21.54 0-39-17.46-39-39s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 92 39 92 4 107.67 4 127s15.67 35 35 35z"/><path fill="#FDC4A8" fill-rule="nonzero" d="M53.925 116.226A1.995 1.995 0 0 0 53 116H25a1.99 1.99 0 0 0-.898.212l14.663 13.406c.39.357.99.348 1.37-.02l13.79-13.372zm1.075 4.53L42.92 132.47a5 5 0 0 1-6.854.1L23 120.624V138a2 2 0 0 0 2 2h28a2 2 0 0 0 2-2v-17.244zM25 112h28a6 6 0 0 1 6 6v20a6 6 0 0 1-6 6H25a6 6 0 0 1-6-6v-20a6 6 0 0 1 6-6z"/><path fill="#F9F9F9" d="M150.12 131c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3-1.527 19.032-17.455 34-36.88 34-19.425 0-35.353-14.968-36.88-34z"/><path fill="#EEE" fill-rule="nonzero" d="M187 167c-21.54 0-39-17.46-39-39s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35s-15.67-35-35-35-35 15.67-35 35 15.67 35 35 35z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M180.51 137H199a2 2 0 0 0 2-2v-16a2 2 0 0 0-2-2h-24a2 2 0 0 0-2 2v22.743l7.51-4.743zm1.157 4l-9.6 6.062a2 2 0 0 1-3.067-1.69V119a6 6 0 0 1 6-6h24a6 6 0 0 1 6 6v16a6 6 0 0 1-6 6h-17.333z"/><path fill="#6B4FBB" d="M180 129a2 2 0 1 1-.001-3.999A2 2 0 0 1 180 129zm7 0a2 2 0 1 1-.001-3.999A2 2 0 0 1 187 129zm7 0a2 2 0 1 1-.001-3.999A2 2 0 0 1 194 129z"/><g><path fill="#F9F9F9" d="M76.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3-1.527 19.032-17.455 34-36.88 34-19.425 0-35.353-14.968-36.88-34z"/><path fill="#EEE" fill-rule="nonzero" d="M113 78c-21.54 0-39-17.46-39-39S91.46 0 113 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S132.33 4 113 4 78 19.67 78 39s15.67 35 35 35z"/><g transform="translate(133 35)"><rect width="7" height="1" y="3" fill="#E1DBF2" rx=".5"/><path fill="#6B4FBB" d="M1.5 7a3.5 3.5 0 1 0 0-7v7z"/></g><g transform="matrix(-1 0 0 1 93 35)"><rect width="7" height="1" y="3" fill="#E1DBF2" rx=".5"/><path fill="#6B4FBB" d="M1.5 7a3.5 3.5 0 1 0 0-7v7z"/></g><path fill="#E1DBF1" fill-rule="nonzero" d="M113 58c10.493 0 19-8.507 19-19s-8.507-19-19-19-19 8.507-19 19 8.507 19 19 19zm0 4c-12.703 0-23-10.297-23-23s10.297-23 23-23 23 10.297 23 23-10.297 23-23 23z"/><path fill="#6B4FBB" d="M109 56a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M97.5 40c0-5.8 4.698-10.5 10.494-10.5h10.012c5.796 0 10.494 4.7 10.494 10.5s-4.698 10.5-10.494 10.5h-10.012C102.198 50.5 97.5 45.8 97.5 40zm3 0c0 4.143 3.355 7.5 7.494 7.5h10.012A7.496 7.496 0 0 0 125.5 40c0-4.143-3.355-7.5-7.494-7.5h-10.012A7.496 7.496 0 0 0 100.5 40z"/><path fill="#6B4FBB" fill-rule="nonzero" d="M109.255 42.406a.998.998 0 0 1 .584-1.287.997.997 0 0 1 1.287.583 2 2 0 0 0 3.76-.038 1 1 0 0 1 1.886.665 4.001 4.001 0 0 1-7.518.076zM105.5 40a1.5 1.5 0 1 1 .001-3.001A1.5 1.5 0 0 1 105.5 40zm15 0a1.5 1.5 0 1 1 .001-3.001A1.5 1.5 0 0 1 120.5 40z"/><path fill="#6B4FBB" d="M112 22h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2zm0 3h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2z" style="mix-blend-mode:multiply"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/wiki_login_empty.svg b/app/assets/images/illustrations/wiki_login_empty.svg new file mode 100644 index 00000000000..1cfa47220a5 --- /dev/null +++ b/app/assets/images/illustrations/wiki_login_empty.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="386" height="298" viewBox="0 0 386 298" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M4 51h16v15.997A5.003 5.003 0 0 1 15.003 72H8.997A5.005 5.005 0 0 1 4 66.997V51z"/><rect id="b" width="24" height="10" y="44" rx="3"/></defs><g fill="none" fill-rule="evenodd" transform="translate(0 3)"><g transform="rotate(15 23.151 968.24)"><rect width="53" height="44" fill="#FFF" stroke="#FDE5D8" stroke-width="3" stroke-linecap="round" rx="5"/><path fill="#FDE5D8" d="M29.5 28.3l2.758-3.861c.962-1.347 2.527-1.34 3.484 0l6.516 9.122c.962 1.347.399 2.439-1.252 2.439H17.994c-1.653 0-2.21-1.099-1.252-2.439l6.516-9.122c.962-1.347 2.527-1.34 3.484 0L29.5 28.3z" opacity=".6"/><circle cx="16" cy="16" r="6" fill="#FDB997"/></g><g transform="scale(-1 1) rotate(25 -75.08 -334.15)"><rect width="3" height="11" x="12.45" y="23.45" fill="#6B4FBB" transform="rotate(45 13.95 28.95)" rx="1.5"/><rect width="3" height="14" x="9.45" y="15.45" fill="#6B4FBB" transform="rotate(45 10.95 22.45)" rx="1.5"/><path fill="#FFF" stroke="#E1DCF1" stroke-width="3" d="M16 39.6C6.871 37.747 0 29.676 0 20 0 8.954 8.954 0 20 0s20 8.954 20 20c0 8.955-5.886 16.536-14 19.084v15.91A5.007 5.007 0 0 1 21 60c-2.761 0-5-2.244-5-5.006V39.6zm4-7.6c6.627 0 12-5.373 12-12S26.627 8 20 8 8 13.373 8 20s5.373 12 12 12z"/></g><g transform="scale(1 -1) rotate(-15 -383.616 -172.407)"><path stroke="#FDE5D8" stroke-width="3" d="M1.5 38.5h9V4c0-1.378-1.12-2.5-2.496-2.5H3.996A2.503 2.503 0 0 0 1.5 4v34.5z"/><rect width="2" height="27" x="5" y="7" fill="#FDA77D" opacity=".8" rx="1"/><path stroke="#FDE5D8" stroke-width="3" d="M2.427 41.553h7.146L6 48.699l-3.573-7.146z"/></g><g transform="rotate(-30 420.145 -545.422)"><path fill="#FFF" stroke="#FDE5D8" stroke-width="3" d="M9 3c0-1.657 1.347-3 3-3 1.657 0 3 1.352 3 3v43H9V3z"/><use fill="#FFF" xlink:href="#a"/><path stroke="#FDE5D8" stroke-width="3" d="M5.5 52.5v14.497A3.505 3.505 0 0 0 8.997 70.5h6.006a3.503 3.503 0 0 0 3.497-3.503V52.5h-13z"/><rect width="2" height="14" x="9" y="51" fill="#FDA77D" rx="1"/><rect width="2" height="14" x="13" y="51" fill="#FDA77D" rx="1"/><use fill="#FFF" xlink:href="#b"/><rect width="21" height="7" x="1.5" y="45.5" stroke="#FDE5D8" stroke-width="3" rx="3"/></g><g transform="translate(72 97.488)"><rect width="125" height="160" fill="#FFF" stroke="#E5E5E5" stroke-width="4" stroke-linecap="round" rx="10"/><rect width="125" height="160" x="125" fill="#FFF" stroke="#E5E5E5" stroke-width="4" stroke-linecap="round" rx="10"/><path fill="#FFF" stroke="#E5E5E5" stroke-width="4" d="M7 12.008C7 8.69 9.686 6 12.993 6H125v148H12.993C9.683 154 7 151.305 7 147.992V12.008zm236 0C243 8.69 240.314 6 237.007 6H125v148h112.007c3.31 0 5.993-2.695 5.993-6.008V12.008z" stroke-linecap="round"/><rect width="84" height="42" x="142" y="29" stroke="#EEE" stroke-width="4" rx="3"/><rect width="88" height="4" x="141" y="93" fill="#E5E5E5" rx="2"/><rect width="88" height="4" x="141" y="107" fill="#BFBFBF" rx="2"/><rect width="56" height="4" x="141" y="121" fill="#E5E5E5" rx="2"/><rect width="56" height="4" x="22" y="93" fill="#E5E5E5" rx="2"/><rect width="26" height="4" x="22" y="27" fill="#BFBFBF" rx="2"/><rect width="56" height="4" x="22" y="41" fill="#E5E5E5" rx="2"/><rect width="36" height="4" x="22" y="55" fill="#BFBFBF" rx="2"/><rect width="56" height="4" x="22" y="69" fill="#E5E5E5" rx="2"/><rect width="36" height="4" x="22" y="107" fill="#E5E5E5" rx="2"/><rect width="56" height="4" x="22" y="121" fill="#BFBFBF" rx="2"/></g><path stroke="#B5A7DD" stroke-width="2.5" d="M23.139 182.922l-1.347-.6a2.004 2.004 0 0 1-1.02-2.64l.815-1.831a1.995 1.995 0 0 1 2.645-1.01l1.308.583a9.959 9.959 0 0 1 2.177-1.876l-.376-1.402a2.004 2.004 0 0 1 1.41-2.455l1.937-.519a1.995 1.995 0 0 1 2.449 1.421l.375 1.402a9.959 9.959 0 0 1 2.824.536l.84-1.158a2.004 2.004 0 0 1 2.796-.448l1.622 1.178a1.995 1.995 0 0 1 .437 2.797l-.867 1.193a9.946 9.946 0 0 1 1.341 2.541l1.461-.05a2.004 2.004 0 0 1 2.075 1.926l.07 2.003a1.995 1.995 0 0 1-1.935 2.067l-1.445.05c-.256.93-.644 1.817-1.15 2.632l.944 1.087a2.004 2.004 0 0 1-.191 2.825l-1.513 1.315a1.995 1.995 0 0 1-2.824-.204l-.963-1.108a10.084 10.084 0 0 1-2.776.744l-.28 1.441a2.004 2.004 0 0 1-2.344 1.588l-1.967-.382a1.995 1.995 0 0 1-1.579-2.35l.275-1.414a10.044 10.044 0 0 1-2.312-1.704l-1.277.678a2.004 2.004 0 0 1-2.709-.822l-.94-1.77a1.995 1.995 0 0 1 .833-2.705l1.29-.687a9.946 9.946 0 0 1-.11-2.872zm10.98 4.93a4 4 0 1 0-2.07-7.727 4 4 0 0 0 2.07 7.728z"/><ellipse cx="197" cy="289.988" fill="#F9F9F9" rx="125" ry="4.5"/><path fill="#6B4FBB" d="M164 100.492a3.002 3.002 0 0 1 3.001-3.004H183a3.006 3.006 0 0 1 3.001 3.004v34.988c0 2.213-1.45 2.954-3.24 1.651l-7.76-5.643-7.76 5.643c-1.789 1.302-3.24.566-3.24-1.651v-34.988z"/><g opacity=".2"><path fill="#FC8A51" d="M5.747 234.768l-2.688 1.114c-1.017.422-1.803-.134-1.754-1.228l.128-2.907-1.115-2.688c-.422-1.017.135-1.803 1.229-1.754l2.907.128 2.687-1.115c1.018-.422 1.803.135 1.755 1.229l-.128 2.907 1.114 2.687c.422 1.018-.134 1.803-1.228 1.755l-2.907-.128zM191.564 37.953l-3.72.164c-1.326.059-1.992-.88-1.48-2.115l1.426-3.438-.164-3.72c-.059-1.326.88-1.992 2.115-1.48l3.438 1.426 3.72-.164c1.326-.059 1.992.88 1.48 2.114l-1.426 3.44.164 3.719c.059 1.326-.88 1.992-2.114 1.48l-3.44-1.426z"/><path fill="#6B4FBB" d="M348.789 75.876l-1.967-2.144c-.744-.812-.49-1.74.555-2.07l2.775-.873 2.144-1.967c.812-.744 1.74-.49 2.07.555l.873 2.775 1.967 2.144c.744.812.49 1.74-.555 2.07l-2.775.873-2.144 1.967c-.812.745-1.74.49-2.07-.555l-.873-2.775zm9.261 164.735l-2.907-.125c-1.1-.048-1.577-.884-1.07-1.855l1.344-2.58.126-2.908c.047-1.1.883-1.577 1.855-1.07l2.58 1.344 2.907.126c1.1.047 1.577.883 1.07 1.855l-1.344 2.58-.125 2.907c-.048 1.1-.884 1.577-1.856 1.07l-2.58-1.344zM88.789 75.876l-1.967-2.144c-.744-.812-.49-1.74.555-2.07l2.775-.873 2.144-1.967c.812-.744 1.74-.49 2.07.555l.873 2.775 1.967 2.144c.744.812.49 1.74-.555 2.07l-2.775.873-2.144 1.967c-.812.745-1.74.49-2.07-.555l-.873-2.775z"/></g></g></svg>
\ No newline at end of file diff --git a/app/assets/images/illustrations/wiki_logout_empty.svg b/app/assets/images/illustrations/wiki_logout_empty.svg new file mode 100644 index 00000000000..c71841f72e5 --- /dev/null +++ b/app/assets/images/illustrations/wiki_logout_empty.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="412" height="260" viewBox="0 0 412 260" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M6.447.894L12 12H0L5.553.894a.5.5 0 0 1 .894 0z"/></defs><g fill="none" fill-rule="evenodd"><path fill="#FEF0E8" fill-rule="nonzero" d="M338 50.287C322.695 41.45 303.124 46.694 294.287 62c-8.836 15.305-3.592 34.876 11.713 43.712 15.306 8.837 34.877 3.593 43.713-11.712 8.837-15.306 3.593-34.877-11.713-43.713zm2-3.464C357.22 56.763 363.118 78.78 353.177 96c-9.941 17.218-31.958 23.118-49.177 13.176-17.218-9.94-23.118-31.958-13.177-49.176C300.764 42.78 322.782 36.88 340 46.823z"/><g transform="rotate(-150 171.003 8.53)"><path fill="#FC6D26" fill-rule="nonzero" d="M4 16v25a2 2 0 1 0 4 0V16H4zm8-4v29a6 6 0 1 1-12 0V12h12z"/><use fill="#D8D8D8" xlink:href="#a"/><path stroke="#FDC4A8" stroke-width="4" d="M6 4.472L3.236 10h5.528L6 4.472z"/><path fill="#FC6D26" d="M9 6L6.447.894a.5.5 0 0 0-.894 0L3 6c.836.628 1.874 1 3 1a4.978 4.978 0 0 0 3-1z"/></g><path fill="#F9F9F9" d="M263.116 237.116A10.002 10.002 0 0 1 254 243h-86c-11.046 0-20-8.954-20-20V121c0-4.056 2.414-7.547 5.884-9.116A9.964 9.964 0 0 0 153 116v106c0 8.837 7.163 16 16 16h90c1.467 0 2.86-.316 4.116-.884z"/><path fill="#EEE" fill-rule="nonzero" d="M214.5 106H163c-5.523 0-10 4.477-10 10v106c0 8.837 7.163 16 16 16h90c5.523 0 10-4.477 10-10v-17.999a10.036 10.036 0 0 1-4 3.167V228a6 6 0 0 1-6 6h-90c-6.627 0-12-5.373-12-12V116a6 6 0 0 1 6-6h7v-4h44.5z"/><path fill="#EEE" fill-rule="nonzero" d="M260 218.268V214h-90a6 6 0 0 0 0 12h86a4 4 0 0 0 4-4v-.268a1.99 1.99 0 0 1-1 .268h-50a2 2 0 0 1 0-4h50c.364 0 .706.097 1 .268zM170 210h90.5a3.5 3.5 0 0 1 3.5 3.5v8.5a8 8 0 0 1-8 8h-86c-5.523 0-10-4.477-10-10s4.477-10 10-10z"/><path fill="#EEE" fill-rule="nonzero" d="M174 110v100h87a6 6 0 0 0 6-6v-88a6 6 0 0 0-6-6h-87zm-4-4h91c5.523 0 10 4.477 10 10v88c0 5.523-4.477 10-10 10h-91V106z"/><path fill="#EFEDF8" d="M230 99h18a6 6 0 0 1 6 6v31.35a3 3 0 0 1-4.68 2.484l-9.277-6.274a1.5 1.5 0 0 0-1.664-.01l-9.731 6.395a3 3 0 0 1-4.648-2.507V105a6 6 0 0 1 6-6z"/><path fill="#C3B8E3" fill-rule="nonzero" d="M236.182 129.207a5.5 5.5 0 0 1 6.102.04l7.716 5.219V105a2 2 0 0 0-2-2h-18a2 2 0 0 0-2 2v29.584l8.182-5.377zM230 99h18a6 6 0 0 1 6 6v31.35a3 3 0 0 1-4.68 2.484l-9.277-6.274a1.5 1.5 0 0 0-1.664-.01l-9.731 6.395a3 3 0 0 1-4.648-2.507V105a6 6 0 0 1 6-6z"/><g fill-rule="nonzero"><path fill="#EFEDF8" d="M156 74c14.912 0 27-12.088 27-27s-12.088-27-27-27-27 12.088-27 27 12.088 27 27 27zm0 4c-17.12 0-31-13.88-31-31s13.88-31 31-31 31 13.88 31 31-13.88 31-31 31z"/><path fill="#6B4FBB" d="M147.535 44.916l-.116 1.086a8.446 8.446 0 0 0 .093 2.44l.2 1.08-2.262 1.202a.495.495 0 0 0-.213.678l.941 1.77c.128.239.434.332.68.201l2.25-1.196.785.775a8.544 8.544 0 0 0 1.967 1.45l.975.522-.486 2.5a.495.495 0 0 0 .392.59l1.968.383a.504.504 0 0 0 .585-.401l.489-2.515 1.086-.13a8.584 8.584 0 0 0 2.363-.633l1.005-.43 1.68 1.933a.495.495 0 0 0 .708.055l1.513-1.315a.504.504 0 0 0 .044-.708l-1.67-1.922.583-.94c.431-.696.761-1.45.978-2.239l.292-1.063 2.547-.089a.495.495 0 0 0 .488-.515l-.07-2.003a.504.504 0 0 0-.523-.48l-2.56.09-.367-1.037a8.446 8.446 0 0 0-1.139-2.159l-.644-.882 1.509-2.076a.495.495 0 0 0-.106-.702l-1.621-1.178a.504.504 0 0 0-.7.116l-1.494 2.057-1.05-.362a8.459 8.459 0 0 0-2.398-.455l-1.1-.047-.66-2.466a.495.495 0 0 0-.613-.36l-1.936.519a.504.504 0 0 0-.35.617l.661 2.466-.93.59a8.459 8.459 0 0 0-1.848 1.594l-.728.838-2.322-1.034a.495.495 0 0 0-.665.25l-.815 1.83a.504.504 0 0 0 .26.661l2.344 1.044zm-3.565 1.697a3.504 3.504 0 0 1-1.78-4.622l.815-1.83a3.495 3.495 0 0 1 4.626-1.77l.346.154c.259-.245.529-.477.81-.697l-.106-.394a3.504 3.504 0 0 1 2.471-4.292l1.936-.519a3.495 3.495 0 0 1 4.286 2.481l.106.395c.353.05.703.116 1.05.198l.222-.306a3.504 3.504 0 0 1 4.89-.78l1.622 1.178a3.495 3.495 0 0 1 .769 4.892l-.258.355c.184.312.354.633.508.962l.42-.014a3.504 3.504 0 0 1 3.625 3.373l.07 2.003a3.495 3.495 0 0 1-3.382 3.618l-.4.014c-.127.332-.27.659-.426.978l.256.294a3.504 3.504 0 0 1-.34 4.941l-1.512 1.315a3.495 3.495 0 0 1-4.94-.351l-.283-.325a11.669 11.669 0 0 1-1.05.28l-.082.424a3.504 3.504 0 0 1-4.103 2.774l-1.967-.382a3.495 3.495 0 0 1-2.765-4.11l.075-.383a11.547 11.547 0 0 1-.858-.633l-.354.188a3.504 3.504 0 0 1-4.738-1.442l-.94-1.77a3.495 3.495 0 0 1 1.453-4.734l.37-.197a11.436 11.436 0 0 1-.041-1.088l-.4-.178zm13.326 5.608a5.5 5.5 0 1 1-2.847-10.625 5.5 5.5 0 0 1 2.847 10.625zm-.776-2.898a2.5 2.5 0 1 0-1.294-4.83 2.5 2.5 0 0 0 1.294 4.83z"/></g><g fill-rule="nonzero"><path fill="#EFEDF8" d="M326.979 222.047c14.403 3.86 29.209-4.688 33.068-19.092 3.86-14.403-4.688-29.209-19.092-33.068-14.403-3.86-29.209 4.688-33.068 19.092-3.86 14.404 4.688 29.209 19.092 33.068zm-1.035 3.864c-16.538-4.431-26.352-21.43-21.92-37.967 4.43-16.538 21.429-26.352 37.966-21.92 16.538 4.43 26.352 21.429 21.92 37.966-4.43 16.538-21.429 26.352-37.966 21.92z"/><path fill="#6B4FBB" d="M329.376 201.598c-4.668-2.621-7.155-8.157-5.706-13.566 1.715-6.402 8.295-10.201 14.697-8.486 6.402 1.716 10.2 8.296 8.485 14.697-1.45 5.41-6.371 8.96-11.725 8.897a3.03 3.03 0 0 1-.074.365l-1.812 6.761a3 3 0 0 1-5.795-1.552l1.812-6.762a3.03 3.03 0 0 1 .118-.354zm3.815-2.733a8 8 0 1 0 4.14-15.455 8 8 0 0 0-4.14 15.455z"/></g><path fill="#FEF0E8" fill-rule="nonzero" d="M91.373 193c17.071-4.574 27.202-22.12 22.628-39.191-4.575-17.071-22.121-27.202-39.192-22.628-17.071 4.574-27.202 22.121-22.628 39.192 4.574 17.071 22.121 27.202 39.192 22.627zm1.035 3.864c-19.204 5.146-38.945-6.25-44.09-25.456-5.146-19.204 6.25-38.945 25.455-44.09 19.205-5.146 38.945 6.25 44.091 25.455 5.146 19.205-6.25 38.945-25.456 44.091z"/><path fill="#FDC4A8" fill-rule="nonzero" d="M70.067 152.122l6.73 25.114 19.318-5.176-6.73-25.114-19.318 5.176zm-1.035-3.864l19.318-5.176a4 4 0 0 1 4.9 2.828l6.729 25.114a4 4 0 0 1-2.829 4.9L77.832 181.1a4 4 0 0 1-4.9-2.829l-6.729-25.114a4 4 0 0 1 2.829-4.899z"/><path fill="#FC6D26" d="M76.898 154.433l7.727-2.07a2 2 0 0 1 1.036 3.863l-7.728 2.07a2 2 0 1 1-1.035-3.863zm1.812 6.761l5.795-1.553a2 2 0 0 1 1.035 3.864l-5.795 1.553a2 2 0 1 1-1.035-3.864zm1.811 6.762l7.728-2.07a2 2 0 0 1 1.035 3.863l-7.727 2.07a2 2 0 1 1-1.036-3.863z"/></g></svg>
\ No newline at end of file diff --git a/app/assets/images/multi-editor-off.png b/app/assets/images/multi-editor-off.png Binary files differnew file mode 100644 index 00000000000..82a6127f853 --- /dev/null +++ b/app/assets/images/multi-editor-off.png diff --git a/app/assets/images/multi-editor-on.png b/app/assets/images/multi-editor-on.png Binary files differnew file mode 100644 index 00000000000..2bcd29abf13 --- /dev/null +++ b/app/assets/images/multi-editor-on.png diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 5d060165f4b..6a0662ba903 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -1,9 +1,10 @@ /* eslint-disable no-param-reassign, class-methods-use-this */ -/* global Pager */ import Cookies from 'js-cookie'; +import Pager from './pager'; +import { localTimeAgo } from './lib/utils/datetime_utility'; -class Activities { +export default class Activities { constructor() { Pager.init(20, true, false, data => data, this.updateTooltips); @@ -15,7 +16,7 @@ class Activities { } updateTooltips() { - gl.utils.localTimeAgo($('.js-timeago', '.content_list')); + localTimeAgo($('.js-timeago', '.content_list')); } reloadActivities() { @@ -33,6 +34,3 @@ class Activities { $sender.closest('li').toggleClass('active'); } } - -window.gl = window.gl || {}; -window.gl.Activities = Activities; diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 34669dd13d6..c1f7fa2aced 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -1,62 +1,59 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, max-len */ - -window.Admin = (function() { - function Admin() { - var modal, showBlacklistType; - $('input#user_force_random_password').on('change', function(elem) { - var elems; - elems = $('#user_password, #user_password_confirmation'); - if ($(this).attr('checked')) { - return elems.val('').attr('disabled', true); - } else { - return elems.removeAttr('disabled'); - } - }); - $('body').on('click', '.js-toggle-colors-link', function(e) { - e.preventDefault(); - return $('.js-toggle-colors-container').toggle(); - }); - $('.log-tabs a').click(function(e) { - e.preventDefault(); - return $(this).tab('show'); - }); - $('.log-bottom').click(function(e) { - var visible_log; - e.preventDefault(); - visible_log = $(".file-content:visible"); - return visible_log.animate({ - scrollTop: visible_log.find('ol').height() - }, "fast"); - }); - modal = $('.change-owner-holder'); - $('.change-owner-link').bind("click", function(e) { - e.preventDefault(); - $(this).hide(); - return modal.show(); - }); - $('.change-owner-cancel-link').bind("click", function(e) { - e.preventDefault(); - modal.hide(); - return $('.change-owner-link').show(); - }); - $('li.project_member').bind('ajax:success', function() { - return gl.utils.refreshCurrentPage(); - }); - $('li.group_member').bind('ajax:success', function() { - return gl.utils.refreshCurrentPage(); - }); - showBlacklistType = function() { - if ($("input[name='blacklist_type']:checked").val() === 'file') { - $('.blacklist-file').show(); - return $('.blacklist-raw').hide(); - } else { - $('.blacklist-file').hide(); - return $('.blacklist-raw').show(); - } - }; - $("input[name='blacklist_type']").click(showBlacklistType); - showBlacklistType(); +import { refreshCurrentPage } from './lib/utils/url_utility'; + +function showBlacklistType() { + if ($('input[name="blacklist_type"]:checked').val() === 'file') { + $('.blacklist-file').show(); + $('.blacklist-raw').hide(); + } else { + $('.blacklist-file').hide(); + $('.blacklist-raw').show(); } +} + +export default function adminInit() { + const modal = $('.change-owner-holder'); + + $('input#user_force_random_password').on('change', function randomPasswordClick() { + const $elems = $('#user_password, #user_password_confirmation'); + if ($(this).attr('checked')) { + $elems.val('').attr('disabled', true); + } else { + $elems.removeAttr('disabled'); + } + }); + + $('body').on('click', '.js-toggle-colors-link', (e) => { + e.preventDefault(); + $('.js-toggle-colors-container').toggle(); + }); + + $('.log-tabs a').on('click', function logTabsClick(e) { + e.preventDefault(); + $(this).tab('show'); + }); + + $('.log-bottom').on('click', (e) => { + e.preventDefault(); + const $visibleLog = $('.file-content:visible'); + $visibleLog.animate({ + scrollTop: $visibleLog.find('ol').height(), + }, 'fast'); + }); + + $('.change-owner-link').on('click', function changeOwnerLinkClick(e) { + e.preventDefault(); + $(this).hide(); + modal.show(); + }); + + $('.change-owner-cancel-link').on('click', (e) => { + e.preventDefault(); + modal.hide(); + $('.change-owner-link').show(); + }); + + $('li.project_member, li.group_member').on('ajax:success', refreshCurrentPage); - return Admin; -})(); + $("input[name='blacklist_type']").on('click', showBlacklistType); + showBlacklistType(); +} diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index d963101028a..21d8c790e90 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import axios from './lib/utils/axios_utils'; const Api = { groupsPath: '/api/:version/groups.json', @@ -6,6 +7,7 @@ const Api = { namespacesPath: '/api/:version/namespaces.json', groupProjectsPath: '/api/:version/groups/:id/projects.json', projectsPath: '/api/:version/projects.json', + projectPath: '/api/:version/projects/:id', projectLabelsPath: '/:namespace_path/:project_path/labels', groupLabelsPath: '/groups/:namespace_path/labels', licensePath: '/api/:version/templates/licenses/:key', @@ -76,6 +78,14 @@ const Api = { .done(projects => callback(projects)); }, + // Return single project + project(projectPath) { + const url = Api.buildUrl(Api.projectPath) + .replace(':id', encodeURIComponent(projectPath)); + + return axios.get(url); + }, + newLabel(namespacePath, projectPath, data, callback) { let url; @@ -115,7 +125,7 @@ const Api = { commitMultiple(id, data) { // see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions const url = Api.buildUrl(Api.commitPath) - .replace(':id', id); + .replace(':id', encodeURIComponent(id)); return this.wrapAjaxCall({ url, type: 'POST', @@ -127,7 +137,7 @@ const Api = { branchSingle(id, branch) { const url = Api.buildUrl(Api.branchSinglePath) - .replace(':id', id) + .replace(':id', encodeURIComponent(id)) .replace(':branch', branch); return this.wrapAjaxCall({ diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js deleted file mode 100644 index 88756884d16..00000000000 --- a/app/assets/javascripts/aside.js +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, max-len */ - -window.Aside = (function() { - function Aside() { - $(document).off("click", "a.show-aside"); - $(document).on("click", 'a.show-aside', function(e) { - var btn, icon; - e.preventDefault(); - btn = $(e.currentTarget); - icon = btn.find('i'); - if (icon.hasClass('fa-angle-left')) { - btn.parent().find('section').hide(); - btn.parent().find('aside').fadeIn(); - return icon.removeClass('fa-angle-left').addClass('fa-angle-right'); - } else { - btn.parent().find('aside').hide(); - btn.parent().find('section').fadeIn(); - return icon.removeClass('fa-angle-right').addClass('fa-angle-left'); - } - }); - } - - return Aside; -})(); diff --git a/app/assets/javascripts/behaviors/copy_as_gfm.js b/app/assets/javascripts/behaviors/copy_as_gfm.js index e7dc4ef8304..c6eca72c51b 100644 --- a/app/assets/javascripts/behaviors/copy_as_gfm.js +++ b/app/assets/javascripts/behaviors/copy_as_gfm.js @@ -74,6 +74,18 @@ const gfmRules = { return `![${el.dataset.title}](${el.getAttribute('src')})`; }, }, + MermaidFilter: { + 'svg.mermaid'(el, text) { + const sourceEl = el.querySelector('text.source'); + if (!sourceEl) return false; + + return `\`\`\`mermaid\n${CopyAsGFM.nodeToGFM(sourceEl)}\n\`\`\``; + }, + 'svg.mermaid style, svg.mermaid g'(el, text) { + // We don't want to include the content of these elements in the copied text. + return ''; + }, + }, MathFilter: { 'pre.code.math[data-math-style=display]'(el, text) { return `\`\`\`math\n${text.trim()}\n\`\`\``; diff --git a/app/assets/javascripts/behaviors/secret_values.js b/app/assets/javascripts/behaviors/secret_values.js new file mode 100644 index 00000000000..1cf0b960eb0 --- /dev/null +++ b/app/assets/javascripts/behaviors/secret_values.js @@ -0,0 +1,42 @@ +import { n__ } from '../locale'; +import { convertPermissionToBoolean } from '../lib/utils/common_utils'; + +export default class SecretValues { + constructor(container) { + this.container = container; + } + + init() { + this.values = this.container.querySelectorAll('.js-secret-value'); + this.placeholders = this.container.querySelectorAll('.js-secret-value-placeholder'); + this.revealButton = this.container.querySelector('.js-secret-value-reveal-button'); + + this.revealText = n__('Reveal value', 'Reveal values', this.values.length); + this.hideText = n__('Hide value', 'Hide values', this.values.length); + + const isRevealed = convertPermissionToBoolean(this.revealButton.dataset.secretRevealStatus); + this.updateDom(isRevealed); + + this.revealButton.addEventListener('click', this.onRevealButtonClicked.bind(this)); + } + + onRevealButtonClicked() { + const previousIsRevealed = convertPermissionToBoolean( + this.revealButton.dataset.secretRevealStatus, + ); + this.updateDom(!previousIsRevealed); + } + + updateDom(isRevealed) { + this.values.forEach((value) => { + value.classList.toggle('hide', !isRevealed); + }); + + this.placeholders.forEach((placeholder) => { + placeholder.classList.toggle('hide', isRevealed); + }); + + this.revealButton.textContent = isRevealed ? this.hideText : this.revealText; + this.revealButton.dataset.secretRevealStatus = isRevealed; + } +} diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js index b70b0a9bbf8..417ac31fc86 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.js +++ b/app/assets/javascripts/behaviors/toggler_behavior.js @@ -5,6 +5,7 @@ // %button.js-toggle-button // %div.js-toggle-content // +import { getLocationHash } from '../lib/utils/url_utility'; $(() => { function toggleContainer(container, toggleState) { @@ -32,7 +33,7 @@ $(() => { // If we're accessing a permalink, ensure it is not inside a // closed js-toggle-container! - const hash = window.gl.utils.getLocationHash(); + const hash = getLocationHash(); const anchor = hash && document.getElementById(hash); const container = anchor && $(anchor).closest('.js-toggle-container'); diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js index 0d590a9dbc4..f7ae6f1cd12 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js +++ b/app/assets/javascripts/blob/blob_file_dropzone.js @@ -1,6 +1,6 @@ /* eslint-disable func-names, object-shorthand, prefer-arrow-callback */ import Dropzone from 'dropzone'; -import '../lib/utils/url_utility'; +import { visitUrl } from '../lib/utils/url_utility'; import { HIDDEN_CLASS } from '../lib/utils/constants'; import csrf from '../lib/utils/csrf'; @@ -49,7 +49,7 @@ export default class BlobFileDropzone { }); this.on('success', function (header, response) { $('#modal-upload-blob').modal('hide'); - window.gl.utils.visitUrl(response.filePath); + visitUrl(response.filePath); }); this.on('maxfilesexceeded', function (file) { dropzoneMessage.addClass(HIDDEN_CLASS); diff --git a/app/assets/javascripts/blob/blob_line_permalink_updater.js b/app/assets/javascripts/blob/blob_line_permalink_updater.js index c8f68860fbd..d36d9f0de2d 100644 --- a/app/assets/javascripts/blob/blob_line_permalink_updater.js +++ b/app/assets/javascripts/blob/blob_line_permalink_updater.js @@ -1,7 +1,9 @@ +import { getLocationHash } from '../lib/utils/url_utility'; + const lineNumberRe = /^L[0-9]+/; const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => { - const hash = gl.utils.getLocationHash(); + const hash = getLocationHash(); if (hash && lineNumberRe.test(hash)) { const hashUrlString = `#${hash}`; diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js index c858a6bb7b4..57b031956e8 100644 --- a/app/assets/javascripts/blob/notebook/index.js +++ b/app/assets/javascripts/blob/notebook/index.js @@ -1,10 +1,8 @@ /* eslint-disable no-new */ import Vue from 'vue'; -import VueResource from 'vue-resource'; +import axios from '../../lib/utils/axios_utils'; import notebookLab from '../../notebook/index.vue'; -Vue.use(VueResource); - export default () => { const el = document.getElementById('js-notebook-viewer'); @@ -50,14 +48,14 @@ export default () => { `, methods: { loadFile() { - this.$http.get(el.dataset.endpoint) - .then(response => response.json()) - .then((res) => { - this.json = res; + axios.get(el.dataset.endpoint) + .then(res => res.data) + .then((data) => { + this.json = data; this.loading = false; }) .catch((e) => { - if (e.status) { + if (e.status !== 200) { this.loadError = true; } diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index 20d23162940..679c883cdcf 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -2,7 +2,6 @@ import _ from 'underscore'; import Vue from 'vue'; -import VueResource from 'vue-resource'; import Flash from '../flash'; import { __ } from '../locale'; import FilteredSearchBoards from './filtered_search_boards'; @@ -25,8 +24,6 @@ import './components/new_list_dropdown'; import './components/modal/index'; import '../vue_shared/vue_resource_interceptor'; -Vue.use(VueResource); - $(() => { const $boardApp = document.getElementById('board-app'); const Store = gl.issueBoards.BoardsStore; @@ -95,14 +92,13 @@ $(() => { Store.disabled = this.disabled; gl.boardService.all() - .then(response => response.json()) - .then((resp) => { - resp.forEach((board) => { + .then(res => res.data) + .then((data) => { + data.forEach((board) => { const list = Store.addList(board, this.defaultAvatar); if (list.type === 'closed') { list.position = Infinity; - list.label = { description: 'Shows all closed issues. Moving an issue to this list closes it' }; } else if (list.type === 'backlog') { list.position = -1; } @@ -113,7 +109,9 @@ $(() => { Store.addBlankState(); this.loading = false; }) - .catch(() => new Flash('An error occurred. Please try again.')); + .catch(() => { + Flash('An error occurred while fetching the board lists. Please try again.'); + }); }, methods: { updateTokens() { @@ -124,7 +122,7 @@ $(() => { if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { newIssue.setFetchingState('subscriptions', true); BoardService.getIssueInfo(sidebarInfoEndpoint) - .then(res => res.json()) + .then(res => res.data) .then((data) => { newIssue.setFetchingState('subscriptions', false); newIssue.updateData({ diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js index edfe7c326db..72db626d3c7 100644 --- a/app/assets/javascripts/boards/components/board_blank_state.js +++ b/app/assets/javascripts/boards/components/board_blank_state.js @@ -65,7 +65,7 @@ export default { // Save the labels gl.boardService.generateDefaultLists() - .then(resp => resp.json()) + .then(res => res.data) .then((data) => { data.forEach((listObj) => { const list = Store.findList('title', listObj.title); diff --git a/app/assets/javascripts/boards/components/board_list.js b/app/assets/javascripts/boards/components/board_list.js index 29aeb8e84aa..84b76a6f1b1 100644 --- a/app/assets/javascripts/boards/components/board_list.js +++ b/app/assets/javascripts/boards/components/board_list.js @@ -115,7 +115,7 @@ export default { }, mounted() { const options = gl.issueBoards.getBoardSortableDefaultOptions({ - scroll: document.querySelectorAll('.boards-list')[0], + scroll: true, group: 'issues', disabled: this.disabled, filter: '.board-list-count, .is-disabled', diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js index faa76da964f..983429550f0 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js +++ b/app/assets/javascripts/boards/components/board_sidebar.js @@ -1,9 +1,8 @@ /* eslint-disable comma-dangle, space-before-function-paren, no-new */ -/* global MilestoneSelect */ -/* global Sidebar */ import Vue from 'vue'; import Flash from '../../flash'; +import Sidebar from '../../right_sidebar'; import eventHub from '../../sidebar/event_hub'; import assigneeTitle from '../../sidebar/components/assignees/assignee_title'; import assignees from '../../sidebar/components/assignees/assignees'; @@ -12,6 +11,7 @@ import './sidebar/remove_issue'; import IssuableContext from '../../issuable_context'; import LabelsSelect from '../../labels_select'; import subscriptions from '../../sidebar/components/subscriptions/subscriptions.vue'; +import MilestoneSelect from '../../milestone_select'; const Store = gl.issueBoards.BoardsStore; diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js index d2044f20ebe..d825ff38587 100644 --- a/app/assets/javascripts/boards/components/modal/index.js +++ b/app/assets/javascripts/boards/components/modal/index.js @@ -89,7 +89,7 @@ gl.issueBoards.IssuesModal = Vue.extend({ page: this.page, per: this.perPage, })) - .then(resp => resp.json()) + .then(res => res.data) .then((data) => { if (clearIssues) { this.issues = []; diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index df2809e1805..e210d69895e 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -40,7 +40,7 @@ class List { save () { return gl.boardService.createList(this.label.id) - .then(resp => resp.json()) + .then(res => res.data) .then((data) => { this.id = data.id; this.type = data.list_type; @@ -90,7 +90,7 @@ class List { } return gl.boardService.getIssuesForList(this.id, data) - .then(resp => resp.json()) + .then(res => res.data) .then((data) => { this.loading = false; this.issuesSize = data.size; @@ -108,7 +108,7 @@ class List { this.issuesSize += 1; return gl.boardService.newIssue(this.id, issue) - .then(resp => resp.json()) + .then(res => res.data) .then((data) => { issue.id = data.id; issue.iid = data.iid; diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index fa7ddd25e1f..d78d4701974 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -1,82 +1,79 @@ -/* eslint-disable space-before-function-paren, comma-dangle, no-param-reassign, camelcase, max-len, no-unused-vars */ - -import Vue from 'vue'; +import axios from '../../lib/utils/axios_utils'; +import { mergeUrlParams } from '../../lib/utils/url_utility'; export default class BoardService { - constructor ({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId }) { - this.boards = Vue.resource(`${boardsEndpoint}{/id}.json`, {}, { - issues: { - method: 'GET', - url: `${gon.relative_url_root}/-/boards/${boardId}/issues.json`, - } - }); - this.lists = Vue.resource(`${listsEndpoint}{/id}`, {}, { - generate: { - method: 'POST', - url: `${listsEndpoint}/generate.json` - } - }); - this.issue = Vue.resource(`${gon.relative_url_root}/-/boards/${boardId}/issues{/id}`, {}); - this.issues = Vue.resource(`${listsEndpoint}{/id}/issues`, {}, { - bulkUpdate: { - method: 'POST', - url: bulkUpdatePath, - }, - }); + constructor({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId }) { + this.boardsEndpoint = boardsEndpoint; + this.boardId = boardId; + this.listsEndpoint = listsEndpoint; + this.listsEndpointGenerate = `${listsEndpoint}/generate.json`; + this.bulkUpdatePath = bulkUpdatePath; + } + + generateBoardsPath(id) { + return `${this.boardsEndpoint}${id ? `/${id}` : ''}.json`; } - all () { - return this.lists.get(); + generateIssuesPath(id) { + return `${this.listsEndpoint}${id ? `/${id}` : ''}/issues`; } - generateDefaultLists () { - return this.lists.generate({}); + static generateIssuePath(boardId, id) { + return `${gon.relative_url_root}/-/boards/${boardId ? `/${boardId}` : ''}/issues${id ? `/${id}` : ''}`; } - createList (label_id) { - return this.lists.save({}, { + all() { + return axios.get(this.listsEndpoint); + } + + generateDefaultLists() { + return axios.post(this.listsEndpointGenerate, {}); + } + + createList(labelId) { + return axios.post(this.listsEndpoint, { list: { - label_id - } + label_id: labelId, + }, }); } - updateList (id, position) { - return this.lists.update({ id }, { + updateList(id, position) { + return axios.put(`${this.listsEndpoint}/${id}`, { list: { - position - } + position, + }, }); } - destroyList (id) { - return this.lists.delete({ id }); + destroyList(id) { + return axios.delete(`${this.listsEndpoint}/${id}`); } - getIssuesForList (id, filter = {}) { + getIssuesForList(id, filter = {}) { const data = { id }; Object.keys(filter).forEach((key) => { data[key] = filter[key]; }); - return this.issues.get(data); + return axios.get(mergeUrlParams(data, this.generateIssuesPath(id))); } - moveIssue (id, from_list_id = null, to_list_id = null, move_before_id = null, move_after_id = null) { - return this.issue.update({ id }, { - from_list_id, - to_list_id, - move_before_id, - move_after_id, + moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) { + return axios.put(BoardService.generateIssuePath(this.boardId, id), { + from_list_id: fromListId, + to_list_id: toListId, + move_before_id: moveBeforeId, + move_after_id: moveAfterId, }); } - newIssue (id, issue) { - return this.issues.save({ id }, { - issue + newIssue(id, issue) { + return axios.post(this.generateIssuesPath(id), { + issue, }); } getBacklog(data) { - return this.boards.issues(data); + return axios.get(mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`)); } bulkUpdate(issueIds, extraData = {}) { @@ -86,15 +83,15 @@ export default class BoardService { }), }; - return this.issues.bulkUpdate(data); + return axios.post(this.bulkUpdatePath, data); } static getIssueInfo(endpoint) { - return Vue.http.get(endpoint); + return axios.get(endpoint); } static toggleIssueSubscription(endpoint) { - return Vue.http.post(endpoint); + return axios.post(endpoint); } } diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index 2cfd6179a25..637d0dbde23 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -30,6 +30,7 @@ export default class Clusters { installHelmPath, installIngressPath, installRunnerPath, + installPrometheusPath, clusterStatus, clusterStatusReason, helpPath, @@ -44,6 +45,7 @@ export default class Clusters { installHelmEndpoint: installHelmPath, installIngressEndpoint: installIngressPath, installRunnerEndpoint: installRunnerPath, + installPrometheusEndpoint: installPrometheusPath, }); this.toggle = this.toggle.bind(this); diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index e5ae439d26e..cd58b88db69 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -67,6 +67,16 @@ export default { and send the results back to GitLab.`, )); }, + prometheusDescription() { + return sprintf( + _.escape(s__('ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications.')), { + gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html", target="_blank" rel="noopener noreferrer"> + ${_.escape(s__('ClusterIntegration|Gitlab Integration'))} + </a>`, + }, + false, + ); + }, }, }; </script> @@ -106,6 +116,16 @@ export default { :request-status="applications.ingress.requestStatus" :request-reason="applications.ingress.requestReason" /> + <application-row + id="prometheus" + :title="applications.prometheus.title" + title-link="https://prometheus.io/docs/introduction/overview/" + :description="prometheusDescription" + :status="applications.prometheus.status" + :status-reason="applications.prometheus.statusReason" + :request-status="applications.prometheus.requestStatus" + :request-reason="applications.prometheus.requestReason" + /> <!-- NOTE: Don't forget to update `clusters.scss` min-height for this block and uncomment `application_spec` tests --> <!-- Add GitLab Runner row, all other plumbing is complete --> </div> diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js index 755c2981c2e..13468578f4f 100644 --- a/app/assets/javascripts/clusters/services/clusters_service.js +++ b/app/assets/javascripts/clusters/services/clusters_service.js @@ -7,6 +7,7 @@ export default class ClusterService { helm: this.options.installHelmEndpoint, ingress: this.options.installIngressEndpoint, runner: this.options.installRunnerEndpoint, + prometheus: this.options.installPrometheusEndpoint, }; } diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index e731cdc3042..bd4a1fb37f9 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -28,6 +28,13 @@ export default class ClusterStore { requestStatus: null, requestReason: null, }, + prometheus: { + title: s__('ClusterIntegration|Prometheus'), + status: null, + statusReason: null, + requestStatus: null, + requestReason: null, + }, }, }; } diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index 50d0cb5c86d..b6a0ece7907 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -121,7 +121,7 @@ export default class ImageFile { return $('.swipe.view', this.file).each((function(_this) { return function(index, view) { var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref; - ref = this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; + ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; $swipeFrame = $('.swipe-frame', view); $swipeWrap = $('.swipe-wrap', view); $swipeBar = $('.swipe-bar', view); @@ -158,7 +158,7 @@ export default class ImageFile { return $('.onion-skin.view', this.file).each((function(_this) { return function(index, view) { var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false; - ref = this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; + ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; $frame = $('.onion-skin-frame', view); $frameAdded = $('.frame.added', view); $track = $('.drag-track', view); @@ -176,6 +176,7 @@ export default class ImageFile { left: dragTrackWidth }); + $frameAdded.css('opacity', 1); framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); _this.initDraggable($dragger, framePadding, function(e, left) { diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index 9b952ea7b60..3a03cbf6b90 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -1,9 +1,10 @@ /* eslint-disable func-names, wrap-iife, consistent-return, no-return-assign, no-param-reassign, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, prefer-arrow-callback */ -/* global Pager */ import { pluralize } from './lib/utils/text_utility'; +import { localTimeAgo } from './lib/utils/datetime_utility'; +import Pager from './pager'; export default (function () { const CommitsList = {}; @@ -91,7 +92,7 @@ export default (function () { $commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${pluralize('commit', commitsCount)}`); } - gl.utils.localTimeAgo($processedData.find('.js-timeago')); + localTimeAgo($processedData.find('.js-timeago')); return processedData; }; diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index 9e5dbd64a7e..144caf1d278 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -1,7 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */ +import { localTimeAgo } from './lib/utils/datetime_utility'; -window.Compare = (function() { - function Compare(opts) { +export default class Compare { + constructor(opts) { this.opts = opts; this.source_loading = $(".js-source-loading"); this.target_loading = $(".js-target-loading"); @@ -34,12 +35,12 @@ window.Compare = (function() { this.initialState(); } - Compare.prototype.initialState = function() { + initialState() { this.getSourceHtml(); - return this.getTargetHtml(); - }; + this.getTargetHtml(); + } - Compare.prototype.getTargetProject = function() { + getTargetProject() { return $.ajax({ url: this.opts.targetProjectUrl, data: { @@ -52,22 +53,22 @@ window.Compare = (function() { return $('.js-target-branch-dropdown .dropdown-content').html(html); } }); - }; + } - Compare.prototype.getSourceHtml = function() { - return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { + getSourceHtml() { + return this.constructor.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { ref: $("input[name='merge_request[source_branch]']").val() }); - }; + } - Compare.prototype.getTargetHtml = function() { - return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', { + getTargetHtml() { + return this.constructor.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', { target_project_id: $("input[name='merge_request[target_project_id]']").val(), ref: $("input[name='merge_request[target_branch]']").val() }); - }; + } - Compare.prototype.sendAjax = function(url, loading, target, data) { + static sendAjax(url, loading, target, data) { var $target; $target = $(target); return $.ajax({ @@ -81,10 +82,8 @@ window.Compare = (function() { loading.hide(); $target.html(html); var className = '.' + $target[0].className.replace(' ', '.'); - gl.utils.localTimeAgo($('.js-timeago', className)); + localTimeAgo($('.js-timeago', className)); } }); - }; - - return Compare; -})(); + } +} diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js index 72c0d98d47c..e633ef8a29e 100644 --- a/app/assets/javascripts/compare_autocomplete.js +++ b/app/assets/javascripts/compare_autocomplete.js @@ -1,68 +1,60 @@ /* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */ -window.CompareAutocomplete = (function() { - function CompareAutocomplete() { - this.initDropdown(); - } - - CompareAutocomplete.prototype.initDropdown = function() { - return $('.js-compare-dropdown').each(function() { - var $dropdown, selected; - $dropdown = $(this); - selected = $dropdown.data('selected'); - const $dropdownContainer = $dropdown.closest('.dropdown'); - const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); - const $filterInput = $('input[type="search"]', $dropdownContainer); - $dropdown.glDropdown({ - data: function(term, callback) { - return $.ajax({ - url: $dropdown.data('refs-url'), - data: { - ref: $dropdown.data('ref'), - search: term, - } - }).done(function(refs) { - return callback(refs); - }); - }, - selectable: true, - filterable: true, - filterRemote: true, - fieldName: $dropdown.data('field-name'), - filterInput: 'input[type="search"]', - renderRow: function(ref) { - var link; - if (ref.header != null) { - return $('<li />').addClass('dropdown-header').text(ref.header); - } else { - link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref)); - return $('<li />').append(link); +export default function initCompareAutocomplete() { + $('.js-compare-dropdown').each(function() { + var $dropdown, selected; + $dropdown = $(this); + selected = $dropdown.data('selected'); + const $dropdownContainer = $dropdown.closest('.dropdown'); + const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); + const $filterInput = $('input[type="search"]', $dropdownContainer); + $dropdown.glDropdown({ + data: function(term, callback) { + return $.ajax({ + url: $dropdown.data('refs-url'), + data: { + ref: $dropdown.data('ref'), + search: term, } - }, - id: function(obj, $el) { - return $el.attr('data-ref'); - }, - toggleLabel: function(obj, $el) { - return $el.text().trim(); - } - }); - $filterInput.on('keyup', (e) => { - const keyCode = e.keyCode || e.which; - if (keyCode !== 13) return; - const text = $filterInput.val(); - $fieldInput.val(text); - $('.dropdown-toggle-text', $dropdown).text(text); - $dropdownContainer.removeClass('open'); - }); - - $dropdownContainer.on('click', '.dropdown-content a', (e) => { - $dropdown.prop('title', e.target.text.replace(/_+?/g, '-')); - if ($dropdown.hasClass('has-tooltip')) { - $dropdown.tooltip('fixTitle'); + }).done(function(refs) { + return callback(refs); + }); + }, + selectable: true, + filterable: true, + filterRemote: true, + fieldName: $dropdown.data('field-name'), + filterInput: 'input[type="search"]', + renderRow: function(ref) { + var link; + if (ref.header != null) { + return $('<li />').addClass('dropdown-header').text(ref.header); + } else { + link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref)); + return $('<li />').append(link); } - }); + }, + id: function(obj, $el) { + return $el.attr('data-ref'); + }, + toggleLabel: function(obj, $el) { + return $el.text().trim(); + } + }); + $filterInput.on('keyup', (e) => { + const keyCode = e.keyCode || e.which; + if (keyCode !== 13) return; + const text = $filterInput.val(); + $fieldInput.val(text); + $('.dropdown-toggle-text', $dropdown).text(text); + $dropdownContainer.removeClass('open'); }); - }; - return CompareAutocomplete; -})(); + $dropdownContainer.on('click', '.dropdown-content a', (e) => { + $dropdown.prop('title', e.target.text.replace(/_+?/g, '-')); + if ($dropdown.hasClass('has-tooltip')) { + $dropdown.tooltip('fixTitle'); + } + }); + }); +} diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js index 46b68ebe158..74520675a7c 100644 --- a/app/assets/javascripts/contextual_sidebar.js +++ b/app/assets/javascripts/contextual_sidebar.js @@ -9,7 +9,7 @@ export default class ContextualSidebar { } initDomElements() { - this.$page = $('.page-with-sidebar'); + this.$page = $('.layout-page'); this.$sidebar = $('.nav-sidebar'); this.$innerScroll = $('.nav-sidebar-inner-scroll', this.$sidebar); this.$overlay = $('.mobile-overlay'); @@ -28,7 +28,7 @@ export default class ContextualSidebar { this.$closeSidebar.on('click', () => this.toggleSidebarNav(false)); this.$overlay.on('click', () => this.toggleSidebarNav(false)); this.$sidebarToggle.on('click', () => { - const value = !this.$sidebar.hasClass('sidebar-icons-only'); + const value = !this.$sidebar.hasClass('sidebar-collapsed-desktop'); this.toggleCollapsedSidebar(value); }); @@ -43,16 +43,16 @@ export default class ContextualSidebar { } toggleSidebarNav(show) { - this.$sidebar.toggleClass('nav-sidebar-expanded', show); + this.$sidebar.toggleClass('sidebar-expanded-mobile', show); this.$overlay.toggleClass('mobile-nav-open', show); - this.$sidebar.removeClass('sidebar-icons-only'); + this.$sidebar.removeClass('sidebar-collapsed-desktop'); } toggleCollapsedSidebar(collapsed) { const breakpoint = bp.getBreakpointSize(); if (this.$sidebar.length) { - this.$sidebar.toggleClass('sidebar-icons-only', collapsed); + this.$sidebar.toggleClass('sidebar-collapsed-desktop', collapsed); this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed); } ContextualSidebar.setCollapsedCookie(collapsed); diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js index 23425672b16..eedbd3feeb5 100644 --- a/app/assets/javascripts/create_merge_request_dropdown.js +++ b/app/assets/javascripts/create_merge_request_dropdown.js @@ -276,13 +276,13 @@ export default class CreateMergeRequestDropdown { let target; let value; - if (event.srcElement === this.branchInput) { + if (event.target === this.branchInput) { target = 'branch'; value = this.branchInput.value; - } else if (event.srcElement === this.refInput) { + } else if (event.target === this.refInput) { target = 'ref'; - value = event.srcElement.value.slice(0, event.srcElement.selectionStart) + - event.srcElement.value.slice(event.srcElement.selectionEnd); + value = event.target.value.slice(0, event.target.selectionStart) + + event.target.value.slice(event.target.selectionEnd); } else { return false; } diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue index f54ea7df522..cbce9205e75 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue @@ -2,6 +2,7 @@ import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import limitWarning from './limit_warning_component.vue'; import totalTime from './total_time_component.vue'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -12,6 +13,7 @@ userAvatarImage, totalTime, limitWarning, + icon, }, }; </script> @@ -52,7 +54,10 @@ </template> <template v-else> <span class="merge-request-branch" v-if="mergeRequest.branch"> - <i class= "fa fa-code-fork"></i> + <icon + name="fork" + :size="16"> + </icon> <a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a> </span> </template> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue index 5d95ddcd90e..508a411e599 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue @@ -3,6 +3,7 @@ import iconBranch from '../svg/icon_branch.svg'; import limitWarning from './limit_warning_component.vue'; import totalTime from './total_time_component.vue'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -13,6 +14,7 @@ userAvatarImage, totalTime, limitWarning, + icon, }, computed: { iconBranch() { @@ -37,7 +39,10 @@ <user-avatar-image :img-src="build.author.avatarUrl"/> <h5 class="item-title"> <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> - <i class="fa fa-code-fork"></i> + <icon + name="fork" + :size="16"> + </icon> <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> <span class="icon-branch" v-html="iconBranch"></span> <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue index 04d5440b77b..88fa6b073ca 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue @@ -3,6 +3,7 @@ import iconBranch from '../svg/icon_branch.svg'; import limitWarning from './limit_warning_component.vue'; import totalTime from './total_time_component.vue'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -12,6 +13,7 @@ components: { totalTime, limitWarning, + icon, }, computed: { iconBuildStatus() { @@ -40,7 +42,10 @@ <a :href="build.url" class="item-build-name">{{ build.name }}</a> · <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> - <i class="fa fa-code-fork"></i> + <icon + name="fork" + :size="16"> + </icon> <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> <span class="icon-branch" v-html="iconBranch"></span> <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue index b41d464475f..2a05c6f001e 100644 --- a/app/assets/javascripts/deploy_keys/components/key.vue +++ b/app/assets/javascripts/deploy_keys/components/key.vue @@ -1,5 +1,6 @@ <script> import actionBtn from './action_btn.vue'; + import { getTimeago } from '../../lib/utils/datetime_utility'; export default { props: { @@ -21,7 +22,7 @@ }, computed: { timeagoDate() { - return gl.utils.getTimeago().format(this.deployKey.created_at); + return getTimeago().format(this.deployKey.created_at); }, editDeployKeyPath() { return `${this.endpoint}/${this.deployKey.id}/edit`; diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index c8874e48c09..a162424b3cf 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -1,4 +1,4 @@ -import './lib/utils/url_utility'; +import { getLocationHash } from './lib/utils/url_utility'; import FilesCommentButton from './files_comment_button'; import SingleFileDiff from './single_file_diff'; import imageDiffHelper from './image_diff/helpers/index'; @@ -31,7 +31,7 @@ export default class Diff { isBound = true; } - if (gl.utils.getLocationHash()) { + if (getLocationHash()) { this.highlightSelectedLine(); } @@ -73,7 +73,7 @@ export default class Diff { } openAnchoredDiff(cb) { - const locationHash = gl.utils.getLocationHash(); + const locationHash = getLocationHash(); const anchoredDiff = locationHash && locationHash.split('_')[0]; if (!anchoredDiff) return; @@ -128,7 +128,7 @@ export default class Diff { } // eslint-disable-next-line class-methods-use-this highlightSelectedLine() { - const hash = gl.utils.getLocationHash(); + const hash = getLocationHash(); const $diffFiles = $('.diff-file'); $diffFiles.find('.hll').removeClass('hll'); diff --git a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js index 06ce84d7599..300b02da663 100644 --- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js +++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js @@ -1,8 +1,8 @@ /* global CommentsStore */ -/* global notes */ import Vue from 'vue'; import collapseIcon from '../icons/collapse_icon.svg'; +import Notes from '../../notes'; import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; const DiffNoteAvatars = Vue.extend({ @@ -129,7 +129,7 @@ const DiffNoteAvatars = Vue.extend({ }, methods: { clickedAvatar(e) { - notes.onAddDiffNote(e); + Notes.instance.onAddDiffNote(e); // Toggle the active state of the toggle all button this.toggleDiscussionsToggleState(); diff --git a/app/assets/javascripts/diff_notes/models/discussion.js b/app/assets/javascripts/diff_notes/models/discussion.js index dc43e4b2cc7..1b8a9af9390 100644 --- a/app/assets/javascripts/diff_notes/models/discussion.js +++ b/app/assets/javascripts/diff_notes/models/discussion.js @@ -2,6 +2,7 @@ /* global NoteModel */ import Vue from 'vue'; +import { localTimeAgo } from '../../lib/utils/datetime_utility'; class DiscussionModel { constructor (discussionId) { @@ -71,7 +72,7 @@ class DiscussionModel { $(`${discussionSelector} .discussion-header`).append(data.discussion_headline_html); } - gl.utils.localTimeAgo($('.js-timeago', `${discussionSelector}`)); + localTimeAgo($('.js-timeago', `${discussionSelector}`)); } else { $discussionHeadline.remove(); } diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 678af8f7b7a..42f61d33f6e 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -5,36 +5,37 @@ import IssuableIndex from './issuable_index'; import Milestone from './milestone'; import IssuableForm from './issuable_form'; import LabelsSelect from './labels_select'; -/* global MilestoneSelect */ +import MilestoneSelect from './milestone_select'; import NewBranchForm from './new_branch_form'; -/* global NotificationsForm */ -/* global NotificationsDropdown */ +import NotificationsForm from './notifications_form'; +import notificationsDropdown from './notifications_dropdown'; import groupAvatar from './group_avatar'; import GroupLabelSubscription from './group_label_subscription'; -/* global LineHighlighter */ +import LineHighlighter from './line_highlighter'; import BuildArtifacts from './build_artifacts'; import CILintEditor from './ci_lint_editor'; import groupsSelect from './groups_select'; -/* global Search */ -/* global Admin */ +import Search from './search'; +import initAdmin from './admin'; import NamespaceSelect from './namespace_select'; import NewCommitForm from './new_commit_form'; import Project from './project'; import projectAvatar from './project_avatar'; -/* global MergeRequest */ -/* global Compare */ -/* global CompareAutocomplete */ -/* global ProjectFindFile */ +import MergeRequest from './merge_request'; +import Compare from './compare'; +import initCompareAutocomplete from './compare_autocomplete'; +import ProjectFindFile from './project_find_file'; import ProjectNew from './project_new'; import projectImport from './project_import'; import Labels from './labels'; import LabelManager from './label_manager'; -/* global Sidebar */ +import Sidebar from './right_sidebar'; import IssuableTemplateSelectors from './templates/issuable_template_selectors'; import Flash from './flash'; import CommitsList from './commits'; import Issue from './issue'; import BindInOut from './behaviors/bind_in_out'; +import SecretValues from './behaviors/secret_values'; import DeleteModal from './branches/branches_delete_modal'; import Group from './group'; import GroupsList from './groups_list'; @@ -72,7 +73,6 @@ import initLegacyFilters from './init_legacy_filters'; import initIssuableSidebar from './init_issuable_sidebar'; import initProjectVisibilitySelector from './project_visibility'; import GpgBadges from './gpg_badges'; -import UserFeatureHelper from './helpers/user_feature_helper'; import initChangesDropdown from './init_changes_dropdown'; import NewGroupChild from './groups/new_group_child'; import AbuseReports from './abuse_reports'; @@ -90,7 +90,8 @@ import memberExpirationDate from './member_expiration_date'; import DueDateSelectors from './due_date_select'; import Diff from './diff'; import ProjectLabelSubscription from './project_label_subscription'; -import ProjectVariables from './project_variables'; +import SearchAutocomplete from './search_autocomplete'; +import Activities from './activities'; (function() { var Dispatcher; @@ -109,6 +110,8 @@ import ProjectVariables from './project_variables'; return false; } + const fail = () => Flash('Error loading dynamic module'); + path = page.split(':'); shortcut_handler = null; @@ -333,7 +336,7 @@ import ProjectVariables from './project_variables'; shortcut_handler = new ShortcutsIssuable(true); break; case 'dashboard:activity': - new gl.Activities(); + new Activities(); break; case 'projects:commit:show': new Diff(); @@ -354,7 +357,7 @@ import ProjectVariables from './project_variables'; $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); break; case 'projects:activity': - new gl.Activities(); + new Activities(); shortcut_handler = new ShortcutsNavigation(); break; case 'projects:commits:show': @@ -372,7 +375,7 @@ import ProjectVariables from './project_variables'; if ($('#tree-slider').length) new TreeView(); if ($('.blob-viewer').length) new BlobViewer(); - if ($('.project-show-activity').length) new gl.Activities(); + if ($('.project-show-activity').length) new Activities(); $('#tree-slider').waitForImages(function() { ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); }); @@ -406,13 +409,13 @@ import ProjectVariables from './project_variables'; }); break; case 'groups:activity': - new gl.Activities(); + new Activities(); break; case 'groups:show': const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); shortcut_handler = new ShortcutsNavigation(); new NotificationsForm(); - new NotificationsDropdown(); + notificationsDropdown(); new ProjectsList(); if (newGroupChildWrapper) { @@ -445,9 +448,6 @@ import ProjectVariables from './project_variables'; break; case 'projects:tree:show': shortcut_handler = new ShortcutsNavigation(); - - if (UserFeatureHelper.isNewRepoEnabled()) break; - new TreeView(); new BlobViewer(); new NewCommitForm($('.js-create-dir-form')); @@ -466,7 +466,6 @@ import ProjectVariables from './project_variables'; shortcut_handler = true; break; case 'projects:blob:show': - if (UserFeatureHelper.isNewRepoEnabled()) break; new BlobViewer(); initBlob(); break; @@ -525,15 +524,25 @@ import ProjectVariables from './project_variables'; case 'projects:settings:ci_cd:show': // Initialize expandable settings panels initSettingsPanels(); + + const runnerToken = document.querySelector('.js-secret-runner-token'); + if (runnerToken) { + const runnerTokenSecretValue = new SecretValues(runnerToken); + runnerTokenSecretValue.init(); + } case 'groups:settings:ci_cd:show': - new ProjectVariables(); + const secretVariableTable = document.querySelector('.js-secret-variable-table'); + if (secretVariableTable) { + const secretVariableTableValues = new SecretValues(secretVariableTable); + secretVariableTableValues.init(); + } break; case 'ci:lints:create': case 'ci:lints:show': new CILintEditor(); break; case 'users:show': - new UserCallout(); + import('./pages/users/show').then(m => m.default()).catch(fail); break; case 'admin:conversational_development_index:show': new UserCallout(); @@ -583,7 +592,7 @@ import ProjectVariables from './project_variables'; // needed in rspec gl.u2fAuthenticate = u2fAuthenticate; case 'admin': - new Admin(); + initAdmin(); switch (path[1]) { case 'broadcast_messages': initBroadcastMessagesForm(); @@ -615,14 +624,14 @@ import ProjectVariables from './project_variables'; break; case 'profiles': new NotificationsForm(); - new NotificationsDropdown(); + notificationsDropdown(); break; case 'projects': new Project(); projectAvatar(); switch (path[1]) { case 'compare': - new CompareAutocomplete(); + initCompareAutocomplete(); break; case 'edit': shortcut_handler = new ShortcutsNavigation(); @@ -638,7 +647,7 @@ import ProjectVariables from './project_variables'; case 'show': new Star(); new ProjectNew(); - new NotificationsDropdown(); + notificationsDropdown(); break; case 'wikis': new Wikis(); @@ -683,7 +692,7 @@ import ProjectVariables from './project_variables'; Dispatcher.prototype.initSearch = function() { // Only when search form is present if ($('.search').length) { - return new gl.SearchAutocomplete(); + return new SearchAutocomplete(); } }; diff --git a/app/assets/javascripts/docs/docs_bundle.js b/app/assets/javascripts/docs/docs_bundle.js new file mode 100644 index 00000000000..a32bd6d0fc7 --- /dev/null +++ b/app/assets/javascripts/docs/docs_bundle.js @@ -0,0 +1,13 @@ +import Mousetrap from 'mousetrap'; + +function addMousetrapClick(el, key) { + el.addEventListener('click', () => Mousetrap.trigger(key)); +} + +function domContentLoaded() { + addMousetrapClick(document.querySelector('.js-trigger-shortcut'), '?'); + addMousetrapClick(document.querySelector('.js-trigger-search-bar'), 's'); +} + +document.addEventListener('DOMContentLoaded', domContentLoaded); + diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 69c57f923b6..c05a83176f2 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -1,3 +1,4 @@ +import { visitUrl } from '../lib/utils/url_utility'; import Flash from '../flash'; import FilteredSearchContainer from './container'; import RecentSearchesRoot from './recent_searches_root'; @@ -126,7 +127,7 @@ class FilteredSearchManager { this.handleInputVisualTokenWrapper = this.handleInputVisualToken.bind(this); this.checkForEnterWrapper = this.checkForEnter.bind(this); this.onClearSearchWrapper = this.onClearSearch.bind(this); - this.checkForBackspaceWrapper = this.checkForBackspace.bind(this); + this.checkForBackspaceWrapper = this.checkForBackspace.call(this); this.removeSelectedTokenKeydownWrapper = this.removeSelectedTokenKeydown.bind(this); this.unselectEditTokensWrapper = this.unselectEditTokens.bind(this); this.editTokenWrapper = this.editToken.bind(this); @@ -179,22 +180,34 @@ class FilteredSearchManager { this.unbindStateEvents(); } - checkForBackspace(e) { - // 8 = Backspace Key - // 46 = Delete Key - if (e.keyCode === 8 || e.keyCode === 46) { - const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + checkForBackspace() { + let backspaceCount = 0; + + // closure for keeping track of the number of backspace keystrokes + return (e) => { + // 8 = Backspace Key + // 46 = Delete Key + if (e.keyCode === 8 || e.keyCode === 46) { + const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(lastVisualToken); + const canEdit = tokenName && this.canEdit && this.canEdit(tokenName, tokenValue); + + if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) { + backspaceCount += 1; + + if (backspaceCount === 2) { + backspaceCount = 0; + this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial(); + gl.FilteredSearchVisualTokens.removeLastTokenPartial(); + } + } - const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(lastVisualToken); - const canEdit = tokenName && this.canEdit && this.canEdit(tokenName, tokenValue); - if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) { - this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial(); - gl.FilteredSearchVisualTokens.removeLastTokenPartial(); + // Reposition dropdown so that it is aligned with cursor + this.dropdownManager.updateCurrentDropdownOffset(); + } else { + backspaceCount = 0; } - - // Reposition dropdown so that it is aligned with cursor - this.dropdownManager.updateCurrentDropdownOffset(); - } + }; } checkForEnter(e) { @@ -566,7 +579,7 @@ class FilteredSearchManager { if (this.updateObject) { this.updateObject(parameterizedUrl); } else { - gl.utils.visitUrl(parameterizedUrl); + visitUrl(parameterizedUrl); } } diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js index 98837c3b2a0..abb04d77f8f 100644 --- a/app/assets/javascripts/fly_out_nav.js +++ b/app/assets/javascripts/fly_out_nav.js @@ -21,7 +21,7 @@ let headerHeight = 50; export const getHeaderHeight = () => headerHeight; -export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-icons-only'); +export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-collapsed-desktop'); export const canShowActiveSubItems = (el) => { if (el.classList.contains('active') && !isSidebarCollapsed()) { @@ -161,13 +161,16 @@ export default () => { const items = [...sidebar.querySelectorAll('.sidebar-top-level-items > li')]; - sidebar.querySelector('.sidebar-top-level-items').addEventListener('mouseleave', () => { - clearTimeout(timeoutId); + const topItems = sidebar.querySelector('.sidebar-top-level-items'); + if (topItems) { + sidebar.querySelector('.sidebar-top-level-items').addEventListener('mouseleave', () => { + clearTimeout(timeoutId); - timeoutId = setTimeout(() => { - if (currentOpenMenu) hideMenu(currentOpenMenu); - }, getHideSubItemsInterval()); - }); + timeoutId = setTimeout(() => { + if (currentOpenMenu) hideMenu(currentOpenMenu); + }, getHideSubItemsInterval()); + }); + } headerHeight = document.querySelector('.nav-sidebar').offsetTop; diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index d918d80df8d..df20e1e9c88 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -57,12 +57,12 @@ class GfmAutoComplete { displayTpl(value) { if (GfmAutoComplete.isLoading(value)) return GfmAutoComplete.Loading.template; // eslint-disable-next-line no-template-curly-in-string - let tpl = '<li>/${name}'; + let tpl = '<li><span class="name">/${name}</span>'; if (value.aliases.length > 0) { - tpl += ' <small>(or /<%- aliases.join(", /") %>)</small>'; + tpl += ' <small class="aliases">(or /<%- aliases.join(", /") %>)</small>'; } if (value.params.length > 0) { - tpl += ' <small><%- params.join(" ") %></small>'; + tpl += ' <small class="params"><%- params.join(" ") %></small>'; } if (value.description !== '') { tpl += '<small class="description"><i><%- description %></i></small>'; diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 7ca783d3af6..64f258aed64 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -2,6 +2,7 @@ /* global fuzzaldrinPlus */ import _ from 'underscore'; import fuzzaldrinPlus from 'fuzzaldrin-plus'; +import { visitUrl } from './lib/utils/url_utility'; import { isObject } from './lib/utils/type_utility'; var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput; @@ -299,7 +300,7 @@ GitLabDropdown = (function() { return function(data) { _this.fullData = data; _this.parseData(_this.fullData); - _this.focusTextInput(true); + _this.focusTextInput(); if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') { return _this.filter.input.trigger('input'); } @@ -789,24 +790,16 @@ GitLabDropdown = (function() { return [selectedObject, isMarking]; }; - GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) { + GitLabDropdown.prototype.focusTextInput = function() { if (this.options.filterable) { - this.dropdown.one('transitionend', () => { - const initialScrollTop = $(window).scrollTop(); + const initialScrollTop = $(window).scrollTop(); - if (this.dropdown.is('.open')) { - this.filterInput.focus(); - } - - if ($(window).scrollTop() < initialScrollTop) { - $(window).scrollTop(initialScrollTop); - } - }); + if (this.dropdown.is('.open')) { + this.filterInput.focus(); + } - if (triggerFocus) { - // This triggers after a ajax request - // in case of slow requests, the dropdown transition could already be finished - this.dropdown.trigger('transitionend'); + if ($(window).scrollTop() < initialScrollTop) { + $(window).scrollTop(initialScrollTop); } } }; @@ -852,7 +845,7 @@ GitLabDropdown = (function() { if ($el.length) { var href = $el.attr('href'); if (href && href !== '#') { - gl.utils.visitUrl(href); + visitUrl(href); } else { $el.trigger('click'); } diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js index e7232ca3712..151a4ce012c 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors.js @@ -1,13 +1,14 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */ import _ from 'underscore'; -import d3 from 'd3'; import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph'; import ContributorsStatGraphUtil from './stat_graph_contributors_util'; -import { n__ } from '../locale'; +import { n__, s__, createDateTimeFormat, sprintf } from '../locale'; export default (function() { - function ContributorsStatGraph() {} + function ContributorsStatGraph() { + this.dateFormat = createDateTimeFormat({ year: 'numeric', month: 'long', day: 'numeric' }); + } ContributorsStatGraph.prototype.init = function(log) { var author_commits, total_commits; @@ -83,9 +84,12 @@ export default (function() { return _.each(author_commits, (function(_this) { return function(d) { _this.redraw_author_commit_info(d); - $(_this.authors[d.author_name].list_item).appendTo("ol"); - _this.authors[d.author_name].set_data(d.dates); - return _this.authors[d.author_name].redraw(); + if (_this.authors[d.author_name] != null) { + $(_this.authors[d.author_name].list_item).appendTo("ol"); + _this.authors[d.author_name].set_data(d.dates); + return _this.authors[d.author_name].redraw(); + } + return ''; }; })(this)); }; @@ -95,18 +99,26 @@ export default (function() { }; ContributorsStatGraph.prototype.change_date_header = function() { - var print, print_date_format, x_domain; - x_domain = ContributorsGraph.prototype.x_domain; - print_date_format = d3.time.format("%B %e %Y"); - print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]); - return $("#date_header").text(print); + const x_domain = ContributorsGraph.prototype.x_domain; + const formattedDateRange = sprintf( + s__('ContributorsPage|%{startDate} – %{endDate}'), + { + startDate: this.dateFormat.format(new Date(x_domain[0])), + endDate: this.dateFormat.format(new Date(x_domain[1])), + }, + ); + return $('#date_header').text(formattedDateRange); }; ContributorsStatGraph.prototype.redraw_author_commit_info = function(author) { - var author_commit_info, author_list_item; - author_list_item = $(this.authors[author.author_name].list_item); - author_commit_info = this.format_author_commit_info(author); - return author_list_item.find("span").html(author_commit_info); + var author_commit_info, author_list_item, $author; + $author = this.authors[author.author_name]; + if ($author != null) { + author_list_item = $(this.authors[author.author_name].list_item); + author_commit_info = this.format_author_commit_info(author); + return author_list_item.find("span").html(author_commit_info); + } + return ''; }; return ContributorsStatGraph; diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js index f64b4638485..9a4012232a0 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js @@ -1,6 +1,15 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */ import _ from 'underscore'; -import d3 from 'd3'; +import { extent, max } from 'd3-array'; +import { select, event as d3Event } from 'd3-selection'; +import { scaleTime, scaleLinear } from 'd3-scale'; +import { axisLeft, axisBottom } from 'd3-axis'; +import { area } from 'd3-shape'; +import { brushX } from 'd3-brush'; +import { timeParse } from 'd3-time-format'; +import { dateTickFormat } from '../lib/utils/tick_formats'; + +const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse }; const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; const hasProp = {}.hasOwnProperty; @@ -70,8 +79,8 @@ export const ContributorsGraph = (function() { }; ContributorsGraph.prototype.create_scale = function(width, height) { - this.x = d3.time.scale().range([0, width]).clamp(true); - return this.y = d3.scale.linear().range([height, 0]).nice(); + this.x = d3.scaleTime().range([0, width]).clamp(true); + return this.y = d3.scaleLinear().range([height, 0]).nice(); }; ContributorsGraph.prototype.draw_x_axis = function() { @@ -93,9 +102,12 @@ export const ContributorsMasterGraph = (function(superClass) { extend(ContributorsMasterGraph, superClass); function ContributorsMasterGraph(data1) { + const $parentElement = $('#contributors-master'); + const parentPadding = parseFloat($parentElement.css('padding-left')) + parseFloat($parentElement.css('padding-right')); + this.data = data1; this.update_content = this.update_content.bind(this); - this.width = $('.content').width() - 70; + this.width = $('.content').width() - parentPadding - (this.MARGIN.left + this.MARGIN.right); this.height = 200; this.x = null; this.y = null; @@ -120,7 +132,7 @@ export const ContributorsMasterGraph = (function(superClass) { ContributorsMasterGraph.prototype.parse_dates = function(data) { var parseDate; - parseDate = d3.time.format("%Y-%m-%d").parse; + parseDate = d3.timeParse("%Y-%m-%d"); return data.forEach(function(d) { return d.date = parseDate(d.date); }); @@ -131,8 +143,10 @@ export const ContributorsMasterGraph = (function(superClass) { }; ContributorsMasterGraph.prototype.create_axes = function() { - this.x_axis = d3.svg.axis().scale(this.x).orient("bottom"); - return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5); + this.x_axis = d3.axisBottom() + .scale(this.x) + .tickFormat(dateTickFormat); + return this.y_axis = d3.axisLeft().scale(this.y).ticks(5); }; ContributorsMasterGraph.prototype.create_svg = function() { @@ -140,16 +154,16 @@ export const ContributorsMasterGraph = (function(superClass) { }; ContributorsMasterGraph.prototype.create_area = function(x, y) { - return this.area = d3.svg.area().x(function(d) { + return this.area = d3.area().x(function(d) { return x(d.date); }).y0(this.height).y1(function(d) { d.commits = d.commits || d.additions || d.deletions; return y(d.commits); - }).interpolate("basis"); + }); }; ContributorsMasterGraph.prototype.create_brush = function() { - return this.brush = d3.svg.brush().x(this.x).on("brushend", this.update_content); + return this.brush = d3.brushX(this.x).extent([[this.x.range()[0], 0], [this.x.range()[1], this.height]]).on("end", this.update_content); }; ContributorsMasterGraph.prototype.draw_path = function(data) { @@ -161,7 +175,12 @@ export const ContributorsMasterGraph = (function(superClass) { }; ContributorsMasterGraph.prototype.update_content = function() { - ContributorsGraph.set_x_domain(this.brush.empty() ? this.x_max_domain : this.brush.extent()); + // d3Event.selection replaces the function brush.empty() calls + if (d3Event.selection != null) { + ContributorsGraph.set_x_domain(d3Event.selection.map(this.x.invert)); + } else { + ContributorsGraph.set_x_domain(this.x_max_domain); + } return $("#brush_change").trigger('change'); }; @@ -219,14 +238,17 @@ export const ContributorsAuthorGraph = (function(superClass) { }; ContributorsAuthorGraph.prototype.create_axes = function() { - this.x_axis = d3.svg.axis().scale(this.x).orient("bottom").ticks(8); - return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5); + this.x_axis = d3.axisBottom() + .scale(this.x) + .ticks(8) + .tickFormat(dateTickFormat); + return this.y_axis = d3.axisLeft().scale(this.y).ticks(5); }; ContributorsAuthorGraph.prototype.create_area = function(x, y) { - return this.area = d3.svg.area().x(function(d) { + return this.area = d3.area().x(function(d) { var parseDate; - parseDate = d3.time.format("%Y-%m-%d").parse; + parseDate = d3.timeParse("%Y-%m-%d"); return x(parseDate(d)); }).y0(this.height).y1((function(_this) { return function(d) { @@ -236,11 +258,12 @@ export const ContributorsAuthorGraph = (function(superClass) { return y(0); } }; - })(this)).interpolate("basis"); + })(this)); }; ContributorsAuthorGraph.prototype.create_svg = function() { - this.list_item = d3.selectAll(".person")[0].pop(); + var persons = document.querySelectorAll('.person'); + this.list_item = persons[persons.length - 1]; return this.svg = d3.select(this.list_item).append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "spark").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")"); }; diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index 2c0b6ab4ea8..241e026b84c 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -5,7 +5,7 @@ import eventHub from '../event_hub'; import { getParameterByName } from '../../lib/utils/common_utils'; import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import { COMMON_STR } from '../constants'; - +import { mergeUrlParams } from '../../lib/utils/url_utility'; import groupsComponent from './groups.vue'; export default { @@ -93,7 +93,7 @@ export default { this.isLoading = false; $.scrollTo(0); - const currentPath = gl.utils.mergeUrlParams({ page }, window.location.href); + const currentPath = mergeUrlParams({ page }, window.location.href); window.history.replaceState({ page: currentPath, }, document.title, currentPath); diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index c76ce762b54..42e79a9e17a 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -1,4 +1,5 @@ <script> +import { visitUrl } from '../../lib/utils/url_utility'; import tooltip from '../../vue_shared/directives/tooltip'; import identicon from '../../vue_shared/components/identicon.vue'; import eventHub from '../event_hub'; @@ -60,7 +61,7 @@ export default { if (this.hasChildren) { eventHub.$emit('toggleChildren', this.group); } else { - gl.utils.visitUrl(this.group.relativePath); + visitUrl(this.group.relativePath); } } }, @@ -76,7 +77,8 @@ export default { class="group-row" > <div - class="group-row-contents"> + class="group-row-contents" + :class="{ 'project-row-contents': !isGroup }"> <item-actions v-if="isGroup" :group="group" @@ -96,7 +98,7 @@ export default { /> </div> <div - class="avatar-container s40 hidden-xs" + class="avatar-container prepend-top-8 prepend-left-5 s24 hidden-xs" :class="{ 'content-loading': group.isChildrenLoading }" > <a @@ -105,11 +107,12 @@ export default { > <img v-if="hasAvatar" - class="avatar s40" + class="avatar s24" :src="group.avatarUrl" /> <identicon v-else + size-class="s24" :entity-id=group.id :entity-name="group.name" /> @@ -122,7 +125,7 @@ export default { :href="group.relativePath" :title="group.fullName" class="no-expand" - data-placement="top" + data-placement="bottom" >{{ // ending bracket must be by closing tag to prevent // link hover text-decoration from over-extending @@ -138,7 +141,8 @@ export default { <div v-if="group.description" class="description"> - {{group.description}} + <span v-html="group.description"> + </span> </div> </div> <group-folder diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue index 09cb79c1afd..0dd0783ce06 100644 --- a/app/assets/javascripts/groups/components/item_actions.vue +++ b/app/assets/javascripts/groups/components/item_actions.vue @@ -1,15 +1,15 @@ <script> -import { s__ } from '../../locale'; -import tooltip from '../../vue_shared/directives/tooltip'; -import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; +import { s__ } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; +import icon from '~/vue_shared/components/icon.vue'; +import modal from '~/vue_shared/components/modal.vue'; import eventHub from '../event_hub'; import { COMMON_STR } from '../constants'; -import Icon from '../../vue_shared/components/icon.vue'; export default { components: { - Icon, - PopupDialog, + icon, + modal, }, directives: { tooltip, @@ -27,7 +27,7 @@ export default { }, data() { return { - dialogStatus: false, + modalStatus: false, }; }, computed: { @@ -43,13 +43,11 @@ export default { }, methods: { onLeaveGroup() { - this.dialogStatus = true; + this.modalStatus = true; }, - leaveGroup(leaveConfirmed) { - this.dialogStatus = false; - if (leaveConfirmed) { - eventHub.$emit('leaveGroup', this.group, this.parentGroup); - } + leaveGroup() { + this.modalStatus = false; + eventHub.$emit('leaveGroup', this.group, this.parentGroup); }, }, }; @@ -64,10 +62,9 @@ export default { :title="editBtnTitle" :aria-label="editBtnTitle" data-container="body" + data-placement="bottom" class="edit-group btn no-expand"> - <icon - name="settings"> - </icon> + <icon name="settings"/> </a> <a v-tooltip @@ -77,13 +74,12 @@ export default { :title="leaveBtnTitle" :aria-label="leaveBtnTitle" data-container="body" + data-placement="bottom" class="leave-group btn no-expand"> - <i - class="fa fa-sign-out" - aria-hidden="true"/> + <icon name="leave"/> </a> - <popup-dialog - v-show="dialogStatus" + <modal + v-show="modalStatus" :primary-button-label="__('Leave')" kind="warning" :title="__('Are you sure?')" diff --git a/app/assets/javascripts/groups/components/item_caret.vue b/app/assets/javascripts/groups/components/item_caret.vue index 959b984816f..9e90fe2b701 100644 --- a/app/assets/javascripts/groups/components/item_caret.vue +++ b/app/assets/javascripts/groups/components/item_caret.vue @@ -1,4 +1,6 @@ <script> +import icon from '~/vue_shared/components/icon.vue'; + export default { props: { isGroupOpen: { @@ -7,9 +9,12 @@ export default { default: false, }, }, + components: { + icon, + }, computed: { iconClass() { - return this.isGroupOpen ? 'fa-caret-down' : 'fa-caret-right'; + return this.isGroupOpen ? 'angle-down' : 'angle-right'; }, }, }; @@ -17,9 +22,9 @@ export default { <template> <span class="folder-caret"> - <i - :class="iconClass" - class="fa" - aria-hidden="true"/> + <icon + :size="12" + :name="iconClass" + /> </span> </template> diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue index 9f8ac138fc3..2e42fb6c9a6 100644 --- a/app/assets/javascripts/groups/components/item_stats.vue +++ b/app/assets/javascripts/groups/components/item_stats.vue @@ -1,10 +1,14 @@ <script> -import tooltip from '../../vue_shared/directives/tooltip'; +import icon from '~/vue_shared/components/icon.vue'; +import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import { ITEM_TYPE, VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE, PROJECT_VISIBILITY_TYPE } from '../constants'; +import itemStatsValue from './item_stats_value.vue'; export default { - directives: { - tooltip, + components: { + icon, + timeAgoTooltip, + itemStatsValue, }, props: { item: { @@ -34,65 +38,47 @@ export default { <template> <div class="stats"> - <span - v-tooltip + <item-stats-value v-if="isGroup" - :title="s__('Subgroups')" - class="number-subgroups" - data-placement="top" - data-container="body"> - <i - class="fa fa-folder" - aria-hidden="true" - /> - {{item.subgroupCount}} - </span> - <span - v-tooltip + css-class="number-subgroups" + icon-name="folder" + :title="__('Subgroups')" + :value="item.subgroupCount" + /> + <item-stats-value v-if="isGroup" - :title="s__('Projects')" - class="number-projects" - data-placement="top" - data-container="body"> - <i - class="fa fa-bookmark" - aria-hidden="true" - /> - {{item.projectCount}} - </span> - <span - v-tooltip + css-class="number-projects" + icon-name="bookmark" + :title="__('Projects')" + :value="item.projectCount" + /> + <item-stats-value v-if="isGroup" - :title="s__('Members')" - class="number-users" - data-placement="top" - data-container="body"> - <i - class="fa fa-users" - aria-hidden="true" - /> - {{item.memberCount}} - </span> - <span + css-class="number-users" + icon-name="users" + :title="__('Members')" + :value="item.memberCount" + /> + <item-stats-value v-if="isProject" - class="project-stars"> - <i - class="fa fa-star" - aria-hidden="true" - /> - {{item.starCount}} - </span> - <span - v-tooltip + css-class="project-stars" + icon-name="star" + :value="item.starCount" + /> + <item-stats-value + css-class="item-visibility" + tooltip-placement="left" + :icon-name="visibilityIcon" :title="visibilityTooltip" - data-placement="left" - data-container="body" - class="item-visibility"> - <i - :class="visibilityIcon" - class="fa" - aria-hidden="true" + /> + <div + class="last-updated" + v-if="isProject" + > + <time-ago-tooltip + tooltip-placement="bottom" + :time="item.updatedAt" /> - </span> + </div> </div> </template> diff --git a/app/assets/javascripts/groups/components/item_stats_value.vue b/app/assets/javascripts/groups/components/item_stats_value.vue new file mode 100644 index 00000000000..f441cabf6d2 --- /dev/null +++ b/app/assets/javascripts/groups/components/item_stats_value.vue @@ -0,0 +1,68 @@ +<script> +import tooltip from '~/vue_shared/directives/tooltip'; +import icon from '~/vue_shared/components/icon.vue'; + +export default { + props: { + title: { + type: String, + required: false, + default: '', + }, + cssClass: { + type: String, + required: false, + default: '', + }, + iconName: { + type: String, + required: true, + }, + tooltipPlacement: { + type: String, + required: false, + default: 'bottom', + }, + /** + * value could either be number or string + * as `memberCount` is always passed as string + * while `subgroupCount` & `projectCount` + * are always number + */ + value: { + type: [Number, String], + required: false, + default: '', + }, + }, + directives: { + tooltip, + }, + components: { + icon, + }, + computed: { + isValuePresent() { + return this.value !== ''; + }, + }, +}; +</script> + +<template> + <span + v-tooltip + data-container="body" + :data-placement="tooltipPlacement" + :class="cssClass" + :title="title" + > + <icon :name="iconName"/> + <span + v-if="isValuePresent" + class="stat-value" + > + {{value}} + </span> + </span> +</template> diff --git a/app/assets/javascripts/groups/components/item_type_icon.vue b/app/assets/javascripts/groups/components/item_type_icon.vue index c02a8ad6d8c..118d94d4937 100644 --- a/app/assets/javascripts/groups/components/item_type_icon.vue +++ b/app/assets/javascripts/groups/components/item_type_icon.vue @@ -1,7 +1,11 @@ <script> +import icon from '~/vue_shared/components/icon.vue'; import { ITEM_TYPE } from '../constants'; export default { + components: { + icon, + }, props: { itemType: { type: String, @@ -16,9 +20,9 @@ export default { computed: { iconClass() { if (this.itemType === ITEM_TYPE.GROUP) { - return this.isGroupOpen ? 'fa-folder-open' : 'fa-folder'; + return this.isGroupOpen ? 'folder-open' : 'folder'; } - return 'fa-bookmark'; + return 'bookmark'; }, }, }; @@ -26,9 +30,6 @@ export default { <template> <span class="item-type-icon"> - <i - :class="iconClass" - class="fa" - aria-hidden="true"/> + <icon :name="iconClass"/> </span> </template> diff --git a/app/assets/javascripts/groups/constants.js b/app/assets/javascripts/groups/constants.js index 6fde41414b3..b8baed682f5 100644 --- a/app/assets/javascripts/groups/constants.js +++ b/app/assets/javascripts/groups/constants.js @@ -29,7 +29,7 @@ export const PROJECT_VISIBILITY_TYPE = { }; export const VISIBILITY_TYPE_ICON = { - public: 'fa-globe', - internal: 'fa-shield', - private: 'fa-lock', + public: 'earth', + internal: 'shield', + private: 'lock', }; diff --git a/app/assets/javascripts/groups/new_group_child.js b/app/assets/javascripts/groups/new_group_child.js index 8e273579aae..a120d501e35 100644 --- a/app/assets/javascripts/groups/new_group_child.js +++ b/app/assets/javascripts/groups/new_group_child.js @@ -1,3 +1,4 @@ +import { visitUrl } from '../lib/utils/url_utility'; import DropLab from '../droplab/drop_lab'; import ISetter from '../droplab/plugins/input_setter'; @@ -54,9 +55,9 @@ export default class NewGroupChild { onClickNewGroupChildButton(e) { if (e.target.dataset.action === NEW_PROJECT) { - gl.utils.visitUrl(this.newGroupPath); + visitUrl(this.newGroupPath); } else if (e.target.dataset.action === NEW_SUBGROUP) { - gl.utils.visitUrl(this.subgroupPath); + visitUrl(this.subgroupPath); } } } diff --git a/app/assets/javascripts/groups/store/groups_store.js b/app/assets/javascripts/groups/store/groups_store.js index a1689f4c5cc..4a7569078a1 100644 --- a/app/assets/javascripts/groups/store/groups_store.js +++ b/app/assets/javascripts/groups/store/groups_store.js @@ -71,7 +71,7 @@ export default class GroupsStore { id: rawGroupItem.id, name: rawGroupItem.name, fullName: rawGroupItem.full_name, - description: rawGroupItem.description, + description: rawGroupItem.markdown_description, visibility: rawGroupItem.visibility, avatarUrl: rawGroupItem.avatar_url, relativePath: rawGroupItem.relative_path, @@ -91,6 +91,7 @@ export default class GroupsStore { subgroupCount: rawGroupItem.subgroup_count, memberCount: rawGroupItem.number_users_with_delimiter, starCount: rawGroupItem.star_count, + updatedAt: rawGroupItem.updated_at, }; } diff --git a/app/assets/javascripts/helpers/user_feature_helper.js b/app/assets/javascripts/helpers/user_feature_helper.js deleted file mode 100644 index 638118a5204..00000000000 --- a/app/assets/javascripts/helpers/user_feature_helper.js +++ /dev/null @@ -1,7 +0,0 @@ -import Cookies from 'js-cookie'; - -export default { - isNewRepoEnabled() { - return Cookies.get('new_repo') === 'true'; - }, -}; diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue new file mode 100644 index 00000000000..704dff981df --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue @@ -0,0 +1,66 @@ +<script> + import { mapState } from 'vuex'; + import icon from '../../../vue_shared/components/icon.vue'; + import listItem from './list_item.vue'; + import listCollapsed from './list_collapsed.vue'; + + export default { + components: { + icon, + listItem, + listCollapsed, + }, + props: { + title: { + type: String, + required: true, + }, + fileList: { + type: Array, + required: true, + }, + }, + computed: { + ...mapState([ + 'currentProjectId', + 'currentBranchId', + 'rightPanelCollapsed', + ]), + }, + methods: { + toggleCollapsed() { + this.$emit('toggleCollapsed'); + }, + }, + + }; +</script> + +<template> + <div class="multi-file-commit-list"> + <list-collapsed + v-if="rightPanelCollapsed" + /> + <template v-else> + <ul + v-if="fileList.length" + class="list-unstyled append-bottom-0" + > + <li + v-for="file in fileList" + :key="file.key" + > + <list-item + :file="file" + /> + </li> + </ul> + <div + v-else + class="help-block prepend-top-0" + > + No changes + </div> + </template> + </div> +</template> diff --git a/app/assets/javascripts/repo/components/commit_sidebar/list_collapsed.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue index 6a0262f271b..6a0262f271b 100644 --- a/app/assets/javascripts/repo/components/commit_sidebar/list_collapsed.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue diff --git a/app/assets/javascripts/repo/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index 742f746e02f..742f746e02f 100644 --- a/app/assets/javascripts/repo/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue new file mode 100644 index 00000000000..26a70f6e748 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide.vue @@ -0,0 +1,95 @@ +<script> +import { mapState, mapGetters } from 'vuex'; +import ideSidebar from './ide_side_bar.vue'; +import ideContextbar from './ide_context_bar.vue'; +import repoTabs from './repo_tabs.vue'; +import repoFileButtons from './repo_file_buttons.vue'; +import ideStatusBar from './ide_status_bar.vue'; +import repoPreview from './repo_preview.vue'; +import repoEditor from './repo_editor.vue'; + +export default { + props: { + emptyStateSvgPath: { + type: String, + required: true, + }, + }, + computed: { + ...mapState([ + 'currentBlobView', + 'selectedFile', + ]), + ...mapGetters([ + 'changedFiles', + 'activeFile', + ]), + }, + components: { + ideSidebar, + ideContextbar, + repoTabs, + repoFileButtons, + ideStatusBar, + repoEditor, + repoPreview, + }, + mounted() { + const returnValue = 'Are you sure you want to lose unsaved changes?'; + window.onbeforeunload = (e) => { + if (!this.changedFiles.length) return undefined; + + Object.assign(e, { + returnValue, + }); + return returnValue; + }; + }, +}; +</script> + +<template> + <div + class="ide-view" + > + <ide-sidebar/> + <div + class="multi-file-edit-pane" + > + <template + v-if="activeFile"> + <repo-tabs/> + <component + class="multi-file-edit-pane-content" + :is="currentBlobView" + /> + <repo-file-buttons/> + <ide-status-bar + :file="selectedFile"/> + </template> + <template + v-else> + <div class="ide-empty-state"> + <div class="row js-empty-state"> + <div class="col-xs-12"> + <div class="svg-content svg-250"> + <img :src="emptyStateSvgPath"> + </div> + </div> + <div class="col-xs-12"> + <div class="text-content text-center"> + <h4> + Welcome to the GitLab IDE + </h4> + <p> + You can select a file in the left sidebar to begin editing and use the right sidebar to commit your changes. + </p> + </div> + </div> + </div> + </div> + </template> + </div> + <ide-contextbar/> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue new file mode 100644 index 00000000000..78c01272af6 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_context_bar.vue @@ -0,0 +1,108 @@ +<script> +import { mapGetters, mapState, mapActions } from 'vuex'; +import repoCommitSection from './repo_commit_section.vue'; +import icon from '../../vue_shared/components/icon.vue'; +import panelResizer from '../../vue_shared/components/panel_resizer.vue'; + +export default { + data() { + return { + width: 290, + }; + }, + components: { + repoCommitSection, + icon, + panelResizer, + }, + computed: { + ...mapState([ + 'rightPanelCollapsed', + ]), + ...mapGetters([ + 'changedFiles', + ]), + currentIcon() { + return this.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right'; + }, + maxSize() { + return window.innerWidth / 2; + }, + panelStyle() { + if (!this.rightPanelCollapsed) { + return { width: `${this.width}px` }; + } + return {}; + }, + }, + methods: { + ...mapActions([ + 'setPanelCollapsedStatus', + 'setResizingStatus', + ]), + toggleCollapsed() { + this.setPanelCollapsedStatus({ + side: 'right', + collapsed: !this.rightPanelCollapsed, + }); + }, + resizingStarted() { + this.setResizingStatus(true); + }, + resizingEnded() { + this.setResizingStatus(false); + }, + }, +}; +</script> + +<template> + <div + class="multi-file-commit-panel" + :class="{ + 'is-collapsed': rightPanelCollapsed, + }" + :style="panelStyle" + > + <div + class="multi-file-commit-panel-section"> + <header + class="multi-file-commit-panel-header" + :class="{ + 'is-collapsed': rightPanelCollapsed, + }" + > + <div + class="multi-file-commit-panel-header-title" + v-if="!rightPanelCollapsed"> + <icon + name="list-bulleted" + :size="18" + /> + Staged + </div> + <button + type="button" + class="btn btn-transparent multi-file-commit-panel-collapse-btn" + @click="toggleCollapsed" + > + <icon + :name="currentIcon" + :size="18" + /> + </button> + </header> + <repo-commit-section + class=""/> + </div> + <panel-resizer + :size.sync="width" + :enabled="!rightPanelCollapsed" + :start-size="290" + :min-size="200" + :max-size="maxSize" + @resize-start="resizingStarted" + @resize-end="resizingEnded" + side="left"/> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue new file mode 100644 index 00000000000..bd3a521ff43 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue @@ -0,0 +1,47 @@ +<script> +import repoTree from './ide_repo_tree.vue'; +import icon from '../../vue_shared/components/icon.vue'; +import newDropdown from './new_dropdown/index.vue'; + +export default { + components: { + repoTree, + icon, + newDropdown, + }, + props: { + projectId: { + type: String, + required: true, + }, + branch: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div class="branch-container"> + <div class="branch-header"> + <div class="branch-header-title"> + <icon + name="branch" + :size="12"> + </icon> + {{ branch.name }} + </div> + <div class="branch-header-btns"> + <new-dropdown + :project-id="projectId" + :branch="branch.name" + path=""/> + </div> + </div> + <div> + <repo-tree + :treeId="branch.treeId"/> + </div> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue new file mode 100644 index 00000000000..61daba6d176 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_project_tree.vue @@ -0,0 +1,47 @@ +<script> +import branchesTree from './ide_project_branches_tree.vue'; +import projectAvatarImage from '../../vue_shared/components/project_avatar/image.vue'; + +export default { + components: { + branchesTree, + projectAvatarImage, + }, + props: { + project: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div class="projects-sidebar"> + <div class="context-header"> + <a + :title="project.name" + :href="project.web_url"> + <div class="avatar-container s40 project-avatar"> + <project-avatar-image + class="avatar-container project-avatar" + :link-href="project.path" + :img-src="project.avatar_url" + :img-alt="project.name" + :img-size="40" + /> + </div> + <div class="sidebar-context-title"> + {{ project.name }} + </div> + </a> + </div> + <div class="multi-file-commit-panel-inner-scroll"> + <branches-tree + v-for="(branch, index) in project.branches" + :key="branch.name" + :project-id="project.path_with_namespace" + :branch="branch"/> + </div> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_repo_tree.vue b/app/assets/javascripts/ide/components/ide_repo_tree.vue new file mode 100644 index 00000000000..bd89ebe47d9 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_repo_tree.vue @@ -0,0 +1,71 @@ +<script> +import { mapState } from 'vuex'; +import repoPreviousDirectory from './repo_prev_directory.vue'; +import repoFile from './repo_file.vue'; +import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue'; +import { treeList } from '../stores/utils'; + +export default { + components: { + repoPreviousDirectory, + repoFile, + skeletonLoadingContainer, + }, + props: { + treeId: { + type: String, + required: true, + }, + }, + computed: { + ...mapState([ + 'trees', + 'isRoot', + ]), + ...mapState({ + projectName(state) { + return state.project.name; + }, + }), + fetchedList() { + return treeList(this.$store.state, this.treeId); + }, + hasPreviousDirectory() { + return !this.isRoot && this.fetchedList.length; + }, + showLoading() { + if (this.trees[this.treeId]) { + return this.trees[this.treeId].loading; + } + return true; + }, + }, +}; +</script> + +<template> +<div> + <div class="ide-file-list"> + <table class="table"> + <tbody + v-if="treeId"> + <repo-previous-directory + v-if="hasPreviousDirectory" + /> + <div + class="multi-file-loading-container" + v-if="showLoading" + v-for="n in 3" + :key="n"> + <skeleton-loading-container/> + </div> + <repo-file + v-for="file in fetchedList" + :key="file.key" + :file="file" + /> + </tbody> + </table> + </div> +</div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue new file mode 100644 index 00000000000..c30018e04b0 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -0,0 +1,108 @@ +<script> +import { mapState, mapActions } from 'vuex'; +import projectTree from './ide_project_tree.vue'; +import icon from '../../vue_shared/components/icon.vue'; +import panelResizer from '../../vue_shared/components/panel_resizer.vue'; +import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue'; + +export default { + data() { + return { + width: 290, + }; + }, + components: { + projectTree, + icon, + panelResizer, + skeletonLoadingContainer, + }, + computed: { + ...mapState([ + 'loading', + 'projects', + 'leftPanelCollapsed', + ]), + currentIcon() { + return this.leftPanelCollapsed ? 'angle-double-right' : 'angle-double-left'; + }, + maxSize() { + return window.innerWidth / 2; + }, + panelStyle() { + if (!this.leftPanelCollapsed) { + return { width: `${this.width}px` }; + } + return {}; + }, + showLoading() { + return this.loading; + }, + }, + methods: { + ...mapActions([ + 'setPanelCollapsedStatus', + 'setResizingStatus', + ]), + toggleCollapsed() { + this.setPanelCollapsedStatus({ + side: 'left', + collapsed: !this.leftPanelCollapsed, + }); + }, + resizingStarted() { + this.setResizingStatus(true); + }, + resizingEnded() { + this.setResizingStatus(false); + }, + }, +}; +</script> + +<template> + <div + class="multi-file-commit-panel" + :class="{ + 'is-collapsed': leftPanelCollapsed, + }" + :style="panelStyle" + > + <div class="multi-file-commit-panel-inner"> + <div + class="multi-file-loading-container" + v-if="showLoading" + v-for="n in 3" + :key="n"> + <skeleton-loading-container/> + </div> + <project-tree + v-for="(project, index) in projects" + :key="project.id" + :project="project"/> + </div> + <button + type="button" + class="btn btn-transparent left-collapse-btn" + @click="toggleCollapsed" + > + <icon + :name="currentIcon" + :size="18" + /> + <span + v-if="!leftPanelCollapsed" + class="collapse-text" + >Collapse sidebar</span> + </button> + <panel-resizer + :size.sync="width" + :enabled="!leftPanelCollapsed" + :start-size="290" + :min-size="200" + :max-size="maxSize" + @resize-start="resizingStarted" + @resize-end="resizingEnded" + side="right"/> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue new file mode 100644 index 00000000000..a24abadd936 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -0,0 +1,71 @@ +<script> +import { mapState } from 'vuex'; +import icon from '../../vue_shared/components/icon.vue'; +import tooltip from '../../vue_shared/directives/tooltip'; +import timeAgoMixin from '../../vue_shared/mixins/timeago'; + +export default { + props: { + file: { + type: Object, + required: true, + }, + }, + components: { + icon, + }, + directives: { + tooltip, + }, + mixins: [ + timeAgoMixin, + ], + computed: { + ...mapState([ + 'selectedFile', + ]), + }, +}; +</script> + +<template> + <div + class="ide-status-bar"> + <div> + <icon + name="branch" + :size="12"> + </icon> + {{ selectedFile.branchId }} + </div> + <div> + <div + v-if="selectedFile.lastCommit && selectedFile.lastCommit.id"> + Last commit: + <a + v-tooltip + :title="selectedFile.lastCommit.message" + :href="selectedFile.lastCommit.url"> + {{ timeFormated(selectedFile.lastCommit.updatedAt) }} by + {{ selectedFile.lastCommit.author }} + </a> + </div> + </div> + <div + class="text-right"> + {{ selectedFile.name }} + </div> + <div + class="text-right"> + {{ selectedFile.eol }} + </div> + <div + class="text-right"> + {{ file.editorRow }}:{{ file.editorColumn }} + </div> + <div + class="text-right"> + {{ selectedFile.fileLanguage }} + </div> + </div> +</template> diff --git a/app/assets/javascripts/repo/components/new_branch_form.vue b/app/assets/javascripts/ide/components/new_branch_form.vue index ba7090e4a9d..2119d373d31 100644 --- a/app/assets/javascripts/repo/components/new_branch_form.vue +++ b/app/assets/javascripts/ide/components/new_branch_form.vue @@ -44,7 +44,7 @@ this.branchName = ''; if (this.dropdownText) { - this.dropdownText.textContent = this.currentBranch; + this.dropdownText.textContent = this.currentBranchId; } this.toggleDropdown(); diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue new file mode 100644 index 00000000000..d475813c4f7 --- /dev/null +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -0,0 +1,101 @@ +<script> + import newModal from './modal.vue'; + import upload from './upload.vue'; + import icon from '../../../vue_shared/components/icon.vue'; + + export default { + props: { + branch: { + type: String, + required: true, + }, + path: { + type: String, + required: true, + }, + parent: { + type: Object, + default: null, + }, + }, + components: { + icon, + newModal, + upload, + }, + data() { + return { + openModal: false, + modalType: '', + }; + }, + methods: { + createNewItem(type) { + this.modalType = type; + this.openModal = true; + }, + hideModal() { + this.openModal = false; + }, + }, + }; +</script> + +<template> + <div class="repo-new-btn pull-right"> + <div class="dropdown"> + <button + type="button" + class="btn btn-sm btn-default dropdown-toggle add-to-tree" + data-toggle="dropdown" + aria-label="Create new file or directory" + > + <icon + name="plus" + :size="12" + css-classes="pull-left" + /> + <icon + name="arrow-down" + :size="12" + css-classes="pull-left" + /> + </button> + <ul class="dropdown-menu dropdown-menu-right"> + <li> + <a + href="#" + role="button" + @click.prevent="createNewItem('blob')" + > + {{ __('New file') }} + </a> + </li> + <li> + <upload + :branch-id="branch" + :path="path" + :parent="parent" + /> + </li> + <li> + <a + href="#" + role="button" + @click.prevent="createNewItem('tree')" + > + {{ __('New directory') }} + </a> + </li> + </ul> + </div> + <new-modal + v-if="openModal" + :type="modalType" + :branch-id="branch" + :path="path" + :parent="parent" + @hide="hideModal" + /> + </div> +</template> diff --git a/app/assets/javascripts/repo/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index ac1f613bb71..0312f56efbd 100644 --- a/app/assets/javascripts/repo/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -1,10 +1,18 @@ <script> - import { mapActions } from 'vuex'; + import { mapActions, mapState } from 'vuex'; import { __ } from '../../../locale'; - import popupDialog from '../../../vue_shared/components/popup_dialog.vue'; + import modal from '../../../vue_shared/components/modal.vue'; export default { props: { + branchId: { + type: String, + required: true, + }, + parent: { + type: Object, + default: null, + }, type: { type: String, required: true, @@ -20,7 +28,7 @@ }; }, components: { - popupDialog, + modal, }, methods: { ...mapActions([ @@ -28,17 +36,23 @@ ]), createEntryInStore() { this.createTempEntry({ + projectId: this.currentProjectId, + branchId: this.branchId, + parent: this.parent, name: this.entryName.replace(new RegExp(`^${this.path}/`), ''), type: this.type, }); - this.toggleModalOpen(); + this.hideModal(); }, - toggleModalOpen() { - this.$emit('toggle'); + hideModal() { + this.$emit('hide'); }, }, computed: { + ...mapState([ + 'currentProjectId', + ]), modalTitle() { if (this.type === 'tree') { return __('Create new directory'); @@ -68,11 +82,11 @@ </script> <template> - <popup-dialog + <modal :title="modalTitle" :primary-button-label="buttonLabel" kind="success" - @toggle="toggleModalOpen" + @cancel="hideModal" @submit="createEntryInStore" > <form @@ -94,5 +108,5 @@ </div> </fieldset> </form> - </popup-dialog> + </modal> </template> diff --git a/app/assets/javascripts/repo/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue index 14ad32f4ae0..2a2f2a241fc 100644 --- a/app/assets/javascripts/repo/components/new_dropdown/upload.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue @@ -1,12 +1,22 @@ <script> - import { mapActions } from 'vuex'; + import { mapActions, mapState } from 'vuex'; export default { props: { - path: { + branchId: { type: String, required: true, }, + parent: { + type: Object, + default: null, + }, + }, + computed: { + ...mapState([ + 'trees', + 'currentProjectId', + ]), }, methods: { ...mapActions([ @@ -22,6 +32,9 @@ this.createTempEntry({ name, + projectId: this.currentProjectId, + branchId: this.branchId, + parent: this.parent, type: 'blob', content: result, base64: !isText, @@ -42,6 +55,9 @@ openFile() { Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file)); }, + startFileUpload() { + this.$refs.fileUpload.click(); + }, }, mounted() { this.$refs.fileUpload.addEventListener('change', this.openFile); @@ -53,16 +69,19 @@ </script> <template> - <label - role="button" - class="menu-item" - > - {{ __('Upload file') }} + <div> + <a + href="#" + role="button" + @click.prevent="startFileUpload" + > + {{ __('Upload file') }} + </a> <input id="file-upload" type="file" class="hidden" ref="fileUpload" /> - </label> + </div> </template> diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index d3344d0c8dc..979721dcb5a 100644 --- a/app/assets/javascripts/repo/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -2,12 +2,12 @@ import { mapGetters, mapState, mapActions } from 'vuex'; import tooltip from '../../vue_shared/directives/tooltip'; import icon from '../../vue_shared/components/icon.vue'; -import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; +import modal from '../../vue_shared/components/modal.vue'; import commitFilesList from './commit_sidebar/list.vue'; export default { components: { - PopupDialog, + modal, icon, commitFilesList, }, @@ -16,16 +16,17 @@ export default { }, data() { return { - showNewBranchDialog: false, + showNewBranchModal: false, submitCommitsLoading: false, startNewMR: false, commitMessage: '', - collapsed: true, }; }, computed: { ...mapState([ - 'currentBranch', + 'currentProjectId', + 'currentBranchId', + 'rightPanelCollapsed', ]), ...mapGetters([ 'changedFiles', @@ -42,12 +43,13 @@ export default { 'checkCommitStatus', 'commitChanges', 'getTreeData', + 'setPanelCollapsedStatus', ]), makeCommit(newBranch = false) { const createNewBranch = newBranch || this.startNewMR; const payload = { - branch: createNewBranch ? `${this.currentBranch}-${new Date().getTime().toString()}` : this.currentBranch, + branch: createNewBranch ? `${this.currentBranchId}-${new Date().getTime().toString()}` : this.currentBranchId, commit_message: this.commitMessage, actions: this.changedFiles.map(f => ({ action: f.tempFile ? 'create' : 'update', @@ -55,16 +57,21 @@ export default { content: f.content, encoding: f.base64 ? 'base64' : 'text', })), - start_branch: createNewBranch ? this.currentBranch : undefined, + start_branch: createNewBranch ? this.currentBranchId : undefined, }; - this.showNewBranchDialog = false; + this.showNewBranchModal = false; this.submitCommitsLoading = true; this.commitChanges({ payload, newMr: this.startNewMR }) .then(() => { this.submitCommitsLoading = false; - this.getTreeData(); + this.$store.dispatch('getTreeData', { + projectId: this.currentProjectId, + branch: this.currentBranchId, + endpoint: `/tree/${this.currentBranchId}`, + force: true, + }); }) .catch(() => { this.submitCommitsLoading = false; @@ -76,7 +83,7 @@ export default { this.checkCommitStatus() .then((branchChanged) => { if (branchChanged) { - this.showNewBranchDialog = true; + this.showNewBranchModal = true; } else { this.makeCommit(); } @@ -86,50 +93,36 @@ export default { }); }, toggleCollapsed() { - this.collapsed = !this.collapsed; + this.setPanelCollapsedStatus({ + side: 'right', + collapsed: !this.rightPanelCollapsed, + }); }, }, }; </script> <template> -<div - class="multi-file-commit-panel" - :class="{ - 'is-collapsed': collapsed, - }" -> - <popup-dialog - v-if="showNewBranchDialog" +<div class="multi-file-commit-panel-section"> + <modal + v-if="showNewBranchModal" :primary-button-label="__('Create new branch')" kind="primary" :title="__('Branch has changed')" :text="__('This branch has changed since you started editing. Would you like to create a new branch?')" - @toggle="showNewBranchDialog = false" + @cancel="showNewBranchModal = false" @submit="makeCommit(true)" /> - <button - v-if="collapsed" - type="button" - class="btn btn-transparent multi-file-commit-panel-collapse-btn is-collapsed prepend-top-10 append-bottom-10" - @click="toggleCollapsed" - > - <i - aria-hidden="true" - class="fa fa-angle-double-left" - > - </i> - </button> <commit-files-list title="Staged" :file-list="changedFiles" - :collapsed="collapsed" + :collapsed="rightPanelCollapsed" @toggleCollapsed="toggleCollapsed" /> <form class="form-horizontal multi-file-commit-form" @submit.prevent="tryCommit" - v-if="!collapsed" + v-if="!rightPanelCollapsed" > <div class="multi-file-commit-fieldset"> <textarea diff --git a/app/assets/javascripts/repo/components/repo_edit_button.vue b/app/assets/javascripts/ide/components/repo_edit_button.vue index 6c1bb4b8566..42d5d709209 100644 --- a/app/assets/javascripts/repo/components/repo_edit_button.vue +++ b/app/assets/javascripts/ide/components/repo_edit_button.vue @@ -1,10 +1,10 @@ <script> import { mapGetters, mapActions, mapState } from 'vuex'; -import popupDialog from '../../vue_shared/components/popup_dialog.vue'; +import modal from '../../vue_shared/components/modal.vue'; export default { components: { - popupDialog, + modal, }, computed: { ...mapState([ @@ -43,14 +43,14 @@ export default { {{buttonLabel}} </span> </button> - <popup-dialog + <modal v-if="discardPopupOpen" class="text-left" :primary-button-label="__('Discard changes')" kind="warning" :title="__('Are you sure?')" :text="__('Are you sure you want to discard your changes?')" - @toggle="closeDiscardPopup" + @cancel="closeDiscardPopup" @submit="toggleEditMode(true)" /> </div> diff --git a/app/assets/javascripts/repo/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index f37cbd1e961..343fd0a5300 100644 --- a/app/assets/javascripts/repo/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -1,6 +1,6 @@ <script> /* global monaco */ -import { mapGetters, mapActions } from 'vuex'; +import { mapState, mapGetters, mapActions } from 'vuex'; import flash from '../../flash'; import monacoLoader from '../monaco_loader'; import Editor from '../lib/editor'; @@ -24,6 +24,9 @@ export default { ...mapActions([ 'getRawFileData', 'changeFileContent', + 'setFileLanguage', + 'setEditorPosition', + 'setFileEOL', ]), initMonaco() { if (this.shouldHideEditor) return; @@ -43,12 +46,36 @@ export default { const model = this.editor.createModel(this.activeFile); this.editor.attachModel(model); + model.onChange((m) => { this.changeFileContent({ file: this.activeFile, content: m.getValue(), }); }); + + // Handle Cursor Position + this.editor.onPositionChange((instance, e) => { + this.setEditorPosition({ + editorRow: e.position.lineNumber, + editorColumn: e.position.column, + }); + }); + + this.editor.setPosition({ + lineNumber: this.activeFile.editorRow, + column: this.activeFile.editorColumn, + }); + + // Handle File Language + this.setFileLanguage({ + fileLanguage: model.language, + }); + + // Get File eol + this.setFileEOL({ + eol: model.eol, + }); }, }, watch: { @@ -57,12 +84,28 @@ export default { this.initMonaco(); } }, + leftPanelCollapsed() { + this.editor.updateDimensions(); + }, + rightPanelCollapsed() { + this.editor.updateDimensions(); + }, + panelResizing(isResizing) { + if (isResizing === false) { + this.editor.updateDimensions(); + } + }, }, computed: { ...mapGetters([ 'activeFile', 'activeFileExtension', ]), + ...mapState([ + 'leftPanelCollapsed', + 'rightPanelCollapsed', + 'panelResizing', + ]), shouldHideEditor() { return this.activeFile.binary && !this.activeFile.raw; }, @@ -76,13 +119,14 @@ export default { class="blob-viewer-container blob-editor-container" > <div - v-show="shouldHideEditor" + v-if="shouldHideEditor" v-html="activeFile.html" > </div> <div v-show="!shouldHideEditor" ref="editor" + class="multi-file-editor-holder" > </div> </div> diff --git a/app/assets/javascripts/repo/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index 75787ad6103..c8b0441d81c 100644 --- a/app/assets/javascripts/repo/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -1,7 +1,9 @@ <script> - import { mapActions, mapGetters } from 'vuex'; + import { mapState } from 'vuex'; import timeAgoMixin from '../../vue_shared/mixins/timeago'; import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue'; + import newDropdown from './new_dropdown/index.vue'; + import fileIcon from '../../vue_shared/components/file_icon.vue'; export default { mixins: [ @@ -9,26 +11,28 @@ ], components: { skeletonLoadingContainer, + newDropdown, + fileIcon, }, props: { file: { type: Object, required: true, }, + showExtraColumns: { + type: Boolean, + default: false, + }, }, computed: { - ...mapGetters([ - 'isCollapsed', + ...mapState([ + 'leftPanelCollapsed', ]), isSubmodule() { return this.file.type === 'submodule'; }, - fileIcon() { - return { - 'fa-spinner fa-spin': this.file.loading, - [this.file.icon]: !this.file.loading, - 'fa-folder-open': !this.file.loading && this.file.opened, - }; + isTree() { + return this.file.type === 'tree'; }, levelIndentation() { return { @@ -39,13 +43,39 @@ return this.file.id.substr(0, 8); }, submoduleColSpan() { - return !this.isCollapsed && this.isSubmodule ? 3 : 1; + return !this.leftPanelCollapsed && this.isSubmodule ? 3 : 1; + }, + fileClass() { + if (this.file.type === 'blob') { + if (this.file.active) { + return 'file-open file-active'; + } + return this.file.opened ? 'file-open' : ''; + } + return ''; + }, + changedClass() { + return { + 'fa-circle unsaved-icon': this.file.changed || this.file.tempFile, + }; }, }, methods: { - ...mapActions([ - 'clickedTreeRow', - ]), + clickFile(row) { + // Manual Action if a tree is selected/opened + if (this.file.type === 'tree' && this.$router.currentRoute.path === `/project${row.url}`) { + this.$store.dispatch('toggleTreeOpen', { + endpoint: this.file.url, + tree: this.file, + }); + } + this.$router.push(`/project${row.url}`); + }, + }, + updated() { + if (this.file.type === 'blob' && this.file.active) { + this.$el.scrollIntoView(); + } }, }; </script> @@ -53,24 +83,39 @@ <template> <tr class="file" - @click.prevent="clickedTreeRow(file)"> + :class="fileClass" + @click="clickFile(file)"> <td class="multi-file-table-name" :colspan="submoduleColSpan" > - <i - class="fa fa-fw file-icon" - :class="fileIcon" - :style="levelIndentation" - aria-hidden="true" - > - </i> <a - :href="file.url" class="repo-file-name" > + <file-icon + :file-name="file.name" + :loading="file.loading" + :folder="file.type === 'tree'" + :opened="file.opened" + :style="levelIndentation" + :size="16" + > + </file-icon> {{ file.name }} </a> + <new-dropdown + v-if="isTree" + :project-id="file.projectId" + :branch="file.branchId" + :path="file.path" + :parent="file"/> + <i + class="fa" + v-if="changedClass" + :class="changedClass" + aria-hidden="true" + > + </i> <template v-if="isSubmodule && file.id"> @ <span class="commit-sha"> @@ -84,7 +129,7 @@ </template> </td> - <template v-if="!isCollapsed && !isSubmodule"> + <template v-if="showExtraColumns && !isSubmodule"> <td class="multi-file-table-col-commit-message hidden-sm hidden-xs"> <a v-if="file.lastCommit.message" diff --git a/app/assets/javascripts/repo/components/repo_file_buttons.vue b/app/assets/javascripts/ide/components/repo_file_buttons.vue index 34f0d51819a..34f0d51819a 100644 --- a/app/assets/javascripts/repo/components/repo_file_buttons.vue +++ b/app/assets/javascripts/ide/components/repo_file_buttons.vue diff --git a/app/assets/javascripts/repo/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue index 8fa637d771f..7eb840c7608 100644 --- a/app/assets/javascripts/repo/components/repo_loading_file.vue +++ b/app/assets/javascripts/ide/components/repo_loading_file.vue @@ -1,5 +1,5 @@ <script> - import { mapGetters } from 'vuex'; + import { mapState } from 'vuex'; import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue'; export default { @@ -7,8 +7,8 @@ skeletonLoadingContainer, }, computed: { - ...mapGetters([ - 'isCollapsed', + ...mapState([ + 'leftPanelCollapsed', ]), }, }; @@ -24,7 +24,7 @@ :small="true" /> </td> - <template v-if="!isCollapsed"> + <template v-if="!leftPanelCollapsed"> <td class="hidden-sm hidden-xs"> <skeleton-loading-container diff --git a/app/assets/javascripts/repo/components/repo_prev_directory.vue b/app/assets/javascripts/ide/components/repo_prev_directory.vue index a2b305bbd05..7cd359ea4ed 100644 --- a/app/assets/javascripts/repo/components/repo_prev_directory.vue +++ b/app/assets/javascripts/ide/components/repo_prev_directory.vue @@ -1,16 +1,14 @@ <script> - import { mapGetters, mapState, mapActions } from 'vuex'; + import { mapState, mapActions } from 'vuex'; export default { computed: { ...mapState([ 'parentTreeUrl', - ]), - ...mapGetters([ - 'isCollapsed', + 'leftPanelCollapsed', ]), colSpanCondition() { - return this.isCollapsed ? undefined : 3; + return this.leftPanelCollapsed ? undefined : 3; }, }, methods: { diff --git a/app/assets/javascripts/repo/components/repo_preview.vue b/app/assets/javascripts/ide/components/repo_preview.vue index 6ce9267f598..3d1e0297bd5 100644 --- a/app/assets/javascripts/repo/components/repo_preview.vue +++ b/app/assets/javascripts/ide/components/repo_preview.vue @@ -1,6 +1,7 @@ <script> -/* global LineHighlighter */ import { mapGetters } from 'vuex'; +import LineHighlighter from '../../line_highlighter'; +import syntaxHighlight from '../../syntax_highlight'; export default { computed: { @@ -13,7 +14,7 @@ export default { }, methods: { highlightFile() { - $(this.$el).find('.file-content').syntaxHighlight(); + syntaxHighlight($(this.$el).find('.file-content')); }, }, mounted() { diff --git a/app/assets/javascripts/repo/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue index fb29a60df66..e7684884b2c 100644 --- a/app/assets/javascripts/repo/components/repo_tab.vue +++ b/app/assets/javascripts/ide/components/repo_tab.vue @@ -1,5 +1,6 @@ <script> import { mapActions } from 'vuex'; +import fileIcon from '../../vue_shared/components/file_icon.vue'; export default { props: { @@ -8,7 +9,9 @@ export default { required: true, }, }, - + components: { + fileIcon, + }, computed: { closeLabel() { if (this.tab.changed || this.tab.tempFile) { @@ -27,16 +30,18 @@ export default { methods: { ...mapActions([ - 'setFileActive', 'closeFile', ]), + clickFile(tab) { + this.$router.push(`/project${tab.url}`); + }, }, }; </script> <template> <li - @click="setFileActive(tab)" + @click="clickFile(tab)" > <button type="button" @@ -61,6 +66,11 @@ export default { :class="{active : tab.active }" :title="tab.url" > + <file-icon + :file-name="tab.name" + :size="16" + > + </file-icon> {{ tab.name }} </div> </li> diff --git a/app/assets/javascripts/repo/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue index ab0bef4f0ac..ab0bef4f0ac 100644 --- a/app/assets/javascripts/repo/components/repo_tabs.vue +++ b/app/assets/javascripts/ide/components/repo_tabs.vue diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js new file mode 100644 index 00000000000..a9cbf8e370f --- /dev/null +++ b/app/assets/javascripts/ide/ide_router.js @@ -0,0 +1,101 @@ +import Vue from 'vue'; +import VueRouter from 'vue-router'; +import store from './stores'; +import flash from '../flash'; +import { + getTreeEntry, +} from './stores/utils'; + +Vue.use(VueRouter); + +/** + * Routes below /-/ide/: + +/project/h5bp/html5-boilerplate/blob/master +/project/h5bp/html5-boilerplate/blob/master/app/js/test.js + +/project/h5bp/html5-boilerplate/mr/123 +/project/h5bp/html5-boilerplate/mr/123/app/js/test.js + +/workspace/123 +/workspace/project/h5bp/html5-boilerplate/blob/my-special-branch +/workspace/project/h5bp/html5-boilerplate/mr/123 + +/ = /workspace + +/settings +*/ + +// Unfortunately Vue Router doesn't work without at least a fake component +// If you do only data handling +const EmptyRouterComponent = { + render(createElement) { + return createElement('div'); + }, +}; + +const router = new VueRouter({ + mode: 'history', + base: `${gon.relative_url_root}/-/ide/`, + routes: [ + { + path: '/project/:namespace/:project', + component: EmptyRouterComponent, + children: [ + { + path: ':targetmode/:branch/*', + component: EmptyRouterComponent, + }, + { + path: 'mr/:mrid', + component: EmptyRouterComponent, + }, + ], + }, + ], +}); + +router.beforeEach((to, from, next) => { + if (to.params.namespace && to.params.project) { + store.dispatch('getProjectData', { + namespace: to.params.namespace, + projectId: to.params.project, + }) + .then(() => { + const fullProjectId = `${to.params.namespace}/${to.params.project}`; + + if (to.params.branch) { + store.dispatch('getBranchData', { + projectId: fullProjectId, + branchId: to.params.branch, + }); + + store.dispatch('getTreeData', { + projectId: fullProjectId, + branch: to.params.branch, + endpoint: `/tree/${to.params.branch}`, + }) + .then(() => { + if (to.params[0]) { + const treeEntry = getTreeEntry(store, `${to.params.namespace}/${to.params.project}/${to.params.branch}`, to.params[0]); + if (treeEntry) { + store.dispatch('handleTreeEntryAction', treeEntry); + } + } + }) + .catch((e) => { + flash('Error while loading the branch files. Please try again.'); + throw e; + }); + } + }) + .catch((e) => { + flash('Error while loading the project data. Please try again.'); + throw e; + }); + } + + next(); +}); + +export default router; diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js new file mode 100644 index 00000000000..e8a19f47cee --- /dev/null +++ b/app/assets/javascripts/ide/index.js @@ -0,0 +1,31 @@ +import Vue from 'vue'; +import ide from './components/ide.vue'; +import store from './stores'; +import router from './ide_router'; +import Translate from '../vue_shared/translate'; + +function initIde(el) { + if (!el) return null; + + return new Vue({ + el, + store, + router, + components: { + ide, + }, + render(createElement) { + return createElement('ide', { + props: { + emptyStateSvgPath: el.dataset.emptyStateSvgPath, + }, + }); + }, + }); +} + +const ideElement = document.getElementById('ide'); + +Vue.use(Translate); + +initIde(ideElement); diff --git a/app/assets/javascripts/repo/lib/common/disposable.js b/app/assets/javascripts/ide/lib/common/disposable.js index 84b29bdb600..84b29bdb600 100644 --- a/app/assets/javascripts/repo/lib/common/disposable.js +++ b/app/assets/javascripts/ide/lib/common/disposable.js diff --git a/app/assets/javascripts/repo/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js index 23c4811e6c0..14d9fe4771e 100644 --- a/app/assets/javascripts/repo/lib/common/model.js +++ b/app/assets/javascripts/ide/lib/common/model.js @@ -28,6 +28,14 @@ export default class Model { return this.model.uri.toString(); } + get language() { + return this.model.getModeId(); + } + + get eol() { + return this.model.getEOL() === '\n' ? 'LF' : 'CRLF'; + } + get path() { return this.file.path; } diff --git a/app/assets/javascripts/repo/lib/common/model_manager.js b/app/assets/javascripts/ide/lib/common/model_manager.js index fd462252795..fd462252795 100644 --- a/app/assets/javascripts/repo/lib/common/model_manager.js +++ b/app/assets/javascripts/ide/lib/common/model_manager.js diff --git a/app/assets/javascripts/repo/lib/decorations/controller.js b/app/assets/javascripts/ide/lib/decorations/controller.js index 0954b7973c4..0954b7973c4 100644 --- a/app/assets/javascripts/repo/lib/decorations/controller.js +++ b/app/assets/javascripts/ide/lib/decorations/controller.js diff --git a/app/assets/javascripts/repo/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js index dc0b1c95e59..dc0b1c95e59 100644 --- a/app/assets/javascripts/repo/lib/diff/controller.js +++ b/app/assets/javascripts/ide/lib/diff/controller.js diff --git a/app/assets/javascripts/repo/lib/diff/diff.js b/app/assets/javascripts/ide/lib/diff/diff.js index 0e37f5c4704..0e37f5c4704 100644 --- a/app/assets/javascripts/repo/lib/diff/diff.js +++ b/app/assets/javascripts/ide/lib/diff/diff.js diff --git a/app/assets/javascripts/repo/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js index e74c4046330..e74c4046330 100644 --- a/app/assets/javascripts/repo/lib/diff/diff_worker.js +++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js diff --git a/app/assets/javascripts/repo/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js index db499444402..51e202b9348 100644 --- a/app/assets/javascripts/repo/lib/editor.js +++ b/app/assets/javascripts/ide/lib/editor.js @@ -22,6 +22,11 @@ export default class Editor { this.modelManager = new ModelManager(this.monaco), this.decorationsController = new DecorationsController(this), ); + + this.debouncedUpdate = _.debounce(() => { + this.updateDimensions(); + }, 200); + window.addEventListener('resize', this.debouncedUpdate, false); } createInstance(domElement) { @@ -32,6 +37,9 @@ export default class Editor { readOnly: false, contextmenu: true, scrollBeyondLastLine: false, + minimap: { + enabled: false, + }, }), this.dirtyDiffController = new DirtyDiffController( this.modelManager, this.decorationsController, @@ -70,10 +78,32 @@ export default class Editor { dispose() { this.disposable.dispose(); + window.removeEventListener('resize', this.debouncedUpdate); // dispose main monaco instance if (this.instance) { this.instance = null; } } + + updateDimensions() { + this.instance.layout(); + } + + setPosition({ lineNumber, column }) { + this.instance.revealPositionInCenter({ + lineNumber, + column, + }); + this.instance.setPosition({ + lineNumber, + column, + }); + } + + onPositionChange(cb) { + this.disposable.add( + this.instance.onDidChangeCursorPosition(e => cb(this.instance, e)), + ); + } } diff --git a/app/assets/javascripts/repo/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js index 701affc466e..701affc466e 100644 --- a/app/assets/javascripts/repo/lib/editor_options.js +++ b/app/assets/javascripts/ide/lib/editor_options.js diff --git a/app/assets/javascripts/repo/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js index af83a1ec0b4..af83a1ec0b4 100644 --- a/app/assets/javascripts/repo/monaco_loader.js +++ b/app/assets/javascripts/ide/monaco_loader.js diff --git a/app/assets/javascripts/repo/services/index.js b/app/assets/javascripts/ide/services/index.js index 994d325e991..1fb24e93f2e 100644 --- a/app/assets/javascripts/repo/services/index.js +++ b/app/assets/javascripts/ide/services/index.js @@ -23,8 +23,11 @@ export default { return Vue.http.get(file.rawPath, { params: { format: 'json' } }) .then(res => res.text()); }, - getBranchData(projectId, currentBranch) { - return Api.branchSingle(projectId, currentBranch); + getProjectData(namespace, project) { + return Api.project(`${namespace}/${project}`); + }, + getBranchData(projectId, currentBranchId) { + return Api.branchSingle(projectId, currentBranchId); }, createBranch(projectId, payload) { const url = Api.buildUrl(Api.createBranchPath).replace(':id', projectId); diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js new file mode 100644 index 00000000000..335882bb6d7 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions.js @@ -0,0 +1,183 @@ +import Vue from 'vue'; +import { visitUrl } from '../../lib/utils/url_utility'; +import flash from '../../flash'; +import service from '../services'; +import * as types from './mutation_types'; + +export const redirectToUrl = (_, url) => visitUrl(url); + +export const setInitialData = ({ commit }, data) => + commit(types.SET_INITIAL_DATA, data); + +export const closeDiscardPopup = ({ commit }) => + commit(types.TOGGLE_DISCARD_POPUP, false); + +export const discardAllChanges = ({ commit, getters, dispatch }) => { + const changedFiles = getters.changedFiles; + + changedFiles.forEach((file) => { + commit(types.DISCARD_FILE_CHANGES, file); + + if (file.tempFile) { + dispatch('closeFile', { file, force: true }); + } + }); +}; + +export const closeAllFiles = ({ state, dispatch }) => { + state.openFiles.forEach(file => dispatch('closeFile', { file })); +}; + +export const toggleEditMode = ( + { state, commit, getters, dispatch }, + force = false, +) => { + const changedFiles = getters.changedFiles; + + if (changedFiles.length && !force) { + commit(types.TOGGLE_DISCARD_POPUP, true); + } else { + commit(types.TOGGLE_EDIT_MODE); + commit(types.TOGGLE_DISCARD_POPUP, false); + dispatch('toggleBlobView'); + + if (!state.editMode) { + dispatch('discardAllChanges'); + } + } +}; + +export const toggleBlobView = ({ commit, state }) => { + if (state.editMode) { + commit(types.SET_EDIT_MODE); + } else { + commit(types.SET_PREVIEW_MODE); + } +}; + +export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => { + if (side === 'left') { + commit(types.SET_LEFT_PANEL_COLLAPSED, collapsed); + } else { + commit(types.SET_RIGHT_PANEL_COLLAPSED, collapsed); + } +}; + +export const setResizingStatus = ({ commit }, resizing) => { + commit(types.SET_RESIZING_STATUS, resizing); +}; + +export const checkCommitStatus = ({ state }) => + service + .getBranchData(state.currentProjectId, state.currentBranchId) + .then((data) => { + const { id } = data.commit; + const selectedBranch = + state.projects[state.currentProjectId].branches[state.currentBranchId]; + + if (selectedBranch.workingReference !== id) { + return true; + } + + return false; + }) + .catch(() => flash('Error checking branch data. Please try again.')); + +export const commitChanges = ( + { commit, state, dispatch, getters }, + { payload, newMr }, +) => + service + .commit(state.currentProjectId, payload) + .then((data) => { + const { branch } = payload; + if (!data.short_id) { + flash(data.message); + return; + } + + const selectedProject = state.projects[state.currentProjectId]; + const lastCommit = { + commit_path: `${selectedProject.web_url}/commit/${data.id}`, + commit: { + message: data.message, + authored_date: data.committed_date, + }, + }; + + flash( + `Your changes have been committed. Commit ${data.short_id} with ${ + data.stats.additions + } additions, ${data.stats.deletions} deletions.`, + 'notice', + ); + + if (newMr) { + dispatch( + 'redirectToUrl', + `${ + selectedProject.web_url + }/merge_requests/new?merge_request%5Bsource_branch%5D=${branch}`, + ); + } else { + commit(types.SET_BRANCH_WORKING_REFERENCE, { + projectId: state.currentProjectId, + branchId: state.currentBranchId, + reference: data.id, + }); + + getters.changedFiles.forEach((entry) => { + commit(types.SET_LAST_COMMIT_DATA, { + entry, + lastCommit, + }); + }); + + dispatch('discardAllChanges'); + dispatch('closeAllFiles'); + + window.scrollTo(0, 0); + } + }) + .catch(() => flash('Error committing changes. Please try again.')); + +export const createTempEntry = ( + { state, dispatch }, + { projectId, branchId, parent, name, type, content = '', base64 = false }, +) => { + const selectedParent = parent || state.trees[`${projectId}/${branchId}`]; + if (type === 'tree') { + dispatch('createTempTree', { + projectId, + branchId, + parent: selectedParent, + name, + }); + } else if (type === 'blob') { + dispatch('createTempFile', { + projectId, + branchId, + parent: selectedParent, + name, + base64, + content, + }); + } +}; + +export const scrollToTab = () => { + Vue.nextTick(() => { + const tabs = document.getElementById('tabs'); + + if (tabs) { + const tabEl = tabs.querySelector('.active .repo-tab'); + + tabEl.focus(); + } + }); +}; + +export * from './actions/tree'; +export * from './actions/file'; +export * from './actions/project'; +export * from './actions/branch'; diff --git a/app/assets/javascripts/ide/stores/actions/branch.js b/app/assets/javascripts/ide/stores/actions/branch.js new file mode 100644 index 00000000000..32bdf7fec22 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/branch.js @@ -0,0 +1,43 @@ +import service from '../../services'; +import flash from '../../../flash'; +import * as types from '../mutation_types'; + +export const getBranchData = ( + { commit, state, dispatch }, + { projectId, branchId, force = false } = {}, +) => new Promise((resolve, reject) => { + if ((typeof state.projects[`${projectId}`] === 'undefined' || + !state.projects[`${projectId}`].branches[branchId]) + || force) { + service.getBranchData(`${projectId}`, branchId) + .then((data) => { + const { id } = data.commit; + commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data }); + commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); + resolve(data); + }) + .catch(() => { + flash('Error loading branch data. Please try again.'); + reject(new Error(`Branch not loaded - ${projectId}/${branchId}`)); + }); + } else { + resolve(state.projects[`${projectId}`].branches[branchId]); + } +}); + +export const createNewBranch = ({ state, commit }, branch) => service.createBranch( + state.currentProjectId, + { + branch, + ref: state.currentBranchId, + }, +) +.then(res => res.json()) +.then((data) => { + const branchName = data.name; + const url = location.href.replace(state.currentBranchId, branchName); + + if (this.$router) this.$router.push(url); + + commit(types.SET_CURRENT_BRANCH, branchName); +}); diff --git a/app/assets/javascripts/repo/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 5bae4fa826a..0f27d5bf1c3 100644 --- a/app/assets/javascripts/repo/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -2,9 +2,9 @@ import { normalizeHeaders } from '../../../lib/utils/common_utils'; import flash from '../../../flash'; import service from '../../services'; import * as types from '../mutation_types'; +import router from '../../ide_router'; import { findEntry, - pushState, setPageTitle, createTemp, findIndexOfFile, @@ -25,7 +25,7 @@ export const closeFile = ({ commit, state, dispatch }, { file, force = false }) dispatch('setFileActive', nextFileToOpen); } else if (!state.openFiles.length) { - pushState(file.parentTreeUrl); + router.push(`/project/${file.projectId}/tree/${file.branchId}/`); } dispatch('getLastCommitData'); @@ -45,6 +45,9 @@ export const setFileActive = ({ commit, state, getters, dispatch }, file) => { // reset hash for line highlighting location.hash = ''; + + commit(types.SET_CURRENT_PROJECT, file.projectId); + commit(types.SET_CURRENT_BRANCH, file.branchId); }; export const getFileData = ({ state, commit, dispatch }, file) => { @@ -63,8 +66,6 @@ export const getFileData = ({ state, commit, dispatch }, file) => { commit(types.TOGGLE_FILE_OPEN, file); dispatch('setFileActive', file); commit(types.TOGGLE_LOADING, file); - - pushState(file.url); }) .catch(() => { commit(types.TOGGLE_LOADING, file); @@ -82,21 +83,39 @@ export const changeFileContent = ({ commit }, { file, content }) => { commit(types.UPDATE_FILE_CONTENT, { file, content }); }; -export const createTempFile = ({ state, commit, dispatch }, { tree, name, content = '', base64 = '' }) => { +export const setFileLanguage = ({ state, commit }, { fileLanguage }) => { + commit(types.SET_FILE_LANGUAGE, { file: state.selectedFile, fileLanguage }); +}; + +export const setFileEOL = ({ state, commit }, { eol }) => { + commit(types.SET_FILE_EOL, { file: state.selectedFile, eol }); +}; + +export const setEditorPosition = ({ state, commit }, { editorRow, editorColumn }) => { + commit(types.SET_FILE_POSITION, { file: state.selectedFile, editorRow, editorColumn }); +}; + +export const createTempFile = ({ state, commit, dispatch }, { projectId, branchId, parent, name, content = '', base64 = '' }) => { + const path = parent.path !== undefined ? parent.path : ''; + // We need to do the replacement otherwise the web_url + file.url duplicate + const newUrl = `/${projectId}/blob/${branchId}/${path}${path ? '/' : ''}${name}`; const file = createTemp({ - name: name.replace(`${state.path}/`, ''), - path: tree.path, + projectId, + branchId, + name: name.replace(`${path}/`, ''), + path, type: 'blob', - level: tree.level !== undefined ? tree.level + 1 : 0, + level: parent.level !== undefined ? parent.level + 1 : 0, changed: true, content, base64, + url: newUrl, }); - if (findEntry(tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`); + if (findEntry(parent.tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`); commit(types.CREATE_TMP_FILE, { - parent: tree, + parent, file, }); commit(types.TOGGLE_FILE_OPEN, file); @@ -106,5 +125,7 @@ export const createTempFile = ({ state, commit, dispatch }, { tree, name, conten dispatch('toggleEditMode', true); } + router.push(`/project${file.url}`); + return Promise.resolve(file); }; diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js new file mode 100644 index 00000000000..02d4bd87ab0 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -0,0 +1,27 @@ +import service from '../../services'; +import flash from '../../../flash'; +import * as types from '../mutation_types'; + +// eslint-disable-next-line import/prefer-default-export +export const getProjectData = ( + { commit, state, dispatch }, + { namespace, projectId, force = false } = {}, +) => new Promise((resolve, reject) => { + if (!state.projects[`${namespace}/${projectId}`] || force) { + commit(types.TOGGLE_LOADING, state); + service.getProjectData(namespace, projectId) + .then(res => res.data) + .then((data) => { + commit(types.TOGGLE_LOADING, state); + commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); + if (!state.currentProjectId) commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); + resolve(data); + }) + .catch(() => { + flash('Error loading project data. Please try again.'); + reject(new Error(`Project not loaded ${namespace}/${projectId}`)); + }); + } else { + resolve(state.projects[`${namespace}/${projectId}`]); + } +}); diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js new file mode 100644 index 00000000000..25909400a75 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/tree.js @@ -0,0 +1,188 @@ +import { visitUrl } from '../../../lib/utils/url_utility'; +import { normalizeHeaders } from '../../../lib/utils/common_utils'; +import flash from '../../../flash'; +import service from '../../services'; +import * as types from '../mutation_types'; +import router from '../../ide_router'; +import { + setPageTitle, + findEntry, + createTemp, + createOrMergeEntry, +} from '../utils'; + +export const getTreeData = ( + { commit, state, dispatch }, + { endpoint, tree = null, projectId, branch, force = false } = {}, +) => new Promise((resolve, reject) => { + // We already have the base tree so we resolve immediately + if (!tree && state.trees[`${projectId}/${branch}`] && !force) { + resolve(); + } else { + if (tree) commit(types.TOGGLE_LOADING, tree); + const selectedProject = state.projects[projectId]; + // We are merging the web_url that we got on the project info with the endpoint + // we got on the tree entry, as both contain the projectId, we replace it in the tree endpoint + const completeEndpoint = selectedProject.web_url + (endpoint).replace(projectId, ''); + if (completeEndpoint && (!tree || !tree.tempFile)) { + service.getTreeData(completeEndpoint) + .then((res) => { + const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); + + setPageTitle(pageTitle); + + return res.json(); + }) + .then((data) => { + if (!state.isInitialRoot) { + commit(types.SET_ROOT, data.path === '/'); + } + + dispatch('updateDirectoryData', { data, tree, projectId, branch }); + const selectedTree = tree || state.trees[`${projectId}/${branch}`]; + + commit(types.SET_PARENT_TREE_URL, data.parent_tree_url); + commit(types.SET_LAST_COMMIT_URL, { tree: selectedTree, url: data.last_commit_path }); + if (tree) commit(types.TOGGLE_LOADING, selectedTree); + + const prevLastCommitPath = selectedTree.lastCommitPath; + if (prevLastCommitPath !== null) { + dispatch('getLastCommitData', selectedTree); + } + resolve(data); + }) + .catch((e) => { + flash('Error loading tree data. Please try again.'); + if (tree) commit(types.TOGGLE_LOADING, tree); + reject(e); + }); + } else { + resolve(); + } + } +}); + +export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => { + if (tree.opened) { + // send empty data to clear the tree + const data = { trees: [], blobs: [], submodules: [] }; + + dispatch('updateDirectoryData', { data, tree, projectId: tree.projectId, branchId: tree.branchId }); + } else { + dispatch('getTreeData', { endpoint, tree, projectId: tree.projectId, branch: tree.branchId }); + } + + commit(types.TOGGLE_TREE_OPEN, tree); +}; + +export const handleTreeEntryAction = ({ commit, dispatch }, row) => { + if (row.type === 'tree') { + dispatch('toggleTreeOpen', { + endpoint: row.url, + tree: row, + }); + } else if (row.type === 'submodule') { + commit(types.TOGGLE_LOADING, row); + visitUrl(row.url); + } else if (row.type === 'blob' && row.opened) { + dispatch('setFileActive', row); + } else { + dispatch('getFileData', row); + } +}; + +export const createTempTree = ( + { state, commit, dispatch }, + { projectId, branchId, parent, name }, +) => { + let selectedTree = parent; + const dirNames = name.replace(new RegExp(`^${state.path}/`), '').split('/'); + + dirNames.forEach((dirName) => { + const foundEntry = findEntry(selectedTree.tree, 'tree', dirName); + + if (!foundEntry) { + const path = selectedTree.path !== undefined ? selectedTree.path : ''; + const tmpEntry = createTemp({ + projectId, + branchId, + name: dirName, + path, + type: 'tree', + level: selectedTree.level !== undefined ? selectedTree.level + 1 : 0, + tree: [], + url: `/${projectId}/blob/${branchId}/${path}${path ? '/' : ''}${dirName}`, + }); + + commit(types.CREATE_TMP_TREE, { + parent: selectedTree, + tmpEntry, + }); + commit(types.TOGGLE_TREE_OPEN, tmpEntry); + + router.push(`/project${tmpEntry.url}`); + + selectedTree = tmpEntry; + } else { + selectedTree = foundEntry; + } + }); +}; + +export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => { + if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return; + + service.getTreeLastCommit(tree.lastCommitPath) + .then((res) => { + const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null; + + commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath }); + + return res.json(); + }) + .then((data) => { + data.forEach((lastCommit) => { + const entry = findEntry(tree.tree, lastCommit.type, lastCommit.file_name); + + if (entry) { + commit(types.SET_LAST_COMMIT_DATA, { entry, lastCommit }); + } + }); + + dispatch('getLastCommitData', tree); + }) + .catch(() => flash('Error fetching log data.')); +}; + +export const updateDirectoryData = ( + { commit, state }, + { data, tree, projectId, branch }, +) => { + if (!tree) { + const existingTree = state.trees[`${projectId}/${branch}`]; + if (!existingTree) { + commit(types.CREATE_TREE, { treePath: `${projectId}/${branch}` }); + } + } + + const selectedTree = tree || state.trees[`${projectId}/${branch}`]; + const level = selectedTree.level !== undefined ? selectedTree.level + 1 : 0; + const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl; + const createEntry = (entry, type) => createOrMergeEntry({ + tree: selectedTree, + projectId: `${projectId}`, + branchId: branch, + entry, + level, + type, + parentTreeUrl, + }); + + const formattedData = [ + ...data.trees.map(t => createEntry(t, 'tree')), + ...data.submodules.map(m => createEntry(m, 'submodule')), + ...data.blobs.map(b => createEntry(b, 'blob')), + ]; + + commit(types.SET_DIRECTORY_DATA, { tree: selectedTree, data: formattedData }); +}; diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js new file mode 100644 index 00000000000..6b51ccff817 --- /dev/null +++ b/app/assets/javascripts/ide/stores/getters.js @@ -0,0 +1,19 @@ +export const changedFiles = state => state.openFiles.filter(file => file.changed); + +export const activeFile = state => state.openFiles.find(file => file.active) || null; + +export const activeFileExtension = (state) => { + const file = activeFile(state); + return file ? `.${file.path.split('.').pop()}` : ''; +}; + +export const canEditFile = (state) => { + const currentActiveFile = activeFile(state); + + return state.canCommit && + (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary); +}; + +export const addedFiles = state => changedFiles(state).filter(f => f.tempFile); + +export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile); diff --git a/app/assets/javascripts/repo/stores/index.js b/app/assets/javascripts/ide/stores/index.js index 6ac9bfd8189..6ac9bfd8189 100644 --- a/app/assets/javascripts/repo/stores/index.js +++ b/app/assets/javascripts/ide/stores/index.js diff --git a/app/assets/javascripts/repo/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index bc3390f1506..69b218a5e7d 100644 --- a/app/assets/javascripts/repo/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -1,16 +1,28 @@ export const SET_INITIAL_DATA = 'SET_INITIAL_DATA'; export const TOGGLE_LOADING = 'TOGGLE_LOADING'; -export const SET_COMMIT_REF = 'SET_COMMIT_REF'; export const SET_PARENT_TREE_URL = 'SET_PARENT_TREE_URL'; export const SET_ROOT = 'SET_ROOT'; -export const SET_PREVIOUS_URL = 'SET_PREVIOUS_URL'; export const SET_LAST_COMMIT_DATA = 'SET_LAST_COMMIT_DATA'; +export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED'; +export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED'; +export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS'; + +// Project Mutation Types +export const SET_PROJECT = 'SET_PROJECT'; +export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT'; +export const TOGGLE_PROJECT_OPEN = 'TOGGLE_PROJECT_OPEN'; + +// Branch Mutation Types +export const SET_BRANCH = 'SET_BRANCH'; +export const SET_BRANCH_WORKING_REFERENCE = 'SET_BRANCH_WORKING_REFERENCE'; +export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN'; // Tree mutation types export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA'; export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN'; export const CREATE_TMP_TREE = 'CREATE_TMP_TREE'; export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL'; +export const CREATE_TREE = 'CREATE_TREE'; // File mutation types export const SET_FILE_DATA = 'SET_FILE_DATA'; @@ -18,6 +30,9 @@ export const TOGGLE_FILE_OPEN = 'TOGGLE_FILE_OPEN'; export const SET_FILE_ACTIVE = 'SET_FILE_ACTIVE'; export const SET_FILE_RAW_DATA = 'SET_FILE_RAW_DATA'; export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; +export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE'; +export const SET_FILE_POSITION = 'SET_FILE_POSITION'; +export const SET_FILE_EOL = 'SET_FILE_EOL'; export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES'; export const CREATE_TMP_FILE = 'CREATE_TMP_FILE'; @@ -28,3 +43,4 @@ export const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE'; export const TOGGLE_DISCARD_POPUP = 'TOGGLE_DISCARD_POPUP'; export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH'; + diff --git a/app/assets/javascripts/repo/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index ae2ba5bedf7..03d81be10a1 100644 --- a/app/assets/javascripts/repo/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -1,4 +1,5 @@ import * as types from './mutation_types'; +import projectMutations from './mutations/project'; import fileMutations from './mutations/file'; import treeMutations from './mutations/tree'; import branchMutations from './mutations/branch'; @@ -32,29 +33,37 @@ export default { discardPopupOpen, }); }, - [types.SET_COMMIT_REF](state, ref) { - Object.assign(state, { - currentRef: ref, - }); - }, [types.SET_ROOT](state, isRoot) { Object.assign(state, { isRoot, isInitialRoot: isRoot, }); }, - [types.SET_PREVIOUS_URL](state, previousUrl) { + [types.SET_LEFT_PANEL_COLLAPSED](state, collapsed) { + Object.assign(state, { + leftPanelCollapsed: collapsed, + }); + }, + [types.SET_RIGHT_PANEL_COLLAPSED](state, collapsed) { + Object.assign(state, { + rightPanelCollapsed: collapsed, + }); + }, + [types.SET_RESIZING_STATUS](state, resizing) { Object.assign(state, { - previousUrl, + panelResizing: resizing, }); }, [types.SET_LAST_COMMIT_DATA](state, { entry, lastCommit }) { Object.assign(entry.lastCommit, { + id: lastCommit.commit.id, url: lastCommit.commit_path, message: lastCommit.commit.message, + author: lastCommit.commit.author_name, updatedAt: lastCommit.commit.authored_date, }); }, + ...projectMutations, ...fileMutations, ...treeMutations, ...branchMutations, diff --git a/app/assets/javascripts/ide/stores/mutations/branch.js b/app/assets/javascripts/ide/stores/mutations/branch.js new file mode 100644 index 00000000000..04b9582c5bb --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/branch.js @@ -0,0 +1,28 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_CURRENT_BRANCH](state, currentBranchId) { + Object.assign(state, { + currentBranchId, + }); + }, + [types.SET_BRANCH](state, { projectPath, branchName, branch }) { + // Add client side properties + Object.assign(branch, { + treeId: `${projectPath}/${branchName}`, + active: true, + workingReference: '', + }); + + Object.assign(state.projects[projectPath], { + branches: { + [branchName]: branch, + }, + }); + }, + [types.SET_BRANCH_WORKING_REFERENCE](state, { projectId, branchId, reference }) { + Object.assign(state.projects[projectId].branches[branchId], { + workingReference: reference, + }); + }, +}; diff --git a/app/assets/javascripts/repo/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index f9ba80b9dc2..5f3655b0092 100644 --- a/app/assets/javascripts/repo/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -6,6 +6,10 @@ export default { Object.assign(file, { active, }); + + Object.assign(state, { + selectedFile: file, + }); }, [types.TOGGLE_FILE_OPEN](state, file) { Object.assign(file, { @@ -42,6 +46,22 @@ export default { changed, }); }, + [types.SET_FILE_LANGUAGE](state, { file, fileLanguage }) { + Object.assign(file, { + fileLanguage, + }); + }, + [types.SET_FILE_EOL](state, { file, eol }) { + Object.assign(file, { + eol, + }); + }, + [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) { + Object.assign(file, { + editorRow, + editorColumn, + }); + }, [types.DISCARD_FILE_CHANGES](state, file) { Object.assign(file, { content: '', diff --git a/app/assets/javascripts/ide/stores/mutations/project.js b/app/assets/javascripts/ide/stores/mutations/project.js new file mode 100644 index 00000000000..2816562a919 --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/project.js @@ -0,0 +1,23 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_CURRENT_PROJECT](state, currentProjectId) { + Object.assign(state, { + currentProjectId, + }); + }, + [types.SET_PROJECT](state, { projectPath, project }) { + // Add client side properties + Object.assign(project, { + tree: [], + branches: {}, + active: true, + }); + + Object.assign(state, { + projects: Object.assign({}, state.projects, { + [projectPath]: project, + }), + }); + }, +}; diff --git a/app/assets/javascripts/repo/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js index 130221c9fda..4fe438ab465 100644 --- a/app/assets/javascripts/repo/stores/mutations/tree.js +++ b/app/assets/javascripts/ide/stores/mutations/tree.js @@ -6,6 +6,15 @@ export default { opened: !tree.opened, }); }, + [types.CREATE_TREE](state, { treePath }) { + Object.assign(state, { + trees: Object.assign({}, state.trees, { + [treePath]: { + tree: [], + }, + }), + }); + }, [types.SET_DIRECTORY_DATA](state, { data, tree }) { Object.assign(tree, { tree: data, diff --git a/app/assets/javascripts/repo/stores/state.js b/app/assets/javascripts/ide/stores/state.js index 0068834831e..61d12096946 100644 --- a/app/assets/javascripts/repo/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -1,10 +1,10 @@ export default () => ({ canCommit: false, - currentBranch: '', - currentBlobView: 'repo-preview', - currentRef: '', + currentProjectId: '', + currentBranchId: '', + currentBlobView: 'repo-editor', discardPopupOpen: false, - editMode: false, + editMode: true, endpoints: {}, isRoot: false, isInitialRoot: false, @@ -12,13 +12,12 @@ export default () => ({ loading: false, onTopOfBranch: false, openFiles: [], + selectedFile: null, path: '', - project: { - id: 0, - name: '', - url: '', - }, parentTreeUrl: '', - previousUrl: '', - tree: [], + trees: {}, + projects: {}, + leftPanelCollapsed: false, + rightPanelCollapsed: true, + panelResizing: false, }); diff --git a/app/assets/javascripts/repo/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index fae1f4439a9..29e3ab5d040 100644 --- a/app/assets/javascripts/repo/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -2,6 +2,8 @@ export const dataStructure = () => ({ id: '', key: '', type: '', + projectId: '', + branchId: '', name: '', url: '', path: '', @@ -15,9 +17,11 @@ export const dataStructure = () => ({ changed: false, lastCommitPath: '', lastCommit: { + id: '', url: '', message: '', updatedAt: '', + author: '', }, tree_url: '', blamePath: '', @@ -31,11 +35,17 @@ export const dataStructure = () => ({ parentTreeUrl: '', renderError: false, base64: false, + editorRow: 1, + editorColumn: 1, + fileLanguage: '', + eol: '', }); export const decorateData = (entity) => { const { id, + projectId, + branchId, type, url, name, @@ -56,6 +66,8 @@ export const decorateData = (entity) => { return { ...dataStructure(), id, + projectId, + branchId, key: `${name}-${type}-${id}`, type, name, @@ -75,24 +87,51 @@ export const decorateData = (entity) => { }; }; -export const findEntry = (state, type, name) => state.tree.find( +/* + Takes the multi-dimensional tree and returns a flattened array. + This allows for the table to recursively render the table rows but keeps the data + structure nested to make it easier to add new files/directories. +*/ +export const treeList = (state, treeId) => { + const baseTree = state.trees[treeId]; + if (baseTree) { + const mapTree = arr => (!arr.tree || !arr.tree.length ? + [] : _.map(arr.tree, a => [a, mapTree(a)])); + + return _.chain(baseTree.tree) + .map(arr => [arr, mapTree(arr)]) + .flatten() + .value(); + } + return []; +}; + +export const getTree = state => (namespace, projectId, branch) => state.trees[`${namespace}/${projectId}/${branch}`]; + +export const getTreeEntry = (store, treeId, path) => { + const fileList = treeList(store.state, treeId); + return fileList ? fileList.find(file => file.path === path) : null; +}; + +export const findEntry = (tree, type, name) => tree.find( f => f.type === type && f.name === name, ); + export const findIndexOfFile = (state, file) => state.findIndex(f => f.path === file.path); export const setPageTitle = (title) => { document.title = title; }; -export const pushState = (url) => { - history.pushState({ url }, '', url); -}; - -export const createTemp = ({ name, path, type, level, changed, content, base64 }) => { +export const createTemp = ({ + projectId, branchId, name, path, type, level, changed, content, base64, url, +}) => { const treePath = path ? `${path}/${name}` : name; return decorateData({ id: new Date().getTime().toString(), + projectId, + branchId, name, type, tempFile: true, @@ -104,11 +143,18 @@ export const createTemp = ({ name, path, type, level, changed, content, base64 } level, base64, renderError: base64, + url, }); }; -export const createOrMergeEntry = ({ tree, entry, type, parentTreeUrl, level }) => { - const found = findEntry(tree, type, entry.name); +export const createOrMergeEntry = ({ tree, + projectId, + branchId, + entry, + type, + parentTreeUrl, + level }) => { + const found = findEntry(tree.tree || tree, type, entry.name); if (found) { return Object.assign({}, found, { @@ -120,6 +166,8 @@ export const createOrMergeEntry = ({ tree, entry, type, parentTreeUrl, level }) return decorateData({ ...entry, + projectId, + branchId, type, parentTreeUrl, level, diff --git a/app/assets/javascripts/image_diff/helpers/badge_helper.js b/app/assets/javascripts/image_diff/helpers/badge_helper.js index 6a6a668308d..eddaeda9578 100644 --- a/app/assets/javascripts/image_diff/helpers/badge_helper.js +++ b/app/assets/javascripts/image_diff/helpers/badge_helper.js @@ -19,12 +19,9 @@ export function addImageBadge(containerEl, { coordinate, badgeText, noteId }) { } export function addImageCommentBadge(containerEl, { coordinate, noteId }) { - const buttonEl = createImageBadge(noteId, coordinate, ['image-comment-badge', 'inverted']); - const iconEl = document.createElement('i'); - iconEl.className = 'fa fa-comment-o'; - iconEl.setAttribute('aria-label', 'comment'); + const buttonEl = createImageBadge(noteId, coordinate, ['image-comment-badge']); + buttonEl.innerHTML = gl.utils.spriteIcon('image-comment-dark'); - buttonEl.appendChild(iconEl); containerEl.appendChild(buttonEl); } diff --git a/app/assets/javascripts/init_issuable_sidebar.js b/app/assets/javascripts/init_issuable_sidebar.js index ada693afc46..e61b37a2d1f 100644 --- a/app/assets/javascripts/init_issuable_sidebar.js +++ b/app/assets/javascripts/init_issuable_sidebar.js @@ -1,8 +1,9 @@ /* eslint-disable no-new */ -/* global MilestoneSelect */ + +import MilestoneSelect from './milestone_select'; import LabelsSelect from './labels_select'; import IssuableContext from './issuable_context'; -/* global Sidebar */ +import Sidebar from './right_sidebar'; import DueDateSelectors from './due_date_select'; @@ -15,5 +16,5 @@ export default () => { new LabelsSelect(); new IssuableContext(sidebarOptions.currentUser); new DueDateSelectors(); - window.sidebar = new Sidebar(); + Sidebar.initialize(); }; diff --git a/app/assets/javascripts/init_legacy_filters.js b/app/assets/javascripts/init_legacy_filters.js index 2cbb70220d0..b6ff97d1279 100644 --- a/app/assets/javascripts/init_legacy_filters.js +++ b/app/assets/javascripts/init_legacy_filters.js @@ -1,9 +1,9 @@ /* eslint-disable no-new */ import LabelsSelect from './labels_select'; -/* global MilestoneSelect */ import subscriptionSelect from './subscription_select'; import UsersSelect from './users_select'; import issueStatusSelect from './issue_status_select'; +import MilestoneSelect from './milestone_select'; export default () => { new UsersSelect(); diff --git a/app/assets/javascripts/init_notes.js b/app/assets/javascripts/init_notes.js index 3a8b4360cb6..882aedfcc76 100644 --- a/app/assets/javascripts/init_notes.js +++ b/app/assets/javascripts/init_notes.js @@ -1,4 +1,4 @@ -/* global Notes */ +import Notes from './notes'; export default () => { const dataEl = document.querySelector('.js-notes-data'); @@ -10,5 +10,7 @@ export default () => { autocomplete, } = JSON.parse(dataEl.innerHTML); - window.notes = new Notes(notesUrl, notesIds, now, diffView, autocomplete); + // Create a singleton so that we don't need to assign + // into the window object, we can just access the current isntance with Notes.instance + Notes.initialize(notesUrl, notesIds, now, diffView, autocomplete); }; diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js index ba2b6737988..2056efe701b 100644 --- a/app/assets/javascripts/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -1,8 +1,7 @@ /* eslint-disable class-methods-use-this, no-new */ -/* global MilestoneSelect */ import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; -import './milestone_select'; +import MilestoneSelect from './milestone_select'; import issueStatusSelect from './issue_status_select'; import subscriptionSelect from './subscription_select'; import LabelsSelect from './labels_select'; @@ -21,7 +20,7 @@ export default class IssuableBulkUpdateSidebar { } initDomElements() { - this.$page = $('.page-with-sidebar'); + this.$page = $('.layout-page'); this.$sidebar = $('.right-sidebar'); this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar'); this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide'); diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 7de07e9403d..411c820cc43 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -8,18 +8,7 @@ import IssuablesHelper from './helpers/issuables_helper'; export default class Issue { constructor() { - if ($('a.btn-close').length) { - this.taskList = new TaskList({ - dataType: 'issue', - fieldName: 'description', - selector: '.detail-page-description', - onSuccess: (result) => { - document.querySelector('#task_status').innerText = result.task_status; - document.querySelector('#task_status_short').innerText = result.task_status_short; - } - }); - this.initIssueBtnEventListeners(); - } + if ($('a.btn-close').length) this.initIssueBtnEventListeners(); Issue.$btnNewBranch = $('#new-branch'); Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap'); @@ -59,7 +48,7 @@ export default class Issue { }) .fail(() => new Flash(issueFailMessage)) .done((data) => { - const isClosedBadge = $('div.status-box-closed'); + const isClosedBadge = $('div.status-box-issue-closed'); const isOpenBadge = $('div.status-box-open'); const projectIssuesCounter = $('.issue_counter'); diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 5bdc7c99503..fc10a43d1bf 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -1,5 +1,6 @@ <script> import Visibility from 'visibilityjs'; +import { visitUrl } from '../../lib/utils/url_utility'; import Poll from '../../lib/utils/poll'; import eventHub from '../event_hub'; import Service from '../services/index'; @@ -8,7 +9,7 @@ import titleComponent from './title.vue'; import descriptionComponent from './description.vue'; import editedComponent from './edited.vue'; import formComponent from './form.vue'; -import '../../lib/utils/url_utility'; +import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor'; export default { props: { @@ -31,7 +32,7 @@ export default { showInlineEditButton: { type: Boolean, required: false, - default: false, + default: true, }, showDeleteButton: { type: Boolean, @@ -149,6 +150,11 @@ export default { editedComponent, formComponent, }, + + mixins: [ + recaptchaModalImplementor, + ], + methods: { openForm() { if (!this.showForm) { @@ -164,34 +170,49 @@ export default { closeForm() { this.showForm = false; }, + updateIssuable() { - this.service.updateIssuable(this.store.formState) - .then(res => res.json()) + return this.service.updateIssuable(this.store.formState) + .then(res => res.data) + .then(data => this.checkForSpam(data)) .then((data) => { if (location.pathname !== data.web_url) { - gl.utils.visitUrl(data.web_url); + visitUrl(data.web_url); } return this.service.getData(); }) - .then(res => res.json()) + .then(res => res.data) .then((data) => { this.store.updateState(data); eventHub.$emit('close.form'); }) - .catch(() => { - eventHub.$emit('close.form'); - window.Flash(`Error updating ${this.issuableType}`); + .catch((error) => { + if (error && error.name === 'SpamError') { + this.openRecaptcha(); + } else { + eventHub.$emit('close.form'); + window.Flash(`Error updating ${this.issuableType}`); + } }); }, + + closeRecaptchaModal() { + this.store.setFormState({ + updateLoading: false, + }); + + this.closeRecaptcha(); + }, + deleteIssuable() { this.service.deleteIssuable() - .then(res => res.json()) + .then(res => res.data) .then((data) => { // Stop the poll so we don't get 404's with the issuable not existing this.poll.stop(); - gl.utils.visitUrl(data.web_url); + visitUrl(data.web_url); }) .catch(() => { eventHub.$emit('close.form'); @@ -204,7 +225,7 @@ export default { this.poll = new Poll({ resource: this.service, method: 'getData', - successCallback: res => res.json().then(data => this.store.updateState(data)), + successCallback: res => this.store.updateState(res.data), errorCallback(err) { throw new Error(err); }, @@ -237,9 +258,9 @@ export default { </script> <template> - <div> +<div> + <div v-if="canUpdate && showForm"> <form-component - v-if="canUpdate && showForm" :form-state="formState" :can-destroy="canDestroy" :issuable-templates="issuableTemplates" @@ -251,30 +272,37 @@ export default { :can-attach-file="canAttachFile" :enable-autocomplete="enableAutocomplete" /> - <div v-else> - <title-component - :issuable-ref="issuableRef" - :can-update="canUpdate" - :title-html="state.titleHtml" - :title-text="state.titleText" - :show-inline-edit-button="showInlineEditButton" - /> - <description-component - v-if="state.descriptionHtml" - :can-update="canUpdate" - :description-html="state.descriptionHtml" - :description-text="state.descriptionText" - :updated-at="state.updatedAt" - :task-status="state.taskStatus" - :issuable-type="issuableType" - :update-url="updateEndpoint" - /> - <edited-component - v-if="hasUpdated" - :updated-at="state.updatedAt" - :updated-by-name="state.updatedByName" - :updated-by-path="state.updatedByPath" - /> - </div> + + <recaptcha-modal + v-show="showRecaptcha" + :html="recaptchaHTML" + @close="closeRecaptchaModal" + /> + </div> + <div v-else> + <title-component + :issuable-ref="issuableRef" + :can-update="canUpdate" + :title-html="state.titleHtml" + :title-text="state.titleText" + :show-inline-edit-button="showInlineEditButton" + /> + <description-component + v-if="state.descriptionHtml" + :can-update="canUpdate" + :description-html="state.descriptionHtml" + :description-text="state.descriptionText" + :updated-at="state.updatedAt" + :task-status="state.taskStatus" + :issuable-type="issuableType" + :update-url="updateEndpoint" + /> + <edited-component + v-if="hasUpdated" + :updated-at="state.updatedAt" + :updated-by-name="state.updatedByName" + :updated-by-path="state.updatedByPath" + /> </div> +</div> </template> diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index b7559ced946..c3f2bf130bb 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,9 +1,14 @@ <script> import animateMixin from '../mixins/animate'; import TaskList from '../../task_list'; + import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor'; export default { - mixins: [animateMixin], + mixins: [ + animateMixin, + recaptchaModalImplementor, + ], + props: { canUpdate: { type: Boolean, @@ -51,6 +56,7 @@ this.updateTaskStatusText(); }, }, + methods: { renderGFM() { $(this.$refs['gfm-content']).renderGFM(); @@ -61,9 +67,19 @@ dataType: this.issuableType, fieldName: 'description', selector: '.detail-page-description', + onSuccess: this.taskListUpdateSuccess.bind(this), }); } }, + + taskListUpdateSuccess(data) { + try { + this.checkForSpam(data); + } catch (error) { + if (error && error.name === 'SpamError') this.openRecaptcha(); + } + }, + updateTaskStatusText() { const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/); const $issuableHeader = $('.issuable-meta'); @@ -109,5 +125,11 @@ :data-update-url="updateUrl" > </textarea> + + <recaptcha-modal + v-show="showRecaptcha" + :html="recaptchaHTML" + @close="closeRecaptcha" + /> </div> </template> diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue index 52fe4ecd08b..4e577546551 100644 --- a/app/assets/javascripts/issue_show/components/fields/description.vue +++ b/app/assets/javascripts/issue_show/components/fields/description.vue @@ -53,7 +53,7 @@ <textarea id="issue-description" class="note-textarea js-gfm-input js-autosize markdown-area" - data-supports-quick-actionss="false" + data-supports-quick-actions="false" aria-label="Description" v-model="formState.description" ref="textarea" diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue index a363d06d950..b7e6eadd440 100644 --- a/app/assets/javascripts/issue_show/components/title.vue +++ b/app/assets/javascripts/issue_show/components/title.vue @@ -79,7 +79,7 @@ v-tooltip v-if="showInlineEditButton && canUpdate" type="button" - class="btn btn-default btn-edit btn-svg" + class="btn btn-default btn-edit btn-svg js-issuable-edit" v-html="pencilIcon" title="Edit title and description" data-placement="bottom" diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js index a21ce41e65e..75dfdedcf1b 100644 --- a/app/assets/javascripts/issue_show/index.js +++ b/app/assets/javascripts/issue_show/index.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import eventHub from './event_hub'; import issuableApp from './components/app.vue'; import '../vue_shared/vue_resource_interceptor'; @@ -7,12 +6,6 @@ document.addEventListener('DOMContentLoaded', () => { const initialDataEl = document.getElementById('js-issuable-app-initial-data'); const props = JSON.parse(initialDataEl.innerHTML.replace(/"/g, '"')); - $('.issuable-edit').on('click', (e) => { - e.preventDefault(); - - eventHub.$emit('open.form'); - }); - return new Vue({ el: document.getElementById('js-issuable-app'), components: { diff --git a/app/assets/javascripts/issue_show/services/index.js b/app/assets/javascripts/issue_show/services/index.js index 6f0fd0b1768..9546eb22c27 100644 --- a/app/assets/javascripts/issue_show/services/index.js +++ b/app/assets/javascripts/issue_show/services/index.js @@ -1,29 +1,20 @@ -import Vue from 'vue'; -import VueResource from 'vue-resource'; - -Vue.use(VueResource); +import axios from '../../lib/utils/axios_utils'; export default class Service { constructor(endpoint) { - this.endpoint = endpoint; - - this.resource = Vue.resource(`${this.endpoint}.json`, {}, { - realtimeChanges: { - method: 'GET', - url: `${this.endpoint}/realtime_changes`, - }, - }); + this.endpoint = `${endpoint}.json`; + this.realtimeEndpoint = `${endpoint}/realtime_changes`; } getData() { - return this.resource.realtimeChanges(); + return axios.get(this.realtimeEndpoint); } deleteIssuable() { - return this.resource.delete(); + return axios.delete(this.endpoint); } updateIssuable(data) { - return this.resource.update(data); + return axios.put(this.endpoint, data); } } diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js index 85ea6330ee9..8f32dcc94e2 100644 --- a/app/assets/javascripts/job.js +++ b/app/assets/javascripts/job.js @@ -1,7 +1,9 @@ import _ from 'underscore'; +import { visitUrl } from './lib/utils/url_utility'; import bp from './breakpoints'; -import { bytesToKiB } from './lib/utils/number_utils'; +import { numberToHumanSize } from './lib/utils/number_utils'; import { setCiStatusFavicon } from './lib/utils/common_utils'; +import { timeFor } from './lib/utils/datetime_utility'; export default class Job { constructor(options) { @@ -94,14 +96,15 @@ export default class Job { // eslint-disable-next-line class-methods-use-this canScroll() { - return this.$document.height() > this.$window.height(); + return $(document).height() > $(window).height(); } toggleScroll() { - const currentPosition = this.$document.scrollTop(); - const scrollHeight = this.$document.height(); + const $document = $(document); + const currentPosition = $document.scrollTop(); + const scrollHeight = $document.height(); - const windowHeight = this.$window.height(); + const windowHeight = $(window).height(); if (this.canScroll()) { if (currentPosition > 0 && (scrollHeight - currentPosition !== windowHeight)) { @@ -125,18 +128,22 @@ export default class Job { this.toggleDisableButton(this.$scrollBottomBtn, true); } } - + // eslint-disable-next-line class-methods-use-this isScrolledToBottom() { - const currentPosition = this.$document.scrollTop(); - const scrollHeight = this.$document.height(); + const $document = $(document); + + const currentPosition = $document.scrollTop(); + const scrollHeight = $document.height(); + + const windowHeight = $(window).height(); - const windowHeight = this.$window.height(); return scrollHeight - currentPosition === windowHeight; } // eslint-disable-next-line class-methods-use-this scrollDown() { - this.$document.scrollTop(this.$document.height()); + const $document = $(document); + $document.scrollTop($document.height()); } scrollToBottom() { @@ -146,7 +153,7 @@ export default class Job { } scrollToTop() { - this.$document.scrollTop(0); + $(document).scrollTop(0); this.hasBeenScrolled = true; this.toggleScroll(); } @@ -191,7 +198,7 @@ export default class Job { // we need to show a message warning the user about that. if (this.logBytes < log.total) { // size is in bytes, we need to calculate KiB - const size = bytesToKiB(this.logBytes); + const size = numberToHumanSize(this.logBytes); $('.js-truncated-info-size').html(`${size}`); this.$truncatedInfo.removeClass('hidden'); } else { @@ -209,7 +216,7 @@ export default class Job { } if (log.status !== this.buildStatus) { - gl.utils.visitUrl(this.pagePath); + visitUrl(this.pagePath); } }) .fail(() => { @@ -260,7 +267,7 @@ export default class Job { if ($date.length) { const date = $date.text(); return $date.text( - gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '), + timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3'))), ); } } diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue index 6d671845f8e..c660828b30e 100644 --- a/app/assets/javascripts/jobs/components/header.vue +++ b/app/assets/javascripts/jobs/components/header.vue @@ -30,6 +30,9 @@ shouldRenderContent() { return !this.isLoading && Object.keys(this.job).length; }, + jobStarted() { + return this.job.started; + }, }, methods: { getActions() { @@ -63,8 +66,9 @@ :time="job.created_at" :user="job.user" :actions="actions" - :hasSidebarButton="true" - /> + :has-sidebar-button="true" + :should-render-triggered-label="jobStarted" + /> <loading-icon v-if="isLoading" size="2" diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index a6f82b247e2..ab3cc29146a 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,59 +1,51 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, vars-on-top, max-len */ -import _ from 'underscore'; -import Cookies from 'js-cookie'; import ContextualSidebar from './contextual_sidebar'; import initFlyOutNav from './fly_out_nav'; -(function() { - var hideEndFade; +function hideEndFade($scrollingTabs) { + $scrollingTabs.each(function scrollTabsLoop() { + const $this = $(this); + $this.siblings('.fade-right').toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')); + }); +} - hideEndFade = function($scrollingTabs) { - return $scrollingTabs.each(function() { - var $this; - $this = $(this); - return $this.siblings('.fade-right').toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')); - }); - }; +export default function initLayoutNav() { + const contextualSidebar = new ContextualSidebar(); + contextualSidebar.bindEvents(); + + initFlyOutNav(); $(document).on('init.scrolling-tabs', () => { const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized'); $scrollingTabs.addClass('is-initialized'); - hideEndFade($scrollingTabs); - $(window).off('resize.nav').on('resize.nav', function() { - return hideEndFade($scrollingTabs); - }); - $scrollingTabs.off('scroll').on('scroll', function(event) { - var $this, currentPosition, maxPosition; - $this = $(this); - currentPosition = $this.scrollLeft(); - maxPosition = $this.prop('scrollWidth') - $this.outerWidth(); + $(window).on('resize.nav', () => { + hideEndFade($scrollingTabs); + }).trigger('resize.nav'); + + $scrollingTabs.on('scroll', function tabsScrollEvent() { + const $this = $(this); + const currentPosition = $this.scrollLeft(); + const maxPosition = $this.prop('scrollWidth') - $this.outerWidth(); + $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0); - return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1); + $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1); }); - $scrollingTabs.each(function () { - var $this = $(this); - var scrollingTabWidth = $this.width(); - var $active = $this.find('.active'); - var activeWidth = $active.width(); + $scrollingTabs.each(function scrollTabsEachLoop() { + const $this = $(this); + const scrollingTabWidth = $this.width(); + const $active = $this.find('.active'); + const activeWidth = $active.width(); if ($active.length) { - var offset = $active.offset().left + activeWidth; + const offset = $active.offset().left + activeWidth; if (offset > scrollingTabWidth - 30) { - var scrollLeft = scrollingTabWidth / 2; - scrollLeft = (offset - scrollLeft) - (activeWidth / 2); + const scrollLeft = (offset - (scrollingTabWidth / 2)) - (activeWidth / 2); + $this.scrollLeft(scrollLeft); } } }); - }); - - $(() => { - const contextualSidebar = new ContextualSidebar(); - contextualSidebar.bindEvents(); - - initFlyOutNav(); - }); -}).call(window); + }).trigger('init.scrolling-tabs'); +} diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js index 7aeeca3b283..8aff0556011 100644 --- a/app/assets/javascripts/lib/utils/axios_utils.js +++ b/app/assets/javascripts/lib/utils/axios_utils.js @@ -2,6 +2,8 @@ import axios from 'axios'; import csrf from './csrf'; axios.defaults.headers.common[csrf.headerKey] = csrf.token; +// Used by Rails to check if it is a valid XHR request +axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; // Maintain a global counter for active requests // see: spec/support/wait_for_requests.rb diff --git a/app/assets/javascripts/lib/utils/cache.js b/app/assets/javascripts/lib/utils/cache.js index 3141f1eeafc..596bd1e388a 100644 --- a/app/assets/javascripts/lib/utils/cache.js +++ b/app/assets/javascripts/lib/utils/cache.js @@ -1,4 +1,4 @@ -class Cache { +export default class Cache { constructor() { this.internalStorage = { }; } @@ -15,5 +15,3 @@ class Cache { delete this.internalStorage[key]; } } - -export default Cache; diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 33cc807912c..03918762842 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -1,3 +1,4 @@ +import { getLocationHash } from './url_utility'; export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index]; @@ -65,7 +66,7 @@ export const disableButtonIfEmptyField = (fieldSelector, buttonSelector, eventNa // automatically adjust scroll position for hash urls taking the height of the navbar into account // https://github.com/twitter/bootstrap/issues/1768 export const handleLocationHash = () => { - let hash = window.gl.utils.getLocationHash(); + let hash = getLocationHash(); if (!hash) return; // This is required to handle non-unicode characters in hash @@ -231,7 +232,7 @@ export const nodeMatchesSelector = (node, selector) => { export const normalizeHeaders = (headers) => { const upperCaseHeaders = {}; - Object.keys(headers).forEach((e) => { + Object.keys(headers || {}).forEach((e) => { upperCaseHeaders[e.toUpperCase()] = headers[e]; }); diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js index 7a72509d234..9a61003ef30 100644 --- a/app/assets/javascripts/lib/utils/constants.js +++ b/app/assets/javascripts/lib/utils/constants.js @@ -1,3 +1,2 @@ -/* eslint-disable import/prefer-default-export */ export const BYTES_IN_KIB = 1024; export const HIDDEN_CLASS = 'hidden'; diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index d0578b230b1..1fa6715180e 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,132 +1,141 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */ - import timeago from 'timeago.js'; import dateFormat from 'vendor/date.format'; import { pluralize } from './text_utility'; - import { - lang, + languageCode, s__, } from '../../locale'; window.timeago = timeago; window.dateFormat = dateFormat; -(function() { - (function(w) { - var base; - var timeagoInstance; +/** + * Given a date object returns the day of the week in English + * @param {date} date + * @returns {String} + */ +export const getDayName = date => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][date.getDay()]; - if (w.gl == null) { - w.gl = {}; - } - if ((base = w.gl).utils == null) { - base.utils = {}; - } - w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; +/** + * @example + * dateFormat('2017-12-05','mmm d, yyyy h:MMtt Z' ) -> "Dec 5, 2017 12:00am GMT+0000" + * @param {date} datetime + * @returns {String} + */ +export const formatDate = datetime => dateFormat(datetime, 'mmm d, yyyy h:MMtt Z'); - w.gl.utils.formatDate = function(datetime) { - return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z'); - }; +/** + * Timeago uses underscores instead of dashes to separate language from country code. + * + * see https://github.com/hustcc/timeago.js/tree/v3.0.0/locales + */ +const timeagoLanguageCode = languageCode().replace(/-/g, '_'); - w.gl.utils.getDayName = function(date) { - return this.days[date.getDay()]; +let timeagoInstance; + +/** + * Sets a timeago Instance + */ +export function getTimeago() { + if (!timeagoInstance) { + const localeRemaining = function getLocaleRemaining(number, index) { + return [ + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], + [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], + [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], + [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')], + [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')], + [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')], + [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')], + [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')], + [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')], + [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')], + [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')], + [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')], + [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')], + ][index]; + }; + const locale = function getLocale(number, index) { + return [ + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], + [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], + [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], + [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')], + [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')], + [s__('Timeago|a day ago'), s__('Timeago|in 1 day')], + [s__('Timeago|%s days ago'), s__('Timeago|in %s days')], + [s__('Timeago|a week ago'), s__('Timeago|in 1 week')], + [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')], + [s__('Timeago|a month ago'), s__('Timeago|in 1 month')], + [s__('Timeago|%s months ago'), s__('Timeago|in %s months')], + [s__('Timeago|a year ago'), s__('Timeago|in 1 year')], + [s__('Timeago|%s years ago'), s__('Timeago|in %s years')], + ][index]; }; - w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) { - $timeagoEls.each((i, el) => { - if (setTimeago) { - // Recreate with custom template - $(el).tooltip({ - template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' - }); - } + timeago.register(timeagoLanguageCode, locale); + timeago.register(`${timeagoLanguageCode}-remaining`, localeRemaining); + timeagoInstance = timeago(); + } - el.classList.add('js-timeago-render'); - }); + return timeagoInstance; +} - gl.utils.renderTimeago($timeagoEls); - }; +/** + * For the given element, renders a timeago instance. + * @param {jQuery} $els + */ +export const renderTimeago = ($els) => { + const timeagoEls = $els || document.querySelectorAll('.js-timeago-render'); - w.gl.utils.getTimeago = function() { - var locale; - - if (!timeagoInstance) { - const localeRemaining = function(number, index) { - return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], - [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], - [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], - [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], - [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')], - [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')], - [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')], - [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')], - [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')], - [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')], - [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')], - [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')], - [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')], - [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')] - ][index]; - }; - locale = function(number, index) { - return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], - [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], - [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], - [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], - [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')], - [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')], - [s__('Timeago|a day ago'), s__('Timeago|in 1 day')], - [s__('Timeago|%s days ago'), s__('Timeago|in %s days')], - [s__('Timeago|a week ago'), s__('Timeago|in 1 week')], - [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')], - [s__('Timeago|a month ago'), s__('Timeago|in 1 month')], - [s__('Timeago|%s months ago'), s__('Timeago|in %s months')], - [s__('Timeago|a year ago'), s__('Timeago|in 1 year')], - [s__('Timeago|%s years ago'), s__('Timeago|in %s years')] - ][index]; - }; - - timeago.register(lang, locale); - timeago.register(`${lang}-remaining`, localeRemaining); - timeagoInstance = timeago(); - } - - return timeagoInstance; - }; + // timeago.js sets timeouts internally for each timeago value to be updated in real time + getTimeago().render(timeagoEls, timeagoLanguageCode); +}; - w.gl.utils.timeFor = function(time, suffix, expiredLabel) { - var timefor; - if (!time) { - return ''; - } - if (new Date(time) < new Date()) { - expiredLabel || (expiredLabel = s__('Timeago|Past due')); - timefor = expiredLabel; - } else { - timefor = gl.utils.getTimeago().format(time, `${lang}-remaining`).trim(); - } - return timefor; - }; +/** + * For the given elements, sets a tooltip with a formatted date. + * @param {jQuery} + * @param {Boolean} setTimeago + */ +export const localTimeAgo = ($timeagoEls, setTimeago = true) => { + $timeagoEls.each((i, el) => { + if (setTimeago) { + // Recreate with custom template + $(el).tooltip({ + template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', + }); + } - w.gl.utils.renderTimeago = function($els) { - const timeagoEls = $els || document.querySelectorAll('.js-timeago-render'); + el.classList.add('js-timeago-render'); + }); - // timeago.js sets timeouts internally for each timeago value to be updated in real time - gl.utils.getTimeago().render(timeagoEls, lang); - }; + renderTimeago($timeagoEls); +}; - w.gl.utils.getDayDifference = function(a, b) { - var millisecondsPerDay = 1000 * 60 * 60 * 24; - var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()); - var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); +/** + * Returns remaining or passed time over the given time. + * @param {*} time + * @param {*} expiredLabel + */ +export const timeFor = (time, expiredLabel) => { + if (!time) { + return ''; + } + if (new Date(time) < new Date()) { + return expiredLabel || s__('Timeago|Past due'); + } + return getTimeago().format(time, `${timeagoLanguageCode}-remaining`).trim(); +}; - return Math.floor((date2 - date1) / millisecondsPerDay); - }; - })(window); -}).call(window); +export const getDayDifference = (a, b) => { + const millisecondsPerDay = 1000 * 60 * 60 * 24; + const date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()); + const date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); + + return Math.floor((date2 - date1) / millisecondsPerDay); +}; /** * Port of ruby helper time_interval_in_words. @@ -162,3 +171,10 @@ export function dateInWords(date, abbreviated = false) { return `${monthName} ${date.getDate()}, ${year}`; } + +window.gl = window.gl || {}; +window.gl.utils = { + ...(window.gl.utils || {}), + getTimeago, + localTimeAgo, +}; diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index 9280b7f150c..cb6e06ea584 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -64,3 +64,12 @@ export const truncate = (string, maxLength) => `${string.substr(0, (maxLength - export function capitalizeFirstCharacter(text) { return `${text[0].toUpperCase()}${text.slice(1)}`; } + +/** + * Replaces all html tags from a string with the given replacement. + * + * @param {String} string + * @param {*} replace + * @returns {String} + */ +export const stripeHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace); diff --git a/app/assets/javascripts/lib/utils/tick_formats.js b/app/assets/javascripts/lib/utils/tick_formats.js new file mode 100644 index 00000000000..0c10a85e336 --- /dev/null +++ b/app/assets/javascripts/lib/utils/tick_formats.js @@ -0,0 +1,39 @@ +import { createDateTimeFormat } from '../../locale'; + +let dateTimeFormats; + +export const initDateFormats = () => { + const dayFormat = createDateTimeFormat({ month: 'short', day: 'numeric' }); + const monthFormat = createDateTimeFormat({ month: 'long' }); + const yearFormat = createDateTimeFormat({ year: 'numeric' }); + + dateTimeFormats = { + dayFormat, + monthFormat, + yearFormat, + }; +}; + +initDateFormats(); + +/** + Formats a localized date in way that it can be used for d3.js axis.tickFormat(). + + That is, it displays + - 4-digit for first of January + - full month name for first of every month + - day and abbreviated month otherwise + + see also https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#tickFormat + */ +export const dateTickFormat = (date) => { + if (date.getDate() !== 1) { + return dateTimeFormats.dayFormat.format(date); + } + + if (date.getMonth() > 0) { + return dateTimeFormats.monthFormat.format(date); + } + + return dateTimeFormats.yearFormat.format(date); +}; diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 17236c91490..a266bb6771f 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -1,93 +1,69 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, guard-for-in, no-restricted-syntax, prefer-template, quotes, max-len */ - -var base; -var w = window; -if (w.gl == null) { - w.gl = {}; -} -if ((base = w.gl).utils == null) { - base.utils = {}; -} // Returns an array containing the value(s) of the // of the key passed as an argument -w.gl.utils.getParameterValues = function(sParam) { - var i, sPageURL, sParameterName, sURLVariables, values; - sPageURL = decodeURIComponent(window.location.search.substring(1)); - sURLVariables = sPageURL.split('&'); - sParameterName = void 0; - values = []; - i = 0; - while (i < sURLVariables.length) { - sParameterName = sURLVariables[i].split('='); +export function getParameterValues(sParam) { + const sPageURL = decodeURIComponent(window.location.search.substring(1)); + + return sPageURL.split('&').reduce((acc, urlParam) => { + const sParameterName = urlParam.split('='); + if (sParameterName[0] === sParam) { - values.push(sParameterName[1].replace(/\+/g, ' ')); + acc.push(sParameterName[1].replace(/\+/g, ' ')); } - i += 1; - } - return values; -}; + + return acc; + }, []); +} + // @param {Object} params - url keys and value to merge // @param {String} url -w.gl.utils.mergeUrlParams = function(params, url) { - var lastChar, newUrl, paramName, paramValue, pattern; - newUrl = decodeURIComponent(url); - for (paramName in params) { - paramValue = params[paramName]; - pattern = new RegExp("\\b(" + paramName + "=).*?(&|$)"); - if (paramValue == null) { - newUrl = newUrl.replace(pattern, ''); +export function mergeUrlParams(params, url) { + let newUrl = Object.keys(params).reduce((acc, paramName) => { + const paramValue = encodeURIComponent(params[paramName]); + const pattern = new RegExp(`\\b(${paramName}=).*?(&|$)`); + + if (paramValue === null) { + return acc.replace(pattern, ''); } else if (url.search(pattern) !== -1) { - newUrl = newUrl.replace(pattern, "$1" + paramValue + "$2"); - } else { - newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue; + return acc.replace(pattern, `$1${paramValue}$2`); } - } + + return `${acc}${acc.indexOf('?') > 0 ? '&' : '?'}${paramName}=${paramValue}`; + }, decodeURIComponent(url)); + // Remove a trailing ampersand - lastChar = newUrl[newUrl.length - 1]; + const lastChar = newUrl[newUrl.length - 1]; + if (lastChar === '&') { newUrl = newUrl.slice(0, -1); } + return newUrl; -}; -// removes parameter query string from url. returns the modified url -w.gl.utils.removeParamQueryString = function(url, param) { - var urlVariables, variables; - url = decodeURIComponent(url); - urlVariables = url.split('&'); - return ((function() { - var j, len, results; - results = []; - for (j = 0, len = urlVariables.length; j < len; j += 1) { - variables = urlVariables[j]; - if (variables.indexOf(param) === -1) { - results.push(variables); - } - } - return results; - })()).join('&'); -}; -w.gl.utils.removeParams = (params) => { +} + +export function removeParamQueryString(url, param) { + const decodedUrl = decodeURIComponent(url); + const urlVariables = decodedUrl.split('&'); + + return urlVariables.filter(variable => variable.indexOf(param) === -1).join('&'); +} + +export function removeParams(params) { const url = document.createElement('a'); url.href = window.location.href; + params.forEach((param) => { - url.search = w.gl.utils.removeParamQueryString(url.search, param); + url.search = removeParamQueryString(url.search, param); }); + return url.href; -}; -w.gl.utils.getLocationHash = function(url) { - var hashIndex; - if (typeof url === 'undefined') { - // Note: We can't use window.location.hash here because it's - // not consistent across browsers - Firefox will pre-decode it - url = window.location.href; - } - hashIndex = url.indexOf('#'); - return hashIndex === -1 ? null : url.substring(hashIndex + 1); -}; +} -w.gl.utils.refreshCurrentPage = () => gl.utils.visitUrl(window.location.href); +export function getLocationHash(url = window.location.href) { + const hashIndex = url.indexOf('#'); + + return hashIndex === -1 ? null : url.substring(hashIndex + 1); +} -// eslint-disable-next-line import/prefer-default-export export function visitUrl(url, external = false) { if (external) { // Simulate `target="blank" rel="noopener noreferrer"` @@ -100,12 +76,10 @@ export function visitUrl(url, external = false) { } } +export function refreshCurrentPage() { + visitUrl(window.location.href); +} + export function redirectTo(url) { return window.location.assign(url); } - -window.gl = window.gl || {}; -window.gl.utils = { - ...(window.gl.utils || {}), - visitUrl, -}; diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index a75d1a4b8d0..fbd381d8ff7 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -175,4 +175,4 @@ LineHighlighter.prototype.__setLocationHash__ = function(value) { }, document.title, value); }; -window.LineHighlighter = LineHighlighter; +export default LineHighlighter; diff --git a/app/assets/javascripts/locale/index.js b/app/assets/javascripts/locale/index.js index 1003b9ba0af..2f4328b56e1 100644 --- a/app/assets/javascripts/locale/index.js +++ b/app/assets/javascripts/locale/index.js @@ -1,8 +1,7 @@ import Jed from 'jed'; import sprintf from './sprintf'; -const langAttribute = document.querySelector('html').getAttribute('lang'); -const lang = (langAttribute || 'en').replace(/-/g, '_'); +const languageCode = () => document.querySelector('html').getAttribute('lang') || 'en'; const locale = new Jed(window.translations || {}); delete window.translations; @@ -47,9 +46,19 @@ const pgettext = (keyOrContext, key) => { return translated[translated.length - 1]; }; -export { lang }; +/** + Creates an instance of Intl.DateTimeFormat for the current locale. + + @param formatOptions for available options, please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat + @returns {Intl.DateTimeFormat} +*/ +const createDateTimeFormat = + formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions); + +export { languageCode }; export { gettext as __ }; export { ngettext as n__ }; export { pgettext as s__ }; export { sprintf }; +export { createDateTimeFormat }; export default locale; diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index dcc0fa63b63..59bfa482bb0 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -1,6 +1,5 @@ /* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import, import/first */ /* global ConfirmDangerModal */ -/* global Aside */ import jQuery from 'jquery'; import _ from 'underscore'; @@ -28,50 +27,28 @@ import './commit/image_file'; // lib/utils import { handleLocationHash } from './lib/utils/common_utils'; -import './lib/utils/datetime_utility'; -import './lib/utils/url_utility'; +import { localTimeAgo, renderTimeago } from './lib/utils/datetime_utility'; +import { getLocationHash, visitUrl } from './lib/utils/url_utility'; // behaviors import './behaviors/'; // everything else -import './activities'; -import './admin'; -import './aside'; import loadAwardsHandler from './awards_handler'; import bp from './breakpoints'; -import './commits'; -import './compare'; -import './compare_autocomplete'; import './confirm_danger_modal'; import Flash, { removeFlashClickListener } from './flash'; import './gl_dropdown'; -import './gl_field_error'; -import './gl_field_errors'; -import './gl_form'; import initTodoToggle from './header'; import initImporterStatus from './importer_status'; -import './layout_nav'; +import initLayoutNav from './layout_nav'; import LazyLoader from './lazy_loader'; import './line_highlighter'; import initLogoAnimation from './logo'; -import './merge_request'; -import './merge_request_tabs'; import './milestone_select'; -import './notes'; -import './notifications_dropdown'; -import './notifications_form'; -import './pager'; import './preview_markdown'; -import './project_find_file'; -import './project_import'; import './projects_dropdown'; -import './projects_list'; -import './syntax_highlight'; import './render_gfm'; -import './right_sidebar'; -import './search'; -import './search_autocomplete'; import initBreadcrumbs from './breadcrumb'; import './dispatcher'; @@ -112,6 +89,7 @@ $(function () { var fitSidebarForSize; initBreadcrumbs(); + initLayoutNav(); initImporterStatus(); initTodoToggle(); initLogoAnimation(); @@ -122,13 +100,13 @@ $(function () { // `hashchange` is not triggered when link target is already in window.location $body.on('click', 'a[href^="#"]', function() { var href = this.getAttribute('href'); - if (href.substr(1) === gl.utils.getLocationHash()) { + if (href.substr(1) === getLocationHash()) { setTimeout(handleLocationHash, 1); } }); if (bootstrapBreakpoint === 'xs') { - const $rightSidebar = $('aside.right-sidebar, .page-with-sidebar'); + const $rightSidebar = $('aside.right-sidebar, .layout-page'); $rightSidebar .removeClass('right-sidebar-expanded') @@ -188,13 +166,13 @@ $(function () { trigger: 'focus', // set the viewport to the main content, excluding the navigation bar, so // the navigation can't overlap the popover - viewport: '.page-with-sidebar' + viewport: '.layout-page' }); $('.trigger-submit').on('change', function () { return $(this).parents('form').submit(); // Form submitter }); - gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true); + localTimeAgo($('abbr.timeago, .js-timeago'), true); // Disable form buttons while a form is submitting $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) { var buttons; @@ -281,11 +259,8 @@ $(function () { return fitSidebarForSize(); }); loadAwardsHandler(); - new Aside(); - gl.utils.renderTimeago(); - - $(document).trigger('init.scrolling-tabs'); + renderTimeago(); $('form.filter-form').on('submit', function (event) { const link = document.createElement('a'); @@ -294,7 +269,7 @@ $(function () { const action = `${this.action}${link.search === '' ? '?' : '&'}`; event.preventDefault(); - gl.utils.visitUrl(`${action}${$(this).serialize()}`); + visitUrl(`${action}${$(this).serialize()}`); }); const flashContainer = document.querySelector('.flash-container'); diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index 17591829b76..94561d6b7c3 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -10,6 +10,7 @@ import './mixins/line_conflict_actions'; import './components/diff_file_editor'; import './components/inline_conflict_lines'; import './components/parallel_conflict_lines'; +import syntaxHighlight from '../syntax_highlight'; $(() => { const INTERACTIVE_RESOLVE_MODE = 'interactive'; @@ -53,7 +54,7 @@ $(() => { mergeConflictsStore.setLoadingState(false); this.$nextTick(() => { - $('.js-syntax-highlight').syntaxHighlight(); + syntaxHighlight($('.js-syntax-highlight')); }); }); }, diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index a9c08df4f93..cb3cdea8111 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -1,148 +1,143 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */ -/* global MergeRequestTabs */ import 'vendor/jquery.waitforimages'; import TaskList from './task_list'; -import './merge_request_tabs'; +import MergeRequestTabs from './merge_request_tabs'; import IssuablesHelper from './helpers/issuables_helper'; import { addDelimiter } from './lib/utils/text_utility'; -(function() { - this.MergeRequest = (function() { - function MergeRequest(opts) { - // Initialize MergeRequest behavior - // - // Options: - // action - String, current controller action - // - this.opts = opts != null ? opts : {}; - this.submitNoteForm = this.submitNoteForm.bind(this); - this.$el = $('.merge-request'); - this.$('.show-all-commits').on('click', (function(_this) { - return function() { - return _this.showAllCommits(); - }; - })(this)); - - this.initTabs(); - this.initMRBtnListeners(); - this.initCommitMessageListeners(); - this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport(); - - if ($("a.btn-close").length) { - this.taskList = new TaskList({ - dataType: 'merge_request', - fieldName: 'description', - selector: '.detail-page-description', - onSuccess: (result) => { - document.querySelector('#task_status').innerText = result.task_status; - document.querySelector('#task_status_short').innerText = result.task_status_short; - } - }); - } - } - - // Local jQuery finder - MergeRequest.prototype.$ = function(selector) { - return this.$el.find(selector); +function MergeRequest(opts) { + // Initialize MergeRequest behavior + // + // Options: + // action - String, current controller action + // + this.opts = opts != null ? opts : {}; + this.submitNoteForm = this.submitNoteForm.bind(this); + this.$el = $('.merge-request'); + this.$('.show-all-commits').on('click', (function(_this) { + return function() { + return _this.showAllCommits(); }; - - MergeRequest.prototype.initTabs = function() { - if (window.mrTabs) { - window.mrTabs.unbindEvents(); + })(this)); + + this.initTabs(); + this.initMRBtnListeners(); + this.initCommitMessageListeners(); + this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport(); + + if ($("a.btn-close").length) { + this.taskList = new TaskList({ + dataType: 'merge_request', + fieldName: 'description', + selector: '.detail-page-description', + onSuccess: (result) => { + document.querySelector('#task_status').innerText = result.task_status; + document.querySelector('#task_status_short').innerText = result.task_status_short; } - window.mrTabs = new gl.MergeRequestTabs(this.opts); - }; - - MergeRequest.prototype.showAllCommits = function() { - this.$('.first-commits').remove(); - return this.$('.all-commits').removeClass('hide'); - }; - - MergeRequest.prototype.initMRBtnListeners = function() { - var _this; - _this = this; - return $('a.btn-close, a.btn-reopen').on('click', function(e) { - var $this, shouldSubmit; - $this = $(this); - shouldSubmit = $this.hasClass('btn-comment'); - if (shouldSubmit && $this.data('submitted')) { - return; - } - - if (this.closeReopenReportToggle) this.closeReopenReportToggle.setDisable(); - - if (shouldSubmit) { - if ($this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')) { - e.preventDefault(); - e.stopImmediatePropagation(); - - _this.submitNoteForm($this.closest('form'), $this); - } - } - }); - }; - - MergeRequest.prototype.submitNoteForm = function(form, $button) { - var noteText; - noteText = form.find("textarea.js-note-text").val(); - if (noteText.trim().length > 0) { - form.submit(); - $button.data('submitted', true); - return $button.trigger('click'); - } - }; - - MergeRequest.prototype.initCommitMessageListeners = function() { - $(document).on('click', 'a.js-with-description-link', function(e) { - var textarea = $('textarea.js-commit-message'); - e.preventDefault(); + }); + } +} + +// Local jQuery finder +MergeRequest.prototype.$ = function(selector) { + return this.$el.find(selector); +}; + +MergeRequest.prototype.initTabs = function() { + if (window.mrTabs) { + window.mrTabs.unbindEvents(); + } + window.mrTabs = new MergeRequestTabs(this.opts); +}; + +MergeRequest.prototype.showAllCommits = function() { + this.$('.first-commits').remove(); + return this.$('.all-commits').removeClass('hide'); +}; + +MergeRequest.prototype.initMRBtnListeners = function() { + var _this; + _this = this; + return $('a.btn-close, a.btn-reopen').on('click', function(e) { + var $this, shouldSubmit; + $this = $(this); + shouldSubmit = $this.hasClass('btn-comment'); + if (shouldSubmit && $this.data('submitted')) { + return; + } - textarea.val(textarea.data('messageWithDescription')); - $('.js-with-description-hint').hide(); - $('.js-without-description-hint').show(); - }); + if (this.closeReopenReportToggle) this.closeReopenReportToggle.setDisable(); - $(document).on('click', 'a.js-without-description-link', function(e) { - var textarea = $('textarea.js-commit-message'); + if (shouldSubmit) { + if ($this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')) { e.preventDefault(); + e.stopImmediatePropagation(); - textarea.val(textarea.data('messageWithoutDescription')); - $('.js-with-description-hint').show(); - $('.js-without-description-hint').hide(); - }); - }; - - MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, newStatusText) { - $('.detail-page-header .status-box') - .removeClass(classToRemove) - .addClass(classToAdd) - .find('span') - .text(newStatusText); - }; - - MergeRequest.prototype.decreaseCounter = function(by = 1) { - const $el = $('.nav-links .js-merge-counter'); - const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0); - - $el.text(addDelimiter(count)); - }; - - MergeRequest.prototype.hideCloseButton = function() { - const el = document.querySelector('.merge-request .js-issuable-actions'); - const closeDropdownItem = el.querySelector('li.close-item'); - if (closeDropdownItem) { - closeDropdownItem.classList.add('hidden'); - // Selects the next dropdown item - el.querySelector('li.report-item').click(); - } else { - // No dropdown just hide the Close button - el.querySelector('.btn-close').classList.add('hidden'); + _this.submitNoteForm($this.closest('form'), $this); } - // Dropdown for mobile screen - el.querySelector('li.js-close-item').classList.add('hidden'); - }; - - return MergeRequest; - })(); -}).call(window); + } + }); +}; + +MergeRequest.prototype.submitNoteForm = function(form, $button) { + var noteText; + noteText = form.find("textarea.js-note-text").val(); + if (noteText.trim().length > 0) { + form.submit(); + $button.data('submitted', true); + return $button.trigger('click'); + } +}; + +MergeRequest.prototype.initCommitMessageListeners = function() { + $(document).on('click', 'a.js-with-description-link', function(e) { + var textarea = $('textarea.js-commit-message'); + e.preventDefault(); + + textarea.val(textarea.data('messageWithDescription')); + $('.js-with-description-hint').hide(); + $('.js-without-description-hint').show(); + }); + + $(document).on('click', 'a.js-without-description-link', function(e) { + var textarea = $('textarea.js-commit-message'); + e.preventDefault(); + + textarea.val(textarea.data('messageWithoutDescription')); + $('.js-with-description-hint').show(); + $('.js-without-description-hint').hide(); + }); +}; + +MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, newStatusText) { + $('.detail-page-header .status-box') + .removeClass(classToRemove) + .addClass(classToAdd) + .find('span') + .text(newStatusText); +}; + +MergeRequest.prototype.decreaseCounter = function(by = 1) { + const $el = $('.nav-links .js-merge-counter'); + const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0); + + $el.text(addDelimiter(count)); +}; + +MergeRequest.prototype.hideCloseButton = function() { + const el = document.querySelector('.merge-request .js-issuable-actions'); + const closeDropdownItem = el.querySelector('li.close-item'); + if (closeDropdownItem) { + closeDropdownItem.classList.add('hidden'); + // Selects the next dropdown item + el.querySelector('li.report-item').click(); + } else { + // No dropdown just hide the Close button + el.querySelector('.btn-close').classList.add('hidden'); + } + // Dropdown for mobile screen + el.querySelector('li.js-close-item').classList.add('hidden'); +}; + +export default MergeRequest; diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 54c1b7a268e..acfc62fe5cb 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -1,5 +1,4 @@ /* eslint-disable no-new, class-methods-use-this */ -/* global notes */ import Cookies from 'js-cookie'; import Flash from './flash'; @@ -11,8 +10,12 @@ import { handleLocationHash, isMetaClick, } from './lib/utils/common_utils'; +import { getLocationHash } from './lib/utils/url_utility'; import initDiscussionTab from './image_diff/init_discussion_tab'; import Diff from './diff'; +import { localTimeAgo } from './lib/utils/datetime_utility'; +import syntaxHighlight from './syntax_highlight'; +import Notes from './notes'; /* eslint-disable max-len */ // MergeRequestTabs @@ -60,387 +63,382 @@ import Diff from './diff'; // /* eslint-enable max-len */ -(() => { - // Store the `location` object, allowing for easier stubbing in tests - let location = window.location; +// Store the `location` object, allowing for easier stubbing in tests +let location = window.location; - class MergeRequestTabs { +export default class MergeRequestTabs { - constructor({ action, setUrl, stubLocation } = {}) { - const mergeRequestTabs = document.querySelector('.js-tabs-affix'); - const navbar = document.querySelector('.navbar-gitlab'); - const paddingTop = 16; + constructor({ action, setUrl, stubLocation } = {}) { + const mergeRequestTabs = document.querySelector('.js-tabs-affix'); + const navbar = document.querySelector('.navbar-gitlab'); + const paddingTop = 16; - this.diffsLoaded = false; - this.pipelinesLoaded = false; - this.commitsLoaded = false; - this.fixedLayoutPref = null; + this.diffsLoaded = false; + this.pipelinesLoaded = false; + this.commitsLoaded = false; + this.fixedLayoutPref = null; - this.setUrl = setUrl !== undefined ? setUrl : true; - this.setCurrentAction = this.setCurrentAction.bind(this); - this.tabShown = this.tabShown.bind(this); - this.showTab = this.showTab.bind(this); - this.stickyTop = navbar ? navbar.offsetHeight - paddingTop : 0; + this.setUrl = setUrl !== undefined ? setUrl : true; + this.setCurrentAction = this.setCurrentAction.bind(this); + this.tabShown = this.tabShown.bind(this); + this.showTab = this.showTab.bind(this); + this.stickyTop = navbar ? navbar.offsetHeight - paddingTop : 0; - if (mergeRequestTabs) { - this.stickyTop += mergeRequestTabs.offsetHeight; - } - - if (stubLocation) { - location = stubLocation; - } + if (mergeRequestTabs) { + this.stickyTop += mergeRequestTabs.offsetHeight; + } - this.bindEvents(); - this.activateTab(action); - this.initAffix(); + if (stubLocation) { + location = stubLocation; } - bindEvents() { - $(document) - .on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) - .on('click', '.js-show-tab', this.showTab); + this.bindEvents(); + this.activateTab(action); + this.initAffix(); + } - $('.merge-request-tabs a[data-toggle="tab"]') - .on('click', this.clickTab); - } + bindEvents() { + $(document) + .on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) + .on('click', '.js-show-tab', this.showTab); - // Used in tests - unbindEvents() { - $(document) - .off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) - .off('click', '.js-show-tab', this.showTab); + $('.merge-request-tabs a[data-toggle="tab"]') + .on('click', this.clickTab); + } - $('.merge-request-tabs a[data-toggle="tab"]') - .off('click', this.clickTab); - } + // Used in tests + unbindEvents() { + $(document) + .off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) + .off('click', '.js-show-tab', this.showTab); - destroyPipelinesView() { - if (this.commitPipelinesTable) { - this.commitPipelinesTable.$destroy(); - this.commitPipelinesTable = null; + $('.merge-request-tabs a[data-toggle="tab"]') + .off('click', this.clickTab); + } - document.querySelector('#commit-pipeline-table-view').innerHTML = ''; - } + destroyPipelinesView() { + if (this.commitPipelinesTable) { + this.commitPipelinesTable.$destroy(); + this.commitPipelinesTable = null; + + document.querySelector('#commit-pipeline-table-view').innerHTML = ''; } + } - showTab(e) { + showTab(e) { + e.preventDefault(); + this.activateTab($(e.target).data('action')); + } + + clickTab(e) { + if (e.currentTarget && isMetaClick(e)) { + const targetLink = e.currentTarget.getAttribute('href'); + e.stopImmediatePropagation(); e.preventDefault(); - this.activateTab($(e.target).data('action')); + window.open(targetLink, '_blank'); } + } - clickTab(e) { - if (e.currentTarget && isMetaClick(e)) { - const targetLink = e.currentTarget.getAttribute('href'); - e.stopImmediatePropagation(); - e.preventDefault(); - window.open(targetLink, '_blank'); + tabShown(e) { + const $target = $(e.target); + const action = $target.data('action'); + + if (action === 'commits') { + this.loadCommits($target.attr('href')); + this.expandView(); + this.resetViewContainer(); + this.destroyPipelinesView(); + } else if (this.isDiffAction(action)) { + this.loadDiff($target.attr('href')); + if (bp.getBreakpointSize() !== 'lg') { + this.shrinkView(); } - } - - tabShown(e) { - const $target = $(e.target); - const action = $target.data('action'); - - if (action === 'commits') { - this.loadCommits($target.attr('href')); - this.expandView(); - this.resetViewContainer(); - this.destroyPipelinesView(); - } else if (this.isDiffAction(action)) { - this.loadDiff($target.attr('href')); - if (bp.getBreakpointSize() !== 'lg') { - this.shrinkView(); - } - if (this.diffViewType() === 'parallel') { - this.expandViewContainer(); - } - this.destroyPipelinesView(); - } else if (action === 'pipelines') { - this.resetViewContainer(); - this.mountPipelinesView(); - } else { - if (bp.getBreakpointSize() !== 'xs') { - this.expandView(); - } - this.resetViewContainer(); - this.destroyPipelinesView(); - - initDiscussionTab(); + if (this.diffViewType() === 'parallel') { + this.expandViewContainer(); } - if (this.setUrl) { - this.setCurrentAction(action); + this.destroyPipelinesView(); + } else if (action === 'pipelines') { + this.resetViewContainer(); + this.mountPipelinesView(); + } else { + if (bp.getBreakpointSize() !== 'xs') { + this.expandView(); } + this.resetViewContainer(); + this.destroyPipelinesView(); + + initDiscussionTab(); + } + if (this.setUrl) { + this.setCurrentAction(action); } + } - scrollToElement(container) { - if (location.hash) { - const offset = 0 - ( - $('.navbar-gitlab').outerHeight() + - $('.js-tabs-affix').outerHeight() - ); - const $el = $(`${container} ${location.hash}:not(.match)`); - if ($el.length) { - $.scrollTo($el[0], { offset }); - } + scrollToElement(container) { + if (location.hash) { + const offset = 0 - ( + $('.navbar-gitlab').outerHeight() + + $('.js-tabs-affix').outerHeight() + ); + const $el = $(`${container} ${location.hash}:not(.match)`); + if ($el.length) { + $.scrollTo($el[0], { offset }); } } + } - // Activate a tab based on the current action - activateTab(action) { - // important note: the .tab('show') method triggers 'shown.bs.tab' event itself - $(`.merge-request-tabs a[data-action='${action}']`).tab('show'); + // Activate a tab based on the current action + activateTab(action) { + // important note: the .tab('show') method triggers 'shown.bs.tab' event itself + $(`.merge-request-tabs a[data-action='${action}']`).tab('show'); + } + + // Replaces the current Merge Request-specific action in the URL with a new one + // + // If the action is "notes", the URL is reset to the standard + // `MergeRequests#show` route. + // + // Examples: + // + // location.pathname # => "/namespace/project/merge_requests/1" + // setCurrentAction('diffs') + // location.pathname # => "/namespace/project/merge_requests/1/diffs" + // + // location.pathname # => "/namespace/project/merge_requests/1/diffs" + // setCurrentAction('show') + // location.pathname # => "/namespace/project/merge_requests/1" + // + // location.pathname # => "/namespace/project/merge_requests/1/diffs" + // setCurrentAction('commits') + // location.pathname # => "/namespace/project/merge_requests/1/commits" + // + // Returns the new URL String + setCurrentAction(action) { + this.currentAction = action; + + // Remove a trailing '/commits' '/diffs' '/pipelines' + let newState = location.pathname.replace(/\/(commits|diffs|pipelines)(\.html)?\/?$/, ''); + + // Append the new action if we're on a tab other than 'notes' + if (this.currentAction !== 'show' && this.currentAction !== 'new') { + newState += `/${this.currentAction}`; } - // Replaces the current Merge Request-specific action in the URL with a new one - // - // If the action is "notes", the URL is reset to the standard - // `MergeRequests#show` route. - // - // Examples: - // - // location.pathname # => "/namespace/project/merge_requests/1" - // setCurrentAction('diffs') - // location.pathname # => "/namespace/project/merge_requests/1/diffs" - // - // location.pathname # => "/namespace/project/merge_requests/1/diffs" - // setCurrentAction('show') - // location.pathname # => "/namespace/project/merge_requests/1" - // - // location.pathname # => "/namespace/project/merge_requests/1/diffs" - // setCurrentAction('commits') - // location.pathname # => "/namespace/project/merge_requests/1/commits" - // - // Returns the new URL String - setCurrentAction(action) { - this.currentAction = action; + // Ensure parameters and hash come along for the ride + newState += location.search + location.hash; - // Remove a trailing '/commits' '/diffs' '/pipelines' - let newState = location.pathname.replace(/\/(commits|diffs|pipelines)(\.html)?\/?$/, ''); + // TODO: Consider refactoring in light of turbolinks removal. - // Append the new action if we're on a tab other than 'notes' - if (this.currentAction !== 'show' && this.currentAction !== 'new') { - newState += `/${this.currentAction}`; - } + // Replace the current history state with the new one without breaking + // Turbolinks' history. + // + // See https://github.com/rails/turbolinks/issues/363 + window.history.replaceState({ + url: newState, + }, document.title, newState); - // Ensure parameters and hash come along for the ride - newState += location.search + location.hash; + return newState; + } - // TODO: Consider refactoring in light of turbolinks removal. + loadCommits(source) { + if (this.commitsLoaded) { + return; + } + this.ajaxGet({ + url: `${source}.json`, + success: (data) => { + document.querySelector('div#commits').innerHTML = data.html; + localTimeAgo($('.js-timeago', 'div#commits')); + this.commitsLoaded = true; + this.scrollToElement('#commits'); + }, + }); + } - // Replace the current history state with the new one without breaking - // Turbolinks' history. - // - // See https://github.com/rails/turbolinks/issues/363 - window.history.replaceState({ - url: newState, - }, document.title, newState); + mountPipelinesView() { + const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); + const CommitPipelinesTable = gl.CommitPipelinesTable; + this.commitPipelinesTable = new CommitPipelinesTable({ + propsData: { + endpoint: pipelineTableViewEl.dataset.endpoint, + helpPagePath: pipelineTableViewEl.dataset.helpPagePath, + emptyStateSvgPath: pipelineTableViewEl.dataset.emptyStateSvgPath, + errorStateSvgPath: pipelineTableViewEl.dataset.errorStateSvgPath, + autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath, + }, + }).$mount(); + + // $mount(el) replaces the el with the new rendered component. We need it in order to mount + // it everytime this tab is clicked - https://vuejs.org/v2/api/#vm-mount + pipelineTableViewEl.appendChild(this.commitPipelinesTable.$el); + } - return newState; + loadDiff(source) { + if (this.diffsLoaded) { + document.dispatchEvent(new CustomEvent('scroll')); + return; } - loadCommits(source) { - if (this.commitsLoaded) { - return; - } - this.ajaxGet({ - url: `${source}.json`, - success: (data) => { - document.querySelector('div#commits').innerHTML = data.html; - gl.utils.localTimeAgo($('.js-timeago', 'div#commits')); - this.commitsLoaded = true; - this.scrollToElement('#commits'); - }, - }); - } + // We extract pathname for the current Changes tab anchor href + // some pages like MergeRequestsController#new has query parameters on that anchor + const urlPathname = parseUrlPathname(source); - mountPipelinesView() { - const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); - const CommitPipelinesTable = gl.CommitPipelinesTable; - this.commitPipelinesTable = new CommitPipelinesTable({ - propsData: { - endpoint: pipelineTableViewEl.dataset.endpoint, - helpPagePath: pipelineTableViewEl.dataset.helpPagePath, - emptyStateSvgPath: pipelineTableViewEl.dataset.emptyStateSvgPath, - errorStateSvgPath: pipelineTableViewEl.dataset.errorStateSvgPath, - autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath, - }, - }).$mount(); + this.ajaxGet({ + url: `${urlPathname}.json${location.search}`, + success: (data) => { + const $container = $('#diffs'); + $container.html(data.html); - // $mount(el) replaces the el with the new rendered component. We need it in order to mount - // it everytime this tab is clicked - https://vuejs.org/v2/api/#vm-mount - pipelineTableViewEl.appendChild(this.commitPipelinesTable.$el); - } + initChangesDropdown(this.stickyTop); - loadDiff(source) { - if (this.diffsLoaded) { - document.dispatchEvent(new CustomEvent('scroll')); - return; - } + if (typeof gl.diffNotesCompileComponents !== 'undefined') { + gl.diffNotesCompileComponents(); + } + + localTimeAgo($('.js-timeago', 'div#diffs')); + syntaxHighlight($('#diffs .js-syntax-highlight')); - // We extract pathname for the current Changes tab anchor href - // some pages like MergeRequestsController#new has query parameters on that anchor - const urlPathname = parseUrlPathname(source); - - this.ajaxGet({ - url: `${urlPathname}.json${location.search}`, - success: (data) => { - const $container = $('#diffs'); - $container.html(data.html); - - initChangesDropdown(this.stickyTop); - - if (typeof gl.diffNotesCompileComponents !== 'undefined') { - gl.diffNotesCompileComponents(); - } - - gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')); - $('#diffs .js-syntax-highlight').syntaxHighlight(); - - if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) { - this.expandViewContainer(); - } - this.diffsLoaded = true; - - new Diff(); - this.scrollToElement('#diffs'); - - $('.diff-file').each((i, el) => { - new BlobForkSuggestion({ - openButtons: $(el).find('.js-edit-blob-link-fork-toggler'), - forkButtons: $(el).find('.js-fork-suggestion-button'), - cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'), - suggestionSections: $(el).find('.js-file-fork-suggestion-section'), - actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'), - }) - .init(); + if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) { + this.expandViewContainer(); + } + this.diffsLoaded = true; + + new Diff(); + this.scrollToElement('#diffs'); + + $('.diff-file').each((i, el) => { + new BlobForkSuggestion({ + openButtons: $(el).find('.js-edit-blob-link-fork-toggler'), + forkButtons: $(el).find('.js-fork-suggestion-button'), + cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'), + suggestionSections: $(el).find('.js-file-fork-suggestion-section'), + actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'), + }) + .init(); + }); + + // Scroll any linked note into view + // Similar to `toggler_behavior` in the discussion tab + const hash = getLocationHash(); + const anchor = hash && $container.find(`.note[id="${hash}"]`); + if (anchor && anchor.length > 0) { + const notesContent = anchor.closest('.notes_content'); + const lineType = notesContent.hasClass('new') ? 'new' : 'old'; + Notes.instance.toggleDiffNote({ + target: anchor, + lineType, + forceShow: true, }); + anchor[0].scrollIntoView(); + handleLocationHash(); + // We have multiple elements on the page with `#note_xxx` + // (discussion and diff tabs) and `:target` only applies to the first + anchor.addClass('target'); + } + }, + }); + } - // Scroll any linked note into view - // Similar to `toggler_behavior` in the discussion tab - const hash = window.gl.utils.getLocationHash(); - const anchor = hash && $container.find(`.note[id="${hash}"]`); - if (anchor && anchor.length > 0) { - const notesContent = anchor.closest('.notes_content'); - const lineType = notesContent.hasClass('new') ? 'new' : 'old'; - notes.toggleDiffNote({ - target: anchor, - lineType, - forceShow: true, - }); - anchor[0].scrollIntoView(); - handleLocationHash(); - // We have multiple elements on the page with `#note_xxx` - // (discussion and diff tabs) and `:target` only applies to the first - anchor.addClass('target'); - } - }, - }); - } + // Show or hide the loading spinner + // + // status - Boolean, true to show, false to hide + toggleLoading(status) { + $('.mr-loading-status .loading').toggle(status); + } - // Show or hide the loading spinner - // - // status - Boolean, true to show, false to hide - toggleLoading(status) { - $('.mr-loading-status .loading').toggle(status); - } + ajaxGet(options) { + const defaults = { + beforeSend: () => this.toggleLoading(true), + error: () => new Flash('An error occurred while fetching this tab.', 'alert'), + complete: () => this.toggleLoading(false), + dataType: 'json', + type: 'GET', + }; + $.ajax($.extend({}, defaults, options)); + } - ajaxGet(options) { - const defaults = { - beforeSend: () => this.toggleLoading(true), - error: () => new Flash('An error occurred while fetching this tab.', 'alert'), - complete: () => this.toggleLoading(false), - dataType: 'json', - type: 'GET', - }; - $.ajax($.extend({}, defaults, options)); - } + diffViewType() { + return $('.inline-parallel-buttons a.active').data('view-type'); + } - diffViewType() { - return $('.inline-parallel-buttons a.active').data('view-type'); - } + isDiffAction(action) { + return action === 'diffs' || action === 'new/diffs'; + } - isDiffAction(action) { - return action === 'diffs' || action === 'new/diffs'; + expandViewContainer() { + const $wrapper = $('.content-wrapper .container-fluid').not('.breadcrumbs'); + if (this.fixedLayoutPref === null) { + this.fixedLayoutPref = $wrapper.hasClass('container-limited'); } + $wrapper.removeClass('container-limited'); + } - expandViewContainer() { - const $wrapper = $('.content-wrapper .container-fluid').not('.breadcrumbs'); - if (this.fixedLayoutPref === null) { - this.fixedLayoutPref = $wrapper.hasClass('container-limited'); - } - $wrapper.removeClass('container-limited'); + resetViewContainer() { + if (this.fixedLayoutPref !== null) { + $('.content-wrapper .container-fluid') + .toggleClass('container-limited', this.fixedLayoutPref); } + } - resetViewContainer() { - if (this.fixedLayoutPref !== null) { - $('.content-wrapper .container-fluid') - .toggleClass('container-limited', this.fixedLayoutPref); - } - } + shrinkView() { + const $gutterIcon = $('.js-sidebar-toggle i:visible'); - shrinkView() { - const $gutterIcon = $('.js-sidebar-toggle i:visible'); + // Wait until listeners are set + setTimeout(() => { + // Only when sidebar is expanded + if ($gutterIcon.is('.fa-angle-double-right')) { + $gutterIcon.closest('a').trigger('click', [true]); + } + }, 0); + } - // Wait until listeners are set - setTimeout(() => { - // Only when sidebar is expanded - if ($gutterIcon.is('.fa-angle-double-right')) { - $gutterIcon.closest('a').trigger('click', [true]); - } - }, 0); + // Expand the issuable sidebar unless the user explicitly collapsed it + expandView() { + if (Cookies.get('collapsed_gutter') === 'true') { + return; } + const $gutterIcon = $('.js-sidebar-toggle i:visible'); - // Expand the issuable sidebar unless the user explicitly collapsed it - expandView() { - if (Cookies.get('collapsed_gutter') === 'true') { - return; + // Wait until listeners are set + setTimeout(() => { + // Only when sidebar is collapsed + if ($gutterIcon.is('.fa-angle-double-left')) { + $gutterIcon.closest('a').trigger('click', [true]); } - const $gutterIcon = $('.js-sidebar-toggle i:visible'); + }, 0); + } - // Wait until listeners are set - setTimeout(() => { - // Only when sidebar is collapsed - if ($gutterIcon.is('.fa-angle-double-left')) { - $gutterIcon.closest('a').trigger('click', [true]); - } - }, 0); - } + initAffix() { + const $tabs = $('.js-tabs-affix'); + const $fixedNav = $('.navbar-gitlab'); + + // Screen space on small screens is usually very sparse + // So we dont affix the tabs on these + if (bp.getBreakpointSize() === 'xs' || !$tabs.length) return; + + /** + If the browser does not support position sticky, it returns the position as static. + If the browser does support sticky, then we allow the browser to handle it, if not + then we default back to Bootstraps affix + **/ + if ($tabs.css('position') !== 'static') return; + + const $diffTabs = $('#diff-notes-app'); + + $tabs.off('affix.bs.affix affix-top.bs.affix') + .affix({ + offset: { + top: () => ( + $diffTabs.offset().top - $tabs.height() - $fixedNav.height() + ), + }, + }) + .on('affix.bs.affix', () => $diffTabs.css({ marginTop: $tabs.height() })) + .on('affix-top.bs.affix', () => $diffTabs.css({ marginTop: '' })); - initAffix() { - const $tabs = $('.js-tabs-affix'); - const $fixedNav = $('.navbar-gitlab'); - - // Screen space on small screens is usually very sparse - // So we dont affix the tabs on these - if (bp.getBreakpointSize() === 'xs' || !$tabs.length) return; - - /** - If the browser does not support position sticky, it returns the position as static. - If the browser does support sticky, then we allow the browser to handle it, if not - then we default back to Bootstraps affix - **/ - if ($tabs.css('position') !== 'static') return; - - const $diffTabs = $('#diff-notes-app'); - - $tabs.off('affix.bs.affix affix-top.bs.affix') - .affix({ - offset: { - top: () => ( - $diffTabs.offset().top - $tabs.height() - $fixedNav.height() - ), - }, - }) - .on('affix.bs.affix', () => $diffTabs.css({ marginTop: $tabs.height() })) - .on('affix-top.bs.affix', () => $diffTabs.css({ marginTop: '' })); - - // Fix bug when reloading the page already scrolling - if ($tabs.hasClass('affix')) { - $tabs.trigger('affix.bs.affix'); - } + // Fix bug when reloading the page already scrolling + if ($tabs.hasClass('affix')) { + $tabs.trigger('affix.bs.affix'); } } - - window.gl = window.gl || {}; - window.gl.MergeRequestTabs = MergeRequestTabs; -})(); +} diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 74e5a4f1cea..0e854295fe3 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -1,236 +1,228 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */ /* global Issuable */ /* global ListMilestone */ import _ from 'underscore'; +import { timeFor } from './lib/utils/datetime_utility'; -(function() { - this.MilestoneSelect = (function() { - function MilestoneSelect(currentProject, els, options = {}) { - var _this, $els; - if (currentProject != null) { - _this = this; - this.currentProject = typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject; - } +export default class MilestoneSelect { + constructor(currentProject, els, options = {}) { + if (currentProject !== null) { + this.currentProject = typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject; + } - $els = $(els); + this.init(els, options); + } - if (!els) { - $els = $('.js-milestone-select'); - } + init(els, options) { + let $els = $(els); - $els.each(function(i, dropdown) { - var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, defaultNo, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, selectedMilestoneDefault, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove; - $dropdown = $(dropdown); - projectId = $dropdown.data('project-id'); - milestonesUrl = $dropdown.data('milestones'); - issueUpdateURL = $dropdown.data('issueUpdate'); - showNo = $dropdown.data('show-no'); - showAny = $dropdown.data('show-any'); - showMenuAbove = $dropdown.data('showMenuAbove'); - showUpcoming = $dropdown.data('show-upcoming'); - showStarted = $dropdown.data('show-started'); - useId = $dropdown.data('use-id'); - defaultLabel = $dropdown.data('default-label'); - defaultNo = $dropdown.data('default-no'); - issuableId = $dropdown.data('issuable-id'); - abilityName = $dropdown.data('ability-name'); - $selectbox = $dropdown.closest('.selectbox'); - $block = $selectbox.closest('.block'); - $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); - $value = $block.find('.value'); - $loading = $block.find('.block-loading').fadeOut(); - selectedMilestoneDefault = (showAny ? '' : null); - selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault); - selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault; - if (issueUpdateURL) { - milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); - milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; - collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- name %><br /><%- remaining %>" data-placement="left" data-html="true"> <%- title %> </span>'); - } - return $dropdown.glDropdown({ - showMenuAbove: showMenuAbove, - data: function(term, callback) { - return $.ajax({ - url: milestonesUrl - }).done(function(data) { - var extraOptions = []; - if (showAny) { - extraOptions.push({ - id: 0, - name: '', - title: 'Any Milestone' - }); - } - if (showNo) { - extraOptions.push({ - id: -1, - name: 'No Milestone', - title: 'No Milestone' - }); - } - if (showUpcoming) { - extraOptions.push({ - id: -2, - name: '#upcoming', - title: 'Upcoming' - }); - } - if (showStarted) { - extraOptions.push({ - id: -3, - name: '#started', - title: 'Started' - }); - } - if (extraOptions.length) { - extraOptions.push('divider'); - } + if (!els) { + $els = $('.js-milestone-select'); + } - callback(extraOptions.concat(data)); - if (showMenuAbove) { - $dropdown.data('glDropdown').positionMenuAbove(); - } - $(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active'); - }); - }, - renderRow: function(milestone) { - return ` - <li data-milestone-id="${milestone.name}"> - <a href='#' class='dropdown-menu-milestone-link'> - ${_.escape(milestone.title)} - </a> - </li> - `; - }, - filterable: true, - search: { - fields: ['title'] - }, - selectable: true, - toggleLabel: function(selected, el, e) { - if (selected && 'id' in selected && $(el).hasClass('is-active')) { - return selected.title; - } else { - return defaultLabel; - } - }, - defaultLabel: defaultLabel, - fieldName: $dropdown.data('field-name'), - text: function(milestone) { - return _.escape(milestone.title); - }, - id: function(milestone) { - if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { - return milestone.name; - } else { - return milestone.id; - } - }, - isSelected: function(milestone) { - return milestone.name === selectedMilestone; - }, - hidden: function() { - $selectbox.hide(); - // display:block overrides the hide-collapse rule - return $value.css('display', ''); - }, - opened: function(e) { - const $el = $(e.currentTarget); - if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) { - selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; - } - $('a.is-active', $el).removeClass('is-active'); - $(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active'); - }, - vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: function(clickEvent) { - const { $el, e } = clickEvent; - let selected = clickEvent.selectedObj; + $els.each((i, dropdown) => { + let collapsedSidebarLabelTemplate, milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; + const $dropdown = $(dropdown); + const projectId = $dropdown.data('project-id'); + const milestonesUrl = $dropdown.data('milestones'); + const issueUpdateURL = $dropdown.data('issueUpdate'); + const showNo = $dropdown.data('show-no'); + const showAny = $dropdown.data('show-any'); + const showMenuAbove = $dropdown.data('showMenuAbove'); + const showUpcoming = $dropdown.data('show-upcoming'); + const showStarted = $dropdown.data('show-started'); + const useId = $dropdown.data('use-id'); + const defaultLabel = $dropdown.data('default-label'); + const defaultNo = $dropdown.data('default-no'); + const issuableId = $dropdown.data('issuable-id'); + const abilityName = $dropdown.data('ability-name'); + const $selectBox = $dropdown.closest('.selectbox'); + const $block = $selectBox.closest('.block'); + const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); + const $value = $block.find('.value'); + const $loading = $block.find('.block-loading').fadeOut(); + selectedMilestoneDefault = (showAny ? '' : null); + selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault); + selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault; - var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore; - if (!selected) return; + if (issueUpdateURL) { + milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); + milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; + collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- name %><br /><%- remaining %>" data-placement="left" data-html="true"> <%- title %> </span>'); + } + return $dropdown.glDropdown({ + showMenuAbove: showMenuAbove, + data: (term, callback) => $.ajax({ + url: milestonesUrl + }).done((data) => { + const extraOptions = []; + if (showAny) { + extraOptions.push({ + id: 0, + name: '', + title: 'Any Milestone' + }); + } + if (showNo) { + extraOptions.push({ + id: -1, + name: 'No Milestone', + title: 'No Milestone' + }); + } + if (showUpcoming) { + extraOptions.push({ + id: -2, + name: '#upcoming', + title: 'Upcoming' + }); + } + if (showStarted) { + extraOptions.push({ + id: -3, + name: '#started', + title: 'Started' + }); + } + if (extraOptions.length) { + extraOptions.push('divider'); + } - if (options.handleClick) { - e.preventDefault(); - options.handleClick(selected); - return; - } + callback(extraOptions.concat(data)); + if (showMenuAbove) { + $dropdown.data('glDropdown').positionMenuAbove(); + } + $(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active'); + }), + renderRow: milestone => ` + <li data-milestone-id="${milestone.name}"> + <a href='#' class='dropdown-menu-milestone-link'> + ${_.escape(milestone.title)} + </a> + </li> + `, + filterable: true, + search: { + fields: ['title'] + }, + selectable: true, + toggleLabel: (selected, el, e) => { + if (selected && 'id' in selected && $(el).hasClass('is-active')) { + return selected.title; + } else { + return defaultLabel; + } + }, + defaultLabel: defaultLabel, + fieldName: $dropdown.data('field-name'), + text: milestone => _.escape(milestone.title), + id: (milestone) => { + if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { + return milestone.name; + } else { + return milestone.id; + } + }, + isSelected: milestone => milestone.name === selectedMilestone, + hidden: () => { + $selectBox.hide(); + // display:block overrides the hide-collapse rule + return $value.css('display', ''); + }, + opened: (e) => { + const $el = $(e.currentTarget); + if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) { + selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; + } + $('a.is-active', $el).removeClass('is-active'); + $(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active'); + }, + vue: $dropdown.hasClass('js-issue-board-sidebar'), + clicked: (clickEvent) => { + const { $el, e } = clickEvent; + let selected = clickEvent.selectedObj; - page = $('body').attr('data-page'); - isIssueIndex = page === 'projects:issues:index'; - isMRIndex = (page === page && page === 'projects:merge_requests:index'); - isSelecting = (selected.name !== selectedMilestone); - selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault; - if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { - e.preventDefault(); - return; - } + let data, boardsStore; + if (!selected) return; - if ($dropdown.closest('.add-issues-modal').length) { - boardsStore = gl.issueBoards.ModalStore.store.filter; - } + if (options.handleClick) { + e.preventDefault(); + options.handleClick(selected); + return; + } - if (boardsStore) { - boardsStore[$dropdown.data('field-name')] = selected.name; - e.preventDefault(); - } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { - return Issuable.filterResults($dropdown.closest('form')); - } else if ($dropdown.hasClass('js-filter-submit')) { - return $dropdown.closest('form').submit(); - } else if ($dropdown.hasClass('js-issue-board-sidebar')) { - if (selected.id !== -1 && isSelecting) { - gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ - id: selected.id, - title: selected.name - })); - } else { - gl.issueBoards.boardStoreIssueDelete('milestone'); - } + const page = $('body').attr('data-page'); + const isIssueIndex = page === 'projects:issues:index'; + const isMRIndex = (page === page && page === 'projects:merge_requests:index'); + const isSelecting = (selected.name !== selectedMilestone); + selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault; + if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { + e.preventDefault(); + return; + } - $dropdown.trigger('loading.gl.dropdown'); - $loading.removeClass('hidden').fadeIn(); + if ($dropdown.closest('.add-issues-modal').length) { + boardsStore = gl.issueBoards.ModalStore.store.filter; + } - gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) - .then(function () { - $dropdown.trigger('loaded.gl.dropdown'); - $loading.fadeOut(); - }) - .catch(() => { - $loading.fadeOut(); - }); + if (boardsStore) { + boardsStore[$dropdown.data('field-name')] = selected.name; + e.preventDefault(); + } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { + return Issuable.filterResults($dropdown.closest('form')); + } else if ($dropdown.hasClass('js-filter-submit')) { + return $dropdown.closest('form').submit(); + } else if ($dropdown.hasClass('js-issue-board-sidebar')) { + if (selected.id !== -1 && isSelecting) { + gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ + id: selected.id, + title: selected.name + })); } else { - selected = $selectbox.find('input[type="hidden"]').val(); - data = {}; - data[abilityName] = {}; - data[abilityName].milestone_id = selected != null ? selected : null; - $loading.removeClass('hidden').fadeIn(); - $dropdown.trigger('loading.gl.dropdown'); - return $.ajax({ - type: 'PUT', - url: issueUpdateURL, - data: data - }).done(function(data) { + gl.issueBoards.boardStoreIssueDelete('milestone'); + } + + $dropdown.trigger('loading.gl.dropdown'); + $loading.removeClass('hidden').fadeIn(); + + gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) + .then(() => { $dropdown.trigger('loaded.gl.dropdown'); $loading.fadeOut(); - $selectbox.hide(); - $value.css('display', ''); - if (data.milestone != null) { - data.milestone.full_path = _this.currentProject.full_path; - data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); - data.milestone.name = data.milestone.title; - $value.html(milestoneLinkTemplate(data.milestone)); - return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); - } else { - $value.html(milestoneLinkNoneTemplate); - return $sidebarCollapsedValue.find('span').text('No'); - } + }) + .catch(() => { + $loading.fadeOut(); }); - } + } else { + selected = $selectBox.find('input[type="hidden"]').val(); + data = {}; + data[abilityName] = {}; + data[abilityName].milestone_id = selected != null ? selected : null; + $loading.removeClass('hidden').fadeIn(); + $dropdown.trigger('loading.gl.dropdown'); + return $.ajax({ + type: 'PUT', + url: issueUpdateURL, + data: data + }).done((data) => { + $dropdown.trigger('loaded.gl.dropdown'); + $loading.fadeOut(); + $selectBox.hide(); + $value.css('display', ''); + if (data.milestone != null) { + data.milestone.full_path = this.currentProject.full_path; + data.milestone.remaining = timeFor(data.milestone.due_date); + data.milestone.name = data.milestone.title; + $value.html(milestoneLinkTemplate(data.milestone)); + return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); + } else { + $value.html(milestoneLinkNoneTemplate); + return $sidebarCollapsedValue.find('span').text('No'); + } + }); } - }); + } }); - } - - return MilestoneSelect; - })(); -}).call(window); + }); + } +} diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index cbe24c0915b..8da723ced03 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -21,6 +21,8 @@ hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics), documentationPath: metricsData.documentationPath, settingsPath: metricsData.settingsPath, + tagsPath: metricsData.tagsPath, + projectPath: metricsData.projectPath, metricsEndpoint: metricsData.additionalMetrics, deploymentEndpoint: metricsData.deploymentEndpoint, emptyGettingStartedSvgPath: metricsData.emptyGettingStartedSvgPath, @@ -112,6 +114,8 @@ :hover-data="hoverData" :update-aspect-ratio="updateAspectRatio" :deployment-data="store.deploymentData" + :project-path="projectPath" + :tags-path="tagsPath" /> </graph-group> </div> diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue index f8782fde927..a50b80c23d0 100644 --- a/app/assets/javascripts/monitoring/components/graph.vue +++ b/app/assets/javascripts/monitoring/components/graph.vue @@ -1,5 +1,8 @@ <script> - import d3 from 'd3'; + import { scaleLinear, scaleTime } from 'd3-scale'; + import { axisLeft, axisBottom } from 'd3-axis'; + import { max, extent } from 'd3-array'; + import { select } from 'd3-selection'; import GraphLegend from './graph/legend.vue'; import GraphFlag from './graph/flag.vue'; import GraphDeployment from './graph/deployment.vue'; @@ -7,10 +10,12 @@ import MonitoringMixin from '../mixins/monitoring_mixins'; import eventHub from '../event_hub'; import measurements from '../utils/measurements'; - import { timeScaleFormat, bisectDate } from '../utils/date_time_formatters'; + import { bisectDate, timeScaleFormat } from '../utils/date_time_formatters'; import createTimeSeries from '../utils/multiple_time_series'; import bp from '../../breakpoints'; + const d3 = { scaleLinear, scaleTime, axisLeft, axisBottom, max, extent, select }; + export default { props: { graphData: { @@ -30,6 +35,14 @@ required: false, default: () => ({}), }, + projectPath: { + type: String, + required: true, + }, + tagsPath: { + type: String, + required: true, + }, }, mixins: [MonitoringMixin], @@ -56,8 +69,8 @@ currentFlagPosition: 0, showFlag: false, showFlagContent: false, - showDeployInfo: true, timeSeries: [], + realPixelRatio: 1, }; }, @@ -74,10 +87,7 @@ }, innerViewBox() { - if ((this.baseGraphWidth - 150) > 0) { - return `0 0 ${this.baseGraphWidth - 150} ${this.baseGraphHeight}`; - } - return '0 0 0 0'; + return `0 0 ${this.baseGraphWidth - 150} ${this.baseGraphHeight}`; }, axisTransform() { @@ -89,6 +99,10 @@ paddingBottom: `${(Math.ceil(this.baseGraphHeight * 100) / this.baseGraphWidth) || 0}%`, }; }, + + deploymentFlagData() { + return this.reducedDeploymentData.find(deployment => deployment.showDeploymentFlag); + }, }, methods: { @@ -109,6 +123,10 @@ this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom; this.baseGraphHeight = this.graphHeight; this.baseGraphWidth = this.graphWidth; + + // pixel offsets inside the svg and outside are not 1:1 + this.realPixelRatio = (this.$refs.baseSvg.clientWidth / this.baseGraphWidth); + this.renderAxesPaths(); this.formatDeployments(); }, @@ -148,25 +166,22 @@ this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20; } - const axisXScale = d3.time.scale() + const axisXScale = d3.scaleTime() .range([0, this.graphWidth - 70]); - const axisYScale = d3.scale.linear() + const axisYScale = d3.scaleLinear() .range([this.graphHeight - this.graphHeightOffset, 0]); const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []); axisXScale.domain(d3.extent(allValues, d => d.time)); axisYScale.domain([0, d3.max(allValues.map(d => d.value))]); - const xAxis = d3.svg.axis() + const xAxis = d3.axisBottom() .scale(axisXScale) - .ticks(d3.time.minute, 60) - .tickFormat(timeScaleFormat) - .orient('bottom'); + .tickFormat(timeScaleFormat); - const yAxis = d3.svg.axis() + const yAxis = d3.axisLeft() .scale(axisYScale) - .ticks(measurements.yTicks) - .orient('left'); + .ticks(measurements.yTicks); d3.select(this.$refs.baseSvg).select('.x-axis').call(xAxis); @@ -252,20 +267,9 @@ :area-color="path.areaColor" /> <graph-deployment - :show-deploy-info="showDeployInfo" :deployment-data="reducedDeploymentData" - :graph-width="graphWidth" - :graph-height="graphHeight" - :graph-height-offset="graphHeightOffset" - /> - <graph-flag - v-if="showFlag" - :current-x-coordinate="currentXCoordinate" - :current-data="currentData" - :current-flag-position="currentFlagPosition" :graph-height="graphHeight" :graph-height-offset="graphHeightOffset" - :show-flag-content="showFlagContent" /> <rect class="prometheus-graph-overlay" @@ -277,6 +281,19 @@ </rect> </svg> </svg> + <graph-flag + :real-pixel-ratio="realPixelRatio" + :current-x-coordinate="currentXCoordinate" + :current-data="currentData" + :graph-height="graphHeight" + :graph-height-offset="graphHeightOffset" + :show-flag-content="showFlagContent" + :time-series="timeSeries" + :unit-of-display="unitOfDisplay" + :current-data-index="currentDataIndex" + :legend-title="legendTitle" + :deployment-flag-data="deploymentFlagData" + /> </div> </div> </template> diff --git a/app/assets/javascripts/monitoring/components/graph/deployment.vue b/app/assets/javascripts/monitoring/components/graph/deployment.vue index e3b8be0c7fb..8d6393d4ce5 100644 --- a/app/assets/javascripts/monitoring/components/graph/deployment.vue +++ b/app/assets/javascripts/monitoring/components/graph/deployment.vue @@ -1,12 +1,6 @@ <script> - import { dateFormat, timeFormat } from '../../utils/date_time_formatters'; - export default { props: { - showDeployInfo: { - type: Boolean, - required: true, - }, deploymentData: { type: Array, required: true, @@ -19,10 +13,6 @@ type: Number, required: true, }, - graphWidth: { - type: Number, - required: true, - }, }, computed: { @@ -32,44 +22,17 @@ }, methods: { - refText(d) { - return d.tag ? d.ref : d.sha.slice(0, 6); - }, - - formatTime(deploymentTime) { - return timeFormat(deploymentTime); - }, - - formatDate(deploymentTime) { - return dateFormat(deploymentTime); - }, - - nameDeploymentClass(deployment) { - return `deploy-info-${deployment.id}`; - }, - transformDeploymentGroup(deployment) { - return `translate(${Math.floor(deployment.xPos) + 1}, 20)`; - }, - - positionFlag(deployment) { - let xPosition = 3; - if (deployment.xPos > (this.graphWidth - 200)) { - xPosition = -97; - } - return xPosition; + return `translate(${Math.floor(deployment.xPos) - 5}, 20)`; }, }, }; </script> <template> - <g - class="deploy-info" - v-if="showDeployInfo"> + <g class="deploy-info"> <g v-for="(deployment, index) in deploymentData" :key="index" - :class="nameDeploymentClass(deployment)" :transform="transformDeploymentGroup(deployment)"> <rect x="0" @@ -86,41 +49,6 @@ :y2="calculatedHeight" stroke="#000"> </line> - <svg - v-if="deployment.showDeploymentFlag" - class="js-deploy-info-box" - :x="positionFlag(deployment)" - y="0" - width="92" - height="60"> - <rect - class="rect-text-metric deploy-info-rect rect-metric" - x="1" - y="1" - rx="2" - width="90" - height="58"> - </rect> - <g - transform="translate(5, 2)"> - <text - class="deploy-info-text text-metric-bold"> - {{refText(deployment)}} - </text> - </g> - <text - class="deploy-info-text" - y="18" - transform="translate(5, 2)"> - {{formatDate(deployment.time)}} - </text> - <text - class="deploy-info-text text-metric-bold" - y="38" - transform="translate(5, 2)"> - {{formatTime(deployment.time)}} - </text> - </svg> </g> <svg height="0" diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue index 10fb7ff6803..62ebc3f419c 100644 --- a/app/assets/javascripts/monitoring/components/graph/flag.vue +++ b/app/assets/javascripts/monitoring/components/graph/flag.vue @@ -1,5 +1,7 @@ <script> import { dateFormat, timeFormat } from '../../utils/date_time_formatters'; + import { formatRelevantDigits } from '../../../lib/utils/number_utils'; + import Icon from '../../../vue_shared/components/icon.vue'; export default { props: { @@ -7,14 +9,15 @@ type: Number, required: true, }, - currentFlagPosition: { - type: Number, - required: true, - }, currentData: { type: Object, required: true, }, + deploymentFlagData: { + type: Object, + required: false, + default: null, + }, graphHeight: { type: Number, required: true, @@ -23,71 +26,173 @@ type: Number, required: true, }, + realPixelRatio: { + type: Number, + required: true, + }, showFlagContent: { type: Boolean, required: true, }, + timeSeries: { + type: Array, + required: true, + }, + unitOfDisplay: { + type: String, + required: true, + }, + currentDataIndex: { + type: Number, + required: true, + }, + legendTitle: { + type: String, + required: true, + }, }, - data() { - return { - circleColorRgb: '#8fbce8', - }; + components: { + Icon, }, computed: { formatTime() { - return timeFormat(this.currentData.time); + return this.deploymentFlagData ? + timeFormat(this.deploymentFlagData.time) : + timeFormat(this.currentData.time); }, formatDate() { - return dateFormat(this.currentData.time); + return this.deploymentFlagData ? + dateFormat(this.deploymentFlagData.time) : + dateFormat(this.currentData.time); + }, + + cursorStyle() { + const xCoordinate = this.deploymentFlagData ? + this.deploymentFlagData.xPos : + this.currentXCoordinate; + + const offsetTop = 20 * this.realPixelRatio; + const offsetLeft = (70 + xCoordinate) * this.realPixelRatio; + const height = (this.graphHeight - this.graphHeightOffset) * this.realPixelRatio; + + return { + top: `${offsetTop}px`, + left: `${offsetLeft}px`, + height: `${height}px`, + }; + }, + + flagOrientation() { + if (this.currentXCoordinate * this.realPixelRatio > 120) { + return 'left'; + } + return 'right'; + }, + }, + + methods: { + seriesMetricValue(series) { + const index = this.deploymentFlagData ? + this.deploymentFlagData.seriesIndex : + this.currentDataIndex; + const value = series.values[index] && + series.values[index].value; + if (isNaN(value)) { + return '-'; + } + return `${formatRelevantDigits(value)}${this.unitOfDisplay}`; }, - calculatedHeight() { - return this.graphHeight - this.graphHeightOffset; + seriesMetricLabel(index, series) { + if (this.timeSeries.length < 2) { + return this.legendTitle; + } + if (series.metricTag) { + return series.metricTag; + } + return `series ${index + 1}`; + }, + + strokeDashArray(type) { + if (type === 'dashed') return '6, 3'; + if (type === 'dotted') return '3, 3'; + return null; }, }, }; </script> + <template> - <g class="mouse-over-flag"> - <line - class="selected-metric-line" - :x1="currentXCoordinate" - :y1="0" - :x2="currentXCoordinate" - :y2="calculatedHeight" - transform="translate(-5, 20)"> - </line> - <svg + <div + class="prometheus-graph-cursor" + :style="cursorStyle" + > + <div v-if="showFlagContent" - class="rect-text-metric" - :x="currentFlagPosition" - y="0"> - <rect - class="rect-metric" - x="4" - y="1" - rx="2" - width="90" - height="40" - transform="translate(-3, 20)"> - </rect> - <text - class="text-metric text-metric-bold" - x="16" - y="35" - transform="translate(-5, 20)"> - {{formatTime}} - </text> - <text - class="text-metric" - x="16" - y="15" - transform="translate(-5, 20)"> - {{formatDate}} - </text> - </svg> - </g> + class="prometheus-graph-flag popover" + :class="flagOrientation" + > + <div class="arrow"></div> + <div class="popover-title"> + <h5 v-if="this.deploymentFlagData"> + Deployed + </h5> + {{formatDate}} at + <strong>{{formatTime}}</strong> + </div> + <div + v-if="this.deploymentFlagData" + class="popover-content deploy-meta-content" + > + <div> + <icon + name="commit" + :size="12"> + </icon> + <a :href="deploymentFlagData.commitUrl"> + {{deploymentFlagData.sha.slice(0, 8)}} + </a> + </div> + <div + v-if="deploymentFlagData.tag"> + <icon + name="label" + :size="12"> + </icon> + <a :href="deploymentFlagData.tagUrl"> + {{deploymentFlagData.ref}} + </a> + </div> + </div> + <div class="popover-content"> + <table> + <tr + v-for="(series, index) in timeSeries" + :key="index" + > + <td> + <svg width="15" height="6"> + <line + :stroke="series.lineColor" + :stroke-dasharray="strokeDashArray(series.lineStyle)" + stroke-width="4" + x1="0" + x2="15" + y1="2" + y2="2"> + </line> + </svg> + </td> + <td>{{seriesMetricLabel(index, series)}}</td> + <td> + <strong>{{seriesMetricValue(series)}}</strong> + </td> + </tr> + </table> + </div> + </div> + </div> </template> diff --git a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js index 31f38aca5d6..6cc67ba57ee 100644 --- a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js +++ b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js @@ -29,13 +29,18 @@ const mixins = { time.setSeconds(this.timeSeries[0].values[0].time.getSeconds()); if (xPos >= 0) { + const seriesIndex = bisectDate(this.timeSeries[0].values, time, 1); + deploymentDataArray.push({ id: deployment.id, time, sha: deployment.sha, + commitUrl: `${this.projectPath}/commit/${deployment.sha}`, tag: deployment.tag, + tagUrl: deployment.tag ? `${this.tagsPath}/${deployment.ref.name}` : null, ref: deployment.ref.name, xPos, + seriesIndex, showDeploymentFlag: false, }); } diff --git a/app/assets/javascripts/monitoring/utils/date_time_formatters.js b/app/assets/javascripts/monitoring/utils/date_time_formatters.js index c4c6b1ac1f5..f3c9acdd93e 100644 --- a/app/assets/javascripts/monitoring/utils/date_time_formatters.js +++ b/app/assets/javascripts/monitoring/utils/date_time_formatters.js @@ -1,16 +1,42 @@ -import d3 from 'd3'; +import { timeFormat as time } from 'd3-time-format'; +import { timeSecond, timeMinute, timeHour, timeDay, timeWeek, timeMonth, timeYear } from 'd3-time'; +import { bisector } from 'd3-array'; -export const dateFormat = d3.time.format('%b %-d, %Y'); -export const timeFormat = d3.time.format('%-I:%M%p'); +const d3 = { + time, + bisector, + timeSecond, + timeMinute, + timeHour, + timeDay, + timeWeek, + timeMonth, + timeYear, +}; + +export const dateFormat = d3.time('%a, %b %-d'); +export const timeFormat = d3.time('%-I:%M%p'); +export const dateFormatWithName = d3.time('%a, %b %-d'); export const bisectDate = d3.bisector(d => d.time).left; -export const timeScaleFormat = d3.time.format.multi([ - ['.%L', d => d.getMilliseconds()], - [':%S', d => d.getSeconds()], - ['%-I:%M', d => d.getMinutes()], - ['%-I %p', d => d.getHours()], - ['%a %-d', d => d.getDay() && d.getDate() !== 1], - ['%b %-d', d => d.getDate() !== 1], - ['%B', d => d.getMonth()], - ['%Y', () => true], -]); +export function timeScaleFormat(date) { + let formatFunction; + if (d3.timeSecond(date) < date) { + formatFunction = d3.time('.%L'); + } else if (d3.timeMinute(date) < date) { + formatFunction = d3.time(':%S'); + } else if (d3.timeHour(date) < date) { + formatFunction = d3.time('%-I:%M'); + } else if (d3.timeDay(date) < date) { + formatFunction = d3.time('%-I %p'); + } else if (d3.timeWeek(date) < date) { + formatFunction = d3.time('%a %d'); + } else if (d3.timeMonth(date) < date) { + formatFunction = d3.time('%b %d'); + } else if (d3.timeYear(date) < date) { + formatFunction = d3.time('%B'); + } else { + formatFunction = d3.time('%Y'); + } + return formatFunction(date); +} diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js index d21a265bd43..4ce3dad440c 100644 --- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js +++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js @@ -1,5 +1,10 @@ -import d3 from 'd3'; import _ from 'underscore'; +import { scaleLinear, scaleTime } from 'd3-scale'; +import { line, area, curveLinear } from 'd3-shape'; +import { extent, max } from 'd3-array'; +import { timeMinute } from 'd3-time'; + +const d3 = { scaleLinear, scaleTime, line, area, curveLinear, extent, max, timeMinute }; const defaultColorPalette = { blue: ['#1f78d1', '#8fbce8'], @@ -38,27 +43,27 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom let lineColor = ''; let areaColor = ''; - const timeSeriesScaleX = d3.time.scale() + const timeSeriesScaleX = d3.scaleTime() .range([0, graphWidth - 70]); - const timeSeriesScaleY = d3.scale.linear() + const timeSeriesScaleY = d3.scaleLinear() .range([graphHeight - graphHeightOffset, 0]); timeSeriesScaleX.domain(xDom); - timeSeriesScaleX.ticks(d3.time.minute, 60); + timeSeriesScaleX.ticks(d3.timeMinute, 60); timeSeriesScaleY.domain(yDom); const defined = d => !isNaN(d.value) && d.value != null; - const lineFunction = d3.svg.line() + const lineFunction = d3.line() .defined(defined) - .interpolate('linear') + .curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate .x(d => timeSeriesScaleX(d.time)) .y(d => timeSeriesScaleY(d.value)); - const areaFunction = d3.svg.area() + const areaFunction = d3.area() .defined(defined) - .interpolate('linear') + .curve(d3.curveLinear) .x(d => timeSeriesScaleX(d.time)) .y0(graphHeight - graphHeightOffset) .y1(d => timeSeriesScaleY(d.value)); diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js index 1d496c64e53..aa377327107 100644 --- a/app/assets/javascripts/namespace_select.js +++ b/app/assets/javascripts/namespace_select.js @@ -1,6 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, no-var, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, max-len */ import Api from './api'; -import './lib/utils/url_utility'; +import { mergeUrlParams } from './lib/utils/url_utility'; export default class NamespaceSelect { constructor(opts) { @@ -50,7 +50,7 @@ export default class NamespaceSelect { } }, url(namespace) { - return gl.utils.mergeUrlParams({ [fieldName]: namespace.id }, window.location.href); + return mergeUrlParams({ [fieldName]: namespace.id }, window.location.href); }, }); } diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js index 6e152497d20..a2f0a44863f 100644 --- a/app/assets/javascripts/new_commit_form.js +++ b/app/assets/javascripts/new_commit_form.js @@ -6,11 +6,12 @@ export default class NewCommitForm { this.branchName = form.find('.js-branch-name'); this.originalBranch = form.find('.js-original-branch'); this.createMergeRequest = form.find('.js-create-merge-request'); - this.createMergeRequestContainer = form.find('.js-create-merge-request-container'); + this.createMergeRequestContainer = form.find( + '.js-create-merge-request-container', + ); this.branchName.keyup(this.renderDestination); this.renderDestination(); } - renderDestination() { var different; different = this.branchName.val() !== this.originalBranch.val(); @@ -23,6 +24,6 @@ export default class NewCommitForm { this.createMergeRequestContainer.hide(); this.createMergeRequest.prop('checked', false); } - return this.wasDifferent = different; + return (this.wasDifferent = different); } } diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index e1ab28978e8..a2b8e6f6495 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -16,6 +16,7 @@ import Autosize from 'autosize'; import 'vendor/jquery.caret'; // required by jquery.atwho import 'vendor/jquery.atwho'; import AjaxCache from '~/lib/utils/ajax_cache'; +import { getLocationHash } from './lib/utils/url_utility'; import Flash from './flash'; import CommentTypeToggle from './comment_type_toggle'; import GLForm from './gl_form'; @@ -24,6 +25,7 @@ import Autosave from './autosave'; import TaskList from './task_list'; import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils'; import imageDiffHelper from './image_diff/helpers/index'; +import { localTimeAgo } from './lib/utils/datetime_utility'; window.autosize = Autosize; @@ -35,6 +37,12 @@ const MAX_VISIBLE_COMMIT_LIST_COUNT = 3; const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm; export default class Notes { + static initialize(notes_url, note_ids, last_fetched_at, view, enableGFM = true) { + if (!this.instance) { + this.instance = new Notes(notes_url, note_ids, last_fetched_at, view, enableGFM); + } + } + constructor(notes_url, note_ids, last_fetched_at, view, enableGFM = true) { this.updateTargetButtons = this.updateTargetButtons.bind(this); this.updateComment = this.updateComment.bind(this); @@ -310,7 +318,7 @@ export default class Notes { setupNewNote($note) { // Update datetime format on the recent note - gl.utils.localTimeAgo($note.find('.js-timeago'), false); + localTimeAgo($note.find('.js-timeago'), false); this.collapseLongCommitList(); this.taskList.init(); @@ -330,7 +338,7 @@ export default class Notes { } static updateNoteTargetSelector($note) { - const hash = gl.utils.getLocationHash(); + const hash = getLocationHash(); // Needs to be an explicit true/false for the jQuery `toggleClass(force)` const addTargetClass = Boolean(hash && $note.filter(`#${hash}`).length > 0); $note.toggleClass('target', addTargetClass); @@ -462,7 +470,7 @@ export default class Notes { this.renderDiscussionAvatar(diffAvatarContainer, noteEntity); } - gl.utils.localTimeAgo($('.js-timeago'), false); + localTimeAgo($('.js-timeago'), false); Notes.checkMergeRequestStatus(); return this.updateNotesCount(1); } diff --git a/app/assets/javascripts/notes/components/issue_comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 78986a450c2..e594377bc40 100644 --- a/app/assets/javascripts/notes/components/issue_comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -15,7 +15,7 @@ import issuableStateMixin from '../mixins/issuable_state'; export default { - name: 'issueCommentForm', + name: 'commentForm', data() { return { note: '', diff --git a/app/assets/javascripts/notes/components/issue_note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index a16c5f6a785..ac4e1ffe53a 100644 --- a/app/assets/javascripts/notes/components/issue_note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -2,7 +2,7 @@ import noteEditedText from './note_edited_text.vue'; import noteAwardsList from './note_awards_list.vue'; import noteAttachment from './note_attachment.vue'; - import issueNoteForm from './issue_note_form.vue'; + import noteForm from './note_form.vue'; import TaskList from '../../task_list'; import autosave from '../mixins/autosave'; @@ -29,7 +29,7 @@ noteEditedText, noteAwardsList, noteAttachment, - issueNoteForm, + noteForm, }, computed: { noteBody() { @@ -87,7 +87,7 @@ <div v-html="note.note_html" class="note-text md"></div> - <issue-note-form + <note-form v-if="isEditing" ref="noteForm" @handleFormUpdate="handleFormUpdate" diff --git a/app/assets/javascripts/notes/components/issue_note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index 4d527cb6643..4d527cb6643 100644 --- a/app/assets/javascripts/notes/components/issue_note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue diff --git a/app/assets/javascripts/notes/components/issue_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 460fde9b62a..11e8f805635 100644 --- a/app/assets/javascripts/notes/components/issue_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -2,12 +2,12 @@ import { mapActions, mapGetters } from 'vuex'; import Flash from '../../flash'; import { SYSTEM_NOTE } from '../constants'; - import issueNote from './issue_note.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; + import noteableNote from './noteable_note.vue'; import noteHeader from './note_header.vue'; import noteSignedOutWidget from './note_signed_out_widget.vue'; import noteEditedText from './note_edited_text.vue'; - import issueNoteForm from './issue_note_form.vue'; + import noteForm from './note_form.vue'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; import autosave from '../mixins/autosave'; @@ -25,12 +25,12 @@ }; }, components: { - issueNote, + noteableNote, userAvatarLink, noteHeader, noteSignedOutWidget, noteEditedText, - issueNoteForm, + noteForm, placeholderNote, placeholderSystemNote, }, @@ -86,7 +86,7 @@ return placeholderNote; } - return issueNote; + return noteableNote; }, componentData(note) { return note.isPlaceholderNote ? note.notes[0] : note; @@ -209,7 +209,7 @@ type="button" class="js-vue-discussion-reply btn btn-text-field" title="Add a reply">Reply...</button> - <issue-note-form + <note-form v-if="isReplying" save-button-title="Comment" :discussion="note" diff --git a/app/assets/javascripts/notes/components/issue_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index 8c81c5d6df3..9186d6ff64a 100644 --- a/app/assets/javascripts/notes/components/issue_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -1,10 +1,11 @@ <script> import { mapGetters, mapActions } from 'vuex'; + import { escape } from 'underscore'; import Flash from '../../flash'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import noteHeader from './note_header.vue'; import noteActions from './note_actions.vue'; - import issueNoteBody from './issue_note_body.vue'; + import noteBody from './note_body.vue'; import eventHub from '../event_hub'; export default { @@ -25,7 +26,7 @@ userAvatarLink, noteHeader, noteActions, - issueNoteBody, + noteBody, }, computed: { ...mapGetters([ @@ -85,7 +86,7 @@ }; this.isRequesting = true; this.oldContent = this.note.note_html; - this.note.note_html = noteText; + this.note.note_html = escape(noteText); this.updateNote(data) .then(() => { @@ -122,9 +123,7 @@ // we need to do this to prevent noteForm inconsistent content warning // this is something we intentionally do so we need to recover the content this.note.note = noteText; - if (this.$refs.noteBody) { - this.$refs.noteBody.$refs.noteForm.note = noteText; // TODO: This could be better - } + this.$refs.noteBody.$refs.noteForm.note = noteText; }, }, created() { @@ -173,7 +172,7 @@ @handleDelete="deleteHandler" /> </div> - <issue-note-body + <note-body :note="note" :can-edit="note.current_user.can_edit" :is-editing="isEditing" diff --git a/app/assets/javascripts/notes/components/issue_notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index 4cfcffa2391..c4cae4b3b6f 100644 --- a/app/assets/javascripts/notes/components/issue_notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -1,18 +1,19 @@ <script> import { mapGetters, mapActions } from 'vuex'; + import { getLocationHash } from '../../lib/utils/url_utility'; import Flash from '../../flash'; import store from '../stores/'; import * as constants from '../constants'; - import issueNote from './issue_note.vue'; - import issueDiscussion from './issue_discussion.vue'; + import noteableNote from './noteable_note.vue'; + import noteableDiscussion from './noteable_discussion.vue'; import systemNote from '../../vue_shared/components/notes/system_note.vue'; - import issueCommentForm from './issue_comment_form.vue'; + import commentForm from './comment_form.vue'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue'; export default { - name: 'issueNotesApp', + name: 'notesApp', props: { noteableData: { type: Object, @@ -35,10 +36,10 @@ }; }, components: { - issueNote, - issueDiscussion, + noteableNote, + noteableDiscussion, systemNote, - issueCommentForm, + commentForm, loadingIcon, placeholderNote, placeholderSystemNote, @@ -68,10 +69,10 @@ } return placeholderNote; } else if (note.individual_note) { - return note.notes[0].system ? systemNote : issueNote; + return note.notes[0].system ? systemNote : noteableNote; } - return issueDiscussion; + return noteableDiscussion; }, getComponentData(note) { return note.individual_note ? note.notes[0] : note; @@ -86,7 +87,7 @@ .then(() => this.checkLocationHash()) .catch(() => { this.isLoading = false; - Flash('Something went wrong while fetching issue comments. Please try again.'); + Flash('Something went wrong while fetching comments. Please try again.'); }); }, initPolling() { @@ -95,7 +96,7 @@ this.poll(); }, checkLocationHash() { - const hash = gl.utils.getLocationHash(); + const hash = getLocationHash(); const element = document.getElementById(hash); if (hash && element) { @@ -146,6 +147,6 @@ /> </ul> - <issue-comment-form /> + <comment-form /> </div> </template> diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index a94163a5f87..d250dd8d25b 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -1,10 +1,10 @@ import Vue from 'vue'; -import issueNotesApp from './components/issue_notes_app.vue'; +import notesApp from './components/notes_app.vue'; document.addEventListener('DOMContentLoaded', () => new Vue({ el: '#js-vue-notes', components: { - issueNotesApp, + notesApp, }, data() { const notesDataset = document.getElementById('js-vue-notes').dataset; @@ -32,7 +32,7 @@ document.addEventListener('DOMContentLoaded', () => new Vue({ }; }, render(createElement) { - return createElement('issue-notes-app', { + return createElement('notes-app', { props: { noteableData: this.noteableData, notesData: this.notesData, diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index f90ac2d9f71..9570d1c00aa 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -1,31 +1,25 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, prefer-arrow-callback, no-else-return, max-len */ import Flash from './flash'; -(function() { - this.NotificationsDropdown = (function() { - function NotificationsDropdown() { - $(document).off('click', '.update-notification').on('click', '.update-notification', function(e) { - var form, label, notificationLevel; - e.preventDefault(); - if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') { - return; - } - notificationLevel = $(this).data('notification-level'); - label = $(this).data('notification-title'); - form = $(this).parents('.notification-form:first'); - form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner'); - form.find('#notification_setting_level').val(notificationLevel); - return form.submit(); - }); - $(document).off('ajax:success', '.notification-form').on('ajax:success', '.notification-form', function(e, data) { - if (data.saved) { - return $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html); - } else { - return new Flash('Failed to save new settings', 'alert'); - } - }); +export default function notificationsDropdown() { + $(document).on('click', '.update-notification', function updateNotificationCallback(e) { + e.preventDefault(); + if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') { + return; } - return NotificationsDropdown; - })(); -}).call(window); + const notificationLevel = $(this).data('notification-level'); + const form = $(this).parents('.notification-form:first'); + + form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner'); + form.find('#notification_setting_level').val(notificationLevel); + form.submit(); + }); + + $(document).on('ajax:success', '.notification-form', (e, data) => { + if (data.saved) { + $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html); + } else { + Flash('Failed to save new settings', 'alert'); + } + }); +} diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js index 2ab9c4fed2c..4534360d577 100644 --- a/app/assets/javascripts/notifications_form.js +++ b/app/assets/javascripts/notifications_form.js @@ -1,55 +1,50 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, max-len */ -(function() { - this.NotificationsForm = (function() { - function NotificationsForm() { - this.toggleCheckbox = this.toggleCheckbox.bind(this); - this.removeEventListeners(); - this.initEventListeners(); - } +export default class NotificationsForm { + constructor() { + this.toggleCheckbox = this.toggleCheckbox.bind(this); + this.initEventListeners(); + } - NotificationsForm.prototype.removeEventListeners = function() { - return $(document).off('change', '.js-custom-notification-event'); - }; + initEventListeners() { + $(document).on('change', '.js-custom-notification-event', this.toggleCheckbox); + } - NotificationsForm.prototype.initEventListeners = function() { - return $(document).on('change', '.js-custom-notification-event', this.toggleCheckbox); - }; + toggleCheckbox(e) { + const $checkbox = $(e.currentTarget); + const $parent = $checkbox.closest('.checkbox'); - NotificationsForm.prototype.toggleCheckbox = function(e) { - var $checkbox, $parent; - $checkbox = $(e.currentTarget); - $parent = $checkbox.closest('.checkbox'); - return this.saveEvent($checkbox, $parent); - }; + this.saveEvent($checkbox, $parent); + } - NotificationsForm.prototype.showCheckboxLoadingSpinner = function($parent) { - return $parent.addClass('is-loading').find('.custom-notification-event-loading').removeClass('fa-check').addClass('fa-spin fa-spinner').removeClass('is-done'); - }; + // eslint-disable-next-line class-methods-use-this + showCheckboxLoadingSpinner($parent) { + $parent.addClass('is-loading') + .find('.custom-notification-event-loading') + .removeClass('fa-check') + .addClass('fa-spin fa-spinner') + .removeClass('is-done'); + } - NotificationsForm.prototype.saveEvent = function($checkbox, $parent) { - var form; - form = $parent.parents('form:first'); - return $.ajax({ - url: form.attr('action'), - method: form.attr('method'), - dataType: 'json', - data: form.serialize(), - beforeSend: (function(_this) { - return function() { - return _this.showCheckboxLoadingSpinner($parent); - }; - })(this) - }).done(function(data) { - $checkbox.enable(); - if (data.saved) { - $parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); - return setTimeout(function() { - return $parent.removeClass('is-loading').find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); - }, 2000); - } - }); - }; + saveEvent($checkbox, $parent) { + const form = $parent.parents('form:first'); - return NotificationsForm; - })(); -}).call(window); + return $.ajax({ + url: form.attr('action'), + method: form.attr('method'), + dataType: 'json', + data: form.serialize(), + beforeSend: () => { + this.showCheckboxLoadingSpinner($parent); + }, + }).done((data) => { + $checkbox.enable(); + if (data.saved) { + $parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); + setTimeout(() => { + $parent.removeClass('is-loading') + .find('.custom-notification-event-loading') + .toggleClass('fa-spin fa-spinner fa-check is-done'); + }, 2000); + } + }); + } +} diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index e3fc1e2fc2f..6552a88b606 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -1,78 +1,74 @@ import { getParameterByName } from '~/lib/utils/common_utils'; -import '~/lib/utils/url_utility'; +import { removeParams } from './lib/utils/url_utility'; -(() => { - const ENDLESS_SCROLL_BOTTOM_PX = 400; - const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000; +const ENDLESS_SCROLL_BOTTOM_PX = 400; +const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000; - const Pager = { - init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) { - this.url = $('.content_list').data('href') || gl.utils.removeParams(['limit', 'offset']); - this.limit = limit; - this.offset = parseInt(getParameterByName('offset'), 10) || this.limit; - this.disable = disable; - this.prepareData = prepareData; - this.callback = callback; - this.loading = $('.loading').first(); - if (preload) { - this.offset = 0; - this.getOld(); - } - this.initLoadMore(); - }, +export default { + init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) { + this.url = $('.content_list').data('href') || removeParams(['limit', 'offset']); + this.limit = limit; + this.offset = parseInt(getParameterByName('offset'), 10) || this.limit; + this.disable = disable; + this.prepareData = prepareData; + this.callback = callback; + this.loading = $('.loading').first(); + if (preload) { + this.offset = 0; + this.getOld(); + } + this.initLoadMore(); + }, - getOld() { - this.loading.show(); - $.ajax({ - type: 'GET', - url: this.url, - data: `limit=${this.limit}&offset=${this.offset}`, - dataType: 'json', - error: () => this.loading.hide(), - success: (data) => { - this.append(data.count, this.prepareData(data.html)); - this.callback(); + getOld() { + this.loading.show(); + $.ajax({ + type: 'GET', + url: this.url, + data: `limit=${this.limit}&offset=${this.offset}`, + dataType: 'json', + error: () => this.loading.hide(), + success: (data) => { + this.append(data.count, this.prepareData(data.html)); + this.callback(); - // keep loading until we've filled the viewport height - if (!this.disable && !this.isScrollable()) { - this.getOld(); - } else { - this.loading.hide(); - } - }, - }); - }, + // keep loading until we've filled the viewport height + if (!this.disable && !this.isScrollable()) { + this.getOld(); + } else { + this.loading.hide(); + } + }, + }); + }, - append(count, html) { - $('.content_list').append(html); - if (count > 0) { - this.offset += count; - } else { - this.disable = true; - } - }, + append(count, html) { + $('.content_list').append(html); + if (count > 0) { + this.offset += count; + } else { + this.disable = true; + } + }, - isScrollable() { - const $w = $(window); - return $(document).height() > $w.height() + $w.scrollTop() + ENDLESS_SCROLL_BOTTOM_PX; - }, + isScrollable() { + const $w = $(window); + return $(document).height() > $w.height() + $w.scrollTop() + ENDLESS_SCROLL_BOTTOM_PX; + }, - initLoadMore() { - $(document).unbind('scroll'); - $(document).endlessScroll({ - bottomPixels: ENDLESS_SCROLL_BOTTOM_PX, - fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS, - fireOnce: true, - ceaseFire: () => this.disable === true, - callback: () => { - if (!this.loading.is(':visible')) { - this.loading.show(); - this.getOld(); - } - }, - }); - }, - }; - - window.Pager = Pager; -})(); + initLoadMore() { + $(document).unbind('scroll'); + $(document).endlessScroll({ + bottomPixels: ENDLESS_SCROLL_BOTTOM_PX, + fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS, + fireOnce: true, + ceaseFire: () => this.disable === true, + callback: () => { + if (!this.loading.is(':visible')) { + this.loading.show(); + this.getOld(); + } + }, + }); + }, +}; diff --git a/app/assets/javascripts/pages/users/show/index.js b/app/assets/javascripts/pages/users/show/index.js new file mode 100644 index 00000000000..f18f98b4e9a --- /dev/null +++ b/app/assets/javascripts/pages/users/show/index.js @@ -0,0 +1,3 @@ +import UserCallout from '~/user_callout'; + +export default () => new UserCallout(); diff --git a/app/assets/javascripts/performance_bar.js b/app/assets/javascripts/performance_bar.js index 9bbdf7f513c..0562a681c4b 100644 --- a/app/assets/javascripts/performance_bar.js +++ b/app/assets/javascripts/performance_bar.js @@ -1,5 +1,6 @@ import 'vendor/peek'; import 'vendor/peek.performance_bar'; +import { getParameterValues } from './lib/utils/url_utility'; export default class PerformanceBar { constructor(opts) { @@ -39,7 +40,7 @@ export default class PerformanceBar { } handleLineProfileLink(e) { - const lineProfilerParameter = gl.utils.getParameterValues('lineprofiler'); + const lineProfilerParameter = getParameterValues('lineprofiler'); const lineProfilerParameterRegex = new RegExp(`lineprofiler=${lineProfilerParameter[0]}`); const shouldToggleModal = lineProfilerParameter.length > 0 && lineProfilerParameterRegex.test(e.currentTarget.href); diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue index 0eaac8dd64f..78322f30685 100644 --- a/app/assets/javascripts/pipelines/components/empty_state.vue +++ b/app/assets/javascripts/pipelines/components/empty_state.vue @@ -1,36 +1,41 @@ <script> -export default { - props: { - helpPagePath: { - type: String, - required: true, + export default { + props: { + helpPagePath: { + type: String, + required: true, + }, + emptyStateSvgPath: { + type: String, + required: true, + }, }, - emptyStateSvgPath: { - type: String, - required: true, - }, - }, -}; + }; </script> - <template> <div class="row empty-state js-empty-state"> <div class="col-xs-12"> - <div class="svg-content"> - <img :src="emptyStateSvgPath"/> + <div class="svg-content svg-250"> + <img :src="emptyStateSvgPath" /> </div> </div> - <div class="col-xs-12 text-center"> + <div class="col-xs-12"> <div class="text-content"> - <h4>Build with confidence</h4> + <h4 class="text-center"> + {{ s__("Pipelines|Build with confidence") }} + </h4> <p> - Continous Integration can help catch bugs by running your tests automatically, - while Continuous Deployment can help you deliver code to your product environment. + {{ s__("Pipelines|Continous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment.") }} </p> - <a :href="helpPagePath" class="btn btn-info"> - Get started with Pipelines - </a> + <div class="text-center"> + <a + :href="helpPagePath" + class="btn btn-info" + > + {{ s__("Pipelines|Get started with Pipelines") }} + </a> + </div> </div> </div> </div> diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue index 08199b4234a..b01c799643c 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue @@ -59,8 +59,26 @@ }, computed: { + status() { + return this.job && this.job.status ? this.job.status : {}; + }, + tooltipText() { - return `${this.job.name} - ${this.job.status.label}`; + const textBuilder = []; + + if (this.job.name) { + textBuilder.push(this.job.name); + } + + if (this.job.name && this.status.label) { + textBuilder.push('-'); + } + + if (this.status.label) { + textBuilder.push(`${this.job.status.label}`); + } + + return textBuilder.join(' '); }, /** @@ -78,8 +96,8 @@ <div class="ci-job-component"> <a v-tooltip - v-if="job.status.has_details" - :href="job.status.details_path" + v-if="status.has_details" + :href="status.details_path" :title="tooltipText" :class="cssClassJobName" data-container="body" @@ -95,6 +113,7 @@ <div v-else v-tooltip + class="js-job-component-tooltip" :title="tooltipText" :class="cssClassJobName" data-container="body" @@ -108,18 +127,18 @@ <action-component v-if="hasAction && !isDropdown" - :tooltip-text="job.status.action.title" - :link="job.status.action.path" - :action-icon="job.status.action.icon" - :action-method="job.status.action.method" + :tooltip-text="status.action.title" + :link="status.action.path" + :action-icon="status.action.icon" + :action-method="status.action.method" /> <dropdown-action-component v-if="hasAction && isDropdown" - :tooltip-text="job.status.action.title" - :link="job.status.action.path" - :action-icon="job.status.action.icon" - :action-method="job.status.action.method" + :tooltip-text="status.action.title" + :link="status.action.path" + :action-icon="status.action.icon" + :action-method="status.action.method" /> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue index 632fc167f2b..f31a91c3403 100644 --- a/app/assets/javascripts/pipelines/components/nav_controls.vue +++ b/app/assets/javascripts/pipelines/components/nav_controls.vue @@ -17,6 +17,11 @@ export default { required: true, }, + resetCachePath: { + type: String, + required: true, + }, + ciLintPath: { type: String, required: true, @@ -46,6 +51,14 @@ export default { </a> <a + data-method="post" + rel="nofollow" + :href="resetCachePath" + class="btn btn-default"> + Clear runner caches + </a> + + <a :href="ciLintPath" class="btn btn-default"> CI Lint diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue index fe1f3b4246a..8fa416168e7 100644 --- a/app/assets/javascripts/pipelines/components/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -50,6 +50,7 @@ canCreatePipeline: pipelinesData.canCreatePipeline, hasCi: pipelinesData.hasCi, ciLintPath: pipelinesData.ciLintPath, + resetCachePath: pipelinesData.resetCachePath, state: this.store.state, scope: getParameterByName('scope') || 'all', page: getParameterByName('page') || '1', @@ -220,6 +221,7 @@ :new-pipeline-path="newPipelinePath" :has-ci-enabled="hasCiEnabled" :help-page-path="helpPagePath" + :resetCachePath="resetCachePath" :ci-lint-path="ciLintPath" :can-create-pipeline="canCreatePipelineParsed " /> diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue index 751a20991af..831aa92ac4f 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -1,5 +1,6 @@ <script> import tooltip from '../../vue_shared/directives/tooltip'; + import icon from '../../vue_shared/components/icon.vue'; export default { props: { @@ -11,6 +12,9 @@ directives: { tooltip, }, + components: { + icon, + }, }; </script> <template> @@ -24,10 +28,9 @@ data-placement="top" data-toggle="dropdown" aria-label="Artifacts"> - <i - class="fa fa-download" - aria-hidden="true"> - </i> + <icon + name="download"> + </icon> <i class="fa fa-caret-down" aria-hidden="true"> diff --git a/app/assets/javascripts/pipelines/pipelines_bundle.js b/app/assets/javascripts/pipelines/pipelines_bundle.js index 923d9bfb248..3e4b6eeb5bf 100644 --- a/app/assets/javascripts/pipelines/pipelines_bundle.js +++ b/app/assets/javascripts/pipelines/pipelines_bundle.js @@ -1,6 +1,9 @@ import Vue from 'vue'; import PipelinesStore from './stores/pipelines_store'; import pipelinesComponent from './components/pipelines.vue'; +import Translate from '../vue_shared/translate'; + +Vue.use(Translate); document.addEventListener('DOMContentLoaded', () => new Vue({ el: '#pipelines-list-vue', diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index 141333b2b4d..ffaafb3ee9e 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -117,12 +117,10 @@ }()); markdownPreview = new window.MarkdownPreview(); - previewButtonSelector = '.js-md-preview-button'; - writeButtonSelector = '.js-md-write-button'; - lastTextareaPreviewed = null; + const markdownToolbar = $('.md-header-toolbar'); $.fn.setupMarkdownPreview = function () { var $form = $(this); @@ -146,6 +144,7 @@ // toggle content $form.find('.md-write-holder').hide(); $form.find('.md-preview-holder').show(); + markdownToolbar.removeClass('active'); markdownPreview.showPreview($form); }); @@ -167,6 +166,7 @@ $form.find('.md-write-holder').show(); $form.find('textarea.markdown-area').focus(); $form.find('.md-preview-holder').hide(); + markdownToolbar.addClass('active'); markdownPreview.hideReferencedCommands($form); }); diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue index 6348a2e331d..36ad618aa46 100644 --- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue +++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue @@ -1,7 +1,7 @@ <script> - import popupDialog from '../../../vue_shared/components/popup_dialog.vue'; - import { __, s__, sprintf } from '../../../locale'; - import csrf from '../../../lib/utils/csrf'; + import modal from '~/vue_shared/components/modal.vue'; + import { __, s__, sprintf } from '~/locale'; + import csrf from '~/lib/utils/csrf'; export default { props: { @@ -22,11 +22,10 @@ return { enteredPassword: '', enteredUsername: '', - isOpen: false, }; }, components: { - popupDialog, + modal, }, computed: { csrfToken() { @@ -69,78 +68,58 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), return this.enteredUsername === this.username; }, - onSubmit(status) { - if (status) { - if (!this.canSubmit()) { - return; - } - - this.$refs.form.submit(); - } - - this.toggleOpen(false); - }, - toggleOpen(isOpen) { - this.isOpen = isOpen; + onSubmit() { + this.$refs.form.submit(); }, }, }; </script> <template> - <div> - <popup-dialog - v-if="isOpen" - :title="s__('Profiles|Delete your account?')" - :text="text" - :kind="`danger ${!canSubmit() && 'disabled'}`" - :primary-button-label="s__('Profiles|Delete account')" - @toggle="toggleOpen" - @submit="onSubmit"> - - <template slot="body" slot-scope="props"> - <p v-html="props.text"></p> + <modal + id="delete-account-modal" + :title="s__('Profiles|Delete your account?')" + :text="text" + kind="danger" + :primary-button-label="s__('Profiles|Delete account')" + @submit="onSubmit" + :submit-disabled="!canSubmit()"> - <form - ref="form" - :action="actionUrl" - method="post"> + <template slot="body" slot-scope="props"> + <p v-html="props.text"></p> - <input - type="hidden" - name="_method" - value="delete" /> - <input - type="hidden" - name="authenticity_token" - :value="csrfToken" /> + <form + ref="form" + :action="actionUrl" + method="post"> - <p id="input-label" v-html="inputLabel"></p> + <input + type="hidden" + name="_method" + value="delete" /> + <input + type="hidden" + name="authenticity_token" + :value="csrfToken" /> - <input - v-if="confirmWithPassword" - name="password" - class="form-control" - type="password" - v-model="enteredPassword" - aria-labelledby="input-label" /> - <input - v-else - name="username" - class="form-control" - type="text" - v-model="enteredUsername" - aria-labelledby="input-label" /> - </form> - </template> + <p id="input-label" v-html="inputLabel"></p> - </popup-dialog> + <input + v-if="confirmWithPassword" + name="password" + class="form-control" + type="password" + v-model="enteredPassword" + aria-labelledby="input-label" /> + <input + v-else + name="username" + class="form-control" + type="text" + v-model="enteredUsername" + aria-labelledby="input-label" /> + </form> + </template> - <button - type="button" - class="btn btn-danger" - @click="toggleOpen(true)"> - {{ s__('Profiles|Delete account') }} - </button> - </div> + </modal> </template> diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js index 635056e0eeb..a93bc935dd0 100644 --- a/app/assets/javascripts/profile/account/index.js +++ b/app/assets/javascripts/profile/account/index.js @@ -1,7 +1,12 @@ import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; + import deleteAccountModal from './components/delete_account_modal.vue'; +Vue.use(Translate); + +const deleteAccountButton = document.getElementById('delete-account-button'); const deleteAccountModalEl = document.getElementById('delete-account-modal'); // eslint-disable-next-line no-new new Vue({ @@ -9,6 +14,9 @@ new Vue({ components: { deleteAccountModal, }, + mounted() { + deleteAccountButton.classList.remove('disabled'); + }, render(createElement) { return createElement('delete-account-modal', { props: { diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js index 0dc02f012e4..ba4ac850346 100644 --- a/app/assets/javascripts/profile/profile.js +++ b/app/assets/javascripts/profile/profile.js @@ -1,4 +1,5 @@ /* eslint-disable comma-dangle, no-unused-vars, class-methods-use-this, quotes, consistent-return, func-names, prefer-arrow-callback, space-before-function-paren, max-len */ +import Cookies from 'js-cookie'; import Flash from '../flash'; import { getPagePath } from '../lib/utils/common_utils'; @@ -7,6 +8,8 @@ import { getPagePath } from '../lib/utils/common_utils'; constructor({ form } = {}) { this.onSubmitForm = this.onSubmitForm.bind(this); this.form = form || $('.edit-user'); + this.newRepoActivated = Cookies.get('new_repo'); + this.setRepoRadio(); this.bindEvents(); this.initAvatarGlCrop(); } @@ -25,6 +28,7 @@ import { getPagePath } from '../lib/utils/common_utils'; bindEvents() { $('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm); + $('input[name="user[multi_file]"]').on('change', this.setNewRepoCookie); $('#user_notification_email').on('change', this.submitForm); $('#user_notified_of_own_activity').on('change', this.submitForm); $('.update-username').on('ajax:before', this.beforeUpdateUsername); @@ -82,6 +86,23 @@ import { getPagePath } from '../lib/utils/common_utils'; } }); } + + setNewRepoCookie() { + if (this.value === 'off') { + Cookies.remove('new_repo'); + } else { + Cookies.set('new_repo', true, { expires_in: 365 }); + } + } + + setRepoRadio() { + const multiEditRadios = $('input[name="user[multi_file]"]'); + if (this.newRepoActivated || this.newRepoActivated === 'true') { + multiEditRadios.filter('[value=on]').prop('checked', true); + } else { + multiEditRadios.filter('[value=off]').prop('checked', true); + } + } } $(function() { diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index 3131e71d9d6..d4f26b81f30 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -1,6 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, no-var, consistent-return, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, no-else-return, newline-per-chained-call, no-shadow, vars-on-top, prefer-template, max-len */ import Cookies from 'js-cookie'; +import { visitUrl } from './lib/utils/url_utility'; import projectSelect from './project_select'; export default class Project { @@ -122,7 +123,7 @@ export default class Project { var action = $form.attr('action'); var divider = action.indexOf('?') === -1 ? '?' : '&'; if (shouldVisit) { - gl.utils.visitUrl(`${action}${divider}${$form.serialize()}`); + visitUrl(`${action}${divider}${$form.serialize()}`); } } }, diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index 19682b20a4a..0da32b4a3cc 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -2,169 +2,163 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus'; -(function() { - this.ProjectFindFile = (function() { - var highlighter; - - function ProjectFindFile(element1, options) { - this.element = element1; - this.options = options; - this.goToBlob = this.goToBlob.bind(this); - this.goToTree = this.goToTree.bind(this); - this.selectRowDown = this.selectRowDown.bind(this); - this.selectRowUp = this.selectRowUp.bind(this); - this.filePaths = {}; - this.inputElement = this.element.find(".file-finder-input"); - // init event - this.initEvent(); - // focus text input box - this.inputElement.focus(); - // load file list - this.load(this.options.url); +// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) +const highlighter = function(element, text, matches) { + var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched; + lastIndex = 0; + highlightText = ""; + matchedChars = []; + for (j = 0, len = matches.length; j < len; j += 1) { + matchIndex = matches[j]; + unmatched = text.substring(lastIndex, matchIndex); + if (unmatched) { + if (matchedChars.length) { + element.append(matchedChars.join("").bold()); + } + matchedChars = []; + element.append(document.createTextNode(unmatched)); } - - ProjectFindFile.prototype.initEvent = function() { - this.inputElement.off("keyup"); - this.inputElement.on("keyup", (function(_this) { - return function(event) { - var oldValue, ref, target, value; - target = $(event.target); - value = target.val(); - oldValue = (ref = target.data("oldValue")) != null ? ref : ""; - if (value !== oldValue) { - target.data("oldValue", value); - _this.findFile(); - return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus(); - } - }; - })(this)); - }; - - ProjectFindFile.prototype.findFile = function() { - var result, searchText; - searchText = this.inputElement.val(); - result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; - return this.renderList(result, searchText); - // find file - }; + matchedChars.push(text[matchIndex]); + lastIndex = matchIndex + 1; + } + if (matchedChars.length) { + element.append(matchedChars.join("").bold()); + } + return element.append(document.createTextNode(text.substring(lastIndex))); +}; + +export default class ProjectFindFile { + constructor(element1, options) { + this.element = element1; + this.options = options; + this.goToBlob = this.goToBlob.bind(this); + this.goToTree = this.goToTree.bind(this); + this.selectRowDown = this.selectRowDown.bind(this); + this.selectRowUp = this.selectRowUp.bind(this); + this.filePaths = {}; + this.inputElement = this.element.find(".file-finder-input"); + // init event + this.initEvent(); + // focus text input box + this.inputElement.focus(); + // load file list + this.load(this.options.url); + } + + initEvent() { + this.inputElement.off("keyup"); + this.inputElement.on("keyup", (function(_this) { + return function(event) { + var oldValue, ref, target, value; + target = $(event.target); + value = target.val(); + oldValue = (ref = target.data("oldValue")) != null ? ref : ""; + if (value !== oldValue) { + target.data("oldValue", value); + _this.findFile(); + return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus(); + } + }; + })(this)); + } + + findFile() { + var result, searchText; + searchText = this.inputElement.val(); + result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; + return this.renderList(result, searchText); + // find file + } // files pathes load - ProjectFindFile.prototype.load = function(url) { - return $.ajax({ - url: url, - method: "get", - dataType: "json", - success: (function(_this) { - return function(data) { - _this.element.find(".loading").hide(); - _this.filePaths = data; - _this.findFile(); - return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus(); - }; - })(this) - }); - }; + load(url) { + return $.ajax({ + url: url, + method: "get", + dataType: "json", + success: (function(_this) { + return function(data) { + _this.element.find(".loading").hide(); + _this.filePaths = data; + _this.findFile(); + return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus(); + }; + })(this) + }); + } // render result - ProjectFindFile.prototype.renderList = function(filePaths, searchText) { - var blobItemUrl, filePath, html, i, j, len, matches, results; - this.element.find(".tree-table > tbody").empty(); - results = []; - for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) { - filePath = filePaths[i]; - if (i === 20) { - break; - } - if (searchText) { - matches = fuzzaldrinPlus.match(filePath, searchText); - } - blobItemUrl = this.options.blobUrlTemplate + "/" + filePath; - html = this.makeHtml(filePath, matches, blobItemUrl); - results.push(this.element.find(".tree-table > tbody").append(html)); - } - return results; - }; - - // highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) - highlighter = function(element, text, matches) { - var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched; - lastIndex = 0; - highlightText = ""; - matchedChars = []; - for (j = 0, len = matches.length; j < len; j += 1) { - matchIndex = matches[j]; - unmatched = text.substring(lastIndex, matchIndex); - if (unmatched) { - if (matchedChars.length) { - element.append(matchedChars.join("").bold()); - } - matchedChars = []; - element.append(document.createTextNode(unmatched)); - } - matchedChars.push(text[matchIndex]); - lastIndex = matchIndex + 1; - } - if (matchedChars.length) { - element.append(matchedChars.join("").bold()); + renderList(filePaths, searchText) { + var blobItemUrl, filePath, html, i, j, len, matches, results; + this.element.find(".tree-table > tbody").empty(); + results = []; + for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) { + filePath = filePaths[i]; + if (i === 20) { + break; } - return element.append(document.createTextNode(text.substring(lastIndex))); - }; - - // make tbody row html - ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) { - var $tr; - $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>"); - if (matches) { - $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)); - } else { - $tr.find("a").attr("href", blobItemUrl); - $tr.find(".str-truncated").text(filePath); + if (searchText) { + matches = fuzzaldrinPlus.match(filePath, searchText); } - return $tr; - }; - - ProjectFindFile.prototype.selectRow = function(type) { - var next, rows, selectedRow; - rows = this.element.find(".files-slider tr.tree-item"); - selectedRow = this.element.find(".files-slider tr.tree-item.selected"); - if (rows && rows.length > 0) { - if (selectedRow && selectedRow.length > 0) { - if (type === "UP") { - next = selectedRow.prev(); - } else if (type === "DOWN") { - next = selectedRow.next(); - } - if (next.length > 0) { - selectedRow.removeClass("selected"); - selectedRow = next; - } - } else { - selectedRow = rows.eq(0); + blobItemUrl = this.options.blobUrlTemplate + "/" + filePath; + html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl); + results.push(this.element.find(".tree-table > tbody").append(html)); + } + return results; + } + + // make tbody row html + static makeHtml(filePath, matches, blobItemUrl) { + var $tr; + $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>"); + if (matches) { + $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)); + } else { + $tr.find("a").attr("href", blobItemUrl); + $tr.find(".str-truncated").text(filePath); + } + return $tr; + } + + selectRow(type) { + var next, rows, selectedRow; + rows = this.element.find(".files-slider tr.tree-item"); + selectedRow = this.element.find(".files-slider tr.tree-item.selected"); + if (rows && rows.length > 0) { + if (selectedRow && selectedRow.length > 0) { + if (type === "UP") { + next = selectedRow.prev(); + } else if (type === "DOWN") { + next = selectedRow.next(); + } + if (next.length > 0) { + selectedRow.removeClass("selected"); + selectedRow = next; } - return selectedRow.addClass("selected").focus(); + } else { + selectedRow = rows.eq(0); } - }; - - ProjectFindFile.prototype.selectRowUp = function() { - return this.selectRow("UP"); - }; + return selectedRow.addClass("selected").focus(); + } + } - ProjectFindFile.prototype.selectRowDown = function() { - return this.selectRow("DOWN"); - }; + selectRowUp() { + return this.selectRow("UP"); + } - ProjectFindFile.prototype.goToTree = function() { - return location.href = this.options.treeUrl; - }; + selectRowDown() { + return this.selectRow("DOWN"); + } - ProjectFindFile.prototype.goToBlob = function() { - var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); + goToTree() { + return location.href = this.options.treeUrl; + } - if ($link.length) { - $link.get(0).click(); - } - }; + goToBlob() { + var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); - return ProjectFindFile; - })(); -}).call(window); + if ($link.length) { + $link.get(0).click(); + } + } +} diff --git a/app/assets/javascripts/project_variables.js b/app/assets/javascripts/project_variables.js deleted file mode 100644 index 567c311f119..00000000000 --- a/app/assets/javascripts/project_variables.js +++ /dev/null @@ -1,39 +0,0 @@ - -const HIDDEN_VALUE_TEXT = '******'; - -export default class ProjectVariables { - constructor() { - this.$revealBtn = $('.js-btn-toggle-reveal-values'); - this.$revealBtn.on('click', this.toggleRevealState.bind(this)); - } - - toggleRevealState(e) { - e.preventDefault(); - - const oldStatus = this.$revealBtn.attr('data-status'); - let newStatus = 'hidden'; - let newAction = 'Reveal Values'; - - if (oldStatus === 'hidden') { - newStatus = 'revealed'; - newAction = 'Hide Values'; - } - - this.$revealBtn.attr('data-status', newStatus); - - const $variables = $('.variable-value'); - - $variables.each((_, variable) => { - const $variable = $(variable); - let newText = HIDDEN_VALUE_TEXT; - - if (newStatus === 'revealed') { - newText = $variable.attr('data-value'); - } - - $variable.text(newText); - }); - - this.$revealBtn.text(newAction); - } -} diff --git a/app/assets/javascripts/projects/project_import_gitlab_project.js b/app/assets/javascripts/projects/project_import_gitlab_project.js index c34927499fc..cec6f0dd5a3 100644 --- a/app/assets/javascripts/projects/project_import_gitlab_project.js +++ b/app/assets/javascripts/projects/project_import_gitlab_project.js @@ -1,7 +1,7 @@ -import '../lib/utils/url_utility'; +import { getParameterValues } from '../lib/utils/url_utility'; const bindEvents = () => { - const path = gl.utils.getParameterValues('path')[0]; + const path = getParameterValues('path')[0]; // get the path url and append it in the inputS $('.js-path-name').val(path); diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 3ecc0c2a6e5..4710e70d619 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -1,6 +1,7 @@ let hasUserDefinedProjectPath = false; -const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => { +const deriveProjectPathFromUrl = ($projectImportUrl) => { + const $currentProjectPath = $projectImportUrl.parents('.toggle-import-form').find('#project_path'); if (hasUserDefinedProjectPath) { return; } @@ -21,7 +22,7 @@ const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => { // extract everything after the last slash const pathMatch = /\/([^/]+)$/.exec(importUrl); if (pathMatch) { - $projectPath.val(pathMatch[1]); + $currentProjectPath.val(pathMatch[1]); } }; @@ -96,7 +97,7 @@ const bindEvents = () => { hasUserDefinedProjectPath = $projectPath.val().trim().length > 0; }); - $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl, $projectPath)); + $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl)); }; document.addEventListener('DOMContentLoaded', bindEvents); diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js index c91a0d9ba41..5482c55f8bb 100644 --- a/app/assets/javascripts/render_gfm.js +++ b/app/assets/javascripts/render_gfm.js @@ -1,12 +1,12 @@ import renderMath from './render_math'; import renderMermaid from './render_mermaid'; - +import syntaxHighlight from './syntax_highlight'; // Render Gitlab flavoured Markdown // // Delegates to syntax highlight and render math & mermaid diagrams. // $.fn.renderGFM = function renderGFM() { - this.find('.js-syntax-highlight').syntaxHighlight(); + syntaxHighlight(this.find('.js-syntax-highlight')); renderMath(this.find('.js-render-math')); renderMermaid(this.find('.js-render-mermaid')); return this; diff --git a/app/assets/javascripts/render_mermaid.js b/app/assets/javascripts/render_mermaid.js index 41942c04a4e..b7cde6fb092 100644 --- a/app/assets/javascripts/render_mermaid.js +++ b/app/assets/javascripts/render_mermaid.js @@ -24,7 +24,25 @@ export default function renderMermaid($els) { }); $els.each((i, el) => { - mermaid.init(undefined, el); + const source = el.textContent; + + mermaid.init(undefined, el, (id) => { + const svg = document.getElementById(id); + + svg.classList.add('mermaid'); + + // pre > code > svg + svg.closest('pre').replaceWith(svg); + + // We need to add the original source into the DOM to allow Copy-as-GFM + // to access it. + const sourceEl = document.createElement('text'); + sourceEl.classList.add('source'); + sourceEl.setAttribute('display', 'none'); + sourceEl.textContent = source; + + svg.appendChild(sourceEl); + }); }); }).catch((err) => { Flash(`Can't load mermaid module: ${err}`); diff --git a/app/assets/javascripts/repo/components/commit_sidebar/list.vue b/app/assets/javascripts/repo/components/commit_sidebar/list.vue deleted file mode 100644 index fb862e7bf01..00000000000 --- a/app/assets/javascripts/repo/components/commit_sidebar/list.vue +++ /dev/null @@ -1,89 +0,0 @@ -<script> - import icon from '../../../vue_shared/components/icon.vue'; - import listItem from './list_item.vue'; - import listCollapsed from './list_collapsed.vue'; - - export default { - components: { - icon, - listItem, - listCollapsed, - }, - props: { - title: { - type: String, - required: true, - }, - fileList: { - type: Array, - required: true, - }, - collapsed: { - type: Boolean, - required: true, - }, - }, - methods: { - toggleCollapsed() { - this.$emit('toggleCollapsed'); - }, - }, - }; -</script> - -<template> - <div class="multi-file-commit-panel-section"> - <header - class="multi-file-commit-panel-header" - :class="{ - 'is-collapsed': collapsed, - }" - > - <icon - name="list-bulleted" - :size="18" - css-classes="append-right-default" - /> - <template v-if="!collapsed"> - {{ title }} - <button - type="button" - class="btn btn-transparent multi-file-commit-panel-collapse-btn" - @click="toggleCollapsed" - > - <i - aria-hidden="true" - class="fa fa-angle-double-right" - > - </i> - </button> - </template> - </header> - <div class="multi-file-commit-list"> - <list-collapsed - v-if="collapsed" - /> - <template v-else> - <ul - v-if="fileList.length" - class="list-unstyled append-bottom-0" - > - <li - v-for="file in fileList" - :key="file.key" - > - <list-item - :file="file" - /> - </li> - </ul> - <div - v-else - class="help-block prepend-top-0" - > - No changes - </div> - </template> - </div> - </div> -</template> diff --git a/app/assets/javascripts/repo/components/new_dropdown/index.vue b/app/assets/javascripts/repo/components/new_dropdown/index.vue deleted file mode 100644 index 781404cf8ca..00000000000 --- a/app/assets/javascripts/repo/components/new_dropdown/index.vue +++ /dev/null @@ -1,89 +0,0 @@ -<script> - import { mapState } from 'vuex'; - import newModal from './modal.vue'; - import upload from './upload.vue'; - import icon from '../../../vue_shared/components/icon.vue'; - - export default { - components: { - icon, - newModal, - upload, - }, - data() { - return { - openModal: false, - modalType: '', - }; - }, - computed: { - ...mapState([ - 'path', - ]), - }, - methods: { - createNewItem(type) { - this.modalType = type; - this.toggleModalOpen(); - }, - toggleModalOpen() { - this.openModal = !this.openModal; - }, - }, - }; -</script> - -<template> - <div> - <ul class="breadcrumb repo-breadcrumb"> - <li class="dropdown"> - <button - type="button" - class="btn btn-default dropdown-toggle add-to-tree" - data-toggle="dropdown" - aria-label="Create new file or directory" - > - <icon - name="plus" - css-classes="pull-left" - /> - <icon - name="arrow-down" - css-classes="pull-left" - /> - </button> - <ul class="dropdown-menu"> - <li> - <a - href="#" - role="button" - @click.prevent="createNewItem('blob')" - > - {{ __('New file') }} - </a> - </li> - <li> - <upload - :path="path" - /> - </li> - <li> - <a - href="#" - role="button" - @click.prevent="createNewItem('tree')" - > - {{ __('New directory') }} - </a> - </li> - </ul> - </li> - </ul> - <new-modal - v-if="openModal" - :type="modalType" - :path="path" - @toggle="toggleModalOpen" - /> - </div> -</template> diff --git a/app/assets/javascripts/repo/components/repo.vue b/app/assets/javascripts/repo/components/repo.vue deleted file mode 100644 index a00e1e9d809..00000000000 --- a/app/assets/javascripts/repo/components/repo.vue +++ /dev/null @@ -1,63 +0,0 @@ -<script> -import { mapState, mapGetters } from 'vuex'; -import RepoSidebar from './repo_sidebar.vue'; -import RepoCommitSection from './repo_commit_section.vue'; -import RepoTabs from './repo_tabs.vue'; -import RepoFileButtons from './repo_file_buttons.vue'; -import RepoPreview from './repo_preview.vue'; -import repoEditor from './repo_editor.vue'; - -export default { - computed: { - ...mapState([ - 'currentBlobView', - ]), - ...mapGetters([ - 'isCollapsed', - 'changedFiles', - ]), - }, - components: { - RepoSidebar, - RepoTabs, - RepoFileButtons, - repoEditor, - RepoCommitSection, - RepoPreview, - }, - mounted() { - const returnValue = 'Are you sure you want to lose unsaved changes?'; - window.onbeforeunload = (e) => { - if (!this.changedFiles.length) return undefined; - - Object.assign(e, { - returnValue, - }); - return returnValue; - }; - }, -}; -</script> - -<template> - <div - class="multi-file" - :class="{ - 'is-collapsed': isCollapsed - }" - > - <repo-sidebar/> - <div - v-if="isCollapsed" - class="multi-file-edit-pane" - > - <repo-tabs /> - <component - class="multi-file-edit-pane-content" - :is="currentBlobView" - /> - <repo-file-buttons /> - </div> - <repo-commit-section /> - </div> -</template> diff --git a/app/assets/javascripts/repo/components/repo_sidebar.vue b/app/assets/javascripts/repo/components/repo_sidebar.vue deleted file mode 100644 index 4ea21913129..00000000000 --- a/app/assets/javascripts/repo/components/repo_sidebar.vue +++ /dev/null @@ -1,85 +0,0 @@ -<script> -import { mapState, mapGetters, mapActions } from 'vuex'; -import RepoPreviousDirectory from './repo_prev_directory.vue'; -import RepoFile from './repo_file.vue'; -import RepoLoadingFile from './repo_loading_file.vue'; - -export default { - components: { - 'repo-previous-directory': RepoPreviousDirectory, - 'repo-file': RepoFile, - 'repo-loading-file': RepoLoadingFile, - }, - created() { - window.addEventListener('popstate', this.popHistoryState); - }, - destroyed() { - window.removeEventListener('popstate', this.popHistoryState); - }, - mounted() { - this.getTreeData(); - }, - computed: { - ...mapState([ - 'loading', - 'isRoot', - ]), - ...mapState({ - projectName(state) { - return state.project.name; - }, - }), - ...mapGetters([ - 'treeList', - 'isCollapsed', - ]), - }, - methods: { - ...mapActions([ - 'getTreeData', - 'popHistoryState', - ]), - }, -}; -</script> - -<template> -<div class="ide-file-list"> - <table class="table"> - <thead> - <tr> - <th - v-if="isCollapsed" - > - </th> - <template v-else> - <th class="name multi-file-table-name"> - Name - </th> - <th class="hidden-sm hidden-xs last-commit"> - Last commit - </th> - <th class="hidden-xs last-update text-right"> - Last update - </th> - </template> - </tr> - </thead> - <tbody> - <repo-previous-directory - v-if="!isRoot && treeList.length" - /> - <repo-loading-file - v-if="!treeList.length && loading" - v-for="n in 5" - :key="n" - /> - <repo-file - v-for="file in treeList" - :key="file.key" - :file="file" - /> - </tbody> - </table> -</div> -</template> diff --git a/app/assets/javascripts/repo/index.js b/app/assets/javascripts/repo/index.js deleted file mode 100644 index b6801af7fcb..00000000000 --- a/app/assets/javascripts/repo/index.js +++ /dev/null @@ -1,106 +0,0 @@ -import Vue from 'vue'; -import { mapActions } from 'vuex'; -import { convertPermissionToBoolean } from '../lib/utils/common_utils'; -import Repo from './components/repo.vue'; -import RepoEditButton from './components/repo_edit_button.vue'; -import newBranchForm from './components/new_branch_form.vue'; -import newDropdown from './components/new_dropdown/index.vue'; -import store from './stores'; -import Translate from '../vue_shared/translate'; - -function initRepo(el) { - if (!el) return null; - - return new Vue({ - el, - store, - components: { - repo: Repo, - }, - methods: { - ...mapActions([ - 'setInitialData', - ]), - }, - created() { - const data = el.dataset; - - this.setInitialData({ - project: { - id: data.projectId, - name: data.projectName, - url: data.projectUrl, - }, - endpoints: { - rootEndpoint: data.url, - newMergeRequestUrl: data.newMergeRequestUrl, - rootUrl: data.rootUrl, - }, - canCommit: convertPermissionToBoolean(data.canCommit), - onTopOfBranch: convertPermissionToBoolean(data.onTopOfBranch), - currentRef: data.ref, - path: data.currentPath, - currentBranch: data.currentBranch, - isRoot: convertPermissionToBoolean(data.root), - isInitialRoot: convertPermissionToBoolean(data.root), - }); - }, - render(createElement) { - return createElement('repo'); - }, - }); -} - -function initRepoEditButton(el) { - return new Vue({ - el, - store, - components: { - repoEditButton: RepoEditButton, - }, - render(createElement) { - return createElement('repo-edit-button'); - }, - }); -} - -function initNewDropdown(el) { - return new Vue({ - el, - store, - components: { - newDropdown, - }, - render(createElement) { - return createElement('new-dropdown'); - }, - }); -} - -function initNewBranchForm() { - const el = document.querySelector('.js-new-branch-dropdown'); - - if (!el) return null; - - return new Vue({ - el, - components: { - newBranchForm, - }, - store, - render(createElement) { - return createElement('new-branch-form'); - }, - }); -} - -const repo = document.getElementById('repo'); -const editButton = document.querySelector('.editable-mode'); -const newDropdownHolder = document.querySelector('.js-new-dropdown'); - -Vue.use(Translate); - -initRepo(repo); -initRepoEditButton(editButton); -initNewBranchForm(); -initNewDropdown(newDropdownHolder); diff --git a/app/assets/javascripts/repo/stores/actions.js b/app/assets/javascripts/repo/stores/actions.js deleted file mode 100644 index 120ce96f44d..00000000000 --- a/app/assets/javascripts/repo/stores/actions.js +++ /dev/null @@ -1,145 +0,0 @@ -import Vue from 'vue'; -import flash from '../../flash'; -import service from '../services'; -import * as types from './mutation_types'; - -export const redirectToUrl = (_, url) => gl.utils.visitUrl(url); - -export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data); - -export const closeDiscardPopup = ({ commit }) => commit(types.TOGGLE_DISCARD_POPUP, false); - -export const discardAllChanges = ({ commit, getters, dispatch }) => { - const changedFiles = getters.changedFiles; - - changedFiles.forEach((file) => { - commit(types.DISCARD_FILE_CHANGES, file); - - if (file.tempFile) { - dispatch('closeFile', { file, force: true }); - } - }); -}; - -export const closeAllFiles = ({ state, dispatch }) => { - state.openFiles.forEach(file => dispatch('closeFile', { file })); -}; - -export const toggleEditMode = ({ state, commit, getters, dispatch }, force = false) => { - const changedFiles = getters.changedFiles; - - if (changedFiles.length && !force) { - commit(types.TOGGLE_DISCARD_POPUP, true); - } else { - commit(types.TOGGLE_EDIT_MODE); - commit(types.TOGGLE_DISCARD_POPUP, false); - dispatch('toggleBlobView'); - - if (!state.editMode) { - dispatch('discardAllChanges'); - } - } -}; - -export const toggleBlobView = ({ commit, state }) => { - if (state.editMode) { - commit(types.SET_EDIT_MODE); - } else { - commit(types.SET_PREVIEW_MODE); - } -}; - -export const checkCommitStatus = ({ state }) => service.getBranchData( - state.project.id, - state.currentBranch, -) - .then((data) => { - const { id } = data.commit; - - if (state.currentRef !== id) { - return true; - } - - return false; - }) - .catch(() => flash('Error checking branch data. Please try again.')); - -export const commitChanges = ({ commit, state, dispatch, getters }, { payload, newMr }) => - service.commit(state.project.id, payload) - .then((data) => { - const { branch } = payload; - if (!data.short_id) { - flash(data.message); - return; - } - - const lastCommit = { - commit_path: `${state.project.url}/commit/${data.id}`, - commit: { - message: data.message, - authored_date: data.committed_date, - }, - }; - - flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); - - if (newMr) { - dispatch('redirectToUrl', `${state.endpoints.newMergeRequestUrl}${branch}`); - } else { - commit(types.SET_COMMIT_REF, data.id); - - getters.changedFiles.forEach((entry) => { - commit(types.SET_LAST_COMMIT_DATA, { - entry, - lastCommit, - }); - }); - - dispatch('discardAllChanges'); - dispatch('closeAllFiles'); - dispatch('toggleEditMode'); - - window.scrollTo(0, 0); - } - }) - .catch(() => flash('Error committing changes. Please try again.')); - -export const createTempEntry = ({ state, dispatch }, { name, type, content = '', base64 = false }) => { - if (type === 'tree') { - dispatch('createTempTree', name); - } else if (type === 'blob') { - dispatch('createTempFile', { - tree: state, - name, - base64, - content, - }); - } -}; - -export const popHistoryState = ({ state, dispatch, getters }) => { - const treeList = getters.treeList; - const tree = treeList.find(file => file.url === state.previousUrl); - - if (!tree) return; - - if (tree.type === 'tree') { - dispatch('toggleTreeOpen', { endpoint: tree.url, tree }); - } -}; - -export const scrollToTab = () => { - Vue.nextTick(() => { - const tabs = document.getElementById('tabs'); - - if (tabs) { - const tabEl = tabs.querySelector('.active .repo-tab'); - - tabEl.focus(); - } - }); -}; - -export * from './actions/tree'; -export * from './actions/file'; -export * from './actions/branch'; diff --git a/app/assets/javascripts/repo/stores/actions/branch.js b/app/assets/javascripts/repo/stores/actions/branch.js deleted file mode 100644 index 61d9a5af3e3..00000000000 --- a/app/assets/javascripts/repo/stores/actions/branch.js +++ /dev/null @@ -1,20 +0,0 @@ -import service from '../../services'; -import * as types from '../mutation_types'; -import { pushState } from '../utils'; - -// eslint-disable-next-line import/prefer-default-export -export const createNewBranch = ({ state, commit }, branch) => service.createBranch( - state.project.id, - { - branch, - ref: state.currentBranch, - }, -).then(res => res.json()) -.then((data) => { - const branchName = data.name; - const url = location.href.replace(state.currentBranch, branchName); - - pushState(url); - - commit(types.SET_CURRENT_BRANCH, branchName); -}); diff --git a/app/assets/javascripts/repo/stores/actions/tree.js b/app/assets/javascripts/repo/stores/actions/tree.js deleted file mode 100644 index aa830e946a2..00000000000 --- a/app/assets/javascripts/repo/stores/actions/tree.js +++ /dev/null @@ -1,162 +0,0 @@ -import { normalizeHeaders } from '../../../lib/utils/common_utils'; -import flash from '../../../flash'; -import service from '../../services'; -import * as types from '../mutation_types'; -import { - pushState, - setPageTitle, - findEntry, - createTemp, - createOrMergeEntry, -} from '../utils'; - -export const getTreeData = ( - { commit, state, dispatch }, - { endpoint = state.endpoints.rootEndpoint, tree = state } = {}, -) => { - commit(types.TOGGLE_LOADING, tree); - - service.getTreeData(endpoint) - .then((res) => { - const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); - - setPageTitle(pageTitle); - - return res.json(); - }) - .then((data) => { - const prevLastCommitPath = tree.lastCommitPath; - if (!state.isInitialRoot) { - commit(types.SET_ROOT, data.path === '/'); - } - - dispatch('updateDirectoryData', { data, tree }); - commit(types.SET_PARENT_TREE_URL, data.parent_tree_url); - commit(types.SET_LAST_COMMIT_URL, { tree, url: data.last_commit_path }); - commit(types.TOGGLE_LOADING, tree); - - if (prevLastCommitPath !== null) { - dispatch('getLastCommitData', tree); - } - - pushState(endpoint); - }) - .catch(() => { - flash('Error loading tree data. Please try again.'); - commit(types.TOGGLE_LOADING, tree); - }); -}; - -export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => { - if (tree.opened) { - // send empty data to clear the tree - const data = { trees: [], blobs: [], submodules: [] }; - - pushState(tree.parentTreeUrl); - - commit(types.SET_PREVIOUS_URL, tree.parentTreeUrl); - dispatch('updateDirectoryData', { data, tree }); - } else { - commit(types.SET_PREVIOUS_URL, endpoint); - dispatch('getTreeData', { endpoint, tree }); - } - - commit(types.TOGGLE_TREE_OPEN, tree); -}; - -export const clickedTreeRow = ({ commit, dispatch }, row) => { - if (row.type === 'tree') { - dispatch('toggleTreeOpen', { - endpoint: row.url, - tree: row, - }); - } else if (row.type === 'submodule') { - commit(types.TOGGLE_LOADING, row); - - gl.utils.visitUrl(row.url); - } else if (row.type === 'blob' && row.opened) { - dispatch('setFileActive', row); - } else { - dispatch('getFileData', row); - } -}; - -export const createTempTree = ({ state, commit, dispatch }, name) => { - let tree = state; - const dirNames = name.replace(new RegExp(`^${state.path}/`), '').split('/'); - - dirNames.forEach((dirName) => { - const foundEntry = findEntry(tree, 'tree', dirName); - - if (!foundEntry) { - const tmpEntry = createTemp({ - name: dirName, - path: tree.path, - type: 'tree', - level: tree.level !== undefined ? tree.level + 1 : 0, - }); - - commit(types.CREATE_TMP_TREE, { - parent: tree, - tmpEntry, - }); - commit(types.TOGGLE_TREE_OPEN, tmpEntry); - - tree = tmpEntry; - } else { - tree = foundEntry; - } - }); - - if (tree.tempFile) { - dispatch('createTempFile', { - tree, - name: '.gitkeep', - }); - } -}; - -export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => { - if (tree.lastCommitPath === null || getters.isCollapsed) return; - - service.getTreeLastCommit(tree.lastCommitPath) - .then((res) => { - const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null; - - commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath }); - - return res.json(); - }) - .then((data) => { - data.forEach((lastCommit) => { - const entry = findEntry(tree, lastCommit.type, lastCommit.file_name); - - if (entry) { - commit(types.SET_LAST_COMMIT_DATA, { entry, lastCommit }); - } - }); - - dispatch('getLastCommitData', tree); - }) - .catch(() => flash('Error fetching log data.')); -}; - -export const updateDirectoryData = ({ commit, state }, { data, tree }) => { - const level = tree.level !== undefined ? tree.level + 1 : 0; - const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl; - const createEntry = (entry, type) => createOrMergeEntry({ - tree, - entry, - level, - type, - parentTreeUrl, - }); - - const formattedData = [ - ...data.trees.map(t => createEntry(t, 'tree')), - ...data.submodules.map(m => createEntry(m, 'submodule')), - ...data.blobs.map(b => createEntry(b, 'blob')), - ]; - - commit(types.SET_DIRECTORY_DATA, { tree, data: formattedData }); -}; diff --git a/app/assets/javascripts/repo/stores/getters.js b/app/assets/javascripts/repo/stores/getters.js deleted file mode 100644 index 5ce9f449905..00000000000 --- a/app/assets/javascripts/repo/stores/getters.js +++ /dev/null @@ -1,40 +0,0 @@ -import _ from 'underscore'; - -/* - Takes the multi-dimensional tree and returns a flattened array. - This allows for the table to recursively render the table rows but keeps the data - structure nested to make it easier to add new files/directories. -*/ -export const treeList = (state) => { - const mapTree = arr => (!arr.tree.length ? [] : _.map(arr.tree, a => [a, mapTree(a)])); - - return _.chain(state.tree) - .map(arr => [arr, mapTree(arr)]) - .flatten() - .value(); -}; - -export const changedFiles = state => state.openFiles.filter(file => file.changed); - -export const activeFile = state => state.openFiles.find(file => file.active); - -export const activeFileExtension = (state) => { - const file = activeFile(state); - return file ? `.${file.path.split('.').pop()}` : ''; -}; - -export const isCollapsed = state => !!state.openFiles.length; - -export const canEditFile = (state) => { - const currentActiveFile = activeFile(state); - const openedFiles = state.openFiles; - - return state.canCommit && - state.onTopOfBranch && - openedFiles.length && - (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary); -}; - -export const addedFiles = state => changedFiles(state).filter(f => f.tempFile); - -export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile); diff --git a/app/assets/javascripts/repo/stores/mutations/branch.js b/app/assets/javascripts/repo/stores/mutations/branch.js deleted file mode 100644 index d8229e8a620..00000000000 --- a/app/assets/javascripts/repo/stores/mutations/branch.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as types from '../mutation_types'; - -export default { - [types.SET_CURRENT_BRANCH](state, currentBranch) { - Object.assign(state, { - currentBranch, - }); - }, -}; diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index fa7f6825d7e..b830fcf7e80 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -3,226 +3,228 @@ import _ from 'underscore'; import Cookies from 'js-cookie'; -(function() { - this.Sidebar = (function() { - function Sidebar(currentUser) { - this.toggleTodo = this.toggleTodo.bind(this); - this.sidebar = $('aside'); - - this.removeListeners(); - this.addEventListeners(); +function Sidebar(currentUser) { + this.toggleTodo = this.toggleTodo.bind(this); + this.sidebar = $('aside'); + + this.removeListeners(); + this.addEventListeners(); +} + +Sidebar.initialize = function(currentUser) { + if (!this.instance) { + this.instance = new Sidebar(currentUser); + } +}; + +Sidebar.prototype.removeListeners = function () { + this.sidebar.off('click', '.sidebar-collapsed-icon'); + this.sidebar.off('hidden.gl.dropdown'); + $('.dropdown').off('loading.gl.dropdown'); + $('.dropdown').off('loaded.gl.dropdown'); + $(document).off('click', '.js-sidebar-toggle'); +}; + +Sidebar.prototype.addEventListeners = function() { + const $document = $(document); + + this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked); + this.sidebar.on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden); + $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); + $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); + + $document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked); + return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); +}; + +Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { + var $allGutterToggleIcons, $this, $thisIcon; + e.preventDefault(); + $this = $(this); + $thisIcon = $this.find('i'); + $allGutterToggleIcons = $('.js-sidebar-toggle i'); + if ($thisIcon.hasClass('fa-angle-double-right')) { + $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); + $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + $('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + } else { + $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); + $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + $('.layout-page').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + + if (gl.lazyLoader) gl.lazyLoader.loadCheck(); + } + if (!triggered) { + Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); + } +}; + +Sidebar.prototype.toggleTodo = function(e) { + var $btnText, $this, $todoLoading, ajaxType, url; + $this = $(e.currentTarget); + ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST'; + if ($this.attr('data-delete-path')) { + url = "" + ($this.attr('data-delete-path')); + } else { + url = "" + ($this.data('url')); + } + + $this.tooltip('hide'); + + return $.ajax({ + url: url, + type: ajaxType, + dataType: 'json', + data: { + issuable_id: $this.data('issuable-id'), + issuable_type: $this.data('issuable-type') + }, + beforeSend: (function(_this) { + return function() { + $('.js-issuable-todo').disable() + .addClass('is-loading'); + }; + })(this) + }).done((function(_this) { + return function(data) { + return _this.todoUpdateDone(data); + }; + })(this)); +}; + +Sidebar.prototype.todoUpdateDone = function(data) { + const deletePath = data.delete_path ? data.delete_path : null; + const attrPrefix = deletePath ? 'mark' : 'todo'; + const $todoBtns = $('.js-issuable-todo'); + + $(document).trigger('todo:toggle', data.count); + + $todoBtns.each((i, el) => { + const $el = $(el); + const $elText = $el.find('.js-issuable-todo-inner'); + + $el.removeClass('is-loading') + .enable() + .attr('aria-label', $el.data(`${attrPrefix}-text`)) + .attr('data-delete-path', deletePath) + .attr('title', $el.data(`${attrPrefix}-text`)); + + if ($el.hasClass('has-tooltip')) { + $el.tooltip('fixTitle'); } - Sidebar.prototype.removeListeners = function () { - this.sidebar.off('click', '.sidebar-collapsed-icon'); - this.sidebar.off('hidden.gl.dropdown'); - $('.dropdown').off('loading.gl.dropdown'); - $('.dropdown').off('loaded.gl.dropdown'); - $(document).off('click', '.js-sidebar-toggle'); - }; - - Sidebar.prototype.addEventListeners = function() { - const $document = $(document); - - this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked); - this.sidebar.on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden); - $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); - $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); - - $document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked); - return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); - }; - - Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { - var $allGutterToggleIcons, $this, $thisIcon; - e.preventDefault(); - $this = $(this); - $thisIcon = $this.find('i'); - $allGutterToggleIcons = $('.js-sidebar-toggle i'); - if ($thisIcon.hasClass('fa-angle-double-right')) { - $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); - $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - } else { - $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); - $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - - if (gl.lazyLoader) gl.lazyLoader.loadCheck(); - } - if (!triggered) { - Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); - } - }; - - Sidebar.prototype.toggleTodo = function(e) { - var $btnText, $this, $todoLoading, ajaxType, url; - $this = $(e.currentTarget); - ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST'; - if ($this.attr('data-delete-path')) { - url = "" + ($this.attr('data-delete-path')); - } else { - url = "" + ($this.data('url')); - } - - $this.tooltip('hide'); - - return $.ajax({ - url: url, - type: ajaxType, - dataType: 'json', - data: { - issuable_id: $this.data('issuable-id'), - issuable_type: $this.data('issuable-type') - }, - beforeSend: (function(_this) { - return function() { - $('.js-issuable-todo').disable() - .addClass('is-loading'); - }; - })(this) - }).done((function(_this) { - return function(data) { - return _this.todoUpdateDone(data); - }; - })(this)); - }; - - Sidebar.prototype.todoUpdateDone = function(data) { - const deletePath = data.delete_path ? data.delete_path : null; - const attrPrefix = deletePath ? 'mark' : 'todo'; - const $todoBtns = $('.js-issuable-todo'); - - $(document).trigger('todo:toggle', data.count); - - $todoBtns.each((i, el) => { - const $el = $(el); - const $elText = $el.find('.js-issuable-todo-inner'); - - $el.removeClass('is-loading') - .enable() - .attr('aria-label', $el.data(`${attrPrefix}-text`)) - .attr('data-delete-path', deletePath) - .attr('title', $el.data(`${attrPrefix}-text`)); - - if ($el.hasClass('has-tooltip')) { - $el.tooltip('fixTitle'); - } - - if ($el.data(`${attrPrefix}-icon`)) { - $elText.html($el.data(`${attrPrefix}-icon`)); - } else { - $elText.text($el.data(`${attrPrefix}-text`)); - } - }); - }; - - Sidebar.prototype.sidebarDropdownLoading = function(e) { - var $loading, $sidebarCollapsedIcon, i, img; - $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); - img = $sidebarCollapsedIcon.find('img'); - i = $sidebarCollapsedIcon.find('i'); - $loading = $('<i class="fa fa-spinner fa-spin"></i>'); - if (img.length) { - img.before($loading); - return img.hide(); - } else if (i.length) { - i.before($loading); - return i.hide(); - } - }; - - Sidebar.prototype.sidebarDropdownLoaded = function(e) { - var $sidebarCollapsedIcon, i, img; - $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); - img = $sidebarCollapsedIcon.find('img'); - $sidebarCollapsedIcon.find('i.fa-spin').remove(); - i = $sidebarCollapsedIcon.find('i'); - if (img.length) { - return img.show(); - } else { - return i.show(); - } - }; - - Sidebar.prototype.sidebarCollapseClicked = function(e) { - var $block, sidebar; - if ($(e.currentTarget).hasClass('dont-change-state')) { - return; - } - sidebar = e.data; - e.preventDefault(); - $block = $(this).closest('.block'); - return sidebar.openDropdown($block); - }; - - Sidebar.prototype.openDropdown = function(blockOrName) { - var $block; - $block = _.isString(blockOrName) ? this.getBlock(blockOrName) : blockOrName; - if (!this.isOpen()) { - this.setCollapseAfterUpdate($block); - this.toggleSidebar('open'); - } - - // Wait for the sidebar to trigger('click') open - // so it doesn't cause our dropdown to close preemptively - setTimeout(() => { - $block.find('.js-sidebar-dropdown-toggle').trigger('click'); - }); - }; - - Sidebar.prototype.setCollapseAfterUpdate = function($block) { - $block.addClass('collapse-after-update'); - return $('.page-with-sidebar').addClass('with-overlay'); - }; - - Sidebar.prototype.onSidebarDropdownHidden = function(e) { - var $block, sidebar; - sidebar = e.data; - e.preventDefault(); - $block = $(e.target).closest('.block'); - return sidebar.sidebarDropdownHidden($block); - }; - - Sidebar.prototype.sidebarDropdownHidden = function($block) { - if ($block.hasClass('collapse-after-update')) { - $block.removeClass('collapse-after-update'); - $('.page-with-sidebar').removeClass('with-overlay'); - return this.toggleSidebar('hide'); - } - }; - - Sidebar.prototype.triggerOpenSidebar = function() { - return this.sidebar.find('.js-sidebar-toggle').trigger('click'); - }; - - Sidebar.prototype.toggleSidebar = function(action) { - if (action == null) { - action = 'toggle'; - } - if (action === 'toggle') { - this.triggerOpenSidebar(); - } - if (action === 'open') { - if (!this.isOpen()) { - this.triggerOpenSidebar(); - } - } - if (action === 'hide') { - if (this.isOpen()) { - return this.triggerOpenSidebar(); - } - } - }; + if ($el.data(`${attrPrefix}-icon`)) { + $elText.html($el.data(`${attrPrefix}-icon`)); + } else { + $elText.text($el.data(`${attrPrefix}-text`)); + } + }); +}; + +Sidebar.prototype.sidebarDropdownLoading = function(e) { + var $loading, $sidebarCollapsedIcon, i, img; + $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); + img = $sidebarCollapsedIcon.find('img'); + i = $sidebarCollapsedIcon.find('i'); + $loading = $('<i class="fa fa-spinner fa-spin"></i>'); + if (img.length) { + img.before($loading); + return img.hide(); + } else if (i.length) { + i.before($loading); + return i.hide(); + } +}; + +Sidebar.prototype.sidebarDropdownLoaded = function(e) { + var $sidebarCollapsedIcon, i, img; + $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); + img = $sidebarCollapsedIcon.find('img'); + $sidebarCollapsedIcon.find('i.fa-spin').remove(); + i = $sidebarCollapsedIcon.find('i'); + if (img.length) { + return img.show(); + } else { + return i.show(); + } +}; + +Sidebar.prototype.sidebarCollapseClicked = function(e) { + var $block, sidebar; + if ($(e.currentTarget).hasClass('dont-change-state')) { + return; + } + sidebar = e.data; + e.preventDefault(); + $block = $(this).closest('.block'); + return sidebar.openDropdown($block); +}; + +Sidebar.prototype.openDropdown = function(blockOrName) { + var $block; + $block = _.isString(blockOrName) ? this.getBlock(blockOrName) : blockOrName; + if (!this.isOpen()) { + this.setCollapseAfterUpdate($block); + this.toggleSidebar('open'); + } + + // Wait for the sidebar to trigger('click') open + // so it doesn't cause our dropdown to close preemptively + setTimeout(() => { + $block.find('.js-sidebar-dropdown-toggle').trigger('click'); + }); +}; + +Sidebar.prototype.setCollapseAfterUpdate = function($block) { + $block.addClass('collapse-after-update'); + return $('.layout-page').addClass('with-overlay'); +}; + +Sidebar.prototype.onSidebarDropdownHidden = function(e) { + var $block, sidebar; + sidebar = e.data; + e.preventDefault(); + $block = $(e.target).closest('.block'); + return sidebar.sidebarDropdownHidden($block); +}; + +Sidebar.prototype.sidebarDropdownHidden = function($block) { + if ($block.hasClass('collapse-after-update')) { + $block.removeClass('collapse-after-update'); + $('.layout-page').removeClass('with-overlay'); + return this.toggleSidebar('hide'); + } +}; + +Sidebar.prototype.triggerOpenSidebar = function() { + return this.sidebar.find('.js-sidebar-toggle').trigger('click'); +}; + +Sidebar.prototype.toggleSidebar = function(action) { + if (action == null) { + action = 'toggle'; + } + if (action === 'toggle') { + this.triggerOpenSidebar(); + } + if (action === 'open') { + if (!this.isOpen()) { + this.triggerOpenSidebar(); + } + } + if (action === 'hide') { + if (this.isOpen()) { + return this.triggerOpenSidebar(); + } + } +}; - Sidebar.prototype.isOpen = function() { - return this.sidebar.is('.right-sidebar-expanded'); - }; +Sidebar.prototype.isOpen = function() { + return this.sidebar.is('.right-sidebar-expanded'); +}; - Sidebar.prototype.getBlock = function(name) { - return this.sidebar.find(".block." + name); - }; +Sidebar.prototype.getBlock = function(name) { + return this.sidebar.find(".block." + name); +}; - return Sidebar; - })(); -}).call(window); +export default Sidebar; diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index 07fee53d814..363322af47a 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -1,118 +1,113 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, max-len */ import Flash from './flash'; import Api from './api'; -(function() { - this.Search = (function() { - function Search() { - var $groupDropdown, $projectDropdown; - $groupDropdown = $('.js-search-group-dropdown'); - $projectDropdown = $('.js-search-project-dropdown'); - this.groupId = $groupDropdown.data('group-id'); - this.eventListeners(); - $groupDropdown.glDropdown({ - selectable: true, - filterable: true, - fieldName: 'group_id', - search: { - fields: ['full_name'] - }, - data: function(term, callback) { - return Api.groups(term, {}, function(data) { +export default class Search { + constructor() { + const $groupDropdown = $('.js-search-group-dropdown'); + const $projectDropdown = $('.js-search-project-dropdown'); + + this.searchInput = '.js-search-input'; + this.searchClear = '.js-search-clear'; + + this.groupId = $groupDropdown.data('group-id'); + this.eventListeners(); + + $groupDropdown.glDropdown({ + selectable: true, + filterable: true, + fieldName: 'group_id', + search: { + fields: ['full_name'], + }, + data(term, callback) { + return Api.groups(term, {}, (data) => { + data.unshift({ + full_name: 'Any', + }); + data.splice(1, 0, 'divider'); + return callback(data); + }); + }, + id(obj) { + return obj.id; + }, + text(obj) { + return obj.full_name; + }, + toggleLabel(obj) { + return `${($groupDropdown.data('default-label'))} ${obj.full_name}`; + }, + clicked: () => Search.submitSearch(), + }); + + $projectDropdown.glDropdown({ + selectable: true, + filterable: true, + fieldName: 'project_id', + search: { + fields: ['name'], + }, + data: (term, callback) => { + this.getProjectsData(term) + .then((data) => { data.unshift({ - full_name: 'Any' + name_with_namespace: 'Any', }); data.splice(1, 0, 'divider'); - return callback(data); - }); - }, - id: function(obj) { - return obj.id; - }, - text: function(obj) { - return obj.full_name; - }, - toggleLabel: function(obj) { - return ($groupDropdown.data('default-label')) + " " + obj.full_name; - }, - clicked: (function(_this) { - return function() { - return _this.submitSearch(); - }; - })(this) - }); - $projectDropdown.glDropdown({ - selectable: true, - filterable: true, - fieldName: 'project_id', - search: { - fields: ['name'] - }, - data: (term, callback) => { - this.getProjectsData(term) - .then((data) => { - data.unshift({ - name_with_namespace: 'Any' - }); - data.splice(1, 0, 'divider'); - return data; - }) - .then(data => callback(data)) - .catch(() => new Flash('Error fetching projects')); - }, - id: function(obj) { - return obj.id; - }, - text: function(obj) { - return obj.name_with_namespace; - }, - toggleLabel: function(obj) { - return ($projectDropdown.data('default-label')) + " " + obj.name_with_namespace; - }, - clicked: (function(_this) { - return function() { - return _this.submitSearch(); - }; - })(this) - }); - } + return data; + }) + .then(data => callback(data)) + .catch(() => new Flash('Error fetching projects')); + }, + id(obj) { + return obj.id; + }, + text(obj) { + return obj.name_with_namespace; + }, + toggleLabel(obj) { + return `${($projectDropdown.data('default-label'))} ${obj.name_with_namespace}`; + }, + clicked: () => Search.submitSearch(), + }); + } - Search.prototype.eventListeners = function() { - $(document).off('keyup', '.js-search-input').on('keyup', '.js-search-input', this.searchKeyUp); - return $(document).off('click', '.js-search-clear').on('click', '.js-search-clear', this.clearSearchField); - }; + eventListeners() { + $(document) + .off('keyup', this.searchInput) + .on('keyup', this.searchInput, this.searchKeyUp); + $(document) + .off('click', this.searchClear) + .on('click', this.searchClear, this.clearSearchField.bind(this)); + } - Search.prototype.submitSearch = function() { - return $('.js-search-form').submit(); - }; + static submitSearch() { + return $('.js-search-form').submit(); + } - Search.prototype.searchKeyUp = function() { - var $input; - $input = $(this); - if ($input.val() === '') { - return $('.js-search-clear').addClass('hidden'); - } else { - return $('.js-search-clear').removeClass('hidden'); - } - }; - - Search.prototype.clearSearchField = function() { - return $('.js-search-input').val('').trigger('keyup').focus(); - }; + searchKeyUp() { + const $input = $(this); + if ($input.val() === '') { + $('.js-search-clear').addClass('hidden'); + } else { + $('.js-search-clear').removeClass('hidden'); + } + } - Search.prototype.getProjectsData = function(term) { - return new Promise((resolve) => { - if (this.groupId) { - Api.groupProjects(this.groupId, term, resolve); - } else { - Api.projects(term, { - order_by: 'id', - }, resolve); - } - }); - }; + clearSearchField() { + return $(this.searchInput).val('').trigger('keyup').focus(); + } - return Search; - })(); -}).call(window); + getProjectsData(term) { + return new Promise((resolve) => { + if (this.groupId) { + Api.groupProjects(this.groupId, term, resolve); + } else { + Api.projects(term, { + order_by: 'id', + }, resolve); + } + }); + } +} diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index e40a3596200..98b524f7e3f 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -8,448 +8,445 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '. * When the user clicks `x` button it cleans the input and closes the dropdown. */ -((global) => { - const KEYCODE = { - ESCAPE: 27, - BACKSPACE: 8, - ENTER: 13, - UP: 38, - DOWN: 40, - }; - - class SearchAutocomplete { - constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) { - this.bindEventContext(); - this.wrap = wrap || $('.search'); - this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); - this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); - this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); - this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); - this.dropdown = this.wrap.find('.dropdown'); - this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle'); - this.dropdownContent = this.dropdown.find('.dropdown-content'); - this.locationBadgeEl = this.getElement('.location-badge'); - this.scopeInputEl = this.getElement('#scope'); - this.searchInput = this.getElement('.search-input'); - this.projectInputEl = this.getElement('#search_project_id'); - this.groupInputEl = this.getElement('#group_id'); - this.searchCodeInputEl = this.getElement('#search_code'); - this.repositoryInputEl = this.getElement('#repository_ref'); - this.clearInput = this.getElement('.js-clear-input'); - this.saveOriginalState(); - - // Only when user is logged in - if (gon.current_user_id) { - this.createAutocomplete(); - } +const KEYCODE = { + ESCAPE: 27, + BACKSPACE: 8, + ENTER: 13, + UP: 38, + DOWN: 40, +}; + +function setSearchOptions() { + var $projectOptionsDataEl = $('.js-search-project-options'); + var $groupOptionsDataEl = $('.js-search-group-options'); + var $dashboardOptionsDataEl = $('.js-search-dashboard-options'); + + if ($projectOptionsDataEl.length) { + gl.projectOptions = gl.projectOptions || {}; + + var projectPath = $projectOptionsDataEl.data('project-path'); + + gl.projectOptions[projectPath] = { + name: $projectOptionsDataEl.data('name'), + issuesPath: $projectOptionsDataEl.data('issues-path'), + issuesDisabled: $projectOptionsDataEl.data('issues-disabled'), + mrPath: $projectOptionsDataEl.data('mr-path'), + }; + } - this.searchInput.addClass('disabled'); - this.saveTextLength(); - this.bindEvents(); - this.dropdownToggle.dropdown(); - } + if ($groupOptionsDataEl.length) { + gl.groupOptions = gl.groupOptions || {}; - // Finds an element inside wrapper element - bindEventContext() { - this.onSearchInputBlur = this.onSearchInputBlur.bind(this); - this.onClearInputClick = this.onClearInputClick.bind(this); - this.onSearchInputFocus = this.onSearchInputFocus.bind(this); - this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this); - this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this); - } - getElement(selector) { - return this.wrap.find(selector); - } + var groupPath = $groupOptionsDataEl.data('group-path'); - saveOriginalState() { - return this.originalState = this.serializeState(); - } + gl.groupOptions[groupPath] = { + name: $groupOptionsDataEl.data('name'), + issuesPath: $groupOptionsDataEl.data('issues-path'), + mrPath: $groupOptionsDataEl.data('mr-path'), + }; + } - saveTextLength() { - return this.lastTextLength = this.searchInput.val().length; + if ($dashboardOptionsDataEl.length) { + gl.dashboardOptions = { + issuesPath: $dashboardOptionsDataEl.data('issues-path'), + mrPath: $dashboardOptionsDataEl.data('mr-path'), + }; + } +} + +export default class SearchAutocomplete { + constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) { + setSearchOptions(); + this.bindEventContext(); + this.wrap = wrap || $('.search'); + this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); + this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); + this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); + this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); + this.dropdown = this.wrap.find('.dropdown'); + this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle'); + this.dropdownContent = this.dropdown.find('.dropdown-content'); + this.locationBadgeEl = this.getElement('.location-badge'); + this.scopeInputEl = this.getElement('#scope'); + this.searchInput = this.getElement('.search-input'); + this.projectInputEl = this.getElement('#search_project_id'); + this.groupInputEl = this.getElement('#group_id'); + this.searchCodeInputEl = this.getElement('#search_code'); + this.repositoryInputEl = this.getElement('#repository_ref'); + this.clearInput = this.getElement('.js-clear-input'); + this.saveOriginalState(); + + // Only when user is logged in + if (gon.current_user_id) { + this.createAutocomplete(); } - createAutocomplete() { - return this.searchInput.glDropdown({ - filterInputBlur: false, - filterable: true, - filterRemote: true, - highlight: true, - enterCallback: false, - filterInput: 'input#search', - search: { - fields: ['text'], - }, - id: this.getSearchText, - data: this.getData.bind(this), - selectable: true, - clicked: this.onClick.bind(this), - }); + this.searchInput.addClass('disabled'); + this.saveTextLength(); + this.bindEvents(); + this.dropdownToggle.dropdown(); + } + + // Finds an element inside wrapper element + bindEventContext() { + this.onSearchInputBlur = this.onSearchInputBlur.bind(this); + this.onClearInputClick = this.onClearInputClick.bind(this); + this.onSearchInputFocus = this.onSearchInputFocus.bind(this); + this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this); + this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this); + } + getElement(selector) { + return this.wrap.find(selector); + } + + saveOriginalState() { + return this.originalState = this.serializeState(); + } + + saveTextLength() { + return this.lastTextLength = this.searchInput.val().length; + } + + createAutocomplete() { + return this.searchInput.glDropdown({ + filterInputBlur: false, + filterable: true, + filterRemote: true, + highlight: true, + enterCallback: false, + filterInput: 'input#search', + search: { + fields: ['text'], + }, + id: this.getSearchText, + data: this.getData.bind(this), + selectable: true, + clicked: this.onClick.bind(this), + }); + } + + getSearchText(selectedObject, el) { + return selectedObject.id ? selectedObject.text : ''; + } + + getData(term, callback) { + if (!term) { + const contents = this.getCategoryContents(); + if (contents) { + this.searchInput.data('glDropdown').filter.options.callback(contents); + this.enableAutocomplete(); + } + return; } - getSearchText(selectedObject, el) { - return selectedObject.id ? selectedObject.text : ''; + // Prevent multiple ajax calls + if (this.loadingSuggestions) { + return; } - getData(term, callback) { - if (!term) { - const contents = this.getCategoryContents(); - if (contents) { - this.searchInput.data('glDropdown').filter.options.callback(contents); - this.enableAutocomplete(); - } - return; - } + this.loadingSuggestions = true; - // Prevent multiple ajax calls - if (this.loadingSuggestions) { + return $.get(this.autocompletePath, { + project_id: this.projectId, + project_ref: this.projectRef, + term: term, + }, (response) => { + var firstCategory, i, lastCategory, len, suggestion; + // Hide dropdown menu if no suggestions returns + if (!response.length) { + this.disableAutocomplete(); return; } - this.loadingSuggestions = true; - - return $.get(this.autocompletePath, { - project_id: this.projectId, - project_ref: this.projectRef, - term: term, - }, (response) => { - var firstCategory, i, lastCategory, len, suggestion; - // Hide dropdown menu if no suggestions returns - if (!response.length) { - this.disableAutocomplete(); - return; - } - - const data = []; - // List results - firstCategory = true; - for (i = 0, len = response.length; i < len; i += 1) { - suggestion = response[i]; - // Add group header before list each group - if (lastCategory !== suggestion.category) { - if (!firstCategory) { - data.push('separator'); - } - if (firstCategory) { - firstCategory = false; - } - data.push({ - header: suggestion.category, - }); - lastCategory = suggestion.category; + const data = []; + // List results + firstCategory = true; + for (i = 0, len = response.length; i < len; i += 1) { + suggestion = response[i]; + // Add group header before list each group + if (lastCategory !== suggestion.category) { + if (!firstCategory) { + data.push('separator'); + } + if (firstCategory) { + firstCategory = false; } data.push({ - id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, - category: suggestion.category, - text: suggestion.label, - url: suggestion.url, - }); - } - // Add option to proceed with the search - if (data.length) { - data.push('separator'); - data.push({ - text: "Result name contains \"" + term + "\"", - url: "/search?search=" + term + "&project_id=" + (this.projectInputEl.val()) + "&group_id=" + (this.groupInputEl.val()), + header: suggestion.category, }); + lastCategory = suggestion.category; } - return callback(data); - }) - .always(() => { this.loadingSuggestions = false; }); - } - - getCategoryContents() { - const userId = gon.current_user_id; - const userName = gon.current_username; - const { projectOptions, groupOptions, dashboardOptions } = gl; - - // Get options - let options; - if (isInGroupsPage() && groupOptions) { - options = groupOptions[getGroupSlug()]; - } else if (isInProjectPage() && projectOptions) { - options = projectOptions[getProjectSlug()]; - } else if (dashboardOptions) { - options = dashboardOptions; + data.push({ + id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, + category: suggestion.category, + text: suggestion.label, + url: suggestion.url, + }); } - - const { issuesPath, mrPath, name, issuesDisabled } = options; - const baseItems = []; - - if (name) { - baseItems.push({ - header: `${name}`, + // Add option to proceed with the search + if (data.length) { + data.push('separator'); + data.push({ + text: "Result name contains \"" + term + "\"", + url: "/search?search=" + term + "&project_id=" + (this.projectInputEl.val()) + "&group_id=" + (this.groupInputEl.val()), }); } + return callback(data); + }) + .always(() => { this.loadingSuggestions = false; }); + } - const issueItems = [ - { - text: 'Issues assigned to me', - url: `${issuesPath}/?assignee_username=${userName}`, - }, - { - text: "Issues I've created", - url: `${issuesPath}/?author_username=${userName}`, - }, - ]; - const mergeRequestItems = [ - { - text: 'Merge requests assigned to me', - url: `${mrPath}/?assignee_username=${userName}`, - }, - { - text: "Merge requests I've created", - url: `${mrPath}/?author_username=${userName}`, - }, - ]; - - let items; - if (issuesDisabled) { - items = baseItems.concat(mergeRequestItems); - } else { - items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems); - } - return items; + getCategoryContents() { + const userId = gon.current_user_id; + const userName = gon.current_username; + const { projectOptions, groupOptions, dashboardOptions } = gl; + + // Get options + let options; + if (isInGroupsPage() && groupOptions) { + options = groupOptions[getGroupSlug()]; + } else if (isInProjectPage() && projectOptions) { + options = projectOptions[getProjectSlug()]; + } else if (dashboardOptions) { + options = dashboardOptions; } - serializeState() { - return { - // Search Criteria - search_project_id: this.projectInputEl.val(), - group_id: this.groupInputEl.val(), - search_code: this.searchCodeInputEl.val(), - repository_ref: this.repositoryInputEl.val(), - scope: this.scopeInputEl.val(), - // Location badge - _location: this.locationBadgeEl.text(), - }; + const { issuesPath, mrPath, name, issuesDisabled } = options; + const baseItems = []; + + if (name) { + baseItems.push({ + header: `${name}`, + }); } - bindEvents() { - this.searchInput.on('keydown', this.onSearchInputKeyDown); - this.searchInput.on('keyup', this.onSearchInputKeyUp); - this.searchInput.on('focus', this.onSearchInputFocus); - this.searchInput.on('blur', this.onSearchInputBlur); - this.clearInput.on('click', this.onClearInputClick); - this.locationBadgeEl.on('click', () => this.searchInput.focus()); + const issueItems = [ + { + text: 'Issues assigned to me', + url: `${issuesPath}/?assignee_username=${userName}`, + }, + { + text: "Issues I've created", + url: `${issuesPath}/?author_username=${userName}`, + }, + ]; + const mergeRequestItems = [ + { + text: 'Merge requests assigned to me', + url: `${mrPath}/?assignee_username=${userName}`, + }, + { + text: "Merge requests I've created", + url: `${mrPath}/?author_username=${userName}`, + }, + ]; + + let items; + if (issuesDisabled) { + items = baseItems.concat(mergeRequestItems); + } else { + items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems); } + return items; + } - enableAutocomplete() { - // No need to enable anything if user is not logged in - if (!gon.current_user_id) { - return; - } + serializeState() { + return { + // Search Criteria + search_project_id: this.projectInputEl.val(), + group_id: this.groupInputEl.val(), + search_code: this.searchCodeInputEl.val(), + repository_ref: this.repositoryInputEl.val(), + scope: this.scopeInputEl.val(), + // Location badge + _location: this.locationBadgeEl.text(), + }; + } - // If the dropdown is closed, we'll open it - if (!this.dropdown.hasClass('open')) { - this.loadingSuggestions = false; - this.dropdownToggle.dropdown('toggle'); - return this.searchInput.removeClass('disabled'); - } + bindEvents() { + this.searchInput.on('keydown', this.onSearchInputKeyDown); + this.searchInput.on('keyup', this.onSearchInputKeyUp); + this.searchInput.on('focus', this.onSearchInputFocus); + this.searchInput.on('blur', this.onSearchInputBlur); + this.clearInput.on('click', this.onClearInputClick); + this.locationBadgeEl.on('click', () => this.searchInput.focus()); + } + + enableAutocomplete() { + // No need to enable anything if user is not logged in + if (!gon.current_user_id) { + return; } - // Saves last length of the entered text - onSearchInputKeyDown() { - return this.saveTextLength(); + // If the dropdown is closed, we'll open it + if (!this.dropdown.hasClass('open')) { + this.loadingSuggestions = false; + this.dropdownToggle.dropdown('toggle'); + return this.searchInput.removeClass('disabled'); } + } - onSearchInputKeyUp(e) { - switch (e.keyCode) { - case KEYCODE.BACKSPACE: - // when trying to remove the location badge - if (this.lastTextLength === 0 && this.badgePresent()) { - this.removeLocationBadge(); - } - // When removing the last character and no badge is present - if (this.lastTextLength === 1) { - this.disableAutocomplete(); - } - // When removing any character from existin value - if (this.lastTextLength > 1) { - this.enableAutocomplete(); - } - break; - case KEYCODE.ESCAPE: - this.restoreOriginalState(); - break; - case KEYCODE.ENTER: + // Saves last length of the entered text + onSearchInputKeyDown() { + return this.saveTextLength(); + } + + onSearchInputKeyUp(e) { + switch (e.keyCode) { + case KEYCODE.BACKSPACE: + // when trying to remove the location badge + if (this.lastTextLength === 0 && this.badgePresent()) { + this.removeLocationBadge(); + } + // When removing the last character and no badge is present + if (this.lastTextLength === 1) { + this.disableAutocomplete(); + } + // When removing any character from existin value + if (this.lastTextLength > 1) { + this.enableAutocomplete(); + } + break; + case KEYCODE.ESCAPE: + this.restoreOriginalState(); + break; + case KEYCODE.ENTER: + this.disableAutocomplete(); + break; + case KEYCODE.UP: + case KEYCODE.DOWN: + return; + default: + // Handle the case when deleting the input value other than backspace + // e.g. Pressing ctrl + backspace or ctrl + x + if (this.searchInput.val() === '') { this.disableAutocomplete(); - break; - case KEYCODE.UP: - case KEYCODE.DOWN: - return; - default: - // Handle the case when deleting the input value other than backspace - // e.g. Pressing ctrl + backspace or ctrl + x - if (this.searchInput.val() === '') { - this.disableAutocomplete(); - } else { - // We should display the menu only when input is not empty - if (e.keyCode !== KEYCODE.ENTER) { - this.enableAutocomplete(); - } + } else { + // We should display the menu only when input is not empty + if (e.keyCode !== KEYCODE.ENTER) { + this.enableAutocomplete(); } - } - this.wrap.toggleClass('has-value', !!e.target.value); + } } + this.wrap.toggleClass('has-value', !!e.target.value); + } - onSearchInputFocus() { - this.isFocused = true; - this.wrap.addClass('search-active'); - if (this.getValue() === '') { - return this.getData(); - } + onSearchInputFocus() { + this.isFocused = true; + this.wrap.addClass('search-active'); + if (this.getValue() === '') { + return this.getData(); } + } - getValue() { - return this.searchInput.val(); - } + getValue() { + return this.searchInput.val(); + } - onClearInputClick(e) { - e.preventDefault(); - this.wrap.toggleClass('has-value', !!e.target.value); - return this.searchInput.val('').focus(); - } + onClearInputClick(e) { + e.preventDefault(); + this.wrap.toggleClass('has-value', !!e.target.value); + return this.searchInput.val('').focus(); + } - onSearchInputBlur(e) { - this.isFocused = false; - this.wrap.removeClass('search-active'); - // If input is blank then restore state - if (this.searchInput.val() === '') { - return this.restoreOriginalState(); - } + onSearchInputBlur(e) { + this.isFocused = false; + this.wrap.removeClass('search-active'); + // If input is blank then restore state + if (this.searchInput.val() === '') { + return this.restoreOriginalState(); } + } - addLocationBadge(item) { - var badgeText, category, value; - category = item.category != null ? item.category + ": " : ''; - value = item.value != null ? item.value : ''; - badgeText = "" + category + value; - this.locationBadgeEl.text(badgeText).show(); - return this.wrap.addClass('has-location-badge'); - } + addLocationBadge(item) { + var badgeText, category, value; + category = item.category != null ? item.category + ": " : ''; + value = item.value != null ? item.value : ''; + badgeText = "" + category + value; + this.locationBadgeEl.text(badgeText).show(); + return this.wrap.addClass('has-location-badge'); + } - hasLocationBadge() { - return this.wrap.is('.has-location-badge'); - } + hasLocationBadge() { + return this.wrap.is('.has-location-badge'); + } - restoreOriginalState() { - var i, input, inputs, len; - inputs = Object.keys(this.originalState); - for (i = 0, len = inputs.length; i < len; i += 1) { - input = inputs[i]; - this.getElement("#" + input).val(this.originalState[input]); - } - if (this.originalState._location === '') { - return this.locationBadgeEl.hide(); - } else { - return this.addLocationBadge({ - value: this.originalState._location, - }); - } + restoreOriginalState() { + var i, input, inputs, len; + inputs = Object.keys(this.originalState); + for (i = 0, len = inputs.length; i < len; i += 1) { + input = inputs[i]; + this.getElement("#" + input).val(this.originalState[input]); } - - badgePresent() { - return this.locationBadgeEl.length; + if (this.originalState._location === '') { + return this.locationBadgeEl.hide(); + } else { + return this.addLocationBadge({ + value: this.originalState._location, + }); } + } - resetSearchState() { - var i, input, inputs, len, results; - inputs = Object.keys(this.originalState); - results = []; - for (i = 0, len = inputs.length; i < len; i += 1) { - input = inputs[i]; - // _location isnt a input - if (input === '_location') { - break; - } - results.push(this.getElement("#" + input).val('')); + badgePresent() { + return this.locationBadgeEl.length; + } + + resetSearchState() { + var i, input, inputs, len, results; + inputs = Object.keys(this.originalState); + results = []; + for (i = 0, len = inputs.length; i < len; i += 1) { + input = inputs[i]; + // _location isnt a input + if (input === '_location') { + break; } - return results; + results.push(this.getElement("#" + input).val('')); } + return results; + } - removeLocationBadge() { - this.locationBadgeEl.hide(); - this.resetSearchState(); - this.wrap.removeClass('has-location-badge'); - return this.disableAutocomplete(); - } + removeLocationBadge() { + this.locationBadgeEl.hide(); + this.resetSearchState(); + this.wrap.removeClass('has-location-badge'); + return this.disableAutocomplete(); + } - disableAutocomplete() { - if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) { - this.searchInput.addClass('disabled'); - this.dropdown.removeClass('open').trigger('hidden.bs.dropdown'); - this.restoreMenu(); - } + disableAutocomplete() { + if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) { + this.searchInput.addClass('disabled'); + this.dropdown.removeClass('open').trigger('hidden.bs.dropdown'); + this.restoreMenu(); } + } - restoreMenu() { - var html; - html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>'; - return this.dropdownContent.html(html); - } + restoreMenu() { + var html; + html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>'; + return this.dropdownContent.html(html); + } - onClick(item, $el, e) { - if (location.pathname.indexOf(item.url) !== -1) { - if (!e.metaKey) e.preventDefault(); - if (!this.badgePresent) { - if (item.category === 'Projects') { - this.projectInputEl.val(item.id); - this.addLocationBadge({ - value: 'This project', - }); - } - if (item.category === 'Groups') { - this.groupInputEl.val(item.id); - this.addLocationBadge({ - value: 'This group', - }); - } + onClick(item, $el, e) { + if (location.pathname.indexOf(item.url) !== -1) { + if (!e.metaKey) e.preventDefault(); + if (!this.badgePresent) { + if (item.category === 'Projects') { + this.projectInputEl.val(item.id); + this.addLocationBadge({ + value: 'This project', + }); + } + if (item.category === 'Groups') { + this.groupInputEl.val(item.id); + this.addLocationBadge({ + value: 'This group', + }); } - $el.removeClass('is-active'); - this.disableAutocomplete(); - return this.searchInput.val('').focus(); } + $el.removeClass('is-active'); + this.disableAutocomplete(); + return this.searchInput.val('').focus(); } } - - global.SearchAutocomplete = SearchAutocomplete; - - $(function() { - var $projectOptionsDataEl = $('.js-search-project-options'); - var $groupOptionsDataEl = $('.js-search-group-options'); - var $dashboardOptionsDataEl = $('.js-search-dashboard-options'); - - if ($projectOptionsDataEl.length) { - gl.projectOptions = gl.projectOptions || {}; - - var projectPath = $projectOptionsDataEl.data('project-path'); - - gl.projectOptions[projectPath] = { - name: $projectOptionsDataEl.data('name'), - issuesPath: $projectOptionsDataEl.data('issues-path'), - issuesDisabled: $projectOptionsDataEl.data('issues-disabled'), - mrPath: $projectOptionsDataEl.data('mr-path'), - }; - } - - if ($groupOptionsDataEl.length) { - gl.groupOptions = gl.groupOptions || {}; - - var groupPath = $groupOptionsDataEl.data('group-path'); - - gl.groupOptions[groupPath] = { - name: $groupOptionsDataEl.data('name'), - issuesPath: $groupOptionsDataEl.data('issues-path'), - mrPath: $groupOptionsDataEl.data('mr-path'), - }; - } - - if ($dashboardOptionsDataEl.length) { - gl.dashboardOptions = { - issuesPath: $dashboardOptionsDataEl.data('issues-path'), - mrPath: $dashboardOptionsDataEl.data('mr-path'), - }; - } - }); -})(window.gl || (window.gl = {})); +} diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index ebe7a99ffae..d2f0d7410da 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -1,5 +1,6 @@ import Cookies from 'js-cookie'; import Mousetrap from 'mousetrap'; +import { refreshCurrentPage, visitUrl } from './lib/utils/url_utility'; import findAndFollowLink from './shortcuts_dashboard_navigation'; const defaultStopCallback = Mousetrap.stopCallback; @@ -38,7 +39,7 @@ export default class Shortcuts { if (typeof findFileURL !== 'undefined' && findFileURL !== null) { Mousetrap.bind('t', () => { - gl.utils.visitUrl(findFileURL); + visitUrl(findFileURL); }); } @@ -50,7 +51,10 @@ export default class Shortcuts { } onToggleHelp(e) { - e.preventDefault(); + if (e.preventDefault) { + e.preventDefault(); + } + Shortcuts.toggleHelp(this.enabledHelp); } @@ -62,7 +66,7 @@ export default class Shortcuts { } else { Cookies.set(performanceBarCookieName, 'true', { path: '/' }); } - gl.utils.refreshCurrentPage(); + refreshCurrentPage(); } static toggleMarkdownPreview(e) { @@ -111,6 +115,9 @@ export default class Shortcuts { static focusSearch(e) { $('#search').focus(); - e.preventDefault(); + + if (e.preventDefault) { + e.preventDefault(); + } } } diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/shortcuts_blob.js index fbc57bb4304..cf309be4f6f 100644 --- a/app/assets/javascripts/shortcuts_blob.js +++ b/app/assets/javascripts/shortcuts_blob.js @@ -1,5 +1,5 @@ /* global Mousetrap */ - +import { getLocationHash, visitUrl } from './lib/utils/url_utility'; import Shortcuts from './shortcuts'; const defaults = { @@ -18,9 +18,9 @@ export default class ShortcutsBlob extends Shortcuts { moveToFilePermalink() { if (this.options.fileBlobPermalinkUrl) { - const hash = gl.utils.getLocationHash(); + const hash = getLocationHash(); const hashUrlString = hash ? `#${hash}` : ''; - gl.utils.visitUrl(`${this.options.fileBlobPermalinkUrl}${hashUrlString}`); + visitUrl(`${this.options.fileBlobPermalinkUrl}${hashUrlString}`); } } } diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js index 4f4f606d293..292e3d6a657 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/shortcuts_issuable.js @@ -1,8 +1,8 @@ /* global Mousetrap */ -/* global sidebar */ import _ from 'underscore'; import 'mousetrap'; +import Sidebar from './right_sidebar'; import ShortcutsNavigation from './shortcuts_navigation'; import { CopyAsGFM } from './behaviors/copy_as_gfm'; @@ -11,7 +11,7 @@ export default class ShortcutsIssuable extends ShortcutsNavigation { super(); this.$replyField = isMergeRequest ? $('.js-main-target-form #note_note') : $('.js-main-target-form .js-vue-comment-form'); - this.editBtn = document.querySelector('.issuable-edit'); + this.editBtn = document.querySelector('.js-issuable-edit'); Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee')); Mousetrap.bind('m', () => ShortcutsIssuable.openSidebarDropdown('milestone')); @@ -69,7 +69,7 @@ export default class ShortcutsIssuable extends ShortcutsNavigation { } static openSidebarDropdown(name) { - sidebar.openDropdown(name); + Sidebar.instance.openDropdown(name); return false; } } diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js index 74c17bc14a2..9e47039d920 100644 --- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js +++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js @@ -1,22 +1,32 @@ import Flash from '../../../flash'; import AssigneeTitle from './assignee_title'; import Assignees from './assignees'; - import Store from '../../stores/sidebar_store'; -import Mediator from '../../sidebar_mediator'; - import eventHub from '../../event_hub'; export default { name: 'SidebarAssignees', data() { return { - mediator: new Mediator(), store: new Store(), loading: false, - field: '', }; }, + props: { + mediator: { + type: Object, + required: true, + }, + field: { + type: String, + required: true, + }, + signedIn: { + type: Boolean, + required: false, + default: false, + }, + }, components: { 'assignee-title': AssigneeTitle, assignees: Assignees, @@ -61,10 +71,6 @@ export default { eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees); eventHub.$off('sidebar.saveAssignees', this.saveAssignees); }, - beforeMount() { - this.field = this.$el.dataset.field; - this.signedIn = typeof this.$el.dataset.signedIn !== 'undefined'; - }, template: ` <div> <assignee-title diff --git a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue index c1296b28db7..6fcd2f95309 100644 --- a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue @@ -1,15 +1,19 @@ <script> import Store from '../../stores/sidebar_store'; -import Mediator from '../../sidebar_mediator'; import participants from './participants.vue'; export default { data() { return { - mediator: new Mediator(), store: new Store(), }; }, + props: { + mediator: { + type: Object, + required: true, + }, + }, components: { participants, }, @@ -21,6 +25,7 @@ export default { <participants :loading="store.isFetching.participants" :participants="store.participants" - :number-of-less-participants="7" /> + :number-of-less-participants="7" + /> </div> </template> diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue index 25acc099699..f4bae1d3dd5 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue @@ -1,6 +1,5 @@ <script> import Store from '../../stores/sidebar_store'; -import Mediator from '../../sidebar_mediator'; import eventHub from '../../event_hub'; import Flash from '../../../flash'; import { __ } from '../../../locale'; @@ -9,11 +8,15 @@ import subscriptions from './subscriptions.vue'; export default { data() { return { - mediator: new Mediator(), store: new Store(), }; }, - + props: { + mediator: { + type: Object, + required: true, + }, + }, components: { subscriptions, }, diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 4032f156b15..56cc78ca0ca 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -10,6 +10,27 @@ import Translate from '../vue_shared/translate'; Vue.use(Translate); +function mountAssigneesComponent(mediator) { + const el = document.getElementById('js-vue-sidebar-assignees'); + + if (!el) return; + + // eslint-disable-next-line no-new + new Vue({ + el, + components: { + SidebarAssignees, + }, + render: createElement => createElement('sidebar-assignees', { + props: { + mediator, + field: el.dataset.field, + signedIn: el.hasAttribute('data-signed-in'), + }, + }), + }); +} + function mountConfidentialComponent(mediator) { const el = document.getElementById('js-confidential-entry-point'); @@ -49,9 +70,10 @@ function mountLockComponent(mediator) { }).$mount(el); } -function mountParticipantsComponent() { +function mountParticipantsComponent(mediator) { const el = document.querySelector('.js-sidebar-participants-entry-point'); + // eslint-disable-next-line no-new if (!el) return; // eslint-disable-next-line no-new @@ -60,11 +82,15 @@ function mountParticipantsComponent() { components: { sidebarParticipants, }, - render: createElement => createElement('sidebar-participants', {}), + render: createElement => createElement('sidebar-participants', { + props: { + mediator, + }, + }), }); } -function mountSubscriptionsComponent() { +function mountSubscriptionsComponent(mediator) { const el = document.querySelector('.js-sidebar-subscriptions-entry-point'); if (!el) return; @@ -75,22 +101,35 @@ function mountSubscriptionsComponent() { components: { sidebarSubscriptions, }, - render: createElement => createElement('sidebar-subscriptions', {}), + render: createElement => createElement('sidebar-subscriptions', { + props: { + mediator, + }, + }), }); } -function mount(mediator) { - const sidebarAssigneesEl = document.getElementById('js-vue-sidebar-assignees'); - // Only create the sidebarAssignees vue app if it is found in the DOM - // We currently do not use sidebarAssignees for the MR page - if (sidebarAssigneesEl) { - new Vue(SidebarAssignees).$mount(sidebarAssigneesEl); - } +function mountTimeTrackingComponent() { + const el = document.getElementById('issuable-time-tracker'); + if (!el) return; + + // eslint-disable-next-line no-new + new Vue({ + el, + components: { + SidebarTimeTracking, + }, + render: createElement => createElement('sidebar-time-tracking', {}), + }); +} + +export function mountSidebar(mediator) { + mountAssigneesComponent(mediator); mountConfidentialComponent(mediator); mountLockComponent(mediator); - mountParticipantsComponent(); - mountSubscriptionsComponent(); + mountParticipantsComponent(mediator); + mountSubscriptionsComponent(mediator); new SidebarMoveIssue( mediator, @@ -98,7 +137,9 @@ function mount(mediator) { $('.js-move-issue-confirmation-button'), ).init(); - new Vue(SidebarTimeTracking).$mount('#issuable-time-tracker'); + mountTimeTrackingComponent(); } -export default mount; +export function getSidebarOptions() { + return JSON.parse(document.querySelector('.js-sidebar-options').innerHTML); +} diff --git a/app/assets/javascripts/sidebar/sidebar_bundle.js b/app/assets/javascripts/sidebar/sidebar_bundle.js index f78287e504b..04c39d7b6b5 100644 --- a/app/assets/javascripts/sidebar/sidebar_bundle.js +++ b/app/assets/javascripts/sidebar/sidebar_bundle.js @@ -1,9 +1,8 @@ import Mediator from './sidebar_mediator'; -import mountSidebar from './mount_sidebar'; +import { mountSidebar, getSidebarOptions } from './mount_sidebar'; function domContentLoaded() { - const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML); - const mediator = new Mediator(sidebarOptions); + const mediator = new Mediator(getSidebarOptions()); mediator.fetch(); mountSidebar(mediator); diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js index d4c07a188b3..d86557e870a 100644 --- a/app/assets/javascripts/sidebar/sidebar_mediator.js +++ b/app/assets/javascripts/sidebar/sidebar_mediator.js @@ -1,3 +1,4 @@ +import { visitUrl } from '../lib/utils/url_utility'; import Flash from '../flash'; import Service from './services/sidebar_service'; import Store from './stores/sidebar_store'; @@ -7,7 +8,6 @@ export default class SidebarMediator { if (!SidebarMediator.singleton) { this.initSingleton(options); } - return SidebarMediator.singleton; } @@ -81,7 +81,7 @@ export default class SidebarMediator { .then(response => response.json()) .then((data) => { if (location.pathname !== data.web_url) { - gl.utils.visitUrl(data.web_url); + visitUrl(data.web_url); } }); } diff --git a/app/assets/javascripts/sidebar/stores/sidebar_store.js b/app/assets/javascripts/sidebar/stores/sidebar_store.js index 73eb25e2333..f20cc6d8cca 100644 --- a/app/assets/javascripts/sidebar/stores/sidebar_store.js +++ b/app/assets/javascripts/sidebar/stores/sidebar_store.js @@ -1,33 +1,37 @@ export default class SidebarStore { - constructor(store) { + constructor(options) { if (!SidebarStore.singleton) { - const { currentUser, rootPath, editable } = store; - this.currentUser = currentUser; - this.rootPath = rootPath; - this.editable = editable; - this.timeEstimate = 0; - this.totalTimeSpent = 0; - this.humanTimeEstimate = ''; - this.humanTimeSpent = ''; - this.assignees = []; - this.isFetching = { - assignees: true, - participants: true, - subscriptions: true, - }; - this.isLoading = {}; - this.autocompleteProjects = []; - this.moveToProjectId = 0; - this.isLockDialogOpen = false; - this.participants = []; - this.subscribed = null; - - SidebarStore.singleton = this; + this.initSingleton(options); } return SidebarStore.singleton; } + initSingleton(options) { + const { currentUser, rootPath, editable } = options; + this.currentUser = currentUser; + this.rootPath = rootPath; + this.editable = editable; + this.timeEstimate = 0; + this.totalTimeSpent = 0; + this.humanTimeEstimate = ''; + this.humanTimeSpent = ''; + this.assignees = []; + this.isFetching = { + assignees: true, + participants: true, + subscriptions: true, + }; + this.isLoading = {}; + this.autocompleteProjects = []; + this.moveToProjectId = 0; + this.isLockDialogOpen = false; + this.participants = []; + this.subscribed = null; + + SidebarStore.singleton = this; + } + setAssigneeData(data) { this.isFetching.assignees = false; if (data.assignees) { diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 3f811c59cb9..95e51bc4e7a 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -2,6 +2,7 @@ import FilesCommentButton from './files_comment_button'; import imageDiffHelper from './image_diff/helpers/index'; +import syntaxHighlight from './syntax_highlight'; const WRAPPER = '<div class="diff-content"></div>'; const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'; @@ -64,7 +65,7 @@ export default class SingleFileDiff { _this.loadingContent.hide(); if (data.html) { _this.content = $(data.html); - _this.content.syntaxHighlight(); + syntaxHighlight(_this.content); } else { _this.hasError = true; _this.content = $(ERROR_HTML); diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js index 662d6b36c16..62bdef76c55 100644 --- a/app/assets/javascripts/syntax_highlight.js +++ b/app/assets/javascripts/syntax_highlight.js @@ -10,17 +10,15 @@ // <div class="js-syntax-highlight"></div> // -$.fn.syntaxHighlight = function() { - var $children; - - if ($(this).hasClass('js-syntax-highlight')) { +export default function syntaxHighlight(el) { + if ($(el).hasClass('js-syntax-highlight')) { // Given the element itself, apply highlighting - return $(this).addClass(gon.user_color_scheme); + return $(el).addClass(gon.user_color_scheme); } else { // Given a parent element, recurse to any of its applicable children - $children = $(this).find('.js-syntax-highlight'); + const $children = $(el).find('.js-syntax-highlight'); if ($children.length) { - return $children.syntaxHighlight(); + return syntaxHighlight($children); } } -}; +} diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js index 2fffe09c74e..748caecf153 100644 --- a/app/assets/javascripts/todos.js +++ b/app/assets/javascripts/todos.js @@ -1,5 +1,5 @@ /* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props */ - +import { visitUrl } from './lib/utils/url_utility'; import UsersSelect from './users_select'; import { isMetaClick } from './lib/utils/common_utils'; @@ -150,7 +150,7 @@ export default class Todos { window.open(todoLink, windowTarget); } else { - gl.utils.visitUrl(todoLink); + visitUrl(todoLink); } } } diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js index 7777ed1c3dc..1a0b2c0415b 100644 --- a/app/assets/javascripts/tree.js +++ b/app/assets/javascripts/tree.js @@ -1,4 +1,5 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, class-methods-use-this */ +import { visitUrl } from './lib/utils/url_utility'; export default class TreeView { constructor() { @@ -14,7 +15,7 @@ export default class TreeView { e.preventDefault(); return window.open(path, '_blank'); } else { - return gl.utils.visitUrl(path); + return visitUrl(path); } } }); @@ -56,7 +57,7 @@ export default class TreeView { } else if (e.which === 13) { path = $('.tree-item.selected .tree-item-file-name a').attr('href'); if (path) { - return gl.utils.visitUrl(path); + return visitUrl(path); } } }); diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index 5e947769f8a..0581239d5a5 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,5 +1,9 @@ import _ from 'underscore'; -import d3 from 'd3'; +import { scaleLinear, scaleThreshold } from 'd3-scale'; +import { select } from 'd3-selection'; +import { getDayName, getDayDifference } from '../lib/utils/datetime_utility'; + +const d3 = { select, scaleLinear, scaleThreshold }; const LOADING_HTML = ` <div class="text-center"> @@ -17,7 +21,7 @@ function getSystemDate(systemUtcOffsetSeconds) { function formatTooltipText({ date, count }) { const dateObject = new Date(date); - const dateDayName = gl.utils.getDayName(dateObject); + const dateDayName = getDayName(dateObject); const dateText = dateObject.format('mmm d, yyyy'); let contribText = 'No contributions'; @@ -27,7 +31,7 @@ function formatTooltipText({ date, count }) { return `${contribText}<br />${dateDayName} ${dateText}`; } -const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); +const initColorKey = () => d3.scaleLinear().range(['#acd5f2', '#254e77']).domain([0, 3]); export default class ActivityCalendar { constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0) { @@ -51,7 +55,7 @@ export default class ActivityCalendar { const oneYearAgo = new Date(today); oneYearAgo.setFullYear(today.getFullYear() - 1); - const days = gl.utils.getDayDifference(oneYearAgo, today); + const days = getDayDifference(oneYearAgo, today); for (let i = 0; i <= days; i += 1) { const date = new Date(oneYearAgo); @@ -204,7 +208,7 @@ export default class ActivityCalendar { initColor() { const colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; - return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange); + return d3.scaleThreshold().domain([0, 10, 20, 30]).range(colorRange); } clickDay(stamp) { diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index 1215b265e28..992baa9a1ef 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -1,4 +1,6 @@ +import Activities from '../activities'; import ActivityCalendar from './activity_calendar'; +import { localTimeAgo } from '../lib/utils/datetime_utility'; /** * UserTabs @@ -138,7 +140,7 @@ export default class UserTabs { const tabSelector = `div#${action}`; this.$parentEl.find(tabSelector).html(data.html); this.loaded[action] = true; - gl.utils.localTimeAgo($('.js-timeago', tabSelector)); + localTimeAgo($('.js-timeago', tabSelector)); }, }); } @@ -169,7 +171,7 @@ export default class UserTabs { }); // eslint-disable-next-line no-new - new gl.Activities(); + new Activities(); this.loaded.activity = true; } diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 759cc9925f4..f249bd036d6 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -541,7 +541,6 @@ function UsersSelect(currentUser, els, options = {}) { options.projectId = $(select).data('project-id'); options.groupId = $(select).data('group-id'); options.showCurrentUser = $(select).data('current-user'); - options.pushCodeToProtectedBranches = $(select).data('push-code-to-protected-branches'); options.authorId = $(select).data('author-id'); options.skipUsers = $(select).data('skip-users'); showNullUser = $(select).data('null-user'); @@ -688,7 +687,6 @@ UsersSelect.prototype.users = function(query, options, callback) { todo_filter: options.todoFilter || null, todo_state_filter: options.todoStateFilter || null, current_user: options.showCurrentUser || null, - push_code_to_protected_branches: options.pushCodeToProtectedBranches || null, author_id: options.authorId || null, skip_users: options.skipUsers || null }, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js index e86a0f7e749..d48f3a01420 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js @@ -1,4 +1,5 @@ -import '~/lib/utils/datetime_utility'; +import { getTimeago } from '~/lib/utils/datetime_utility'; +import { visitUrl } from '../../lib/utils/url_utility'; import Flash from '../../flash'; import MemoryUsage from './mr_widget_memory_usage'; import StatusIcon from './mr_widget_status_icon'; @@ -16,7 +17,7 @@ export default { }, methods: { formatDate(date) { - return gl.utils.getTimeago().format(date); + return getTimeago().format(date); }, hasExternalUrls(deployment = {}) { return deployment.external_url && deployment.external_url_formatted; @@ -33,10 +34,10 @@ export default { if (isConfirmed) { MRWidgetService.stopEnvironment(deployment.stop_url) - .then(res => res.json()) - .then((res) => { - if (res.redirect_url) { - gl.utils.visitUrl(res.redirect_url); + .then(res => res.data) + .then((data) => { + if (data.redirect_url) { + visitUrl(data.redirect_url); } }) .catch(() => { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js index 13e4cb5717e..85bfd03a3cf 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js @@ -1,5 +1,6 @@ import tooltip from '../../vue_shared/directives/tooltip'; import { pluralize } from '../../lib/utils/text_utility'; +import icon from '../../vue_shared/components/icon.vue'; export default { name: 'MRWidgetHeader', @@ -9,6 +10,9 @@ export default { directives: { tooltip, }, + components: { + icon, + }, computed: { shouldShowCommitsBehindText() { return this.mr.divergedCommitsCount > 0; @@ -81,10 +85,9 @@ export default { data-toggle="dropdown" aria-label="Download as" role="button"> - <i - class="fa fa-download" - aria-hidden="true"> - </i> + <icon + name="download"> + </icon> <i class="fa fa-caret-down" aria-hidden="true"> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js index a8c686e5065..69e70ba1dd6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js @@ -102,11 +102,11 @@ export default { return res; } - return res.json(); + return res.data; }) - .then((res) => { - this.computeGraphData(res.metrics, res.deployment_time); - return res; + .then((data) => { + this.computeGraphData(data.metrics, data.deployment_time); + return data; }) .catch(() => { this.loadFailed = true; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js index b25cc3443ef..dd8b2665b1d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js @@ -16,9 +16,9 @@ export default { <div class="media-body"> <mr-widget-author-and-time actionText="Closed by" - :author="mr.closedEvent.author" - :dateTitle="mr.closedEvent.updatedAt" - :dateReadable="mr.closedEvent.formattedUpdatedAt" + :author="mr.metrics.closedBy" + :dateTitle="mr.metrics.closedAt" + :dateReadable="mr.metrics.readableClosedAt" /> <section class="mr-info-list"> <p> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js index 05c4a28be88..bd349111bbd 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js @@ -31,9 +31,9 @@ export default { cancelAutomaticMerge() { this.isCancellingAutoMerge = true; this.service.cancelAutomaticMerge() - .then(res => res.json()) - .then((res) => { - eventHub.$emit('UpdateWidgetData', res); + .then(res => res.data) + .then((data) => { + eventHub.$emit('UpdateWidgetData', data); }) .catch(() => { this.isCancellingAutoMerge = false; @@ -49,9 +49,9 @@ export default { this.isRemovingSourceBranch = true; this.service.mergeResource.save(options) - .then(res => res.json()) - .then((res) => { - if (res.status === 'merge_when_pipeline_succeeds') { + .then(res => res.data) + .then((data) => { + if (data.status === 'merge_when_pipeline_succeeds') { eventHub.$emit('MRWidgetUpdateRequested'); } }) @@ -65,10 +65,12 @@ export default { <div class="mr-widget-body media"> <status-icon status="success" /> <div class="media-body"> - <h4> - Set by - <mr-widget-author :author="mr.setToMWPSBy" /> - to be merged automatically when the pipeline succeeds + <h4 class="flex-container-block"> + <span class="append-right-10"> + Set by + <mr-widget-author :author="mr.setToMWPSBy" /> + to be merged automatically when the pipeline succeeds + </span> <a v-if="mr.canCancelAutomaticMerge" @click.prevent="cancelAutomaticMerge" @@ -94,8 +96,13 @@ export default { <p v-if="mr.shouldRemoveSourceBranch"> The source branch will be removed </p> - <p v-else> - The source branch will not be removed + <p + v-else + class="flex-container-block" + > + <span class="append-right-10"> + The source branch will not be removed + </span> <a v-if="canRemoveSourceBranch" :disabled="isRemovingSourceBranch" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js index 2dfd87ed904..ba9681680ef 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js @@ -47,9 +47,9 @@ export default { removeSourceBranch() { this.isMakingRequest = true; this.service.removeSourceBranch() - .then(res => res.json()) - .then((res) => { - if (res.message === 'Branch was removed') { + .then(res => res.data) + .then((data) => { + if (data.message === 'Branch was removed') { eventHub.$emit('MRWidgetUpdateRequested', () => { this.isMakingRequest = false; }); @@ -68,9 +68,9 @@ export default { <div class="space-children"> <mr-widget-author-and-time actionText="Merged by" - :author="mr.mergedEvent.author" - :date-title="mr.mergedEvent.updatedAt" - :date-readable="mr.mergedEvent.formattedUpdatedAt" /> + :author="mr.metrics.mergedBy" + :date-title="mr.metrics.mergedAt" + :date-readable="mr.metrics.readableMergedAt" /> <a v-if="mr.canRevertInCurrentMR" v-tooltip diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js index be37dd87de9..e82fb979162 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js @@ -135,16 +135,16 @@ export default { this.isMakingRequest = true; this.service.merge(options) - .then(res => res.json()) - .then((res) => { - const hasError = res.status === 'failed' || res.status === 'hook_validation_error'; + .then(res => res.data) + .then((data) => { + const hasError = data.status === 'failed' || data.status === 'hook_validation_error'; - if (res.status === 'merge_when_pipeline_succeeds') { + if (data.status === 'merge_when_pipeline_succeeds') { eventHub.$emit('MRWidgetUpdateRequested'); - } else if (res.status === 'success') { + } else if (data.status === 'success') { this.initiateMergePolling(); } else if (hasError) { - eventHub.$emit('FailedToMerge', res.merge_error); + eventHub.$emit('FailedToMerge', data.merge_error); } }) .catch(() => { @@ -159,9 +159,9 @@ export default { }, handleMergePolling(continuePolling, stopPolling) { this.service.poll() - .then(res => res.json()) - .then((res) => { - if (res.state === 'merged') { + .then(res => res.data) + .then((data) => { + if (data.state === 'merged') { // If state is merged we should update the widget and stop the polling eventHub.$emit('MRWidgetUpdateRequested'); eventHub.$emit('FetchActionsContent'); @@ -174,11 +174,11 @@ export default { // If user checked remove source branch and we didn't remove the branch yet // we should start another polling for source branch remove process - if (this.removeSourceBranch && res.source_branch_exists) { + if (this.removeSourceBranch && data.source_branch_exists) { this.initiateRemoveSourceBranchPolling(); } - } else if (res.merge_error) { - eventHub.$emit('FailedToMerge', res.merge_error); + } else if (data.merge_error) { + eventHub.$emit('FailedToMerge', data.merge_error); stopPolling(); } else { // MR is not merged yet, continue polling until the state becomes 'merged' @@ -199,11 +199,11 @@ export default { }, handleRemoveBranchPolling(continuePolling, stopPolling) { this.service.poll() - .then(res => res.json()) - .then((res) => { + .then(res => res.data) + .then((data) => { // If source branch exists then we should continue polling // because removing a source branch is a background task and takes time - if (res.source_branch_exists) { + if (data.source_branch_exists) { continuePolling(); } else { // Branch is removed. Update widget, stop polling and hide the spinner diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue new file mode 100644 index 00000000000..09276ba2769 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -0,0 +1,133 @@ +<script> + import simplePoll from '../../../lib/utils/simple_poll'; + import eventHub from '../../event_hub'; + import statusIcon from '../mr_widget_status_icon'; + import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; + import Flash from '../../../flash'; + + export default { + name: 'MRWidgetRebase', + props: { + mr: { + type: Object, + required: true, + }, + service: { + type: Object, + required: true, + }, + }, + components: { + statusIcon, + loadingIcon, + }, + data() { + return { + isMakingRequest: false, + rebasingError: null, + }; + }, + computed: { + status() { + if (this.mr.rebaseInProgress || this.isMakingRequest) { + return 'loading'; + } + if (!this.mr.canPushToSourceBranch && !this.mr.rebaseInProgress) { + return 'warning'; + } + return 'success'; + }, + showDisabledButton() { + return ['failed', 'loading'].includes(this.status); + }, + }, + methods: { + rebase() { + this.isMakingRequest = true; + this.rebasingError = null; + + this.service.rebase() + .then(() => { + simplePoll(this.checkRebaseStatus); + }) + .catch((error) => { + this.rebasingError = error.merge_error; + this.isMakingRequest = false; + Flash('Something went wrong. Please try again.'); + }); + }, + checkRebaseStatus(continuePolling, stopPolling) { + this.service.poll() + .then(res => res.data) + .then((res) => { + if (res.rebase_in_progress) { + continuePolling(); + } else { + this.isMakingRequest = false; + + if (res.merge_error && res.merge_error.length) { + this.rebasingError = res.merge_error; + Flash('Something went wrong. Please try again.'); + } + + eventHub.$emit('MRWidgetUpdateRequested'); + stopPolling(); + } + }) + .catch(() => { + this.isMakingRequest = false; + Flash('Something went wrong. Please try again.'); + stopPolling(); + }); + }, + }, + }; +</script> +<template> + <div class="mr-widget-body media"> + <status-icon + :status="status" + :show-disabled-button="showDisabledButton" + /> + + <div class="rebase-state-find-class-convention media media-body space-children"> + <template v-if="mr.rebaseInProgress || isMakingRequest"> + <span class="bold"> + Rebase in progress + </span> + </template> + <template v-if="!mr.rebaseInProgress && !mr.canPushToSourceBranch"> + <span class="bold"> + Fast-forward merge is not possible. + Rebase the source branch onto + <span class="label-branch">{{mr.targetBranch}}</span> + to allow this merge request to be merged. + </span> + </template> + <template v-if="!mr.rebaseInProgress && mr.canPushToSourceBranch && !isMakingRequest"> + <div class="accept-merge-holder clearfix js-toggle-container accept-action media space-children"> + <button + type="button" + class="btn btn-sm btn-reopen btn-success" + :disabled="isMakingRequest" + @click="rebase"> + <loading-icon v-if="isMakingRequest" /> + Rebase + </button> + <span + v-if="!rebasingError" + class="bold"> + Fast-forward merge is not possible. + Rebase the source branch onto the target branch or merge target + branch into source branch to allow this merge request to be merged. + </span> + <span + v-else + class="bold danger"> + {{rebasingError}} + </span> + </div> + </template> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js index 4f83350e07c..13461440ef2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js @@ -23,9 +23,9 @@ export default { removeWIP() { this.isMakingRequest = true; this.service.removeWIP() - .then(res => res.json()) - .then((res) => { - eventHub.$emit('UpdateWidgetData', res); + .then(res => res.data) + .then((data) => { + eventHub.$emit('UpdateWidgetData', data); new window.Flash('The merge request can now be merged.', 'notice'); // eslint-disable-line $('.merge-request .detail-page-description .title').text(this.mr.title); }) diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js index 5bd8b99420a..940f3d9b2d0 100644 --- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js +++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js @@ -32,6 +32,7 @@ export { default as UnresolvedDiscussionsState } from './components/states/mr_wi export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked'; export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds'; +export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed'; export { default as CheckingState } from './components/states/mr_widget_checking'; export { default as MRWidgetStore } from './stores/mr_widget_store'; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js index 9cb3edead86..2075f8e4fec 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js @@ -10,6 +10,7 @@ import { MergedState, ClosedState, MergingState, + RebaseState, WipState, ArchivedState, ConflictsState, @@ -62,7 +63,7 @@ export default { return this.mr.hasCI; }, shouldRenderRelatedLinks() { - return !!this.mr.relatedLinks; + return !!this.mr.relatedLinks && !this.mr.isNothingToMergeState; }, shouldRenderDeployments() { return this.mr.deployments.length; @@ -79,19 +80,20 @@ export default { ciEnvironmentsStatusPath: store.ciEnvironmentsStatusPath, statusPath: store.statusPath, mergeActionsContentPath: store.mergeActionsContentPath, + rebasePath: store.rebasePath, }; return new MRWidgetService(endpoints); }, checkStatus(cb) { return this.service.checkStatus() - .then(res => res.json()) - .then((res) => { - this.handleNotification(res); - this.mr.setData(res); + .then(res => res.data) + .then((data) => { + this.handleNotification(data); + this.mr.setData(data); this.setFaviconHelper(); if (cb) { - cb.call(null, res); + cb.call(null, data); } }) .catch(() => { @@ -124,10 +126,10 @@ export default { }, fetchDeployments() { return this.service.fetchDeployments() - .then(res => res.json()) - .then((res) => { - if (res.length) { - this.mr.deployments = res; + .then(res => res.data) + .then((data) => { + if (data.length) { + this.mr.deployments = data; } }) .catch(() => { @@ -137,9 +139,9 @@ export default { fetchActionsContent() { this.service.fetchMergeActionsContent() .then((res) => { - if (res.body) { + if (res.data) { const el = document.createElement('div'); - el.innerHTML = res.body; + el.innerHTML = res.data; document.body.appendChild(el); Project.initRefSwitcher(); } @@ -232,6 +234,7 @@ export default { 'mr-widget-pipeline-failed': PipelineFailedState, 'mr-widget-merge-when-pipeline-succeeds': MergeWhenPipelineSucceedsState, 'mr-widget-auto-merge-failed': AutoMergeFailed, + 'mr-widget-rebase': RebaseState, }, template: ` <div class="mr-state-widget prepend-top-default"> diff --git a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js index 99f5c305df5..fecbfec2214 100644 --- a/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js +++ b/app/assets/javascripts/vue_merge_request_widget/services/mr_widget_service.js @@ -1,57 +1,51 @@ -import Vue from 'vue'; -import VueResource from 'vue-resource'; - -Vue.use(VueResource); +import axios from '../../lib/utils/axios_utils'; export default class MRWidgetService { constructor(endpoints) { - this.mergeResource = Vue.resource(endpoints.mergePath); - this.mergeCheckResource = Vue.resource(endpoints.statusPath); - this.cancelAutoMergeResource = Vue.resource(endpoints.cancelAutoMergePath); - this.removeWIPResource = Vue.resource(endpoints.removeWIPPath); - this.removeSourceBranchResource = Vue.resource(endpoints.sourceBranchPath); - this.deploymentsResource = Vue.resource(endpoints.ciEnvironmentsStatusPath); - this.pollResource = Vue.resource(`${endpoints.statusPath}?serializer=basic`); - this.mergeActionsContentResource = Vue.resource(endpoints.mergeActionsContentPath); + this.endpoints = endpoints; } merge(data) { - return this.mergeResource.save(data); + return axios.post(this.endpoints.mergePath, data); } cancelAutomaticMerge() { - return this.cancelAutoMergeResource.save(); + return axios.post(this.endpoints.cancelAutoMergePath); } removeWIP() { - return this.removeWIPResource.save(); + return axios.post(this.endpoints.removeWIPPath); } removeSourceBranch() { - return this.removeSourceBranchResource.delete(); + return axios.delete(this.endpoints.sourceBranchPath); } fetchDeployments() { - return this.deploymentsResource.get(); + return axios.get(this.endpoints.ciEnvironmentsStatusPath); } poll() { - return this.pollResource.get(); + return axios.get(`${this.endpoints.statusPath}?serializer=basic`); } checkStatus() { - return this.mergeCheckResource.get(); + return axios.get(`${this.endpoints.statusPath}?serializer=widget`); } fetchMergeActionsContent() { - return this.mergeActionsContentResource.get(); + return axios.get(this.endpoints.mergeActionsContentPath); + } + + rebase() { + return axios.post(this.endpoints.rebasePath); } static stopEnvironment(url) { - return Vue.http.post(url); + return axios.post(url); } static fetchMetrics(metricsUrl) { - return Vue.http.get(`${metricsUrl}.json`); + return axios.get(`${metricsUrl}.json`); } } diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js index 7c15abfff10..f7f0c1b6cb7 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js @@ -1,30 +1,34 @@ +import { stateKey } from './state_maps'; + export default function deviseState(data) { if (data.project_archived) { - return 'archived'; + return stateKey.archived; } else if (data.branch_missing) { - return 'missingBranch'; + return stateKey.missingBranch; } else if (!data.commits_count) { - return 'nothingToMerge'; + return stateKey.nothingToMerge; } else if (this.mergeStatus === 'unchecked') { - return 'checking'; + return stateKey.checking; } else if (data.has_conflicts) { - return 'conflicts'; + return stateKey.conflicts; } else if (data.work_in_progress) { - return 'workInProgress'; + return stateKey.workInProgress; } else if (this.onlyAllowMergeIfPipelineSucceeds && this.isPipelineFailed) { - return 'pipelineFailed'; + return stateKey.pipelineFailed; } else if (this.hasMergeableDiscussionsState) { - return 'unresolvedDiscussions'; + return stateKey.unresolvedDiscussions; } else if (this.isPipelineBlocked) { - return 'pipelineBlocked'; + return stateKey.pipelineBlocked; } else if (this.hasSHAChanged) { - return 'shaMismatch'; + return stateKey.shaMismatch; } else if (this.mergeWhenPipelineSucceeds) { - return this.mergeError ? 'autoMergeFailed' : 'mergeWhenPipelineSucceeds'; + return this.mergeError ? stateKey.autoMergeFailed : stateKey.mergeWhenPipelineSucceeds; } else if (!this.canMerge) { - return 'notAllowedToMerge'; + return stateKey.notAllowedToMerge; + } else if (this.shouldBeRebased) { + return stateKey.rebase; } else if (this.canBeMerged) { - return 'readyToMerge'; + return stateKey.readyToMerge; } return null; } diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index c1f7e64f580..ed004b3bb08 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -1,5 +1,7 @@ import Timeago from 'timeago.js'; import { getStateKey } from '../dependencies'; +import { stateKey } from './state_maps'; +import { formatDate } from '../../lib/utils/datetime_utility'; export default class MergeRequestStore { @@ -24,6 +26,7 @@ export default class MergeRequestStore { this.divergedCommitsCount = data.diverged_commits_count; this.pipeline = data.pipeline || {}; this.deployments = this.deployments || data.deployments || []; + this.initRebase(data); if (data.issues_links) { const links = data.issues_links; @@ -37,9 +40,8 @@ export default class MergeRequestStore { } this.updatedAt = data.updated_at; - this.mergedEvent = MergeRequestStore.getEventObject(data.merge_event); - this.closedEvent = MergeRequestStore.getEventObject(data.closed_event); - this.setToMWPSBy = MergeRequestStore.getAuthorObject({ author: data.merge_user || {} }); + this.metrics = MergeRequestStore.buildMetrics(data.metrics); + this.setToMWPSBy = MergeRequestStore.formatUserObject(data.merge_user || {}); this.mergeUserId = data.merge_user_id; this.currentUserId = gon.current_user_id; this.sourceBranchPath = data.source_branch_path; @@ -119,43 +121,53 @@ export default class MergeRequestStore { } } - static getEventObject(event) { - return { - author: MergeRequestStore.getAuthorObject(event), - updatedAt: gl.utils.formatDate(MergeRequestStore.getEventUpdatedAtDate(event)), - formattedUpdatedAt: MergeRequestStore.getEventDate(event), - }; + get isNothingToMergeState() { + return this.state === stateKey.nothingToMerge; + } + + initRebase(data) { + this.canPushToSourceBranch = data.can_push_to_source_branch; + this.rebaseInProgress = data.rebase_in_progress; + this.approvalsLeft = !data.approved; + this.rebasePath = data.rebase_path; } - static getAuthorObject(event) { - if (!event) { + static buildMetrics(metrics) { + if (!metrics) { return {}; } return { - name: event.author.name || '', - username: event.author.username || '', - webUrl: event.author.web_url || '', - avatarUrl: event.author.avatar_url || '', + mergedBy: MergeRequestStore.formatUserObject(metrics.merged_by), + closedBy: MergeRequestStore.formatUserObject(metrics.closed_by), + mergedAt: formatDate(metrics.merged_at), + closedAt: formatDate(metrics.closed_at), + readableMergedAt: MergeRequestStore.getReadableDate(metrics.merged_at), + readableClosedAt: MergeRequestStore.getReadableDate(metrics.closed_at), }; } - static getEventUpdatedAtDate(event) { - if (!event) { - return ''; + static formatUserObject(user) { + if (!user) { + return {}; } - return event.updated_at; + return { + name: user.name || '', + username: user.username || '', + webUrl: user.web_url || '', + avatarUrl: user.avatar_url || '', + }; } - static getEventDate(event) { - const timeagoInstance = new Timeago(); - - if (!event) { + static getReadableDate(date) { + if (!date) { return ''; } - return timeagoInstance.format(MergeRequestStore.getEventUpdatedAtDate(event)); + const timeagoInstance = new Timeago(); + + return timeagoInstance.format(date); } } diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js index 9074a064a6d..29d5bd4a1da 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js @@ -17,6 +17,7 @@ const stateToComponentMap = { failedToMerge: 'mr-widget-failed-to-merge', autoMergeFailed: 'mr-widget-auto-merge-failed', shaMismatch: 'mr-widget-sha-mismatch', + rebase: 'mr-widget-rebase', }; const statesToShowHelpWidget = [ @@ -29,8 +30,27 @@ const statesToShowHelpWidget = [ 'pipelineFailed', 'pipelineBlocked', 'autoMergeFailed', + 'rebase', ]; +export const stateKey = { + archived: 'archived', + missingBranch: 'missingBranch', + nothingToMerge: 'nothingToMerge', + checking: 'checking', + conflicts: 'conflicts', + workInProgress: 'workInProgress', + pipelineFailed: 'pipelineFailed', + unresolvedDiscussions: 'unresolvedDiscussions', + pipelineBlocked: 'pipelineBlocked', + shaMismatch: 'shaMismatch', + autoMergeFailed: 'autoMergeFailed', + mergeWhenPipelineSucceeds: 'mergeWhenPipelineSucceeds', + notAllowedToMerge: 'notAllowedToMerge', + readyToMerge: 'readyToMerge', + rebase: 'rebase', +}; + export default { stateToComponentMap, statesToShowHelpWidget, diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index 52814de8b2d..59ca9a0a6d4 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -2,13 +2,14 @@ import commitIconSvg from 'icons/_icon_commit.svg'; import userAvatarLink from './user_avatar/user_avatar_link.vue'; import tooltip from '../directives/tooltip'; + import Icon from '../../vue_shared/components/icon.vue'; export default { props: { /** * Indicates the existance of a tag. * Used to render the correct icon, if true will render `fa-tag` icon, - * if false will render `fa-code-fork` icon. + * if false will render a svg sprite fork icon */ tag: { type: Boolean, @@ -107,6 +108,7 @@ }, components: { userAvatarLink, + Icon, }, created() { this.commitIconSvg = commitIconSvg; @@ -123,11 +125,10 @@ class="fa fa-tag" aria-hidden="true"> </i> - <i + <icon v-if="!tag" - class="fa fa-code-fork" - aria-hidden="true"> - </i> + name="fork"> + </icon> </div> <a diff --git a/app/assets/javascripts/vue_shared/components/expand_button.vue b/app/assets/javascripts/vue_shared/components/expand_button.vue new file mode 100644 index 00000000000..05e48ed297f --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/expand_button.vue @@ -0,0 +1,46 @@ +<script> + import { __ } from '~/locale'; + /** + * Port of detail_behavior expand button. + * + * @example + * <expand-button> + * <template slot="expanded"> + * Text goes here. + * </template> + * </expand-button> + */ + export default { + name: 'expandButton', + data() { + return { + isCollapsed: true, + }; + }, + computed: { + ariaLabel() { + return __('Click to expand text'); + }, + }, + methods: { + onClick() { + this.isCollapsed = !this.isCollapsed; + }, + }, + }; +</script> +<template> + <span> + <button + type="button" + v-show="isCollapsed" + class="text-expander btn-blank" + :aria-label="ariaLabel" + @click="onClick"> + ... + </button> + <span v-show="!isCollapsed"> + <slot name="expanded"></slot> + </span> + </span> +</template> diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue new file mode 100644 index 00000000000..65c64967fdc --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/file_icon.vue @@ -0,0 +1,92 @@ +<script> + import getIconForFile from './file_icon/file_icon_map'; + import loadingIcon from '../../vue_shared/components/loading_icon.vue'; + import icon from '../../vue_shared/components/icon.vue'; + + /* This is a re-usable vue component for rendering a svg sprite + icon + + Sample configuration: + + <file-icon + name="retry" + :size="32" + css-classes="top" + /> + + */ + export default { + props: { + fileName: { + type: String, + required: true, + }, + + folder: { + type: Boolean, + required: false, + default: false, + }, + + opened: { + type: Boolean, + required: false, + default: false, + }, + + loading: { + type: Boolean, + required: false, + default: false, + }, + + size: { + type: Number, + required: false, + default: 16, + }, + + cssClasses: { + type: String, + required: false, + default: '', + }, + }, + components: { + loadingIcon, + icon, + }, + computed: { + spriteHref() { + const iconName = getIconForFile(this.fileName) || 'file'; + return `${gon.sprite_file_icons}#${iconName}`; + }, + folderIconName() { + // We don't have a open folder icon yet + return this.opened ? 'folder' : 'folder'; + }, + iconSizeClass() { + return this.size ? `s${this.size}` : ''; + }, + }, + }; +</script> +<template> + <span> + <svg + :class="[iconSizeClass, cssClasses]" + v-if="!loading && !folder"> + <use + v-bind="{'xlink:href':spriteHref}"/> + </svg> + <icon + v-if="!loading && folder" + :name="folderIconName" + :size="size" + /> + <loading-icon + v-if="loading" + :inline="true" + /> + </span> +</template> diff --git a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js new file mode 100644 index 00000000000..9ffbaae3ea5 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js @@ -0,0 +1,589 @@ +const fileExtensionIcons = { + html: 'html', + htm: 'html', + html_vm: 'html', + asp: 'html', + jade: 'pug', + pug: 'pug', + md: 'markdown', + 'md.rendered': 'markdown', + markdown: 'markdown', + 'markdown.rendered': 'markdown', + rst: 'markdown', + blink: 'blink', + css: 'css', + scss: 'sass', + sass: 'sass', + less: 'less', + json: 'json', + yaml: 'yaml', + 'YAML-tmLanguage': 'yaml', + yml: 'yaml', + xml: 'xml', + plist: 'xml', + xsd: 'xml', + dtd: 'xml', + xsl: 'xml', + xslt: 'xml', + resx: 'xml', + iml: 'xml', + xquery: 'xml', + tmLanguage: 'xml', + manifest: 'xml', + project: 'xml', + png: 'image', + jpeg: 'image', + jpg: 'image', + gif: 'image', + svg: 'image', + ico: 'image', + tif: 'image', + tiff: 'image', + psd: 'image', + psb: 'image', + ami: 'image', + apx: 'image', + bmp: 'image', + bpg: 'image', + brk: 'image', + cur: 'image', + dds: 'image', + dng: 'image', + exr: 'image', + fpx: 'image', + gbr: 'image', + img: 'image', + jbig2: 'image', + jb2: 'image', + jng: 'image', + jxr: 'image', + pbm: 'image', + pgf: 'image', + pic: 'image', + raw: 'image', + webp: 'image', + js: 'javascript', + ejs: 'javascript', + esx: 'javascript', + jsx: 'react', + tsx: 'react', + ini: 'settings', + dlc: 'settings', + dll: 'settings', + config: 'settings', + conf: 'settings', + properties: 'settings', + prop: 'settings', + settings: 'settings', + option: 'settings', + props: 'settings', + toml: 'settings', + prefs: 'settings', + 'sln.dotsettings': 'settings', + 'sln.dotsettings.user': 'settings', + ts: 'typescript', + 'd.ts': 'typescript-def', + marko: 'markojs', + pdf: 'pdf', + xlsx: 'table', + xls: 'table', + csv: 'table', + tsv: 'table', + vscodeignore: 'vscode', + vsixmanifest: 'vscode', + vsix: 'vscode', + 'code-workplace': 'vscode', + suo: 'visualstudio', + sln: 'visualstudio', + csproj: 'visualstudio', + vb: 'visualstudio', + pdb: 'database', + sql: 'database', + pks: 'database', + pkb: 'database', + accdb: 'database', + mdb: 'database', + sqlite: 'database', + cs: 'csharp', + zip: 'zip', + tar: 'zip', + gz: 'zip', + xz: 'zip', + bzip2: 'zip', + gzip: 'zip', + '7z': 'zip', + rar: 'zip', + tgz: 'zip', + exe: 'exe', + msi: 'exe', + java: 'java', + jar: 'java', + jsp: 'java', + c: 'c', + m: 'c', + h: 'h', + cc: 'cpp', + cpp: 'cpp', + mm: 'cpp', + cxx: 'cpp', + hpp: 'hpp', + go: 'go', + py: 'python', + url: 'url', + sh: 'console', + ksh: 'console', + csh: 'console', + tcsh: 'console', + zsh: 'console', + bash: 'console', + bat: 'console', + cmd: 'console', + ps1: 'powershell', + psm1: 'powershell', + psd1: 'powershell', + ps1xml: 'powershell', + psc1: 'powershell', + pssc: 'powershell', + gradle: 'gradle', + doc: 'word', + docx: 'word', + rtf: 'word', + cer: 'certificate', + cert: 'certificate', + crt: 'certificate', + pub: 'key', + key: 'key', + pem: 'key', + asc: 'key', + gpg: 'key', + woff: 'font', + woff2: 'font', + ttf: 'font', + eot: 'font', + suit: 'font', + otf: 'font', + bmap: 'font', + fnt: 'font', + odttf: 'font', + ttc: 'font', + font: 'font', + fonts: 'font', + sui: 'font', + ntf: 'font', + mrf: 'font', + lib: 'lib', + bib: 'lib', + rb: 'ruby', + erb: 'ruby', + fs: 'fsharp', + fsx: 'fsharp', + fsi: 'fsharp', + fsproj: 'fsharp', + swift: 'swift', + ino: 'arduino', + dockerignore: 'docker', + dockerfile: 'docker', + tex: 'tex', + cls: 'tex', + sty: 'tex', + pptx: 'powerpoint', + ppt: 'powerpoint', + pptm: 'powerpoint', + potx: 'powerpoint', + pot: 'powerpoint', + potm: 'powerpoint', + ppsx: 'powerpoint', + ppsm: 'powerpoint', + pps: 'powerpoint', + ppam: 'powerpoint', + ppa: 'powerpoint', + webm: 'movie', + mkv: 'movie', + flv: 'movie', + vob: 'movie', + ogv: 'movie', + ogg: 'movie', + gifv: 'movie', + avi: 'movie', + mov: 'movie', + qt: 'movie', + wmv: 'movie', + yuv: 'movie', + rm: 'movie', + rmvb: 'movie', + mp4: 'movie', + m4v: 'movie', + mpg: 'movie', + mp2: 'movie', + mpeg: 'movie', + mpe: 'movie', + mpv: 'movie', + m2v: 'movie', + vdi: 'virtual', + vbox: 'virtual', + 'vbox-prev': 'virtual', + ics: 'email', + mp3: 'music', + flac: 'music', + m4a: 'music', + wma: 'music', + aiff: 'music', + coffee: 'coffee', + txt: 'document', + graphql: 'graphql', + rs: 'rust', + raml: 'raml', + xaml: 'xaml', + hs: 'haskell', + kt: 'kotlin', + kts: 'kotlin', + patch: 'git', + lua: 'lua', + clj: 'clojure', + cljs: 'clojure', + groovy: 'groovy', + r: 'r', + rmd: 'r', + dart: 'dart', + as: 'actionscript', + mxml: 'mxml', + ahk: 'autohotkey', + swf: 'flash', + swc: 'swc', + cmake: 'cmake', + asm: 'assembly', + a51: 'assembly', + inc: 'assembly', + nasm: 'assembly', + s: 'assembly', + ms: 'assembly', + agc: 'assembly', + ags: 'assembly', + aea: 'assembly', + argus: 'assembly', + mitigus: 'assembly', + binsource: 'assembly', + vue: 'vue', + ml: 'ocaml', + mli: 'ocaml', + cmx: 'ocaml', + 'js.map': 'javascript-map', + 'css.map': 'css-map', + lock: 'lock', + hbs: 'handlebars', + mustache: 'handlebars', + pl: 'perl', + pm: 'perl', + hx: 'haxe', + 'spec.ts': 'test-ts', + 'test.ts': 'test-ts', + 'ts.snap': 'test-ts', + 'spec.tsx': 'test-jsx', + 'test.tsx': 'test-jsx', + 'tsx.snap': 'test-jsx', + 'spec.jsx': 'test-jsx', + 'test.jsx': 'test-jsx', + 'jsx.snap': 'test-jsx', + 'spec.js': 'test-js', + 'test.js': 'test-js', + 'js.snap': 'test-js', + 'routing.ts': 'angular-routing', + 'routing.js': 'angular-routing', + 'module.ts': 'angular', + 'module.js': 'angular', + 'ng-template': 'angular', + 'component.ts': 'angular-component', + 'component.js': 'angular-component', + 'guard.ts': 'angular-guard', + 'guard.js': 'angular-guard', + 'service.ts': 'angular-service', + 'service.js': 'angular-service', + 'pipe.ts': 'angular-pipe', + 'pipe.js': 'angular-pipe', + 'filter.js': 'angular-pipe', + 'directive.ts': 'angular-directive', + 'directive.js': 'angular-directive', + 'resolver.ts': 'angular-resolver', + 'resolver.js': 'angular-resolver', + pp: 'puppet', + ex: 'elixir', + exs: 'elixir', + ls: 'livescript', + erl: 'erlang', + twig: 'twig', + jl: 'julia', + elm: 'elm', + pure: 'purescript', + tpl: 'smarty', + styl: 'stylus', + re: 'reason', + rei: 'reason', + cmj: 'bucklescript', + merlin: 'merlin', + v: 'verilog', + vhd: 'verilog', + sv: 'verilog', + svh: 'verilog', + nb: 'mathematica', + wl: 'wolframlanguage', + wls: 'wolframlanguage', + njk: 'nunjucks', + nunjucks: 'nunjucks', + robot: 'robot', + sol: 'solidity', + au3: 'autoit', + haml: 'haml', + yang: 'yang', + tf: 'terraform', + 'tf.json': 'terraform', + tfvars: 'terraform', + tfstate: 'terraform', + 'blade.php': 'laravel', + 'inky.php': 'laravel', + applescript: 'applescript', + cake: 'cake', + feature: 'cucumber', + nim: 'nim', + nimble: 'nim', + apib: 'apiblueprint', + apiblueprint: 'apiblueprint', + tag: 'riot', + vfl: 'vfl', + kl: 'kl', + pcss: 'postcss', + sss: 'postcss', + todo: 'todo', + cfml: 'coldfusion', + cfc: 'coldfusion', + lucee: 'coldfusion', + cabal: 'cabal', + nix: 'nix', + slim: 'slim', + http: 'http', + rest: 'http', + rql: 'restql', + restql: 'restql', + kv: 'kivy', + graphcool: 'graphcool', + sbt: 'sbt', + 'reducer.ts': 'ngrx-reducer', + 'rootReducer.ts': 'ngrx-reducer', + 'state.ts': 'ngrx-state', + 'actions.ts': 'ngrx-actions', + 'effects.ts': 'ngrx-effects', + cr: 'crystal', + 'drone.yml': 'drone', + cu: 'cuda', + cuh: 'cuda', + log: 'log', +}; + +const fileNameIcons = { + '.jscsrc': 'json', + '.jshintrc': 'json', + 'tsconfig.json': 'json', + 'tslint.json': 'json', + 'composer.lock': 'json', + '.jsbeautifyrc': 'json', + '.esformatter': 'json', + 'cdp.pid': 'json', + '.htaccess': 'xml', + '.jshintignore': 'settings', + '.buildignore': 'settings', + makefile: 'settings', + '.mrconfig': 'settings', + '.yardopts': 'settings', + 'gradle.properties': 'gradle', + gradlew: 'gradle', + 'gradle-wrapper.properties': 'gradle', + license: 'certificate', + 'license.md': 'certificate', + 'license.md.rendered': 'certificate', + 'license.txt': 'certificate', + licence: 'certificate', + 'licence.md': 'certificate', + 'licence.md.rendered': 'certificate', + 'licence.txt': 'certificate', + dockerfile: 'docker', + 'docker-compose.yml': 'docker', + '.mailmap': 'email', + '.gitignore': 'git', + '.gitconfig': 'git', + '.gitattributes': 'git', + '.gitmodules': 'git', + '.gitkeep': 'git', + 'git-history': 'git', + '.Rhistory': 'r', + 'cmakelists.txt': 'cmake', + 'cmakecache.txt': 'cmake', + 'angular-cli.json': 'angular', + '.angular-cli.json': 'angular', + '.vfl': 'vfl', + '.kl': 'kl', + 'postcss.config.js': 'postcss', + '.postcssrc.js': 'postcss', + 'project.graphcool': 'graphcool', + 'webpack.js': 'webpack', + 'webpack.ts': 'webpack', + 'webpack.base.js': 'webpack', + 'webpack.base.ts': 'webpack', + 'webpack.config.js': 'webpack', + 'webpack.config.ts': 'webpack', + 'webpack.common.js': 'webpack', + 'webpack.common.ts': 'webpack', + 'webpack.config.common.js': 'webpack', + 'webpack.config.common.ts': 'webpack', + 'webpack.config.common.babel.js': 'webpack', + 'webpack.config.common.babel.ts': 'webpack', + 'webpack.dev.js': 'webpack', + 'webpack.dev.ts': 'webpack', + 'webpack.config.dev.js': 'webpack', + 'webpack.config.dev.ts': 'webpack', + 'webpack.config.dev.babel.js': 'webpack', + 'webpack.config.dev.babel.ts': 'webpack', + 'webpack.prod.js': 'webpack', + 'webpack.prod.ts': 'webpack', + 'webpack.server.js': 'webpack', + 'webpack.server.ts': 'webpack', + 'webpack.client.js': 'webpack', + 'webpack.client.ts': 'webpack', + 'webpack.config.server.js': 'webpack', + 'webpack.config.server.ts': 'webpack', + 'webpack.config.client.js': 'webpack', + 'webpack.config.client.ts': 'webpack', + 'webpack.config.production.babel.js': 'webpack', + 'webpack.config.production.babel.ts': 'webpack', + 'webpack.config.prod.babel.js': 'webpack', + 'webpack.config.prod.babel.ts': 'webpack', + 'webpack.config.prod.js': 'webpack', + 'webpack.config.prod.ts': 'webpack', + 'webpack.config.production.js': 'webpack', + 'webpack.config.production.ts': 'webpack', + 'webpack.config.staging.js': 'webpack', + 'webpack.config.staging.ts': 'webpack', + 'webpack.config.babel.js': 'webpack', + 'webpack.config.babel.ts': 'webpack', + 'webpack.config.base.babel.js': 'webpack', + 'webpack.config.base.babel.ts': 'webpack', + 'webpack.config.base.js': 'webpack', + 'webpack.config.base.ts': 'webpack', + 'webpack.config.staging.babel.js': 'webpack', + 'webpack.config.staging.babel.ts': 'webpack', + 'webpack.config.coffee': 'webpack', + 'webpack.config.test.js': 'webpack', + 'webpack.config.test.ts': 'webpack', + 'webpack.config.vendor.js': 'webpack', + 'webpack.config.vendor.ts': 'webpack', + 'webpack.config.vendor.production.js': 'webpack', + 'webpack.config.vendor.production.ts': 'webpack', + 'webpack.test.js': 'webpack', + 'webpack.test.ts': 'webpack', + 'webpack.dist.js': 'webpack', + 'webpack.dist.ts': 'webpack', + 'webpackfile.js': 'webpack', + 'webpackfile.ts': 'webpack', + 'ionic.config.json': 'ionic', + '.io-config.json': 'ionic', + 'gulpfile.js': 'gulp', + 'gulpfile.ts': 'gulp', + 'gulpfile.babel.js': 'gulp', + 'package.json': 'nodejs', + 'package-lock.json': 'nodejs', + '.nvmrc': 'nodejs', + '.npmignore': 'npm', + '.npmrc': 'npm', + '.yarnrc': 'yarn', + 'yarn.lock': 'yarn', + '.yarnclean': 'yarn', + '.yarn-integrity': 'yarn', + 'yarn-error.log': 'yarn', + 'androidmanifest.xml': 'android', + '.env': 'tune', + '.env.example': 'tune', + '.babelrc': 'babel', + 'contributing.md': 'contributing', + 'contributing.md.rendered': 'contributing', + 'readme.md': 'readme', + 'readme.md.rendered': 'readme', + changelog: 'changelog', + 'changelog.md': 'changelog', + 'changelog.md.rendered': 'changelog', + CREDITS: 'credits', + 'credits.txt': 'credits', + 'credits.md': 'credits', + 'credits.md.rendered': 'credits', + '.flowconfig': 'flow', + 'favicon.ico': 'favicon', + 'karma.conf.js': 'karma', + 'karma.conf.ts': 'karma', + 'karma.conf.coffee': 'karma', + 'karma.config.js': 'karma', + 'karma.config.ts': 'karma', + 'karma-main.js': 'karma', + 'karma-main.ts': 'karma', + '.bithoundrc': 'bithound', + 'appveyor.yml': 'appveyor', + '.travis.yml': 'travis', + 'protractor.conf.js': 'protractor', + 'protractor.conf.ts': 'protractor', + 'protractor.conf.coffee': 'protractor', + 'protractor.config.js': 'protractor', + 'protractor.config.ts': 'protractor', + 'fuse.js': 'fusebox', + procfile: 'heroku', + '.editorconfig': 'editorconfig', + '.gitlab-ci.yml': 'gitlab', + '.bowerrc': 'bower', + 'bower.json': 'bower', + '.eslintrc.js': 'eslint', + '.eslintrc.yaml': 'eslint', + '.eslintrc.yml': 'eslint', + '.eslintrc.json': 'eslint', + '.eslintrc': 'eslint', + '.eslintignore': 'eslint', + 'code_of_conduct.md': 'conduct', + 'code_of_conduct.md.rendered': 'conduct', + '.watchmanconfig': 'watchman', + 'aurelia.json': 'aurelia', + 'mocha.opts': 'mocha', + jenkinsfile: 'jenkins', + 'firebase.json': 'firebase', + '.firebaserc': 'firebase', + 'rollup.config.js': 'rollup', + 'rollup.config.ts': 'rollup', + 'rollup-config.js': 'rollup', + 'rollup-config.ts': 'rollup', + 'rollup.config.prod.js': 'rollup', + 'rollup.config.prod.ts': 'rollup', + 'rollup.config.dev.js': 'rollup', + 'rollup.config.dev.ts': 'rollup', + 'rollup.config.prod.vendor.js': 'rollup', + 'rollup.config.prod.vendor.ts': 'rollup', + '.hhconfig': 'hack', + '.stylelintrc': 'stylelint', + 'stylelint.config.js': 'stylelint', + '.stylelintrc.json': 'stylelint', + '.stylelintrc.yaml': 'stylelint', + '.stylelintrc.yml': 'stylelint', + '.stylelintrc.js': 'stylelint', + '.stylelintignore': 'stylelint', + '.codeclimate.yml': 'code-climate', + '.prettierrc': 'prettier', + 'prettier.config.js': 'prettier', + '.prettierrc.js': 'prettier', + '.prettierrc.json': 'prettier', + '.prettierrc.yaml': 'prettier', + '.prettierrc.yml': 'prettier', + 'nodemon.json': 'nodemon', + '.sonarrc': 'sonar', + browserslist: 'browserlist', + '.browserslistrc': 'browserlist', + '.snyk': 'snyk', + '.drone.yml': 'drone', +}; + +export default function getIconForFile(name) { + return fileNameIcons[name] || + fileExtensionIcons[name ? name.split('.').pop() : ''] || + ''; +} diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index d305bd6acdc..2209bc0f9cf 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -45,6 +45,11 @@ export default { required: false, default: false, }, + shouldRenderTriggeredLabel: { + type: Boolean, + required: false, + default: true, + }, }, directives: { @@ -82,7 +87,12 @@ export default { {{itemName}} #{{itemId}} </strong> - triggered + <template v-if="shouldRenderTriggeredLabel"> + triggered + </template> + <template v-else> + created + </template> <timeago-tooltip :time="time" /> diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue index 4216660da8c..365229ea274 100644 --- a/app/assets/javascripts/vue_shared/components/icon.vue +++ b/app/assets/javascripts/vue_shared/components/icon.vue @@ -36,6 +36,30 @@ required: false, default: '', }, + + width: { + type: Number, + required: false, + default: null, + }, + + height: { + type: Number, + required: false, + default: null, + }, + + y: { + type: Number, + required: false, + default: null, + }, + + x: { + type: Number, + required: false, + default: null, + }, }, computed: { @@ -51,7 +75,11 @@ <template> <svg - :class="[iconSizeClass, cssClasses]"> + :class="[iconSizeClass, cssClasses]" + :width="width" + :height="height" + :x="x" + :y="y"> <use v-bind="{'xlink:href':spriteHref}"/> </svg> diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 6c575d8eb49..36d2d1dc164 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -72,7 +72,9 @@ Preview </a> </li> - <li class="md-header-toolbar"> + <li + class="md-header-toolbar" + :class="{ active: !previewMarkdown }"> <toolbar-button tag="**" button-title="Add bold text" diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.js b/app/assets/javascripts/vue_shared/components/memory_graph.js index 643b77e04c7..f37ef1a5ca3 100644 --- a/app/assets/javascripts/vue_shared/components/memory_graph.js +++ b/app/assets/javascripts/vue_shared/components/memory_graph.js @@ -1,3 +1,5 @@ +import { getTimeago } from '../../lib/utils/datetime_utility'; + export default { name: 'MemoryGraph', props: { @@ -16,7 +18,7 @@ export default { }, computed: { getFormattedMedian() { - const deployedSince = gl.utils.getTimeago().format(this.deploymentTime * 1000); + const deployedSince = getTimeago().format(this.deploymentTime * 1000); return `Deployed ${deployedSince}`; }, }, diff --git a/app/assets/javascripts/vue_shared/components/popup_dialog.vue b/app/assets/javascripts/vue_shared/components/modal.vue index 47efee64c6e..00089dfef38 100644 --- a/app/assets/javascripts/vue_shared/components/popup_dialog.vue +++ b/app/assets/javascripts/vue_shared/components/modal.vue @@ -1,8 +1,12 @@ <script> export default { - name: 'popup-dialog', + name: 'modal', props: { + id: { + type: String, + required: false, + }, title: { type: String, required: false, @@ -38,7 +42,8 @@ export default { }, primaryButtonLabel: { type: String, - required: true, + required: false, + default: '', }, submitDisabled: { type: Boolean, @@ -61,11 +66,11 @@ export default { }, methods: { - close() { - this.$emit('toggle', false); + emitCancel(event) { + this.$emit('cancel', event); }, - emitSubmit(status) { - this.$emit('submit', status); + emitSubmit(event) { + this.$emit('submit', event); }, }, }; @@ -74,7 +79,9 @@ export default { <template> <div class="modal-open"> <div - class="modal popup-dialog" + :id="id" + class="modal" + :class="id ? '' : 'show'" role="dialog" tabindex="-1" > @@ -92,7 +99,8 @@ export default { <button type="button" class="close pull-right" - @click="close" + @click="emitCancel($event)" + data-dismiss="modal" aria-label="Close" > <span aria-hidden="true">×</span> @@ -109,21 +117,27 @@ export default { type="button" class="btn pull-left" :class="btnCancelKindClass" - @click="close"> + @click="emitCancel($event)" + data-dismiss="modal"> {{ closeButtonLabel }} </button> <button + v-if="primaryButtonLabel" type="button" - class="btn pull-right" + class="btn pull-right js-primary-button" :disabled="submitDisabled" :class="btnKindClass" - @click="emitSubmit(true)"> + @click="emitSubmit($event)" + data-dismiss="modal"> {{ primaryButtonLabel }} </button> </div> </div> </div> </div> - <div class="modal-backdrop fade in" /> + <div + v-if="!id" + class="modal-backdrop fade in"> + </div> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue new file mode 100644 index 00000000000..4371534d345 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue @@ -0,0 +1,91 @@ +<script> +export default { + props: { + startSize: { + type: Number, + required: true, + }, + side: { + type: String, + required: true, + }, + minSize: { + type: Number, + required: false, + default: 0, + }, + maxSize: { + type: Number, + required: false, + default: Number.MAX_VALUE, + }, + enabled: { + type: Boolean, + required: false, + default: true, + }, + }, + data() { + return { + size: this.startSize, + }; + }, + computed: { + className() { + return `drag${this.side}`; + }, + cursorStyle() { + if (this.enabled) { + return { cursor: 'ew-resize' }; + } + return {}; + }, + }, + methods: { + resetSize(e) { + e.preventDefault(); + this.size = this.startSize; + this.$emit('update:size', this.size); + }, + startDrag(e) { + if (this.enabled) { + e.preventDefault(); + this.startPos = e.clientX; + this.currentStartSize = this.size; + document.addEventListener('mousemove', this.drag); + document.addEventListener('mouseup', this.endDrag, { once: true }); + this.$emit('resize-start', this.size); + } + }, + drag(e) { + e.preventDefault(); + let moved = e.clientX - this.startPos; + if (this.side === 'left') moved = -moved; + let newSize = this.currentStartSize + moved; + if (newSize < this.minSize) { + newSize = this.minSize; + } else if (newSize > this.maxSize) { + newSize = this.maxSize; + } + this.size = newSize; + + this.$emit('update:size', newSize); + }, + endDrag(e) { + e.preventDefault(); + document.removeEventListener('mousemove', this.drag); + this.$emit('resize-end', this.size); + }, + }, +}; +</script> + +<template> + <div + class="dragHandle" + :class="className" + :style="cursorStyle" + @mousedown="startDrag" + @dblclick="resetSize" + ></div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue new file mode 100644 index 00000000000..dce23bd65f6 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue @@ -0,0 +1,103 @@ +<script> + +/* This is a re-usable vue component for rendering a project avatar that + does not need to link to the project's profile. The image and an optional + tooltip can be configured by props passed to this component. + + Sample configuration: + + <project-avatar-image + :lazy="true" + :img-src="projectAvatarSrc" + :img-alt="tooltipText" + :tooltip-text="tooltipText" + tooltip-placement="top" + /> + +*/ + +import defaultAvatarUrl from 'images/no_avatar.png'; +import { placeholderImage } from '../../../lazy_loader'; +import tooltip from '../../directives/tooltip'; + +export default { + name: 'ProjectAvatarImage', + props: { + lazy: { + type: Boolean, + required: false, + default: false, + }, + imgSrc: { + type: String, + required: false, + default: defaultAvatarUrl, + }, + cssClasses: { + type: String, + required: false, + default: '', + }, + imgAlt: { + type: String, + required: false, + default: 'project avatar', + }, + size: { + type: Number, + required: false, + default: 20, + }, + tooltipText: { + type: String, + required: false, + default: '', + }, + tooltipPlacement: { + type: String, + required: false, + default: 'top', + }, + }, + directives: { + tooltip, + }, + computed: { + // API response sends null when gravatar is disabled and + // we provide an empty string when we use it inside project avatar link. + // In both cases we should render the defaultAvatarUrl + sanitizedSource() { + return this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc; + }, + resultantSrcAttribute() { + return this.lazy ? placeholderImage : this.sanitizedSource; + }, + tooltipContainer() { + return this.tooltipText ? 'body' : null; + }, + avatarSizeClass() { + return `s${this.size}`; + }, + }, +}; +</script> + +<template> + <img + v-tooltip + class="avatar" + :class="{ + lazy, + [avatarSizeClass]: true, + [cssClasses]: true + }" + :src="resultantSrcAttribute" + :width="size" + :height="size" + :alt="imgAlt" + :data-src="sanitizedSource" + :data-container="tooltipContainer" + :data-placement="tooltipPlacement" + :title="tooltipText" + /> +</template> diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue new file mode 100644 index 00000000000..16d60bb2876 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue @@ -0,0 +1,85 @@ +<script> +import modal from './modal.vue'; + +export default { + name: 'recaptcha-modal', + + props: { + html: { + type: String, + required: false, + default: '', + }, + }, + + data() { + return { + script: {}, + scriptSrc: 'https://www.google.com/recaptcha/api.js', + }; + }, + + components: { + modal, + }, + + methods: { + appendRecaptchaScript() { + this.removeRecaptchaScript(); + + const script = document.createElement('script'); + script.src = this.scriptSrc; + script.classList.add('js-recaptcha-script'); + script.async = true; + script.defer = true; + + this.script = script; + + document.body.appendChild(script); + }, + + removeRecaptchaScript() { + if (this.script instanceof Element) this.script.remove(); + }, + + close() { + this.removeRecaptchaScript(); + this.$emit('close'); + }, + + submit() { + this.$el.querySelector('form').submit(); + }, + }, + + watch: { + html() { + this.appendRecaptchaScript(); + }, + }, + + mounted() { + window.recaptchaDialogCallback = this.submit.bind(this); + }, +}; +</script> + +<template> +<modal + kind="warning" + class="recaptcha-modal js-recaptcha-modal" + :hide-footer="true" + :title="__('Please solve the reCAPTCHA')" + @cancel="close" +> + <div slot="body"> + <p> + {{__('We want to be sure it is you, please confirm you are not a robot.')}} + </p> + <div + ref="recaptcha" + v-html="html" + ></div> + </div> +</modal> +</template> diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index 710452bb3d3..33096b53cf8 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -122,11 +122,17 @@ export default { return items; }, + showPagination() { + return this.pageInfo.totalPages > 1; + }, }, }; </script> <template> - <div class="gl-pagination"> + <div + v-if="showPagination" + class="gl-pagination" + > <ul class="pagination clearfix"> <li v-for="item in getItems" diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue index ddc9ddbc3a3..4277d9281a0 100644 --- a/app/assets/javascripts/vue_shared/components/toggle_button.vue +++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue @@ -1,6 +1,13 @@ <script> + import { s__ } from '../../locale'; + import icon from './icon.vue'; import loadingIcon from './loading_icon.vue'; + const ICON_ON = 'status_success_borderless'; + const ICON_OFF = 'status_failed_borderless'; + const LABEL_ON = s__('ToggleButton|Toggle Status: ON'); + const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF'); + export default { props: { name: { @@ -22,19 +29,10 @@ required: false, default: false, }, - enabledText: { - type: String, - required: false, - default: 'Enabled', - }, - disabledText: { - type: String, - required: false, - default: 'Disabled', - }, }, components: { + icon, loadingIcon, }, @@ -43,6 +41,15 @@ event: 'change', }, + computed: { + toggleIcon() { + return this.value ? ICON_ON : ICON_OFF; + }, + ariaLabel() { + return this.value ? LABEL_ON : LABEL_OFF; + }, + }, + methods: { toggleFeature() { if (!this.disabledInput) this.$emit('change', !this.value); @@ -60,10 +67,8 @@ /> <button type="button" - aria-label="Toggle" class="project-feature-toggle" - :data-enabled-text="enabledText" - :data-disabled-text="disabledText" + :aria-label="ariaLabel" :class="{ 'is-checked': value, 'is-disabled': disabledInput, @@ -72,6 +77,11 @@ @click="toggleFeature" > <loadingIcon class="loading-icon" /> + <span class="toggle-icon"> + <icon + css-classes="toggle-icon-svg" + :name="toggleIcon"/> + </span> </button> </label> </template> diff --git a/app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js b/app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js new file mode 100644 index 00000000000..ff1f565e79a --- /dev/null +++ b/app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js @@ -0,0 +1,36 @@ +import recaptchaModal from '../components/recaptcha_modal.vue'; + +export default { + data() { + return { + showRecaptcha: false, + recaptchaHTML: '', + }; + }, + + components: { + recaptchaModal, + }, + + methods: { + openRecaptcha() { + this.showRecaptcha = true; + }, + + closeRecaptcha() { + this.showRecaptcha = false; + }, + + checkForSpam(data) { + if (!data.recaptcha_html) return data; + + this.recaptchaHTML = data.recaptcha_html; + + const spamError = new Error(data.error_message); + spamError.name = 'SpamError'; + spamError.message = 'SpamError'; + + throw spamError; + }, + }, +}; diff --git a/app/assets/javascripts/vue_shared/mixins/timeago.js b/app/assets/javascripts/vue_shared/mixins/timeago.js index 20f63ab663c..4e3b9d7b767 100644 --- a/app/assets/javascripts/vue_shared/mixins/timeago.js +++ b/app/assets/javascripts/vue_shared/mixins/timeago.js @@ -1,4 +1,4 @@ -import '../../lib/utils/datetime_utility'; +import { formatDate, getTimeago } from '../../lib/utils/datetime_utility'; /** * Mixin with time ago methods used in some vue components @@ -6,13 +6,13 @@ import '../../lib/utils/datetime_utility'; export default { methods: { timeFormated(time) { - const timeago = gl.utils.getTimeago(); + const timeago = getTimeago(); return timeago.format(time); }, tooltipTitle(time) { - return gl.utils.formatDate(time); + return formatDate(time); }, }, }; diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 26db2386879..077d0424093 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -71,7 +71,7 @@ vertical-align: top; &.s16 { font-size: 12px; line-height: 1.33; } - &.s24 { font-size: 14px; line-height: 1.8; } + &.s24 { font-size: 13px; line-height: 1.8; } &.s26 { font-size: 20px; line-height: 1.33; } &.s32 { font-size: 20px; line-height: 30px; } &.s40 { font-size: 16px; line-height: 38px; } diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 91976ca1f56..c5c7afe25be 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -10,7 +10,6 @@ color: $gl-text-color; font-weight: $gl-font-weight-normal; font-size: 14px; - line-height: 36px; &.diff-collapsed { padding: 5px; diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 3f630f82e29..fcc420923f9 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -4,8 +4,8 @@ padding: 1px 5px; font-size: 12px; color: $blue-500; - width: 23px; - height: 23px; + width: 24px; + height: 24px; border: 1px solid $blue-500; &:hover, diff --git a/app/assets/stylesheets/framework/contextual-sidebar.scss b/app/assets/stylesheets/framework/contextual-sidebar.scss index b73932eb7e1..1acde98c3ae 100644 --- a/app/assets/stylesheets/framework/contextual-sidebar.scss +++ b/app/assets/stylesheets/framework/contextual-sidebar.scss @@ -1,4 +1,6 @@ .page-with-contextual-sidebar { + transition: padding-left $sidebar-transition-duration; + @media (min-width: $screen-md-min) { padding-left: $contextual-sidebar-collapsed-width; } @@ -7,12 +9,6 @@ padding-left: $contextual-sidebar-width; } - // Override position: absolute - .right-sidebar { - position: fixed; - height: calc(100% - #{$header-height}); - } - .issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header { padding: 10px 0 15px; } @@ -27,8 +23,10 @@ .context-header { position: relative; margin-right: 2px; + width: $contextual-sidebar-width; a { + transition: padding $sidebar-transition-duration; font-weight: $gl-font-weight-bold; display: flex; align-items: center; @@ -63,10 +61,10 @@ } .nav-sidebar { + transition: width $sidebar-transition-duration, left $sidebar-transition-duration; position: fixed; z-index: 400; width: $contextual-sidebar-width; - transition: left $sidebar-transition-duration; top: $header-height; bottom: 0; left: 0; @@ -74,16 +72,15 @@ box-shadow: inset -2px 0 0 $border-color; transform: translate3d(0, 0, 0); - &:not(.sidebar-icons-only) { + &:not(.sidebar-collapsed-desktop) { @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { box-shadow: inset -2px 0 0 $border-color, 2px 1px 3px $dropdown-shadow-color; } } - &.sidebar-icons-only { - width: auto; - min-width: $contextual-sidebar-collapsed-width; + &.sidebar-collapsed-desktop { + width: $contextual-sidebar-collapsed-width; .nav-sidebar-inner-scroll { overflow-x: hidden; @@ -108,12 +105,11 @@ } } - &.nav-sidebar-expanded { + &.sidebar-expanded-mobile { left: 0; } a { - transition: none; text-decoration: none; } @@ -126,9 +122,10 @@ white-space: nowrap; a { + transition: padding $sidebar-transition-duration; display: flex; align-items: center; - padding: 12px 16px; + padding: 12px 15px; color: $gl-text-color-secondary; } @@ -288,7 +285,8 @@ > a { margin-left: 4px; - padding-left: 12px; + // Subtract width of left border on active element + padding-left: 11px; } .badge { @@ -313,15 +311,17 @@ .toggle-sidebar-button, .close-nav-button { width: $contextual-sidebar-width - 2px; + transition: width $sidebar-transition-duration; position: fixed; bottom: 0; - padding: 16px; + padding: $gl-padding; background-color: $gray-light; border: 0; border-top: 2px solid $border-color; color: $gl-text-color-secondary; display: flex; align-items: center; + line-height: 1; svg { margin-right: 8px; @@ -343,25 +343,22 @@ } } +.collapse-text { + white-space: nowrap; + overflow: hidden; +} -.sidebar-icons-only { +.sidebar-collapsed-desktop { .context-header { - height: 61px; + height: 60px; + width: $contextual-sidebar-collapsed-width; a { padding: 10px 4px; } } - li a { - padding: 12px 15px; - } - .sidebar-top-level-items > li { - &.active a { - padding-left: 12px; - } - .sidebar-sub-level-items { &:not(.flyout-list) { display: none; @@ -374,8 +371,8 @@ } .toggle-sidebar-button { - width: $contextual-sidebar-collapsed-width - 2px; padding: 16px; + width: $contextual-sidebar-collapsed-width - 2px; .collapse-text, .icon-angle-double-left { diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 8d83554d813..bc907a390d8 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -16,27 +16,18 @@ @mixin set-visible { transform: translateY(0); - visibility: visible; - opacity: 1; - transition-duration: 100ms, 150ms, 25ms; - transition-delay: 35ms, 50ms, 25ms; + display: block; } @mixin set-invisible { transform: translateY(-10px); - visibility: hidden; - opacity: 0; - transition-property: opacity, transform, visibility; - transition-duration: 70ms, 250ms, 250ms; - transition-timing-function: linear, $dropdown-animation-timing; - transition-delay: 25ms, 50ms, 0ms; + display: none; } .open { .dropdown-menu, .dropdown-menu-nav { @include set-visible; - display: block; min-height: 40px; @media (max-width: $screen-xs-max) { @@ -55,6 +46,11 @@ } } +// Get search dropdown to line up with other nav dropdowns +.search-input-container .dropdown-menu { + margin-top: 11px; +} + .dropdown-toggle { padding: 6px 8px 6px 10px; background-color: $white-light; @@ -143,20 +139,48 @@ } } +@mixin dropdown-item-hover { + background-color: $dropdown-item-hover-bg; + color: $gl-text-color; + outline: 0; + + // make sure the text color is not overriden + &.text-danger { + color: $brand-danger; + } + + .avatar { + border-color: $white-light; + } +} + @mixin dropdown-link { + background: transparent; + border: 0; + border-radius: 0; + box-shadow: none; display: block; + font-weight: $gl-font-weight-normal; position: relative; - padding: 5px 8px; + padding: 8px 16px; color: $gl-text-color; - line-height: initial; - border-radius: 2px; - white-space: nowrap; + line-height: normal; + white-space: normal; overflow: hidden; + text-align: left; + width: 100%; + + // make sure the text color is not overriden + &.text-danger { + color: $brand-danger; + } &:hover, + &:active, &:focus, &.is-focused { - background-color: $dropdown-link-hover-bg; + @include dropdown-item-hover; + text-decoration: none; .badge { @@ -166,6 +190,13 @@ &.dropdown-menu-user-link { line-height: 16px; + padding-top: 10px; + padding-bottom: 7px; + white-space: nowrap; + + .dropdown-menu-user-username { + display: block; + } } .icon-play { @@ -179,7 +210,6 @@ .dropdown-menu, .dropdown-menu-nav { @include set-invisible; - display: block; position: absolute; width: auto; top: 100%; @@ -187,8 +217,8 @@ z-index: 300; min-width: 240px; max-width: 500px; - margin-top: 2px; - margin-bottom: 2px; + margin-top: $dropdown-vertical-offset; + margin-bottom: 24px; font-size: 14px; font-weight: $gl-font-weight-normal; padding: 8px 0; @@ -197,6 +227,10 @@ border-radius: $border-radius-base; box-shadow: 0 2px 4px $dropdown-shadow-color; + &.dropdown-open-top { + margin-bottom: $dropdown-vertical-offset; + } + &.dropdown-open-left { right: 0; left: auto; @@ -227,16 +261,27 @@ } li { + display: block; text-align: left; list-style: none; - padding: 0 10px; + padding: 0 1px; + + a, + button, + .menu-item { + @include dropdown-link; + } } .divider { height: 1px; - margin: 6px 10px; + margin: 6px 0; padding: 0; background-color: $dropdown-divider-color; + + &:hover { + background-color: $dropdown-divider-color; + } } .separator { @@ -247,10 +292,6 @@ background-color: $dropdown-divider-color; } - a { - @include dropdown-link; - } - .dropdown-menu-empty-item a { &:hover, &:focus { @@ -262,7 +303,7 @@ color: $gl-text-color-secondary; font-size: 13px; line-height: 22px; - padding: 0 16px; + padding: 8px 16px; } &.capitalize-header .dropdown-header { @@ -277,7 +318,7 @@ .separator + .dropdown-header, .separator + .dropdown-bold-header { - padding-top: 2px; + padding-top: 10px; } .unclickable { @@ -298,48 +339,28 @@ } .dropdown-menu li { - padding: $gl-btn-padding; cursor: pointer; + &.droplab-item-active button { + @include dropdown-item-hover; + } + > a, > button { display: flex; margin: 0; - padding: 0; - border-radius: 0; text-overflow: inherit; - background-color: inherit; - color: inherit; - border: inherit; text-align: left; - &:hover, - &:focus { - background-color: inherit; - color: inherit; - } - &.btn .fa:not(:last-child) { margin-left: 5px; } } - &:hover, - &:focus { - background-color: $dropdown-hover-color; - color: $white-light; - } - &.droplab-item-selected i { visibility: visible; } - &.divider { - margin: 0 8px; - padding: 0; - border-top: $gray-darkest; - } - .icon { visibility: hidden; } @@ -431,11 +452,6 @@ } } -.dropdown-menu-user-link { - padding-top: 10px; - padding-bottom: 7px; -} - .dropdown-menu-user-full-name { display: block; font-weight: $gl-font-weight-normal; @@ -464,41 +480,44 @@ .dropdown-menu-align-right { left: auto; right: 0; - margin-top: -5px; } .dropdown-menu-selectable { - a { - padding-left: 26px; - position: relative; + li { + a { + padding: 8px 40px; + position: relative; + + &.is-indeterminate, + &.is-active { + color: $gl-text-color; + + &::before { + position: absolute; + left: 16px; + top: 16px; + transform: translateY(-50%); + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } - &.is-indeterminate, - &.is-active { - font-weight: $gl-font-weight-bold; - color: $gl-text-color; - - &::before { - position: absolute; - left: 6px; - top: 50%; - transform: translateY(-50%); - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + &.dropdown-menu-user-link { + &::before { + top: 50%; + } + } } - } - &.is-indeterminate::before { - content: "\f068"; - } + &.is-indeterminate::before { + content: "\f068"; + } - &.is-active::before { - content: "\f00c"; - position: absolute; - top: 50%; - transform: translateY(-50%); + &.is-active::before { + content: "\f00c"; + } } } } @@ -735,136 +754,6 @@ } } -@mixin dropdown-item-hover { - background-color: $dropdown-item-hover-bg; - color: $gl-text-color; -} - -// TODO: change global style and remove mixin -@mixin new-style-dropdown($selector: '') { - #{$selector}.dropdown-menu, - #{$selector}.dropdown-menu-nav { - margin-bottom: 24px; - - &.dropdown-open-top { - margin-bottom: $dropdown-vertical-offset; - } - - li { - display: block; - padding: 0 1px; - - &:hover { - background-color: transparent; - } - - &.divider { - margin: 6px 0; - - &:hover { - background-color: $dropdown-divider-color; - } - } - - &.dropdown-header { - padding: 8px 16px; - } - - &.droplab-item-active button { - @include dropdown-item-hover; - } - - a, - button, - .menu-item { - margin-bottom: 0; - border-radius: 0; - box-shadow: none; - padding: 8px 16px; - text-align: left; - white-space: normal; - width: 100%; - font-weight: $gl-font-weight-normal; - line-height: normal; - - &.dropdown-menu-user-link { - white-space: nowrap; - - .dropdown-menu-user-username { - display: block; - } - } - - // make sure the text color is not overriden - &.text-danger { - color: $brand-danger; - } - - &.is-focused, - &:hover, - &:active, - &:focus { - @include dropdown-item-hover; - - background-color: $dropdown-item-hover-bg; - color: $gl-text-color; - - // make sure the text color is not overriden - &.text-danger { - color: $brand-danger; - } - } - - &.is-active { - font-weight: inherit; - - &::before { - top: 16px; - } - - &.dropdown-menu-user-link::before { - top: 50%; - transform: translateY(-50%); - } - } - } - - &.dropdown-menu-empty-item a { - &:hover, - &:focus { - background-color: transparent; - } - } - } - - &.dropdown-menu-selectable { - li { - a { - padding: 8px 40px; - - &.is-indeterminate::before, - &.is-active::before { - left: 16px; - } - } - } - } - } - - #{$selector}.dropdown-menu-align-right { - margin-top: 2px; - } - - .open { - #{$selector}.dropdown-menu, - #{$selector}.dropdown-menu-nav { - @media (max-width: $screen-xs-max) { - max-width: 100%; - } - } - } -} - @media (max-width: $screen-xs-max) { .navbar-gitlab { li.header-projects, @@ -891,9 +780,6 @@ } } -@include new-style-dropdown('.breadcrumbs-list .dropdown '); -@include new-style-dropdown('.js-namespace-select + '); - header.header-content .dropdown-menu.projects-dropdown-menu { padding: 0; } diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 609f33582e1..1588036aeae 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -396,3 +396,8 @@ span.idiff { .file-fork-suggestion-note { margin-right: 1.5em; } + +.label-lfs { + color: $common-gray-light; + border: 1px solid $common-gray-light; +} diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index cec38eea464..2d7465401f1 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -50,8 +50,6 @@ } .filtered-search-wrapper { - @include new-style-dropdown; - display: -webkit-flex; display: flex; @@ -165,16 +163,6 @@ } } -.droplab-dropdown li.filtered-search-token { - padding: 0; - - &:hover, - &:focus { - background-color: inherit; - color: inherit; - } -} - .filtered-search-term { .name { background-color: inherit; @@ -336,21 +324,12 @@ .filtered-search-history-dropdown-content { max-height: none; -} - -.filtered-search-history-dropdown-item, -.filtered-search-history-clear-button { - @include dropdown-link; - - overflow: hidden; - width: 100%; - margin: 0.5em 0; - background-color: transparent; - border: 0; - text-align: left; - white-space: nowrap; - text-overflow: ellipsis; + .filtered-search-history-dropdown-item, + .filtered-search-history-clear-button { + white-space: nowrap; + text-overflow: ellipsis; + } } .filtered-search-history-dropdown-token { @@ -402,24 +381,9 @@ } } -%filter-dropdown-item-btn-hover { - text-decoration: none; - outline: 0; - - .avatar { - border-color: $white-light; - } -} - .droplab-dropdown .dropdown-menu .filter-dropdown-item { .btn { - border: 0; - width: 100%; - text-align: left; - padding: 8px 16px; text-overflow: ellipsis; - overflow: hidden; - border-radius: 0; .fa { width: 15px; @@ -434,11 +398,6 @@ height: 17px; top: 0; } - - &:hover, - &:focus { - @extend %filter-dropdown-item-btn-hover; - } } .dropdown-light-content { @@ -459,17 +418,9 @@ word-break: break-all; } } - - &.droplab-item-active .btn { - @extend %filter-dropdown-item-btn-hover; - } } .filter-dropdown-loading { padding: 8px 16px; text-align: center; } - -.issues-details-filters { - @include new-style-dropdown; -} diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index f985a3aea5c..ad160f37641 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -1,10 +1,4 @@ -.content-wrapper.page-with-new-nav { - margin-top: $header-height; -} - .navbar-gitlab { - @include new-style-dropdown; - &.navbar-gitlab { padding: 0 16px; z-index: 1000; @@ -522,7 +516,7 @@ .header-user { .dropdown-menu-nav { width: auto; - min-width: 140px; + min-width: 160px; margin-top: 4px; color: $gl-text-color; left: auto; diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index 78a8e57ddbb..fd5c3c81a53 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -19,6 +19,16 @@ max-width: 425px; width: 100%; } + + $image-widths: 250 306 394; + @each $width in $image-widths { + &.svg-#{$width} { + img, + svg { + width: #{$width + 'px'}; + } + } + } } @mixin svg-size($size) { diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 1537b0744cc..1d8bd26cf1a 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -24,10 +24,14 @@ font-size: $gl-font-size; line-height: 25px; - &.status-box-closed { + &.status-box-mr-closed { background-color: $gl-danger; } + &.status-box-issue-closed { + background-color: $gl-primary; + } + &.status-box-merged { background-color: $gl-primary; } diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index cb324ccc440..3f0268541a4 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -24,6 +24,7 @@ body { } .content-wrapper { + margin-top: $header-height; padding-bottom: 100px; } @@ -105,11 +106,11 @@ body { } } -.page-with-sidebar > .content-wrapper { +.layout-page > .content-wrapper { min-height: calc(100vh - #{$header-height}); } -.with-performance-bar .page-with-sidebar { +.with-performance-bar .layout-page { margin-top: $header-height + $performance-bar-height; } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index e6e6c4c3963..dcd98cb522f 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -12,6 +12,7 @@ padding: 10px 15px; min-height: 20px; border-bottom: 1px solid $list-border; + word-wrap: break-word; &::after { content: " "; @@ -125,15 +126,11 @@ ul.content-list { } .description { - p { - @include str-truncated; - margin-bottom: 0; - } + @include str-truncated; + color: $gl-text-color-secondary; } .controls { - @include new-style-dropdown; - float: right; > .control-text { @@ -316,7 +313,7 @@ ul.indent-list { border: 2px solid $white-normal; &.identicon { - line-height: 30px; + line-height: 15px; } } } @@ -350,14 +347,19 @@ ul.indent-list { .folder-caret { width: 15px; + + svg { + margin-bottom: 2px; + } } .item-type-icon { + margin-top: 2px; width: 20px; } > .group-row:not(.has-children) { - .folder-caret .fa { + .folder-caret { opacity: 0; } } @@ -440,12 +442,61 @@ ul.indent-list { .avatar-container > a { width: 100%; + text-decoration: none; } &.has-more-items { display: block; padding: 20px 10px; } + + .stats { + position: relative; + line-height: 46px; + + > span { + display: inline-flex; + align-items: center; + height: 16px; + min-width: 30px; + } + + > span:last-child { + margin-right: 0; + } + + .stat-value { + margin: 2px 0 0 5px; + } + } + + .controls { + margin-left: 5px; + + > .btn { + margin-right: $btn-xs-side-margin; + } + } + } + + .project-row-contents .stats { + line-height: inherit; + + > span:first-child { + margin-left: 25px; + } + + .item-visibility { + margin-right: 0; + } + + .last-updated { + position: absolute; + right: 12px; + min-width: 250px; + text-align: right; + color: $gl-text-color-secondary; + } } } @@ -457,12 +508,12 @@ ul.indent-list { ul.group-list-tree { li.group-row { - &.has-description .title { - line-height: inherit; + > .group-row-contents .title { + line-height: $list-text-height; } - &:not(.has-description) .title { - line-height: $list-text-height; + &.has-description > .group-row-contents .title { + line-height: inherit; } } } diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index 5389eb0a5f2..938f5f49c09 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -74,7 +74,7 @@ } .md-header-tab { - @media(max-width: $screen-xs-max) { + @media (max-width: $screen-xs-max) { flex: 1; width: 100%; border-bottom: 1px solid $border-color; @@ -82,16 +82,23 @@ } } -.md-header-toolbar { - margin-left: auto; +.nav-links { + li.md-header-toolbar { + margin-left: auto; + display: none; - @media(max-width: $screen-xs-max) { - flex: none; - display: flex; - justify-content: center; - width: 100%; - padding-top: $gl-padding-top; - padding-bottom: $gl-padding-top; + &.active { + display: block; + + @media (max-width: $screen-xs-max) { + flex: none; + display: flex; + justify-content: center; + width: 100%; + padding-top: $gl-padding-top; + padding-bottom: $gl-padding-top; + } + } } } @@ -175,7 +182,7 @@ margin-left: $gl-padding; margin-right: -5px; - @media(max-width: $screen-xs-max) { + @media (max-width: $screen-xs-max) { margin-left: 0; margin-right: 0; } @@ -185,6 +192,17 @@ overflow-y: auto; overflow-x: hidden; + .name, + small.aliases, + small.params { + float: left; + } + + small.aliases, + small.params { + padding: 2px 5px; + } + small.description { float: right; padding: 3px 5px; @@ -202,6 +220,7 @@ } ul > li { + @include clearfix; white-space: nowrap; } @@ -239,7 +258,7 @@ } } -@media(max-width: $screen-xs-max) { +@media (max-width: $screen-xs-max) { .atwho-view-ul { width: 350px; } diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 600a1f53b58..a12f28efce6 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -111,21 +111,4 @@ aside:not(.right-sidebar) { display: none; } - - .show-aside { - display: block !important; - } -} - -.show-aside { - display: none; - position: fixed; - right: 0; - top: 30%; - padding: 5px 15px; - background: $show-aside-bg; - font-size: 20px; - color: $show-aside-color; - z-index: 100; - box-shadow: 0 1px 2px $show-aside-shadow; } diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 5c9838c1029..1be66d0ab21 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -44,7 +44,21 @@ body.modal-open { } } -.modal.popup-dialog { - display: block; +.modal { + background-color: $black-transparent; + z-index: 2100; + + @media (min-width: $screen-md-min) { + .modal-dialog { + margin: 30px auto; + } + } } +.recaptcha-modal .recaptcha-form { + display: inline-block; + + .recaptcha { + margin: 0; + } +} diff --git a/app/assets/stylesheets/framework/secondary-navigation-elements.scss b/app/assets/stylesheets/framework/secondary-navigation-elements.scss index 8498b37abe4..5f67126bafa 100644 --- a/app/assets/stylesheets/framework/secondary-navigation-elements.scss +++ b/app/assets/stylesheets/framework/secondary-navigation-elements.scss @@ -86,8 +86,6 @@ } .nav-controls { - @include new-style-dropdown; - display: inline-block; float: right; text-align: right; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 792981fdc48..d61809cb0a4 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -50,6 +50,11 @@ &:not(.disabled) { cursor: pointer; } + + svg { + width: $gl-padding; + height: $gl-padding; + } } } @@ -85,11 +90,6 @@ .right-sidebar { border-left: 1px solid $border-color; height: calc(100% - #{$header-height}); - - &.affix { - position: fixed; - top: $header-height; - } } .with-performance-bar .right-sidebar.affix { @@ -139,10 +139,6 @@ } } -.issuable-sidebar { - @include new-style-dropdown; -} - .pikaday-container { .pika-single { margin-top: 2px; diff --git a/app/assets/stylesheets/framework/toggle.scss b/app/assets/stylesheets/framework/toggle.scss index 71765da3908..0cd83df218f 100644 --- a/app/assets/stylesheets/framework/toggle.scss +++ b/app/assets/stylesheets/framework/toggle.scss @@ -27,7 +27,7 @@ border: 0; outline: 0; display: block; - width: 100px; + width: 50px; height: 24px; cursor: pointer; user-select: none; @@ -42,31 +42,31 @@ background: none; } - &::before { - color: $feature-toggle-text-color; - font-size: 12px; - line-height: 24px; - position: absolute; - top: 0; - left: 25px; - right: 5px; - text-align: center; - overflow: hidden; - text-overflow: ellipsis; - animation: animate-disabled .2s ease-in; - content: attr(data-disabled-text); - } - - &::after { + .toggle-icon { position: relative; display: block; - content: ""; - width: 22px; - height: 18px; left: 0; border-radius: 9px; background: $feature-toggle-color; transition: all .2s ease; + + &, + .toggle-icon-svg { + width: 18px; + height: 18px; + } + + .toggle-icon-svg { + fill: $feature-toggle-color-disabled; + } + + .toggle-status-checked { + display: none; + } + + .toggle-status-unchecked { + display: inline; + } } .loading-icon { @@ -77,11 +77,10 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - } &.is-loading { - &::before { + .toggle-icon { display: none; } @@ -100,15 +99,20 @@ &.is-checked { background: $feature-toggle-color-enabled; - &::before { - left: 5px; - right: 25px; - animation: animate-enabled .2s ease-in; - content: attr(data-enabled-text); - } + .toggle-icon { + left: calc(100% - 18px); - &::after { - left: calc(100% - 22px); + .toggle-icon-svg { + fill: $feature-toggle-color-enabled; + } + + .toggle-status-checked { + display: inline; + } + + .toggle-status-unchecked { + display: none; + } } } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 0817cce114c..d0999e60e65 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -178,6 +178,10 @@ font-weight: inherit; } + dd { + margin-left: $gl-padding; + } + ul, ol { padding: 0; @@ -343,8 +347,6 @@ a > code { @extend .ref-name; } -@include new-style-dropdown('.git-revision-dropdown'); - /** * Apply Markdown typography * diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 4f99c27eff1..f7853909f56 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -5,10 +5,9 @@ $grid-size: 8px; $gutter_collapsed_width: 62px; $gutter_width: 290px; $gutter_inner_width: 250px; -$sidebar-transition-duration: .15s; +$sidebar-transition-duration: .3s; $sidebar-breakpoint: 1024px; $default-transition-duration: .15s; -$right-sidebar-transition-duration: .3s; $contextual-sidebar-width: 220px; $contextual-sidebar-collapsed-width: 50px; @@ -220,6 +219,7 @@ $gl-input-padding: 10px; $gl-vert-padding: 6px; $gl-padding-top: 10px; $gl-sidebar-padding: 22px; +$gl-bar-padding: 3px; /* * Misc @@ -246,9 +246,6 @@ $btn-sm-side-margin: 7px; $btn-xs-side-margin: 5px; $issue-status-expired: $orange-500; $issuable-sidebar-color: $gl-text-color-secondary; -$show-aside-bg: #eee; -$show-aside-color: #777; -$show-aside-shadow: #ddd; $group-path-color: #999; $namespace-kind-color: #aaa; $panel-heading-link-color: #777; @@ -722,7 +719,7 @@ $issuable-warning-icon-margin: 4px; Image Commenting cursor */ $image-comment-cursor-left-offset: 12; -$image-comment-cursor-top-offset: 30; +$image-comment-cursor-top-offset: 12; /* Popup @@ -730,3 +727,8 @@ Popup $popup-triangle-size: 15px; $popup-triangle-border-size: 1px; $popup-box-shadow-color: rgba(90, 90, 90, 0.05); + +/* +Multi file editor +*/ +$border-color-settings: #e1e1e1; diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 735fc4babd7..2f3a80daa90 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -16,6 +16,10 @@ .commit-sha, .commit-info { margin-left: 4px; + + .fork-svg { + margin-right: 4px; + } } .ref-name { @@ -79,7 +83,7 @@ } .limit-icon { - margin: 0 8px; + margin: 0 4px; } .limit-message { diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 3683afa07de..2803144ef1d 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -57,7 +57,7 @@ position: relative; @media (min-width: $screen-sm-min) { - transition: width $right-sidebar-transition-duration; + transition: width $sidebar-transition-duration; width: 100%; &.is-compact { @@ -415,7 +415,7 @@ margin: 5px; } -.page-with-contextual-sidebar.page-with-sidebar .issue-boards-sidebar { +.page-with-contextual-sidebar.layout-page .issue-boards-sidebar { .issuable-sidebar-header { position: relative; } @@ -453,8 +453,8 @@ .right-sidebar.right-sidebar-expanded { &.boards-sidebar-slide-enter-active, &.boards-sidebar-slide-leave-active { - transition: width $right-sidebar-transition-duration, - padding $right-sidebar-transition-duration; + transition: width $sidebar-transition-duration, + padding $sidebar-transition-duration; } &.boards-sidebar-slide-enter, diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index f139f4ab650..98d460339cd 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -323,8 +323,6 @@ } .build-dropdown { - @include new-style-dropdown; - margin: $gl-padding 0; padding: 0; diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss index c303f016ff9..7b8ee026357 100644 --- a/app/assets/stylesheets/pages/clusters.scss +++ b/app/assets/stylesheets/pages/clusters.scss @@ -6,15 +6,13 @@ .cluster-applications-table { // Wait for the Vue to kick-in and render the applications block - min-height: 302px; + min-height: 400px; } .clusters-dropdown-menu { max-width: 100%; } -@include new-style-dropdown('.clusters-dropdown '); - .clusters-container { .nav-bar-right { padding: $gl-padding-top $gl-padding; diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index 292e0ad394b..3b35beb7695 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -1,6 +1,4 @@ #cycle-analytics { - @include new-style-dropdown; - max-width: 1000px; margin: 24px auto 0; position: relative; diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 52e4d904b9b..2f2c04206e2 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -32,8 +32,6 @@ } .detail-page-header-actions { - @include new-style-dropdown; - align-self: center; flex-shrink: 0; flex: 0 0 auto; diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 848d7f144dc..60b07537799 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -581,8 +581,6 @@ } .commit-stat-summary { - @include new-style-dropdown; - @media (min-width: $screen-sm-min) { margin-left: -$gl-padding; padding-left: $gl-padding; @@ -732,18 +730,18 @@ .frame.click-to-comment { position: relative; - cursor: image-url('icon_image_comment.svg') + cursor: image-url('illustrations/image_comment_light_cursor.svg') $image-comment-cursor-left-offset $image-comment-cursor-top-offset, auto; // Retina cursor - cursor: -webkit-image-set(image-url('icon_image_comment.svg') 1x, image-url('icon_image_comment@2x.svg') 2x) + cursor: -webkit-image-set(image-url('illustrations/image_comment_light_cursor.svg') 1x, image-url('illustrations/image_comment_light_cursor@2x.svg') 2x) $image-comment-cursor-left-offset $image-comment-cursor-top-offset, auto; .comment-indicator { position: absolute; padding: 0; width: (2px * $image-comment-cursor-left-offset); - height: (1px * $image-comment-cursor-top-offset); + height: (2px * $image-comment-cursor-top-offset); // center the indicator to match the top left click region margin-top: (-1px * $image-comment-cursor-top-offset) + 2; margin-left: (-1px * $image-comment-cursor-left-offset) + 1; @@ -778,15 +776,20 @@ .frame .badge, .frame .image-comment-badge { // Center align badges on the frame - transform: translate3d(-50%, -50%, 0); + transform: translate(-50%, -50%); } .image-comment-badge { - @include btn-comment-icon; position: absolute; + width: 24px; + height: 24px; + padding: 0; + background: none; + border: 0; - &.inverted { - border-color: $white-light; + > svg { + width: 100%; + height: 100%; } } diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index c586dab4cf2..8ecda50602d 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -204,8 +204,6 @@ .gitlab-ci-yml-selector, .dockerfile-selector, .template-type-selector { - @include new-style-dropdown; - display: inline-block; vertical-align: top; font-family: $regular_font; diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index b0795353ec1..794bc668562 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -12,8 +12,6 @@ .environments-container { .ci-table { - @include new-style-dropdown; - .deployment-column { > span { word-break: break-all; @@ -201,8 +199,9 @@ stroke-width: 1; } -.deploy-info-text { - dominant-baseline: text-before-edge; +.divider-line { + stroke-width: 1; + stroke: $gray-darkest; } .prometheus-state { @@ -249,6 +248,73 @@ } } +.prometheus-graph-cursor { + position: absolute; + background: $theme-gray-600; + width: 1px; +} + +.prometheus-graph-flag { + display: block; + min-width: 160px; + + h5 { + padding: 0; + margin: 0; + font-size: 14px; + line-height: 1.2; + } + + table { + border-collapse: collapse; + padding: 0; + margin: 0; + } + + td { + vertical-align: middle; + + + td { + padding-left: 5px; + vertical-align: top; + } + } + + .deploy-meta-content { + border-bottom: 1px solid $white-dark; + + svg { + height: 15px; + vertical-align: bottom; + } + } + + &.popover { + &.left { + left: auto; + right: 0; + margin-right: 10px; + } + + &.right { + left: 0; + right: auto; + margin-left: 10px; + } + + > .arrow { + top: 40px; + } + + > .popover-title, + > .popover-content { + padding: 5px 8px; + font-size: 12px; + white-space: nowrap; + } + } +} + .prometheus-svg-container { position: relative; height: 0; @@ -312,6 +378,20 @@ stroke: $gray-darker; } + .deploy-info-text { + dominant-baseline: text-before-edge; + font-size: 12px; + } + + .deploy-info-text-link { + font-family: $monospace_font; + fill: $gl-link-color; + + &:hover { + fill: $gl-link-hover-color; + } + } + @media (max-width: $screen-sm-max) { .label-axis-text, .text-metric-usage, diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index eea8b7dd193..da096354b5a 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -159,7 +159,6 @@ } } - /* * Last push widget */ @@ -182,6 +181,12 @@ .event-item { padding-left: 0; + &.event-inline { + .event-title { + line-height: 20px; + } + } + .event-title { white-space: normal; overflow: visible; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 11ee1232bfe..e1637618ab2 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -122,11 +122,11 @@ } .right-sidebar { - position: absolute; + position: fixed; top: $header-height; bottom: 0; right: 0; - transition: width $right-sidebar-transition-duration; + transition: width $sidebar-transition-duration; background: $gray-light; z-index: 200; overflow: hidden; @@ -470,7 +470,8 @@ } } - .milestone-title span { + .milestone-title span, + .collapse-truncated-title { @include str-truncated(100%); display: block; margin: 0 4px; @@ -487,12 +488,6 @@ } } - .dropdown-content { - a:hover { - color: inherit; - } - } - .dropdown-menu-toggle { width: 100%; padding-top: 6px; @@ -507,14 +502,10 @@ top: $header-height + $performance-bar-height; .issuable-sidebar { - height: calc(100% - #{$header-height} - #{$performance-bar-height}); + height: calc(100% - #{$performance-bar-height}); } } -.sidebar-move-issue-dropdown { - @include new-style-dropdown; -} - .sidebar-move-issue-confirmation-button { width: 100%; @@ -619,11 +610,19 @@ } .issuable-status-box { - float: none; - display: inline-block; + align-self: stretch; + display: flex; + justify-content: center; + align-items: center; margin-top: 0; - height: auto; - align-self: center; + padding-left: 9px; + padding-right: 9px; + + @media (min-width: $screen-sm-min) { + display: inline-block; + height: auto; + align-self: center; + } } .issuable-gutter-toggle { diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index af1df8b8802..c48e58af691 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -142,8 +142,6 @@ ul.related-merge-requests > li { } .issue-form { - @include new-style-dropdown; - .select2-container { width: 250px !important; } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 92abe82df4c..e8cd8a4905c 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -116,8 +116,6 @@ } .manage-labels-list { - @include new-style-dropdown; - > li:not(.empty-message):not(.is-not-draggable) { background-color: $white-light; cursor: move; diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 18c48405ecd..3422829de58 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -58,8 +58,6 @@ } .member-form-control { - @include new-style-dropdown; - @media (max-width: $screen-xs-max) { padding-bottom: 5px; margin-left: 0; @@ -73,8 +71,6 @@ } .member-search-form { - @include new-style-dropdown; - position: relative; @media (min-width: $screen-sm-min) { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 2afb17334e3..f887a11004f 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -485,8 +485,6 @@ } .mr-source-target { - @include new-style-dropdown; - display: flex; flex-wrap: wrap; justify-content: space-between; @@ -608,8 +606,6 @@ } .mr-version-controls { - @include new-style-dropdown; - position: relative; background: $gray-light; color: $gl-text-color; @@ -728,6 +724,6 @@ } } -.merge-request-form { - @include new-style-dropdown; +.fork-sprite { + margin-right: -5px; } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index ebb5d121433..6d4ccd53e12 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -23,8 +23,6 @@ .new-note, .note-edit-form { .note-form-actions { - @include new-style-dropdown; - position: relative; margin: $gl-padding 0 0; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 4d5613c292b..26e6e8688b6 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -490,8 +490,6 @@ ul.notes { } .note-actions { - @include new-style-dropdown; - align-self: flex-start; flex-shrink: 0; display: inline-flex; diff --git a/app/assets/stylesheets/pages/notifications.scss b/app/assets/stylesheets/pages/notifications.scss index c28b1e68008..bdf07a99daf 100644 --- a/app/assets/stylesheets/pages/notifications.scss +++ b/app/assets/stylesheets/pages/notifications.scss @@ -14,7 +14,3 @@ font-size: 18px; } } - -.notification-form { - @include new-style-dropdown; -} diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss index 7e2297c283f..b698a4f9afa 100644 --- a/app/assets/stylesheets/pages/pipeline_schedules.scss +++ b/app/assets/stylesheets/pages/pipeline_schedules.scss @@ -39,6 +39,10 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + + svg { + vertical-align: middle; + } } .next-run-cell { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index cb24274c612..05c1033c5f7 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -143,6 +143,12 @@ fill: $gl-text-color-secondary; } + .sprite { + width: 12px; + height: 12px; + fill: $gl-text-color; + } + .fa { font-size: 12px; color: $gl-text-color; @@ -286,8 +292,6 @@ // Pipeline visualization .pipeline-actions { - @include new-style-dropdown; - border-bottom: 0; } @@ -703,9 +707,6 @@ button.mini-pipeline-graph-dropdown-toggle { } } -@include new-style-dropdown('.big-pipeline-graph-dropdown-menu'); -@include new-style-dropdown('.mini-pipeline-graph-dropdown-menu'); - // dropdown content for big and mini pipeline .big-pipeline-graph-dropdown-menu, .mini-pipeline-graph-dropdown-menu { @@ -804,7 +805,6 @@ button.mini-pipeline-graph-dropdown-toggle { font-weight: normal; line-height: $line-height-base; white-space: nowrap; - border-radius: 3px; .ci-job-name-component { align-items: center; diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss index c197494b152..68d40b56133 100644 --- a/app/assets/stylesheets/pages/profiles/preferences.scss +++ b/app/assets/stylesheets/pages/profiles/preferences.scss @@ -20,6 +20,22 @@ } } +.multi-file-editor-options { + label { + margin-right: 20px; + text-align: center; + } + + .preview { + font-size: 0; + + img { + border: 1px solid $border-color-settings; + border-radius: 4px; + } + } +} + .application-theme { label { margin-right: 20px; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 674588752d2..6f4c678c4b8 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -323,8 +323,6 @@ } .project-repo-buttons { - @include new-style-dropdown; - .project-action-button .dropdown-menu { max-height: 250px; overflow-y: auto; @@ -898,8 +896,6 @@ pre.light-well { .new-protected-branch, .new-protected-tag { - @include new-style-dropdown; - label { margin-top: 6px; font-weight: $gl-font-weight-normal; @@ -919,8 +915,6 @@ pre.light-well { .protected-branches-list, .protected-tags-list { - @include new-style-dropdown; - margin-bottom: 30px; .settings-message { diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 402412eae71..6cb32408a48 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -1,16 +1,3 @@ -.modal.popup-dialog { - display: block; - background-color: $black-transparent; - z-index: 2100; - - @media (min-width: $screen-md-min) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - } -} - .project-refs-form, .project-refs-target-form { display: inline-block; @@ -35,9 +22,10 @@ } } -.multi-file { +.ide-view { display: flex; - height: calc(100vh - 145px); + height: calc(100vh - #{$header-height}); + color: $almost-black; border-top: 1px solid $white-dark; border-bottom: 1px solid $white-dark; @@ -50,10 +38,41 @@ .ide-file-list { flex: 1; - overflow: scroll; .file { cursor: pointer; + + &.file-open { + background: $white-normal; + } + + .repo-file-name { + white-space: nowrap; + text-overflow: ellipsis; + } + + .unsaved-icon { + color: $indigo-700; + float: right; + font-size: smaller; + line-height: 20px; + } + + .repo-new-btn { + display: none; + margin-top: -4px; + margin-bottom: -4px; + } + + &:hover { + .repo-new-btn { + display: block; + } + + .unsaved-icon { + display: none; + } + } } a { @@ -68,17 +87,36 @@ .multi-file-table-name, .multi-file-table-col-commit-message { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + overflow: visible; max-width: 0; + padding: 6px 12px; } -.multi-file-table-name { +.multi-file-loading-container { + margin-top: 10px; + padding: 10px; + + .animation-container { + background: $gray-light; + + div { + background: $gray-light; + } + } +} + +table.table tr td.multi-file-table-name { width: 350px; + padding: 6px 12px; + + svg { + vertical-align: middle; + margin-right: 2px; + } } .multi-file-table-col-commit-message { + white-space: nowrap; width: 50%; } @@ -92,7 +130,7 @@ .multi-file-tabs { display: flex; - overflow: scroll; + overflow-x: auto; background-color: $white-normal; box-shadow: inset 0 -1px $white-dark; @@ -109,6 +147,10 @@ border-bottom: 1px solid $white-dark; cursor: pointer; + svg { + vertical-align: middle; + } + &.active { background-color: $white-light; border-bottom-color: $white-light; @@ -141,9 +183,38 @@ height: 0; } +.blob-editor-container { + flex: 1; + height: 0; + display: flex; + flex-direction: column; + justify-content: center; + + .vertical-center { + min-height: auto; + } +} + +.multi-file-editor-holder { + height: 100%; +} + .multi-file-editor-btn-group { - padding: $grid-size; + padding: $gl-bar-padding $gl-padding; border-top: 1px solid $white-dark; + border-bottom: 1px solid $white-dark; + background: $white-light; +} + +.ide-status-bar { + padding: $gl-bar-padding $gl-padding; + background: $white-light; + display: flex; + justify-content: space-between; + + svg { + vertical-align: middle; + } } // Not great, but this is to deal with our current output @@ -151,10 +222,6 @@ height: 100%; overflow: scroll; - .blob-viewer { - height: 100%; - } - .file-content.code { display: flex; @@ -175,18 +242,102 @@ } } +.file-content.blob-no-preview { + a { + margin-left: auto; + margin-right: auto; + } +} + .multi-file-commit-panel { display: flex; + position: relative; flex-direction: column; height: 100%; width: 290px; - padding: $gl-padding; + padding: 0; background-color: $gray-light; - border-left: 1px solid $white-dark; + padding-right: 3px; + + .projects-sidebar { + display: flex; + flex-direction: column; + } + + .multi-file-commit-panel-inner { + display: flex; + flex: 1; + flex-direction: column; + } + + .multi-file-commit-panel-inner-scroll { + display: flex; + flex: 1; + flex-direction: column; + overflow: auto; + } &.is-collapsed { width: 60px; - padding: 0; + + .multi-file-commit-list { + padding-top: $gl-padding; + overflow: hidden; + } + + .multi-file-context-bar-icon { + align-items: center; + + svg { + float: none; + margin: 0; + } + } + } + + .branch-container { + border-left: 4px solid $indigo-700; + margin-bottom: $gl-bar-padding; + } + + .branch-header { + background: $white-dark; + display: flex; + } + + .branch-header-title { + flex: 1; + padding: $grid-size $gl-padding; + color: $indigo-700; + font-weight: $gl-font-weight-bold; + + svg { + vertical-align: middle; + } + } + + .branch-header-btns { + padding: $gl-vert-padding $gl-padding; + } + + .left-collapse-btn { + display: none; + background: $gray-light; + text-align: left; + border-top: 1px solid $white-dark; + + svg { + vertical-align: middle; + } + } +} + +.multi-file-context-bar-icon { + padding: 10px; + + svg { + margin-right: 10px; + float: left; } } @@ -199,9 +350,9 @@ .multi-file-commit-panel-header { display: flex; align-items: center; - padding: 0 0 12px; margin-bottom: 12px; border-bottom: 1px solid $white-dark; + padding: $gl-btn-padding 0; &.is-collapsed { border-bottom: 1px solid $white-dark; @@ -210,23 +361,33 @@ margin-left: auto; margin-right: auto; } + + .multi-file-commit-panel-collapse-btn { + margin-right: auto; + margin-left: auto; + border-left: 0; + } } } -.multi-file-commit-panel-collapse-btn { - padding-top: 0; - padding-bottom: 0; - margin-left: auto; - font-size: 20px; +.multi-file-commit-panel-header-title { + display: flex; + flex: 1; + padding: $gl-btn-padding; - &.is-collapsed { - margin-right: auto; + svg { + margin-right: $gl-btn-padding; } } +.multi-file-commit-panel-collapse-btn { + border-left: 1px solid $white-dark; +} + .multi-file-commit-list { flex: 1; - overflow: scroll; + overflow: auto; + padding: $gl-padding; } .multi-file-commit-list-item { @@ -257,7 +418,7 @@ } .multi-file-commit-form { - padding-top: 12px; + padding: $gl-padding; border-top: 1px solid $white-dark; } @@ -308,3 +469,67 @@ } } } + +.ide-loading { + display: flex; + height: 100vh; + align-items: center; + justify-content: center; +} + +.ide-empty-state { + display: flex; + height: 100vh; + align-items: center; + justify-content: center; +} + +.repo-new-btn { + .dropdown-toggle svg { + margin-top: -2px; + margin-bottom: 2px; + } + + .dropdown-menu { + left: auto; + right: 0; + + label { + font-weight: $gl-font-weight-normal; + padding: 5px 8px; + margin-bottom: 0; + } + } +} + +.ide-flash-container.flash-container { + margin-top: $header-height; + margin-bottom: 0; +} + +.with-performance-bar { + .ide-flash-container.flash-container { + margin-top: $header-height + $performance-bar-height; + } + + .ide-view { + height: calc(100vh - #{$header-height + $performance-bar-height}); + } +} + + +.dragHandle { + position: absolute; + top: 0; + bottom: 0; + width: 3px; + background-color: $white-dark; + + &.dragright { + right: 0; + } + + &.dragleft { + left: 0; + } +} diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index fe455a04960..c9363188505 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -108,19 +108,7 @@ input[type="checkbox"]:hover { // Custom dropdown positioning .dropdown-menu { - transition-property: opacity, transform; - transition-duration: 250ms, 250ms; - transition-delay: 0ms, 25ms; - transition-timing-function: $dropdown-animation-timing; - transform: translateY(0); - opacity: 0; - display: block; left: -5px; - padding: 0; - - ul { - padding: 10px 0; - } } .dropdown-content { @@ -157,13 +145,6 @@ input[type="checkbox"]:hover { background-color: $nav-badge-bg; border-color: $border-color; } - - .dropdown-menu { - transition-duration: 100ms, 75ms; - transition-delay: 75ms, 100ms; - transform: translateY(7px); - opacity: 1; - } } &.has-value { @@ -185,8 +166,6 @@ input[type="checkbox"]:hover { } .search-holder { - @include new-style-dropdown; - @media (min-width: $screen-sm-min) { display: -webkit-flex; display: flex; diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 5d630c7d61e..6353482ede7 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -268,3 +268,7 @@ margin: 0 0 5px 17px; } } + +.deprecated-service { + cursor: default; +} diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss index cede147d559..8e2c42c1bd3 100644 --- a/app/assets/stylesheets/pages/stat_graph.scss +++ b/app/assets/stylesheets/pages/stat_graph.scss @@ -10,7 +10,6 @@ } .axis { - fill: $stat-graph-axis-fill; font-size: 10px; } @@ -54,9 +53,7 @@ } .selection rect { - fill: $stat-graph-selection-fill; fill-opacity: 0.1; - stroke: $stat-graph-selection-stroke; stroke-width: 1px; stroke-opacity: 0.4; shape-rendering: crispedges; diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 2139a029fc7..a79772ea37b 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -265,7 +265,3 @@ font-weight: $gl-font-weight-bold; } } - -.todos-filters { - @include new-style-dropdown; -} diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 5d14323e4bc..e0ee7e9aa3d 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -1,6 +1,4 @@ .tree-holder { - @include new-style-dropdown; - .nav-block { margin: 10px 0; diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 2ce26de1768..a94726887d9 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -1,4 +1,6 @@ class Admin::GroupsController < Admin::ApplicationController + include MembersPresentation + before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update] def index @@ -10,8 +12,10 @@ class Admin::GroupsController < Admin::ApplicationController def show @group = Group.with_statistics.joins(:route).group('routes.path').find_by_full_path(params[:id]) - @members = @group.members.order("access_level DESC").page(params[:members_page]) - @requesters = AccessRequestsFinder.new(@group).execute(current_user) + @members = present_members( + @group.members.order("access_level DESC").page(params[:members_page])) + @requesters = present_members( + AccessRequestsFinder.new(@group).execute(current_user)) @projects = @group.projects.with_statistics.page(params[:projects_page]) end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 50cf2643390..3afe66c3566 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,4 +1,6 @@ class Admin::ProjectsController < Admin::ApplicationController + include MembersPresentation + before_action :project, only: [:show, :transfer, :repository_check] before_action :group, only: [:show, :transfer] @@ -19,11 +21,14 @@ class Admin::ProjectsController < Admin::ApplicationController def show if @group - @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]) + @group_members = present_members( + @group.members.order("access_level DESC").page(params[:group_members_page])) end - @project_members = @project.members.page(params[:project_members_page]) - @requesters = AccessRequestsFinder.new(@project).execute(current_user) + @project_members = present_members( + @project.members.page(params[:project_members_page])) + @requesters = present_members( + AccessRequestsFinder.new(@project).execute(current_user)) end def transfer diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index cde1e284d2d..86bade49ec9 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -8,12 +8,12 @@ class AutocompleteController < ApplicationController def users @users = AutocompleteUsersFinder.new(params: params, current_user: current_user, project: @project, group: @group).execute - render json: @users, only: [:name, :username, :id], methods: [:avatar_url] + render json: UserSerializer.new.represent(@users) end def user @user = User.find(params[:id]) - render json: @user, only: [:name, :username, :id], methods: [:avatar_url] + render json: UserSerializer.new.represent(@user) end def projects diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb index 2c9c095a5d7..a145049dc7d 100644 --- a/app/controllers/concerns/boards_responses.rb +++ b/app/controllers/concerns/boards_responses.rb @@ -24,11 +24,11 @@ module BoardsResponses end def respond_with_boards - respond_with(@boards) + respond_with(@boards) # rubocop:disable Gitlab/ModuleWithInstanceVariables end def respond_with_board - respond_with(@board) + respond_with(@board) # rubocop:disable Gitlab/ModuleWithInstanceVariables end def respond_with(resource) diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb index 782f0be9c4a..6f4fdcdaa4f 100644 --- a/app/controllers/concerns/creates_commit.rb +++ b/app/controllers/concerns/creates_commit.rb @@ -1,6 +1,8 @@ module CreatesCommit extend ActiveSupport::Concern + include Gitlab::Utils::StrongMemoize + # rubocop:disable Gitlab/ModuleWithInstanceVariables def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil) if can?(current_user, :push_code, @project) @project_to_commit_into = @project @@ -45,6 +47,7 @@ module CreatesCommit end end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def authorize_edit_tree! return if can_collaborate_with_project? @@ -77,6 +80,7 @@ module CreatesCommit end end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def new_merge_request_path project_new_merge_request_path( @project_to_commit_into, @@ -88,20 +92,28 @@ module CreatesCommit } ) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def existing_merge_request_path - project_merge_request_path(@project, @merge_request) + project_merge_request_path(@project, @merge_request) # rubocop:disable Gitlab/ModuleWithInstanceVariables end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def merge_request_exists? - return @merge_request if defined?(@merge_request) - - @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened - .find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch) + strong_memoize(:merge_request) do + MergeRequestsFinder.new(current_user, project_id: @project.id) + .execute + .opened + .find_by( + source_project_id: @project_to_commit_into, + source_branch: @branch_name, + target_branch: @start_branch) + end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def different_project? - @project_to_commit_into != @project + @project_to_commit_into != @project # rubocop:disable Gitlab/ModuleWithInstanceVariables end def create_merge_request? @@ -109,6 +121,6 @@ module CreatesCommit # as the target branch in the same project, # we don't want to create a merge request. params[:create_merge_request].present? && - (different_project? || @start_branch != @branch_name) + (different_project? || @start_branch != @branch_name) # rubocop:disable Gitlab/ModuleWithInstanceVariables end end diff --git a/app/controllers/concerns/group_tree.rb b/app/controllers/concerns/group_tree.rb index 9d4f97aa443..b10147835f3 100644 --- a/app/controllers/concerns/group_tree.rb +++ b/app/controllers/concerns/group_tree.rb @@ -1,4 +1,5 @@ module GroupTree + # rubocop:disable Gitlab/ModuleWithInstanceVariables def render_group_tree(groups) @groups = if params[:filter].present? Gitlab::GroupHierarchy.new(groups.search(params[:filter])) @@ -20,5 +21,6 @@ module GroupTree render json: serializer.represent(@groups) end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables end end diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index 744e448e8df..74a4f437dc8 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -17,15 +17,15 @@ module IssuableActions end def update - @issuable = update_service.execute(issuable) + @issuable = update_service.execute(issuable) # rubocop:disable Gitlab/ModuleWithInstanceVariables respond_to do |format| format.html do - recaptcha_check_with_fallback { render :edit } + recaptcha_check_if_spammable { render :edit } end format.json do - render_entity_json + recaptcha_check_if_spammable(false) { render_entity_json } end end @@ -55,7 +55,6 @@ module IssuableActions def destroy Issuable::DestroyService.new(issuable.project, current_user).execute(issuable) - TodoService.new.destroy_issuable(issuable, current_user) name = issuable.human_class_name flash[:notice] = "The #{name} was successfully deleted." @@ -80,10 +79,16 @@ module IssuableActions private + def recaptcha_check_if_spammable(should_redirect = true, &block) + return yield unless issuable.is_a? Spammable + + recaptcha_check_with_fallback(should_redirect, &block) + end + def render_conflict_response respond_to do |format| format.html do - @conflict = true + @conflict = true # rubocop:disable Gitlab/ModuleWithInstanceVariables render :edit end @@ -98,7 +103,7 @@ module IssuableActions end def labels - @labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute + @labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute # rubocop:disable Gitlab/ModuleWithInstanceVariables end def authorize_destroy_issuable! @@ -108,7 +113,7 @@ module IssuableActions end def authorize_admin_issuable! - unless can?(current_user, :"admin_#{resource_name}", @project) + unless can?(current_user, :"admin_#{resource_name}", @project) # rubocop:disable Gitlab/ModuleWithInstanceVariables return access_denied! end end @@ -142,6 +147,7 @@ module IssuableActions @resource_name ||= controller_name.singularize end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def render_entity_json if @issuable.valid? render json: serializer.represent(@issuable) @@ -149,6 +155,7 @@ module IssuableActions render json: { errors: @issuable.errors.full_messages }, status: :unprocessable_entity end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def serializer raise NotImplementedError @@ -159,6 +166,6 @@ module IssuableActions end def parent - @project || @group + @project || @group # rubocop:disable Gitlab/ModuleWithInstanceVariables end end diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index f3c9251225f..b25e753a5ad 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -2,6 +2,7 @@ module IssuableCollections extend ActiveSupport::Concern include SortingHelper include Gitlab::IssuableMetadata + include Gitlab::Utils::StrongMemoize included do helper_method :finder @@ -9,6 +10,7 @@ module IssuableCollections private + # rubocop:disable Gitlab/ModuleWithInstanceVariables def set_issuables_index @issuables = issuables_collection @issuables = @issuables.page(params[:page]) @@ -33,6 +35,7 @@ module IssuableCollections @users.push(author) if author end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def issuables_collection finder.execute.preload(preload_for_collection) @@ -41,7 +44,7 @@ module IssuableCollections def redirect_out_of_range(total_pages) return false if total_pages.zero? - out_of_range = @issuables.current_page > total_pages + out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables if out_of_range redirect_to(url_for(params.merge(page: total_pages, only_path: true))) @@ -51,7 +54,7 @@ module IssuableCollections end def issuable_page_count - page_count_for_relation(@issuables, finder.row_count) + page_count_for_relation(@issuables, finder.row_count) # rubocop:disable Gitlab/ModuleWithInstanceVariables end def page_count_for_relation(relation, row_count) @@ -66,6 +69,7 @@ module IssuableCollections finder_class.new(current_user, filter_params) end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def filter_params set_sort_order_from_cookie set_default_state @@ -90,6 +94,7 @@ module IssuableCollections @filter_params.permit(IssuableFinder::VALID_PARAMS) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def set_default_state params[:state] = 'opened' if params[:state].blank? @@ -129,9 +134,9 @@ module IssuableCollections end def finder - return @finder if defined?(@finder) - - @finder = issuable_finder_for(@finder_type) + strong_memoize(:finder) do + issuable_finder_for(@finder_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables + end end def collection_type diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index ad594903331..3ba1235cee0 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -2,10 +2,9 @@ module IssuesAction extend ActiveSupport::Concern include IssuableCollections + # rubocop:disable Gitlab/ModuleWithInstanceVariables def issues @finder_type = IssuesFinder - @label = finder.labels.first - @issues = issuables_collection .non_archived .page(params[:page]) @@ -17,4 +16,5 @@ module IssuesAction format.atom { render layout: 'xml.atom' } end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables end diff --git a/app/controllers/concerns/members_presentation.rb b/app/controllers/concerns/members_presentation.rb new file mode 100644 index 00000000000..c0622516fd3 --- /dev/null +++ b/app/controllers/concerns/members_presentation.rb @@ -0,0 +1,11 @@ +module MembersPresentation + extend ActiveSupport::Concern + + def present_members(members) + Gitlab::View::Presenter::Factory.new( + members, + current_user: current_user, + presenter_class: MembersPresenter + ).fabricate! + end +end diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index 8b569a01afd..a9cc13038bf 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -2,14 +2,15 @@ module MergeRequestsAction extend ActiveSupport::Concern include IssuableCollections + # rubocop:disable Gitlab/ModuleWithInstanceVariables def merge_requests @finder_type = MergeRequestsFinder - @label = finder.labels.first @merge_requests = issuables_collection.page(params[:page]) @issuable_meta_data = issuable_meta_data(@merge_requests, collection_type) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables private diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index 081f3336780..d92cf8b4894 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -6,7 +6,7 @@ module MilestoneActions format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_merge_requests_tab", { - merge_requests: @milestone.sorted_merge_requests, + merge_requests: @milestone.sorted_merge_requests, # rubocop:disable Gitlab/ModuleWithInstanceVariables show_project_name: true }) end @@ -18,7 +18,7 @@ module MilestoneActions format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_participants_tab", { - users: @milestone.participants + users: @milestone.participants # rubocop:disable Gitlab/ModuleWithInstanceVariables }) end end @@ -29,7 +29,7 @@ module MilestoneActions format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_labels_tab", { - labels: @milestone.labels + labels: @milestone.labels # rubocop:disable Gitlab/ModuleWithInstanceVariables }) end end @@ -43,6 +43,7 @@ module MilestoneActions } end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def milestone_redirect_path if @project project_milestone_path(@project, @milestone) @@ -52,4 +53,5 @@ module MilestoneActions dashboard_milestone_path(@milestone.safe_title, title: @milestone.title) end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables end diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb index be2e1b47feb..e82a5650935 100644 --- a/app/controllers/concerns/notes_actions.rb +++ b/app/controllers/concerns/notes_actions.rb @@ -1,5 +1,6 @@ module NotesActions include RendersNotes + include Gitlab::Utils::StrongMemoize extend ActiveSupport::Concern included do @@ -30,6 +31,7 @@ module NotesActions render json: notes_json end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def create create_params = note_params.merge( merge_request_diff_head_sha: params[:merge_request_diff_head_sha], @@ -47,7 +49,9 @@ module NotesActions format.html { redirect_back_or_default } end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables + # rubocop:disable Gitlab/ModuleWithInstanceVariables def update @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) @@ -60,6 +64,7 @@ module NotesActions format.html { redirect_back_or_default } end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def destroy if note.editable? @@ -138,7 +143,7 @@ module NotesActions end else template = "discussions/_diff_discussion" - @fresh_discussion = true + @fresh_discussion = true # rubocop:disable Gitlab/ModuleWithInstanceVariables locals = { discussions: [discussion], on_image: on_image } end @@ -191,7 +196,7 @@ module NotesActions end def noteable - @noteable ||= notes_finder.target || @note&.noteable + @noteable ||= notes_finder.target || @note&.noteable # rubocop:disable Gitlab/ModuleWithInstanceVariables end def require_noteable! @@ -211,20 +216,21 @@ module NotesActions end def note_project - return @note_project if defined?(@note_project) - return nil unless project + strong_memoize(:note_project) do + return nil unless project - note_project_id = params[:note_project_id] + note_project_id = params[:note_project_id] - @note_project = - if note_project_id.present? - Project.find(note_project_id) - else - project - end + the_project = + if note_project_id.present? + Project.find(note_project_id) + else + project + end - return access_denied! unless can?(current_user, :create_note, @note_project) + return access_denied! unless can?(current_user, :create_note, the_project) - @note_project + the_project + end end end diff --git a/app/controllers/concerns/oauth_applications.rb b/app/controllers/concerns/oauth_applications.rb index 9849aa93fa6..f0a68f23566 100644 --- a/app/controllers/concerns/oauth_applications.rb +++ b/app/controllers/concerns/oauth_applications.rb @@ -14,6 +14,6 @@ module OauthApplications end def load_scopes - @scopes = Doorkeeper.configuration.scopes + @scopes ||= Doorkeeper.configuration.scopes end end diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb index e9b9e9b38bc..90bb7a87b45 100644 --- a/app/controllers/concerns/preview_markdown.rb +++ b/app/controllers/concerns/preview_markdown.rb @@ -1,6 +1,7 @@ module PreviewMarkdown extend ActiveSupport::Concern + # rubocop:disable Gitlab/ModuleWithInstanceVariables def preview_markdown result = PreviewMarkdownService.new(@project, current_user, params).execute @@ -20,4 +21,5 @@ module PreviewMarkdown } } end + # rubocop:enable Gitlab/ModuleWithInstanceVariables end diff --git a/app/controllers/concerns/renders_commits.rb b/app/controllers/concerns/renders_commits.rb index bb2c1dfa00a..fb41dc1e8a8 100644 --- a/app/controllers/concerns/renders_commits.rb +++ b/app/controllers/concerns/renders_commits.rb @@ -1,6 +1,6 @@ module RendersCommits def prepare_commits_for_rendering(commits) - Banzai::CommitRenderer.render(commits, @project, current_user) + Banzai::CommitRenderer.render(commits, @project, current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables commits end diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb index 824ad06465c..e7ef297879f 100644 --- a/app/controllers/concerns/renders_notes.rb +++ b/app/controllers/concerns/renders_notes.rb @@ -1,4 +1,5 @@ module RendersNotes + # rubocop:disable Gitlab/ModuleWithInstanceVariables def prepare_notes_for_rendering(notes, noteable = nil) preload_noteable_for_regular_notes(notes) preload_max_access_for_authors(notes, @project) @@ -7,6 +8,7 @@ module RendersNotes notes end + # rubocop:enable Gitlab/ModuleWithInstanceVariables private diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb index be2e6c7f193..3d61458c064 100644 --- a/app/controllers/concerns/service_params.rb +++ b/app/controllers/concerns/service_params.rb @@ -66,7 +66,7 @@ module ServiceParams FILTER_BLANK_PARAMS = [:password].freeze def service_params - dynamic_params = @service.event_channel_names + @service.event_names + dynamic_params = @service.event_channel_names + @service.event_names # rubocop:disable Gitlab/ModuleWithInstanceVariables service_params = params.permit(:id, service: ALLOWED_PARAMS_CE + dynamic_params) if service_params[:service].is_a?(Hash) diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb index ffea712a833..9095cc7f783 100644 --- a/app/controllers/concerns/snippets_actions.rb +++ b/app/controllers/concerns/snippets_actions.rb @@ -4,6 +4,7 @@ module SnippetsActions def edit end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def raw disposition = params[:inline] == 'false' ? 'attachment' : 'inline' @@ -14,6 +15,7 @@ module SnippetsActions filename: @snippet.sanitized_file_name ) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables private diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb index ada0dde87fb..922aa58a00f 100644 --- a/app/controllers/concerns/spammable_actions.rb +++ b/app/controllers/concerns/spammable_actions.rb @@ -2,6 +2,7 @@ module SpammableActions extend ActiveSupport::Concern include Recaptcha::Verify + include Gitlab::Utils::StrongMemoize included do before_action :authorize_submit_spammable!, only: :mark_as_spam @@ -18,13 +19,13 @@ module SpammableActions private def ensure_spam_config_loaded! - return @spam_config_loaded if defined?(@spam_config_loaded) - - @spam_config_loaded = Gitlab::Recaptcha.load_configurations! + strong_memoize(:spam_config_loaded) do + Gitlab::Recaptcha.load_configurations! + end end - def recaptcha_check_with_fallback(&fallback) - if spammable.valid? + def recaptcha_check_with_fallback(should_redirect = true, &fallback) + if should_redirect && spammable.valid? redirect_to spammable_path elsif render_recaptcha? ensure_spam_config_loaded! @@ -33,7 +34,18 @@ module SpammableActions flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' end - render :verify + respond_to do |format| + format.html do + render :verify + end + + format.json do + locals = { spammable: spammable, script: false, has_submit: false } + recaptcha_html = render_to_string(partial: 'shared/recaptcha_form', formats: :html, locals: locals) + + render json: { recaptcha_html: recaptcha_html } + end + end else yield end diff --git a/app/controllers/concerns/toggle_subscription_action.rb b/app/controllers/concerns/toggle_subscription_action.rb index 92cb534343e..776583579e8 100644 --- a/app/controllers/concerns/toggle_subscription_action.rb +++ b/app/controllers/concerns/toggle_subscription_action.rb @@ -12,7 +12,7 @@ module ToggleSubscriptionAction private def subscribable_project - @project || raise(NotImplementedError) + @project ||= raise(NotImplementedError) end def subscribable_resource diff --git a/app/controllers/concerns/with_performance_bar.rb b/app/controllers/concerns/with_performance_bar.rb index ed253042701..230bbe4b1aa 100644 --- a/app/controllers/concerns/with_performance_bar.rb +++ b/app/controllers/concerns/with_performance_bar.rb @@ -6,6 +6,7 @@ module WithPerformanceBar end def peek_enabled? + return true if Rails.env.development? return false unless Gitlab::PerformanceBar.enabled?(current_user) if RequestStore.active? diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 8fc234a62b1..21e77431176 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,5 +1,6 @@ class Groups::GroupMembersController < Groups::ApplicationController include MembershipActions + include MembersPresentation include SortingHelper # Authorize @@ -14,15 +15,17 @@ class Groups::GroupMembersController < Groups::ApplicationController @members = @members.search(params[:search]) if params[:search].present? @members = @members.sort(@sort) @members = @members.page(params[:page]).per(50) - @members.includes(:user) + @members = present_members(@members.includes(:user)) - @requesters = AccessRequestsFinder.new(@group).execute(current_user) + @requesters = present_members( + AccessRequestsFinder.new(@group).execute(current_user)) @group_member = @group.group_members.new end def update - @group_member = @group.group_members.find(params[:id]) + @group_member = @group.members_and_requesters.find(params[:id]) + .present(current_user: current_user) return render_403 unless can?(current_user, :update_group_member, @group_member) diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb new file mode 100644 index 00000000000..1ff25a45398 --- /dev/null +++ b/app/controllers/ide_controller.rb @@ -0,0 +1,6 @@ +class IdeController < ApplicationController + layout 'nav_only' + + def index + end +end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 68a52f40342..57761bfbe26 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,6 +1,8 @@ class PasswordsController < Devise::PasswordsController include Gitlab::CurrentSettings + skip_before_action :require_no_authentication, only: [:edit, :update] + before_action :resource_from_email, only: [:create] before_action :check_password_authentication_available, only: [:create] before_action :throttle_reset, only: [:create] diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 9e79852e378..6025a40348b 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -86,4 +86,8 @@ class Projects::ApplicationController < ApplicationController def require_pages_enabled! not_found unless @project.pages_available? end + + def check_issues_available! + return render_404 unless @project.feature_available?(:issues, current_user) + end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 770381472c5..d838b8dc29e 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -5,9 +5,6 @@ class Projects::BlobController < Projects::ApplicationController include RendersBlob include ActionView::Helpers::SanitizeHelper - # Raised when given an invalid file path - InvalidPathError = Class.new(StandardError) - prepend_before_action :authenticate_user!, only: [:edit] before_action :require_non_empty_project, except: [:new, :create] @@ -61,7 +58,6 @@ class Projects::BlobController < Projects::ApplicationController create_commit(Files::UpdateService, success_path: -> { after_edit_path }, failure_view: :edit, failure_path: project_blob_path(@project, @id)) - rescue Files::UpdateService::FileChangedError @conflict = true render :edit @@ -132,7 +128,6 @@ class Projects::BlobController < Projects::ApplicationController def assign_blob_vars @id = params[:id] @ref, @path = extract_ref(@id) - rescue InvalidPathError render_404 end diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index e36105ddc11..949e54ff819 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -2,6 +2,7 @@ class Projects::BoardsController < Projects::ApplicationController include BoardsResponses include IssuableCollections + before_action :check_issues_available! before_action :authorize_read_board!, only: [:index, :show] before_action :assign_endpoint_vars diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 56df9991fda..cabafe26357 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -46,14 +46,16 @@ class Projects::BranchesController < Projects::ApplicationController result = CreateBranchService.new(project, current_user) .execute(branch_name, ref) - if params[:issue_iid] + success = (result[:status] == :success) + + if params[:issue_iid] && success issue = IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:issue_iid]) SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue end respond_to do |format| format.html do - if result[:status] == :success + if success if redirect_to_autodeploy redirect_to url_to_autodeploy_setup(project, branch_name), notice: view_context.autodeploy_flash_notice(branch_name) @@ -67,7 +69,7 @@ class Projects::BranchesController < Projects::ApplicationController end format.json do - if result[:status] == :success + if success render json: { name: branch_name, url: project_tree_url(@project, branch_name) } else render json: result[:messsage], status: :unprocessable_entity diff --git a/app/controllers/projects/clusters/gcp_controller.rb b/app/controllers/projects/clusters/gcp_controller.rb index b64f7a2a6bd..25608df0b9c 100644 --- a/app/controllers/projects/clusters/gcp_controller.rb +++ b/app/controllers/projects/clusters/gcp_controller.rb @@ -1,6 +1,7 @@ class Projects::Clusters::GcpController < Projects::ApplicationController before_action :authorize_read_cluster! before_action :authorize_google_api, except: [:login] + before_action :authorize_google_project_billing, only: [:new] before_action :authorize_create_cluster!, only: [:new, :create] def login @@ -22,15 +23,20 @@ class Projects::Clusters::GcpController < Projects::ApplicationController end def create - @cluster = ::Clusters::CreateService - .new(project, current_user, create_params) - .execute(token_in_session) + case google_project_billing_status + when 'true' + @cluster = ::Clusters::CreateService + .new(project, current_user, create_params) + .execute(token_in_session) - if @cluster.persisted? - redirect_to project_cluster_path(project, @cluster) + return redirect_to project_cluster_path(project, @cluster) if @cluster.persisted? + when 'false' + flash[:error] = _('Please enable billing for one of your projects to be able to create a cluster.') else - render :new + flash[:error] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.') end + + render :new end private @@ -39,6 +45,7 @@ class Projects::Clusters::GcpController < Projects::ApplicationController params.require(:cluster).permit( :enabled, :name, + :environment_scope, provider_gcp_attributes: [ :gcp_project_id, :zone, @@ -57,6 +64,17 @@ class Projects::Clusters::GcpController < Projects::ApplicationController end end + def authorize_google_project_billing + redis_token_key = CheckGcpProjectBillingWorker.store_session_token(token_in_session) + CheckGcpProjectBillingWorker.perform_async(redis_token_key) + end + + def google_project_billing_status + Gitlab::Redis::SharedState.with do |redis| + redis.get(CheckGcpProjectBillingWorker.redis_shared_state_key_for(token_in_session)) + end + end + def token_in_session @token_in_session ||= session[GoogleApi::CloudPlatform::Client.session_key_for_token] diff --git a/app/controllers/projects/clusters/user_controller.rb b/app/controllers/projects/clusters/user_controller.rb index d7678512073..d0db64b2fa9 100644 --- a/app/controllers/projects/clusters/user_controller.rb +++ b/app/controllers/projects/clusters/user_controller.rb @@ -26,6 +26,7 @@ class Projects::Clusters::UserController < Projects::ApplicationController params.require(:cluster).permit( :enabled, :name, + :environment_scope, platform_kubernetes_attributes: [ :namespace, :api_url, diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb index 0907daacbc3..1dc7f1b3a7f 100644 --- a/app/controllers/projects/clusters_controller.rb +++ b/app/controllers/projects/clusters_controller.rb @@ -8,11 +8,8 @@ class Projects::ClustersController < Projects::ApplicationController STATUS_POLLING_INTERVAL = 10_000 def index - @scope = params[:scope] || 'all' - @clusters = ClustersFinder.new(project, current_user, @scope).execute.page(params[:page]) - @active_count = ClustersFinder.new(project, current_user, :active).execute.count - @inactive_count = ClustersFinder.new(project, current_user, :inactive).execute.count - @all_count = @active_count + @inactive_count + clusters = ClustersFinder.new(project, current_user, :all).execute + @clusters = clusters.page(params[:page]).per(20) end def new @@ -90,6 +87,7 @@ class Projects::ClustersController < Projects::ApplicationController if cluster.managed? params.require(:cluster).permit( :enabled, + :environment_scope, platform_kubernetes_attributes: [ :namespace ] @@ -98,6 +96,7 @@ class Projects::ClustersController < Projects::ApplicationController params.require(:cluster).permit( :enabled, :name, + :environment_scope, platform_kubernetes_attributes: [ :api_url, :token, diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index d7a3441a245..384f18b316c 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -194,10 +194,6 @@ class Projects::IssuesController < Projects::ApplicationController render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user) end - def check_issues_available! - return render_404 unless @project.feature_available?(:issues, current_user) - end - def render_issue_json if @issue.valid? render json: serializer.represent(@issue) diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 1c4c09c772f..4865ec3dfe5 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -110,7 +110,7 @@ class Projects::JobsController < Projects::ApplicationController def erase if @build.erase(erased_by: current_user) redirect_to project_job_path(project, @build), - notice: "Build has been successfully erased!" + notice: "Job has been successfully erased!" else respond_422 end diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 1511fc08c89..dc524b790a0 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -9,7 +9,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap before_action :build_merge_request, except: [:create] def new - define_new_vars + # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/40934 + Gitlab::GitalyClient.allow_n_plus_1_calls do + define_new_vars + end end def create diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index e7b3b73024b..2e8a738b6d9 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -10,6 +10,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort] before_action :set_issuables_index, only: [:index] before_action :authenticate_user!, only: [:assign_related_issues] + before_action :check_user_can_push_to_source_branch!, only: [:rebase] def index @merge_requests = @issuables @@ -131,7 +132,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo .new(project, current_user, wip_event: 'unwip') .execute(@merge_request) - render json: serializer.represent(@merge_request) + render json: serialize_widget(@merge_request) end def commit_change_content @@ -147,7 +148,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo .new(@project, current_user) .cancel(@merge_request) - render json: serializer.represent(@merge_request) + render json: serialize_widget(@merge_request) end def merge @@ -223,6 +224,12 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo render json: environments end + def rebase + RebaseWorker.perform_async(@merge_request.id, current_user.id) + + render nothing: true, status: 200 + end + protected alias_method :subscribable_resource, :merge_request @@ -304,6 +311,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end end + def serialize_widget(merge_request) + serializer.represent(merge_request, serializer: 'widget') + end + def serializer MergeRequestSerializer.new(current_user: current_user, project: merge_request.project) end @@ -318,4 +329,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @finder_type = MergeRequestsFinder super end + + def check_user_can_push_to_source_branch! + return access_denied! unless @merge_request.source_branch_exists? + + access_check = ::Gitlab::UserAccess + .new(current_user, project: @merge_request.source_project) + .can_push_to_branch?(@merge_request.source_branch) + + access_denied! unless access_check + end end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 627cb2bd93c..5940fae8dd0 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -11,7 +11,7 @@ class Projects::NotesController < Projects::ApplicationController # Controller actions are returned from AbstractController::Base and methods of parent classes are # excluded in order to return only specific controller related methods. # That is ok for the app (no :create method in ancestors) - # but fails for tests because there is a :create method on FactoryGirl (one of the ancestors) + # but fails for tests because there is a :create method on FactoryBot (one of the ancestors) # # see https://github.com/rails/rails/blob/v4.2.7/actionpack/lib/abstract_controller/base.rb#L78 # diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index ec7c645df5a..b478e7b5e05 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -1,9 +1,11 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController before_action :schedule, except: [:index, :new, :create] + before_action :play_rate_limit, only: [:play] + before_action :authorize_play_pipeline_schedule!, only: [:play] before_action :authorize_read_pipeline_schedule! before_action :authorize_create_pipeline_schedule!, only: [:new, :create] - before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create] + before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create, :play] before_action :authorize_admin_pipeline_schedule!, only: [:destroy] def index @@ -40,6 +42,18 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController end end + def play + job_id = RunPipelineScheduleWorker.perform_async(schedule.id, current_user.id) + + if job_id + flash[:notice] = "Successfully scheduled a pipeline to run. Go to the <a href=\"#{project_pipelines_path(@project)}\">Pipelines page</a> for details.".html_safe + else + flash[:alert] = 'Unable to schedule a pipeline to run immediately' + end + + redirect_to pipeline_schedules_path(@project) + end + def take_ownership if schedule.update(owner: current_user) redirect_to pipeline_schedules_path(@project) @@ -60,6 +74,17 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController private + def play_rate_limit + return unless current_user + + limiter = ::Gitlab::ActionRateLimiter.new(action: :play_pipeline_schedule) + + return unless limiter.throttled?([current_user, schedule], 1) + + flash[:alert] = 'You cannot play this scheduled pipeline at the moment. Please wait a minute.' + redirect_to pipeline_schedules_path(@project) + end + def schedule @schedule ||= project.pipeline_schedules.find(params[:id]) end @@ -70,6 +95,10 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController variables_attributes: [:id, :key, :value, :_destroy] ) end + def authorize_play_pipeline_schedule! + return access_denied! unless can?(current_user, :play_pipeline_schedule, schedule) + end + def authorize_update_pipeline_schedule! return access_denied! unless can?(current_user, :update_pipeline_schedule, schedule) end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 7ad7b3003af..e146d0d3cd5 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -29,6 +29,8 @@ class Projects::PipelinesController < Projects::ApplicationController @pipelines_count = PipelinesFinder .new(project).execute.count + @pipelines.map(&:commit) # List commits for batch loading + respond_to do |format| format.html format.json do diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index d925dcd21ff..d7372beb9d3 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -1,5 +1,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController include MembershipActions + include MembersPresentation include SortingHelper # Authorize @@ -20,13 +21,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController @group_links = @group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) end - @project_members = @project_members.sort(@sort).page(params[:page]) - @requesters = AccessRequestsFinder.new(@project).execute(current_user) + @project_members = present_members(@project_members.sort(@sort).page(params[:page])) + @requesters = present_members(AccessRequestsFinder.new(@project).execute(current_user)) @project_member = @project.project_members.new end def update - @project_member = @project.project_members.find(params[:id]) + @project_member = @project.members_and_requesters.find(params[:id]) + .present(current_user: current_user) return render_403 unless can?(current_user, :update_project_member, @project_member) diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 9f9773575a5..c950d0f7001 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -29,17 +29,17 @@ class Projects::RunnersController < Projects::ApplicationController def resume if Ci::UpdateRunnerService.new(@runner).update(active: true) - redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' + redirect_to runners_path(@project), notice: 'Runner was successfully updated.' else - redirect_to runner_path(@runner), alert: 'Runner was not updated.' + redirect_to runners_path(@project), alert: 'Runner was not updated.' end end def pause if Ci::UpdateRunnerService.new(@runner).update(active: false) - redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' + redirect_to runners_path(@project), notice: 'Runner was successfully updated.' else - redirect_to runner_path(@runner), alert: 'Runner was not updated.' + redirect_to runners_path(@project), alert: 'Runner was not updated.' end end diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index b029b31f9af..86717bb7242 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -11,6 +11,16 @@ module Projects define_auto_devops_variables end + def reset_cache + if ResetProjectCacheService.new(@project, current_user).execute + flash[:notice] = _("Project cache successfully reset.") + else + flash[:error] = _("Unable to reset project cache.") + end + + redirect_to project_pipelines_path(@project) + end + private def define_runners_variables diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index f3719059f88..f752a46f828 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -26,6 +26,7 @@ class Projects::TreeController < Projects::ApplicationController respond_to do |format| format.html do + lfs_blob_ids @last_commit = @repository.last_commit_for_path(@commit.id, @tree.path) || @commit end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 3882fa4791d..6f229b08c0c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -9,6 +9,7 @@ class ProjectsController < Projects::ApplicationController before_action :repository, except: [:index, :new, :create] before_action :assign_ref_vars, only: [:show], if: :repo_exists? before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?] + before_action :lfs_blob_ids, only: [:show], if: [:repo_exists?, :project_view_files?] before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export] # Authorize @@ -272,7 +273,7 @@ class ProjectsController < Projects::ApplicationController render 'projects/empty' if @project.empty_repo? else - if @project.wiki_enabled? + if can?(current_user, :read_wiki, @project) @project_wiki = @project.wiki @wiki_home = @project_wiki.find_page('home', params[:version_id]) elsif @project.feature_available?(:issues, current_user) @@ -352,7 +353,7 @@ class ProjectsController < Projects::ApplicationController end def repo_exists? - project.repository_exists? && !project.empty_repo? && project.repo + project.repository_exists? && !project.empty_repo? rescue Gitlab::Git::Repository::NoRepository project.repository.expire_exists_cache diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index b46ec5e5350..493e7985d75 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -374,19 +374,14 @@ class IssuableFinder end def by_label(items) - if labels? + return items unless labels? + + items = if filter_by_no_label? - items = items.without_label + items.without_label else - items = items.with_label(label_names, params[:sort]) - items_projects = projects(items) - - if items_projects - label_ids = LabelsFinder.new(current_user, project_ids: items_projects).execute(skip_authorization: true).select(:id) - items = items.where(labels: { id: label_ids }) - end + items.with_label(label_names, params[:sort]) end - end items end diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index ce432ddbfe6..6de9eb89468 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -1,4 +1,6 @@ class LabelsFinder < UnionFinder + include Gitlab::Utils::StrongMemoize + def initialize(current_user, params = {}) @current_user = current_user @params = params @@ -32,6 +34,8 @@ class LabelsFinder < UnionFinder label_ids << project.labels end end + elsif only_group_labels? + label_ids << Label.where(group_id: group.id) else label_ids << Label.where(group_id: projects.group_ids) label_ids << Label.where(project_id: projects.select(:id)) @@ -51,6 +55,13 @@ class LabelsFinder < UnionFinder items.where(title: title) end + def group + strong_memoize(:group) do + group = Group.find(params[:group_id]) + authorized_to_read_labels?(group) && group + end + end + def group? params[:group_id].present? end @@ -63,6 +74,10 @@ class LabelsFinder < UnionFinder params[:project_ids].present? end + def only_group_labels? + params[:only_group_labels] + end + def title params[:title] || params[:name] end @@ -96,9 +111,9 @@ class LabelsFinder < UnionFinder @projects end - def authorized_to_read_labels?(project) + def authorized_to_read_labels?(label_parent) return true if skip_authorization - Ability.allowed?(current_user, :read_label, project) + Ability.allowed?(current_user, :read_label, label_parent) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4754a67450f..d13407a06c8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -306,7 +306,7 @@ module ApplicationHelper cookies["sidebar_collapsed"] == "true" end - def show_new_repo? + def show_new_ide? cookies["new_repo"] == "true" && body_data_page != 'projects:show' end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 18075ee8be7..f9dcb32f7c4 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -8,7 +8,7 @@ module BlobHelper %w(credits changelog news copying copyright license authors) end - def edit_path(project = @project, ref = @ref, path = @path, options = {}) + def edit_blob_path(project = @project, ref = @ref, path = @path, options = {}) project_edit_blob_path(project, tree_join(ref, path), options[:link_opts]) @@ -26,10 +26,10 @@ module BlobHelper button_tag 'Edit', class: "#{common_classes} disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } # This condition applies to anonymous or users who can edit directly elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) - link_to 'Edit', edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" + link_to 'Edit', edit_blob_path(project, ref, path, options), class: "#{common_classes} btn-sm" elsif current_user && can?(current_user, :fork_project, project) continue_params = { - to: edit_path(project, ref, path, options), + to: edit_blob_path(project, ref, path, options), notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } @@ -41,6 +41,43 @@ module BlobHelper end end + def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) + "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" + end + + def ide_edit_text + "#{_('Multi Edit')} <span class='label label-primary'>#{_('Beta')}</span>".html_safe + end + + def ide_blob_link(project = @project, ref = @ref, path = @path, options = {}) + return unless show_new_ide? + + blob = options.delete(:blob) + blob ||= project.repository.blob_at(ref, path) rescue nil + + return unless blob && blob.readable_text? + + common_classes = "btn js-edit-ide #{options[:extra_class]}" + + if !on_top_of_branch?(project, ref) + button_tag ide_edit_text, class: "#{common_classes} disabled has-tooltip", title: _('You can only edit files when you are on a branch'), data: { container: 'body' } + # This condition applies to anonymous or users who can edit directly + elsif current_user && can_modify_blob?(blob, project, ref) + link_to ide_edit_text, ide_edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" + elsif current_user && can?(current_user, :fork_project, project) + continue_params = { + to: ide_edit_path(project, ref, path, options), + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now + } + fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params) + + button_tag ide_edit_text, + class: common_classes, + data: { fork_path: fork_path } + end + end + def modify_file_link(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:) return unless current_user @@ -118,20 +155,24 @@ module BlobHelper icon("#{file_type_icon_class('file', mode, name)} fw") end - def blob_raw_path + def blob_raw_url(only_path: false) if @build && @entry - raw_project_job_artifacts_path(@project, @build, path: @entry.path) + raw_project_job_artifacts_url(@project, @build, path: @entry.path, only_path: only_path) elsif @snippet if @snippet.project_id - raw_project_snippet_path(@project, @snippet) + raw_project_snippet_url(@project, @snippet, only_path: only_path) else - raw_snippet_path(@snippet) + raw_snippet_url(@snippet, only_path: only_path) end elsif @blob - project_raw_path(@project, @id) + project_raw_url(@project, @id, only_path: only_path) end end + def blob_raw_path + blob_raw_url(only_path: true) + end + # SVGs can contain malicious JavaScript; only include whitelisted # elements and attributes. Note that this whitelist is by no means complete # and may omit some elements. @@ -228,7 +269,7 @@ module BlobHelper return if blob.empty? if blob.raw_binary? || blob.stored_externally? - icon = icon('download') + icon = sprite_icon('download') title = 'Download' else icon = icon('file-code-o') diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb index 686437fc99a..2641a98e29e 100644 --- a/app/helpers/branches_helper.rb +++ b/app/helpers/branches_helper.rb @@ -23,4 +23,12 @@ module BranchesHelper def protected_branch?(project, branch) ProtectedBranch.protected?(project, branch.name) end + + def diverging_count_label(count) + if count >= Repository::MAX_DIVERGING_COUNT + "#{Repository::MAX_DIVERGING_COUNT - 1}+" + else + count.to_s + end + end end diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb new file mode 100644 index 00000000000..7e4eb06b99d --- /dev/null +++ b/app/helpers/clusters_helper.rb @@ -0,0 +1,5 @@ +module ClustersHelper + def has_multiple_clusters?(project) + false + end +end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 2d304f7eb91..0333c29e2fd 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -63,7 +63,7 @@ module CommitsHelper # Returns a link formatted as a commit branch link def commit_branch_link(url, text) link_to(url, class: 'label label-gray ref-name branch-link') do - icon('code-fork', class: 'append-right-5') + "#{text}" + sprite_icon('fork', size: 16, css_class: 'fork-svg') + "#{text}" end end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index e82136f0177..1ce487e6592 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -104,15 +104,23 @@ module DiffHelper ].join(' ').html_safe end - def diff_file_blob_raw_path(diff_file) - project_raw_path(@project, tree_join(diff_file.content_sha, diff_file.file_path)) + def diff_file_blob_raw_url(diff_file, only_path: false) + project_raw_url(@project, tree_join(diff_file.content_sha, diff_file.file_path), only_path: only_path) end - def diff_file_old_blob_raw_path(diff_file) + def diff_file_old_blob_raw_url(diff_file, only_path: false) sha = diff_file.old_content_sha return unless sha - project_raw_path(@project, tree_join(diff_file.old_content_sha, diff_file.old_path)) + project_raw_url(@project, tree_join(diff_file.old_content_sha, diff_file.old_path), only_path: only_path) + end + + def diff_file_blob_raw_path(diff_file) + diff_file_blob_raw_url(diff_file, only_path: true) + end + + def diff_file_old_blob_raw_path(diff_file) + diff_file_old_blob_raw_url(diff_file, only_path: true) end def diff_file_html_data(project, diff_file_path, diff_commit_id) diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index b5dece38de1..e26ce6da030 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -35,7 +35,7 @@ module FormHelper multi_select: true, 'input-meta': 'name', 'always-show-selectbox': true, - current_user_info: current_user.to_json(only: [:id, :name]) + current_user_info: UserSerializer.new.represent(current_user) } } end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index a77aa0ad2cc..7f3c118c7ab 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -182,6 +182,11 @@ module GitlabRoutingHelper edit_project_pipeline_schedule_path(project, schedule) end + def play_pipeline_schedule_path(schedule, *args) + project = schedule.project + play_project_pipeline_schedule_path(project, schedule, *args) + end + def take_ownership_pipeline_schedule_path(schedule, *args) project = schedule.project take_ownership_project_pipeline_schedule_path(project, schedule, *args) diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index c6a83f21ceb..c5522ff7a69 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -30,6 +30,13 @@ module IconsHelper ActionController::Base.helpers.image_path('icons.svg', host: sprite_base_url) end + def sprite_file_icons_path + # SVG Sprites currently don't work across domains, so in the case of a CDN + # we have to set the current path deliberately to prevent addition of asset_host + sprite_base_url = Gitlab.config.gitlab.url if ActionController::Base.asset_host + ActionController::Base.helpers.image_path('file_icons.svg', host: sprite_base_url) + end + def sprite_icon(icon_name, size: nil, css_class: nil) css_classes = size ? "s#{size}" : "" css_classes << " #{css_class}" unless css_class.blank? diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 4c60f4b0cd0..2668cf78afe 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -32,7 +32,7 @@ module IssuablesHelper end end - def serialize_issuable(issuable) + def serialize_issuable(issuable, serializer: nil) serializer_klass = case issuable when Issue IssueSerializer @@ -42,7 +42,7 @@ module IssuablesHelper serializer_klass .new(current_user: current_user, project: issuable.project) - .represent(issuable) + .represent(issuable, serializer: serializer) .to_json end @@ -362,7 +362,7 @@ module IssuablesHelper moveIssueEndpoint: move_namespace_project_issue_path(namespace_id: issuable.project.namespace.to_param, project_id: issuable.project, id: issuable), projectsAutocompleteEndpoint: autocomplete_projects_path(project_id: @project.id), editable: can_edit_issuable, - currentUser: current_user.as_json(only: [:username, :id, :name], methods: :avatar_url), + currentUser: UserSerializer.new.represent(current_user), rootPath: root_path, fullPath: @project.full_path } diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 212cdbb8157..0f110bd25c5 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -74,7 +74,7 @@ module IssuesHelper elsif item.try(:merged?) 'status-box-merged' elsif item.closed? - 'status-box-closed' + 'status-box-mr-closed' elsif item.try(:upcoming?) 'status-box-upcoming' else diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index e1ba7898ee6..c1c19062c91 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -1,6 +1,13 @@ module LabelsHelper include ActionView::Helpers::TagHelper + def show_label_issuables_link?(label, issuables_type, current_user: nil, project: nil) + return true if label.is_a?(GroupLabel) + return true unless project + + project.feature_available?(issuables_type, current_user) + end + # Link to a Label # # label - Label object to link to diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index 41d471cc92f..a3129cac2b1 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -1,11 +1,4 @@ module MembersHelper - # Returns a `<action>_<source>_member` association, e.g.: - # - admin_project_member, update_project_member, destroy_project_member - # - admin_group_member, update_group_member, destroy_group_member - def action_member_permission(action, member) - "#{action}_#{member.type.underscore}".to_sym - end - def remove_member_message(member, user: nil) user = current_user if defined?(current_user) diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 8e822ed0ea2..aaee6eaeedd 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -58,7 +58,7 @@ module PreferencesHelper user_view elsif user_view == "activity" "activity" - elsif @project.wiki_enabled? + elsif can?(current_user, :read_wiki, @project) "wiki" elsif @project.feature_available?(:issues, current_user) "projects/issues/issues" diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 4a6b22b5ff6..f7bdcc6fd9c 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -389,7 +389,7 @@ module ProjectsHelper end def add_special_file_path(project, file_name:, commit_message: nil, branch_name: nil, context: nil) - commit_message ||= s_("CommitMessage|Add %{file_name}") % { file_name: file_name.downcase } + commit_message ||= s_("CommitMessage|Add %{file_name}") % { file_name: file_name } project_new_blob_path( project, project.default_branch || 'master', diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 2f57660516d..0f9ac958f95 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -139,7 +139,7 @@ module SearchHelper id: "filtered-search-#{type}", placeholder: 'Search or filter results...', data: { - 'username-params' => @users.to_json(only: [:id, :username]) + 'username-params' => UserSerializer.new.represent(@users) }, autocomplete: 'off' } diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 1a4f1431bdc..6cefcde558a 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -73,7 +73,6 @@ module SelectsHelper email_user: opts[:email_user] || false, first_user: opts[:first_user] && current_user ? current_user.username : false, current_user: opts[:current_user] || false, - "push-code-to-protected-branches" => opts[:push_code_to_protected_branches], author_id: opts[:author_id] || '', skip_users: opts[:skip_users] ? opts[:skip_users].map(&:id) : nil } diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index 3707bb5ba36..240783bc7fd 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -27,5 +27,16 @@ module ServicesHelper "#{event}_events" end + def service_save_button(service) + button_tag(class: 'btn btn-save', type: 'submit', disabled: service.deprecated?) do + icon('spinner spin', class: 'hidden js-btn-spinner') + + content_tag(:span, 'Save changes', class: 'js-btn-label') + end + end + + def disable_fields_service?(service) + !current_controller?("admin/services") && service.deprecated? + end + extend self end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index b05eb93b465..36a311dfa8a 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -43,14 +43,20 @@ module SortingHelper end def groups_sort_options_hash - options = { + { + sort_value_name => sort_title_name, + sort_value_name_desc => sort_title_name_desc, sort_value_recently_created => sort_title_recently_created, sort_value_oldest_created => sort_title_oldest_created, sort_value_recently_updated => sort_title_recently_updated, sort_value_oldest_updated => sort_title_oldest_updated } + end - options + def admin_groups_sort_options_hash + groups_sort_options_hash.merge( + sort_value_largest_group => sort_title_largest_group + ) end def member_sort_options_hash diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 77a82b895ce..50e17fe7717 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -5,7 +5,7 @@ module Emails @commit = @note.noteable @target_url = project_commit_url(*note_target_url_options) - mail_answer_thread(@commit, note_thread_options(recipient_id)) + mail_answer_note_thread(@commit, @note, note_thread_options(recipient_id)) end def note_issue_email(recipient_id, note_id) @@ -13,7 +13,7 @@ module Emails @issue = @note.noteable @target_url = project_issue_url(*note_target_url_options) - mail_answer_thread(@issue, note_thread_options(recipient_id)) + mail_answer_note_thread(@issue, @note, note_thread_options(recipient_id)) end def note_merge_request_email(recipient_id, note_id) @@ -21,7 +21,7 @@ module Emails @merge_request = @note.noteable @target_url = project_merge_request_url(*note_target_url_options) - mail_answer_thread(@merge_request, note_thread_options(recipient_id)) + mail_answer_note_thread(@merge_request, @note, note_thread_options(recipient_id)) end def note_snippet_email(recipient_id, note_id) @@ -29,7 +29,7 @@ module Emails @snippet = @note.noteable @target_url = project_snippet_url(*note_target_url_options) - mail_answer_thread(@snippet, note_thread_options(recipient_id)) + mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id)) end def note_personal_snippet_email(recipient_id, note_id) @@ -37,7 +37,7 @@ module Emails @snippet = @note.noteable @target_url = snippet_url(@note.noteable) - mail_answer_thread(@snippet, note_thread_options(recipient_id)) + mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id)) end private diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 9efabe3f44e..ec886e993c3 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -119,8 +119,8 @@ class Notify < BaseMailer headers['Reply-To'] = address fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze - headers['References'] ||= '' - headers['References'] << ' ' << fallback_reply_message_id + headers['References'] ||= [] + headers['References'] << fallback_reply_message_id @reply_by_email = true end @@ -156,6 +156,18 @@ class Notify < BaseMailer mail_thread(model, headers) end + def mail_answer_note_thread(model, note, headers = {}) + headers['Message-ID'] = message_id(note) + headers['In-Reply-To'] = message_id(note.references.last) + headers['References'] = note.references.map { |ref| message_id(ref) } + + headers['X-GitLab-Discussion-ID'] = note.discussion.id if note.part_of_discussion? + + headers[:subject]&.prepend('Re: ') + + mail_thread(model, headers) + end + def reply_key @reply_key ||= SentNotification.reply_key end diff --git a/app/models/blob.rb b/app/models/blob.rb index 29e762724e3..19ad110db58 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -77,9 +77,15 @@ class Blob < SimpleDelegator end def self.lazy(project, commit_id, path) - BatchLoader.for(commit_id: commit_id, path: path).batch do |items, loader| - project.repository.blobs_at(items.map(&:values)).each do |blob| - loader.call({ commit_id: blob.commit_id, path: blob.path }, blob) if blob + BatchLoader.for({ project: project, commit_id: commit_id, path: path }).batch do |items, loader| + items_by_project = items.group_by { |i| i[:project] } + + items_by_project.each do |project, items| + items = items.map { |i| i.values_at(:commit_id, :path) } + + project.repository.blobs_at(items).each do |blob| + loader.call({ project: blob.project, commit_id: blob.commit_id, path: blob.path }, blob) if blob + end end end end diff --git a/app/models/blob_viewer/dependency_manager.rb b/app/models/blob_viewer/dependency_manager.rb index a8d9be945dc..cc4950240af 100644 --- a/app/models/blob_viewer/dependency_manager.rb +++ b/app/models/blob_viewer/dependency_manager.rb @@ -27,10 +27,17 @@ module BlobViewer private - def package_name_from_json(key) - prepare! + def json_data + @json_data ||= begin + prepare! + JSON.parse(blob.data) + rescue + {} + end + end - JSON.parse(blob.data)[key] rescue nil + def package_name_from_json(key) + json_data[key] end def package_name_from_method_call(name) diff --git a/app/models/blob_viewer/package_json.rb b/app/models/blob_viewer/package_json.rb index 09221efb56c..46cd2f04f4d 100644 --- a/app/models/blob_viewer/package_json.rb +++ b/app/models/blob_viewer/package_json.rb @@ -16,7 +16,25 @@ module BlobViewer @package_name ||= package_name_from_json('name') end + def package_type + private? ? 'private package' : super + end + def package_url + private? ? homepage : npm_url + end + + private + + def private? + !!json_data['private'] + end + + def homepage + json_data['homepage'] + end + + def npm_url "https://www.npmjs.com/package/#{package_name}" end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 7cf8bdd968b..b65daa376d2 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -80,7 +80,7 @@ module Ci before_save :ensure_token before_destroy { unscoped_project } - after_create do |build| + after_create unless: :importing? do |build| run_after_commit { BuildHooksWorker.perform_async(build.id) } end @@ -468,7 +468,14 @@ module Ci end def cache - [options[:cache]] + cache = options[:cache] + + if cache && project.jobs_cache_index + cache = cache.merge( + key: "#{cache[:key]}:#{project.jobs_cache_index}") + end + + [cache] end def credentials @@ -498,7 +505,6 @@ module Ci end def valid_dependency? - return false unless complete? return false if artifacts_expired? return false if erased? diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index eebbf7c4218..d4690da3be6 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -228,6 +228,10 @@ module Ci statuses.select(:stage).distinct.count end + def total_size + statuses.count(:id) + end + def stages_names statuses.order(:stage_idx).distinct .pluck(:stage, :stage_idx).map(&:first) @@ -283,8 +287,12 @@ module Ci Ci::Pipeline.truncate_sha(sha) end + # NOTE: This is loaded lazily and will never be nil, even if the commit + # cannot be found. + # + # Use constructs like: `pipeline.commit.present?` def commit - @commit ||= project.commit_by(oid: sha) + @commit ||= Commit.lazy(project, sha) end def branch? @@ -334,12 +342,9 @@ module Ci end def latest? - return false unless ref - - commit = project.commit(ref) - return false unless commit + return false unless ref && commit.present? - commit.sha == sha + project.commit(ref) == commit end def retried diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb index c7949d11ef8..193bb48e54d 100644 --- a/app/models/clusters/applications/helm.rb +++ b/app/models/clusters/applications/helm.rb @@ -3,32 +3,19 @@ module Clusters class Helm < ActiveRecord::Base self.table_name = 'clusters_applications_helm' + include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus - belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id - default_value_for :version, Gitlab::Kubernetes::Helm::HELM_VERSION - validates :cluster, presence: true - - after_initialize :set_initial_status - - def self.application_name - self.to_s.demodulize.underscore - end - def set_initial_status return unless not_installable? self.status = 'installable' if cluster&.platform_kubernetes_active? end - def name - self.class.application_name - end - def install_command - Gitlab::Kubernetes::Helm::InstallCommand.new(name, true) + Gitlab::Kubernetes::Helm::InstallCommand.new(name, install_helm: true) end end end diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index 44bd979741e..9024f1df1cd 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -3,41 +3,22 @@ module Clusters class Ingress < ActiveRecord::Base self.table_name = 'clusters_applications_ingress' + include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus - belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id - - validates :cluster, presence: true - default_value_for :ingress_type, :nginx default_value_for :version, :nginx - after_initialize :set_initial_status - enum ingress_type: { nginx: 1 } - def self.application_name - self.to_s.demodulize.underscore - end - - def set_initial_status - return unless not_installable? - - self.status = 'installable' if cluster&.application_helm_installed? - end - - def name - self.class.application_name - end - def chart 'stable/nginx-ingress' end def install_command - Gitlab::Kubernetes::Helm::InstallCommand.new(name, false, chart) + Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart) end end end diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb new file mode 100644 index 00000000000..9b0787ee6ca --- /dev/null +++ b/app/models/clusters/applications/prometheus.rb @@ -0,0 +1,26 @@ +module Clusters + module Applications + class Prometheus < ActiveRecord::Base + VERSION = "2.0.0".freeze + + self.table_name = 'clusters_applications_prometheus' + + include ::Clusters::Concerns::ApplicationCore + include ::Clusters::Concerns::ApplicationStatus + + default_value_for :version, VERSION + + def chart + 'stable/prometheus' + end + + def chart_values_file + "#{Rails.root}/vendor/#{name}/values.yaml" + end + + def install_command + Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart, chart_values_file: chart_values_file) + end + end + end +end diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 55419189282..5ecbd4cbceb 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -6,7 +6,8 @@ module Clusters APPLICATIONS = { Applications::Helm.application_name => Applications::Helm, - Applications::Ingress.application_name => Applications::Ingress + Applications::Ingress.application_name => Applications::Ingress, + Applications::Prometheus.application_name => Applications::Prometheus }.freeze belongs_to :user @@ -21,6 +22,7 @@ module Clusters has_one :application_helm, class_name: 'Clusters::Applications::Helm' has_one :application_ingress, class_name: 'Clusters::Applications::Ingress' + has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus' accepts_nested_attributes_for :provider_gcp, update_only: true accepts_nested_attributes_for :platform_kubernetes, update_only: true @@ -62,7 +64,8 @@ module Clusters def applications [ application_helm || build_application_helm, - application_ingress || build_application_ingress + application_ingress || build_application_ingress, + application_prometheus || build_application_prometheus ] end diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb new file mode 100644 index 00000000000..a98fa85a5ff --- /dev/null +++ b/app/models/clusters/concerns/application_core.rb @@ -0,0 +1,29 @@ +module Clusters + module Concerns + module ApplicationCore + extend ActiveSupport::Concern + + included do + belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id + + validates :cluster, presence: true + + after_initialize :set_initial_status + + def set_initial_status + return unless not_installable? + + self.status = 'installable' if cluster&.application_helm_installed? + end + + def self.application_name + self.to_s.demodulize.underscore + end + + def name + self.class.application_name + end + end + end + end +end diff --git a/app/models/commit.rb b/app/models/commit.rb index 307e4fcedfe..39d7f5b159d 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -52,6 +52,20 @@ class Commit diffs.reduce(0) { |sum, d| sum + Gitlab::Git::Util.count_lines(d.diff) } end + def order_by(collection:, order_by:, sort:) + return collection unless %w[email name commits].include?(order_by) + return collection unless %w[asc desc].include?(sort) + + collection.sort do |a, b| + operands = [a, b].tap { |o| o.reverse! if sort == 'desc' } + + attr1, attr2 = operands.first.public_send(order_by), operands.second.public_send(order_by) # rubocop:disable PublicSend + + # use case insensitive comparison for string values + order_by.in?(%w[email name]) ? attr1.casecmp(attr2) : attr1 <=> attr2 + end + end + # Truncate sha to 8 characters def truncate_sha(sha) sha[0..MIN_SHA_LENGTH] @@ -72,6 +86,20 @@ class Commit def valid_hash?(key) !!(/\A#{COMMIT_SHA_PATTERN}\z/ =~ key) end + + def lazy(project, oid) + BatchLoader.for({ project: project, oid: oid }).batch do |items, loader| + items_by_project = items.group_by { |i| i[:project] } + + items_by_project.each do |project, commit_ids| + oids = commit_ids.map { |i| i[:oid] } + + project.repository.commits_by(oids: oids).each do |commit| + loader.call({ project: commit.project, oid: commit.id }, commit) if commit + end + end + end + end end attr_accessor :raw @@ -89,7 +117,7 @@ class Commit end def ==(other) - (self.class === other) && (raw == other.raw) + other.is_a?(self.class) && raw == other.raw end def self.reference_prefix @@ -210,8 +238,8 @@ class Commit notes.includes(:author) end - def method_missing(m, *args, &block) - @raw.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend + def method_missing(method, *args, &block) + @raw.__send__(method, *args, &block) # rubocop:disable GitlabSecurity/PublicSend end def respond_to_missing?(method, include_private = false) @@ -343,7 +371,7 @@ class Commit # # Returns a symbol def uri_type(path) - entry = @raw.tree.path(path) + entry = @raw.rugged_tree_entry(path) if entry[:type] == :blob blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: entry[:name]), @project) blob.image? || blob.video? ? :raw : :blob diff --git a/app/models/concerns/blocks_json_serialization.rb b/app/models/concerns/blocks_json_serialization.rb new file mode 100644 index 00000000000..8019e6adc1c --- /dev/null +++ b/app/models/concerns/blocks_json_serialization.rb @@ -0,0 +1,16 @@ +# Overrides `as_json` and `to_json` to raise an exception when called in order +# to prevent accidentally exposing attributes +# +# Not that that would ever happen... but just in case. +module BlocksJsonSerialization + extend ActiveSupport::Concern + + JsonSerializationError = Class.new(StandardError) + + def to_json(*) + raise JsonSerializationError, + "JSON serialization has been disabled on #{self.class.name}" + end + + alias_method :as_json, :to_json +end diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index 98776eab424..4ae5dd8c677 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -11,7 +11,7 @@ module CacheMarkdownField extend ActiveSupport::Concern # Increment this number every time the renderer changes its output - CACHE_VERSION = 2 + CACHE_VERSION = 3 # changes to these attributes cause the cache to be invalidates INVALIDATED_BY = %w[author project].freeze @@ -85,8 +85,7 @@ module CacheMarkdownField def cached_html_up_to_date?(markdown_field) html_field = cached_markdown_fields.html_field(markdown_field) - cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present? # rubocop:disable GitlabSecurity/PublicSend - return false unless cached + return false if cached_html_for(markdown_field).nil? && !__send__(markdown_field).nil? # rubocop:disable GitlabSecurity/PublicSend markdown_changed = attribute_changed?(markdown_field) || false html_changed = attribute_changed?(html_field) || false diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb new file mode 100644 index 00000000000..89d0474a596 --- /dev/null +++ b/app/models/concerns/deployment_platform.rb @@ -0,0 +1,48 @@ +module DeploymentPlatform + def deployment_platform + @deployment_platform ||= + find_cluster_platform_kubernetes || + find_kubernetes_service_integration || + build_cluster_and_deployment_platform + end + + private + + def find_cluster_platform_kubernetes + clusters.find_by(enabled: true)&.platform_kubernetes + end + + def find_kubernetes_service_integration + services.deployment.reorder(nil).find_by(active: true) + end + + def build_cluster_and_deployment_platform + return unless kubernetes_service_template + + cluster = ::Clusters::Cluster.create(cluster_attributes_from_service_template) + cluster.platform_kubernetes if cluster.persisted? + end + + def kubernetes_service_template + @kubernetes_service_template ||= KubernetesService.active.find_by_template + end + + def cluster_attributes_from_service_template + { + name: 'kubernetes-template', + projects: [self], + provider_type: :user, + platform_type: :kubernetes, + platform_kubernetes_attributes: platform_kubernetes_attributes_from_service_template + } + end + + def platform_kubernetes_attributes_from_service_template + { + api_url: kubernetes_service_template.api_url, + ca_pem: kubernetes_service_template.ca_pem, + token: kubernetes_service_template.token, + namespace: kubernetes_service_template.namespace + } + end +end diff --git a/app/models/concerns/discussion_on_diff.rb b/app/models/concerns/discussion_on_diff.rb index 4b4d519f3df..db9770fabf4 100644 --- a/app/models/concerns/discussion_on_diff.rb +++ b/app/models/concerns/discussion_on_diff.rb @@ -9,7 +9,6 @@ module DiscussionOnDiff :original_line_code, :diff_file, :diff_line, - :for_line?, :active?, :created_at_diff?, @@ -39,17 +38,16 @@ module DiscussionOnDiff # Returns an array of at most 16 highlighted lines above a diff note def truncated_diff_lines(highlight: true) lines = highlight ? highlighted_diff_lines : diff_lines + + initial_line_index = [diff_line.index - NUMBER_OF_TRUNCATED_DIFF_LINES + 1, 0].max + prev_lines = [] - lines.each do |line| + lines[initial_line_index..diff_line.index].each do |line| if line.meta? prev_lines.clear else prev_lines << line - - break if for_line?(line) - - prev_lines.shift if prev_lines.length >= NUMBER_OF_TRUNCATED_DIFF_LINES end end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 5ca4a7086cb..4251561a0a0 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -96,7 +96,7 @@ module Issuable strip_attributes :title - after_save :record_metrics, unless: :imported? + after_save :ensure_metrics, unless: :imported? # We want to use optimistic lock for cases when only title or description are involved # http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html @@ -335,11 +335,6 @@ module Issuable false end - def record_metrics - metrics = self.metrics || create_metrics - metrics.record! - end - ## # Override in issuable specialization # @@ -347,6 +342,10 @@ module Issuable false end + def ensure_metrics + self.metrics || create_metrics + end + ## # Overriden in MergeRequest # diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index b43eaeaeea0..c013e5a708f 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -44,13 +44,11 @@ module Mentionable end def all_references(current_user = nil, extractor: nil) - @extractors ||= {} - # Use custom extractor if it's passed in the function parameters. if extractor - @extractors[current_user] = extractor + extractors[current_user] = extractor else - extractor = @extractors[current_user] ||= Gitlab::ReferenceExtractor.new(project, current_user) + extractor = extractors[current_user] ||= Gitlab::ReferenceExtractor.new(project, current_user) extractor.reset_memoized_values end @@ -69,6 +67,10 @@ module Mentionable extractor end + def extractors + @extractors ||= {} + end + def mentioned_users(current_user = nil) all_references(current_user).users end diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index 7026f565706..fd6703831e4 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -103,9 +103,11 @@ module Milestoneish end def memoize_per_user(user, method_name) - @memoized ||= {} - @memoized[method_name] ||= {} - @memoized[method_name][user&.id] ||= yield + memoized_users[method_name][user&.id] ||= yield + end + + def memoized_users + @memoized_users ||= Hash.new { |h, k| h[k] = {} } end # override in a class that includes this module to get a faster query diff --git a/app/models/concerns/note_on_diff.rb b/app/models/concerns/note_on_diff.rb index f734952fa6c..510b8868462 100644 --- a/app/models/concerns/note_on_diff.rb +++ b/app/models/concerns/note_on_diff.rb @@ -14,10 +14,6 @@ module NoteOnDiff raise NotImplementedError end - def for_line?(line) - raise NotImplementedError - end - def original_line_code raise NotImplementedError end diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb index 5d75b2aa6a3..86f28f30032 100644 --- a/app/models/concerns/noteable.rb +++ b/app/models/concerns/noteable.rb @@ -46,6 +46,7 @@ module Noteable notes.inc_relations_for_view.grouped_diff_discussions(*args) end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def resolvable_discussions @resolvable_discussions ||= if defined?(@discussions) @@ -54,6 +55,7 @@ module Noteable discussion_notes.resolvable.discussions(self) end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def discussions_resolvable? resolvable_discussions.any?(&:resolvable?) diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index ce69fd34ac5..e48bc0be410 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -56,15 +56,17 @@ module Participable # # Returns an Array of User instances. def participants(current_user = nil) - @participants ||= Hash.new do |hash, user| - hash[user] = raw_participants(user) - end - - @participants[current_user] + all_participants[current_user] end private + def all_participants + @all_participants ||= Hash.new do |hash, user| + hash[user] = raw_participants(user) + end + end + def raw_participants(current_user = nil) current_user ||= author ext = Gitlab::ReferenceExtractor.new(project, current_user) diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb index cb59b4da3d7..b3fec99c816 100644 --- a/app/models/concerns/project_features_compatibility.rb +++ b/app/models/concerns/project_features_compatibility.rb @@ -2,6 +2,7 @@ # # After migrating issues_enabled merge_requests_enabled builds_enabled snippets_enabled and wiki_enabled # fields to a new table "project_features", support for the old fields is still needed in the API. +require 'gitlab/utils' module ProjectFeaturesCompatibility extend ActiveSupport::Concern diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb index e961c97e337..afacdb8cb12 100644 --- a/app/models/concerns/relative_positioning.rb +++ b/app/models/concerns/relative_positioning.rb @@ -10,12 +10,12 @@ module RelativePositioning after_save :save_positionable_neighbours end - def project_ids - [project.id] + def min_relative_position + self.class.in_parents(parent_ids).minimum(:relative_position) end def max_relative_position - self.class.in_projects(project_ids).maximum(:relative_position) + self.class.in_parents(parent_ids).maximum(:relative_position) end def prev_relative_position @@ -23,7 +23,7 @@ module RelativePositioning if self.relative_position prev_pos = self.class - .in_projects(project_ids) + .in_parents(parent_ids) .where('relative_position < ?', self.relative_position) .maximum(:relative_position) end @@ -36,7 +36,7 @@ module RelativePositioning if self.relative_position next_pos = self.class - .in_projects(project_ids) + .in_parents(parent_ids) .where('relative_position > ?', self.relative_position) .minimum(:relative_position) end @@ -52,7 +52,7 @@ module RelativePositioning # to its predecessor. This process will recursively move all the predecessors until we have a place if (after.relative_position - before.relative_position) < 2 before.move_before - @positionable_neighbours = [before] + @positionable_neighbours = [before] # rubocop:disable Gitlab/ModuleWithInstanceVariables end self.relative_position = position_between(before.relative_position, after.relative_position) @@ -63,9 +63,9 @@ module RelativePositioning pos_after = before.next_relative_position if before.shift_after? - issue_to_move = self.class.in_projects(project_ids).find_by!(relative_position: pos_after) + issue_to_move = self.class.in_parents(parent_ids).find_by!(relative_position: pos_after) issue_to_move.move_after - @positionable_neighbours = [issue_to_move] + @positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables pos_after = issue_to_move.relative_position end @@ -78,9 +78,9 @@ module RelativePositioning pos_before = after.prev_relative_position if after.shift_before? - issue_to_move = self.class.in_projects(project_ids).find_by!(relative_position: pos_before) + issue_to_move = self.class.in_parents(parent_ids).find_by!(relative_position: pos_before) issue_to_move.move_before - @positionable_neighbours = [issue_to_move] + @positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables pos_before = issue_to_move.relative_position end @@ -92,6 +92,10 @@ module RelativePositioning self.relative_position = position_between(max_relative_position || START_POSITION, MAX_POSITION) end + def move_to_start + self.relative_position = position_between(min_relative_position || START_POSITION, MIN_POSITION) + end + # Indicates if there is an issue that should be shifted to free the place def shift_after? next_pos = next_relative_position @@ -132,6 +136,7 @@ module RelativePositioning end end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def save_positionable_neighbours return unless @positionable_neighbours @@ -140,4 +145,5 @@ module RelativePositioning status end + # rubocop:enable Gitlab/ModuleWithInstanceVariables end diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb index f006a271327..b6c7b6735b9 100644 --- a/app/models/concerns/resolvable_discussion.rb +++ b/app/models/concerns/resolvable_discussion.rb @@ -31,15 +31,11 @@ module ResolvableDiscussion end def resolvable? - return @resolvable if @resolvable.present? - - @resolvable = potentially_resolvable? && notes.any?(&:resolvable?) + @resolvable ||= potentially_resolvable? && notes.any?(&:resolvable?) end def resolved? - return @resolved if @resolved.present? - - @resolved = resolvable? && notes.none?(&:to_be_resolved?) + @resolved ||= resolvable? && notes.none?(&:to_be_resolved?) end def first_note @@ -49,13 +45,13 @@ module ResolvableDiscussion def first_note_to_resolve return unless resolvable? - @first_note_to_resolve ||= notes.find(&:to_be_resolved?) + @first_note_to_resolve ||= notes.find(&:to_be_resolved?) # rubocop:disable Gitlab/ModuleWithInstanceVariables end def last_resolved_note return unless resolved? - @last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last + @last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last # rubocop:disable Gitlab/ModuleWithInstanceVariables end def resolved_notes @@ -95,7 +91,7 @@ module ResolvableDiscussion yield(notes_relation) # Set the notes array to the updated notes - @notes = notes_relation.fresh.to_a + @notes = notes_relation.fresh.to_a # rubocop:disable Gitlab/ModuleWithInstanceVariables self.class.memoized_values.each do |var| instance_variable_set(:"@#{var}", nil) diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index 22fde2eb134..5c1cce98ad4 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -88,7 +88,7 @@ module Routable def full_name if route && route.name.present? - @full_name ||= route.name + @full_name ||= route.name # rubocop:disable Gitlab/ModuleWithInstanceVariables else update_route if persisted? @@ -112,7 +112,7 @@ module Routable def expires_full_path_cache RequestStore.delete(full_path_key) if RequestStore.active? - @full_path = nil + @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables end def build_full_path @@ -127,7 +127,7 @@ module Routable def uncached_full_path if route && route.path.present? - @full_path ||= route.path + @full_path ||= route.path # rubocop:disable Gitlab/ModuleWithInstanceVariables else update_route if persisted? @@ -166,7 +166,7 @@ module Routable route || build_route(source: self) route.path = build_full_path route.name = build_full_name - @full_path = nil - @full_name = nil + @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables + @full_name = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables end end diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb index 731d9b9a745..5e4274619c4 100644 --- a/app/models/concerns/spammable.rb +++ b/app/models/concerns/spammable.rb @@ -12,6 +12,7 @@ module Spammable attr_accessor :spam attr_accessor :spam_log + alias_method :spam?, :spam after_validation :check_for_spam, on: [:create, :update] @@ -34,10 +35,6 @@ module Spammable end end - def spam? - @spam - end - def check_for_spam error_msg = if Gitlab::Recaptcha.enabled? "Your #{spammable_entity_type} has been recognized as spam. "\ diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb index b3020484738..99dbd4fbacf 100644 --- a/app/models/concerns/storage/legacy_namespace.rb +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -34,6 +34,8 @@ module Storage # So we basically we mute exceptions in next actions begin send_update_instructions + write_projects_repository_config + true rescue # Returning false does not rollback after_* transaction but gives diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 25e2d8ea24e..d07041c2fdf 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -39,7 +39,7 @@ module Taskable def task_list_items return [] if description.blank? - @task_list_items ||= Taskable.get_tasks(description) + @task_list_items ||= Taskable.get_tasks(description) # rubocop:disable Gitlab/ModuleWithInstanceVariables end def tasks diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb index 9f403d96ed5..5911b56c34c 100644 --- a/app/models/concerns/time_trackable.rb +++ b/app/models/concerns/time_trackable.rb @@ -21,9 +21,10 @@ module TimeTrackable has_many :timelogs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def spend_time(options) @time_spent = options[:duration] - @time_spent_user = options[:user] + @time_spent_user = User.find(options[:user_id]) @spent_at = options[:spent_at] @original_total_time_spent = nil @@ -36,6 +37,7 @@ module TimeTrackable end end alias_method :spend_time=, :spend_time + # rubocop:enable Gitlab/ModuleWithInstanceVariables def total_time_spent timelogs.sum(:time_spent) @@ -52,9 +54,10 @@ module TimeTrackable private def reset_spent_time - timelogs.new(time_spent: total_time_spent * -1, user: @time_spent_user) + timelogs.new(time_spent: total_time_spent * -1, user: @time_spent_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables end + # rubocop:disable Gitlab/ModuleWithInstanceVariables def add_or_subtract_spent_time timelogs.new( time_spent: time_spent, @@ -62,16 +65,19 @@ module TimeTrackable spent_at: @spent_at ) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def check_negative_time_spent return if time_spent.nil? || time_spent == :reset - # we need to cache the total time spent so multiple calls to #valid? - # doesn't give a false error - @original_total_time_spent ||= total_time_spent - - if time_spent < 0 && (time_spent.abs > @original_total_time_spent) + if time_spent < 0 && (time_spent.abs > original_total_time_spent) errors.add(:time_spent, 'Time to subtract exceeds the total time spent') end end + + # we need to cache the total time spent so multiple calls to #valid? + # doesn't give a false error + def original_total_time_spent + @original_total_time_spent ||= total_time_spent + end end diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb index 4a65738214b..bd6af622bfb 100644 --- a/app/models/diff_discussion.rb +++ b/app/models/diff_discussion.rb @@ -22,12 +22,14 @@ class DiffDiscussion < Discussion def merge_request_version_params return unless for_merge_request? - return {} if active? - if on_merge_request_commit? - { commit_id: commit_id } - else - noteable.version_params_for(position.diff_refs) + version_params = get_params + + return version_params unless on_merge_request_commit? && commit_id + + version_params ||= {} + version_params.tap do |params| + params[:commit_id] = commit_id end end @@ -37,4 +39,12 @@ class DiffDiscussion < Discussion position: position.to_json ) end + + private + + def get_params + return {} if active? + + noteable.version_params_for(position.diff_refs) + end end diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index b53d44cda95..15122cbc693 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -21,7 +21,7 @@ class DiffNote < Note before_validation :set_original_position, on: :create before_validation :update_position, on: :create, if: :on_text? - before_validation :set_line_code + before_validation :set_line_code, if: :on_text? after_save :keep_around_commits def discussion_class(*) @@ -61,10 +61,6 @@ class DiffNote < Note @diff_line ||= diff_file&.line_for_position(self.original_position) end - def for_line?(line) - diff_file.position(line) == self.original_position - end - def original_line_code return unless on_text? diff --git a/app/models/event.rb b/app/models/event.rb index 0997b056c6a..8a79100de5a 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -48,7 +48,18 @@ class Event < ActiveRecord::Base belongs_to :author, class_name: "User" belongs_to :project - belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations + + belongs_to :target, -> { + # If the association for "target" defines an "author" association we want to + # eager-load this so Banzai & friends don't end up performing N+1 queries to + # get the authors of notes, issues, etc. + if reflections['events'].active_record.reflect_on_association(:author) + includes(:author) + else + self + end + }, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations + has_one :push_event_payload # Callbacks diff --git a/app/models/identity.rb b/app/models/identity.rb index ff811e19f8a..b3fa7d8176a 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -8,20 +8,30 @@ class Identity < ActiveRecord::Base validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider, case_sensitive: false } validates :user_id, uniqueness: { scope: :provider } + before_save :ensure_normalized_extern_uid, if: :extern_uid_changed? + scope :with_provider, ->(provider) { where(provider: provider) } scope :with_extern_uid, ->(provider, extern_uid) do iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider) end def ldap? - provider.starts_with?('ldap') + Gitlab::OAuth::Provider.ldap_provider?(provider) end def self.normalize_uid(provider, uid) - if provider.to_s.starts_with?('ldap') + if Gitlab::OAuth::Provider.ldap_provider?(provider) Gitlab::LDAP::Person.normalize_dn(uid) else uid.to_s end end + + private + + def ensure_normalized_extern_uid + return if extern_uid.nil? + + self.extern_uid = Identity.normalize_uid(self.provider, self.extern_uid) + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 33db197e612..ad4a3c737ff 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -10,6 +10,9 @@ class Issue < ActiveRecord::Base include RelativePositioning include TimeTrackable include ThrottledTouch + include IgnorableColumn + + ignore_column :assignee_id, :branch_name DueDateStruct = Struct.new(:title, :name).freeze NoDueDate = DueDateStruct.new('No Due Date', '0').freeze @@ -32,6 +35,8 @@ class Issue < ActiveRecord::Base validates :project, presence: true + alias_attribute :parent_ids, :project_id + scope :in_projects, ->(project_ids) { where(project_id: project_ids) } scope :assigned, -> { where('EXISTS (SELECT TRUE FROM issue_assignees WHERE issue_id = issues.id)') } @@ -75,6 +80,10 @@ class Issue < ActiveRecord::Base acts_as_paranoid + class << self + alias_method :in_parents, :in_projects + end + def self.reference_prefix '#' end @@ -273,6 +282,11 @@ class Issue < ActiveRecord::Base private + def ensure_metrics + super + metrics.record! + end + # Returns `true` if the given User can read the current Issue. # # This method duplicates the same check of issue_policy.rb diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index c36be956ff0..d90cafd14b4 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -38,11 +38,7 @@ class LegacyDiffNote < Note end def diff_line - @diff_line ||= diff_file.line_for_line_code(self.line_code) if diff_file - end - - def for_line?(line) - line.discussable? && diff_file.line_code(line) == self.line_code + @diff_line ||= diff_file&.line_for_line_code(self.line_code) end def original_line_code diff --git a/app/models/member.rb b/app/models/member.rb index 2fe5fda985f..c47145667b5 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -4,6 +4,7 @@ class Member < ActiveRecord::Base include Importable include Expirable include Gitlab::Access + include Presentable attr_accessor :raw_invite_token diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 422f138c4ea..ef58816937c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -8,6 +8,7 @@ class MergeRequest < ActiveRecord::Base include ManualInverseAssociation include EachBatch include ThrottledTouch + include Gitlab::Utils::StrongMemoize ignore_column :locked_at, :ref_fetched @@ -52,6 +53,7 @@ class MergeRequest < ActiveRecord::Base serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize after_create :ensure_merge_request_diff, unless: :importing? + after_update :clear_memoized_shas after_update :reload_diff_if_branch_changed # When this attribute is true some MR validation is ignored @@ -83,6 +85,14 @@ class MergeRequest < ActiveRecord::Base transition locked: :opened end + before_transition any => :opened do |merge_request| + merge_request.merge_jid = nil + + merge_request.run_after_commit do + UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id) + end + end + state :opened state :closed state :merged @@ -146,6 +156,13 @@ class MergeRequest < ActiveRecord::Base '!' end + def rebase_in_progress? + # The source project can be deleted + return false unless source_project + + source_project.repository.rebase_in_progress?(id) + end + # Use this method whenever you need to make sure the head_pipeline is synced with the # branch head commit, for example checking if a merge request can be merged. # For more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/40004 @@ -387,13 +404,17 @@ class MergeRequest < ActiveRecord::Base end def source_branch_head - return unless source_project - - source_project.repository.commit(source_branch_ref) if source_branch_ref + strong_memoize(:source_branch_head) do + if source_project && source_branch_ref + source_project.repository.commit(source_branch_ref) + end + end end def target_branch_head - target_project.repository.commit(target_branch_ref) + strong_memoize(:target_branch_head) do + target_project.repository.commit(target_branch_ref) + end end def branch_merge_base_commit @@ -525,6 +546,13 @@ class MergeRequest < ActiveRecord::Base end end + def clear_memoized_shas + @target_branch_sha = @source_branch_sha = nil + + clear_memoization(:source_branch_head) + clear_memoization(:target_branch_head) + end + def reload_diff_if_branch_changed if (source_branch_changed? || target_branch_changed?) && (source_branch_head && target_branch_head) @@ -586,7 +614,7 @@ class MergeRequest < ActiveRecord::Base check_if_can_be_merged - can_be_merged? + can_be_merged? && !should_be_rebased? end def mergeable_state?(skip_ci_check: false) @@ -866,11 +894,11 @@ class MergeRequest < ActiveRecord::Base def state_icon_name if merged? - "check" + "git-merge" elsif closed? - "times" + "close" else - "circle-o" + "issue-open-m" end end diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index cdc408738be..9e660eccd86 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -1,12 +1,6 @@ class MergeRequest::Metrics < ActiveRecord::Base belongs_to :merge_request belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :pipeline_id - - def record! - if merge_request.merged? && self.merged_at.blank? - self.merged_at = Time.now - end - - self.save - end + belongs_to :latest_closed_by, class_name: 'User' + belongs_to :merged_by, class_name: 'User' end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index c37aa0a594b..e35de9b97ee 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -104,19 +104,19 @@ class MergeRequestDiff < ActiveRecord::Base def base_commit return unless base_commit_sha - project.commit(base_commit_sha) + project.commit_by(oid: base_commit_sha) end def start_commit return unless start_commit_sha - project.commit(start_commit_sha) + project.commit_by(oid: start_commit_sha) end def head_commit return unless head_commit_sha - project.commit(head_commit_sha) + project.commit_by(oid: head_commit_sha) end def commit_shas diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 901dbf2ba69..bdcc9159d26 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -40,6 +40,7 @@ class Namespace < ActiveRecord::Base namespace_path: true validate :nesting_level_allowed + validate :allowed_path_by_redirects delegate :name, to: :owner, allow_nil: true, prefix: true @@ -257,4 +258,21 @@ class Namespace < ActiveRecord::Base Namespace.where(id: descendants.select(:id)) .update_all(share_with_group_lock: true) end + + def allowed_path_by_redirects + return if path.nil? + + errors.add(:path, "#{path} has been taken before. Please use another one") if namespace_previously_created_with_same_path? + end + + def namespace_previously_created_with_same_path? + RedirectRoute.permanent.exists?(path: path) + end + + def write_projects_repository_config + all_projects.find_each do |project| + project.expires_full_path_cache # we need to clear cache to validate renames correctly + project.write_repository_config + end + end end diff --git a/app/models/note.rb b/app/models/note.rb index c4c2ab8e67d..184fbd5f5ae 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -360,6 +360,16 @@ class Note < ActiveRecord::Base end end + def references + refs = [noteable] + + if part_of_discussion? + refs += discussion.notes.take_while { |n| n.id < id } + end + + refs + end + def expire_etag_cache return unless noteable&.discussions_rendered_on_frontend? @@ -401,6 +411,9 @@ class Note < ActiveRecord::Base end noteable_object&.touch + + # We return the noteable object so we can re-use it in EE for ElasticSearch. + noteable_object end def banzai_render_context(field) diff --git a/app/models/project.rb b/app/models/project.rb index 6ae15a0a50f..fbe65e700a4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -19,6 +19,7 @@ class Project < ActiveRecord::Base include Routable include GroupDescendant include Gitlab::SQL::Pattern + include DeploymentPlatform extend Gitlab::ConfigHelper extend Gitlab::CurrentSettings @@ -226,7 +227,7 @@ class Project < ActiveRecord::Base delegate :name, to: :owner, allow_nil: true, prefix: true delegate :members, to: :team, prefix: true delegate :add_user, :add_users, to: :team - delegate :add_guest, :add_reporter, :add_developer, :add_master, to: :team + delegate :add_guest, :add_reporter, :add_developer, :add_master, :add_role, to: :team # Validations validates :creator, presence: true, on: :create @@ -639,7 +640,7 @@ class Project < ActiveRecord::Base end def import? - external_import? || forked? || gitlab_project_import? + external_import? || forked? || gitlab_project_import? || bare_repository_import? end def no_import? @@ -659,7 +660,8 @@ class Project < ActiveRecord::Base end def import_started? - import? && import_status == 'started' + # import? does SQL work so only run it if it looks like there's an import running + import_status == 'started' && import? end def import_scheduled? @@ -678,6 +680,10 @@ class Project < ActiveRecord::Base Gitlab::UrlSanitizer.new(import_url).masked_url end + def bare_repository_import? + import_type == 'bare_repository' + end + def gitlab_project_import? import_type == 'gitlab_project' end @@ -899,12 +905,6 @@ class Project < ActiveRecord::Base @ci_service ||= ci_services.reorder(nil).find_by(active: true) end - # TODO: This will be extended for multiple enviroment clusters - def deployment_platform - @deployment_platform ||= clusters.find_by(enabled: true)&.platform_kubernetes - @deployment_platform ||= services.where(category: :deployment).reorder(nil).find_by(active: true) - end - def monitoring_services services.where(category: :monitoring) end @@ -950,7 +950,9 @@ class Project < ActiveRecord::Base def send_move_instructions(old_path_with_namespace) # New project path needs to be committed to the DB or notification will # retrieve stale information - run_after_commit { NotificationService.new.project_was_moved(self, old_path_with_namespace) } + run_after_commit do + NotificationService.new.project_was_moved(self, old_path_with_namespace) + end end def owner @@ -962,15 +964,19 @@ class Project < ActiveRecord::Base end def execute_hooks(data, hooks_scope = :push_hooks) - hooks.public_send(hooks_scope).each do |hook| # rubocop:disable GitlabSecurity/PublicSend - hook.async_execute(data, hooks_scope.to_s) + run_after_commit_or_now do + hooks.public_send(hooks_scope).each do |hook| # rubocop:disable GitlabSecurity/PublicSend + hook.async_execute(data, hooks_scope.to_s) + end end end def execute_services(data, hooks_scope = :push_hooks) # Call only service hooks that are active for this scope - services.public_send(hooks_scope).each do |service| # rubocop:disable GitlabSecurity/PublicSend - service.async_execute(data) + run_after_commit_or_now do + services.public_send(hooks_scope).each do |service| # rubocop:disable GitlabSecurity/PublicSend + service.async_execute(data) + end end end @@ -981,10 +987,6 @@ class Project < ActiveRecord::Base false end - def repo - repository.rugged - end - def url_to_repo gitlab_shell.url_to_repo(full_path) end @@ -1409,6 +1411,8 @@ class Project < ActiveRecord::Base end def after_rename_repo + write_repository_config + path_before_change = previous_changes['path'].first # We need to check if project had been rolled out to move resource to hashed storage or not and decide @@ -1421,6 +1425,16 @@ class Project < ActiveRecord::Base Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path) end + def write_repository_config(gl_full_path: full_path) + # We'd need to keep track of project full path otherwise directory tree + # created with hashed storage enabled cannot be usefully imported using + # the import rake task. + repository.rugged.config['gitlab.fullpath'] = gl_full_path + rescue Gitlab::Git::Repository::NoRepository => e + Rails.logger.error("Error writing to .git/config for project #{full_path} (#{id}): #{e.message}.") + nil + end + def rename_repo_notify! send_move_instructions(full_path_was) expires_full_path_cache @@ -1436,6 +1450,7 @@ class Project < ActiveRecord::Base import_finish remove_import_jid update_project_counter_caches + after_create_default_branch end def update_project_counter_caches @@ -1449,6 +1464,27 @@ class Project < ActiveRecord::Base end end + def after_create_default_branch + return unless default_branch + + # Ensure HEAD points to the default branch in case it is not master + change_head(default_branch) + + if current_application_settings.default_branch_protection != Gitlab::Access::PROTECTION_NONE && !ProtectedBranch.protected?(self, default_branch) + params = { + name: default_branch, + push_access_levels_attributes: [{ + access_level: current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER + }], + merge_access_levels_attributes: [{ + access_level: current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER + }] + } + + ProtectedBranches::CreateService.new(self, creator, params).execute(skip_authorization: true) + end + end + def remove_import_jid return unless import_jid diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 1c065e1ddbd..2be35b6ea9d 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -46,6 +46,8 @@ class JiraService < IssueTrackerService context_path: url.path, auth_type: :basic, read_timeout: 120, + use_cookies: true, + additional_cookies: ['OBBasicAuth=fromDialog'], use_ssl: url.scheme == 'https' } end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index b82567ce2b3..c72b01b64af 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -31,6 +31,7 @@ class KubernetesService < DeploymentService before_validation :enforce_namespace_to_lower_case + validate :deprecation_validation, unless: :template? validates :namespace, allow_blank: true, length: 1..63, @@ -145,6 +146,17 @@ class KubernetesService < DeploymentService @kubeclient ||= build_kubeclient! end + def deprecated? + !active + end + + def deprecation_message + content = <<-MESSAGE.strip_heredoc + Kubernetes service integration has been deprecated. #{deprecated_message_content} your clusters using the new <a href=\'#{Gitlab::Routing.url_helpers.project_clusters_path(project)}'/>Clusters</a> page + MESSAGE + content.html_safe + end + TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze private @@ -226,4 +238,20 @@ class KubernetesService < DeploymentService def enforce_namespace_to_lower_case self.namespace = self.namespace&.downcase end + + def deprecation_validation + return if active_changed?(from: true, to: false) + + if deprecated? + errors[:base] << deprecation_message + end + end + + def deprecated_message_content + if active? + "Your cluster information on this page is still editable, but you are advised to disable and reconfigure" + else + "Fields on this page are now uneditable, you can configure" + end + end end diff --git a/app/models/project_team.rb b/app/models/project_team.rb index c679758973a..a9e5cfb8240 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -7,36 +7,24 @@ class ProjectTeam @project = project end - # Shortcut to add users - # - # Use: - # @team << [@user, :master] - # @team << [@users, :master] - # - def <<(args) - users, access, current_user = *args - - if users.respond_to?(:each) - add_users(users, access, current_user: current_user) - else - add_user(users, access, current_user: current_user) - end - end - def add_guest(user, current_user: nil) - self << [user, :guest, current_user] + add_user(user, :guest, current_user: current_user) end def add_reporter(user, current_user: nil) - self << [user, :reporter, current_user] + add_user(user, :reporter, current_user: current_user) end def add_developer(user, current_user: nil) - self << [user, :developer, current_user] + add_user(user, :developer, current_user: current_user) end def add_master(user, current_user: nil) - self << [user, :master, current_user] + add_user(user, :master, current_user: current_user) + end + + def add_role(user, role, current_user: nil) + send(:"add_#{role}", user, current_user: current_user) # rubocop:disable GitlabSecurity/PublicSend end def find_member(user_id) diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb index 31de204d824..20532527346 100644 --- a/app/models/redirect_route.rb +++ b/app/models/redirect_route.rb @@ -17,4 +17,32 @@ class RedirectRoute < ActiveRecord::Base where(wheres, path, "#{sanitize_sql_like(path)}/%") end + + scope :permanent, -> do + if column_permanent_exists? + where(permanent: true) + else + none + end + end + + scope :temporary, -> do + if column_permanent_exists? + where(permanent: [false, nil]) + else + all + end + end + + default_value_for :permanent, false + + def permanent=(value) + if self.class.column_permanent_exists? + super + end + end + + def self.column_permanent_exists? + ActiveRecord::Base.connection.column_exists?(:redirect_routes, :permanent) + end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 751306188a0..9c879e2006b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -4,6 +4,7 @@ class Repository REF_MERGE_REQUEST = 'merge-requests'.freeze REF_KEEP_AROUND = 'keep-around'.freeze REF_ENVIRONMENTS = 'environments'.freeze + MAX_DIVERGING_COUNT = 1000 RESERVED_REFS_NAMES = %W[ heads @@ -117,6 +118,18 @@ class Repository @commit_cache[oid] = find_commit(oid) end + def commits_by(oids:) + return [] unless oids.present? + + commits = Gitlab::Git::Commit.batch_by_oid(raw_repository, oids) + + if commits.present? + Commit.decorate(commits, @project) + else + [] + end + end + def commits(ref, path: nil, limit: nil, offset: nil, skip_merges: false, after: nil, before: nil) options = { repo: raw_repository, @@ -220,6 +233,12 @@ class Repository branch_names.include?(branch_name) end + def tag_exists?(tag_name) + return false unless raw_repository + + tag_names.include?(tag_name) + end + def ref_exists?(ref) !!raw_repository&.ref_exists?(ref) rescue ArgumentError @@ -260,11 +279,12 @@ class Repository cache.fetch(:"diverging_commit_counts_#{branch.name}") do # Rugged seems to throw a `ReferenceError` when given branch_names rather # than SHA-1 hashes - number_commits_behind = raw_repository - .count_commits_between(branch.dereferenced_target.sha, root_ref_hash) - - number_commits_ahead = raw_repository - .count_commits_between(root_ref_hash, branch.dereferenced_target.sha) + number_commits_behind, number_commits_ahead = + raw_repository.count_commits_between( + root_ref_hash, + branch.dereferenced_target.sha, + left_right: true, + max_count: MAX_DIVERGING_COUNT) { behind: number_commits_behind, ahead: number_commits_ahead } end @@ -690,7 +710,9 @@ class Repository def tags_sorted_by(value) case value - when 'name' + when 'name_asc' + VersionSorter.sort(tags) { |tag| tag.name } + when 'name_desc' VersionSorter.rsort(tags) { |tag| tag.name } when 'updated_desc' tags_sorted_by_committed_date.reverse @@ -701,10 +723,14 @@ class Repository end end - def contributors + # Params: + # + # order_by: name|email|commits + # sort: asc|desc default: 'asc' + def contributors(order_by: nil, sort: 'asc') commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true) - commits.group_by(&:author_email).map do |email, commits| + commits = commits.group_by(&:author_email).map do |email, commits| contributor = Gitlab::Contributor.new contributor.email = email @@ -718,6 +744,7 @@ class Repository contributor end + Commit.order_by(collection: commits, order_by: order_by, sort: sort) end def refs_contains_sha(ref_type, sha) @@ -756,34 +783,30 @@ class Repository end def create_dir(user, path, **options) - options[:user] = user options[:actions] = [{ action: :create_dir, file_path: path }] - multi_action(**options) + multi_action(user, **options) end def create_file(user, path, content, **options) - options[:user] = user options[:actions] = [{ action: :create, file_path: path, content: content }] - multi_action(**options) + multi_action(user, **options) end def update_file(user, path, content, **options) previous_path = options.delete(:previous_path) action = previous_path && previous_path != path ? :move : :update - options[:user] = user options[:actions] = [{ action: action, file_path: path, previous_path: previous_path, content: content }] - multi_action(**options) + multi_action(user, **options) end def delete_file(user, path, **options) - options[:user] = user options[:actions] = [{ action: :delete, file_path: path }] - multi_action(**options) + multi_action(user, **options) end def with_cache_hooks @@ -797,59 +820,14 @@ class Repository result.newrev end - def with_branch(user, *args) - with_cache_hooks do - Gitlab::Git::OperationService.new(user, raw_repository).with_branch(*args) do |start_commit| - yield start_commit - end - end - end + def multi_action(user, **options) + start_project = options.delete(:start_project) - # rubocop:disable Metrics/ParameterLists - def multi_action( - user:, branch_name:, message:, actions:, - author_email: nil, author_name: nil, - start_branch_name: nil, start_project: project) - - with_branch( - user, - branch_name, - start_branch_name: start_branch_name, - start_repository: start_project.repository.raw_repository) do |start_commit| - - index = Gitlab::Git::Index.new(raw_repository) - - if start_commit - index.read_tree(start_commit.rugged_commit.tree) - parents = [start_commit.sha] - else - parents = [] - end - - actions.each do |options| - index.public_send(options.delete(:action), options) # rubocop:disable GitlabSecurity/PublicSend - end - - options = { - tree: index.write_tree, - message: message, - parents: parents - } - options.merge!(get_committer_and_author(user, email: author_email, name: author_name)) - - create_commit(options) + if start_project + options[:start_repository] = start_project.repository.raw_repository end - end - # rubocop:enable Metrics/ParameterLists - def get_committer_and_author(user, email: nil, name: nil) - committer = user_to_committer(user) - author = Gitlab::Git.committer_hash(email: email, name: name) || committer - - { - author: author, - committer: committer - } + with_cache_hooks { raw.multi_action(user, **options) } end def can_be_merged?(source_sha, target_branch) @@ -931,7 +909,7 @@ class Repository def merge_base(first_commit_id, second_commit_id) first_commit_id = commit(first_commit_id).try(:id) || first_commit_id second_commit_id = commit(second_commit_id).try(:id) || second_commit_id - rugged.merge_base(first_commit_id, second_commit_id) + raw_repository.merge_base(first_commit_id, second_commit_id) rescue Rugged::ReferenceError nil end @@ -971,8 +949,7 @@ class Repository tmp_remote_name = true end - add_remote(remote_name, url) - set_remote_as_mirror(remote_name, refmap: refmap) + add_remote(remote_name, url, mirror_refmap: refmap) fetch_remote(remote_name, forced: forced) ensure remove_remote(remote_name) if tmp_remote_name @@ -986,10 +963,6 @@ class Repository raw_repository.fetch_source_branch!(source_repository.raw_repository, source_branch, local_ref) end - def remote_exists?(name) - raw_repository.remote_exists?(name) - end - def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:) raw_repository.compare_source_branch(target_branch_name, source_repository.raw_repository, source_branch_name, straight: straight) end @@ -1079,6 +1052,13 @@ class Repository @project.repository_storage_path end + def rebase(user, merge_request) + raw.rebase(user, merge_request.id, branch: merge_request.source_branch, + branch_sha: merge_request.source_branch_sha, + remote_repository: merge_request.target_project.repository.raw, + remote_branch: merge_request.target_branch) + end + private # TODO Generice finder, later split this on finders by Ref or Oid diff --git a/app/models/route.rb b/app/models/route.rb index 97e8a6ad9e9..7ba3ec06041 100644 --- a/app/models/route.rb +++ b/app/models/route.rb @@ -8,6 +8,8 @@ class Route < ActiveRecord::Base presence: true, uniqueness: { case_sensitive: false } + validate :ensure_permanent_paths + after_create :delete_conflicting_redirects after_update :delete_conflicting_redirects, if: :path_changed? after_update :create_redirect_for_old_path @@ -40,7 +42,7 @@ class Route < ActiveRecord::Base # We are not calling route.delete_conflicting_redirects here, in hopes # of avoiding deadlocks. The parent (self, in this method) already # called it, which deletes conflicts for all descendants. - route.create_redirect(old_path) if attributes[:path] + route.create_redirect(old_path, permanent: permanent_redirect?) if attributes[:path] end end end @@ -50,16 +52,30 @@ class Route < ActiveRecord::Base end def conflicting_redirects - RedirectRoute.matching_path_and_descendants(path) + RedirectRoute.temporary.matching_path_and_descendants(path) end - def create_redirect(path) - RedirectRoute.create(source: source, path: path) + def create_redirect(path, permanent: false) + RedirectRoute.create(source: source, path: path, permanent: permanent) end private def create_redirect_for_old_path - create_redirect(path_was) if path_changed? + create_redirect(path_was, permanent: permanent_redirect?) if path_changed? + end + + def permanent_redirect? + source_type != "Project" + end + + def ensure_permanent_paths + return if path.nil? + + errors.add(:path, "#{path} has been taken before. Please use another one") if conflicting_redirect_exists? + end + + def conflicting_redirect_exists? + RedirectRoute.permanent.matching_path_and_descendants(path).exists? end end diff --git a/app/models/service.rb b/app/models/service.rb index 3c4f1885dd0..24ba3039707 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -44,6 +44,7 @@ class Service < ActiveRecord::Base scope :pipeline_hooks, -> { where(pipeline_events: true, active: true) } scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) } scope :external_issue_trackers, -> { issue_trackers.active.without_defaults } + scope :deployment, -> { where(category: 'deployment') } default_value_for :category, 'common' @@ -263,6 +264,18 @@ class Service < ActiveRecord::Base service end + def deprecated? + false + end + + def deprecation_message + nil + end + + def self.find_by_template + find_by(template: true) + end + private def cache_project_has_external_issue_tracker diff --git a/app/models/user.rb b/app/models/user.rb index af1c36d9c93..4484ee9ff4c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -18,6 +18,7 @@ class User < ActiveRecord::Base include CreatedAtFilterable include IgnorableColumn include BulkMemberAccessLoad + include BlocksJsonSerialization DEFAULT_NOTIFICATION_LEVEL = :participating @@ -93,8 +94,8 @@ class User < ActiveRecord::Base has_one :user_synced_attributes_metadata, autosave: true # Groups - has_many :members, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent + has_many :members + has_many :group_members, -> { where(requested_at: nil) }, source: 'GroupMember' has_many :groups, through: :group_members has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group @@ -102,7 +103,7 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :project_members, -> { where(requested_at: nil) } has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :users_star_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -315,6 +316,8 @@ class User < ActiveRecord::Base # # Returns an ActiveRecord::Relation. def search(query) + query = query.downcase + order = <<~SQL CASE WHEN users.name = %{query} THEN 0 @@ -324,8 +327,11 @@ class User < ActiveRecord::Base END SQL - fuzzy_search(query, [:name, :email, :username]) - .reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name) + where( + fuzzy_arel_match(:name, query) + .or(fuzzy_arel_match(:username, query)) + .or(arel_table[:email].eq(query)) + ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name) end # searches user by given pattern @@ -333,15 +339,17 @@ class User < ActiveRecord::Base # This method uses ILIKE on PostgreSQL and LIKE on MySQL. def search_with_secondary_emails(query) + query = query.downcase + email_table = Email.arel_table matched_by_emails_user_ids = email_table .project(email_table[:user_id]) - .where(Email.fuzzy_arel_match(:email, query)) + .where(email_table[:email].eq(query)) where( fuzzy_arel_match(:name, query) - .or(fuzzy_arel_match(:email, query)) .or(fuzzy_arel_match(:username, query)) + .or(arel_table[:email].eq(query)) .or(arel_table[:id].in(matched_by_emails_user_ids)) ) end @@ -731,7 +739,7 @@ class User < ActiveRecord::Base def ldap_user? if identities.loaded? - identities.find { |identity| identity.provider.start_with?('ldap') && !identity.extern_uid.nil? } + identities.find { |identity| Gitlab::OAuth::Provider.ldap_provider?(identity.provider) && !identity.extern_uid.nil? } else identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"]) end @@ -786,10 +794,7 @@ class User < ActiveRecord::Base # `User.select(:id)` raises # `ActiveModel::MissingAttributeError: missing attribute: projects_limit` # without this safeguard! - return unless has_attribute?(:projects_limit) - - connection_default_value_defined = new_record? && !projects_limit_changed? - return unless projects_limit.nil? || connection_default_value_defined + return unless has_attribute?(:projects_limit) && projects_limit.nil? self.projects_limit = current_application_settings.default_projects_limit end @@ -1054,13 +1059,13 @@ class User < ActiveRecord::Base end def todos_done_count(force: false) - Rails.cache.fetch(['users', id, 'todos_done_count'], force: force) do + Rails.cache.fetch(['users', id, 'todos_done_count'], force: force, expires_in: 20.minutes) do TodosFinder.new(self, state: :done).execute.count end end def todos_pending_count(force: false) - Rails.cache.fetch(['users', id, 'todos_pending_count'], force: force) do + Rails.cache.fetch(['users', id, 'todos_pending_count'], force: force, expires_in: 20.minutes) do TodosFinder.new(self, state: :pending).execute.count end end diff --git a/app/models/user_synced_attributes_metadata.rb b/app/models/user_synced_attributes_metadata.rb index 9f374304164..548b99b69d9 100644 --- a/app/models/user_synced_attributes_metadata.rb +++ b/app/models/user_synced_attributes_metadata.rb @@ -6,11 +6,11 @@ class UserSyncedAttributesMetadata < ActiveRecord::Base SYNCABLE_ATTRIBUTES = %i[name email location].freeze def read_only?(attribute) - Gitlab.config.omniauth.sync_profile_from_provider && synced?(attribute) + sync_profile_from_provider? && synced?(attribute) end def read_only_attributes - return [] unless Gitlab.config.omniauth.sync_profile_from_provider + return [] unless sync_profile_from_provider? SYNCABLE_ATTRIBUTES.select { |key| synced?(key) } end @@ -22,4 +22,10 @@ class UserSyncedAttributesMetadata < ActiveRecord::Base def set_attribute_synced(attribute, value) write_attribute("#{attribute}_synced", value) end + + private + + def sync_profile_from_provider? + Gitlab::OAuth::Provider.sync_profile_from_provider?(provider) + end end diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb index 4e689a9efd5..6363c382ff8 100644 --- a/app/policies/ci/pipeline_policy.rb +++ b/app/policies/ci/pipeline_policy.rb @@ -2,16 +2,18 @@ module Ci class PipelinePolicy < BasePolicy delegate { @subject.project } - condition(:protected_ref) do - access = ::Gitlab::UserAccess.new(@user, project: @subject.project) + condition(:protected_ref) { ref_protected?(@user, @subject.project, @subject.tag?, @subject.ref) } - if @subject.tag? - !access.can_create_tag?(@subject.ref) + rule { protected_ref }.prevent :update_pipeline + + def ref_protected?(user, project, tag, ref) + access = ::Gitlab::UserAccess.new(user, project: project) + + if tag + !access.can_create_tag?(ref) else - !access.can_update_branch?(@subject.ref) + !access.can_update_branch?(ref) end end - - rule { protected_ref }.prevent :update_pipeline end end diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb index 6b7598e1821..abcf536b2f7 100644 --- a/app/policies/ci/pipeline_schedule_policy.rb +++ b/app/policies/ci/pipeline_schedule_policy.rb @@ -2,13 +2,23 @@ module Ci class PipelineSchedulePolicy < PipelinePolicy alias_method :pipeline_schedule, :subject + condition(:protected_ref) do + ref_protected?(@user, @subject.project, @subject.project.repository.tag_exists?(@subject.ref), @subject.ref) + end + condition(:owner_of_schedule) do can?(:developer_access) && pipeline_schedule.owned_by?(@user) end + rule { can?(:developer_access) }.policy do + enable :play_pipeline_schedule + end + rule { can?(:master_access) | owner_of_schedule }.policy do enable :update_pipeline_schedule enable :admin_pipeline_schedule end + + rule { protected_ref }.prevent :play_pipeline_schedule end end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index d2d45e402b0..f0bcba588a2 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -28,12 +28,18 @@ class GroupPolicy < BasePolicy with_options scope: :subject, score: 0 condition(:request_access_enabled) { @subject.request_access_enabled } - rule { public_group } .enable :read_group + rule { public_group }.policy do + enable :read_group + enable :read_list + enable :read_label + end + rule { logged_in_viewable }.enable :read_group rule { guest }.policy do enable :read_group enable :upload_file + enable :read_label end rule { admin } .enable :read_group diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index f599eab42f2..1dd8f0a25a9 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -241,7 +241,6 @@ class ProjectPolicy < BasePolicy rule { repository_disabled }.policy do prevent :push_code - prevent :push_code_to_protected_branches prevent :download_code prevent :fork_project prevent :read_commit_status diff --git a/app/presenters/group_member_presenter.rb b/app/presenters/group_member_presenter.rb new file mode 100644 index 00000000000..8f53dfa105e --- /dev/null +++ b/app/presenters/group_member_presenter.rb @@ -0,0 +1,15 @@ +class GroupMemberPresenter < MemberPresenter + private + + def admin_member_permission + :admin_group_member + end + + def update_member_permission + :update_group_member + end + + def destroy_member_permission + :destroy_group_member + end +end diff --git a/app/presenters/member_presenter.rb b/app/presenters/member_presenter.rb new file mode 100644 index 00000000000..7d2f9303b8f --- /dev/null +++ b/app/presenters/member_presenter.rb @@ -0,0 +1,38 @@ +class MemberPresenter < Gitlab::View::Presenter::Delegated + presents :member + + def access_level_roles + member.class.access_level_roles + end + + def can_resend_invite? + invite? && + can?(current_user, admin_member_permission, source) + end + + def can_update? + can?(current_user, update_member_permission, member) + end + + def can_remove? + can?(current_user, destroy_member_permission, member) + end + + def can_approve? + request? && can_update? + end + + private + + def admin_member_permission + raise NotImplementedError + end + + def update_member_permission + raise NotImplementedError + end + + def destroy_member_permission + raise NotImplementedError + end +end diff --git a/app/presenters/members_presenter.rb b/app/presenters/members_presenter.rb new file mode 100644 index 00000000000..e4aba37b69e --- /dev/null +++ b/app/presenters/members_presenter.rb @@ -0,0 +1,15 @@ +class MembersPresenter < Gitlab::View::Presenter::Delegated + include Enumerable + + presents :members + + def to_ary + to_a + end + + def each + members.each do |member| + yield member.present(current_user: current_user) + end + end +end diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index ab4c87c0169..c6806b7cc26 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -76,6 +76,12 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end end + def rebase_path + if !rebase_in_progress? && should_be_rebased? && user_can_push_to_source_branch? + rebase_project_merge_request_path(project, merge_request) + end + end + def target_branch_tree_path if target_branch_exists? project_tree_path(project, target_branch) @@ -152,6 +158,10 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated user_can_collaborate_with_project? && can_be_cherry_picked? end + def can_push_to_source_branch? + source_branch_exists? && user_can_push_to_source_branch? + end + private def conflicts @@ -174,6 +184,14 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end.sort.to_sentence end + def user_can_push_to_source_branch? + return false unless source_branch_exists? + + ::Gitlab::UserAccess + .new(current_user, project: source_project) + .can_push_to_branch?(source_branch) + end + def user_can_collaborate_with_project? can?(current_user, :push_code, project) || (current_user && current_user.already_forked?(project)) diff --git a/app/presenters/project_member_presenter.rb b/app/presenters/project_member_presenter.rb new file mode 100644 index 00000000000..7f42d2b70df --- /dev/null +++ b/app/presenters/project_member_presenter.rb @@ -0,0 +1,15 @@ +class ProjectMemberPresenter < MemberPresenter + private + + def admin_member_permission + :admin_project_member + end + + def update_member_permission + :update_project_member + end + + def destroy_member_permission + :destroy_project_member + end +end diff --git a/app/serializers/concerns/with_pagination.rb b/app/serializers/concerns/with_pagination.rb index d29e22d6740..89631b73fcf 100644 --- a/app/serializers/concerns/with_pagination.rb +++ b/app/serializers/concerns/with_pagination.rb @@ -14,7 +14,7 @@ module WithPagination # we shouldn't try to paginate single resources def represent(resource, opts = {}) if paginated? && resource.respond_to?(:page) - super(@paginator.paginate(resource), opts) + super(paginator.paginate(resource), opts) else super(resource, opts) end diff --git a/app/serializers/event_entity.rb b/app/serializers/event_entity.rb deleted file mode 100644 index 935d67a4f37..00000000000 --- a/app/serializers/event_entity.rb +++ /dev/null @@ -1,4 +0,0 @@ -class EventEntity < Grape::Entity - expose :author, using: UserEntity - expose :updated_at -end diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb index 37240bfb0b1..aca4e4ca488 100644 --- a/app/serializers/group_child_entity.rb +++ b/app/serializers/group_child_entity.rb @@ -1,6 +1,7 @@ class GroupChildEntity < Grape::Entity include ActionView::Helpers::NumberHelper include RequestAwareEntity + include MarkupHelper expose :id, :name, :description, :visibility, :full_name, :created_at, :updated_at, :avatar_url @@ -59,6 +60,10 @@ class GroupChildEntity < Grape::Entity number_with_delimiter(instance.member_count) end + expose :markdown_description do |instance| + markdown_description + end + private def membership @@ -74,4 +79,8 @@ class GroupChildEntity < Grape::Entity def type object.class.name.downcase end + + def markdown_description + markdown_field(object, :description) + end end diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb index 3b5a4fd4f79..6f31fbd6b7c 100644 --- a/app/serializers/issuable_entity.rb +++ b/app/serializers/issuable_entity.rb @@ -3,14 +3,6 @@ class IssuableEntity < Grape::Entity expose :id expose :iid - expose :author_id expose :description - expose :lock_version - expose :milestone_id expose :title - expose :updated_by_id - expose :created_at - expose :updated_at - expose :milestone, using: API::Entities::Milestone - expose :labels, using: LabelEntity end diff --git a/app/serializers/issuable_sidebar_entity.rb b/app/serializers/issuable_sidebar_entity.rb index ff23d8bf0c7..29138c803df 100644 --- a/app/serializers/issuable_sidebar_entity.rb +++ b/app/serializers/issuable_sidebar_entity.rb @@ -1,4 +1,5 @@ class IssuableSidebarEntity < Grape::Entity + include TimeTrackableEntity include RequestAwareEntity expose :participants, using: ::API::Entities::UserBasic do |issuable| @@ -8,9 +9,4 @@ class IssuableSidebarEntity < Grape::Entity expose :subscribed do |issuable| issuable.subscribed?(request.current_user, issuable.project) end - - expose :time_estimate - expose :total_time_spent - expose :human_time_estimate - expose :human_total_time_spent end diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 9d52b8d9752..0bdd4d7a272 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -2,7 +2,15 @@ class IssueEntity < IssuableEntity include TimeTrackableEntity expose :state + expose :milestone_id + expose :updated_by_id + expose :created_at + expose :updated_at expose :deleted_at + expose :milestone, using: API::Entities::Milestone + expose :labels, using: LabelEntity + expose :lock_version + expose :author_id expose :confidential expose :discussion_locked expose :assignees, using: API::Entities::UserBasic diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb index 72e56a2c77f..523b522d449 100644 --- a/app/serializers/job_entity.rb +++ b/app/serializers/job_entity.rb @@ -4,6 +4,8 @@ class JobEntity < Grape::Entity expose :id expose :name + expose :started?, as: :started + expose :build_path do |build| build.target_url || path_to(:namespace_project_job, build) end diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb index d54a6516aed..e4aec977f01 100644 --- a/app/serializers/merge_request_basic_entity.rb +++ b/app/serializers/merge_request_basic_entity.rb @@ -4,4 +4,5 @@ class MergeRequestBasicEntity < IssuableSidebarEntity expose :merge_error expose :state expose :source_branch_exists?, as: :source_branch_exists + expose :rebase_in_progress?, as: :rebase_in_progress end diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb new file mode 100644 index 00000000000..3548107ac16 --- /dev/null +++ b/app/serializers/merge_request_metrics_entity.rb @@ -0,0 +1,6 @@ +class MergeRequestMetricsEntity < Grape::Entity + expose :latest_closed_at, as: :closed_at + expose :merged_at + expose :latest_closed_by, as: :closed_by, using: UserEntity + expose :merged_by, using: UserEntity +end diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb index e9d98d8baca..caf193bdae3 100644 --- a/app/serializers/merge_request_serializer.rb +++ b/app/serializers/merge_request_serializer.rb @@ -1,14 +1,14 @@ class MergeRequestSerializer < BaseSerializer # This overrided method takes care of which entity should be used - # to serialize the `merge_request` based on `basic` key in `opts` param. + # to serialize the `merge_request` based on `serializer` key in `opts` param. # Hence, `entity` doesn't need to be declared on the class scope. def represent(merge_request, opts = {}) entity = case opts[:serializer] when 'basic', 'sidebar' MergeRequestBasicEntity - else - MergeRequestEntity + else # It's 'widget' + MergeRequestWidgetEntity end super(merge_request, opts, entity) diff --git a/app/serializers/merge_request_entity.rb b/app/serializers/merge_request_widget_entity.rb index eece9445dca..48cd2317f46 100644 --- a/app/serializers/merge_request_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -1,8 +1,5 @@ -class MergeRequestEntity < IssuableEntity - include TimeTrackableEntity - +class MergeRequestWidgetEntity < IssuableEntity expose :state - expose :deleted_at expose :in_progress_merge_commit_sha expose :merge_commit_sha expose :merge_error @@ -20,9 +17,21 @@ class MergeRequestEntity < IssuableEntity merge_request.project.merge_requests_ff_only_enabled end - # Events - expose :merge_event, using: EventEntity - expose :closed_event, using: EventEntity + expose :metrics do |merge_request| + metrics = build_metrics(merge_request) + + MergeRequestMetricsEntity.new(metrics).as_json + end + + expose :rebase_commit_sha + expose :rebase_in_progress?, as: :rebase_in_progress + + expose :can_push_to_source_branch do |merge_request| + presenter(merge_request).can_push_to_source_branch? + end + expose :rebase_path do |merge_request| + presenter(merge_request).rebase_path + end # User entities expose :merge_user, using: UserEntity @@ -181,4 +190,27 @@ class MergeRequestEntity < IssuableEntity @presenters ||= {} @presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user) end + + # Once SchedulePopulateMergeRequestMetricsWithEventsData fully runs, + # we can remove this method and just serialize MergeRequest#metrics + # instead. See https://gitlab.com/gitlab-org/gitlab-ce/issues/41587 + def build_metrics(merge_request) + # There's no need to query and serialize metrics data for merge requests that are not + # merged or closed. + return unless merge_request.merged? || merge_request.closed? + return merge_request.metrics if merge_request.merged? && merge_request.metrics&.merged_by_id + return merge_request.metrics if merge_request.closed? && merge_request.metrics&.latest_closed_by_id + + build_metrics_from_events(merge_request) + end + + def build_metrics_from_events(merge_request) + closed_event = merge_request.closed_event + merge_event = merge_request.merge_event + + MergeRequest::Metrics.new(latest_closed_at: closed_event&.updated_at, + latest_closed_by: closed_event&.author, + merged_at: merge_event&.updated_at, + merged_by: merge_event&.author) + end end diff --git a/app/services/check_gcp_project_billing_service.rb b/app/services/check_gcp_project_billing_service.rb new file mode 100644 index 00000000000..854adf2177d --- /dev/null +++ b/app/services/check_gcp_project_billing_service.rb @@ -0,0 +1,8 @@ +class CheckGcpProjectBillingService + def execute(token) + client = GoogleApi::CloudPlatform::Client.new(token, nil) + client.projects_list.select do |project| + client.projects_get_billing_info(project.name).billingEnabled + end + end +end diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 1e5f2ed4dd2..c8b112132b3 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -12,18 +12,19 @@ module Ci def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, &block) @pipeline = Ci::Pipeline.new - command = OpenStruct.new(source: source, - origin_ref: params[:ref], - checkout_sha: params[:checkout_sha], - after_sha: params[:after], - before_sha: params[:before], - trigger_request: trigger_request, - schedule: schedule, - ignore_skip_ci: ignore_skip_ci, - save_incompleted: save_on_errors, - seeds_block: block, - project: project, - current_user: current_user) + command = Gitlab::Ci::Pipeline::Chain::Command.new( + source: source, + origin_ref: params[:ref], + checkout_sha: params[:checkout_sha], + after_sha: params[:after], + before_sha: params[:before], + trigger_request: trigger_request, + schedule: schedule, + ignore_skip_ci: ignore_skip_ci, + save_incompleted: save_on_errors, + seeds_block: block, + project: project, + current_user: current_user) sequence = Gitlab::Ci::Pipeline::Chain::Sequence .new(pipeline, command, SEQUENCE) @@ -80,7 +81,7 @@ module Ci end def related_merge_requests - MergeRequest.where(source_project: pipeline.project, source_branch: pipeline.ref) + MergeRequest.opened.where(source_project: pipeline.project, source_branch: pipeline.ref) end end end diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index c8b6450c9b5..f832b79ef21 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -38,11 +38,15 @@ module Ci begin # In case when 2 runners try to assign the same build, second runner will be declined # with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method. - build.runner_id = runner.id - build.run! - register_success(build) - - return Result.new(build, true) + begin + build.runner_id = runner.id + build.run! + register_success(build) + + return Result.new(build, true) + rescue Ci::Build::MissingDependenciesError + build.drop!(:missing_dependency_failure) + end rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError # We are looping to find another build that is not conflicting # It also indicates that this build can be picked and passed to runner. @@ -54,9 +58,6 @@ module Ci # we still have to return 409 in the end, # to make sure that this is properly handled by runner. valid = false - rescue Ci::Build::MissingDependenciesError - build.drop!(:missing_dependency_failure) - valid = false end end diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb index 9a4ce31cb39..cba1b920f7c 100644 --- a/app/services/clusters/applications/base_helm_service.rb +++ b/app/services/clusters/applications/base_helm_service.rb @@ -18,7 +18,7 @@ module Clusters end def helm_api - @helm_api ||= Gitlab::Kubernetes::Helm.new(kubeclient) + @helm_api ||= Gitlab::Kubernetes::Helm::Api.new(kubeclient) end def install_command diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb index 7d45b4aa26a..26eb274f4d5 100644 --- a/app/services/concerns/issues/resolve_discussions.rb +++ b/app/services/concerns/issues/resolve_discussions.rb @@ -1,24 +1,28 @@ module Issues module ResolveDiscussions + include Gitlab::Utils::StrongMemoize + attr_reader :merge_request_to_resolve_discussions_of_iid, :discussion_to_resolve_id + # rubocop:disable Gitlab/ModuleWithInstanceVariables def filter_resolve_discussion_params @merge_request_to_resolve_discussions_of_iid ||= params.delete(:merge_request_to_resolve_discussions_of) @discussion_to_resolve_id ||= params.delete(:discussion_to_resolve) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def merge_request_to_resolve_discussions_of - return @merge_request_to_resolve_discussions_of if defined?(@merge_request_to_resolve_discussions_of) - - @merge_request_to_resolve_discussions_of = MergeRequestsFinder.new(current_user, project_id: project.id) - .execute - .find_by(iid: merge_request_to_resolve_discussions_of_iid) + strong_memoize(:merge_request_to_resolve_discussions_of) do + MergeRequestsFinder.new(current_user, project_id: project.id) + .execute + .find_by(iid: merge_request_to_resolve_discussions_of_iid) + end end def discussions_to_resolve return [] unless merge_request_to_resolve_discussions_of - @discussions_to_resolve ||= + @discussions_to_resolve ||= # rubocop:disable Gitlab/ModuleWithInstanceVariables if discussion_to_resolve_id discussion_or_nil = merge_request_to_resolve_discussions_of .find_discussion(discussion_to_resolve_id) diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb index 6328d567a07..44dc90b3462 100644 --- a/app/services/event_create_service.rb +++ b/app/services/event_create_service.rb @@ -103,6 +103,6 @@ class EventCreateService author_id: current_user.id ) - Event.create(attributes) + Event.create!(attributes) end end diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index 38231f66009..8d4b9f14780 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -1,11 +1,14 @@ module Files class BaseService < Commits::CreateService + FileChangedError = Class.new(StandardError) + def initialize(*args) super @author_email = params[:author_email] @author_name = params[:author_name] @commit_message = params[:commit_message] + @last_commit_sha = params[:last_commit_sha] @file_path = params[:file_path] @previous_path = params[:previous_path] @@ -13,5 +16,16 @@ module Files @file_content = params[:file_content] @file_content = Base64.decode64(@file_content) if params[:file_content_encoding] == 'base64' end + + def file_has_changed?(path, commit_id) + return false unless commit_id + + last_commit = Gitlab::Git::Commit + .last_for_path(@start_project.repository, @start_branch, path) + + return false unless last_commit + + last_commit.sha != commit_id + end end end diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb index 7952e5c95d4..32a57484d4e 100644 --- a/app/services/files/delete_service.rb +++ b/app/services/files/delete_service.rb @@ -11,5 +11,15 @@ module Files start_project: @start_project, start_branch_name: @start_branch) end + + private + + def validate! + super + + if file_has_changed?(@file_path, @last_commit_sha) + raise FileChangedError, "You are attempting to delete a file that has been previously updated." + end + end end end diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb index bfacc462847..a03c59f569d 100644 --- a/app/services/files/multi_service.rb +++ b/app/services/files/multi_service.rb @@ -1,8 +1,10 @@ module Files class MultiService < Files::BaseService + UPDATE_FILE_ACTIONS = %w(update move delete).freeze + def create_commit! repository.multi_action( - user: current_user, + current_user, message: @commit_message, branch_name: @branch_name, actions: params[:actions], @@ -11,6 +13,8 @@ module Files start_project: @start_project, start_branch_name: @start_branch ) + rescue ArgumentError => e + raise_error(e) end private @@ -18,14 +22,16 @@ module Files def validate! super - params[:actions].each do |action| - validate_action!(action) - end + params[:actions].each { |action| validate_file_status!(action) } end - def validate_action!(action) - unless Gitlab::Git::Index::ACTIONS.include?(action[:action].to_s) - raise_error("Unknown action '#{action[:action]}'") + def validate_file_status!(action) + return unless UPDATE_FILE_ACTIONS.include?(action[:action]) + + file_path = action[:previous_path] || action[:file_path] + + if file_has_changed?(file_path, action[:last_commit_id]) + raise_error("The file has changed since you started editing it: #{file_path}") end end end diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index bcca1386bed..1902d1cea72 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -1,13 +1,5 @@ module Files class UpdateService < Files::BaseService - FileChangedError = Class.new(StandardError) - - def initialize(*args) - super - - @last_commit_sha = params[:last_commit_sha] - end - def create_commit! repository.update_file(current_user, @file_path, @file_content, message: @commit_message, @@ -21,21 +13,10 @@ module Files private - def file_has_changed? - return false unless @last_commit_sha && last_commit - - @last_commit_sha != last_commit.sha - end - - def last_commit - @last_commit ||= Gitlab::Git::Commit - .last_for_path(@start_project.repository, @start_branch, @file_path) - end - def validate! super - if file_has_changed? + if file_has_changed?(@file_path, @last_commit_sha) raise FileChangedError, "You are attempting to update a file that has changed since you started editing it." end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index bb61136e33b..e6fd193ffb3 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -154,24 +154,7 @@ class GitPushService < BaseService offset = [@push_commits_count - PROCESS_COMMIT_LIMIT, 0].max @push_commits = project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT) - # Ensure HEAD points to the default branch in case it is not master - project.change_head(branch_name) - - # Set protection on the default branch if configured - if current_application_settings.default_branch_protection != PROTECTION_NONE && !ProtectedBranch.protected?(@project, @project.default_branch) - - params = { - name: @project.default_branch, - push_access_levels_attributes: [{ - access_level: current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER - }], - merge_access_levels_attributes: [{ - access_level: current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER - }] - } - - ProtectedBranches::CreateService.new(@project, current_user, params).execute - end + @project.after_create_default_branch end def build_push_data diff --git a/app/services/issuable/destroy_service.rb b/app/services/issuable/destroy_service.rb index 0610b401213..7197a426a72 100644 --- a/app/services/issuable/destroy_service.rb +++ b/app/services/issuable/destroy_service.rb @@ -1,8 +1,10 @@ module Issuable class DestroyService < IssuableBaseService def execute(issuable) - if issuable.destroy - issuable.update_project_counter_caches + TodoService.new.destroy_target(issuable) do |issuable| + if issuable.destroy + issuable.update_project_counter_caches + end end end end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 2c51ac13815..e7463e6e25c 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -106,12 +106,14 @@ class IssuableBaseService < BaseService end def merge_quick_actions_into_params!(issuable) + original_description = params.fetch(:description, issuable.description) + description, command_params = QuickActions::InterpretService.new(project, current_user) - .execute(params[:description], issuable) + .execute(original_description, issuable) # Avoid a description already set on an issuable to be overwritten by a nil - params[:description] = description if params.key?(:description) + params[:description] = description if description params.merge!(command_params) end diff --git a/app/services/members/approve_access_request_service.rb b/app/services/members/approve_access_request_service.rb index c13f289f61e..2a2bb0cae5b 100644 --- a/app/services/members/approve_access_request_service.rb +++ b/app/services/members/approve_access_request_service.rb @@ -35,8 +35,17 @@ module Members def can_update_access_requester?(access_requester, opts = {}) access_requester && ( opts[:force] || - can?(current_user, action_member_permission(:update, access_requester), access_requester) + can?(current_user, update_member_permission(access_requester), access_requester) ) end + + def update_member_permission(member) + case member + when GroupMember + :update_group_member + when ProjectMember + :update_project_member + end + end end end diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb index 46c505baf8b..05b93ac8fdb 100644 --- a/app/services/members/destroy_service.rb +++ b/app/services/members/destroy_service.rb @@ -36,7 +36,16 @@ module Members end def can_destroy_member?(member) - member && can?(current_user, action_member_permission(:destroy, member), member) + member && can?(current_user, destroy_member_permission(member), member) + end + + def destroy_member_permission(member) + case member + when GroupMember + :destroy_group_member + when ProjectMember + :destroy_project_member + end end end end diff --git a/app/services/merge_request_metrics_service.rb b/app/services/merge_request_metrics_service.rb new file mode 100644 index 00000000000..9248de14a53 --- /dev/null +++ b/app/services/merge_request_metrics_service.rb @@ -0,0 +1,19 @@ +class MergeRequestMetricsService + delegate :update!, to: :@merge_request_metrics + + def initialize(merge_request_metrics) + @merge_request_metrics = merge_request_metrics + end + + def merge(event) + update!(merged_by_id: event.author_id, merged_at: event.created_at) + end + + def close(event) + update!(latest_closed_by_id: event.author_id, latest_closed_at: event.created_at) + end + + def reopen + update!(latest_closed_by_id: nil, latest_closed_at: nil) + end +end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 6b32d65a74b..20a2b50d3de 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -24,6 +24,10 @@ module MergeRequests private + def merge_request_metrics_service(merge_request) + MergeRequestMetricsService.new(merge_request.metrics) + end + def create_assignee_note(merge_request) SystemNoteService.change_assignee( merge_request, merge_request.project, current_user, merge_request.assignee) diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb index 40213c99014..f727ec002e7 100644 --- a/app/services/merge_requests/close_service.rb +++ b/app/services/merge_requests/close_service.rb @@ -8,7 +8,7 @@ module MergeRequests merge_request.allow_broken = true if merge_request.close - event_service.close_mr(merge_request, current_user) + create_event(merge_request) create_note(merge_request) notification_service.close_mr(merge_request, current_user) todo_service.close_merge_request(merge_request, current_user) @@ -19,5 +19,16 @@ module MergeRequests merge_request end + + private + + def create_event(merge_request) + # Making sure MergeRequest::Metrics updates are in sync with + # Event creation. + Event.transaction do + close_event = event_service.close_mr(merge_request, current_user) + merge_request_metrics_service(merge_request).close(close_event) + end + end end end diff --git a/app/services/merge_requests/conflicts/list_service.rb b/app/services/merge_requests/conflicts/list_service.rb index 0f677a996f7..ca9a33678e4 100644 --- a/app/services/merge_requests/conflicts/list_service.rb +++ b/app/services/merge_requests/conflicts/list_service.rb @@ -23,7 +23,7 @@ module MergeRequests # when there are no conflict files. conflicts.files.each(&:lines) @conflicts_can_be_resolved_in_ui = conflicts.files.length > 0 - rescue Rugged::OdbError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing + rescue Gitlab::Git::CommandError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing @conflicts_can_be_resolved_in_ui = false end end diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb index b1d6bac4d4a..c78e78afcd1 100644 --- a/app/services/merge_requests/post_merge_service.rb +++ b/app/services/merge_requests/post_merge_service.rb @@ -9,7 +9,7 @@ module MergeRequests close_issues(merge_request) todo_service.merge_merge_request(merge_request, current_user) merge_request.mark_as_merged - create_merge_event(merge_request, current_user) + create_event(merge_request) create_note(merge_request) notification_service.merge_mr(merge_request, current_user) execute_hooks(merge_request, 'merge') @@ -34,5 +34,14 @@ module MergeRequests def create_merge_event(merge_request, current_user) EventCreateService.new.merge_mr(merge_request, current_user) end + + def create_event(merge_request) + # Making sure MergeRequest::Metrics updates are in sync with + # Event creation. + Event.transaction do + merge_event = create_merge_event(merge_request, current_user) + merge_request_metrics_service(merge_request).merge(merge_event) + end + end end end diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb new file mode 100644 index 00000000000..0d5a25fa28e --- /dev/null +++ b/app/services/merge_requests/rebase_service.rb @@ -0,0 +1,30 @@ +module MergeRequests + class RebaseService < MergeRequests::WorkingCopyBaseService + def execute(merge_request) + @merge_request = merge_request + + if rebase + success + else + error('Failed to rebase. Should be done manually') + end + end + + def rebase + if merge_request.rebase_in_progress? + log_error('Rebase task canceled: Another rebase is already in progress', save_message_on_model: true) + return false + end + + rebase_sha = repository.rebase(current_user, merge_request) + + merge_request.update_attributes(rebase_commit_sha: rebase_sha) + + true + rescue => e + log_error('Failed to rebase branch:') + log_error(e.message, save_message_on_model: true) + false + end + end +end diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb index c599a90f9fe..120677a7149 100644 --- a/app/services/merge_requests/reopen_service.rb +++ b/app/services/merge_requests/reopen_service.rb @@ -4,7 +4,7 @@ module MergeRequests return merge_request unless can?(current_user, :update_merge_request, merge_request) if merge_request.reopen - event_service.reopen_mr(merge_request, current_user) + create_event(merge_request) create_note(merge_request, 'reopened') notification_service.reopen_mr(merge_request, current_user) execute_hooks(merge_request, 'reopen') @@ -16,5 +16,16 @@ module MergeRequests merge_request end + + private + + def create_event(merge_request) + # Making sure MergeRequest::Metrics updates are in sync with + # Event creation. + Event.transaction do + event_service.reopen_mr(merge_request, current_user) + merge_request_metrics_service(merge_request).reopen + end + end end end diff --git a/app/services/merge_requests/working_copy_base_service.rb b/app/services/merge_requests/working_copy_base_service.rb new file mode 100644 index 00000000000..186e05bf966 --- /dev/null +++ b/app/services/merge_requests/working_copy_base_service.rb @@ -0,0 +1,24 @@ +module MergeRequests + class WorkingCopyBaseService < MergeRequests::BaseService + attr_reader :merge_request + + def source_project + @source_project ||= merge_request.source_project + end + + def target_project + @target_project ||= merge_request.target_project + end + + def log_error(message, save_message_on_model: false) + Gitlab::GitLogger.error("#{self.class.name} error (#{merge_request.to_reference(full: true)}): #{message}") + + merge_request.update(merge_error: message) if save_message_on_model + end + + # Don't try to print expensive instance variables. + def inspect + "#<#{self.class} #{merge_request.to_reference(full: true)}>" + end + end +end diff --git a/app/services/notes/destroy_service.rb b/app/services/notes/destroy_service.rb index b819bd17039..fb78420d324 100644 --- a/app/services/notes/destroy_service.rb +++ b/app/services/notes/destroy_service.rb @@ -1,7 +1,9 @@ module Notes class DestroyService < BaseService def execute(note) - note.destroy + TodoService.new.destroy_target(note) do |note| + note.destroy + end end end end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 71533da31b1..01838ec6b5d 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -56,11 +56,7 @@ module Projects after_create_actions if @project.persisted? - if @project.errors.empty? - @project.import_schedule if @project.import? - else - fail(error: @project.errors.full_messages.join(', ')) - end + import_schedule @project rescue ActiveRecord::RecordInvalid => e @@ -92,6 +88,7 @@ module Projects log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"") unless @project.gitlab_project_import? + @project.write_repository_config @project.create_wiki unless skip_wiki? create_services_from_active_templates(@project) @@ -164,5 +161,15 @@ module Projects @project.path = @project.name.dup.parameterize end end + + private + + def import_schedule + if @project.errors.empty? + @project.import_schedule if @project.import? && !@project.bare_repository_import? + else + fail(error: @project.errors.full_messages.join(', ')) + end + end end end diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 03be7039b2a..348eb0bf8d8 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -26,7 +26,7 @@ module Projects name: @project.name, path: @project.path, shared_runners_enabled: @project.shared_runners_enabled, - namespace_id: @params[:namespace].try(:id) || current_user.namespace.id + namespace_id: target_namespace.id } if @project.avatar.present? && @project.avatar.image? @@ -74,14 +74,14 @@ module Projects Projects::ForksCountService.new(@project).refresh_cache end + def target_namespace + @target_namespace ||= @params[:namespace] || current_user.namespace + end + def allowed_visibility_level - project_level = @project.visibility_level + target_level = [@project.visibility_level, target_namespace.visibility_level].min - if Gitlab::VisibilityLevel.non_restricted_level?(project_level) - project_level - else - Gitlab::VisibilityLevel.highest_allowed_level - end + Gitlab::VisibilityLevel.closest_allowed_level(target_level) end end end diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb index 7212e7524ab..67178de75de 100644 --- a/app/services/projects/hashed_storage/migrate_repository_service.rb +++ b/app/services/projects/hashed_storage/migrate_repository_service.rb @@ -27,7 +27,9 @@ module Projects result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki") end - unless result + if result + project.write_repository_config + else rollback_folder_move project.storage_version = nil end diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index e5cd6fcdfe3..26765e5c3f3 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -75,6 +75,8 @@ module Projects project.old_path_with_namespace = @old_path project.expires_full_path_cache + write_repository_config(@new_path) + execute_system_hooks end rescue Exception # rubocop:disable Lint/RescueException @@ -98,6 +100,10 @@ module Projects project.save! end + def write_repository_config(full_path) + project.write_repository_config(gl_full_path: full_path) + end + def refresh_permissions # This ensures we only schedule 1 job for every user that has access to # the namespaces. @@ -110,6 +116,7 @@ module Projects def rollback_side_effects rollback_folder_move update_namespace_and_visibility(@old_namespace) + write_repository_config(@old_path) end def rollback_folder_move diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb index c499f384426..842fe4e09c4 100644 --- a/app/services/projects/unlink_fork_service.rb +++ b/app/services/projects/unlink_fork_service.rb @@ -5,7 +5,7 @@ module Projects if fork_source = @project.fork_source fork_source.lfs_objects.find_each do |lfs_object| - lfs_object.projects << @project + lfs_object.projects << @project unless lfs_object.projects.include?(@project) end refresh_forks_count(fork_source) diff --git a/app/services/protected_branches/create_service.rb b/app/services/protected_branches/create_service.rb index a84e335340d..6212fd69077 100644 --- a/app/services/protected_branches/create_service.rb +++ b/app/services/protected_branches/create_service.rb @@ -2,8 +2,8 @@ module ProtectedBranches class CreateService < BaseService attr_reader :protected_branch - def execute - raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_project, project) + def execute(skip_authorization: false) + raise Gitlab::Access::AccessDeniedError unless skip_authorization || can?(current_user, :admin_project, project) project.protected_branches.create(params) end diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 06ac86cd5a9..669c1ba0a22 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -405,7 +405,7 @@ module QuickActions if time_spent @updates[:spend_time] = { duration: time_spent, - user: current_user, + user_id: current_user.id, spent_at: time_spent_date } end @@ -428,7 +428,7 @@ module QuickActions current_user.can?(:"admin_#{issuable.to_ability_name}", project) end command :remove_time_spent do - @updates[:spend_time] = { duration: :reset, user: current_user } + @updates[:spend_time] = { duration: :reset, user_id: current_user.id } end desc "Append the comment with #{SHRUG}" diff --git a/app/services/reset_project_cache_service.rb b/app/services/reset_project_cache_service.rb new file mode 100644 index 00000000000..a162a6eedb9 --- /dev/null +++ b/app/services/reset_project_cache_service.rb @@ -0,0 +1,5 @@ +class ResetProjectCacheService < BaseService + def execute + @project.increment!(:jobs_cache_index) + end +end diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index ff188102b62..92a32e703af 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -1,13 +1,16 @@ module Search class GlobalService attr_accessor :current_user, :params + attr_reader :default_project_filter def initialize(user, params) @current_user, @params = user, params.dup + @default_project_filter = true end def execute - Gitlab::SearchResults.new(current_user, projects, params[:search]) + Gitlab::SearchResults.new(current_user, projects, params[:search], + default_project_filter: default_project_filter) end def projects diff --git a/app/services/search/group_service.rb b/app/services/search/group_service.rb index 29478e3251f..b4efba68715 100644 --- a/app/services/search/group_service.rb +++ b/app/services/search/group_service.rb @@ -5,6 +5,7 @@ module Search def initialize(user, group, params) super(user, params) + @default_project_filter = false @group = group end diff --git a/app/services/spam_check_service.rb b/app/services/spam_check_service.rb index 11030bee8f1..d4ade869777 100644 --- a/app/services/spam_check_service.rb +++ b/app/services/spam_check_service.rb @@ -7,16 +7,19 @@ # - params with :request # module SpamCheckService + # rubocop:disable Gitlab/ModuleWithInstanceVariables def filter_spam_check_params @request = params.delete(:request) @api = params.delete(:api) @recaptcha_verified = params.delete(:recaptcha_verified) @spam_log_id = params.delete(:spam_log_id) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables # In order to be proceed to the spam check process, @spammable has to be # a dirty instance, which means it should be already assigned with the new # attribute values. + # rubocop:disable Gitlab/ModuleWithInstanceVariables def spam_check(spammable, user) spam_service = SpamService.new(spammable, @request) @@ -24,4 +27,5 @@ module SpamCheckService user.spam_logs.find_by(id: @spam_log_id)&.update!(recaptcha_verified: true) end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 575853fd66b..c2ca404b179 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -31,12 +31,20 @@ class TodoService mark_pending_todos_as_done(issue, current_user) end - # When we destroy an issuable we should: + # When we destroy a todo target we should: # - # * refresh the todos count cache for the current user + # * refresh the todos count cache for all users with todos on the target # - def destroy_issuable(issuable, user) - user.update_todos_count_cache + # This needs to yield back to the caller to destroy the target, because it + # collects the todo users before the todos themselves are deleted, then + # updates the todo counts for those users. + # + def destroy_target(target) + todo_users = User.where(id: target.todos.pending.select(:user_id)).to_a + + yield target + + todo_users.each(&:update_todos_count_cache) end # When we reassign an issue we should: diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb index 8e20de8dfa5..00db8a2c434 100644 --- a/app/services/users/destroy_service.rb +++ b/app/services/users/destroy_service.rb @@ -31,6 +31,11 @@ module Users return user end + # Calling all before/after_destroy hooks for the user because + # there is no dependent: destroy in the relationship. And the removal + # is done by a foreign_key. Otherwise they won't be called + user.members.find_each { |member| member.run_callbacks(:destroy) } + user.solo_owned_groups.each do |group| Groups::DestroyService.new(group, current_user).execute end diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 535251fef5e..25946ba6eaf 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -11,23 +11,7 @@ .search-field-holder = search_field_tag :name, project_name, class: "form-control search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: 'Search by name' = icon("search", class: "search-icon") - .dropdown - - toggle_text = @sort.present? ? sort_options_hash[@sort] : sort_title_recently_created - = dropdown_toggle(toggle_text, { toggle: 'dropdown' }) - %ul.dropdown-menu.dropdown-menu-align-right - %li.dropdown-header - Sort by - %li - = link_to admin_groups_path(sort: sort_value_recently_created, name: project_name) do - = sort_title_recently_created - = link_to admin_groups_path(sort: sort_value_oldest_created, name: project_name) do - = sort_title_oldest_created - = link_to admin_groups_path(sort: sort_value_recently_updated, name: project_name) do - = sort_title_recently_updated - = link_to admin_groups_path(sort: sort_value_oldest_updated, name: project_name) do - = sort_title_oldest_updated - = link_to admin_groups_path(sort: sort_value_largest_group, name: project_name) do - = sort_title_largest_group + = render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash = link_to new_admin_group_path, class: "btn btn-new" do New group %ul.content-list diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml index 6bf979a937e..23f9927cfee 100644 --- a/app/views/admin/system_info/show.html.haml +++ b/app/views/admin/system_info/show.html.haml @@ -15,7 +15,7 @@ Unable to collect CPU info .col-sm-4 .light-well - %h4 Memory + %h4 Memory Usage .data - if @memory %h1 #{number_to_human_size(@memory.active_bytes)} / #{number_to_human_size(@memory.total_bytes)} @@ -24,7 +24,7 @@ Unable to collect memory info .col-sm-4 .light-well - %h4 Disks + %h4 Disk Usage .data - @disks.each do |disk| %h1 #{number_to_human_size(disk[:bytes_used])} / #{number_to_human_size(disk[:bytes_total])} @@ -34,4 +34,4 @@ .light-well %h4 Uptime .data - %h1= time_ago_with_tooltip(Rails.application.config.booted_at) + %h1= distance_of_time_in_words_to_now(Rails.application.config.booted_at) diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 98ff592eb64..63c5a15de1c 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -157,7 +157,6 @@ %ul %li User will not be able to login %li User will not be able to access git repositories - %li User will be removed from joined projects and groups %li Personal projects will be left %li Owned groups will be left %br diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml index 2bac69bc536..6e399fc7392 100644 --- a/app/views/ci/variables/_index.html.haml +++ b/app/views/ci/variables/_index.html.haml @@ -10,5 +10,7 @@ %p.settings-message.text-center.append-bottom-0 No variables found, add one with the form above. - else - = render "ci/variables/table" - %button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values + .js-secret-variable-table + = render "ci/variables/table" + %button.btn.btn-info.js-secret-value-reveal-button{ data: { secret_reveal_status: 'false' } } + = n_('Reveal value', 'Reveal values', @variables.size) diff --git a/app/views/ci/variables/_table.html.haml b/app/views/ci/variables/_table.html.haml index 71a0b56c4f4..2298930d0c7 100644 --- a/app/views/ci/variables/_table.html.haml +++ b/app/views/ci/variables/_table.html.haml @@ -15,7 +15,11 @@ - if variable.id? %tr %td.variable-key= variable.key - %td.variable-value{ "data-value" => variable.value }****** + %td.variable-value + %span.js-secret-value-placeholder + = '*' * 6 + %span.hide.js-secret-value + = variable.value %td.variable-protected= Gitlab::Utils.boolean_to_yes_no(variable.protected) %td.variable-menu = link_to variable.edit_path, class: "btn btn-transparent btn-variable-edit" do diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index a5686002328..20ca6ec969a 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -83,12 +83,12 @@ You're all done! - elsif current_user.todos.any? .todos-all-done - .svg-content + .svg-content.svg-250 = image_tag 'illustrations/todos_all_done.svg' - if todos_filter_empty? %h4.text-center = Gitlab.config.gitlab.no_todos_messages.sample - %p.text-center + %p Are you looking for things to do? Take a look at = succeed "," do = link_to "the opened issues", issues_dashboard_path @@ -104,7 +104,7 @@ = image_tag 'illustrations/todos_empty.svg' .todos-empty-content %h4 - Todos let you see what you should do next. + Todos let you see what you should do next %p When an issue or merge request is assigned to you, or when you %strong diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index 72eab964766..6364f0be4a3 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "Applications", oauth_applications_path +- breadcrumb_title @application.name - page_title @application.name, "Applications" - @content_class = "limit-container-width" unless fluid_layout diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index 9a763887b30..f85f5c5be88 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -7,7 +7,8 @@ %span.pushed #{event.action_name} #{event.ref_type} %strong - commits_link = project_commits_path(project, event.ref_name) - = link_to_if project.repository.branch_exists?(event.ref_name), event.ref_name, commits_link, class: 'ref-name' + - should_link = event.tag? ? project.repository.tag_exists?(event.ref_name) : project.repository.branch_exists?(event.ref_name) + = link_to_if should_link, event.ref_name, commits_link, class: 'ref-name' = render "events/event_scope", event: event diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 16038ef2f79..76a8099d7c0 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -13,13 +13,13 @@ = group_icon(@group, alt: '', class: 'avatar group-avatar s160') %p.light - if @group.avatar? - You can change your group avatar here + You can change the group avatar here - else You can upload a group avatar here = render 'shared/choose_group_avatar_button', f: f - if @group.avatar? %hr - = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" + = link_to _('Remove avatar'), group_avatar_path(@group.to_param), data: { confirm: _("Avatar will be removed. Are you sure?")}, method: :delete, class: "btn btn-danger btn-inverted" = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 021de4f0caf..b8692009225 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,3 +1,5 @@ += webpack_bundle_tag 'docs' + %div - if current_application_settings.help_page_text.present? = markdown_field(current_application_settings, :help_page_text) @@ -37,8 +39,12 @@ Quick help %ul.well-list %li= link_to 'See our website for getting help', support_url - %li= link_to 'Use the search bar on the top of this page', '#', onclick: 'Shortcuts.focusSearch(event)' - %li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.toggleHelp()' + %li + %button.btn-blank.btn-link.js-trigger-search-bar{ type: 'button' } + Use the search bar on the top of this page + %li + %button.btn-blank.btn-link.js-trigger-shortcut{ type: 'button' } + Use shortcuts - unless current_application_settings.help_page_hide_commercial_content? %li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/' %li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare' diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml new file mode 100644 index 00000000000..cb413f197de --- /dev/null +++ b/app/views/ide/index.html.haml @@ -0,0 +1,12 @@ +- page_title 'IDE' + +- content_for :page_specific_javascripts do + = webpack_bundle_tag 'common_vue' + = webpack_bundle_tag 'ide' + +.ide-flash-container.flash-container + +#ide.ide-loading{ data: {"empty-state-svg-path" => image_path('illustrations/multi_file_editor_empty.svg')} } + .text-center + = icon('spinner spin 2x') + %h2.clgray= _('Loading the GitLab IDE...') diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 25ed610466a..eba9cd253bb 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,7 +1,7 @@ -.page-with-sidebar{ class: page_with_sidebar_class } +.layout-page{ class: page_with_sidebar_class } - if defined?(nav) && nav = render "layouts/nav/sidebar/#{nav}" - .content-wrapper.page-with-new-nav + .content-wrapper = render 'shared/outdated_browser' .mobile-overlay .alert-wrapper diff --git a/app/views/layouts/_recaptcha_verification.html.haml b/app/views/layouts/_recaptcha_verification.html.haml index 77c77dc6754..e6f87ddd383 100644 --- a/app/views/layouts/_recaptcha_verification.html.haml +++ b/app/views/layouts/_recaptcha_verification.html.haml @@ -1,5 +1,4 @@ - humanized_resource_name = spammable.class.model_name.human.downcase -- resource_name = spammable.class.model_name.singular %h3.page-title Anti-spam verification @@ -8,16 +7,4 @@ %p #{"We detected potential spam in the #{humanized_resource_name}. Please solve the reCAPTCHA to proceed."} -= form_for form do |f| - .recaptcha - - params[resource_name].each do |field, value| - = hidden_field(resource_name, field, value: value) - = hidden_field_tag(:spam_log_id, spammable.spam_log.id) - = hidden_field_tag(:recaptcha_verification, true) - = recaptcha_tags - - -# Yields a block with given extra params. - = yield - - .row-content-block.footer-block - = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-create' += render 'shared/recaptcha_form', spammable: spammable diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 99e7f3b568d..39eb71c2bac 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -56,6 +56,8 @@ = link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username } %li = link_to "Settings", profile_path + %li + = link_to "Turn on multi edit", profile_preferences_path - if current_user %li = link_to "Help", help_path diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index e0d8d9cb402..4013181da9c 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -49,6 +49,12 @@ = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do Projects + - if current_controller?('ide') + %li.line-separator.hidden-xs + = nav_link(controller: 'ide') do + = link_to '#', class: 'dashboard-shortcuts-web-ide', title: 'Web IDE' do + Web IDE + - if current_user.admin? || Gitlab::Sherlock.enabled? %li.line-separator.hidden-xs - if current_user.admin? diff --git a/app/views/layouts/nav/projects_dropdown/_show.html.haml b/app/views/layouts/nav/projects_dropdown/_show.html.haml index a7370180bf6..32a24c101fc 100644 --- a/app/views/layouts/nav/projects_dropdown/_show.html.haml +++ b/app/views/layouts/nav/projects_dropdown/_show.html.haml @@ -1,4 +1,4 @@ -- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: @project.web_url, avatar_url: @project.avatar_url } if @project&.persisted? +- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: project_path(@project), avatar_url: @project.avatar_url } if @project&.persisted? .projects-dropdown-container .project-dropdown-sidebar %ul diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml index 0ec07605631..dd086f70641 100644 --- a/app/views/layouts/nav/sidebar/_admin.html.haml +++ b/app/views/layouts/nav/sidebar/_admin.html.haml @@ -1,4 +1,4 @@ -.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } +.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar-inner-scroll .context-header = link_to admin_root_path, title: 'Admin Overview' do @@ -130,7 +130,7 @@ %span.badge.count= number_with_delimiter(AbuseReport.count(:all)) %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do - = link_to admin_broadcast_messages_path do + = link_to admin_abuse_reports_path do %strong.fly-out-top-item-name #{ _('Abuse Reports') } %span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(AbuseReport.count(:all)) diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 0bf318b0b66..0c27b09f7b1 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -1,7 +1,7 @@ - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute -.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } +.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar-inner-scroll .context-header = link_to group_path(@group), title: @group.name do diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index 7e23f9c1f05..a5a62a0695f 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -1,4 +1,4 @@ -.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } +.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar-inner-scroll .context-header = link_to profile_path, title: 'Profile Settings' do diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 53a9162b703..1fa3a3041fd 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -1,4 +1,4 @@ -.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } +.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar-inner-scroll - can_edit = can?(current_user, :admin_project, @project) .context-header @@ -299,9 +299,10 @@ Charts -# Shortcut to Issues > New Issue - %li.hidden - = link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do - Create a new issue + - if project_nav_tab?(:issues) + %li.hidden + = link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do + Create a new issue -# Shortcut to Pipelines > Jobs - if project_nav_tab? :builds @@ -316,5 +317,6 @@ Commits -# Shortcut to issue boards - %li.hidden - = link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards' + - if project_nav_tab?(:issues) + %li.hidden + = link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards' diff --git a/app/views/layouts/nav_only.html.haml b/app/views/layouts/nav_only.html.haml new file mode 100644 index 00000000000..6fa4b39dc10 --- /dev/null +++ b/app/views/layouts/nav_only.html.haml @@ -0,0 +1,13 @@ +!!! 5 +%html{ lang: I18n.locale, class: page_class } + = render "layouts/head" + %body{ class: "#{user_application_theme} #{@body_class}", data: { page: body_data_page } } + = render 'peek/bar' + = render "layouts/header/default" + = render 'shared/outdated_browser' + .mobile-overlay + .alert-wrapper + = render "layouts/broadcast" + = yield :flash_message + = render "layouts/flash" + = yield diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml index 574a8f2fa50..bae37292d62 100644 --- a/app/views/notify/pipeline_success_email.html.haml +++ b/app/views/notify/pipeline_success_email.html.haml @@ -109,7 +109,7 @@ API %tr %td{ colspan: 2, style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:300;line-height:1.4;padding:15px 5px;text-align:center;" } - - job_count = @pipeline.statuses.latest.size + - job_count = @pipeline.total_size - stage_count = @pipeline.stages_count successfully completed #{job_count} #{'job'.pluralize(job_count)} diff --git a/app/views/notify/pipeline_success_email.text.erb b/app/views/notify/pipeline_success_email.text.erb index ddced2279e1..39622cf7f02 100644 --- a/app/views/notify/pipeline_success_email.text.erb +++ b/app/views/notify/pipeline_success_email.text.erb @@ -22,11 +22,11 @@ Committed by: <%= commit.committer_name %> <% end -%> <% end -%> -<% build_count = @pipeline.statuses.latest.size -%> +<% job_count = @pipeline.total_size -%> <% stage_count = @pipeline.stages_count -%> <% if @pipeline.user -%> Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by <%= @pipeline.user.name %> ( <%= user_url(@pipeline.user) %> ) <% else -%> Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by API <% end -%> -successfully completed <%= build_count %> <%= 'build'.pluralize(build_count) %> in <%= stage_count %> <%= 'stage'.pluralize(stage_count) %>. +successfully completed <%= job_count %> <%= 'job'.pluralize(job_count) %> in <%= stage_count %> <%= 'stage'.pluralize(stage_count) %>. diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index ced58dffcdc..79e197ad08b 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -17,10 +17,6 @@ Status: #{current_user.two_factor_enabled? ? 'Enabled' : 'Disabled'} - if current_user.two_factor_enabled? = link_to 'Manage two-factor authentication', profile_two_factor_auth_path, class: 'btn btn-info' - = link_to 'Disable', profile_two_factor_auth_path, - method: :delete, - data: { confirm: "Are you sure? This will invalidate your registered applications and U2F devices." }, - class: 'btn btn-danger' - else .append-bottom-10 = link_to 'Enable two-factor authentication', profile_two_factor_auth_path, class: 'btn btn-success' @@ -88,11 +84,13 @@ = s_('Profiles|Deleting an account has the following effects:') = render 'users/deletion_guidance', user: current_user + %button#delete-account-button.btn.btn-danger.disabled{ data: { toggle: 'modal', + target: '#delete-account-modal' } } + = s_('Profiles|Delete account') + #delete-account-modal{ data: { action_url: user_registration_path, confirm_with_password: ('true' if current_user.confirm_deletion_with_password?), username: current_user.username } } - %button.btn.btn-danger.disabled - = s_('Profiles|Delete account') - else - if @user.solo_owned_groups.present? %p diff --git a/app/views/profiles/audit_log.html.haml b/app/views/profiles/audit_log.html.haml index 1a392e29e2a..cbea5ca605a 100644 --- a/app/views/profiles/audit_log.html.haml +++ b/app/views/profiles/audit_log.html.haml @@ -4,7 +4,7 @@ .row.prepend-top-default .col-lg-4.profile-settings-sidebar - %h3.prepend-top-0 + %h4.prepend-top-0 = page_title %p This is a security log of important events involving your account. diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml index 8dbb8aef31b..86ebec0179c 100644 --- a/app/views/profiles/gpg_keys/index.html.haml +++ b/app/views/profiles/gpg_keys/index.html.haml @@ -1,4 +1,5 @@ - page_title "GPG Keys" +- @content_class = "limit-container-width" unless fluid_layout = render 'profiles/head' .row.prepend-top-default diff --git a/app/views/profiles/keys/show.html.haml b/app/views/profiles/keys/show.html.haml index 172c0450381..7b7960708c4 100644 --- a/app/views/profiles/keys/show.html.haml +++ b/app/views/profiles/keys/show.html.haml @@ -1,3 +1,5 @@ +- add_to_breadcrumbs "SSH Keys", profile_keys_path +- breadcrumb_title @key.title - page_title @key.title, "SSH Keys" - @content_class = "limit-container-width" unless fluid_layout = render 'profiles/head' diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index e98fdfc7a3d..202eccb7bb6 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -10,16 +10,16 @@ %li= msg = hidden_field_tag :notification_type, 'global' - .row + .row.prepend-top-default .col-lg-4.profile-settings-sidebar - %h4 + %h4.prepend-top-0 = page_title %p You can specify notification level per group or per project. %p By default, all projects and groups will use the global notifications setting. .col-lg-8 - %h5 + %h5.prepend-top-0 Global notification settings = form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f| diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 66d1d1e8d44..65328791ce5 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -3,6 +3,23 @@ = render 'profiles/head' = form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f| + .col-lg-4 + %h4.prepend-top-0 + GitLab multi file editor + %p Unlock an additional editing experience which makes it possible to edit and commit multiple files + .col-lg-8.multi-file-editor-options + = label_tag do + .preview.append-bottom-10= image_tag "multi-editor-off.png" + = f.radio_button :multi_file, "off", checked: true + Off + = label_tag do + .preview.append-bottom-10= image_tag "multi-editor-on.png" + = f.radio_button :multi_file, "on", checked: false + On + + .col-sm-12 + %hr + .col-lg-4.application-theme %h4.prepend-top-0 GitLab navigation theme diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 79f334176a5..0f773933ac2 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -22,18 +22,15 @@ .clearfix.avatar-image.append-bottom-default = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160' - %h5.prepend-top-0 - Upload new avatar + %h5.prepend-top-0= _("Upload new avatar") .prepend-top-5.append-bottom-10 - %a.btn.js-choose-user-avatar-button - Browse file... - %span.avatar-file-name.prepend-left-default.js-avatar-filename No file chosen + %button.btn.js-choose-user-avatar-button{ type: 'button' }= _("Choose file...") + %span.avatar-file-name.prepend-left-default.js-avatar-filename= _("No file chosen") = f.file_field_without_bootstrap :avatar, class: 'js-user-avatar-input hidden', accept: 'image/*' - .help-block - The maximum file size allowed is 200KB. + .help-block= _("The maximum file size allowed is 200KB.") - if @user.avatar? %hr - = link_to 'Remove avatar', profile_avatar_path, data: { confirm: 'Avatar will be removed. Are you sure?' }, method: :delete, class: 'btn btn-gray' + = link_to _('Remove avatar'), profile_avatar_path, data: { confirm: _('Avatar will be removed. Are you sure?') }, method: :delete, class: 'btn btn-danger btn-inverted' %hr .row .col-lg-4.profile-settings-sidebar diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 0b03276efcc..5207dac3ac2 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -1,5 +1,5 @@ - page_title 'Two-Factor Authentication', 'Account' -- add_to_breadcrumbs("Account", profile_account_path) +- add_to_breadcrumbs("Two-Factor Authentication", profile_account_path) - @content_class = "limit-container-width" unless fluid_layout = render 'profiles/head' @@ -18,7 +18,12 @@ Use an app on your mobile device to enable two-factor authentication (2FA). .col-lg-8 - if current_user.two_factor_otp_enabled? - = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page." + %p + You've already enabled two-factor authentication using mobile authenticator applications. In order to register a different device, you must first disable two-factor authentication. + = link_to 'Disable two-factor authentication', profile_two_factor_auth_path, + method: :delete, + data: { confirm: "Are you sure? This will invalidate your registered applications and U2F devices." }, + class: 'btn btn-danger' - else %p Download the Google Authenticator application from App Store or Google Play Store and scan this code. diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index 3a7a99462a6..79530e78154 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -7,7 +7,7 @@ .nav-block = render 'projects/tree/tree_header', tree: @tree - - if !show_new_repo? && commit + - if commit = render 'shared/commit_well', commit: commit, ref: ref, project: project = render 'projects/tree/tree_content', tree: @tree, content_url: content_url diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml index 56eecece54c..b68eb47c6b4 100644 --- a/app/views/projects/_last_push.html.haml +++ b/app/views/projects/_last_push.html.haml @@ -1,18 +1,19 @@ - event = last_push_event - if event && show_last_push_widget?(event) - .row-content-block.top-block.hidden-xs.white - .event-last-push - .event-last-push-text - %span= s_("LastPushEvent|You pushed to") - %strong - = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name' + %div{ class: container_class } + .row-content-block.top-block.hidden-xs.white + .event-last-push + .event-last-push-text + %span= s_("LastPushEvent|You pushed to") + %strong + = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name' - - if event.project != @project - %span= s_("LastPushEvent|at") - %strong= link_to_project event.project + - if event.project != @project + %span= s_("LastPushEvent|at") + %strong= link_to_project event.project - #{time_ago_with_tooltip(event.created_at)} + #{time_ago_with_tooltip(event.created_at)} - .pull-right - = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do - #{ _('Create merge request') } + .pull-right + = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do + #{ _('Create merge request') } diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index c5e3a7945bd..8212ab9a31e 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -17,7 +17,7 @@ %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 } Preview - %li.md-header-toolbar + %li.md-header-toolbar.active = markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: "Add bold text" }) = markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: "Add italic text" }) = markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" }) diff --git a/app/views/projects/_merge_request_fast_forward_settings.html.haml b/app/views/projects/_merge_request_fast_forward_settings.html.haml index 9d357293a2f..8129c72feb2 100644 --- a/app/views/projects/_merge_request_fast_forward_settings.html.haml +++ b/app/views/projects/_merge_request_fast_forward_settings.html.haml @@ -10,4 +10,4 @@ No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. %br %span.descr - When fast-forward merge is not possible, the user must first rebase locally. + When fast-forward merge is not possible, the user is given the option to rebase. diff --git a/app/views/projects/_merge_request_merge_settings.html.haml b/app/views/projects/_merge_request_merge_settings.html.haml index 1dd8778f800..f6e5712ce81 100644 --- a/app/views/projects/_merge_request_merge_settings.html.haml +++ b/app/views/projects/_merge_request_merge_settings.html.haml @@ -8,7 +8,7 @@ %br %span.descr Pipelines need to be configured to enable this feature. - = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds') + = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds'), target: '_blank' .checkbox = form.label :only_allow_merge_if_all_discussions_are_resolved do = form.check_box :only_allow_merge_if_all_discussions_are_resolved diff --git a/app/views/projects/_merge_request_rebase_settings.html.haml b/app/views/projects/_merge_request_rebase_settings.html.haml index c52e09573a6..54e0b73d24c 100644 --- a/app/views/projects/_merge_request_rebase_settings.html.haml +++ b/app/views/projects/_merge_request_rebase_settings.html.haml @@ -10,4 +10,4 @@ This way you could make sure that if this merge request would build, after merging to target branch it would also build. %br %span.descr - When fast-forward merge is not possible, the user must first rebase locally. + When fast-forward merge is not possible, the user is given the option to rebase. diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index fe02cbcbf95..7ff7466e561 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -18,7 +18,7 @@ .tree-controls = link_to download_project_job_artifacts_path(@project, @build), rel: 'nofollow', download: '', class: 'btn btn-default download' do - = icon('download') + = sprite_icon('download') Download artifacts archive .tree-content-holder diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 7777f55ddd7..c9fa90acd11 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -3,7 +3,7 @@ .file-holder-bottom-radius.file-holder.file.append-bottom-default .js-file-title.file-title.clearfix{ data: { current_action: action } } .editor-ref - = icon('code-fork') + = sprite_icon('fork', size: 12) = ref %span.editor-file-name - if current_action?(:edit) || current_action?(:update) diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index 281363d2e01..2a77dedd9a2 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -12,6 +12,7 @@ .btn-group{ role: "group" }< = edit_blob_link + = ide_blob_link - if current_user = replace_blob_link = delete_blob_link diff --git a/app/views/projects/blob/_header_content.html.haml b/app/views/projects/blob/_header_content.html.haml index 98bedae650a..5d457a50c49 100644 --- a/app/views/projects/blob/_header_content.html.haml +++ b/app/views/projects/blob/_header_content.html.haml @@ -8,3 +8,6 @@ %small = number_to_human_size(blob.raw_size) + + - if blob.stored_externally? && blob.external_storage == :lfs + %span.label.label-lfs.append-right-5 LFS diff --git a/app/views/projects/blob/_template_selectors.html.haml b/app/views/projects/blob/_template_selectors.html.haml index 2a178325041..5b092427496 100644 --- a/app/views/projects/blob/_template_selectors.html.haml +++ b/app/views/projects/blob/_template_selectors.html.haml @@ -3,15 +3,15 @@ Template .template-selector-dropdowns-wrap .template-type-selector.js-template-type-selector-wrap.hidden - = dropdown_tag("Choose type", options: { toggle_class: 'btn js-template-type-selector', title: "Choose a template type" } ) + = dropdown_tag("Choose type", options: { toggle_class: 'js-template-type-selector', title: "Choose a template type" } ) .license-selector.js-license-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a license template", options: { toggle_class: 'btn js-license-selector', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } ) + = dropdown_tag("Apply a license template", options: { toggle_class: 'js-license-selector', title: "Apply a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } ) .gitignore-selector.js-gitignore-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a .gitignore template", options: { toggle_class: 'btn js-gitignore-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) + = dropdown_tag("Apply a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } ) .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a GitLab CI Yaml template", options: { toggle_class: 'btn js-gitlab-ci-yml-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) + = dropdown_tag("Apply a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } ) .dockerfile-selector.js-dockerfile-selector-wrap.js-template-selector-wrap.hidden - = dropdown_tag("Apply a Dockerfile template", options: { toggle_class: 'btn js-dockerfile-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } ) + = dropdown_tag("Apply a Dockerfile template", options: { toggle_class: 'js-dockerfile-selector', title: "Apply a template", filter: true, placeholder: "Filter", data: { data: dockerfile_names } } ) .template-selectors-undo-menu.hidden %span.text-info Template applied %button.btn.btn-sm.btn-info Undo diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index c4712bf3736..4d358052d43 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -6,21 +6,14 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'blob' - - if show_new_repo? - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'repo' - = render 'projects/last_push' %div{ class: container_class } - - if show_new_repo? - = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_blob_path(@project, @id) - - else - #tree-holder.tree-holder - = render 'blob', blob: @blob + #tree-holder.tree-holder + = render 'blob', blob: @blob - if can_modify_blob?(@blob) = render 'projects/blob/remove' - - title = "Replace #{@blob.name}" - = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put + - title = "Replace #{@blob.name}" + = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put diff --git a/app/views/projects/blob/viewers/_dependency_manager.html.haml b/app/views/projects/blob/viewers/_dependency_manager.html.haml index a0f0215a5ff..87aa7c1dbf8 100644 --- a/app/views/projects/blob/viewers/_dependency_manager.html.haml +++ b/app/views/projects/blob/viewers/_dependency_manager.html.haml @@ -6,6 +6,6 @@ - if viewer.package_name and defines a #{viewer.package_type} named %strong< - = link_to viewer.package_name, viewer.package_url, target: '_blank', rel: 'noopener noreferrer' + = link_to_if viewer.package_url.present?, viewer.package_name, viewer.package_url, target: '_blank', rel: 'noopener noreferrer' = link_to 'Learn more', viewer.manager_url, target: '_blank', rel: 'noopener noreferrer' diff --git a/app/views/projects/blob/viewers/_download.html.haml b/app/views/projects/blob/viewers/_download.html.haml index 253566c43be..f9b1da05a00 100644 --- a/app/views/projects/blob/viewers/_download.html.haml +++ b/app/views/projects/blob/viewers/_download.html.haml @@ -2,6 +2,6 @@ .center.render-error.vertical-center = link_to blob_raw_path do %h1.light - = icon('download') + = sprite_icon('download') %h4 Download (#{number_to_human_size(viewer.blob.raw_size)}) diff --git a/app/views/projects/blob/viewers/_image.html.haml b/app/views/projects/blob/viewers/_image.html.haml index 26ea028c5d7..2a8cefac005 100644 --- a/app/views/projects/blob/viewers/_image.html.haml +++ b/app/views/projects/blob/viewers/_image.html.haml @@ -1,2 +1,3 @@ .file-content.image_file - = image_tag(blob_raw_path, alt: viewer.blob.name) + -# Uses the full URL rather than the path, to prevent it from getting prefixed with the asset host. + = image_tag(blob_raw_url, alt: viewer.blob.name) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 573050e597d..1da0e865a41 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -8,7 +8,8 @@ %li{ class: "js-branch-#{branch.name}" } %div = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do - = icon('code-fork', class: 'append-right-5') + "#{branch.name}" + = sprite_icon('fork', size: 12) + = branch.name - if branch.name == @repository.root_ref %span.label.label-primary default @@ -65,16 +66,16 @@ = icon("trash-o") - if branch.name != @repository.root_ref - .divergence-graph{ title: s_('%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead') % { number_commits_behind: number_commits_behind, + .divergence-graph{ title: s_('%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead') % { number_commits_behind: diverging_count_label(number_commits_behind), default_branch: @repository.root_ref, - number_commits_ahead: number_commits_ahead } } + number_commits_ahead: diverging_count_label(number_commits_ahead) } } .graph-side .bar.bar-behind{ style: "width: #{number_commits_behind * bar_graph_width_factor}%" } - %span.count.count-behind= number_commits_behind + %span.count.count-behind= diverging_count_label(number_commits_behind) .graph-separator .graph-side .bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" } - %span.count.count-ahead= number_commits_ahead + %span.count.count-ahead= diverging_count_label(number_commits_ahead) - if commit diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 2baaaf6ac5b..e9d8fc75142 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -20,7 +20,7 @@ .col-sm-10.create-from .dropdown = hidden_field_tag :ref, default_ref - = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide form-control js-branch-select git-revision-dropdown-toggle', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do + = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select git-revision-dropdown-toggle', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do .text-left.dropdown-toggle-text= default_ref = icon('chevron-down') = render 'shared/ref_dropdown', dropdown_class: 'wide' diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index fa6f6d0b588..fa9a9bfc8f7 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -3,7 +3,7 @@ - if !project.empty_repo? && can?(current_user, :download_code, project) .project-action-button.dropdown.inline> %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') } - = icon('download') + = sprite_icon('download') = icon("caret-down") %span.sr-only= _('Select Archive Format') %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 86510b8ab93..0cd2d45c74b 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -22,7 +22,7 @@ - if ref - if job.ref .icon-container - = job.tag? ? icon('tag') : icon('code-fork') + = job.tag? ? icon('tag') : sprite_icon('fork', css_class: 'sprite') = link_to job.ref, project_ref_path(job.project, job.ref), class: "ref-name" - else .light none @@ -96,7 +96,7 @@ .pull-right - if can?(current_user, :read_build, job) && job.artifacts? = link_to download_project_job_artifacts_path(job.project, job), rel: 'nofollow', download: '', title: 'Download artifacts', class: 'btn btn-build' do - = icon('download') + = sprite_icon('download') - if can?(current_user, :update_build, job) - if job.active? = link_to cancel_project_job_path(job.project, job, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do diff --git a/app/views/projects/clusters/_advanced_settings.html.haml b/app/views/projects/clusters/_advanced_settings.html.haml index 7032b892029..8a13713ae02 100644 --- a/app/views/projects/clusters/_advanced_settings.html.haml +++ b/app/views/projects/clusters/_advanced_settings.html.haml @@ -11,5 +11,5 @@ %label.text-danger = s_('ClusterIntegration|Remove cluster integration') %p - = s_('ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine.') - = link_to(s_('ClusterIntegration|Remove integration'), namespace_project_cluster_path(@project.namespace, @project, @cluster.id), method: :delete, class: 'btn btn-danger', data: { confirm: "Are you sure you want to remove cluster integration from this project? This will not delete your cluster on Google Kubernetes Engine"}) + = s_("ClusterIntegration|Remove this cluster's configuration from this project. This will not delete your actual cluster.") + = link_to(s_('ClusterIntegration|Remove integration'), namespace_project_cluster_path(@project.namespace, @project, @cluster.id), method: :delete, class: 'btn btn-danger', data: { confirm: s_("ClusterIntegration|Are you sure you want to remove this cluster's integration? This will not delete your actual cluster.")}) diff --git a/app/views/projects/clusters/_banner.html.haml b/app/views/projects/clusters/_banner.html.haml index 76a66fb92a2..26ca3307a4a 100644 --- a/app/views/projects/clusters/_banner.html.haml +++ b/app/views/projects/clusters/_banner.html.haml @@ -1,6 +1,6 @@ -%h4= s_('ClusterIntegration|Enable cluster integration') -.settings-content +%h4= s_('ClusterIntegration|Cluster integration') +.settings-content .hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' } = s_('ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine') %p.js-error-reason @@ -11,11 +11,4 @@ .hidden.js-cluster-success.alert.alert-success.alert-block.append-bottom-10{ role: 'alert' } = s_('ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster\'s details') - %p - - if @cluster.enabled? - - if can?(current_user, :update_cluster, @cluster) - = s_('ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab\'s connection to it.') - - else - = s_('ClusterIntegration|Cluster integration is enabled for this project.') - - else - = s_('ClusterIntegration|Cluster integration is disabled for this project.') + %p= s_('ClusterIntegration|Control how your cluster integrates with GitLab') diff --git a/app/views/projects/clusters/_cluster.html.haml b/app/views/projects/clusters/_cluster.html.haml index 18ca01d2d49..3943dfc0856 100644 --- a/app/views/projects/clusters/_cluster.html.haml +++ b/app/views/projects/clusters/_cluster.html.haml @@ -4,7 +4,7 @@ .table-mobile-content = link_to cluster.name, namespace_project_cluster_path(@project.namespace, @project, cluster) .table-section.section-30 - .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment pattern") + .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment scope") .table-mobile-content= cluster.environment_scope .table-section.section-30 .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Project namespace") @@ -16,7 +16,8 @@ class: "js-toggle-cluster-list project-feature-toggle #{'is-checked' if cluster.enabled?} #{'is-disabled' if !cluster.can_toggle_cluster?}", "aria-label": s_("ClusterIntegration|Toggle Cluster"), disabled: !cluster.can_toggle_cluster?, - data: { "enabled-text": s_("ClusterIntegration|Active"), - "disabled-text": s_("ClusterIntegration|Inactive"), - endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } } + data: { endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } } = icon("spinner spin", class: "loading-icon") + %span.toggle-icon + = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') + = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') diff --git a/app/views/projects/clusters/_empty_state.html.haml b/app/views/projects/clusters/_empty_state.html.haml index e629cc58b06..b525f4efc83 100644 --- a/app/views/projects/clusters/_empty_state.html.haml +++ b/app/views/projects/clusters/_empty_state.html.haml @@ -1,12 +1,12 @@ .row.empty-state .col-xs-12 .svg-content= image_tag 'illustrations/clusters_empty.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - %h4= s_('ClusterIntegration|Integrate cluster automation') + %h4.text-center= s_('ClusterIntegration|Integrate cluster automation') - link_to_help_page = link_to(s_('ClusterIntegration|Learn more about Clusters'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer') %p= s_('ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page} - %p + .text-center = link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success' diff --git a/app/views/projects/clusters/_enabled.html.haml b/app/views/projects/clusters/_enabled.html.haml deleted file mode 100644 index 70c677f7856..00000000000 --- a/app/views/projects/clusters/_enabled.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field| - = form_errors(@cluster) - .form-group.append-bottom-20 - %label.append-bottom-10 - = field.hidden_field :enabled, { class: 'js-toggle-input'} - - %button{ type: 'button', - class: "js-toggle-cluster project-feature-toggle #{'is-checked' unless !@cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}", - "aria-label": s_("ClusterIntegration|Toggle Cluster"), - disabled: !can?(current_user, :update_cluster, @cluster), - data: { "enabled-text": s_("ClusterIntegration|Active"), "disabled-text": s_("ClusterIntegration|Inactive"), } } - - - if can?(current_user, :update_cluster, @cluster) - .form-group - = field.submit _('Save'), class: 'btn btn-success' diff --git a/app/views/projects/clusters/_integration_form.html.haml b/app/views/projects/clusters/_integration_form.html.haml new file mode 100644 index 00000000000..9d593ffc021 --- /dev/null +++ b/app/views/projects/clusters/_integration_form.html.haml @@ -0,0 +1,33 @@ += form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field| + = form_errors(@cluster) + .form-group.append-bottom-20 + %h5= s_('ClusterIntegration|Integration status') + %p + - if @cluster.enabled? + - if can?(current_user, :update_cluster, @cluster) + = s_('ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab\'s connection to it.') + - else + = s_('ClusterIntegration|Cluster integration is enabled for this project.') + - else + = s_('ClusterIntegration|Cluster integration is disabled for this project.') + %label.append-bottom-10 + = field.hidden_field :enabled, { class: 'js-toggle-input'} + + %button{ type: 'button', + class: "js-toggle-cluster project-feature-toggle #{'is-checked' unless !@cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}", + "aria-label": s_("ClusterIntegration|Toggle Cluster"), + disabled: !can?(current_user, :update_cluster, @cluster) } + %span.toggle-icon + = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') + = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') + + .form-group + %h5= s_('ClusterIntegration|Environment scope') + %p + = s_("ClusterIntegration|Choose which of your project's environments will use this cluster.") + = link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments') + = field.text_field :environment_scope, class: 'form-control js-select-on-focus', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope') + + - if can?(current_user, :update_cluster, @cluster) + .form-group + = field.submit _('Save changes'), class: 'btn btn-success' diff --git a/app/views/projects/clusters/_tabs.html.haml b/app/views/projects/clusters/_tabs.html.haml deleted file mode 100644 index c8120e806fa..00000000000 --- a/app/views/projects/clusters/_tabs.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -.top-area.scrolling-tabs-container.inner-page-scroll-tabs - .fade-left= icon("angle-left") - .fade-right= icon("angle-right") - %ul.nav-links.scrolling-tabs - %li{ class: ('active' if @scope == 'active') }> - = link_to project_clusters_path(@project, scope: :active), class: "js-active-tab" do - = s_("ClusterIntegration|Active") - %span.badge= @active_count - %li{ class: ('active' if @scope == 'inactive') }> - = link_to project_clusters_path(@project, scope: :inactive), class: "js-inactive-tab" do - = s_("ClusterIntegration|Inactive") - %span.badge= @inactive_count - %li{ class: ('active' if @scope.nil? || @scope == 'all') }> - = link_to project_clusters_path(@project), class: "js-all-tab" do - = s_("ClusterIntegration|All") - %span.badge= @all_count diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml index 0f6bae97571..e384b60d8d9 100644 --- a/app/views/projects/clusters/gcp/_form.html.haml +++ b/app/views/projects/clusters/gcp/_form.html.haml @@ -7,6 +7,9 @@ .form-group = field.label :name, s_('ClusterIntegration|Cluster name') = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Cluster name') + .form-group + = field.label :environment_scope, s_('ClusterIntegration|Environment scope') + = field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope') = field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field| .form-group diff --git a/app/views/projects/clusters/gcp/_header.html.haml b/app/views/projects/clusters/gcp/_header.html.haml index f23d5b80e4f..e2d7326a312 100644 --- a/app/views/projects/clusters/gcp/_header.html.haml +++ b/app/views/projects/clusters/gcp/_header.html.haml @@ -10,5 +10,5 @@ - link_to_requirements = link_to(s_('ClusterIntegration|meets the requirements'), 'https://cloud.google.com/kubernetes-engine/docs/quickstart', target: '_blank', rel: 'noopener noreferrer') = s_('ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters').html_safe % { link_to_requirements: link_to_requirements } %li - - link_to_container_project = link_to(s_('ClusterIntegration|Google Kubernetes Engine project'), target: '_blank', rel: 'noopener noreferrer') + - link_to_container_project = link_to(s_('ClusterIntegration|Google Kubernetes Engine project'), 'https://console.cloud.google.com/home/dashboard', target: '_blank', rel: 'noopener noreferrer') = s_('ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below').html_safe % { link_to_container_project: link_to_container_project } diff --git a/app/views/projects/clusters/gcp/_show.html.haml b/app/views/projects/clusters/gcp/_show.html.haml index 3fa9f69708a..f3122a1bf47 100644 --- a/app/views/projects/clusters/gcp/_show.html.haml +++ b/app/views/projects/clusters/gcp/_show.html.haml @@ -8,6 +8,7 @@ = form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field| = form_errors(@cluster) + = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field| .form-group = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL') diff --git a/app/views/projects/clusters/gcp/login.html.haml b/app/views/projects/clusters/gcp/login.html.haml index e97ce01893a..878ebaded88 100644 --- a/app/views/projects/clusters/gcp/login.html.haml +++ b/app/views/projects/clusters/gcp/login.html.haml @@ -12,6 +12,8 @@ - if @authorize_url = link_to @authorize_url do = image_tag('auth_buttons/signin_with_google.png', width: '191px') + = _('or') + = link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer') - else - link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer') = s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link } diff --git a/app/views/projects/clusters/index.html.haml b/app/views/projects/clusters/index.html.haml index 104e39b0e06..74dbe859eea 100644 --- a/app/views/projects/clusters/index.html.haml +++ b/app/views/projects/clusters/index.html.haml @@ -2,23 +2,21 @@ - page_title "Clusters" .clusters-container - - if !@clusters.empty? - = render "tabs" + - if @clusters.empty? + = render "empty_state" + - else + .top-area.adjust + .nav-text + = s_("ClusterIntegration|Clusters can be used to deploy applications and to provide Review Apps for this project") .ci-table.js-clusters-list .gl-responsive-table-row.table-row-header{ role: "row" } .table-section.section-30{ role: "rowheader" } = s_("ClusterIntegration|Cluster") .table-section.section-30{ role: "rowheader" } - = s_("ClusterIntegration|Environment pattern") + = s_("ClusterIntegration|Environment scope") .table-section.section-30{ role: "rowheader" } = s_("ClusterIntegration|Project namespace") .table-section.section-10{ role: "rowheader" } - @clusters.each do |cluster| = render "cluster", cluster: cluster.present(current_user: current_user) = paginate @clusters, theme: "gitlab" - - elsif @scope == 'all' - = render "empty_state" - - else - = render "tabs" - .prepend-top-20.text-center - = s_("ClusterIntegration|There are no clusters to show") diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml index fe6dacf1f0d..c7c84b5a42c 100644 --- a/app/views/projects/clusters/show.html.haml +++ b/app/views/projects/clusters/show.html.haml @@ -9,6 +9,7 @@ .edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path, install_helm_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :helm), install_ingress_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :ingress), + install_prometheus_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :prometheus), toggle_status: @cluster.enabled? ? 'true': 'false', cluster_status: @cluster.status_name, cluster_status_reason: @cluster.status_reason, @@ -17,9 +18,9 @@ .js-cluster-application-notice .flash-container - %section.settings.no-animate.expanded + %section.settings.no-animate.expanded#cluster-integration = render 'banner' - = render 'enabled' + = render 'integration_form' .cluster-applications-table#js-cluster-applications @@ -40,6 +41,6 @@ %h4= _('Advanced settings') %button.btn.js-settings-toggle = expanded ? 'Collapse' : 'Expand' - %p= s_('ClusterIntegration|Manage cluster integration on your GitLab project') + %p= s_("ClusterIntegration|Advanced options on this cluster's integration") .settings-content = render 'advanced_settings' diff --git a/app/views/projects/clusters/user/_form.html.haml b/app/views/projects/clusters/user/_form.html.haml index 4a9bd5186c6..babfca0c567 100644 --- a/app/views/projects/clusters/user/_form.html.haml +++ b/app/views/projects/clusters/user/_form.html.haml @@ -3,6 +3,9 @@ .form-group = field.label :name, s_('ClusterIntegration|Cluster name') = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Cluster name') + .form-group + = field.label :environment_scope, s_('ClusterIntegration|Environment scope') + = field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope') = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field| .form-group diff --git a/app/views/projects/commit/_limit_exceeded_message.html.haml b/app/views/projects/commit/_limit_exceeded_message.html.haml index 84a52d49487..a264f3517c4 100644 --- a/app/views/projects/commit/_limit_exceeded_message.html.haml +++ b/app/views/projects/commit/_limit_exceeded_message.html.haml @@ -1,7 +1,7 @@ .has-tooltip{ class: "limit-box limit-box-#{objects} prepend-left-5", data: { title: "Project has too many #{label_for_message} to search"} } .limit-icon - if objects == :branch - = icon('code-fork') + = sprite_icon('fork', size: 12) - else = icon('tag') .limit-message diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 618a6355d23..d66066a6d0b 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -38,8 +38,8 @@ .commiter - commit_author_link = commit_author_link(commit, avatar: false, size: 24) - - commit_timeago = time_ago_with_tooltip(commit.committed_date, placement: 'bottom') - - commit_text = _('%{commit_author_link} committed %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago } + - commit_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom') + - commit_text = _('%{commit_author_link} authored %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago } #{ commit_text.html_safe } .commit-actions.flex-row.hidden-xs diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index a518fced2b4..d0c8a699608 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -5,22 +5,24 @@ = link_to icon('exchange'), { from: params[:to], to: params[:from] }, class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Swap revisions' .form-group.dropdown.compare-form-group.to.js-compare-to-dropdown .input-group.inline-input-group - %span.input-group-addon Source + %span.input-group-addon + = s_("CompareBranches|Source") = hidden_field_tag :to, params[:to] = button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do - .dropdown-toggle-text.str-truncated= params[:to] || 'Select branch/tag' + .dropdown-toggle-text.str-truncated= params[:to] || _("Select branch/tag") = render 'shared/ref_dropdown' .compare-ellipsis.inline ... .form-group.dropdown.compare-form-group.from.js-compare-from-dropdown .input-group.inline-input-group - %span.input-group-addon Target + %span.input-group-addon + = s_("CompareBranches|Target") = hidden_field_tag :from, params[:from] = button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip git-revision-dropdown-toggle", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do - .dropdown-toggle-text.str-truncated= params[:from] || 'Select branch/tag' + .dropdown-toggle-text.str-truncated= params[:from] || _("Select branch/tag") = render 'shared/ref_dropdown' - = button_tag "Compare", class: "btn btn-create commits-compare-btn" + = button_tag s_("CompareBranches|Compare"), class: "btn btn-create commits-compare-btn" - if @merge_request.present? - = link_to "View open merge request", project_merge_request_path(@project, @merge_request), class: 'prepend-left-10 btn' + = link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'prepend-left-10 btn' - elsif create_mr_button? - = link_to "Create merge request", create_mr_path, class: 'prepend-left-10 btn' + = link_to _("Create merge request"), create_mr_path, class: 'prepend-left-10 btn' diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 3ad0166e9cd..14c64b3534a 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -3,22 +3,16 @@ - page_title "Compare" %div{ class: container_class } + %h3.page-title + = _("Compare Git revisions") .sub-header-block - Compare Git revisions. - %br - Choose a branch/tag (e.g. - = succeed ')' do + - example_master = capture do %code.ref-name master - or enter a commit SHA (e.g. - = succeed ')' do + - example_sha = capture do %code.ref-name 4eedf23 - to see what's changed or to create a merge request. + = (_("Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request.") % { master: example_master, sha: example_sha }).html_safe %br - Changes are shown as if the - %b source - revision was being merged into the - %b target - revision. + = (_("Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision.")).html_safe .prepend-top-20 = render "form" diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index f87f1d476f5..8da55664878 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -1,5 +1,5 @@ - @no_container = true -- add_to_breadcrumbs "Compare Revisions", project_compare_index_path(@project) +- add_to_breadcrumbs _("Compare Revisions"), project_compare_index_path(@project) - page_title "#{params[:from]}...#{params[:to]}" %div{ class: container_class } @@ -13,12 +13,13 @@ .light-well .center %h4 - There isn't anything to compare. + = s_("CompareBranches|There isn't anything to compare.") %p.slead - if params[:to] == params[:from] - %span.ref-name= params[:from] - and - %span.ref-name= params[:to] - are the same. + - source_branch = capture do + %span.ref-name= params[:from] + - target_branch = capture do + %span.ref-name= params[:to] + = (s_("CompareBranches|%{source_branch} and %{target_branch} are the same.") % { source_branch: source_branch, target_branch: target_branch }).html_safe - else - You'll need to use different branch names to get a valid comparison. + = _("You'll need to use different branch names to get a valid comparison.") diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml index e75ae87e771..75dd4c9ae15 100644 --- a/app/views/projects/deploy_keys/_index.html.haml +++ b/app/views/projects/deploy_keys/_index.html.haml @@ -3,7 +3,7 @@ .settings-header %h4 Deploy Keys - %button.btn.js-settings-toggle + %button.btn.js-settings-toggle.qa-expand-deploy-keys = expanded ? 'Collapse' : 'Expand' %p Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one. diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml index 014486be868..c7ac687e4a6 100644 --- a/app/views/projects/deployments/_commit.html.haml +++ b/app/views/projects/deployments/_commit.html.haml @@ -2,7 +2,7 @@ .branch-commit - if deployment.ref %span.icon-container - = deployment.tag? ? icon('tag') : icon('code-fork') + = deployment.tag? ? icon('tag') : sprite_icon('fork', css_class: 'sprite') = link_to deployment.ref, project_ref_path(@project, deployment.ref), class: "ref-name" .icon-container.commit-icon = custom_icon("icon_commit") diff --git a/app/views/projects/diffs/_replaced_image_diff.html.haml b/app/views/projects/diffs/_replaced_image_diff.html.haml index 8fc232b464e..6dffc7c4390 100644 --- a/app/views/projects/diffs/_replaced_image_diff.html.haml +++ b/app/views/projects/diffs/_replaced_image_diff.html.haml @@ -1,7 +1,7 @@ - blob = diff_file.blob - old_blob = diff_file.old_blob -- blob_raw_path = diff_file_blob_raw_path(diff_file) -- old_blob_raw_path = diff_file_old_blob_raw_path(diff_file) +- blob_raw_url = diff_file_blob_raw_url(diff_file) +- old_blob_raw_url = diff_file_old_blob_raw_url(diff_file) - click_to_comment = local_assigns.fetch(:click_to_comment, true) - diff_view_data = local_assigns.fetch(:diff_view_data, '') - class_name = '' @@ -13,7 +13,7 @@ .two-up.view .wrap .frame.deleted - = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false) + = image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false) %p.image-info.hide %span.meta-filesize= number_to_human_size(old_blob.size) | @@ -23,7 +23,7 @@ %strong H: %span.meta-height .wrap - = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path } + = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path } %p.image-info.hide %span.meta-filesize= number_to_human_size(blob.size) | @@ -36,9 +36,9 @@ .swipe.view.hide .swipe-frame .frame.deleted - = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false) + = image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false) .swipe-wrap - = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path } + = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path } %span.swipe-bar %span.top-handle %span.bottom-handle @@ -46,8 +46,8 @@ .onion-skin.view.hide .onion-skin-frame .frame.deleted - = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false) - = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path } + = image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false) + = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path } .controls .transparent .drag-track diff --git a/app/views/projects/diffs/_single_image_diff.html.haml b/app/views/projects/diffs/_single_image_diff.html.haml index 6b0c6bbe48f..12be8beab39 100644 --- a/app/views/projects/diffs/_single_image_diff.html.haml +++ b/app/views/projects/diffs/_single_image_diff.html.haml @@ -1,7 +1,7 @@ - blob = diff_file.blob - old_blob = diff_file.old_blob -- blob_raw_path = diff_file_blob_raw_path(diff_file) -- old_blob_raw_path = diff_file_old_blob_raw_path(diff_file) +- blob_raw_url = diff_file_blob_raw_url(diff_file) +- old_blob_raw_url = diff_file_old_blob_raw_url(diff_file) - click_to_comment = local_assigns.fetch(:click_to_comment, true) - diff_view_data = local_assigns.fetch(:diff_view_data, '') - class_name = '' @@ -12,5 +12,5 @@ .image.js-single-image{ data: diff_view_data } .wrap - single_class_name = diff_file.deleted_file? ? 'deleted' : 'added' - = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "#{single_class_name} #{class_name} js-image-frame", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.file_path } + = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "#{single_class_name} #{class_name} js-image-frame", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.file_path } %p.image-info= number_to_human_size(blob.size) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 71206f3a386..e16d132f869 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -42,24 +42,23 @@ = f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control" %p.help-block Separate tags with commas. %fieldset.features - %h5.prepend-top-0 - Project avatar + %h5.prepend-top-0= _("Project avatar") .form-group - if @project.avatar? - .avatar-container.s160 + .avatar-container.s160.append-bottom-15 = project_icon(@project.full_path, alt: '', class: 'avatar project-avatar s160') - %p.light - - if @project.avatar_in_git - Project avatar in repository: #{ @project.avatar_in_git } - %a.choose-btn.btn.js-choose-project-avatar-button - Browse file... - %span.file_name.prepend-left-default.js-avatar-filename No file chosen - = f.file_field :avatar, class: "js-project-avatar-input hidden" - .help-block The maximum file size allowed is 200KB. + - if @project.avatar_in_git + %p.light + = _("Project avatar in repository: %{link}").html_safe % { link: @project.avatar_in_git } + .prepend-top-5.append-bottom-10 + %button.btn.js-choose-project-avatar-button{ type: 'button' }= _("Choose file...") + %span.file_name.prepend-left-default.js-avatar-filename= _("No file chosen") + = f.file_field :avatar, class: "js-project-avatar-input hidden" + .help-block= _("The maximum file size allowed is 200KB.") - if @project.avatar? %hr - = link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" - = f.submit 'Save changes', class: "btn btn-save" + = link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted" + = f.submit 'Save changes', class: "btn btn-success" %section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) } .settings-header diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index e0aedcac5e1..ad94113fffd 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -15,8 +15,10 @@ #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'), "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'), - "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started'), - "empty-loading-svg-path": image_path('illustrations/monitoring/loading'), - "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect'), + "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'), + "empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'), + "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'), "additional-metrics": additional_metrics_project_environment_path(@project, @environment, format: :json), + "project-path": project_path(@project), + "tags-path": project_tags_path(@project), "has-metrics": "#{@environment.has_metrics?}", deployment_endpoint: project_environment_deployments_path(@project, @environment, format: :json) } } diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml index d365bcd4ecc..e8a89b8c6fc 100644 --- a/app/views/projects/forks/error.html.haml +++ b/app/views/projects/forks/error.html.haml @@ -2,7 +2,7 @@ - if @forked_project && !@forked_project.saved? .alert.alert-danger.alert-block %h4 - %i.fa.fa-code-fork + = sprite_icon('fork', size: 16) Fork Error! %p You tried to fork @@ -21,5 +21,4 @@ %p = link_to new_project_fork_path(@project), title: "Fork", class: "btn" do - %i.fa.fa-code-fork Try to fork again diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index b98dc09534f..2599ce5c4b8 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -19,7 +19,7 @@ - if ref - if generic_commit_status.ref .icon-container - = generic_commit_status.tags.any? ? icon('tag') : icon('code-fork') + = generic_commit_status.tags.any? ? icon('tag') : sprite_icon('fork', size: 10) = link_to generic_commit_status.ref, project_commits_path(generic_commit_status.project, generic_commit_status.ref) - else .light none diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 2f7aece7440..1f28d8acff6 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -14,12 +14,12 @@ .detail-page-header .detail-page-header-body - .issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) } - = icon('check', class: "hidden-sm hidden-md hidden-lg") + .issuable-status-box.status-box.status-box-issue-closed{ class: issue_button_visibility(@issue, false) } + = sprite_icon('mobile-issue-close', size: 16, css_class: 'hidden-sm hidden-md hidden-lg') %span.hidden-xs Closed .issuable-status-box.status-box.status-box-open{ class: issue_button_visibility(@issue, true) } - = icon('circle-o', class: "hidden-sm hidden-md hidden-lg") + = sprite_icon('issue-open-m', size: 16, css_class: 'hidden-sm hidden-md hidden-lg') %span.hidden-xs Open .issuable-meta @@ -39,8 +39,6 @@ = icon('caret-down') .dropdown-menu.dropdown-menu-align-right.hidden-lg %ul - - if can_update_issue - %li= link_to 'Edit', edit_project_issue_path(@project, @issue), class: 'issuable-edit' - unless current_user == @issue.author %li= link_to 'Report abuse', new_abuse_report_path(user_id: @issue.author.id, ref_url: issue_url(@issue)) - if can_update_issue @@ -52,9 +50,6 @@ %li.divider %li= link_to 'New issue', new_project_issue_path(@project), title: 'New issue', id: 'new_issue_link' - - if can_update_issue - = link_to 'Edit', edit_project_issue_path(@project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' - = render 'shared/issuable/close_reopen_button', issuable: @issue, can_update: can_update_issue - if can_report_spam diff --git a/app/views/projects/jobs/_empty_state.html.haml b/app/views/projects/jobs/_empty_state.html.haml new file mode 100644 index 00000000000..c66313bdbf3 --- /dev/null +++ b/app/views/projects/jobs/_empty_state.html.haml @@ -0,0 +1,17 @@ +- illustration = local_assigns.fetch(:illustration) +- illustration_size = local_assigns.fetch(:illustration_size) +- title = local_assigns.fetch(:title) +- content = local_assigns.fetch(:content) +- action = local_assigns.fetch(:action, nil) + +.row.empty-state + .col-xs-12 + .svg-content{ class: illustration_size } + = image_tag illustration + .col-xs-12 + .text-content + %h4.text-center= title + %p= content + - if action + .text-center + = action diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 1d0aaa47b60..8b05440fc78 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -54,41 +54,53 @@ Job has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)} - else Job has been erased #{time_ago_with_tooltip(@build.erased_at)} + - if @build.started? + .build-trace-container.prepend-top-default + .top-bar.js-top-bar + .js-truncated-info.truncated-info.hidden-xs.pull-left.hidden< + Showing last + %span.js-truncated-info-size.truncated-info-size>< + of log - + %a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw - .build-trace-container.prepend-top-default - .top-bar.js-top-bar - .js-truncated-info.truncated-info.hidden-xs.pull-left.hidden< - Showing last - %span.js-truncated-info-size.truncated-info-size>< - KiB of log - - %a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw + .controllers.pull-right + - if @build.has_trace? + = link_to raw_project_job_path(@project, @build), + title: 'Show complete raw', + data: { placement: 'top', container: 'body' }, + class: 'js-raw-link-controller has-tooltip controllers-buttons' do + = icon('file-text-o') - .controllers.pull-right - - if @build.has_trace? - = link_to raw_project_job_path(@project, @build), - title: 'Show complete raw', - data: { placement: 'top', container: 'body' }, - class: 'js-raw-link-controller has-tooltip controllers-buttons' do - = icon('file-text-o') - - - if @build.erasable? && can?(current_user, :erase_build, @build) - = link_to erase_project_job_path(@project, @build), - method: :post, - data: { confirm: 'Are you sure you want to erase this build?', placement: 'top', container: 'body' }, - title: 'Erase job log', - class: 'has-tooltip js-erase-link controllers-buttons' do - = icon('trash') - .has-tooltip.controllers-buttons{ title: 'Scroll to top', data: { placement: 'top', container: 'body'} } - %button.js-scroll-up.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } - = custom_icon('scroll_up') - .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} } - %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } - = custom_icon('scroll_down') - - %pre.build-trace#build-trace - %code.bash.js-build-output - .build-loader-animation.js-build-refresh + - if @build.erasable? && can?(current_user, :erase_build, @build) + = link_to erase_project_job_path(@project, @build), + method: :post, + data: { confirm: 'Are you sure you want to erase this build?', placement: 'top', container: 'body' }, + title: 'Erase job log', + class: 'has-tooltip js-erase-link controllers-buttons' do + = icon('trash') + .has-tooltip.controllers-buttons{ title: 'Scroll to top', data: { placement: 'top', container: 'body'} } + %button.js-scroll-up.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } + = custom_icon('scroll_up') + .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} } + %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } + = custom_icon('scroll_down') + %pre.build-trace#build-trace + %code.bash.js-build-output + .build-loader-animation.js-build-refresh + - elsif @build.playable? + = render 'empty_state', + illustration: 'illustrations/manual_action.svg', + illustration_size: 'svg-394', + title: _('This job requires a manual action'), + content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.'), + action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), class: 'btn btn-primary', title: _('Trigger this manual action') ) + - else + = render 'empty_state', + illustration: 'illustrations/job_not_triggered.svg', + illustration_size: 'svg-306', + title: _('This job has not been triggered yet'), + content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered.') = render "sidebar" diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 2b5e8711b0a..f45a000833b 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -30,7 +30,7 @@ %span.project-ref-path = link_to project_ref_path(merge_request.project, merge_request.target_branch), class: 'ref-name' do - = icon('code-fork') + = sprite_icon('fork', size: 12, css_class: 'fork-sprite') = merge_request.target_branch - if merge_request.labels.any? diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml index 135f9ab0aff..22c8b6b513d 100644 --- a/app/views/projects/merge_requests/_mr_title.html.haml +++ b/app/views/projects/merge_requests/_mr_title.html.haml @@ -7,7 +7,7 @@ .detail-page-header .detail-page-header-body .issuable-status-box.status-box{ class: status_box_class(@merge_request) } - = icon(@merge_request.state_icon_name, class: "hidden-sm hidden-md hidden-lg") + = sprite_icon(@merge_request.state_icon_name, size: 16, css_class: 'hidden-sm hidden-md hidden-lg') %span.hidden-xs = @merge_request.state_human_name @@ -27,7 +27,7 @@ .dropdown-menu.dropdown-menu-align-right.hidden-lg %ul - if can_update_merge_request - %li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit' + %li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) - unless current_user == @merge_request.author %li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)) - if can_update_merge_request @@ -37,6 +37,6 @@ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request' - if can_update_merge_request - = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "hidden-xs hidden-sm btn btn-grouped issuable-edit" + = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "hidden-xs hidden-sm btn btn-grouped js-issuable-edit" = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request diff --git a/app/views/projects/merge_requests/diffs/_diffs.html.haml b/app/views/projects/merge_requests/diffs/_diffs.html.haml index 60c91024b23..986ba5ae02d 100644 --- a/app/views/projects/merge_requests/diffs/_diffs.html.haml +++ b/app/views/projects/merge_requests/diffs/_diffs.html.haml @@ -4,14 +4,17 @@ = render 'projects/merge_requests/diffs/commit_widget' - if @merge_request_diff&.empty? - .nothing-here-block - = image_tag 'illustrations/merge_request_changes_empty.svg' - = succeed '.' do - No changes between - %span.ref-name= @merge_request.source_branch - and - %span.ref-name= @merge_request.target_branch - %p= link_to 'Create commit', project_new_blob_path(@project, @merge_request.source_branch), class: 'btn btn-save' + .row.empty-state.nothing-here-block + .col-xs-12 + .svg-content= image_tag 'illustrations/merge_request_changes_empty.svg' + .col-xs-12 + .text-content.text-center + %p + No changes between + %span.ref-name= @merge_request.source_branch + and + %span.ref-name= @merge_request.target_branch + .text-center= link_to 'Create commit', project_new_blob_path(@project, @merge_request.source_branch), class: 'btn btn-save' - else - diff_viewable = @merge_request_diff ? @merge_request_diff.collected? || @merge_request_diff.overflow? : true - if diff_viewable diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index abff702fd9d..8740c6895df 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -20,7 +20,7 @@ -# haml-lint:disable InlineJavaScript :javascript window.gl = window.gl || {}; - window.gl.mrWidgetData = #{serialize_issuable(@merge_request)} + window.gl.mrWidgetData = #{serialize_issuable(@merge_request, serializer: 'widget')} #js-vue-mr-widget.mr-widget diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index bd8c38292d6..800e234275c 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -3,7 +3,7 @@ %td = pipeline_schedule.description %td.branch-name-cell - = icon('code-fork') + = sprite_icon('fork', size: 12) - if pipeline_schedule.ref.present? = link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name" %td @@ -26,10 +26,12 @@ = pipeline_schedule.owner&.name %td .pull-right.btn-group + - if can?(current_user, :play_pipeline_schedule, pipeline_schedule) + = link_to play_pipeline_schedule_path(pipeline_schedule), method: :post, title: s_('Play'), class: 'btn' do + = icon('play') - if can?(current_user, :update_pipeline_schedule, pipeline_schedule) = link_to take_ownership_pipeline_schedule_path(pipeline_schedule), method: :post, title: s_('PipelineSchedules|Take ownership'), class: 'btn' do = s_('PipelineSchedules|Take ownership') - - if can?(current_user, :update_pipeline_schedule, pipeline_schedule) = link_to edit_pipeline_schedule_path(pipeline_schedule), title: _('Edit'), class: 'btn' do = icon('pencil') - if can?(current_user, :admin_pipeline_schedule, pipeline_schedule) diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index f5149306734..85946aec1f2 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,6 +1,6 @@ #js-pipeline-header-vue.pipeline-header-container -- if @commit +- if @commit.present? .commit-box %h3.commit-title = markdown(@commit.title, pipeline: :single_line) @@ -8,28 +8,28 @@ %pre.commit-description = preserve(markdown(@commit.description, pipeline: :single_line)) -.info-well - - if @commit.status - .well-segment.pipeline-info - .icon-container - = icon('clock-o') - = pluralize @pipeline.statuses.count(:id), "job" - - if @pipeline.ref - from - = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name" - - if @pipeline.duration - in - = time_interval_in_words(@pipeline.duration) - - if @pipeline.queued_duration - = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})" + .info-well + - if @commit.status + .well-segment.pipeline-info + .icon-container + = icon('clock-o') + = pluralize @pipeline.total_size, "job" + - if @pipeline.ref + from + = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name" + - if @pipeline.duration + in + = time_interval_in_words(@pipeline.duration) + - if @pipeline.queued_duration + = "(queued for #{time_interval_in_words(@pipeline.queued_duration)})" - .well-segment.branch-info - .icon-container.commit-icon - = custom_icon("icon_commit") - = link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short" - = link_to("#", class: "js-details-expand hidden-xs hidden-sm") do - %span.text-expander - \... - %span.js-details-content.hide - = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" - = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") + .well-segment.branch-info + .icon-container.commit-icon + = custom_icon("icon_commit") + = link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short" + = link_to("#", class: "js-details-expand hidden-xs hidden-sm") do + %span.text-expander + \... + %span.js-details-content.hide + = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" + = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index ad61f033a1c..398a1c46746 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -8,7 +8,7 @@ %li.js-builds-tab-link = link_to builds_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do Jobs - %span.badge.js-builds-counter= pipeline.statuses.count + %span.badge.js-builds-counter= pipeline.total_size - if failed_builds.present? %li.js-failures-tab-link = link_to failures_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index b2e71cff6ce..f8555f11aab 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -10,7 +10,8 @@ "new-pipeline-path" => new_project_pipeline_path(@project), "can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s, "has-ci" => @repository.gitlab_ci_yml, - "ci-lint-path" => ci_lint_path } } + "ci-lint-path" => ci_lint_path, + "reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } } = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('pipelines') diff --git a/app/views/projects/pipelines_settings/_show.html.haml b/app/views/projects/pipelines_settings/_show.html.haml index c63e716180c..c5f9f5aa15b 100644 --- a/app/views/projects/pipelines_settings/_show.html.haml +++ b/app/views/projects/pipelines_settings/_show.html.haml @@ -40,10 +40,14 @@ = form.text_field :domain, class: 'form-control', placeholder: 'domain.com' %hr - .form-group.append-bottom-default + .form-group.append-bottom-default.js-secret-runner-token = f.label :runners_token, "Runner token", class: 'label-light' - = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' + .form-control.js-secret-value-placeholder + = '*' * 20 + = f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89' %p.help-block The secure token used by the Runner to checkout the project + %button.btn.btn-info.prepend-top-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: 'false' } } + = _('Reveal value') %hr .form-group diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index e71d58ec26d..16bcf671c25 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -1,11 +1,13 @@ +- project = local_assigns.fetch(:project) +- members = local_assigns.fetch(:members) + .panel.panel-default .panel-heading.flex-project-members-panel %span.flex-project-title Members of - %strong - #{@project.name} - %span.badge= @project_members.total_count - = form_tag project_project_members_path(@project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do + %strong= project.name + %span.badge= members.total_count + = form_tag project_project_members_path(project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index fd5d3ec56da..d81103c3a92 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -37,5 +37,5 @@ - if @group_links.any? = render 'projects/project_members/groups', group_links: @group_links - = render 'projects/project_members/team', members: @project_members + = render 'projects/project_members/team', project: @project, members: @project_members = paginate @project_members, theme: "gitlab" diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index 25d862ab4de..6376496ee1a 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -17,6 +17,10 @@ .pull-right - if @project_runners.include?(runner) + - if runner.active? + = link_to 'Pause', pause_project_runner_path(@project, runner), method: :post, class: 'btn btn-sm btn-danger', data: { confirm: "Are you sure?" } + - else + = link_to 'Resume', resume_project_runner_path(@project, runner), method: :post, class: 'btn btn-success btn-sm' - if runner.belongs_to_one_project? = link_to 'Remove Runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - else diff --git a/app/views/projects/services/_deprecated_message.html.haml b/app/views/projects/services/_deprecated_message.html.haml new file mode 100644 index 00000000000..fea9506a4bb --- /dev/null +++ b/app/views/projects/services/_deprecated_message.html.haml @@ -0,0 +1,3 @@ +.flash-container.flash-container-page + .flash-alert.deprecated-service + %span= @service.deprecation_message diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index c0b1c62e8ef..21acd857ce7 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -13,10 +13,7 @@ = render 'shared/service_settings', form: form, subject: @service - if @service.editable? .footer-block.row-content-block - %button.btn.btn-save{ type: 'submit' } - = icon('spinner spin', class: 'hidden js-btn-spinner') - %span.js-btn-label - Save changes + = service_save_button(@service) - if @service.valid? && @service.activated? - unless @service.can_test? diff --git a/app/views/projects/services/edit.html.haml b/app/views/projects/services/edit.html.haml index 25770df1c90..df1fd583670 100644 --- a/app/views/projects/services/edit.html.haml +++ b/app/views/projects/services/edit.html.haml @@ -2,4 +2,6 @@ - page_title @service.title, "Services" - add_to_breadcrumbs("Settings", edit_project_path(@project)) += render 'deprecated_message' if @service.deprecation_message + = render 'form' diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 031efa903c5..6e105a5521a 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -14,13 +14,13 @@ .form-group = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 - = text_field_tag :tag_name, params[:tag_name], required: true, tabindex: 1, autofocus: true, class: 'form-control' + = text_field_tag :tag_name, params[:tag_name], required: true, autofocus: true, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10.create-from .dropdown = hidden_field_tag :ref, default_ref - = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide form-control js-branch-select', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do + = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do .text-left.dropdown-toggle-text= default_ref = render 'shared/ref_dropdown', dropdown_class: 'wide' .help-block @@ -28,7 +28,7 @@ .form-group = label_tag :message, nil, class: 'control-label' .col-sm-10 - = text_area_tag :message, @message, required: false, tabindex: 3, class: 'form-control', rows: 5 + = text_area_tag :message, @message, required: false, class: 'form-control', rows: 5 .help-block = s_('TagsPage|Optionally, add a message to the tag.') %hr @@ -41,6 +41,6 @@ .help-block = s_('TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.') .form-actions - = button_tag s_('TagsPage|Create tag'), class: 'btn btn-create', tabindex: 3 + = button_tag s_('TagsPage|Create tag'), class: 'btn btn-create' = link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'btn btn-cancel' %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml index c51af901699..8c1c532cb3e 100644 --- a/app/views/projects/tree/_blob_item.html.haml +++ b/app/views/projects/tree/_blob_item.html.haml @@ -1,9 +1,12 @@ +- is_lfs_blob = @lfs_blob_ids.include?(blob_item.id) %tr{ class: "tree-item #{tree_hex_class(blob_item)}" } %td.tree-item-file-name = tree_icon(type, blob_item.mode, blob_item.name) - file_name = blob_item.name = link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do %span= file_name + - if is_lfs_blob + %span.label.label-lfs.prepend-left-5 LFS %td.hidden-xs.tree-commit %td.tree-time-ago.cgray.text-right = render 'projects/tree/spinner' diff --git a/app/views/projects/tree/_old_tree_content.html.haml b/app/views/projects/tree/_old_tree_content.html.haml deleted file mode 100644 index 6ea78851b8d..00000000000 --- a/app/views/projects/tree/_old_tree_content.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path } - .table-holder - %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } - %thead - %tr - %th= s_('ProjectFileTree|Name') - %th.hidden-xs - .pull-left= _('Last commit') - %th.text-right= _('Last update') - - if @path.present? - %tr.tree-item - %td.tree-item-file-name - = link_to "..", project_tree_path(@project, up_dir_path), class: 'prepend-left-10' - %td - %td.hidden-xs - - = render_tree(tree) - - - if tree.readme - = render "projects/tree/readme", readme: tree.readme - -- if can_edit_tree? - = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post - = render 'projects/blob/new_dir' diff --git a/app/views/projects/tree/_old_tree_header.html.haml b/app/views/projects/tree/_old_tree_header.html.haml deleted file mode 100644 index 7f636b7e0e8..00000000000 --- a/app/views/projects/tree/_old_tree_header.html.haml +++ /dev/null @@ -1,64 +0,0 @@ -- if on_top_of_branch? - - addtotree_toggle_attributes = { href: '#', 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown' } -- else - - addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' } - -%ul.breadcrumb.repo-breadcrumb - %li - = link_to project_tree_path(@project, @ref) do - = @project.path - - path_breadcrumbs do |title, path| - %li - = link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path)) - - - if current_user - %li - %a.btn.add-to-tree{ addtotree_toggle_attributes } - = sprite_icon('plus', size: 16, css_class: 'pull-left') - = sprite_icon('arrow-down', size: 16, css_class: 'pull-left') - - if on_top_of_branch? - .add-to-tree-dropdown - %ul.dropdown-menu - - if can_edit_tree? - %li - = link_to project_new_blob_path(@project, @id) do - #{ _('New file') } - %li - = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do - #{ _('Upload file') } - %li - = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do - #{ _('New directory') } - - elsif can?(current_user, :fork_project, @project) - %li - - continue_params = { to: project_new_blob_path(@project, @id), - notice: edit_in_new_fork_notice, - notice_now: edit_in_new_fork_notice_now } - - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, - continue: continue_params) - = link_to fork_path, method: :post do - #{ _('New file') } - %li - - continue_params = { to: request.fullpath, - notice: edit_in_new_fork_notice + " Try to upload a file again.", - notice_now: edit_in_new_fork_notice_now } - - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, - continue: continue_params) - = link_to fork_path, method: :post do - #{ _('Upload file') } - %li - - continue_params = { to: request.fullpath, - notice: edit_in_new_fork_notice + " Try to create a new directory again.", - notice_now: edit_in_new_fork_notice_now } - - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, - continue: continue_params) - = link_to fork_path, method: :post do - #{ _('New directory') } - - %li.divider - %li - = link_to new_project_branch_path(@project) do - #{ _('New branch') } - %li - = link_to new_project_tag_path(@project) do - #{ _('New tag') } diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index a4bdd67209d..6ea78851b8d 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,5 +1,24 @@ -- content_url = local_assigns.fetch(:content_url, nil) -- if show_new_repo? - = render 'shared/repo/repo', project: @project, content_url: content_url -- else - = render 'projects/tree/old_tree_content', tree: tree +.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path } + .table-holder + %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } + %thead + %tr + %th= s_('ProjectFileTree|Name') + %th.hidden-xs + .pull-left= _('Last commit') + %th.text-right= _('Last update') + - if @path.present? + %tr.tree-item + %td.tree-item-file-name + = link_to "..", project_tree_path(@project, up_dir_path), class: 'prepend-left-10' + %td + %td.hidden-xs + + = render_tree(tree) + + - if tree.readme + = render "projects/tree/readme", readme: tree.readme + +- if can_edit_tree? + = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post + = render 'projects/blob/new_dir' diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index c02f7ee37ed..d1ecef39475 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -2,16 +2,78 @@ .tree-ref-holder = render 'shared/ref_switcher', destination: 'tree', path: @path, show_create: true - - if show_new_repo? && can_push_branch?(@project, @ref) - .js-new-dropdown - - else - = render 'projects/tree/old_tree_header' + - if on_top_of_branch? + - addtotree_toggle_attributes = { href: '#', 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown' } + - else + - addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' } + + %ul.breadcrumb.repo-breadcrumb + %li + = link_to project_tree_path(@project, @ref) do + = @project.path + - path_breadcrumbs do |title, path| + %li + = link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path)) + + - if current_user + %li + %a.btn.add-to-tree{ addtotree_toggle_attributes } + = sprite_icon('plus', size: 16, css_class: 'pull-left') + = sprite_icon('arrow-down', size: 16, css_class: 'pull-left') + - if on_top_of_branch? + .add-to-tree-dropdown + %ul.dropdown-menu + - if can_edit_tree? + %li + = link_to project_new_blob_path(@project, @id) do + #{ _('New file') } + %li + = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do + #{ _('Upload file') } + %li + = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do + #{ _('New directory') } + - elsif can?(current_user, :fork_project, @project) + %li + - continue_params = { to: project_new_blob_path(@project, @id), + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + #{ _('New file') } + %li + - continue_params = { to: request.fullpath, + notice: edit_in_new_fork_notice + " Try to upload a file again.", + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + #{ _('Upload file') } + %li + - continue_params = { to: request.fullpath, + notice: edit_in_new_fork_notice + " Try to create a new directory again.", + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + #{ _('New directory') } + + %li.divider + %li + = link_to new_project_branch_path(@project) do + #{ _('New branch') } + %li + = link_to new_project_tag_path(@project) do + #{ _('New tag') } .tree-controls - - if show_new_repo? - .editable-mode - - else - = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' + - if show_new_ide? + = succeed " " do + = link_to ide_edit_path(@project, @id), class: 'btn btn-default' do + = ide_edit_text + + = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' = render 'projects/find_file_link' diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 64cc70053ef..709be20e00f 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -6,11 +6,7 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") -- if show_new_repo? - - content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'repo' += render 'projects/last_push' -%div{ class: [(container_class unless show_new_repo?), ("limit-container-width" unless fluid_layout)] } - = render 'projects/last_push' +%div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] } = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) diff --git a/app/views/shared/_choose_group_avatar_button.html.haml b/app/views/shared/_choose_group_avatar_button.html.haml index 94295970acf..75c65520350 100644 --- a/app/views/shared/_choose_group_avatar_button.html.haml +++ b/app/views/shared/_choose_group_avatar_button.html.haml @@ -1,7 +1,4 @@ -%button.choose-btn.btn.btn-sm.js-choose-group-avatar-button{ type: 'button' } - %i.fa.fa-paperclip - %span Choose File ... - -%span.file_name.js-avatar-filename File name... -= f.file_field :avatar, class: 'js-group-avatar-input hidden' -.light The maximum file size allowed is 200KB. +%button.btn.js-choose-group-avatar-button{ type: 'button' }= _("Choose File ...") +%span.file_name.js-avatar-filename= _("No file chosen") += f.file_field :avatar, class: "js-group-avatar-input hidden" +.help-block= _("The maximum file size allowed is 200KB.") diff --git a/app/views/shared/_field.html.haml b/app/views/shared/_field.html.haml index 795447a9ca6..aea0a8fd8e0 100644 --- a/app/views/shared/_field.html.haml +++ b/app/views/shared/_field.html.haml @@ -7,6 +7,7 @@ - choices = field[:choices] - default_choice = field[:default_choice] - help = field[:help] +- disabled = disable_fields_service?(@service) .form-group - if type == "password" && value.present? @@ -15,14 +16,14 @@ = form.label name, title, class: "control-label" .col-sm-10 - if type == 'text' - = form.text_field name, class: "form-control", placeholder: placeholder, required: required + = form.text_field name, class: "form-control", placeholder: placeholder, required: required, disabled: disabled - elsif type == 'textarea' - = form.text_area name, rows: 5, class: "form-control", placeholder: placeholder, required: required + = form.text_area name, rows: 5, class: "form-control", placeholder: placeholder, required: required, disabled: disabled - elsif type == 'checkbox' - = form.check_box name + = form.check_box name, disabled: disabled - elsif type == 'select' - = form.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } + = form.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control", disabled: disabled} - elsif type == 'password' - = form.password_field name, autocomplete: "new-password", class: "form-control", required: value.blank? && :required + = form.password_field name, autocomplete: "new-password", class: "form-control", required: value.blank? && required, disabled: disabled - if help %span.help-block= help diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 3fcc33044e9..81d07074325 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -2,6 +2,8 @@ - status = label_subscription_status(label, @project).inquiry if current_user - subject = local_assigns[:subject] - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user +- show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project) +- show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project) %li{ id: label_css_id, data: { id: label.id } } = render "shared/label_row", label: label @@ -12,12 +14,14 @@ = icon('caret-down') .dropdown-menu.dropdown-menu-align-right %ul - %li - = link_to_label(label, subject: subject, type: :merge_request) do - View merge requests - %li - = link_to_label(label, subject: subject) do - View open issues + - if show_label_merge_requests_link + %li + = link_to_label(label, subject: subject, type: :merge_request) do + View merge requests + - if show_label_issues_link + %li + = link_to_label(label, subject: subject) do + View open issues - if current_user %li.label-subscription - if can_subscribe_to_label_in_different_levels?(label) @@ -35,13 +39,20 @@ %li = link_to 'Edit', edit_label_path(label) %li - = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: {confirm: 'Remove this label? Are you sure?'} + = link_to 'Delete', + destroy_label_path(label), + title: 'Delete', + method: :delete, + data: {confirm: 'Remove this label? Are you sure?'}, + class: 'text-danger' .pull-right.hidden-xs.hidden-sm.hidden-md - = link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do - view merge requests - = link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do - view open issues + - if show_label_merge_requests_link + = link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do + view merge requests + - if show_label_issues_link + = link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do + view open issues - if current_user .label-subscription.inline diff --git a/app/views/shared/_recaptcha_form.html.haml b/app/views/shared/_recaptcha_form.html.haml new file mode 100644 index 00000000000..93a4301f366 --- /dev/null +++ b/app/views/shared/_recaptcha_form.html.haml @@ -0,0 +1,20 @@ +- resource_name = spammable.class.model_name.singular +- humanized_resource_name = spammable.class.model_name.human.downcase +- script = local_assigns.fetch(:script, true) +- method = params[:action] == 'create' ? :post : :put +- has_submit = local_assigns.fetch(:has_submit, true) + += form_for resource_name, method: method, html: { class: 'recaptcha-form js-recaptcha-form' } do |f| + .recaptcha + - params[resource_name].each do |field, value| + = hidden_field(resource_name, field, value: value) + = hidden_field_tag(:spam_log_id, spammable.spam_log.id) + = hidden_field_tag(:recaptcha_verification, true) + = recaptcha_tags script: script, callback: 'recaptchaDialogCallback' + + -# Yields a block with given extra params. + = yield + + - if has_submit + .row-content-block.footer-block + = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-create' diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml index f4a4bfaec54..479bd2cdb38 100644 --- a/app/views/shared/_ref_switcher.html.haml +++ b/app/views/shared/_ref_switcher.html.haml @@ -1,6 +1,6 @@ - show_create = local_assigns.fetch(:show_create, false) -- show_new_branch_form = show_new_repo? && show_create && can?(current_user, :push_code, @project) +- show_new_branch_form = show_new_ide? && show_create && can?(current_user, :push_code, @project) - dropdown_toggle_text = @ref || @project.default_branch = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do = hidden_field_tag :destination, destination diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index 7ca14ac93cc..61b39afb5d4 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -11,7 +11,7 @@ .form-group = form.label :active, "Active", class: "control-label" .col-sm-10 - = form.check_box :active + = form.check_box :active, disabled: disable_fields_service?(@service) - if @service.supported_events.present? .form-group diff --git a/app/views/shared/_show_aside.html.haml b/app/views/shared/_show_aside.html.haml deleted file mode 100644 index 3ac9b11b4fa..00000000000 --- a/app/views/shared/_show_aside.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -= link_to '#aside', class: 'show-aside' do - %i.fa.fa-angle-left diff --git a/app/views/shared/boards/components/_sidebar.html.haml b/app/views/shared/boards/components/_sidebar.html.haml index b3f73e96b81..8e5e32e9f16 100644 --- a/app/views/shared/boards/components/_sidebar.html.haml +++ b/app/views/shared/boards/components/_sidebar.html.haml @@ -1,5 +1,4 @@ -%board-sidebar{ "inline-template" => true, - ":current-user" => "#{current_user ? current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) : {}}" } +%board-sidebar{ "inline-template" => true, ":current-user" => (UserSerializer.new.represent(current_user) || {}).to_json } %transition{ name: "boards-sidebar-slide" } %aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" } .issuable-sidebar diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml index de26fa8bbf3..62437f5fc9d 100644 --- a/app/views/shared/empty_states/_issues.html.haml +++ b/app/views/shared/empty_states/_issues.html.haml @@ -6,18 +6,22 @@ .col-xs-12 .svg-content = image_tag 'illustrations/issues.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - - if has_button && current_user + - if current_user %h4 - The Issue Tracker is the place to add things that need to be improved or solved in a project + = _("The Issue Tracker is the place to add things that need to be improved or solved in a project") %p - Issues can be bugs, tasks or ideas to be discussed. - Also, issues are searchable and filterable. - - if project_select_button - = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues - - else - = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link' + = _("Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.") + - if has_button + .text-center + - if project_select_button + = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues + - else + = link_to 'New issue', button_path, class: 'btn btn-success', title: 'New issue', id: 'new_issue_link' - else + %h4.text-center= _("There are no issues to show") + %p + = _("The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project.") .text-center - %h4 There are no issues to show. + = link_to _('Register / Sign In'), new_user_session_path, class: 'btn btn-success' diff --git a/app/views/shared/empty_states/_labels.html.haml b/app/views/shared/empty_states/_labels.html.haml index a65634dce53..04db9de3606 100644 --- a/app/views/shared/empty_states/_labels.html.haml +++ b/app/views/shared/empty_states/_labels.html.haml @@ -2,10 +2,10 @@ .col-xs-12 .svg-content = image_tag 'illustrations/labels.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - %h4 Labels can be applied to issues and merge requests to categorize them. - %p You can also star a label to make it a priority label. + %h4= _("Labels can be applied to issues and merge requests to categorize them.") + %p= _("You can also star a label to make it a priority label.") - if can?(current_user, :admin_label, @project) - = link_to 'New label', new_project_label_path(@project), class: 'btn btn-new', title: 'New label', id: 'new_label_link' - = link_to 'Generate a default set of labels', generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: 'Generate a default set of labels', id: 'generate_labels_link' + = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-new', title: _('New label'), id: 'new_label_link' + = link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link' diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml index 67f906903e9..2edf3557df4 100644 --- a/app/views/shared/empty_states/_merge_requests.html.haml +++ b/app/views/shared/empty_states/_merge_requests.html.haml @@ -6,17 +6,18 @@ .col-xs-12 .svg-content = image_tag 'illustrations/merge_requests.svg' - .col-xs-12.text-center + .col-xs-12 .text-content - if has_button %h4 - Merge requests are a place to propose changes you've made to a project and discuss those changes with others. + = _("Merge requests are a place to propose changes you've made to a project and discuss those changes with others") %p - Interested parties can even contribute by pushing commits if they want to. - - if project_select_button - = render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request', type: :merge_requests - - else - = link_to 'New merge request', button_path, class: 'btn btn-new', title: 'New merge request', id: 'new_merge_request_link' + = _("Interested parties can even contribute by pushing commits if they want to.") + .text-center + - if project_select_button + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests + - else + = link_to _('New merge request'), button_path, class: 'btn btn-new', title: _('New merge request'), id: 'new_merge_request_link' - else %h4.text-center - There are no merge requests to show. + = _("There are no merge requests to show") diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml index 8e6747ca740..1a259b679c7 100644 --- a/app/views/shared/groups/_dropdown.html.haml +++ b/app/views/shared/groups/_dropdown.html.haml @@ -1,3 +1,4 @@ +- options_hash = local_assigns.fetch(:options_hash, groups_sort_options_hash) - show_archive_options = local_assigns.fetch(:show_archive_options, false) - if @sort.present? - default_sort_by = @sort @@ -10,12 +11,12 @@ .dropdown.inline.js-group-filter-dropdown-wrap.append-right-10 %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.dropdown-label - = sort_options_hash[default_sort_by] + = options_hash[default_sort_by] = icon('chevron-down') %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable %li.dropdown-header = _("Sort by") - - groups_sort_options_hash.each do |value, title| + - options_hash.each do |value, title| %li.js-filter-sort-order = link_to filter_groups_path(sort: value), class: ("is-active" if default_sort_by == value) do = title diff --git a/app/views/shared/issuable/_assignees.html.haml b/app/views/shared/issuable/_assignees.html.haml index 217af7c9fac..fc86f855865 100644 --- a/app/views/shared/issuable/_assignees.html.haml +++ b/app/views/shared/issuable/_assignees.html.haml @@ -1,14 +1,10 @@ -- max_render = 3 -- max = [max_render, issue.assignees.length].min +- max_render = 4 +- assignees_rendering_overflow = issue.assignees.size > max_render +- render_count = assignees_rendering_overflow ? max_render - 1 : max_render +- more_assignees_count = issue.assignees.size - render_count -- issue.assignees.take(max).each do |assignee| +- issue.assignees.take(render_count).each do |assignee| = link_to_member(@project, assignee, name: false, title: "Assigned to :name") -- if issue.assignees.length > max_render - - counter = issue.assignees.length - max_render - - %span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old', 'original-title' => "+#{counter} more assignees" } } - - if counter < 99 - = "+#{counter}" - - else - 99+ +- if more_assignees_count.positive? + %span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old', 'original-title' => "+#{more_assignees_count} more assignees" } } +#{more_assignees_count} diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 2c27dd638a7..71878e93255 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -1,9 +1,9 @@ - show_roles = local_assigns.fetch(:show_roles, true) - show_controls = local_assigns.fetch(:show_controls, true) - force_mobile_view = local_assigns.fetch(:force_mobile_view, false) +- member = local_assigns.fetch(:member) - user = local_assigns.fetch(:user, member.user) - source = member.source -- can_admin_member = can?(current_user, action_member_permission(:update, member), member) %li.member{ class: dom_class(member), id: dom_id(member) } %span.list-item-name @@ -50,18 +50,17 @@ .controls.member-controls - if show_controls && member.source == current_resource - - if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source) + - if member.can_resend_invite? = link_to icon('paper-plane'), polymorphic_path([:resend_invite, member]), method: :post, class: 'btn btn-default prepend-left-10 hidden-xs', title: 'Resend invite' - - if user != current_user && can_admin_member + - if user != current_user && member.can_update? = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| = f.hidden_field :access_level .member-form-control.dropdown.append-right-5 %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", - disabled: !can_admin_member, data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]" } } %span.dropdown-toggle-text = member.human_access @@ -70,23 +69,22 @@ = dropdown_title("Change permissions") .dropdown-content %ul - - member.class.access_level_roles.each do |role, role_id| + - member.access_level_roles.each do |role, role_id| %li = link_to role, "javascript:void(0)", class: ("is-active" if member.access_level == role_id), data: { id: role_id, el_id: dom_id(member) } .prepend-left-5.clearable-input.member-form-control - = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member, data: { el_id: dom_id(member) } + = f.text_field :expires_at, + class: 'form-control js-access-expiration-date js-member-update-control', + placeholder: 'Expiration date', + id: "member_expires_at_#{member.id}", + data: { el_id: dom_id(member) } %i.clear-icon.js-clear-input - else %span.member-access-text= member.human_access - - if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source) - = link_to 'Resend invite', polymorphic_path([:resend_invite, member]), - method: :post, - class: 'btn btn-default prepend-left-10 visible-xs-block' - - - elsif member.request? && can_admin_member + - if member.can_approve? = link_to polymorphic_path([:approve_access_request, member]), method: :post, class: 'btn btn-success prepend-left-10', @@ -96,7 +94,7 @@ - unless force_mobile_view = icon('check inverse', class: 'hidden-xs') - - if can?(current_user, action_member_permission(:destroy, member), member) + - if member.can_remove? - if current_user == user = link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]), method: :delete, diff --git a/app/views/shared/members/_requests.html.haml b/app/views/shared/members/_requests.html.haml index 09b9944082f..1fbd6bcc4cb 100644 --- a/app/views/shared/members/_requests.html.haml +++ b/app/views/shared/members/_requests.html.haml @@ -1,10 +1,13 @@ +- membership_source = local_assigns.fetch(:membership_source) +- requesters = local_assigns.fetch(:requesters) - force_mobile_view = local_assigns.fetch(:force_mobile_view, false) -- if requesters.any? - .panel.panel-default.prepend-top-default{ class: ('panel-mobile' if force_mobile_view ) } - .panel-heading - Users requesting access to - %strong= membership_source.name - %span.badge= requesters.size - %ul.content-list.members-list - = render partial: 'shared/members/member', collection: requesters, as: :member, locals: { force_mobile_view: force_mobile_view } +- return if requesters.empty? + +.panel.panel-default.prepend-top-default{ class: ('panel-mobile' if force_mobile_view ) } + .panel-heading + Users requesting access to + %strong= membership_source.name + %span.badge= requesters.size + %ul.content-list.members-list + = render partial: 'shared/members/member', collection: requesters, as: :member, locals: { force_mobile_view: force_mobile_view } diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index c978d9e4821..98e0161f7d1 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -20,8 +20,8 @@ - if note.is_a?(DiffNote) && note.on_image? - if show_image_comment_badge && note_counter == 0 -# Only show this for the first comment in the discussion - %span.image-comment-badge.inverted - = icon('comment-o') + %span.image-comment-badge + = sprite_icon('image-comment-dark') - elsif note_counter == 0 - counter = badge_counter if local_assigns[:badge_counter] - badge_class = "hidden" if @fresh_discussion || counter.nil? diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 003f5fa52eb..63aa4e29ec9 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -52,7 +52,7 @@ = render_project_pipeline_status(project.pipeline_status) - if forks %span.prepend-left-10 - = icon('code-fork') + = sprite_icon('fork') = number_with_delimiter(project.forks_count) - if stars %span.prepend-left-10 diff --git a/app/views/shared/repo/_repo.html.haml b/app/views/shared/repo/_repo.html.haml deleted file mode 100644 index 87e8c416194..00000000000 --- a/app/views/shared/repo/_repo.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -- @no_container = true; -#repo{ data: { root: @path.empty?.to_s, - root_url: project_tree_path(project), - url: content_url, - current_branch: @ref, - ref: @commit.id, - project_name: project.name, - project_url: project_path(project), - project_id: project.id, - new_merge_request_url: namespace_project_new_merge_request_path(project.namespace, project, merge_request: { source_branch: '' }), - can_commit: (!!can_push_branch?(project, @ref)).to_s, - on_top_of_branch: (!!on_top_of_branch?(project, @ref)).to_s, - current_path: @path } } diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml new file mode 100644 index 00000000000..50e876b1d19 --- /dev/null +++ b/app/workers/all_queues.yml @@ -0,0 +1,101 @@ +--- +- cronjob:admin_email +- cronjob:expire_build_artifacts +- cronjob:gitlab_usage_ping +- cronjob:import_export_project_cleanup +- cronjob:pipeline_schedule +- cronjob:prune_old_events +- cronjob:remove_expired_group_links +- cronjob:remove_expired_members +- cronjob:remove_old_web_hook_logs +- cronjob:remove_unreferenced_lfs_objects +- cronjob:repository_archive_cache +- cronjob:repository_check_batch +- cronjob:requests_profiles +- cronjob:schedule_update_user_activity +- cronjob:stuck_ci_jobs +- cronjob:stuck_import_jobs +- cronjob:stuck_merge_jobs +- cronjob:trending_projects + +- gcp_cluster:cluster_install_app +- gcp_cluster:cluster_provision +- gcp_cluster:cluster_wait_for_app_installation +- gcp_cluster:wait_for_cluster_creation +- gcp_cluster:check_gcp_project_billing + +- github_import_advance_stage +- github_importer:github_import_import_diff_note +- github_importer:github_import_import_issue +- github_importer:github_import_import_note +- github_importer:github_import_import_pull_request +- github_importer:github_import_refresh_import_jid +- github_importer:github_import_stage_finish_import +- github_importer:github_import_stage_import_base_data +- github_importer:github_import_stage_import_issues_and_diff_notes +- github_importer:github_import_stage_import_notes +- github_importer:github_import_stage_import_pull_requests +- github_importer:github_import_stage_import_repository + +- pipeline_cache:expire_job_cache +- pipeline_cache:expire_pipeline_cache +- pipeline_creation:create_pipeline +- pipeline_creation:run_pipeline_schedule +- pipeline_default:build_coverage +- pipeline_default:build_trace_sections +- pipeline_default:pipeline_metrics +- pipeline_default:pipeline_notification +- pipeline_default:update_head_pipeline_for_merge_request +- pipeline_hooks:build_hooks +- pipeline_hooks:pipeline_hooks +- pipeline_processing:build_finished +- pipeline_processing:build_queue +- pipeline_processing:build_success +- pipeline_processing:pipeline_process +- pipeline_processing:pipeline_success +- pipeline_processing:pipeline_update +- pipeline_processing:stage_update + +- repository_check:repository_check_clear +- repository_check:repository_check_single_repository + +- default +- mailers # ActionMailer::DeliveryJob.queue_name + +- authorized_projects +- background_migration +- create_gpg_signature +- delete_merged_branches +- delete_user +- email_receiver +- emails_on_push +- expire_build_instance_artifacts +- git_garbage_collect +- gitlab_shell +- group_destroy +- invalid_gpg_signature_update +- irker +- merge +- namespaceless_project_destroy +- new_issue +- new_merge_request +- new_note +- pages +- post_receive +- process_commit +- project_cache +- project_destroy +- project_export +- project_migrate_hashed_storage +- project_service +- propagate_service_template +- reactive_caching +- rebase +- repository_fork +- repository_import +- storage_migrator +- system_hook_push +- update_merge_requests +- update_user_activity +- upload_checksum +- web_hook diff --git a/app/workers/background_migration_worker.rb b/app/workers/background_migration_worker.rb index aeb3bc019b9..376703f6319 100644 --- a/app/workers/background_migration_worker.rb +++ b/app/workers/background_migration_worker.rb @@ -1,10 +1,53 @@ class BackgroundMigrationWorker include ApplicationWorker + # The minimum amount of time between processing two jobs of the same migration + # class. + # + # This interval is set to 5 minutes so autovacuuming and other maintenance + # related tasks have plenty of time to clean up after a migration has been + # performed. + MIN_INTERVAL = 5.minutes.to_i + # Performs the background migration. # # See Gitlab::BackgroundMigration.perform for more information. + # + # class_name - The class name of the background migration to run. + # arguments - The arguments to pass to the migration class. def perform(class_name, arguments = []) - Gitlab::BackgroundMigration.perform(class_name, arguments) + should_perform, ttl = perform_and_ttl(class_name) + + if should_perform + Gitlab::BackgroundMigration.perform(class_name, arguments) + else + # If the lease could not be obtained this means either another process is + # running a migration of this class or we ran one recently. In this case + # we'll reschedule the job in such a way that it is picked up again around + # the time the lease expires. + self.class.perform_in(ttl || MIN_INTERVAL, class_name, arguments) + end + end + + def perform_and_ttl(class_name) + if always_perform? + # In test environments `perform_in` will run right away. This can then + # lead to stack level errors in the above `#perform`. To work around this + # we'll just perform the migration right away in the test environment. + [true, nil] + else + lease = lease_for(class_name) + + [lease.try_obtain, lease.ttl] + end + end + + def lease_for(class_name) + Gitlab::ExclusiveLease + .new("#{self.class.name}:#{class_name}", timeout: MIN_INTERVAL) + end + + def always_perform? + Rails.env.test? end end diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb index 5efa9180f5e..97d80305bec 100644 --- a/app/workers/build_finished_worker.rb +++ b/app/workers/build_finished_worker.rb @@ -2,7 +2,7 @@ class BuildFinishedWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :processing + queue_namespace :pipeline_processing def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb index 6705a1c2709..cbfca8c342c 100644 --- a/app/workers/build_hooks_worker.rb +++ b/app/workers/build_hooks_worker.rb @@ -2,7 +2,7 @@ class BuildHooksWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :hooks + queue_namespace :pipeline_hooks def perform(build_id) Ci::Build.find_by(id: build_id) diff --git a/app/workers/build_queue_worker.rb b/app/workers/build_queue_worker.rb index fc775a84dc0..e4f4e6c1d9e 100644 --- a/app/workers/build_queue_worker.rb +++ b/app/workers/build_queue_worker.rb @@ -2,7 +2,7 @@ class BuildQueueWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :processing + queue_namespace :pipeline_processing def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb index ec049821ad7..4b9097bc5e4 100644 --- a/app/workers/build_success_worker.rb +++ b/app/workers/build_success_worker.rb @@ -2,7 +2,7 @@ class BuildSuccessWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :processing + queue_namespace :pipeline_processing def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| diff --git a/app/workers/check_gcp_project_billing_worker.rb b/app/workers/check_gcp_project_billing_worker.rb new file mode 100644 index 00000000000..557af14ee57 --- /dev/null +++ b/app/workers/check_gcp_project_billing_worker.rb @@ -0,0 +1,59 @@ +require 'securerandom' + +class CheckGcpProjectBillingWorker + include ApplicationWorker + include ClusterQueue + + LEASE_TIMEOUT = 15.seconds.to_i + SESSION_KEY_TIMEOUT = 5.minutes + BILLING_TIMEOUT = 1.hour + + def self.get_session_token(token_key) + Gitlab::Redis::SharedState.with do |redis| + redis.get(get_redis_session_key(token_key)) + end + end + + def self.store_session_token(token) + generate_token_key.tap do |token_key| + Gitlab::Redis::SharedState.with do |redis| + redis.set(get_redis_session_key(token_key), token, ex: SESSION_KEY_TIMEOUT) + end + end + end + + def self.redis_shared_state_key_for(token) + "gitlab:gcp:#{token.hash}:billing_enabled" + end + + def perform(token_key) + return unless token_key + + token = self.get_session_token(token_key) + return unless token + return unless try_obtain_lease_for(token) + + billing_enabled_projects = CheckGcpProjectBillingService.new.execute(token) + Gitlab::Redis::SharedState.with do |redis| + redis.set(self.class.redis_shared_state_key_for(token), + !billing_enabled_projects.empty?, + ex: BILLING_TIMEOUT) + end + end + + private + + def self.generate_token_key + SecureRandom.uuid + end + + def self.get_redis_session_key(token_key) + "gitlab:gcp:session:#{token_key}" + end + + def try_obtain_lease_for(token) + Gitlab::ExclusiveLease + .new("check_gcp_project_billing_worker:#{token.hash}", timeout: LEASE_TIMEOUT) + .try_obtain + end +end diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb index 9c3bdabc49e..37586e161c9 100644 --- a/app/workers/concerns/application_worker.rb +++ b/app/workers/concerns/application_worker.rb @@ -3,13 +3,23 @@ Sidekiq::Worker.extend ActiveSupport::Concern module ApplicationWorker extend ActiveSupport::Concern - include Sidekiq::Worker + include Sidekiq::Worker # rubocop:disable Cop/IncludeSidekiqWorker included do - sidekiq_options queue: base_queue_name + set_queue end module ClassMethods + def inherited(subclass) + subclass.set_queue + end + + def set_queue + queue_name = [queue_namespace, base_queue_name].compact.join(':') + + sidekiq_options queue: queue_name # rubocop:disable Cop/SidekiqOptionsQueue + end + def base_queue_name name .sub(/\AGitlab::/, '') @@ -18,6 +28,16 @@ module ApplicationWorker .tr('/', '_') end + def queue_namespace(new_namespace = nil) + if new_namespace + sidekiq_options queue_namespace: new_namespace + + set_queue + else + get_sidekiq_options['queue_namespace']&.to_s + end + end + def queue get_sidekiq_options['queue'].to_s end diff --git a/app/workers/concerns/cluster_queue.rb b/app/workers/concerns/cluster_queue.rb index a5074d13220..24b9f145220 100644 --- a/app/workers/concerns/cluster_queue.rb +++ b/app/workers/concerns/cluster_queue.rb @@ -5,6 +5,6 @@ module ClusterQueue extend ActiveSupport::Concern included do - sidekiq_options queue: :gcp_cluster + queue_namespace :gcp_cluster end end diff --git a/app/workers/concerns/cronjob_queue.rb b/app/workers/concerns/cronjob_queue.rb index e918bb011e0..b6581779f6a 100644 --- a/app/workers/concerns/cronjob_queue.rb +++ b/app/workers/concerns/cronjob_queue.rb @@ -4,6 +4,7 @@ module CronjobQueue extend ActiveSupport::Concern included do - sidekiq_options queue: :cronjob, retry: false + queue_namespace :cronjob + sidekiq_options retry: false end end diff --git a/app/workers/concerns/gitlab/github_import/queue.rb b/app/workers/concerns/gitlab/github_import/queue.rb index a2bee361b86..22c2ce458e8 100644 --- a/app/workers/concerns/gitlab/github_import/queue.rb +++ b/app/workers/concerns/gitlab/github_import/queue.rb @@ -4,12 +4,14 @@ module Gitlab extend ActiveSupport::Concern included do + queue_namespace :github_importer + # If a job produces an error it may block a stage from advancing # forever. To prevent this from happening we prevent jobs from going to # the dead queue. This does mean some resources may not be imported, but # this is better than a project being stuck in the "import" state # forever. - sidekiq_options queue: 'github_importer', dead: false, retry: 5 + sidekiq_options dead: false, retry: 5 end end end diff --git a/app/workers/concerns/new_issuable.rb b/app/workers/concerns/new_issuable.rb index eb0d6c9c36c..526ed0bad07 100644 --- a/app/workers/concerns/new_issuable.rb +++ b/app/workers/concerns/new_issuable.rb @@ -9,15 +9,15 @@ module NewIssuable end def set_user(user_id) - @user = User.find_by(id: user_id) + @user = User.find_by(id: user_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables - log_error(User, user_id) unless @user + log_error(User, user_id) unless @user # rubocop:disable Gitlab/ModuleWithInstanceVariables end def set_issuable(issuable_id) - @issuable = issuable_class.find_by(id: issuable_id) + @issuable = issuable_class.find_by(id: issuable_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables - log_error(issuable_class, issuable_id) unless @issuable + log_error(issuable_class, issuable_id) unless @issuable # rubocop:disable Gitlab/ModuleWithInstanceVariables end def log_error(record_class, record_id) diff --git a/app/workers/concerns/pipeline_queue.rb b/app/workers/concerns/pipeline_queue.rb index ddf45b91345..e77093a6902 100644 --- a/app/workers/concerns/pipeline_queue.rb +++ b/app/workers/concerns/pipeline_queue.rb @@ -5,14 +5,6 @@ module PipelineQueue extend ActiveSupport::Concern included do - sidekiq_options queue: 'pipeline_default' - end - - class_methods do - def enqueue_in(group:) - raise ArgumentError, 'Unspecified queue group!' if group.empty? - - sidekiq_options queue: "pipeline_#{group}" - end + queue_namespace :pipeline_default end end diff --git a/app/workers/concerns/project_import_options.rb b/app/workers/concerns/project_import_options.rb new file mode 100644 index 00000000000..10b971344f7 --- /dev/null +++ b/app/workers/concerns/project_import_options.rb @@ -0,0 +1,23 @@ +module ProjectImportOptions + extend ActiveSupport::Concern + + included do + IMPORT_RETRY_COUNT = 5 + + sidekiq_options retry: IMPORT_RETRY_COUNT, status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION + + # We only want to mark the project as failed once we exhausted all retries + sidekiq_retries_exhausted do |job| + project = Project.find(job['args'].first) + + action = if project.forked? + "fork" + else + "import" + end + + project.mark_import_as_failed("Every #{action} attempt has failed: #{job['error_message']}. Please try again.") + Sidekiq.logger.warn "Failed #{job['class']} with #{job['args']}: #{job['error_message']}" + end + end +end diff --git a/app/workers/concerns/project_start_import.rb b/app/workers/concerns/project_start_import.rb index 0704ebbb0fd..4e55a1ee3d6 100644 --- a/app/workers/concerns/project_start_import.rb +++ b/app/workers/concerns/project_start_import.rb @@ -1,3 +1,4 @@ +# Used in EE by mirroring module ProjectStartImport def start(project) if project.import_started? && project.import_jid == self.jid diff --git a/app/workers/concerns/repository_check_queue.rb b/app/workers/concerns/repository_check_queue.rb index a597321ccf4..43fb66c31b0 100644 --- a/app/workers/concerns/repository_check_queue.rb +++ b/app/workers/concerns/repository_check_queue.rb @@ -3,6 +3,8 @@ module RepositoryCheckQueue extend ActiveSupport::Concern included do - sidekiq_options queue: :repository_check, retry: false + queue_namespace :repository_check + + sidekiq_options retry: false end end diff --git a/app/workers/create_pipeline_worker.rb b/app/workers/create_pipeline_worker.rb index 00cd7b85b9f..c3ac35e54f5 100644 --- a/app/workers/create_pipeline_worker.rb +++ b/app/workers/create_pipeline_worker.rb @@ -2,7 +2,7 @@ class CreatePipelineWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :creation + queue_namespace :pipeline_creation def perform(project_id, user_id, ref, source, params = {}) project = Project.find(project_id) diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb index a591e2da519..7217364a9f2 100644 --- a/app/workers/expire_job_cache_worker.rb +++ b/app/workers/expire_job_cache_worker.rb @@ -2,7 +2,7 @@ class ExpireJobCacheWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :cache + queue_namespace :pipeline_cache def perform(job_id) job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id) diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb index a3ac32b437d..db73d37868a 100644 --- a/app/workers/expire_pipeline_cache_worker.rb +++ b/app/workers/expire_pipeline_cache_worker.rb @@ -2,7 +2,7 @@ class ExpirePipelineCacheWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :cache + queue_namespace :pipeline_cache def perform(pipeline_id) pipeline = Ci::Pipeline.find_by(id: pipeline_id) @@ -13,7 +13,7 @@ class ExpirePipelineCacheWorker store.touch(project_pipelines_path(project)) store.touch(project_pipeline_path(project, pipeline)) - store.touch(commit_pipelines_path(project, pipeline.commit)) if pipeline.commit + store.touch(commit_pipelines_path(project, pipeline.commit)) unless pipeline.commit.nil? store.touch(new_merge_request_pipelines_path(project)) each_pipelines_merge_request_path(project, pipeline) do |path| store.touch(path) diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb index 400396d5755..f7f498af840 100644 --- a/app/workers/gitlab/github_import/advance_stage_worker.rb +++ b/app/workers/gitlab/github_import/advance_stage_worker.rb @@ -9,7 +9,7 @@ module Gitlab class AdvanceStageWorker include ApplicationWorker - sidekiq_options queue: 'github_importer_advance_stage', dead: false + sidekiq_options dead: false INTERVAL = 30.seconds.to_i diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb index 62f733c02fc..3ec81d040b4 100644 --- a/app/workers/pages_worker.rb +++ b/app/workers/pages_worker.rb @@ -1,7 +1,7 @@ class PagesWorker include ApplicationWorker - sidekiq_options queue: :pages, retry: false + sidekiq_options retry: false def perform(action, *arg) send(action, *arg) # rubocop:disable GitlabSecurity/PublicSend diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb index 661c29efe88..c94918ff4ee 100644 --- a/app/workers/pipeline_hooks_worker.rb +++ b/app/workers/pipeline_hooks_worker.rb @@ -2,7 +2,7 @@ class PipelineHooksWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :hooks + queue_namespace :pipeline_hooks def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb index 07dbf6a971e..24424b3f472 100644 --- a/app/workers/pipeline_process_worker.rb +++ b/app/workers/pipeline_process_worker.rb @@ -2,7 +2,7 @@ class PipelineProcessWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :processing + queue_namespace :pipeline_processing def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb index 68c40a259e1..2ab0739a17f 100644 --- a/app/workers/pipeline_success_worker.rb +++ b/app/workers/pipeline_success_worker.rb @@ -2,7 +2,7 @@ class PipelineSuccessWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :processing + queue_namespace :pipeline_processing def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline| diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb index 24a8a9fbed5..fc9da2d45b1 100644 --- a/app/workers/pipeline_update_worker.rb +++ b/app/workers/pipeline_update_worker.rb @@ -2,7 +2,7 @@ class PipelineUpdateWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :processing + queue_namespace :pipeline_processing def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/rebase_worker.rb b/app/workers/rebase_worker.rb new file mode 100644 index 00000000000..090987778a2 --- /dev/null +++ b/app/workers/rebase_worker.rb @@ -0,0 +1,12 @@ +class RebaseWorker + include ApplicationWorker + + def perform(merge_request_id, current_user_id) + current_user = User.find(current_user_id) + merge_request = MergeRequest.find(merge_request_id) + + MergeRequests::RebaseService + .new(merge_request.source_project, current_user) + .execute(merge_request) + end +end diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index a07ef1705a1..d1c57b82681 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -1,11 +1,8 @@ class RepositoryForkWorker - ForkError = Class.new(StandardError) - include ApplicationWorker include Gitlab::ShellAdapter include ProjectStartImport - - sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION + include ProjectImportOptions def perform(project_id, forked_from_repository_storage_path, source_disk_path) project = Project.find(project_id) @@ -18,20 +15,12 @@ class RepositoryForkWorker result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_disk_path, project.repository_storage_path, project.disk_path) - raise ForkError, "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result + raise "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result project.repository.after_import - raise ForkError, "Project #{project_id} had an invalid repository after fork" unless project.valid_repo? + raise "Project #{project_id} had an invalid repository after fork" unless project.valid_repo? project.import_finish - rescue ForkError => ex - fail_fork(project, ex.message) - raise - rescue => ex - return unless project - - fail_fork(project, ex.message) - raise ForkError, "#{ex.class} #{ex.message}" end private @@ -42,9 +31,4 @@ class RepositoryForkWorker Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while forking.") false end - - def fail_fork(project, message) - Rails.logger.error(message) - project.mark_import_as_failed(message) - end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 55715c83cb1..31e2798c36b 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -1,11 +1,8 @@ class RepositoryImportWorker - ImportError = Class.new(StandardError) - include ApplicationWorker include ExceptionBacktrace include ProjectStartImport - - sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION + include ProjectImportOptions def perform(project_id) project = Project.find(project_id) @@ -23,17 +20,9 @@ class RepositoryImportWorker # to those importers to mark the import process as complete. return if service.async? - raise ImportError, result[:message] if result[:status] == :error + raise result[:message] if result[:status] == :error project.after_import - rescue ImportError => ex - fail_import(project, ex.message) - raise - rescue => ex - return unless project - - fail_import(project, ex.message) - raise ImportError, "#{ex.class} #{ex.message}" end private @@ -44,8 +33,4 @@ class RepositoryImportWorker Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.") false end - - def fail_import(project, message) - project.mark_import_as_failed(message) - end end diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb new file mode 100644 index 00000000000..8f5138fc873 --- /dev/null +++ b/app/workers/run_pipeline_schedule_worker.rb @@ -0,0 +1,22 @@ +class RunPipelineScheduleWorker + include ApplicationWorker + include PipelineQueue + + queue_namespace :pipeline_creation + + def perform(schedule_id, user_id) + schedule = Ci::PipelineSchedule.find_by(id: schedule_id) + user = User.find_by(id: user_id) + + return unless schedule && user + + run_pipeline_schedule(schedule, user) + end + + def run_pipeline_schedule(schedule, user) + Ci::CreatePipelineService.new(schedule.project, + user, + ref: schedule.ref) + .execute(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule) + end +end diff --git a/app/workers/stage_update_worker.rb b/app/workers/stage_update_worker.rb index 69f2318d83b..e4b683fca33 100644 --- a/app/workers/stage_update_worker.rb +++ b/app/workers/stage_update_worker.rb @@ -2,7 +2,7 @@ class StageUpdateWorker include ApplicationWorker include PipelineQueue - enqueue_in group: :processing + queue_namespace :pipeline_processing def perform(stage_id) Ci::Stage.find_by(id: stage_id).try do |stage| diff --git a/app/workers/stuck_merge_jobs_worker.rb b/app/workers/stuck_merge_jobs_worker.rb index 36d2a2e6466..16394293c79 100644 --- a/app/workers/stuck_merge_jobs_worker.rb +++ b/app/workers/stuck_merge_jobs_worker.rb @@ -23,7 +23,12 @@ class StuckMergeJobsWorker merge_requests = MergeRequest.where(id: completed_ids) merge_requests.where.not(merge_commit_sha: nil).update_all(state: :merged) - merge_requests.where(merge_commit_sha: nil).update_all(state: :opened, merge_jid: nil) + + merge_requests_to_reopen = merge_requests.where(merge_commit_sha: nil) + + # Do not reopen merge requests using direct queries. + # We rely on state machine callbacks to update head_pipeline_id + merge_requests_to_reopen.each(&:unlock_mr) Rails.logger.info("Updated state of locked merge jobs. JIDs: #{completed_jids.join(', ')}") end diff --git a/app/workers/update_head_pipeline_for_merge_request_worker.rb b/app/workers/update_head_pipeline_for_merge_request_worker.rb index 0a2e9b63578..f09d89aa170 100644 --- a/app/workers/update_head_pipeline_for_merge_request_worker.rb +++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb @@ -1,15 +1,25 @@ class UpdateHeadPipelineForMergeRequestWorker include ApplicationWorker - - sidekiq_options queue: 'pipeline_default' + include PipelineQueue def perform(merge_request_id) merge_request = MergeRequest.find(merge_request_id) pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last return unless pipeline && pipeline.latest? - raise ArgumentError, 'merge request sha does not equal pipeline sha' if merge_request.diff_head_sha != pipeline.sha + + if merge_request.diff_head_sha != pipeline.sha + log_error_message_for(merge_request) + + return + end merge_request.update_attribute(:head_pipeline_id, pipeline.id) end + + def log_error_message_for(merge_request) + Rails.logger.error( + "Outdated head pipeline for active merge request: id=#{merge_request.id}, source_branch=#{merge_request.source_branch}, diff_head_sha=#{merge_request.diff_head_sha}" + ) + end end |