Change with care
Changes to this item may stop the Card Interface from working.
/* cardsInterface * - Given a Blackboard page with a list of items * - Find all the items using the expected card data format * - Insert into the first item on the page a cards interface * data format * - Card's indicating by "Card Image: URL" in the description, though the URL can be empty * - Card title - heading of Blackboard item * - Card Label - Specify the label to apply to the card (default Module) * - Module number - just the order in which they appear in the list * - picture - heading includes Card Image:**url** OR inserted image with title attribute = 'Card Image' * - description - the rest of the description * - DATE * - Card Date: Mar 5 * Specify the date to be displayed * - Card Date Label: Due * Specify the label for the date - default Commencing */ var PARAMS = {} var TOOLTIPSTER_ADDED = false; var DOWNLOAD_LABEL = "Download File" /* Griffith Calendar Term dates * 2022 * - GU T1, T2, T3 3221 3225, 3228 * - OUA 2221 2223 2225 2227 * - QCM GU + QCM * * 2021 * - OUA Study Periods 1-4 * 2211, 2213 2215 2217 * - GU T1, T2, T3 * 3211 3215 3218 * - QCM T1 T2 * 3211QCM 3215QCM * 2020 * - OUA Study Periods 1-4 * 2201 2203 2205 2207 * - GU T1, T2, T3 * 3201 3205 3208 * 2019 * - OUA SP 3, 4 * 2195 2197 * - GU T1, T2, T3 * 3191 3195 319 */ var TERM_DATES = { // From 2022 OUA study periods match GU trimesters // These added by assignment below 3221: { 0: { start: "2022-03-07", stop: "2022-03-13" }, 1: { start: "2022-03-14", stop: "2022-03-20" }, 2: { start: "2022-03-21", stop: "2022-03-28" }, 3: { start: "2022-03-28", stop: "2022-04-03" }, 4: { start: "2022-04-04", stop: "2022-04-10" }, 5: { start: "2022-04-18", stop: "2022-04-24" }, 6: { start: "2022-04-25", stop: "2022-05-01" }, 7: { start: "2022-05-02", stop: "2022-05-08" }, 8: { start: "2022-05-09", stop: "2022-05-15" }, 9: { start: "2022-05-16", stop: "2022-05-22" }, 10: { start: "2022-05-23", stop: "2022-05-29" }, 11: { start: "2022-05-30", stop: "2022-06-05" }, 12: { start: "2022-06-06", stop: "2022-06-12" }, 13: { start: "2022-06-13", stop: "2022-06-19" }, 14: { start: "2022-06-20", stop: "2022-06-26" }, 15: { start: "2022-06-27", stop: "2022-07-03" }, exam: { start: "2022-06-13", stop: "2022-06-25" }, }, '3221QCM': { 0: { start: "2022-02-21", stop: "2022-02-27" }, 1: { start: "2022-02-28", stop: "2022-03-06" }, 2: { start: "2022-03-07", stop: "2022-03-13" }, 3: { start: "2022-03-14", stop: "2022-03-20" }, 4: { start: "2022-03-21", stop: "2022-03-27" }, 5: { start: "2022-03-28", stop: "2022-04-03" }, 6: { start: "2022-04-04", stop: "2022-04-10" }, 7: { start: "2022-04-18", stop: "2022-04-24" }, 8: { start: "2022-04-25", stop: "2022-05-01" }, 9: { start: "2022-05-09", stop: "2022-05-15" }, 10: { start: "2022-05-16", stop: "2022-05-22" }, 11: { start: "2022-05-23", stop: "2022-05-29" }, 12: { start: "2022-05-30", stop: "2022-06-05" }, 13: { start: "2022-06-06", stop: "2022-06-12" }, 14: { start: "2022-06-13", stop: "2022-06-19" }, 15: { start: "2022-06-20", stop: "2022-07-26" }, exam: { start: "2022-06-13", stop: "2022-06-25" }, }, 3225: { 0: { start: "2022-07-11", stop: "2022-07-17" }, 1: { start: "2022-07-18", stop: "2022-07-24" }, 2: { start: "2022-07-25", stop: "2022-07-31" }, 3: { start: "2022-08-01", stop: "2022-08-07" }, 4: { start: "2022-08-08", stop: "2022-08-14" }, 5: { start: "2022-08-22", stop: "2022-08-28" }, 6: { start: "2022-08-29", stop: "2022-09-04" }, 7: { start: "2022-09-05", stop: "2022-09-11" }, 8: { start: "2022-09-12", stop: "2022-09-18" }, 9: { start: "2022-09-19", stop: "2022-09-25" }, 10: { start: "2022-09-26", stop: "2022-10-02" }, 11: { start: "2022-10-03", stop: "2022-10-09" }, 12: { start: "2022-10-10", stop: "2022-10-16" }, 13: { start: "2022-10-17", stop: "2022-10-23" }, 14: { start: "2022-10-24", stop: "2022-10-30" }, 15: { start: "2022-10-31", stop: "2022-11-06" }, exam: { start: "2022-10-20", stop: "2022-10-29" }, }, '3225QCM': { 0: { start: "2022-07-18", stop: "2022-07-24" }, 1: { start: "2022-07-25", stop: "2022-07-31" }, 2: { start: "2022-08-01", stop: "2022-08-07" }, 3: { start: "2022-08-08", stop: "2022-08-14" }, 4: { start: "2022-08-15", stop: "2022-08-21" }, 5: { start: "2022-08-22", stop: "2022-08-28" }, 6: { start: "2022-09-05", stop: "2022-09-11" }, 7: { start: "2022-09-12", stop: "2022-09-18" }, 8: { start: "2022-09-19", stop: "2022-09-25" }, 9: { start: "2022-10-03", stop: "2022-10-09" }, 10: { start: "2022-10-10", stop: "2022-10-16" }, 11: { start: "2022-10-17", stop: "2022-10-23" }, 12: { start: "2022-10-24", stop: "2022-10-30" }, 13: { start: "2022-10-31", stop: "2022-11-06" }, 14: { start: "2022-11-07", stop: "2022-11-13" }, 15: { start: "2022-11-14", stop: "2022-07-20" }, exam: { start: "2022-11-07", stop: "2022-11-19" }, }, 3228: { 0: { start: "2022-10-31", stop: "2022-11-06" }, 1: { start: "2022-11-07", stop: "2022-11-13" }, 2: { start: "2022-11-14", stop: "2022-11-20" }, 3: { start: "2022-11-21", stop: "2022-11-27" }, 4: { start: "2022-11-28", stop: "2022-12-04" }, 5: { start: "2022-12-05", stop: "2022-12-11" }, 6: { start: "2022-12-12", stop: "2022-12-18" }, 7: { start: "2022-12-19", stop: "2022-12-25" }, 8: { start: "2023-01-09", stop: "2023-01-15" }, 9: { start: "2023-01-16", stop: "2023-01-22" }, 10: { start: "2023-01-23", stop: "2023-01-29" }, 11: { start: "2023-01-30", stop: "2023-02-05" }, 12: { start: "2023-02-06", stop: "2023-02-12" }, 13: { start: "2023-02-13", stop: "2023-02-19" }, 14: { start: "2023-02-20", stop: "2023-02-26" }, 15: { start: "2023-02-27", stop: "2023-03-05" }, // exam: { start: "2023-02-17", stop: "2023-02-26" }, }, 2211: { 0: { start: "2021-02-22", stop: "2021-02-28" }, 1: { start: "2021-03-01", stop: "2021-03-07" }, 2: { start: "2021-03-08", stop: "2021-03-14" }, 3: { start: "2021-03-15", stop: "2021-03-21" }, 4: { start: "2021-03-22", stop: "2021-03-28" }, 5: { start: "2021-03-29", stop: "2021-04-04" }, 6: { start: "2021-04-05", stop: "2021-04-11" }, 7: { start: "2021-04-12", stop: "2021-04-18" }, 8: { start: "2021-04-19", stop: "2021-04-25" }, 9: { start: "2021-04-26", stop: "2021-05-02" }, 10: { start: "2021-05-03", stop: "2021-05-09" }, 11: { start: "2021-05-10", stop: "2021-05-16" }, 12: { start: "2021-05-17", stop: "2021-05-23" }, 13: { start: "2021-05-24", stop: "2021-05-30" }, 14: { start: "2021-05-31", stop: "2021-06-06" }, exam: { start: "2021-05-31", stop: "2021-06-06" }, }, 2213: { 1: { start: "2021-05-31", stop: "2021-06-06" }, 2: { start: "2021-06-07", stop: "2021-06-13" }, 3: { start: "2021-06-14", stop: "2021-06-20" }, 4: { start: "2021-06-21", stop: "2021-06-27" }, 5: { start: "2021-06-28", stop: "2021-07-04" }, 6: { start: "2021-07-05", stop: "2021-07-11" }, 7: { start: "2021-07-12", stop: "2021-07-18" }, 8: { start: "2021-07-19", stop: "2021-07-25" }, 9: { start: "2021-07-26", stop: "2021-08-01" }, 10: { start: "2021-08-02", stop: "2021-08-08" }, 11: { start: "2021-08-09", stop: "2021-08-15" }, 12: { start: "2021-08-16", stop: "2021-08-22" }, 13: { start: "2021-08-23", stop: "2021-08-29" }, exam: { start: "2021-08-30", stop: "2021-09-05" }, }, 2215: { 0: { start: "2021-08-23", stop: "2021-08-29" }, 1: { start: "2021-08-30", stop: "2021-09-05" }, 2: { start: "2021-09-06", stop: "2021-09-12" }, 3: { start: "2021-09-13", stop: "2021-09-19" }, 4: { start: "2021-09-20", stop: "2021-09-26" }, 5: { start: "2021-09-27", stop: "2021-10-03" }, 6: { start: "2021-10-04", stop: "2021-10-10" }, 7: { start: "2021-10-11", stop: "2021-10-17" }, 8: { start: "2021-10-18", stop: "2021-10-24" }, 9: { start: "2021-10-25", stop: "2021-10-31" }, 10: { start: "2021-11-01", stop: "2021-11-07" }, 11: { start: "2021-11-08", stop: "2021-11-14" }, 12: { start: "2021-11-15", stop: "2021-11-21" }, 13: { start: "2021-11-22", stop: "2021-11-28" }, 14: { start: "2021-11-29", stop: "2021-12-05" }, exam: { start: "2021-11-29", stop: "2021-12-05" }, }, 2217: { 0: { start: "2021-11-22", stop: "2021-11-28" }, 1: { start: "2021-11-29", stop: "2021-12-05" }, 2: { start: "2021-12-06", stop: "2021-12-12" }, 3: { start: "2021-12-13", stop: "2021-12-19" }, 4: { start: "2021-12-20", stop: "2021-12-26" }, 5: { start: "2021-12-27", stop: "2022-01-02" }, 6: { start: "2022-01-03", stop: "2022-01-09" }, 7: { start: "2022-01-10", stop: "2022-01-16" }, 8: { start: "2022-01-17", stop: "2022-01-23" }, 9: { start: "2022-01-24", stop: "2022-01-30" }, 10: { start: "2022-01-31", stop: "2022-02-06" }, 11: { start: "2022-02-07", stop: "2022-02-13" }, 12: { start: "2022-02-14", stop: "2022-02-20" }, 13: { start: "2022-02-21", stop: "2022-02-27" }, exam: { start: "2022-02-28", stop: "2022-03-04" }, }, 3218: { 0: { start: "2021-11-01", stop: "2021-11-07" }, 1: { start: "2021-11-08", stop: "2021-11-14" }, 2: { start: "2021-11-15", stop: "2021-11-21" }, 3: { start: "2021-11-22", stop: "2021-11-28" }, 4: { start: "2021-11-29", stop: "2021-12-05" }, 5: { start: "2021-12-06", stop: "2021-12-12" }, 6: { start: "2021-12-13", stop: "2021-12-19" }, 7: { start: "2021-12-20", stop: "2021-12-26" }, 8: { start: "2022-01-10", stop: "2022-01-16" }, 9: { start: "2022-01-17", stop: "2022-01-23" }, 10: { start: "2022-01-24", stop: "2022-01-30" }, 11: { start: "2022-01-31", stop: "2022-02-06" }, 12: { start: "2022-02-07", stop: "2022-02-13" }, 13: { start: "2022-02-14", stop: "2022-02-20" }, 14: { start: "2022-02-21", stop: "2022-02-27" }, 15: { start: "2022-02-28", stop: "2022-03-06" }, exam: { start: "2022-02-17", stop: "2022-02-26" }, }, 3215: { 0: { start: "2021-07-12", stop: "2021-07-18" }, 1: { start: "2021-07-19", stop: "2021-07-25" }, 2: { start: "2021-07-26", stop: "2021-08-01" }, 3: { start: "2021-08-02", stop: "2021-08-08" }, 4: { start: "2021-08-16", stop: "2021-08-22" }, 5: { start: "2021-08-23", stop: "2021-08-29" }, 6: { start: "2021-08-30", stop: "2021-09-05" }, 7: { start: "2021-09-06", stop: "2021-09-12" }, 8: { start: "2021-09-13", stop: "2021-09-19" }, 9: { start: "2021-09-20", stop: "2021-09-26" }, 10: { start: "2021-09-27", stop: "2021-10-03" }, 11: { start: "2021-10-04", stop: "2021-10-10" }, 12: { start: "2021-10-11", stop: "2021-10-17" }, 13: { start: "2021-10-18", stop: "2021-10-24" }, 14: { start: "2021-10-25", stop: "2021-10-31" }, 15: { start: "2021-11-01", stop: "2021-11-07" }, exam: { start: "2021-10-21", stop: "2021-10-31" }, }, 3211: { 0: { start: "2021-03-01", stop: "2021-03-07" }, 1: { start: "2021-03-08", stop: "2021-03-14" }, 2: { start: "2021-03-15", stop: "2021-03-21" }, 3: { start: "2021-03-22", stop: "2021-03-28" }, 4: { start: "2021-03-29", stop: "2021-04-04" }, 5: { start: "2021-04-12", stop: "2021-04-18" }, 6: { start: "2021-04-19", stop: "2021-04-25" }, 7: { start: "2021-04-26", stop: "2021-05-02" }, 8: { start: "2021-05-03", stop: "2021-05-09" }, 9: { start: "2021-05-10", stop: "2021-05-16" }, 10: { start: "2021-05-17", stop: "2021-05-23" }, 11: { start: "2021-05-24", stop: "2021-05-30" }, 12: { start: "2021-05-31", stop: "2021-06-06" }, 13: { start: "2021-06-07", stop: "2021-06-13" }, 14: { start: "2021-06-14", stop: "2021-06-20" }, 15: { start: "2021-06-21", stop: "2021-06-27" }, exam: { start: "2021-06-10", stop: "2021-06-19" }, }, "3215QCM": { 0: { start: "2021-07-12", stop: "2021-07-18" }, 1: { start: "2021-07-19", stop: "2021-07-25" }, 2: { start: "2021-07-26", stop: "2021-08-01" }, 3: { start: "2021-08-02", stop: "2021-08-08" }, 4: { start: "2021-08-09", stop: "2021-08-15" }, 5: { start: "2021-08-16", stop: "2021-08-22" }, 6: { start: "2021-08-30", stop: "2021-09-05" }, 7: { start: "2021-09-06", stop: "2021-09-12" }, 8: { start: "2021-09-13", stop: "2021-09-19" }, 9: { start: "2021-09-20", stop: "2021-09-26" }, 10: { start: "2021-10-04", stop: "2021-10-10" }, 11: { start: "2021-10-11", stop: "2021-10-17" }, 12: { start: "2021-10-18", stop: "2021-10-24" }, 13: { start: "2021-10-25", stop: "2021-10-31" }, 14: { start: "2021-11-01", stop: "2021-11-07" }, 15: { start: "2021-11-08", stop: "2021-11-14" }, exam: { start: "2021-10-30", stop: "2021-11-13" }, }, "3211QCM": { 0: { start: "2021-02-22", stop: "2021-02-28" }, 1: { start: "2021-03-01", stop: "2021-03-07" }, 2: { start: "2021-03-08", stop: "2021-03-14" }, 3: { start: "2021-03-15", stop: "2021-03-21" }, 4: { start: "2021-03-22", stop: "2021-03-29" }, 5: { start: "2021-03-29", stop: "2021-04-04" }, 6: { start: "2021-04-12", stop: "2021-04-18" }, 7: { start: "2021-04-19", stop: "2021-04-25" }, 8: { start: "2021-04-26", stop: "2021-05-02" }, 9: { start: "2021-05-10", stop: "2021-05-16" }, 10: { start: "2021-05-17", stop: "2021-05-23" }, 11: { start: "2021-05-24", stop: "2021-05-30" }, 12: { start: "2021-05-31", stop: "2021-06-06" }, 13: { start: "2021-06-07", stop: "2021-03-13" }, 14: { start: "2021-06-14", stop: "2021-03-20" }, 15: { start: "2021-06-21", stop: "2021-03-26" }, exam: { start: "2021-06-12", stop: "2021-06-26" }, }, 2201: { 0: { start: "2020-02-24", stop: "2020-03-01" }, 1: { start: "2020-03-02", stop: "2020-03-08" }, 2: { start: "2020-03-09", stop: "2020-03-15" }, 3: { start: "2020-03-16", stCop: "2020-03-22" }, 4: { start: "2020-03-23", stop: "2020-03-29" }, 5: { start: "2020-03-30", stop: "2020-04-05" }, 6: { start: "2020-04-06", stop: "2020-04-12" }, 7: { start: "2020-04-13", stop: "2020-04-19" }, 8: { start: "2020-04-20", stop: "2020-04-26" }, 9: { start: "2020-04-27", stop: "2020-05-03" }, 10: { start: "2020-05-04", stop: "2020-05-10" }, 11: { start: "2020-05-11", stop: "2020-05-17" }, 12: { start: "2020-05-18", stop: "2020-05-24" }, 13: { start: "2020-05-25", stop: "2020-05-31" }, 14: { start: "2020-06-01", stop: "2020-06-05" }, exam: { start: "2020-06-01", stop: "2020-06-05" }, }, 2203: { 0: { start: "2020-05-25", stop: "2020-05-31" }, 1: { start: "2020-06-01", stop: "2020-06-07" }, 2: { start: "2020-06-08", stop: "2020-06-14" }, 3: { start: "2020-06-15", stop: "2020-06-21" }, 4: { start: "2020-06-22", stop: "2020-06-28" }, 5: { start: "2020-06-29", stop: "2020-07-05" }, 6: { start: "2020-07-06", stop: "2020-07-12" }, 7: { start: "2020-07-13", stop: "2020-07-19" }, 8: { start: "2020-07-20", stop: "2020-07-26" }, 9: { start: "2020-07-27", stop: "2020-08-02" }, 10: { start: "2020-08-03", stop: "2020-08-09" }, 11: { start: "2020-08-10", stop: "2020-05-17" }, 12: { start: "2020-08-17", stop: "2020-05-24" }, 13: { start: "2020-08-24", stop: "2020-05-31" }, 14: { start: "2020-08-31", stop: "2020-09-06" }, exam: { start: "2020-08-31", stop: "2020-09-04" }, }, 2205: { 0: { start: "2020-08-24", stop: "2020-09-30" }, 1: { start: "2020-08-31", stop: "2020-09-06" }, 2: { start: "2020-09-07", stop: "2020-09-13" }, 3: { start: "2020-09-14", stop: "2020-09-20" }, 4: { start: "2020-09-21", stop: "2020-09-27" }, 5: { start: "2020-09-28", stop: "2020-10-04" }, 6: { start: "2020-10-05", stop: "2020-10-11" }, 7: { start: "2020-10-12", stop: "2020-10-19" }, 8: { start: "2020-10-19", stop: "2020-10-25" }, 9: { start: "2020-10-26", stop: "2020-11-01" }, 10: { start: "2020-11-02", stop: "2020-11-08" }, 11: { start: "2020-11-09", stop: "2020-11-15" }, 12: { start: "2020-11-16", stop: "2020-11-22" }, 13: { start: "2020-11-23", stop: "2020-11-29" }, 14: { start: "2020-11-30", stop: "2020-12-06" }, 15: { start: "2020-12-07", stop: "2020-12-13" }, exam: { start: "2020-12-07", stop: "2020-12-13" }, }, 2207: { 0: { start: "2020-11-23", stop: "2020-11-29" }, 1: { start: "2020-11-30", stop: "2020-12-06" }, 2: { start: "2020-12-07", stop: "2020-12-13" }, 3: { start: "2020-12-14", stop: "2020-12-20" }, 4: { start: "2020-12-21", stop: "2020-12-27" }, 5: { start: "2020-12-28", stop: "2021-01-03" }, 6: { start: "2021-01-04", stop: "2021-01-10" }, 7: { start: "2021-01-11", stop: "2021-01-17" }, 8: { start: "2021-01-18", stop: "2021-01-24" }, 9: { start: "2021-01-25", stop: "2021-01-31" }, 10: { start: "2021-02-01", stop: "2021-02-07" }, 11: { start: "2021-02-08", stop: "2021-02-14" }, 12: { start: "2021-02-15", stop: "2021-02-21" }, 13: { start: "2021-02-22", stop: "2021-02-28" }, 14: { start: "2021-03-01", stop: "2021-03-07" }, 15: { start: "2021-03-08", stop: "2021-03-14" }, exam: { start: "2021-03-01", stop: "2021-03-07" }, }, 3208: { 0: { start: "2020-10-26", stop: "2020-11-01" }, 1: { start: "2020-11-02", stop: "2020-11-08" }, 2: { start: "2020-11-09", stop: "2020-11-15" }, 3: { start: "2020-11-16", stop: "2020-11-22" }, 4: { start: "2020-11-23", stop: "2020-11-29" }, 5: { start: "2020-11-30", stop: "2020-12-06" }, 6: { start: "2020-12-07", stop: "2020-12-13" }, 7: { start: "2020-12-14", stop: "2020-12-20" }, 8: { start: "2021-01-04", stop: "2021-01-10" }, 9: { start: "2021-01-11", stop: "2021-01-17" }, 10: { start: "2021-01-18", stop: "2021-01-24" }, 11: { start: "2021-01-25", stop: "2021-01-31" }, 12: { start: "2021-02-01", stop: "2021-02-07" }, 13: { start: "2021-02-08", stop: "2021-02-14" }, exam: { start: "2021-02-08", stop: "2021-02-20" }, }, 3205: { 0: { start: "2020-07-06", stop: "2020-07-12" }, 1: { start: "2020-07-13", stop: "2020-07-19" }, 2: { start: "2020-07-20", stop: "2020-08-26" }, 3: { start: "2020-07-27", stop: "2020-08-02" }, 4: { start: "2020-08-03", stop: "2020-08-16" }, 5: { start: "2020-08-17", stop: "2020-08-23" }, 6: { start: "2020-08-24", stop: "2020-08-30" }, 7: { start: "2020-08-31", stop: "2020-09-06" }, 8: { start: "2020-09-07", stop: "2020-09-13" }, 9: { start: "2020-09-14", stop: "2020-09-20" }, 10: { start: "2020-09-21", stop: "2020-09-27" }, 11: { start: "2020-09-28", stop: "2020-10-04" }, 12: { start: "2020-10-05", stop: "2020-10-11" }, 13: { start: "2020-10-12", stop: "2020-10-18" }, 14: { start: "2020-10-19", stop: "2020-10-25" }, 15: { start: "2020-10-27", stop: "2020-11-01" }, exam: { start: "2020-10-12", stop: "2020-10-18" }, }, 3201: { 0: { start: "2020-02-17", stop: "2020-02-23" }, 1: { start: "2020-02-24", stop: "2020-03-01" }, 2: { start: "2020-03-02", stop: "2020-03-08" }, 3: { start: "2020-03-09", stop: "2020-03-15" }, 4: { start: "2020-03-16", stop: "2020-03-22" }, 5: { start: "2020-03-23", stop: "2020-03-29" }, 6: { start: "2020-03-30", stop: "2020-04-05" }, 7: { start: "2020-04-13", stop: "2020-04-19" }, 8: { start: "2020-04-20", stop: "2020-04-26" }, 9: { start: "2020-04-27", stop: "2020-05-03" }, 10: { start: "2020-05-04", stop: "2020-05-10" }, 11: { start: "2020-05-11", stop: "2020-05-17" }, 12: { start: "2020-05-18", stop: "2020-05-24" }, 13: { start: "2020-05-25", stop: "2020-05-31" }, exam: { start: "2020-06-01", stop: "2020-06-07" }, }, 3198: { 0: { start: "2019-10-21", stop: "2019-10-27" }, 1: { start: "2019-10-28", stop: "2019-11-03" }, 2: { start: "2019-11-04", stop: "2019-11-10" }, 3: { start: "2019-11-11", stop: "2019-11-17" }, 4: { start: "2019-11-18", stop: "2019-11-24" }, 5: { start: "2019-11-25", stop: "2019-12-1" }, 6: { start: "2019-12-02", stop: "2019-12-08" }, 7: { start: "2019-12-09", stop: "2019-12-15" }, 8: { start: "2019-12-16", stop: "2019-12-22" }, 9: { start: "2020-01-06", stop: "2020-01-12" }, 10: { start: "2020-01-13", stop: "2020-01-19" }, 11: { start: "2020-01-20", stop: "2020-01-26" }, 12: { start: "2020-01-27", stop: "2020-02-02" }, 13: { start: "2020-02-03", stop: "2020-02-09" }, exam: { start: "2020-02-06", stop: "2020-02-15" }, }, 2197: { 0: { start: "2019-11-18", stop: "2019-11-24" }, 1: { start: "2019-11-25", stop: "2019-12-01" }, 2: { start: "2019-12-02", stop: "2019-12-08" }, 3: { start: "2019-12-09", stop: "2019-12-15" }, 4: { start: "2019-12-16", stop: "2019-12-22" }, 5: { start: "2019-12-23", stop: "2019-09-29" }, 6: { start: "2019-12-30", stop: "2020-01-05" }, 7: { start: "2020-01-06", stop: "2020-01-12" }, 8: { start: "2020-01-13", stop: "2020-01-19" }, 9: { start: "2020-01-20", stop: "2020-01-26" }, 10: { start: "2020-01-27", stop: "2020-02-02" }, 11: { start: "2020-02-03", stop: "2020-02-09" }, 12: { start: "2020-02-10", stop: "2020-02-16" }, 13: { start: "2019-02-17", stop: "2020-02-23" }, 14: { start: "2020-02-24", stop: "2020-03-01" }, 15: { start: "2020-03-02", stop: "2020-03-08" }, }, 2195: { 0: { start: "2019-08-19", stop: "2019-09-25" }, 1: { start: "2019-08-26", stop: "2019-09-01" }, 2: { start: "2019-09-02", stop: "2019-09-18" }, 3: { start: "2019-09-09", stop: "2019-09-15" }, 4: { start: "2019-09-16", stop: "2019-09-22" }, 5: { start: "2019-09-23", stop: "2019-09-29" }, 6: { start: "2019-09-30", stop: "2019-10-06" }, 7: { start: "2019-10-07", stop: "2019-10-13" }, 8: { start: "2019-10-14", stop: "2019-08-20" }, 9: { start: "2019-10-21", stop: "2019-10-27" }, 10: { start: "2019-10-28", stop: "2019-11-03" }, 11: { start: "2019-11-04", stop: "2019-11-10" }, 12: { start: "2019-11-11", stop: "2019-11-17" }, 13: { start: "2019-11-18", stop: "2019-11-24" }, 14: { start: "2019-11-25", stop: "2019-12-01" }, 15: { start: "2019-10-07", stop: "2019-10-13" }, }, 3195: { 0: { start: "2019-07-01", stop: "2019-07-07" }, 1: { start: "2019-07-08", stop: "2019-07-14" }, 2: { start: "2019-07-15", stop: "2019-07-21" }, 3: { start: "2019-07-22", stop: "2019-07-28" }, 4: { start: "2019-07-29", stop: "2019-08-04" }, 5: { start: "2019-08-05", stop: "2019-08-11" }, 6: { start: "2019-08-19", stop: "2019-08-25" }, 7: { start: "2019-08-26", stop: "2019-09-01" }, 8: { start: "2019-09-02", stop: "2019-09-08" }, 9: { start: "2019-09-09", stop: "2019-09-15" }, 10: { start: "2019-09-16", stop: "2019-09-22" }, 11: { start: "2019-09-23", stop: "2019-09-29" }, 12: { start: "2019-09-30", stop: "2019-10-06" }, 13: { start: "2019-10-07", stop: "2019-10-13" }, 14: { start: "2019-10-14", stop: "2019-10-20" }, 15: { start: "2019-10-21", stop: "2019-10-27" }, exam: { start: "2019-10-10", stop: "2019-10-19" }, }, 3191: { 0: { start: "2019-02-18", stop: "2019-02-24" }, 1: { start: "2019-02-25", stop: "2019-03-03" }, 2: { start: "2019-03-04", stop: "2019-03-10" }, 3: { start: "2019-03-11", stop: "2019-03-17" }, 4: { start: "2019-03-18", stop: "2019-03-24" }, 5: { start: "2019-03-25", stop: "2019-03-31" }, 6: { start: "2019-04-01", stop: "2019-04-07" }, 7: { start: "2019-04-08", stop: "2019-04-14" }, 8: { start: "2019-04-22", stop: "2019-04-28" }, 9: { start: "2019-04-29", stop: "2019-05-05" }, 10: { start: "2019-05-06", stop: "2019-05-12" }, 11: { start: "2019-05-13", stop: "2019-05-19" }, 12: { start: "2019-05-20", stop: "2019-05-26" }, 13: { start: "2019-05-27", stop: "2019-06-02" }, 14: { start: "2019-06-03", stop: "2019-06-09" }, 15: { start: "2019-06-10", stop: "2019-06-17" }, exam: { start: "2019-05-30", stop: "2019-06-08" }, }, }; TERM_DATES[2222] = TERM_DATES[3221]; TERM_DATES[2224] = TERM_DATES[3225]; TERM_DATES[2226] = TERM_DATES[3228]; // TERM/YEAR specify default period // SET_DATE is used for testing activePic, specify a date strong for now var TERM = "3215", DEFAULT_YEAR = 2021, SET_DATE = ""; var MONTHS = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; const MONTHS_HASH = { Jan: 0, January: 0, Feb: 1, February: 1, Mar: 2, March: 2, Apr: 3, April: 3, May: 4, Jun: 5, June: 5, Jul: 6, July: 6, Aug: 7, August: 7, Sep: 8, September: 8, Oct: 9, October: 9, Nov: 10, November: 10, Dec: 11, December: 11, }; // kludge to parse card image when Blackboard inserts one of its icons const BBIMG = "/images/ci/icons/cmlink_generic.gif"; // Interface design from https://codepen.io/njs/pen/BVdwZB // TEMPLATES - by 6 // define the template types const NUM_TEMPLATES = 6, HORIZONTAL = 0, // original 3 cards per row VERTICAL = 1, // 1 card per row HORIZONTAL_NOENGAGE = 2, // original, but no engage PEOPLE = 5, ASSESSMENT = 6; // horizontal but show off people (BCI) version // Whether the images will be hidden var HIDE_IMAGES = false; // Whether or not xAPI logging is turned on // - turned on by adding "logging" to Card Interface var LOGGING = false; // Define the wrapper around the card interface var interfaceHtmlTemplate = Array(NUM_TEMPLATES); // Kludge - hard code CSS path to enable shifting from // dev to live //var CARDS_CSS="https://djon.es/gu/cards.css"; var CARDS_CSS = "https://s3.amazonaws.com/filebucketdave/banner.js/cards.css"; interfaceHtmlTemplate[HORIZONTAL] = `
{DESCRIPTION}
{LINK_ITEM} {EDIT_ITEM}{ASSESSMENT_TYPE}
Learning outcomes: {LEARNING_OUTCOMES}
Engage
'; linkItemHtmlTemplate[VERTICAL] = ""; /*`Do you need to update the Card Interface tweak code?
Are your card images appearing in wrong place (in the description)? This solution might help
Changes to this item may stop the Card Interface from working.
If this item is hidden the Card Interface will not work.
You can add content to the Card Interface item using the Blackboard content editor. But the cards will always appear after your content.
but checks with. */ const CARD_METADATA_FIELDS = [ "card label", "card number", "card date", "card date label", "card engage", "card coming soon", "card coming soon label", "assessment type", "assessment weighting", "assessment outcomes", "card image", "card image iframe", "card image background", "card image size", "card image active", ]; function extractCardMetaData(descriptionObject) { // define hash to put values into it let metaDataValues = {}; let description = jQuery(descriptionObject).html(); // remove new lines from description description = description.replace(/(?:\r\n|\r|\n)/g, " "); // break up description into collection of ps and focus // use outerHTML to get the surrounding
etc so that it can be removed from // the description // TODO: Does this change screw up the complex shit that other people can // do when they use line breaks, include HTML etc let elementHtmlObjects = jQuery(descriptionObject).find("p"); let elementContent = jQuery(elementHtmlObjects) .toArray() .map((x) => x.innerHTML); let tmpMetaData = []; //console.log("----------------------- extractCardMetaData"); // check and break up the ps into individual bits of meta data let maxLength = elementContent.length; for (i = 0; i < maxLength; i++) { // console.log(` _____________ working on para ${i} == ${elementContent[i]}`); // work on a temp copy of description //let partialDescription = elementContent[i].innerHTML; let partialDescription = elementContent[i]; // get rid of newlines (definitely needed) partialDescription = partialDescription.replace(/(?:\r\n|\r|\n)/g, " "); CARD_METADATA_FIELDS.forEach(function (element) { // search for the element, but initially assume that there is another // metadata variable within the current item (i.e.
) // This happens when a
between metadata // look for element, followed by a card metadata let re = new RegExp( "(" + element + "\\s*:\\s*.*)cards+(?:label|number|date|date label|image size|image active)[^:]*:", "mi" ); let m = partialDescription.match(re); // if not, check for assessment if (!m) { re = new RegExp( "(" + element + "\\s*:\\s*.*)assessments+(?:type|weighting|outcomes)[^:]*:", "mi" ); m = partialDescription.match(re); } // if found, then we need extract just the matched element, leaving // the rest for a later iteration if (m) { // console.log(` -- found partial Descripiton match ${m[1]}`); // remove match from partialDescription, leaving any other potential // card stuff there for later (hence why m[1], not m[0]) partialDescription = partialDescription.replace(m[1], ""); // remove the match from the broader description //description = description.replace(m[1],''); // TODO does raise the question of why m[0] okay here description = description.replace(m[1], ""); // added element for later processing - but remove the tmpMetaData.push(m[1].replace(/ /gi, " ")); } else { // the
contains just the one metadata, replace the whole para // bad at RE, so check if it's the last one // console.log(" -- bad RE???"); re = new RegExp("(" + element + "\\s*:\\s*.*)", "mi"); // re = new RegExp( "
// description = description.replace(m[1],''); description = description.replace(m[1], ""); // added element for later processing - but remove any tmpMetaData.push(m[1].replace(/ /gi, " ")); } else { //console.log(` Search for ${element} no match`); } } }); } // console.log("---------------------- Finished parsing Ps"); // console.log(tmpMetaData); // At this stage tmpMetaData contains "html" for each card meta data // format should be "card label: value" // Loop thru each tmpMetaData element and extract value appropriately // place in an object label -> value for (i = 0; i < tmpMetaData.length; i++) { // extract the metaData label m[1] and value m[2] let re = new RegExp("\\s*(card\\s*[^:]*)\\s*:\\s*(.*)", "im"); let m = tmpMetaData[i].match(re, "im"); // didn't find a card value, try one of the assessment ones if (!m) { re = new RegExp("\\s*(assessment\\s*[^:]*)\\s*:\\s*(.*)", "im"); m = tmpMetaData[i].match(re, "im"); } if (m) { // extract label and value // ensure label matches METADATA name archetypes let label = m[1].trim().replace(/\\s*/, " ").toLowerCase(); let value = m[2]; // make sure the HTML in value is valid let div = document.createElement("div"); div.innerHTML = value; let newValue = div.innerHTML; metaDataValues[label] = newValue; } else { } } // used to edit the description element and ensure that it is correct HTML let div = document.createElement("div"); div.innerHTML = description; // not used in inlineImage (yet) // Aim here is to what? // - find any image that has a title Card Image **this one** // - set any inline image to have a card title Card Image const regex = /^card image$/i; let inlineImage = jQuery(descriptionObject) .find("img") .filter(function (x) { const title = jQuery(this).attr("title") || ""; //return ( typeof(title)!=="undefined" && title.match(regex) ); return title.match(regex); }); // Exclude /images/ci/icon/cmlink_generic.gif from img if (inlineImage.length && !inlineImage[0].src.includes(BBIMG)) { // we have real image // replace the card image value with the inline image metaDataValues["card image"] = inlineImage[0].src; // remove the inline image content from the description let img = jQuery(div).find(`img[src="${inlineImage[0].src}"]`).remove(); } // there may also be other .contextMenuContainer elements that will need to be removed // because Bb needs to do more work, but only does it if they are in .vtbgenerated (which cards are not) // there may be other Bb additions that need cleaning // e.g. // - TODO spans with attr data-ally-scoreindicator // remove the .contextMenuContainers from description let menuContainers = jQuery(div).find(".contextMenuContainer").remove(); // Make sure that the description is valid HTML (mostly closing tags) // jQuery handles this by default description = div.innerHTML; // remove any empty
tags from desciption description = description.replace(/
\s*<\/p>/g, ""); // add the description minus metadata to metaDataValues, for later use metaDataValues["description"] = description; return metaDataValues; } //------------------------------------------------------ // FUNCTIONS to handle card meta data changes //handleCardImage() // - given value associated with "card image", could be URL or html function handleCardImage(param) { let picUrl = "", cardBGcolour = "black"; // is it a data URI, just return it regex = /^data:((?:\w+\/(?:(?!;).)+)?)((?:;[\w\W]*?[^;])*),(.+)$/; if (regex.test(param)) { return [param, cardBGcolour]; } // check to see if it's a colour, rather than an image // TODO might need to modify identifyPicUrl to remove extraneous // lead html if there is a href?? after img src is checked?? picUrl = identifyPicUrl(param); cardBGcolour = identifyCardBackgroundColour(param); // TODO/CHECK previously there was a test to remove a trainling
from end // Maybe this should be handled in the picURL return [picUrl.trim(), cardBGcolour]; } // handleCardImageIframe // - given the HTML for an iframe, modify any height/width params // to be more responsive function handleCardImageIframe(param) { // replace the width and height // width should be 100% // height auto x = param.match(/width="[^"]+"/i); if (x) { param = param.replace(x[0], 'width="100%"'); } x = param.match(/height="[^"]+"/i); if (x) { param = param.replace(x[0], 'height="auto"'); } // also need to replace any style attributes x = param.match(/style="[^"]+"/i); if (x) { param = param.replace(x[0], ""); } return param; } // handleCardImageSize // - return contain if set function handleCardImageSize(param) { if (param.includes("contain")) { return "contain"; } return ""; } //************************************************** // handleCardDate( description ) // - given a description for an item find and parse Card Date // - return an object that has two members // - start - start or only date {date:??,month:??} // - stop - end date // Options include // - specify specific date by text // Card Date: Mar 5 // - specify date by text and time // Card Date: HH:MM Mar 5 // - specify date by week of Griffith term (monday) // Card Date: Week 1 // - specify date by week of Griffith term (monday) and time // Card Date: HH:MM Week 1 // - specify a date range // Card Date: Mar 5-Mar 10 // Card Date: Week 3-5 // - specify a day of the week // Card Date: Monday Week 5 // Card Date: Mon Week 5 // - specify a day of the week // Add HH::MM to the front of the above function handleCardDate(param) { let empty1 = { date: "", week: "" }; let empty2 = { date: "", week: "" }; let date = { start: empty1, stop: empty2 }; // object to return param = param.replace(/<[^>]+>/, ""); // is it a range (i.e. contain a -) let m = param.match(/^(.*)-(.*)$/); if (m) { // get first date and break it down date.start = parseDate(m[1]); // get second date and break it down // TODO Week 3-5 results in m[2] being just 5 (need to add week) // m[2]==int then add week date.stop = parseDate(m[2].trim(), true); // if ( /^\+?(0|[1-9]\d*)$/.test(m[2].trim()) ) { // m[2] = "Week ".concat(m[2].trim()); // } // date.stop = parseDate(m[2]); if ( typeof date.start !== "undefined" && "stop" in date && "time" in date.stop && date.stop.time === "" ) { date.stop.time = "23:59"; } } else { // not a range // get the date and break it down date.start = parseDate(param); } if (typeof date === "undefined") { return { start: empty1, stop: empty2 }; } // if no time defined, set the default (midnight) if (typeof date.start !== "undefined" && date.start.time === "") { date.start.time = "0:01"; } return date; } /** * @function parseDate * @param {String} param * @param {Boolean} endRange is the date the end of a date range? * @returns {Object} date * @description Convert string date - (HH:MM) (Week 1) (Mar 25) into date * If endRange and date is trimester week, then get the date for Friday */ function parseDate(param, endRange = false) { let date = {}; // object to return let time = ""; // check for a time at the start of the date and save it away // then add it at the end // HH:MM 24-hour format, optional leading 0, but with whitespace at end const regex = /\s*([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]\s*/; let m = param.match(regex); if (m) { // save the time time = m[0]; // remove time from param param = param.replace(regex, ""); } // a number by itself is the scond part of a week period // add week if (/^\+?(0|[1-9]\d*)$/.test(param)) { param = "Week ".concat(param); } // is it a week of trimester m = param.match(/^\s*week\s*([0-9]*)/i); if (m) { week = m[1]; if (!endRange) { // if not end of range, just get Monday date = getTermDate(week); } else { // end of range should be set to Friday date = getTermDate(week, true, "Fri"); } } else { // does it have a day of week // start date becomes start of week + number of days in m = param.match( /^\s*\b(((mon|tue(s)?|wed(nes)?|thu|thur(s)?|fri|sat(ur)?|sun)(day)?))\b\s*week *([0-9]*)\s*$/i ); if (m) { day = m[1]; week = m[m.length - 1]; date = getTermDate(week, true, day); } else { // is it the an actual date m = param.match(/ *([a-z]+) ([0-9]+)/i); if (m) { date = { month: m[1], date: m[2], year: DEFAULT_YEAR }; } //else { // Fall back to check for exam period // m = param.match(/ *exam *(period)*/i); // if (m) { // date.start = getTermDate('exam'); // date.stop = getTermDate('exam', false); // } // } } } if (typeof date !== "undefined") { if (time !== "") { date.time = time; } else { date.time = ""; } } return date; } // Given some HTML, remove all the HTML code, trim and return the text function cleanTrimHtml(html) { const aux = document.createElement("div"); aux.innerHTML = html; return aux.innerText.trim(); } // handleCardLabelNumber // - given hash with last number for each label type and label and number // return the appropriate [ label, number] to use for the card // - label is the label specified for the card, // - if nothing, default to module // - number specify card number, // - if nothing & nothing in numbering element set to 1, // - else set to the next value from numbering element // Labels can only ever be text // storage for the multiple label numberings used across all cards var CARD_LABEL_NUMBERING = {}; function handleCardLabelNumber(label, number) { // Handle the cases where label is // - empty - we don't want a label // - undefined - we want the default label // ensure label is empty HTML (incl as empty) trimLabel = cleanTrimHtml(label); if (trimLabel === "") { // return no label or number if the label is empty (but defined) return ["", ""]; } else if (typeof number !== "undefined" && number.match(/none/i)) { // if there is a card number and it is the word "none", then // return the label and an empty number // TODO, should this be label of trimLabel. i.e allow user defined // html to be included as part of the label? return [label, ""]; } else if (typeof label === "undefined") { // set the label to the DEFAULT if no label specified // numbering gets decided below. trimLabel = DEFAULT_CARD_LABEL; label = DEFAULT_CARD_LABEL; } // Update the numbering schemes // - no existing numbering, set to 1 // - otherwise increment existing if (!(trimLabel in CARD_LABEL_NUMBERING)) { CARD_LABEL_NUMBERING[trimLabel] = 1; } else { // if it does exist increment to next value CARD_LABEL_NUMBERING[trimLabel] += 1; } // if specific number specified, set numbering to that if (typeof number !== "undefined") { CARD_LABEL_NUMBERING[trimLabel] = parseInt(number); } return [label, CARD_LABEL_NUMBERING[trimLabel]]; } //-------------------------------- // extractCardsFromContent( myCard) // - given an array of cards (HTML) convert into a reasonabl edatastructure function extractCardsFromContent(myCards) { let items = []; // reset card numbering CARD_LABEL_NUMBERING = {}; // Loop through each card and construct the items array with card data myCards.each(function (idx) { // jQuery(this) - is the vtbgenerated div for a BbItem //------- check for any review status element review = getReviewStatus(this); // Parse the description and remove the Card Image data // vtbegenerated_div is specific to Blackboard. // But it also appears to change allwith a class to div with // the match class, hence the not[class] selector jQuery(this) .children('div.vtbegenerated_div,div:not([class=""])') .replaceWith(function () { return jQuery("
", { html: jQuery(this).html() }); }); var description = jQuery(this).html(); // - get rid of any inserted by Bb description = description.replace(/ /gi, " "); description = description.replace(/\n/gi, ""); // extract all the possible meta data let cardMetaData = extractCardMetaData(this); // now have cardMetaData with all meta data and the non meta data // description. Need to make the necessary changes based on data // loop through each of the elements (but not description) // tmp variables used to hold results before putting into single card object let bgSize = "", dateLabel = "Commencing", engageLabel = "", picUrl, cardBGcolour, cardImageBGColour = undefined; let label = DEFAULT_CARD_LABEL, activePicUrl = "", number = " ", iframe = ""; let date, comingSoon, comingSoonLabel = "Available"; let assessmentType = "", assessmentWeighting = "", assessmentOutcomes = ""; for (let index in cardMetaData) { switch (index) { case "card image background": cardImageBGColour = identifyCardBackgroundColour(cardMetaData[index]); break; case "card image": [picUrl, cardBGcolour] = handleCardImage(cardMetaData[index]); break; case "card image active": [activePicUrl, cardBGcolour] = handleCardImage(cardMetaData[index]); break; case "card image iframe": iframe = handleCardImageIframe(cardMetaData[index]); break; case "card image size": bgSize = handleCardImageSize(cardMetaData[index]); break; case "card date": date = handleCardDate(cardMetaData[index]); break; case "card date label": dateLabel = cardMetaData[index]; break; case "card engage": engageLabel = cardMetaData[index]; break; case "card coming soon": comingSoon = handleCardDate(cardMetaData[index]); break; case "card coming soon label": comingSoonLabel = cardMetaData[index]; break; case "assessment type": assessmentType = cardMetaData[index]; break; case "assessment weighting": assessmentWeighting = cardMetaData[index]; break; case "assessment outcomes": assessmentOutcomes = cardMetaData[index]; break; } } // handle card label and card number together [label, number] = handleCardLabelNumber( cardMetaData["card label"], cardMetaData["card number"] ); // description changed to remove all the meta data description = cardMetaData["description"]; // TODO is this still used? // Find any ItemDetailsHeaders that indicate the item is hidden hidden = jQuery(this) .parent() .find(".contextItemDetailsHeaders") .filter(":contains('Item is hidden from students.')"); //.siblings('contextItemDetailsHeaders') // Grab the link that the card is pointing to // need to get back to the header which is up one div, a sibling, then span var header = jQuery(this).parent().siblings(".item").find("span")[2]; var title = jQuery(header).html(), link, linkTarget = ""; //-------------------------------- // Three options for link // 1. A link on the header (e.g. content folder) // 2. No link (e.g. a content item) // 3. A link in the attached filed (content item with attached file) // This one is kludgy. e.g. doesn't handle multiple files. // Currently sets the link to the last file // TODO figure out what do with multiple files link = jQuery(header).parents("a").attr("href"); linkTarget = jQuery(header).parents("a").attr("target"); // if link is empty, must be content item if (link === undefined) { // check to see if there are attached fileds filesThere = jQuery(this) .parent() .find(".contextItemDetailsHeaders") .filter(":contains('Attached Files:')"); if (filesThere !== undefined) { // get a list of all attached files lis = jQuery(this) .parent() .find(".contextItemDetailsHeaders") .children(".detailsValue") .children("ul") .children("li"); // loop through the files and get the link lis.each(function (idx, li) { // get the link link = jQuery(li).children("a").attr("href"); }); } //.siblings('contextItemDetailsHeaders') } // get the itemId to allow for "edit" link in card var itemId = jQuery(this).parents(".liItem").attr("id"); // Hide the contentItem TODO Only do this if display page var tweak_bb_active_url_pattern = "listContent.jsp"; if (location.href.indexOf(tweak_bb_active_url_pattern) > 0) { // TODO un comment this Reviewed jQuery(this).parent().parent().hide(); //console.log( "content item " + contentItem.html()); } if (typeof cardImageBGColour !== "undefined") { cardBGcolour = cardImageBGColour; } // save the item for later var item = { title: title, picUrl: picUrl, bgSize: bgSize, cardBGcolour: cardBGcolour, description: description, date: date, label: label, link: link, linkTarget: linkTarget, review: review, dateLabel: dateLabel, engageLabel: engageLabel, id: itemId, activePicUrl: activePicUrl, comingSoon: comingSoon, comingSoonLabel: comingSoonLabel, assessmentWeighting: assessmentWeighting, assessmentOutcomes: assessmentOutcomes, assessmentType: assessmentType, }; if (number !== "x") { item.moduleNum = number; } if (iframe !== "") { item.iframe = iframe; } // only add the card to display if // - VIEW MODE is on and it's not hidden // - EDIT MODE is on if (hidden.length === 0 || LOCATION < 0) { // add message that item is hidden to students when EDIT mode on if (hidden.length === 1) { item.description = item.description.concat(HIDDEN_FROM_STUDENTS); } items.push(item); } }); //console.log(items); return items; } /** * @function removeBlackboardIcon * @param {Object} cardInterface jQuery object for where the card interface will go * @description If exists, update cardInterface to remove Blackboard icon */ function removeBlackboardIcon(cardInterface) { let container = jQuery(cardInterface).parent().parent(); // hide the icon let icon = jQuery(container).find("img.item_icon").css("display", "none"); // update the padding on the div let div = jQuery(container).find("div.details").css("padding-left", "10px"); } /**** * addCardInterface( cardInterface, items ) * - Given an array of items to translate into cards add the HTML etc * to generate the card interface * - add it to cardInterface element * */ function addCardInterface(cardInterface, items) { // Define which template to use let template = HORIZONTAL; let linkTemplate = HORIZONTAL; let engageVerb = "Engage"; // Define the text for Review Status let MARK_REVIEWED = "Mark Reviewed"; let REVIEWED = "Reviewed"; let NO_CARD_NUMBER = false; let NO_COMING_SOON = false; if (cardInterface.length === 0) { console.log( "Card: Can't find item with heading 'Card Interface' in which to insert card interface" ); return false; } else { // get the title - text only, stripped of whitespace before/after var cardInterfaceTitle = jQuery.trim(cardInterface.text()); //Extract parameters var m = cardInterfaceTitle.match(/Card Interface *([^<]*)/i); var WIDTH = "md:w-1/3"; if (m) { newParams = parse_parameters(m[1]); if (newParams) { newParams.forEach(function (element) { m = element.match(/template=["']vertical['"]/i); m1 = element.match(/template=vertical/i); if (m || m1) { template = VERTICAL; } else if (element.match(/template=['"]horizontal['"]/i)) { template = HORIZONTAL; } else if (element.match(/nocardnumber/i)) { NO_CARD_NUMBER = true; } else if (element.match(/nocomingsoon/i)) { NO_COMING_SOON = true; } else if (element.match(/noimages/i)) { HIDE_IMAGES = true; } else if ((x = element.match(/template=by([2-6])/i))) { WIDTH = "md:w-1/" + x[1]; } else if ((x = element.match(/by([2-6])/i))) { WIDTH = "md:w-1/" + x[1]; } else if ((x = element.match(/[Bb][yY]1/))) { WIDTH = "md:w-full"; } else if (element.match(/people/i)) { template = PEOPLE; } else if (element.match(/noengage/i)) { linkTemplate = HORIZONTAL_NOENGAGE; } else if (element.match(/logging/i)) { LOGGING = true; } else if ((m = element.match(/engage=([^']*)/))) { engageVerb = m[1]; } else if ((m = element.match(/template=assessment/i))) { template = ASSESSMENT; } else if ((m = element.match(/set[Dd]ate=([^\s]*)/))) { SET_DATE = m[1]; } else if ((m = element.match(/^reviewed=([^']*)/iu))) { REVIEWED = m[1]; } else if ((m = element.match(/^markReviewed=(.+)/i))) { MARK_REVIEWED = m[1]; } else if ((m = element.match(/^downloadButtonLabel=(.+)/i))) { DOWNLOAD_LABEL = m[1]; } }); } } // if no match, stay with default } // console.log("LOGGING IS " + LOGGING); // make the h3 for the Card Interface item disappear // (Can't hide the parent as then you can't edit via Bb) // Need to have the span in order to be able to reorder cardInterface.html(''); // Get the content area in which to insert the HTML var firstItem = cardInterface.parent().siblings(".details"); // Use the card HTML template and the data in items to generate // HTML for each card var cards = ""; var moduleNum = 1; items.forEach(function (idx) { let cardHtml = cardHtmlTemplate[template]; let linkHtml = linkItemHtmlTemplate[linkTemplate]; // coming soon // By default comingSoon is empty let comingSoon = ""; // TODO need to only display this if outside the date if (typeof idx.comingSoon !== "undefined" && !NO_COMING_SOON) { if (!inDateRange(idx.comingSoon, false)) { // we have coming soon and NOT in the available date range // generate the html comingSoon = generateDateHtml( comingSoonHtmlTemplate[template], dualComingSoonHtmlTemplate[template], idx.comingSoon, true ); comingSoon = comingSoon.replace( "{COMING_SOON_LABEL}", idx.comingSoonLabel ); // if students are viewing remove the link stuff if (window.tweak_bb.display_view) { // don't show an engage button linkHtml = ""; // remove the clickableCard link and hover shadow cardHtml = cardHtml .replace("clickablecard", "") .replace("hover:outline-none hover:shadow-outline ", ""); } } } cardHtml = cardHtml.replace("{COMING_SOON}", comingSoon); // TODO either here, or above in the link section need to remove // the link cardHtml = cardHtml.replace("{WIDTH}", WIDTH); // replace the default background colour if a different one // is specific if (idx.cardBGcolour) { cardHtml = cardHtml.replace( /background-color:\s*rgb\(255,255,255\)/i, "background-color: " + idx.cardBGcolour ); } ///g, ""); cardHtml = cardHtml.replace("", ""); // remove the shadow/border effect cardHtml = cardHtml.replace("hover:outline-none", ""); cardHtml = cardHtml.replace("hover:shadow-outline", ""); // don't count it as a module // cardHtml = cardHtml.replace(idx.label + ' ' + moduleNum, ''); //moduleNum--; } // If there is a linkTarget in Blackboard if (typeof idx.linkTarget !== "undefined") { // replace "{LINK}" with "{LINK}" target="linkTarget" cardHtml = cardHtml.replace( /"{LINK}"/g, '"{LINK}" target="' + idx.linkTarget + '"' ); } if (typeof idx.link !== "undefined") { cardHtml = cardHtml.replace(/{LINK}/g, idx.link); } else { cardHtml = cardHtml.replace( //g, "" ); cardHtml = cardHtml.replace(/class="clickablecard /, 'class="'); } // Should we add a link to edit/view the original content if (location.href.indexOf("listContentEditable.jsp") > 0) { editLink = editLinkTemplate.replace("{ID}", idx.id); cardHtml = cardHtml.replace(/{EDIT_ITEM}/, editLink); } else { //cardHtml = cardHtml.replace(/{EDIT_ITEM}/,''); //editLink = editLinkTemplate.replace('{ID}', idx.id); editLink = ''; cardHtml = cardHtml.replace(/{EDIT_ITEM}/, editLink); } // standard date let date = ""; date = generateDateHtml( dateHtmlTemplate[template], dualDateHtmlTemplate[template], idx.date ); date = date.replace("{DATE_LABEL}", idx.dateLabel); cardHtml = cardHtml.replace("{DATE}", date); // add the individual card html to the collection cards = cards.concat(cardHtml); }); // STick the cards into the complete card HTML var interfaceHtml = interfaceHtmlTemplate[template]; interfaceHtml = interfaceHtml.replace("{CARDS}", cards); // Insert the HTML to the selected item(s) //return false; jQuery(firstItem).append(interfaceHtml); } /** * @function to12 * @param {String} t 24 hour * @returns {String} time converted to 12 hour with am/pm */ function to12(t) { if (typeof t === "undefined") { return ""; } // break home and set hh, m const regex = /^\s*([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])\s*/; let m = t.match(regex); // we have a 24 hour time, convert it if (m) { let h, hh, mins, dd = "AM"; hh = parseInt(m[1]); mins = parseInt(m[2]); h = hh; // set PM if (h >= 12) { h = hh - 12; dd = "PM"; } if (h == 0) { h = 12; } if (mins < 10) { mins = `0${mins}`; } return `${h}:${mins} ${dd}`; } // not a 24 hour time show nothing return ""; } /** * @function generateDateHtml * @params singleTemplate {String} HTML for a single date * @params dualTemplate {String} HTML for a dual date * @params date {Object} the date data structure * @params comingSoon {Boolean} indicate if it's a comingSoon date * @description parse the date object and use the correct template to * construct date html to be added to the card */ function generateDateHtml( singleTemplate, dualTemplate, date, comingSoon = false ) { // by default no html let cardHtml = ""; if ( typeof date !== "undefined" && typeof date.start !== "undefined" && "month" in date.start ) { // Do we have dual dates - both start and stop? if (date.stop.month ) { // start and stop dates //cardHtml = cardHtml.replace('{DATE}', dualDateHtmlTemplate[template]); cardHtml = dualTemplate; cardHtml = cardHtml.replace(/{MONTH_START}/g, date.start.month); cardHtml = cardHtml.replace(/{DATE_START}/g, date.start.date); cardHtml = cardHtml.replace(/{MONTH_STOP}/g, date.stop.month); cardHtml = cardHtml.replace(/{DATE_STOP}/g, date.stop.date); cardHtml = cardHtml.replace(/{TIME_STOP}/g, to12(date.stop.time)); cardHtml = cardHtml.replace(/{TIME_START}/g, to12(date.start.time)); //-- handle the {WEEK} variable for dual dates if (!date.start.hasOwnProperty("week")) { // no start week, so week is empty cardHtml = cardHtml.replace("{WEEK}", ""); } else { // if exam, use that template // other wise construct dual week let weekHtml = examPeriodTemplate; if (date.start.week !== "exam") { // not exam, then set to dualWeekHtml if ( date.start.week!==date.stop.week) { weekHtml = dualWeekHtmlTemplate.replace( "{WEEK_START}", date.start.week); weekHtml = weekHtml.replace("{WEEK_STOP}", date.stop.week); } else { // same week, so using single week template weekHtml = weekHtmlTemplate.replace("{WEEK}", `Week ${date.start.week}`); } } cardHtml = cardHtml.replace("{WEEK}", weekHtml); } // handle the {TIME} variable for dual dates const defaultStartTime = date.start.time === "0:01"; const defaultStopTime = date.stop.time === "23:59"; if (defaultStartTime && defaultStopTime) { // default time just remove {TIME} cardHtml = cardHtml.replace("{TIME}", ""); } else { // show the non default time let timeHtml = dualTimeHtmlTemplate; timeHtml = timeHtml.replace("{TIME_START}", to12(date.start.time)); timeHtml = timeHtml.replace("{TIME_STOP}", to12(date.stop.time)); cardHtml = cardHtml.replace("{TIME}", timeHtml); } // handle the specific days for dual dates const startDay = "day" in date.start; const stopDay = "day" in date.stop; if ((!startDay && !stopDay) || (!startDay && date.stop.day === "Fri")) { // no specific days, don't add days cardHtml = cardHtml.replace("{DAYS}", ""); } else { // at least one specific day let dayHtml = dualDayHtmlTemplate; if (startDay) { dayHtml = dayHtml.replace("{DAY_START}", date.start.day); } else { dayHtml = dayHtml.replace("{DAY_START}", "Mon"); } if (stopDay) { dayHtml = dayHtml.replace("{DAY_STOP}", date.stop.day); } else { dayHtml = dayHtml.replace("{DAY_STOP}", "Fri"); } cardHtml = cardHtml.replace("{DAYS}", dayHtml); } } else { // just start date //cardHtml = cardHtml.replace('{DATE}', dateHtmlTemplate[template]); cardHtml = singleTemplate; cardHtml = cardHtml.replace(/{MONTH}/g, date.start.month); cardHtml = cardHtml.replace(/{DATE}/g, date.start.date); // not yet // cardHtml = cardHtml.replace(/{TIME}/g, to12(date.start.time)); // cardHtml = cardHtml.replace(/{DATE_LABEL}/g, idx.dateLabel); if (!date.start.hasOwnProperty("week")) { cardHtml = cardHtml.replace("{WEEK}", ""); } else { // SKETCHY TODO change added block around else let weekReplace = "Week " + date.start.week; if ("day" in date.start) { weekReplace = date.start.day + " " + weekReplace; } let weekHtml = weekHtmlTemplate.replace("{WEEK}", weekReplace); cardHtml = cardHtml.replace("{WEEK}", weekHtml); } // handle the {TIME} variable for single date if ("time" in date.start) { // comingSoon dates include the default time if (!comingSoon && date.start.time === "0:01") { // default time just remove {TIME} cardHtml = cardHtml.replace("{TIME}", ""); } else { // show the non default time if (!comingSoon) { let timeHtml = timeHtmlTemplate; timeHtml = timeHtml.replace("{TIME_START}", to12(date.start.time)); cardHtml = cardHtml.replace("{TIME}", timeHtml); } else { cardHtml = cardHtml.replace("{TIME}", to12(date.start.time)); } } } } } return cardHtml; } /** * @function inDateRange * @param cardDate {Object} card.date object * @param assumeStop {Boolean} true if assuming a stop date if one not specified * @returns {Boolean} true if the current time (or SET_DATE) is within the * date range */ function inDateRange(cardDate, assumeStop = true) { if (typeof cardDate !== "undefined") { let start, stop, now; // Set now to current date OR SET_DATE if we want to do testing if (SET_DATE === "") { now = new Date(); } else { now = new Date(SET_DATE); } // set the start date if (cardDate.start.hasOwnProperty("month") && cardDate.start.month !== "") { start = convertToDate(cardDate.start); } // set the card stop date // - to card.date.stop if valid // - to the end of the week if using a week // - to the end of the day if no stop if (cardDate.stop.hasOwnProperty("month") && cardDate.stop.month !== "") { if (cardDate.stop.time === "") { cardDate.stop.time = "23:59"; } stop = convertToDate(cardDate.stop); } else if ( cardDate.start.hasOwnProperty("week") && cardDate.start.week !== "" ) { // there's no end date, but there is a start week // so set stop to end of that week, but only if inWeek is true if (cardDate.start.week in TERM_DATES[TERM]) { if (assumeStop) { stop = new Date(TERM_DATES[TERM][cardDate.start.week].stop); stop.setHours(23, 59, 0); } } else { // problem with week, just set it to end of date if (typeof start !== "undefined" && assumeStop) { stop = new Date(start.getTime()); stop.setHours(23, 59, 0); } } /* } else { // no week for stop, meaning it's just on the day stop = new Date(start.getTime()); stop.setHours(23, 59, 0); */ } // figure out if we're in range if (typeof stop !== "undefined") { // if stop defined, check in range return now >= start && now <= stop; } else { // check passed start return now >= start; } } return false; } /** * @function convertToDate * @param {Object} dateObj * @returns {Date} Javascript date object * Converts the simple date object into a Javascript date object */ function convertToDate(dateObj) { // check for valid month?? let date = new Date( dateObj.year, MONTHS_HASH[dateObj.month], parseInt(dateObj.date) ); // if time set time if ("time" in dateObj && dateObj.time !== "") { // split into hours minutes let m = dateObj.time.match( /^\s*([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])\s*/ ); if (m) { date.setHours(m[1], m[2], 0); } } return date; } //********************* // getTermDate( week, day ) // - given a week of Griffith semester return date for the // start of that week // - optional pass day of the week, add more days Monday=0 function getTermDate(week, startWeek = true, dayOfWeek = "Monday") { if (typeof TERM_DATES[TERM] === "undefined") { return undefined; } dayOfWeek = dayOfWeek.toLowerCase(); //console.log("TERM is " + TERM + " week is " + week); var date = { date: "", month: "", week: week, year: 0 }; if (week < 0 || week > 15) { if (week !== "exam") { return date; } } var start; if (startWeek === true) { // setting start week if (typeof TERM_DATES[TERM][week] !== "undefined") { start = TERM_DATES[TERM][week].start; //[week].start; } } else { start = TERM_DATES[TERM][week].stop; } var d = new Date(start); // if dayOfWeek is not Monday, add some days if (dayOfWeek !== "monday") { var dayToNum = { tuesday: 1, tue: 1, wednesday: 2, wed: 2, thursday: 3, thu: 3, friday: 4, fri: 4, saturday: 5, sat: 5, sunday: 6, sun: 6, }; // add in the day abbreviation so it can appear date.day = dayOfWeek.charAt(0).toUpperCase() + dayOfWeek.substr(1, 2); if (dayOfWeek in dayToNum) { d.setDate(d.getDate() + dayToNum[dayOfWeek.toLowerCase()]); } } date.month = MONTHS[d.getMonth()]; date.date = d.getDate(); date.year = d.getFullYear(); return date; } //************************************************************* // picUrl = setImage( card ) // - given card object containing information about a card // - return picUrl if no active card image // - return picUrl if there is an active card image, but it's // not the date // - return activePicUrl if there is one and it's not the date function setImage(card) { // only use activePicURL if it is set and there are dates on // the card if (card.activePicUrl !== "" && typeof card.date !== "undefined") { // there is an activePicUrl, check if it should be active // active means that the current date falls within the start/stop // dates for the card var start, stop, now; // Set now to current date OR SET_DATE if we want to do testing if (SET_DATE === "") { now = new Date(); } else { now = new Date(SET_DATE); } // set the start date if ( card.date.start.hasOwnProperty("month") && card.date.start.month !== "" ) { start = new Date( parseInt(DEFAULT_YEAR), //MONTHS.indexOf(card.date.start.month), MONTHS_HASH[card.date.start.month], parseInt(card.date.start.date) ); } // set the card stop date // - to card.date.stop if valid // - to the end of the week if using a week // - to the end of the day if no stop if (card.date.stop.hasOwnProperty("month") && card.date.stop.month !== "") { stop = new Date( DEFAULT_YEAR, MONTHS_HASH[card.date.stop.month], card.date.stop.date ); stop.setHours(23, 59, 0); } else if (card.date.start.hasOwnProperty("week")) { // there's no end date, but there is a start week // so set stop to end of that week if (card.date.start.week in TERM_DATES[TERM]) { stop = new Date(TERM_DATES[TERM][card.date.start.week].stop); stop.setHours(23, 59, 0); } else { // problem with week, just set it to end of date if (typeof start !== "undefined") { stop = new Date(start.getTime()); stop.setHours(23, 59, 0); } } } else { // no week for stop, meaning it's just on the day stop = new Date(start.getTime()); stop.setHours(23, 59, 0); } if (now >= start && now <= stop) { return card.activePicUrl; } } return card.picUrl; } //************************************************************** // cardBGcolour = identifyCardBackgroundColour( value ); // return undefined if value is not a valid CSS colour // Otherwise return rgb(X,Y,Z) function identifyCardBackgroundColour(input) { // don't both if it's an empty string or a URL (or relative URL) url = input.match(/^\s*http/i) || input.match(/^\//); if (input === "" || url) { return undefined; } var div = document.createElement("div"), m; div.style.color = input; // add to DOMTree to work document.body.appendChild(div); // extract the rgb numbers m = getComputedStyle(div).color.match( /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i ); if (m) { return "rgb(" + m[1] + "," + m[2] + "," + m[3] + ")"; } return undefined; } //************************************************************** // picUrl = identifyPicUrl( value ) // TODO - return "" if value is not a valid URI // Otherwise return the value function identifyPicUrl(value) { let re = new RegExp(/img[^>]*src="([^"]*)"/, "i"); let m = value.match(re); // found an image if (m) { // not a BBIMG, then return it if (!m[1].includes(BBIMG)) { return m[1]; } // is a BBIMG try extract the link // kludge because I couldn't get registers to work in JS REs re = new RegExp(/([^>]*)<\/a>/, "i"); m = value.match(re); if (m) { if (m[1] === m[2]) { return m[1]; } } } // is there a link to the image re = new RegExp('href="([^"]*)', "i"); m = value.match(re); // if it's a return the picUrl if (m) { return m[1]; } // remove all html and just use the text content that's left let tmp = document.createElement("DIV"); tmp.innerHTML = value; value = tmp.textContent || tmp.innerText || ""; // must be just a lone URL TODO check it actually does return value; } //----------------------------------------------------------------- // getReviewStatus // - given a vtbgenerated item from Bb Item, check to see if the // parent div contains a review status element (anchor with class // button-5) // - if not return NULL // - if there is one return the link (which indicates with it's // mark reviewed, or reviewed) function getReviewStatus(vtbgen) { // get parent var parent = jQuery(vtbgen).parent(); // check to see if it has the anchor with class button-5 review = jQuery(parent).find("a.button-5"); if (review.length === 0) { return undefined; } else { return jQuery(review).attr("href"); } } //--------------------------------------------------------------------- // Given a string of parameters use some Stack Overflow provided // regular expression magic to split it up into its component parts function parse_parameters(cmdline) { // var re_next_arg = /^\s*((?:(?:"(?:\\.|[^"])*")|(?:'[^']*')|\\.|\S)+)\s*(.*)$/; let re_next_arg = /^\s*((?:(?:"(?:\\.|[^"])*")|(?:'[^']*')|\\.|\S)+)\s*(.*)$/; let next_arg = ["", "", cmdline]; let args = []; while ((next_arg = re_next_arg.exec(next_arg[2]))) { let quoted_arg = next_arg[1]; let unquoted_arg = ""; while (quoted_arg.length > 0) { if (/^"/.test(quoted_arg)) { let quoted_part = /^"((?:\\.|[^"])*)"(.*)$/.exec(quoted_arg); unquoted_arg += quoted_part[1].replace(/\\(.)/g, "$1"); quoted_arg = quoted_part[2]; } else if (/^'/.test(quoted_arg)) { let quoted_part = /^'([^']*)'(.*)$/.exec(quoted_arg); unquoted_arg += quoted_part[1]; quoted_arg = quoted_part[2]; } else if (/^\\/.test(quoted_arg)) { unquoted_arg += quoted_arg[1]; quoted_arg = quoted_arg.substring(2); } else { unquoted_arg += quoted_arg[0]; quoted_arg = quoted_arg.substring(1); } } args[args.length] = unquoted_arg; } return args; } /**** * function addExpandPrintButtons * - add buttons (only downloadButton) to top of card interface * - adapted from similar for Content Interface */ function addButtons() { // jQuery("#GU_ContentInterface").prepend(EXPAND_COLLAPSE_BUTTON_HTML); // show downloadButton for downloadButtonURL and downloadButtonLabel if (PARAMS.downloadButtonURL) { let tooltip = ""; let tt_class = ""; if (PARAMS.downloadButtonTip) { tooltip = `data-tooltip-content="${PARAMS.downloadButtonTip}"`; tt_class = 'class="ci-tooltip"'; addToolTipster(); // ** where's this } const download_button = ` `; // add the expand buttons - only Content Interface // insert before #guCardInterface let buttonDiv = document.createElement("div"); buttonDiv.id = "guCardButtons"; buttonDiv.style.paddingBottom="1em"; let cardInterface = document.getElementById("guCardInterface"); cardInterface.parentElement.insertBefore(buttonDiv,cardInterface) buttonDiv.insertAdjacentHTML('beforeend',download_button); // jQuery("div#guCardButtons").prepend(download_button); return true; } } /************************************************ * checkParams * - given the card interfaace element check for paraemters * - set object attributes and return it * - parameters come from both * - the title of the Content Interface content item * - a Web Link content item that has Content Document in the title */ function checkParams(cardInterface) { var paramsObj = {}; // checking parameters in the card interface title is done elsewhere /********** * * check other content items for other parameters that are Content Items * - Only look for those defined in global ITEM_LINK_PARAMETERS * - Looking for the link associated with item, what they are pointing to * - ITEM_PARAMS defines * - key - is the expected title of the Blackboard item * - element - define attribute name to add to paramsObj to contain jQuery element * to find in the Blackboard item * - item - define pramsObj attribute name for the actual value */ let ITEM_LINK_PARAMETERS = { downloadButtonURL: { element: "downloadButtonElement", item: "downloadButtonURL", }, downloadButtonTip: { element: "downloadButtonTipElement", item: "downloadButtonTip", type: "contentItem", }, }; for (let paramKey in ITEM_LINK_PARAMETERS) { const obj = ITEM_LINK_PARAMETERS[paramKey]; // element is the h3 wrapped around the link element = jQuery(tweak_bb.page_id + " > " + tweak_bb.row_element) .find(".item h3") .filter(':contains("' + paramKey + '")') .eq(0); // only if it's found if (element.length > 0) { paramsObj[obj.element] = element; // the type of content, depends on the type if (obj.type === "contentItem") { // content is element.parent.sibling(div.details) // and then remove all the vtbgenertedt_div crap let tipContent = jQuery(paramsObj[obj.element]) .parent() .next("div.details") .html(); tipContent = tipContent.replace(/class="vtbegenerated_div"/g, ""); tipContent = tipContent.replace(/class="vtbegenerated"/g, ""); tipContent = tipContent.replace(/(?:\r\n|\r|\n)/g, " "); paramsObj[obj.item] = tipContent; } else { // assume a link item paramsObj[obj.element] = element; paramsObj[obj.item] = jQuery(paramsObj[obj.element]) .children("a") .attr("href"); } // hide the elements jQuery(paramsObj[obj.element]).parents("li").hide(); } } //console.log(paramsObj); return paramsObj; } /** * function addToolTipster * - add the JS for tooltips */ function addToolTipster() { if (!TOOLTIPSTER_ADDED) { // add tooltipster if there are footnotes jQuery("head").append( "" ); getScript( //"https://cdn.jsdelivr.net/npm/tooltipster@4.2.8/dist/js/tooltipster.bundle.js", "https://cdn.jsdelivr.net/npm/tooltipster@4.2.8/dist/js/tooltipster.bundle.min.js", onScriptLoad ); TOOLTIPSTER_ADDED = true; } } // https://blog.kevinchisholm.com/javascript/jquery-getscript-alternatives/ function getScript(scriptUrl, callback) { const script = document.createElement('script'); script.src = scriptUrl; script.onload = callback; document.body.appendChild(script); } function onScriptLoad() { docWidth = Math.floor(jQuery(document).width() / 2); jQuery(".ci-tooltip").tooltipster({ maxWidth: docWidth }); }