/**
 * @file DTPS Canvas LMS Integration
 * @author jottocraft
 * 
 * @copyright Copyright (c) 2018-2023 jottocraft
 * @license MIT
 * 
 * JSDoc documentation for these LMS functions can be found near the end of core.js
 */

//DTPS LMS configuration for Canvas
var dtpsLMS = {
    name: "Canvas LMS",
    shortName: "Canvas",
    legalName: "Canvas LMS or Instructure Inc.",
    description: "Power+ integration for Canvas LMS",
    url: "https://www.instructure.com/canvas/",
    inboxURL: "/conversations",
    logo: "https://i.imgur.com/rGjNVoc.png",
    source: "https://gitlab.com/jottocraft/dtps/-/blob/main/scripts/lms/canvas.js",
    genericGradebook: true
};


/**
 * Common headers used for Canvas web requests.
 * This variable is specific to Canvas LMS integration in DTPS and is not required for other LMS integrations.
 */
dtpsLMS.commonHeaders = { Accept: "application/json+canvas-string-ids, application/json", "From": "jottocraft/dtps <ua@jottocraft.com>" };

/**
 * List of teachers from dtpsLMS.fetchClasses for use in other methods
 * This variable is specific to Canvas LMS integration in DTPS and is not required for other LMS integrations.
 */
dtpsLMS.teacherCache = {};

/**
 * A queue for API requests
 */
dtpsLMS.fetchQueue = [];

/**
 * A function that behaves the same way as fetch, although it adds the request to the queue for spacing
 */
dtpsLMS.fetchWrapper = function () {
    //Start a queue interval if it isn't already started
    if (!dtpsLMS.fetchInterval) {
        dtpsLMS.fetchInterval = setInterval(function () {
            //Run request at the start of the queue
            if (dtpsLMS.fetchQueue[0]) {
                dtpsLMS.fetchQueue[0]();
            }
        }, dtps.remoteConfig.canvasRequestSpacing);
    }

    return new Promise((resolve, reject) => {
        dtpsLMS.fetchQueue.push(() => {
            dtpsLMS.fetchQueue.shift();
            fetch(...arguments).then(function (d) {
                resolve(...arguments);
            }).catch(reject);
        });
    });
}

//Fetch userdata from Canvas
dtpsLMS.fetchUser = function () {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/users/self", { headers: dtpsLMS.commonHeaders }).then(response => {
            if (response.status !== 200) reject({ action: "login", redirectURL: "/?dtpsLogin=true" });

            return response.json();
        }).then(userData => {
            var user = {
                name: userData.name,
                id: userData.id,
                photoURL: userData.avatar_url
            };

            dtpsLMS.fetchWrapper("/api/v1/users/self/observees?include[]=avatar_url", { headers: dtpsLMS.commonHeaders }).then(response => {
                return response.json();
            }).then(childrenData => {
                if (childrenData && childrenData.length) {
                    //Parent account
                    user.children = childrenData.map(child => {
                        return {
                            name: child.name.split(" ")[0],
                            id: child.id,
                            photoURL: child.avatar_url
                        }
                    });
                }

                resolve(user);
            }).catch(reject);
        });
    })
}

//Fetch unread message count from Canvas
dtpsLMS.fetchUnreadMessageCount = function () {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/conversations/unread_count", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(data => {
            resolve(data.unread_count);
        }).catch(reject);
    });
}

//Fetch class data from Canvas
dtpsLMS.fetchClasses = function (userID) {
    return new Promise(function (resolve, reject) {
        Promise.all([
            dtpsLMS.fetchWrapper("/api/v1/users/" + dtps.user.id + "/colors", { headers: dtpsLMS.commonHeaders }),
            dtpsLMS.fetchWrapper("/api/v1/users/" + userID + "/dashboard_positions", { headers: dtpsLMS.commonHeaders }),
            dtpsLMS.fetchWrapper("/api/v1/users/" + userID + "/courses?per_page=100&enrollment_state=active&include[]=term&include[]=total_scores&include[]=account&include[]=teachers&include[]=course_image&include[]=tabs&include[]=sections", { headers: dtpsLMS.commonHeaders })
        ]).then(responses => {
            return Promise.all(responses.map(r => r.json()));
        }).then(data => {
            var [colorData, dashboardData, courseData] = data;
            var courses = [];

            //Add courses from canvas to courses array as a DTPS course object
            courseData.forEach((course, index) => {
                const forceAllClasses = fluid.get("pref-showAllClasses") == "true";
                if (!forceAllClasses) {
                    if (course.end_at && (new Date() > new Date(course.end_at))) return;
                    if (course.term?.end_at && (new Date() > new Date(course.term?.end_at))) return;
                }

                var termSegments = course.course_code.split(" - ");
                var dtpsCourse = {
                    name: course.course_code,
                    id: course.id,
                    lmsID: course.id,
                    people: !dtps.user.parent,
                    userID: userID,
                    period: course.sections && course.sections[0] && (course.sections.find(section => /[0-9](?=\(A)/.test(section.name)) || course.sections[0]).name,
                    section: course.sections && course.sections[0] && (course.sections.find(section => /[0-9](?=\(A)/.test(section.name)) || course.sections[0]).name,
                    subject: window.localStorage["pref-fullNames"] == "true" ? course.course_code : (course.original_name ? course.name : course.course_code.split(" - ")[0]),
                    homepage: course.default_view == "wiki",
                    term: termSegments[termSegments.length - 2],
                    color: colorData.custom_colors["course_" + course.id],
                    grade: course.enrollments[0].computed_current_score,
                    letter: course.enrollments[0].computed_current_grade,
                    image: course.image_download_url,
                    newDiscussionThreadURL: '/courses/' + course.id + '/discussion_topics/new',
                    pages: course.tabs.map(tab => tab.id).includes("pages"),
                    modules: course.tabs.map(tab => tab.id).includes("modules"),
                    discussions: true || course.tabs.map(tab => tab.id).includes("discussions"),
                    endDate: course.end_at,
                    startDate: course.start_at,
                    termEndDate: course.term?.end_at,
                    termStartDate: course.term?.start_at
                };

                //Save teachers in cache
                dtpsLMS.teacherCache[course.id] = course.teachers;

                if (course.teachers.length == 1) {
                    dtpsCourse.teacher = {
                        name: course.teachers[0] && course.teachers[0].display_name,
                        id: course.teachers[0] && course.teachers[0].id,
                        photoURL: course.teachers[0] && course.teachers[0].avatar_image_url
                    };
                } else {
                    var matches = 0;
                    course.teachers.forEach(teacher => {
                        teacher.display_name.split(" ").forEach((fragment) => {
                            if (dtpsCourse.name.includes(fragment)) {
                                dtpsCourse.teacher = {
                                    name: teacher.display_name,
                                    id: teacher.id,
                                    photoURL: teacher.avatar_image_url
                                };
                                matches++;
                            }
                        });
                    });
                    if (matches > 1) delete dtpsCourse.teacher;
                }

                courses.push(dtpsCourse);
            });

            //Sort courses array
            courses.sort(function (a, b) {
                var keyA = dashboardData.dashboard_positions["course_" + a.id],
                    keyB = dashboardData.dashboard_positions["course_" + b.id];

                if (keyA < keyB) return -1;
                if (keyA > keyB) return 1;
                return 0;
            });

            resolve(courses);
        }).catch(reject);
    })
}

//Fetches assignment data from Canvas
dtpsLMS.fetchAssignments = function (userID, classID) {
    return new Promise(function (resolve, reject) {
        Promise.all([
            dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/students/submissions?include[]=rubric_assessment&include[]=submission_comments&per_page=100&student_ids[]=" + userID, { headers: dtpsLMS.commonHeaders }),
            dtpsLMS.fetchWrapper("/api/v1/users/" + userID + "/courses/" + classID + "/assignments?per_page=100", { headers: dtpsLMS.commonHeaders })
        ]).then((responses) => {
            return Promise.all(responses.map(r => r.json()));
        }).then(data => {
            var [submissionData, assignmentData] = data;

            //All fetches have been completed successfully
            var assignments = [];

            //Add assignments from canvas to assignments array as a DTPS assignment object
            assignmentData.forEach((assignment, index) => {
                //Define dtpsAssignment
                var dtpsAssignment = {
                    title: assignment.name,
                    body: assignment.description,
                    id: assignment.id,
                    dueAt: assignment.due_at,
                    url: assignment.html_url,
                    locked: assignment.locked_for_user,
                    publishedAt: assignment.created_at,
                    value: assignment.points_possible
                };

                //Save score names to an array temporarily
                //This is because the scoreNames can only be found in the rubric data from Canvas
                //And we don't know which name to use until the outcome score data is processed
                var temporaryScoreNames = {};

                //Add rubric data from Canvas to the dtpsAssignment
                if (assignment.rubric) {
                    assignment.rubric.forEach(canvasRubric => {
                        //Add rubric array to assignment if it doesn't exist yet
                        if (!dtpsAssignment.rubric) dtpsAssignment.rubric = [];

                        dtpsAssignment.rubric.push({
                            title: canvasRubric.description,
                            description: canvasRubric.long_description,
                            id: canvasRubric.id,
                            value: canvasRubric.points,
                            outcome: canvasRubric.outcome_id,
                            assignmentTitle: assignment.name,
                            assignmentID: assignment.id,
                            assignmentDue: assignment.due_at
                        });

                        temporaryScoreNames[canvasRubric.id] = {};
                        canvasRubric.ratings.forEach(canvasRating => {
                            temporaryScoreNames[canvasRubric.id][canvasRating.points] = canvasRating.description;
                        });
                    })
                }

                //Add submission data from Canvas to the dtpsAssignment
                submissionData.forEach(submission => {
                    if (submission.assignment_id == assignment.id) {
                        //Add scores from this submission to the rubric
                        if (submission.rubric_assessment) {
                            let matches = 0;
                            if (dtpsAssignment.rubric) {
                                dtpsAssignment.rubric.forEach(rubric => {
                                    if (submission.rubric_assessment[rubric.id]) {
                                        rubric.score = submission.rubric_assessment[rubric.id].points;
                                        rubric.scoreName = temporaryScoreNames[rubric.id][rubric.score];
                                        matches++;
                                    }
                                });
                            }
                        }

                        //Check for turned in, late, missing, gradedAt, and feedback
                        dtpsAssignment.turnedIn = submission.submission_type !== null;
                        if (dtpsAssignment.turnedIn && dtpsAssignment.rubric) dtpsAssignment.rubric.forEach(r => r.assignmentSubmitted = true);
                        dtpsAssignment.late = submission.late;
                        dtpsAssignment.missing = submission.missing;
                        dtpsAssignment.gradedAt = submission.graded_at;
                        dtpsAssignment.grade = submission.score;
                        var letter = submission.grade;
                        if (letter == "complete") letter = "✔️";
                        if (letter == "incomplete") letter = "❌";
                        if (isNaN(letter)) dtpsAssignment.letter = letter; //letter cannot be a number

                        //Check for submission comments
                        if (submission.submission_comments) {
                            //Add feedback array to assignment
                            dtpsAssignment.feedback = [];

                            //Add each comment to feedback array
                            submission.submission_comments.forEach(comment => {
                                var feedback = {
                                    comment: comment.comment
                                };

                                //Add author to feedback if found
                                if (comment.author) {
                                    feedback.author = {
                                        name: comment.author.display_name,
                                        id: comment.author.id,
                                        photoURL: comment.author.avatar_image_url
                                    }
                                }

                                dtpsAssignment.feedback.push(feedback)
                            });
                        }
                    }
                });

                //Add assignment to results array
                assignments.push(dtpsAssignment);
            });

            resolve(assignments);
        }).catch(reject);
    });
}

//Fetches modules data from Canvas
dtpsLMS.fetchModules = function (userID, classID) {
    return new Promise(function (resolve, reject) {
        Promise.all([
            dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/modules?include[]=items&include[]=content_details", { headers: dtpsLMS.commonHeaders }),
            dtpsLMS.fetchWrapper("/courses/" + classID + "/modules/progressions", { headers: dtpsLMS.commonHeaders })
        ]).then(responses => {
            return Promise.all(responses.map(r => r.json()));
        }).then(data => {
            var [modulesData, progressionData] = data;

            //Get collapsed modules from progression data
            var collapsedModules = {};

            //Loop over progression data
            progressionData.forEach(prog => {
                //Store collapsed state
                collapsedModules[prog.context_module_progression.context_module_id] = prog.context_module_progression.collapsed;
            })

            //Parse data from Canvas
            var dtpsModules = modulesData.map(module => {
                //Create module items array
                var moduleItems = [];

                //Add module items to array
                module.items.forEach(item => {
                    if (item.type.toUpperCase() == "ASSIGNMENT") {
                        moduleItems.push({
                            type: "assignment",
                            title: item.title,
                            id: item.content_id,
                            indent: item.indent,
                            url: item.html_url,
                            completed: item.completion_requirement && item.completion_requirement.completed
                        })
                    } else if (item.type.toUpperCase() == "PAGE") {
                        moduleItems.push({
                            type: "page",
                            title: item.title,
                            id: item.page_url,
                            indent: item.indent,
                            url: item.html_url,
                            completed: item.completion_requirement && item.completion_requirement.completed
                        })
                    } else if (item.type.toUpperCase() == "DISCUSSION") {
                        moduleItems.push({
                            type: "discussion",
                            title: item.title,
                            id: item.content_id,
                            indent: item.indent,
                            url: item.html_url,
                            completed: item.completion_requirement && item.completion_requirement.completed
                        })
                    } else if (item.type.toUpperCase() == "EXTERNALURL") {
                        moduleItems.push({
                            type: "url",
                            title: item.title,
                            url: item.external_url,
                            indent: item.indent,
                            completed: item.completion_requirement && item.completion_requirement.completed
                        })
                    } else if (item.type.toUpperCase() == "EXTERNALTOOL") {
                        moduleItems.push({
                            type: "embed",
                            title: item.title,
                            url: item.html_url,
                            indent: item.indent,
                            completed: item.completion_requirement && item.completion_requirement.completed
                        });
                    } else if (item.type.toUpperCase() == "SUBHEADER") {
                        moduleItems.push({
                            type: "header",
                            title: item.title,
                            indent: item.indent,
                            completed: item.completion_requirement && item.completion_requirement.completed
                        })
                    }
                })

                return {
                    id: module.id,
                    title: module.name,
                    collapsed: collapsedModules[module.id] || false,
                    items: moduleItems
                }
            })

            //Resolve with module data
            resolve(dtpsModules);
        }).catch(reject);
    });
}

//Collapses a module in Canvas
dtpsLMS.collapseModule = function (classID, modID, collapsed) {
    return dtpsLMS.fetchWrapper("/courses/" + classID + "/modules/" + modID + "/collapse", {
        method: "POST",
        headers: {
            "Accept": "application/json, text/javascript, application/json+canvas-string-ids, */*; q=0.01",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "X-CSRF-Token": decodeURIComponent(document.cookie).split("_csrf_token=")[1].split(";")[0]
        },
        body: "_method=POST&collapse=" + (collapsed ? 1 : 0) + "&authenticity_token=" + decodeURIComponent(document.cookie).split("_csrf_token=")[1].split(";")[0]
    });
}

//Collapses all modules in Canvas
dtpsLMS.collapseAllModules = function (classID, collapsed) {
    return dtpsLMS.fetchWrapper("/courses/" + classID + "/collapse_all_modules", {
        method: "POST",
        headers: {
            "Accept": "application/json, text/javascript, application/json+canvas-string-ids, */*; q=0.01",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "X-CSRF-Token": decodeURIComponent(document.cookie).split("_csrf_token=")[1].split(";")[0]
        },
        body: "_method=POST&collapse=" + (collapsed ? 1 : 0) + "&authenticity_token=" + decodeURIComponent(document.cookie).split("_csrf_token=")[1].split(";")[0]
    });
}

//Fetches announcement data from Canvas
dtpsLMS.fetchAnnouncements = function (classID) {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/announcements?context_codes[]=course_" + classID, { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(data => {
            var dtpsAnnouncements = data.map(function (announcement) {
                return {
                    title: announcement.title,
                    postedAt: announcement.created_at,
                    body: announcement.message,
                    url: announcement.html_url
                }
            });

            resolve(dtpsAnnouncements);
        }).catch(reject);
    });
}

//Fetches user data from Canvas
dtpsLMS.fetchUsers = function (classID) {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/sections?include[]=avatar_url&include[]=students&per_page=99", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(data => {
            var teachers = dtpsLMS.teacherCache[classID];
            var sections = [{
                title: "Teachers",
                id: "lms.dtps.canvas-teachers",
                users: []
            }];

            teachers.forEach(teacher => {
                sections[0].users.push({
                    name: teacher.display_name,
                    id: teacher.id,
                    photoURL: teacher.avatar_image_url,
                    url: "/courses/" + classID + "/users/" + teacher.id
                });
            });

            data.forEach(section => {
                if (!section.students) return;
                var users = [];
                section.students.forEach(student => {
                    users.push({
                        name: student.short_name,
                        id: student.id,
                        photoURL: student.avatar_url,
                        url: "/courses/" + classID + "/users/" + student.id
                    });
                });
                sections.push({
                    title: section.name,
                    id: section.id,
                    users
                });
            });
            resolve(sections);
        }).catch(reject);
    });
}

//Fetches homepage data from Canvas
dtpsLMS.fetchHomepage = function (classID) {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/front_page", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(data => {
            resolve(data.body);
        }).catch(reject);
    });
}

//Fetches discussion thread data from Canvas
dtpsLMS.fetchDiscussionThreads = function (classID) {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/discussion_topics", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(data => {
            var dtpsDiscussionThreads = data.map(function (thread) {
                return {
                    title: thread.title,
                    id: thread.id,
                    locked: thread.locked_for_user
                }
            });

            resolve(dtpsDiscussionThreads);
        }).catch(reject);
    })
}

//Fetches discussion thread posts from Canvas
dtpsLMS.fetchDiscussionPosts = function (classID, threadID) {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/discussion_topics/" + threadID + "/", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(threadData => {
            //Check if this discussion is a group discussion
            if (threadData.group_topic_children && threadData.group_topic_children.length) {
                //This discussion is probably a group discussion, check groups, then fetch posts
                dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/groups?only_own_groups=true", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(groupData => {
                    //Get array of group IDs
                    var myGroups = groupData.map(group => group.id);

                    //Set default group and discussion ID variables
                    var groupID = null;
                    var groupDiscussionID = null;

                    //Check every group this discussion has and see if one of them is in myGroups, set that group as the group to fetch posts for
                    threadData.group_topic_children.forEach(group => {
                        if (myGroups.includes(group.group_id)) {
                            groupID = group.group_id;
                            groupDiscussionID = group.id;
                        }
                    });

                    if (!groupID || !groupDiscussionID) {
                        //Couldn't find a group match, fetch class discussion
                        dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/discussion_topics/" + threadID + "/view", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(responsesData => {
                            parseResponse(responsesData, "/courses/" + classID + "/discussion_topics/" + threadID);
                        });
                    } else {
                        //Group match found, fetch group discussion
                        dtpsLMS.fetchWrapper("/api/v1/groups/" + groupID + "/discussion_topics/" + groupDiscussionID + "/view", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(responsesData => {
                            parseResponse(responsesData, "/groups/" + groupID + "/discussion_topics/" + groupDiscussionID);
                        });
                    }
                });
            } else {
                //Not a group discussion, directly fetch posts
                dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/discussion_topics/" + threadID + "/view", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(responsesData => {
                    parseResponse(responsesData, "/courses/" + classID + "/discussion_topics/" + threadID);
                });
            }


            //This function handles the response from Canavs and returns the data to DTPS
            //BaseURL is the baseURL of the thread, since this might be different for group discussions
            function parseResponse(responsesData, baseURL) {
                //Define discussions post array
                var dtpsDiscussionPosts = [];

                //Define the initial post
                var initialPost = {
                    id: threadID,
                    body: threadData.message,
                    postedAt: threadData.created_at,
                    replyURL: baseURL
                };

                //Check for author
                if (threadData.author && threadData.author.display_name) {
                    initialPost.author = {
                        name: threadData.author.display_name,
                        id: threadData.author.id,
                        photoURL: threadData.author.avatar_image_url
                    };
                }

                //Add initial post to array
                dtpsDiscussionPosts.push(initialPost);

                if (responsesData.view) {
                    //Get thread author information
                    var people = {};
                    if (responsesData.participants) {
                        responsesData.participants.forEach(participant => {
                            people[participant.id] = participant;
                        })
                    }

                    //If there are posts found from the second request, add those as well
                    responsesData.view.forEach(function (post) {
                        if (!post.deleted) {
                            var replies = [];

                            //Get replies for this post
                            if (post.replies) {
                                //If this post has replies, flatten them into a single array
                                function addReplies(arr, depth) {
                                    //Loop over replies to add
                                    arr.forEach(reply => {
                                        //Add this reply to the array
                                        if (!reply.deleted) {
                                            //Define reply object
                                            var dtpsReply = {
                                                id: reply.id,
                                                body: reply.message,
                                                postedAt: reply.created_at,
                                                replyURL: baseURL + "/entry-" + reply.id,
                                                depth: depth
                                            };

                                            //Check for reply author
                                            if (people[reply.user_id]) {
                                                dtpsReply.author = {
                                                    name: people[reply.user_id].display_name,
                                                    id: reply.user_id,
                                                    photoURL: people[reply.user_id].avatar_image_url
                                                }
                                            }

                                            //Add reply to flattened array
                                            replies.push(dtpsReply);
                                        }

                                        //Add nested replies to array
                                        if (reply.replies) addReplies(reply.replies, depth + 1);
                                    });
                                }

                                addReplies(post.replies, 0);
                            }

                            //Define post object
                            var dtpsPost = {
                                id: post.id,
                                body: post.message,
                                postedAt: post.created_at,
                                replies: replies,
                                replyURL: baseURL + "/entry-" + post.id
                            };

                            //Check for post author
                            if (people[post.user_id]) {
                                dtpsPost.author = {
                                    name: people[post.user_id].display_name,
                                    id: post.user_id,
                                    photoURL: people[post.user_id].avatar_image_url
                                }
                            }

                            //Add post to array
                            dtpsDiscussionPosts.push(dtpsPost);
                        }
                    });
                }

                resolve({
                    title: threadData.title,
                    id: threadData.id,
                    locked: threadData.locked_for_user,
                    posts: dtpsDiscussionPosts
                });
            }
        }).catch(reject);
    });
}

//Fetches pages list data from Canvas
dtpsLMS.fetchPages = function (classID) {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/pages?per_page=100", { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(data => {
            var dtpsPages = data.map(function (page) {
                var dtpsPage = {
                    title: page.title,
                    id: page.url
                };

                return dtpsPage;
            });

            resolve(dtpsPages);
        }).catch(reject);
    })
}

//Fetches pages list data from Canvas
dtpsLMS.fetchPageContent = function (classID, pageID) {
    return new Promise(function (resolve, reject) {
        dtpsLMS.fetchWrapper("/api/v1/courses/" + classID + "/pages/" + pageID, { headers: dtpsLMS.commonHeaders }).then(response => response.json()).then(data => {
            //Resolve with full page object
            var dtpsPage = {
                title: data.title,
                id: data.url,
                updatedAt: data.updated_at,
                content: data.body
            };

            //Check for page author
            if (data.last_edited_by) {
                dtpsPage.author = {
                    name: data.last_edited_by.display_name,
                    id: data.last_edited_by.id,
                    photoURL: data.last_edited_by.avatar_image_url
                }
            }

            resolve(dtpsPage);
        }).catch(reject);
    })
}

//Load Power+
jQuery.getScript(window.dtpsBaseURL + "/scripts/core.js");