NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Dashboard Card Sorter // @namespace https://github.com/jamesjonesmath/canvancement // @description Sort dashboard course cards using drag and drop // @include https://*.instructure.com/ // @version 1 // @grant none // @license MIT // ==/UserScript== (function() { if (window.location.pathname !== '/') { return; } var scope = '/dashboard/order'; var namespace = 'canvancement'; var cardOrder; var hasLoaded = false; try { loadCardOrder(); checkCards(); } catch (e) { console.log(e); } function makeSortable() { hasLoaded = true; if (typeof cardOrder !== 'undefined' && cardOrder.length > 0) { sortCards(); } var box = document.querySelector('div#DashboardCard_Container > div.ic-DashboardCard__box'); var cards = box.childNodes; if (cards.length < 2) { return; } $('div.ic-DashboardCard__box').sortable({ 'containment' : 'parent', 'items' : '> div', 'update' : saveCardOrder }); return; } function checkCards() { var el = document.querySelector('div#DashboardCard_Container > div.ic-DashboardCard__box a.ic-DashboardCard__link'); if (el) { makeSortable(); } else { var src = document.querySelector('div#DashboardCard_Container'); var observer = new MutationObserver(function(mutations) { observer.disconnect(); makeSortable(); }); var config = { childList : true, }; observer.observe(src, config); } } function getCardOrder() { var order = []; var links = document.querySelectorAll('div#DashboardCard_Container > div.ic-DashboardCard__box a.ic-DashboardCard__link'); if (links.length === 0) { return; } var courseRegex = new RegExp('/courses/([0-9]+)$'); for (var i = 0; i < links.length; i++) { var matches = courseRegex.exec(links[i].href); if (matches) { order.push(matches[1]); } } return order; } function sortCards() { var box = document.querySelector('div#DashboardCard_Container > div.ic-DashboardCard__box'); var cards = box.childNodes; if (cards.length < 2) { deleteCardOrder(); return; } var order = getCardOrder(); // New cards var pos = 0; var needsUpdated = false; var j; var k; var id; var el; for (k = 0; k < order.length; k++) { id = cardOrder[k]; j = cardOrder.indexOf(id); if (j === -1) { el = cards[k]; box.insertBefore(el, cards[pos]); order.splice(k, 1); order.splice(pos, 0, id); pos++; } } // Existing cards for (j = 0; j < cardOrder.length; j++) { id = cardOrder[j]; k = order.indexOf(id); if (k === -1) { needsUpdated = true; continue; } if (k === pos) { pos++; continue; } el = cards[k]; box.insertBefore(el, cards[pos]); order.splice(k, 1); order.splice(pos, 0, id); pos++; } if (needsUpdated) { saveCardOrder(); } return; } function saveCardOrder(event, ui) { var currentOrder = getCardOrder(); var url = '/api/v1/users/self/custom_data' + scope; var parms = { 'ns' : namespace, 'data' : currentOrder.join(',') }; $.ajax({ 'url' : url, 'type' : 'PUT', 'data' : parms }); } function loadCardOrder() { var url = '/api/v1/users/self/custom_data' + scope; var parms = { 'ns' : namespace }; $.getJSON(url, parms, function(data) { cardOrder = data.data.split(','); if (hasLoaded) { sortCards(); } }); return; } function deleteCardOrder() { var url = '/api/v1/users/self/custom_data' + scope; var parms = { 'ns' : namespace }; $.ajax({ 'url' : url, 'type' : 'DELETE', 'data' : parms }); cardOrder = undefined; return; } })();