Skip to content

Instantly share code, notes, and snippets.

@robert-carroll
Last active May 12, 2021 22:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robert-carroll/12717270d15c885f2908371e7bfab838 to your computer and use it in GitHub Desktop.
Save robert-carroll/12717270d15c885f2908371e7bfab838 to your computer and use it in GitHub Desktop.
CanvasLMS module mashup - incomplete
/*
odd behvior on student view of modules
expand/collapse button:
expand collapse works as expected
-- but save state of student view doesn't seem to hold on refresh sometimes?
-- on page load, clicking the collapse button collapses, and on refresh the state of each module is collapsed
-- but expanding, and refreshing does not result in open modules... am I clicking the right selectors for the POST?
-- if we start the page with some modules expanded, and some collapsed, the toolbar button indicates all are expanded
, must check the count and set the state
-- toggle states aren't working correctly.
-- what if we need to detect the change on load... ie the state is being edited with ajax or onload.
*/
const mm = {
toolbar: controls => {
var filters = controls.filters.map(f => `<li><button class="btn filter active" id="${f['id']}" aria-pressed="true" title="${f['label']}"><i class="${f['icon']}"></i></button></li>`).join(' '),
divider = `<li class="separator"></li>`,
toggles = controls.toggles.map(f => `<li><button class="btn toggle active" id="${f['id']}" aria-pressed="true" title="${f['label']}"><i class="${f['icon']}"></i></button></li>`).join(' '),
html = `<div><ul id="module_filters">${filters+divider+toggles}</ul></div>`
// document.getElementById('module_filters').remove();
document.querySelector('div.header-bar').insertAdjacentHTML('beforeend', html)
},
filters: () => {
return document.querySelectorAll('#module_filters button.filter');
},
toggles: () => {
return document.querySelectorAll('#module_filters button.toggle');
},
lyons: {
toggle: (btn, state) => {
console.log(state)
// if a state is not specified, we'll switch it by default
state = state || btn.getAttribute('aria-pressed') == 'true' ? 'false' : 'true'
btn.setAttribute('aria-pressed', state)
if (btn.getAttribute('aria-pressed') == 'true') {
btn.classList.add('active')
} else if (btn.classList.contains('active')) {
btn.classList.remove('active')
}
// if (state == 'true') {
// btn.classList.add('active')
// } else {
// btn.classList.remove('active')
// }
// let flip = btn.getAttribute('aria-pressed') == 'true' ? 'false' : 'true';
// btn.setAttribute('aria-pressed', flip)
// if (btn.getAttribute('aria-pressed') == 'true') {
// btn.classList.add('active')
// } else if (btn.classList.contains('active')) {
// btn.classList.remove('active')
// },
},
hide: items => {
console.log('hide')
items.forEach(item => {
item.classList.add('module_filters_hide')
})
},
show: items => {
console.log('show')
items.forEach(item => {
item.classList.remove('module_filters_hide')
})
},
},
cowan: {
callback: () => {
console.log('cowan: callback')
switch (mm.cowan.button.state) {
case 'true':
mm.cowan.collapse([])
break;
case 'false':
mm.cowan.expand([])
break;
}
},
modules: {
get state() {
// if any modules are collapsed return collapsed
let collapsed_count = document.querySelectorAll('#context_modules .collapsed_module')
if (collapsed_count.length >= 1) {
return 'collapsed'
} else {
return 'expanded'
}
},
set state(s) {
console.log('match module states')
if(s == 'collapsed') {
mm.cowan.button.state = 'collapse'
} else if(s == 'expanded') {
mm.cowan.button.state = 'expand'
}
}
},
click: targets => {
targets.forEach(i => i.click())
},
collapse: items => {
console.log('collapse')
var collapse_btns = document.querySelectorAll('#context_modules span.collapse_module_link');
mm.cowan.click(collapse_btns)
mm.cowan.button.state = 'collapse'
// if collapse is clicked, and all items are collapsed, set the filter buttons to inactive
// toggle buttons off
mm.filters().forEach(off => {
off.setAttribute('aria-pressed', 'false')
off.classList.remove('active')
})
},
expand: items => {
console.log('expand')
// on expand/collapse reset, we need to unhide anything that's hidden
document.querySelectorAll('.module_filters_hide').forEach(item => {
item.classList.remove('module_filters_hide')
})
// all these expand the modules when clicked or .clicked(), none of them save, even though the POST is successful
//var expand_btns = document.querySelectorAll('#context_modules .collapsed_module span.collapse_module_link');
//var expand_btns = document.querySelectorAll(`#context_modules .expand_module_link:not([style='display: none;'])`);
//var expand_btns = document.querySelectorAll(`#context_modules .expand_module_link[aria-expanded="false"`);
var expand_btns = document.querySelectorAll('#context_modules span.expand_module_link')
mm.cowan.click(expand_btns)
mm.cowan.button.state = 'expand'
// also need to reset the filter buttons to active
mm.filters().forEach(on => {
on.setAttribute('aria-pressed', 'true')
on.classList.add('active')
})
},
button: {
get state() {
return document.getElementById('expand_collapse').getAttribute('aria-pressed')
},
set state(s) {
// set button state
switch(s) {
case 'expand':
document.getElementById('expand_collapse').classList.add('active')
document.getElementById('expand_collapse').setAttribute('aria-pressed', 'true')
document.getElementById('expand_collapse').setAttribute('title', 'Collapse All Modules')
mm.filters().forEach(btn => mm.lyons.toggle(btn), 'true')
break;
case 'collapse':
document.getElementById('expand_collapse').classList.remove('active')
document.getElementById('expand_collapse').setAttribute('aria-pressed', 'false')
document.getElementById('expand_collapse').setAttribute('title', 'Expand All Modules')
mm.filters().forEach(btn => mm.lyons.toggle(btn), 'false')
break;
}
}
}
}
}
var controls = {
filters: [
{ id: 'wiki_page', label: 'Pages', icon: 'icon-document' },
{ id: 'assignment', label: 'Assignments', icon: 'icon-assignment' },
{ id: 'quiz', label: 'Quizzes', icon: 'icon-quiz' },
{ id: 'discussion_topic', label: 'Discussion Topics', icon: 'icon-discussion' },
{ id: 'external_url', label: 'Links', icon: 'icon-link' },
{ id: 'attachment', label: 'Files', icon: 'icon-paperclip' },
{ id: 'context_external_tool', label: 'External Tools', icon: 'icon-integrations' },
{ id: 'completed_item', label: 'Completed Items', icon: 'icon-check' },
{ id: '_requirement', label: 'Prerequisites', icon: 'icon-prerequisite' },
],
toggles: [
{ id: 'expand_collapse', label: 'Collapse All Modules', icon: 'icon-collapse', callback: mm.cowan.callback },
{ id: 'module_progress', label: 'Module Progress', icon: 'icon-complete', callback: mp }
]
};
// toolbar html
mm.toolbar(controls)
mm.filters().forEach(btn => {
console.log('filters: init')
// double click, toggle filter
btn.addEventListener('click', function (e) {
var is_pressed = btn.getAttribute('aria-pressed')
//console.log(`${e.type} - ${btn.id}: ${is_pressed}`)
// toggle state
switch (is_pressed) {
case 'true':
mm.lyons.hide(document.querySelectorAll(`li.${btn.id}`))
// student view sees .lti-quiz
if (btn.id == 'quiz') {
mm.lyons.hide(document.querySelectorAll(`li.lti-${btn.id}`))
}
mm.lyons.toggle(btn, 'true')
break;
case 'false':
mm.lyons.show(document.querySelectorAll(`li.${btn.id}`))
// student view sees .lti-quiz
if (btn.id == 'quiz') {
mm.lyons.show(document.querySelectorAll(`li.lti-${btn.id}`))
}
mm.lyons.toggle(btn, 'false')
break;
}
// switch the state of the expand/collapse button
// if any filters are active, show the collapse button
var active = document.querySelectorAll('#module_filters button[aria-pressed="false"]')
if (active.length > 0 && document.getElementById('expand_collapse')) {
mm.cowan.button.state = 'collapse'
}
// if a filter item is clicked, that we expand all modules with that content type for visibility
var modules_collapsed_with_filtered_content = document.querySelectorAll(`#context_modules .collapsed_module li[id^="context_module_item_"].${btn.id}`)
if (modules_collapsed_with_filtered_content.length > 0) {
for (let i = 0; i < modules_collapsed_with_filtered_content.length; i++) {
var module_id = modules_collapsed_with_filtered_content[i].parentNode.parentNode.parentNode.getAttribute('data-module-id')
document.querySelector(`.expand_module_link[aria-controls="context_module_content_${module_id}"]`).click()
}
}
});
// double click toggle, disable all filters and enable clicked filter
btn.addEventListener('dblclick', function (e) {
console.log(`${e.type} - ${btn.id}`)
// can't do 2 things here, need to know what the state of the button is and do the right action
console.log(btn.getAttribute('aria-pressed'))
// toggle buttons off
mm.filters().forEach(off => {
off.setAttribute('aria-pressed', 'false')
off.classList.remove('active')
})
// wish this would work
// var hide_leg = Array.from(document.querySelectorAll(`li[id^="context_module_item_"]:not(.${btn.id})`))
// var hide_lti = Array.from(document.querySelectorAll(`li[id^="context_module_item_"]:not(.lti-${btn.id})`))
// // what to hide
// const hide_items = [...new Set(hide_leg.concat(hide_lti).map(item => item.id))];
// mm.lyons.hide(hide_items)
// var show_leg = Array.from(document.querySelectorAll(`li.${btn.id}.module_filters_hide`))
// var show_lti = Array.from(document.querySelectorAll(`li[id^="context_module_item_"]:not(.lti-${btn.id})`))
// // what to hide
// const show_items = [...new Set(show_leg.concat(show_lti).map(item => item.id))];
// mm.lyons.show(show_items)
// items to hide
mm.lyons.hide(document.querySelectorAll(`li[id^="context_module_item_"]:not(.${btn.id})`))
// student view sees .lti-quiz too
if (btn.id == 'quiz') {
mm.lyons.hide(document.querySelectorAll(`li[id^="context_module_item_"]:not(.lti-${btn.id})`))
}
// items to show
mm.lyons.show(document.querySelectorAll(`li.${btn.id}.module_filters_hide`))
// student view sees .lti-quiz too
if (btn.id == 'quiz') {
mm.lyons.show(document.querySelectorAll(`li.lti-${btn.id}.module_filters_hide`))
}
// active button
btn.classList.add('active')
btn.setAttribute('aria-pressed', 'true')
});
})
function mp(btn) {
console.log('mp')
console.log(btn.getAttribute('aria-pressed'))
}
function cb(callback, btn) {
callback(btn)
}
mm.toggles().forEach(btn => {
console.log('filters: toggles')
btn.addEventListener('click', e => {
cb(controls.toggles.filter(t => t.id == btn.id)[0].callback, btn)
});
});
//}
// physical click collapse
// collapse=1&_method=POST
// physical click expand
// collapse=0&_method=POST
// when the tool bar is loaded with collapsed modules
// // expand button
// collapse=1&_method=POST
// // collapse button
// collapse=0&_method=POST
// ?? test and update the filters and toggles if the page loads collapsed
// this matches the button state with the modules state (any closed module)
// mm.cowan.modules.state = mm.cowan.modules.state
#module_filters {
list-style-type: none;
display: inline;
margin: 0;
}
#module_filters li {
padding: 0 .5em 0 0;
display: inline-block;
}
#module_filters li.separator::after {
content: " ";
border-left: 1px dashed grey;
padding: -1px;
}
#module_filters button {
background: none;
border: none;
border-color: transparent;
box-shadow: none;
margin: 0;
outline-style: none;
padding: 0 0 4px 0;
}
#module_filters button.active {
color: #00AC18;
}
.module_filters_hide {
display: none;
}#module_filters {
list-style-type: none;
display: inline;
margin: 0;
}
#module_filters li {
padding: 0 .5em 0 0;
display: inline-block;
}
#module_filters li.separator::after {
content: " ";
border-left: 1px dashed grey;
padding: -1px;
}
#module_filters button {
background: none;
border: none;
border-color: transparent;
box-shadow: none;
margin: 0;
outline-style: none;
padding: 0 0 4px 0;
}
#module_filters button.active {
color: #00AC18;
}
.module_filters_hide {
display: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment