-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exposing Global Variables #4
Comments
All variables have been encapsulated in this code below: class Contextify {
constructor(buttons, theme, container) {
this.contextEvent = 'contextmenu';
this.keyPressEvent = 'keydown';
this.mouseClickEvent = 'mousedown';
this.mouseEnterEvent = 'mouseenter';
this.mouseLeaveEvent = 'mouseleave';
this.clickAttribute = 'click';
this.childAttribute = 'child';
this.typeAttribute = 'type';
this.iconAttribute = 'icon';
this.textAttribute = 'text';
this.hotkeyAttribute = 'hotkey';
this.colourAttribute = 'colour';
this.textColourAttribute = 'textcolour';
this.enabledAttribute = 'enabled';
this.childTimeout = 200;
this.container = (typeof container === 'undefined') ? document.body : container;
this.parent = null;
this.active = false;
this.focused = false;
this.root = true;
this.buttons = buttons;
this.children = [];
this.hotkey = [];
this.pressed = [];
this.keypressFunc = null;
this.theme = theme;
this.assignTheme(theme);
this.constructEvents();
this.register();
}
assignTheme(theme) {
if (this.link !== undefined) {
document.getElementsByTagName('head')[0].removeChild(this.link);
}
this.link = document.createElement('link');
this.link.rel = 'stylesheet';
this.link.type = 'text/css';
this.link.href = 'themes/contextify-' + theme + '.css';
document.getElementsByTagName('HEAD')[0].appendChild(this.link);
}
constructEvents() {
this.container.addEventListener(this.contextEvent, this.handleContextMenu.bind(this));
this.container.addEventListener(this.mouseClickEvent, this.handleClick.bind(this));
}
handleContextMenu(event) {
event.preventDefault();
if (this.root && event.target !== this.menu) {
for (const child of this.children) {
if (event.target === child.menu)
return;
}
this.show(event.clientX, event.clientY);
}
}
handleClick(event) {
event.preventDefault();
if (this.root) {
if (this.menu !== undefined) {
if (!this.menuEntryMember(event.target)) {
this.hide(false);
}
}
}
}
mouseLeave(event) {
this.leaveTimeout = setTimeout(() => {
this.focused = false;
if (!this.root) {
if (this.hasActiveChild() && !this.getActiveChild().focused) {
this.hide(false);
} else if (!this.hasActiveChild()) {
this.hide(false);
if (!this.parent.root && !this.parent.focused)
this.parent.hide(false);
}
} else {
if (this.hasActiveChild()) {
const child = this.getActiveChild();
if (!child.focused)
child.hide(false);
}
}
}, this.childTimeout);
}
mouseEnter(event) {
this.focused = true;
if (this.leaveTimeout !== undefined)
clearTimeout(this.leaveTimeout);
}
constructMenu() {
this.menu = document.createElement('div');
this.menu.classList.add('context');
for (const button of this.buttons)
this.menu.appendChild(this.parseButtonData(button));
this.container.appendChild(this.menu);
this.menu.addEventListener(this.mouseLeaveEvent, this.mouseLeave.bind(this));
this.menu.addEventListener(this.mouseEnterEvent, this.mouseEnter.bind(this));
}
parseButtonData(button) {
const menuEntry = document.createElement('div');
menuEntry.classList.add('menuEntry');
const type = this.hasAttribute(button, this.typeAttribute) ? button[this.typeAttribute] : 'button';
if ('separator' === type) {
menuEntry.classList.add('separator');
return menuEntry;
}
if (this.hasAttribute(button, this.childAttribute)) {
this.buildChildMenu(menuEntry, button);
} else {
const closeSubMenu = event => {
for (const child of this.children) {
if (child.active) {
child.hide(false);
}
}
}
menuEntry.addEventListener(this.mouseEnterEvent, closeSubMenu);
}
menuEntry.classList.add('contextButton');
if (this.hasAttribute(button, this.colourAttribute)) {
if (this.validateColourCode(button[this.colourAttribute])) {
let rgba = this.translateColourCodes(button[this.colourAttribute].toString());
menuEntry.style.cssText = `background-color: ${rgba}`;
}
}
if (this.hasAttribute(button, this.textColourAttribute)) {
if (this.validateColourCode(button[this.textColourAttribute])) {
let rgba = this.translateColourCodes(button[this.textColourAttribute].toString());
menuEntry.style.cssText = `color: ${rgba}`;
}
}
this.appendLabelAndIcon(button, menuEntry, button[this.textAttribute].toString());
menuEntry.id = button[this.textAttribute];
if (this.hasAttribute(button, this.enabledAttribute)) {
menuEntry.classList.add(button[this.enabledAttribute] ? this.enabledAttribute : 'disabled');
}
if (this.hasAttribute(button, this.clickAttribute)) {
if (typeof button[this.clickAttribute] === 'function') {
this.assignClickHandler(button, menuEntry, this.parent, button[this.clickAttribute]);
}
}
return menuEntry;
}
buildChildMenu(menuEntry, button) {
const childMenu = new Contextify(button[this.childAttribute], this.theme, this.container);
childMenu.parent = this;
childMenu.root = false;
const openSubMenu = event => {
if (button[this.enabledAttribute] === undefined || button[this.enabledAttribute]) {
let x = this.menu.offsetLeft + this.menu.clientWidth + menuEntry.offsetLeft;
let y = this.menu.offsetTop + menuEntry.offsetTop;
if (!childMenu.active) {
childMenu.show(x, y);
}
else childMenu.hide(false);
}
}
this.children.push(childMenu);
menuEntry.addEventListener(this.mouseEnterEvent, openSubMenu);
}
assignClickHandler(button, menuEntry, parent, func) {
menuEntry.addEventListener(this.clickAttribute, function () {
func({ handled: false, button: menuEntry, parent: parent });
});
if (this.hasAttribute(button, this.hotkeyAttribute)) {
const hotkey = document.createElement('span');
const hotkeyName = button[this.hotkeyAttribute];
hotkey.classList.add(this.hotkeyAttribute);
hotkey.innerText = hotkeyName;
if (hotkeyName.includes(' + ')) {
this.hotkey = hotkeyName.split(' + ');
}
this.keypressFunc = func;
menuEntry.appendChild(hotkey);
}
}
hasAttribute(button, attribute) {
return button.hasOwnProperty(attribute);
}
validateColourCode(colour) {
let splitColour = colour.split(':');
for (let i = 0; i < 3; i++) {
if (!/^(([0-1]?[0-9]?[0-9])|([2][0-4][0-9])|(25[0-5]))$/i.exec(splitColour[i])) {
console.error("[Contextify]: Invalid rgba colour attribute format. Please use format: 255:255:255:1.0");
return false;
}
}
if (!/^(0(\.\d+)?|1(\.0+)?)$/i.exec(splitColour[3])) {
console.error("[Contextify]: Invalid rgba colour attribute format. Please use format: 255:255:255:1.0");
return false;
}
return true;
}
appendLabelAndIcon(button, parent, text) {
if (this.hasAttribute(button, this.iconAttribute)) {
if (this.validateFontAwesome()) {
const icon = document.createElement('span');
icon.classList.add('fa');
icon.classList.add(button[this.iconAttribute]);
icon.classList.add(this.iconAttribute);
parent.appendChild(icon);
}
else console.warn('Warning: Use of icons requires fontawesome. Icons will not load.');
}
const label = document.createElement('span');
label.classList.add('label');
label.innerText = text === undefined ? '' : text;
parent.appendChild(label);
}
show(x, y) {
if (this.active) this.hide(false);
this.constructMenu();
this.setPosition(x, y);
this.active = true;
}
hide(hideParent) {
if (this.active) {
this.active = false;
this.hideChildren();
this.container.removeChild(this.menu);
if (hideParent && this.parent !== null && this.parent.active) {
this.parent.hide(false);
}
}
}
hideChildren() {
for (const child of this.children) {
if (child.active) {
child.hide(false);
}
}
}
hasActiveChild() {
return this.getActiveChild() !== null;
}
getActiveChild() {
if (this.children !== undefined) {
for (const child of this.children) {
if (child.active)
return child;
}
}
return null;
}
setPosition(x, y) {
const screenWidth = this.container.offsetWidth;
const menuWidth = this.menu.offsetWidth;
if (x + menuWidth > screenWidth) {
// Touching right side, adjust position by subtracting 200
this.menu.style.left = `${x - 200}px`;
} else if (x < 0) {
// Touching left side, adjust position by adding 200
this.menu.style.left = `${x + 200}px`;
} else {
// Normal position
this.menu.style.left = `${x}px`;
}
this.menu.style.top = `${y}px`;
}
disableButton(button) {
this.container.querySelector(button).classList.remove('enabled');
this.container.querySelector(button).classList.add('disabled');
}
enableButton(button) {
this.container.querySelector(button).classList.remove('disabled');
this.container.querySelector(button).classList.add('enabled');
}
translateColourCodes(code) {
let split = code.split(':');
return 'rgba(' + split[0] + ',' + split[1] + ',' + split[2] + ',' + split[3] + ')';
}
menuEntryMember(obj) {
return (obj?.classList?.contains('menuEntry') ||
obj?.parentElement?.classList?.contains('menuEntry'));
}
validateFontAwesome() {
var span = document.createElement('span');
span.className = 'fa';
span.style.display = 'none';
document.body.insertBefore(span, document.body.firstChild);
function css(element, property) {
return window.getComputedStyle(element, null).getPropertyValue(property);
}
let cdnExists = css(span, 'font-family').toLowerCase() === 'fontawesome';
let jsExists = window.FontAwesomeKitConfig !== undefined;
document.body.removeChild(span);
return cdnExists || jsExists;
}
register() {
this.container.addEventListener(this.contextEvent, this.handleContextMenu.bind(this));
this.container.addEventListener(this.mouseClickEvent, this.handleClick.bind(this));
}
deregister() {
this.container.removeEventListener(this.contextEvent, this.handleContextMenu.bind(this));
this.container.removeEventListener(this.mouseClickEvent, this.handleClick.bind(this));
}
} Will make a PR request shortly. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You need to make this a module. Plus encapsulate your variables. As this code is almost usable to developers until you do both.
The text was updated successfully, but these errors were encountered: