Update Compass QoL Enhancer to 1.13.0
- Slightly refactor code - Prevent the dashboard tab in the user page being "dsh" - Learning tasks can now be linked to
This commit is contained in:
		
							parent
							
								
									387213d063
								
							
						
					
					
						commit
						127b846b67
					
				| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
// @name        Compass QoL Enhancer
 | 
			
		||||
// @namespace   blankie-scripts
 | 
			
		||||
// @match       http*://*.compass.education/*
 | 
			
		||||
// @version     1.12.0
 | 
			
		||||
// @version     1.13.0
 | 
			
		||||
// @author      blankie
 | 
			
		||||
// @description A userscript that adds small but useful features for Compass, such as the ability to close windows by clicking on the background
 | 
			
		||||
// @inject-into page
 | 
			
		||||
| 
						 | 
				
			
			@ -12,13 +12,12 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
 | 
			
		||||
let qolTabOpened = false;
 | 
			
		||||
// we make a copy of window.location.hash because the dashboard tab on a user's page would set the fragment to #dsh for some reason
 | 
			
		||||
let hashCopy = window.location.hash;
 | 
			
		||||
let qolLearningTaskOpened = false;
 | 
			
		||||
 | 
			
		||||
// needed because .toString() adds a trailing = for empty values
 | 
			
		||||
function serializeURLSearchParams(query) {
 | 
			
		||||
URLSearchParams.prototype.laxToString = function() {
 | 
			
		||||
    let out = "";
 | 
			
		||||
    for (let [key, value] of query) {
 | 
			
		||||
    for (let [key, value] of this) {
 | 
			
		||||
        if (out) {
 | 
			
		||||
            out += "&";
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -30,9 +29,18 @@ function serializeURLSearchParams(query) {
 | 
			
		|||
    }
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
function getHashSearch() {
 | 
			
		||||
    return new URLSearchParams(window.location.hash.substring(1));
 | 
			
		||||
}
 | 
			
		||||
function getExtClass(className) {
 | 
			
		||||
    if (!unsafeWindow.Ext) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
    return unsafeWindow.Ext.ClassManager.get(className);
 | 
			
		||||
}
 | 
			
		||||
function getPanelItemHash(panelId, isDefault) {
 | 
			
		||||
    if (window.location.pathname === "/Organise/Activities/Activity.aspx") {
 | 
			
		||||
        let query = new URLSearchParams(window.location.hash.substring(1));
 | 
			
		||||
        let query = getHashSearch();
 | 
			
		||||
        if (panelId === "learningtasks") {
 | 
			
		||||
            query.delete("qol_open_tab");
 | 
			
		||||
            query.set("openLearningTaskTab", "");
 | 
			
		||||
| 
						 | 
				
			
			@ -43,12 +51,52 @@ function getPanelItemHash(panelId, isDefault) {
 | 
			
		|||
            query.set("qol_open_tab", panelId);
 | 
			
		||||
            query.delete("openLearningTaskTab");
 | 
			
		||||
        }
 | 
			
		||||
        return `#${serializeURLSearchParams(query)}`;
 | 
			
		||||
        query.delete("qol_learning_task");
 | 
			
		||||
        return `#${query.laxToString()}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return `#${encodeURIComponent(panelId)}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Prevent the dashboard tab in the user page being "dsh"
 | 
			
		||||
let UserProfileNewWidget = getExtClass("Compass.widgets.UserProfileNewWidget");
 | 
			
		||||
if (UserProfileNewWidget) {
 | 
			
		||||
    let original = UserProfileNewWidget.prototype.createItems;
 | 
			
		||||
    UserProfileNewWidget.prototype.createItems = function() {
 | 
			
		||||
        let res = original.apply(this, arguments);
 | 
			
		||||
        delete res[0].listeners;
 | 
			
		||||
        return res;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Automatically open learning tasks specified in the URL
 | 
			
		||||
let LearningTasksDetailsWidgetNew = getExtClass("Compass.widgets.LearningTasksDetailsWidgetNew");
 | 
			
		||||
if (LearningTasksDetailsWidgetNew) {
 | 
			
		||||
    let original = LearningTasksDetailsWidgetNew.prototype.forceOpenLearningTaskOnLoad;
 | 
			
		||||
    LearningTasksDetailsWidgetNew.prototype.forceOpenLearningTaskOnLoad = function() {
 | 
			
		||||
        // stole this from the function above
 | 
			
		||||
        let toOpen = this.initialLoadComplete && !this.firstLoad;
 | 
			
		||||
        let exit = !toOpen || this.openLearningTaskId || qolLearningTaskOpened;
 | 
			
		||||
        original.apply(this, arguments);
 | 
			
		||||
        if (exit) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        qolLearningTaskOpened = true;
 | 
			
		||||
        let learningTask = parseInt(getHashSearch().get("qol_learning_task"), 10);
 | 
			
		||||
        if (isNaN(learningTask)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let learningTaskRecord = this.taskStore.findRecord("id", learningTask);
 | 
			
		||||
        if (!learningTaskRecord) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.launchLearningTasksSubmissionWidget(learningTaskRecord.data.gridRecordId, false);
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateInstanceButton(instanceDetails, instanceButton, offset) {
 | 
			
		||||
    // Make previous/next session buttons links
 | 
			
		||||
    let index = instanceDetails.instanceStore.indexOfId(instanceDetails.m_instanceId);
 | 
			
		||||
| 
						 | 
				
			
			@ -60,42 +108,64 @@ function updateInstanceButton(instanceDetails, instanceButton, offset) {
 | 
			
		|||
        instanceButton.el.dom.removeAttribute("href");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleInstanceButtonClick(event) {
 | 
			
		||||
    if (event.ctrlKey) {
 | 
			
		||||
        event.stopImmediatePropagation();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleActivityManager(element) {
 | 
			
		||||
    let instanceDetails = unsafeWindow.Ext.getCmp(element.id).m_InstanceDetailsWidget;
 | 
			
		||||
    let instanceNavigatorToolbar = instanceDetails.getInstanceNavigatorToolbar();
 | 
			
		||||
    let previousInstanceButton = instanceNavigatorToolbar.getComponent("previousInstanceButton");
 | 
			
		||||
    let nextInstanceButton = instanceNavigatorToolbar.getComponent("nextInstanceButton");
 | 
			
		||||
    let comboSelectInstance = instanceDetails.getCmbSelectIntance(); // not a typo :)
 | 
			
		||||
 | 
			
		||||
    let realUpdateInstanceHeader = instanceDetails.updateInstanceHeader;
 | 
			
		||||
    instanceDetails.updateInstanceHeader = function() {
 | 
			
		||||
        realUpdateInstanceHeader.apply(this, arguments);
 | 
			
		||||
        updateInstanceButton(instanceDetails, previousInstanceButton, -1);
 | 
			
		||||
        updateInstanceButton(instanceDetails, nextInstanceButton, 1);
 | 
			
		||||
let InstanceDetailsWidget = getExtClass("Compass.widgets.InstanceDetailsWidget");
 | 
			
		||||
if (InstanceDetailsWidget) {
 | 
			
		||||
    let originalUpdateInstanceHeader = InstanceDetailsWidget.prototype.updateInstanceHeader;
 | 
			
		||||
    InstanceDetailsWidget.prototype.updateInstanceHeader = function() {
 | 
			
		||||
        originalUpdateInstanceHeader.apply(this, arguments);
 | 
			
		||||
        let toolbar = this.getInstanceNavigatorToolbar();
 | 
			
		||||
        updateInstanceButton(this, toolbar.getComponent("previousInstanceButton"), -1);
 | 
			
		||||
        updateInstanceButton(this, toolbar.getComponent("nextInstanceButton"), 1);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (let button of [previousInstanceButton, nextInstanceButton]) {
 | 
			
		||||
        button.el.dom.addEventListener("click", handleInstanceButtonClick, {passive: true});
 | 
			
		||||
        // move all previous handlers back to the front
 | 
			
		||||
        // new relic's browser agent would've borked this, but it did not??
 | 
			
		||||
        // i'll take what i can get i guess
 | 
			
		||||
        // (note to self: unsafeWindow.NREUM = true if this breaks)
 | 
			
		||||
        for (let handler of unsafeWindow.Ext.EventManager.getEventListenerCache(button, "click")) {
 | 
			
		||||
            button.el.dom.removeEventListener("click", handler.wrap);
 | 
			
		||||
            button.el.dom.addEventListener("click", handler.wrap);
 | 
			
		||||
    let originalGetInstanceNavigatorToolbar = InstanceDetailsWidget.prototype.getInstanceNavigatorToolbar;
 | 
			
		||||
    InstanceDetailsWidget.prototype.getInstanceNavigatorToolbar = function() {
 | 
			
		||||
        if (this.instanceNavigatorToolbar) {
 | 
			
		||||
            return this.instanceNavigatorToolbar;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
        let toolbar = originalGetInstanceNavigatorToolbar.apply(this, arguments);
 | 
			
		||||
        let previousButton = toolbar.getComponent("previousInstanceButton");
 | 
			
		||||
        let nextButton = toolbar.getComponent("nextInstanceButton");
 | 
			
		||||
 | 
			
		||||
    comboSelectInstance.tpl.html = comboSelectInstance.tpl.html.replace("<div ", "<div data-qol-session={id} ");
 | 
			
		||||
        previousButton.preventDefault = false;
 | 
			
		||||
        let originalPreviousButtonHandler = previousButton.handler;
 | 
			
		||||
        previousButton.handler = function(_, event) {
 | 
			
		||||
            if (!event.ctrlKey) {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                originalPreviousButtonHandler.apply(this, arguments);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        nextButton.preventDefault = false;
 | 
			
		||||
        let originalNextButtonHandler = nextButton.handler;
 | 
			
		||||
        nextButton.handler = function(_, event) {
 | 
			
		||||
            if (!event.ctrlKey) {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                originalNextButtonHandler.apply(this, arguments);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return toolbar;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // not a typo :)
 | 
			
		||||
    let originalGetCmbSelectIntance = InstanceDetailsWidget.prototype.getCmbSelectIntance;
 | 
			
		||||
    InstanceDetailsWidget.prototype.getCmbSelectIntance = function() {
 | 
			
		||||
        if (this.cmbSelectInstance) {
 | 
			
		||||
            return this.cmbSelectInstance;
 | 
			
		||||
        }
 | 
			
		||||
        let combo = originalGetCmbSelectIntance.apply(this, arguments);
 | 
			
		||||
        // Make sessions in the sessions dropdown links
 | 
			
		||||
        combo.tpl.html = combo.tpl.html.replace(
 | 
			
		||||
            /<div (.+?) style="(.+?)">(.+?)<\/div>/,
 | 
			
		||||
            "<a href='#session/{id}' onclick='event.ctrlKey && event.stopImmediatePropagation()' $1 style='display: block; text-decoration: none; $2'>$3</a>"
 | 
			
		||||
        );
 | 
			
		||||
        return combo;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function handleNewCalendarEvent(element, calendar) {
 | 
			
		||||
    // Turn each calendar event into a link so that Link Hints can recognize it
 | 
			
		||||
    let a = document.createElement("a");
 | 
			
		||||
| 
						 | 
				
			
			@ -107,26 +177,27 @@ function handleNewCalendarEvent(element, calendar) {
 | 
			
		|||
    // fix weird aboveline (underline but above the text) on hover
 | 
			
		||||
    a.style.textDecoration = "none";
 | 
			
		||||
    a.replaceChildren(...element.childNodes);
 | 
			
		||||
    let preventCompassHandler = false;
 | 
			
		||||
 | 
			
		||||
    let calendarData = calendar.getEventRecordFromEl(element).data;
 | 
			
		||||
 | 
			
		||||
    if (a.classList.contains("activity-type-1")) {
 | 
			
		||||
        // Add a link for activities/"standard classes"
 | 
			
		||||
        a.href = `/Organise/Activities/Activity.aspx?targetUserId=${calendarData.targetStudentId}#session/${calendarData.instanceId}`;
 | 
			
		||||
        preventCompassHandler = true;
 | 
			
		||||
    } else if (a.classList.contains("activity-type-2")) {
 | 
			
		||||
        // Add a link for action centre events
 | 
			
		||||
        a.href = `/Organise/Activities/Events/Event.aspx?eventId=${calendarData.activityId}`;
 | 
			
		||||
        if (calendarData.targetStudentId) {
 | 
			
		||||
            a.href += `&userId=${calendarData.targetStudentId}`;
 | 
			
		||||
        }
 | 
			
		||||
        preventCompassHandler = true;
 | 
			
		||||
    } else if (a.classList.contains("activity-type-10")) {
 | 
			
		||||
        // Add a link for learning tasks
 | 
			
		||||
        let calendarPanel = calendar.ownerCalendarPanel;
 | 
			
		||||
        a.href = calendarPanel.targetUserId !== undefined ? `/Records/User.aspx?userId=${calendarPanel.targetUserId}#learningTasks` : "/Records/User.aspx#learningTasks";
 | 
			
		||||
        preventCompassHandler = true;
 | 
			
		||||
        a.href = "/Records/User.aspx"
 | 
			
		||||
        if (calendarPanel.targetUserId !== undefined) {
 | 
			
		||||
            a.href += `?userId=${calendarPanel.targetUserId}`;
 | 
			
		||||
        }
 | 
			
		||||
        // Link specifically to the learning task
 | 
			
		||||
        a.href += `#learningTasks&qol_learning_task=${calendarData.learningTaskId}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Show the finish time for applicable calendar events
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +211,7 @@ function handleNewCalendarEvent(element, calendar) {
 | 
			
		|||
 | 
			
		||||
    // prevent ctrl-clicking from changing the current tab
 | 
			
		||||
    // it seems like the link thing actually effectively killed the default handler anyway
 | 
			
		||||
    if (preventCompassHandler) {
 | 
			
		||||
    if (a.href) {
 | 
			
		||||
        a.addEventListener("click", function(event) {
 | 
			
		||||
            event.stopImmediatePropagation();
 | 
			
		||||
        }, {passive: true});
 | 
			
		||||
| 
						 | 
				
			
			@ -151,10 +222,6 @@ function handleNewCalendarEvent(element, calendar) {
 | 
			
		|||
 | 
			
		||||
function handlePanelItem(panel, panelItem, isDefault, tabToOpen) {
 | 
			
		||||
    let panelId = panelItem.itemId || panelItem.id;
 | 
			
		||||
    // i don't know why but dashboard is dsh
 | 
			
		||||
    if (window.location.pathname === "/Records/User.aspx" && panelId === "dashboard") {
 | 
			
		||||
        panelId = "dsh";
 | 
			
		||||
    }
 | 
			
		||||
    let panelItemHash = getPanelItemHash(panelId, isDefault);
 | 
			
		||||
 | 
			
		||||
    // Automatically open tab specified in fragment
 | 
			
		||||
| 
						 | 
				
			
			@ -164,10 +231,6 @@ function handlePanelItem(panel, panelItem, isDefault, tabToOpen) {
 | 
			
		|||
        // it does look like that this bug only manifests if the tab is activated while the initial loading thing is shown
 | 
			
		||||
        setTimeout(function() {
 | 
			
		||||
            panel.setActiveTab(panelItem);
 | 
			
		||||
            // reference the now active tab in the fragment if necessary (cough cough the reason why hashCopy exists)
 | 
			
		||||
            if (panelItemHash !== window.location.hash) {
 | 
			
		||||
                history.replaceState("", "", panelItemHash);
 | 
			
		||||
            }
 | 
			
		||||
        }, 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -186,7 +249,7 @@ function handlePanelItem(panel, panelItem, isDefault, tabToOpen) {
 | 
			
		|||
        }
 | 
			
		||||
        // prevent the browser from scrolling to the body
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        if (panelItemHash !== window.location.hash) {
 | 
			
		||||
        if (window.location.hash !== panelItemHash && (window.location.pathname === "/Organise/Activities/Activity.aspx" || !window.location.hash.startsWith(`${panelItemHash}&`))) {
 | 
			
		||||
            // Automatically add a reference to the tab when it is clicked
 | 
			
		||||
            history.pushState("", "", panelItemHash);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -200,29 +263,24 @@ function handleLearningTasksTable(element) {
 | 
			
		|||
            event.stopImmediatePropagation();
 | 
			
		||||
        }, {passive: true});
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSessionItem(element) {
 | 
			
		||||
    // Make sessions in the sessions dropdown links
 | 
			
		||||
    let style = getComputedStyle(element);
 | 
			
		||||
    // Add links to learning tasks themselves
 | 
			
		||||
    let gridView = Ext.getCmp(element.parentElement.id);
 | 
			
		||||
    for (let item of gridView.store.data.items) {
 | 
			
		||||
        let data = item.data;
 | 
			
		||||
        let a = element.querySelector(`[gridrecordid="${data.gridRecordId}"]`);
 | 
			
		||||
 | 
			
		||||
    let a = document.createElement("a");
 | 
			
		||||
    a.href = `#session/${element.dataset.qolSession}`;
 | 
			
		||||
    a.style.display = "block";
 | 
			
		||||
    a.style.textDecoration = "none";
 | 
			
		||||
    a.style.padding = style.padding;
 | 
			
		||||
    a.style.color = style.color;
 | 
			
		||||
    a.append(...element.childNodes);
 | 
			
		||||
    a.addEventListener("click", function(event) {
 | 
			
		||||
        if (event.ctrlKey) {
 | 
			
		||||
            event.stopImmediatePropagation();
 | 
			
		||||
        } else {
 | 
			
		||||
            event.preventDefault();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    element.style.padding = 0;
 | 
			
		||||
    element.append(a);
 | 
			
		||||
        let query = getHashSearch();
 | 
			
		||||
        query.set("qol_learning_task", data.id);
 | 
			
		||||
        a.href = `#${query.laxToString()}`;
 | 
			
		||||
        a.addEventListener("click", function(event) {
 | 
			
		||||
            if (event.ctrlKey) {
 | 
			
		||||
                event.stopImmediatePropagation();
 | 
			
		||||
            } else {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleCKEditor(instance) {
 | 
			
		||||
| 
						 | 
				
			
			@ -252,10 +310,6 @@ function handleNewNode(node, observer) {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (node.id === "CompassManagersActivityDefaultManager") {
 | 
			
		||||
        handleActivityManager(node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (node.parentElement && (node.classList.contains("ext-cal-hd-ct") || node.classList.contains("ext-cal-bg-tbl") || node.classList.contains("ext-cal-inner-ct") || node.classList.contains("ext-cal-mdv"))) {
 | 
			
		||||
        let calendar = unsafeWindow.Ext.getCmp(node.closest(".x-component").id);
 | 
			
		||||
        if (calendar.view) {
 | 
			
		||||
| 
						 | 
				
			
			@ -266,11 +320,10 @@ function handleNewNode(node, observer) {
 | 
			
		|||
        }
 | 
			
		||||
    } else if (!qolTabOpened && node.classList.contains("x-panel")) {
 | 
			
		||||
        qolTabOpened = true;
 | 
			
		||||
        let tabToOpen = hashCopy.substring(1) || null;
 | 
			
		||||
        if (window.location.pathname === "/Organise/Activities/Activity.aspx") {
 | 
			
		||||
            tabToOpen = (new URLSearchParams(tabToOpen)).get("qol_open_tab");
 | 
			
		||||
        }
 | 
			
		||||
        let panel = unsafeWindow.Ext.getCmp(node.id);
 | 
			
		||||
        let tabToOpen = window.location.pathname === "/Organise/Activities/Activity.aspx"
 | 
			
		||||
            ? getHashSearch().get("qol_open_tab")
 | 
			
		||||
            : (window.location.hash.replace(/^#(.+?)&.*$/, "$1") || null);
 | 
			
		||||
        for (let i = 0; i < panel.items.items.length; i++) {
 | 
			
		||||
            handlePanelItem(panel, panel.items.items[i], i === 0, tabToOpen);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -284,8 +337,6 @@ function handleNewNode(node, observer) {
 | 
			
		|||
        }
 | 
			
		||||
    } else if (node.localName === "table" && node.closest(".sel-learning-tasks-widget")) {
 | 
			
		||||
        handleLearningTasksTable(node);
 | 
			
		||||
    } else if (node.classList.contains("x-boundlist-item") && node.dataset.qolSession) {
 | 
			
		||||
        handleSessionItem(node);
 | 
			
		||||
    } else if (node.classList.contains("cke")) {
 | 
			
		||||
        let instance = unsafeWindow.CKEDITOR.instances[node.id.substring(4)];
 | 
			
		||||
        handleCKEditor(instance);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,11 +37,13 @@ A userscript that adds small but useful features for Compass. Features include:
 | 
			
		|||
  creating a new tab
 | 
			
		||||
- Ctrl-clicking an activity in a user's learning tasks tab no longer collapses
 | 
			
		||||
  everything else
 | 
			
		||||
- Learning tasks now being links (you can ctrl-click them)
 | 
			
		||||
- The previous/next buttons and sessions dropdown are now links (you can now
 | 
			
		||||
  use [Link Hints] and ctrl-click to open them)
 | 
			
		||||
- News feed items can now be opened by simply clicking on their background
 | 
			
		||||
- The context menu that only says "Copy" is now suppressed
 | 
			
		||||
- The option to remember logins is unchecked by default
 | 
			
		||||
- The dashboard tab in a user's profile no longer points you to #dsb
 | 
			
		||||
 | 
			
		||||
[Link Hints]: https://lydell.github.io/LinkHints/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue