//-------------------------------------------------------------------------------------
// <copyright file="subpubextender.js" company="Microsoft Corporation">
//     (c) Copyright Microsoft Learning. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------
var microsoft;
(function (microsoft) {
    (function (learning) {
        (function (mlx) {
            /***********************************************************************
            * PubSubExtender class which implements the Publish/Subscribe pattern.
            ***********************************************************************/
            var PubSubExtender = (function () {
                function PubSubExtender() {
                    this.subPubSubscriberTokens = -1;
                    this.subPubEvents = [];
                }
                PubSubExtender.prototype.notifySubscribers = function (eventType, args) {
                    for (var m in this.subPubEvents) {
                        for (var i = 0, j = this.subPubEvents[m].length; i < j; i++) {
                            if ((parseInt(m) & eventType) == eventType) {
                                this.subPubEvents[m][i].callbackFn(eventType, args);
                            }
                        }
                    }
                };

                PubSubExtender.prototype.subscribe = function (eventType, callbackFn) {
                    if (!this.subPubEvents[eventType]) {
                        this.subPubEvents[eventType] = [];
                    }

                    var token = ++this.subPubSubscriberTokens;
                    this.subPubEvents[eventType].push({
                        callbackFn: callbackFn,
                        token: token
                    });

                    return token;
                };

                PubSubExtender.prototype.unsubscribe = function (token) {
                    for (var m in this.subPubEvents) {
                        if (this.subPubEvents[m]) {
                            for (var i = 0, j = this.subPubEvents[m].length; i < j; i++) {
                                if (this.subPubEvents[m][i].token === token) {
                                    this.subPubEvents[m].splice(i, 1);
                                }
                            }
                        }
                    }
                };
                return PubSubExtender;
            })();
            mlx.PubSubExtender = PubSubExtender;
        })(learning.mlx || (learning.mlx = {}));
        var mlx = learning.mlx;
    })(microsoft.learning || (microsoft.learning = {}));
    var learning = microsoft.learning;
})(microsoft || (microsoft = {}));
;//-------------------------------------------------------------------------------------
// <copyright file="scorm12Runtime.js" company="Microsoft Corporation">
//     (c) Copyright Microsoft Learning. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var microsoft;
(function (microsoft) {
    (function (learning) {
        /// <reference path="../../../jquery/jquery.d.ts" />
        /// <reference path="../../../knockout/knockout.d.ts" />
        /// <reference path="../../utility.ts" />
        /// <reference path="../../Shared/Models/settingsModel.ts" />
        /// <reference path="../../../WebTrends/WebTrendsHelper.ts" />
        /// <reference path="courseActivityStateProxy.ts" />
        /// <reference path="coursePlayerModel.d.ts" />
        /// <reference path="../../Shared/subPubExtender.ts" />
        (function (mlx) {
            

            /***********************************************************************
            * Scorm12ManifestReader class.  Loads the imsmanifestlite.json file and
            * provides various functions to read various manifest elements/attributes.
            ***********************************************************************/
            var Scorm12ManifestReader = (function () {
                function Scorm12ManifestReader(tunnel) {
                    this.scoTunnel = tunnel || ScoTunnel;
                }
                Scorm12ManifestReader.prototype.initialize = function (courseId, version, url) {
                    var self = this, deferred = $.Deferred();

                    this.courseId = courseId;
                    this.baseUrl = url;

                    self.scoTunnel.ajax({
                        url: url + '/imsmanifestlite.json',
                        dataType: "json",
                        cache: false
                    }).done($.proxy(function (data) {
                        if (data) {
                            self.manifestJSON = data;
                        }

                        deferred.resolve();
                    }, this)).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                Scorm12ManifestReader.prototype.initializeManifestJson = function (courseId, version, url) {
                    var self = this, deferred = $.Deferred();

                    this.courseId = courseId;
                    this.baseUrl = url;

                    self.scoTunnel.ajax({
                        url: url + '/imsmanifest.json',
                        dataType: "json",
                        cache: false
                    }).done($.proxy(function (data) {
                        if (data) {
                            self.manifestJSON = data;
                        }

                        deferred.resolve();
                    }, this)).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                Scorm12ManifestReader.prototype.getCourseTitle = function () {
                    return (this.manifestJSON && this.manifestJSON.manifest && this.manifestJSON.manifest.metadata && this.manifestJSON.manifest.metadata.title) ? this.manifestJSON.manifest.metadata.title : "";
                };

                // Returns the default organization as the root activity
                Scorm12ManifestReader.prototype.getRootActivity = function () {
                    var self = this, org = null;

                    if (this.manifestJSON && this.manifestJSON.manifest && this.manifestJSON.manifest.organizations && this.manifestJSON.manifest.organizations.organization) {
                        var orgs = this.manifestJSON.manifest.organizations, orgsChildren = this.manifestJSON.manifest.organizations.organization;

                        for (var i = 0, len = orgsChildren.length; i < len; i++) {
                            if (orgsChildren[i]["@identifier"] == orgs["@default"]) {
                                org = orgsChildren[i];
                                break;
                            }
                        }
                    }

                    return org;
                };

                // Returns the item with the specified identifier value.
                Scorm12ManifestReader.prototype.getActivityNode = function (itemId) {
                    var rootActivity = this.getRootActivity();
                    return this.getActivityNodeHelper(this, rootActivity.item, itemId);
                };

                Scorm12ManifestReader.prototype.getActivityNodeHelper = function (self, activities, id) {
                    var activity = "";

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i] && activities[i]["@identifier"] == id) {
                            activity = activities[i];
                            break;
                        }

                        var children = activities[i].item;
                        if (children && children.length > 0) {
                            activity = this.getActivityNodeHelper(self, children, id);
                            if (activity)
                                break;
                        }
                    }
                    return activity;
                };

                Scorm12ManifestReader.prototype.getActivityLineage = function (itemId) {
                    var rootActivity = this.getRootActivity();
                    return this.getActivityLineageHelper(this, null, rootActivity.item, itemId);
                };

                Scorm12ManifestReader.prototype.getActivityLineageHelper = function (self, parent, activities, id) {
                    var activity;

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i] && activities[i]["@identifier"] == id) {
                            activity = $.extend({}, parent);
                            activity.item = [];
                            activity.item.push(activities[i]);
                            break;
                        }

                        var children = activities[i].item;
                        if (children && children.length > 0) {
                            var tmp = this.getActivityLineageHelper(self, activities[i], children, id);
                            if (tmp) {
                                if (parent) {
                                    activity = $.extend({}, parent);
                                    activity.item = [];
                                    activity.item.push(tmp);
                                } else {
                                    activity = tmp;
                                }
                                break;
                            }
                        }
                    }

                    return activity;
                };

                // Returns the resource for the specified item.
                Scorm12ManifestReader.prototype.getResourceNode = function (activity) {
                    return activity && activity.resource ? activity.resource : undefined;
                };

                // Returns the item node with the specified identifier value.
                Scorm12ManifestReader.prototype.getActivityId = function (activity) {
                    return activity["@identifier"];
                };

                // Returns the title text for the specified item node.
                Scorm12ManifestReader.prototype.getActivityTitle = function (activity) {
                    return activity.title;
                };

                // Returns whether the activity is marked for delete.
                Scorm12ManifestReader.prototype.getActivityDeleted = function (activity) {
                    return (activity && activity["@isdeleted"] && activity["@isdeleted"] === "true") ? true : false;
                };

                // Returns the description for the specified item node.
                Scorm12ManifestReader.prototype.getActivityDescription = function (activity) {
                    return (activity && activity.metadata && activity.metadata.description) ? activity.metadata.description : "";
                };

                Scorm12ManifestReader.prototype.getActivityParameters = function (activity) {
                    return activity && activity["@parameters"] ? activity["@parameters"] : "";
                };

                // Returns the child items for the specified node.
                Scorm12ManifestReader.prototype.getActivityChildren = function (activityNode, initializationData, filterPreviouslyDeletedItems) {
                    if (typeof initializationData === "undefined") { initializationData = {}; }
                    if (typeof filterPreviouslyDeletedItems === "undefined") { filterPreviouslyDeletedItems = false; }
                    var children = [], count = 0;

                    // By default, it will return only visible manifest items.
                    if (activityNode && activityNode.item) {
                        var items = activityNode.item, activity = undefined, isvisible = undefined, isdeleted = undefined, global = undefined, globalCourseVersion = undefined, deletedVersion = undefined, activityHasChildren = false;

                        if (Object.keys(initializationData).length > 0) {
                            global = initializationData["global"];
                            globalCourseVersion = (global && typeof global.courseVersion != "undefined") ? global.courseVersion : "";
                        }

                        for (var index = 0, len = items.length; index < len; index++) {
                            activity = items[index];
                            activityHasChildren = (activity && activity.item) ? activity.item.length > 0 : false;

                            if (activity && (activity.resource || activityHasChildren)) {
                                isvisible = activity["@isvisible"] ? activity["@isvisible"] : "true";
                                isdeleted = activity["@isdeleted"] ? activity["@isdeleted"] : "false";

                                if (isvisible == "true" && isdeleted == "false") {
                                    children[count++] = $.extend(true, {}, activity);
                                } else if (filterPreviouslyDeletedItems) {
                                    // This will include items which were deleted after user had launched the course.
                                    deletedVersion = activity["deletedVersion"] ? activity["deletedVersion"] : "";

                                    // Include item if globalcourseversion is not present and deletedVersion is not null.
                                    // This will be the case when user has already taken a course and some content is deleted.
                                    if ((globalCourseVersion === "" && deletedVersion !== "") || deletedVersion > globalCourseVersion) {
                                        children[count++] = $.extend(true, {}, activity);
                                    }
                                }
                            }
                        }
                    }

                    return children;
                };

                // Returns the href value for the specified resource.
                Scorm12ManifestReader.prototype.getResourceHRef = function (resource) {
                    return resource && resource["@href"] ? resource["@href"] : "";
                };

                // Returns the scormtype value for the specified resource.  If no scormtype
                // value is specified the default is "sco" for manifest backward compatability.
                Scorm12ManifestReader.prototype.getResourceScormType = function (resource) {
                    return resource && resource["@scormtype"] ? resource["@scormtype"] : "sco";
                };

                // Returns the author value for the specied resource.
                Scorm12ManifestReader.prototype.getResourceAuthor = function (resource) {
                    return (resource && resource.metadata && resource.metadata.copyright) ? resource.metadata.copyright : "";
                };

                // Returns the duration value for the specied resource.
                Scorm12ManifestReader.prototype.getResourceDuration = function (resource) {
                    return (resource && resource.metadata && resource.metadata.duration) ? resource.metadata.duration : "";
                };

                // Returns the description value for the specified resource.
                Scorm12ManifestReader.prototype.getResourceDescription = function (resource) {
                    return (resource && resource.metadata && resource.metadata.description) ? resource.metadata.description : "";
                };

                // Returns the learning resource type value for the specied resource from the metadata
                Scorm12ManifestReader.prototype.getLearningResourceType = function (resource) {
                    return (resource && resource.metadata && resource.metadata.learningresourcetype) ? resource.metadata.learningresourcetype : "";
                };

                // Returns the prerequisites for the specified item.  Only simple prerequisites
                // (a single item identifier) are currently supported.
                Scorm12ManifestReader.prototype.getPrerequisites = function (activity) {
                    return (activity && activity.prerequisites) ? activity.prerequisites : "";
                };

                // Returns the number of assessments within the course
                Scorm12ManifestReader.prototype.getAssessmentsCount = function () {
                    var rootActivity = this.getRootActivity();
                    return this.getLearningResourceTypeCountHelper(this, rootActivity.item, ManifestLearningResourceType.Assessment);
                };

                // Returns the number of Knowledge Checks within the course
                Scorm12ManifestReader.prototype.getKnowledgeCheckCount = function () {
                    var rootActivity = this.getRootActivity();
                    return this.getLearningResourceTypeCountHelper(this, rootActivity.item, ManifestLearningResourceType.KnowledgeCheck);
                };

                // Returns the number of instances of the specified resource type within the course
                Scorm12ManifestReader.prototype.getLearningResourceTypeCount = function (learningResourceType) {
                    var rootActivity = this.getRootActivity();
                    return this.getLearningResourceTypeCountHelper(this, rootActivity.item, learningResourceType);
                };

                Scorm12ManifestReader.prototype.getLearningResourceTypeCountHelper = function (self, activities, learningResourceType) {
                    var count = 0;

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i] && ((!activities[i]["@isvisible"] || activities[i]["@isvisible"] && activities[i]["@isvisible"] == "true") && (!activities[i]["@isdeleted"] || activities[i]["@isdeleted"] && activities[i]["@isdeleted"] == "false"))) {
                            if (activities[i].resource && activities[i].resource.metadata && activities[i].resource.metadata.learningresourcetype && activities[i].resource.metadata.learningresourcetype.toLowerCase() == learningResourceType.toLowerCase()) {
                                count++;
                            }

                            if (activities[i].item) {
                                var children = activities[i].item;
                                if (children && children.length > 0) {
                                    count += this.getLearningResourceTypeCountHelper(self, children, learningResourceType);
                                }
                            }
                        }
                    }

                    return count;
                };

                // Returns the number of modules within the course
                Scorm12ManifestReader.prototype.getModulesCount = function () {
                    var rootActivity = this.getRootActivity(), activities = rootActivity.item, count = 0;

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if ((!activities[i]["@isvisible"] || activities[i]["@isvisible"] && activities[i]["@isvisible"] == "true") && (!activities[i]["@isdeleted"] || activities[i]["@isdeleted"] && activities[i]["@isdeleted"] == "false")) {
                            if (!activities[i].resource || (activities[i].resource && activities[i].resource.metadata && activities[i].resource.metadata.learningresourcetype && activities[i].resource.metadata.learningresourcetype != ManifestLearningResourceType.Assessment)) {
                                count++;
                            }
                        }
                    }

                    return count;
                };

                // Returns the total duration in HH:MM:SS for the specified content type
                Scorm12ManifestReader.prototype.getTotalVideoDuration = function () {
                    var rootActivity = this.getRootActivity(), durationSeconds = this.getTotalSecondsForContentType(rootActivity.item, ManifestLearningResourceType.Video);

                    return microsoft.learning.mlx.utility.convertSecondsToHHMMSS(durationSeconds);
                };

                // Returns the total duration in HH:MM:SS for the specified activity and its children irrespective of content type
                Scorm12ManifestReader.prototype.getDurationTimeRollupForActivity = function (activity) {
                    var durationSeconds = 0;

                    if (activity) {
                        if (activity.resource && activity.resource.metadata && activity.resource.metadata.duration) {
                            durationSeconds = microsoft.learning.mlx.utility.convertHHMMSSToSeconds(activity.resource.metadata.duration);
                        }

                        if (activity.item) {
                            durationSeconds += this.getTotalSecondsForContentType(activity.item, ManifestLearningResourceType.Any);
                        }
                    }

                    return microsoft.learning.mlx.utility.convertSecondsToHHMMSS(durationSeconds);
                };

                // Returns the total duration in seconds for the specified activities and content type
                Scorm12ManifestReader.prototype.getTotalSecondsForContentType = function (activities, learningResourceType) {
                    var totalSeconds = 0;

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i] && ((!activities[i]["@isvisible"] || activities[i]["@isvisible"] && activities[i]["@isvisible"] == "true") && (!activities[i]["@isdeleted"] || activities[i]["@isdeleted"] && activities[i]["@isdeleted"] == "false"))) {
                            if (activities[i].resource && activities[i].resource.metadata) {
                                if (activities[i].resource.metadata.learningresourcetype) {
                                    var duration = activities[i].resource.metadata.duration;
                                    if (duration) {
                                        if (learningResourceType != ManifestLearningResourceType.Any) {
                                            if (activities[i].resource.metadata.learningresourcetype == learningResourceType) {
                                                totalSeconds += microsoft.learning.mlx.utility.convertHHMMSSToSeconds(duration);
                                            }
                                        } else {
                                            totalSeconds += microsoft.learning.mlx.utility.convertHHMMSSToSeconds(duration);
                                        }
                                    }
                                }
                            }

                            if (activities[i].item) {
                                var children = activities[i].item;
                                if (children && children.length > 0) {
                                    totalSeconds += this.getTotalSecondsForContentType(children, learningResourceType);
                                }
                            }
                        }
                    }

                    return totalSeconds;
                };

                // Returns an array of assessment item identifiers
                Scorm12ManifestReader.prototype.getAssessmentIdentifiers = function (visibility) {
                    var rootActivity = this.getRootActivity();

                    return this.getIdentifiers(this, rootActivity.item, visibility, ManifestLearningResourceType.Assessment);
                };

                // Returns an array of video item identifiers
                Scorm12ManifestReader.prototype.getVideoIdentifiers = function (visibility) {
                    var rootActivity = this.getRootActivity();

                    return this.getIdentifiers(this, rootActivity.item, visibility, ManifestLearningResourceType.Video);
                };

                // Determines whether the activity has the specified visibility
                Scorm12ManifestReader.prototype.activityHasVisibility = function (activity, visibility) {
                    var hasVisibility = false;

                    if ((visibility == ManifestItemVisibility.Hidden || visibility == ManifestItemVisibility.Any) && activity["@isvisible"] && activity["@isvisible"] == "false") {
                        hasVisibility = true;
                    } else if ((visibility == ManifestItemVisibility.Visible || visibility == ManifestItemVisibility.Any) && (!activity["@isvisible"] || activity["@isvisible"] && activity["@isvisible"] == "true")) {
                        hasVisibility = true;
                    } else if ((visibility == ManifestItemVisibility.SoftDeleted || visibility == ManifestItemVisibility.Any) && (activity["@isdeleted"] && activity["@isdeleted"] == "true")) {
                        hasVisibility = true;
                    } else if ((visibility == ManifestItemVisibility.VisibleNotSoftDeleted || visibility == ManifestItemVisibility.Any) && (!activity["@isdeleted"] || activity["@isdeleted"] && activity["@isdeleted"] == "false") && (!activity["@isvisible"] || activity["@isvisible"] && activity["@isvisible"] == "true")) {
                        hasVisibility = true;
                    }

                    return hasVisibility;
                };

                Scorm12ManifestReader.prototype.getIdentifiers = function (self, activities, visibility, learningResourceType) {
                    if (typeof learningResourceType === "undefined") { learningResourceType = ManifestLearningResourceType.Any; }
                    var activityIds = [];

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i]) {
                            if (activities[i].resource && activities[i].resource.metadata && activities[i].resource.metadata.learningresourcetype && (activities[i].resource.metadata.learningresourcetype === learningResourceType || learningResourceType === ManifestLearningResourceType.Any)) {
                                if (this.activityHasVisibility(activities[i], visibility)) {
                                    var id = activities[i]["@identifier"];
                                    activityIds.push(id);
                                }
                            }

                            if (activities[i].item) {
                                var children = activities[i].item;
                                if (children && children.length > 0) {
                                    var ids = this.getIdentifiers(self, children, visibility, learningResourceType);
                                    activityIds = activityIds.concat(ids);
                                }
                            }
                        }
                    }

                    return activityIds;
                };

                // Returns an array of lesson item identifiers depending on the passed ManifestItemVisibility and ManifestResourceScormType.
                // By default will return all visible items where scormtype equals "sco" .
                Scorm12ManifestReader.prototype.getLessonIdentifiers = function (visibility, manifestResourceScormType) {
                    if (typeof visibility === "undefined") { visibility = ManifestItemVisibility.Visible; }
                    if (typeof manifestResourceScormType === "undefined") { manifestResourceScormType = ManifestResourceScormType.SCO; }
                    var rootActivity = this.getRootActivity();

                    return this.getLessonIdentifiersHelper(this, rootActivity.item, visibility, manifestResourceScormType);
                };

                Scorm12ManifestReader.prototype.getLessonIdentifiersHelper = function (self, activities, visibility, manifestResourceScormType) {
                    var lessonIds = [];

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i]) {
                            if (activities[i].resource) {
                                var resourceScormType = activities[i].resource["@scormtype"] ? activities[i].resource["@scormtype"] : "sco";

                                if ((resourceScormType === manifestResourceScormType || manifestResourceScormType === ManifestResourceScormType.Any) && this.activityHasVisibility(activities[i], visibility)) {
                                    var id = activities[i]["@identifier"];
                                    lessonIds.push(id);
                                }
                            }

                            if (activities[i].item) {
                                var children = activities[i].item;
                                if (children && children.length > 0) {
                                    var ids = this.getLessonIdentifiersHelper(self, children, visibility, manifestResourceScormType);
                                    lessonIds = lessonIds.concat(ids);
                                }
                            }
                        }
                    }

                    return lessonIds;
                };

                // Returns the version of the SCO content from resource metadata
                Scorm12ManifestReader.prototype.getResourceVersion = function (resource) {
                    return (resource && resource.metadata && resource.metadata.version) ? resource.metadata.version : "";
                };

                // Returns the version date/time stamp of the SCO content from resource metadata
                Scorm12ManifestReader.prototype.getResourceUpdatedDate = function (resource) {
                    return (resource && resource.metadata && resource.metadata.updateddate) ? resource.metadata.updateddate : "";
                };

                // Returns the created version of the SCO content from resource metadata
                Scorm12ManifestReader.prototype.getResourceCreatedVersion = function (resource) {
                    return (resource && resource.metadata && resource.metadata.createdVersion) ? resource.metadata.createdVersion : "";
                };

                // Returns an associative array of item durations in seconds.  The returned values depend on the passed ManifestItemVisibility.
                // By default we return all non-deleted visible items.
                Scorm12ManifestReader.prototype.getDurations = function (visibility) {
                    var rootActivity = this.getRootActivity();

                    // FIXME: The MLXTunnel's clone function doesn't handle associative arrays correctly so for now
                    // we'll convert our array to an object as a work-around.
                    return $.extend({}, this.getDurationsHelper(this, rootActivity.item, visibility));
                };

                Scorm12ManifestReader.prototype.getDurationsHelper = function (self, activities, visibility) {
                    if (typeof visibility === "undefined") { visibility = ManifestItemVisibility.VisibleNotSoftDeleted; }
                    var scoDurations = [];

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i]) {
                            if (activities[i].resource && activities[i].resource.metadata && activities[i].resource.metadata.duration) {
                                if (this.activityHasVisibility(activities[i], visibility)) {
                                    var id = activities[i]["@identifier"];
                                    scoDurations[id] = microsoft.learning.mlx.utility.convertHHMMSSToSeconds(activities[i].resource.metadata.duration);
                                }
                            }

                            if (activities[i].item) {
                                var children = activities[i].item;
                                if (children && children.length > 0) {
                                    var durations = this.getDurationsHelper(self, children, visibility);
                                    $.extend(true, scoDurations, durations);
                                }
                            }
                        }
                    }

                    return scoDurations;
                };

                // Determines whether the imsmanifest contains duration metadata (excludes assessment)
                Scorm12ManifestReader.prototype.isDurationAvailable = function () {
                    var rootActivity = this.getRootActivity();
                    return this.isDurationAvailableHelper(this, rootActivity.item);
                };

                Scorm12ManifestReader.prototype.isDurationAvailableHelper = function (self, activities) {
                    var isAvailable = false;

                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i].resource && activities[i].resource.metadata && activities[i].resource.metadata.duration) {
                            isAvailable = true;
                            break;
                        }

                        var children = self.getActivityChildren(activities[i]);
                        if (children && children.length > 0) {
                            isAvailable = this.isDurationAvailableHelper(self, children);
                            if (isAvailable)
                                break;
                        }
                    }

                    return isAvailable;
                };

                // Returns the markers for the specified activity
                Scorm12ManifestReader.prototype.getActivityMarkers = function (activityNode) {
                    var activityMarkers;

                    if (activityNode && activityNode.markers) {
                        var markers = activityNode.markers, markersLength = markers.length, currMarker;

                        if (markersLength) {
                            activityMarkers = [];

                            for (var i = 0; i < markersLength; i++) {
                                currMarker = markers[i];
                                activityMarkers.push({
                                    id: ko.observable(currMarker["@id"]),
                                    type: ko.observable(currMarker["@type"]),
                                    location: ko.observable(currMarker.location),
                                    title: ko.observable(currMarker.title)
                                });
                            }
                        }
                    }

                    return activityMarkers;
                };
                return Scorm12ManifestReader;
            })();
            mlx.Scorm12ManifestReader = Scorm12ManifestReader;

            var ManifestItemVisibility = (function () {
                function ManifestItemVisibility() {
                }
                ManifestItemVisibility.Hidden = 0;
                ManifestItemVisibility.Visible = 1;
                ManifestItemVisibility.SoftDeleted = 2;
                ManifestItemVisibility.VisibleNotSoftDeleted = 3;
                ManifestItemVisibility.Any = 4;
                return ManifestItemVisibility;
            })();
            mlx.ManifestItemVisibility = ManifestItemVisibility;

            var ManifestLearningResourceType = (function () {
                function ManifestLearningResourceType() {
                }
                ManifestLearningResourceType.Video = "Video";
                ManifestLearningResourceType.Demo = "Demonstration";
                ManifestLearningResourceType.Simulation = "Simulation";
                ManifestLearningResourceType.Lab = "Lab";
                ManifestLearningResourceType.Assessment = "Exam";
                ManifestLearningResourceType.KnowledgeCheck = "Self Assessment";
                ManifestLearningResourceType.Word = "Microsoft Word";
                ManifestLearningResourceType.Excel = "Microsoft Excel";
                ManifestLearningResourceType.PowerPoint = "Microsoft PowerPoint";
                ManifestLearningResourceType.PDF = "PDF";
                ManifestLearningResourceType.Zip = "Zip";
                ManifestLearningResourceType.Embed = "Embed";
                ManifestLearningResourceType.HtmlCode = "HTML Code";
                ManifestLearningResourceType.DocumentDownload = "Document Download";
                ManifestLearningResourceType.Any = "";
                return ManifestLearningResourceType;
            })();
            mlx.ManifestLearningResourceType = ManifestLearningResourceType;

            var ManifestResourceScormType = (function () {
                function ManifestResourceScormType() {
                }
                ManifestResourceScormType.Asset = "asset";
                ManifestResourceScormType.SCO = "sco";
                ManifestResourceScormType.Any = "";
                return ManifestResourceScormType;
            })();
            mlx.ManifestResourceScormType = ManifestResourceScormType;

            /***********************************************************************
            * AiccScriptProcessor class.  Contains various methods to handle aicc_script
            * expressions (refer to SCORM_1.2_CAM.pdf section 2.3.2.5.1).  For now, we only
            * support a single prerequisite (manifest item identifier) or multiple prerequisites
            * which are delimited using the '&' operator. In the future, if we need to support
            * additional operators, we'll have to provide a more robust implementation.
            ***********************************************************************/
            var AiccScriptProcessor = (function () {
                function AiccScriptProcessor(sequencer) {
                    this.sequencer = sequencer;
                }
                AiccScriptProcessor.prototype.createPrerequistesExpression = function (itemIds) {
                    var prerequisitesExpression = "";

                    if (itemIds.length > 0) {
                        prerequisitesExpression = (itemIds.length == 1) ? itemIds[0] : itemIds.join("&");
                    }

                    return prerequisitesExpression;
                };

                AiccScriptProcessor.prototype.isValidPrerequistesExpression = function (prerequisitesExpression) {
                    var re = /^\w*(&{1}\w{1,})*/g;
                    return re.test(prerequisitesExpression);
                };

                AiccScriptProcessor.prototype.getItemIdentifiersFromPrerequistes = function (prerequisitesExpression) {
                    return (prerequisitesExpression != "") ? prerequisitesExpression.split("&") : [];
                };

                // Determines whether prerequisites have been satisfied.
                AiccScriptProcessor.prototype.prerequisitesSatisfied = function (prerequisitesExpression) {
                    var itemIds = prerequisitesExpression ? this.getItemIdentifiersFromPrerequistes(prerequisitesExpression) : [], isPassedOrCompleted = true;

                    if (itemIds.length > 0) {
                        var activity, resource, learningResourceType;

                        for (var i = 0, len = itemIds.length; i < len; i++) {
                            activity = this.sequencer.getSpecifiedActivity(itemIds[i], false);
                            resource = activity && activity.resource();
                            learningResourceType = resource ? resource.learningResourceType : "";

                            if (activity && learningResourceType !== ManifestLearningResourceType.Assessment && !activity.completed()) {
                                isPassedOrCompleted = false;
                                break;
                            }
                        }
                    }

                    return isPassedOrCompleted;
                };
                return AiccScriptProcessor;
            })();
            mlx.AiccScriptProcessor = AiccScriptProcessor;

            /***********************************************************************
            * Scorm12Sequencer class.  Provides various functions used
            * to navigate to activities as well as logic to detemine whether navigation
            * can take place due to sequencing rules.
            ***********************************************************************/
            var Scorm12Sequencer = (function () {
                function Scorm12Sequencer() {
                }
                Scorm12Sequencer.prototype.initialize = function (coursePlayerModel, activityState, aiccScriptProcessor) {
                    this.coursePlayerModel = coursePlayerModel;
                    this.activityState = activityState;
                    this.aiccScriptProcessor = aiccScriptProcessor;
                };

                // Returns the next activity relative to the current activity.
                Scorm12Sequencer.prototype.getNextActivity = function () {
                    var activity = this.coursePlayerModel.activeResource().parent;

                    return this.getNextActivityHelper(activity);
                };

                // Returns the next activity relative to the specified activity id
                Scorm12Sequencer.prototype.getNextActivityById = function (activityId) {
                    var activity = this.getSpecifiedActivity(activityId);

                    return this.getNextActivityHelper(activity);
                };

                // Helper function which returns the next activity relative to the specified activity.
                Scorm12Sequencer.prototype.getNextActivityHelper = function (activity) {
                    var origActivity = activity;

                    do {
                        if (activity && activity.activities().length > 0) {
                            activity = activity.activities()[0];
                        } else if (activity && activity.parent && activity.activityIndex() < activity.parent.activities().length - 1) {
                            activity = activity.parent.activities()[activity.activityIndex() + 1];
                        } else if (activity && activity.parent && activity.parent.parent && activity.parent.activityIndex() <= activity.parent.parent.activities().length - 1) {
                            do {
                                activity = activity.parent;

                                if (activity && activity.parent && activity.activityIndex() < activity.parent.activities().length - 1) {
                                    activity = activity.parent.activities()[activity.activityIndex() + 1];
                                    break;
                                }
                            } while(activity);
                        } else if (activity && activity.parent && activity.activityIndex() == activity.parent.activities().length - 1) {
                            activity = null;
                            break;
                        }

                        if (!activity || (activity instanceof CoursePlayerModel)) {
                            activity = null;
                            break;
                        }
                    } while(activity && (!activity.resource() || !this.aiccScriptProcessor.prerequisitesSatisfied(activity.mlxPrerequisites())));

                    return activity;
                };

                // Returns the previous activity relative to the current activity.
                Scorm12Sequencer.prototype.getPrevActivity = function () {
                    var activity = this.coursePlayerModel.activeResource().parent;

                    return this.getPrevActivityHelper(activity);
                };

                // Returns the previous activity relative to the specified activity id
                Scorm12Sequencer.prototype.getPrevActivityById = function (activityId) {
                    var activity = this.getSpecifiedActivity(activityId);

                    return this.getPrevActivityHelper(activity);
                };

                // Helper function which returns the previous activity relative to the specified activity.
                Scorm12Sequencer.prototype.getPrevActivityHelper = function (activity) {
                    var origActivity = activity;

                    do {
                        if (activity && activity.parent && activity.activityIndex() - 1 < 0) {
                            activity = activity.parent;
                        } else if (activity.parent && activity.activityIndex() - 1 >= 0) {
                            activity = activity.parent.activities()[activity.activityIndex() - 1];

                            while (activity && activity.activities().length > 0) {
                                activity = activity.activities()[activity.activities().length - 1];
                            }
                        }

                        if (!activity || (activity instanceof CoursePlayerModel)) {
                            activity = null;
                            break;
                        }
                    } while(activity && (!activity.resource() || !this.aiccScriptProcessor.prerequisitesSatisfied(activity.mlxPrerequisites())));

                    return activity;
                };

                // Returns the activity last visited by the user.
                Scorm12Sequencer.prototype.getStartingActivity = function () {
                    var self = this, deferred = $.Deferred(), activities = ko.utils.unwrapObservable(this.coursePlayerModel.activities());

                    this.activityState.getValue('mlx.start_location', true).done(function (startingId) {
                        // Couldn't find the activity or there wasn't a starting activity ID to return to.
                        var result = null;

                        if (startingId != "") {
                            var startingActivity = self.getSpecifiedActivityHelper(activities, startingId);
                            if (startingActivity && self.aiccScriptProcessor.prerequisitesSatisfied(startingActivity.mlxPrerequisites())) {
                                result = startingActivity;
                            }
                        }

                        deferred.resolve(result);
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                // Gets the activity specified by the activityId
                Scorm12Sequencer.prototype.getSpecifiedActivity = function (activityId, checkPrerequisites) {
                    if (typeof checkPrerequisites === "undefined") { checkPrerequisites = true; }
                    var activities = ko.utils.unwrapObservable(this.coursePlayerModel.activities()), result = null;

                    if (activityId != "") {
                        var activity = this.getSpecifiedActivityHelper(activities, activityId), prerequisitesSatisfied = (checkPrerequisites && activity != null) ? this.aiccScriptProcessor.prerequisitesSatisfied(activity.mlxPrerequisites()) : true;

                        if (activity && prerequisitesSatisfied) {
                            result = activity;
                        }
                    }

                    return result;
                };

                // Helper function which returns the starting activity.
                Scorm12Sequencer.prototype.getSpecifiedActivityHelper = function (activities, id) {
                    var startActivity = null;

                    for (var i = 0, j = activities.length; i < j; i++) {
                        if (activities[i].id() == id) {
                            startActivity = activities[i];
                            break;
                        } else if (ko.utils.unwrapObservable(activities[i].activities()).length > 0) {
                            startActivity = this.getSpecifiedActivityHelper(ko.utils.unwrapObservable(activities[i].activities()), id);
                            if (startActivity) {
                                break;
                            }
                        }
                    }

                    return startActivity;
                };
                return Scorm12Sequencer;
            })();
            mlx.Scorm12Sequencer = Scorm12Sequencer;

            /***********************************************************************
            * Activity class.
            * For now, we are assuming that Activities snap to a hierarchical structure
            * To that end, each activity has an index which indicates the order of this activity
            * in relation to its siblings, a single parent, and an array of child Activities.
            * In future, we could imagine an adaptive learning navigational algorithm
            * that heuristically determines the index of the activity based on a bunch of params.
            * For more advanced algorithms, the assumption of an hierarchical structure may not be valid.
            * We should revisit the structural assumptions at that time.
            ***********************************************************************/
            var Activity = (function () {
                function Activity() {
                    var _this = this;
                    this.activities = ko.observableArray([]);
                    this.id = ko.observable("");
                    this.title = ko.observable("");
                    this.fullTitle = ko.observable("");
                    this.activityIndex = ko.observable(-1);
                    this.resource = ko.observable(null);
                    this.prerequisites = ko.observable("");
                    this.mlxPrerequisites = ko.observable("");
                    this.childrenVisible = ko.observable(true);
                    this.contentLevel = ko.observable(-1);
                    this.completed = ko.observable(false);
                    this.completedWithChildren = false;
                    this.duration = ko.observable(0);
                    this.expectedDurationTime = ko.observable("00:00:00");
                    this.progressTime = ko.observable("00:00:00");
                    this.progressPercentage = ko.observable(0);
                    this.assessmentQuestionsCount = ko.observable(0);
                    this.assessmentQuestionsCorrectCount = ko.observable(0);
                    this.firstAccessed = ko.observable();
                    this.lastAccessed = ko.observable();
                    this.completedDate = ko.observable();
                    this.descendantsAndSelfActivityCount = 0;
                    this.progress = ko.observable(0);
                    this.versionChanged = ko.observable(false);
                    this.updatedContentAvailable = ko.observable(false);
                    this.newContentAvailable = ko.observable(false);
                    this.newContentAvailableSinceUserLastAccessed = ko.observable(false);
                    this.description = ko.observable("");
                    this.activityMarkedForDelete = false;
                    this.userLaunchVersion = "";
                    this.requiredForCourseCompletion = false;
                    // New for MVA
                    this.markers = ko.observableArray([]);
                    // Determines whether this activity contains an active resource,
                    // or active resource in its sub-tree.
                    this.isActive = ko.computed(function () {
                        var resource = _this.resource();
                        if (resource && resource.isActive()) {
                            return true;
                        }

                        var hasActiveTopic = false, activities = _this.activities(), activity = undefined;

                        for (var i = 0, len = activities.length; i < len; i++) {
                            activity = activities[i];
                            if (activity.isActive()) {
                                hasActiveTopic = true;
                            }
                        }
                        return hasActiveTopic;
                    }, this);

                    // Determines whether prerequistes have been satisfied for the current activity.
                    this.prerequisitesSatisified = ko.computed(function () {
                        var prerequisites = ko.utils.unwrapObservable(_this.mlxPrerequisites);
                        if (prerequisites) {
                            return _this.coursePlayerModel.prerequisitesSatisfied(prerequisites);
                        }

                        return true;
                    }, this);
                }
                Activity.prototype.getDurationRollup = function (currentActivity) {
                    var duration = 0, formattedDuration = "";

                    if (currentActivity.activities().length > 0) {
                        var activities = currentActivity.activities(), activity = undefined;

                        for (var i = 0, len = activities.length; i < len; i++) {
                            activity = activities[i];
                            if (activity.completed()) {
                                duration += activity.duration();
                            }
                        }

                        formattedDuration = microsoft.learning.mlx.utility.convertSecondsToHHMMSS(duration);
                    }

                    return formattedDuration;
                };

                // Initializes an activity.
                Activity.prototype.init = function (platformBuildVersion, activityJsonNode, parent, activityIndex, manifestReader, activityState, contentLevel, coursePlayerModel, initializationData, filterPreviouslyDeletedItems) {
                    if (typeof filterPreviouslyDeletedItems === "undefined") { filterPreviouslyDeletedItems = false; }
                    if (manifestReader) {
                        this.id(manifestReader.getActivityId(activityJsonNode));

                        var self = this, id = this.id();

                        this.coursePlayerModel = coursePlayerModel;
                        this.title(manifestReader.getActivityTitle(activityJsonNode));
                        this.fullTitle(contentLevel === 1 /* Module */ ? ("Module " + (activityIndex + 1) + ": " + this.title()) : this.title()); // Is this still needed?
                        this.activityIndex(activityIndex);
                        this.parent = parent;
                        this.prerequisites(manifestReader.getPrerequisites(activityJsonNode));
                        this.contentLevel(contentLevel);
                        this.activityState = activityState;
                        this.markers(manifestReader.getActivityMarkers(activityJsonNode));
                        this.description(manifestReader.getActivityDescription(activityJsonNode));
                        this.activityMarkedForDelete = manifestReader.getActivityDeleted(activityJsonNode);

                        var global = initializationData["global"];
                        this.userLaunchVersion = (global && typeof global.courseVersion != "undefined") ? global.courseVersion : "";

                        var activityInitData = initializationData[id], activities = this.activities(), activityChildren = manifestReader.getActivityChildren(activityJsonNode, initializationData, filterPreviouslyDeletedItems);

                        for (var index = 0, len = activityChildren.length; index < len; index++) {
                            self.activities.push((new Activity()).init(platformBuildVersion, activityChildren[index], self, index, manifestReader, activityState, contentLevel + 1, coursePlayerModel, initializationData, filterPreviouslyDeletedItems));
                        }

                        // Set the expected duration in HH:MM:SS for the module
                        if (contentLevel == 1 /* Module */) {
                            self.expectedDurationTime(manifestReader.getDurationTimeRollupForActivity(activityJsonNode));
                        }

                        if (!filterPreviouslyDeletedItems) {
                            coursePlayerModel.activityArray[id] = self;
                        }

                        if (!coursePlayerModel.isCourseContentDeletedSinceUserLastLaunch) {
                            if (this.activityMarkedForDelete) {
                                if (activityJsonNode["deletedVersion"] > this.userLaunchVersion) {
                                    coursePlayerModel.isCourseContentDeletedSinceUserLastLaunch = true;
                                }
                            }
                        }

                        var resourceNode = manifestReader.getResourceNode(activityJsonNode);
                        if (resourceNode) {
                            var isCompleted = false;

                            self.resource(new Resource(resourceNode, activityJsonNode, self, manifestReader, platformBuildVersion));
                            self.duration(self.resource().duration());
                            self.requiredForCourseCompletion = self.resource().scormType === "sco";

                            if (!self.description()) {
                                self.description(self.resource().description());
                            }

                            var lessonStatus = (activityInitData != undefined && activityInitData.lessonStatus != undefined) ? activityInitData.lessonStatus : "not attempted";

                            if (lessonStatus == "completed" || lessonStatus == "passed" || lessonStatus == "failed") {
                                isCompleted = true;
                            }

                            if (isCompleted && activities.length && !self.completedWithChildren) {
                                self.completedWithChildren = true;
                            }

                            if (!(isCompleted && (!activities.length || (activities.length && self.childrenAreCompleted() && self.completedWithChildren)))) {
                                isCompleted = false;
                            }

                            var parent = self.parent;
                            if (parent instanceof Activity && !(isCompleted && !parent.completed() && parent.completedWithChildren && parent.childrenAreCompleted())) {
                                self.parent.completed(false);
                            }

                            var resource = self.resource();

                            if (self.resourceVersionHasChanged(activityInitData)) {
                                self.versionChanged(true);
                                self.updatedContentAvailable(true);

                                // If the version has been updated and the item is partially completed,
                                // clear the existing progress.
                                if (lessonStatus == "incomplete") {
                                    self.activityState.resetActivityProgress(id);
                                    lessonStatus = "not attempted";
                                    isCompleted = false;
                                }
                            }

                            // If the activity's resource is new and the item hasn't been attempted, indicate the new
                            // content is available
                            if (resource.createdVersion != "" && lessonStatus == "not attempted") {
                                self.newContentAvailable(true);
                                if (resource.createdVersion > this.userLaunchVersion) {
                                    self.newContentAvailableSinceUserLastAccessed(true);
                                }
                            }

                            self.lessonStatus = lessonStatus;
                            self.completed(isCompleted);

                            // Set assessment results
                            if (isCompleted && (resource.learningResourceType === ManifestLearningResourceType.Assessment || resource.learningResourceType.toLowerCase() === ManifestLearningResourceType.KnowledgeCheck.toLowerCase())) {
                                self.activityState.getCompletedAssessmentQuestionsInfo(id).done(function (result) {
                                    self.assessmentQuestionsCount(result.totalQuestionsCount);
                                    self.assessmentQuestionsCorrectCount(result.correctQuestionsCount);
                                });
                            }

                            // Set accessed dates
                            if (activityInitData != undefined) {
                                self.firstAccessed(activityInitData.firstAccessed);
                                self.lastAccessed(activityInitData.lastAccessed);
                                self.completedDate(activityInitData.completedDate);
                            }

                            // Set video progress
                            if (resource.learningResourceType === ManifestLearningResourceType.Video && activityInitData != undefined && activityInitData.lessonLocation !== undefined) {
                                var videoStreamOffset = Math.round(activityInitData.lessonLocation), videoElapsedSeconds;

                                if (videoStreamOffset) {
                                    videoElapsedSeconds = parseFloat(videoStreamOffset.toString());
                                } else {
                                    videoElapsedSeconds = 0;
                                }

                                var elapsedTime = microsoft.learning.mlx.utility.convertSecondsToHHMMSS(Math.round(videoElapsedSeconds));

                                self.progressTime(elapsedTime);
                                self.progressPercentage(Math.round(videoElapsedSeconds / self.duration() * 100));
                            }

                            // Set prerequisites for assessment
                            if (activityInitData != undefined && activityInitData.mlxPrerequisites !== undefined) {
                                self.mlxPrerequisites(activityInitData.mlxPrerequisites);
                            }
                        } else {
                            // The item doesn't have a corresponding resource
                            // so we'll mark it as completed if it has children.
                            // This allows checkmarking to rollup properly and show
                            // the activity as completed within the syllabus.
                            if (activities.length) {
                                self.completedWithChildren = true;
                            }
                        }
                    }

                    return this;
                };

                Activity.prototype.childrenAreCompleted = function () {
                    var childrenCompleted = false, completedCount = 0, activities = this.activities(), activity = undefined, resource = undefined;

                    for (var i = 0, len = activities.length; i < len; i++) {
                        activity = activities[i];

                        // Consider the not attempted, but deleted lesson as completed.
                        // Scenario:- User accesses version 1.0.0.0, a new lesson gets added in 1.0.0.1
                        // and later deleted in 1.0.0.2. User accesses 1.0.0.2 version.
                        resource = activity && activity.resource();
                        if (activity.completed() || !activity.requiredForCourseCompletion || (activity.newContentAvailable() && activity.activityMarkedForDelete && resource.createdVersion > activity.userLaunchVersion)) {
                            completedCount++;
                        }
                    }

                    if (completedCount == activities.length) {
                        childrenCompleted = true;
                    }

                    return childrenCompleted;
                };

                Activity.prototype.childrenAreStarted = function () {
                    var childrenStarted = false, activities = this.activities(), childIds = [], deferred = $.Deferred();

                    for (var i = 0, len = activities.length; i < len; i++) {
                        // Check whether any of the children are completed, if so
                        // we won't bother checking the activity state.
                        if (activities[i].completed()) {
                            childrenStarted = true;
                            break;
                        }

                        childIds.push(activities[i].id());
                    }

                    if (childrenStarted) {
                        // We determined that at least one child has been completed, so resolve the promise immediately.
                        deferred.resolve(childrenStarted);
                    } else {
                        // No child activities have been completed, so check the activity state to determine whether
                        // any have been started.
                        this.activityState.getScopedValues("cmi.core.lesson_status", childIds, true).done(function (results) {
                            for (var i = 0, len = results.length; i < len; i++) {
                                if (results[i].status !== "not attempted") {
                                    childrenStarted = true;
                                    break;
                                }
                            }

                            deferred.resolve(childrenStarted);
                        });
                    }

                    return deferred.promise();
                };

                // Determines whether the version specified within the imsmanifest for the resource is
                // greater than the version within the activity state.
                Activity.prototype.resourceVersionHasChanged = function (activityInitData) {
                    var versionChanged = false, currVersion = this.resource() ? this.resource().version : '', priorVersion = activityInitData != undefined ? activityInitData.version : "1.0.0.0";

                    if (currVersion > priorVersion) {
                        versionChanged = true;
                    }

                    return versionChanged;
                };

                // Factory static method to build the activity tree
                Activity.buildActivityTree = function (manifestReader, coursePlayerModel, activityState, activities, previousActivities, platformBuildVersion) {
                    var rootActivity = manifestReader.getRootActivity(), deferred = $.Deferred();

                    activityState.getInitialActivityTreeData().done(function (initializationData) {
                        // The order is important here. First we should calculate the previous version and then head to the current.
                        // We are building the list of activities which include visible items and items marked for delete since user last launched the course.
                        // We use this to determine if deletion might have resulted in module/course completion.
                        var activityChildrenPreviousVersion = manifestReader.getActivityChildren(rootActivity, initializationData, true);
                        for (var index = 0, len = activityChildrenPreviousVersion.length; index < len; index++) {
                            previousActivities.push((new Activity()).init(platformBuildVersion, activityChildrenPreviousVersion[index], coursePlayerModel, index, manifestReader, activityState, 1 /* Module */, coursePlayerModel, initializationData, true));
                        }

                        var activityChildren = manifestReader.getActivityChildren(rootActivity);
                        for (var index = 0, len = activityChildren.length; index < len; index++) {
                            activities.push((new Activity()).init(platformBuildVersion, activityChildren[index], coursePlayerModel, index, manifestReader, activityState, 1 /* Module */, coursePlayerModel, initializationData));
                        }

                        deferred.resolve(activities);
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                Activity.buildActivitySubtreeById = function (id, manifestReader, coursePlayerModel, activityState, activities, platformBuildVersion) {
                    var activityJsonNode = manifestReader.getActivityLineage(id), deferred = $.Deferred();

                    if (activityJsonNode) {
                        activityState.getInitialActivityTreeData().done(function (initializationData) {
                            activities.push((new Activity()).init(platformBuildVersion, activityJsonNode, coursePlayerModel, 0, manifestReader, activityState, 1 /* Module */, coursePlayerModel, initializationData));
                            deferred.resolve(activities);
                        }).fail(function () {
                            deferred.reject();
                        });
                    } else {
                        // The id couldn't be found or the item has been deleted
                        deferred.reject();
                    }

                    return deferred.promise();
                };

                Activity.createActivityById = function (id, manifestReader, coursePlayerModel, activityState, platformBuildVersion) {
                    var activityJsonNode = manifestReader.getActivityNode(id), activity = undefined, deferred = $.Deferred();

                    if (activityJsonNode && !manifestReader.getActivityDeleted(activityJsonNode)) {
                        activityState.getInitialActivityTreeData().done(function (initializationData) {
                            activity = new Activity();
                            activity.init(platformBuildVersion, activityJsonNode, coursePlayerModel, 1, manifestReader, activityState, 1 /* Module */, coursePlayerModel, initializationData);
                            deferred.resolve(activity);
                        }).fail(function () {
                            deferred.reject();
                        });
                    } else {
                        // The id couldn't be found or the item has been deleted
                        deferred.reject();
                    }

                    return deferred.promise();
                };
                return Activity;
            })();
            mlx.Activity = Activity;

            /***********************************************************************
            * Resource class. Encapsulates information about an
            * activity's resource and provides functions to activate/deactivate
            * the resource.
            ***********************************************************************/
            var Resource = (function () {
                function Resource(resourceNode, activityJsonNode, parent, manifestReader, platformBuildVersion) {
                    this.content = ko.observable("");
                    this.isActive = ko.observable(false);
                    this.resRef = manifestReader.getResourceHRef(resourceNode) + manifestReader.getActivityParameters(activityJsonNode);
                    this.parent = parent;
                    this.content = ko.observable("");
                    this.isActive = ko.observable(false);
                    this.durationText = ko.observable(manifestReader.getResourceDuration(resourceNode));
                    this.duration = ko.observable(this.durationText() ? microsoft.learning.mlx.utility.convertHHMMSSToSeconds(this.durationText()) : 0);
                    this.learningResourceType = manifestReader.getLearningResourceType(resourceNode);
                    this.mediaType = ko.observable(this.parent.coursePlayerModel.getMediaType(this.learningResourceType));
                    this.version = manifestReader.getResourceVersion(resourceNode);
                    this.updateDate = manifestReader.getResourceUpdatedDate(resourceNode);
                    this.createdVersion = manifestReader.getResourceCreatedVersion(resourceNode);
                    this.description = ko.observable(manifestReader.getResourceDescription(resourceNode));
                    this.baseUrl = manifestReader.baseUrl;
                    this.platformBuildVersion = platformBuildVersion;
                    this.scormType = manifestReader.getResourceScormType(resourceNode);
                }
                // Activates the resource.
                Resource.prototype.activateResource = function (hashValue, serverDateTimeMs) {
                    var url, resourceType = this.learningResourceType;

                    if (!this.isActive()) {
                        // If the resource is not active, we will create the url for the resource.
                        if (this.platformBuildVersion && this.resRef.toLowerCase().startsWith('../../common/')) {
                            // Construct the resource URL using the build number and the resource URL
                            url = this.baseUrl + "/../../" + this.platformBuildVersion + "/common/" + this.resRef.slice(13, this.resRef.length);
                        } else {
                            // Construct the resource URL using the base URL and resrouce reference
                            url = this.baseUrl + "/" + this.resRef;
                        }

                        // Special processing for specific learning resource types
                        if (resourceType && (resourceType == ManifestLearningResourceType.Assessment || resourceType.toLowerCase() == ManifestLearningResourceType.KnowledgeCheck.toLowerCase() || resourceType == ManifestLearningResourceType.Video || resourceType == ManifestLearningResourceType.Word || resourceType == ManifestLearningResourceType.Excel || resourceType == ManifestLearningResourceType.PowerPoint || resourceType == ManifestLearningResourceType.PDF || resourceType == ManifestLearningResourceType.Zip || resourceType == ManifestLearningResourceType.Embed || resourceType == ManifestLearningResourceType.HtmlCode || resourceType == ManifestLearningResourceType.DocumentDownload)) {
                            var params = {};

                            // Append the course id as a query string parameter
                            // so that driver files that are served from common folders can still reference
                            // the right course properly
                            var courseIdAndLangMatch = /https?:\/\/([^\/]*)\/([0-9]+-[0-9]+\/[a-z]+-[a-z]+)/i.exec(url);

                            if (!courseIdAndLangMatch) {
                                courseIdAndLangMatch = /https?:\/\/([^\/]*)\/r([0-9]+)\/([0-9]+-[0-9]+\/[a-z]+-[a-z]+)/i.exec(url);
                            }

                            if (courseIdAndLangMatch && courseIdAndLangMatch.length > 2) {
                                params.courseIdAndLang = courseIdAndLangMatch[courseIdAndLangMatch.length - 1];
                            }

                            var isPreview = microsoft.learning.mlx.utility.getQueryStringParamValue("preview");
                            if (isPreview && isPreview !== '') {
                                params.preview = isPreview;
                            }

                            if (resourceType === ManifestLearningResourceType.Video) {
                                params.serverDateTime = serverDateTimeMs;
                                params.apiHost = MLX.context.apiHost;
                            }

                            if (this.isVersionedResourceType()) {
                                params.v = platformVersion;
                            }

                            url = microsoft.learning.mlx.utility.addQueryStringParams(url, params, false);
                        }
                    } else {
                        // The resource is already active so we'll use the existing url.
                        url = this.content();
                    }

                    // Append the hash if one exists.
                    if (hashValue) {
                        var hashArr = hashValue.split("=");

                        if (hashArr.length === 2) {
                            url = microsoft.learning.mlx.utility.updateUrlHashParamValue(url, hashArr[0], hashArr[1]);
                        }
                    } else {
                        var posQueryString = url.lastIndexOf("?"), posHash = url.lastIndexOf("#");

                        // Remove all hash params if they occur after a querystring.  This is primarily intended to clear the video bookmark.
                        if (posQueryString > 0 && posHash > posQueryString) {
                            url = url.substring(0, posHash);
                        }
                    }

                    if (resourceType === ManifestLearningResourceType.Video) {
                        // Add hash param to indicate whether video should play immediately.
                        url = microsoft.learning.mlx.utility.updateUrlHashParamValue(url, "play", this.parent.coursePlayerModel.startVideoPlayback);
                    }

                    // For now, content will be hosted w/in our site.
                    this.content(url);
                    this.isActive(true);
                };

                // Deactivates an active resource,
                Resource.prototype.deactivateResource = function () {
                    // Unload existing resources via a blank html page.
                    // We do this because LMS APIs can be called during
                    // window.onunload and we don't want data model
                    // element values to be scoped incorrectly.  We use
                    // ScoTunnel rather than the content observable
                    // in order to get a promise that can be used to
                    // wait until the page has loaded before loading
                    // the next activity.
                    //
                    // Appending a querystring containing a random
                    // sequence of chars to fix an issue where blank.html is already loaded
                    // and it blocks subsequent navigation and content loading.
                    var self = this, promise = ScoTunnel.proxy({
                        target: "SCO_Wrapper_API.reloadFrame",
                        data: [MLX.context.scheme + "://" + MLX.context.apiHost + "/blank.html?" + Math.random().toString(20).substring(3)]
                    }).done(function () {
                        self.isActive(false);
                    });

                    return promise;
                };

                Resource.prototype.updateUrlHashParamValue = function (param, value) {
                    var self = this, url = microsoft.learning.mlx.utility.updateUrlHashParamValue(self.content(), param, value);
                    self.content(url);
                };

                Resource.prototype.isVersionedResourceType = function () {
                    var resourceType = this.learningResourceType;
                    if (resourceType === ManifestLearningResourceType.Lab || resourceType === ManifestLearningResourceType.Assessment || resourceType.toLowerCase() === ManifestLearningResourceType.KnowledgeCheck.toLowerCase() || resourceType === ManifestLearningResourceType.Video) {
                        return true;
                    }
                    return false;
                };
                return Resource;
            })();
            mlx.Resource = Resource;

            /***********************************************************************
            * CoursePlayerModel class.  Instantiates various
            * runtime objects required by the course player and provides wrappers for
            * navigation-related functions.
            ***********************************************************************/
            var CoursePlayerModel = (function (_super) {
                __extends(CoursePlayerModel, _super);
                function CoursePlayerModel(platformBuildVersion, manifestReader, sequencer, globalSettings, webTrendsHelper) {
                    _super.call(this);
                    this.activities = ko.observableArray([]);
                    this.previousActivities = ko.observableArray([]);
                    this.durationCompleted = 0;
                    this.courseCompleted = false;
                    // webTrendsHelper: WebTrendsHelper;
                    this.activeResource = ko.observable(null);
                    this.courseLoaded = ko.observable(false);
                    this.courseTitle = ko.observable("");
                    this.isDurationAvailable = ko.observable(false);
                    this.isCourseContentDeletedSinceUserLastLaunch = false;
                    this.skipAutoPlayExams = false;
                    this.autoPlay = ko.observable(false);
                    this.launchFirstActivityOnCourseLoad = ko.observable(false);
                    this.moduleCount = 0;
                    this.assessmentCount = 0;
                    this.knowledgeCheckCount = 0;
                    this.expectedCourseDuration = "00:00:00";
                    this.startVideoPlayback = false;
                    this.uiMode = 0 /* Normal */;
                    this.courseHasPreviouslyStarted = false;
                    this.rollupParentCompletion = true;

                    this.manifestReader = manifestReader != undefined ? manifestReader : new Scorm12ManifestReader(null);
                    this.sequencer = sequencer != undefined ? sequencer : new microsoft.learning.mlx.Scorm12Sequencer();

                    //this.webTrendsHelper = webTrendsHelper != undefined ? webTrendsHelper : new microsoft.learning.mlx.WebTrendsHelper();
                    this.aiccScriptProcessor = new AiccScriptProcessor(this.sequencer);
                    this.platformBuildVersion = platformBuildVersion;

                    // New for MVA
                    this.globalSettings = globalSettings;
                }
                // Builds an activity tree for the course and initializes its members.
                CoursePlayerModel.prototype.initialize = function (courseId, activityState, courseVersion, courseLevel, courseLanguageCode, userId, skipAutoPlayExams) {
                    this.activityState = activityState;
                    this.userId = userId;
                    this.isAnonymousRequest = (!MLX.context.currentUser.isAuthenticated && MLX.context.isAnonymousTenant) ? true : false;
                    var self = this, deferred = $.Deferred();

                    self.rollupParentCompletion = true;
                    self.autoPlay(self.globalSettings.getValue("AutoPlay"));
                    self.startVideoPlayback = false;

                    self.autoPlay.subscribe(function () {
                        self.globalSettings.setValue("AutoPlay", self.autoPlay());
                        self.globalSettings.saveSettings(self.userId, self.isAnonymousRequest);
                    });

                    self.courseIdentifier = courseId;
                    self.courseVersion = courseVersion;
                    self.courseLanguageCode = courseLanguageCode;
                    self.skipAutoPlayExams = skipAutoPlayExams;
                    self.activities.removeAll();
                    self.previousActivities.removeAll();
                    self.activityArray = [];
                    self.isDurationAvailable(self.manifestReader.isDurationAvailable());
                    self.activityState.getDurationCompleted().done(function (result) {
                        self.durationCompleted = parseInt(result) || 0;
                        self.courseCompleted = (self.durationCompleted === 100);
                    });
                    self.courseTitle(self.manifestReader.getCourseTitle());
                    self.sequencer.initialize(self, activityState, self.aiccScriptProcessor);
                    self.courseLevel = courseLevel;
                    self.moduleCount = self.manifestReader.getModulesCount();
                    self.assessmentCount = self.manifestReader.getAssessmentsCount();
                    self.expectedCourseDuration = self.manifestReader.getTotalVideoDuration();
                    self.activityState.activityStateDataExists().done(function (result) {
                        self.courseHasPreviouslyStarted = result;
                    });

                    // Clearing flag for new call.
                    self.isCourseContentDeletedSinceUserLastLaunch = false;

                    Activity.buildActivityTree(self.manifestReader, self, self.activityState, self.activities, self.previousActivities, self.platformBuildVersion).done(function () {
                        self.activityState.initializeTopicAndAssessmentValues(self.getTopicsTotal(), self.manifestReader.getAssessmentsCount(), self.manifestReader.getAssessmentIdentifiers(ManifestItemVisibility.Visible), self.manifestReader.getAssessmentIdentifiers(ManifestItemVisibility.Hidden), self.manifestReader.getDurations(ManifestItemVisibility.VisibleNotSoftDeleted), self.manifestReader.getLessonIdentifiers(ManifestItemVisibility.SoftDeleted, ManifestResourceScormType.Any), self.manifestReader.getLessonIdentifiers(ManifestItemVisibility.VisibleNotSoftDeleted, ManifestResourceScormType.Asset)).done(function () {
                            // We do this here rather than immediately after buildActivityTree in order to wait
                            // for completion of the async calls which determine the status of each activity.
                            var activities = self.activities(), previousActivities = self.previousActivities(), activity, previousActivity, activityStateDataChanged = false, newlyAddedContent = false, previousActivitiesCompleted = false, firedCompletionEvent = false;

                            for (var i = 0, len = activities.length; i < len; i++) {
                                activity = activities[i];
                                previousActivity = ko.utils.arrayFirst(previousActivities, function (activityObj) {
                                    return activityObj.id() == activity.id();
                                });

                                // Indicate whether the module-level activity has updated content somewhere within
                                // its subtree
                                activity.updatedContentAvailable(self.subtreeHasPropertyValueSetToTrue(activity, "versionChanged"));

                                // If updated content is available, we removed the activity state for partially
                                // completed video SCOs.  Set a flag to indicate that we need to save.
                                if (activity.updatedContentAvailable()) {
                                    activityStateDataChanged = true;
                                }

                                // Indicate whether the module-level activity has new content somewhere within its subtree
                                activity.newContentAvailable(self.subtreeHasPropertyValueSetToTrue(activity, "newContentAvailable"));
                                activity.newContentAvailableSinceUserLastAccessed(self.subtreeHasPropertyValueSetToTrue(activity, "newContentAvailableSinceUserLastAccessed"));
                                if (activity.newContentAvailableSinceUserLastAccessed()) {
                                    newlyAddedContent = true;
                                }

                                // Update the progress for each module.
                                activity.descendantsAndSelfActivityCount = self.getDescendantsAndSelfActivityCount(activity);
                                self.updateProgress(activity);

                                // Fix the initial checkmarking rollup for activities that have children
                                // which have been completed but do not have a resource themselves.
                                if (activity.parent instanceof microsoft.learning.mlx.CoursePlayerModel && !activity.resource() && activity.activities().length && activity.childrenAreCompleted()) {
                                    // Cases where we don't have to raise:-
                                    // 1. user has completed the course and one of the lesson gets deleted.
                                    // 2. user has completed the course, author adds a new lesson and deletes it while user hadn't accessed the content.
                                    // (this can happen at module level as well and is handled seperately).
                                    // 3. user revists the completed course.
                                    if (previousActivity && !previousActivity.childrenAreCompleted()) {
                                        self.setCompleted(activity.id());
                                        firedCompletionEvent = true;
                                    }
                                    activity.completed(true);
                                }
                            }

                            // Check to see if course completion needs to be fired.
                            // This is applicable for the scenario when module deletion results in
                            // course completion.
                            if (!firedCompletionEvent) {
                                firedCompletionEvent = self.handleCourseCompletionEvent(previousActivities, activities);
                            }

                            // Save the activity state and progress.
                            // First check if user is authenticated and active.
                            // State should be saved only when new content is available or
                            // existing content has been modified or
                            // module/course has been completed coz of deletion.
                            if (MLX.context.currentUser.isAuthenticated && MLX.context.currentUser.isActive) {
                                if (activityStateDataChanged || newlyAddedContent || firedCompletionEvent) {
                                    self.activityState.save(true);
                                }
                            }

                            self.courseLoaded(true);
                            deferred.resolve();
                        }).fail(function () {
                            deferred.reject();
                        });
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                // Builds a single activity via its ID and initializes its members.
                CoursePlayerModel.prototype.initializeActivity = function (activityId, courseId, activityState, courseVersion, courseLevel, courseLanguageCode, userId) {
                    this.activityState = activityState;
                    this.userId = userId;

                    var self = this, deferred = $.Deferred();

                    self.rollupParentCompletion = false;
                    self.autoPlay(false);
                    self.startVideoPlayback = false;
                    self.courseIdentifier = courseId;
                    self.courseVersion = courseVersion;
                    self.courseLanguageCode = courseLanguageCode;
                    self.activities.removeAll();
                    self.previousActivities.removeAll();
                    self.activityArray = [];
                    self.isDurationAvailable(self.manifestReader.isDurationAvailable());
                    self.activityState.getDurationCompleted().done(function (result) {
                        self.durationCompleted = parseInt(result) || 0;
                        self.courseCompleted = (self.durationCompleted === 100);
                    });
                    self.courseTitle(self.manifestReader.getCourseTitle());
                    self.sequencer.initialize(self, activityState, self.aiccScriptProcessor);
                    self.courseLevel = courseLevel;
                    self.moduleCount = self.manifestReader.getModulesCount();
                    self.assessmentCount = self.manifestReader.getAssessmentsCount();
                    self.expectedCourseDuration = self.manifestReader.getTotalVideoDuration();
                    self.activityState.activityStateDataExists().done(function (result) {
                        self.courseHasPreviouslyStarted = result;
                    });

                    Activity.buildActivitySubtreeById(activityId, self.manifestReader, self, self.activityState, self.activities, self.platformBuildVersion).done(function () {
                        self.activityState.initializeTopicAndAssessmentValues(self.getTopicsTotal(), self.manifestReader.getAssessmentsCount(), self.manifestReader.getAssessmentIdentifiers(ManifestItemVisibility.Visible), self.manifestReader.getAssessmentIdentifiers(ManifestItemVisibility.Hidden), self.manifestReader.getDurations(ManifestItemVisibility.VisibleNotSoftDeleted), self.manifestReader.getLessonIdentifiers(ManifestItemVisibility.SoftDeleted, ManifestResourceScormType.Any), self.manifestReader.getLessonIdentifiers(ManifestItemVisibility.VisibleNotSoftDeleted, ManifestResourceScormType.Asset)).done(function () {
                            // We do this here rather than immediately after buildActivityTree in order to wait
                            // for completion of the async calls which determine the status of each activity.
                            var activities = self.activities(), activity, activityStateDataChanged = false, newlyAddedContent = false, firedCompletionEvent = false;

                            for (var i = 0, len = activities.length; i < len; i++) {
                                activity = activities[i];

                                // Indicate whether the module-level activity has updated content somewhere within
                                // its subtree
                                activity.updatedContentAvailable(self.subtreeHasPropertyValueSetToTrue(activity, "versionChanged"));

                                // If updated content is available, we removed the activity state for partially
                                // completed video SCOs.  Set a flag to indicate that we need to save.
                                if (activity.updatedContentAvailable()) {
                                    activityStateDataChanged = true;
                                }

                                // Indicate whether the module-level activity has new content somewhere within its subtree
                                activity.newContentAvailable(self.subtreeHasPropertyValueSetToTrue(activity, "newContentAvailable"));
                                activity.newContentAvailableSinceUserLastAccessed(self.subtreeHasPropertyValueSetToTrue(activity, "newContentAvailableSinceUserLastAccessed"));
                                if (activity.newContentAvailableSinceUserLastAccessed()) {
                                    newlyAddedContent = true;
                                }
                            }

                            // Save the activity state and progress.
                            // First check if user is authenticated and active.
                            // State should be saved only when new content is available or
                            // existing content has been modified or
                            // module/course has been completed coz of deletion.
                            if (MLX.context.currentUser.isAuthenticated && MLX.context.currentUser.isActive) {
                                if (activityStateDataChanged || newlyAddedContent || firedCompletionEvent) {
                                    self.activityState.save(true);
                                }
                            }

                            self.courseLoaded(true);
                            deferred.resolve();
                        }).fail(function () {
                            deferred.reject();
                        });
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                // Ensures that the course completion event is fired.
                // Scenarios:-
                // User has completed all lessons in all modules except 1 and that module gets deleted.
                // User has completed all the lessons in all modules and course level assessment gets deleted.
                CoursePlayerModel.prototype.handleCourseCompletionEvent = function (previousActivities, currentActivities) {
                    var self = this, previousActivitiesCompleted = false, currentActivitiesCompleted = false;

                    for (var i = 0, len = previousActivities.length; i < len; i++) {
                        var activityObj = previousActivities[i];
                        if (activityObj && activityObj.activities().length) {
                            if (activityObj.childrenAreCompleted()) {
                                previousActivitiesCompleted = true;
                            } else {
                                previousActivitiesCompleted = false;
                                break;
                            }
                        } else {
                            previousActivitiesCompleted = self.determineAssessmentCompletion(activityObj);
                        }
                    }

                    for (var i = 0, len = currentActivities.length; i < len; i++) {
                        var activityObj = currentActivities[i];
                        if (activityObj.childrenAreCompleted()) {
                            currentActivitiesCompleted = true;
                        } else {
                            currentActivitiesCompleted = false;
                            break;
                        }
                    }

                    // Fire course completion event here.
                    if (!previousActivitiesCompleted && currentActivitiesCompleted) {
                        // Pass a dummy activity, as we just need to fire course completion event.
                        self.raiseCompletionEvent(new Activity());
                        return true;
                    }

                    return false;
                };

                CoursePlayerModel.prototype.determineAssessmentCompletion = function (activityObj) {
                    var assessmentCompleted = false;

                    // Handle the case where a course level assessment was added and deleted in subsequent version and user
                    // hadn't accessed the course.
                    if (activityObj && !activityObj.activities().length) {
                        var resource = activityObj.resource();
                        assessmentCompleted = (activityObj.completed() || activityObj.newContentAvailableSinceUserLastAccessed() && activityObj.activityMarkedForDelete);
                    }

                    return assessmentCompleted;
                };

                CoursePlayerModel.prototype.getMediaType = function (learningResourceType) {
                    var mediaType = "";

                    if (learningResourceType) {
                        mediaType = learningResourceType.toLowerCase().replace(' ', '-');
                    }

                    return mediaType;
                };

                CoursePlayerModel.prototype.setCompleted = function (activityId) {
                    this.rollupActivityCompletion(activityId);
                    return this.durationCompleted;
                };

                CoursePlayerModel.prototype.setCompletedActivities = function (activities) {
                    var self = this;
                    $.each(activities, function (index, activityId) {
                        self.rollupActivityCompletion(activityId);
                    });
                    return self.durationCompleted;
                };

                CoursePlayerModel.prototype.attemptAutoNavigation = function () {
                    var self = this, deferred = $.Deferred();

                    if (self.autoPlay()) {
                        if (!self.skipAutoPlayExams) {
                            // Automatically navigate to the next activity
                            self.getNextResource().done(function () {
                                deferred.resolve();
                            }).fail(function () {
                                deferred.reject();
                            });
                        } else {
                            // Automatically navigate to the next not exam activity
                            self.getNextNotExamResource().done(function () {
                                deferred.resolve();
                            }).fail(function () {
                                deferred.reject();
                            });
                        }
                    }

                    return deferred.promise();
                };

                CoursePlayerModel.prototype.rollupActivityCompletion = function (activityId) {
                    var activity = this.activityArray[activityId];
                    if (!activity.completed()) {
                        if (activity.activities().length && !activity.completedWithChildren) {
                            activity.completedWithChildren = true;
                        }

                        if (!activity.activities().length || (activity.activities().length && activity.childrenAreCompleted() && activity.completedWithChildren)) {
                            if (activity.requiredForCourseCompletion) {
                                this.durationCompleted = this.durationCompleted + activity.duration();
                            }
                            activity.completed(true);
                        }

                        if (this.rollupParentCompletion) {
                            // Rollup parent activity completion
                            var parent = activity.parent;
                            if (parent && parent.completed) {
                                do {
                                    if (!parent.completed() && parent.completedWithChildren) {
                                        if (parent.childrenAreCompleted()) {
                                            this.durationCompleted = this.durationCompleted + parent.duration();
                                            parent.completed(true);
                                            this.raiseCompletionEvent(parent);
                                        } else {
                                            break;
                                        }
                                        parent = parent.parent ? parent.parent : null;
                                    } else {
                                        break;
                                    }
                                } while(parent && !(parent instanceof CoursePlayerModel));
                            }
                        }

                        this.updateProgress(activity);
                        this.raiseCompletionEvent(activity);
                    }
                    //this.webTrendsHelper.logLearningResourceEnd(this.courseTitle(), this.courseIdentifier, (activity.resource()) ? activity.resource().learningResourceType : '', activity.contentLevel(), activity.title());
                };

                CoursePlayerModel.prototype.raiseCompletionEvent = function (activity) {
                    var self = this;

                    if (activity) {
                        $.when(self.activityState.getCompletionStats()).done(function (courseStats) {
                            var courseInfo = {
                                "course": {
                                    "id": self.courseIdentifier,
                                    "languageCode": self.courseLanguageCode,
                                    "title": self.courseTitle(),
                                    "version": self.courseVersion
                                }
                            };

                            $.extend(courseInfo.course, courseStats);

                            var resource = activity && activity.resource();

                            // CoursePlayerEvent.AssessmentCompleted is handled by a separate function
                            if (!resource || (resource && resource.learningResourceType !== ManifestLearningResourceType.Assessment)) {
                                if (activity.contentLevel() === 1 /* Module */) {
                                    var moduleInfo = {
                                        "module": {
                                            "id": activity.id(),
                                            "title": activity.title()
                                        }
                                    };

                                    if (resource) {
                                        moduleInfo.module.learningResourceType = resource.learningResourceType;
                                    }

                                    $.extend(courseInfo, moduleInfo);
                                    self.notifySubscribers(32 /* ModuleCompleted */, courseInfo);
                                } else if (activity.contentLevel() === 2 /* Lesson */) {
                                    var lessonInfo = {
                                        "module": {
                                            "id": activity.parent.id(),
                                            "title": activity.parent.title()
                                        },
                                        "lesson": {
                                            "id": activity.id(),
                                            "title": activity.title()
                                        }
                                    };

                                    if (resource) {
                                        lessonInfo.lesson.learningResourceType = resource.learningResourceType;
                                    }

                                    $.extend(courseInfo, lessonInfo);
                                    self.notifySubscribers(8 /* LessonCompleted */, courseInfo);
                                } else if (activity.contentLevel() === 3 /* Topic */) {
                                    var topicInfo = {
                                        "module": {
                                            "id": activity.parent.parent.id(),
                                            "title": activity.parent.parent.title()
                                        },
                                        "lesson": {
                                            "id": activity.parent.id(),
                                            "title": activity.parent.title()
                                        },
                                        "topic": {
                                            "id": activity.id(),
                                            "title": activity.title(),
                                            "learningResourceType": resource.learningResourceType
                                        }
                                    };

                                    $.extend(courseInfo, topicInfo);
                                    self.notifySubscribers(2 /* TopicCompleted */, courseInfo);
                                }
                            }

                            if (!self.courseCompleted && courseStats.percentCompleted === 100) {
                                self.courseCompleted = true;
                                self.notifySubscribers(512 /* CourseCompleted */, courseInfo);
                            }
                        });
                    }
                };

                CoursePlayerModel.prototype.raiseStartedEvent = function (activity) {
                    var courseInfo = {
                        "course": {
                            "id": this.courseIdentifier,
                            "title": this.courseTitle()
                        }
                    }, resource = activity && activity.resource();

                    if (activity.contentLevel() === 2 /* Lesson */) {
                        var self = this, moduleActivity = activity && activity.parent;

                        moduleActivity.childrenAreStarted().done(function (started) {
                            if (!started) {
                                var moduleInfo = {
                                    "module": {
                                        "id": moduleActivity.id(),
                                        "title": moduleActivity.title()
                                    }
                                };

                                $.extend(courseInfo, moduleInfo);
                                self.notifySubscribers(16 /* ModuleStarted */, courseInfo);
                            }
                        });
                    }

                    // CoursePlayerEvent.AssessmentStarted is handled by a separate function
                    if (!resource || (resource && resource.learningResourceType !== ManifestLearningResourceType.Assessment)) {
                        if (activity.contentLevel() === 2 /* Lesson */) {
                            var lessonInfo = {
                                "module": {
                                    "id": activity.parent.id(),
                                    "title": activity.parent.title()
                                },
                                "lesson": {
                                    "id": activity.id(),
                                    "title": activity.title()
                                }
                            };

                            if (resource) {
                                lessonInfo.lesson.learningResourceType = resource.learningResourceType;
                            }

                            $.extend(courseInfo, lessonInfo);
                            this.notifySubscribers(4 /* LessonStarted */, courseInfo);
                        } else if (activity.contentLevel() === 3 /* Topic */) {
                            var topicInfo = {
                                "module": {
                                    "id": activity.parent.parent.id(),
                                    "title": activity.parent.parent.title()
                                },
                                "lesson": {
                                    "id": activity.parent.id(),
                                    "title": activity.parent.title()
                                },
                                "topic": {
                                    "id": activity.id(),
                                    "title": activity.title(),
                                    "learningResourceType": resource.learningResourceType
                                }
                            };

                            $.extend(courseInfo, topicInfo);
                            this.notifySubscribers(1 /* TopicStarted */, courseInfo);
                        }
                    }
                };

                CoursePlayerModel.prototype.raiseAssessmentStartedEvent = function (attemptCount) {
                    var activity = this.activeResource().parent, resource = activity && activity.resource();

                    if (activity && resource && resource.learningResourceType === ManifestLearningResourceType.Assessment) {
                        var courseInfo = {
                            "course": {
                                "id": this.courseIdentifier,
                                "title": this.courseTitle()
                            }
                        };

                        if (activity.contentLevel() === 1 /* Module */) {
                            var moduleInfo = {
                                "assessment": {
                                    "id": activity.id(),
                                    "title": activity.title(),
                                    "attempts": attemptCount
                                }
                            };

                            $.extend(courseInfo, moduleInfo);
                        } else if (activity.contentLevel() === 2 /* Lesson */) {
                            var lessonInfo = {
                                "module": {
                                    "id": activity.parent.id(),
                                    "title": activity.parent.title()
                                },
                                "assessment": {
                                    "id": activity.id(),
                                    "title": activity.title(),
                                    "attempts": attemptCount
                                }
                            };

                            $.extend(courseInfo, lessonInfo);
                        }

                        this.notifySubscribers(64 /* AssessmentStarted */, courseInfo);
                    }
                };

                CoursePlayerModel.prototype.raiseAssessmentCompletedEvent = function (result) {
                    var activity = this.activeResource().parent;
                    this.raiseAssessmentCompletedEventForActivity(activity, result);
                };

                CoursePlayerModel.prototype.raiseAssessmentCompletedEventForActivity = function (activity, result) {
                    var self = this, resource = activity && activity.resource();

                    if (activity && resource && resource.learningResourceType === ManifestLearningResourceType.Assessment) {
                        $.when(self.activityState.getCompletionStats()).done(function (courseStats) {
                            var courseInfo = {
                                "course": {
                                    "id": self.courseIdentifier,
                                    "title": self.courseTitle()
                                }
                            };

                            $.extend(courseInfo.course, courseStats);

                            if (activity.contentLevel() === 1 /* Module */) {
                                var moduleInfo = {
                                    "assessment": {
                                        "id": activity.id(),
                                        "title": activity.title(),
                                        "result": result
                                    }
                                };

                                $.extend(courseInfo, moduleInfo);
                            } else if (activity.contentLevel() === 2 /* Lesson */) {
                                var lessonInfo = {
                                    "module": {
                                        "id": activity.parent.id(),
                                        "title": activity.parent.title()
                                    },
                                    "assessment": {
                                        "id": activity.id(),
                                        "title": activity.title(),
                                        "result": result
                                    }
                                };

                                $.extend(courseInfo, lessonInfo);
                            }

                            self.notifySubscribers(128 /* AssessmentCompleted */, courseInfo);
                        });
                    }
                };

                CoursePlayerModel.prototype.raiseAssessmentCompletedEventsForTestout = function (assessmentIds, result) {
                    var activity;

                    for (var i = 0, len = assessmentIds.length; i < len; i++) {
                        activity = this.sequencer.getSpecifiedActivity(assessmentIds[i]);

                        if (activity) {
                            this.raiseAssessmentCompletedEventForActivity(activity, result);
                        }
                    }
                };

                CoursePlayerModel.prototype.getCurrentActivityTitle = function () {
                    var activity = this.activeResource().parent;
                    return activity.title();
                };

                CoursePlayerModel.prototype.getCurrentResourceVersion = function () {
                    return this.activeResource().version || this.activeResource().createdVersion || "";
                };

                CoursePlayerModel.prototype.raiseDisplayContentRequestEvent = function (url) {
                    var activity = this.activeResource().parent;

                    var eventData = {
                        "course": {
                            "id": this.courseIdentifier,
                            "languageCode": this.courseLanguageCode,
                            "title": this.courseTitle(),
                            "version": this.courseVersion
                        },
                        "module": {
                            "id": activity.parent.id(),
                            "title": activity.parent.title()
                        },
                        "lesson": {
                            "id": activity.id(),
                            "title": activity.title(),
                            "learningresourcetype": activity.resource().learningResourceType
                        },
                        "resource": {
                            "url": url
                        }
                    };

                    this.notifySubscribers(4194304 /* DisplayContentRequest */, eventData);
                };

                CoursePlayerModel.prototype.raiseCourseStartedEvent = function () {
                    var courseInfo = {
                        "course": {
                            "id": this.courseIdentifier,
                            "title": this.courseTitle()
                        }
                    };

                    this.notifySubscribers(256 /* CourseStarted */, courseInfo);
                };

                CoursePlayerModel.prototype.raiseVideoPlayerEvent = function (action, actionInfo) {
                    var activity = this.activeResource().parent, coursePlayerEvent;

                    if (activity.contentLevel() === 2 /* Lesson */) {
                        var lessonInfo = {
                            "module": {
                                "id": activity.parent.id(),
                                "title": activity.parent.title()
                            },
                            "lesson": {
                                "id": activity.id(),
                                "title": activity.title()
                            }
                        };

                        $.extend(actionInfo, lessonInfo);
                    } else if (activity.contentLevel() === 3 /* Topic */) {
                        var topicInfo = {
                            "module": {
                                "id": activity.parent.parent.id(),
                                "title": activity.parent.parent.title()
                            },
                            "lesson": {
                                "id": activity.parent.id(),
                                "title": activity.parent.title()
                            },
                            "topic": {
                                "id": activity.id(),
                                "title": activity.title()
                            }
                        };

                        $.extend(actionInfo, topicInfo);
                    }

                    switch (action) {
                        case VideoPlayerAction.Play:
                            coursePlayerEvent = 1024 /* VideoPlaybackStarted */;
                            break;
                        case VideoPlayerAction.Stop:
                        case VideoPlayerAction.VideoEnded:
                        case VideoPlayerAction.Exit:
                            coursePlayerEvent = 2048 /* VideoPlaybackStopped */;
                            break;
                        case VideoPlayerAction.Pause:
                            coursePlayerEvent = 4096 /* VideoPlaybackPaused */;
                            break;
                        case VideoPlayerAction.Skip:
                        case VideoPlayerAction.Scrub:
                        case VideoPlayerAction.Bookmark:
                            coursePlayerEvent = 8192 /* VideoPlaybackSkipped */;
                            break;
                        case VideoPlayerAction.Milestone:
                            coursePlayerEvent = 16384 /* VideoPlaybackMilestone */;
                            break;
                        case VideoPlayerAction.Volume:
                            coursePlayerEvent = 65536 /* VideoVolumeChanged */;
                            break;
                        case VideoPlayerAction.ClosedCaption:
                            coursePlayerEvent = 32768 /* VideoCaptionStateChanged */;
                            break;
                        case VideoPlayerAction.Resolution:
                            coursePlayerEvent = 131072 /* VideoResolutionChanged */;
                            break;
                        case VideoPlayerAction.Speed:
                            coursePlayerEvent = 262144 /* VideoSpeedChanged */;
                            break;
                        case VideoPlayerAction.Download:
                            coursePlayerEvent = 524288 /* VideoDownloaded */;
                            break;
                        case VideoPlayerAction.Fullscreen:
                            coursePlayerEvent = 1048576 /* VideoFullscreenChanged */;
                            break;
                        case VideoPlayerAction.AccumulatedPlaybackTime:
                            coursePlayerEvent = 2097152 /* VideoPlaybackAccumulatedTime */;
                            break;
                        case VideoPlayerAction.TextTrackLoad:
                            coursePlayerEvent = 8388608 /* TranscriptLoaded */;
                            break;
                    }

                    if (coursePlayerEvent) {
                        this.notifySubscribers(coursePlayerEvent, actionInfo);
                    }
                };

                CoursePlayerModel.prototype.updateVideoProgress = function (activityId, videoElapsedSeconds) {
                    var activity = this.activityArray[activityId];
                    if (activity) {
                        var elapsedTime = microsoft.learning.mlx.utility.convertSecondsToHHMMSS(Math.round(videoElapsedSeconds));
                        activity.progressTime(elapsedTime);

                        var progressPercentage = Math.round(videoElapsedSeconds / activity.duration() * 100);
                        activity.progressPercentage(progressPercentage);
                    }
                };

                CoursePlayerModel.prototype.updateAssessmentProgress = function (activityId, totalQuestionsCount, correctQuestionsCount) {
                    var activity = this.activityArray[activityId];
                    if (activity) {
                        activity.assessmentQuestionsCount(totalQuestionsCount);
                        activity.assessmentQuestionsCorrectCount(correctQuestionsCount);
                    }
                };

                CoursePlayerModel.prototype.updateActivityDateStamps = function (activityId, result) {
                    var activity = this.activityArray[activityId];
                    activity.firstAccessed(result.firstAccessed);
                    activity.lastAccessed(result.lastAccessed);
                    activity.completedDate(result.completedDate);
                };

                // Navigates to the previous activity in relation to the current activity.
                CoursePlayerModel.prototype.getPrevResource = function () {
                    var prevActivity = this.sequencer.getPrevActivity();
                    return this.activateResource(prevActivity, null);
                };

                // Navigates to the next activity in relation to the current activity.
                CoursePlayerModel.prototype.getNextResource = function () {
                    var nextActivity = this.sequencer.getNextActivity();
                    return this.activateResource(nextActivity, null);
                };

                // Navigates to the next not exam activity in relation to the current activity.
                CoursePlayerModel.prototype.getNextNotExamResource = function () {
                    var activity = this.sequencer.getNextActivity();

                    while (activity && activity.resource() && (activity.resource().learningResourceType == microsoft.learning.mlx.ManifestLearningResourceType.Assessment || activity.resource().learningResourceType.toLowerCase() == microsoft.learning.mlx.ManifestLearningResourceType.KnowledgeCheck.toLowerCase())) {
                        activity = this.sequencer.getNextActivityById(activity.id());
                    }

                    return this.activateResource(activity, null);
                };

                // Navigates to the selected activity and allows an optional hash to
                // be included in the URL when the resource is loaded.
                CoursePlayerModel.prototype.getSelectedResource = function (selectedActivity, hashValue) {
                    return this.activateResource(selectedActivity, hashValue);
                };

                // Navigates to the specified activity via its ID and allows an optional hash to
                // be included in the URL when the resource is loaded.
                CoursePlayerModel.prototype.getSpecifiedResource = function (activityId, hashValue) {
                    var activity = this.sequencer.getSpecifiedActivity(activityId);
                    return this.activateResource(activity, hashValue);
                };

                // Navigates to the activity where the user left off during their prior session.
                CoursePlayerModel.prototype.startCourse = function (startingActivity) {
                    var self = this, deferred = $.Deferred();

                    //this.startVideoPlayback = true;
                    if (startingActivity) {
                        self.activateResource(startingActivity, null).done(function () {
                            deferred.resolve();
                        }).fail(function () {
                            deferred.reject();
                        });
                    } else {
                        self.sequencer.getStartingActivity().done(function (activity) {
                            //self.webTrendsHelper.logCourseStart(self.courseTitle(), self.courseIdentifier);
                            if (activity) {
                                self.activateResource(activity, null).done(function () {
                                    deferred.resolve();
                                }).fail(function () {
                                    deferred.reject();
                                });
                            } else {
                                var activities = ko.utils.unwrapObservable(self.activities());
                                self.activateFirstResource(activities[0]).done(function () {
                                    deferred.resolve();
                                }).fail(function () {
                                    deferred.reject();
                                });
                            }
                        }).fail(function () {
                            deferred.reject();
                        });
                    }

                    return deferred.promise();
                };

                // End the course by unloading the active resource, and setting courseLoaded false
                CoursePlayerModel.prototype.endCourse = function () {
                    var self = this, deferred = $.Deferred();

                    if (this.courseLoaded()) {
                        if (this.activeResource()) {
                            // Handle the window.onload event for the iframe when blank.htm is set as the
                            // source during deactivation.  We wait for the blank page to load before loading our
                            // next content.  Note that we use jQuery .one here rather than .load, otherwise we'll
                            // get multiple callbacks and the highlighting of nodes in the left nav starts to
                            // behave strangely (multiple hightlighted items).
                            this.doDeactivation().done(function () {
                                self.courseLoaded(false);
                                self.activities.removeAll();

                                //self.webTrendsHelper.logCourseEnd(self.courseTitle(), self.courseIdentifier);
                                // Wait until the save operation has completed before resolving
                                var callback = function () {
                                    self.activityState.isSaveCompleted().done(function (result) {
                                        if (result !== true) {
                                            window.setTimeout(callback, 200);
                                        } else {
                                            deferred.resolve();
                                        }
                                    }).fail(function () {
                                        deferred.reject();
                                    });
                                };

                                window.setTimeout(callback, 500);
                            }).fail(function () {
                                deferred.reject();
                            });
                        } else {
                            deferred.resolve();
                        }
                    } else {
                        deferred.resolve();
                    }

                    return deferred.promise();
                };

                CoursePlayerModel.prototype.sendCoursePlayerEventToBIService = function (coursePlayerEventId, eventData) {
                    return MLX.ajax({
                        url: '/sdk/bievents/v1.0/' + coursePlayerEventId,
                        type: 'POST',
                        dataType: 'json',
                        data: JSON.stringify(eventData),
                        contentType: 'application/json'
                    }).fail(function (data) {
                        console.log('Failed to send BI event data to the MLX Platform. ' + data.statusCode + ' ' + data.statusMessage + ' ' + data.responseText);
                    });
                };

                CoursePlayerModel.prototype.sendEmbeddedPlayerEventToBIService = function (playerEventId, eventData) {
                    return MLX.ajax({
                        url: '/sdk/bievents/v1.0/embedded/' + playerEventId,
                        type: 'POST',
                        dataType: 'json',
                        data: JSON.stringify(eventData),
                        contentType: 'application/json'
                    }).fail(function (data) {
                        console.log('Failed to send BI event data to the MLX Platform. ' + data.statusCode + ' ' + data.statusMessage + ' ' + data.responseText);
                    });
                };

                // Activates the first activity/resource.
                CoursePlayerModel.prototype.activateFirstResource = function (activity) {
                    while (activity.activities().length > 0) {
                        activity = activity.activities()[0];
                    }
                    return this.activateResource(activity, null);
                };

                // Activates the last activity/resource.
                CoursePlayerModel.prototype.activateLastResource = function (activity) {
                    while (activity.activities().length > 0) {
                        activity = activity.activities()[activity.activities().length - 1];
                    }
                    return this.activateResource(activity, null);
                };

                // Activates the specified activity/resource, if its prerequisites
                // (sequencing rules) have been satisfied.
                CoursePlayerModel.prototype.activateResource = function (activity, hashValue) {
                    var self = this, deferred = $.Deferred(), resource = activity && activity.resource();

                    if (resource && !resource.isActive()) {
                        var isSatisfied = this.aiccScriptProcessor.prerequisitesSatisfied(activity.mlxPrerequisites());
                        if (isSatisfied) {
                            if (this.activeResource()) {
                                this.doDeactivation().done(function () {
                                    self.doActivation(activity, hashValue).done(function () {
                                        deferred.resolve();
                                    });
                                }).fail(function () {
                                    deferred.reject();
                                });
                            } else {
                                this.doActivation(activity, hashValue).done(function () {
                                    deferred.resolve();
                                }).fail(function () {
                                    deferred.reject();
                                });
                            }
                        } else {
                            deferred.reject();
                            // TODO: Display a message to the user if navigation cannot take place due
                            // to prerequisites not having been satisfied.
                        }
                    } else if (resource && resource.isActive()) {
                        if (!this.startVideoPlayback) {
                            this.startVideoPlayback = true;
                        }

                        // MVA bookmarks - Update the URL without reloading the page
                        resource.activateResource(hashValue);
                        deferred.resolve();
                    } else {
                        deferred.reject();
                    }

                    return deferred.promise();
                };

                // Displays the resource for the activity and sets the activity as
                // the current one within the view model.
                CoursePlayerModel.prototype.doActivation = function (activity, hashValue) {
                    var _this = this;
                    var self = this, deferred = $.Deferred();

                    // Ping the server to trigger reauthentication if the token has expired.
                    MLX.ajax({
                        url: MLX.context.scheme + "://" + MLX.context.apiHost + "/services/learners/courseactivities/ping",
                        dataType: "json"
                    }).done(function (serverDateTimeMs) {
                        // Navigate to the next activity once the ping has succeeded.
                        var activityId = activity.id(), resource = activity.resource(), resourceVersion = resource.version || resource.createdVersion || "";

                        // Set the current activity id so that
                        // LMS API calls are scoped properly.
                        self.activityState.initializeCurrentActivity(activityId, resource.learningResourceType, resourceVersion).done(function () {
                            // Load the content for the activity
                            resource.activateResource(hashValue, serverDateTimeMs);

                            //self.webTrendsHelper.logLearningResourceStart(self.courseTitle(), self.courseIdentifier, resource.learningResourceType, activity.contentLevel(), activity.title());
                            // Update the view model
                            self.activeResource(resource);

                            if (!self.courseHasPreviouslyStarted) {
                                self.courseHasPreviouslyStarted = true;
                                self.raiseCourseStartedEvent();
                            }

                            // Always raise the started event when an activity is launched.
                            self.raiseStartedEvent(activity);

                            // Raise an event to notify the tenant application that the embedly link's
                            // content should be displayed externally (task #72456).  Note that we didn't
                            // add this code within Resource.activateResource because the call to
                            // raiseDisplayContentRequestEvent requires that activeResource is already set.
                            if (resource.learningResourceType == ManifestLearningResourceType.Embed) {
                                var embedSourceJSON = microsoft.learning.mlx.utility.getQueryStringParamValueFromUrl(resource.content(), "embedSourceJSON");

                                if (embedSourceJSON) {
                                    var embedObj = JSON.parse(embedSourceJSON);

                                    if (embedObj && embedObj.Urls && embedObj.Urls.length && embedObj.Urls[0].Url) {
                                        _this.raiseDisplayContentRequestEvent(embedObj.Urls[0].Url);
                                    }
                                }
                            }

                            deferred.resolve();
                        }).fail(function () {
                            deferred.reject();
                        });
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                // Unloads the resource for the activity and flags the activity as
                // not active.
                CoursePlayerModel.prototype.doDeactivation = function () {
                    // MVA feature - If the user navigated to the course player page to view course info
                    // then manually played a video, all subsequent videos should start automatically.
                    var resource = this.activeResource();
                    if (resource && !this.startVideoPlayback) {
                        this.startVideoPlayback = true;
                    }

                    return resource ? resource.deactivateResource() : $.Deferred().resolve();
                };

                // Helper function which determines the total number of SCOs within
                // the respective course which are visible and haven't been soft-deleted.
                // For MLX courses, only the leaf-node activities are counted.  For older
                // courses, block-level nodes are also counted if they have a resource.
                CoursePlayerModel.prototype.getTopicsTotal = function () {
                    var identifiers = this.manifestReader.getLessonIdentifiers(ManifestItemVisibility.VisibleNotSoftDeleted);
                    return identifiers.length;
                };

                CoursePlayerModel.prototype.getDescendantsAndSelfActivityCount = function (activity) {
                    var count = 0;

                    if (activity && activity.requiredForCourseCompletion && activity.resource()) {
                        count++;
                    }

                    count += this.getDescendantsAndSelfActivityCountHelper(activity.activities);

                    return count;
                };

                CoursePlayerModel.prototype.getDescendantsAndSelfActivityCountHelper = function (activities) {
                    var count = 0, topics = activities(), topic;

                    for (var i = 0, len = topics.length; i < len; i++) {
                        topic = topics[i];

                        if (topic.requiredForCourseCompletion && topic.resource()) {
                            count++;
                        }

                        if (topic.activities().length > 0) {
                            count += this.getDescendantsAndSelfActivityCountHelper(topic.activities);
                        }
                    }

                    return count;
                };

                // Gets the root activity (Module) for the specified activity
                CoursePlayerModel.prototype.getRootActivity = function (activity) {
                    while (activity && !(activity.parent instanceof CoursePlayerModel)) {
                        activity = activity.parent;
                    }

                    return activity;
                };

                // Updates module progress for the related activity
                CoursePlayerModel.prototype.updateProgress = function (activity) {
                    var progressPercentage = 0, completedCount = 0, rootActivity = this.getRootActivity(activity);

                    if (rootActivity && rootActivity.descendantsAndSelfActivityCount > 0) {
                        completedCount = this.getDescendantsAndSelfCompletedCount(rootActivity);
                        progressPercentage = Math.round(completedCount / rootActivity.descendantsAndSelfActivityCount * 100);
                    }

                    rootActivity.progress(progressPercentage);
                };

                CoursePlayerModel.prototype.setCurrentUiMode = function (mode) {
                    this.uiMode = mode;
                    var isFullscreen = mode === 1 /* Fullscreen */;
                    return this.sendScoWrapperMessage(ManifestLearningResourceType.Video, ScoWrapperTargetFunction.UpdateUrlHashParamValue, [VideoPlayerAction.Fullscreen, isFullscreen]);
                };

                CoursePlayerModel.prototype.getDescendantsAndSelfCompletedCount = function (activity) {
                    var count = 0;

                    // Count completed activities with resources which are required for completion
                    if (activity && activity.requiredForCourseCompletion && activity.resource() && activity.completed()) {
                        count++;
                    }

                    count += this.getCompletedActivitiesCount(activity.activities);

                    return count;
                };

                CoursePlayerModel.prototype.getCompletedActivitiesCount = function (activities) {
                    var self = this, count = 0;

                    ko.utils.arrayForEach(activities(), function (activity) {
                        // Count completed activities with resources which are required for completion
                        if (activity && activity.requiredForCourseCompletion && activity.resource() && activity.completed()) {
                            count++;
                        }

                        if (activity.activities().length > 0) {
                            count += self.getCompletedActivitiesCount(activity.activities);
                        }
                    });
                    return count;
                };

                CoursePlayerModel.prototype.subtreeHasPropertyValueSetToTrue = function (activity, property) {
                    if (activity && activity.resource() && activity[property]()) {
                        return true;
                    }

                    if (activity && activity.activities().length > 0) {
                        return this.descendantsHavePropertyValueSetToTrue(activity.activities(), property);
                    } else {
                        return false;
                    }
                };

                CoursePlayerModel.prototype.descendantsHavePropertyValueSetToTrue = function (activities, property) {
                    for (var i = 0, len = activities.length; i < len; i++) {
                        if (activities[i].resource() && activities[i][property]()) {
                            return true;
                        }

                        if (activities[i].activities().length > 0) {
                            return this.descendantsHavePropertyValueSetToTrue(activities[i].activities(), property);
                        }
                    }

                    return false;
                };

                CoursePlayerModel.prototype.getAssessmentPrerequisites = function (activityId, prerequisiteRelationType, activityResourceAvailability, completionStatus) {
                    var activity = this.sequencer.getSpecifiedActivity(activityId);
                    return this.getSiblingAndDescendantIdentifiers(activity, prerequisiteRelationType, activityResourceAvailability, completionStatus, 1 /* SCO */);
                };

                CoursePlayerModel.prototype.getAssessmentPrerequisitesExpression = function (activityId) {
                    var ids = this.getAssessmentPrerequisites(activityId, 0 /* Siblings */, 2 /* Any */, 2 /* Any */);
                    return this.aiccScriptProcessor.createPrerequistesExpression(ids);
                };

                CoursePlayerModel.prototype.setPrerequisites = function (activityId, prerequisitesExpression) {
                    if (prerequisitesExpression && this.aiccScriptProcessor.isValidPrerequistesExpression(prerequisitesExpression)) {
                        var activity = this.sequencer.getSpecifiedActivity(activityId);
                        if (activity) {
                            activity.mlxPrerequisites(prerequisitesExpression);
                        }
                    }
                };

                CoursePlayerModel.prototype.prerequisitesSatisfied = function (prerequisitesExpression) {
                    return this.aiccScriptProcessor.prerequisitesSatisfied(prerequisitesExpression);
                };

                CoursePlayerModel.prototype.getSiblingAndDescendantIdentifiers = function (activity, prerequisiteRelationType, activityResourceAvailability, completionStatus, scormType) {
                    if (typeof scormType === "undefined") { scormType = 2 /* Any */; }
                    var activityIds = [];

                    if (activity && activity.parent) {
                        var activities = activity.parent.activities();

                        for (var i = 0, len = activities.length; i < len; i++) {
                            // Skip the specified activity
                            if (activities[i].id() === activity.id()) {
                                continue;
                            }

                            $.merge(activityIds, this.getSelfAndDescendantIds(activities[i], activities[i].activities, prerequisiteRelationType, activityResourceAvailability, completionStatus, scormType));
                        }
                    }

                    return activityIds;
                };

                CoursePlayerModel.prototype.getSelfAndDescendantIds = function (activity, activities, prerequisiteRelationType, activityResourceAvailability, completionStatus, scormType) {
                    var _this = this;
                    if (typeof scormType === "undefined") { scormType = 2 /* Any */; }
                    var activityIds = [], descendantIds;

                    if (((activityResourceAvailability === 0 /* Present */ || activityResourceAvailability === 2 /* Any */) && activity.resource() && (scormType === 2 /* Any */ || (scormType === 1 /* SCO */ && activity.resource() && activity.resource().scormType === "sco"))) || ((activityResourceAvailability === 1 /* NotPresent */ || activityResourceAvailability === 2 /* Any */) && !activity.resource()) && (completionStatus === 0 /* Completed */ && activity.completed() || completionStatus === 1 /* NotCompleted */ && !activity.completed() || completionStatus === 2 /* Any */)) {
                        activityIds.push(activity.id());
                    }

                    if (prerequisiteRelationType === 1 /* SiblingsAndDescendants */) {
                        ko.utils.arrayForEach(activities(), function (activity) {
                            descendantIds = _this.getSelfAndDescendantIds(activity, activity.activities, prerequisiteRelationType, activityResourceAvailability, completionStatus, scormType);
                            $.merge(activityIds, descendantIds);
                        });
                    }

                    return activityIds;
                };

                CoursePlayerModel.prototype.getPercentCompleted = function () {
                    return this.activityState.getPercentCompleted();
                };

                CoursePlayerModel.prototype.getCompletionStats = function () {
                    return this.activityState.getCompletionStats();
                };

                CoursePlayerModel.prototype.createActivityById = function (activityId) {
                    return Activity.createActivityById(activityId, this.manifestReader, this, this.activityState, this.platformBuildVersion);
                };

                CoursePlayerModel.prototype.requestVideoPause = function () {
                    return this.sendScoWrapperMessage(ManifestLearningResourceType.Video, ScoWrapperTargetFunction.RequestVideoPlayerAction, VideoPlayerAction.Pause);
                };

                CoursePlayerModel.prototype.requestVideoPlay = function () {
                    return this.sendScoWrapperMessage(ManifestLearningResourceType.Video, ScoWrapperTargetFunction.RequestVideoPlayerAction, VideoPlayerAction.Play);
                };

                CoursePlayerModel.prototype.requestGoToVideoPosition = function (eventData) {
                    return this.sendScoWrapperMessage(ManifestLearningResourceType.Video, ScoWrapperTargetFunction.RequestVideoPlayerAction, [VideoPlayerAction.GoToPosition, eventData]);
                };

                CoursePlayerModel.prototype.getCourseCumulativeVideoPlaybackTime = function () {
                    var activityIds = this.manifestReader.getVideoIdentifiers(ManifestItemVisibility.VisibleNotSoftDeleted);
                    return this.activityState.getAggregatedTotalTime(activityIds);
                };

                CoursePlayerModel.prototype.sendScoWrapperMessage = function (learningResourceType, target, data) {
                    var promise = $.Deferred().reject().promise();

                    var resource = this.activeResource();
                    if (resource) {
                        if (resource.learningResourceType === learningResourceType) {
                            promise = ScoTunnel.proxy({
                                target: target,
                                data: Array.isArray(data) ? data : [data]
                            });
                        }
                    }

                    return promise;
                };
                return CoursePlayerModel;
            })(mlx.PubSubExtender);
            mlx.CoursePlayerModel = CoursePlayerModel;

            (function (CoursePlayerUiMode) {
                CoursePlayerUiMode[CoursePlayerUiMode["Normal"] = 0] = "Normal";
                CoursePlayerUiMode[CoursePlayerUiMode["Fullscreen"] = 1] = "Fullscreen";
            })(mlx.CoursePlayerUiMode || (mlx.CoursePlayerUiMode = {}));
            var CoursePlayerUiMode = mlx.CoursePlayerUiMode;

            (function (PrerequisiteRelationType) {
                PrerequisiteRelationType[PrerequisiteRelationType["Siblings"] = 0] = "Siblings";
                PrerequisiteRelationType[PrerequisiteRelationType["SiblingsAndDescendants"] = 1] = "SiblingsAndDescendants";
            })(mlx.PrerequisiteRelationType || (mlx.PrerequisiteRelationType = {}));
            var PrerequisiteRelationType = mlx.PrerequisiteRelationType;

            (function (ActivityResourceAvailability) {
                ActivityResourceAvailability[ActivityResourceAvailability["Present"] = 0] = "Present";
                ActivityResourceAvailability[ActivityResourceAvailability["NotPresent"] = 1] = "NotPresent";
                ActivityResourceAvailability[ActivityResourceAvailability["Any"] = 2] = "Any";
            })(mlx.ActivityResourceAvailability || (mlx.ActivityResourceAvailability = {}));
            var ActivityResourceAvailability = mlx.ActivityResourceAvailability;

            (function (ActivityCompletionStatus) {
                ActivityCompletionStatus[ActivityCompletionStatus["Completed"] = 0] = "Completed";
                ActivityCompletionStatus[ActivityCompletionStatus["NotCompleted"] = 1] = "NotCompleted";
                ActivityCompletionStatus[ActivityCompletionStatus["Any"] = 2] = "Any";
            })(mlx.ActivityCompletionStatus || (mlx.ActivityCompletionStatus = {}));
            var ActivityCompletionStatus = mlx.ActivityCompletionStatus;

            (function (ScormType) {
                ScormType[ScormType["Asset"] = 0] = "Asset";
                ScormType[ScormType["SCO"] = 1] = "SCO";
                ScormType[ScormType["Any"] = 2] = "Any";
            })(mlx.ScormType || (mlx.ScormType = {}));
            var ScormType = mlx.ScormType;

            (function (CoursePlayerEvent) {
                CoursePlayerEvent[CoursePlayerEvent["TopicStarted"] = 1] = "TopicStarted";
                CoursePlayerEvent[CoursePlayerEvent["TopicCompleted"] = 2] = "TopicCompleted";
                CoursePlayerEvent[CoursePlayerEvent["LessonStarted"] = 4] = "LessonStarted";
                CoursePlayerEvent[CoursePlayerEvent["LessonCompleted"] = 8] = "LessonCompleted";
                CoursePlayerEvent[CoursePlayerEvent["ModuleStarted"] = 16] = "ModuleStarted";
                CoursePlayerEvent[CoursePlayerEvent["ModuleCompleted"] = 32] = "ModuleCompleted";
                CoursePlayerEvent[CoursePlayerEvent["AssessmentStarted"] = 64] = "AssessmentStarted";
                CoursePlayerEvent[CoursePlayerEvent["AssessmentCompleted"] = 128] = "AssessmentCompleted";
                CoursePlayerEvent[CoursePlayerEvent["CourseStarted"] = 256] = "CourseStarted";
                CoursePlayerEvent[CoursePlayerEvent["CourseCompleted"] = 512] = "CourseCompleted";
                CoursePlayerEvent[CoursePlayerEvent["VideoPlaybackStarted"] = 1024] = "VideoPlaybackStarted";
                CoursePlayerEvent[CoursePlayerEvent["VideoPlaybackStopped"] = 2048] = "VideoPlaybackStopped";
                CoursePlayerEvent[CoursePlayerEvent["VideoPlaybackPaused"] = 4096] = "VideoPlaybackPaused";
                CoursePlayerEvent[CoursePlayerEvent["VideoPlaybackSkipped"] = 8192] = "VideoPlaybackSkipped";
                CoursePlayerEvent[CoursePlayerEvent["VideoPlaybackMilestone"] = 16384] = "VideoPlaybackMilestone";
                CoursePlayerEvent[CoursePlayerEvent["VideoCaptionStateChanged"] = 32768] = "VideoCaptionStateChanged";
                CoursePlayerEvent[CoursePlayerEvent["VideoVolumeChanged"] = 65536] = "VideoVolumeChanged";
                CoursePlayerEvent[CoursePlayerEvent["VideoResolutionChanged"] = 131072] = "VideoResolutionChanged";
                CoursePlayerEvent[CoursePlayerEvent["VideoSpeedChanged"] = 262144] = "VideoSpeedChanged";
                CoursePlayerEvent[CoursePlayerEvent["VideoDownloaded"] = 524288] = "VideoDownloaded";
                CoursePlayerEvent[CoursePlayerEvent["VideoFullscreenChanged"] = 1048576] = "VideoFullscreenChanged";
                CoursePlayerEvent[CoursePlayerEvent["VideoPlaybackAccumulatedTime"] = 2097152] = "VideoPlaybackAccumulatedTime";
                CoursePlayerEvent[CoursePlayerEvent["DisplayContentRequest"] = 4194304] = "DisplayContentRequest";
                CoursePlayerEvent[CoursePlayerEvent["TranscriptLoaded"] = 8388608] = "TranscriptLoaded";
                CoursePlayerEvent[CoursePlayerEvent["All"] = CoursePlayerEvent.TopicStarted | CoursePlayerEvent.TopicCompleted | CoursePlayerEvent.LessonStarted | CoursePlayerEvent.LessonCompleted | CoursePlayerEvent.ModuleStarted | CoursePlayerEvent.ModuleCompleted | CoursePlayerEvent.AssessmentStarted | CoursePlayerEvent.AssessmentCompleted | CoursePlayerEvent.CourseStarted | CoursePlayerEvent.CourseCompleted | CoursePlayerEvent.VideoPlaybackStarted | CoursePlayerEvent.VideoPlaybackStopped | CoursePlayerEvent.VideoPlaybackPaused | CoursePlayerEvent.VideoPlaybackSkipped | CoursePlayerEvent.VideoPlaybackMilestone | CoursePlayerEvent.VideoCaptionStateChanged | CoursePlayerEvent.VideoVolumeChanged | CoursePlayerEvent.VideoResolutionChanged | CoursePlayerEvent.VideoSpeedChanged | CoursePlayerEvent.VideoDownloaded | CoursePlayerEvent.VideoFullscreenChanged | CoursePlayerEvent.VideoPlaybackAccumulatedTime | CoursePlayerEvent.DisplayContentRequest | CoursePlayerEvent.TranscriptLoaded] = "All";
            })(mlx.CoursePlayerEvent || (mlx.CoursePlayerEvent = {}));
            var CoursePlayerEvent = mlx.CoursePlayerEvent;

            var VideoPlayerAction = (function () {
                function VideoPlayerAction() {
                }
                VideoPlayerAction.Play = "play";
                VideoPlayerAction.Pause = "pause";
                VideoPlayerAction.Skip = "skip";
                VideoPlayerAction.Scrub = "scrub";
                VideoPlayerAction.Stop = "stop";
                VideoPlayerAction.Exit = "exit";
                VideoPlayerAction.VideoEnded = "ended";
                VideoPlayerAction.Bookmark = "bookmark";
                VideoPlayerAction.Resolution = "resolution";
                VideoPlayerAction.Download = "download";
                VideoPlayerAction.Speed = "playbackspeed";
                VideoPlayerAction.Volume = "volume";
                VideoPlayerAction.ClosedCaption = "closedcaption";
                VideoPlayerAction.Fullscreen = "fullscreen";
                VideoPlayerAction.Milestone = "milestone";
                VideoPlayerAction.AccumulatedPlaybackTime = "accumulatedvideoplayback";
                VideoPlayerAction.TextTrackLoad = "texttrackload";
                VideoPlayerAction.GoToPosition = "gotoposition";
                return VideoPlayerAction;
            })();

            (function (ActivityContentLevel) {
                ActivityContentLevel[ActivityContentLevel["Module"] = 1] = "Module";
                ActivityContentLevel[ActivityContentLevel["Lesson"] = 2] = "Lesson";
                ActivityContentLevel[ActivityContentLevel["Topic"] = 3] = "Topic";
            })(mlx.ActivityContentLevel || (mlx.ActivityContentLevel = {}));
            var ActivityContentLevel = mlx.ActivityContentLevel;

            var ScoWrapperTargetFunction = (function () {
                function ScoWrapperTargetFunction() {
                }
                ScoWrapperTargetFunction.UpdateUrlHashParamValue = "SCO_Wrapper_API.updateUrlHashParamValue";
                ScoWrapperTargetFunction.RequestVideoPlayerAction = "SCO_Wrapper_API.requestVideoPlayerAction";
                return ScoWrapperTargetFunction;
            })();
        })(learning.mlx || (learning.mlx = {}));
        var mlx = learning.mlx;
    })(microsoft.learning || (microsoft.learning = {}));
    var learning = microsoft.learning;
})(microsoft || (microsoft = {}));

// Expose course player object model via MLX object for SDK
var MLX = MLX || {};
MLX.PubSubExtender = microsoft.learning.mlx.PubSubExtender;
MLX.Scorm12ManifestReader = microsoft.learning.mlx.Scorm12ManifestReader;
MLX.Scorm12Sequencer = microsoft.learning.mlx.Scorm12Sequencer;
MLX.Activity = microsoft.learning.mlx.Activity;
MLX.Resource = microsoft.learning.mlx.Resource;
MLX.CoursePlayerModel = microsoft.learning.mlx.CoursePlayerModel;
MLX.ManifestItemVisibility = microsoft.learning.mlx.ManifestItemVisibility;
MLX.ManifestLearningResourceType = microsoft.learning.mlx.ManifestLearningResourceType;
MLX.ManifestResourceScormType = microsoft.learning.mlx.ManifestResourceScormType;
MLX.CoursePlayerUiMode = microsoft.learning.mlx.CoursePlayerUiMode;
MLX.CoursePlayerEvent = microsoft.learning.mlx.CoursePlayerEvent;
;//-------------------------------------------------------------------------------------
// <copyright file="courseActivityStateProxy.js" company="Microsoft Corporation">
//     (c) Copyright Microsoft Learning. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------
var microsoft;
(function (microsoft) {
    (function (learning) {
        /// <reference path="../../../jquery/jquery.d.ts" />
        /// <reference path="courseActivityState.d.ts" />
        /// <reference path="../../XDomain/MLXTunnel.ts" />
        (function (mlx) {
            /***********************************************************************
            * CourseActivityStateProxy class. This maintains the runtime
            * state related to SCO interactions with the CMI data model via the LMS
            * API Adapter.
            ***********************************************************************/
            var CourseActivityStateProxy = (function () {
                function CourseActivityStateProxy(tunnel) {
                    this.tunnel = tunnel;
                }
                CourseActivityStateProxy.prototype.initialize = function (courseId, organizationId, courseVersion, courseLanguage, userProfileLanguage, languageId, userId, countryId, isAnonymousRequest, organizationChannel, isUserActive) {
                    if (typeof languageId === "undefined") { languageId = 0; }
                    this.currentActivityId = "";

                    return this.tunnel.proxy({
                        target: "ActivityState.initialize",
                        data: arguments
                    });
                };

                // Sets the current activity id
                CourseActivityStateProxy.prototype.initializeCurrentActivity = function (activityId, learningResourceType, resourceVersion) {
                    var self = this;
                    return this.tunnel.proxy({
                        target: "ActivityState.initializeCurrentActivity",
                        data: arguments
                    }).done(function () {
                        self.currentActivityId = activityId;
                    });
                };

                // Sets the total number of available lessons, total number of assessments
                // and sets the list of item identifiers pertaining to assessment SCOs
                CourseActivityStateProxy.prototype.initializeTopicAndAssessmentValues = function (topicsTotal, assessmentsTotal, assessmentsIdentifiers, hiddenAssessmentsIdentifiers, scoDurations, softDeletedIdentifiers, assetIdentifiers) {
                    return this.tunnel.proxy({
                        target: "ActivityState.initializeTopicAndAssessmentValues",
                        data: arguments
                    });
                };

                // Gets the course completion percentage
                CourseActivityStateProxy.prototype.getPercentCompleted = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.getPercentCompleted",
                        data: arguments
                    });
                };

                // Gets the course completion percentage
                CourseActivityStateProxy.prototype.getTotalTopics = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.getTotalTopics",
                        data: arguments
                    });
                };

                // Gets the course completion percentage
                CourseActivityStateProxy.prototype.getDurationCompleted = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.getDurationCompleted",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.getCompletionStats = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.getCompletionStats",
                        data: arguments
                    });
                };

                // Gets the value of the supplied cmi data model element for the current scope.
                CourseActivityStateProxy.prototype.getValue = function (property, isLms) {
                    return this.getScopedValue(property, this.currentActivityId, isLms);
                };

                // Gets the value of the supplied cmi data model element for the specified scope.
                CourseActivityStateProxy.prototype.getScopedValue = function (property, scope, isLms) {
                    return this.tunnel.proxy({
                        target: "ActivityState.getScopedValue",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.getCompletedAssessmentQuestionsInfo = function (activityId) {
                    return this.tunnel.proxy({
                        target: "ActivityState.getCompletedAssessmentQuestionsInfo",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.resetActivityProgress = function (activityId) {
                    return this.tunnel.proxy({
                        target: "ActivityState.resetActivityProgress",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.getInitialActivityTreeData = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.getInitialActivityTreeData",
                        data: arguments
                    });
                };

                // Indicates whether the current call to save state data has completed.
                CourseActivityStateProxy.prototype.isSaveCompleted = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.isSaveCompleted",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.save = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.save",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.getScopedValues = function (property, scopes, isLMS) {
                    return this.tunnel.proxy({
                        target: "ActivityState.getScopedValues",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.activityStateDataExists = function () {
                    return this.tunnel.proxy({
                        target: "ActivityState.activityStateDataExists",
                        data: arguments
                    });
                };

                CourseActivityStateProxy.prototype.getAggregatedTotalTime = function (activityIds) {
                    return this.tunnel.proxy({
                        target: "ActivityState.getAggregatedTotalTime",
                        data: arguments
                    });
                };
                return CourseActivityStateProxy;
            })();
            mlx.CourseActivityStateProxy = CourseActivityStateProxy;
        })(learning.mlx || (learning.mlx = {}));
        var mlx = learning.mlx;
    })(microsoft.learning || (microsoft.learning = {}));
    var learning = microsoft.learning;
})(microsoft || (microsoft = {}));

// Expose course player object model via MLX object for SDK
var MLX = MLX || {};
MLX.CourseActivityStateProxy = microsoft.learning.mlx.CourseActivityStateProxy;
;//-------------------------------------------------------------------------------------
// <copyright file="coursePlayer.js" company="Microsoft Corporation">
//     (c) Copyright Microsoft Learning. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
/// <reference path="../../../jquery/jquery.d.ts" />
/// <reference path="scorm12Runtime.ts" />
/// <reference path="courseActivityState.d.ts" />
/// <reference path="courseActivityStateProxy.ts" />
/// <reference path="../../Shared/Models/settingsModel.ts" />
/// <reference path="../../Shared/Models/mvaSettingsModel.ts" />
/// <reference path="../../../WebTrends/WebTrendsHelper.ts" />
/// <reference path="../../Learner/Models/courseDetailsModel.ts" />
var ScoTunnel, CoursePlayerModel;


var microsoft;
(function (microsoft) {
    (function (learning) {
        (function (mlx) {
            

            var CoursePlayer = (function (_super) {
                __extends(CoursePlayer, _super);
                function CoursePlayer(scoTunnel, coursePlayerModel, courseDetailsModel, activityStateProxy, globalSettings, webTrendsHelper /*optional*/ ) {
                    var _this = this;
                    _super.call(this);
                    this.isTranscript = false;
                    this.isPreview = false;
                    this.isAnonymousRequest = false;
                    this.skipAutoPlayExams = false;
                    this.activities = ko.observableArray([]);
                    this.currentActivity = ko.computed(function () {
                        if (_this.coursePlayerModel && _this.coursePlayerModel.activeResource()) {
                            return _this.coursePlayerModel.activeResource().parent;
                        }

                        return null;
                    }, this, { deferEvaluation: true });
                    this.currentActivityTitle = ko.computed(function () {
                        var currentActivityTitle = "";

                        if (_this.coursePlayerModel && _this.coursePlayerModel.activeResource()) {
                            currentActivityTitle = _this.coursePlayerModel.activeResource().parent.title();
                        }

                        return currentActivityTitle;
                    }, this, { deferEvaluation: true });
                    this.previousActivityTitle = ko.computed(function () {
                        var previousActivityTitle = "";

                        if (_this.coursePlayerModel) {
                            var activeResource = _this.coursePlayerModel && _this.coursePlayerModel.activeResource();

                            if (activeResource) {
                                var activity = _this.sequencer.getPrevActivity();

                                if (activity && activity.title) {
                                    previousActivityTitle = activity.title();
                                }
                            }
                        }

                        return previousActivityTitle;
                    }, this, { deferEvaluation: true });
                    this.previousActivityParentTitle = ko.computed(function () {
                        var previousActivityParentTitle = "";

                        if (_this.coursePlayerModel) {
                            var activeResource = _this.coursePlayerModel && _this.coursePlayerModel.activeResource();

                            if (activeResource) {
                                var activity = _this.sequencer.getPrevActivity();

                                if (activity && activity.title) {
                                    previousActivityParentTitle = activity.parent.title();
                                }
                            }
                        }

                        return previousActivityParentTitle;
                    }, this, { deferEvaluation: true });
                    this.nextActivityTitle = ko.computed(function () {
                        var nextActivityTitle = "";

                        if (_this.coursePlayerModel) {
                            var activeResource = _this.coursePlayerModel.activeResource();

                            if (activeResource) {
                                var activity = _this.sequencer.getNextActivity();

                                if (activity && activity.title) {
                                    nextActivityTitle = activity.title();
                                }
                            }
                        }

                        return nextActivityTitle;
                    }, this, { deferEvaluation: true });
                    this.nextActivityParentTitle = ko.computed(function () {
                        var nextActivityParentTitle = "";

                        if (_this.coursePlayerModel) {
                            var activeResource = _this.coursePlayerModel.activeResource();

                            if (activeResource) {
                                var activity = _this.sequencer.getNextActivity();

                                if (activity && activity.title) {
                                    nextActivityParentTitle = activity.parent.title();
                                }
                            }
                        }

                        return nextActivityParentTitle;
                    }, this, { deferEvaluation: true });
                    this.autoPlay = ko.computed({
                        read: function () {
                            return _this.coursePlayerModel ? _this.coursePlayerModel.autoPlay() : false;
                        },
                        write: function (value) {
                            if (_this.coursePlayerModel) {
                                _this.coursePlayerModel.autoPlay(value);
                            }
                        },
                        owner: this,
                        deferEvaluation: true
                    });

                    var self = this, globalSettingsDeferred = $.Deferred();
                    this.initDeferred = $.Deferred();
                    this.scoTunnel = scoTunnel != undefined ? scoTunnel : ScoTunnel;
                    this.courseDetailsModel = courseDetailsModel != undefined ? courseDetailsModel : new microsoft.learning.mlx.CourseDetailsModel();
                    this.manifestReader = new microsoft.learning.mlx.Scorm12ManifestReader(scoTunnel);
                    this.sequencer = new microsoft.learning.mlx.Scorm12Sequencer();

                    if (globalSettings != undefined) {
                        this.globalSettings = globalSettings;
                        globalSettingsDeferred.resolve();
                    } else if (MLX.context.appChannel === "5" && typeof Configurations !== "undefined" && Configurations.mvaApiTargetHostname && !MLX.context.isMVAMigrationCompleted && !MLX.context.currentUser.isMVAMigratedUser) {
                        MLX.initializeMvaApiTunnel(Configurations.mvaApiTargetHostname, "https").done(function () {
                            self.globalSettings = new microsoft.learning.mlx.MvaSettingsModel(mlx.SettingsContext.Client, Configurations.mvaApiTargetHostname, "https");
                            globalSettingsDeferred.resolve();
                        });
                    } else {
                        this.globalSettings = new microsoft.learning.mlx.settingsModel(mlx.SettingsContext.Client);
                        globalSettingsDeferred.resolve();
                    }

                    $.when(globalSettingsDeferred).done(function () {
                        //self.webTrendsHelper = webTrendsHelper != undefined ? webTrendsHelper : new microsoft.learning.mlx.WebTrendsHelper();
                        self.coursePlayerModel = coursePlayerModel != undefined ? coursePlayerModel : new microsoft.learning.mlx.CoursePlayerModel(MLX.context.platformBuildVersion, self.manifestReader, self.sequencer, self.globalSettings);

                        // Set global CoursePlayerModel required by CoursePlayerModelProxy
                        window.CoursePlayerModel = self.coursePlayerModel;

                        // Expose CoursePlayerModel events to subscribers
                        self.coursePlayerModel.subscribe(mlx.CoursePlayerEvent.All, function (event, args) {
                            self.notifySubscribers(event, args);
                        });

                        self.initDeferred.resolve();
                    });

                    this.activityState = activityStateProxy != undefined ? activityStateProxy : new microsoft.learning.mlx.CourseActivityStateProxy(this.scoTunnel);
                }
                CoursePlayer.prototype.startCourse = function (startingActivity) {
                    return this.coursePlayerModel ? this.coursePlayerModel.startCourse(startingActivity) : $.Deferred().reject();
                };

                // Instantiate the activity state model used by both the API Adapter
                // and runtime engine.
                CoursePlayer.prototype.loadCourse = function (courseId, organizationId, languageId, courseVersion, userId, isTranscript, isPreview, userLanguage, countryId, skipAutoPlayExams) {
                    if (typeof languageId === "undefined") { languageId = 0; }
                    if (typeof courseVersion === "undefined") { courseVersion = ""; }
                    var self = this, deferred = $.Deferred();

                    this.courseId = courseId;
                    this.organizationId = organizationId;
                    this.languageId = !isNaN(languageId) ? languageId : 0;
                    this.userId = userId ? userId : MLX.context.currentUser.currentUserId;
                    this.isTranscript = isTranscript === true;
                    this.isPreview = isPreview === true;
                    this.activities = ko.observableArray([]);
                    this.courseDetails = null;
                    this.courseVersion = courseVersion;
                    this.userLanguage = userLanguage;
                    this.countryId = countryId;
                    this.skipAutoPlayExams = skipAutoPlayExams;

                    if (this.userLanguage) {
                        // set culture cookie at platform domain
                        MLX.sendCustomMessage(this.userLanguage, '', 'SetUserLanguage');
                    }

                    $.when(self.initDeferred).done(function () {
                        var loadingInitPromise = self.doLoadingInitialization();

                        $.when(loadingInitPromise).done(function () {
                            self.coursePlayerModel.initialize(self.courseId, self.activityState, self.courseVersion, self.courseDetailsModel.overview.details.level(), self.courseDetailsModel.courseLanguageCode, self.userId, self.skipAutoPlayExams).done(function () {
                                self.activities = self.coursePlayerModel.activities;

                                var additionalCourseDetails = {
                                    moduleCount: self.coursePlayerModel.moduleCount,
                                    assessmentCount: self.coursePlayerModel.assessmentCount,
                                    estimatedCourseDuration: self.coursePlayerModel.expectedCourseDuration,
                                    isDurationAvailable: self.coursePlayerModel.isDurationAvailable(),
                                    isCourseContentDeletedSinceUserLastLaunch: self.coursePlayerModel.isCourseContentDeletedSinceUserLastLaunch
                                };

                                $.extend(self.courseDetails, additionalCourseDetails);

                                deferred.resolve();
                            }).fail(function () {
                                deferred.reject();
                            });
                        }).fail(function () {
                            deferred.reject();
                        });
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                CoursePlayer.prototype.loadActivityById = function (itemId, courseId, organizationId, languageId, courseVersion, userId, userLanguage, countryId) {
                    if (typeof languageId === "undefined") { languageId = 0; }
                    if (typeof courseVersion === "undefined") { courseVersion = ""; }
                    var self = this, deferred = $.Deferred();

                    this.courseId = courseId;
                    this.organizationId = organizationId;
                    this.languageId = !isNaN(languageId) ? languageId : 0;
                    this.userId = userId ? userId : MLX.context.currentUser.currentUserId;
                    this.isTranscript = false;
                    this.isPreview = false;
                    this.activities = ko.observableArray([]);
                    this.courseDetails = null;
                    this.courseVersion = courseVersion;
                    this.userLanguage = userLanguage;
                    this.countryId = countryId;

                    if (this.userLanguage) {
                        // set culture cookie at platform domain
                        MLX.sendCustomMessage(this.userLanguage, '', 'SetUserLanguage');
                    }

                    $.when(self.initDeferred).done(function () {
                        var loadingInitPromise = self.doLoadingInitialization();

                        $.when(loadingInitPromise).done(function () {
                            self.coursePlayerModel.initializeActivity(itemId, self.courseId, self.activityState, self.courseVersion, self.courseDetailsModel.overview.details.level(), self.courseDetailsModel.courseLanguageCode, self.userId).done(function () {
                                self.activities = self.coursePlayerModel.activities;

                                var additionalCourseDetails = {
                                    moduleCount: self.coursePlayerModel.moduleCount,
                                    assessmentCount: self.coursePlayerModel.assessmentCount,
                                    estimatedCourseDuration: self.coursePlayerModel.expectedCourseDuration,
                                    isDurationAvailable: self.coursePlayerModel.isDurationAvailable()
                                };

                                $.extend(self.courseDetails, additionalCourseDetails);

                                deferred.resolve();
                            }).fail(function () {
                                deferred.reject();
                            });
                        }).fail(function () {
                            deferred.reject();
                        });
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                CoursePlayer.prototype.doLoadingInitialization = function () {
                    var self = this, deferred = $.Deferred();

                    MLX.sendCustomMessage(self.courseId, '', 'SetCurrentCourseId');

                    self.isAnonymousRequest = (!MLX.context.currentUser.isAuthenticated && MLX.context.isAnonymousTenant) ? true : false;

                    var globalSettingsInitPromise = self.globalSettings.loadSettings(self.userId, self.isAnonymousRequest);

                    MLX.getCourseBaseUrl(this.courseId, this.courseVersion, this.languageId, this.isTranscript, this.isPreview).done(function (baseUrl) {
                        self.baseUrl = baseUrl;

                        if (!self.courseVersion) {
                            self.courseVersion = microsoft.learning.mlx.utility.getVersionFromBaseUrl(baseUrl);
                        }

                        self.courseDetailsModel.initialize(self.courseId, self.courseVersion, self.baseUrl, self.isPreview).done(function () {
                            self.courseDetails = {
                                overview: self.courseDetailsModel.overview,
                                requirements: self.courseDetailsModel.requirements,
                                assessment: self.courseDetailsModel.assessment,
                                courseTitle: self.courseDetailsModel.courseTitle,
                                courseNumber: self.courseDetailsModel.courseNumber(),
                                courseLanguageCode: self.courseDetailsModel.courseLanguageCode,
                                courseImageUrl: self.courseDetailsModel.baseUrl + "/thumbnail.png",
                                isLiveEvent: self.courseDetailsModel.overview.liveEvent != undefined ? true : false,
                                isRegisteredForLiveEvent: self.courseDetailsModel.isRegisteredForLiveEvent,
                                percentageCourseCompleted: self.courseDetailsModel.percentageCourseCompleted,
                                doesUserHaveAccessToCourse: self.courseDetailsModel.doesUserHaveAccessToCourse,
                                publishedDate: self.courseDetailsModel.publishedDate,
                                expiryDate: self.courseDetailsModel.expirationDate,
                                retirementDate: self.courseDetailsModel.retirementDate,
                                lastAccessedDate: self.courseDetailsModel.lastAccessedDate,
                                isVersionModified: self.courseDetailsModel.isVersionModified,
                                getLearnerCourseDetails: self.courseDetailsModel.getLearnerCourseDetails,
                                courseVersion: self.courseVersion,
                                isRetired: self.courseDetailsModel.isRetired,
                                replacementCourseId: self.courseDetailsModel.replacementCourseId,
                                replacementCourseTitle: self.courseDetailsModel.replacementCourseTitle,
                                replacementCourseLanguageCode: self.courseDetailsModel.replacementCourseLanguageCode,
                                replacementCourseImageUrl: self.courseDetailsModel.replacementCourseImageUrl,
                                replacementCourseLevel: self.courseDetailsModel.replacementCourseLevel,
                                replacementCoursePublishedDate: self.courseDetailsModel.replacementCoursePublishedDate,
                                replacementCourseShortDescription: self.courseDetailsModel.replacementCourseShortDescription,
                                replacementCourseInstructors: self.courseDetailsModel.replacementCourseInstructors,
                                instructorsWithDisplayname: self.courseDetailsModel.instructorsWithDisplayname
                            };

                            if (!self.courseDetails.isLiveEvent) {
                                self.organizationChannel = MLX.context.appChannel;
                                var activityStateInitPromise = self.activityState.initialize(self.courseId, self.organizationId, self.courseVersion, self.courseDetailsModel.courseLanguageCode, MLX.context.currentUser.PreferredLanguage, self.languageId, self.userId, self.countryId, self.isAnonymousRequest, self.organizationChannel, MLX.context.currentUser.isActive), manifestReaderInitPromise = self.manifestReader.initialize(self.courseId, self.courseVersion, self.baseUrl);

                                $.when(self.initDeferred, globalSettingsInitPromise, activityStateInitPromise, manifestReaderInitPromise).done(function () {
                                    deferred.resolve();
                                }).fail(function () {
                                    deferred.reject();
                                });
                            } else {
                                deferred.resolve();
                            }
                        }).fail(function () {
                            deferred.reject();
                        });
                    }).fail(function () {
                        deferred.reject();
                    });

                    return deferred.promise();
                };

                CoursePlayer.prototype.endCourse = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.endCourse() : $.Deferred().reject();
                };

                CoursePlayer.prototype.launchPreviousActivity = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.getPrevResource() : $.Deferred().reject();
                };

                CoursePlayer.prototype.launchNextActivity = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.getNextResource() : $.Deferred().reject();
                };

                CoursePlayer.prototype.getActivityById = function (activityId) {
                    return this.sequencer.getSpecifiedActivity(activityId);
                };

                CoursePlayer.prototype.launchActivityById = function (activityId, hashValue) {
                    return this.coursePlayerModel ? this.coursePlayerModel.getSpecifiedResource(activityId, hashValue) : $.Deferred().reject();
                };

                CoursePlayer.prototype.launchActivity = function (activity, hashValue) {
                    return this.coursePlayerModel ? this.coursePlayerModel.getSelectedResource(activity, hashValue) : $.Deferred().reject();
                };

                CoursePlayer.prototype.endCurrentActivity = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.doDeactivation() : $.Deferred().reject();
                };

                CoursePlayer.prototype.setCurrentUiMode = function (mode) {
                    if (this.coursePlayerModel) {
                        this.coursePlayerModel.setCurrentUiMode(mode);
                    }
                };

                CoursePlayer.prototype.getPercentCompleted = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.getPercentCompleted() : $.Deferred().reject();
                };

                CoursePlayer.prototype.getCompletionStats = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.getCompletionStats() : $.Deferred().reject();
                };

                CoursePlayer.prototype.createActivityById = function (activityId) {
                    return this.coursePlayerModel ? this.coursePlayerModel.createActivityById(activityId) : $.Deferred().reject();
                };

                CoursePlayer.prototype.getPreviousActivityById = function (activityId) {
                    var activity = null;

                    if (activityId) {
                        activity = this.sequencer.getPrevActivityById(activityId);
                    }

                    return activity;
                };

                CoursePlayer.prototype.getPreviousActivityIdById = function (activityId) {
                    var activity = this.getPreviousActivityById(activityId);

                    return activity ? activity.id() : "";
                };

                CoursePlayer.prototype.getNextActivityById = function (activityId) {
                    var activity = null;

                    if (activityId) {
                        activity = this.sequencer.getNextActivityById(activityId);
                    }

                    return activity;
                };

                CoursePlayer.prototype.getNextActivityIdById = function (activityId) {
                    var activity = this.getNextActivityById(activityId);

                    return activity ? activity.id() : "";
                };

                CoursePlayer.prototype.getCurrentModuleIndex = function () {
                    if (this.coursePlayerModel && this.coursePlayerModel.activeResource()) {
                        var currentActivity = this.coursePlayerModel.activeResource().parent, currentModule = this.coursePlayerModel.getRootActivity(currentActivity), indexofCurrentActivity = $.inArray(currentModule, this.activities());

                        return (indexofCurrentActivity > 0 ? indexofCurrentActivity : 0);
                    }

                    return 0;
                };

                CoursePlayer.prototype.requestVideoPause = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.requestVideoPause() : $.Deferred().reject();
                };

                CoursePlayer.prototype.requestVideoPlay = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.requestVideoPlay() : $.Deferred().reject();
                };

                CoursePlayer.prototype.requestGoToVideoPosition = function (postition) {
                    return this.coursePlayerModel ? this.coursePlayerModel.requestGoToVideoPosition(postition) : $.Deferred().reject();
                };

                CoursePlayer.prototype.getCourseCumulativeVideoPlaybackTime = function () {
                    return this.coursePlayerModel ? this.coursePlayerModel.getCourseCumulativeVideoPlaybackTime() : $.Deferred().reject();
                };
                return CoursePlayer;
            })(mlx.PubSubExtender);
            mlx.CoursePlayer = CoursePlayer;
        })(learning.mlx || (learning.mlx = {}));
        var mlx = learning.mlx;
    })(microsoft.learning || (microsoft.learning = {}));
    var learning = microsoft.learning;
})(microsoft || (microsoft = {}));

// Expose course player object model via MLX object for SDK
var MLX = MLX || {};
MLX.CoursePlayer = microsoft.learning.mlx.CoursePlayer;
;//-------------------------------------------------------------------------------------
// <copyright file="learningPlanModel.js" company="Microsoft Corporation">           
//     (c) Copyright Microsoft Learning. All rights reserved.               
// </copyright>                                                                
//-------------------------------------------------------------------------------------
/// <reference path="../../../_references.js" />

microsoft.learning.mlx.learningPlanModel = (function () {
    var self = undefined;

    var obj = function learningPlanModel(orgId) {
        self = this;
        this.orgId = orgId;
        this.tristateMappingOptions = new microsoft.learning.mlx.TristateMappingOptions(self);

        this.traverseTree = function (item, callback) {
            callback(item);
            if (item.Children && item.Children() && item.Children().length > 0) {
                ko.utils.arrayForEach(item.Children(), function (childItem) { self.traverseTree(childItem, callback); });
            }
        };
       
        this.currentLearningPlan = {
            Name: ko.observable().extend({ required: "" }).extend({ regex: /^[^<>{};]*$/i }).extend({ maxLength: 300 }), //
            ParentProducts: ko.observableArray([]),
            SelectedProducts: ko.observableArray([]),
            ExistingProducts: ko.observableArray([]),
            learningPlanAddedProducts:ko.observableArray([]),
            TotalProductsCount: ko.observable(0),
            Users: ko.observableArray([]),
            Groups: ko.observableArray([]),
            LearningPlanAllAssignedUsers: ko.observableArray([]),
            SelectedUsers: ko.observableArray([]),
            InitialUserCount: ko.observable(0),
            Id: ko.observable(),
            OrgId: self.orgId,
            AccessCodes: ko.observableArray([]),
            CourseActivityReports: ko.observableArray([]),
            AssessmentActivityReports: ko.observableArray([]),
            LoadedProducts: {},
            learningPlanUsersHash: [],
            currentBulkAccessCode: {
                NumberOfCodes: ko.observable().extend({ regex: /^([1-9][0-9]{0,4}|10000)$/i }).extend({ maxValue: parseInt(inviteUsersCount) }),
                SendCodeInEmail: ko.observable(false),
                groupId: ko.observable(0),
                isValid: function () {
                    return (this.NumberOfCodes.errorCode() <= 0);
                }
            },
            loadProductChildren: function (parentProduct, pageIndex, productsPerPage, languageId) {
                var deferred = $.Deferred();
                var currentPlan = this;
                
                var promise = MLX.ajax({
                    url: '/services/products/' + parentProduct.Id() + '/Children?pageIndex=' + pageIndex + '&productsPerPage=' + productsPerPage + '&languageId=' + languageId,
                });

                promise.done(function (data) {
                    if (!parentProduct.Children || !parentProduct.Children()) {
                        parentProduct.Children = ko.observableArray([]);
                    }
                    
                    if (data && data.length > 0) {
                        var productsMapping = {
                            create: function (options) {
                                options.data.SelectionState = 0;
                                options.data.ChildrenVisible = false;
                                options.data.LoadingChildren = false;
                                return ko.mapping.fromJS(options.data, {});
                            }
                        };

                        ko.utils.arrayForEach(data, function (targetItem) {
                            var newProduct = ko.mapping.fromJS(targetItem, productsMapping);
                            currentPlan.LoadedProducts['P' + targetItem.Id] = newProduct;
                            newProduct.OfferId(parentProduct.OfferId());
                            if ($.grep(currentPlan.ExistingProducts(), function (arrayItem) {
                                return arrayItem.Id() == newProduct.Id() && arrayItem.SelectionState() == 1;
                            }).length > 0) {
                                newProduct.SelectionState(1);
                            };

                            parentProduct.Children.push(newProduct);
                        });
                    }
                    
                    parentProduct.ChildrenPopulated(true);
                    deferred.resolve(data);
                    //microsoft.learning.mlx.utility.batchInsertToObservableArray(data, parentProduct.Children, { batchSize: 10, interval: 50, mapping: self.tristateMappingOptions }, deferred);

                    //// Marks the selection state by traversing down the tree
                    //self.traverseTree(parentProduct, function (calledItem) { calledItem.SelectionState(parentProduct.SelectionState()); });
                    
                }).fail(function () {
                    deferred.reject();
                });

                return deferred.promise();
            },
            addProductInline: function (productId) {
                var currentPlan = this;
                var deferred = $.Deferred();
                var promise = self.getProductHierarchy(productId);
                promise.done(function (hierarchyData) {
                    if (hierarchyData && hierarchyData.length > 0) {
                        var rootItem = {
                            SelectionState: 2,
                            Children: [],
                            Id: hierarchyData[0]
                        };

                        var currentItem = rootItem;
                        
                        for (var i = 1; i < hierarchyData.length; i++) {
                            currentItem.Children.push({
                                SelectionState: i == (hierarchyData.length - 1) ? 1 : 2,
                                Children: [],
                                Id: hierarchyData[i]
                            });

                            currentItem = currentItem.Children[0];
                        }

                        currentPlan.markAndLoad([rootItem], currentPlan.ParentProducts(), deferred, deferred, true);
                    }
                }).fail(function () {
                    deferred.reject();
                });
                
                return deferred.promise();
            },
            removeProductInline: function (productId) {
                var currentPlan = this;
                var deferred = $.Deferred();
                var promise = self.getProductHierarchy(productId);
                promise.done(function (hierarchyData) {
                    if (hierarchyData && hierarchyData.length > 0) {
                        var itemArray = currentPlan.ParentProducts();
                        for (var i = 0; i < hierarchyData.length; i++) {
                            for (var j = 0; j < itemArray.length; j++) {
                                if (hierarchyData[i] == itemArray[j].Id()) {
                                    if (itemArray[j].Id() == productId) {
                                        itemArray[j].SelectionState(0);
                                        deferred.resolve(itemArray[j]);
                                    }
                                    itemArray = itemArray[j].Children();
                                    break;
                                }
                            }
                        }
                    }
                }).fail(function () {
                    deferred.reject();
                });

                return deferred.promise();
            },
            loadLearningPlanProducts: function () {
                var currentPlan = this;
                var deferred = $.Deferred();
               
                // Edit LP scenario
                if (currentPlan.Id()) {
                    var selectedProductsPromise = MLX.ajax({
                        url: '/services/learningplans/' + currentPlan.Id() + '?organizationId=' + self.orgId
                    });

                    selectedProductsPromise.done(function (data) {
                        currentPlan.ExistingProducts.removeAll();
                        ko.mapping.fromJS(data.ChildProducts, {}, currentPlan.ExistingProducts);
                        $.each(currentPlan.learningPlanAddedProducts(), function (i, item) {
                            if ($.grep(currentPlan.ExistingProducts(), function (newItem) { return newItem.Id() == item.Id() }).length == 0) {
                                currentPlan.ExistingProducts.push(item);
                            }
                        });
                        deferred.resolve(data);
                    }).fail(function () {
                        deferred.reject();
                    });
                }
                else {
                    // New LP - just return immediately
                    deferred.resolve();
                }

                return deferred.promise();
            }, 
            loadProducts: function (languageId) {
                // loads the top level products for new learning plan or loads the hierarchy of selected learning plans
                // in case of edit

                var currentPlan = this;
                var deferred = $.Deferred();

                var promise = MLX.ajax({
                    url: '/services/organizations/' + this.OrgId + '/subscribedproducts?languageId=' + languageId
                });

                promise.done(function (data) {
                    currentPlan.ParentProducts.removeAll();
                    ko.mapping.fromJS(data, self.tristateMappingOptions, currentPlan.ParentProducts);

                    // Edit LP scenario
                    if (currentPlan.Id()) {
                        var selectedProductsPromise = MLX.ajax({
                            url: '/services/learningplans/' + currentPlan.Id() + '?organizationId=' + self.orgId
                        });

                        selectedProductsPromise.done(function (data) {
                            currentPlan.ExistingProducts.removeAll();
                            ko.mapping.fromJS(data.ChildProducts, {}, currentPlan.ExistingProducts);
                            $.each(currentPlan.learningPlanAddedProducts(), function (i, item) {
                                if ($.grep(currentPlan.ExistingProducts(), function (newItem) { return newItem.Id() == item.Id() }).length == 0) {
                                    currentPlan.ExistingProducts.push(item);
                                }
                            });
                            deferred.resolve(data);
                        }).fail(function () {
                            deferred.reject();
                        });
                    }
                    else {
                        // New LP - just return immediately
                        deferred.resolve(data);
                    }
                }).fail(function () {
                    deferred.reject();
                });

                return deferred.promise();
            },
            markAndLoad: function (source, target, deferred, rootDeferred, returnFirstMarkedItem) {
                // marks the items found in the source in the target and recursively 
                // loads items in the target through loadProductChildren while marking respective items
                // from source's children into the target's children
                var currentPlan = this;
                var pending = false;
                
                // this array will hold the recursive deferreds. If you imagine a tree, a node will resolve
                // when all its children are resolved. A node's children are stored in this array.
                var innerDeferreds = [];
                $.each(source, function (index, sourceItem) {
                    ko.utils.arrayForEach(target, function (targetItem) {
                        if (targetItem.Id() == sourceItem.Id) {
                            if (sourceItem.SelectionState == 1) {
                                targetItem.SelectionState(sourceItem.SelectionState);
                                
                                if (returnFirstMarkedItem) {
                                    rootDeferred.resolve(targetItem);
                                }

                            } else {
                                pending = true;
                                
                                if (targetItem.ChildrenPopulated()) {
                                    var populatedDeferred = $.Deferred();
                                    innerDeferreds.push(populatedDeferred);
                                    currentPlan.markAndLoad(sourceItem.Children, targetItem.Children(), populatedDeferred, rootDeferred, returnFirstMarkedItem);
                                } else {
                                    var unpopulatedDeferred = $.Deferred();
                                    innerDeferreds.push(unpopulatedDeferred);
                                    var childrenPromise = currentPlan.loadProductChildren(targetItem);
                                    childrenPromise.done(function () {
                                        currentPlan.markAndLoad(sourceItem.Children, targetItem.Children(), unpopulatedDeferred, rootDeferred, returnFirstMarkedItem);
                                    });
                                }
                            }
                        }
                    });
                });

                $.when.apply($, innerDeferreds).done(function () {
                    deferred.resolve();
                });

                // If there are no more items, resolve the promise
                if (innerDeferreds.length <= 0) {
                    deferred.resolve();
                }
            },
            loadSelectedProducts: function () {
                var currentPlan = this;
                var promise = MLX.ajax({
                    url: '/services/organizations/' + this.OrgId + '/learningplan/' + this.Id()
                });
                promise.done(function (data) {
                    ko.mapping.fromJS(data, {}, currentPlan.SelectedProducts);
                });

                return promise;
            },
            isValid: function () {
                return ((this.Name.errorCode() <= 0)
                    && (ko.utils.arrayFilter(this.ExistingProducts(), function (product) {
                        if (product.SelectionState() > 0)
                            return product;
                    }).length > 0));
            },
            removeUnselected: function (arrayToClean) {
                var currentPlan = this;
                return $.map(arrayToClean, function (item) {
                    if (item.SelectionState == 0) return null;
                    for (var property in item) {
                        if (property != 'Id' && property != 'Children' && property != 'HasChildren' &&
                            property != 'OfferId' && property != 'Offers' && property != 'SelectionState') {
                            delete item[property];
                        }
                    }
                    if (item.SelectionState == 1) {
                        delete item['Children'];
                        return item;
                    }
                    if (item.Children) {
                        item.Children = currentPlan.removeUnselected(item.Children);
                    }
                    return item;
                });
            },

            save: function () {
                if (!this.isValid()) {
                    return false;
                }

                var unmapped = {};
                var create = !this.Id();

                if (create == true) {
                    unmapped.ParentProducts = this.removeUnselected(ko.mapping.toJS(this.ParentProducts));
                    unmapped.Name = $('#learning-plan-create-input').val();
                }
                else {
                    unmapped = ko.mapping.toJS(this);
                    unmapped.ParentProducts = this.removeUnselected(ko.mapping.toJS(this.ParentProducts));
                }
                $.each(unmapped.ParentProducts, function (index, product) {
                    if (product.SelectionState > 0) {
                        var offerExists = false;
                        if (!unmapped.Offers) {
                            unmapped.Offers = [];
                        }
                        $.each(unmapped.Offers, function (ind, offer) {
                            if (offer == product.OfferId) {
                                offerExists = true;
                                return false;
                            }
                        });
                        if (!offerExists) {
                            unmapped.Offers.push(product.OfferId);
                        }
                    }
                });

                unmapped.ParentProducts = null;

                unmapped.ChildProducts = $.map($.grep(this.ExistingProducts(), function (product) {
                    return product.SelectionState() == 1;
                }), function (arrayItem, index) {
                    return {
                        "Id": arrayItem.Id(),
                        "Name": arrayItem.Name(),
                        "SelectionState": arrayItem.SelectionState() ? 1 : 0,
                        "ImageUrl": arrayItem.ImageUrl()
                    };
                });
                unmapped.learningPlanUsersHash = [];
                var url = (create == true) ? ('/services/organizations/' + this.OrgId + '/learningplans') :
                    ('/services/learningplans/' + this.Id() + '?organizationId=' + this.OrgId + '&whr='
                    + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href));
                var method = (create == true) ? "POST" : "PUT";

                var promise = MLX.ajax({
                    url: url,
                    type: method,
                    data: JSON.stringify(unmapped),
                    contentType: 'application/json',
                    dataType: 'json'
                });

                promise.done(function (data) {
                    self.currentLearningPlan.ParentProducts.removeAll();
                    ko.mapping.fromJS(data, { 'ignore': ['ParentProducts'] }, self.currentLearningPlan);

                    var learningPlanFromList = $.grep(self.learningPlansList(), function (item) {
                        return item.Id() == data.Id;
                    });
                    
                    if (learningPlanFromList.length == 0) {
                        self.learningPlansList.unshift(ko.mapping.fromJS(data, self.learningPlanListItemMapping));
                    }
                    else {
                        ko.mapping.fromJS(data, self.learningPlanListItemMapping, learningPlanFromList[0]);
                    }
                });

                return promise;
            },
            loadSelectedUsers: function () {
                var currentPlan = this;
                var learningPlanUsersPromise = MLX.ajax({
                    url: '/services/learningplans/' + this.Id() + '/Users?organizationId=' + this.OrgId,
                });

                learningPlanUsersPromise.done(function (data) {                    
                    ko.mapping.fromJS(data, {}, currentPlan.LearningPlanAllAssignedUsers);
                });

                return learningPlanUsersPromise;
            },

            loadGroups: function (pageIndex, groupsPerPage, searchCriteria, paging) {
                var allGroups = undefined;
                var currentPlan = this;
                var groupDeferred = $.Deferred();

                var allGroupsPromise = MLX.ajax({
                    url: '/services/groups/' + this.OrgId + '/pageIndex/' + pageIndex + '?groupsPerPage=' + groupsPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria),
                });


                $.when(allGroupsPromise).then(function (data) {
                    var groupMapping = {
                        create: function (options) {
                            var mappedItem = ko.mapping.fromJS(options.data, {});
                            mappedItem.Selected = ko.observable(false);
                            mappedItem.isSelected = ko.observable(false);
                            mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                            return mappedItem;
                        }
                    };
                    allGroups = data.Groups;
                    var newGroupList = ko.mapping.fromJS(allGroups, groupMapping)();
                    var underlyingArray = currentPlan.Groups();
                    if (!paging) {
                        underlyingArray.splice(0, underlyingArray.length);
                    }
                    ko.utils.arrayPushAll(underlyingArray, newGroupList);
                    currentPlan.TotalGroups = data.TotalResultCount;
                    currentPlan.Groups.valueHasMutated();
                    groupDeferred.resolve(allGroups);
                });

                return groupDeferred.promise();

            },

            loadUsers: function (pageIndex, usersPerPage, searchCriteria, newContext, paging) {
                var allUsers = undefined;
                var learningPlanUsersPromise = undefined;
                var currentPlan = this;
                if (newContext) {
                    currentPlan.learningPlanUsersHash = [];
                    learningPlanUsersPromise = MLX.ajax({
                        url: '/services/learningplans/' + this.Id() + '/Users?organizationId=' + this.OrgId,
                    });
                    learningPlanUsersPromise.done(function (data) {
                        var lpUserMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(true);
                                return mappedItem;
                            }
                        };
                        $.each(data, function (index, user) {
                            currentPlan.learningPlanUsersHash[user.Id] = user.Id;
                        });
                        currentPlan.InitialUserCount(data.length);
                        ko.mapping.fromJS(data, lpUserMapping, currentPlan.SelectedUsers);
                    });
                }
                else {
                    learningPlanUsersPromise = $.Deferred().resolve();
                }

                var userBundle = {};

                var deferred = $.Deferred();

                learningPlanUsersPromise.done(function () {

                    var numUsersPerPage = currentPlan.InitialUserCount() + usersPerPage;
                    var allUsersPromise = MLX.ajax({
                        url: '/services/organizations/' + currentPlan.OrgId + '/Users' + '?pageIndex=' + pageIndex + '&usersPerPage=' + numUsersPerPage + '&searchcriteria=' + encodeURIComponent(searchCriteria) + '&activeOnly=true',
                    });
                    $.when(allUsersPromise).then(function (data) {
                        allUsers = data;
                        // Filter out inactive users as they are not eligiable to be added to learning plans
                        if (allUsers.length > 0)
                            userBundle.moreUsers = true;
                        else
                            userBundle.moreUsers = false;
                        allUsers = $.grep(allUsers, function (user, i) {
                            return (user.IsActive && currentPlan.learningPlanUsersHash[user.Id] === undefined);
                        });

                        var userMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(false);
                                return mappedItem;
                            }
                        };
                        var newUsersList = ko.mapping.fromJS(allUsers, userMapping)();
                        var underlyingArray = currentPlan.Users();
                        if (!paging) {
                            underlyingArray.splice(0, underlyingArray.length);
                        }
                        ko.utils.arrayPushAll(underlyingArray, newUsersList);
                        currentPlan.Users.valueHasMutated();
                        userBundle.users = allUsers;
                        deferred.resolve(userBundle);


                    }, function () {
                        deferred.reject();
                    });

                });
                return deferred.promise();
            },

            loadOrgGroups: function (pageIndex, groupsPerPage, searchCriteria, paging) {
                var allGroups = undefined;
                var currentPlan = this;
                var groupDeferred = $.Deferred();

                var allGroupsPromise = MLX.ajax({
                    url: '/services/groups/' + this.OrgId + '/orggroups?' + 'pageIndex=' + pageIndex + '&groupsPerPage=' + groupsPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria),
                });


                $.when(allGroupsPromise).then(function (data) {
                    var groupMapping = {
                        create: function (options) {
                            var mappedItem = ko.mapping.fromJS(options.data, {});
                            mappedItem.Selected = ko.observable(false);
                            mappedItem.isSelected = ko.observable(false);
                            mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                            return mappedItem;
                        }
                    };
                    allGroups = data.Groups;
                    var newGroupList = ko.mapping.fromJS(allGroups, groupMapping)();
                    var underlyingArray = currentPlan.Groups();
                    if (!paging) {
                        underlyingArray.splice(0, underlyingArray.length);
                    }
                    ko.utils.arrayPushAll(underlyingArray, newGroupList);
                    currentPlan.TotalGroups = data.TotalResultCount;
                    currentPlan.Groups.valueHasMutated();
                    groupDeferred.resolve(allGroups);
                });

                return groupDeferred.promise();

            },

            loadAssignedUsers: function (pageIndex, usersPerPage, searchCriteria, newContext, paging) {

                var learningPlanUsersPromise = undefined;
                var currentPlan = this;
                if (newContext) {
                    currentPlan.learningPlanUsersHash = [];
                    learningPlanUsersPromise = MLX.ajax({
                        url: '/services/learningplans/' + this.Id() + '/Users/organizationId/' + this.OrgId + '?pageIndex=' + pageIndex + '&usersPerPage=' + usersPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria),
                    });
                    learningPlanUsersPromise.done(function (data) {
                        var lpUserMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(true);
                                mappedItem.isSelected = ko.observable(false);
                                mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                                return mappedItem;
                            }
                        };
                        $.each(data.Users, function (index, user) {
                            currentPlan.learningPlanUsersHash[user.Id] = user.Id;
                        });
                        currentPlan.InitialUserCount(data.Users.length);
                        var newUsersList = ko.mapping.fromJS(data.Users, lpUserMapping)();
                        var underlyingArray = currentPlan.SelectedUsers();
                        if (!paging) {
                            underlyingArray.splice(0, underlyingArray.length);
                        }
                        ko.utils.arrayPushAll(underlyingArray, newUsersList);
                        currentPlan.SelectedUsers.valueHasMutated();
                        currentPlan.TotalAssignedRecords = data.TotalResultCount;
                    });
                }
                else {
                    learningPlanUsersPromise = $.Deferred().resolve();
                }

                return learningPlanUsersPromise;
            },

            loadUnassignedUsers: function (pageIndex, usersPerPage, searchCriteria, paging) {
                var userBundle = {};
                var allUsers = undefined;
                var currentPlan = this;
                var deferred = $.Deferred();
                var userSearchFilter = { OrganizationId: currentPlan.OrgId, Status: 1, PageIndex: pageIndex, SearchCriteria: searchCriteria, UsersPerPage: usersPerPage, NotInLearningPlanId: currentPlan.Id()};

                var allUsersPromise = MLX.ajax({
                    url: '/services/organizations/Users',
                    type: 'POST',
                    data: JSON.stringify(userSearchFilter),
                    contentType: 'application/json',
                    dataType: 'json'
                });
                $.when(allUsersPromise).then(function (data) {
                    allUsers = data.Users;
                    // Filter out inactive users as they are not eligiable to be added to learning plans
                    if (allUsers.length > 0)
                        userBundle.moreUsers = true;
                    else
                        userBundle.moreUsers = false;

                    var userMapping = {
                        create: function (options) {
                            var mappedItem = ko.mapping.fromJS(options.data, {});
                            mappedItem.Selected = ko.observable(false);
                            mappedItem.isSelected = ko.observable(false);
                            mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                            return mappedItem;
                        }
                    };
                    var newUsersList = ko.mapping.fromJS(allUsers, userMapping)();
                    var underlyingArray = currentPlan.Users();
                    if (!paging) {
                        underlyingArray.splice(0, underlyingArray.length);
                    }
                    ko.utils.arrayPushAll(underlyingArray, newUsersList);
                    currentPlan.Users.valueHasMutated();
                    currentPlan.TotalUnassignedUsers = data.TotalResultCount;
                    userBundle.users = allUsers;
                    userBundle.TotalUnassignedUsers = data.TotalResultCount;
                    deferred.resolve(userBundle);
                }, function () {
                    deferred.reject();
                });

                return deferred.promise();
            },

            isUserInLearningPlan: function (userId) {
                return this.learningPlanUsersHash[userId];
            },
            assignUsers: function (usersToBeAdded, usersTobeRemoved) {
                var currentPlan = this;
                var assignedUsers = [];

                $.each(currentPlan.LearningPlanAllAssignedUsers(), function (i, assignedUser) {
                    assignedUsers.push(assignedUser.Id());
                });

                $.each(usersToBeAdded, function (index, addedUser) {
                    assignedUsers.push(addedUser.Id());
                });

                $.each(usersTobeRemoved, function (index, removedUser) {
                    idx = assignedUsers.indexOf(removedUser.Id());
                    if (idx > -1) {
                        assignedUsers.splice(idx, 1);
                    }
                });

                return MLX.ajax({                                        
                    url: '/services/learningPlans/' + currentPlan.Id() + '/Users?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href) + '&organizationId=' + this.OrgId,
                    type: 'PUT',
                    data: JSON.stringify(assignedUsers),
                    contentType: 'application/json',
                    dataType: 'json'
                });
            },
            loadAccessCodes: function (accessCodeMapping) {
                var currentPlan = this;
                var accessCodesPromise = MLX.ajax({
                    url: '/services/learningplans/' + this.Id() + '/accesscodes?organizationId=' + self.orgId
                });
                accessCodesPromise.done(function (data) {
                    ko.mapping.fromJS(data, accessCodeMapping, currentPlan.AccessCodes);
                });
                return accessCodesPromise;
            },
            newBulkAccessCode: function () {
                this.currentBulkAccessCode.NumberOfCodes('');
                this.currentBulkAccessCode.SendCodeInEmail(true);
            },
            saveCurrentBulkAccessCode: function (groupToAssign) {
                var currentPlan = this;
                var createAccessCodePromise = MLX.ajax({
                    url: '/services/learningPlans/' + this.Id() + '/AccessCodes?numberOfUsages=' + this.currentBulkAccessCode.NumberOfCodes() + '&groupID=' + groupToAssign + '&organizationId=' + this.OrgId,
                    type: 'POST',
                    contentType: 'application/json',
                    dataType: 'json'
                });

                return createAccessCodePromise.pipe(function (data) {
                    if (!currentPlan.currentBulkAccessCode.SendCodeInEmail()) {
                        return $.Deferred().resolve(data);
                    }
                    self.emailAccessCode(data.Id);
                    createAccessCodePromise.resolve(data);
                    return createAccessCodePromise;
                });
                
            },
            loadActivityReport: function () {
                var currentPlan = this;
                var reportPromise = MLX.ajax({
                    url: '/services/reports/learningplans/' + currentPlan.Id() + '?organizationId=' + self.orgId
                });

                reportPromise.done(function (data) {
                    microsoft.learning.mlx.utility.addBIRefreshDate(data);
                    ko.mapping.fromJS(data, {}, currentPlan.CourseActivityReports);
                });
                return reportPromise;
            },
            loadAssessmentReport: function () {
                var currentPlan = this;
                var reportPromise = MLX.ajax({
                    url: '/services/reports/learningplans/' + currentPlan.Id() + '/assessments?organizationId=' + self.orgId
                });

                reportPromise.done(function (data) {
                    microsoft.learning.mlx.utility.addBIRefreshDate(data);
                    ko.mapping.fromJS(data, {}, currentPlan.AssessmentActivityReports);
                });
                return reportPromise;
            }
        };
        
    };

    obj.prototype.learningPlansList = ko.observableArray([]);

    obj.prototype.newLearningPlan = function () {
        if (self.currentLearningPlan.Id()) {
            self.currentLearningPlan.Name('');
            self.currentLearningPlan.ParentProducts.removeAll();
            self.currentLearningPlan.Id('');
            self.currentLearningPlan.TotalProductsCount(0);
            self.currentLearningPlan.ExistingProducts.removeAll();
            self.currentLearningPlan.LoadedProducts = {};
        }
    };

    obj.prototype.prepareLearningPlanForEdit = function (learningPlanFromList) {
        this.currentLearningPlan.Name(learningPlanFromList.Name());
        this.currentLearningPlan.Id(learningPlanFromList.Id());
        this.currentLearningPlan.TotalProductsCount(learningPlanFromList.TotalProductsCount());
        self.currentLearningPlan.ExistingProducts.removeAll();
        self.currentLearningPlan.LoadedProducts = {};
    };
    
    obj.prototype.emailAccessCode = function (accessCodeId) {
        return MLX.ajax({
            url: '/services/organizations/' + this.orgId + '/AccessCode/' + accessCodeId + '?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href.split(":")[0] + '://' + window.location.host),
            type: 'POST',
            contentType: 'application/json',
            dataType: 'json'
        });
    };

    obj.prototype.deactivateBulkAccessCodes = function (ids) {
        var jsonParams = JSON.stringify(ids);
        return MLX.ajax({
            type: "POST",
            url: '/services/organizations/' + self.orgId + '/DeactivateBulkUseAccessCodesForLearningPlan?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href),
            data: jsonParams,
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        }).pipe(function () {
            $.each(self.currentLearningPlan.AccessCodes(), function (index, ac) {
                var currentId;
                if (typeof ac.Id === "function") {
                    currentId = ac.Id().toString();
                }
                else {
                    currentId = ac.Id.toString();
                }
                if (ids.indexOf(currentId) >= 0) {
                    ac.UsagesRemaining = 0;
                }
            });
            return $.Deferred().resolve();
        });
    };


    obj.prototype.getLearningPlans = function (learningPlanListItemMapping, pageIndex, learningPlansPerPage, searchCriteria, newContext) {
        
        var promise = MLX.ajax({
            url: '/services/organizations/' + self.orgId + '/learningplans' + '?pageIndex=' + pageIndex + '&learningPlansPerPage=' + learningPlansPerPage + '&searchcriteria=' + encodeURIComponent(searchCriteria),
        });
        self.learningPlanListItemMapping = learningPlanListItemMapping;
        promise.done(function (data) {
            var newLearningPlanList = ko.mapping.fromJS(data, self.learningPlanListItemMapping)();            
            var underlyingArray = self.learningPlansList();
            if (newContext) {
                underlyingArray.splice(0, underlyingArray.length);
            }
            ko.utils.arrayPushAll(underlyingArray, newLearningPlanList);
            self.learningPlansList.valueHasMutated();
        });

        return promise;
    };

    // bind validations
    return obj;
})();
;//-------------------------------------------------------------------------------------
// <copyright file="candidateMasterProfileModel.js" company="Microsoft Corporation">           
//     (c) Copyright Microsoft Learning. All rights reserved.               
// </copyright>                                                                
//-------------------------------------------------------------------------------------
/// <reference path="../../../_references.js" />

microsoft.learning.mlx.candidateMasterProfileModel = (function () {
    var self = undefined;

    var obj = function candidateMasterProfileModel(candidateExamRegistrationViewModel) {
        self = this;
        self.parent = candidateExamRegistrationViewModel;

        self.getCandidateUserProfile = function () {
            var userProfile = new candidateProfile();
            userProfile.BasicProfile.FirstName(self.parent.firstName);
            userProfile.BasicProfile.LastName(self.parent.lastName);
            userProfile.BasicProfile.JobFunctionId(self.parent.selectedJobFunction);
            userProfile.BasicProfile.ContactEmailAddress(self.parent.contactEmailAddress);
            userProfile.BasicProfile.PreferredLanguage(self.parent.selectedLanguage);
            userProfile.BasicProfile.CountryCode(self.parent.selectedCountry);
            userProfile.BasicProfile.PostalCode(self.parent.postalCode);

            userProfile.BasicProfile.AcceptedTermsOfUse(ko.computed(function () {
                return self.parent.termAccepted() == true ? 1 : 0;
            }));

            if (self.parent.legalFirstName() != "" && self.parent.legalLastName() != "") {
                userProfile.LegalProfile.Salutation(self.parent.selectedTitle);
                userProfile.LegalProfile.LegalFirstName(self.parent.legalFirstName);
                userProfile.LegalProfile.LegalMiddleName(self.parent.legalMiddleName);
                userProfile.LegalProfile.LegalLastName(self.parent.legalLastName);
            } else {
                userProfile.LegalProfile = null;
            }

            var address = new userAddressLine();
            address.AddressLine1(self.parent.address1);
            address.AddressLine2(self.parent.address2);
            address.AddressLine3(self.parent.address3);
            address.City(self.parent.city);
            address.CountryCode(self.parent.selectedExtendedCountry);
            address.StateProvinceCode(self.parent.selectedStateRegion);
            address.PostalCode(self.parent.extendedPostalCode);
            userProfile.Addresses.push(address);

            var phone = new userPhoneData();
            phone.AreaCode(self.parent.phonAreaCode);
            phone.CountryCode(self.parent.phoneCountryCode);
            phone.PhoneNumber(self.parent.phoneNumber);
            phone.Extension(self.parent.phoneExtension);
            userProfile.Phones.push(phone);

            userProfile.PrivacyPreferences.MSMarketingEmailOptin(self.parent.msEmailOption);
            userProfile.PrivacyPreferences.MSPartnersMarketingEmailOptin(self.parent.msPartnerEmailOption);
            userProfile.EdpProfile = new Object();
            userProfile.EdpProfile.LastTimeProfileSentToVue = self.parent.LastTimeProfileSentToVue;

            return userProfile;
        };

        self.getCandidateUserProfileToJSON = function () {
            return ko.toJSON(self.getCandidateUserProfile());
        }

        function assignBlankForNull(testValue) {
            return ((testValue == undefined) || (testValue == null)) ? '' : testValue;
        }

        self.setCandidateUserProfileOnKO = function (userProfile) {

            if (userProfile.BasicProfile) {
                self.parent.firstName(assignBlankForNull(userProfile.BasicProfile.FirstName));
                self.parent.lastName(assignBlankForNull(userProfile.BasicProfile.LastName));
                self.parent.selectedJobFunction(userProfile.BasicProfile.JobFunctionId);
                self.parent.contactEmailAddress(assignBlankForNull(userProfile.BasicProfile.ContactEmailAddress));
                self.parent.selectedLanguage(userProfile.BasicProfile.PreferredLanguage);
                self.parent.postalCode(assignBlankForNull(userProfile.BasicProfile.PostalCode));
                self.parent.selectedCountry(userProfile.BasicProfile.CountryCode);
                self.parent.termAccepted(assignBlankForNull(userProfile.BasicProfile.AcceptedTermsOfUse));
            }
            if (userProfile.LegalProfile) {
                self.parent.selectedTitle(assignBlankForNull(userProfile.LegalProfile.Salutation));
                self.parent.legalFirstName(assignBlankForNull(userProfile.LegalProfile.LegalFirstName));
                self.parent.legalMiddleName(assignBlankForNull(userProfile.LegalProfile.LegalMiddleName));
                self.parent.legalLastName(assignBlankForNull(userProfile.LegalProfile.LegalLastName));
            }

            if (userProfile.Addresses && userProfile.Addresses[0]) {
                self.parent.address1(assignBlankForNull(userProfile.Addresses[0].AddressLine1));
                self.parent.address2(assignBlankForNull(userProfile.Addresses[0].AddressLine2));
                self.parent.address3(assignBlankForNull(userProfile.Addresses[0].AddressLine3));
                self.parent.city(assignBlankForNull(userProfile.Addresses[0].City));
                self.parent.selectedExtendedCountry(assignBlankForNull(userProfile.Addresses[0].CountryCode));
                self.parent.selectedStateRegion(assignBlankForNull(userProfile.Addresses[0].StateProvinceCode));
                self.parent.extendedPostalCode(assignBlankForNull(userProfile.Addresses[0].PostalCode));
            }

            if (userProfile.Phones && userProfile.Phones[0]) {
                self.parent.phonAreaCode(assignBlankForNull(userProfile.Phones[0].AreaCode));
                self.parent.phoneCountryCode(assignBlankForNull(userProfile.Phones[0].CountryCode));
                self.parent.phoneNumber(assignBlankForNull(userProfile.Phones[0].PhoneNumber));
                self.parent.phoneExtension(assignBlankForNull(userProfile.Phones[0].Extension));
            }
            if (userProfile.PrivacyPreferences) {
                self.parent.msEmailOption(assignBlankForNull(userProfile.PrivacyPreferences.MSMarketingEmailOptin));
                self.parent.msPartnerEmailOption(assignBlankForNull(userProfile.PrivacyPreferences.MSPartnersMarketingEmailOptin))
            }
            if (userProfile.EdpProfile) {
                self.parent.LastTimeProfileSentToVue = userProfile.EdpProfile.LastTimeProfileSentToVue;
            }
        };

        self.setExamProfile = function (examId, examName, locale, action, returnUrl) {
            var examProfile = new candidateExamProfile();
            examProfile.ExamCode(examId);
            examProfile.ExamName(examName);
            examProfile.Locale(locale);
            examProfile.Action(action);
            examProfile.ReturnUrl(returnUrl);

            return examProfile;
        }
     

    };

    function candidateProfile() {
        var UserMasterProfile = {
            BasicProfile: {
                FirstName: ko.observable(),
                MiddleName: ko.observable(),
                LastName: ko.observable(),
                ContactEmailAddress: ko.observable(),
                CountryCode: ko.observable(),
                PostalCode: ko.observable(),
                JobTitle: ko.observable(),
                JobFunctionId: ko.observable(),
                PreferredLanguage: ko.observable(),
                MSMarketingEmailOption: ko.observable(),
                MSPartnersMarketingEmailOption: ko.observable(),
                AcceptedTermsOfUse: ko.observable(false)
            },
            LegalProfile: {
                Salutation: ko.observable(),
                LegalFirstName: ko.observable(),
                LegalMiddleName: ko.observable(),
                LegalLastName: ko.observable()
            },
            Addresses: ko.observableArray([]),
            Phones: ko.observableArray([]),
            EdpProfile: {
                LastTimeProfileSentToVue: null
            },
            PrivacyPreferences: {
                MSMarketingEmailOptin: ko.observable(false),
                MSPartnersMarketingEmailOptin: ko.observable(false)
            }
        };
        return UserMasterProfile;
    };

    function userAddressLine() {
        var UserAddressProfile = {
            AddressType: ko.observable(),
            AddressLine1: ko.observable(),
            AddressLine2: ko.observable(),
            AddressLine3: ko.observable(),
            City: ko.observable(),
            Country: ko.observable(),
            PostalCode: ko.observable(),
            StateProvinceCode: ko.observable(),
            CountryCode: ko.observable()
        };

        return UserAddressProfile;
    };

    function userPhoneData() {
        var UserPhoneProfile = {
            PhoneType: ko.observable(),
            CountryCode: ko.observable(),
            AreaCode: ko.observable(),
            PhoneNumber: ko.observable(),
            Extension: ko.observable()
        };

        return UserPhoneProfile;
    };

    function candidateExamProfile() {
        var ExamProfile = {
            ExamCode: ko.observable(),
            ExamName: ko.observable(),
            Locale: ko.observable(),
            Action: ko.observable(),
            ReturnUrl: ko.observable()
        }

        return ExamProfile;
    };
    return obj;
})();
;//-------------------------------------------------------------------------------------
// <copyright file="SettingsModelBase.js" company="Microsoft Corporation">
//     (c) Copyright Microsoft Learning. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------
var microsoft;
(function (microsoft) {
    (function (learning) {
        /// <reference path="../../../jquery/jquery.d.ts" />
        /// <reference path="settingsModel.d.ts" />
        /// <reference path="../../XDomain/MlxTunnel.ts" />
        (function (mlx) {
            var SettingsModelBase = (function () {
                function SettingsModelBase(settingsContext) {
                    this.hasChanged = false;
                    this.settingsContext = settingsContext;

                    switch (settingsContext) {
                        case SettingsContext.Client:
                            this.settings = { AutoPlayState: false };
                            break;
                        case SettingsContext.Content:
                            this.settings = { ClosedCaptionState: -1, VolumeState: 0 };
                            break;
                    }
                }
                SettingsModelBase.prototype.getValue = function (property) {
                    var value = "";

                    if (this.settings) {
                        switch (property) {
                            case "ClosedCaptioning":
                                value = this.settings["ClosedCaptionState"];
                                break;
                            case "AutoPlay":
                                value = this.settings["AutoPlayState"];
                                break;
                            case "Volume":
                                value = this.settings["VolumeState"];
                                break;
                        }
                    }

                    return value;
                };

                SettingsModelBase.prototype.setValue = function (property, value) {
                    if (this.settings) {
                        switch (property) {
                            case "ClosedCaptioning":
                                if (this.settings["ClosedCaptionState"] !== value) {
                                    this.settings["ClosedCaptionState"] = value;
                                    this.hasChanged = true;
                                }
                                break;
                            case "AutoPlay":
                                if (this.settings["AutoPlayState"] !== value) {
                                    this.settings["AutoPlayState"] = value;
                                    this.hasChanged = true;
                                }
                                break;
                            case "Volume":
                                if (this.settings["VolumeState"] !== value) {
                                    this.settings["VolumeState"] = value;
                                    this.hasChanged = true;
                                }
                                break;
                        }
                    }
                };

                SettingsModelBase.prototype.loadSettings = function (userId, isAnonymousRequest) {
                    if (typeof isAnonymousRequest === "undefined") { isAnonymousRequest = false; }
                    return null;
                };

                SettingsModelBase.prototype.saveSettings = function (userId, isAnonymousRequest) {
                    if (typeof isAnonymousRequest === "undefined") { isAnonymousRequest = false; }
                    return null;
                };
                return SettingsModelBase;
            })();
            mlx.SettingsModelBase = SettingsModelBase;

            var SettingsContext = (function () {
                function SettingsContext() {
                }
                SettingsContext.Client = "clientSettings";
                SettingsContext.Content = "contentSettings";
                return SettingsContext;
            })();
            mlx.SettingsContext = SettingsContext;
        })(learning.mlx || (learning.mlx = {}));
        var mlx = learning.mlx;
    })(microsoft.learning || (microsoft.learning = {}));
    var learning = microsoft.learning;
})(microsoft || (microsoft = {}));
;//-------------------------------------------------------------------------------------
// <copyright file="settingsModel.js" company="Microsoft Corporation">
//     (c) Copyright Microsoft Learning. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var microsoft;
(function (microsoft) {
    (function (learning) {
        /// <reference path="../../../jquery/jquery.d.ts" />
        /// <reference path="settingsModel.d.ts" />
        /// <reference path="settingsModelBase.ts" />
        (function (mlx) {
            

            var settingsModel = (function (_super) {
                __extends(settingsModel, _super);
                function settingsModel(settingsContext) {
                    _super.call(this, settingsContext);
                }
                settingsModel.prototype.loadSettings = function (userId, isAnonymousRequest) {
                    if (typeof isAnonymousRequest === "undefined") { isAnonymousRequest = false; }
                    var self = this, deferred = $.Deferred(), url;
                    if (userId || isAnonymousRequest) {
                        if (isAnonymousRequest) {
                            url = "/services/settings/anonymous/settingtype/";
                        } else {
                            url = "/services/settings/learner/settingtype/";
                        }

                        if (typeof LmsTunnel != 'undefined') {
                            LmsTunnel.proxy({
                                target: "MLX.ajax",
                                data: [{
                                        url: url + self.settingsContext,
                                        dataType: "json",
                                        cache: false
                                    }]
                            }).done(function (result) {
                                if (!$.isEmptyObject(result)) {
                                    self.settings = result;
                                }
                                deferred.resolve();
                            }).fail(function () {
                                deferred.reject();
                            });
                        } else {
                            MLX.ajax({
                                url: url + self.settingsContext,
                                dataType: "json",
                                cache: false
                            }).done(function (result) {
                                if (!$.isEmptyObject(result)) {
                                    self.settings = result;
                                }
                                deferred.resolve();
                            }).fail(function () {
                                deferred.reject();
                            });
                        }
                    } else {
                        deferred.resolve();
                    }
                    return deferred.promise();
                };

                settingsModel.prototype.saveSettings = function (userId, isAnonymousRequest) {
                    if (typeof isAnonymousRequest === "undefined") { isAnonymousRequest = false; }
                    var self = this, promise, url;

                    if (this.settings) {
                        if (this.hasChanged) {
                            if (isAnonymousRequest) {
                                url = "/services/settings/anonymous";
                            } else {
                                url = "/services/settings/learner";
                            }

                            if (typeof LmsTunnel != 'undefined') {
                                promise = LmsTunnel.proxy({
                                    target: "MLX.ajax",
                                    data: [{
                                            type: 'PUT',
                                            contentType: 'application/json',
                                            url: url,
                                            data: JSON.stringify(self.settings),
                                            dataType: "json"
                                        }]
                                }).done(function () {
                                    self.hasChanged = false;
                                });
                            } else {
                                promise = MLX.ajax({
                                    type: 'PUT',
                                    contentType: 'application/json',
                                    url: url,
                                    data: JSON.stringify(self.settings),
                                    dataType: "json"
                                }).done(function () {
                                    self.hasChanged = false;
                                });
                            }
                        } else {
                            promise = $.Deferred().resolve();
                        }
                    } else {
                        promise = $.Deferred().reject();
                    }

                    return promise;
                };
                return settingsModel;
            })(mlx.SettingsModelBase);
            mlx.settingsModel = settingsModel;
        })(learning.mlx || (learning.mlx = {}));
        var mlx = learning.mlx;
    })(microsoft.learning || (microsoft.learning = {}));
    var learning = microsoft.learning;
})(microsoft || (microsoft = {}));
;//-------------------------------------------------------------------------------------
// <copyright file="MvaSettingsModel.js" company="Microsoft Corporation">
//     (c) Copyright Microsoft Learning. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var microsoft;
(function (microsoft) {
    (function (learning) {
        /// <reference path="../../../jquery/jquery.d.ts" />
        /// <reference path="settingsModel.d.ts" />
        /// <reference path="settingsModelBase.ts" />
        /// <reference path="../../XDomain/MlxTunnel.ts" />
        (function (mlx) {
            var MvaSettingsModel = (function (_super) {
                __extends(MvaSettingsModel, _super);
                function MvaSettingsModel(settingsContext, mvaApiTargetHostname, scheme) {
                    _super.call(this, settingsContext);
                    this.mvaApiTargetHostname = mvaApiTargetHostname;
                    this.scheme = scheme;
                }
                MvaSettingsModel.prototype.loadSettings = function (userId, isAnonymousRequest) {
                    if (typeof isAnonymousRequest === "undefined") { isAnonymousRequest = false; }
                    var self = this, deferred = $.Deferred();

                    if (userId) {
                        MvaApiTunnel.ajax({
                            type: "GET",
                            url: self.scheme + "://" + self.mvaApiTargetHostname + "/api/usersettings/" + userId,
                            cache: false
                        }).done(function (result) {
                            if (result) {
                                $.extend(self.settings, JSON.parse(result));
                            }
                            deferred.resolve();
                        }).fail(function () {
                            console.log("Failure getting user settings JSON");
                            deferred.reject();
                        });
                    } else {
                        deferred.reject();
                    }

                    return deferred.promise();
                };

                MvaSettingsModel.prototype.saveSettings = function (userId, isAnonymousRequest) {
                    if (typeof isAnonymousRequest === "undefined") { isAnonymousRequest = false; }
                    var self = this, promise;

                    if (userId && this.settings) {
                        if (this.hasChanged) {
                            promise = MvaApiTunnel.ajax({
                                type: "PUT",
                                contentType: "application/json",
                                url: this.scheme + "://" + this.mvaApiTargetHostname + "/api/usersettings/" + userId,
                                data: JSON.stringify(this.settings)
                            }).done(function () {
                                self.hasChanged = false;
                            });
                        } else {
                            promise = $.Deferred().resolve();
                        }
                    } else {
                        promise = $.Deferred().reject();
                    }

                    return promise;
                };
                return MvaSettingsModel;
            })(mlx.SettingsModelBase);
            mlx.MvaSettingsModel = MvaSettingsModel;
        })(learning.mlx || (learning.mlx = {}));
        var mlx = learning.mlx;
    })(microsoft.learning || (microsoft.learning = {}));
    var learning = microsoft.learning;
})(microsoft || (microsoft = {}));
;//-------------------------------------------------------------------------------------
// <copyright file="SaActivationModel.js" company="Microsoft Corporation">           
//     (c) Copyright Microsoft Learning. All rights reserved.               
// </copyright>                                                                
//-------------------------------------------------------------------------------------
/// <reference path="../../../_references.js" />

microsoft.learning.mlx.orgAggregateModel = (function () {

    var obj = function (orgId, learningPlansCount, groupsCount, usersCount) {
        this.orgId = orgId;
        this.groupsCount = ko.observable(groupsCount);
        this.learningPlansCount = ko.observable(learningPlansCount);
        this.usersCount = ko.observable(usersCount);
    };

    obj.prototype.orgAggregate = function () {
        var self = this,
            orgAgg = MLX.ajax({
            url: '/Services/organizations/' + this.orgId + '/AggregateData',
            type: 'get',
            data: JSON.stringify(this.orgId),
            contentType: 'application/json'
        }).done(function (data) {
            self.groupsCount(data.GroupsCount);
            self.learningPlansCount(data.LearningPlansCount);
            self.usersCount(data.UsersCount);
        });      
        return orgAgg;
    };

    obj.prototype.getSubscriptionDetails = function () {
        var self = this;
        subscriptionDetails = MLX.ajax({
            url: '/services/organizations/GetSASubscriptionDetails/' + this.orgId,
            type: 'get',
            contentType: 'application/json'
        });
        return subscriptionDetails;
    };
    return obj;   
})();;//-------------------------------------------------------------------------------------
// <copyright file="reportsModel.js" company="Microsoft Corporation">           
//     (c) Copyright Microsoft Learning. All rights reserved.               
// </copyright>                                                                
//-------------------------------------------------------------------------------------
/// <reference path="../../../_references.js" />

microsoft.learning.mlx.reportsModel = (function () {
    var self = undefined;

    var obj = function reportsModel(orgId) {
        self = this;
        this.OrganizationId = ko.observable(orgId);
        this.LearningPlans = ko.observableArray([]);
        this.LearningPlanCourses = ko.observableArray([]);
        this.CourseAssessments = ko.observableArray([]);
        this.LearnerCourseProgress = ko.observableArray([]);
    };

    obj.prototype.downloadOrganizationProgressReport = function () {
        var promise = MLX.ajax({
            url: '/services/reports/learningplans/progress?organizationId=' + self.OrganizationId(),
        });
        promise.done(function (data) {
            ko.mapping.fromJS(data, {
                create: function (item) {
                    var mappedItem = ko.mapping.fromJS(item.data);
                    mappedItem.ChildrenVisible = ko.observable(false);
                    mappedItem.ChildrenPopulated = ko.observable(false);
                    return mappedItem;
                }
            }, self.LearningPlans);
        });
        return promise;
    };

    obj.prototype.downloadLearningPlanProgressReport = function (learningPlan) {
        if (!learningPlan.ChildrenPopulated()) {
            var promise = MLX.ajax({
                url: '/services/reports/learningplans/' + learningPlan.Id() + '/progress?organizationId=' + self.OrganizationId(),
            });
            promise.done(function (data) {
                learningPlan.LearningPlanCourses = ko.mapping.fromJS(data);
                learningPlan.ChildrenPopulated(true);
            });
            return promise;
        }
        else {
            return $.Deferred().resolve();
        }
    };

    obj.prototype.downloadCourseAssesmentsProgressReport = function (course) {
        var promise = MLX.ajax({
            url: '/services/reports/courseassessments/' + course.Id() + '/progress?organizationId=' + self.OrganizationId(),
        });
        promise.done(function (data) {
            ko.mapping.fromJS(data, {}, self.CourseAssessments);
        });
        return promise;
    };

    obj.prototype.downloadLearnerCourseProgressReport = function (learningPlan) {
        var promise = MLX.ajax({
            url: '/services/reports/Learningplans/' + learningPlan.Id() + '/learnersprogress?organizationId=' + self.OrganizationId(),
        });
        promise.done(function (data) {
            $.each(data, function (index, learningplan) {
                if (learningplan.ModulesCompleted) {
                    var TotalModules = learningplan.PercentageModulesCompleted != 0 ? learningplan.ModulesCompleted * 100 / learningplan.PercentageModulesCompleted : 0;
                    learningplan.TotalModules = TotalModules;
                }
            });
            ko.mapping.fromJS(data, {}, self.LearnerCourseProgress);
        });
        return promise;
    };
    return obj;
})();;//-------------------------------------------------------------------------------------
// <copyright file="usersModel.js" company="Microsoft Corporation">           
//     (c) Copyright Microsoft Learning. All rights reserved.               
// </copyright>                                                                
//-------------------------------------------------------------------------------------
/// <reference path="../../../_references.js" />

// Since we are binding to inviteUsersCount we have to make sure its defined.
// Currently its only defined by the cshtml view in the ITASA app and not in the other UI apps.
if (!window.inviteUsersCount) {
    window.inviteUsersCount = 0;
}

microsoft.learning.mlx.usersModel = (function () {
    var self = undefined;

    var obj = function usersModel(orgId) {
        self = this;
        this.orgId = orgId;
            
        this.currentUser = {
            FirstName: ko.observable(),
            LastName: ko.observable(),
            EmailAddress: ko.observable(),
            ClassDepartment: ko.observable(),
            IsActive: ko.observable(true),
            Id: ko.observable(-1),
            IsEnrolled: ko.observable(false),
            UserRoles: ko.observableArray([]),
            UniqueID: ko.observable(),
           
            CourseActivityReports: ko.observableArray([]),
            AssessmentActivityReports: ko.observableArray([]),

            save: function (ValidationContext) {

                var currentUser = this;
                var isCreate = (currentUser.Id() == -1);

                currentUser.Roles = ko.observableArray();
                currentUser.Roles.push("AnonymousAuthenticatedUser");
                currentUser.Roles.push("Learner");
                //Replacing groups with blank string since we do not update/add groups when we edit/create user. 
                currentUser.Groups = "";

                $.each(currentUser.UserRoles(), function (index, role) {
                    if (role.Selected() == true) {
                        currentUser.Roles.push(role.Name());
                    }
                });

                var url = isCreate ? '/services/users/?organizationId=' + self.orgId : '/services/users/' + currentUser.Id() + '?organizationId=' + self.orgId;
                
                if (MLX.context.appChannel == 2 && this.isLearnerOnly()) ////for SA managed learner
                {
                    url = url + '&sendEnrollmentLink=' + 'false';
                   
                }
                else 
                { 
                    url = url + '&sendEnrollmentLink=' + (!currentUser.IsEnrolled() ? 'true' : 'false');
                    
                }
                
                url = url + '&whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href);

                return MLX.ajax({
                    type: isCreate ? 'POST' : 'PUT',
                    url: url,
                    dataType: 'json',
                    contentType: 'application/json',
                    data: JSON.stringify(ko.toJS(currentUser)),
                }).done(function (data) {
                    if (currentUser.Id() != -1) {
                        ko.mapping.fromJS(data, self.userMapping, self.userToUpdateOnSave);
                    }
                    else {
                        currentUser.Id(data.Id);
                        self.userToUpdateOnSave = ko.mapping.fromJS(data, self.userMapping);
                    }
                    self.usersList.remove(self.userToUpdateOnSave);
                    self.usersList.unshift(self.userToUpdateOnSave);
                });
            },

            isValid: function (ValidationContext) {
                ValidationContext.validateAll();
                return this.FirstName.errorCode() <= 0
                    && this.LastName.errorCode() <= 0
                    && this.EmailAddress.errorCode() <= 0
                    && this.ClassDepartment.errorCode() <= 0
                    && this.UniqueID.errorCode() <= 0;
            },
            isLearnerOnly:function ()
            {
                var currentUser = this;

                var learnerOnly = false;
                if (currentUser.Roles.indexOf("Learner") > -1) {
                    learnerOnly = true;
                }

                if (currentUser.Roles.indexOf("Instructor") > -1 || currentUser.Roles.indexOf("Administrator") > -1 || currentUser.Roles.indexOf("ReportsAdministrator") > -1) {
                    learnerOnly = false;
                }
                return learnerOnly;
            },
            loadActivityReport: function () {
                var thisUser = this;
                var reportPromise = MLX.ajax({
                    url: '/services/reports/users/' + thisUser.Id() + '?organizationId=' + self.orgId
                });

                reportPromise.done(function (data) {
                    microsoft.learning.mlx.utility.addBIRefreshDate(data);
                    ko.mapping.fromJS(data, {}, thisUser.CourseActivityReports);
                });
                return reportPromise;
            },
            loadAssessmentReport: function () {
                var thisUser = this;
                var reportPromise = MLX.ajax({
                    url: '/services/reports/users/' + thisUser.Id() + '/assessments?organizationId=' + self.orgId
                });

                reportPromise.done(function (data) {
                    microsoft.learning.mlx.utility.addBIRefreshDate(data);
                    ko.mapping.fromJS(data, {}, thisUser.AssessmentActivityReports);
                });
                return reportPromise;
            }
        };
        this.currentBulkAccessCode = {
            NumberOfCodes: ko.observable(50).extend({ required: "" }).extend({ maxValue: inviteUsersCount || 20000 }),
            SendCodeInEmail: ko.observable(false)
        };
    };
        
    obj.prototype.usersList = ko.observableArray([]);
    obj.prototype.allRoles = undefined;
    //obj.prototype.newBulkAccessCode = ko.observable(50).extend({ required: "" }).extend({ maxValue: 500 });
    //obj.prototype.sendBulkEnrollment = ko.observable(true);
        
    obj.prototype.bulkAccessCodes = ko.observableArray([]);

    obj.prototype.uploadResults = {
        createdRows: ko.observableArray(),
        duplicateRows: ko.observableArray(),
        invalidRows: ko.observableArray(),
        failedRows: ko.observableArray(),
        totalRecords: ko.observable(0),
        uploadError: ko.observable('')
    };
        
    obj.prototype.loadAllRoles = function () {
        if (!self.allRoles) {
            var promise = MLX.ajax({
                url: '/services/organizations/Roles'
            });

            promise.done(function (data) {
                self.allRoles = data;
            });

            return promise;
        }
        else {
            return $.Deferred().resolve();
        }
    };

    obj.prototype.showBulkUploadControl = function (divId, domainValue) {
        var uploadFormDiv = $('#' + divId);
        var width = uploadFormDiv.width();
        var height = uploadFormDiv.height();
        uploadFormDiv.html('<iframe scrolling="no" id="bulkUploadUsers" frameBorder="0" allowtransparency="true" src="https://' + MLX.context.apiHost + '/administration/UploadUserImportFile?channelId=' + MLX.context.appChannel + '"  style="height:' + height + 'px;width:' + width + 'px;"></iframe>');
    };

    obj.prototype.submitBulkUpload = function () {
        var getUploadTokenPromise = MLX.ajax({
            url: '/Administration/NewUploadToken',
            type: 'GET'
        });

        var bulkUploadPromise = getUploadTokenPromise.pipe(function (data) {
            var whrParam = microsoft.learning.mlx.utility.getQueryStringParamValue('whr');
            return MLX.sendCustomMessage({ token: data.token, formName: 'form-upload-users', whr: whrParam }, 'bulkUploadUsers');
        });

        bulkUploadPromise.done(function (data) {
            ko.mapping.fromJS(data, {}, self.uploadResults);
        }).fail(function (data) {
            var test = data;
        });;

        return bulkUploadPromise;
    };
        
    obj.prototype.mapCurrentUserRoles = function () {
        var roleMapping = {
            create: function (options) {
                var mappedItem = {
                    Name: ko.observable(options.data),
                    DisplayName: ko.observable(''),
                    Description: ko.observable(''),
                    IsVisible: ko.observable(false)
                };
                var roleFound = false;
                if (self.currentUser.Roles) {
                    $.each(self.currentUser.Roles(), function (index, role) {
                        if (role == mappedItem.Name()) {
                            roleFound = true;
                        }
                    });
                }
                if (roleFound) {
                    mappedItem.Selected = ko.observable(true);
                } else {
                    mappedItem.Selected = ko.observable(false);
                }

                if (arguments[0].data == "Learner") {
                    mappedItem.Selected = ko.observable(true);
                    mappedItem.Disabled = ko.observable(true);
                } else {
                    mappedItem.Disabled = ko.observable(false);
                }
                return mappedItem;
            }
        };

        ko.mapping.fromJS(self.allRoles, roleMapping, self.currentUser.UserRoles);
    };

    obj.prototype.newUser = function () {
        self.currentUser.FirstName('');
        self.currentUser.LastName('');
        self.currentUser.EmailAddress('');
        self.currentUser.ClassDepartment('');
        self.currentUser.IsActive(true);
        self.currentUser.Id(-1);
        self.currentUser.IsEnrolled(false);
        self.currentUser.UniqueID('');
        if (self.currentUser.Roles) {
            self.currentUser.Roles.removeAll();
        }
            
        // load all roles
        var promise = self.loadAllRoles();
        promise.done(function () {
            self.mapCurrentUserRoles();
        });

        return promise;
    };

    obj.prototype.prepareUserForEdit = function (user) {
        self.userToUpdateOnSave = user;
        ko.mapping.fromJS(ko.mapping.toJS(user), {}, this.currentUser);
        var promise = self.loadAllRoles();
        promise.done(function () {
            self.mapCurrentUserRoles();
        });

        return promise;
    };
           
    obj.prototype.loadUsers = function (viewModelMapping, pageIndex, usersPerPage, searchCriteria, newContext) {
        var promise = MLX.ajax({
            url: '/services/organizations/' + self.orgId + '/Users' + '?pageIndex=' + pageIndex + '&usersPerPage=' + usersPerPage + '&searchcriteria=' + encodeURIComponent(searchCriteria),
        });
      
        self.userMapping = {
            create: function (options) {
                var bindableUser = viewModelMapping.create(options);
                bindableUser.AssignedCoursesCount = ko.observable();
                bindableUser.UserAddedCoursesCount = ko.observable();
                bindableUser.LearningPlansCount = ko.observable();
                bindableUser.UserGroups = ko.observableArray([]);
                
                bindableUser.loadAdditionalDetails = function () {
                    var thisUser = this;

                    return MLX.ajax({
                        url: '/Services/users/' + thisUser.Id() + '/Details?organizationId=' + self.orgId 
                    }).done(function (data) {
                        thisUser.AssignedCoursesCount(data.AssignedCoursesCount);
                        thisUser.UserAddedCoursesCount(data.UserAddedCoursesCount);
                        thisUser.LearningPlansCount(data.LearningPlansCount);
                        ko.mapping.fromJS(data.UserGroups, {}, thisUser.UserGroups);
                    });
                };
                return bindableUser;
            },
            update: function (options) {
                return viewModelMapping.update(options);
            }
        };

        promise.done(function (data) {
            var newUsersList = ko.mapping.fromJS(data, self.userMapping)();
            var underlyingArray = self.usersList();
            if (newContext) {
                underlyingArray.splice(0, underlyingArray.length);
            }
            ko.utils.arrayPushAll(underlyingArray, newUsersList);
            self.usersList.valueHasMutated();
        });

        return promise;
    };

    obj.prototype.loadOrganizationUsers = function (viewModelMapping, searchFilter, paging) {
        var self = this;
        var promise = MLX.ajax({
            url: '/services/organizations/Users',
            type: 'POST',
            data: JSON.stringify(searchFilter),
            contentType: 'application/json'
        });
        self.userMapping = {
            create: function (options) {
                var bindableUser = viewModelMapping.create(options);
                bindableUser.AssignedCoursesCount = ko.observable();
                bindableUser.UserAddedCoursesCount = ko.observable();
                bindableUser.LearningPlansCount = ko.observable();
                bindableUser.UserGroups = ko.observableArray([]);

                bindableUser.loadAdditionalDetails = function () {
                    var thisUser = this;

                    return MLX.ajax({
                        url: '/Services/users/' + thisUser.Id() + '/Details?organizationId=' + self.orgId
                    }).done(function (data) {
                        thisUser.AssignedCoursesCount(data.AssignedCoursesCount);
                        thisUser.UserAddedCoursesCount(data.UserAddedCoursesCount);
                        thisUser.LearningPlansCount(data.LearningPlansCount);
                        ko.mapping.fromJS(data.UserGroups, {}, thisUser.UserGroups);
                    });
                };
                return bindableUser;
            },
            update: function (options) {
                return viewModelMapping.update(options);
            }
        };
        promise.done(function (data) {
            var newUsersList = ko.mapping.fromJS(data.Users, viewModelMapping);
            var underlyingArray = self.usersList();
            if (!paging) {
                underlyingArray.splice(0, underlyingArray.length);
            }
            ko.utils.arrayPushAll(underlyingArray, newUsersList());
            self.usersList.valueHasMutated();
        });
        return promise;
    };

    obj.prototype.EditUsersStatus = function (data) {
        var promise = MLX.ajax({
            url: '/services/users/EditUserStatus' + '?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href) + '&sendEnrollmentLink=' + 'true',
            type: 'PUT',
            data: JSON.stringify(data),
            contentType: 'application/json'
        });
        return promise;
    };

    obj.prototype.EditUserRoles = function (data) {
        data.Roles.push("AnonymousAuthenticatedUser");
        var promise = MLX.ajax({
            url: '/services/users/EditUserRoles' + '?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href) + '&sendEnrollmentLink=' + 'true',
            type: 'PUT',
            data: JSON.stringify(data),
            contentType: 'application/json'
        });
        return promise;
    };

    obj.prototype.EditOrganizationUserRoles = function (data) {
        var promise = MLX.ajax({
            url: '/services/users/EditUserOrganizationRole?sendMailNotification=false',
            type: 'PUT',
            data: JSON.stringify(data),
            contentType: 'application/json'
        });
        return promise;
    };

    obj.prototype.GetUserOrganizations = function (isActiveUsers, isAdmin) {
        var promise = MLX.ajax({
            url: '/services/users/UserOrganizations?isActive=' + isActiveUsers + '&isAdmin=' + isAdmin,
            type: 'GET',
            contentType: 'application/json'
        });
        return promise;
    };

    obj.prototype.getBulkAccessCodes = function () {
        return MLX.ajax({
            url: '/Services/organizations/' + self.orgId + '/UserAccessCodes'
        }).done(function (data) {
            ko.mapping.fromJS(data, {}, self.bulkAccessCodes);
        });
    };

    obj.prototype.newBulkAccessCode = function () {
        this.currentBulkAccessCode.NumberOfCodes('');
        this.currentBulkAccessCode.SendCodeInEmail(true);
    };

    obj.prototype.sendBulkAccessCodeEmail = function (bulkAccessCodeId) {
        return MLX.ajax({
            url: '/services/organizations/' + self.orgId + '/AccessCode/' + bulkAccessCodeId + '?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href.split(":")[0] + '://' + window.location.host),
            type: 'POST'
        });
    };

    obj.prototype.sendInviteUserEnrollCodeEmail = function (bulkAccessCodeId) {
        return MLX.ajax({
            url: '/services/organizations/InviteUserEnrollmentCode/' + bulkAccessCodeId + '?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href.split(":")[0] + '://' + window.location.host),
            type: 'POST'
        });
    };

    obj.prototype.generateBulkAccessCode = function (numberOfCodes, roleNumber) {
        if (numberOfCodes <= 0)
            return false;

        return MLX.ajax({
            url: '/Services/organizations/' + self.orgId + '/UserAccessCodes?numberOfUsages=' + numberOfCodes + '&roleNumber=' + roleNumber,
            type: 'POST'
        }).pipe(function (data) {
                
            self.bulkAccessCodes.unshift(data);
            if (self.currentBulkAccessCode.SendCodeInEmail()) {
                return self.sendInviteUserEnrollCodeEmail(data.Id);
            }
            return $.Deferred().resolve();
        });
    };

    obj.prototype.deactivateBulkAccessCodes = function (ids) {
        var jsonParams = JSON.stringify(ids);
        return MLX.ajax({
            type: "POST",
            url: '/services/organizations/' + self.orgId + '/DeactivateBulkUseAccessCodes?whr=' + microsoft.learning.mlx.utility.getQueryStringParamValue('whr') + '&applicationDomain=' + encodeURIComponent(window.location.href),
            data: jsonParams,
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        }).pipe(function () {
            $.each(self.bulkAccessCodes(), function (index, ac) {
                var currentId;
                if (typeof ac.Id === "function") {
                    currentId = ac.Id().toString();
                }
                else {
                    currentId = ac.Id.toString();
                }
                if (ids.indexOf(currentId) >= 0) {
                    ac.UsagesRemaining = 0;
                }
            });
            return $.Deferred().resolve();
        });
    };

    obj.prototype.savePrivacySetting = function (privacyData) {
        var promise = MLX.ajax({
            url: '/services/organizations/OrganizationPrivacySettings',
            type: 'PUT',
            data: JSON.stringify(privacyData),
            contentType: 'application/json'
        });
        return promise;
    };

    // bind validations
    return obj;
})();
;//-------------------------------------------------------------------------------------
// <copyright file="groupsModel.js" company="Microsoft Corporation">           
//     (c) Copyright Microsoft Learning. All rights reserved.               
// </copyright>                                                                
//-------------------------------------------------------------------------------------
/// <reference path="../../../_references.js" />

microsoft.learning.mlx.groupsModel = (function () {
    var obj = function groupsModel(orgId) {
        var self = this;
        this.orgId = orgId;

        this.currentGroup = {
            Id: ko.observable(-1),
            Name: ko.observable().extend({ required: "" }).extend({ regex: /^[^!+\{\}\~\`@#$%*<>()\^/\\"?&;=:.,\]\[]*$/i }).extend({ maxLength: 50 }),
            Description: ko.observable().extend({ regex: /^[^!+\{\}\~\`@#$%*<>()\^/\\"?&;=:.,\]\[]*$/i }).extend({ maxLength: 200 }),
            GroupUsers: ko.observableArray([]),
            numGroupUsers: 0,
            numSubGroups: 0,
            Users: ko.observableArray([]),
            Groups: ko.observableArray([]),
            SubGroups: ko.observableArray([]),
            groupUsersHash: [],
            subGroupHash: [],
            CourseActivityReports: ko.observableArray([]),
            AssessmentActivityReports: ko.observableArray([]),
            save: function (ValidationContext, groupListItemMapping) {
                if (ValidationContext) {
                    if (!this.isValid(ValidationContext)) {
                        return false;
                    }
                }
                
                var currentGroup = this;
                var trimmedGroup = {};
                var isCreate = (currentGroup.Id() == -1);

                trimmedGroup.Id = currentGroup.Id();
                trimmedGroup.Name = $.trim(currentGroup.Name());
                trimmedGroup.Description = $.trim(currentGroup.Description());

                return MLX.ajax({
                    type: isCreate ? 'POST' : 'PUT',
                    url: isCreate ? '/services/groups/?organizationId=' + self.orgId : '/services/groups/' + currentGroup.Id() + '/?organizationId=' + self.orgId,
                    dataType: 'json',
                    contentType: 'application/json',
                    data: JSON.stringify(trimmedGroup),
                }).done(function (data) {
                    if (currentGroup.Id() != -1) {
                        ko.mapping.fromJS(data, groupListItemMapping, self.groupToUpdateOnSave);
                    }
                    else {
                        currentGroup.Id(data.Id);
                        self.groupToUpdateOnSave = ko.mapping.fromJS(data, groupListItemMapping);
                    }
                    self.groupsList.remove(self.groupToUpdateOnSave);
                    self.groupsList.unshift(self.groupToUpdateOnSave);
                });
            },

            deleteGroup: function () {
                var currentGroup = this;
                return MLX.ajax({
                    type: 'DELETE',
                    url: '/services/groups/' + currentGroup.Id() + '/?organizationId=' + self.orgId,
                    dataType: 'json',
                    contentType: 'application/json'
                }).done(function (data) {
                    currentGroup.Id(data.Id);
                    self.groupsList.remove(self.groupToUpdateOnSave);
                });
            },

            isValid: function (ValidationContext) {
                ValidationContext.validateAll();
                return this.Name.errorCode() <= 0
                    && this.Description.errorCode() <= 0;
            },


            assignSubGroups: function (addedGroups, removedGroups) {
                var currentGroup = this;
                var idsToBeAdded = new Array();
                var idsToBeRemoved = new Array();
                                
                for (var addedIndex in addedGroups) {
                    if (addedGroups.hasOwnProperty(addedIndex)) {
                        var parsed = addedIndex.split("_");
                        if (parsed.length > 1 && parsed[0].toLowerCase() == "gr") {
                            idsToBeAdded.push(Number(parsed[1]));
                        }
                    }
                };
                for (var removedIndex in removedGroups) {
                    if (removedGroups.hasOwnProperty(removedIndex)) {
                        var parsed = removedIndex.split("_");
                        if (parsed.length > 1 && parsed[0].toLowerCase() == "gr") {
                            idsToBeRemoved.push(Number(parsed[1]));
                        }
                    }
                };

                var groupDetails = {
                    IdsToBeAdded: idsToBeAdded,
                    IdsToBeRemoved: idsToBeRemoved
                };
               

                return MLX.ajax({
                    url: '/services/groups/' + currentGroup.Id() + '/GroupsUpdate/?organizationId=' + self.orgId,
                    type: 'PUT',
                    data: JSON.stringify(groupDetails),
                    contentType: 'application/json',
                    dataType: 'json'
                });

            },
            

            loadSubGroups: function (pageIndex, groupsPerPage, searchCriteria, newContext, paging) {
                var currentGroup = this;
                var subGroupPromise = undefined;
                var groupDeferred = $.Deferred();
                if (newContext) {
                    // SubGroup will not change
                    currentGroup.numSubGroups = 0;
                    currentGroup.subGroupHash = []; //Re-Initialize the hash
                    var subGroupPromise = MLX.ajax({
                        url: '/services/groups/' + currentGroup.Id() + '/Groups/?organizationId=' + self.orgId
                    });

                    subGroupPromise.done(function (subGroups) {
                        var subGroupMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(true);
                                return mappedItem;
                            }
                        };
                        ko.mapping.fromJS(subGroups, subGroupMapping, currentGroup.SubGroups);
                        currentGroup.numSubGroups = subGroups.length;

                        $.each(subGroups, function (index, subGroup) {
                            currentGroup.subGroupHash[subGroup.Id] = subGroup.Id;
                        });

                    });

                }
                else {
                    subGroupPromise = $.Deferred().resolve();
                }


                var allGroups = undefined;

                subGroupPromise.done(function () {
                    var numGroupsPerPage = currentGroup.numSubGroups + groupsPerPage;
                    var allGroupsPromise = MLX.ajax({
                        url: '/services/groups/' + self.orgId + '/?pageIndex=' + pageIndex + '&groupsPerPage=' + numGroupsPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria),
                    });

                    allGroupsPromise.done(function (data) {
                        allGroups = data;
                        allGroups = $.grep(allGroups, function (group, i) {
                            // Need to filter current group from subgroup list and filter already existing groups
                            return (group.Id !== currentGroup.Id() && (currentGroup.subGroupHash[group.Id] === undefined));
                        });

                        var groupMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(false);
                                mappedItem.isSelected = ko.observable(false);
                                mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                                return mappedItem;
                            }
                        };

                        var newGroupList = ko.mapping.fromJS(allGroups, groupMapping)();
                        var underlyingArray = currentGroup.Groups();
                        if (!paging) {
                            underlyingArray.splice(0, underlyingArray.length);
                        }
                        ko.utils.arrayPushAll(underlyingArray, newGroupList);
                        currentGroup.Groups.valueHasMutated();
                        groupDeferred.resolve(allGroups);
                    });

                });

                return groupDeferred.promise();
            },
            loadUnassignedUsers: function (pageIndex, usersPerPage, searchCriteria, paging) {
                var userBundle = {};
                var allUsers = undefined;
                var currentPlan = this;
                var deferred = $.Deferred();
                var userSearchFilter = { OrganizationId: currentPlan.OrgId, Status: 1, PageIndex: pageIndex, SearchCriteria: encodeURIComponent(searchCriteria), UsersPerPage: usersPerPage, NotInLearningPlanId: currentPlan.Id() };

                var allUsersPromise = MLX.ajax({
                    url: '/services/organizations/Users',
                    type: 'POST',
                    data: JSON.stringify(userSearchFilter),
                    contentType: 'application/json',
                    dataType: 'json'
                });
                $.when(allUsersPromise).then(function (data) {
                    allUsers = data.Users;
                    // Filter out inactive users as they are not eligiable to be added to learning plans
                    if (allUsers.length > 0)
                        userBundle.moreUsers = true;
                    else
                        userBundle.moreUsers = false;

                    var userMapping = {
                        create: function (options) {
                            var mappedItem = ko.mapping.fromJS(options.data, {});
                            mappedItem.Selected = ko.observable(false);
                            mappedItem.isSelected = ko.observable(false);
                            mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                            return mappedItem;
                        }
                    };
                    var newUsersList = ko.mapping.fromJS(allUsers, userMapping)();
                    var underlyingArray = currentPlan.Users();
                    if (!paging) {
                        underlyingArray.splice(0, underlyingArray.length);
                    }
                    ko.utils.arrayPushAll(underlyingArray, newUsersList);
                    currentPlan.Users.valueHasMutated();
                    currentPlan.TotalUnassignedUsers = data.TotalResultCount;
                    userBundle.users = allUsers;
                    userBundle.TotalUnassignedUsers = data.TotalResultCount;
                    deferred.resolve(userBundle);
                }, function () {
                    deferred.reject();
                });

                return deferred.promise();
            },
            loadGroupUsers: function (pageIndex, usersPerPage, searchCriteria, newContext, paging) {
                var currentGroup = this;
                var groupUsersPromise = undefined;
                if (newContext) {
                    currentGroup.numGroupUsers = 0;
                    currentGroup.groupUsersHash = []; //Re-Initialize the hash
                    groupUsersPromise = MLX.ajax({
                        url: '/services/groups/' + currentGroup.Id() + '/Users/?organizationId=' + self.orgId
                    });
                    groupUsersPromise.done(function (data) {
                        var groupUserMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(true);
                                mappedItem.isSelected = ko.observable(false);
                                mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                                return mappedItem;
                            }
                        };                       
                        ko.mapping.fromJS(data, groupUserMapping, currentGroup.GroupUsers);
                        currentGroup.numGroupUsers = data.length;
                        $.each(data, function (index, user) {
                            currentGroup.groupUsersHash[user.Id] = user.Id;
                        });
                    });
                }
                else {
                    groupUsersPromise = $.Deferred().resolve();
                }

                //var allUsersPromise = undefined;
                var allUsers = undefined;

                var userDeferred = $.Deferred();
                groupUsersPromise.done(function () {
                    var numUsersPerPage = currentGroup.numGroupUsers + usersPerPage;
                    var allUsersPromise = MLX.ajax({
                        url: '/services/organizations/' + self.orgId + '/Users' + '?pageIndex=' + pageIndex + '&usersPerPage=' + numUsersPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria) + '&activeOnly=true',
                    });

                    allUsersPromise.done(function (data) {
                        allUsers = data;
                        // Filter out inactive users as they are not eligible to be added to groups
                        allUsers = $.grep(allUsers, function (user, i) {
                            return (user.IsActive && (currentGroup.groupUsersHash[user.Id] === undefined));
                        });
                        var userMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(false);
                                mappedItem.isSelected = ko.observable(false);
                                mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                                return mappedItem;
                            }
                        };
                        var newUsersList = ko.mapping.fromJS(allUsers, userMapping)();
                        var underlyingArray = currentGroup.Users();
                        if (!paging) {
                            underlyingArray.splice(0, underlyingArray.length);
                        }
                        ko.utils.arrayPushAll(underlyingArray, newUsersList);
                        currentGroup.Users.valueHasMutated();

                        userDeferred.resolve(allUsers);
                    });
                });
                return userDeferred.promise();
            },

            loadGroupAssignedUsers: function (pageIndex, usersPerPage, searchCriteria, newContext, paging) {
                var currentGroup = this;
                var groupUsersPromise = undefined;
                var userDeferred = $.Deferred();
                if (newContext) {
                    currentGroup.numGroupUsers = 0;
                    currentGroup.groupUsersHash = []; //Re-Initialize the hash                    
                    groupUsersPromise = MLX.ajax({
                        url: '/services/groups/' + currentGroup.Id() + '/Users/organizationId/' + self.orgId + '?pageIndex=' + pageIndex + '&Status=1&usersPerPage=' + usersPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria),
                    });
                    groupUsersPromise.done(function (data) {
                        var groupUserMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(true);
                                mappedItem.isSelected = ko.observable(false);
                                mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                                return mappedItem;
                            }
                        };
                        $.each(data, function (index, user) {
                            currentGroup.groupUsersHash[user.Id] = user.Id;
                        });
                        currentGroup.numGroupUsers = data.Users.length;

                        var newUsersList = ko.mapping.fromJS(data.Users, groupUserMapping)();
                        var underlyingArray = currentGroup.GroupUsers();
                        if (!paging) {
                            underlyingArray.splice(0, underlyingArray.length);
                        }
                        ko.utils.arrayPushAll(underlyingArray, newUsersList);
                        currentGroup.GroupUsers.valueHasMutated();
                        currentGroup.TotalUsersAssignedRecords = data.TotalResultCount;
                        userDeferred.resolve(data);
                    });
                }
                else {
                    groupUsersPromise = $.Deferred().resolve();
                }
                return userDeferred.promise();
            },

            loadGroupUnassignedUsers: function (pageIndex, usersPerPage, searchCriteria, paging) {
                var currentGroup = this;
                var allUsers = undefined;
                var userBundle = {};
                var userDeferred = $.Deferred();

                var groupUsersSearchFilter = { OrganizationId: self.orgId, Status: 1, PageIndex: pageIndex, SearchCriteria: searchCriteria, UsersPerPage: usersPerPage, NotInLearningPlanId: 0, NotInGroupId: currentGroup.Id()};

                var groupUsersPromise = MLX.ajax({
                    url: '/services/organizations/Users',
                    type: 'POST',
                    data: JSON.stringify(groupUsersSearchFilter),
                    contentType: 'application/json',
                    dataType: 'json'
                });

                $.when(groupUsersPromise).then(function (data) {                    
                    allUsers = data.Users;
                    // Filter out inactive users as they are not eligible to be added to groups

                    var userMapping = {
                        create: function (options) {
                            var mappedItem = ko.mapping.fromJS(options.data, {});
                            mappedItem.Selected = ko.observable(false);
                            mappedItem.isSelected = ko.observable(false);
                            mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                            return mappedItem;
                        }
                    };
                    var newUsersList = ko.mapping.fromJS(allUsers, userMapping)();
                    var underlyingArray = currentGroup.Users();
                    if (!paging) {
                        underlyingArray.splice(0, underlyingArray.length);
                    }
                    ko.utils.arrayPushAll(underlyingArray, newUsersList);
                    currentGroup.Users.valueHasMutated();
                    currentGroup.TotalUsersUnassignedUsers = data.TotalResultCount;
                    userBundle.users = allUsers;
                    userBundle.TotalUsersUnassignedUsers = data.TotalResultCount;
                    userDeferred.resolve(userBundle);
                });
                return userDeferred.promise();
            },

            loadAssignedSubGroups: function (pageIndex, groupsPerPage, searchCriteria, newContext, paging) {                
                var currentGroup = this;
                var subGroups = undefined;
                var groupDeferred = $.Deferred();
                if (newContext) {
                    // SubGroup will not change
                    currentGroup.numSubGroups = 0;
                    currentGroup.subGroupHash = []; //Re-Initialize the hash

                    var subGroupPromise = MLX.ajax({
                        url: '/services/groups/' + currentGroup.Id() + '/subgroups/?organizationId=' + self.orgId + '&pageIndex=' + pageIndex + '&groupsPerPage=' + groupsPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria) + '&isAssigned=true',
                    });
                    $.when(subGroupPromise).then(function (data) {
                        var subGroupMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(true);
                                mappedItem.isSelected = ko.observable(false);
                                mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                                return mappedItem;
                            }
                        };
                        subGroups = data.Groups;
                        $.each(subGroups, function (index, subGroup) {
                            currentGroup.subGroupHash[subGroup.Id] = subGroup.Id;
                        });
                        currentGroup.numSubGroups = subGroups.length;
                        var newGroupList = ko.mapping.fromJS(subGroups, subGroupMapping)();
                        var underlyingArray = currentGroup.SubGroups();
                        if (!paging) {
                            underlyingArray.splice(0, underlyingArray.length);
                        }
                        ko.utils.arrayPushAll(underlyingArray, newGroupList);
                        currentGroup.TotalAssignedGroups = data.TotalResultCount;
                        currentGroup.SubGroups.valueHasMutated();
                        groupDeferred.resolve(subGroups);
                    });

                }
                else {
                    subGroupPromise = $.Deferred().resolve();
                }
                return groupDeferred.promise();
            },

            loadUnassignedSubGroups: function (pageIndex, groupsPerPage, searchCriteria, newContext, paging) {                
                var currentGroup = this;
                var allGroups = undefined;
                var groupDeferred = $.Deferred();
                
                    var allGroupsPromise = MLX.ajax({
                        url: '/services/groups/' + currentGroup.Id() + '/subgroups/?organizationId=' + self.orgId + '&pageIndex=' + pageIndex + '&groupsPerPage=' + groupsPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria) + '&isAssigned=false',
                    });

                    allGroupsPromise.done(function (data) {
                        var groupMapping = {
                            create: function (options) {
                                var mappedItem = ko.mapping.fromJS(options.data, {});
                                mappedItem.Selected = ko.observable(false);
                                mappedItem.isSelected = ko.observable(false);
                                mappedItem.pageIndex = ko.observable(parseInt(pageIndex) + 1);
                                return mappedItem;
                            }
                        };

                        allGroups = data.Groups;
                        allGroups = $.grep(allGroups, function (group, i) {
                            // Need to filter current group from subgroup list and filter already existing groups
                            return (group.Id !== currentGroup.Id() && (currentGroup.subGroupHash[group.Id] === undefined));
                        });

                        var newGroupList = ko.mapping.fromJS(allGroups, groupMapping)();
                        var underlyingArray = currentGroup.Groups();
                        if (!paging) {
                            underlyingArray.splice(0, underlyingArray.length);
                        }
                        ko.utils.arrayPushAll(underlyingArray, newGroupList);
                        currentGroup.TotalUnassignedGroups = data.TotalResultCount;
                        currentGroup.Groups.valueHasMutated();
                        groupDeferred.resolve(allGroups);
                    });

                return groupDeferred.promise();
            },

            assignUsersToGroup: function (addedUsers, removedUsers) {                
                var currentGroup = this;                
                var idsToBeAdded = new Array();
                var idsToBeRemoved = new Array();


                for (var addedIndex in addedUsers) {
                    if (addedUsers.hasOwnProperty(addedIndex)) {
                        var parsed = addedIndex.split("_");
                        if (parsed.length > 1 && parsed[0].toLowerCase() == "user") {
                            idsToBeAdded.push(Number(parsed[1]));
                        }
                    }
                };
                for (var removedIndex in removedUsers) {
                    if (removedUsers.hasOwnProperty(removedIndex)) {
                        var parsed = removedIndex.split("_");
                        if (parsed.length > 1 && parsed[0].toLowerCase() == "user") {                           
                            idsToBeRemoved.push(Number(parsed[1]));
                        }
                    }
                };
                
                var userDetails = {
                    IdsToBeAdded: idsToBeAdded,
                    IdsToBeRemoved: idsToBeRemoved
                };


                return MLX.ajax({
                    url: '/services/groups/' + currentGroup.Id() + '/GroupUsers/?organizationId=' + self.orgId,
                    type: 'PUT',
                    data: JSON.stringify(userDetails),
                    contentType: 'application/json',
                    dataType: 'json'
                });
            },

            loadActivityReport: function () {
                var currentGroup = this;
                var reportPromise = MLX.ajax({
                    url: '/services/reports/groups/' + currentGroup.Id() + '?organizationId=' + self.orgId
                });

                reportPromise.done(function (data) {
                    microsoft.learning.mlx.utility.addBIRefreshDate(data);
                    ko.mapping.fromJS(data, {}, currentGroup.CourseActivityReports);
                });
                return reportPromise;
            },
            loadAssessmentReport: function () {
                var currentGroup = this;
                var reportPromise = MLX.ajax({
                    url: '/services/reports/groups/' + currentGroup.Id() + '/assessments?organizationId=' + self.orgId
                });

                reportPromise.done(function (data) {
                    microsoft.learning.mlx.utility.addBIRefreshDate(data);
                    ko.mapping.fromJS(data, {}, currentGroup.AssessmentActivityReports);
                });
                return reportPromise;
            }
        };
    };


    obj.prototype.getGroupAggregates = function (groupId) {
        var self = this;
        var aggregatePromise = MLX.ajax({
            url: '/services/groups/' + groupId + '/Detail/?organizationId=' + self.orgId
        });
        return aggregatePromise;
    };


    obj.prototype.getAllGroupUsers = function (groupId) {
        var self = this;
        var allUsersPromise = MLX.ajax({
            url: '/services/groups/' + groupId + '/AllUsers/?organizationId=' + self.orgId
        });
        return allUsersPromise;
    };



    obj.prototype.groupsList = ko.observableArray([]);

    obj.prototype.loadGroups = function (groupListMapping, pageIndex, groupsPerPage, searchCriteria, newContext) {
        var self = this;
        var promise = MLX.ajax({
            url: '/services/groups/' + self.orgId + '/?pageIndex=' + pageIndex + '&groupsPerPage=' + groupsPerPage + '&searchCriteria=' + encodeURIComponent(searchCriteria),
        });

        promise.done(function (data) {
            var newGroupsList = ko.mapping.fromJS(data, groupListMapping)();
            var underlyingArray = self.groupsList();
            if (newContext) {
                underlyingArray.splice(0, underlyingArray.length);
            }
            ko.utils.arrayPushAll(underlyingArray, newGroupsList);
            self.groupsList.valueHasMutated();
        });

        return promise;
    };

    obj.prototype.getGroupUsers = function (groupId) {
        var self = this;
        var groupUsersPromise = MLX.ajax({
            url: '/services/groups/' + groupId + '/Users/?organizationId=' + self.orgId
        });
        return groupUsersPromise;
    };

    obj.prototype.newGroup = function () {
        var self = this;
        self.currentGroup.Name('');
        self.currentGroup.Description('');
        self.currentGroup.Users([]);
        self.currentGroup.GroupUsers([]);
        self.currentGroup.SubGroups([]);
        self.currentGroup.AssessmentActivityReports([]);
        self.currentGroup.groupUsersHash = [];
        self.currentGroup.subGroupHash = [];
        self.currentGroup.CourseActivityReports([]);
        self.currentGroup.Groups([]);
        self.currentGroup.Id(-1);
    }

    obj.prototype.prepareGroupForEdit = function (groupFromList) {
        var self = this;
        self.groupToUpdateOnSave = groupFromList;
        ko.mapping.fromJS(ko.mapping.toJS(groupFromList), {}, this.currentGroup);
    };
    return obj;
}
)();
;microsoft.learning.mlx.tenantRetirementModel = (function () {
    var self = undefined;

    var obj = function tenantRetirementModel() {
        self = this;
    };

    obj.prototype.getRetirementMessage = function (isLearner) {
        var url = '/sdk/tenantretirement/v1.0/message/' + isLearner;
        var promise = MLX.ajax({
            url: url,
            type: 'GET',
        });
        return promise;
    }

    obj.prototype.getRetirementMessageByChannel = function (channelId,isLearner) {
        var url = '/sdk/tenantretirement/v1.0/' + channelId +'/message/' + isLearner;
        var promise = MLX.ajax({
            url: url,
            type: 'GET',
        });
        return promise;
    }

    return obj;
})();
;