Skip to content

Instantly share code, notes, and snippets.

@marcofugaro
Last active September 14, 2020 15:16
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save marcofugaro/e3d6f154891440629529c910a2798c99 to your computer and use it in GitHub Desktop.
Save marcofugaro/e3d6f154891440629529c910a2798c99 to your computer and use it in GitHub Desktop.
Paste this in the console of your github profile page and start drawing on your contributions!

Made to work along with gitfiti, you can draw with this tool and then export the data and use it with gitfiti.

##Usage

  1. Paste the code in the console of any github profile page which has the contributions graph
  2. Start drawing! Left click to increment the color, right click to clear the cell, clear button to clear everything! Dragging also works!
  3. Once you're happy you can export the data by typing ContributionsDraw.export() into the console.
  4. Or if you want you can also import existing data with ContributionsDraw.import([...])

NOTE: This is developed for and tested in Chrome, other browsers might not support yet some ES6 stuff, you might need to compile it to ES5.

const ContributionsDraw = {
colors: [
'#eeeeee',
'#d6e685',
'#8cc665',
'#44a340',
'#1e6823',
],
container: document.querySelector('.js-calendar-graph-svg'),
clearButton: null,
weeksOfTheYear: null,
isLeftClickDown: false,
isRightClickDown: false,
init() {
this._addClearButton();
this.addEventListeners();
[...this.container.querySelectorAll('.day')].forEach((el) => {
el.style.cursor = 'pointer';
});
// disable dragging and selection
this.container.style.cssText = '-webkit-user-select: none; -webkit-user-drag: none;';
// cache the contribution columns
this.weeksOfTheYear = [...this.container.querySelector('g').childNodes].filter((el) => el.nodeName === 'g');
},
addEventListeners() {
this.container.addEventListener('mousedown', (e) => {
switch (event.which) {
case 1:
this.isLeftClickDown = true;
break;
case 3:
this.isRightClickDown = true;
break;
}
if (e.target.classList.contains('day')) {
this._increaseColour(e.target);
}
});
this.container.addEventListener('mouseup', (e) => {
this.isLeftClickDown = false;
this.isRightClickDown = false;
});
this.container.addEventListener('mouseover', (e) => {
e.preventDefault();
e.stopPropagation();
if (this.isLeftClickDown) {
this._increaseColour(e.target);
}
if (this.isRightClickDown) {
this._clearCell(e.target);
}
});
// prevent github default behaviour
this.container.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
});
this.container.addEventListener('contextmenu', (e) => {
e.preventDefault();
e.stopPropagation();
if(e.target.classList.contains('day')) {
this._clearCell(e.target);
}
});
this.clearButton.addEventListener('click', this.clear.bind(this));
},
_increaseColour(el) {
const currentColor = el.getAttribute('fill');
const index = this.colors.indexOf(currentColor);
const nextIndex = index === this.colors.length - 1 ? this.colors.length - 1 : index + 1;
el.setAttribute('fill', this.colors[nextIndex]);
},
_clearCell(el) {
el.setAttribute('fill', this.colors[0]);
},
_addClearButton() {
this.clearButton = document.createElement("button");
this.clearButton.textContent = 'Clear';
this.clearButton.className = 'btn btn-sm';
this.clearButton.style.float = 'right';
this.clearButton.style.marginLeft = '10px';
const buttonContainer = this.container.parentNode.nextElementSibling;
buttonContainer.insertBefore(this.clearButton, buttonContainer.firstChild);
},
clear() {
[...this.container.querySelectorAll('.day')].forEach((el) => {
el.setAttribute('fill', this.colors[0]);
});
},
import(data) {
data.forEach((el, dayOfTheWeek) => {
el.forEach((value, week) => {
const cell = [...this.weeksOfTheYear[week].querySelectorAll('.day')][dayOfTheWeek];
if(cell) {
cell.setAttribute('fill', this.colors[value]);
}
});
});
},
export() {
const daysOfTheWeek = [...this.weeksOfTheYear[0].querySelectorAll('.day')];
const data = daysOfTheWeek.map((el, i) => {
return [...this.weeksOfTheYear].map((week) => {
// check if it might be one of the remaining days of the current week
if (i >= week.children.length) {
return 0;
}
const color = week.children[i].getAttribute('fill');
return this.colors.indexOf(color);
});
});
console.log(JSON.stringify(data));
},
};
ContributionsDraw.init();
@BSBussell
Copy link

Sorry if this is an old or dead tool but I was trying to use this and found that the code wasn't working with Firefox. A quick scan and I found the issue to be with the event.which, which should be e.which. here's the fixed code if anyone’s having trouble getting this to work

const ContributionsDraw = {
  colors: [
    '#eeeeee',
    '#d6e685',
    '#8cc665',
    '#44a340',
    '#1e6823',
  ],
  container: document.querySelector('.js-calendar-graph-svg'),
  clearButton: null,
  weeksOfTheYear: null,
  isLeftClickDown: false,
  isRightClickDown: false,

  init() {
    this._addClearButton();

    this.addEventListeners();

    [...this.container.querySelectorAll('.day')].forEach((el) => {
      el.style.cursor = 'pointer';
    });

    // disable dragging and selection
    this.container.style.cssText = '-webkit-user-select: none; -webkit-user-drag: none;';

    // cache the contribution columns
    this.weeksOfTheYear = [...this.container.querySelector('g').childNodes].filter((el) => el.nodeName === 'g');
  },

  addEventListeners() {
    this.container.addEventListener('mousedown', (e) => {
      switch (e.which) {
        case 1:
          this.isLeftClickDown = true;
          break;
        case 3:
          this.isRightClickDown = true;
          break;
      }

      if (e.target.classList.contains('day')) {
        this._increaseColour(e.target);
      }
    });

    this.container.addEventListener('mouseup', (e) => {
      this.isLeftClickDown = false;
      this.isRightClickDown = false;
    });

    this.container.addEventListener('mouseover', (e) => {
      e.preventDefault();
      e.stopPropagation();

      if (this.isLeftClickDown) {
        this._increaseColour(e.target);
      }
      if (this.isRightClickDown) {
        this._clearCell(e.target);
      }
    });

    // prevent github default behaviour
    this.container.addEventListener('click', (e) => {
      e.preventDefault();
      e.stopPropagation();
    });

    this.container.addEventListener('contextmenu', (e) => {
      e.preventDefault();
      e.stopPropagation();

      if(e.target.classList.contains('day')) {
        this._clearCell(e.target);
      }
    });

    this.clearButton.addEventListener('click', this.clear.bind(this));
  },

  _increaseColour(el) {
    const currentColor = el.getAttribute('fill');
    const index = this.colors.indexOf(currentColor);
    const nextIndex = index === this.colors.length - 1 ? this.colors.length - 1 : index + 1;

    el.setAttribute('fill', this.colors[nextIndex]);
  },

  _clearCell(el) {
    el.setAttribute('fill', this.colors[0]);
  },

  _addClearButton() {
    this.clearButton = document.createElement("button");
    this.clearButton.textContent = 'Clear';
    this.clearButton.className = 'btn btn-sm';
    this.clearButton.style.float = 'right';
    this.clearButton.style.marginLeft = '10px';

    const buttonContainer = this.container.parentNode.nextElementSibling;

    buttonContainer.insertBefore(this.clearButton, buttonContainer.firstChild);
  },

  clear() {
    [...this.container.querySelectorAll('.day')].forEach((el) => {
      el.setAttribute('fill', this.colors[0]);
    });
  },

  import(data) {
    data.forEach((el, dayOfTheWeek) => {
      el.forEach((value, week) => {
        const cell = [...this.weeksOfTheYear[week].querySelectorAll('.day')][dayOfTheWeek];

        if(cell) {
          cell.setAttribute('fill', this.colors[value]);
        }
      });
    });
  },

  export() {
    const daysOfTheWeek = [...this.weeksOfTheYear[0].querySelectorAll('.day')];

    const data = daysOfTheWeek.map((el, i) => {
      return [...this.weeksOfTheYear].map((week) => {
        // check if it might be one of the remaining days of the current week
        if (i >= week.children.length) {
          return 0;
        }

        const color = week.children[i].getAttribute('fill');
        return this.colors.indexOf(color);
      });
    });

    console.log(JSON.stringify(data));
  },
};

ContributionsDraw.init();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment