<commit_before>'use strict';

// Hades Extreme

// TODO: arcane font doors
// TODO: arcane utterance orbs
// TODO: fire/ice tethers (0060|0061)

[{
  zoneRegex: /^The Minstrel's Ballad: Hades's Elegy$/,
  timelineFile: 'hades-ex.txt',
  timelineTriggers: [
    {
      id: 'HadesEx Comet',
      regex: /Comet 1/,
      beforeSeconds: 5,
      condition: function(data) {
        return data.role == 'tank';
      },
      infoText: {
        en: 'Comet Towers',
      },
    },
  ],
  triggers: [
    {
      id: 'HadesEx Shadow Spread',
      regex: / 14:47A8:Hades starts using Shadow Spread/,
      alertText: {
        en: 'Protean',
        de: 'Himmelsrichtungen',
        fr: 'Position',
        ja: '散開',
      },
    },
    {
      id: 'HadesEx Ravenous Assault',
      regex: / 14:47A6:Hades starts using Ravenous Assault on (\y{Name})\./,
      alertText: function(data, matches) {
        if (matches[1] == data.me) {
          return {
            en: 'Tank Buster on YOU',
            de: 'Tankbuster auf DIR',
            fr: 'Tankbuster sur VOUS',
          };
        }
        if (data.role == 'healer') {
          return {
            en: 'Buster on ' + data.ShortName(matches[1]),
            de: 'Tankbuster auf ' + data.ShortName(matches[1]),
            fr: 'Tankbuster sur ' + data.ShortName(matches[1]),
          };
        }
      },
      infoText: function(data, matches) {
        if (matches[1] == data.me || data.role != 'tank')
          return;

        return {
          en: 'Buster on ' + data.ShortName(matches[1]),
          de: 'Tankbuster auf ' + data.ShortName(matches[1]),
          fr: 'Tankbuster sur ' + data.ShortName(matches[1]),
        };
      },
    },
    {
      id: 'HadesEx Bad Faith Left 1',
      regex: / 14:47AB:Hades starts using Bad Faith/,
      infoText: {
        en: 'Left',
        de: 'Links',
        fr: 'Gauche',
        cn: '左',
      },
    },
    {
      id: 'HadesEx Bad Faith Left 2',
      regex: / 14:47AB:Hades starts using Bad Faith/,
      delaySeconds: 5,
      infoText: {
        en: 'Then Right',
        de: 'Links',
        fr: 'Gauche',
        cn: '左',
      },
    },
    {
      id: 'HadesEx Bad Faith Right 1',
      regex: / 14:47AC:Hades starts using Bad Faith/,
      infoText: {
        en: 'Right',
        de: 'Rechts',
        fr: 'Droite',
        cn: '右',
      },
    },
    {
      id: 'HadesEx Bad Faith Right 2',
      regex: / 14:47AC:Hades starts using Bad Faith/,
      delaySeconds: 5,
      infoText: {
        en: 'Then Left',
        de: 'Links',
        fr: 'Gauche',
        cn: '左',
      },
    },
    {
      id: 'HadesEx Quake III',
      regex: / 14:47B8:Nabriales's Shade starts using Quake III/,
      condition: function(data) {
        return data.role == 'tank' || data.role == 'healer';
      },
      infoText: {
        en: 'aoe',
      },
    },
    {
      id: 'HadesEx Dark II Tether',
      regex: / 23:\y{ObjectId}:Shadow of the Ancients:\y{ObjectId}:(\y{Name}):....:....:0011:/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Point Tether Out',
      },
    },
    {
      id: 'HadesEx Ancient Water 3',
      regex: / 1B:\y{ObjectId}:(\y{Name}):....:....:003E:/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      infoText: {
        en: 'Healer Stacks',
      },
      run: function(data, matches) {
        data.waterDark = data.waterDark || {};
        data.waterDark[matches[1]] = 'stack';
      },
    },
    {
      id: 'HadesEx Ancient Darkness',
      regex: / 1B:\y{ObjectId}:(\y{Name}):....:....:0060:/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Tank Spread',
      },
      run: function(data, matches) {
        data.waterDark = data.waterDark || {};
        data.waterDark[matches[1]] = 'spread';
      },
    },
    {
      id: 'HadesEx Ancient Water Unmarked',
      regex: / 1B:\y{ObjectId}:\y{Name}:....:....:(?:0030|0060):/,
      suppressSeconds: 5,
      infoText: function(data) {
        if (data.me in data.waterDark)
          return;
        return {
          en: 'Healer Stacks',
        };
      },
    },
    {
      id: 'HadesEx Shades Too Close',
      regex: / 23:\y{ObjectId}:(?:Igeyorhm's Shade|Lahabrea's Shade):\y{ObjectId}:(?:Igeyorhm's Shade|Lahabrea's Shade):....:....:000E:/,
      suppressSeconds: 10,
      condition: function(data) {
        return data.role == 'tank';
      },
      alarmText: {
        en: 'Move Shades Apart',
      },
    },
    {
      id: 'HadesEx Spheres',
      regex: / 14:47BD:Igeyorhm's Shade starts using Blizzard Sphere/,
      condition: function(data) {
        return data.role == 'tank';
      },
      infoText: function(data) {
        if (!data.sphereCount)
          return;
        return {
          en: 'tank swap soon',
        };
      },
      run: function(data) {
        data.sphereCount = (data.sphereCount || 0) + 1;
      },
    },
    {
      // TODO: maybe this should say "switch" the second time?
      id: 'HadesEx Burning Brand',
      regex: / 1A:\y{ObjectId}:(\y{Name}) gains the effect of Burning Brand/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Attack Igeyorhm',
      },
      run: function(data) {
        data.brand = 'fire';
      },
    },
    {
      id: 'HadesEx Freezing Brand',
      regex: / 1A:\y{ObjectId}:(\y{Name}) gains the effect of Freezing Brand/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Attack Lahabrea',
      },
      run: function(data) {
        data.brand = 'ice';
      },
    },
    {
      id: 'HadesEx Blizzard IV',
      regex: / 14:47C3:Igeyorhm's Shade starts using Blizzard IV on (\y{Name})\./,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Tank Buster on YOU',
        de: 'Tankbuster auf DIR',
        fr: 'Tankbuster sur VOUS',
      },
    },
    {
      id: 'HadesEx Fire IV',
      regex: / 14:47C2:Lahabrea's Shade starts using Fire IV on (\y{Name})\./,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Tank Buster on YOU',
        de: 'Tankbuster auf DIR',
        fr: 'Tankbuster sur VOUS',
      },
    },
    {
      id: 'HadesEx Healers Blizzard/Fire IV',
      regex: / (?:14:47C3:Igeyorhm's Shade|14:47C2:Lahabrea's Shade)/,
      suppressSeconds: 5,
      alertText: {
        en: 'Tank Busters',
      },
    },
    {
      id: 'HadesEx Doom',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Doom/,
      suppressSeconds: 5,
      condition: function(data) {
        return data.role == 'healer';
      },
      alertText: {
        en: 'Heal T/H to Full',
      },
    },
    {
      id: 'HadesEx Shriek',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Cursed Shriek from (.*?) for (\y{Float}) Seconds\./,
      delaySeconds: function(data, matches) {
        return parseFloat(matches[1]) - 2;
      },
      alarmText: {
        en: 'Look Away',
      },
    },
    {
      id: 'HadesEx Beyond Death',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Beyond Death/,
      durationSeconds: 8,
      alertText: {
        en: 'Get Killed',
      },
    },
    {
      id: 'HadesEx Ancient Circle',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Ancient Circle from (.*?) for (\y{Float}) Seconds\./,
      delaySeconds: function(data, matches) {
        return parseFloat(matches[1]) - 5;
      },
      infoText: {
        en: 'Donut on YOU',
      },
    },
  ],
}];
<commit_msg>raidboss: handle hadesex water/dark markers better<commit_after>'use strict';

// Hades Extreme

// TODO: arcane font doors
// TODO: arcane utterance orbs
// TODO: fire/ice tethers (0060|0061)

[{
  zoneRegex: /^The Minstrel's Ballad: Hades's Elegy$/,
  timelineFile: 'hades-ex.txt',
  timelineTriggers: [
    {
      id: 'HadesEx Comet',
      regex: /Comet 1/,
      beforeSeconds: 5,
      condition: function(data) {
        return data.role == 'tank';
      },
      infoText: {
        en: 'Comet Towers',
      },
    },
  ],
  triggers: [
    {
      id: 'HadesEx Shadow Spread',
      regex: / 14:47A8:Hades starts using Shadow Spread/,
      alertText: {
        en: 'Protean',
        de: 'Himmelsrichtungen',
        fr: 'Position',
        ja: '散開',
      },
    },
    {
      id: 'HadesEx Ravenous Assault',
      regex: / 14:47A6:Hades starts using Ravenous Assault on (\y{Name})\./,
      alertText: function(data, matches) {
        if (matches[1] == data.me) {
          return {
            en: 'Tank Buster on YOU',
            de: 'Tankbuster auf DIR',
            fr: 'Tankbuster sur VOUS',
          };
        }
        if (data.role == 'healer') {
          return {
            en: 'Buster on ' + data.ShortName(matches[1]),
            de: 'Tankbuster auf ' + data.ShortName(matches[1]),
            fr: 'Tankbuster sur ' + data.ShortName(matches[1]),
          };
        }
      },
      infoText: function(data, matches) {
        if (matches[1] == data.me || data.role != 'tank')
          return;

        return {
          en: 'Buster on ' + data.ShortName(matches[1]),
          de: 'Tankbuster auf ' + data.ShortName(matches[1]),
          fr: 'Tankbuster sur ' + data.ShortName(matches[1]),
        };
      },
    },
    {
      id: 'HadesEx Bad Faith Left 1',
      regex: / 14:47AB:Hades starts using Bad Faith/,
      infoText: {
        en: 'Left',
        de: 'Links',
        fr: 'Gauche',
        cn: '左',
      },
    },
    {
      id: 'HadesEx Bad Faith Left 2',
      regex: / 14:47AB:Hades starts using Bad Faith/,
      delaySeconds: 5,
      infoText: {
        en: 'Then Right',
        de: 'Links',
        fr: 'Gauche',
        cn: '左',
      },
    },
    {
      id: 'HadesEx Bad Faith Right 1',
      regex: / 14:47AC:Hades starts using Bad Faith/,
      infoText: {
        en: 'Right',
        de: 'Rechts',
        fr: 'Droite',
        cn: '右',
      },
    },
    {
      id: 'HadesEx Bad Faith Right 2',
      regex: / 14:47AC:Hades starts using Bad Faith/,
      delaySeconds: 5,
      infoText: {
        en: 'Then Left',
        de: 'Links',
        fr: 'Gauche',
        cn: '左',
      },
    },
    {
      id: 'HadesEx Quake III',
      regex: / 14:47B8:Nabriales's Shade starts using Quake III/,
      condition: function(data) {
        return data.role == 'tank' || data.role == 'healer';
      },
      infoText: {
        en: 'aoe',
      },
    },
    {
      id: 'HadesEx Dark II Tether',
      regex: / 23:\y{ObjectId}:Shadow of the Ancients:\y{ObjectId}:(\y{Name}):....:....:0011:/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Point Tether Out',
      },
    },
    {
      id: 'HadesEx Ancient Water 3',
      regex: / 1B:\y{ObjectId}:(\y{Name}):....:....:003E:/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      infoText: {
        en: 'Healer Stacks',
      },
      run: function(data, matches) {
        data.waterDarkMarker = true;
      },
    },
    {
      id: 'HadesEx Ancient Darkness',
      regex: / 1B:\y{ObjectId}:(\y{Name}):....:....:0060:/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Tank Spread',
      },
      run: function(data, matches) {
        data.waterDarkMarker = true;
      },
    },
    {
      id: 'HadesEx Ancient Water Unmarked',
      regex: / 1B:\y{ObjectId}:\y{Name}:....:....:(?:0030|0060):/,
      delaySeconds: 0.5,
      suppressSeconds: 5,
      infoText: function(data) {
        if (data.waterDarkMarker)
          return;
        return {
          en: 'Healer Stacks',
        };
      },
    },
    {
      id: 'HadesEx Shades Too Close',
      regex: / 23:\y{ObjectId}:(?:Igeyorhm's Shade|Lahabrea's Shade):\y{ObjectId}:(?:Igeyorhm's Shade|Lahabrea's Shade):....:....:000E:/,
      suppressSeconds: 10,
      condition: function(data) {
        return data.role == 'tank';
      },
      alarmText: {
        en: 'Move Shades Apart',
      },
    },
    {
      id: 'HadesEx Spheres',
      regex: / 14:47BD:Igeyorhm's Shade starts using Blizzard Sphere/,
      condition: function(data) {
        return data.role == 'tank';
      },
      infoText: function(data) {
        if (!data.sphereCount)
          return;
        return {
          en: 'tank swap soon',
        };
      },
      run: function(data) {
        data.sphereCount = (data.sphereCount || 0) + 1;
      },
    },
    {
      // TODO: maybe this should say "switch" the second time?
      id: 'HadesEx Burning Brand',
      regex: / 1A:\y{ObjectId}:(\y{Name}) gains the effect of Burning Brand/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Attack Igeyorhm',
      },
      run: function(data) {
        data.brand = 'fire';
      },
    },
    {
      id: 'HadesEx Freezing Brand',
      regex: / 1A:\y{ObjectId}:(\y{Name}) gains the effect of Freezing Brand/,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Attack Lahabrea',
      },
      run: function(data) {
        data.brand = 'ice';
      },
    },
    {
      id: 'HadesEx Blizzard IV',
      regex: / 14:47C3:Igeyorhm's Shade starts using Blizzard IV on (\y{Name})\./,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Tank Buster on YOU',
        de: 'Tankbuster auf DIR',
        fr: 'Tankbuster sur VOUS',
      },
    },
    {
      id: 'HadesEx Fire IV',
      regex: / 14:47C2:Lahabrea's Shade starts using Fire IV on (\y{Name})\./,
      condition: function(data, matches) {
        return data.me == matches[1];
      },
      alertText: {
        en: 'Tank Buster on YOU',
        de: 'Tankbuster auf DIR',
        fr: 'Tankbuster sur VOUS',
      },
    },
    {
      id: 'HadesEx Healers Blizzard/Fire IV',
      regex: / (?:14:47C3:Igeyorhm's Shade|14:47C2:Lahabrea's Shade)/,
      suppressSeconds: 5,
      alertText: {
        en: 'Tank Busters',
      },
    },
    {
      id: 'HadesEx Doom',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Doom/,
      suppressSeconds: 5,
      condition: function(data) {
        return data.role == 'healer';
      },
      alertText: {
        en: 'Heal T/H to Full',
      },
    },
    {
      id: 'HadesEx Shriek',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Cursed Shriek from (.*?) for (\y{Float}) Seconds\./,
      delaySeconds: function(data, matches) {
        return parseFloat(matches[1]) - 2;
      },
      alarmText: {
        en: 'Look Away',
      },
    },
    {
      id: 'HadesEx Beyond Death',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Beyond Death/,
      durationSeconds: 8,
      alertText: {
        en: 'Get Killed',
      },
    },
    {
      id: 'HadesEx Ancient Circle',
      regex: / 1A:\y{ObjectId}:\y{Name} gains the effect of Ancient Circle from (.*?) for (\y{Float}) Seconds\./,
      delaySeconds: function(data, matches) {
        return parseFloat(matches[1]) - 5;
      },
      infoText: {
        en: 'Donut on YOU',
      },
    },
  ],
}];
<|endoftext|>


<commit_before>//
// Copyright (C) 2012 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.

import FilesystemObject from './FilesystemObject.coffee'
import identityMapMixin from 'backbone-identity-map'
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
import FilesCollection from '../collections/FilesCollection'
import natcompare from '@canvas/util/natcompare'
import urlHelper from 'url-encoding'

// `full_name` will be something like "course files/some folder/another".
// For routing in the react app in the browser, we want something that will take that "course files"
// out. because urls will end up being /courses/2/files/folder/some folder/another
const EVERYTHING_BEFORE_THE_FIRST_SLASH = /^[^\/]+\/?/
let filesEnv = null

function getSortProp(model, sortProp) {
  // if we are sorting by name use 'display_name' for files and 'name' for folders.
  if (sortProp === 'name' && !(model instanceof Folder)) {
    return model.get('display_name')
  } else if (sortProp === 'user') {
    return __guard__(model.get('user'), x => x.display_name) || ''
  } else if (sortProp === 'usage_rights') {
    return __guard__(model.get('usage_rights'), x1 => x1.license_name) || ''
  } else {
    return model.get(sortProp)
  }
}

class __Folder extends FilesystemObject {
  initialize(options) {
    if (!this.contentTypes) this.contentTypes = options != null ? options.contentTypes : undefined
    if (!this.useVerifiers) this.useVerifiers = options != null ? options.useVerifiers : undefined
    this.setUpFilesAndFoldersIfNeeded()
    this.on('change:sort change:order', this.setQueryStringParams)
    return super.initialize(...arguments)
  }

  url() {
    if (this.isNew()) {
      return super.url(...arguments)
    } else {
      return `/api/v1/folders/${this.id}`
    }
  }

  parse(response) {
    const json = super.parse(...arguments)
    if (!this.contentTypes) this.contentTypes = response.contentTypes
    if (!this.useVerifiers) this.useVerifiers = response.useVerifiers
    this.setUpFilesAndFoldersIfNeeded()

    this.folders.url = response.folders_url
    this.files.url = response.files_url

    return json
  }

  setUpFilesAndFoldersIfNeeded() {
    if (!this.folders) {
      this.folders = new FoldersCollection([], {parentFolder: this})
    }
    if (!this.files) {
      return (this.files = new FilesCollection([], {parentFolder: this}))
    }
  }

  getSubtrees() {
    return this.folders
  }

  getItems() {
    return this.files
  }

  expand(force = false, options = {}) {
    let fetchDfd
    this.isExpanded = true
    this.trigger('expanded')
    if (this.expandDfd || force) {
      return $.when()
    }
    this.isExpanding = true
    this.trigger('beginexpanding')
    this.expandDfd = $.Deferred().done(() => {
      this.isExpanding = false
      return this.trigger('endexpanding')
    })

    const selfHasntBeenFetched =
      this.folders.url === this.folders.constructor.prototype.url ||
      this.files.url === this.files.constructor.prototype.url
    if (selfHasntBeenFetched || force) {
      fetchDfd = this.fetch()
    }
    return $.when(fetchDfd).done(() => {
      let filesDfd, foldersDfd
      if (this.get('folders_count') !== 0) {
        foldersDfd = this.folders.fetch()
      }
      if (this.get('files_count') !== 0 && !options.onlyShowSubtrees) {
        filesDfd = this.files.fetch()
      }
      return $.when(foldersDfd, filesDfd).done(this.expandDfd.resolve)
    })
  }

  collapse() {
    this.isExpanded = false
    return this.trigger('collapsed')
  }

  toggle(options) {
    if (this.isExpanded) {
      return this.collapse()
    } else {
      return this.expand(false, options)
    }
  }

  previewUrl() {
    let needle
    if (((needle = this.get('context_type')), ['Course', 'Group'].includes(needle))) {
      return `/${`${this.get('context_type').toLowerCase()}s`}/${this.get(
        'context_id'
      )}/files/{{id}}/preview`
    }
  }

  isEmpty() {
    return (
      !!(this.files.loadedAll && this.files.length === 0) &&
      (this.folders.loadedAll && this.folders.length === 0)
    )
  }

  urlPath() {
    let relativePath = (this.get('full_name') || '').replace(EVERYTHING_BEFORE_THE_FIRST_SLASH, '')
    relativePath = urlHelper.encodeSpecialChars(relativePath)
    relativePath = relativePath
      .split('/')
      .map(component => encodeURIComponent(component))
      .join('/')

    if (!filesEnv) filesEnv = require('../../react/modules/filesEnv').default // circular dep

    // when we are viewing all files we need to pad the context_asset_string on the front of the url
    // so it would be something like /files/folder/users_1/some/sub/folder
    if (filesEnv.showingAllContexts) {
      const assetString = `${__guard__(this.get('context_type'), x => x.toLowerCase())}s_${this.get(
        'context_id'
      )}`
      relativePath = `${assetString}/${relativePath}`
    }

    return relativePath
  }

  // #
  // Special sorter for handling sorting with special properties
  // It's been enhanced to sort naturally when certain sortProps
  // are used.
  childrenSorter(sortProp = 'name', sortOrder = 'asc', a, b) {
    // Only use natural mode for instances we expect strings in.
    let res
    const naturalMode = ['name', 'user', 'usage_rights'].includes(sortProp)

    // Get actual values for the properties we are sorting by.
    a = getSortProp(a, sortProp)
    b = getSortProp(b, sortProp)

    if (naturalMode) {
      res = natcompare.strings(a, b)
    } else {
      res = (() => {
        if (a === b) {
          return 0
        } else if (a > b || a === undefined) {
          return 1
        } else if (a < b || b === undefined) {
          return -1
        } else {
          throw new Error('wat? error sorting')
        }
      })()
    }

    if (sortOrder === 'desc') {
      res = 0 - res
    }
    return res
  }

  children({sort, order}) {
    return this.folders
      .toArray()
      .concat(this.files.toArray())
      .sort(this.childrenSorter.bind(null, sort, order))
  }
}
__Folder.resolvePath = function(contextType, contextId, folderPath) {
  folderPath = urlHelper.decodeSpecialChars(folderPath)

  const url = `/api/v1/${contextType}/${contextId}/folders/by_path${folderPath}`
  return $.getJSON(url).pipe(folders =>
    folders.map(folderAttrs => new Folder(folderAttrs, {parse: true}))
  )
}

__Folder.prototype.defaults = {name: ''}

const Folder = identityMapMixin(__Folder)
export default Folder

class FoldersCollection extends PaginatedCollection {
  static initClass() {
    this.optionProperty('parentFolder')
    this.prototype.model = Folder
  }

  parse(response) {
    if (response) {
      response.forEach(folder => {
        folder.contentTypes = this.parentFolder.contentTypes
        return (folder.useVerifiers = this.parentFolder.useVerifiers)
      })
    }
    return super.parse(...arguments)
  }
}
FoldersCollection.initClass()

// FoldersCollection is defined inside of this file, and not where it
// should be, because RequireJS sucks at figuring out circular dependencies.
// '../collections/FoldersCollection' just grabs this and re-exports it.
Folder.FoldersCollection = FoldersCollection

function __guard__(value, transform) {
  return typeof value !== 'undefined' && value !== null ? transform(value) : undefined
}
<commit_msg>treesame commit of origin/stable/2022-02-16<commit_after>//
// Copyright (C) 2012 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.

import FilesystemObject from './FilesystemObject.coffee'
import identityMapMixin from 'backbone-identity-map'
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
import FilesCollection from '../collections/FilesCollection'
import natcompare from '@canvas/util/natcompare'
import urlHelper from 'url-encoding'

// `full_name` will be something like "course files/some folder/another".
// For routing in the react app in the browser, we want something that will take that "course files"
// out. because urls will end up being /courses/2/files/folder/some folder/another
const EVERYTHING_BEFORE_THE_FIRST_SLASH = /^[^\/]+\/?/
let filesEnv = null

function getSortProp(model, sortProp) {
  // if we are sorting by name use 'display_name' for files and 'name' for folders.
  if (sortProp === 'name' && !(model instanceof Folder)) {
    return model.get('display_name')
  } else if (sortProp === 'user') {
    return __guard__(model.get('user'), x => x.display_name) || ''
  } else if (sortProp === 'usage_rights') {
    return __guard__(model.get('usage_rights'), x1 => x1.license_name) || ''
  } else {
    return model.get(sortProp)
  }
}

class __Folder extends FilesystemObject {
  initialize(options) {
    if (!this.contentTypes) this.contentTypes = options != null ? options.contentTypes : undefined
    if (!this.useVerifiers) this.useVerifiers = options != null ? options.useVerifiers : undefined
    this.setUpFilesAndFoldersIfNeeded()
    this.on('change:sort change:order', this.setQueryStringParams)
    return super.initialize(...arguments)
  }

  url() {
    if (this.isNew()) {
      return super.url(...arguments)
    } else {
      return `/api/v1/folders/${this.id}`
    }
  }

  parse(response) {
    const json = super.parse(...arguments)
    if (!this.contentTypes) this.contentTypes = response.contentTypes
    if (!this.useVerifiers) this.useVerifiers = response.useVerifiers
    this.setUpFilesAndFoldersIfNeeded()

    this.folders.url = response.folders_url
    this.files.url = response.files_url

    return json
  }

  setUpFilesAndFoldersIfNeeded() {
    if (!this.folders) {
      this.folders = new FoldersCollection([], {parentFolder: this})
    }
    if (!this.files) {
      return (this.files = new FilesCollection([], {parentFolder: this}))
    }
  }

  getSubtrees() {
    return this.folders
  }

  getItems() {
    return this.files
  }

  expand(force = false, options = {}) {
    let fetchDfd
    this.isExpanded = true
    this.trigger('expanded')
    if (this.expandDfd || force) {
      return $.when()
    }
    this.isExpanding = true
    this.trigger('beginexpanding')
    this.expandDfd = $.Deferred().done(() => {
      this.isExpanding = false
      return this.trigger('endexpanding')
    })

    const selfHasntBeenFetched =
      this.folders.url === this.folders.constructor.prototype.url ||
      this.files.url === this.files.constructor.prototype.url
    if (selfHasntBeenFetched || force) {
      fetchDfd = this.fetch()
    }
    return $.when(fetchDfd).done(() => {
      let filesDfd, foldersDfd
      if (this.get('folders_count') !== 0) {
        foldersDfd = this.folders.fetch({data: {per_page: this.get('folders_count')}})
      }
      if (this.get('files_count') !== 0 && !options.onlyShowSubtrees) {
        filesDfd = this.files.fetch()
      }
      return $.when(foldersDfd, filesDfd).done(this.expandDfd.resolve)
    })
  }

  collapse() {
    this.isExpanded = false
    return this.trigger('collapsed')
  }

  toggle(options) {
    if (this.isExpanded) {
      return this.collapse()
    } else {
      return this.expand(false, options)
    }
  }

  previewUrl() {
    let needle
    if (((needle = this.get('context_type')), ['Course', 'Group'].includes(needle))) {
      return `/${`${this.get('context_type').toLowerCase()}s`}/${this.get(
        'context_id'
      )}/files/{{id}}/preview`
    }
  }

  isEmpty() {
    return (
      !!(this.files.loadedAll && this.files.length === 0) &&
      this.folders.loadedAll &&
      this.folders.length === 0
    )
  }

  urlPath() {
    let relativePath = (this.get('full_name') || '').replace(EVERYTHING_BEFORE_THE_FIRST_SLASH, '')
    relativePath = urlHelper.encodeSpecialChars(relativePath)
    relativePath = relativePath
      .split('/')
      .map(component => encodeURIComponent(component))
      .join('/')

    if (!filesEnv) filesEnv = require('../../react/modules/filesEnv').default // circular dep

    // when we are viewing all files we need to pad the context_asset_string on the front of the url
    // so it would be something like /files/folder/users_1/some/sub/folder
    if (filesEnv.showingAllContexts) {
      const assetString = `${__guard__(this.get('context_type'), x => x.toLowerCase())}s_${this.get(
        'context_id'
      )}`
      relativePath = `${assetString}/${relativePath}`
    }

    return relativePath
  }

  // #
  // Special sorter for handling sorting with special properties
  // It's been enhanced to sort naturally when certain sortProps
  // are used.
  childrenSorter(sortProp = 'name', sortOrder = 'asc', a, b) {
    // Only use natural mode for instances we expect strings in.
    let res
    const naturalMode = ['name', 'user', 'usage_rights'].includes(sortProp)

    // Get actual values for the properties we are sorting by.
    a = getSortProp(a, sortProp)
    b = getSortProp(b, sortProp)

    if (naturalMode) {
      res = natcompare.strings(a, b)
    } else {
      res = (() => {
        if (a === b) {
          return 0
        } else if (a > b || a === undefined) {
          return 1
        } else if (a < b || b === undefined) {
          return -1
        } else {
          throw new Error('wat? error sorting')
        }
      })()
    }

    if (sortOrder === 'desc') {
      res = 0 - res
    }
    return res
  }

  children({sort, order}) {
    return this.folders
      .toArray()
      .concat(this.files.toArray())
      .sort(this.childrenSorter.bind(null, sort, order))
  }
}
__Folder.resolvePath = function (contextType, contextId, folderPath) {
  folderPath = urlHelper.decodeSpecialChars(folderPath)

  const url = `/api/v1/${contextType}/${contextId}/folders/by_path${folderPath}`
  return $.getJSON(url).pipe(folders =>
    folders.map(folderAttrs => new Folder(folderAttrs, {parse: true}))
  )
}

__Folder.prototype.defaults = {name: ''}

const Folder = identityMapMixin(__Folder)
export default Folder

class FoldersCollection extends PaginatedCollection {
  static initClass() {
    this.optionProperty('parentFolder')
    this.prototype.model = Folder
  }

  parse(response) {
    if (response) {
      response.forEach(folder => {
        folder.contentTypes = this.parentFolder.contentTypes
        return (folder.useVerifiers = this.parentFolder.useVerifiers)
      })
    }
    return super.parse(...arguments)
  }
}
FoldersCollection.initClass()

// FoldersCollection is defined inside of this file, and not where it
// should be, because RequireJS sucks at figuring out circular dependencies.
// '../collections/FoldersCollection' just grabs this and re-exports it.
Folder.FoldersCollection = FoldersCollection

function __guard__(value, transform) {
  return typeof value !== 'undefined' && value !== null ? transform(value) : undefined
}
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import classnames from 'classnames'

import TemplateControlBar from 'src/dashboards/components/TemplateControlBar'
import LayoutRenderer from 'shared/components/LayoutRenderer'
import FancyScrollbar from 'shared/components/FancyScrollbar'

const Dashboard = ({
  source,
  dashboard,
  onAddCell,
  onEditCell,
  timeRange,
  autoRefresh,
  onRenameCell,
  onUpdateCell,
  onDeleteCell,
  synchronizer,
  onPositionChange,
  inPresentationMode,
  onOpenTemplateManager,
  templatesIncludingDashTime,
  onSummonOverlayTechnologies,
  onSelectTemplate,
  showTemplateControlBar,
}) => {
  const cells = dashboard.cells.map(cell => {
    const dashboardCell = {...cell}
    dashboardCell.queries = dashboardCell.queries.map(
      ({label, query, queryConfig, db}) => ({
        label,
        query,
        queryConfig,
        db,
        database: db,
        text: query,
      })
    )
    return dashboardCell
  })

  return (
    <FancyScrollbar
      className={classnames('page-contents', {
        'presentation-mode': inPresentationMode,
      })}
    >

        {inPresentationMode
          ? null
          : <TemplateControlBar
              dashboard={dashboard.templates}
              onSelectTemplate={onSelectTemplate}
              onOpenTemplateManager={onOpenTemplateManager}
              isOpen={showTemplateControlBar}
            />}
        {cells.length
          ? <LayoutRenderer
              templates={templatesIncludingDashTime}
              cells={cells}
              timeRange={timeRange}
              autoRefresh={autoRefresh}
              source={source}
              onPositionChange={onPositionChange}
              onEditCell={onEditCell}
              onRenameCell={onRenameCell}
              onUpdateCell={onUpdateCell}
              onDeleteCell={onDeleteCell}
              onSummonOverlayTechnologies={onSummonOverlayTechnologies}
              synchronizer={synchronizer}
            />
          :

This Dashboard has no Graphs

              <button className="btn btn-primary btn-m" onClick={onAddCell}>
                Add Graph
              </button>

}

    </FancyScrollbar>
  )
}

const {arrayOf, bool, func, shape, string, number} = PropTypes

Dashboard.propTypes = {
  dashboard: shape({
    templates: arrayOf(
      shape({
        type: string.isRequired,
        tempVar: string.isRequired,
        query: shape({
          db: string,
          rp: string,
          influxql: string,
        }),
        values: arrayOf(
          shape({
            type: string.isRequired,
            value: string.isRequired,
            selected: bool,
          })
        ).isRequired,
      })
    ).isRequired,
  }),
  templatesIncludingDashTime: arrayOf(shape()).isRequired,
  inPresentationMode: bool,
  onAddCell: func,
  onPositionChange: func,
  onEditCell: func,
  onRenameCell: func,
  onUpdateCell: func,
  onDeleteCell: func,
  onSummonOverlayTechnologies: func,
  synchronizer: func,
  source: shape({
    links: shape({
      proxy: string,
    }).isRequired,
  }).isRequired,
  autoRefresh: number.isRequired,
  timeRange: shape({}).isRequired,
  onOpenTemplateManager: func.isRequired,
  onSelectTemplate: func.isRequired,
  updateTempVarValues: func.isRequired,
  showTemplateControlBar: bool,
}

export default Dashboard
<commit_msg>Cleanup<commit_after>import React, {PropTypes} from 'react'
import classnames from 'classnames'

import TemplateControlBar from 'src/dashboards/components/TemplateControlBar'
import LayoutRenderer from 'shared/components/LayoutRenderer'
import FancyScrollbar from 'shared/components/FancyScrollbar'

const Dashboard = ({
  source,
  dashboard,
  onAddCell,
  onEditCell,
  timeRange,
  autoRefresh,
  onRenameCell,
  onUpdateCell,
  onDeleteCell,
  synchronizer,
  onPositionChange,
  inPresentationMode,
  onOpenTemplateManager,
  templatesIncludingDashTime,
  onSummonOverlayTechnologies,
  onSelectTemplate,
  showTemplateControlBar,
}) => {
  const cells = dashboard.cells.map(cell => {
    const dashboardCell = {...cell}
    dashboardCell.queries = dashboardCell.queries.map(
      ({label, query, queryConfig, db}) => ({
        label,
        query,
        queryConfig,
        db,
        database: db,
        text: query,
      })
    )
    return dashboardCell
  })

  return (
    <FancyScrollbar
      className={classnames('page-contents', {
        'presentation-mode': inPresentationMode,
      })}
    >

        {inPresentationMode
          ? null
          : <TemplateControlBar
              templates={dashboard.templates}
              onSelectTemplate={onSelectTemplate}
              onOpenTemplateManager={onOpenTemplateManager}
              isOpen={showTemplateControlBar}
            />}
        {cells.length
          ? <LayoutRenderer
              templates={templatesIncludingDashTime}
              cells={cells}
              timeRange={timeRange}
              autoRefresh={autoRefresh}
              source={source}
              onPositionChange={onPositionChange}
              onEditCell={onEditCell}
              onRenameCell={onRenameCell}
              onUpdateCell={onUpdateCell}
              onDeleteCell={onDeleteCell}
              onSummonOverlayTechnologies={onSummonOverlayTechnologies}
              synchronizer={synchronizer}
            />
          :

This Dashboard has no Graphs

              <button className="btn btn-primary btn-m" onClick={onAddCell}>
                Add Graph
              </button>

}

    </FancyScrollbar>
  )
}

const {arrayOf, bool, func, shape, string, number} = PropTypes

Dashboard.propTypes = {
  dashboard: shape({
    templates: arrayOf(
      shape({
        type: string.isRequired,
        tempVar: string.isRequired,
        query: shape({
          db: string,
          rp: string,
          influxql: string,
        }),
        values: arrayOf(
          shape({
            type: string.isRequired,
            value: string.isRequired,
            selected: bool,
          })
        ).isRequired,
      })
    ).isRequired,
  }),
  templatesIncludingDashTime: arrayOf(shape()).isRequired,
  inPresentationMode: bool,
  onAddCell: func,
  onPositionChange: func,
  onEditCell: func,
  onRenameCell: func,
  onUpdateCell: func,
  onDeleteCell: func,
  onSummonOverlayTechnologies: func,
  synchronizer: func,
  source: shape({
    links: shape({
      proxy: string,
    }).isRequired,
  }).isRequired,
  autoRefresh: number.isRequired,
  timeRange: shape({}).isRequired,
  onOpenTemplateManager: func.isRequired,
  onSelectTemplate: func.isRequired,
  showTemplateControlBar: bool,
}

export default Dashboard
<|endoftext|>


<commit_before>import { OHIF } from 'meteor/ohif:core';
import { $ } from 'meteor/jquery';
import { cornerstone, cornerstoneMath, cornerstoneTools } from 'meteor/ohif:cornerstone';

window.resolve = [];

OHIF.measurements.getImageDataUrl = ({
    imageType='image/jpeg',
    quality=1,
    width=512,
    height=512,
    cacheImage=true,
    imagePath,
    measurement,
    alwaysVisibleText=true
}) => {
    imagePath = imagePath || measurement.imagePath;
    const imageId = OHIF.viewerbase.getImageIdForImagePath(imagePath);

    // Create a deep copy of the measurement to prevent changing its original properties
    if (measurement) {
        measurement = $.extend(true, {}, measurement);
    }

    return new Promise((resolve, reject) => {
        window.resolve.push(resolve);
        const loadMethod = cacheImage ? 'loadAndCacheImage' : 'loadImage';
        cornerstone[loadMethod](imageId).then(image => {
            // Create a cornerstone enabled element to handle the image
            const enabledElement = createEnabledElement(width, height);
            const element = enabledElement.element;

            // Display the image on cornerstone's canvas
            cornerstone.displayImage(element, image);

            // Add the measurement state and enable the tool if a measurement was given
            if (measurement) {
                const state = Object.assign({}, measurement, { active: true });
                cornerstoneTools.addToolState(element, measurement.toolType, state);
                cornerstoneTools[measurement.toolType].enable(element);
            }

            // Resolve the current promise giving the dataUrl as parameter
            const renderedCallback = () => {
                const dataUrl = enabledElement.canvas.toDataURL(imageType, quality);

                // Disable the tool and clear the measurement state if a measurement was given
                if (measurement) {
                    cornerstoneTools[measurement.toolType].disable(element);
                    cornerstoneTools.clearToolState(element, measurement.toolType);
                }

                // Destroy the cornerstone enabled element, removing it from the DOM
                destroyEnabledElement(enabledElement);

                // Resolve the promise with the image's data URL
                resolve(dataUrl);
            };

            // Wait for image rendering to get its data URL
            $(element).one('CornerstoneImageRendered', () => {
                if (measurement && alwaysVisibleText) {
                    rearrangeTextBox(image, measurement, element).then(() => renderedCallback());
                } else {
                    renderedCallback();
                }
            });
        });
    });
};

const getPoint = (x, y) => {
    return {
        x,
        y
    };
};

const lineRectangleIntersection = (line, rect) => {
    let intersection;

    Object.keys(rect).forEach(side => {
        if (intersection) return;
        const rectSegment = rect[side];
        intersection = cornerstoneMath.lineSegment.intersectLine(line, rectSegment);
    });

    return intersection;
};

const rearrangeTextBox = (image, measurement, element) => new Promise((resolve, reject) => {
    const handles = measurement && measurement.handles;
    if (!handles) return resolve();
    const { textBox, start, end } = handles;
    if (!textBox || !textBox.boundingBox || !start || !end) return resolve();

    // Build the dashed line segment
    let dashedLine = new cornerstoneMath.Line3();
    const maxX = Math.max(start.x, end.x);
    const minX = Math.min(start.x, end.x);
    const maxY = Math.max(start.y, end.y);
    const minY = Math.min(start.y, end.y);
    dashedLine.start = getPoint(minX + ((maxX - minX) / 2), minY + ((maxY - minY) / 2));
    dashedLine.end = getPoint(textBox.x, textBox.y);

    // Build the bounding rectangle
    const x0 = (textBox.boundingBox.width / 2);
    const x1 = image.width - x0;
    const y0 = (textBox.boundingBox.height / 2);
    const y1 = image.height - y0;
    const topLeft = getPoint(x0, y0);
    const topRight = getPoint(x1, y0);
    const bottomLeft = getPoint(x0, y1);
    const bottomRight = getPoint(x1, y1);
    const boundingRect = {
        top: new cornerstoneMath.Line3(topLeft, topRight),
        left: new cornerstoneMath.Line3(topLeft, bottomLeft),
        right: new cornerstoneMath.Line3(topRight, bottomRight),
        bottom: new cornerstoneMath.Line3(bottomLeft, bottomRight)
    };

    // Check if the measurement center is outside the bounding rectangle
    const imageCenter = getPoint(image.width / 2, image.height / 2);
    const imageCenterToMeasurement = new cornerstoneMath.Line3();
    imageCenterToMeasurement.start = imageCenter;
    imageCenterToMeasurement.end = dashedLine.start;
    if (lineRectangleIntersection(imageCenterToMeasurement, boundingRect)) {
        dashedLine = new cornerstoneMath.Line3(imageCenter, dashedLine.end);
    }

    // Check if the text box is outside the image area
    const intersection = lineRectangleIntersection(dashedLine, boundingRect);
    if (intersection) {
        textBox.boundingBox.left = intersection.x - x0;
        textBox.boundingBox.top = intersection.y - y0;
        Object.assign(textBox, intersection);
        cornerstone.updateImage(element);
        $(element).one('CornerstoneImageRendered', () => resolve());
    } else {
        resolve();
    }
});

const createEnabledElement = (width, height) => {
    const $element = $('

').css({
        height,
        left: 0,
        position: 'fixed',
        top: 0,
        visibility: 'hidden',
        width,
        'z-index': -1
    });

    const element = $element[0];
    $element.appendTo(document.body);
    cornerstone.enable(element, { renderer: OHIF.cornerstone.renderer });

    const enabledElement = cornerstone.getEnabledElement(element);
    enabledElement.toolStateManager = cornerstoneTools.newImageIdSpecificToolStateManager();

    return enabledElement;
};

const destroyEnabledElement = enabledElement => {
    cornerstone.disable(enabledElement.element);
    $(enabledElement.element).remove();
};
<commit_msg>Adding option for viewport.voi on getImageDataUrl<commit_after>import { OHIF } from 'meteor/ohif:core';
import { $ } from 'meteor/jquery';
import { cornerstone, cornerstoneMath, cornerstoneTools } from 'meteor/ohif:cornerstone';

window.resolve = [];

OHIF.measurements.getImageDataUrl = ({
    imageType='image/jpeg',
    quality=1,
    width=512,
    height=512,
    cacheImage=true,
    imagePath,
    measurement,
    alwaysVisibleText=true,
    viewport
}) => {
    imagePath = imagePath || measurement.imagePath;
    const imageId = OHIF.viewerbase.getImageIdForImagePath(imagePath);

    // Create a deep copy of the measurement to prevent changing its original properties
    if (measurement) {
        measurement = $.extend(true, {}, measurement);
    }

    return new Promise((resolve, reject) => {
        window.resolve.push(resolve);
        const loadMethod = cacheImage ? 'loadAndCacheImage' : 'loadImage';
        cornerstone[loadMethod](imageId).then(image => {
            // Create a cornerstone enabled element to handle the image
            const enabledElement = createEnabledElement(width, height);
            const element = enabledElement.element;

            // Display the image on cornerstone's canvas
            cornerstone.displayImage(element, image);

            // Add the measurement state and enable the tool if a measurement was given
            if (measurement) {
                const state = Object.assign({}, measurement, { active: true });
                cornerstoneTools.addToolState(element, measurement.toolType, state);
                cornerstoneTools[measurement.toolType].enable(element);
            }

            // Set the viewport voi if present
            if (viewport && viewport.voi) {
                const csViewport = cornerstone.getViewport(element);
                Object.assign(csViewport, { voi: viewport.voi });
                cornerstone.setViewport(element, csViewport);
            }

            // Resolve the current promise giving the dataUrl as parameter
            const renderedCallback = () => {
                const dataUrl = enabledElement.canvas.toDataURL(imageType, quality);

                // Disable the tool and clear the measurement state if a measurement was given
                if (measurement) {
                    cornerstoneTools[measurement.toolType].disable(element);
                    cornerstoneTools.clearToolState(element, measurement.toolType);
                }

                // Destroy the cornerstone enabled element, removing it from the DOM
                destroyEnabledElement(enabledElement);

                // Resolve the promise with the image's data URL
                resolve(dataUrl);
            };

            // Wait for image rendering to get its data URL
            $(element).one('CornerstoneImageRendered', () => {
                if (measurement && alwaysVisibleText) {
                    rearrangeTextBox(image, measurement, element).then(() => renderedCallback());
                } else {
                    renderedCallback();
                }
            });
        });
    });
};

const getPoint = (x, y) => {
    return {
        x,
        y
    };
};

const lineRectangleIntersection = (line, rect) => {
    let intersection;

    Object.keys(rect).forEach(side => {
        if (intersection) return;
        const rectSegment = rect[side];
        intersection = cornerstoneMath.lineSegment.intersectLine(line, rectSegment);
    });

    return intersection;
};

const rearrangeTextBox = (image, measurement, element) => new Promise((resolve, reject) => {
    const handles = measurement && measurement.handles;
    if (!handles) return resolve();
    const { textBox, start, end } = handles;
    if (!textBox || !textBox.boundingBox || !start || !end) return resolve();

    // Build the dashed line segment
    let dashedLine = new cornerstoneMath.Line3();
    const maxX = Math.max(start.x, end.x);
    const minX = Math.min(start.x, end.x);
    const maxY = Math.max(start.y, end.y);
    const minY = Math.min(start.y, end.y);
    dashedLine.start = getPoint(minX + ((maxX - minX) / 2), minY + ((maxY - minY) / 2));
    dashedLine.end = getPoint(textBox.x, textBox.y);

    // Build the bounding rectangle
    const x0 = (textBox.boundingBox.width / 2);
    const x1 = image.width - x0;
    const y0 = (textBox.boundingBox.height / 2);
    const y1 = image.height - y0;
    const topLeft = getPoint(x0, y0);
    const topRight = getPoint(x1, y0);
    const bottomLeft = getPoint(x0, y1);
    const bottomRight = getPoint(x1, y1);
    const boundingRect = {
        top: new cornerstoneMath.Line3(topLeft, topRight),
        left: new cornerstoneMath.Line3(topLeft, bottomLeft),
        right: new cornerstoneMath.Line3(topRight, bottomRight),
        bottom: new cornerstoneMath.Line3(bottomLeft, bottomRight)
    };

    // Check if the measurement center is outside the bounding rectangle
    const imageCenter = getPoint(image.width / 2, image.height / 2);
    const imageCenterToMeasurement = new cornerstoneMath.Line3();
    imageCenterToMeasurement.start = imageCenter;
    imageCenterToMeasurement.end = dashedLine.start;
    if (lineRectangleIntersection(imageCenterToMeasurement, boundingRect)) {
        dashedLine = new cornerstoneMath.Line3(imageCenter, dashedLine.end);
    }

    // Check if the text box is outside the image area
    const intersection = lineRectangleIntersection(dashedLine, boundingRect);
    if (intersection) {
        textBox.boundingBox.left = intersection.x - x0;
        textBox.boundingBox.top = intersection.y - y0;
        Object.assign(textBox, intersection);
        cornerstone.updateImage(element);
        $(element).one('CornerstoneImageRendered', () => resolve());
    } else {
        resolve();
    }
});

const createEnabledElement = (width, height) => {
    const $element = $('

').css({
        height,
        left: 0,
        position: 'fixed',
        top: 0,
        visibility: 'hidden',
        width,
        'z-index': -1
    });

    const element = $element[0];
    $element.appendTo(document.body);
    cornerstone.enable(element, { renderer: OHIF.cornerstone.renderer });

    const enabledElement = cornerstone.getEnabledElement(element);
    enabledElement.toolStateManager = cornerstoneTools.newImageIdSpecificToolStateManager();

    return enabledElement;
};

const destroyEnabledElement = enabledElement => {
    cornerstone.disable(enabledElement.element);
    $(enabledElement.element).remove();
};
<|endoftext|>


<commit_before>dateUtil = {
    day: [
        "Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"
    ],
    month: [
        "Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec",
    ],
    padTime: function(time) {
       return ("00" + time).slice(-2);
    },
    getTime: function(date) {
        return this.padTime(date.getHours()) + ':' + this.padTime(date.getMinutes()) + ':' + this.padTime(date.getSeconds());
    },
    getDate: function(date) {
        return this.day[date.getDay()] + ' ' + (date.getUTCDay() + 1)+ ' ' + this.month[date.getUTCMonth()];
    }
};<commit_msg>Removed unnecessary comma<commit_after>dateUtil = {
    day: [
        "Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"
    ],
    month: [
        "Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"
    ],
    padTime: function(time) {
       return ("00" + time).slice(-2);
    },
    getTime: function(date) {
        return this.padTime(date.getHours()) + ':' + this.padTime(date.getMinutes()) + ':' + this.padTime(date.getSeconds());
    },
    getDate: function(date) {
        return this.day[date.getDay()] + ' ' + (date.getUTCDay() + 1)+ ' ' + this.month[date.getUTCMonth()];
    }
};<|endoftext|>


<commit_before>/*global define*/
define([
    '../../Core/Check',
    '../../Core/defined',
    '../../Core/defineProperties',
    '../../Core/destroyObject',
    '../../ThirdParty/knockout',
    '../getElement',
    '../CesiumInspector/Cesium3DTilesInspectorViewModel'
    ], function(
        Check,
        defined,
        defineProperties,
        destroyObject,
        knockout,
        getElement,
        Cesium3DTilesInspectorViewModel) {
    'use strict';

    /**
     * Inspector widget to aid in debugging 3D tiles
     *
     * @alias Cesium3DTilesInspector
     * @constructor
     *
     * @param {Element|String} container The DOM element or ID that will contain the widget.
     * @param {Scene} scene the Scene instance to use.
     *
     * @exception {DeveloperError} container is required.
     * @exception {DeveloperError} scene is required.
     */
    function Cesium3DTilesInspector(container, scene) {
        //>includeStart('debug', pragmas.debug);
        Check.defined(container, 'container');
        Check.typeOf.object(scene, 'scene');
        //>>includeEnd('debug');

        container = getElement(container);
        var that = this;
        var viewModel = new Cesium3DTilesInspectorViewModel(scene);
        this._viewModel = viewModel;
        this._container = container;

        this._inspectorModel = {
            inspectorVisible: true,
            tilesetVisible: false,
            displayVisible: false,
            updateVisible: false,
            loggingVisible: false,
            styleVisible: false,
            toggleInspector: function() {
                that._inspectorModel.inspectorVisible = !that._inspectorModel.inspectorVisible;
            },
            toggleTileset: function() {
                that._inspectorModel.tilesetVisible = !that._inspectorModel.tilesetVisible;
            },
            toggleDisplay: function() {
                that._inspectorModel.displayVisible = !that._inspectorModel.displayVisible;
            },
            toggleUpdate: function() {
                that._inspectorModel.updateVisible = !that._inspectorModel.updateVisible;
            },
            toggleLogging: function() {
                that._inspectorModel.loggingVisible = !that._inspectorModel.loggingVisible;
            },
            toggleStyle: function() {
                that._inspectorModel.styleVisible = !that._inspectorModel.styleVisible;
            }
        };

        knockout.track(this._inspectorModel, ['inspectorVisible', 'tilesetVisible', 'displayVisible', 'updateVisible', 'loggingVisible', 'styleVisible']);

        var element = document.createElement('div');
        this._element = element;
        var text = document.createElement('div');
        text.textContent = '3D Tiles Inspector';
        text.className = 'cesium-cesiumInspector-button';
        text.setAttribute('data-bind', 'click: toggleInspector');
        element.appendChild(text);
        element.className = 'cesium-cesiumInspector';
        element.setAttribute('data-bind', 'css: { "cesium-cesiumInspector-visible" : inspectorVisible, "cesium-cesiumInspector-hidden" : !inspectorVisible }');
        container.appendChild(this._element);

        var tilesetPanel = makeSection('Tileset', 'tilesetVisible', 'toggleTileset');
        var displayPanel = makeSection('Display', 'displayVisible', 'toggleDisplay');
        var updatePanel = makeSection('Update', 'updateVisible', 'toggleUpdate');
        var loggingPanel = makeSection('Logging', 'loggingVisible', 'toggleLogging');
        var stylePanel = makeSection('Style', 'styleVisible', 'toggleStyle');

        // first add and bind all the toggleable panels
        element.appendChild(tilesetPanel);
        element.appendChild(displayPanel);
        element.appendChild(updatePanel);
        element.appendChild(loggingPanel);
        element.appendChild(stylePanel);
        knockout.applyBindings(this._inspectorModel, element);

        // build and bind each panel separately
        var tilesetURL = document.createElement('div');
        tilesetURL.setAttribute('data-bind', 'html: tilesetURL');
        tilesetURL.setAttribute('style', 'word-break: break-all;');
        tilesetPanel.contents.appendChild(tilesetURL);
        tilesetPanel.contents.appendChild(makeButton('_togglePickTileset', 'Pick Tileset', '_pickActive'));
        tilesetPanel.contents.appendChild(makeButton('trimTilesCache', 'Trim Tiles Cache'));
        tilesetPanel.contents.appendChild(makeCheckbox('picking', 'Enable Picking'));
        knockout.applyBindings(viewModel, tilesetPanel.contents);

        displayPanel.contents.appendChild(makeCheckbox('colorize', 'Colorize'));
        displayPanel.contents.appendChild(makeCheckbox('wireframe', 'Wireframe'));
        displayPanel.contents.appendChild(makeCheckbox('showBoundingVolumes', 'Bounding Volumes'));
        displayPanel.contents.appendChild(makeCheckbox('showContentBoundingVolumes', 'Content Volumes'));
        displayPanel.contents.appendChild(makeCheckbox('showRequestVolumes', 'Request Volumes'));
        knockout.applyBindings(viewModel, displayPanel.contents);

        updatePanel.contents.appendChild(makeCheckbox('suspendUpdates', 'Suspend Updates'));
        updatePanel.contents.appendChild(makeCheckbox('dynamicSSE', 'Dynamic SSE'));
        var sseContainer = document.createElement('div');
        sseContainer.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : !dynamicSSE, "cesium-cesiumInspector-hide" : dynamicSSE}');
        sseContainer.appendChild(makeRangeInput('maximumSSE', 0, 128, 0.5, 'Maximum SSE'));
        updatePanel.contents.appendChild(sseContainer);

        var dynamicSSEContainer = document.createElement('div');
        dynamicSSEContainer.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : dynamicSSE, "cesium-cesiumInspector-hide" : !dynamicSSE}');
        dynamicSSEContainer.appendChild(makeRangeInput('dynamicSSEDensity', 0, 1, 0.01, 'SSE Density'));
        dynamicSSEContainer.appendChild(makeRangeInput('dynamicSSEFactor', 0, 10, 0.1, 'SSE Factor'));
        updatePanel.contents.appendChild(dynamicSSEContainer);
        knockout.applyBindings(viewModel, updatePanel.contents);

        loggingPanel.contents.appendChild(makeCheckbox('performance', 'Performance'));
        loggingPanel.contents.appendChild(this._viewModel._performanceDisplay._container);
        this._viewModel._performanceDisplay._container.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : performance, "cesium-cesiumInspector-hide" : !performance}');
        loggingPanel.contents.appendChild(makeCheckbox('showStats', 'Stats'));
        var stats = document.createElement('div');
        stats.setAttribute('data-bind', 'html: statsText, css: {"cesium-cesiumInspector-show" : showStats, "cesium-cesiumInspector-hide" : !showStats}');
        stats.setAttribute('style', 'font-size: 11px');
        loggingPanel.contents.appendChild(stats);
        loggingPanel.contents.appendChild(makeCheckbox('showPickStats', 'Pick Stats'));
        var pickStats = document.createElement('div');
        pickStats.setAttribute('data-bind', 'html: pickStatsText, css: {"cesium-cesiumInspector-show" : showPickStats, "cesium-cesiumInspector-hide" : !showPickStats}');
        pickStats.setAttribute('style', 'font-size: 11px');
        loggingPanel.contents.appendChild(pickStats);
        knockout.applyBindings(viewModel, loggingPanel.contents);

        var styleEditor = document.createElement('textarea');
        styleEditor.setAttribute('data-bind', 'value: styleString');
        stylePanel.contents.className = 'cesium-cesiumInspector-styleEditor';
        stylePanel.contents.appendChild(styleEditor);
        stylePanel.contents.appendChild(document.createElement('br'));
        var closeStylesBtn = makeButton('toggleStyle', 'Close');
        stylePanel.contents.appendChild(closeStylesBtn);
        knockout.applyBindings(this._inspectorModel, closeStylesBtn);
        knockout.applyBindings(viewModel, styleEditor);
    }

    defineProperties(Cesium3DTilesInspector.prototype, {
        /**
         * Gets the parent container.
         * @memberof Cesium3DTilesInspector.prototype
         *
         * @type {Element}
         */
        container : {
            get : function() {
                return this._container;
            }
        },

        /**
         * Gets the view model.
         * @memberof Cesium3DTilesInspector.prototype
         *
         * @type {Cesium3DTilesInspectorViewModel}
         */
        viewModel : {
            get : function() {
                return this._viewModel;
            }
        }
    });

    /**
     * Sets the callback function to call on tileset load
     *
     * @param {Function} callback the callback
     */
    Cesium3DTilesInspector.prototype.onLoad = function(callback) {
        this._onLoad = callback;
    };

    /**
     * Sets the callback function to call on tileset unload
     *
     * @param {Function} callback the callback
     */
    Cesium3DTilesInspector.prototype.onUnload = function(callback) {
        this._onUnload = callback;
    };

    /**
     * Sets the callback function to call on feature selection
     *
     * @param {Function} callback the callback
     */
    Cesium3DTilesInspector.prototype.onSelect = function(callback) {
        this._onSelect = callback;
    };

    /**
     * @returns {Boolean} true if the object has been destroyed, false otherwise.
     */
    Cesium3DTilesInspector.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the widget.  Should be called if permanently
     * removing the widget from layout.
     */
    Cesium3DTilesInspector.prototype.destroy = function() {
        knockout.cleanNode(this._element);
        this._container.removeChild(this._element);
        this.viewModel.destroy();

        return destroyObject(this);
    };

    function makeSection(name, visibleProp, toggleProp) {
        var panel = document.createElement('div');
        panel.className = 'cesium-cesiumInspector-dropDown';

        var header = document.createElement('div');
        header.className = 'cesium-cesiumInspector-sectionHeader';
        var toggle = document.createElement('span');
        toggle.className = 'cesium-cesiumInspector-toggleSwitch';

        var bindings = [];
        if (defined(toggleProp)) {
            if (defined(visibleProp)) {
                bindings.push('text: ' + visibleProp + ' ? "-" : "+"');
            }
            bindings.push('click: ' + toggleProp);
        }

        toggle.setAttribute('data-bind', bindings.join(', '));
        header.appendChild(toggle);
        header.appendChild(document.createTextNode(name));

        var section = document.createElement('div');
        section.className = 'cesium-cesiumInspector-section';
        if (defined(visibleProp)) {
            section.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : ' + visibleProp + ', "cesium-cesiumInspector-hide" : !' + visibleProp + '}');
        }

        panel.appendChild(header);
        panel.appendChild(section);

        var contents = document.createElement('div');
        section.appendChild(contents);
        panel.contents = contents;
        return panel;
    }

    function makeCheckbox(property, text, enable) {
        var container = document.createElement('div');
        var checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        var binding = 'checked: ' + property;
        if (defined(enable)) {
            binding += ', enable: ' + enable;
        }
        checkbox.setAttribute('data-bind', binding);
        container.appendChild(checkbox);
        container.appendChild(document.createTextNode(text));
        return container;
    }

    function makeRangeInput(property, min, max, step, text) {
        var container = document.createElement('div');
        container.className = 'cesium-cesiumInspector-slider';
        var input = document.createElement('input');
        input.setAttribute('data-bind', 'value: ' + property);
        input.type = 'number';

        var slider = document.createElement('input');
        slider.type = 'range';
        slider.min = min;
        slider.max = max;
        slider.step = step;
        slider.setAttribute('data-bind', 'value: ' + property);

        container.appendChild(document.createTextNode(text));
        var wrapper = document.createElement('div');
        wrapper.appendChild(input);
        wrapper.appendChild(slider);
        container.appendChild(wrapper);

        return container;
    }

    function makeButton(action, text, active) {
        var button = document.createElement('input');
        button.type = 'button';
        button.value = text;
        button.className = 'cesium-cesiumInspector-pickButton';
        var binding = 'click: ' + action;
        if (defined(active)) {
            binding += ', css: {"cesium-cesiumInspector-pickButtonHighlight" : ' + active + '}';
        }
        button.setAttribute('data-bind', binding);

        return button;
    }

    return Cesium3DTilesInspector;
});
<commit_msg>update sliders onchange<commit_after>/*global define*/
define([
    '../../Core/Check',
    '../../Core/defined',
    '../../Core/defineProperties',
    '../../Core/destroyObject',
    '../../ThirdParty/knockout',
    '../getElement',
    '../CesiumInspector/Cesium3DTilesInspectorViewModel'
    ], function(
        Check,
        defined,
        defineProperties,
        destroyObject,
        knockout,
        getElement,
        Cesium3DTilesInspectorViewModel) {
    'use strict';

    /**
     * Inspector widget to aid in debugging 3D tiles
     *
     * @alias Cesium3DTilesInspector
     * @constructor
     *
     * @param {Element|String} container The DOM element or ID that will contain the widget.
     * @param {Scene} scene the Scene instance to use.
     *
     * @exception {DeveloperError} container is required.
     * @exception {DeveloperError} scene is required.
     */
    function Cesium3DTilesInspector(container, scene) {
        //>includeStart('debug', pragmas.debug);
        Check.defined(container, 'container');
        Check.typeOf.object(scene, 'scene');
        //>>includeEnd('debug');

        container = getElement(container);
        var that = this;
        var viewModel = new Cesium3DTilesInspectorViewModel(scene);
        this._viewModel = viewModel;
        this._container = container;

        this._inspectorModel = {
            inspectorVisible: true,
            tilesetVisible: false,
            displayVisible: false,
            updateVisible: false,
            loggingVisible: false,
            styleVisible: false,
            toggleInspector: function() {
                that._inspectorModel.inspectorVisible = !that._inspectorModel.inspectorVisible;
            },
            toggleTileset: function() {
                that._inspectorModel.tilesetVisible = !that._inspectorModel.tilesetVisible;
            },
            toggleDisplay: function() {
                that._inspectorModel.displayVisible = !that._inspectorModel.displayVisible;
            },
            toggleUpdate: function() {
                that._inspectorModel.updateVisible = !that._inspectorModel.updateVisible;
            },
            toggleLogging: function() {
                that._inspectorModel.loggingVisible = !that._inspectorModel.loggingVisible;
            },
            toggleStyle: function() {
                that._inspectorModel.styleVisible = !that._inspectorModel.styleVisible;
            }
        };

        knockout.track(this._inspectorModel, ['inspectorVisible', 'tilesetVisible', 'displayVisible', 'updateVisible', 'loggingVisible', 'styleVisible']);

        var element = document.createElement('div');
        this._element = element;
        var text = document.createElement('div');
        text.textContent = '3D Tiles Inspector';
        text.className = 'cesium-cesiumInspector-button';
        text.setAttribute('data-bind', 'click: toggleInspector');
        element.appendChild(text);
        element.className = 'cesium-cesiumInspector';
        element.setAttribute('data-bind', 'css: { "cesium-cesiumInspector-visible" : inspectorVisible, "cesium-cesiumInspector-hidden" : !inspectorVisible }');
        container.appendChild(this._element);

        var tilesetPanel = makeSection('Tileset', 'tilesetVisible', 'toggleTileset');
        var displayPanel = makeSection('Display', 'displayVisible', 'toggleDisplay');
        var updatePanel = makeSection('Update', 'updateVisible', 'toggleUpdate');
        var loggingPanel = makeSection('Logging', 'loggingVisible', 'toggleLogging');
        var stylePanel = makeSection('Style', 'styleVisible', 'toggleStyle');

        // first add and bind all the toggleable panels
        element.appendChild(tilesetPanel);
        element.appendChild(displayPanel);
        element.appendChild(updatePanel);
        element.appendChild(loggingPanel);
        element.appendChild(stylePanel);
        knockout.applyBindings(this._inspectorModel, element);

        // build and bind each panel separately
        var tilesetURL = document.createElement('div');
        tilesetURL.setAttribute('data-bind', 'html: tilesetURL');
        tilesetURL.setAttribute('style', 'word-break: break-all;');
        tilesetPanel.contents.appendChild(tilesetURL);
        tilesetPanel.contents.appendChild(makeButton('_togglePickTileset', 'Pick Tileset', '_pickActive'));
        tilesetPanel.contents.appendChild(makeButton('trimTilesCache', 'Trim Tiles Cache'));
        tilesetPanel.contents.appendChild(makeCheckbox('picking', 'Enable Picking'));
        knockout.applyBindings(viewModel, tilesetPanel.contents);

        displayPanel.contents.appendChild(makeCheckbox('colorize', 'Colorize'));
        displayPanel.contents.appendChild(makeCheckbox('wireframe', 'Wireframe'));
        displayPanel.contents.appendChild(makeCheckbox('showBoundingVolumes', 'Bounding Volumes'));
        displayPanel.contents.appendChild(makeCheckbox('showContentBoundingVolumes', 'Content Volumes'));
        displayPanel.contents.appendChild(makeCheckbox('showRequestVolumes', 'Request Volumes'));
        knockout.applyBindings(viewModel, displayPanel.contents);

        updatePanel.contents.appendChild(makeCheckbox('suspendUpdates', 'Suspend Updates'));
        updatePanel.contents.appendChild(makeCheckbox('dynamicSSE', 'Dynamic SSE'));
        var sseContainer = document.createElement('div');
        sseContainer.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : !dynamicSSE, "cesium-cesiumInspector-hide" : dynamicSSE}');
        sseContainer.appendChild(makeRangeInput('maximumSSE', 0, 128, 0.5, 'Maximum SSE'));
        updatePanel.contents.appendChild(sseContainer);

        var dynamicSSEContainer = document.createElement('div');
        dynamicSSEContainer.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : dynamicSSE, "cesium-cesiumInspector-hide" : !dynamicSSE}');
        dynamicSSEContainer.appendChild(makeRangeInput('dynamicSSEDensity', 0, 1, 0.01, 'SSE Density'));
        dynamicSSEContainer.appendChild(makeRangeInput('dynamicSSEFactor', 0, 10, 0.1, 'SSE Factor'));
        updatePanel.contents.appendChild(dynamicSSEContainer);
        knockout.applyBindings(viewModel, updatePanel.contents);

        loggingPanel.contents.appendChild(makeCheckbox('performance', 'Performance'));
        loggingPanel.contents.appendChild(this._viewModel._performanceDisplay._container);
        this._viewModel._performanceDisplay._container.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : performance, "cesium-cesiumInspector-hide" : !performance}');
        loggingPanel.contents.appendChild(makeCheckbox('showStats', 'Stats'));
        var stats = document.createElement('div');
        stats.setAttribute('data-bind', 'html: statsText, css: {"cesium-cesiumInspector-show" : showStats, "cesium-cesiumInspector-hide" : !showStats}');
        stats.setAttribute('style', 'font-size: 11px');
        loggingPanel.contents.appendChild(stats);
        loggingPanel.contents.appendChild(makeCheckbox('showPickStats', 'Pick Stats'));
        var pickStats = document.createElement('div');
        pickStats.setAttribute('data-bind', 'html: pickStatsText, css: {"cesium-cesiumInspector-show" : showPickStats, "cesium-cesiumInspector-hide" : !showPickStats}');
        pickStats.setAttribute('style', 'font-size: 11px');
        loggingPanel.contents.appendChild(pickStats);
        knockout.applyBindings(viewModel, loggingPanel.contents);

        var styleEditor = document.createElement('textarea');
        styleEditor.setAttribute('data-bind', 'value: styleString');
        stylePanel.contents.className = 'cesium-cesiumInspector-styleEditor';
        stylePanel.contents.appendChild(styleEditor);
        stylePanel.contents.appendChild(document.createElement('br'));
        var closeStylesBtn = makeButton('toggleStyle', 'Close');
        stylePanel.contents.appendChild(closeStylesBtn);
        knockout.applyBindings(this._inspectorModel, closeStylesBtn);
        knockout.applyBindings(viewModel, styleEditor);
    }

    defineProperties(Cesium3DTilesInspector.prototype, {
        /**
         * Gets the parent container.
         * @memberof Cesium3DTilesInspector.prototype
         *
         * @type {Element}
         */
        container : {
            get : function() {
                return this._container;
            }
        },

        /**
         * Gets the view model.
         * @memberof Cesium3DTilesInspector.prototype
         *
         * @type {Cesium3DTilesInspectorViewModel}
         */
        viewModel : {
            get : function() {
                return this._viewModel;
            }
        }
    });

    /**
     * Sets the callback function to call on tileset load
     *
     * @param {Function} callback the callback
     */
    Cesium3DTilesInspector.prototype.onLoad = function(callback) {
        this._onLoad = callback;
    };

    /**
     * Sets the callback function to call on tileset unload
     *
     * @param {Function} callback the callback
     */
    Cesium3DTilesInspector.prototype.onUnload = function(callback) {
        this._onUnload = callback;
    };

    /**
     * Sets the callback function to call on feature selection
     *
     * @param {Function} callback the callback
     */
    Cesium3DTilesInspector.prototype.onSelect = function(callback) {
        this._onSelect = callback;
    };

    /**
     * @returns {Boolean} true if the object has been destroyed, false otherwise.
     */
    Cesium3DTilesInspector.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the widget.  Should be called if permanently
     * removing the widget from layout.
     */
    Cesium3DTilesInspector.prototype.destroy = function() {
        knockout.cleanNode(this._element);
        this._container.removeChild(this._element);
        this.viewModel.destroy();

        return destroyObject(this);
    };

    function makeSection(name, visibleProp, toggleProp) {
        var panel = document.createElement('div');
        panel.className = 'cesium-cesiumInspector-dropDown';

        var header = document.createElement('div');
        header.className = 'cesium-cesiumInspector-sectionHeader';
        var toggle = document.createElement('span');
        toggle.className = 'cesium-cesiumInspector-toggleSwitch';

        var bindings = [];
        if (defined(toggleProp)) {
            if (defined(visibleProp)) {
                bindings.push('text: ' + visibleProp + ' ? "-" : "+"');
            }
            bindings.push('click: ' + toggleProp);
        }

        toggle.setAttribute('data-bind', bindings.join(', '));
        header.appendChild(toggle);
        header.appendChild(document.createTextNode(name));

        var section = document.createElement('div');
        section.className = 'cesium-cesiumInspector-section';
        if (defined(visibleProp)) {
            section.setAttribute('data-bind', 'css: {"cesium-cesiumInspector-show" : ' + visibleProp + ', "cesium-cesiumInspector-hide" : !' + visibleProp + '}');
        }

        panel.appendChild(header);
        panel.appendChild(section);

        var contents = document.createElement('div');
        section.appendChild(contents);
        panel.contents = contents;
        return panel;
    }

    function makeCheckbox(property, text, enable) {
        var container = document.createElement('div');
        var checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        var binding = 'checked: ' + property;
        if (defined(enable)) {
            binding += ', enable: ' + enable;
        }
        checkbox.setAttribute('data-bind', binding);
        container.appendChild(checkbox);
        container.appendChild(document.createTextNode(text));
        return container;
    }

    function makeRangeInput(property, min, max, step, text) {
        var container = document.createElement('div');
        container.className = 'cesium-cesiumInspector-slider';
        var input = document.createElement('input');
        input.setAttribute('data-bind', 'value: ' + property);
        input.type = 'number';

        var slider = document.createElement('input');
        slider.type = 'range';
        slider.min = min;
        slider.max = max;
        slider.step = step;
        slider.setAttribute('data-bind', 'valueUpdate: "input", value: ' + property);

        container.appendChild(document.createTextNode(text));
        var wrapper = document.createElement('div');
        wrapper.appendChild(input);
        wrapper.appendChild(slider);
        container.appendChild(wrapper);

        return container;
    }

    function makeButton(action, text, active) {
        var button = document.createElement('input');
        button.type = 'button';
        button.value = text;
        button.className = 'cesium-cesiumInspector-pickButton';
        var binding = 'click: ' + action;
        if (defined(active)) {
            binding += ', css: {"cesium-cesiumInspector-pickButtonHighlight" : ' + active + '}';
        }
        button.setAttribute('data-bind', binding);

        return button;
    }

    return Cesium3DTilesInspector;
});
<|endoftext|>


<commit_before>const API_URL = '/api/table-of-contents'
const SIDEBAR = './content/_data/sidebar.json'
const { getTitle } = require('../../utils')

describe('APIs', () => {
  beforeEach(() => {
    cy.viewport('macbook-15')
    cy.visit(API_URL)
  })

  it('contains a sidebar', () => {
    cy.readFile(SIDEBAR).then(({ api }) => {
      const sidebarItems = api[0].children

      cy.wrap(sidebarItems).each((category) => {
        const pages = category.children

        cy.get('.app-sidebar')
          .contains(category.title)
          .then(($category) => {
            cy.get(`[data-test="${category.title}-children"]`).then(($ul) => {
              if ($ul.hasClass('hidden')) {
                cy.wrap($category).scrollIntoView().click()
              }
            })
          })

        cy.wrap(pages).each((page) => {
          const pageTitle = page.title

          cy.contains(
            page.slug === 'table-of-contents'
              ? '.app-sidebar a'
              : `.app-sidebar [data-test="${category.slug}"] a`,
            pageTitle
          ).click({ force: true })

          const redirects = {
            'table-of-contents': '/api/table-of-contents',
            'all-assertions': '/guides/references/assertions',
          }

          cy.location('pathname').should(
            'equal',
            page.redirect ||
              redirects[page.slug] ||
              `/api/${category.slug}/${page.slug}`
          )

          cy.contains(
            page.slug === 'table-of-contents' || page.slug === 'all-assertions'
              ? '.app-sidebar a'
              : `.app-sidebar [data-test="${category.slug}"] a`,
            page.slug === 'all-assertions' ? 'Assertions' : pageTitle
          ).should(
            'have.class',
            'nuxt-link-exact-active nuxt-link-active active-sidebar-link'
          )

          cy.get('.active-sidebar-link').should('have.length', 1)

          const titleAliases = {
            'all-assertions': 'Assertions',
            _: 'Cypress._',
            $: 'Cypress.$',
            minimatch: 'Cypress.minimatch',
            promise: 'Cypress.Promise',
            sinon: 'Cypress.sinon',
            blob: 'Cypress.Blob',
            moment: 'Cypress.moment',
            'custom-commands': 'Custom Commands',
            cookies: 'Cypress.Cookies',
            'screenshot-api': 'Cypress.Screenshot',
            'selector-playground-api': 'Cypress.SelectorPlayground',
            'cypress-server': 'Cypress.Server',
            arch: 'Cypress.arch',
            browser: 'Cypress.browser',
            config: 'Cypress.config',
            dom: 'Cypress.dom',
            env: 'Cypress.env',
            isbrowser: 'Cypress.isBrowser',
            iscy: 'Cypress.isCy',
            'cypress-log': 'Cypress.log',
            platform: 'Cypress.platform',
            spec: 'Cypress.spec',
            'testing-type': 'Cypress.testingType',
            version: 'Cypress.version',
            'configuration-api': 'Configuration API',
            'preprocessors-api': 'Preprocessors API',
            'before-run-api': 'Before Run API',
            'after-run-api': 'After Run API',
            'before-spec-api': 'Before Spec API',
            'after-spec-api': 'After Spec API',
            'browser-launch-api': 'Browser Launch API',
            'after-screenshot-api': 'After Screenshot API',
          }

          cy.title().should(
            'equal',
            page.redirect?.includes('assertions')
              ? getTitle('Assertions')
              : getTitle(titleAliases[page.slug] || pageTitle)
          )

          cy.get('.main-content-title').contains(
            page.redirect?.includes('assertions')
              ? 'Assertions'
              : titleAliases[page.slug] || pageTitle
          )

          cy.visualSnapshot(`/api/${category.slug}/${page.slug}`)
        })

        cy.visit(API_URL)
      })
    })
  })
})
<commit_msg>tests: allow exception for All Assertions redirect in api.spec.js<commit_after>const API_URL = '/api/table-of-contents'
const SIDEBAR = './content/_data/sidebar.json'
const { getTitle } = require('../../utils')

describe('APIs', () => {
  beforeEach(() => {
    cy.viewport('macbook-15')
    cy.visit(API_URL)
  })

  it('contains a sidebar', () => {
    cy.readFile(SIDEBAR).then(({ api }) => {
      const sidebarItems = api[0].children

      cy.wrap(sidebarItems).each((category) => {
        const pages = category.children

        cy.get('.app-sidebar')
          .contains(category.title)
          .then(($category) => {
            cy.get(`[data-test="${category.title}-children"]`).then(($ul) => {
              if ($ul.hasClass('hidden')) {
                cy.wrap($category).scrollIntoView().click()
              }
            })
          })

        cy.wrap(pages).each((page) => {
          const pageTitle = page.title

          cy.contains(
            page.slug === 'table-of-contents'
              ? '.app-sidebar a'
              : `.app-sidebar [data-test="${category.slug}"] a`,
            pageTitle
          ).click({ force: true })

          const redirects = {
            'table-of-contents': '/api/table-of-contents',
            'all-assertions': '/guides/references/assertions',
          }

          cy.location('pathname').should(
            'equal',
            page.redirect ||
              redirects[page.slug] ||
              `/api/${category.slug}/${page.slug}`
          )

          cy.contains(
            page.slug === 'table-of-contents' ||
              page.redirect === '/guides/references/assertions'
              ? '.app-sidebar a'
              : `.app-sidebar [data-test="${category.slug}"] a`,
            page.redirect === '/guides/references/assertions'
              ? 'Assertions'
              : pageTitle
          ).should(
            'have.class',
            'nuxt-link-exact-active nuxt-link-active active-sidebar-link'
          )

          cy.get('.active-sidebar-link').should('have.length', 1)

          const titleAliases = {
            'all-assertions': 'Assertions',
            _: 'Cypress._',
            $: 'Cypress.$',
            minimatch: 'Cypress.minimatch',
            promise: 'Cypress.Promise',
            sinon: 'Cypress.sinon',
            blob: 'Cypress.Blob',
            moment: 'Cypress.moment',
            'custom-commands': 'Custom Commands',
            cookies: 'Cypress.Cookies',
            'screenshot-api': 'Cypress.Screenshot',
            'selector-playground-api': 'Cypress.SelectorPlayground',
            'cypress-server': 'Cypress.Server',
            arch: 'Cypress.arch',
            browser: 'Cypress.browser',
            config: 'Cypress.config',
            dom: 'Cypress.dom',
            env: 'Cypress.env',
            isbrowser: 'Cypress.isBrowser',
            iscy: 'Cypress.isCy',
            'cypress-log': 'Cypress.log',
            platform: 'Cypress.platform',
            spec: 'Cypress.spec',
            'testing-type': 'Cypress.testingType',
            version: 'Cypress.version',
            'configuration-api': 'Configuration API',
            'preprocessors-api': 'Preprocessors API',
            'before-run-api': 'Before Run API',
            'after-run-api': 'After Run API',
            'before-spec-api': 'Before Spec API',
            'after-spec-api': 'After Spec API',
            'browser-launch-api': 'Browser Launch API',
            'after-screenshot-api': 'After Screenshot API',
          }

          cy.title().should(
            'equal',
            page.redirect?.includes('assertions')
              ? getTitle('Assertions')
              : getTitle(titleAliases[page.slug] || pageTitle)
          )

          cy.get('.main-content-title').contains(
            page.redirect?.includes('assertions')
              ? 'Assertions'
              : titleAliases[page.slug] || pageTitle
          )

          cy.visualSnapshot(`/api/${category.slug}/${page.slug}`)
        })

        cy.visit(API_URL)
      })
    })
  })
})
<|endoftext|>


<commit_before>/*!
 * Copyright by Oleg Efimov
 *
 * See license text in LICENSE file
 */

// Require dependencies
var Buffer = require('buffer').Buffer;
var assert = require('./assert');
var strtok = require('strtok');

// Require UBJSON types definition
var ubjsonTypes = require("./ubjson-types.js");

// Require class for int64 storing
var Int64 = require('./int64.js');

/** internal
 * ubjsonPack(stream, buffer, offset, value, flush) -> Integer
 * - stream (Stream|null): Stream to write packed UBJSON (optional)
 * - buffer (Buffer): Buffer to write packed UBJSON
 * - offset (Number): Buffer offset to write
 * - value (mixed): JavaScript value to pack
 * - flush (Function|null): Flush function, used to dump packed data to stream (optional)
 *
 * Pack a value into the given stream (or buffer and offset),
 * flushing using the provided function.
 *
 * Returns buffer offset after write.
 **/
function ubjsonPack(s, b, bo, v, flush) {
  assert.ok(b instanceof Buffer);
  assert.ok(typeof bo === 'number');
  assert.ok(bo >= 0);
  assert.ok(bo < b.length);

  v = (v === null) ? undefined : v;
  flush = (typeof flush !== "function" && s !== null) ?
    function(b, o) {
      s.write(b.toString('binary', 0, o), 'binary');
    } : flush;

  var len = 0;

  switch (typeof v) {
    case 'undefined':
      return strtok.UINT8.put(b, bo, ubjsonTypes.NULL, flush);

    case 'boolean':
      return strtok.UINT8.put(b, bo, (v) ? ubjsonTypes.TRUE : ubjsonTypes.FALSE, flush);

    case 'number':
      var encodeNumberAsHuge = false;

      // Useful for debugging FPN: http://www.binaryconvert.com/

      if (~~v === v) {
        if (-128 <= v && v <= 127) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.BYTE, flush);
          len += strtok.INT8.put(b, bo + len, v, flush);
        } else if (-32428 <= v && v <= 32427) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.INT16, flush);
          len += strtok.INT16_BE.put(b, bo + len, v, flush);
        } else if (-2147483648 <= v && v <= 2147483647) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.INT32, flush);
          len += strtok.INT32_BE.put(b, bo + len, v, flush);
        } else {
          encodeNumberAsHuge = true;
        }
      } else {
        var absV = Math.abs(v);

        if (1.18e-38 < absV && absV < 3.40e38) {
          len  = strtok.UINT8.put(b, bo + len, ubjsonTypes.FLOAT, flush);
          len += strtok.FLOAT_BE.put(b, bo + len, v, flush);
        } else if (2.23e-308 < absV && absV < 1.79e308) {
          len  = strtok.UINT8.put(b, bo + len, ubjsonTypes.DOUBLE, flush);
          len += strtok.DOUBLE_BE.put(b, bo + len, v, flush);
        } else {
          encodeNumberAsHuge = true;
        }
      }

      if (encodeNumberAsHuge) {
        var stringV = v.toString();

        // 255 = 0xFF is valid for huges
        if (stringV.length <= 255) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTHUGE, flush);
          len += strtok.UINT8.put(b, bo + len, stringV.length, flush);
        } else {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.HUGE, flush);
          len += strtok.UINT32_BE.put(b, bo + len, stringV.length, flush);
        }

        len += b.write(stringV, bo + len, 'utf-8');
      }

      return len;

    case 'string':
      var stringByteLength = Buffer.byteLength(v, 'utf-8');

      // 255 = 0xFF is valid for strings
      if (stringByteLength <= 255) {
        len  = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTSTRING, flush);
        len += strtok.UINT8.put(b, bo + len, stringByteLength, flush);
      } else {
        len  = strtok.UINT8.put(b, bo, ubjsonTypes.STRING, flush);
        len += strtok.UINT32_BE.put(b, bo + len, stringByteLength, flush);
      }

      var UTF8STRING = new strtok.StringType(stringByteLength, 'utf-8');
      len += UTF8STRING.put(b, bo + len, v, flush);

      return len;

    case 'object':
      if (v === null) {
        len = strtok.UINT8.put(b, bo, ubjsonTypes.NULL, flush);
      } else if (v instanceof Int64) {
        len = strtok.UINT8.put(b, bo, ubjsonTypes.INT64, flush);
        len += strtok.INT64_BE.put(b, bo + len, v, flush);
      } else if (Array.isArray(v)) {
        // 255 (0xFF) is reserved fo unknown-length containers
        if (v.length <= 254) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTARRAY, flush);
          len += strtok.UINT8.put(b, bo + len, v.length, flush);
        } else {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.ARRAY, flush);
          len += strtok.UINT32_BE.put(b, bo + len, v.length, flush);
        }

        v.forEach(function(vv) {
          len += ubjsonPack(s, b, bo + len, vv, flush);
        });
      } else if (v.constructor === Object) {
        var vk = Object.keys(v);

        // 255 (0xFF) is reserved fo unknown-length containers
        if (vk.length <= 254) {
          len = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTOBJECT, flush);
          len += strtok.UINT8.put(b, bo + len, vk.length, flush);
        } else {
          len = strtok.UINT8.put(b, bo, ubjsonTypes.OBJECT, flush);
          len += strtok.UINT32_BE.put(b, bo + len, vk.length, flush);
        }

        vk.forEach(function(k) {
          len += ubjsonPack(s, b, bo + len, k, flush);
          len += ubjsonPack(s, b, bo + len, v[k], flush);
        });
      } else {
        throw new Error('Cannot pack object constructed by ' + v.constructor.valueOf());
      }

      return len;

    default:
      throw new Error('Cannot pack value of type ' + typeof v);
  }
}

// Export
module.exports = ubjsonPack;
<commit_msg>Modify ubjson-pack.js to work with all buffer implementations.<commit_after>/*!
 * Copyright by Oleg Efimov
 *
 * See license text in LICENSE file
 */

// Require dependencies
var Buffer = require('buffer').Buffer;
var assert = require('./assert');
var strtok = require('strtok');

// Require UBJSON types definition
var ubjsonTypes = require("./ubjson-types.js");

// Require class for int64 storing
var Int64 = require('./int64.js');

/** internal
 * ubjsonPack(stream, buffer, offset, value, flush) -> Integer
 * - stream (Stream|null): Stream to write packed UBJSON (optional)
 * - buffer (Buffer): Buffer to write packed UBJSON
 * - offset (Number): Buffer offset to write
 * - value (mixed): JavaScript value to pack
 * - flush (Function|null): Flush function, used to dump packed data to stream (optional)
 *
 * Pack a value into the given stream (or buffer and offset),
 * flushing using the provided function.
 *
 * Returns buffer offset after write.
 **/
function ubjsonPack(s, b, bo, v, flush) {
  //assert.ok(b instanceof Buffer);
  assert.ok(typeof bo === 'number');
  assert.ok(bo >= 0);
  assert.ok(bo < b.length);

  v = (v === null) ? undefined : v;
  flush = (typeof flush !== "function" && s !== null) ?
    function(b, o) {
      s.write(b.toString('binary', 0, o), 'binary');
    } : flush;

  var len = 0;

  switch (typeof v) {
    case 'undefined':
      return strtok.UINT8.put(b, bo, ubjsonTypes.NULL, flush);

    case 'boolean':
      return strtok.UINT8.put(b, bo, (v) ? ubjsonTypes.TRUE : ubjsonTypes.FALSE, flush);

    case 'number':
      var encodeNumberAsHuge = false;

      // Useful for debugging FPN: http://www.binaryconvert.com/

      if (~~v === v) {
        if (-128 <= v && v <= 127) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.BYTE, flush);
          len += strtok.INT8.put(b, bo + len, v, flush);
        } else if (-32428 <= v && v <= 32427) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.INT16, flush);
          len += strtok.INT16_BE.put(b, bo + len, v, flush);
        } else if (-2147483648 <= v && v <= 2147483647) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.INT32, flush);
          len += strtok.INT32_BE.put(b, bo + len, v, flush);
        } else {
          encodeNumberAsHuge = true;
        }
      } else {
        var absV = Math.abs(v);

        if (1.18e-38 < absV && absV < 3.40e38) {
          len  = strtok.UINT8.put(b, bo + len, ubjsonTypes.FLOAT, flush);
          len += strtok.FLOAT_BE.put(b, bo + len, v, flush);
        } else if (2.23e-308 < absV && absV < 1.79e308) {
          len  = strtok.UINT8.put(b, bo + len, ubjsonTypes.DOUBLE, flush);
          len += strtok.DOUBLE_BE.put(b, bo + len, v, flush);
        } else {
          encodeNumberAsHuge = true;
        }
      }

      if (encodeNumberAsHuge) {
        var stringV = v.toString();

        // 255 = 0xFF is valid for huges
        if (stringV.length <= 255) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTHUGE, flush);
          len += strtok.UINT8.put(b, bo + len, stringV.length, flush);
        } else {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.HUGE, flush);
          len += strtok.UINT32_BE.put(b, bo + len, stringV.length, flush);
        }

        len += b.write(stringV, bo + len, 'utf-8');
      }

      return len;

    case 'string':
      var stringByteLength = Buffer.byteLength(v, 'utf-8');

      // 255 = 0xFF is valid for strings
      if (stringByteLength <= 255) {
        len  = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTSTRING, flush);
        len += strtok.UINT8.put(b, bo + len, stringByteLength, flush);
      } else {
        len  = strtok.UINT8.put(b, bo, ubjsonTypes.STRING, flush);
        len += strtok.UINT32_BE.put(b, bo + len, stringByteLength, flush);
      }

      var UTF8STRING = new strtok.StringType(stringByteLength, 'utf-8');
      len += UTF8STRING.put(b, bo + len, v, flush);

      return len;

    case 'object':
      if (v === null) {
        len = strtok.UINT8.put(b, bo, ubjsonTypes.NULL, flush);
      } else if (v instanceof Int64) {
        len = strtok.UINT8.put(b, bo, ubjsonTypes.INT64, flush);
        len += strtok.INT64_BE.put(b, bo + len, v, flush);
      } else if (Array.isArray(v)) {
        // 255 (0xFF) is reserved fo unknown-length containers
        if (v.length <= 254) {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTARRAY, flush);
          len += strtok.UINT8.put(b, bo + len, v.length, flush);
        } else {
          len  = strtok.UINT8.put(b, bo, ubjsonTypes.ARRAY, flush);
          len += strtok.UINT32_BE.put(b, bo + len, v.length, flush);
        }

        v.forEach(function(vv) {
          len += ubjsonPack(s, b, bo + len, vv, flush);
        });
      } else /* if (v.constructor === Object) */ {
        var vk = Object.keys(v);

        // 255 (0xFF) is reserved fo unknown-length containers
        if (vk.length <= 254) {
          len = strtok.UINT8.put(b, bo, ubjsonTypes.SHORTOBJECT, flush);
          len += strtok.UINT8.put(b, bo + len, vk.length, flush);
        } else {
          len = strtok.UINT8.put(b, bo, ubjsonTypes.OBJECT, flush);
          len += strtok.UINT32_BE.put(b, bo + len, vk.length, flush);
        }

        vk.forEach(function(k) {
          len += ubjsonPack(s, b, bo + len, k, flush);
          len += ubjsonPack(s, b, bo + len, v[k], flush);
        });
      } /* else {
        throw new Error('Cannot pack object constructed by ' + v.constructor.valueOf());
      } */

      return len;

    default:
      throw new Error('Cannot pack value of type ' + typeof v);
  }
}

// Export
module.exports = ubjsonPack;
<|endoftext|>