├── icon.png ├── icon-dev.png ├── logo128.png ├── logo48.png ├── popup.html ├── github.css ├── options.html ├── options.js ├── LICENSE ├── manifest.json ├── .circleci └── config.yml ├── README.md ├── circleci-icon.svg ├── background.js ├── logo.svg ├── popup.js ├── github.js └── vendor ├── algoliasearch-4.1.0.min.js └── jquery-3.5.1.min.js /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FelicianoTech/pointless/HEAD/icon.png -------------------------------------------------------------------------------- /icon-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FelicianoTech/pointless/HEAD/icon-dev.png -------------------------------------------------------------------------------- /logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FelicianoTech/pointless/HEAD/logo128.png -------------------------------------------------------------------------------- /logo48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FelicianoTech/pointless/HEAD/logo48.png -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Getting Started Extension's Popup 5 | 19 | 20 | 21 |

More features coming soon. View the roadmap on our GitHub Page.

22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /github.css: -------------------------------------------------------------------------------- 1 | a.cci{ 2 | margin-left: 0; 3 | } 4 | a.cci.status-dot{ 5 | margin: 0 8px 0 0; 6 | } 7 | a.cci span.default-build-status{ 8 | display: inline-block; 9 | border-radius: 50%; 10 | background-color: #DEDEDE; 11 | width: 12px; 12 | height: 12px; 13 | transition: background-color 2s; 14 | } 15 | a.cci span.default-build-status.passed{ 16 | background-color: green; 17 | } 18 | 19 | a.cci span.default-build-status.failed{ 20 | background-color: red; 21 | } 22 | 23 | a.cci span.default-build-status.error{ 24 | background-color: black; 25 | } 26 | 27 | a.cci span.default-build-status.running{ 28 | background-color: cyan; 29 | } 30 | 31 | img.circleci-icon{ 32 | width: 14px; 33 | height: 14px; 34 | vertical-align: text-top; 35 | } 36 | -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pointless Settings 5 | 26 | 27 | 28 | 29 | required 30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /options.js: -------------------------------------------------------------------------------- 1 | // Saves options to chrome.storage.sync. 2 | function save_options() { 3 | var token = document.getElementById('token').value; 4 | chrome.storage.sync.set({ 5 | apiToken: token 6 | }, function() { 7 | // Update status to let user know options were saved. 8 | var status = document.getElementById('status'); 9 | status.textContent = 'Options saved.'; 10 | setTimeout(function() { 11 | status.textContent = ''; 12 | }, 750); 13 | }); 14 | 15 | var bgJS = chrome.extension.getBackgroundPage(); 16 | bgJS.init(); 17 | } 18 | 19 | // Restores select box and checkbox state using the preferences 20 | // stored in chrome.storage. 21 | function restore_options() { 22 | chrome.storage.sync.get({ 23 | apiToken: "" 24 | }, function(items) { 25 | document.getElementById('token').value = items.apiToken; 26 | }); 27 | } 28 | document.addEventListener('DOMContentLoaded', restore_options); 29 | document.getElementById('save').addEventListener('click', 30 | save_options); 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ricardo N Feliciano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Pointless - a CircleCI Chrome Extension", 4 | "short_name": "Pointless", 5 | "version": "9.9.9.9", 6 | "description": "Pointless is a CircleCI extension that enhances browsing experience on websites such as GitHub.", 7 | "author": "Ricardo N Feliciano - https://www.Feliciano.Tech", 8 | "minimum_chrome_version": "75", 9 | 10 | "icons":{ 11 | "48": "logo48.png", 12 | "128": "logo128.png" 13 | }, 14 | 15 | "background": { 16 | "scripts": [ 17 | "vendor/algoliasearch-4.1.0.min.js", 18 | "background.js" 19 | ] 20 | }, 21 | 22 | "omnibox": { "keyword": "ci" }, 23 | 24 | "options_ui":{ 25 | "page": "options.html", 26 | "chrome_style": true 27 | }, 28 | 29 | "browser_action":{ 30 | "default_icon": "icon-dev.png", 31 | "default_title": "Pointless - a CircleCI Chrome Extension", 32 | "default_popup": "popup.html" 33 | }, 34 | 35 | "content_scripts":[ 36 | { 37 | "matches": ["https://github.com/*/*"], 38 | "css": ["github.css"], 39 | "js": ["vendor/jquery-3.5.1.min.js", "github.js"] 40 | } 41 | ], 42 | 43 | "homepage_url": "https://github.com/felicianotech/pointless", 44 | 45 | "permissions": [ 46 | "activeTab", 47 | "https://ajax.googleapis.com/", 48 | "https://circleci.com/", 49 | "storage" 50 | ], 51 | 52 | "web_accessible_resources": [ 53 | "circleci-icon.svg" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | workflows: 3 | main: 4 | jobs: 5 | - build: 6 | filters: 7 | tags: 8 | only: /^\d{4}\.\d+$/ 9 | - publish: 10 | requires: 11 | - build 12 | filters: 13 | branches: 14 | ignore: /.*/ 15 | tags: 16 | only: /^\d{4}\.\d+$/ 17 | jobs: 18 | build: 19 | docker: 20 | - image: cibuilds/chrome-extension:latest 21 | steps: 22 | - checkout 23 | - run: 24 | name: "Prepare Extension For Production" 25 | command: | 26 | if [[ $CIRCLE_TAG != "" ]];then 27 | echo "Applying real version." 28 | jq '.version = env.CIRCLE_TAG' manifest.json | sponge manifest.json 29 | fi 30 | jq '.browser_action.default_icon = "icon.png"' manifest.json | sponge manifest.json 31 | - run: 32 | name: "Filler - Proper CI/CD needs to be implemented." 33 | command: echo "The start of our build process." 34 | - run: apk add --no-cache zip 35 | - run: 36 | name: "Package Extension" 37 | command: zip -r pointless.zip . -x *.git* 38 | - persist_to_workspace: 39 | root: /root/project 40 | paths: 41 | - pointless.zip 42 | publish: 43 | docker: 44 | - image: cibuilds/chrome-extension:latest 45 | environment: 46 | - APP_ID: edmkpfdmophaaeedepooedlhioimljai 47 | steps: 48 | - attach_workspace: 49 | at: /root/workspace 50 | - store_artifacts: 51 | path: "/root/workspace/pointless.zip" 52 | - run: 53 | name: "Publish to the Google Chrome Store" 54 | command: publish /root/workspace/pointless.zip 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pointless - a CircleCI Chrome Extension [![Build Status](https://circleci.com/gh/felicianotech/pointless.svg?style=shield)](https://circleci.com/gh/felicianotech/pointless) [![Software License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/felicianotech/pointless/master/LICENSE) 2 | 3 | Pointless Logo 4 | 5 | Pointless is a CircleCI Chrome Extension that enhances your browsing experience while developing. For example, while viewing a project on GitHub, you can see its build status and follow/unfollow that project on CircleCI right from the GitHub page. 6 | 7 | ## Installing 8 | 9 | Pointless can be installed via the [Chrome Store](https://chrome.google.com/webstore/detail/pointless-a-circleci-chro/edmkpfdmophaaeedepooedlhioimljai). 10 | 11 | ## Configuring 12 | 13 | This extension requires a CircleCI [Personal API Token](https://circleci.com/account/api). Once you create a token on CircleCI's website, you can add it to Pointless by right-clicking the CircleCI icon at the top-right corner of Google Chrome and clicking "Options". 14 | 15 | ## Features 16 | 17 | - **build status** (GitHub) - a colored icon will appear to the right of the project name, showing the CircleCI build status of the default branch. 18 | 19 | - **follow/unfollow projects** (GitHub) - on a repo page, a CircleCI "Follow"/"Unfollow" button will appear next to the "Watch", "Star", and "Fork" GitHub buttons. 20 | 21 | - **docs search** (Omnibox/address bar) - search CircleCI Docs (all of docs or a single section) right from Chrome's Omnibox. Instructions below. 22 | 23 | ## How-To 24 | 25 | ### Searching 26 | 27 | Pointless supports quick, special searches. Currently, only CircleCI Docs can be searched but more search types are on the roadmap. 28 | 29 | ### Search CircleCI Docs 30 | 31 | In the Omnibox (address bar) type `ci` and press tab to activate Pointless' Search. Then, type `d`, a space, and then one or more search terms to search all of CircleCI Docs. You can search 1.0 Docs specifically by using `d1` instead of `d`, for 2.0, `d2`, for API Docs, `da`, and for CCIE Docs, `de`. 32 | 33 | Note that the search results will appear as a suggested page underneath the Omnibox. Hitting `Enter` will bring you to the CircleCi Docs index. 34 | 35 | ### Examples 36 | 37 | Search all of CircleCI Docs for Docker by typing: `ci d docker` 38 | 39 | Search for Yarn in CircleCI 2.0 Docs: `ci d2 yarn` 40 | -------------------------------------------------------------------------------- /circleci-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | function init(){ 2 | 3 | chrome.storage.sync.get({ 4 | apiToken: "" 5 | }, function(items){ 6 | apiToken = items.apiToken; 7 | 8 | if( apiToken.length == 0 ){ 9 | chrome.browserAction.setBadgeText( {text:"!"} ); 10 | chrome.browserAction.setBadgeBackgroundColor( {color:[179,58,58,255]} ); 11 | }else{ 12 | chrome.browserAction.setBadgeText( {text:""} ); 13 | } 14 | }); 15 | 16 | } 17 | 18 | function navigate( url ){ 19 | 20 | chrome.tabs.query( {active: true, currentWindow: true }, function( tabs ){ 21 | chrome.tabs.update( tabs[0].id, { url: url } ); 22 | }); 23 | } 24 | 25 | // Handle search logic specific to CircleCI Docs 26 | function searchDocs( searchType, text, suggest){ 27 | 28 | var suggestions = []; 29 | 30 | chrome.omnibox.setDefaultSuggestion({ description: "CircleCI Docs results:" }); 31 | 32 | docsIndex.search( text, { 33 | hitsPerPage: 7, 34 | attributesToRetrieve: [ 'title', 'url' ], 35 | filters: "collection:cci2", 36 | }).then(({ hits }) => { 37 | hits.forEach(function( hit ){ 38 | suggestions.push({ content: "https://circleci.com/docs" + hit.url, description: "" + hit.title + "" }); 39 | }); 40 | 41 | suggest( suggestions ); 42 | }); 43 | } 44 | 45 | // Handle search logic specific to CircleCI Orbs 46 | function searchOrbs( searchType, text, suggest){ 47 | 48 | var suggestions = []; 49 | 50 | chrome.omnibox.setDefaultSuggestion({ description: "CircleCI Orb Registry results:" }); 51 | 52 | orbIndex.search( text, { 53 | hitsPerPage: 7, 54 | attributesToRetrieve: [ 'objectID', 'url' ], 55 | }).then(({ hits }) => { 56 | 57 | hits.forEach(function( hit ){ 58 | suggestions.push( { content: hit.url, description: "" + hit.objectID + "" } ); 59 | }); 60 | 61 | suggest( suggestions ); 62 | }); 63 | } 64 | 65 | 66 | 67 | /*----------------------------------------------------------------------------- 68 | * Main 69 | *---------------------------------------------------------------------------*/ 70 | 71 | var apiToken = ""; 72 | 73 | init(); 74 | 75 | // Setup Algolia v4 JavaScript client 76 | //import algoliasearch from 'algoliasearch/lite'; 77 | const client = algoliasearch( "U0RXNGRK45", "7fb53cd578f887582b8e3085221a4f65" ); 78 | const docsIndex = client.initIndex( "documentation" ); 79 | const orbIndex = client.initIndex( "orbs-prod" ); 80 | 81 | // Omnibox Search - currently searches CircleCI Docs and Orbs 82 | chrome.omnibox.onInputChanged.addListener( function( text, suggest ){ 83 | 84 | var searchType = text.split(" ")[0]; 85 | text = text.substring( searchType.length ); 86 | 87 | switch( searchType ){ 88 | case "d": 89 | // the old docs options below are kept for backwards compatibility 90 | // remove sometime after June 2020 91 | case "d1": 92 | case "d2": 93 | case "de": 94 | case "da": 95 | searchDocs( searchType, text, suggest ); 96 | break; 97 | case "o": 98 | searchOrbs( searchType, text, suggest ); 99 | break; 100 | default: 101 | chrome.omnibox.setDefaultSuggestion( { description: "Search type not recognized." } ); 102 | } 103 | }); 104 | 105 | 106 | // Omnibox Search - visit the URL of the slected suggestion 107 | chrome.omnibox.onInputEntered.addListener( function( text, disposition ){ 108 | navigate( text ); 109 | }); 110 | 111 | chrome.runtime.onConnect.addListener(function(port){ 112 | console.assert(port.name == "github"); 113 | port.onMessage.addListener(function(msg){ 114 | 115 | if(msg.request == "api-token"){ 116 | port.postMessage({request: "api-token", response: apiToken}); 117 | } 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 67 | 70 | 75 | 80 | 85 | 90 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * Get the current URL. 7 | * 8 | * @param {function(string)} callback - called when the URL of the current tab 9 | * is found. 10 | */ 11 | function getCurrentTabUrl(callback) { 12 | // Query filter to be passed to chrome.tabs.query - see 13 | // https://developer.chrome.com/extensions/tabs#method-query 14 | var queryInfo = { 15 | active: true, 16 | currentWindow: true 17 | }; 18 | 19 | chrome.tabs.query(queryInfo, function(tabs) { 20 | // chrome.tabs.query invokes the callback with a list of tabs that match the 21 | // query. When the popup is opened, there is certainly a window and at least 22 | // one tab, so we can safely assume that |tabs| is a non-empty array. 23 | // A window can only have one active tab at a time, so the array consists of 24 | // exactly one tab. 25 | var tab = tabs[0]; 26 | 27 | // A tab is a plain object that provides information about the tab. 28 | // See https://developer.chrome.com/extensions/tabs#type-Tab 29 | var url = tab.url; 30 | 31 | // tab.url is only available if the "activeTab" permission is declared. 32 | // If you want to see the URL of other tabs (e.g. after removing active:true 33 | // from |queryInfo|), then the "tabs" permission is required to see their 34 | // "url" properties. 35 | console.assert(typeof url == 'string', 'tab.url should be a string'); 36 | 37 | callback(url); 38 | }); 39 | 40 | // Most methods of the Chrome extension APIs are asynchronous. This means that 41 | // you CANNOT do something like this: 42 | // 43 | // var url; 44 | // chrome.tabs.query(queryInfo, function(tabs) { 45 | // url = tabs[0].url; 46 | // }); 47 | // alert(url); // Shows "undefined", because chrome.tabs.query is async. 48 | } 49 | 50 | /** 51 | * @param {string} searchTerm - Search term for Google Image search. 52 | * @param {function(string,number,number)} callback - Called when an image has 53 | * been found. The callback gets the URL, width and height of the image. 54 | * @param {function(string)} errorCallback - Called when the image is not found. 55 | * The callback gets a string that describes the failure reason. 56 | */ 57 | function getImageUrl(searchTerm, callback, errorCallback) { 58 | // Google image search - 100 searches per day. 59 | // https://developers.google.com/image-search/ 60 | var searchUrl = 'https://ajax.googleapis.com/ajax/services/search/images' + 61 | '?v=1.0&q=' + encodeURIComponent(searchTerm); 62 | var x = new XMLHttpRequest(); 63 | x.open('GET', searchUrl); 64 | // The Google image search API responds with JSON, so let Chrome parse it. 65 | x.responseType = 'json'; 66 | x.onload = function() { 67 | // Parse and process the response from Google Image Search. 68 | var response = x.response; 69 | if (!response || !response.responseData || !response.responseData.results || 70 | response.responseData.results.length === 0) { 71 | errorCallback('No response from Google Image search!'); 72 | return; 73 | } 74 | var firstResult = response.responseData.results[0]; 75 | // Take the thumbnail instead of the full image to get an approximately 76 | // consistent image size. 77 | var imageUrl = firstResult.tbUrl; 78 | var width = parseInt(firstResult.tbWidth); 79 | var height = parseInt(firstResult.tbHeight); 80 | console.assert( 81 | typeof imageUrl == 'string' && !isNaN(width) && !isNaN(height), 82 | 'Unexpected respose from the Google Image Search API!'); 83 | callback(imageUrl, width, height); 84 | }; 85 | x.onerror = function() { 86 | errorCallback('Network error.'); 87 | }; 88 | x.send(); 89 | } 90 | 91 | function renderStatus(statusText) { 92 | document.getElementById('status').textContent = statusText; 93 | } 94 | 95 | document.addEventListener('DOMContentLoaded', function() { 96 | getCurrentTabUrl(function(url) { 97 | // Put the image URL in Google search. 98 | renderStatus('Performing Google Image search for ' + url); 99 | 100 | getImageUrl(url, function(imageUrl, width, height) { 101 | 102 | renderStatus('Search term: ' + url + '\n' + 103 | 'Google image search result: ' + imageUrl); 104 | var imageResult = document.getElementById('image-result'); 105 | // Explicitly set the width/height to minimize the number of reflows. For 106 | // a single image, this does not matter, but if you're going to embed 107 | // multiple external images in your page, then the absence of width/height 108 | // attributes causes the popup to resize multiple times. 109 | imageResult.width = width; 110 | imageResult.height = height; 111 | imageResult.src = imageUrl; 112 | imageResult.hidden = false; 113 | 114 | }, function(errorMessage) { 115 | renderStatus('Cannot display image. ' + errorMessage); 116 | }); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /github.js: -------------------------------------------------------------------------------- 1 | function addRepoPageItems(){ 2 | 3 | if( $( "h1 svg.octicon-repo ~ strong[itemprop='name'] + a.cci, h1 svg.octicon-lock ~ strong[itemprop='name'] + a.cci" ).length == 0 ){ 4 | 5 | // Start with a grey status indicator until we determine the project's build status 6 | $("h1 svg.octicon-repo ~ strong[itemprop='name'], h1 svg.octicon-lock ~ strong[itemprop='name']").after(''); 7 | } 8 | 9 | if( $( ".cci.btn" ).length == 0 ){ 10 | 11 | // start with a follow button until we determine if we're actually following this project 12 | $("ul.pagehead-actions").prepend(genPageAction); 13 | $( ".cci.btn" ).click(function( e ){ 14 | 15 | if( apiToken.length == 0 ){ 16 | alert( "Your CircleCI API token must be set in Pointless' Options page to use this feature." ); 17 | return 18 | } 19 | 20 | const followConfirm = "Are you sure you want to follow this project on CircleCI?"; 21 | const unFollowConfirm = "Are you sure you want to stop following this project? If you are the last person following, the project will stop building. Continue?"; 22 | const firstFollowConfirm = "This will start building this project on CircleCI with you following the project. Are you sure?"; 23 | const cantFollowAlert = "You do not have the proper permissions to follow this project."; 24 | 25 | if( project.following ){ 26 | 27 | if( confirm( unFollowConfirm ) == true ){ 28 | $.post( apiURL + "project/github/" + project.org + "/" + project.repo + "/unfollow?circle-token=" + apiToken, function( data ){ 29 | 30 | $( ".cci.btn span" ).text( "Follow" ); 31 | $( ".cci.btn" ).attr( "title", "Follow this project on CircleCI." ); 32 | alert( "You have successfully unfollowed " + project.org + "/" + project.repo + "." ); 33 | }); 34 | } 35 | } else{ 36 | if( project.writable ){ 37 | 38 | if( confirm( followConfirm ) == true ){ 39 | $.post( apiURL + "project/github/" + project.org + "/" + project.repo + "/follow?circle-token=" + apiToken, function( data ){ 40 | 41 | $( ".cci.btn span" ).text( "Unfollow" ); 42 | $( ".cci.btn" ).attr( "title", "Unfollow this project on CircleCI." ); 43 | alert( "You have successfully followed " + project.org + "/" + project.repo + "." ); 44 | }); 45 | } 46 | } else{ 47 | alert( cantFollowAlert ); 48 | } 49 | } 50 | 51 | e.preventDefault(); 52 | }); 53 | } 54 | } 55 | 56 | function genPageAction(){ 57 | 58 | return ` 59 |
  • 60 | 61 | Follow 62 | 63 |
  • `; 64 | } 65 | 66 | function getAPIToken(){ 67 | 68 | port = chrome.runtime.connect( {name: "github"} ); 69 | port.postMessage( {request: "api-token"} ); 70 | port.onMessage.addListener( function( msg ){ 71 | if( msg.request == "api-token"){ 72 | apiToken = msg.response; 73 | 74 | initGitHub(); 75 | } 76 | }); 77 | } 78 | 79 | function initGitHub(){ 80 | 81 | addRepoPageItems(); 82 | 83 | $.getJSON( apiURL + "project/github/" + project.org + "/" + project.repo + "/settings?circle-token=" + apiToken, function( data, httpStatus ){ 84 | 85 | project.defaultBranch = data.default_branch; 86 | project.following = data.following; 87 | // The below line is broken. Temp removing it. 88 | //project.writable = data.scopes.includes("write-settings"); 89 | project.building = data.has_usable_key; 90 | project.foss = data.oss; 91 | 92 | // Get build status if project is building on CircleCI 93 | if( project.building ){ 94 | 95 | // Choose branch based on contex 96 | var curBranch = ""; 97 | // if we're on the main page, viewing a non-default branch 98 | if( window.location.pathname.split("/")[3] == "tree" ){ 99 | curBranch = window.location.pathname.split("/")[4]; 100 | // base branch detection for PRs is causing problems with GitHub's new UI. For now, we'll disable and just use the default. 101 | //}else if( window.location.pathname.split("/")[3] == "pull" ){ 102 | // curBranch = $( "div.TableObject-item--primary a.author + span.commit-ref span.css-truncate-target" ).text(); 103 | }else{ 104 | curBranch = project.defaultBranch; 105 | } 106 | 107 | $.getJSON( apiURL + "project/github/" + project.org + "/" + project.repo + "/tree/" + curBranch + "?circle-token=" + apiToken, function( data ){ 108 | lastBuild = data[0].status; 109 | 110 | // check build status of last build that completed 111 | var classStatus; 112 | 113 | switch( lastBuild ){ 114 | case "success": 115 | case "fixed": 116 | classStatus = "passed"; 117 | break; 118 | 119 | case "failed": 120 | case "timedout": 121 | classStatus = "failed"; 122 | break; 123 | 124 | case "running": 125 | classStatus = "running"; 126 | break; 127 | 128 | default: 129 | classStatus = "error"; 130 | } 131 | 132 | $( "h1 svg.octicon-repo ~ a.cci span, h1 svg.octicon-lock ~ a.cci span" ).addClass( classStatus ); 133 | $( "h1 svg.octicon-repo ~ a.cci, h1 svg.octicon-lock ~ a.cci" ).attr({ 134 | "href": "https://app.circleci.com/pipelines/github/" + project.org + "/" + project.repo + "?branch=" + curBranch, 135 | "title": curBranch + " branch build status: " + classStatus + ". Click to visit project on CircleCI." 136 | }); 137 | }); 138 | } 139 | 140 | // update CircleCI pagehead-action with following status if needed 141 | if( project.following ){ 142 | $( ".cci.btn span" ).text( "Unfollow" ); 143 | $( ".cci.btn" ).attr( "title", "Unfollow this project on CircleCI." ); 144 | }else if( !project.writable && !project.foss ){ 145 | $( ".cci.btn" ).attr( "title", "You do not have permission to follow this project on CircleCI." ); 146 | }else{ 147 | $( ".cci.btn" ).attr( "title", "Follow this project on CircleCI." ); 148 | } 149 | }) 150 | } 151 | 152 | apiURL = "https://circleci.com/api/v1.1/"; 153 | 154 | project = { 155 | org: window.location.pathname.split("/")[1], 156 | repo: window.location.pathname.split("/")[2], 157 | defaultBranch: "master", // asume master until told otherwise 158 | following: false, 159 | writable: false, // whether the user has write permission to the repo 160 | building: true, // whether the project is building on CircleCI 161 | foss: false 162 | }; 163 | 164 | 165 | var port; 166 | var apiToken; 167 | 168 | $(document).ready(function(){ 169 | 170 | 171 | getAPIToken(); 172 | 173 | $(document).on('pjax:end', function (t) { 174 | initGitHub(); 175 | }); 176 | 177 | }); 178 | -------------------------------------------------------------------------------- /vendor/algoliasearch-4.1.0.min.js: -------------------------------------------------------------------------------- 1 | /*! algoliasearch-lite.umd.js | 4.1.0 | © Algolia, inc. | https://github.com/algolia/algoliasearch-client-javascript */ 2 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).algoliasearch=t()}(this,(function(){"use strict";function e(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function t(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function r(r){for(var n=1;n=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}function o(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(!(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e)))return;var r=[],n=!0,o=!1,a=void 0;try{for(var u,i=e[Symbol.iterator]();!(n=(u=i.next()).done)&&(r.push(u.value),!t||r.length!==t);n=!0);}catch(e){o=!0,a=e}finally{try{n||null==i.return||i.return()}finally{if(o)throw a}}return r}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function a(e){return function(e){if(Array.isArray(e)){for(var t=0,r=new Array(e.length);t2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return Promise.resolve().then((function(){var r=JSON.stringify(e),n=a()[r];return Promise.all([n||t(),void 0!==n])})).then((function(e){var t=o(e,2),n=t[0],a=t[1];return Promise.all([n,a||r.miss(n)])})).then((function(e){return o(e,1)[0]}))},set:function(e,t){return Promise.resolve().then((function(){var o=a();return o[JSON.stringify(e)]=t,n().setItem(r,JSON.stringify(o)),t}))},delete:function(e){return Promise.resolve().then((function(){var t=a();delete t[JSON.stringify(e)],n().setItem(r,JSON.stringify(t))}))},clear:function(){return Promise.resolve().then((function(){n().removeItem(r)}))}}}function i(e){var t=a(e.caches),r=t.shift();return void 0===r?{get:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}},n=t();return n.then((function(e){return Promise.all([e,r.miss(e)])})).then((function(e){return o(e,1)[0]}))},set:function(e,t){return Promise.resolve(t)},delete:function(e){return Promise.resolve()},clear:function(){return Promise.resolve()}}:{get:function(e,n){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return r.get(e,n,o).catch((function(){return i({caches:t}).get(e,n,o)}))},set:function(e,n){return r.set(e,n).catch((function(){return i({caches:t}).set(e,n)}))},delete:function(e){return r.delete(e).catch((function(){return i({caches:t}).delete(e)}))},clear:function(){return r.clear().catch((function(){return i({caches:t}).clear()}))}}}function s(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{serializable:!0},t={};return{get:function(r,n){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}},a=JSON.stringify(r);if(a in t)return Promise.resolve(e.serializable?JSON.parse(t[a]):t[a]);var u=n(),i=o&&o.miss||function(){return Promise.resolve()};return u.then((function(e){return i(e)})).then((function(){return u}))},set:function(r,n){return t[JSON.stringify(r)]=e.serializable?JSON.stringify(n):n,Promise.resolve(n)},delete:function(e){return delete t[JSON.stringify(e)],Promise.resolve()},clear:function(){return t={},Promise.resolve()}}}function c(e){for(var t=e.length-1;t>0;t--){var r=Math.floor(Math.random()*(t+1)),n=e[t];e[t]=e[r],e[r]=n}return e}function l(e,t){return Object.keys(void 0!==t?t:{}).forEach((function(r){e[r]=t[r](e)})),e}function f(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n0?n:void 0,timeout:r.timeout||t,headers:r.headers||{},queryParameters:r.queryParameters||{},cacheable:r.cacheable}}var m={Read:1,Write:2,Any:3},p=1,v=2,g=3;function y(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:p;return r({},e,{status:t,lastUpdate:Date.now()})}function b(e){return{protocol:e.protocol||"https",url:e.url,accept:e.accept||m.Any}}var O="GET",P="POST";function q(e,t){return Promise.all(t.map((function(t){return e.get(t,(function(){return Promise.resolve(y(t))}))}))).then((function(e){var r=e.filter((function(e){return function(e){return e.status===p||Date.now()-e.lastUpdate>12e4}(e)})),n=e.filter((function(e){return function(e){return e.status===g&&Date.now()-e.lastUpdate<=12e4}(e)})),o=[].concat(a(r),a(n));return{getTimeout:function(e,t){return(0===n.length&&0===e?1:n.length+3+e)*t},statelessHosts:o.length>0?o.map((function(e){return b(e)})):t}}))}function j(e,t,n,o){var u=[],i=function(e,t){if(e.method===O||void 0===e.data&&void 0===t.data)return;var n=Array.isArray(e.data)?e.data:r({},e.data,{},t.data);return JSON.stringify(n)}(n,o),s=function(e,t){var n=r({},e.headers,{},t.headers),o={};return Object.keys(n).forEach((function(e){var t=n[e];o[e.toLowerCase()]=t})),o}(e,o),c=n.method,l=n.method!==O?{}:r({},n.data,{},o.data),f=r({"x-algolia-agent":e.userAgent.value},e.queryParameters,{},l,{},o.queryParameters),h=0,d=function t(r,a){var l=r.pop();if(void 0===l)throw{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:A(u)};var d={data:i,headers:s,method:c,url:w(l,n.path,f),connectTimeout:a(h,e.timeouts.connect),responseTimeout:a(h,o.timeout)},m=function(e){var t={request:d,response:e,host:l,triesLeft:r.length};return u.push(t),t},p={onSucess:function(e){return function(e){try{return JSON.parse(e.content)}catch(t){throw function(e,t){return{name:"DeserializationError",message:e,response:t}}(t.message,e)}}(e)},onRetry:function(n){var o=m(n);return n.isTimedOut&&h++,Promise.all([e.logger.info("Retryable failure",x(o)),e.hostsCache.set(l,y(l,n.isTimedOut?g:v))]).then((function(){return t(r,a)}))},onFail:function(e){throw m(e),function(e,t){var r=e.content,n=e.status,o=r;try{o=JSON.parse(r).message}catch(e){}return function(e,t,r){return{name:"ApiError",message:e,status:t,transporterStackTrace:r}}(o,n,t)}(e,A(u))}};return e.requester.send(d).then((function(e){return function(e,t){return function(e){var t=e.status;return e.isTimedOut||function(e){var t=e.isTimedOut,r=e.status;return!t&&0==~~r}(e)||2!=~~(t/100)&&4!=~~(t/100)}(e)?t.onRetry(e):2==~~(e.status/100)?t.onSucess(e):t.onFail(e)}(e,p)}))};return q(e.hostsCache,t).then((function(e){return d(a(e.statelessHosts).reverse(),e.getTimeout)}))}function S(e){var t={value:"Algolia for JavaScript (".concat(e,")"),add:function(e){var r="; ".concat(e.segment).concat(void 0!==e.version?" (".concat(e.version,")"):"");return-1===t.value.indexOf(r)&&(t.value="".concat(t.value).concat(r)),t}};return t}function w(e,t,r){var n=T(r),o="".concat(e.protocol,"://").concat(e.url,"/").concat("/"===t.charAt(0)?t.substr(1):t);return n.length&&(o+="?".concat(n)),o}function T(e){return Object.keys(e).map((function(t){return f("%s=%s",t,(r=e[t],"[object Object]"===Object.prototype.toString.call(r)||"[object Array]"===Object.prototype.toString.call(r)?JSON.stringify(e[t]):e[t]));var r})).join("&")}function A(e){return e.map((function(e){return x(e)}))}function x(e){var t=e.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return r({},e,{request:r({},e.request,{headers:r({},e.request.headers,{},t)})})}var C=function(e){var t=e.appId,n=function(e,t,r){var n={"x-algolia-api-key":r,"x-algolia-application-id":t};return{headers:function(){return e===h.WithinHeaders?n:{}},queryParameters:function(){return e===h.WithinQueryParameters?n:{}}}}(void 0!==e.authMode?e.authMode:h.WithinHeaders,t,e.apiKey),a=function(e){var t=e.hostsCache,r=e.logger,n=e.requester,a=e.requestsCache,u=e.responsesCache,i=e.timeouts,s=e.userAgent,c=e.hosts,l=e.queryParameters,f={hostsCache:t,logger:r,requester:n,requestsCache:a,responsesCache:u,timeouts:i,userAgent:s,headers:e.headers,queryParameters:l,hosts:c.map((function(e){return b(e)})),read:function(e,t){var r=d(t,f.timeouts.read),n=function(){return j(f,f.hosts.filter((function(e){return 0!=(e.accept&m.Read)})),e,r)};if(!0!==(void 0!==r.cacheable?r.cacheable:e.cacheable))return n();var a={request:e,mappedRequestOptions:r,transporter:{queryParameters:f.queryParameters,headers:f.headers}};return f.responsesCache.get(a,(function(){return f.requestsCache.get(a,(function(){return f.requestsCache.set(a,n()).then((function(e){return Promise.all([f.requestsCache.delete(a),e])}),(function(e){return Promise.all([f.requestsCache.delete(a),Promise.reject(e)])})).then((function(e){var t=o(e,2);t[0];return t[1]}))}))}),{miss:function(e){return f.responsesCache.set(a,e)}})},write:function(e,t){return j(f,f.hosts.filter((function(e){return 0!=(e.accept&m.Write)})),e,d(t,f.timeouts.write))}};return f}(r({hosts:[{url:"".concat(t,"-dsn.algolia.net"),accept:m.Read},{url:"".concat(t,".algolia.net"),accept:m.Write}].concat(c([{url:"".concat(t,"-1.algolianet.com")},{url:"".concat(t,"-2.algolianet.com")},{url:"".concat(t,"-3.algolianet.com")}]))},e,{headers:r({},n.headers(),{},{"content-type":"application/x-www-form-urlencoded"},{},e.headers),queryParameters:r({},n.queryParameters(),{},e.queryParameters)}));return l({transporter:a,appId:t,addAlgoliaAgent:function(e,t){a.userAgent.add({segment:e,version:t})},clearCache:function(){return Promise.all([a.requestsCache.clear(),a.responsesCache.clear()]).then((function(){}))}},e.methods)},N=function(e){return function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n={transporter:e.transporter,appId:e.appId,indexName:t};return l(n,r.methods)}},k=function(e){return function(t,n){var o=t.map((function(e){return r({},e,{params:T(e.params||{})})}));return e.transporter.read({method:P,path:"1/indexes/*/queries",data:{requests:o},cacheable:!0},n)}},J=function(e){return function(t,o){return Promise.all(t.map((function(t){var a=t.params,u=a.facetName,i=a.facetQuery,s=n(a,["facetName","facetQuery"]);return N(e)(t.indexName,{methods:{searchForFacetValues:I}}).searchForFacetValues(u,i,r({},o,{},s))})))}},E=function(e){return function(t,r){return e.transporter.read({method:P,path:f("1/indexes/%s/query",e.indexName),data:{query:t},cacheable:!0},r)}},I=function(e){return function(t,r,n){return e.transporter.read({method:P,path:f("1/indexes/%s/facets/%s/query",e.indexName,t),data:{facetQuery:r},cacheable:!0},n)}},F=1,R=2,D=3;function W(e,t,n){var o,a={appId:e,apiKey:t,timeouts:{connect:1,read:2,write:30},requester:{send:function(e){return new Promise((function(t){var r=new XMLHttpRequest;r.open(e.method,e.url,!0),Object.keys(e.headers).forEach((function(t){return r.setRequestHeader(t,e.headers[t])}));var n,o=function(e,n){return setTimeout((function(){r.abort(),t({status:0,content:n,isTimedOut:!0})}),1e3*e)},a=o(e.connectTimeout,"Connection timeout");r.onreadystatechange=function(){r.readyState>r.OPENED&&void 0===n&&(clearTimeout(a),n=o(e.responseTimeout,"Socket timeout"))},r.onerror=function(){0===r.status&&(clearTimeout(a),clearTimeout(n),t({content:r.responseText||"Network request failed",status:r.status,isTimedOut:!1}))},r.onload=function(){clearTimeout(a),clearTimeout(n),t({content:r.responseText,status:r.status,isTimedOut:!1})},r.send(e.data)}))}},logger:(o=D,{debug:function(e,t){return F>=o&&console.debug(e,t),Promise.resolve()},info:function(e,t){return R>=o&&console.info(e,t),Promise.resolve()},error:function(e,t){return console.error(e,t),Promise.resolve()}}),responsesCache:s(),requestsCache:s({serializable:!1}),hostsCache:i({caches:[u({key:"".concat("4.1.0","-").concat(e)}),s()]}),userAgent:S("4.1.0").add({segment:"Browser",version:"lite"}),authMode:h.WithinQueryParameters};return C(r({},a,{},n,{methods:{search:k,searchForFacetValues:J,multipleQueries:k,multipleSearchForFacetValues:J,initIndex:function(e){return function(t){return N(e)(t,{methods:{search:E,searchForFacetValues:I}})}}}}))}return W.version="4.1.0",W})); 3 | -------------------------------------------------------------------------------- /vendor/jquery-3.5.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0