├── .gitignore
├── LICENSE
├── README.md
├── central
├── README.md
├── auto-tbd.user.js
├── central-collapsable-sidebar.user.js
├── central-estimates-time-conversion.user.js
├── central-friendly-ui-for-multiple-pr.user.js
├── central-issues-kanban.user.js
├── central-link-pr-formatting.user.js
├── central-links.user.js
├── central-my-favorite-tickets.user.js
├── central-new-issue-templates.user.js
├── central-redirect-to-jira.user.js
├── clocking-nag.user.js
├── cmd-save.user.js
├── issue-list-tools.user.js
├── issue-summary.user.js
├── key-commands.js
├── over-estimate.user.js
├── party-llamacorn.user.js
├── qa-headers.user.js
├── qa-testing-instructions.user.js
├── relay-for-toggl.user.js
├── sticky-clocker-dropdowns.user.js
├── target-blank.user.js
└── uncheck-email-all.user.js
├── dotorg
├── README.md
├── dotorg-helper.user.js
├── dotorg-hider.user.js
└── dotorg-review-collector.user.js
├── github
├── README.md
├── github-pr-tools-merge.user.js
├── github-pr-tools.user.js
├── github-products-branch-protection.user.js
└── github-pull-prefix-removal.user.js
├── img
└── toggl.gif
├── jira
├── README.md
├── jira-bulk-edit-uncheck-email.user.js
├── jira-convert-threads-to-links.user.js
├── jira-dashboard-styles-sprint-health-gadget.user.js
├── jira-global-nav-dashboard.user.js
├── jira-global-nav-your-work.user.js
├── jira-mywork-hide-events.user.js
└── jira-remove-harvest.user.js
├── liveagent
├── README.md
├── liveagent-clickafy-urls.user.js
└── liveagent-plugin-versions.user.js
├── other
├── README.md
├── tribe-sniffer.min.js
└── tribe-sniffer.user.js
├── premium-forum
├── README.md
├── premium-forum-collapse-convo.user.js
├── premium-forum-move-status-box.user.js
├── premium-forum-plugin-versions.user.js
├── premium-forum-private-topic.user.js
├── premium-forum-reformat-sysinfo.user.js
├── premium-forum-thread-list-colors.user.js
└── premium-forum-users-licenses.user.js
├── uservoice
├── README.md
└── uservoice.user.js
└── waitForKeyElements.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Matthew Batchelder
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tampermonkey-scripts
2 |
3 | These are some common Modern Tribe Tampermonkey scripts.
4 |
5 | ## Installation
6 |
7 | 1. Install [Tampermonkey](https://tampermonkey.net/)
8 | 1. Select a script in this repo that you wish to use. View the file and click the _Raw_ button at the top of the file to view its source
9 | 1. Copy the source
10 | 1. Open Tampermonkey in your browser and click the Add Script tab (icon with a plus symbol)
11 | 1. Paste the source into the script window and hit save
12 | 1. Voila!
13 |
14 | ## Scripts
15 |
16 | * [GitHub](/github)
17 | * [Jira](/jira)
18 | * [LiveAgent](/liveagent)
19 | * [UserVoice](/uservoice)
20 | * [WP.org](/dotorg)
21 | * [Other](other)
22 |
23 | ## Old Scripts
24 |
25 | These ones are largely retired, but may still have some use.
26 |
27 | * [Central](/central)
28 | * [Premium Forum](/premium-forum)
29 |
30 |
--------------------------------------------------------------------------------
/central/README.md:
--------------------------------------------------------------------------------
1 | # Central
2 |
3 | ## `central-redirect-to-jira.user.js`
4 |
5 | Redirect single Central Issue links to finding it in Jira.
6 | Won't work for issues that didn't migrate over to Jira.
7 | Won't work if your Central Issue URL has cruft on it, such as anchor links.
8 |
9 | 
10 |
11 | ## `central-collapsable-sidebar.user.js`
12 |
13 | Improves the content space and make sure sidebar is collapsible to the same width of http://tri.be/ and sidebar is accessible just by moving the cursor to the sidebar to make it available when only needed it.
14 |
15 | 
16 |
17 | ## `central-link-pr-formatting.user.js`
18 |
19 | Format the links that points to Pull Request in Central from a format like: `https://github.com/moderntribe/event-tickets-plus/pull/490` into a format like `event-tickets-plus#490
20 | `event-tickets-plus#490` similar to what GitHub does when a Pull Request is referenced inside of an issue.
21 |
22 | ## `central-friendly-ui-for-multiple-pr.user.js`
23 |
24 | Improves the UI when an issue has more than a single Pull Request to improve the addition / removal of new Pull Requests.
25 |
26 | 
27 |
28 | ## `central-estimates-time-conversion.user.js`
29 |
30 | Updates the estimate field to make it wider and available to accepts entries such as: 4h 5m and make them work exactly the same as **Spent time** field.
31 |
32 | ## `central-issues-kanban.user.js`
33 |
34 | Creates a kanban board for all issues queried. The board is hidden by default and can be toggled open and closed.
35 |
36 | ## `central-links.user.js`
37 |
38 | Turns the _Pull Request_ field in Modern Tribe's "Central" issues pages into a link.
39 |
40 | ## `central-my-favorite-tickets.user.js`
41 |
42 | Add your favorite ticket numbers to the main menu for easy access.
43 | The ticket numbers and descriptions need to be manually adjusted in the script.
44 |
45 | 
46 |
47 | ## `central-new-issue-templates.user.js`
48 |
49 | Starter Templates for New Central Issues
50 |
51 | ## `clocking-nag.user.js`
52 |
53 | Shows your week of clocking at the top of Central and prompts you to enter some clocked time if you are falling behind.
54 |
55 | 
56 |
57 |
58 | ## `issue-list-tools.user.js`
59 |
60 | This script provides handy tools when viewing an issue list:
61 |
62 | * Toggle open/close row groupings of issues
63 |
64 | Ok...fine...that's just one tool, but this has room to grow.
65 |
66 | ## `issue-summary.user.js`
67 |
68 | * Color-codes central issues by status and swaps out issue types (Support, Feature, Bug) with FontAwesome "icons".
69 | * Adds a summary of on-page issues grouped by issue status.
70 |
71 | 
72 |
73 | ## `key-commands.js`
74 |
75 | Sorta like vim/gmail key commands
76 | * `/` places your cursor in the search box.
77 | * in query view: `j` and `k` move your selection up or down. Pressing `enter` on the selection takes you to that ticket.
78 | * `i` takes you to the My Query.
79 | If you don't like the query view `i` takes you to, change the URL in line 15.
80 |
81 | ## `over-estimate.user.js`
82 |
83 | * highlight the spent hours with pink if they are over estimate
84 | * works on both issue lists and single issues
85 |
86 | ## `party-llamacorn.user.js`
87 |
88 | When a ticket is set to _Pending Merge_ or _Complete_, a llamacorn flies across the screen.
89 |
90 | ## `qa-headers.user.js`
91 |
92 | Styles QA headers so scanning QA activity on tickets is quicker. Relies on using the following headers in Central:
93 |
94 | ```
95 | h2. QA PASSED (plus any extra text you want here)
96 |
97 | h2. RETURNED (plus any extra text you want here)
98 | ```
99 |
100 | ## `qa-testing-instructions.user.js`
101 |
102 | Adds a button to the update form in a specific issue.
103 | When clicking the button, the notes and testing instructions fields are automatically populated with formatted starter text.
104 |
105 | ## `relay-for-toggl.user.js`
106 |
107 | Adds a button to tasks in central to start toggl.
108 | You need to provide your toggl API key and the proxy address.
109 | You are welcome to use the proxy at https://relay-for-toggl.herokuapp.com/
110 | Proxy repo: https://github.com/binarygary/relay-for-toggl
111 | 
112 |
113 | ## `sticky-clocker-dropdowns.user.js`
114 |
115 | Sets the __project__ and __activity__ to previous values after clocking time on the clocking tool.
116 | This does not work if the clocking tool is opened in a pop-up window.
117 |
118 | ## `target-blank.user.js`
119 |
120 | Auto-adds `target="_blank"` to all external links.
121 |
122 | ## `uncheck-email-all.user.js`
123 |
124 | Auto-uncheck the "email all" checkbox so you only send an email when you _really_ want to.
125 |
--------------------------------------------------------------------------------
/central/auto-tbd.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Auto-TBD on new
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Auto-sets TBD on new issues
6 | // @author You
7 | // @include /^https:\/\/central.tri.be\/projects\/premium-plugins\/issues\/new\/?/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | var central_links = {};
12 |
13 | ( function( $, my ) {
14 | my.init = function() {
15 | $( '#issue_fixed_version_id' ).val( 444 );
16 | };
17 |
18 | $( function() {
19 | my.init();
20 | });
21 | } )( jQuery, central_links );
22 |
--------------------------------------------------------------------------------
/central/central-collapsable-sidebar.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Collapsable Sidebar
3 | // @namespace http://tampermonkey.net/
4 | // @version 0.1
5 | // @description Collapse sidebar on the left on central, available on hover on the sidebar (only on desktop).
6 | // @author Crisoforo Gaspar Hernandez
7 | // @include https://central.tri.be/*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | var sidebarContainer = document.getElementById('side-container');
13 | var sidebar = document.getElementById('sidebar');
14 | var menu = document.getElementById('main-menu');
15 | var wrapper = document.getElementById('wrapper');
16 | var topMenu = document.getElementById('top-menu');
17 | var navLogo = document.getElementById('nav-logo');
18 | var banner = document.querySelector('a.banner');
19 |
20 | if ( ! sidebarContainer || ! sidebar || ! menu || ! topMenu ) {
21 | return;
22 | }
23 |
24 | var mobileBreakpoint = 950;
25 | var logoURL = 'https://tri.be/content/uploads/2017/06/tribelogo.png';
26 |
27 | // Create a new logo element
28 | var img = document.createElement('img');
29 | img.style.width = '48px';
30 | img.style.position = 'absolute';
31 | img.style.right = '15px';
32 | img.style.top = '25px';
33 | img.setAttribute('src', logoURL);
34 | img.style.transition = 'opacity .1s ease-in';
35 | navLogo.insertAdjacentElement( 'beforebegin', img );
36 |
37 | menu.style.transition = 'opacity .25s ease-in';
38 | sidebar.style.transition = 'opacity .1s ease-in';
39 | navLogo.style.transition = 'opacity .1s ease-in';
40 | sidebarContainer.style.transition = 'transform .25s ease-in';
41 |
42 | var isOpen = true;
43 |
44 | function close() {
45 | sidebarContainer.style.transform = 'translateX(-120px)';
46 | sidebar.style.opacity = '0';
47 | menu.style.opacity = '0';
48 | navLogo.style.opacity = '0';
49 | sidebar.style.pointerEvents = 'none';
50 | menu.style.pointerEvents = 'none';
51 | navLogo.style.pointerEvents = 'none';
52 | isOpen = false;
53 | }
54 |
55 | function open() {
56 | img.style.opacity = '0';
57 | topMenu.style.zIndex = '1';
58 | wrapper.style.width = 'calc(100% - 80px)';
59 | sidebarContainer.style.transform = 'translateX(0)';
60 |
61 | if ( banner ) {
62 | banner.style.width = 'calc(100% - 80px)';
63 | }
64 | isOpen = true;
65 | }
66 |
67 | sidebarContainer.addEventListener('transitionend', function( event ) {
68 | if ( 'propertyName' in event && 'transform' === event.propertyName ) {
69 | if ( isOpen ) {
70 | sidebar.style.opacity = '1';
71 | menu.style.opacity = '1';
72 | navLogo.style.opacity = '1';
73 | sidebar.style.pointerEvents = 'auto';
74 | menu.style.pointerEvents = 'auto';
75 | navLogo.style.pointerEvents = 'auto';
76 | } else {
77 | img.style.opacity = '1';
78 | }
79 | topMenu.style.zIndex = isOpen ? '1' : '';
80 | }
81 | });
82 |
83 | function destroy() {
84 | img.style.opacity = '0';
85 | topMenu.style.zIndex = '';
86 | wrapper.style.width = '';
87 | sidebarContainer.style.transform = 'translateX(0)';
88 | isOpen = true;
89 | // Remove Listeners
90 | sidebarContainer.removeEventListener('mouseenter', open);
91 | sidebarContainer.removeEventListener('mouseleave', close);
92 | }
93 |
94 | function init() {
95 | // Adjust new width
96 | wrapper.style.width = 'calc(100% - 80px)';
97 |
98 | if ( banner ) {
99 | banner.style.width = 'calc(100% - 80px)';
100 | }
101 | topMenu.style.zIndex = '1';
102 | // Attach Listeners
103 | sidebarContainer.addEventListener('mouseenter', open);
104 | sidebarContainer.addEventListener('mouseleave', close);
105 |
106 | isOpen = true;
107 | // By default it should be closed
108 | close();
109 | }
110 |
111 | function onResize() {
112 | if ( document.documentElement.clientWidth <= mobileBreakpoint ) {
113 | destroy();
114 | } else {
115 | init();
116 | }
117 | }
118 |
119 | // Fire it manually on start.
120 | onResize();
121 | window.addEventListener( 'resize', onResize );
122 | })();
123 |
--------------------------------------------------------------------------------
/central/central-estimates-time-conversion.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Central Estimates time conversion
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Makes sure estimate field accepts format such as 2h 5m and converts it into an float value.
6 | // @author Crisoforo Gaspar Hernandez
7 | // @include https://central.tri.be/issues/*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | var input = document.getElementById('issue_estimated_hours');
15 | var KEYS = {
16 | MINUTE: 'm',
17 | HOUR: 'h',
18 | };
19 |
20 | if ( ! input ) {
21 | return;
22 | }
23 |
24 | input.style.width = '40px';
25 | var NOT_FOUND = -1;
26 | var decimalPlaces = 2;
27 |
28 | function to_float( str ) {
29 | var number = parseFloat( str );
30 | return isNaN( number ) ? 0 : number;
31 | }
32 |
33 | function format( input ) {
34 | return input.toFixed( decimalPlaces );
35 | }
36 |
37 | function getAmount( value ) {
38 | return value
39 | // Separate words
40 | .split(' ')
41 | // Translate strings to float numbers
42 | .map( function( chunk ) {
43 | if ( chunk.indexOf( KEYS.HOUR ) !== NOT_FOUND ) {
44 | return to_float( chunk.replace( KEYS.HOUR, '') );
45 | } else if ( chunk.indexOf( KEYS.MINUTE ) !== NOT_FOUND ) {
46 | return to_float( chunk.replace( KEYS.MINUTE, '') ) / 60 ;
47 | } else {
48 | return 0;
49 | }
50 | // return the math (sum) on them
51 | }).reduce(function(accumulator, currentValue) {
52 | return accumulator + currentValue;
53 | }, 0);
54 | }
55 |
56 | function onBlur() {
57 | if ( ! input.value ) {
58 | return;
59 | }
60 |
61 | var value = input.value.toLowerCase();
62 |
63 | // does not contain an h or an m
64 | if ( value.indexOf( KEYS.HOUR ) === NOT_FOUND && value.indexOf( KEYS.MINUTE ) === NOT_FOUND ) {
65 | input.value = format( to_float( value ) );
66 | } else {
67 | input.value = format( to_float( getAmount( value ) ) );
68 | }
69 | }
70 |
71 | input.addEventListener( 'blur', onBlur );
72 |
73 | })();
74 |
--------------------------------------------------------------------------------
/central/central-friendly-ui-for-multiple-pr.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Friendly UI for PR list and forums Threads
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Update the UI to allow improve the UI to add more than one PR to an issue.
6 | // @author Crisoforo Gaspar Hernandez
7 | // @include https://central.tri.be/issues/*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | var prEl = document.querySelector('#issue_custom_field_values_56');
15 | var issueForm = document.querySelector('#issue-form');
16 |
17 | if ( ! issueForm || ! prEl ) {
18 | return;
19 | }
20 |
21 | hide(prEl);
22 |
23 | var PULL_REQUESTS = [];
24 | var ZERO = 0;
25 | var currentValue = toMultipleValue(prEl.value).map( function( value ) {
26 | createDynamicField( value, prEl.parentNode );
27 | });
28 |
29 | if ( currentValue.length === ZERO ) {
30 | createDynamicField( '', prEl.parentNode );
31 | }
32 |
33 | function createDynamicField( value, parentNode ) {
34 | var container = document.createElement('div');
35 |
36 | var input = document.createElement('input');
37 | input.style.width = 'calc(100% - 80px)';
38 | input.style.padding = '2px 5px';
39 | input.style.marginRight = '10px';
40 | input.value = value;
41 |
42 | var add = document.createElement('button');
43 | add.innerText = '+';
44 | addButtonStyles(add);
45 |
46 | var remove = document.createElement('button');
47 | remove.innerText = '-';
48 | addButtonStyles(remove);
49 | remove.style.backgroundColor = '#e4554a';
50 |
51 | // Add field
52 | add.addEventListener('click', function(e) {
53 | e.preventDefault();
54 | createDynamicField( '', parentNode );
55 | });
56 |
57 | // Remove Field
58 | remove.addEventListener('click', function(e) {
59 | e.preventDefault();
60 |
61 | prEl.parentNode.removeChild( container );
62 | PULL_REQUESTS.splice(container.index, 1);
63 |
64 | // If all has been removed add one back so we never are empty
65 | if ( PULL_REQUESTS.length === 0 ) {
66 | createDynamicField( '', parentNode );
67 | }
68 | });
69 |
70 |
71 | container.appendChild(input);
72 | container.appendChild(add);
73 | container.appendChild(remove);
74 | // Save them for future use.
75 | container.index = PULL_REQUESTS.length;
76 | PULL_REQUESTS.push( container );
77 |
78 | container.style.marginBottom = '5px';
79 |
80 | parentNode.appendChild( container );
81 | }
82 |
83 |
84 | issueForm.addEventListener('submit', function( e ) {
85 | prEl.value = toSingleValue( PULL_REQUESTS );
86 | });
87 |
88 | function toSingleValue( group ) {
89 | return group.map(function(container) {
90 | return container.querySelector('input');
91 | }).filter(function( input ) {
92 | return input.value.replace(/\s/g, '').length > 0;
93 | }).map( function( item ) {
94 | return item && item.value ? item.value : '';
95 | }).join( ' ' );
96 | }
97 |
98 | function toMultipleValue( singleValue ) {
99 | var values = singleValue ? singleValue.trim().split(' ') : [];
100 | return values.map(function( item ) {
101 | return item.replace(/\s/g,'');
102 | }).filter(function( item ) {
103 | return item.length > 0;
104 | });
105 | }
106 |
107 | function hide( el ) {
108 | if ( el ) {
109 | el.style.display = 'none';
110 | }
111 | }
112 |
113 | function addButtonStyles( el ) {
114 | el.style.padding = '2px 5px';
115 | el.style.border = 'none';
116 | el.style.backgroundColor = '#1ca8c7';
117 | el.style.margin = '0 7px 0 0';
118 | el.style.color = '#FFF';
119 | el.style.fontWeight = 'bold';
120 | el.style.width = '20px';
121 | }
122 |
123 | })();
124 |
--------------------------------------------------------------------------------
/central/central-issues-kanban.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Central Issues Kanban
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Display a Kanban board of queried tickets
6 | // @author Paul Kim, Aaron Hanson
7 | // @include /https?:\/\/central.tri.be\/(projects\/)*[^\/]*\/?issues\/?/
8 | // @exclude /https?:\/\/central.tri.be\/(projects\/)*[^\/]*\/?issues\/[0-9]+\/?/
9 | // @grant none
10 | // ==/UserScript==
11 |
12 | ( function($) {
13 |
14 | var columns = {
15 | new: [],
16 | inProgress: [],
17 | designQA: [],
18 | codeReview: [],
19 | qa: [],
20 | merge: [],
21 | smoketest: [],
22 | release: [],
23 | };
24 |
25 | var status = {
26 | new: 'New',
27 | inProgress: 'In Progress',
28 | designQA: 'Design QA',
29 | codeReview: 'Pending Code Review',
30 | qa: 'Pending QA',
31 | merge: 'Pending Merge',
32 | smoketest: 'Pending Smoketest',
33 | release: 'Pending Release',
34 | };
35 |
36 | var keys = Object.keys(columns);
37 |
38 | var el = {
39 | titleBar: null,
40 | kanban: null,
41 | toggle: null,
42 | };
43 |
44 | var state = {
45 | showKanban: false,
46 | };
47 |
48 | var addStyles = function() {
49 | var css = '.kanban{display:-webkit-box;display:-ms-flexbox;display:flex;overflow-x:scroll;margin-bottom:2rem}.kanban a{text-decoration:none;color:#000}.kanban--hidden{display:none}.kanban-toggle{margin:2rem 0}.kanban-toggle__button{margin:0px;border:none;background:#157F9D;color:#fff;border-radius:2px;padding:5px 8px}.kanban-toggle__button:hover{background:#1CA8C7}.kanban__column{-webkit-box-flex:0;-ms-flex:none;flex:none;width:250px;margin:.5rem;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.kanban__column-header{-webkit-box-flex:0;-ms-flex:none;flex:none}.kanban__column-content{padding:.25rem;background:#f9f9f9;-webkit-box-flex:1;-ms-flex:auto;flex:auto}.card{background:#fff;margin:.75rem .45rem;padding:.45rem .75rem .45rem 1.25rem;border:#e6e6e6 1px solid;position:relative;vertical-align:middle;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-box-shadow:0 0 1px transparent;box-shadow:0 0 1px transparent;-webkit-backface-visibility:hidden;backface-visibility:hidden;-moz-osx-font-smoothing:grayscale;-webkit-transition-duration:0.3s;transition-duration:0.3s;-webkit-transition-property:-webkit-transform;transition-property:-webkit-transform;transition-property:transform;transition-property:transform, -webkit-transform}.card:before{content:"";height:calc(100% + 2px);width:5px;background:#666;position:absolute;top:-1px;left:-1px}.card:hover{background-color:#f9f9f9;-webkit-transform:scale(1.03);transform:scale(1.03)}.card .card__issue,.card .card__subject,.card .card__assigned-to{margin:.5rem 0;display:block}.card .card__issue a:hover,.card .card__issue a:focus,.card .card__subject a:hover,.card .card__subject a:focus,.card .card__assigned-to a:hover,.card .card__assigned-to a:focus{color:#136379}.card .card__issue{text-transform:uppercase;font-size:.75rem}.card .card__issue a{color:#21A6CB}.card .card__subject{font-size:1rem;margin:0}.card .card__assigned-to{font-size:.75rem}.card .card__assigned-to a{color:#666}.card--p1:before{background-color:#EA3546}.card--p2:before{background-color:#FF6700}.card--p3:before{background-color:#70BF67}.card--p4:before{background-color:#43BCCD}.card--p5:before{background-color:#9F9AA4}';
50 | var head = document.head;
51 | var style = document.createElement('style');
52 | style.type = 'text/css';
53 | style.appendChild(document.createTextNode(css));
54 |
55 | head.appendChild(style);
56 | };
57 |
58 | var insertAfter = function(newNode, refNode) {
59 | refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
60 | };
61 |
62 | var arrangeCard = function(card) {
63 | switch(card.status) {
64 | case 'New':
65 | columns.new.push(card);
66 | break;
67 | case 'In Progress':
68 | columns.inProgress.push(card);
69 | break;
70 | case 'Design QA':
71 | columns.designQA.push(card);
72 | break;
73 | case 'Pending Code Review':
74 | columns.codeReview.push(card);
75 | break;
76 | case 'Pending QA':
77 | columns.qa.push(card);
78 | break;
79 | case 'Pending Merge':
80 | columns.merge.push(card);
81 | break;
82 | case 'Pending Smoketest':
83 | columns.smoketest.push(card);
84 | break;
85 | case 'Pending Release':
86 | columns.release.push(card);
87 | break;
88 | }
89 | };
90 |
91 | var getCards = function() {
92 | var issuesArray = [].slice.call(document.querySelectorAll('#issue-list-body tr.issue'));
93 |
94 | issuesArray.forEach(function(issue) {
95 | var card = {
96 | issueNumber: issue.querySelector('.issue').innerHTML,
97 | status: issue.querySelector('.status').textContent,
98 | priority: issue.querySelector('.priority').textContent,
99 | subject: issue.querySelector('.subject').innerHTML,
100 | assignedTo: issue.querySelector('.assigned_to').innerHTML,
101 | };
102 |
103 | arrangeCard(card);
104 | });
105 | };
106 |
107 | var comparePriority = function(a, b) {
108 | return a.priority - b.priority;
109 | };
110 |
111 | var sortColumns = function() {
112 | keys.forEach(function(key) {
113 | columns[key].sort(comparePriority);
114 | });
115 | };
116 |
117 | var getColumnHeader = function(title) {
118 | var headerHtml = '';
119 | headerHtml += '';
120 | headerHtml += ' ';
121 |
122 | return headerHtml;
123 | };
124 |
125 | var getCardHtml = function(card) {
126 | var cardHtml = '
';
127 | cardHtml += '' + card.issueNumber + ' ';
128 | cardHtml += '
' + card.subject + '';
129 | cardHtml += '' + card.assignedTo + ' ';
130 | cardHtml += ' ';
131 |
132 | return cardHtml;
133 | };
134 |
135 | var getKanbanHtml = function() {
136 | var html = '';
137 |
138 | keys.forEach(function(key) {
139 | html += '';
140 | html += getColumnHeader(status[key]);
141 | html += '
';
142 | columns[key].forEach(function(card) {
143 | html += getCardHtml(card);
144 | });
145 | html += '
';
146 | html += '
';
147 | });
148 |
149 | return html;
150 | };
151 |
152 | var setKanban = function() {
153 | var wrapper = document.createElement('div');
154 | wrapper.classList.add('kanban', 'kanban--hidden');
155 | wrapper.innerHTML = getKanbanHtml();
156 | el.kanban = wrapper;
157 | insertAfter(wrapper, el.titleBar);
158 | };
159 |
160 | var getToggleHtml = function() {
161 | var button = document.createElement('button');
162 | button.classList.add('kanban-toggle__button');
163 | button.textContent = 'Toggle Kanban';
164 | el.toggle = button;
165 | return button;
166 | };
167 |
168 | var setToggle = function() {
169 | var wrapper = document.createElement('div');
170 | wrapper.classList.add('kanban-toggle');
171 | wrapper.appendChild(getToggleHtml());
172 | insertAfter(wrapper, el.titleBar);
173 | };
174 |
175 | var handleClick = function() {
176 | if (state.kanbanShown) {
177 | el.kanban.classList.add('kanban--hidden');
178 | state.kanbanShown = false;
179 | } else {
180 | el.kanban.classList.remove('kanban--hidden');
181 | state.kanbanShown = true;
182 | }
183 | };
184 |
185 | var bindEvents = function() {
186 | el.toggle.addEventListener('click', handleClick);
187 | };
188 |
189 | var init = function() {
190 | el.titleBar = document.querySelector('#content .title-bar');
191 | addStyles();
192 | getCards();
193 | sortColumns();
194 | setKanban();
195 | setToggle();
196 | bindEvents();
197 | };
198 |
199 | init();
200 |
201 | } )(jQuery);
202 |
--------------------------------------------------------------------------------
/central/central-link-pr-formatting.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Github PR name formatting.
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Format Github PR Links same as Github does.
6 | // @author Crisoforo Gaspar Hernandez
7 | // @match https://central.tri.be/issues/*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | var links = Array.prototype.slice.call( document.querySelectorAll('.linkified') );
15 |
16 | links.forEach(function(node) {
17 | node.innerText = node.innerText.replace('https://github.com/moderntribe/', '').replace('/pull/', '#');
18 | });
19 |
20 | })();
--------------------------------------------------------------------------------
/central/central-links.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Anchor tags in Central
3 | // @namespace https://central.tri.be/
4 | // @version 0.1.1
5 | // @description Adds anchor tags to some links in central
6 | // @author Gustavo Bordoni
7 | // @include /^https:\/\/central.tri.be(\/.*)?/
8 | // @require http://soapbox.github.io/linkifyjs/js/linkify/linkify.min.js
9 | // @require http://soapbox.github.io/linkifyjs/js/linkify/linkify-jquery.min.js
10 | // @grant none
11 | // ==/UserScript==
12 |
13 | var central_links = {};
14 |
15 | ( function( $, my ) {
16 | my.init = function() {
17 | my.build_styles();
18 |
19 | var regExpGoogleDocs = /docs\.google\.com.+\/d\/([^\/]+)/ig;
20 |
21 | my.$headings = $( 'table.attributes' ).find( 'th:contains(Pull Request:), th:contains(Forum Threads:), th:contains(User Story:), th:contains(UserVoice Threads:)' );
22 | my.$headings.each( function() {
23 | var $this = $( this );
24 | var $link_cell = $this.next( 'td' );
25 |
26 | $link_cell.linkify({
27 | target: "_blank",
28 | format: function( value, type ) {
29 | var matches = regExpGoogleDocs.exec( value );
30 |
31 | if ( ! matches ){
32 | return value;
33 | }
34 |
35 | return 'Google#' + matches[1];
36 | }
37 | });
38 |
39 | $link_cell.find( 'a' ).append( ' ' );
40 | } );
41 | };
42 |
43 | my.build_styles = function() {
44 | $( 'head' ).append( '' );
45 | my.$styles = $( document.getElementById( 'tribe-anchor-tags-styles' ) );
46 |
47 | my.$styles.html( [
48 | '.attachments .files a {',
49 | 'display: inline-block;',
50 | '}',
51 |
52 | '' ].join( "\n" ) );
53 | };
54 |
55 | $( function() {
56 | my.init();
57 | });
58 | } )( jQuery, central_links );
59 |
--------------------------------------------------------------------------------
/central/central-my-favorite-tickets.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Central > My favorite tickets
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Add your favorite ticket numbers to the main menu for easy access
6 | // @author Andras Guseo
7 | // @match https://central.tri.be/*
8 | // @grant none
9 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/central-my-favorite-tickets.user.js
10 | // ==/UserScript==
11 |
12 | (function() {
13 | 'use strict';
14 |
15 | // Here you can define what the main menu item text should be
16 | var menuName = 'Favorites';
17 |
18 | // List your tickets here. The note can be whatever you want to appear in the menu.
19 | var tickets = {
20 | 120914: { note: "Oversight" },
21 | 120205: { note: "Helpdesk" },
22 | 120264: { note: "Supporting Support" },
23 | 120265: { note: ".org Pass" },
24 | 120288: { note: "1:1 with team" },
25 | 120262: { note: "Weekly Support Scrum" },
26 | 120349: { note: "Recruiting & Onboarding" },
27 | 120643: { note: "Plugins Weekly Status" },
28 | 120668: { note: "Products Management Bi-Weeky Meeting" },
29 | };
30 |
31 | // Putting together the HTML
32 | var menu;
33 | menu = '' + menuName + ' ';
34 | menu += '';
40 |
41 | //console.log( menu );
42 |
43 | // Creating the new menu item
44 | var list = document.getElementById("account-nav");
45 | var newItem = document.createElement("LI");
46 | newItem.id = 'mytickets-menu';
47 | newItem.className = 'drop-down';
48 | newItem.innerHTML = menu;
49 |
50 | // Adding the new menu item in DOM
51 | list.insertBefore( newItem, list.childNodes[0]);
52 |
53 | // Adding listeners
54 | newItem.addEventListener( 'mouseover', showSubmenu );
55 | newItem.addEventListener( 'mouseout', hideSubmenu );
56 |
57 | var subMenu = document.getElementById( 'mytickets-menu-ul' );
58 |
59 | // Animations
60 | function showSubmenu() {
61 | subMenu.style.display = 'block';
62 | newItem.classList.add( 'open' );
63 | }
64 | function hideSubmenu() {
65 | subMenu.style.display = 'none';
66 | newItem.classList.remove( 'open' );
67 | }
68 |
69 | /**
70 | * === Changelog ===
71 | * 0.1 - 2019-03-04
72 | * Initial release
73 | */
74 | })();
75 |
--------------------------------------------------------------------------------
/central/central-new-issue-templates.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Starter Templates for New Central Issues
3 | // @namespace https://central.tri.be/
4 | // @version 0.1.1
5 | // @description The applicable template inserts into the empty Description field after selecting an Issue Tracker status (Bug, Feature, etc.)
6 | // @author Clifford Paulick
7 | // @include https://central.tri.be/projects/*/issues/new
8 | // @grant none
9 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/central-new-issue-templates.user.js
10 | // ==/UserScript==
11 |
12 | let central_new_issue_templates = {};
13 |
14 | (function ( $, app ) {
15 | 'use strict';
16 |
17 | /**
18 | * Input values as the object keys. Map to the appropriate template name as the object values.
19 | */
20 | const map_input_values_to_template_name = {
21 | '1' : 'bug',
22 | '14': 'feature',
23 | '2' : 'feature',
24 | '10': 'support',
25 | '9' : 'task',
26 | '11': 'discussion',
27 | };
28 |
29 | /**
30 | * The template name as the object keys. The template text as the object values.
31 | */
32 | const templates = {
33 | 'feature' :
34 | 'h2. +*Info*+' +
35 | '\n\n' +
36 | '*Explanation of the proposal:*' +
37 | '\n' +
38 | '*Screencast/Screenshots recording explaining item:*' +
39 | '\n' +
40 | '*Priority recommendation:*' +
41 | '\n' +
42 | '*Preferred Solution/Outcome:*' +
43 | '\n\n' +
44 | 'h2. +*User Stories*+' +
45 | '\n\n' +
46 | '# as a ... I expect' +
47 | '\n' +
48 | '# as a ... I expect' +
49 | '\n' +
50 | '# as a ... I expect' +
51 | '\n\n' +
52 | 'h2. +*Application Details*+' +
53 | '\n\n' +
54 | 'Classic/Block,' +
55 | '\n' +
56 | 'Specific deliverable,' +
57 | '\n' +
58 | 'Product focus/area,' +
59 | '\n' +
60 | 'etc' +
61 | '\n\n' +
62 | 'h2. +*Extras*+' +
63 | '\n\n' +
64 | '*Relevant documents/files:*' +
65 | '\n' +
66 | '_Add all applicable Support Help Desk threads and/or UserVoice threads to the ticket\'s "Forum Threads" field._',
67 | 'bug':
68 | 'h2. +*Info*+\n' +
69 | '\n' +
70 | '*Overview of the issue:*' +
71 | '\n' +
72 | '*Recommended Solution:*' +
73 | '\n' +
74 | '*Priority recommendation:*' +
75 | '\n\n' +
76 | 'h2. +*Steps To Reproduce:*+' +
77 | '\n\n' +
78 | '**Environmental requirements (plugins, WP version, etc)**' +
79 | '\n\n' +
80 | '_setup requirements (create events, tickets, blocks, ar-modal, etc)_' +
81 | '\n\n' +
82 | '# Do this' +
83 | '\n' +
84 | '# expectation' +
85 | '\n' +
86 | '# actual (bug)' +
87 | '\n' +
88 | '# then this' +
89 | '\n\n' +
90 | 'h2. +*Extras*+' +
91 | '\n\n' +
92 | '*Screenshot:* ' +
93 | '\n' +
94 | '*Video:* ' +
95 | '\n' +
96 | '*Sandbox URL of the issue:* ' +
97 | '\n' +
98 | '*Relevant documents/files* _(attach or link)_' +
99 | '\n' +
100 | '*Customer System Info* _(attach as text file)_' +
101 | '\n' +
102 | '_Add all applicable Support Help Desk threads and/or UserVoice threads to the ticket\'s "Forum Threads" field._',
103 | };
104 |
105 | /**
106 | * Upon Issue Tracker selection, set the Description to the template value.
107 | *
108 | * If the Description is not empty, bail.
109 | */
110 | $( 'select#issue_tracker_id' ).change( function () {
111 | // Issue Tracker value
112 | let value = $( this ).val();
113 |
114 | // Description textarea
115 | let desc = $( 'textarea#issue_description' );
116 | let desc_text = desc.val();
117 | let desc_text_is_a_template = false;
118 |
119 | // Get the selection's template text
120 | let template_type = map_input_values_to_template_name[ value ]; // e.g. 'bug'
121 | let template_text = templates[ template_type ]; // full template text
122 |
123 | // If Description is not blank, detect if it is one of the templates, such as if selecting 'bug' then changing mind to 'feature'
124 | $.each( templates, function ( key, value ) {
125 | if ( value === desc_text ) {
126 | desc_text_is_a_template = true;
127 | return false; // break;
128 | }
129 | } );
130 |
131 | // If selection type does not have an associated template...
132 | if ( !template_text ) {
133 | if ( desc_text_is_a_template ) {
134 | // remove the previously-inserted template, such as going from 'bug' (has template) to 'support' (does not have a template)
135 | desc.val( '' );
136 | } else {
137 | // bail if no template to insert or remove
138 | return;
139 | }
140 | }
141 |
142 | // If Description is blank or is one of the templates, insert the template
143 | if (
144 | '' === desc_text
145 | || desc_text_is_a_template
146 | ) {
147 | desc.val( template_text );
148 | }
149 | } );
150 | })( jQuery, central_new_issue_templates );
151 |
--------------------------------------------------------------------------------
/central/central-redirect-to-jira.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Automatically redirect Central Issues to Jira Issues.
3 | // @namespace https://central.tri.be/
4 | // @version 0.0.1
5 | // @description You don't live in the past, and you want to be automated into the future.
6 | // @author Clifford Paulick
7 | // @include https://central.tri.be/issues/*
8 | // @grant none
9 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/central/central-redirect-to-jira.user.js
10 | //
11 | // 2min demo video: https://share.getcloudapp.com/04ugxG4Q
12 | //
13 | // JQL search syntax references:
14 | // https://community.atlassian.com/t5/Jira-questions/How-to-create-a-jira-URL-to-do-a-JQL-search/qaq-p/305793
15 | // https://confluence.atlassian.com/jirasoftwarecloud/advanced-searching-fields-reference-764478339.html
16 | // https://confluence.atlassian.com/jiracoreserver073/advanced-searching-fields-reference-861257219.html#Advancedsearching-fieldsreference-customCustomfield
17 | //
18 | // ==/UserScript==
19 |
20 | let central_redirect_to_jira = {};
21 |
22 | (function ( app ) {
23 | 'use strict';
24 |
25 | function redirect() {
26 | window.location = encodeURI( 'https://moderntribe.atlassian.net/issues/?jql=cf[10037]="' + window.location.href + '"' );
27 | }
28 |
29 | document.write( 'Redirecting you to find this issue in Jira...' );
30 | setTimeout( redirect, 500 ); // 0.5 seconds
31 | })( central_redirect_to_jira );
32 |
--------------------------------------------------------------------------------
/central/clocking-nag.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Clocking Nag
3 | // @namespace https://central.tri.be/
4 | // @version 0.1.3
5 | // @description Nag about clocking right in Central
6 | // @author Matthew Batchelder
7 | // @include /https?:\/\/central(dev)?.tri.be/
8 | // @exclude /https?:\/\/central(dev)?.tri.be/login
9 | // @grant none
10 | // @downloadURL https://github.com/moderntribe/tampermonkey-scripts/raw/master/clocking-nag.user.js
11 | // ==/UserScript==
12 |
13 | /**
14 | * SET YOUR GOAL HOURS FOR THE DAY
15 | */
16 | var central_clocking_nag_goal_hours = 7;
17 |
18 | /**
19 | * SET YOUR WORK DAYS
20 | *
21 | * 0 = Sunday
22 | * 1 = Monday
23 | * 2 = Tuesday
24 | * 3 = Wednesday
25 | * 4 = Thursday
26 | * 5 = Friday
27 | * 6 = Saturday
28 | */
29 | var central_clocking_nag_work_days = [ 1, 2, 3, 4, 5 ];
30 |
31 | ( function( $, goal_hours, work_days ) {
32 | 'use strict';
33 |
34 | Number.prototype.padLeft = function( base, chr ) {
35 | var len = ( String( base || 10 ).length - String( this ).length ) + 1;
36 | return len > 0 ? new Array(len).join( chr || '0' ) + this : this;
37 | };
38 |
39 | var obj = {
40 | userId: null,
41 | goalHours: goal_hours,
42 | workDays: work_days,
43 | numDays: 7,
44 | failLevel: 0,
45 | batch: [],
46 | batchSize: 4,
47 | datesToFetch: [],
48 | clocking: {
49 | day: [],
50 | total: 0,
51 | currentWeekTotal: null
52 | }
53 | };
54 |
55 | /**
56 | * Initializes the clocking nav
57 | */
58 | obj.init = function() {
59 | // grab ID from page
60 | if ( null === obj.userId ) {
61 | var userId = $( document.getElementById( 'account-nav' ) ).find( '.last-child a[href^="/users/"]' ).attr( 'href' ).replace( '/users/', '' );
62 | obj.userId = parseInt( userId, 10 );
63 | }
64 |
65 | obj.buildStyles();
66 | obj.generateDates();
67 | obj.generateBatches();
68 | obj.fetchBatches();
69 | };
70 |
71 | /**
72 | * Generate dates that we'll be fetching
73 | */
74 | obj.generateDates = function() {
75 | for ( var dateCounter = obj.numDays - 1; dateCounter >= 0; dateCounter-- ) {
76 | var tempDate = new Date();
77 | tempDate.setDate( tempDate.getDate() - dateCounter );
78 | tempDate = obj.formatDate( tempDate );
79 | obj.clocking.day[ tempDate ] = 0;
80 | obj.datesToFetch.push( tempDate );
81 | }
82 | };
83 |
84 | /**
85 | * Splits date ranges into chunks that Central can handle and triggers the fetch process
86 | */
87 | obj.generateBatches = function() {
88 | var fromTo = { from: null, to: null };
89 | for ( var i = 0; obj.datesToFetch.length; i++ ) {
90 | var date = obj.datesToFetch.shift();
91 |
92 | if ( null === fromTo.from ) {
93 | fromTo.from = date;
94 | }
95 |
96 | if ( ! obj.datesToFetch.length ) {
97 | fromTo.to = date;
98 | }
99 |
100 | if ( ( obj.batchSize - 1 ) === i % obj.batchSize || ! obj.datesToFetch.length ) {
101 | fromTo.to = date;
102 |
103 | obj.batch.push( $.extend( {}, fromTo ) );
104 |
105 | fromTo = { from: null, to: null };
106 | }
107 | }
108 | };
109 |
110 | /**
111 | * Fetches the batches
112 | */
113 | obj.fetchBatches = function() {
114 | for ( var i in obj.batch ) {
115 | if ( ! obj.batch.hasOwnProperty( i ) ) {
116 | continue;
117 | }
118 |
119 | obj.fetchData( obj.batch[ i ] );
120 | }
121 | };
122 |
123 | /**
124 | * Fetches a batch date range and adjusts data accordingly
125 | */
126 | obj.fetchData = function( fromTo ) {
127 | var url = 'https://central.tri.be/time_entries.json?sort=spent_on&from=' + fromTo.from + '&to=' + fromTo.to + '&user_id=' + obj.userId;
128 | var args = {
129 | dataType: 'json',
130 | type: 'GET',
131 | url: url
132 | };
133 |
134 | var jqxhr = $.ajax( args );
135 |
136 | jqxhr.done( function( data ) {
137 | for ( var i in data.time_entries ) {
138 | if ( ! data.time_entries.hasOwnProperty( i ) ) {
139 | continue;
140 | }
141 |
142 | var item = data.time_entries[ i ];
143 | var hours = parseFloat( item.hours );
144 | var date = new Date( item.spent_on + ' 00:01' );
145 |
146 | // if we aren't tracking this day, skip it
147 | if ( 'undefined' === typeof obj.clocking.day[ item.spent_on ] ) {
148 | continue;
149 | }
150 |
151 | obj.clocking.day[ item.spent_on ] += hours;
152 | obj.clocking.total += hours;
153 | }
154 |
155 | obj.batch.shift();
156 |
157 | if ( ! obj.batch.length ) {
158 | obj.calculateWeekTotal();
159 | obj.clockingTracker( obj.clocking );
160 | }
161 | } );
162 | };
163 |
164 | /**
165 | * Calculates the current week's total
166 | */
167 | obj.calculateWeekTotal = function() {
168 | var currentDay = null;
169 | var encounteredMonday = false;
170 |
171 | for ( var i in obj.clocking.day ) {
172 | if ( ! obj.clocking.day.hasOwnProperty( i ) ) {
173 | continue;
174 | }
175 |
176 | var date = new Date( i + ' 00:01' );
177 | currentDay = date.getDay();
178 |
179 | if ( 1 === currentDay ) {
180 | encounteredMonday = true;
181 | obj.clocking.currentWeekTotal = obj.clocking.day[ i ];
182 | } else if ( ! encounteredMonday ) {
183 | continue;
184 | } else {
185 | obj.clocking.currentWeekTotal += obj.clocking.day[ i ];
186 | }
187 | }
188 | };
189 |
190 | /**
191 | * Generates the clocking tracker UI
192 | */
193 | obj.clockingTracker = function( clocking ) {
194 | var $header = $( document.getElementById( 'top-menu' ) );
195 | if ( ! $header.length ) {
196 | $header = $( '.navbar' );
197 | }
198 | $header.after( '' );
199 |
200 | var $tracker = $( document.getElementById( 'clocking-tracker' ) );
201 | var $days = $tracker.find( '.tracker-days' );
202 | var dayCounter = 0;
203 |
204 | for ( var i in clocking.day ) {
205 | if ( ! clocking.day.hasOwnProperty( i ) ) {
206 | continue;
207 | }
208 | dayCounter++;
209 |
210 | var classes = '';
211 | var day = i.split( '-' );
212 | day = day[1] + '/' + day[2];
213 |
214 | var date = new Date( i + ' 00:01' );
215 | var isWorkDay = true;
216 | var hoursClocked = parseFloat( clocking.day[ i ] ).toFixed( 2 );
217 | var title = '';
218 | var dateInURL = date.getFullYear() + '-' + ( date.getMonth() + 1 ) + '-' + date.getDate();
219 | var dayDetailsLink = 'https://central.tri.be/time_entries?from=' + dateInURL + '&to=' + dateInURL + '&user_id=' + obj.userId;
220 |
221 | if ( -1 === $.inArray( date.getDay(), obj.workDays ) ) {
222 | // not a work day!
223 | isWorkDay = false;
224 | classes += ' non-work-day';
225 | title = "Non-work day!";
226 | } else if ( isWorkDay && dayCounter === obj.numDays ) {
227 | classes += ' current-day';
228 | } else if ( isWorkDay && obj.goalHours > 0 && obj.goalHours - ( obj.goalHours * 0.4 ) > hoursClocked ) {
229 | // underclocked by 40%!
230 | classes += ' severely-under-clocked';
231 | title = "You under clocked this day by a LOT.";
232 | obj.failLevel += 5;
233 | } else if ( isWorkDay && obj.goalHours > 0 && obj.goalHours - ( obj.goalHours * 0.2 ) > hoursClocked ) {
234 | // underclocked by 20%
235 | classes += ' under-clocked';
236 | title = "You under clocked this day.";
237 | obj.failLevel += 1;
238 | }
239 |
240 | if ( dayCounter < ( obj.numDays - 3 ) ) {
241 | classes += ' unclockable';
242 | title += ' This day is too old to clock to.';
243 | }
244 |
245 | var $day = '' + hoursClocked + ' ' + day + ' ';
246 |
247 | $days.append( $day );
248 | }
249 |
250 | var message = '';
251 | var messageSeverity = '';
252 |
253 | // observe when clocking has been caught up due to extra hours per day
254 | if ( obj.failLevel > 0 && obj.clocking.total >= ( obj.workDays.length * obj.goalHours ) ) {
255 | obj.failLevel = 0;
256 | message = 'Looks like you have caught up by working more hours per day! Nicely done, you beast!';
257 | messageSeverity = 'severity-good';
258 | }
259 |
260 | if ( obj.failLevel > obj.numDays * 1.5 ) {
261 | message = 'Whoa. You are really far behind and need to clock some hours. Take a break and clock some now .';
262 | messageSeverity = 'severity-high';
263 | } else if ( obj.failLevel > obj.numDays ) {
264 | message = 'You need to clock some hours. Take a break and clock some now.';
265 | messageSeverity = 'severity-medium';
266 | } else if ( obj.failLevel > obj.numDays / 2 ) {
267 | message = 'It looks like you are running a bit behind on clocking. Play catch-up now?';
268 | messageSeverity = 'severity-low';
269 | }
270 |
271 | if ( '' !== message ) {
272 | $tracker.append( '' + message + '
' );
273 | }
274 |
275 | $tracker.append( '' + clocking.currentWeekTotal.toFixed( 2 ) + ' hour(s) clocked since Monday
' );
276 | $tracker.fadeIn( 'fast' );
277 | };
278 |
279 | /**
280 | * Formats date into YYYY-MM-DD
281 | */
282 | obj.formatDate = function( d ) {
283 | return [
284 | d.getFullYear(),
285 | ( d.getMonth() + 1 ).padLeft(),
286 | d.getDate().padLeft()
287 | ].join('-');
288 | };
289 |
290 | /**
291 | * Adds CSS for clocking tracker
292 | */
293 | obj.buildStyles = function() {
294 | $( 'head' ).append( '' );
295 | obj.$styles = $( document.getElementById( 'tribe-clocking-tracker-styles' ) );
296 | obj.$styles.html( `
297 | #clocking-tracker {
298 | background: #fcfcfc;
299 | border-bottom: #eee;
300 | display: none;
301 | padding: 0.5rem 1rem;
302 | width: auto;
303 | }
304 |
305 | #clocking-tracker .tracker-message {
306 | margin-top: 0.5rem;
307 | text-align: center;
308 | }
309 |
310 |
311 | #clocking-tracker .tracker-message.severity-medium {
312 | font-size: 1.2rem;
313 | }
314 |
315 | #clocking-tracker .tracker-message.severity-high {
316 | font-size: 1.4rem;
317 | }
318 |
319 | #clocking-tracker .tracker-message.severity-high b,
320 | #clocking-tracker .tracker-message.severity-high i {
321 | color: #c30c0c;
322 | }
323 |
324 | #clocking-tracker .tracker-message.severity-good {
325 | color: #6baf74;
326 | }
327 |
328 | #clocking-tracker .tracker-current-week {
329 | color: #ccc;
330 | font-size: 0.7rem;
331 | text-align: center;
332 | }
333 |
334 | #clocking-tracker .tracker-days {
335 | display: flex;
336 | flex-direction: row;
337 | width: 100%;
338 | }
339 |
340 | #clocking-tracker .tracker-day {
341 | background-color: #eee;
342 | border-radius: 3px;
343 | color: #24292e;
344 | margin-right: 1rem;
345 | max-width: 100%;
346 | padding: 1rem 1rem 0.5rem;
347 | text-align: center;
348 | width: 14%;
349 | }
350 |
351 | #clocking-tracker .non-work-day {
352 | opacity: 0.3;
353 | }
354 |
355 | #clocking-tracker .unclockable {
356 | background-image: url( https://i.imgur.com/9Fv736o.png );
357 | }
358 |
359 | #clocking-tracker .tracker-day:last-child {
360 | margin-right: 0;
361 | }
362 |
363 | #clocking-tracker .tracker-hours {
364 | display: block;
365 | font-size: 1.1rem;
366 | margin-bottom: .5rem;
367 | }
368 |
369 | #clocking-tracker .tracker-date {
370 | display: block;
371 | color: #999;
372 | font-size: 0.7rem;
373 | }
374 |
375 | #clocking-tracker .under-clocked {
376 | background-color: #f4af49;
377 | }
378 |
379 | #clocking-tracker .under-clocked .tracker-hours {
380 | color: #fff;
381 | }
382 |
383 | #clocking-tracker .under-clocked .tracker-date {
384 | color: #f6e6cf;
385 | }
386 |
387 | #clocking-tracker .severely-under-clocked {
388 | background-color: #c30c0c;
389 | }
390 |
391 | #clocking-tracker .severely-under-clocked .tracker-hours {
392 | color: #fff;
393 | }
394 |
395 | #clocking-tracker .severely-under-clocked .tracker-date {
396 | color: #cb9e9e;
397 | }
398 |
399 | @media screen and (max-width: 950px) {
400 | #clocking-tracker {
401 | padding: .25rem .5rem;
402 | position: relative;
403 | top: 90px;
404 | }
405 |
406 | #clocking-tracker .tracker-day {
407 | margin-right: 0.25rem;
408 | padding: 0.5rem 0.5rem 0.25rem;
409 | }
410 |
411 | #clocking-tracker .tracker-days {
412 | margin-right: 0.5rem;
413 | padding: 0.5rem 0.5rem 0.25rem;
414 | }
415 |
416 | #clocking-tracker .tracker-hours {
417 | font-size: 0.9rem;
418 | margin-bottom: 0.25rem;
419 | }
420 |
421 | #clocking-tracker .tracker-date {
422 | font-size: 0.6rem;
423 | }
424 |
425 | #clocking-tracker .tracker-day:nth-child(1),
426 | #clocking-tracker .tracker-day:nth-child(2) {
427 | display: none;
428 | }
429 | }
430 | ` );
431 | };
432 |
433 | $( function() {
434 | obj.init();
435 | } );
436 | } )( jQuery, central_clocking_nag_goal_hours, central_clocking_nag_work_days );
437 |
--------------------------------------------------------------------------------
/central/cmd-save.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name CMD + SAVE a ticket
3 | // @namespace https://central.tri.be/
4 | // @version 0.1.0
5 | // @description When editing a ticket, hit cmd + save to save the ticket
6 | // @author Aaron Hanson
7 | // @include /https?:\/\/central(dev)?.tri.be/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function( $ ) {
12 |
13 | $(window).bind('keydown', function(event) {
14 | if (event.ctrlKey || event.metaKey) {
15 | switch (String.fromCharCode(event.which).toLowerCase()) {
16 | case 's':
17 | event.preventDefault();
18 | $('#issue-form').submit();
19 | break;
20 | case 'i':
21 |
22 | }
23 | }
24 | });
25 | })( jQuery );
26 |
--------------------------------------------------------------------------------
/central/issue-list-tools.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Issue list tools
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Adds tools to interact with the issue list
6 | // @author Matthew Batchelder
7 | // @include /https?:\/\/central(dev)?.tri.be.*\/issues/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function( $ ) {
12 | 'use strict';
13 |
14 | var obj = {};
15 |
16 | /**
17 | * Initializes the clocking nav
18 | */
19 | obj.init = function() {
20 | var $issueList = $( document.getElementById( 'issue-list' ) );
21 | $issueList.before( '' );
22 | $( document ).on( 'click', '.collapse-row-groups', function( e ) {
23 | e.preventDefault();
24 | $( '.expander' ).click();
25 | } );
26 |
27 | obj.buildStyles();
28 | };
29 |
30 | /**
31 | * Adds CSS for clocking tracker
32 | */
33 | obj.buildStyles = function() {
34 | $( 'head' ).append( '' );
35 | obj.$styles = $( document.getElementById( 'tribe-issue-list-tools-styles' ) );
36 | obj.$styles.html( `
37 | .tribe-row-tools {
38 | padding: .5rem 0;
39 | }
40 | ` );
41 | };
42 |
43 | $( function() {
44 | obj.init();
45 | } );
46 | })( jQuery );
47 |
--------------------------------------------------------------------------------
/central/issue-summary.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Central issue summary
3 | // @namespace https://central.tri.be/
4 | // @version 0.2.6
5 | // @description Generate a ticket summary from visible tickets
6 | // @author Matthew Batchelder, Nick Pelton & Gustavo Bordoni
7 | // @include /https?:\/\/central(dev)?.tri.be\/(projects\/)*[^\/]*\/?issues\/?/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function( $ ) {
12 | var my = {};
13 |
14 | my.statuses_colors = {
15 | 'Design QA': {
16 | color: '#fad8c7'
17 | },
18 | 'Proposed': {
19 | color: '#fffff6'
20 | },
21 | 'New': {
22 | color: '#ffffe2'
23 | },
24 | 'In Progress': {
25 | color: '#bfd4f2'
26 | },
27 | 'Pending Code Review': {
28 | color: '#bfe5bf'
29 | },
30 | 'Pending Merge': {
31 | color: '#009800',
32 | text: '#fff'
33 | },
34 | 'Pending Manager': {
35 | color: '#76dbdf'
36 | },
37 | 'Pending Customer': {
38 | color: '#e2c688'
39 | },
40 | 'On Hold': {
41 | color: '#dbc7e3'
42 | },
43 | 'Pending Final Signoff': {
44 | color: '#ccbfa2'
45 | },
46 | 'Pending QA': {
47 | color: '#fad8c7'
48 | },
49 | 'Pending Smoketest': {
50 | color: '#e9b398'
51 | },
52 | 'Declined': {
53 | color: '#969696',
54 | text: '#fff'
55 | },
56 | 'Pending Release': {
57 | color: '#7e987b'
58 | },
59 | 'Complete': {
60 | color: '#91be9c'
61 | },
62 | 'Pending Estimate': {
63 | color: '#ffc2a0'
64 | },
65 | 'Pending Scope': {
66 | color: '#dafcf4'
67 | },
68 | 'Skills Interview': {
69 | color: '#74e68b'
70 | },
71 | 'Skills Screening': {
72 | color: '#dafcf4'
73 | },
74 | 'Pending Notice of Decline': {
75 | color: '#f88888'
76 | },
77 | 'Recruiter Interview': {
78 | color: '#d1e3ff'
79 | },
80 | 'The Bench': {
81 | color: '#e0d1af'
82 | },
83 | 'Needs More Info': {
84 | color: '#ff94c7'
85 | },
86 | 'Future Review': {
87 | color: '#8e85ff'
88 | },
89 | 'Trial in Progress': {
90 | color: '#91badf'
91 | },
92 | 'Onboarding': {
93 | color: '#6ec772'
94 | },
95 | 'Pending Gig': {
96 | color: '#65f5c7'
97 | }
98 | };
99 |
100 | // Bubble template
101 | my.TLP = `
102 |
103 |
{count}
104 |
{label}
105 |
106 | `;
107 |
108 | my.shift = false;
109 | my.ctrl = false;
110 |
111 | // Render bubble and insert into DOM
112 | my.addBubble = function( count, label, class_name, bg_color, txt_color ){
113 |
114 | var options = {
115 | count: count || 0,
116 | label: label || "empty",
117 | class_name: class_name || "",
118 | bg_color: bg_color || "",
119 | txt_color: txt_color || ""
120 | };
121 |
122 | var template = my.TLP,
123 | $template;
124 |
125 | template = template.replace( /{count}/g, options.count );
126 | template = template.replace( /{label}/g, options.label );
127 | template = template.replace( /{class_name}/g, options.class_name + ( options.class_name ? ' tribe-clickable' : '' ) );
128 | template = template.replace( /{bg_color}/g, options.bg_color );
129 | template = template.replace( /{txt_color}/g, options.txt_color );
130 |
131 | $template = $( template );
132 | $template.attr( 'data-bubble', JSON.stringify( options ) );
133 |
134 | my.$summary_container.append( $template );
135 | };
136 |
137 |
138 | my.statuses = {};
139 |
140 | my.build_styles = function() {
141 | $( 'head' ).append( '' );
142 | my.$styles = $( document.getElementById( 'tribe-ticket-status-styles' ) );
143 |
144 | my.$styles.html( `
145 | .tribe-status {
146 | border:1px solid #eee;
147 | border-radius:5px;
148 | box-shadow:0 0 3px 0px rgba( 0, 0, 0, 0.1 );
149 | display:inline-block;
150 | margin:.25rem;
151 | text-align:center;
152 | }
153 |
154 | .tribe-status.tribe-clickable {
155 | cursor: pointer;
156 | }
157 |
158 | .tribe-status.tribe-active {
159 | border: 1px #3e3e3e solid;
160 | }
161 | ` );
162 | };
163 |
164 | my.init = function() {
165 | my.build_styles();
166 |
167 | my.$table = $( 'table.list.issues' );
168 | my.$issues = my.$table.find( 'tr.issue' );
169 | my.$non_parent_issues = my.$issues.filter( ':not(.parent)' );
170 |
171 | // Create a list of Status Names by Code
172 | my.$table.find( 'td.status' ).each( function(){
173 | var
174 | $this = $( this ),
175 | $row = $this.parents( 'tr' ).eq( 0 ),
176 | code = $row.attr( 'class' ).match( /status\-[0-9]+/g )[0],
177 | name = $this.text();
178 |
179 | my.statuses[ code ] = name;
180 | } );
181 |
182 | $( document ).on( 'keyup keydown', function ( e ) {
183 | my.shift = 16 === e.keyCode && ! my.shift;
184 | my.ctrl = 91 === e.keyCode && ! my.ctrl;
185 | } );
186 |
187 | $( document ).on( 'click', '.tribe-status.tribe-clickable', function ( event ) {
188 | var $this = $( this ),
189 | data = $this.data( 'bubble' ),
190 | $rows = my.$issues,
191 | filter = '';
192 |
193 | my.$issues.hide();
194 |
195 | if ( my.ctrl ) {
196 | $this.toggleClass( 'tribe-active' );
197 |
198 | $( '.tribe-status.tribe-active' ).each( function ( k, status ) {
199 | if ( filter ) {
200 | filter = filter + ', ';
201 | }
202 | filter = filter + '.' + $( status ).data( 'bubble' ).class_name;
203 | } );
204 | } else {
205 | $( '.tribe-status' ).not( $this ).removeClass( 'tribe-active' );
206 | $this.toggleClass( 'tribe-active' );
207 |
208 | if ( $this.hasClass( 'tribe-active' ) ) {
209 | filter = '.' + data.class_name;
210 | }
211 | }
212 |
213 | if ( filter ) {
214 | $rows.filter( filter ).show();
215 | } else {
216 | $rows.show();
217 | }
218 | } );
219 |
220 | // Clear any existing summary
221 | $('.tribe-status-summary').remove();
222 |
223 | my.$summary_container = $( '
' );
224 | my.$titleBar = $( '#content .title-bar' );
225 | var issue_count = my.$table.find( 'tr.issue' ).length;
226 |
227 | if ( ! issue_count ) {
228 | return;
229 | }
230 |
231 | // Ticket Count
232 | var $summary = $( 'Issue count: ' + issue_count + '
' );
233 | my.$summary_container.append( $summary );
234 |
235 | // Ticket Summary
236 | $.each( my.statuses, function( code, name ) {
237 | var $rows = my.$table.find( 'tr.' + code );
238 |
239 | if ( ! $rows.length || 'undefined' === typeof my.statuses_colors[ name ] ) {
240 | return;
241 | }
242 |
243 | var text_color = '';
244 |
245 | if ( 'undefined' !== typeof my.statuses_colors[ name ].text ) {
246 | text_color = 'color:' + my.statuses_colors[ name ].text + ';';
247 | }
248 |
249 | my.addBubble( $rows.length, name, code, my.statuses_colors[ name ].color, text_color );
250 | } );
251 |
252 | // Estimate summary
253 | var that = this;
254 | that.$rows = this.$non_parent_issues.find( '.estimated_hours' );
255 |
256 | if ( that.$rows.length ) {
257 |
258 | that.estimates = 0;
259 | $.each( this.$rows, function(index, $object){
260 | val = ( parseFloat( $($object).html() ) || 0 );
261 | that.estimates += val;
262 | });
263 |
264 | that.hours = 0;
265 | that.$rows = this.$non_parent_issues.find( '.spent_hours' );
266 | $.each(this.$rows, function(index, $object){
267 | val = ( parseFloat( $($object).html() ) || 0 );
268 | that.hours += val;
269 | });
270 |
271 | that.diff = that.estimates - that.hours;
272 | that.diff_text = "Under";
273 | if(that.diff < 0){
274 | that.diff_text = "Over";
275 | }
276 |
277 | my.addBubble( Math.round( that.estimates * 100 ) / 100, "Hrs Est." );
278 | my.addBubble( Math.round( that.hours * 100 ) / 100, "Hrs Spent" );
279 | my.addBubble( Math.round( that.diff * 100 ) / 100, "Hrs " + that.diff_text );
280 |
281 | }
282 |
283 | // Add to DOM
284 | my.$summary_container.find('.tribe-status:first' ).css( { 'margin-left': '0' } );
285 | my.$titleBar.append( my.$summary_container );
286 |
287 | my.color_code();
288 | };
289 |
290 | my.color_code = function() {
291 | my.$issues.each( function() {
292 | var $issue = $( this );
293 | var $status = $issue.find( '.status' );
294 | var $tracker = $issue.find( '.tracker' );
295 | var name = $status.text();
296 | var color = 'transparent';
297 | var text = '#000';
298 |
299 | if ( 'undefined' === typeof my.statuses_colors[ name ] ) {
300 | // Marks undefined Status so we can add colors later on
301 | console.log( 'Undefined Status in this Query:', name );
302 | return;
303 | }
304 |
305 | if ( 'undefined' !== typeof my.statuses_colors[ name ].text ) {
306 | text = my.statuses_colors[ name ].text;
307 | }
308 |
309 | if ( 'undefined' !== typeof my.statuses_colors[ name ].color ) {
310 | color = my.statuses_colors[ name ].color;
311 | }
312 |
313 | $status.css( {
314 | 'background-color': color,
315 | 'text-align': 'center',
316 | 'color': text
317 | } );
318 |
319 | var icon = null;
320 |
321 | switch ( $.trim( $tracker.text() ) ) {
322 | case 'Bug':
323 | icon = 'bug';
324 | break;
325 | case 'Enhancement':
326 | icon = 'wrench';
327 | break;
328 | case 'Feature':
329 | icon = 'star-o';
330 | break;
331 | case 'Support':
332 | icon = 'life-ring';
333 | break;
334 | case 'Resource':
335 | icon = 'user';
336 | break;
337 | case 'Task':
338 | icon = 'sticky-note-o';
339 | break;
340 | }
341 |
342 | if ( ! $tracker.find( 'i.fa' ).length ) {
343 | $tracker
344 | .css( {
345 | 'text-align': 'center'
346 | } )
347 | .html( ' ' );
348 | }
349 | } );
350 | };
351 |
352 | $( function() {
353 |
354 | // If page is update by Ajax, re-run our script
355 | var target = document.querySelector('#content');
356 |
357 | // create an observer instance
358 | var observer = new MutationObserver(function(mutations) {
359 | mutations.forEach(function(mutation) {
360 | my.init();
361 | });
362 | });
363 |
364 | // configuration of the observer:
365 | var config = { attributes: true, childList: true, characterData: true };
366 |
367 | // pass in the target node, as well as the observer options
368 | observer.observe( target, config );
369 |
370 | my.init();
371 | } );
372 | } )( jQuery );
373 |
--------------------------------------------------------------------------------
/central/key-commands.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Key Commands for Central
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Add some basic key commands to central.
6 | // @author Gary Kovar
7 | // @match *://central.tri.be/issues*
8 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/key-commands.js
9 | // @updateURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/key-commands.js
10 | // @grant none
11 | // ==/UserScript==
12 |
13 | var selected;
14 | var bg = '#2DD39C';
15 |
16 | (function(window, $, app, selected, bg ) {
17 | var query = 'https://central.tri.be/issues?query_id=3270';
18 |
19 | $( "tr.issue" ).click( function () {
20 | selected = $( this );
21 | selected.css( "background-color", bg );
22 | } );
23 |
24 | $(document).keydown(function(e) {
25 |
26 | var _target = $(e.target);
27 | var _focused = $(document.activeElement);
28 | var _inputting = _focused.get(0).tagName.toLowerCase()==="textarea" || _focused.get(0).tagName.toLowerCase()==="input";
29 |
30 |
31 | // / (forward slash) key = search
32 | if (!_inputting && e.keyCode===191) {
33 | e.preventDefault();
34 | document.getElementById( 'q' ).focus();
35 | document.getElementById( 'q' ).select();
36 | return;
37 | }
38 |
39 | // i key = query url specified (inbox)
40 | if (!_inputting && e.keyCode===73) {
41 | e.preventDefault();
42 | window.location = query;
43 | return;
44 | }
45 |
46 | // use j or k to scroll up and down, press enter to go to the ticket.
47 | if ( window.location.href == query && !_inputting && ( e.keyCode===74 || e.keyCode===75 ) ) {
48 | if ( typeof selected == "undefined"){
49 | selected = $( "tr.issue" ).first();
50 | selected.css( "background-color", bg );
51 | return;
52 | }
53 |
54 | selected.css( "background-color", "" );
55 | if ( e.keyCode ===74 ) {
56 | $next = selected.next();
57 | if( ! $next.length ) {
58 | selected = $( "tr.issue" ).first();
59 | } else {
60 | selected = selected.next();
61 | }
62 | } else {
63 | $prev = selected.prev();
64 | if( ! $prev.length ) {
65 | selected = $( "tr.issue" ).last();
66 | } else {
67 | selected = selected.prev();
68 | }
69 | }
70 | selected.css( "background-color", bg );
71 | position = selected.position();
72 | window.scrollTo(position.left, position.top);
73 | }
74 |
75 | if ( window.location.href == query && !_inputting && e.keyCode===13 ) {
76 | $id = $(selected).attr("id");
77 | if ( typeof $id == "undefined"){
78 | return;
79 | }
80 | $issue_id = $id.substring(6);
81 | window.location = 'https://central.tri.be/issues/' + $issue_id;
82 | }
83 |
84 | });
85 |
86 | })( window, jQuery, window.tribeKeyCommands, selected, bg );
87 |
--------------------------------------------------------------------------------
/central/over-estimate.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Tickets over estimate
3 | // @namespace https://central.tri.be/
4 | // @version 0.1.1
5 | // @description Adds highlighting when a ticket has more hours clocked than the estimate
6 | // @author Zach Tirrell
7 | // @include /^https:\/\/central.tri.be\/(\/.*)*/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | var central_over_estimate = {};
12 | (function( $, my ) {
13 | 'use strict';
14 |
15 | my.init = function() {
16 | my.highlight_single();
17 | my.highlight_table();
18 | };
19 |
20 | my.highlight_single = function() {
21 | var $estimated = $( '.issue.details td.estimated-hours' );
22 | var $spent = $( '.issue.details td.spent-time' );
23 |
24 | var estimated = parseFloat( $estimated.html() );
25 | if ( isNaN( estimated ) ) {
26 | return;
27 | }
28 |
29 | var spent = parseFloat( $spent.find( 'a' ).html() );
30 | if ( isNaN( spent ) || spent <= estimated ) {
31 | return;
32 | }
33 |
34 | $spent.css( 'background-color', 'pink' );
35 | $spent.find( 'a' ).css( 'font-weight', 'bold' );
36 | };
37 |
38 | my.highlight_table = function() {
39 | $( '.list.issues .issue' ).each( function() {
40 | var $estimated = $( this ).find( 'td.estimated_hours' );
41 | var $spent = $( this ).find( 'td.spent_hours' );
42 |
43 | var estimated = parseFloat( $estimated.html() );
44 | if ( isNaN( estimated ) ) {
45 | return;
46 | }
47 |
48 | var spent = parseFloat( $spent.html() );
49 | if ( isNaN( spent ) || spent <= estimated ) {
50 | return;
51 | }
52 | $spent.css( 'background-color', 'pink' );
53 | } );
54 | };
55 |
56 | $( function() {
57 | my.init();
58 | } );
59 | })( jQuery, central_over_estimate );
--------------------------------------------------------------------------------
/central/party-llamacorn.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Party Llamacorn
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Party time!
6 | // @author Paul Kim, Aaron Hanson
7 | // @include /https?:\/\/central.tri.be\/issues\/[0-9]+\/?/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function($) {
12 |
13 | var showLlamacorn = function() {
14 | var status = document.querySelector('#content .meta td.status').textContent;
15 | return status == 'Pending Merge' || status == 'Complete';
16 | };
17 |
18 | var setStyles = function() {
19 | var css = '.llamacorn-wrapper{position:absolute;top:50%;transform:translateY(-50%);width:100%;pointer-events:none;z-index:9999;overflow:hidden}.llamacorn{position:relative;left:0;transform:translateX(-100%);transition:1.3s}.llamacorn-fly{left:100%;transform:translateX(0)}';
20 | var head = document.head;
21 | var style = document.createElement('style');
22 | style.type = 'text/css';
23 | style.appendChild(document.createTextNode(css));
24 |
25 | head.appendChild(style);
26 | };
27 |
28 | var createImage = function() {
29 | var image = document.createElement('img');
30 | image.src = 'https://central.tri.be/attachments/105168/llamacorn.png';
31 | image.classList.add('llamacorn');
32 | return image;
33 | };
34 |
35 | var createImageWrapper = function() {
36 | var wrapper = document.createElement('div');
37 | wrapper.classList.add('llamacorn-wrapper');
38 | wrapper.appendChild(createImage());
39 | document.body.appendChild(wrapper);
40 | };
41 |
42 | var imageLoaded = function() {
43 | $('.llamacorn').addClass('llamacorn-fly');
44 | };
45 |
46 | var removeLlamacorn = function() {
47 | $('.llamacorn-wrapper').remove();
48 | };
49 |
50 | var bindEvent = function() {
51 | $('.llamacorn').each(function() {
52 | if( this.complete ) {
53 | imageLoaded.call( this );
54 | } else {
55 | $(this).one('load', imageLoaded);
56 | }
57 | });
58 | $('.llamacorn').on('transitionend', removeLlamacorn);
59 | };
60 |
61 | var init = function() {
62 | if (showLlamacorn()) {
63 | setStyles();
64 | createImageWrapper();
65 | bindEvent();
66 | }
67 | };
68 |
69 | init();
70 |
71 | } )(jQuery);
72 |
--------------------------------------------------------------------------------
/central/qa-headers.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Central QA Headers
3 | // @namespace https://central.tri.be/
4 | // @version 1.0
5 | // @description Applies color background to QA template headers for better visibility
6 | // @author Nicole Ramsey
7 | // @include /https?:\/\/central(dev)?.tri.be\/(projects\/)*[^\/]*\/?issues\/?/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function( $ ) {
12 | 'use strict';
13 | var $el;
14 | var $heading;
15 | var headings = [
16 | {
17 | 'selector': 'a[name^="QA-PASSED-"]',
18 | 'icon': 'check',
19 | 'background': 'green',
20 | 'color': '#fff',
21 | 'stroke': '#0f690f'
22 | },
23 | {
24 | 'selector': 'a[name^="RETURNED-"]',
25 | 'icon': 'undo',
26 | 'background': '#f4af49',
27 | 'color': '#fff',
28 | 'stroke': '#db8e15'
29 | },
30 | ];
31 |
32 | for ( var i in headings ) {
33 | $el = $( headings[ i ].selector );
34 |
35 | // Find the closest h2
36 | $heading = $el.next( 'h2' );
37 |
38 | // Add the icon
39 | $heading.prepend( ' ' );
40 |
41 | // Style the heading
42 | $heading.css( {
43 | 'background-color': headings[ i ].background,
44 | 'color': headings[ i ].color,
45 | 'font-weight': 'bold',
46 | '-webkit-text-fill-color': headings[ i ].color,
47 | '-webkit-text-stroke': '1px ' + headings[ i ].stroke
48 | } );
49 | }
50 | })( jQuery );
51 |
--------------------------------------------------------------------------------
/central/qa-testing-instructions.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name QA Testing Instructions
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Click button to add QA Testing Instructions format
6 | // @author Paul Kim
7 | // @include /https?:\/\/central.tri.be\/issues\/[0-9]+\/?/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function() {
12 |
13 | var text = {
14 | tiFormat: `h2. QA Testing Instructions
15 |
16 | *URL to test:* ##Add URL to test##
17 |
18 | h3. Feature Request
19 |
20 | ##Add feature request##
21 |
22 | h3. Solution
23 |
24 | ##Add solution##
25 |
26 | h3. Steps to Replicate
27 |
28 | ##Add steps to replicate##`,
29 | seeNote: 'See note #',
30 | };
31 |
32 | var el = {
33 | button: null,
34 | };
35 |
36 | var addStyles = function() {
37 | var css = '.qa-testing-instructions{margin-bottom:10px}.qa-testing-instructions__button{margin:0px;border:none;background:#157F9D;color:#fff;border-radius:2px;padding:5px 8px}.qa-testing-instructions__button:hover,.qa-testing-instructions__button:focus{background:#1CA8C7}';
38 | var head = document.head;
39 | var style = document.createElement('style');
40 | style.type = 'text/css';
41 | style.appendChild(document.createTextNode(css));
42 |
43 | head.appendChild(style);
44 | };
45 |
46 | var getButton = function() {
47 | var button = document.createElement('button');
48 | button.classList.add('qa-testing-instructions__button');
49 | button.type = 'button';
50 | button.textContent = 'Add Testing Instructions';
51 | el.button = button;
52 | return button;
53 | };
54 |
55 | var insertAfter = function(newNode, refNode) {
56 | refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
57 | };
58 |
59 | var addQatiButton = function() {
60 | var wrapper = document.createElement('div');
61 | wrapper.classList.add('qa-testing-instructions');
62 | wrapper.appendChild(getButton());
63 | insertAfter(wrapper, el.form.querySelector('.issue-notes-new legend'));
64 | };
65 |
66 | var getNoteNum = function() {
67 | return document.querySelectorAll('.journal').length + 1;
68 | };
69 |
70 | var handleClick = function() {
71 | el.form.querySelector('.issue-notes-new #notes').value = text.tiFormat;
72 | el.form.querySelector('#issue_custom_field_values_24').value = text.seeNote + getNoteNum();
73 | };
74 |
75 | var bindEvents = function() {
76 | el.button.addEventListener('click', handleClick);
77 | };
78 |
79 | var init = function() {
80 | el.form = document.querySelector('#issue-form');
81 | addStyles();
82 | addQatiButton();
83 | bindEvents();
84 | };
85 |
86 | init();
87 |
88 | } )();
89 |
90 |
--------------------------------------------------------------------------------
/central/relay-for-toggl.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Relay Toggl for Tribe
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Adds a button to record task/description and start time to toggl.
6 | // @author Gary Kovar
7 | // @match *://central.tri.be/issues/*
8 | // ==/UserScript==
9 |
10 | // Your Toggl API key
11 | var apiKeyToggl = 'toggleAPIkey';
12 |
13 | // Or use your own
14 | var proxyURL = 'https://relay-for-toggl.herokuapp.com/';
15 |
16 | // Default activity text (ex. Back End Development)
17 | var defaultActivity = '';
18 |
19 | // This may differ from project to project, use defaultActivity for this to auto-detect
20 | var defaultActivityID = '';
21 |
22 | // You can customize the default timer text to make it easy for certain workflows
23 | var defaultDescription = '';
24 |
25 | window.tribeToggl = {};
26 |
27 | (function( window, $, app, apiKeyToggl ) {
28 | 'use strict';
29 |
30 | var cache = {};
31 |
32 | app.init = function() {
33 | $('.title-bar-actions div').append(' ');
34 |
35 | app.setupCache();
36 | app.setupProjects();
37 | app.getButton();
38 | };
39 |
40 | app.setupCache = function() {
41 | cache.id = window.location.pathname.match(/\d+/)[0];
42 | cache.project = $('title').text().trim().split('-')[0];
43 | cache.taskName = $('#issue_subject').val();
44 | cache.projects = $('ul #projects-menu li');
45 | cache.project_names = [];
46 | cache.projects.each(function(proj) {
47 | cache.project_names.push($(this).text().replace('»', '').trim());
48 | } );
49 | cache.projectID = '';
50 | cache.activityNumber = '';
51 | cache.activityText = '';
52 | cache.activityDescription = '';
53 | };
54 |
55 | // This makes sure each project is already set up.
56 | app.setupProjects = function() {
57 | $.post( proxyURL,
58 | { request: 'project',
59 | action: 'setup',
60 | api_key: apiKeyToggl,
61 | projects : cache.project_names,
62 | project_name : cache.project
63 | }, 'json' )
64 | .done(function( data ) {
65 | cache.projectId = data;
66 | });
67 | };
68 |
69 | app.getButton = function() {
70 | $.post( proxyURL,
71 | { request: 'time',
72 | action: 'running',
73 | api_key: apiKeyToggl
74 | }, 'json' )
75 | .done(function( data ) {
76 | data = $.parseJSON( data );
77 |
78 | $('#toggl_container').html(app.startTime( data.running ) );
79 |
80 | var $toggl_relay = $('#toggl-relay');
81 |
82 | $toggl_relay.on('change', function (e) {
83 | cache.activityNumber = $(e.target).val();
84 | cache.activityText = $(e.target).find("option:selected").text();
85 | $( 'button.time' ).show();
86 | $( 'button.time-new' ).on( 'click', function() {
87 | cache.activityDescription = $('#toggl_timerdescription').val();
88 | $('.time').remove();
89 | app.startToggl();
90 | });
91 | });
92 |
93 | if ( '' !== defaultActivity ) {
94 | var defaultActivityID = '';
95 |
96 | $('option', $toggl_relay).each( function() {
97 | var $this = $(this);
98 |
99 | if ( $this.text() === defaultActivity ) {
100 | defaultActivityID = $this.val();
101 | }
102 | } );
103 |
104 | $toggl_relay.val( defaultActivityID );
105 | $toggl_relay.trigger('change');
106 | }
107 |
108 | if ( '' !== defaultDescription ) {
109 | $('#toggl_timerdescription').val( defaultDescription );
110 | }
111 | });
112 | };
113 |
114 | app.startTime = function( running ) {
115 |
116 | var input = ' ';
117 |
118 | //if ( running === 0 ) {
119 | return app.activityType() + input + 'Start Timer ';
120 | //} else {
121 | // return app.activityType() + input + 'Stop Timer / Start on This Task ' ;
122 | //}
123 | };
124 |
125 | app.activityType = function() {
126 | return '' + $('#time_entry_activity_id').html() + ' ';
127 | };
128 |
129 | app.startToggl = function() {
130 | $.post( proxyURL,
131 | { request: 'time',
132 | action: 'start',
133 | api_key: apiKeyToggl,
134 | description: '[' + cache.id + '][' + cache.activityText + '][' + cache.activityDescription + ']',
135 | pid : cache.projectId
136 | }, 'json' )
137 | .done( function () {
138 | app.getButton();
139 | } );
140 | };
141 |
142 |
143 | app.init();
144 |
145 |
146 | })( window, jQuery, window.tribeToggl, apiKeyToggl );
147 |
--------------------------------------------------------------------------------
/central/sticky-clocker-dropdowns.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Sticky Clocker Dropdowns
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Persistent project and activity fields after submitting hours on the clocking tool
6 | // @author Paul Kim
7 | // @include /^https:\/\/central.tri.be(\/.*)?/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function($) {
12 | var state = {
13 | projectId: null,
14 | activityId: null,
15 | submitted: false,
16 | };
17 |
18 | var keys = {
19 | projectId: 'project_id',
20 | activityId: 'time_entry%5Bactivity_id%5D',
21 | };
22 |
23 | var handleProject = function(event, xhr, settings) {
24 | if (settings.hasOwnProperty('data')) {
25 | var dataArr = settings.data.split('&');
26 |
27 | dataArr.forEach(function(query) {
28 | var queryArr = query.split('=');
29 | var key = queryArr[0];
30 |
31 | if (key === keys.projectId) {
32 | state.projectId = queryArr[1];
33 | }
34 | if (key === keys.activityId) {
35 | state.activityId = queryArr[1];
36 | }
37 | });
38 |
39 | if (xhr.status === 201) {
40 | if (state.projectId != null) {
41 | $('#clocking-tool .project_id').val(state.projectId).change();
42 | state.projectId = null;
43 | }
44 |
45 | state.submitted = true;
46 | }
47 | }
48 | };
49 |
50 | var handleActivity = function(event, xhr, settings) {
51 | if (state.submitted && settings.url.startsWith('/clocking_tool/activities.json') && state.activityId != null) {
52 | $('#clocking-tool .time_entry_activity_id').val(state.activityId).change();
53 | state.submitted = false;
54 | state.activityId = null;
55 | }
56 | };
57 |
58 | var bindEvents = function() {
59 | $('#clocking-tool form').ajaxComplete(handleProject);
60 | $('#clocking-tool form').ajaxComplete(handleActivity);
61 | };
62 |
63 | var init = function() {
64 | bindEvents();
65 | };
66 |
67 | init();
68 |
69 | } )(jQuery);
70 |
--------------------------------------------------------------------------------
/central/target-blank.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Central target blank on external links
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description Adds target=blank on external links
6 | // @author Matthew Batchelder
7 | // @include /https?:\/\/central(dev)?.tri.be.*\/.*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function( $ ) {
12 | 'use strict';
13 |
14 | var obj = {};
15 |
16 | /**
17 | * Initialize
18 | */
19 | obj.init = function() {
20 | var $a = $( 'a' ).not( 'a[href^="https://central"]' ).not( 'a[href^="/"]' );
21 | $a.attr( 'target', '_blank' );
22 | };
23 |
24 | $( function() {
25 | obj.init();
26 | } );
27 | })( jQuery );
--------------------------------------------------------------------------------
/central/uncheck-email-all.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Uncheck email all
3 | // @namespace http://your.homepage/
4 | // @version 0.1
5 | // @description Uncheck the email all checkbox
6 | // @author You
7 | // @include /^https:\/\/central.tri.be(\/.*)?/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | ( function( $ ) {
12 | $( '#send_notification' ).prop( 'checked', false );
13 | })( jQuery );
14 |
--------------------------------------------------------------------------------
/dotorg/README.md:
--------------------------------------------------------------------------------
1 | # WP.org Scripts
2 |
3 | ## `dotorg-helper.user.js`
4 |
5 | In runs in the .org forums for Modern Tribe plugins. It colors the resolved threads green, and the threads which have the last voice from a team member to light yellow. Don't use together with dotorg Hider.
6 |
7 | ## `dotorg-hider.user.js`
8 |
9 | A twin to .org Helper. The script runs in the .org forums for Modern Tribe plugins. It hides threads that don't need attention: resolved threads and threads where last voice is a team member. Don't use together with dotorg Helper.
10 |
--------------------------------------------------------------------------------
/dotorg/dotorg-helper.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name .org Helper for Modern Tribe Support
3 | // @namespace http://tampermonkey.net/
4 | // @version 1.7.4
5 | // @description The script runs in the .org forums for Modern Tribe plugins. It colors resolved threads green, and threads where last voice is a team member light yellow.
6 | // @author Andras Guseo
7 | // @include https://wordpress.org/support/plugin/pardot*
8 | // @include https://wordpress.org/support/plugin/the-events-calendar*
9 | // @include https://wordpress.org/support/plugin/event-tickets*
10 | // @include https://wordpress.org/support/plugin/gigpress*
11 | // @include https://wordpress.org/support/plugin/image-widget*
12 | // @include https://wordpress.org/support/plugin/advanced-post-manager*
13 | // @downloadURL https://github.com/moderntribe/tampermonkey-scripts/raw/master/dotorg/dotorg-helper.user.js
14 | // @grant none
15 | // ==/UserScript==
16 |
17 | /**
18 | * Marks resolved threads green.
19 | * Marks threads blue where Modern Tribe is the last voice. (No action needed.)
20 | * Marks threads yellow that are more than a month old.
21 | */
22 |
23 | (function() {
24 | 'use strict';
25 |
26 | // Get all lines in an array
27 | var x = document.getElementsByClassName( 'type-topic' );
28 |
29 | // MT team members
30 | var mtteam = [
31 | 'aguseo', // Andras Guseo - 2016-04-25
32 | 'alaasalama', // Alaa Salama - 2018-11-19 to 2020-08-31
33 | 'barryhughes-1', // Barry Hughes - xxxx-xx-xx to 2020-07-31
34 | 'bordoni', // Gustavo Bordoni
35 | 'bskousen3', // Brendan Skousen - 2017-10-23
36 | 'brianjessee', // Brian Jessee
37 | 'borkweb', // Mattew Batchelder - 2020-03-30
38 | 'brook-tribe', // Brook - xxxx-xx-xx to 2017-xx-xx
39 | 'chikaibeneme', // Chika Ibeneme - 2020-02-10
40 | 'cliffpaulick', // Clifford Paulick
41 | 'cliffseal', // Cliff Seal - Pardot
42 | 'cswebd3v', // Chris Swenson - 2020-09-01
43 | 'courane01', // Courtney Robertson - 2017-02-22
44 | 'deblynprado', // Deblyn Prado - 2019-04-11
45 | 'djbramer', // Dan Bramer
46 | 'erishel', // Edward Rishel - 2018-03-12 to 2018-12-31
47 | 'eugenekyale', // Eugene Kyale - 2020-09-01
48 | 'eugenetribe', // Eugene Kyale - 2020-09-01
49 | 'geoffbel', // Geoffroy 'LeGeoff' Belanger - 2016-01-20
50 | 'geoffgraham', // Geoff Graham
51 | 'ggwicz', // George Gecewicz - xxxx-xx-xx to 2017-xx-xx
52 | 'iammarta', // Marta Kozak - 2020-09-01
53 | 'jaimemarchwinski', // Jaime Marchwinski - 2017-08-31
54 | 'jentheo', // Jennifer Theodore - 2017-05-08
55 | 'jeremy80', // Jeremy Marchandeau - 2018-03-26
56 | 'juanfra', // Juan Francisco Aldasoro
57 | 'koriashton', // Kori Ashton - 2020-09-01
58 | 'lucasbustamante', // Lucas Bustamante - 2020-03-30
59 | 'mandraagora', // Wolf Bishop - 2020-03-04
60 | 'masoodak', // Masood Khan - 2020-09-01
61 | 'matumu', // Marho Atumu - 2020-09-01
62 | 'mitogh', // Crisoforo Hernandez
63 | 'neillmcshea', // Neill McShae
64 | 'nicosantos', // Nico Santos - 2015-xx-xx to 2019-xx-xx
65 | 'nikrosales', // Nik Rosales - 2020-02-24
66 | 'patriciahillebrandt', // Patricia Hillebrandt - 2017-06-09
67 | 'rafsuntaskin', // Rafsun Chowdhury - 2020-03-30
68 | 'sdenike', // Shelby DeNike - 2018-10-08 to 2019-08-31
69 | 'sjaure', // Santiago Jaureguiberry - 2019-05-26
70 | 'skyshab', // Jason 'Sky' Shabatura - 2018-01-02
71 | 'tokyobiyori', // Ali Darwich - 2018-11-19
72 | 'tribalmike', // Mike Cotton - 2018-10-11
73 | 'tribecari', // Caroline - 2016-05-16 to 2017-12-31
74 | 'translationsbymoderntribe', // Modern Tribe Translations - 2020-03-30
75 | 'vicskf', // Victor Zarranz - 2017-xx-xx
76 | 'zbtirrell', // Zach Tirrell -
77 | ];
78 |
79 | var i, j;
80 | var resolvedColor = '#98fb98';
81 | var lastVoiceColor = '#add8e6';
82 | var closeColor = '#ffe463';
83 |
84 | // Check every line
85 | for( i = 0; i < x.length; i++ ) {
86 |
87 | // Check if the line is resolved
88 | for( j = 0; j < mtteam.length; j++ ) {
89 | //console.log();
90 | var m = x[i].innerHTML.search( 'class="resolved"' );
91 | if( m > 0 ) {
92 | x[i].style.backgroundColor = resolvedColor;
93 |
94 | // If resolved then skip
95 | continue;
96 | }
97 |
98 | // If not resolved, check if tha last voice is a team member
99 | var n = x[i].innerHTML.search( 'href="https://wordpress.org/support/users/' + mtteam[j] + '/"' );
100 |
101 | if ( n > 0 ) {
102 | var o = x[i].innerHTML.search( /[1-9] (month[s]?)/ );
103 | if ( o > 0 ) {
104 | x[i].style.backgroundColor = closeColor;
105 | continue;
106 | }
107 | x[i].style.backgroundColor = lastVoiceColor;
108 | }
109 | }
110 | }
111 |
112 |
113 | })();
114 |
--------------------------------------------------------------------------------
/dotorg/dotorg-hider.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name .org Hider for Modern Tribe Support
3 | // @namespace http://tampermonkey.net/
4 | // @version 1.7.3
5 | // @description The script runs in the .org forums for Modern Tribe plugins. A twin to .org Helper. It hides threads that don't need attention: resolved threads and threads where last voice is a team member.
6 | // @author Andras Guseo
7 | // @include https://wordpress.org/support/plugin/pardot*
8 | // @include https://wordpress.org/support/plugin/the-events-calendar*
9 | // @include https://wordpress.org/support/plugin/event-tickets*
10 | // @include https://wordpress.org/support/plugin/gigpress*
11 | // @include https://wordpress.org/support/plugin/image-widget*
12 | // @include https://wordpress.org/support/plugin/advanced-post-manager*
13 | // @downloadURL https://github.com/moderntribe/tampermonkey-scripts/raw/master/dotorg/dotorg-hider.user.js
14 | // @grant none
15 | // ==/UserScript==
16 |
17 | /**
18 | * Hides all threads that are resolved or last voice is Modern Tribe.
19 | * Marks threads yellow that are more than a month old.
20 | */
21 |
22 | (function() {
23 | 'use strict';
24 |
25 | // Get all lines in an array
26 | var x = document.getElementsByClassName( 'type-topic' );
27 |
28 | // MT team members
29 | var mtteam = [
30 | 'aguseo', // Andras Guseo
31 | 'alaasalama', // Alaa Salama
32 | 'barryhughes-1', // Barry Hughes
33 | 'bordoni', // Gustavo Bordoni
34 | 'bskousen3', // Brendan Skousen
35 | 'brianjessee', // Brian Jessee
36 | 'borkweb', // Mattew Batchelder - 2020-03-30
37 | 'brook-tribe', // Brook
38 | 'chikaibeneme', // Chika Ibeneme - 2020-02-10
39 | 'cliffpaulick', // Clifford Paulick
40 | 'cliffseal', // Cliff Seal - Pardot
41 | 'cswebd3v', // Chris Swenson - 2020-09-01
42 | 'courane01', // Courtney Robertson
43 | 'deblynprado', // Deblyn Prado
44 | 'djbramer', // Dan Bramer
45 | 'erishel', // Edward Rishel
46 | 'eugenekyale', // Eugene Kyale - 2020-09-01
47 | 'geoffbel', // Geoffroy 'LeGeoff' Belanger
48 | 'geoffgraham', // Geoff Graham
49 | 'ggwicz', // George Gecewicz
50 | 'jaimemarchwinski', // Jaime Marchwinski
51 | 'jentheo', // Jennifer Theodore
52 | 'jeremy80', // Jeremy Marchandeau
53 | 'juanfra', // Juan Francisco Aldasoro
54 | 'koriashton', // Kori Ashton - 2020-09-01
55 | 'lucasbustamante', // Lucas Bustamante - 2020-03-30
56 | 'mandraagora', // Wolf Bishop - 2020-03-04
57 | 'masoodak', // Masood Khan - 2020-09-01
58 | 'mitogh', // Crisoforo Hernandez
59 | 'neillmcshea', // Neill McShae
60 | 'nicosantos', // Nico Santos
61 | 'nikrosales', // Nik Rosales
62 | 'patriciahillebrandt', // Patricia Hillebrandt
63 | 'rafsuntaskin', // Rafsun Chowdhury -2020-03-30
64 | 'sdenike', // Shelby DeNike
65 | 'sjaure', // Santiago Jaureguiberry
66 | 'skyshab', // Jason 'Sky' Shabatura
67 | 'tokyobiyori', // Ali Darwich
68 | 'tribalmike', // Mike Cotton
69 | 'tribecari', // Caroline
70 | 'translationsbymoderntribe', // Modern Tribe Translations - 2020-03-30
71 | 'vicskf', // Victor Zarranz
72 | 'zbtirrell', // Zach Tirrell
73 | ];
74 |
75 | var i, j;
76 |
77 | // Check every line
78 | for( i = 0; i < x.length; i++ ) {
79 |
80 | // Check if the line is resolved
81 | for( j = 0; j < mtteam.length; j++ ) {
82 | //console.log();
83 | var m = x[i].innerHTML.search( 'class="resolved"' );
84 | if( m > 0 ) {
85 | x[i].style.display = 'none';
86 |
87 | // If resolved then skip
88 | continue;
89 | }
90 |
91 | // If not resolved, check if tha last voice is a team member
92 | var n = x[i].innerHTML.search( 'href="https://wordpress.org/support/users/' + mtteam[j] + '/"' );
93 |
94 | if ( n > 0 ) {
95 | var o = x[i].innerHTML.search( /[1-9] (month[s]?)/ );
96 | if ( o > 0 ) {
97 | x[i].style.backgroundColor = '#ffe463';
98 | continue;
99 | }
100 | x[i].style.display = 'none';
101 | }
102 | }
103 | }
104 |
105 |
106 | })();
107 |
--------------------------------------------------------------------------------
/dotorg/dotorg-review-collector.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name dotorg Review Collector
3 | // @namespace http://tampermonkey.net/
4 | // @version 1.0
5 | // @description Try to collect reivews from WordPress.org
6 | // @author Andras Guseo
7 | // @match https://wordpress.org/support/topic/*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | // Your code here...
15 | var stars = document.getElementsByClassName('dashicons-star-filled').length;
16 |
17 | console.log(stars);
18 |
19 | var submissionDate = document.getElementsByClassName('bbp-topic-permalink')[0].title.split(' at ');
20 | console.log(submissionDate[0]);
21 |
22 | var threadURL = document.getElementsByClassName('bbp-topic-permalink')[0].href;
23 |
24 | var writer = document.getElementsByClassName('bbp-user-nicename'); //[0].innerHTML.replace('(','').replace(')','');
25 |
26 | var author = writer[0].innerHTML.replace('(','').replace(')','');
27 | var reply = "";
28 | if( writer.length > 1 ) {
29 | reply = writer[1].innerHTML.replace('(','').replace(')','');
30 | }
31 | console.log(author);
32 |
33 | var title = document.getElementsByClassName('page-title')[0].innerHTML;
34 |
35 | console.log(title);
36 |
37 | var infoContainer = document.createElement('input');
38 | infoContainer.id = 'infoContainerId';
39 | infoContainer.style.width = '100%';
40 | var dotorgHead = document.getElementById('wordpress-org');
41 | //var popup = document.getElementsByClassName('modal-time-entry');
42 | dotorgHead.parentNode.insertBefore(infoContainer, dotorgHead);
43 |
44 | infoContainer.value = stars + ';out of 5 stars;' + submissionDate[0] + ';' + submissionDate[1] + ';' + author + ';' + title + ';' + reply + ';' + threadURL;
45 | infoContainer.onclick = 'copyFunction';
46 |
47 | infoContainer.addEventListener( 'click', function( e ) {
48 | var target = e.target;
49 | console.log(target.nodeName);
50 | if ( 'input' !== target.nodeName.toLowerCase() ) {
51 | return true;
52 | }
53 |
54 | var ticket_id = target.id;
55 | console.log(ticket_id);
56 | copyIt( ticket_id );
57 | } );
58 |
59 | function copyIt( element ) {
60 | console.log( element );
61 | var copyText = document.getElementById( element );
62 | copyText.select();
63 | document.execCommand( "Copy" );
64 | }
65 |
66 | })();
67 |
--------------------------------------------------------------------------------
/github/README.md:
--------------------------------------------------------------------------------
1 | # GitHub Scripts
2 |
3 | ## `github-products-branch-protection.user.js`
4 |
5 | Auto selects some checkboxes for setting default branch protection on Products in GitHub.
6 |
7 | ## `github-pull-prefix-removal.user.js`
8 |
9 | When viewing Pull Requests on GitHub, if the PR is against a Modern Tribe repo, it removes the `moderntribe/` prefix for easier scanning.
10 |
--------------------------------------------------------------------------------
/github/github-pr-tools-merge.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name GitHub PR Tools (merge)
3 | // @namespace https://github.com/
4 | // @version 0.2
5 | // @description Adds Merge It button
6 | // @author Scott Kingsley Clark
7 | // @include /https?:\/\/github\.com.*\/pull\/.*/
8 | // @require https://code.jquery.com/jquery-3.2.1.min.js
9 | // @grant none
10 | // ==/UserScript==
11 |
12 | (function() {
13 | 'use strict';
14 |
15 | const $issue_label_container = jQuery('.label-select-menu');
16 | const $issue_label_heading_button = jQuery('summary.discussion-sidebar-heading', $issue_label_container);
17 |
18 | jQuery('Mark Merge ').insertBefore($issue_label_container);
19 |
20 | const $merge_it = jQuery('#merge-it');
21 |
22 | $merge_it.on('click', function() {
23 | $issue_label_heading_button.click();
24 |
25 | setTimeout(function(){
26 | jQuery( 'div.select-menu-item', $issue_label_container ).each( function() {
27 | var $label_row = jQuery(this),
28 | label_selected = $label_row.hasClass('selected'),
29 | label_text = jQuery('span.name', $label_row).text();
30 |
31 | if ('merge' === label_text && ! label_selected) {
32 | $label_row.click();
33 | } else if ( 'code review' === label_text && label_selected ) {
34 | $label_row.click();
35 | }
36 | } );
37 | }, 200);
38 |
39 | setTimeout(function(){
40 | $issue_label_heading_button.click();
41 | }, 400);
42 | });
43 | })();
44 |
--------------------------------------------------------------------------------
/github/github-pr-tools.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name GitHub PR Tools
3 | // @namespace https://github.com/
4 | // @version 0.1
5 | // @description Adds PR helper tools
6 | // @author Matthew Batchelder
7 | // @include /https?:\/\/github\.com.*\/pull\/.*/
8 | // @require https://code.jquery.com/jquery-3.2.1.min.js
9 | // @grant none
10 | // ==/UserScript==
11 |
12 | ( function( $ ) {
13 | 'use strict';
14 |
15 | var obj = {
16 | specFiles: []
17 | };
18 |
19 | /**
20 | * Initialize
21 | */
22 | obj.init = function() {
23 | var $reviewTools = $( '.pr-review-tools' );
24 |
25 | var specFiles = obj.getSpecFiles();
26 |
27 | $reviewTools.prepend( 'Hide spec.js
' );
28 | $reviewTools.on( 'click', '#tribe-suppress-spec', function( e ) {
29 | var specFiles = obj.getSpecFiles();
30 |
31 | var $el = $( this );
32 | var isActive = $el.hasClass( 'active' );
33 |
34 | $( '#tribe-suppress-spec-count' ).text( ' (' + specFiles.length + ')' );
35 |
36 | var $showhide = $( '#tribe-suppress-spec-showhide' );
37 |
38 | specFiles.forEach( function( el ) {
39 | $( '.js-details-target', el ).click();
40 | } );
41 |
42 | if ( isActive ) {
43 | $el.removeClass( 'active' );
44 | $showhide.html( 'Hide' );
45 | } else {
46 | $el.addClass( 'active' );
47 | $showhide.html( 'Show' );
48 | }
49 | } );
50 | };
51 |
52 | obj.getSpecFiles = function() {
53 | // Reset spec files array
54 | obj.specFiles = [];
55 |
56 | var jsFiles = document.getElementsByClassName( 'js-file' );
57 |
58 | Array.prototype.forEach.call( jsFiles, function( el, index, array ) {
59 | var fileloc = el.querySelector( '.file-info a' );
60 | if ( fileloc.innerText.match( /\.spec\.js/ ) ) {
61 | obj.specFiles.push( el );
62 | }
63 | } );
64 |
65 | return obj.specFiles;
66 | };
67 |
68 | $( function() {
69 | obj.init();
70 | } );
71 | })( jQuery );
72 |
--------------------------------------------------------------------------------
/github/github-products-branch-protection.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name GitHub Branch Protection
3 | // @namespace github.com
4 | // @version 0.1
5 | // @description Checks all the required fields for basic protection of Branches
6 | // @author Gustavo Bordoni
7 | // @match https://github.com
8 | // @include /^https:\/\/github.com\/.*/
9 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
10 | // @grant none
11 | // ==/UserScript==
12 |
13 |
14 | (function( $ ) {
15 | 'use strict';
16 |
17 | $( document ).ready( function(){
18 | var $secure = $( 'input[name="secure"]' );
19 |
20 | if ( $secure.is( ':checked' ) ) {
21 | return;
22 | }
23 |
24 | $secure.trigger( 'click' );
25 |
26 | $( 'input[name="has_required_reviews"]' ).trigger( 'click' );
27 | $( 'input[name="dismiss_stale_reviews_on_push"]' ).trigger( 'click' );
28 | $( 'input[name="required_reviews_enforce_dismissal"]' ).trigger( 'click' );
29 |
30 | $( 'input[name="authorized_users_or_teams"]' ).trigger( 'click' );
31 |
32 | $( '.js-protected-branch-settings' )
33 | .append( ' ' )
34 | .append( ' ' );
35 |
36 | });
37 | })( jQuery );
38 |
--------------------------------------------------------------------------------
/github/github-pull-prefix-removal.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Suppress moderntribe/
3 | // @namespace https://central.tri.be/
4 | // @version 0.1
5 | // @description When on a combined pull request list, remove moderntribe/ so the list is more easily scannable
6 | // @author Matthew Batchelder
7 | // @include /https:\/\/github.com\/pulls?.*q=is%3Aopen\+is%3Apr\+sort%3Aupdated-desc\+repo%3Amoderntribe%2Fevents-pro.*/
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | $( '.issue-title-link.issue-nwo-link' ).each( function() {
12 | var $el = $( this );
13 | $el.html( $el.html().replace( /moderntribe\//, '' ) );
14 | } );
15 |
16 | $( '.issue-title-link.js-navigation-open' ).each( function() {
17 | var $el = $( this );
18 | var jqxhr = $.get( $el.attr( 'href' ) );
19 | jqxhr.done( function( data ) {
20 | var $page = $( data );
21 | var $author = $page.find( '.pull-header-username' );
22 | var $meta = $el.closest( '.js-issue-row' ).find( '.issue-meta' );
23 | $meta.prepend( ' ' );
24 | $meta.find( '.tribe-by' ).append( 'by: ' ).append( $author );
25 | } );
26 | });
27 |
--------------------------------------------------------------------------------
/img/toggl.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/66da2fe43442e283d6b5fdccf6249ef36db177b0/img/toggl.gif
--------------------------------------------------------------------------------
/jira/README.md:
--------------------------------------------------------------------------------
1 | # Jira scripts
2 |
3 | ## Dashboard tweaks
4 |
5 | ### Project list styles
6 |
7 | Script: [`jira-dashboard-styles-projects.user.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-dashboard-styles-projects.user.js)
8 |
9 | Improves the styling of the Project List gadget.
10 |
11 | ### Reduce size of Sprint Health Gadget on the Dashboard
12 |
13 | Script: [`jira-dashboard-styles-sprint-health.user.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-dashboard-styles-sprint-health-gadget.user.js)
14 |
15 | This script injects styles that reduce the font size of the Sprint Health Gadget within the Dashboard.
16 |
17 | * Before: http://p.tri.be/PaGYhL
18 | * After: http://p.tri.be/9XVPS1
19 |
20 | ## Navigation tweaks
21 |
22 | ### Add "Dashboard" link in Global Nav
23 |
24 | Script: [`jira-global-nav-dashboard.user.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-global-nav-dashboard.user.js)
25 |
26 | This adds a *Dashboard icon* to the Global Navigation sidebar, allowing you to one-click to your dashboard if you have set your default page to the *Your Work* page.
27 |
28 | Screencast: http://p.tri.be/ov7Grn
29 |
30 | ### Make urls in the "Forum Threads" field clickable links
31 |
32 | Script: [`jira-convert-threads-to-links.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-convert-threads-to-links.js)
33 |
34 | This checks for the "Forum Threads" field and scrapes it for urls, wrapping them in anchor tags so they become clickable links.
35 |
36 | Screenshot: http://p.tri.be/wCrT13
37 |
38 | ### Add "Your Work" link in Global Nav
39 |
40 | Script: [`jira-global-nav-your-work.user.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-global-nav-your-work.user.js)
41 |
42 | This adds a *Your Work* icon to the Global Navigation sidebar, allowing you to one-click to the *Your Work* page if you have set your default page to the *Dashboard* page.
43 |
44 | Screencast: Basically...the same as the `jira-global-nav-dashboard.user.js`, only the inverse.
45 |
46 | ## Ticket tweaks
47 |
48 | ### Remove Harvest from ticket page
49 |
50 | Script: [`jira-remove-harvest.user.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-remove-harvest.user.js)
51 |
52 | Removes the Harvest section in the sidebar of Jira.
53 |
54 | ### Uncheck email checkbox when bulk editing
55 |
56 | Script: [`jira-bulk-edit-uncheck-email.user.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-bulk-edit-uncheck-email.user.js)
57 |
58 | When bulk editing tickets, automatically uncheck the _Send mail for this update_ checkbox.
59 |
60 | ## Tempo tweaks
61 |
62 | ### Hide private events in My Work / Tempo
63 |
64 | Script: [`jira-mywork-hide-events.user.js`](https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-mywork-hide-events.user.js)
65 |
66 | If you are using the Google Calendar integration in Tempo you can hide events on the My Work page by defining keywords or strings. When the script finds a keyword in the event title the event will be hidden on the My Work page.
67 | Predefined keywords:
68 | * Hide
69 | * Busy
70 | * Private
71 | * Night
72 |
73 | You can also adjust or define your own keywords in the script.
74 |
--------------------------------------------------------------------------------
/jira/jira-bulk-edit-uncheck-email.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Jira: Bulk Edit Uncheck Email Checkbox
3 | // @namespace https://moderntribe.atlassian.net/
4 | // @version 0.0.1
5 | // @description Inject items into the global nav
6 | // @author Modern Tribe Products
7 | // @match https://moderntribe.atlassian.net/secure/BulkEditDetails.jspa
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | document.getElementById( 'sendBulkNotificationCB' ).checked = false;
15 |
16 | })();
17 |
--------------------------------------------------------------------------------
/jira/jira-convert-threads-to-links.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Make LA strings links
3 | // @namespace https://moderntribe.atlassian.net
4 | // @version 0.1
5 | // @description Convert text urls to clickable links in the "Forum Threads" field in Jira's "new view"
6 | // @author Stephen Page
7 | // @include https://moderntribe.atlassian.net/browse/*
8 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
9 | // @require https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/waitForKeyElements.js
10 | // @downloadURL https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-convert-threads-to-links.user.js
11 | // @grant none
12 | // ==/UserScript==
13 |
14 | if ( ! document.getElementById( 'jira' ) ) {
15 | return;
16 | }
17 |
18 | waitForKeyElements("[aria-label='Edit Forum Threads']", wrap_fu, true );
19 |
20 | function wrapLines(str, tmpl) {
21 | return str.replace(/http.+$/gm, tmpl || '$& ');
22 | }
23 |
24 | function wrap_fu(threadsButton) {
25 | const sib = threadsButton.prev();
26 | const text = sib.html();
27 |
28 | sib.html( wrapLines( text ) );
29 | }
30 |
--------------------------------------------------------------------------------
/jira/jira-dashboard-styles-sprint-health-gadget.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Jira Dashboard: Sprint Health Gadget Styles
3 | // @namespace https://moderntribe.atlassian.net/
4 | // @version 0.1
5 | // @description Adjust styles of the Sprint Health Gadget
6 | // @author Matthew Batchelder
7 | // @include /https?:\/\/moderntribe.atlassian.net\/secure\/Dashboard.jspa/
8 | // @grant GM_addStyle
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | var styles = `
15 | .sprint-health-dashboard-item h1,
16 | .sprint-health-dashboard-item h1 span,
17 | .sprint-health-dashboard-item h3 {
18 | font-size: 1rem;
19 | }
20 |
21 | .sprint-health-dashboard-item .ag-metric-value {
22 | font-size: 1.25rem;
23 | }
24 |
25 | .sprint-health-dashboard-item .ag-inner-area {
26 | padding: 0.5rem;
27 | }
28 |
29 | .sprint-health-dashboard-item .ag-status-header {
30 | margin-bottom: 0.5rem;
31 | }
32 | `;
33 | GM_addStyle( styles );
34 | })();
35 |
--------------------------------------------------------------------------------
/jira/jira-global-nav-dashboard.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Jira: Add Dashboard to Global Nav
3 | // @namespace https://moderntribe.atlassian.net/
4 | // @version 0.0.1
5 | // @description Inject items into the global nav
6 | // @author Modern Tribe Products
7 | // @include https://moderntribe.atlassian.net/*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | let links = [
15 | {
16 | "name": 'Dashboard',
17 | "url": '/secure/Dashboard.jspa',
18 | "svg": ' '
19 | }
20 | ];
21 |
22 | function insertNavItem( item ) {
23 | let navItem = document.createElement( 'span' );
24 | navItem.setAttribute( 'class', 'tampermonkey-addition' );
25 | let html = `
26 |
41 | `;
42 | navItem.innerHTML = html;
43 | document.querySelectorAll( 'span[data-test-id="navigation-next.global-navigation.components.global-nav-with-id"] > div > div' )[0].appendChild( navItem );
44 | }
45 |
46 | for ( let i in links ) {
47 | insertNavItem( links[i] );
48 | }
49 | })();
50 |
--------------------------------------------------------------------------------
/jira/jira-global-nav-your-work.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Jira: Add Your Work to Global Nav
3 | // @namespace https://moderntribe.atlassian.net/
4 | // @version 0.0.1
5 | // @description Inject items into the global nav
6 | // @author Modern Tribe Products
7 | // @include https://moderntribe.atlassian.net/*
8 | // @grant none
9 | // ==/UserScript==
10 |
11 | (function() {
12 | 'use strict';
13 |
14 | let links = [
15 | {
16 | "name": 'Your Work',
17 | "url": '/jira/your-work',
18 | "svg": ' '
19 | }
20 | ];
21 |
22 | function insertNavItem( item ) {
23 | let navItem = document.createElement( 'span' );
24 | navItem.setAttribute( 'class', 'tampermonkey-addition' );
25 | let html = `
26 |
41 | `;
42 | navItem.innerHTML = html;
43 | document.querySelectorAll( 'span[data-test-id="navigation-next.global-navigation.components.global-nav-with-id"] > div > div' )[0].appendChild( navItem );
44 | }
45 |
46 | for ( let i in links ) {
47 | insertNavItem( links[i] );
48 | }
49 | })();
50 |
--------------------------------------------------------------------------------
/jira/jira-mywork-hide-events.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Jira: Hide private events in My Work
3 | // @namespace https://moderntribe.atlassian.net/
4 | // @version 0.4
5 | // @description Hide private / unneeded events from your Google calendar on the Mywork page to reduce clutter
6 | // @author Andras Guseo
7 | // @include https://app.tempo.io/timesheets/jira/my-work/*
8 | // @include https://app.tempo.io/io/web/tempo-app*
9 | // @downloadURL https://github.com/moderntribe/tampermonkey-scripts/raw/master/jira/jira-mywork-hide-events.user.js
10 | // @grant none
11 | // ==/UserScript==
12 |
13 | /**
14 | * If you are using the Google Calendar integration in Tempo
15 | * you can hide events on the My Work page by defining keywords
16 | * or strings. When the script finds a keyword in the event title
17 | * the event will be hidden on the My Work page.
18 | * You can define your own keywords at the top of the script a bit below.
19 | * They can be simple words like 'Private' or full event titles like
20 | * 'Dentist appointment'.
21 | *
22 | * Examples that will be hidden based on the keywords / strings defined below:
23 | * Busy - dinner with mom
24 | * Hide - Dentist appointment yearly checkup
25 | * Busy with kids
26 | * Private investigator
27 | * Nighttime
28 | *
29 | * Dev notes:
30 | * The @include needs to match the url of the iframe.
31 | */
32 |
33 | (function() {
34 | 'use strict';
35 |
36 | /**************/
37 | /* SETTINGS */
38 | /**************/
39 |
40 | /**
41 | * Define the class to look for. This changes on occasion
42 | */
43 | var rowClass = 'sc-MKjYC';
44 |
45 | /**
46 | * Define the keywords / strings that we are looking for in event titles
47 | * that should be hidden. Case sensitive!
48 | */
49 | var strings = [
50 | 'Hide',
51 | 'Busy',
52 | 'Private',
53 | 'Night',
54 | ];
55 |
56 | /******************/
57 | /* SETTINGS END */
58 | /******************/
59 |
60 | // The array / object where we store the calendar entries
61 | var slots;
62 |
63 | // Do we want error logging in the console?
64 | var log = false;
65 |
66 | // Run every 2 seconds...
67 | var startScript = window.setInterval( hideThem, 2000 );
68 |
69 | function hideThem() {
70 |
71 | // Collect all the events
72 | slots = document.getElementsByClassName( rowClass );
73 |
74 | if ( log ) console.log( 'Number of slots: ' + slots.length );
75 |
76 | // Walk the object
77 | if ( slots.length > 0 ) {
78 | for ( var i = 0; i < slots.length; i++ ) {
79 |
80 | // Bail if we already did the excercise
81 | if ( slots[i].classList.contains( 'weredone' ) ) {
82 | if ( log ) console.log( 'Skipped');
83 | continue;
84 | }
85 | // Add a class, so we know we have checked this already
86 | else {
87 | slots[i].classList.add( 'weredone' );
88 | }
89 |
90 | // Get the HTML from the row
91 | var row = slots[i].innerHTML;
92 |
93 | // Check if one of the predefined strings is in the row
94 | for ( var j = 0; j < strings.length; j++ ) {
95 |
96 | // If yes, then hide it
97 | if ( row.search( strings[j] ) > 0 ) {
98 | if ( log ) console.log( 'Found one: ' + i );
99 | slots[i].style.display = 'none';
100 | }
101 | }
102 | }
103 | }
104 | else {
105 | console.log( 'DOM not ready.' );
106 | }
107 | }
108 | })();
109 |
110 | /** Changelog
111 | *
112 | * === 0.4 - 2020-03-04 ===
113 | * Set up a settings part at the top
114 | * Broke out the class name to be a setting
115 | *
116 | * === 0.3 - 2020-02-26 ===
117 | * Updated the @include URL
118 | * Updated the class name the script is looking for
119 | * Added @downloadURL
120 | * Added checks to reduce resource needs
121 | * Housekeeping
122 | *
123 | * === 0.1 - 2020-01-16 ===
124 | * Initial version
125 | */
126 |
--------------------------------------------------------------------------------
/jira/jira-remove-harvest.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Jira: Remove Harvest time tracking widget
3 | // @namespace https://moderntribe.atlassian.net/
4 | // @version 0.0.1
5 | // @description Customize and remove modules from Edit Screen
6 | // @author Modern Tribe Products
7 | // @include https://moderntribe.atlassian.net/browse/*
8 | // @grant GM_addStyle
9 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/jira/jira-remove-harvest.user.js
10 | // @run-at document-start
11 | // ==/UserScript==
12 |
13 | (function() {
14 | 'use strict';
15 |
16 | GM_addStyle( '#harvest__toggle-timer { display: none; }' );
17 |
18 | })();
19 |
--------------------------------------------------------------------------------
/liveagent/README.md:
--------------------------------------------------------------------------------
1 | # LiveAgent Scripts
2 |
3 | ## `liveagent-clickafy-urls.user.js`
4 |
5 | Intended for the Events Support team. Works on the LiveAgent platform.
6 | The script makes the following strings / URLs clickable and will open in a new tab / window:
7 | * User's site URL
8 | * Issue Tracker ID
9 | * Central ticket ID (works with both `123456` and `#123456` formats)
10 | * Jira ticket ID
11 | * user ID
12 | * Sandbox site URL
13 |
14 | ## `liveagent-plugin-versions.user.js`
15 |
16 | Intended for the Products Support team. Works on the LiveAgent platform.
17 | The script displays the version numbers of our plugins by release date at the top of the screen.
18 | The script has to be manually updated with new version numbers after a new release. Update notes are in the file.
19 |
20 | You can customize the script with adjusting the following variables:
21 | * `log` - Boolean, to enable logging in the console for troubleshooting. Default: _false_
22 | * `startHidden` - Boolean, whether the table should be hidden or visible on load. Default: _true_
23 | * `startRight` - String, e.g. '350px'. Defines the starting position of the table, which is the distance from the right edge of the browser window. Default: _350px_
24 | * `secondColumnWidth` - Integer, to set the width of the first two columns of the table. Default: _70_
25 | * `initialRows` - Integer, to set how many rows should be shown on load and on collapsing the table. Default: _1_
26 | * `scrollOnCollapse` - Boolean, whether the plugin versions should scroll to the most recent version (the bottom of the table) on collapse. Default: _true_
27 | * `expandedHeight` - Integer, to set the height of the table when expanded. Default: _300_
28 |
29 | Note: the changes on script update will be lost.
--------------------------------------------------------------------------------
/liveagent/liveagent-clickafy-urls.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name LiveAgent - Clickafy URLs
3 | // @namespace http://tampermonkey.net/
4 | // @version 2.0
5 | // @description Make the Central and Jira issue tracker IDs, the user ID, the user's website, and the sandbox site URL clickable in LiveAgent
6 | // @author Andras Guseo
7 | // @include https://support.theeventscalendar.com/agent/*
8 | // @include https://theeventscalendar.ladesk.com/agent/*
9 | // @downloadURL https://github.com/moderntribe/tampermonkey-scripts/raw/master/liveagent/liveagent-clickafy-urls.user.js
10 | // @grant none
11 | // ==/UserScript==
12 |
13 | (function () {
14 | 'use strict';
15 |
16 | // If you set this to true you will see log messages in the console
17 | var log = false;
18 | if (log) console.log('Starting Clickafy Script');
19 |
20 | // Run the script every 5 seconds. This is necessary due to the dynamic nature of LiveAgent
21 | var startScript = window.setInterval(clickableCentral, 5000);
22 |
23 | var fields = ["Central ID", "Issue Tracker ID", "Site's URL", "WordPress ID", "Sandbox URL"];
24 | var field = "",
25 | url = "";
26 |
27 | function clickableCentral() {
28 |
29 | // Get the rows is an object
30 | var rows = document.getElementsByClassName('gwt-TextBox');
31 |
32 | // Walk the object
33 | for (var i = 0; i < rows.length; i++) {
34 |
35 | // Get the HTML from the row
36 | field = rows[i].name;
37 |
38 | // If it's not a field we're looking for, then skip.
39 | if (fields.indexOf(field) < 0) {
40 | if (log) console.log('Skipped. Field not in array: ' + field);
41 | continue;
42 | }
43 |
44 | // Create the id for the field
45 | var id = field.toLowerCase().replace(' ', '-').replace("'", '');
46 | if (log) console.log('ID: ' + id);
47 |
48 | // Check if the ID exists. If it exists it means we already created it, so skip.
49 | if (null != document.getElementById(id)) {
50 | if (log) console.log('Skipped at ID: ' + id);
51 | continue;
52 | }
53 |
54 | // Now we can start creating...
55 |
56 | // Get the value of the field
57 | var val = rows[i].value;
58 | if (log) console.log(field + ': ' + val);
59 |
60 | // If they are URLs, then use the value
61 | if (val.search('http') >= 0) {
62 | if (log) console.log("Found 'http', using value as URL. . " + val);
63 | url = val;
64 | } else if (field == "Site's URL") {
65 | if (log) console.log("Found Site's URL - " + val);
66 | url = 'https://' + val;
67 | } else if (field == "WordPress ID") {
68 | if (log) console.log("Found WordPress ID - " + val);
69 | url = 'https://theeventscalendar.com/wp-admin/user-edit.php?user_id=' + val;
70 | } else if (field == "Issue Tracker ID" || field == "Central ID") {
71 | if (log) console.log("Found Issue Tracker ID - " + val);
72 | /* If it doesn't contain a dash, then it's Central */
73 | if (val.search('-') < 0) {
74 | if (log) console.log("Found Central - " + val);
75 | url = 'https://central.tri.be/issues/';
76 | }
77 | /* Otherwise it is Jira */
78 | else {
79 | if (log) console.log("Found Jira - " + val);
80 | url = 'https://moderntribe.atlassian.net/browse/';
81 | }
82 | url = url + val;
83 | }
84 |
85 | //Creating the container.
86 | var linkContainer = document.createElement('span');
87 | linkContainer.id = id;
88 | linkContainer.innerHTML = '👁️ ';
89 | linkContainer.style = 'position: absolute; right: 10px; z-index: 9;';
90 | rows[i].parentNode.insertBefore(linkContainer, rows[i]);
91 |
92 | } // for ( var i=0; i0?(n+="YES\nEditor: ",a.length>0?n+="Block":n+="Classic"):(n+="NO\nDesign: ",s.length>0?n+="V2":n+="V1"),n+="\nShortcode: ",l.length>0?n+="YES":y.length>0?n+="Yes, by 3rd party plugin:\n - The Events Calendar Shortcode & Block":null!=E&&E.innerHTML.length>0?n+="Yes, by 3rd party plugin:\n - The Events Calendar Shortcode and Templates Addon":n+="NO",e=0;e0){console.log(t.substr(t.indexOf("=")+1)),n+="AUTOPTIMIZE FOUND!",n+="\n------------------\n";break}for(n+="\nTheme: ",e=0;e=0?n+=C.substr(C.indexOf("=")+1):n+="not found"}for(e=0;e=0&&(n+="\n"+p[e].content),p[e].content.search("WPML")>=0&&(n+="\n"+p[e].content));if(n+="\n------\nCaching / Minification:\n",null!=g)for(e=0;e0&&(b=d[e]);if(null!=h)for(e=0;e0&&(b=d[e]);for(e=0;e's: WPML, WooCommerce, etc
72 | var metaTags = document.getElementsByTagName( 'meta' );
73 | var metas = [
74 | 'WooCommerce',
75 | 'WPML',
76 | 'Divi',
77 | ];
78 |
79 | /**
80 | * Caching plugins
81 | */
82 | var caching = [
83 | 'WP-Super-Cache',
84 | 'WP Fastest Cache',
85 | 'W3 Total Cache',
86 | 'Hummingbird',
87 | 'WP Rocket',
88 | 'Endurance Page Cache',
89 | 'LiteSpeed Cache',
90 | 'WP Super Minify',
91 | ];
92 |
93 | /**
94 | * Work
95 | */
96 |
97 | // Check for competitor
98 | var competitorHtml = checkIfCompetitor();
99 |
100 | // If there is no competitor or tribe product found, then quit
101 | if ( false === competitorHtml && false === checkIfTribe() ) {
102 | console.log( 'Tribe or competitor not found. Quitting.' );
103 | return false;
104 | }
105 |
106 | // String V1 or V2
107 | var tecDesignVersion = getTecDesignVersion();
108 |
109 | // String: 'view' or n/a
110 | var tecView = getTecView( tecDesignVersion );
111 |
112 | // String
113 | var editorUsed = getEditor( tecView );
114 |
115 | // String
116 | var shortcode = getShortcode();
117 |
118 | // HTML markup
119 | var versionNumbers = getVersionNumbers( tecDesignVersion );
120 |
121 | // String
122 | var theme = getTheme();
123 |
124 | // HTML markup
125 | var WordPress = getOtherPlugins( true );
126 |
127 | // HTML markup
128 | var otherPlugins = getOtherPlugins( false );
129 |
130 | // Bool
131 | var autoptimize = checkAutoptimize();
132 |
133 | // String
134 | var cachingPlugin = checkCaching();
135 |
136 | /**
137 | * Render
138 | */
139 | var finalHtml = renderStyle() + renderMarkup();
140 | if ( window.self === window.top ) {
141 | document.getElementsByTagName( 'body' )[ 0 ].insertAdjacentHTML( 'afterend', finalHtml );
142 | document.getElementById( 'sniffer-container' ).style.right = -document.getElementById( 'sniffer-container' ).offsetWidth + 55 + "px";
143 | document.getElementById( 'hider' ).addEventListener( 'click', hideBlock );
144 | }
145 |
146 | /**
147 | * Functions
148 | */
149 |
150 | /**
151 | * Checking for competitor products
152 | *
153 | * @returns {string|boolean} Returns the product name or false if not found.
154 | */
155 | function checkIfCompetitor() {
156 |
157 | if ( logLevel1 ) console.log( "Checking for competitors." );
158 |
159 | for( var competitor in competitors ) {
160 |
161 | if ( logLevel2 ) console.log( "Checking for: " + competitor );
162 |
163 | if ( null !== document.getElementById( competitors[ competitor ].id ) ) {
164 |
165 | if ( logLevel1 ) console.log( competitor + ' found.' );
166 |
167 | //var competitorHtml = competitor;
168 |
169 | return competitor;
170 | }
171 | }
172 | return false;
173 | }
174 |
175 | /**
176 | * Check for 'tribe' in body classes to see if Modern Tribe plugins are present
177 | *
178 | * @returns {boolean}
179 | */
180 | function checkIfTribe() {
181 | for( var i = 0; i < bodyClasses.length; i++ ) {
182 | if ( bodyClasses[ i ].startsWith( "tribe" ) ) {
183 | return true;
184 | }
185 | }
186 | return false;
187 | }
188 |
189 | /**
190 | * Get the used design version for Modern Tribe Products based on the presence of a V2 css class
191 | *
192 | * @returns {string} V1 or V2
193 | */
194 | function getTecDesignVersion() {
195 | // We use breakpoint to check for design version
196 | var design = 'V1';
197 | if ( document.getElementsByClassName( 'tribe-common--breakpoint-xsmall' ).length > 0 ) {
198 | design = 'V2';
199 | }
200 | if ( logLevel1 ) console.log ( 'Design used: ' + design );
201 | return design;
202 | }
203 |
204 | /**
205 | * Get the calendar view of the current page.
206 | *
207 | * @param design The design used in The Events Calendar, V1 or V2.
208 | *
209 | * @returns {string} Name of the view or n/a.
210 | *
211 | * @todo Remove css if not needed
212 | * @todo In the log line use the appropriate css (css1 or css2) based on design
213 | */
214 | function getTecView( design ) {
215 | var element;
216 | //var css;
217 |
218 | // Go through the views row by row
219 | for( var view in views ) {
220 | if ( logLevel2 ) console.log( design + ' - ' + view + ': ' + views[ view ].css2 );
221 | if ( design == 'V1' ) {
222 | element = document.getElementsByClassName( views[ view ].css1 );
223 | //css = views[ view ].css1;
224 | } else if ( design == 'V2' ) {
225 | element = document.getElementsByClassName( views[ view ].css2 );
226 | //css = views[ view ].css1;
227 | }
228 |
229 | // Check if the class exists
230 | if ( element.length > 0 ) {
231 | if ( logLevel1 ) console.log( 'Found view: ' + view );
232 | return view;
233 | }
234 | }
235 | return 'n/a';
236 | }
237 |
238 | /**
239 | * Try to get the editor used on single event page.
240 | *
241 | * @param view
242 | *
243 | * @returns {string} The editor used or n/a.
244 | */
245 | function getEditor( view ) {
246 | // Block editor on single event page
247 | var editor = document.getElementsByClassName( 'tribe-blocks-editor' );
248 |
249 | if ( view == 'Single event view' ) {
250 | if ( editor.length > 0 ) {
251 | editor = 'Block editor';
252 | } else {
253 | editor = 'Classic editor';
254 | }
255 | } else {
256 | editor = 'n/a';
257 | }
258 |
259 | return editor;
260 | }
261 |
262 | /**
263 | * Check if page was generated by any kind of shortcode.
264 | *
265 | * @returns {string} Returns if the page is generated with shortcode and if yes, then which plugin was used.
266 | */
267 | function getShortcode() {
268 | // Tribe shortcodes
269 | var shortcodeV1 = document.getElementsByClassName( 'tribe-events-shortcode' );
270 | var shortcodeV2 = document.getElementsByClassName( 'tribe-events-view--shortcode' );
271 | // Events Calendar Shortcode & Block
272 | var ecsb = document.getElementsByClassName( 'ecs-events' );
273 | // Events Calendar Shortcode and Templates Addon
274 | var ect = document.getElementById( 'ect-events-list-content' );
275 | // This holds the answer
276 | var shortcode = '';
277 |
278 | if ( shortcodeV1.length > 0 || shortcodeV2.length > 0 ) {
279 | shortcode = 'Yes';
280 | } else {
281 | if ( ecsb.length > 0 ) {
282 | shortcode = "Yes, by 3rd party plugin:\n - The Events Calendar Shortcode & Block ";
283 | } else if ( ect != null && ect.innerHTML.length > 0 ) {
284 | shortcode = "Yes, by 3rd party plugin:\n - The Events Calendar Shortcode and Templates Addon ";
285 | } else {
286 | shortcode = "NO";
287 | }
288 | }
289 | if ( logLevel1 ) console.log( 'Shortcode: ' + shortcode );
290 | return shortcode;
291 | }
292 |
293 | /**
294 | * Try to get the theme used
295 | *
296 | * @returns {string} The theme name or slug
297 | */
298 | function getTheme() {
299 | var theme = "couldn't identify";
300 |
301 | // Checking for the theme
302 | for( var i = 0; i < bodyClasses.length; i++ ) {
303 | // Check for Avada
304 | if ( logLevel2 ) console.log( 'Checking: ' + bodyClasses[i] + ' for...' );
305 | if ( logLevel2 ) console.log( '...Avada...' );
306 | if ( bodyClasses[ i ].startsWith( "avada-" ) ) {
307 | var avadaVersion = document.getElementById( products[ 'Avada' ].css ).getAttribute( 'href' );
308 | theme = 'Avada ' + getVersionNumber( avadaVersion );
309 | break;
310 | }
311 |
312 | // Check for other themes
313 | if ( logLevel2 ) console.log( '...other themes...' );
314 | if ( bodyClasses[ i ].startsWith( "tribe-theme-" ) ) {
315 | theme = bodyClasses[ i ].substr( 12 );
316 | break;
317 | }
318 | // Check for more other themes
319 | if ( logLevel2 ) console.log( '...more other themes...' );
320 | if ( bodyClasses[ i ].startsWith( "theme-" ) ) {
321 | theme = bodyClasses[ i ].substr( 6 );
322 | break;
323 | }
324 | }
325 | // Check for even more other themes
326 | if ( theme == '' ) {
327 | if ( logLevel2 ) console.log( '...even more other themes...' );
328 | for( i = 0; i < links.length; i++ ) {
329 | var link = links[ i ].href.match( /(themes\/).{2,}?(\/)/ );
330 | if ( links[ i ].href != undefined && link != null ) {
331 | theme = link[ 0 ].slice( 7, -1 );
332 | break;
333 | }
334 | }
335 | }
336 |
337 | if ( logLevel1 ) console.log( 'Theme found: ' + theme );
338 | return theme;
339 | }
340 |
341 | /**
342 | * Retrieves the version number of a product
343 | *
344 | * @param resourceUrl The URL of the resource
345 | *
346 | * @returns {string} The version number
347 | */
348 | function getVersionNumber( resourceUrl ) {
349 | // Grab the version number
350 | var versionNumber = resourceUrl.substr( resourceUrl.indexOf( "=" ) + 1 );
351 |
352 | // Check if there is anything after the version number
353 | var misc = versionNumber.search( '&' );
354 | if ( misc > 0 ) {
355 | versionNumber = versionNumber.substr( 0, misc );
356 | }
357 |
358 | if ( logLevel2 ) console.log( resourceUrl + ' | ' + versionNumber );
359 | return versionNumber;
360 | }
361 |
362 | /**
363 | * Looking for other plugins and their version numbers
364 | *
365 | * @param wp Boolean if we want to retrieve WordPress version number or something else
366 | *
367 | * @returns {*}
368 | */
369 | function getOtherPlugins( wp = false ) {
370 | var other = '';
371 | // Looking for WordPress and other plugins
372 | for( var i = 0; i < metaTags.length; i++ ) {
373 | // Skip if meta is not 'generator'
374 | if ( metaTags[ i ].name != 'generator' ) continue;
375 |
376 | if ( logLevel2 ) console.log( 'Checking ' + metaTags[i].content + ' for...' );
377 | if ( ! wp ) {
378 | for( var j = 0; j < metas.length; j++ ) {
379 | if ( logLevel2 ) console.log( '...' + metas[j] + '...' );
380 | if ( metaTags[ i ].content.search( metas[j] ) >= 0 ) {
381 | other += lineify( metaTags[ i ].content ); //'' + metaTags[i].content + '
';
382 | if ( logLevel2 ) console.log( other );
383 | }
384 | }
385 | }
386 | else {
387 | // WordPress
388 | if ( logLevel2 ) console.log( 'Checking ' + metaTags[i].content + ' for WordPress' );
389 | if ( metaTags[ i ].content.search( 'WordPress' ) == 0 ) {
390 | other += lineify( metaTags[ i ].content ); //'' + metaTags[i].content.split(" ")[0]; + '
';
391 | if ( logLevel2 ) console.log( other );
392 | return other;
393 | }
394 | }
395 | }
396 |
397 | return other;
398 | }
399 |
400 | /**
401 | * Get Modern Tribe plugin version numbers.
402 | *
403 | * @param design The design used in The Events Calendar, V1 or V2.
404 | *
405 | * @returns {string} HTML markup of the list of plugins with version numbers
406 | *
407 | * @todo Eliminate one Filter Bar version
408 | */
409 | function getVersionNumbers( design ) {
410 | var plugins = '';
411 | var version = '';
412 |
413 | // Version numbers
414 | // Go through the products row by row
415 | for( var product in products ) {
416 | //if ( logLevel2 ) console.log( design + product + ', ' + products[ product ].css );
417 |
418 | // Skip the following
419 | if ( product == 'Avada' ) continue;
420 |
421 | // Eliminate one TEC
422 | if ( (design == 'V1' || design == '(V1)') && product == 'The Events Calendar V2' ) continue;
423 | if ( design == 'V2' && product == 'The Events Calendar V1' ) continue;
424 |
425 | var x = document.getElementById( products[ product ].css );
426 |
427 | // If the stylesheet is found, then let's look at the href attrib / URL
428 | if ( x != null ) x = x.getAttribute( "href" );
429 |
430 | // Look for version number
431 | if ( x != null && x.search( "=" ) >= 0 ) {
432 | version = getVersionNumber( x );
433 | }
434 | // ... or add 'not found'
435 | else {
436 | version = 'not found';
437 | }
438 |
439 | if ( logLevel1 ) console.log( product + ' ' + version + ' found' );
440 | // Open the line
441 | plugins += '' + product + ': ';
442 | plugins += version;
443 | // Close the line
444 | plugins += '
';
445 |
446 | }
447 |
448 | return plugins;
449 | }
450 |
451 | /**
452 | * Checking for Autoptimize
453 | *
454 | * @returns {boolean}
455 | */
456 | function checkAutoptimize() {
457 | for( var i = 0; i < links.length; i++ ) {
458 | var source = links[ i ].getAttribute( 'href' );
459 | if ( source != null ) {
460 | if ( source.search( 'autoptimize' ) > 0 ) {
461 | if ( logLevel1 ) console.log( 'Autoptimize found.' );
462 | return true;
463 | }
464 | }
465 | }
466 |
467 | return false;
468 | }
469 |
470 | /**
471 | * Checking for caching and minification plugins. Returns the first found.
472 | *
473 | * @returns {string}
474 | */
475 | function checkCaching() {
476 | // Last tag before the closing body. For caching plugins.
477 | var prevSib = document.lastChild.previousSibling.nodeValue;
478 | var lastChi = document.lastChild.nodeValue;
479 | var i;
480 |
481 | // Checking last sibling
482 | if( logLevel2 ) console.log( 'Caching: checking prevSib for...' );
483 | if ( prevSib != null ) {
484 | for( i = 0; i < caching.length; i++ ) {
485 | if( logLevel2 ) console.log( '...' + caching[i] );
486 | if ( prevSib.search( caching[ i ] ) > 0 ) {
487 | if( logLevel1 ) console.log( 'Caching found: ' + caching[i] );
488 | return caching[ i ];
489 | }
490 | }
491 | }
492 | if ( logLevel2 ) console.log( '...No prevSib found in DOM' );
493 |
494 | // Checking last child
495 | if( logLevel2 ) console.log( 'Caching: checking lastChi for...' );
496 | if ( lastChi != null ) {
497 | for( i = 0; i < caching.length; i++ ) {
498 | if( logLevel2 ) console.log( '...' + caching[i] );
499 | if ( lastChi.search( caching[ i ] ) > 0 ) {
500 | if( logLevel1 ) console.log( 'Caching found: ' + caching[i] );
501 | return caching[ i ];
502 | }
503 | }
504 | }
505 | if ( logLevel2 ) console.log( '...No lastChi found in DOM' );
506 |
507 | // Asset CleanUp: Page Speed Booster
508 | // IDs for ACU
509 | if( logLevel2 ) console.log( 'Caching: checking for ACU...' );
510 | var cachingACU = [ 'wpacu-combined-css-body-1', 'wpacu-combined-js-body-group-1' ];
511 | for( i = 0; i < cachingACU.length; i++ ) {
512 | if ( null != document.getElementById( cachingACU[ i ] ) ) {
513 | if( logLevel1 ) console.log( 'Caching found: Asset CleanUp: Page Speed Booster' );
514 | return "Asset CleanUp: Page Speed Booster";
515 | }
516 | }
517 |
518 | // Swift Performance caching plugin
519 | if( logLevel2 ) console.log( 'Caching: checking for Swift Performance...' );
520 | if ( document.getElementsByClassName( 'swift-in-viewport' ).length > 0 ) {
521 | if( logLevel1 ) console.log( 'Caching found: Swift Performance' );
522 | return "Swift Performance";
523 | }
524 |
525 | if( logLevel1 ) console.log( 'No caching found' );
526 | return 'not found';
527 | }
528 |
529 | /**
530 | * Create a renderable line with HTML markup
531 | *
532 | * @param line
533 | *
534 | * @returns {string}
535 | */
536 | function lineify( line ) {
537 | line = line.split( " " );
538 | return '' + line[ 0 ] + ': ' + line[ 1 ].replace( 'ver:', '' ).replace( 'v.', '' ) + '
';
539 | }
540 |
541 | /**
542 | * Rendering
543 | */
544 | function renderStyle() {
545 | var style = '';
555 |
556 | return style;
557 | }
558 |
559 | function renderMarkup() {
560 |
561 | var html = '';
562 | html = '';
563 | html += '
Tribe Sniffer ' + snifferVersionNumber + '
';
564 | html += '
';
565 | html += '
';
566 | html += '
';
567 |
568 | if ( false !== competitorHtml ) {
569 | html += '
' + competitorHtml + '
';
570 | } else {
571 | html += '
About this page ';
572 | html += '
View: ';
573 | html += tecView;
574 | html += '
';
575 |
576 | html += '
';
577 | html += 'Editor used: ';
578 | html += editorUsed;
579 | html += '
';
580 |
581 | html += '
Design: ';
582 | html += tecDesignVersion;
583 | html += '
';
584 |
585 | html += '
Generated by shortcode: ';
586 | html += shortcode;
587 | html += '
';
588 |
589 | html += '
Theme used: ';
590 | html += theme;
591 | html += '
';
592 |
593 | html += '
';
594 |
595 | html += '
';
596 | html += '
Versions ';
597 |
598 | html += WordPress;
599 |
600 | html += versionNumbers;
601 |
602 | html += otherPlugins;
603 |
604 | html += '';
605 |
606 | html += '
';
607 | html += '
Caching / Minification ';
608 |
609 | if ( true === autoptimize ) {
610 | html += '
AUTOPTIMIZE FOUND!!!
';
611 | }
612 |
613 | html += '
' + cachingPlugin + '
';
614 |
615 | }
616 | html += '
';
617 |
618 | return html;
619 | }
620 |
621 | /**
622 | * Animation
623 | */
624 | function hideBlock() {
625 | var block = document.getElementById( 'sniffer-container' );
626 | var str = document.getElementById( 'hider' );
627 | var right = window.outerWidth - block.offsetLeft;
628 | var hideRight = -block.offsetWidth + 55;
629 | if ( logLevel2 ) console.log( 'block.offsetLeft: ' + block.offsetLeft );
630 | if ( logLevel2 ) console.log( 'block.offsetWidth: ' + block.offsetWidth );
631 | if ( logLevel2 ) console.log( 'window.outerWidth: ' + window.outerWidth );
632 | if ( logLevel2 ) console.log( 'right: ' + right );
633 | if ( logLevel2 ) console.log( 'hideRight: ' + hideRight );
634 |
635 |
636 | if ( block.offsetLeft + 150 > window.outerWidth ) {
637 | block.style.right = "10px";
638 | if ( logLevel2 ) console.log( 'move left' );
639 | } else {
640 | block.style.right = hideRight + "px";
641 | if ( logLevel2 ) console.log( 'move right' );
642 | }
643 | }
644 |
645 | })();
646 |
--------------------------------------------------------------------------------
/premium-forum/README.md:
--------------------------------------------------------------------------------
1 | # Premium Forum
2 |
3 | ## `premium-forum-collapse-convo.user.js`
4 |
5 | You have a long exchange with a client. Tired of always scrolling down and up? This is for you.
6 | If there are more than 6 replies in a thread, then everything between the first and the last will be collapsed.
7 |
8 | Screenshot: https://cloudup.com/cn2yDKGCZ4v
9 |
10 | ## `premium-forum-move-status-box.user.js`
11 |
12 | Moves the Assignee box and the Status box to the top, above the posts.
13 |
14 | ## `premium-forum-plugin-versions.user.js`
15 |
16 | Shows the following info:
17 | * Plugin version numbers of the last 5 releases
18 | * Version numbers of the user's plugins, if system information is submitted
19 | * The users nick for easy access / copy-paste
20 |
21 | Screenshot: https://cloudup.com/cU1Tp-rggXe
22 |
23 | ## `premium-forum-private-topic.user.js`
24 |
25 | If the thread is marked private then puts a red label at the top and bottom, and draws a red border around the posts.
26 |
27 | ## `premium-forum-reformat-sysinfo.user.js`
28 |
29 | Reformats the user submitted system information if it comes through without line breaks.
30 |
31 | It will re-color "View raw system information" to red to signal the formatting.
32 |
33 | If you still see improvement areas, like a plugin in 2 lines, let me know.
34 |
35 | Screenshot before: https://cloudup.com/ibZxM-aYwLA
36 |
37 | Screenshot after: https://cloudup.com/i4JtjHdOgaP
38 |
39 | ## `premium-forum-thread-list-colors.user.js`
40 |
41 | Colors the thead list on theeventscalendar.com based on urgency.
42 | * Critical (past a 24h): red
43 | * Overdue (past 20h): orange
44 | * Resolved: green
45 |
46 | Screenshot: https://cloudup.com/ccdWJTkiF30
47 |
48 | ## `premium-forum-users-licenses.user.js`
49 |
50 | Show the user's licenses at the bottom right, making them visible when the thread is long.
51 |
--------------------------------------------------------------------------------
/premium-forum/premium-forum-collapse-convo.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Premium Forum Extras - Collapse Convo
3 | // @namespace https://theeventscalendar.com/
4 | // @version 0.2
5 | // @description You have a long exchange with a client. Tired of always scrolling down and up? This is for you. Kicks in after 6 replies.
6 | // @author Andras Guseo
7 | // @include https://theeventscalendar.com/wp-admin/post.php?*
8 | // @match https://theeventscalendar.com/wp-admin/post.php?*
9 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/premium-forum-collapse-convo.user.js
10 | // @grant none
11 | // @run-at document-idle
12 |
13 | // ==/UserScript==
14 |
15 | (function() {
16 | 'use strict';
17 |
18 | /**
19 | * Show a red label if the topic is private
20 | */
21 | var heads = document.getElementsByClassName( 'bbp-reply-header' );
22 | var threads = document.getElementsByClassName( 'type-reply' );
23 | var tlen = threads.length;
24 | if ( tlen > 6 ) {
25 | var lastFour = 2 * tlen - 3;
26 | var ntlen = '.bbp-body div:nth-child(n+' + lastFour + ')';
27 | //console.log( 'tlen: ' + tlen);
28 | $( '.bbp-reply-header' ).css({ 'display': 'none' });
29 | $( '.type-reply' ).css({ 'display': 'none' });
30 | $( ntlen ).css({ 'display': 'block' });
31 |
32 | var htmlstring;
33 | htmlstring = '
';
34 | htmlstring += tlen-3;
35 | htmlstring += ' more
';
36 |
37 | var htmlJumpToTop;
38 | htmlJumpToTop = 'Hide`em again | Jump to first post ';
39 |
40 | var htmlHideemTop;
41 | htmlHideemTop = '';
42 |
43 | $( '#bbp-topic-0-lead' ).after( htmlstring );
44 | $( '#count-container' ).css({ 'cursor': 'pointer' });
45 | $( '.count-number' ).css({ 'display': 'inline-block', 'float': 'left', 'clear': 'both' });
46 | $( '.count-round' ).css({ 'border-width': '1px', 'border-style': 'solid', 'border-color': '#ccc', 'border-radius': '50%', 'display': 'inline-block', 'padding': '5px', 'margin-left': '5px', 'margin-right': '5px', 'width': '20px', 'text-align': 'center' });
47 | $( '.count-line' ).css({ 'display': 'inline-block', 'float': 'left', 'clear': 'both', 'width': '20px', 'border-right-width': '3px', 'border-right-style': 'dashed', 'border-right-color': '#999', 'margin-top': '1px', 'margin-bottom': '1px' });
48 |
49 | $( '#topic-0-replies' ).after( htmlJumpToTop );
50 |
51 | $( '#bbp-topic-0-lead' ).after( htmlHideemTop );
52 | $( '.hideem' ).css({ 'cursor': 'pointer', 'display': 'none' });
53 |
54 | // Handle hover
55 | if ( document.getElementById( 'count-container' ) != null ) {
56 | document.getElementById( 'count-container' ).addEventListener( 'click', showPosts );
57 | document.getElementById( 'hideem' ).addEventListener( 'click', hidePosts );
58 | document.getElementById( 'hideemTop' ).addEventListener( 'click', hidePosts );
59 | }
60 | }
61 |
62 | function showPosts() {
63 | $( '.bbp-reply-header' ).css({ 'display': 'block' });
64 | $( '.type-reply' ).css({ 'display': 'block' });
65 | $( '#count-container' ).css({ 'display': 'none' });
66 | $( '.hideem' ).css({ 'display': 'inline-block' });
67 | }
68 | function hidePosts() {
69 | $( '.bbp-reply-header' ).css({ 'display': 'none' });
70 | $( '.type-reply' ).css({ 'display': 'none' });
71 | $( ntlen ).css({ 'display': 'block' });
72 | $( '#count-container' ).css({ 'display': 'block' });
73 | $( '.hideem' ).css({ 'display': 'none' });
74 | }
75 |
76 | })();
77 |
--------------------------------------------------------------------------------
/premium-forum/premium-forum-move-status-box.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Premium Forum Extras - Move status box
3 | // @namespace https://theeventscalendar.com/
4 | // @version 0.8
5 | // @description Move Assignee and Status box to the top
6 | // @author Andras Guseo
7 | // @include https://theeventscalendar.com/wp-admin/post.php?*
8 | // @match https://theeventscalendar.com/wp-admin/post.php?*
9 | // @grant none
10 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/premium-forum-move-status-box.user.js
11 | // @run-at document-idle
12 |
13 | // ==/UserScript==
14 |
15 | (function() {
16 | 'use strict';
17 |
18 | /**
19 | * Move Assignee and Status box to the top
20 | */
21 | $( '#staff_update_box' ).detach().insertAfter( '#bbps_extra' );
22 |
23 | })();
--------------------------------------------------------------------------------
/premium-forum/premium-forum-plugin-versions.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Premium Forum Extras - Latest plugin versions
3 | // @namespace https://theeventscalendar.com/
4 | // @version 1.6
5 | // @description Display our plugins' latest version numbers (manually updated) and the user's version numbers from sysinfo
6 | // @author Andras Guseo
7 | // @include https://theeventscalendar.com/wp-admin/post.php?*
8 | // @match https://theeventscalendar.com/wp-admin/post.php?*
9 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/premium-forum-plugin-versions.user.js
10 | // @grant none
11 | // ==/UserScript==
12 |
13 | (function() {
14 | 'use strict';
15 |
16 | /**
17 | * Defining our plugin version history
18 | * When a new release is out then:
19 | * - update script version number in the header
20 | * - make a copy of the last line
21 | * - adjust the starting number
22 | * - adjust the note: show|last (usually show the last 5 versions)
23 | * - add the new plugin version numbers
24 | * - if it is a new version compared to last release, add 'x' at the end, like '4.6.19x'
25 | * - if it is a hotfix, then creating a new line is not needed, just update the version number in the last line (See Event Tickets (eti) in line 12)
26 | */
27 | var pluginHistory = {
28 | 0: { note: "", date: "", name: "", tec: "", pro: "", eti: "", etp: "", ebt: "", cev: "", ctx: "", fib: "", apm: "", iwp: "", woo: "", edd: "" },
29 | 1: { note: "", date: "Jan 7", name: "M18.01", tec: "4.6.9x", pro: "4.4.21", eti: "4.6.3", etp: "4.6.2", ebt: "4.4.9", cev: "4.5.8", ctx: "4.5.3", fib: "4.5.2", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
30 | 2: { note: "", date: "Jan 22", name: "M18.02", tec: "4.6.10x", pro: "4.4.22x", eti: "4.6.3", etp: "4.6.2", ebt: "4.4.9", cev: "4.5.8", ctx: "4.5.3", fib: "4.5.3x", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
31 | 3: { note: "", date: "Feb 14", name: "M18.03", tec: "4.6.11.1x", pro: "4.4.23x", eti: "4.6.3.1x", etp: "4.6.2", ebt: "4.4.9", cev: "4.5.9x", ctx: "4.5.3", fib: "4.5.3", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
32 | 4: { note: "", date: "Mar 8", name: "M18.04", tec: "4.6.12x", pro: "4.4.24.2x", eti: "4.6.3.1", etp: "4.6.2", ebt: "4.4.9", cev: "4.5.9", ctx: "4.5.3", fib: "4.5.4x", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
33 | 5: { note: "", date: "Mar 13", name: "TC", tec: "4.6.12", pro: "4.4.24.2", eti: "4.7x", etp: "4.7x", ebt: "4.4.9", cev: "4.5.9", ctx: "4.5.3", fib: "4.5.4", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
34 | 6: { note: "", date: "Mar 28", name: "M18.05", tec: "4.6.13x", pro: "4.4.24.2", eti: "4.7.1x", etp: "4.7.1x", ebt: "4.4.9", cev: "4.5.10x", ctx: "4.5.4x", fib: "4.5.4", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
35 | 7: { note: "", date: "Apr 4", name: "M18.06", tec: "4.6.14.1x", pro: "4.4.25x", eti: "4.7.2x", etp: "4.7.2x", ebt: "4.4.9", cev: "4.5.11x", ctx: "4.5.4", fib: "4.5.5x", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
36 | 8: { note: "", date: "May 9", name: "M18.07", tec: "4.6.15x", pro: "4.4.26x", eti: "4.7.2", etp: "4.7.2", ebt: "4.4.9", cev: "4.5.11", ctx: "4.5.4", fib: "4.5.5", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
37 | 9: { note: "", date: "May 16", name: "TEC", tec: "4.6.16x", pro: "4.4.26", eti: "4.7.2", etp: "4.7.2", ebt: "4.4.9", cev: "4.5.11", ctx: "4.5.4", fib: "4.5.5", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
38 | 10: { note: "", date: "May 29", name: "M18.08", tec: "4.6.17x", pro: "4.4.27x", eti: "4.7.3.1x", etp: "4.7.3x", ebt: "4.4.9", cev: "4.5.12x", ctx: "4.5.4", fib: "4.5.6x", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
39 | 11: { note: "", date: "Jun 4", name: "ETR", tec: "4.6.18x", pro: "4.4.27", eti: "4.7.3.1", etp: "4.7.3", ebt: "4.5x", cev: "4.5.12", ctx: "4.5.4", fib: "4.5.6", apm: "4.4", iwp: "1.0.2", woo: "", edd: "" },
40 | 12: { note: "", date: "Jun 20", name: "M18.09", tec: "4.6.19x", pro: "4.4.28x", eti: "4.7.4.1x", etp: "4.7.4x", ebt: "4.5.1x", cev: "4.5.12", ctx: "4.5.4", fib: "4.5.6", apm: "4.4", iwp: "1.0.2", woo: "3.4.3", edd: "2.9.3" },
41 | 13: { note: "show", date: "Jul 9", name: "M18.10", tec: "4.6.20.1x", pro: "4.4.29.2x", eti: "4.7.5.1x", etp: "4.7.5x", ebt: "4.5.1", cev: "4.5.12", ctx: "4.5.4", fib: "4.5.6", apm: "4.4", iwp: "1.0.2", woo: "3.4.3", edd: "2.9.3" },
42 | 14: { note: "show", date: "Aug 1", name: "M18.11", tec: "4.6.21x", pro: "4.4.30.1x", eti: "4.7.6x", etp: "4.7.6x", ebt: "4.5.2x", cev: "4.5.13x", ctx: "4.5.5x", fib: "4.5.7x", apm: "4.4", iwp: "1.0.2", woo: "3.4.4", edd: "2.9.6" },
43 | 15: { note: "show", date: "Aug 22", name: "M18.12", tec: "4.6.22.1x", pro: "4.4.31x", eti: "4.8x", etp: "4.8x", ebt: "4.5.2", cev: "4.5.13.1x", ctx: "4.5.6x", fib: "4.5.7", apm: "4.4", iwp: "1.0.2", woo: "3.4.4", edd: "2.9.6" },
44 | 16: { note: "show", date: "Sep 12", name: "M18.13", tec: "4.6.23x", pro: "4.4.32x", eti: "4.8.1x", etp: "4.8.1x", ebt: "4.5.3x", cev: "4.5.13.1", ctx: "4.5.6", fib: "4.5.8x", apm: "4.4", iwp: "1.0.2", woo: "3.4.5", edd: "2.9.7" },
45 | 17: { note: "last", date: "Oct 3", name: "M18.14", tec: "4.6.24.1x", pro: "4.4.33x", eti: "4.8.2.1x", etp: "4.8.2x", ebt: "4.5.4x", cev: "4.5.13.1", ctx: "4.5.6", fib: "4.5.8", apm: "4.4", iwp: "1.0.2", woo: "3.4.5", edd: "2.9.8" },
46 | };
47 |
48 | var pluginNames = ['tec', 'pro', 'eti', 'etp', 'ebt', 'cev', 'ctx', 'fib', 'apm', 'iwp'];
49 |
50 | /**
51 | * Defining our plugins
52 | * (Probably "version" here is not needed.)
53 | */
54 | var pluginVersions = {
55 | tec: { name: '(The Events Calendar version )(.{0,})( by )(){0,1}(Modern Tribe, Inc.)', namelength: '28', version: '', curr: '#currtecver', user: '#usertecver' },
56 | pro: { name: '(Events Calendar PRO version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '28', version: '', curr: '#currprover', user: '#userprover' },
57 | eti: { name: '(Event Tickets version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '22', version: '', curr: '#curretiver', user: '#useretiver' },
58 | etp: { name: '(Event Tickets Plus version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '27', version: '', curr: '#curretpver', user: '#useretpver' },
59 | ebt: { name: '(The Events Calendar: Eventbrite Tickets version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '48', version: '', curr: '#currebtver', user: '#userebtver' },
60 | cev: { name: '(The Events Calendar: Community Events version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '46', version: '', curr: '#currcevver', user: '#usercevver' },
61 | ctx: { name: '(The Events Calendar: Community Events Tickets version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '54', version: '', curr: '#currctxver', user: '#userctxver' },
62 | fib: { name: '(The Events Calendar: Filter Bar version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '40', version: '', curr: '#currfibver', user: '#userfibver' },
63 | apm: { name: '(Advanced Post Manager version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '30', version: '', curr: '#currapmver', user: '#userapmver' },
64 | iwp: { name: '(Image Widget Plus version )(.{0,})( by )( ){0,1}(Modern Tribe, Inc.)', namelength: '26', version: '', curr: '#curriwpver', user: '#useriwpver' },
65 | woo: { name: '(WooCommerce version )(.{0,})( by )( ){0,1}(Automattic)', namelength: '20', version: '', curr: '#currecmver', user: '#userecmver' },
66 | edd: { name: '(Easy Digital Downloads version )(.{0,})( by )( ){0,1}(Easy Digital Downloads)', namelength: '31', version: '', curr: '#currecmver', user: '#userecmver' }
67 | };
68 |
69 | /**
70 | * i = contains the sysinfo from the sysinfo box
71 | * k = counter
72 | * inReply = returns a positive if sysinfo is submitted/found in a reply
73 | * replyHtml = stores the HTML of a reply in the cycle
74 | * ecmUsed = stores the string of the eCommerce solution used by the client (woo|edd)
75 | * userEcmVer = stores the version number of the eCommerce solution used by the client
76 | */
77 | var i, k, inReply, replyHtml, ecmUsed = "-", userEcmVer = "-";
78 |
79 | /**
80 | * Getting the system info in a string
81 | * fullstats = contains the full html string of the sysinfo box
82 | */
83 | i = document.getElementsByClassName( 'system-info' );
84 |
85 | if ( i.length > 0 ) {
86 | var fullstats;
87 | fullstats = i[0].innerHTML;
88 | }
89 |
90 | // console.log("FS1" + fullstats);
91 |
92 | /**
93 | * If system information is submitted in a reply, take that instead
94 | * Go from the last reply forward
95 | * replies = array containing all the replies
96 | */
97 | var replies = document.getElementsByClassName( 'bbp-reply-content' );
98 | for ( k = replies.length-1; k >= 0; k-- ) {
99 | replyHtml = replies[k].innerHTML;
100 | // Search for the string "Home URL" case insensitive
101 | inReply = replyHtml.search( /home url/i );
102 | if ( inReply >= 0 ) {
103 | fullstats = replyHtml;
104 | break;
105 | }
106 | }
107 |
108 | //console.log("FS2" + fullstats);
109 | //console.log( typeof fullstats );
110 |
111 | // If there is no sysinfo, then stop the execution of the scirpt
112 |
113 | if ( typeof fullstats == 'undefined' ) return;
114 |
115 | /**
116 | * j = counter
117 | * pname = plugin name + plugin name length = start of version number
118 | * pby = start of by | end of version number
119 | * pver = version number string
120 | * result = ???
121 | */
122 | var j, pname, pby, pver;
123 | var result = "";
124 |
125 | /**
126 | * Going through the array and checking for our plugins in system info
127 | * key = the short name of the plugin
128 | */
129 | for( var key in pluginVersions ) {
130 |
131 | //console.log( 'key: ' + key );
132 |
133 | // This is the for-cycle for named arrays
134 | if ( pluginVersions.hasOwnProperty( key ) ) {
135 |
136 | // If plugin name is found in the sysinfo ...
137 | pname = fullstats.search( pluginVersions[key].name );
138 |
139 | if ( pname != -1 ) {
140 | // Starting position of version number = start of plugin name + plugin name length
141 | pname = parseInt( fullstats.search( pluginVersions[key].name ) ) + parseInt( pluginVersions[key].namelength );
142 | //console.log("p2: " + pname);
143 | // Starting position of by (after the plugin name) | end of version number
144 | pby = fullstats.indexOf( "by", pname );
145 | //console.log("pby: " + pby);
146 | // Get version number only
147 | pver = fullstats.substring( pname, pby );
148 | //console.log("pver: " + pver);
149 | // Trim it
150 | pluginVersions[key].version = pver.trim();
151 | // Which eCommerce used?
152 | if ( key == "woo" || key == "edd" ) {
153 | ecmUsed = key;
154 | }
155 | }
156 | // If plugin name is not found in the sysinfo
157 | else {
158 | pluginVersions[key].version = "-";
159 | }
160 | }
161 | }
162 |
163 | /**
164 | * Table of the plugin versions
165 | */
166 | var htmlstring = '';
252 |
253 | // Formatting
254 | $( '#wp-admin-bar-top-secondary' ).after( htmlstring );
255 | $( '#plugin-versions' ).css({ 'position': 'fixed', 'bottom': '0', 'right': '150px', 'background-color': 'rgb(35, 40, 45)', 'color': '#eee' });
256 | $( '.versions td' ).css({ 'line-height': '1.5em !important' });
257 | $( '.version-number' ).css({ 'font-weight': 'bold' });
258 | $( '.row' ).css({ 'display': 'none', 'text-align': 'center' });
259 | $( '.alwayson' ).css({ 'display': 'table-row' });
260 |
261 | // Handle hover
262 | if ( document.getElementById( 'plugin-versions' ) != null ) {
263 | document.getElementById( 'plugin-versions' ).addEventListener( 'mouseover', showRows );
264 | document.getElementById( 'plugin-versions' ).addEventListener( 'mouseout', hideRows );
265 | }
266 |
267 | // Compare current and user, and color it
268 | for( var plugin in pluginVersions ) {
269 | // This is the for-cycle for named arrays
270 | if ( pluginVersions.hasOwnProperty( plugin ) && pluginVersions[plugin].version != "-" ) {
271 | if ( $( pluginVersions[plugin].curr ).html() == pluginVersions[plugin].version ) {
272 | $( pluginVersions[plugin].user ).css({ 'color': '#2dd39c', 'font-weight': 'bold' });
273 | }
274 | else {
275 | $( pluginVersions[plugin].user ).css({ 'color': '#e4554a', 'font-weight': 'bold' });
276 | }
277 | }
278 | }
279 |
280 | /* Hover/unhover actions */
281 | function showRows() {
282 | $( '.row' ).css({ 'display': 'table-row ' });
283 | }
284 | function hideRows() {
285 | $( '.row' ).css({ 'display': 'none' });
286 | $( '.alwayson' ).css({ 'display': 'table-row' });
287 | }
288 |
289 | })();
290 |
--------------------------------------------------------------------------------
/premium-forum/premium-forum-private-topic.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Premium Forum Extras - Private topic
3 | // @namespace https://theeventscalendar.com/
4 | // @version 0.8
5 | // @description Shows a red label if topic is private
6 | // @author Andras Guseo
7 | // @include https://theeventscalendar.com/wp-admin/post.php?*
8 | // @match https://theeventscalendar.com/wp-admin/post.php?*
9 | // @grant none
10 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/premium-forum-private-topic.user.js
11 | // @run-at document-idle
12 |
13 | // ==/UserScript==
14 |
15 | (function() {
16 | 'use strict';
17 |
18 | /**
19 | * Show a red label if the topic is private
20 | */
21 | if ( document.getElementById( 'mark_private' ) !== null ) {
22 | var isPrivate = document.getElementById( 'mark_private' ).checked;
23 | var top = document.getElementById( 'bbps_extra' );
24 | var replybox = document.getElementById( 'new-reply-0' );
25 | var convo = document.getElementById( 'bbps_conversation' );
26 |
27 | if ( true === isPrivate ) {
28 |
29 | convo.style.border = '2px solid red';
30 | var privateTopic = document.createElement( 'div' );
31 | privateTopic.id = 'privateTopic';
32 | privateTopic.innerHTML = 'This is a fully private topic.';
33 | privateTopic.style.fontWeight = 'bold';
34 | privateTopic.style.color = 'red';
35 |
36 | replybox.insertBefore( privateTopic, replybox.firstChild );
37 | var privateTopic2 = privateTopic.cloneNode( true );
38 | top.parentNode.insertBefore( privateTopic2, top.nextSibling );
39 | }
40 | }
41 |
42 | })();
--------------------------------------------------------------------------------
/premium-forum/premium-forum-reformat-sysinfo.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Premium Forum Extras - Reformat Sysinfo
3 | // @namespace https://theeventscalendar.com/
4 | // @version 0.1
5 | // @description Reformat system information if it comes through without line breaks. If you still see improvement areas, let me know.
6 | // @author Andras Guseo
7 | // @include https://theeventscalendar.com/wp-admin/post.php?*
8 | // @match https://theeventscalendar.com/wp-admin/post.php?*
9 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/premium-forum-reformat-sysinfo.user.js
10 | // @grant none
11 | // ==/UserScript==
12 |
13 | // @to-test https://theeventscalendar.com/wp-admin/post.php?post=1597502&action=edit
14 | // @to-test https://theeventscalendar.com/wp-admin/post.php?post=1597502&action=edit
15 |
16 | (function() {
17 | 'use strict';
18 |
19 | // Get sysinfo
20 | var s = document.getElementsByClassName( 'system-info' );
21 |
22 | if ( s.length > 0 ) {
23 | var fullstats = s[0].innerHTML;
24 |
25 | // Normal case #1
26 | if ( fullstats.startsWith('') ) {
27 | //console.log( 'p' )
28 | }
29 | // Normal case #2
30 | else if ( fullstats.startsWith('
&])/g, ' $&' );
38 | // Split plugin list into new lines
39 | // (exclude also Google Analytics Dashboard for WP (GADWP))
40 | fullstats = fullstats.replace( /[^(GADWP)]\)/g, '$& ' );
41 | // Split WordPress version into new line
42 | // and skip plugins that end with "for WordPress version x.y"
43 | fullstats = fullstats.replace( /(wordpress version)\s([4-5])([0-9\.]{2,5})/ig, ' $& ' );
44 | // Split 'Theme' into new line
45 | // And exclude iThemes, ThemeFusions, theme-fusion etc.
46 | fullstats = fullstats.replace( /theme[^\w\-]/ig, ' $& ' );
47 |
48 | var titles = [
49 | 'Home URL',
50 | 'Site URL',
51 | 'Site Language',
52 | 'Character Set',
53 | 'Name',
54 | 'Email',
55 | 'Install keys',
56 | 'Permalink Structure',
57 | 'PHP version',
58 | 'Server',
59 | 'SAPI',
60 | 'Plugins',
61 | 'Network Plugins',
62 | 'MU Plugins',
63 | 'Multisite',
64 | 'Settings',
65 | 'Community Add',
66 | 'Community List',
67 | 'Community Options',
68 | 'WP Timezone',
69 | 'WP GMT Offset',
70 | 'Default PHP Timezone',
71 | 'WP Date Format',
72 | 'WP Time Format',
73 | 'Week Starts On',
74 | 'Common Library Dir',
75 | 'Common Library Version',
76 | ];
77 |
78 | // Split section headers into new lines
79 | for ( var t = 0; t < titles.length; t++ ) {
80 | fullstats = fullstats.replace( titles[t], ' ' + titles[t].toUpperCase() + ' ' );
81 | fullstats = fullstats.replace( titles[t].toUpperCase(), ' ' + titles[t].toUpperCase() + ' ' );
82 | }
83 |
84 | // Replace double new lines to single
85 | fullstats = fullstats.replace( /( )(\s){0,1}( )/ig, ' ' );
86 |
87 | // Remove blank lines from beginning
88 | fullstats = fullstats.replace( /(.*)home url/ig, 'HOME URL' );
89 |
90 | // Replace sysinfo
91 | s[0].innerHTML = fullstats;
92 |
93 | // Color red to signal change
94 | $( '.when-collapsed' ).css({ 'color': 'red' });
95 | $( '.when-expanded' ).css({ 'color': 'red' });
96 | } // else {
97 | }
98 |
99 | })();
100 |
--------------------------------------------------------------------------------
/premium-forum/premium-forum-thread-list-colors.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Premium Forum Extras - Thread List Colors
3 | // @namespace https://theeventscalendar.com/
4 | // @version 0.1
5 | // @description Colors thead list based on urgency: Critical (past a 24h): red; Overdue (past 20h): orange; Resolved: green
6 | // @author Andras Guseo
7 | // @include https://theeventscalendar.com/wp-admin/edit.php?post_type=topic&page=tribe-support-queues*
8 | // @include https://theeventscalendar.com/wp-admin/edit.php?page=tribe-support-queues&post_type=topic*
9 | // @match https://theeventscalendar.com/wp-admin/edit.php?post_type=topic&page=tribe-support-queues*
10 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/premium-forum-thread-list-colors.user.js
11 | // @grant none
12 | // ==/UserScript==
13 |
14 | (function() {
15 | 'use strict';
16 |
17 | var resolvedRows, criticalRows, i;
18 | criticalRows = document.getElementsByClassName( 'column-activity' );
19 | resolvedRows = document.getElementsByClassName( 'column-status' );
20 |
21 | for ( i = 0; i < criticalRows.length; i++ ) {
22 |
23 | /* Resolved - green */
24 | var replyHtml = resolvedRows[i].innerHTML;
25 | var isResolved = replyHtml.search( /resolved/gi );
26 | if ( isResolved >= 0 ) {
27 | resolvedRows[i].parentNode.style.backgroundColor = 'lightgreen';
28 | continue;
29 | }
30 |
31 | /* Overdue - past 20 hours - orange */
32 | replyHtml = criticalRows[i].innerHTML;
33 | var isOverdue = replyHtml.search( /^(2)([0-9]{1})h/g );
34 | if ( isOverdue >= 0 ) {
35 | criticalRows[i].parentNode.style.backgroundColor = '#f4af49';
36 | continue;
37 | }
38 |
39 | /* Critical - past 24 hours - red */
40 | var isCritical = replyHtml.search( /^([0-9]{1,2})(D|W|M|Y)/g );
41 | if ( isCritical >= 0 ) {
42 | criticalRows[i].parentNode.style.backgroundColor = 'rgba(228, 85, 74,0.3)';
43 | continue;
44 | }
45 |
46 | }
47 |
48 | })();
49 |
--------------------------------------------------------------------------------
/premium-forum/premium-forum-users-licenses.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Premium Forum Extras - Show user's licenses
3 | // @namespace https://theeventscalendar.com/
4 | // @version 0.8
5 | // @description Show the user's licenses at the bottom right
6 | // @author Andras Guseo
7 | // @include https://theeventscalendar.com/wp-admin/post.php?*
8 | // @match https://theeventscalendar.com/wp-admin/post.php?*
9 | // @grant none
10 | // @downloadURL https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/premium-forum-users-licenses.user.js
11 | // @run-at document-idle
12 |
13 | // ==/UserScript==
14 |
15 | (function() {
16 | 'use strict';
17 |
18 | /**
19 | * Show the user's licenses at the bottom right
20 | */
21 | $( '.licenses' ).clone().appendTo( '#poststuff' ).addClass( 'newLicenses' );
22 |
23 | var cssLicenses = 'li.newLicenses { position: fixed; bottom: 80px; right: 20px; font-size: 85%; list-style: none; tight: 20px; z-index: -1; } li.newLicenses .nonactive { color: #A00000; } ',
24 | head = document.head || document.getElementsByTagName( 'head' )[0],
25 | style = document.createElement( 'style' );
26 |
27 | style.type = 'text/css';
28 | if ( style.styleSheet ) {
29 | style.styleSheet.cssText = 'css';
30 | }
31 | else {
32 | style.appendChild( document.createTextNode( cssLicenses ) );
33 | }
34 |
35 | head.appendChild( style );
36 |
37 | })();
--------------------------------------------------------------------------------
/uservoice/README.md:
--------------------------------------------------------------------------------
1 | # UserVoice Scripts
2 |
3 | ## `uservoice.user.js`
4 |
5 | Tweaks the UserVoice UI by throwing out portions of the interface that we don't care about; resizing stuff; and other small tweaks.
6 |
--------------------------------------------------------------------------------
/uservoice/uservoice.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Uservoice Overrides
3 | // @namespace http://tampermonkey.net/
4 | // @version 0.1
5 | // @description Minimize some UI crap
6 | // @author Matthew Batchelder
7 | // @include /https:\/\/tribe.uservoice.com\/admin\/.*/
8 | // @grant none
9 | // ==/UserScript==
10 | /* jshint -W097 */
11 | 'use strict';
12 |
13 | var mt_uservoice = {};
14 |
15 | ( function( $, my ) {
16 | 'use strict';
17 |
18 | my.init = function() {
19 | var $graph = $( '.cols-5.mb2 + .content-box' );
20 | var $graph_body = $graph.find( '.content-box-body' );
21 | var $graph_header = $graph.find( 'header' );
22 | var $activity_stream = $( '#activity-stream' );
23 |
24 | $graph.on( 'click', 'header', function( e ) {
25 | if ( $graph_body.is( ':visible' ) ) {
26 | $graph_body.hide();
27 | } else {
28 | $graph_body.show();
29 | }
30 | } );
31 |
32 | $activity_stream.find( '.hfeed .hentry:not(.supported) h3.entry-title a' ).each( function() {
33 | var $el = $( this );
34 | var href = $el.attr( 'href' );
35 | var $article = $el.closest( 'article' );
36 | var $footer = $article.find( 'footer' );
37 |
38 | var jqxhr = $.get( href );
39 |
40 | jqxhr.done( function( data ) {
41 | var $html = $( data );
42 | var breadcrumbs = $html.find( '.breadcrumbs' ).html();
43 | if ( breadcrumbs ) {
44 | $footer.prepend( '' + breadcrumbs + ' @
' );
45 | }
46 |
47 | var $chicklet = $html.find( '.vote_chicklet .chicklet' );
48 | var votes = $chicklet.find( 'strong' ).html();
49 |
50 | if ( votes ) {
51 | $footer.prepend( '' + votes + ' Votes
' );
52 | }
53 | } );
54 | } );
55 |
56 | $activity_stream.addClass( 'hide-supported' );
57 | $activity_stream.find( '.activity-stream-header-table thead th[data-object-type="supported"]' ).addClass( 'is-disabled' );
58 |
59 | $( '.blog-posts' ).closest( '.cols-span-1' ).hide();
60 |
61 | $( '.activity-stream-header-table' ).each( function() {
62 | var $table = $( this );
63 | $table.find( 'tbody td' ).each( function() {
64 | var $cell = $( this );
65 | if ( '0' === $cell.html() ) {
66 | $cell.hide();
67 | $table.find( 'thead th[data-object-type="' + $cell.data( 'object-type' ) + '"]' ).hide();
68 | }
69 | } );
70 | } );
71 |
72 | my.build_styles();
73 | };
74 |
75 | my.build_styles = function() {
76 | $( 'head' ).append( '' );
77 | my.$styles = $( document.getElementById( 'bork-uservoice-styles' ) );
78 |
79 | my.$styles.html( [
80 | '#nav {',
81 | ' opacity: 0.1;',
82 | ' transition: all 0.5s ease;',
83 | '}',
84 | '#nav:hover {',
85 | ' opacity: 1;',
86 | '}',
87 | '.cols-5.mb2 + .content-box .content-box-body {',
88 | ' display: none;',
89 | '}',
90 | '.cols-5.mb2 + .content-box > header {',
91 | ' cursor: pointer;',
92 | '}',
93 | '.placard-value {',
94 | ' font-size: 19px;',
95 | '}',
96 | '#activity-stream .tribe-breadcrumbs {',
97 | ' float: left;',
98 | ' line-height: 14px;',
99 | ' margin-right: .25rem;',
100 | '}',
101 | '.tribe-votes {',
102 | ' background: #fff;',
103 | ' border: 1px solid #b3b3b3;',
104 | ' border-radius: 5px;',
105 | ' padding: .25rem;',
106 | ' position: absolute;',
107 | ' right: 55px;',
108 | ' top: 12px;',
109 | '}',
110 | '#activity-stream .hentry {',
111 | ' position: relative;',
112 | '}',
113 | '' ].join( "\n" ) );
114 | };
115 |
116 |
117 | $( function() {
118 | my.init();
119 | } );
120 | } )( jQuery, mt_uservoice );
121 |
--------------------------------------------------------------------------------
/waitForKeyElements.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Source: https://gist.github.com/BrockA/2625891
3 | * Use this in your TamperMonkey script header:
4 | // @require https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js
5 | // @require https://raw.githubusercontent.com/moderntribe/tampermonkey-scripts/master/waitForKeyElements.js
6 | */
7 |
8 | /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
9 | that detects and handles AJAXed content.
10 |
11 | Usage example:
12 |
13 | waitForKeyElements (
14 | "div.comments"
15 | , commentCallbackFunction
16 | );
17 |
18 | //--- Page-specific function to do what we want when the node is found.
19 | function commentCallbackFunction (jNode) {
20 | jNode.text ("This comment changed by waitForKeyElements().");
21 | }
22 |
23 | IMPORTANT: This function requires your script to have loaded jQuery.
24 | */
25 | function waitForKeyElements (
26 | selectorTxt, /* Required: The jQuery selector string that
27 | specifies the desired element(s).
28 | */
29 | actionFunction, /* Required: The code to run when elements are
30 | found. It is passed a jNode to the matched
31 | element.
32 | */
33 | bWaitOnce, /* Optional: If false, will continue to scan for
34 | new elements even after the first match is
35 | found.
36 | */
37 | iframeSelector /* Optional: If set, identifies the iframe to
38 | search.
39 | */
40 | ) {
41 | var targetNodes, btargetsFound;
42 |
43 | if (typeof iframeSelector == "undefined")
44 | targetNodes = jQuery(selectorTxt);
45 | else
46 | targetNodes = jQuery(iframeSelector).contents ()
47 | .find (selectorTxt);
48 |
49 | if (targetNodes && targetNodes.length > 0) {
50 | btargetsFound = true;
51 | /*--- Found target node(s). Go through each and act if they
52 | are new.
53 | */
54 | targetNodes.each ( function () {
55 | var jThis = jQuery(this);
56 | var alreadyFound = jThis.data ('alreadyFound') || false;
57 |
58 | if (!alreadyFound) {
59 | //--- Call the payload function.
60 | var cancelFound = actionFunction (jThis);
61 | if (cancelFound)
62 | btargetsFound = false;
63 | else
64 | jThis.data ('alreadyFound', true);
65 | }
66 | } );
67 | }
68 | else {
69 | btargetsFound = false;
70 | }
71 |
72 | //--- Get the timer-control variable for this selector.
73 | var controlObj = waitForKeyElements.controlObj || {};
74 | var controlKey = selectorTxt.replace (/[^\w]/g, "_");
75 | var timeControl = controlObj [controlKey];
76 |
77 | //--- Now set or clear the timer as appropriate.
78 | if (btargetsFound && bWaitOnce && timeControl) {
79 | //--- The only condition where we need to clear the timer.
80 | clearInterval (timeControl);
81 | delete controlObj [controlKey]
82 | }
83 | else {
84 | //--- Set a timer, if needed.
85 | if ( ! timeControl) {
86 | timeControl = setInterval ( function () {
87 | waitForKeyElements ( selectorTxt,
88 | actionFunction,
89 | bWaitOnce,
90 | iframeSelector
91 | );
92 | },
93 | 300
94 | );
95 | controlObj [controlKey] = timeControl;
96 | }
97 | }
98 | waitForKeyElements.controlObj = controlObj;
99 | }
--------------------------------------------------------------------------------