<commit_before><script>
  import BaseModal from './BaseModal'
  import { mapActions, mapGetters } from 'vuex'
  import { internationalizeConfig, getNextLevelForLevel, getNextLevelLink, tryCopy, internationalizeLevelType, internationalizeContentType } from 'ozaria/site/common/ozariaUtils'
  import utils from 'core/utils'
  import urls from 'core/urls'
  import api from 'core/api'
  import ModalCharCustomization from 'ozaria/site/components/char-customization/ModalCharCustomization'
  import ClassroomLib from '../../../../app/models/ClassroomLib'

  export default Vue.extend({
    components: {
      BaseModal,
      ModalCharCustomization
    },
    props: {
      currentLevel: {
        type: Object,
        required: true
      },
      capstoneStage: {
        type: String,
        default: null
      },
      courseId: {
        type: String,
        default: null
      },
      courseInstanceId: {
        type: String,
        default: null
      },
      currentIntroContent: {
        type: Object,
        default: () => { return undefined }
      },
      introLevelComplete: {
        type: Boolean,
        default: undefined
      },
      goToNextDirectly: {
        type: Boolean,
        default: false
      },
      showShareModal: {
        type: Boolean,
        default: false
      }
    },
    data: () => ({
      nextLevelLink: '',
      editCapstoneLevelData: undefined,
      capstoneLevelSession: {},
      isFirstLevel: undefined,
      showCharCx: false,
      classroom: undefined,
      nextLevelIsLocked: false,
      doReload: true // Reload browser while loading the next URL. This is to fix the memory leak in cinematics.
    }),
    computed: {
      ...mapGetters({
        levelsList: 'unitMap/getCurrentLevelsList',
        userLocale: 'me/preferredLocale',
        campaignData: 'campaigns/getCampaignData'
      }),
      shareModal () {
        return !me.isSessionless() && (this.showShareModal || this.editCapstoneLevelData)
      },
      currentContent () {
        if (this.shareModal) {
          return this.editCapstoneLevelData || {}
        }
        return this.currentIntroContent || this.currentLevel.attributes || this.currentLevel || {}
      },
      contentName () {
        return utils.i18n(this.currentContent,'displayName') || utils.i18n(this.currentContent,'name')
      },
      contentType () {
        if (this.currentContent.ozariaType) {
          return internationalizeLevelType(this.currentContent.ozariaType, true)
        } else {
          return internationalizeContentType(this.currentContent.contentType)
        }
      },
      learningGoals () {
        const specificArticles = (this.currentContent.documentation || {}).specificArticles
        const learningGoals = _.find(specificArticles, { name: 'Learning Goals' })
        let learningGoalsText
        if (learningGoals) {
          learningGoalsText = internationalizeConfig(learningGoals, this.userLocale).body
        }
        return learningGoalsText
      },
      shareURL () {
        if (this.editCapstoneLevelData && this.capstoneLevelSession) {
          const shareUrlOptions = {
            level: this.editCapstoneLevelData,
            session: { _id: this.capstoneLevelSession._id }
          }
          if (this.courseId) {
            shareUrlOptions.course = { _id: this.courseId }
          }
          return urls.playDevLevel(shareUrlOptions)
        }
        return ''
      },
      charCxModal () {
        return this.isFirstLevel && !(me.get('ozariaUserOptions') || {}).tints
      },
      codeLanguage () {
        return utils.getQueryVariable('codeLanguage')
      }
    },
    async mounted () {
      if (!this.currentLevel) {
        // TODO handle_error_ozaria
        console.error('Level data are required for victory modal')
        return noty({ text: 'Error in victory screen', layout: 'topCenter', type: 'error', timeout: 2000 })
      }

      // TODO: replace with Ozaria sound
      // TODO Use new audio system post-august launch
      // Backbone.Mediator.publish('audio-player:play-sound', { trigger: 'victory' })

      try {
        if (!this.currentIntroContent || this.introLevelComplete) { // Fetch next level only if its not an intro level, or all content in intro level is complete
          await this.getNextLevelLink()
        }
        if (this.goToNextDirectly) {
          return application.router.navigate(this.nextLevelLink, { trigger: true }, this.doReload)
        }
      } catch (e) {
        // TODO handle_error_ozaria
        console.error('Error in victory modal', e)
      }
    },

    methods: {
      ...mapActions({
        buildLevelsData: 'unitMap/buildLevelsData'
      }),
      async fetchRequiredData (campaignHandle) {
        // TODO: Fix duplicate fetching here. The `buildLevelsData` also fetches this.
        //       Ideally we use vuex getters to get the already fetched classroom.
        const promises = []
        promises.push(api.courseInstances.get({ courseInstanceID: this.courseInstanceId }).then(async courseInstance => {
          const classroomId = courseInstance.classroomID
          // classroom snapshot of the levels for the course
          this.classroom = await api.classrooms.get({ classroomID: classroomId })
        }))

        promises.push(this.buildLevelsData({ campaignHandle, courseInstanceId: this.courseInstanceId, courseId: this.courseId, classroom: this.classroom }))
        return Promise.all(promises)
      },

      async getNextLevelLink () {
        const campaignHandle = this.currentLevel.campaign || this.currentLevel.attributes.campaign
        await this.fetchRequiredData(campaignHandle)
        const currentLevelData = this.levelsList[this.currentLevel.original || this.currentLevel.attributes.original]
        this.isFirstLevel = currentLevelData.first
        let currentLevelStage
        if (currentLevelData.isPlayedInStages && this.capstoneStage) {
          currentLevelStage = parseInt(this.capstoneStage)
        }
        const nextLevel = getNextLevelForLevel(currentLevelData, currentLevelStage) || {}
        this.nextLevelIsLocked = ClassroomLib.isStudentOnLockedLevel(this.classroom, me.get('_id'), this.courseId, nextLevel.original)
        if (this.nextLevelIsLocked) {
          noty({
            layout: 'center',
            type: 'info',
            text: this.$t('teacher_dashboard.teacher_locked_message'),
            buttons: [{
              text: this.$t('play.back_to_dashboard'),
              onClick: ($noty) => {
                $noty.close()
                application.router.navigate('/students', { trigger: true })
              }
            }]
          })
        }
        if (nextLevel.original && !this.showShareModal && this.levelsList[nextLevel.original]) {
          const nextLevelLinkOptions = {
            courseId: this.courseId,
            courseInstanceId: this.courseInstanceId,
            codeLanguage: this.codeLanguage
          }
          if (me.isSessionless()) {<commit_msg>Don't load classroom for sessionless user in victory modal (#587)<commit_after><script>
  import BaseModal from './BaseModal'
  import { mapActions, mapGetters } from 'vuex'
  import { internationalizeConfig, getNextLevelForLevel, getNextLevelLink, tryCopy, internationalizeLevelType, internationalizeContentType } from 'ozaria/site/common/ozariaUtils'
  import utils from 'core/utils'
  import urls from 'core/urls'
  import api from 'core/api'
  import ModalCharCustomization from 'ozaria/site/components/char-customization/ModalCharCustomization'
  import ClassroomLib from '../../../../app/models/ClassroomLib'

  export default Vue.extend({
    components: {
      BaseModal,
      ModalCharCustomization
    },
    props: {
      currentLevel: {
        type: Object,
        required: true
      },
      capstoneStage: {
        type: String,
        default: null
      },
      courseId: {
        type: String,
        default: null
      },
      courseInstanceId: {
        type: String,
        default: null
      },
      currentIntroContent: {
        type: Object,
        default: () => { return undefined }
      },
      introLevelComplete: {
        type: Boolean,
        default: undefined
      },
      goToNextDirectly: {
        type: Boolean,
        default: false
      },
      showShareModal: {
        type: Boolean,
        default: false
      }
    },
    data: () => ({
      nextLevelLink: '',
      editCapstoneLevelData: undefined,
      capstoneLevelSession: {},
      isFirstLevel: undefined,
      showCharCx: false,
      classroom: undefined,
      nextLevelIsLocked: false,
      doReload: true // Reload browser while loading the next URL. This is to fix the memory leak in cinematics.
    }),
    computed: {
      ...mapGetters({
        levelsList: 'unitMap/getCurrentLevelsList',
        userLocale: 'me/preferredLocale',
        campaignData: 'campaigns/getCampaignData'
      }),
      shareModal () {
        return !me.isSessionless() && (this.showShareModal || this.editCapstoneLevelData)
      },
      currentContent () {
        if (this.shareModal) {
          return this.editCapstoneLevelData || {}
        }
        return this.currentIntroContent || this.currentLevel.attributes || this.currentLevel || {}
      },
      contentName () {
        return utils.i18n(this.currentContent,'displayName') || utils.i18n(this.currentContent,'name')
      },
      contentType () {
        if (this.currentContent.ozariaType) {
          return internationalizeLevelType(this.currentContent.ozariaType, true)
        } else {
          return internationalizeContentType(this.currentContent.contentType)
        }
      },
      learningGoals () {
        const specificArticles = (this.currentContent.documentation || {}).specificArticles
        const learningGoals = _.find(specificArticles, { name: 'Learning Goals' })
        let learningGoalsText
        if (learningGoals) {
          learningGoalsText = internationalizeConfig(learningGoals, this.userLocale).body
        }
        return learningGoalsText
      },
      shareURL () {
        if (this.editCapstoneLevelData && this.capstoneLevelSession) {
          const shareUrlOptions = {
            level: this.editCapstoneLevelData,
            session: { _id: this.capstoneLevelSession._id }
          }
          if (this.courseId) {
            shareUrlOptions.course = { _id: this.courseId }
          }
          return urls.playDevLevel(shareUrlOptions)
        }
        return ''
      },
      charCxModal () {
        return this.isFirstLevel && !(me.get('ozariaUserOptions') || {}).tints
      },
      codeLanguage () {
        return utils.getQueryVariable('codeLanguage')
      }
    },
    async mounted () {
      if (!this.currentLevel) {
        // TODO handle_error_ozaria
        console.error('Level data are required for victory modal')
        return noty({ text: 'Error in victory screen', layout: 'topCenter', type: 'error', timeout: 2000 })
      }

      // TODO: replace with Ozaria sound
      // TODO Use new audio system post-august launch
      // Backbone.Mediator.publish('audio-player:play-sound', { trigger: 'victory' })

      try {
        if (!this.currentIntroContent || this.introLevelComplete) { // Fetch next level only if its not an intro level, or all content in intro level is complete
          await this.getNextLevelLink()
        }
        if (this.goToNextDirectly) {
          return application.router.navigate(this.nextLevelLink, { trigger: true }, this.doReload)
        }
      } catch (e) {
        // TODO handle_error_ozaria
        console.error('Error in victory modal', e)
      }
    },

    methods: {
      ...mapActions({
        buildLevelsData: 'unitMap/buildLevelsData'
      }),
      async fetchRequiredData (campaignHandle) {
        // TODO: Fix duplicate fetching here. The `buildLevelsData` also fetches this.
        //       Ideally we use vuex getters to get the already fetched classroom.
        const promises = []

        // Only do this load if we have a courseInstanceId. We won't have for hour of code for example.
        if (this.courseInstanceId) {
          promises.push(api.courseInstances.get({ courseInstanceID: this.courseInstanceId }).then(async courseInstance => {
            const classroomId = courseInstance.classroomID
            // classroom snapshot of the levels for the course
            this.classroom = await api.classrooms.get({ classroomID: classroomId })
          }))
        }

        promises.push(this.buildLevelsData({ campaignHandle, courseInstanceId: this.courseInstanceId, courseId: this.courseId, classroom: this.classroom }))
        return Promise.all(promises)
      },

      async getNextLevelLink () {
        const campaignHandle = this.currentLevel.campaign || this.currentLevel.attributes.campaign
        await this.fetchRequiredData(campaignHandle)
        const currentLevelData = this.levelsList[this.currentLevel.original || this.currentLevel.attributes.original]
        this.isFirstLevel = currentLevelData.first
        let currentLevelStage
        if (currentLevelData.isPlayedInStages && this.capstoneStage) {
          currentLevelStage = parseInt(this.capstoneStage)
        }
        const nextLevel = getNextLevelForLevel(currentLevelData, currentLevelStage) || {}
        if (this.classroom) {
          this.nextLevelIsLocked = ClassroomLib.isStudentOnLockedLevel(this.classroom, me.get('_id'), this.courseId, nextLevel.original)
        }

        if (this.nextLevelIsLocked) {
          noty({
            layout: 'center',
            type: 'info',
            text: this.$t('teacher_dashboard.teacher_locked_message'),
            buttons: [{
              text: this.$t('play.back_to_dashboard'),
              onClick: ($noty) => {
                $noty.close()
                application.router.navigate('/students', { trigger: true })
              }
            }]
          })
        }
        if (nextLevel.original && !this.showShareModal && this.levelsList[nextLevel.original]) {
          const nextLevelLinkOptions = {
            courseId: this.courseId,
            courseInstanceId: this.courseInstanceId,
            codeLanguage: this.codeLanguage
          }
          if (me.isSessionless()) {
{'fraction_non_alphanumeric': 0.10288468254522595, 'fraction_numerical': 0.0013270936648739262, 'mean_word_length': 2.321271166782649, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6031114', 'n_tokens_mistral': 3880, 'n_tokens_neox': 3839, 'n_words': 839}