<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import store from '@/store'

export default {
  name: 'storage',
  title: 'label.storage',
  icon: 'database',
  children: [
    {
      name: 'volume',
      title: 'label.volumes',
      icon: 'hdd',
      docHelp: 'adminguide/storage.html#working-with-volumes',
      permission: ['listVolumesMetrics'],
      resourceType: 'Volume',
      columns: () => {
        const fields = ['name', 'state', 'type', 'vmname', 'sizegb']
        const metricsFields = ['diskkbsread', 'diskkbswrite', 'diskiopstotal']

        if (store.getters.userInfo.roletype === 'Admin') {
          metricsFields.push({
            physicalsize: (record) => {
              return record.physicalsize ? parseFloat(record.physicalsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) + 'GB' : ''
            }
          })
          metricsFields.push('utilization')
        }

        if (store.getters.metrics) {
          fields.push(...metricsFields)
        }

        if (store.getters.userInfo.roletype === 'Admin') {
          fields.push('account')
          fields.push('storage')
        } else if (store.getters.userInfo.roletype === 'DomainAdmin') {
          fields.push('account')
        }
        fields.push('zonename')

        return fields
      },
      details: ['name', 'id', 'type', 'storagetype', 'diskofferingdisplaytext', 'deviceid', 'sizegb', 'physicalsize', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'diskiopstotal', 'miniops', 'maxiops', 'path'],
      related: [{
        name: 'snapshot',
        title: 'label.snapshots',
        param: 'volumeid'
      }],
      searchFilters: ['name', 'zoneid', 'domainid', 'account', 'state', 'tags'],
      actions: [
        {
          api: 'createVolume',
          icon: 'plus',
          docHelp: 'adminguide/storage.html#creating-a-new-volume',
          label: 'label.action.create.volume',
          listView: true,
          popup: true,
          component: () => import('@/views/storage/CreateVolume.vue')
        },
        {
          api: 'createVolume',
          icon: 'cloud-upload',
          docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine',
          label: 'label.upload.volume.from.local',
          listView: true,
          popup: true,
          component: () => import('@/views/storage/UploadLocalVolume.vue')
        },
        {
          api: 'uploadVolume',
          icon: 'link',
          docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine',
          label: 'label.upload.volume.from.url',
          listView: true,
          args: ['url', 'name', 'zoneid', 'format', 'diskofferingid', 'checksum'],
          mapping: {
            format: {
              options: ['RAW', 'VHD', 'VHDX', 'OVA', 'QCOW2']
            }
          }
        },
        {
          api: 'attachVolume',
          icon: 'paper-clip',
          label: 'label.action.attach.disk',
          message: 'message.confirm.attach.disk',
          args: ['virtualmachineid'],
          dataView: true,
          show: (record) => { return record.type !== 'ROOT' && ['Allocated', 'Ready', 'Uploaded'].includes(record.state) && !('virtualmachineid' in record) }
        },
        {
          api: 'detachVolume',
          icon: 'link',
          label: 'label.action.detach.disk',
          message: 'message.detach.disk',
          dataView: true,
          show: (record) => {
            return record.type !== 'ROOT' && 'virtualmachineid' in record && record.virtualmachineid &&
              ['Running', 'Stopped', 'Destroyed'].includes(record.vmstate)
          }
        },
        {
          api: 'createSnapshot',
          icon: 'camera',
          docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
          label: 'label.action.take.snapshot',
          dataView: true,
          show: (record) => { return record.state === 'Ready' },
          popup: true,
          component: () => import('@/views/storage/TakeSnapshot.vue')
        },
        {
          api: 'createSnapshotPolicy',
          icon: 'clock-circle',
          docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
          label: 'label.action.recurring.snapshot',
          dataView: true,
          show: (record) => { return record.state === 'Ready' },
          popup: true,
          component: () => import('@/views/storage/RecurringSnapshotVolume.vue'),
          mapping: {
            volumeid: {
              value: (record) => { return record.id }
            },
            intervaltype: {
              options: ['HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY']
            }
          }
        },
        {
          api: 'resizeVolume',
          icon: 'fullscreen',
          docHelp: 'adminguide/storage.html#resizing-volumes',
          label: 'label.action.resize.volume',
          dataView: true,
          popup: true,
          show: (record) => { return ['Allocated', 'Ready'].includes(record.state) },
          component: () => import('@/views/storage/ResizeVolume.vue')
        },
        {
          api: 'migrateVolume',
          icon: 'drag',
          docHelp: 'adminguide/storage.html#id2',
          label: 'label.migrate.volume',
          args: ['volumeid', 'storageid', 'livemigrate'],
          dataView: true,
          show: (record, store) => { return record && record.state === 'Ready' && ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) },
          popup: true,
          component: () => import('@/views/storage/MigrateVolume.vue')
        },
        {
          api: 'extractVolume',
          icon: 'cloud-download',
          label: 'label.action.download.volume',
          message: 'message.download.volume.confirm',
          dataView: true,
          show: (record) => { return record && record.state === 'Ready' && (record.vmstate === 'Stopped' || record.virtualmachineid == null) },
          args: ['zoneid', 'mode'],
          mapping: {
            zoneid: {
              value: (record) => { return record.zoneid }
            },
            mode: {
              value: (record) => { return 'HTTP_DOWNLOAD' }
            }
          },
          response: (result) => { return `Please click <a href="${result.volume.url}" target="_blank">${result.volume.url}</a> to download.` }
        },
        {
          api: 'createTemplate',
          icon: 'picture',
          label: 'label.action.create.template.from.volume',
          dataView: true,
          show: (record) => { return (record.type === 'ROOT' && record.vmstate === 'Stopped') || (record.type !== 'ROOT' && !('virtualmachineid' in record) && !['Allocated', 'Uploaded', 'Destroy'].includes(record.state)) },
          args: ['volumeid', 'name', 'displaytext', 'ostypeid', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'requireshvm', 'passwordenabled', 'sshkeyenabled'],
          mapping: {
            volumeid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'recoverVolume',
          icon: 'medicine-box',
          label: 'label.action.recover.volume',
          message: 'message.action.recover.volume',
          dataView: true,
          show: (record, store) => {
            return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervolume) && record.state === 'Destroy'
          }
        },
        {
          api: 'deleteVolume',
          icon: 'delete',
          label: 'label.action.delete.volume',
          message: 'message.action.delete.volume',
          dataView: true,
          groupAction: true,
          show: (record, store) => {
            return ['Expunging', 'Expunged', 'UploadError'].includes(record.state) ||
              ((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervolume) && record.state === 'Destroy')
          }
        },
        {
          api: 'destroyVolume',
          icon: 'delete',
          label: 'label.action.destroy.volume',
          message: 'message.action.destroy.volume',
          dataView: true,
          args: (record, store) => {
            return (!['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !store.features.allowuserexpungerecovervolumestore) ? [] : ['expunge']
          },
          show: (record, store) => {
            return (!['Creating'].includes(record.state) && record.type !== 'ROOT' && !('virtualmachineid' in record) && record.state !== 'Destroy')
          }
        }
      ]
    },
    {
      name: 'snapshot',
      title: 'label.snapshots',
      icon: 'build',
      docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
      permission: ['listSnapshots'],
      resourceType: 'Snapshot',
      columns: () => {
        var fields = ['name', 'state', 'volumename', 'intervaltype', 'created']
        if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
          fields.push('account')
        }
        return fields
      },
      details: ['name', 'id', 'volumename', 'intervaltype', 'account', 'domain', 'created'],
      searchFilters: ['name', 'domainid', 'account', 'tags'],
      actions: [
        {
          api: 'createTemplate',
          icon: 'picture',
          label: 'label.create.template',
          dataView: true,
          show: (record) => { return record.state === 'BackedUp' },
          args: ['snapshotid', 'name', 'displaytext', 'ostypeid', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'requireshvm', 'passwordenabled', 'sshkeyenabled'],
          mapping: {
            snapshotid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'createVolume',
          icon: 'hdd',
          label: 'label.action.create.volume',
          dataView: true,
          show: (record) => { return record.state === 'BackedUp' },
          args: ['snapshotid', 'name'],
          mapping: {
            snapshotid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'revertSnapshot',
          icon: 'sync',
          label: 'label.action.revert.snapshot',
          message: 'message.action.revert.snapshot',
          dataView: true,
          show: (record) => { return record.state === 'BackedUp' && record.revertable }
        },
        {
          api: 'deleteSnapshot',
          icon: 'delete',
          label: 'label.action.delete.snapshot',
          message: 'message.action.delete.snapshot',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' }
        }
      ]
    },
    {
      name: 'vmsnapshot',
      title: 'label.vm.snapshots',
      icon: 'camera',
      docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
      permission: ['listVMSnapshot'],
      resourceType: 'VMSnapshot',
      columns: () => {
        var fields = ['displayname', 'state', 'type', 'current', 'parentName', 'created']
        if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
          fields.push('account')
        }
        return fields
      },
      details: ['name', 'id', 'displayname', 'description', 'type', 'current', 'parentName', 'virtualmachineid', 'account', 'domain', 'created'],
      searchFilters: ['name', 'domainid', 'account', 'tags'],
      actions: [
        {
          api: 'revertToVMSnapshot',
          icon: 'sync',
          label: 'label.action.vmsnapshot.revert',
          message: 'label.action.vmsnapshot.revert',
          dataView: true,
          show: (record) => { return record.state === 'Ready' },
          args: ['vmsnapshotid'],
          mapping: {
            vmsnapshotid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'deleteVMSnapshot',
          icon: 'delete',
          label: 'label.action.vmsnapshot.delete',
          message: 'message.action.vmsnapshot.delete',
          dataView: true,
          show: (record) => { return ['Ready', 'Expunging', 'Error'].includes(record.state) },
          args: ['vmsnapshotid'],
          mapping: {
            vmsnapshotid: {
              value: (record) => { return record.id }
            }
          }
        }
      ]
    },
    {
      name: 'backup',
      title: 'label.backup',
      icon: 'cloud-upload',
      permission: ['listBackups'],
      columns: [{ name: (record) => { return record.virtualmachinename } }, 'virtualmachinename', 'status', 'type', 'created', 'account', 'zone'],
      details: ['virtualmachinename', 'id', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'],
      actions: [
        {
          api: 'restoreBackup',
          icon: 'sync',
          docHelp: 'adminguide/virtual_machines.html#restoring-vm-backups',
          label: 'label.backup.restore',
          message: 'message.backup.restore',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' }
        },
        {
          api: 'restoreVolumeFromBackupAndAttachToVM',
          icon: 'paper-clip',
          label: 'label.backup.attach.restore',
          message: 'message.backup.attach.restore',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' },
          popup: true,
          component: () => import('@/views/storage/RestoreAttachBackupVolume.vue')
        },
        {
          api: 'removeVirtualMachineFromBackupOffering',
          icon: 'scissor',
          label: 'label.backup.offering.remove',
          message: 'message.backup.offering.remove',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' },
          args: ['forced', 'virtualmachineid'],
          mapping: {
            forced: {
              value: (record) => { return true }
            },
            virtualmachineid: {
              value: (record) => { return record.virtualmachineid }
            }
          }
        },
        {
          api: 'deleteBackup',
          icon: 'delete',
          label: 'label.delete.backup',
          message: 'message.delete.backup',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' }
        }
      ]
    }
  ]
}
<commit_msg>config: show utilisation to users<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

import store from '@/store'

export default {
  name: 'storage',
  title: 'label.storage',
  icon: 'database',
  children: [
    {
      name: 'volume',
      title: 'label.volumes',
      icon: 'hdd',
      docHelp: 'adminguide/storage.html#working-with-volumes',
      permission: ['listVolumesMetrics'],
      resourceType: 'Volume',
      columns: () => {
        const fields = ['name', 'state', 'type', 'vmname', 'sizegb']
        const metricsFields = ['diskkbsread', 'diskkbswrite', 'diskiopstotal']

        if (store.getters.userInfo.roletype === 'Admin') {
          metricsFields.push({
            physicalsize: (record) => {
              return record.physicalsize ? parseFloat(record.physicalsize / (1024.0 * 1024.0 * 1024.0)).toFixed(2) + 'GB' : ''
            }
          })
        }
        metricsFields.push('utilization')

        if (store.getters.metrics) {
          fields.push(...metricsFields)
        }

        if (store.getters.userInfo.roletype === 'Admin') {
          fields.push('account')
          fields.push('storage')
        } else if (store.getters.userInfo.roletype === 'DomainAdmin') {
          fields.push('account')
        }
        fields.push('zonename')

        return fields
      },
      details: ['name', 'id', 'type', 'storagetype', 'diskofferingdisplaytext', 'deviceid', 'sizegb', 'physicalsize', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'diskiopstotal', 'miniops', 'maxiops', 'path'],
      related: [{
        name: 'snapshot',
        title: 'label.snapshots',
        param: 'volumeid'
      }],
      searchFilters: ['name', 'zoneid', 'domainid', 'account', 'state', 'tags'],
      actions: [
        {
          api: 'createVolume',
          icon: 'plus',
          docHelp: 'adminguide/storage.html#creating-a-new-volume',
          label: 'label.action.create.volume',
          listView: true,
          popup: true,
          component: () => import('@/views/storage/CreateVolume.vue')
        },
        {
          api: 'createVolume',
          icon: 'cloud-upload',
          docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine',
          label: 'label.upload.volume.from.local',
          listView: true,
          popup: true,
          component: () => import('@/views/storage/UploadLocalVolume.vue')
        },
        {
          api: 'uploadVolume',
          icon: 'link',
          docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine',
          label: 'label.upload.volume.from.url',
          listView: true,
          args: ['url', 'name', 'zoneid', 'format', 'diskofferingid', 'checksum'],
          mapping: {
            format: {
              options: ['RAW', 'VHD', 'VHDX', 'OVA', 'QCOW2']
            }
          }
        },
        {
          api: 'attachVolume',
          icon: 'paper-clip',
          label: 'label.action.attach.disk',
          message: 'message.confirm.attach.disk',
          args: ['virtualmachineid'],
          dataView: true,
          show: (record) => { return record.type !== 'ROOT' && ['Allocated', 'Ready', 'Uploaded'].includes(record.state) && !('virtualmachineid' in record) }
        },
        {
          api: 'detachVolume',
          icon: 'link',
          label: 'label.action.detach.disk',
          message: 'message.detach.disk',
          dataView: true,
          show: (record) => {
            return record.type !== 'ROOT' && 'virtualmachineid' in record && record.virtualmachineid &&
              ['Running', 'Stopped', 'Destroyed'].includes(record.vmstate)
          }
        },
        {
          api: 'createSnapshot',
          icon: 'camera',
          docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
          label: 'label.action.take.snapshot',
          dataView: true,
          show: (record) => { return record.state === 'Ready' },
          popup: true,
          component: () => import('@/views/storage/TakeSnapshot.vue')
        },
        {
          api: 'createSnapshotPolicy',
          icon: 'clock-circle',
          docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
          label: 'label.action.recurring.snapshot',
          dataView: true,
          show: (record) => { return record.state === 'Ready' },
          popup: true,
          component: () => import('@/views/storage/RecurringSnapshotVolume.vue'),
          mapping: {
            volumeid: {
              value: (record) => { return record.id }
            },
            intervaltype: {
              options: ['HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY']
            }
          }
        },
        {
          api: 'resizeVolume',
          icon: 'fullscreen',
          docHelp: 'adminguide/storage.html#resizing-volumes',
          label: 'label.action.resize.volume',
          dataView: true,
          popup: true,
          show: (record) => { return ['Allocated', 'Ready'].includes(record.state) },
          component: () => import('@/views/storage/ResizeVolume.vue')
        },
        {
          api: 'migrateVolume',
          icon: 'drag',
          docHelp: 'adminguide/storage.html#id2',
          label: 'label.migrate.volume',
          args: ['volumeid', 'storageid', 'livemigrate'],
          dataView: true,
          show: (record, store) => { return record && record.state === 'Ready' && ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) },
          popup: true,
          component: () => import('@/views/storage/MigrateVolume.vue')
        },
        {
          api: 'extractVolume',
          icon: 'cloud-download',
          label: 'label.action.download.volume',
          message: 'message.download.volume.confirm',
          dataView: true,
          show: (record) => { return record && record.state === 'Ready' && (record.vmstate === 'Stopped' || record.virtualmachineid == null) },
          args: ['zoneid', 'mode'],
          mapping: {
            zoneid: {
              value: (record) => { return record.zoneid }
            },
            mode: {
              value: (record) => { return 'HTTP_DOWNLOAD' }
            }
          },
          response: (result) => { return `Please click <a href="${result.volume.url}" target="_blank">${result.volume.url}</a> to download.` }
        },
        {
          api: 'createTemplate',
          icon: 'picture',
          label: 'label.action.create.template.from.volume',
          dataView: true,
          show: (record) => { return (record.type === 'ROOT' && record.vmstate === 'Stopped') || (record.type !== 'ROOT' && !('virtualmachineid' in record) && !['Allocated', 'Uploaded', 'Destroy'].includes(record.state)) },
          args: ['volumeid', 'name', 'displaytext', 'ostypeid', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'requireshvm', 'passwordenabled', 'sshkeyenabled'],
          mapping: {
            volumeid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'recoverVolume',
          icon: 'medicine-box',
          label: 'label.action.recover.volume',
          message: 'message.action.recover.volume',
          dataView: true,
          show: (record, store) => {
            return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervolume) && record.state === 'Destroy'
          }
        },
        {
          api: 'deleteVolume',
          icon: 'delete',
          label: 'label.action.delete.volume',
          message: 'message.action.delete.volume',
          dataView: true,
          groupAction: true,
          show: (record, store) => {
            return ['Expunging', 'Expunged', 'UploadError'].includes(record.state) ||
              ((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervolume) && record.state === 'Destroy')
          }
        },
        {
          api: 'destroyVolume',
          icon: 'delete',
          label: 'label.action.destroy.volume',
          message: 'message.action.destroy.volume',
          dataView: true,
          args: (record, store) => {
            return (!['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !store.features.allowuserexpungerecovervolumestore) ? [] : ['expunge']
          },
          show: (record, store) => {
            return (!['Creating'].includes(record.state) && record.type !== 'ROOT' && !('virtualmachineid' in record) && record.state !== 'Destroy')
          }
        }
      ]
    },
    {
      name: 'snapshot',
      title: 'label.snapshots',
      icon: 'build',
      docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
      permission: ['listSnapshots'],
      resourceType: 'Snapshot',
      columns: () => {
        var fields = ['name', 'state', 'volumename', 'intervaltype', 'created']
        if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
          fields.push('account')
        }
        return fields
      },
      details: ['name', 'id', 'volumename', 'intervaltype', 'account', 'domain', 'created'],
      searchFilters: ['name', 'domainid', 'account', 'tags'],
      actions: [
        {
          api: 'createTemplate',
          icon: 'picture',
          label: 'label.create.template',
          dataView: true,
          show: (record) => { return record.state === 'BackedUp' },
          args: ['snapshotid', 'name', 'displaytext', 'ostypeid', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'requireshvm', 'passwordenabled', 'sshkeyenabled'],
          mapping: {
            snapshotid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'createVolume',
          icon: 'hdd',
          label: 'label.action.create.volume',
          dataView: true,
          show: (record) => { return record.state === 'BackedUp' },
          args: ['snapshotid', 'name'],
          mapping: {
            snapshotid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'revertSnapshot',
          icon: 'sync',
          label: 'label.action.revert.snapshot',
          message: 'message.action.revert.snapshot',
          dataView: true,
          show: (record) => { return record.state === 'BackedUp' && record.revertable }
        },
        {
          api: 'deleteSnapshot',
          icon: 'delete',
          label: 'label.action.delete.snapshot',
          message: 'message.action.delete.snapshot',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' }
        }
      ]
    },
    {
      name: 'vmsnapshot',
      title: 'label.vm.snapshots',
      icon: 'camera',
      docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
      permission: ['listVMSnapshot'],
      resourceType: 'VMSnapshot',
      columns: () => {
        var fields = ['displayname', 'state', 'type', 'current', 'parentName', 'created']
        if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
          fields.push('account')
        }
        return fields
      },
      details: ['name', 'id', 'displayname', 'description', 'type', 'current', 'parentName', 'virtualmachineid', 'account', 'domain', 'created'],
      searchFilters: ['name', 'domainid', 'account', 'tags'],
      actions: [
        {
          api: 'revertToVMSnapshot',
          icon: 'sync',
          label: 'label.action.vmsnapshot.revert',
          message: 'label.action.vmsnapshot.revert',
          dataView: true,
          show: (record) => { return record.state === 'Ready' },
          args: ['vmsnapshotid'],
          mapping: {
            vmsnapshotid: {
              value: (record) => { return record.id }
            }
          }
        },
        {
          api: 'deleteVMSnapshot',
          icon: 'delete',
          label: 'label.action.vmsnapshot.delete',
          message: 'message.action.vmsnapshot.delete',
          dataView: true,
          show: (record) => { return ['Ready', 'Expunging', 'Error'].includes(record.state) },
          args: ['vmsnapshotid'],
          mapping: {
            vmsnapshotid: {
              value: (record) => { return record.id }
            }
          }
        }
      ]
    },
    {
      name: 'backup',
      title: 'label.backup',
      icon: 'cloud-upload',
      permission: ['listBackups'],
      columns: [{ name: (record) => { return record.virtualmachinename } }, 'virtualmachinename', 'status', 'type', 'created', 'account', 'zone'],
      details: ['virtualmachinename', 'id', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'],
      actions: [
        {
          api: 'restoreBackup',
          icon: 'sync',
          docHelp: 'adminguide/virtual_machines.html#restoring-vm-backups',
          label: 'label.backup.restore',
          message: 'message.backup.restore',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' }
        },
        {
          api: 'restoreVolumeFromBackupAndAttachToVM',
          icon: 'paper-clip',
          label: 'label.backup.attach.restore',
          message: 'message.backup.attach.restore',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' },
          popup: true,
          component: () => import('@/views/storage/RestoreAttachBackupVolume.vue')
        },
        {
          api: 'removeVirtualMachineFromBackupOffering',
          icon: 'scissor',
          label: 'label.backup.offering.remove',
          message: 'message.backup.offering.remove',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' },
          args: ['forced', 'virtualmachineid'],
          mapping: {
            forced: {
              value: (record) => { return true }
            },
            virtualmachineid: {
              value: (record) => { return record.virtualmachineid }
            }
          }
        },
        {
          api: 'deleteBackup',
          icon: 'delete',
          label: 'label.delete.backup',
          message: 'message.delete.backup',
          dataView: true,
          show: (record) => { return record.state !== 'Destroyed' }
        }
      ]
    }
  ]
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2016 Dmitry Brant.
 http://dmitrybrant.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

function parseFormat(reader)
{
	var results = new ResultNode("ZIP structure");
	try {
		var stream = new DataStream(reader);

        var contentResults = new ResultNode("ZIP contents");
        var fileList = [];
        var mimeType = "";

        try {
            var zipInfo = zipReadContents(stream, contentResults, fileList);
            if (zipInfo.mimeType !== undefined) {
                mimeType = zipInfo.mimeType;
            }
        } catch (e) {
            console.log("Error while reading ZIP contents: " + e);
        }

        var fileExt = "ZIP";
        var fileType = "Standard ZIP file";

        if (fileList.indexOf("word/document.xml") >= 0) {
            fileExt = "DOCX";
            fileType = "Microsoft Word (2010 and above) document";
        } else if (fileList.indexOf("ppt/presentation.xml") >= 0) {
            fileExt = "PPTX";
            fileType = "Microsoft PowerPoint (2010 and above) presentation";
        } else if (fileList.indexOf("xl/workbook.xml") >= 0) {
            fileExt = "XLSX";
            fileType = "Microsoft Excel (2010 and above) spreadsheet";
        } else if (fileList.indexOf("FixedDocSeq.fdseq") >= 0) {
            fileExt = "XPS";
            fileType = "Microsoft XPS document";
        } else if (fileList.indexOf("AndroidManifest.xml") >= 0) {
            fileExt = "APK";
            fileType = "Android application package";
        } else if (fileList.indexOf("snote/snote.xml") >= 0) {
            fileExt = "SNB";
            fileType = "Exported Samsung S-Note file";
        } else if (mimeType.indexOf("opendocument.text") >= 0) {
            fileExt = "ODT";
            fileType = "OpenDocument text file";
        } else if (mimeType.indexOf("opendocument.spreadsheet") >= 0) {
            fileExt = "ODS";
            fileType = "OpenDocument spreadsheet";
        } else if (mimeType.indexOf("opendocument.presentation") >= 0) {
            fileExt = "ODP";
            fileType = "OpenDocument presentation";
        } else if (mimeType.indexOf("opendocument.graphics") >= 0) {
            fileExt = "ODG";
            fileType = "OpenDocument graphics file";
        } else if (mimeType.indexOf("application/epub") >= 0) {
            fileExt = "EPUB";
            fileType = "Electronic publication or e-book";
        }

        results.add("File type", fileType);
        results.add("File extension", fileExt);
        results.addResult(contentResults);

    } catch(e) {
		console.log("Error while reading ZIP: " + e);
	}
	return results;
}

function zipReadContents(stream, results, fileList) {
    var zipInfo = {};
    var chunkType;
    var fileName;
    var versionNeeded, flags, compressionMethod;
    var lastModTime, lastModDate, crc32;
    var compressedSize, uncompressedSize, fileNameLength;
    var extraFieldLength;
    var node;

    while (!stream.eof()) {
        //read chunk type...
        if (stream.readAsciiString(2) != "PK") {
            break;
        }
        chunkType = stream.readUShortLe();

        node = results.add("0x" + chunkType.toString(16), zipChunkName(chunkType));

        if (chunkType == 0x0403) {
            //compressed file data!
            versionNeeded = stream.readUShortLe();
            flags = stream.readUShortLe();
            compressionMethod = stream.readUShortLe();
            lastModTime = stream.readUShortLe();
            lastModDate = stream.readUShortLe();

            crc32 = stream.readUIntLe();
            compressedSize = stream.readUIntLe();
            uncompressedSize = stream.readUIntLe();
            fileNameLength = stream.readUShortLe();
            extraFieldLength = stream.readUShortLe();

            //sanity
            if (fileNameLength > 10000) {
                break;
            }

            //read file name...
            fileName = stream.readAsciiString(fileNameLength);

            node.add("Name", fileName);
            node.add("Size (compressed)", compressedSize);
            node.add("Size (uncompressed)", uncompressedSize);

            if (fileList !== null) {
                fileList.push(fileName);
            }

            stream.seek(extraFieldLength, 1);

            if (fileName == "mimetype" && compressedSize == uncompressedSize && compressedSize < 256) {
                // it's trying to tell us something!
                zipInfo.mimeType = stream.readAsciiString(compressedSize);
            } else {
                stream.seek(compressedSize, 1);
            }

            //read data descriptor?
            if ((flags & 0x8) != 0) {
                //int s = SearchForSig(disk, new byte[]{0x50, 0x4B, 0x7, 0x8}, 10000);
                //if (s >= 0) {
                //    fileSize += s;
                //} else {
                //    haveHeader = false;
                //    break;
                //}
            }
        } else if (chunkType == 0x0807) {

            var dataCrc32 = stream.readUIntLe();
            var dataCompSize = stream.readUIntLe();
            var dataUncompSize = stream.readUIntLe();

            node.add("Size (compressed)", dataCompSize);
            node.add("Size (uncompressed)", dataUncompSize);

        } else if (chunkType == 0x0806) {

            //archive extra data record!
            extraFieldLength = stream.readUIntLe();
            stream.seek(extraFieldLength, 1);

            node.add("Size", extraFieldLength);

        } else if (chunkType == 0x0201) {

            //directory file header!
            var versionMadeBy = stream.readUShortLe();
            versionNeeded = stream.readUShortLe();
            flags = stream.readUShortLe();
            compressionMethod = stream.readUShortLe();
            lastModTime = stream.readUShortLe();
            lastModDate = stream.readUShortLe();

            crc32 = stream.readUIntLe();
            compressedSize = stream.readUIntLe();
            uncompressedSize = stream.readUIntLe();
            fileNameLength = stream.readUShortLe();
            extraFieldLength = stream.readUShortLe();
            var fileCommentLength = stream.readUShortLe();
            var diskNumberStart = stream.readUShortLe();
            var internalFileAttr = stream.readUShortLe();
            var externalFileAttr = stream.readUIntLe();
            var relHeaderOffset = stream.readUIntLe();

            fileName = stream.readAsciiString(fileNameLength);

            node.add("Name", fileName);
            node.add("Size (compressed)", compressedSize);
            node.add("Size (uncompressed)", uncompressedSize);

            //read extra field...
            stream.seek(extraFieldLength, 1);

            //read file comment...
            if (fileCommentLength > 0 && fileCommentLength < 4096) {
                var fileComment = stream.readAsciiString(fileCommentLength);
                node.add("Comment", fileComment);
            } else {
                stream.seek(fileCommentLength, 1);
            }

        } else if (chunkType == 0x0505) {

            //digital signature!
            var dataLength = stream.readUShortLe();
            stream.seek(dataLength, 1);

            node.add("Size", dataLength);

        } else if (chunkType == 0x0606) {

            //zip64 end of central directory!
            var sizeOfRecord = stream.readLongLe();
            if (sizeOfRecord > 0) {
                stream.seek(sizeOfRecord, 1);
            }

            node.add("Size", sizeOfRecord);

        } else if (chunkType == 0x0706) {

            //zip64 end of central dir locator!
            var numberofDisk = stream.readUIntLe();
            var offsetToCentralDirRec = stream.readLongLe();
            var numTotalDisks = stream.readUIntLe();

        } else if (chunkType == 0x0605) {

            //directory file header!
            var diskNumber = stream.readUShortLe();
            var numDiskWithCentralDir = stream.readUShortLe();
            var localCentralDirEntries = stream.readUShortLe();
            var totalCentralDirEntries = stream.readUShortLe();
            var sizeOfCentralDir = stream.readUIntLe();
            var offsetToCentralDir = stream.readUIntLe();
            var zipCommentLength = stream.readUShortLe();

            if (zipCommentLength > 0 && zipCommentLength < 4096) {
                var zipComment = stream.readAsciiString(zipCommentLength);
                node.add("Comment", zipComment);
            } else {
                stream.seek(zipCommentLength, 1);
            }

        } else {
            break;
        }
    }
    return zipInfo;
}

function zipChunkName(id) {
    switch (id)
    {
        case 0x0403: return "Local file header";
        case 0x0806: return "Archive extra data";
        case 0x0201: return "Central directory file header";
        case 0x0505: return "Digital signature";
        case 0x0606: return "Zip64 end of central directory record";
        case 0x0706: return "Zip64 end of central directory locator";
        case 0x0605: return "End of central directory record";
    }
    return "Unknown";
}<commit_msg>More .ZIP subtypes.<commit_after>/*
 Copyright (c) 2016 Dmitry Brant.
 http://dmitrybrant.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

function parseFormat(reader)
{
	var results = new ResultNode("ZIP structure");
	try {
		var stream = new DataStream(reader);

        var contentResults = new ResultNode("ZIP contents");
        var fileList = [];
        var mimeType = "";

        try {
            var zipInfo = zipReadContents(stream, contentResults, fileList);
            if (zipInfo.mimeType !== undefined) {
                mimeType = zipInfo.mimeType;
            }
        } catch (e) {
            console.log("Error while reading ZIP contents: " + e);
        }

        var fileExt = "ZIP";
        var fileType = "Standard ZIP file";

        if (fileList.indexOf("word/document.xml") >= 0) {
            fileExt = "DOCX";
            fileType = "Microsoft Word (2010 and above) document";
        } else if (fileList.indexOf("ppt/presentation.xml") >= 0) {
            fileExt = "PPTX";
            fileType = "Microsoft PowerPoint (2010 and above) presentation";
        } else if (fileList.indexOf("xl/workbook.xml") >= 0) {
            fileExt = "XLSX";
            fileType = "Microsoft Excel (2010 and above) spreadsheet";
        } else if (fileList.indexOf("FixedDocSeq.fdseq") >= 0) {
            fileExt = "XPS";
            fileType = "Microsoft XPS document";
        } else if (fileList.indexOf("AndroidManifest.xml") >= 0) {
            fileExt = "APK";
            fileType = "Android application package";
        } else if (fileList.indexOf("snote/snote.xml") >= 0) {
            fileExt = "SNB";
            fileType = "Exported Samsung S-Note file";
        } else if (fileList.indexOf("content/riffData.cdr") >= 0) {
            fileExt = "CDR";
            fileType = "CorelDraw image";
        } else if (fileList.indexOf("Root.xml") >= 0 && fileList.indexOf("summary.xml") >= 0) {
            if (fileList.indexOf("preview.png") >= 0) {
                fileExt = "DPP";
                fileType = "Serif DrawPlus document";
            } else if (fileList.indexOf("preview.jpg") >= 0) {
                fileExt = "PPP";
                fileType = "Serif PagePlus document";
            }
        } else if (fileList.indexOf("doc.kml") >= 0) {
            fileExt = "KMZ";
            fileType = "Google Earth location data";
        } else if (mimeType.indexOf("opendocument.text") >= 0) {
            fileExt = "ODT";
            fileType = "OpenDocument text file";
        } else if (mimeType.indexOf("opendocument.spreadsheet") >= 0) {
            fileExt = "ODS";
            fileType = "OpenDocument spreadsheet";
        } else if (mimeType.indexOf("opendocument.presentation") >= 0) {
            fileExt = "ODP";
            fileType = "OpenDocument presentation";
        } else if (mimeType.indexOf("opendocument.graphics") >= 0) {
            fileExt = "ODG";
            fileType = "OpenDocument graphics file";
        } else if (mimeType.indexOf("application/epub") >= 0) {
            fileExt = "EPUB";
            fileType = "Electronic publication or e-book";
        }

        results.add("File type", fileType);
        results.add("File extension", fileExt);
        results.addResult(contentResults);

    } catch(e) {
		console.log("Error while reading ZIP: " + e);
	}
	return results;
}

function zipReadContents(stream, results, fileList) {
    var zipInfo = {};
    var chunkType;
    var fileName;
    var versionNeeded, flags, compressionMethod;
    var lastModTime, lastModDate, crc32;
    var compressedSize, uncompressedSize, fileNameLength;
    var extraFieldLength;
    var node;

    while (!stream.eof()) {
        //read chunk type...
        if (stream.readAsciiString(2) != "PK") {
            break;
        }
        chunkType = stream.readUShortLe();

        node = results.add("0x" + chunkType.toString(16), zipChunkName(chunkType));

        if (chunkType == 0x0403) {
            //compressed file data!
            versionNeeded = stream.readUShortLe();
            flags = stream.readUShortLe();
            compressionMethod = stream.readUShortLe();
            lastModTime = stream.readUShortLe();
            lastModDate = stream.readUShortLe();

            crc32 = stream.readUIntLe();
            compressedSize = stream.readUIntLe();
            uncompressedSize = stream.readUIntLe();
            fileNameLength = stream.readUShortLe();
            extraFieldLength = stream.readUShortLe();

            //sanity
            if (fileNameLength > 10000) {
                break;
            }

            //read file name...
            fileName = stream.readAsciiString(fileNameLength);

            node.add("Name", fileName);
            node.add("Size (compressed)", compressedSize);
            node.add("Size (uncompressed)", uncompressedSize);

            if (fileList !== null) {
                fileList.push(fileName);
            }

            stream.seek(extraFieldLength, 1);

            if (fileName == "mimetype" && compressedSize == uncompressedSize && compressedSize < 256) {
                // it's trying to tell us something!
                zipInfo.mimeType = stream.readAsciiString(compressedSize);
            } else {
                stream.seek(compressedSize, 1);
            }

            //read data descriptor?
            if ((flags & 0x8) != 0) {
                //int s = SearchForSig(disk, new byte[]{0x50, 0x4B, 0x7, 0x8}, 10000);
                //if (s >= 0) {
                //    fileSize += s;
                //} else {
                //    haveHeader = false;
                //    break;
                //}
            }
        } else if (chunkType == 0x0807) {

            var dataCrc32 = stream.readUIntLe();
            var dataCompSize = stream.readUIntLe();
            var dataUncompSize = stream.readUIntLe();

            node.add("Size (compressed)", dataCompSize);
            node.add("Size (uncompressed)", dataUncompSize);

        } else if (chunkType == 0x0806) {

            //archive extra data record!
            extraFieldLength = stream.readUIntLe();
            stream.seek(extraFieldLength, 1);

            node.add("Size", extraFieldLength);

        } else if (chunkType == 0x0201) {

            //directory file header!
            var versionMadeBy = stream.readUShortLe();
            versionNeeded = stream.readUShortLe();
            flags = stream.readUShortLe();
            compressionMethod = stream.readUShortLe();
            lastModTime = stream.readUShortLe();
            lastModDate = stream.readUShortLe();

            crc32 = stream.readUIntLe();
            compressedSize = stream.readUIntLe();
            uncompressedSize = stream.readUIntLe();
            fileNameLength = stream.readUShortLe();
            extraFieldLength = stream.readUShortLe();
            var fileCommentLength = stream.readUShortLe();
            var diskNumberStart = stream.readUShortLe();
            var internalFileAttr = stream.readUShortLe();
            var externalFileAttr = stream.readUIntLe();
            var relHeaderOffset = stream.readUIntLe();

            fileName = stream.readAsciiString(fileNameLength);

            node.add("Name", fileName);
            node.add("Size (compressed)", compressedSize);
            node.add("Size (uncompressed)", uncompressedSize);

            //read extra field...
            stream.seek(extraFieldLength, 1);

            //read file comment...
            if (fileCommentLength > 0 && fileCommentLength < 4096) {
                var fileComment = stream.readAsciiString(fileCommentLength);
                node.add("Comment", fileComment);
            } else {
                stream.seek(fileCommentLength, 1);
            }

        } else if (chunkType == 0x0505) {

            //digital signature!
            var dataLength = stream.readUShortLe();
            stream.seek(dataLength, 1);

            node.add("Size", dataLength);

        } else if (chunkType == 0x0606) {

            //zip64 end of central directory!
            var sizeOfRecord = stream.readLongLe();
            if (sizeOfRecord > 0) {
                stream.seek(sizeOfRecord, 1);
            }

            node.add("Size", sizeOfRecord);

        } else if (chunkType == 0x0706) {

            //zip64 end of central dir locator!
            var numberofDisk = stream.readUIntLe();
            var offsetToCentralDirRec = stream.readLongLe();
            var numTotalDisks = stream.readUIntLe();

        } else if (chunkType == 0x0605) {

            //directory file header!
            var diskNumber = stream.readUShortLe();
            var numDiskWithCentralDir = stream.readUShortLe();
            var localCentralDirEntries = stream.readUShortLe();
            var totalCentralDirEntries = stream.readUShortLe();
            var sizeOfCentralDir = stream.readUIntLe();
            var offsetToCentralDir = stream.readUIntLe();
            var zipCommentLength = stream.readUShortLe();

            if (zipCommentLength > 0 && zipCommentLength < 4096) {
                var zipComment = stream.readAsciiString(zipCommentLength);
                node.add("Comment", zipComment);
            } else {
                stream.seek(zipCommentLength, 1);
            }

        } else {
            break;
        }
    }
    return zipInfo;
}

function zipChunkName(id) {
    switch (id)
    {
        case 0x0403: return "Local file header";
        case 0x0806: return "Archive extra data";
        case 0x0201: return "Central directory file header";
        case 0x0505: return "Digital signature";
        case 0x0606: return "Zip64 end of central directory record";
        case 0x0706: return "Zip64 end of central directory locator";
        case 0x0605: return "End of central directory record";
    }
    return "Unknown";
}<|endoftext|>


<commit_before>/*
DOM.js
author: morgl
*/

function onTrackShow(evt){
    var trackCount = Player.getTrackCount();
    Player.addTracks(evt.tracks);
    var trackLen = evt.tracks.length;
    for(var i = 0; i < trackLen; i++){
        var track = evt.tracks[i];
        var itemContainer = $( "<div>", { class: "col-xs-12 col-sm-6 col-md-3  placeholders"} );
        var miniatureContainer = $( '<div>', { class : 'miniature' } ).appendTo(itemContainer);
        if ( track.artwork_url ) {
            $( '<img />', { src : track.artwork_url.replace('large','t200x200') } ).appendTo(miniatureContainer);
        }
        $( '<span>', { 'data-trackindex': trackCount + i, class : 'miniature-play play glyphicon glyphicon-play' }     ).appendTo(miniatureContainer);
        itemContainer.appendTo('#content-target').hide().fadeIn(600);
    }
    if(evt.finish){
        unloading();
        if (trackCount == 0){
            show_nothing_found();
        }
    }
};

$( "#content-target" ).on( "click", ".play, .pause", function() {
    var $this = $(this);
    $this.toggleClass("play glyphicon-play pause glyphicon-pause");
    if(!Player.isPlaying){
        loading();
        if(!Player.play($this.data('trackindex')))
            unloading();
    }
    else {
        var myIndex = $(this).data('trackindex');
        if(Player.currentIndex == myIndex)
            Player.pause();
        else{
            loading();
            if(!Player.play(myIndex))
                unloading();
        }
    }
});

$( '#player-control-play' ).click(function(){
    if(!Player.isPlaying){
        loading();
        if(Player.play())
            unloading();
    }
    else
        Player.pause();
});

$( '#player-control-backward' ).click(function(){
    loading();
    if(Player.backward())
        unloading();
});

$( '#player-control-forward' ).click(function(){
    loading();
    if(Player.forward())
        unloading();
});

function formSubmit(){
    var mode = $('#cboModeSelector').val();
    var tags = $('#txtTags').val();
    var window = $('#cboWindowSelector').val();
    $('#content-target').html('');
    Player.clearTracks();
    loading();
    SoundBuzz.getBuzz(mode, tags, window, onTrackShow);
    return false;
};

function loading(){
    $('#overlay').addClass('loading');
    $('#overlay').append( $( '<div>', { id : 'loading' } ) );
};

function unloading(){
    $('#overlay').removeClass('loading');
    $('#loading').remove();
};

function show_nothing_found(){
    $('#player-control-play').find('.glyphicon').removeClass('glyphicon-pause').addClass('glyphicon-play');
    $('#content-target').html('');
    $( '<img />', { src   : 'img/badass.png',
                    alt   : 'not found',
                    class : 'placeholders' } ).appendTo('#content-target');
    $( '<p>', { class : 'badass',
                text  : 'Watch out, we got a badass over here' } ).appendTo('#content-target');
    $( '<p>', { class : 'nothing',
                text  : 'Nothing matches your demands, your highness...'} ).appendTo('#content-target');
}

Player.togglePlayCallback = function(playerInfo){

    if(playerInfo.isPlaying)
        unloading();

    var headerPlay = $('#player-control-play').find('.glyphicon');
    var miniaturePlay = $('.miniature-play[data-trackindex='+playerInfo.trackIndex+']');
    $('.miniature-play.pause').toggleClass('play glyphicon-play pause glyphicon-pause');
    if((playerInfo.isPlaying && headerPlay.hasClass('glyphicon-play')) ||
      (!playerInfo.isPlaying && headerPlay.hasClass('glyphicon-pause'))){
        headerPlay.toggleClass('glyphicon-play glyphicon-pause');
    }
    miniaturePlay.toggleClass('play glyphicon-play pause glyphicon-pause');
}
<commit_msg>fixed unloading when operation not succeeded<commit_after>/*
DOM.js
author: morgl
*/

function onTrackShow(evt){
    var trackCount = Player.getTrackCount();
    Player.addTracks(evt.tracks);
    var trackLen = evt.tracks.length;
    for(var i = 0; i < trackLen; i++){
        var track = evt.tracks[i];
        var itemContainer = $( "<div>", { class: "col-xs-12 col-sm-6 col-md-3  placeholders"} );
        var miniatureContainer = $( '<div>', { class : 'miniature' } ).appendTo(itemContainer);
        if ( track.artwork_url ) {
            $( '<img />', { src : track.artwork_url.replace('large','t200x200') } ).appendTo(miniatureContainer);
        }
        $( '<span>', { 'data-trackindex': trackCount + i, class : 'miniature-play play glyphicon glyphicon-play' }     ).appendTo(miniatureContainer);
        itemContainer.appendTo('#content-target').hide().fadeIn(600);
    }
    if(evt.finish){
        unloading();
        if (trackCount == 0){
            show_nothing_found();
        }
    }
};

$( "#content-target" ).on( "click", ".play, .pause", function() {
    var $this = $(this);
    $this.toggleClass("play glyphicon-play pause glyphicon-pause");
    if(!Player.isPlaying){
        loading();
        if(!Player.play($this.data('trackindex')))
            unloading();
    }
    else {
        var myIndex = $(this).data('trackindex');
        if(Player.currentIndex == myIndex)
            Player.pause();
        else{
            loading();
            if(!Player.play(myIndex))
                unloading();
        }
    }
});

$( '#player-control-play' ).click(function(){
    if(!Player.isPlaying){
        loading();
        if(!Player.play())
            unloading();
    }
    else
        Player.pause();
});

$( '#player-control-backward' ).click(function(){
    loading();
    if(!Player.backward())
        unloading();
});

$( '#player-control-forward' ).click(function(){
    loading();
    if(!Player.forward())
        unloading();
});

function formSubmit(){
    var mode = $('#cboModeSelector').val();
    var tags = $('#txtTags').val();
    var window = $('#cboWindowSelector').val();
    $('#content-target').html('');
    Player.clearTracks();
    loading();
    SoundBuzz.getBuzz(mode, tags, window, onTrackShow);
    return false;
};

function loading(){
    $('#overlay').addClass('loading');
    $('#overlay').append( $( '<div>', { id : 'loading' } ) );
};

function unloading(){
    $('#overlay').removeClass('loading');
    $('#loading').remove();
};

function show_nothing_found(){
    $('#player-control-play').find('.glyphicon').removeClass('glyphicon-pause').addClass('glyphicon-play');
    $('#content-target').html('');
    $( '<img />', { src   : 'img/badass.png',
                    alt   : 'not found',
                    class : 'placeholders' } ).appendTo('#content-target');
    $( '<p>', { class : 'badass',
                text  : 'Watch out, we got a badass over here' } ).appendTo('#content-target');
    $( '<p>', { class : 'nothing',
                text  : 'Nothing matches your demands, your highness...'} ).appendTo('#content-target');
}

Player.togglePlayCallback = function(playerInfo){

    if(playerInfo.isPlaying)
        unloading();

    var headerPlay = $('#player-control-play').find('.glyphicon');
    var miniaturePlay = $('.miniature-play[data-trackindex='+playerInfo.trackIndex+']');
    $('.miniature-play.pause').toggleClass('play glyphicon-play pause glyphicon-pause');
    if((playerInfo.isPlaying && headerPlay.hasClass('glyphicon-play')) ||
      (!playerInfo.isPlaying && headerPlay.hasClass('glyphicon-pause'))){
        headerPlay.toggleClass('glyphicon-play glyphicon-pause');
    }
    miniaturePlay.toggleClass('play glyphicon-play pause glyphicon-pause');
}
<|endoftext|>


<commit_before>var assert = require('assert');
var crypto = require('crypto');
var fs = require('fs');
var http = require('http');
var TileJSON = require('..');
var Step = require('step');

var fixtures = {
    'world-bright': JSON.parse(fs.readFileSync(__dirname + '/fixtures/world-bright.tilejson')),
    'grid': JSON.parse(fs.readFileSync(__dirname + '/fixtures/grid.tilejson'))
};

function md5(str) {
    return crypto.createHash('md5').update(str).digest('hex');
}

var world_bright;
before(function(done) {
    new TileJSON('tilejson://' + __dirname + '/fixtures/world-bright.tilejson', function(err, source) {
        world_bright = source;
        done(err);
    });
});
after(function(done) { world_bright.close(done); });

var grid_source;
before(function(done) {
    new TileJSON('tilejson://' + __dirname + '/fixtures/grid.tilejson', function(err, source) {
        grid_source = source;
        done(err);
    });
});
after(function(done) { grid_source.close(done); });

describe('load file', function() {
    it('should load a tilejson file', function(done) {
        new TileJSON('tilejson://' + __dirname + '/fixtures/world-bright.tilejson', function(err, source) {
            assert.ifError(err);
            assert.ok(source.data);
            source.getTile(0, 0, 0, function(err, data, headers) {
                assert.ifError(err);
                assert.ok('Cache-Control' in headers);
                assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
                done();
            });
        });
    });

    it('should return ENOENT for missing file', function(done) {
         new TileJSON('tilejson://' + __dirname + '/fixtures/enoent.tilejson', function(err, source) {
            assert.ok(err);
            assert.equal(err.code, 'ENOENT');
            done();
        });
    });

    it('should return parser error for invalid JSON', function(done) {
         new TileJSON('tilejson://' + __dirname + '/fixtures/bad.tilejson', function(err, source) {
            assert.ok(err);
            assert.equal(err.type, 'unexpected_token');
            done();
        });
    });

    it('should not attempt to load source from cache', function(done) {
         new TileJSON('tilejson://' + __dirname + '/fixtures/bad.tilejson', function(err, source) {
            assert.ok(err);
            assert.equal(err.type, 'unexpected_token');
            done();
        });
    });
});

describe('load http', function() {
    it('loads a tilejson file', function(done) {
        new TileJSON('http://a.tiles.mapbox.com/v3/mapbox.world-bright.json', function(err, source) {
            assert.ifError(err);
            assert.ok(source.data);
            source.getTile(0, 0, 0, function(err, data, headers) {
                assert.ifError(err);
                assert.ok('Cache-Control' in headers);
                assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
                done();
            });
        });
    });

    it('errors on 404', function(done) {
        new TileJSON('http://a.tiles.mapbox.com/v3/mapbox.doesnotexist.json', function(err, source) {
            assert.equal('Tileset does not exist', err.message);
            done();
        });
    });

    it('errors on bad JSON', function(done) {
        new TileJSON('http://a.tiles.mapbox.com/v3/mapbox.world-bright.jsonp', function(err, source) {
            assert.equal('Unexpected token g', err.message);
            done();
        });
    });
});

describe('load data', function() {
    it('loads directly from data', function(done) {
        new TileJSON({ data: fixtures['world-bright'] }, function(err, source) {
            assert.ifError(err);
            assert.ok(source.data);
            source.getTile(0, 0, 0, function(err, data, headers) {
                assert.ifError(err);
                assert.ok('Cache-Control' in headers);
                assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
                done();
            });
        });
    });
});

describe('locking IO', function() {
    it('avoids multiple IO calls', function(done) {
        var stats = {
            a: { once: 0, many: 0 },
            b: { once: 0, many: 0 }
        };
        var remaining = 10;
        for (var i = 0; i < 4; i++) {
            var lock = TileJSON.Locking('a', function(err, data) {
                stats.a.many++;
                assert.ifError(err);
                assert.ok(data);
                if (--remaining === 0) {
                    assert.equal(1, stats.a.once);
                    assert.equal(4, stats.a.many);
                    done();
                }
            });
            lock(function(callback) {
                stats.a.once++;
                fs.readFile(__dirname + '/fixtures/world-bright.tilejson', 'utf8', callback);
            });
        }
        for (var i = 0; i < 6; i++) {
            var lock = TileJSON.Locking('b', function(err, data) {
                stats.b.many++;
                assert.ifError(err);
                assert.ok(data);
                if (--remaining === 0) {
                    assert.equal(1, stats.b.once);
                    assert.equal(6, stats.b.many);
                    done();
                }
            });
            lock(function(callback) {
                stats.b.once++;
                fs.readFile(__dirname + '/fixtures/world-bright.tilejson', 'utf8', callback);
            });
        }
    });
    it('completes multiple callbacks', function(done) {
        var url = __dirname + '/fixtures/world-bright.tilejson';
        var stats = { once: 0, many: 0 };
        var remaining = 4;
        for (var i = 0; i < 4; i++) {
            var callback = function(err, data) {
                stats.many++;
                assert.ifError(err);
                if (--remaining === 0) {
                    assert.equal(4, stats.many);
                    done();
                }
            };
            var lock = TileJSON.Locking(url, function(err, data) {
                assert.ifError(err);
                assert.ok(data);
                return callback(null, data);
            });
            lock(function(callback) {
                stats.once++;
                fs.readFile(url, 'utf8', function(err, buffer) {
                    if (err) return callback(err);
                    try { var data = JSON.parse(buffer); }
                    catch(err) { return callback(err); }
                    callback(null, data)
                });
            });
        }
    });
    it('completes multiple callbacks asynchronously', function(done) {
        this.timeout(10000);
        var url = __dirname + '/fixtures/world-bright.tilejson';
        var stats = { once: 0, many: 0 };
        var callback = function(err, data) {
            stats.many++;
            assert.ifError(err);
            assert.ok(data);
            return this(null, data);
        }
        Step(function() {
            var lock = TileJSON.Locking(url, callback.bind(this));
            lock(function(callback) {
                stats.once++;
                fs.readFile(url, 'utf8', function(err, buffer) {
                    if (err) return callback(err);
                    try { var data = JSON.parse(buffer); }
                    catch(err) { return callback(err); }
                    callback(null, data)
                });
            });
        }, function(err, data) {
            assert.ifError(err);
            assert.ok(data);
            var lock = TileJSON.Locking(url, callback.bind(this));
            lock(function(callback) {
                stats.once++;
                fs.readFile(url, 'utf8', function(err, buffer) {
                    if (err) return callback(err);
                    try { var data = JSON.parse(buffer); }
                    catch(err) { return callback(err); }
                    callback(null, data)
                });
            });
        }, function(err, data) {
            assert.ifError(err);
            assert.ok(data);
            assert.equal(2, stats.many);
            done();
        });
    });
});

describe('tiles', function() {
    it('should load tile 0/0/0', function(done) {
        world_bright.getTile(0, 0, 0, function(err, data) {
            if (err) throw err;
            assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
            done();
        });
    });

    it('should load tile 2/2/2', function(done) {
        world_bright.getTile(2, 2, 2, function(err, data) {
            if (err) throw err;
            assert.equal('84044cc921ee458cd1ece905e2682db0', md5(data));
            done();
        });
    });
});

describe('grids', function() {
    it('should fail for non-existent grid', function(done) {
        world_bright.getGrid(0, 0, 0, function(err, data) {
            assert.ok(err);
            assert.equal(err.message, 'Grid does not exist');
            done();
        });
    });

    it('should load grid 6/29/30', function(done) {
        grid_source.getGrid(6, 29, 30, function(err, data, headers) {
            if (err) throw err;
            assert.equal('4f8790dc72e204132531f1e12dea20a1', md5(JSON.stringify(data)));
            assert.ok('Content-Type' in headers);
            assert.ok('Last-Modified' in headers);
            assert.ok('ETag' in headers);
            done();
        });
    });
});

describe('tiles from bad server', function() {
    var tilejson;
    var server;

    before(function(done) {
        new TileJSON('tilejson://' + __dirname + '/fixtures/invalid.tilejson?timeout=200', function(err, source) {
            tilejson = source;
            done(err);
        });
    });

    before(function(done) {
        server = http.createServer(function (req, res) {
            if (req.url === '/tiles/1/0/0.png') {
                // Wait forever.
            } else  {
                res.writeHead(500);
                res.end();
            }
        }).listen(38923, done);
    });

    it('should load a tile from the specified tilejson source', function(done) {
        tilejson.getTile(0, 0, 0, function(err, data, headers) {
            assert.ok(err);
            assert.equal(err.message, 'Server returned HTTP 500');
            done();
        });
    });

    it('should abort when the server takes too long', function(done) {
        tilejson.getTile(1, 0, 0, function(err, data, headers) {
            assert.ok(err);
            assert.equal(err.message, 'Timed out after 200ms');
            done();
        });
    });

    after(function(done) {
        if (tilejson) tilejson.close(done); else done();
    });

    after(function() {
        server.close();
    });
});

describe('get retry', function() {
    var tilejson;
    var server;
    var connectionCount;

    beforeEach(function(done) {
        connectionCount = 0;
        new TileJSON('tilejson://' + __dirname + '/fixtures/invalid.tilejson?timeout=200', function(err, source) {
            tilejson = source;
            done(err);
        });
    });

    it('should retry on socket hangup', function(done) {
        function setupServer(callback) {
            server = http.createServer(function (req, res) {});
            server.on('connection', function(socket) {
                connectionCount++;
                socket.destroy();
            });
            server.listen(38923);
            callback();
        }

        (setupServer(function() {
            tilejson.getTile(2, 2, 2, function(err, data, headers) {
                assert.equal(err.code, 'ECONNRESET');
                assert.equal(connectionCount, 2);
                server.close();
                done();
            });
        }));
    });

    describe('http status', function(){
        before(function(done) {
            server = http.createServer(function (req, res) {
                connectionCount++;
                if (req.url === '/tiles/5/0/0.png') {
                    res.writeHead(500);
                } else  {
                    res.writeHead(400);
                }
                res.end();
            }).listen(38923, done);
        });

        it('500 should retry', function(done) {
            tilejson.getTile(5, 0, 0, function(err, data, headers) {
                assert.equal(err.status, 500);
                assert.equal(connectionCount, 2);
                done();
            });
        });

        it('400 should not retry', function(done) {
            tilejson.getTile(4, 0, 0, function(err, data, headers) {
                assert.equal(err.status, 400);
                assert.equal(connectionCount, 1);
                done();
            });
        });
    });
});
<commit_msg>abstract once function, clarify info and data args<commit_after>var assert = require('assert');
var crypto = require('crypto');
var fs = require('fs');
var http = require('http');
var TileJSON = require('..');
var Step = require('step');

var fixtures = {
    'world-bright': JSON.parse(fs.readFileSync(__dirname + '/fixtures/world-bright.tilejson')),
    'grid': JSON.parse(fs.readFileSync(__dirname + '/fixtures/grid.tilejson'))
};

function md5(str) {
    return crypto.createHash('md5').update(str).digest('hex');
}

var world_bright;
before(function(done) {
    new TileJSON('tilejson://' + __dirname + '/fixtures/world-bright.tilejson', function(err, source) {
        world_bright = source;
        done(err);
    });
});
after(function(done) { world_bright.close(done); });

var grid_source;
before(function(done) {
    new TileJSON('tilejson://' + __dirname + '/fixtures/grid.tilejson', function(err, source) {
        grid_source = source;
        done(err);
    });
});
after(function(done) { grid_source.close(done); });

describe('load file', function() {
    it('should load a tilejson file', function(done) {
        new TileJSON('tilejson://' + __dirname + '/fixtures/world-bright.tilejson', function(err, source) {
            assert.ifError(err);
            assert.ok(source.data);
            source.getTile(0, 0, 0, function(err, data, headers) {
                assert.ifError(err);
                assert.ok('Cache-Control' in headers);
                assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
                done();
            });
        });
    });

    it('should return ENOENT for missing file', function(done) {
         new TileJSON('tilejson://' + __dirname + '/fixtures/enoent.tilejson', function(err, source) {
            assert.ok(err);
            assert.equal(err.code, 'ENOENT');
            done();
        });
    });

    it('should return parser error for invalid JSON', function(done) {
         new TileJSON('tilejson://' + __dirname + '/fixtures/bad.tilejson', function(err, source) {
            assert.ok(err);
            assert.equal(err.type, 'unexpected_token');
            done();
        });
    });

    it('should not attempt to load source from cache', function(done) {
         new TileJSON('tilejson://' + __dirname + '/fixtures/bad.tilejson', function(err, source) {
            assert.ok(err);
            assert.equal(err.type, 'unexpected_token');
            done();
        });
    });
});

describe('load http', function() {
    it('loads a tilejson file', function(done) {
        new TileJSON('http://a.tiles.mapbox.com/v3/mapbox.world-bright.json', function(err, source) {
            assert.ifError(err);
            assert.ok(source.data);
            source.getTile(0, 0, 0, function(err, data, headers) {
                assert.ifError(err);
                assert.ok('Cache-Control' in headers);
                assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
                done();
            });
        });
    });

    it('errors on 404', function(done) {
        new TileJSON('http://a.tiles.mapbox.com/v3/mapbox.doesnotexist.json', function(err, source) {
            assert.equal('Tileset does not exist', err.message);
            done();
        });
    });

    it('errors on bad JSON', function(done) {
        new TileJSON('http://a.tiles.mapbox.com/v3/mapbox.world-bright.jsonp', function(err, source) {
            assert.equal('Unexpected token g', err.message);
            done();
        });
    });
});

describe('load data', function() {
    it('loads directly from data', function(done) {
        new TileJSON({ data: fixtures['world-bright'] }, function(err, source) {
            assert.ifError(err);
            assert.ok(source.data);
            source.getTile(0, 0, 0, function(err, data, headers) {
                assert.ifError(err);
                assert.ok('Cache-Control' in headers);
                assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
                done();
            });
        });
    });
});

describe('locking IO', function() {
    it('avoids multiple IO calls', function(done) {
        var stats = {
            a: { once: 0, many: 0 },
            b: { once: 0, many: 0 }
        };
        var remaining = 10;
        for (var i = 0; i < 4; i++) {
            var lock = TileJSON.Locking('a', function(err, data) {
                stats.a.many++;
                assert.ifError(err);
                assert.ok(data);
                if (--remaining === 0) {
                    assert.equal(1, stats.a.once);
                    assert.equal(4, stats.a.many);
                    done();
                }
            });
            lock(function(callback) {
                stats.a.once++;
                fs.readFile(__dirname + '/fixtures/world-bright.tilejson', 'utf8', callback);
            });
        }
        for (var i = 0; i < 6; i++) {
            var lock = TileJSON.Locking('b', function(err, data) {
                stats.b.many++;
                assert.ifError(err);
                assert.ok(data);
                if (--remaining === 0) {
                    assert.equal(1, stats.b.once);
                    assert.equal(6, stats.b.many);
                    done();
                }
            });
            lock(function(callback) {
                stats.b.once++;
                fs.readFile(__dirname + '/fixtures/world-bright.tilejson', 'utf8', callback);
            });
        }
    });
    it('completes multiple callbacks', function(done) {
        var url = __dirname + '/fixtures/world-bright.tilejson';
        var stats = { once: 0, many: 0 };
        var remaining = 4;
        for (var i = 0; i < 4; i++) {
            var callback = function(err, data) {
                stats.many++;
                assert.ifError(err);
                if (--remaining === 0) {
                    assert.equal(4, stats.many);
                    done();
                }
            };
            var lock = TileJSON.Locking(url, function(err, data) {
                assert.ifError(err);
                assert.ok(data);
                return callback(null, data);
            });
            lock(function(callback) {
                stats.once++;
                fs.readFile(url, 'utf8', function(err, buffer) {
                    if (err) return callback(err);
                    try { var data = JSON.parse(buffer); }
                    catch(err) { return callback(err); }
                    callback(null, data)
                });
            });
        }
    });
    it('completes multiple callbacks asynchronously', function(done) {
        this.timeout(10000);
        var url = __dirname + '/fixtures/world-bright.tilejson';
        var stats = { once: 0, many: 0 };
        var callback = function(err, data) {
            stats.many++;
            assert.ifError(err);
            assert.ok(data);
            return this(null, data);
        }
        var once = function(callback) {
            stats.once++;
            fs.readFile(url, 'utf8', function(err, buffer) {
                if (err) return callback(err);
                try { var data = JSON.parse(buffer); }
                catch(err) { return callback(err); }
                callback(null, data)
            });
        };
        Step(function() {
            var lock = TileJSON.Locking(url, callback.bind(this));
            lock(once);
        }, function(err, info) {
            assert.ifError(err);
            assert.ok(info);
            var lock = TileJSON.Locking(url, callback.bind(this));
            lock(once);
        }, function(err, data) {
            assert.ifError(err);
            assert.ok(data);
            assert.equal(2, stats.many);
            done();
        });
    });
});

describe('tiles', function() {
    it('should load tile 0/0/0', function(done) {
        world_bright.getTile(0, 0, 0, function(err, data) {
            if (err) throw err;
            assert.equal('943ca1495e3b6e8d84dab88227904190', md5(data));
            done();
        });
    });

    it('should load tile 2/2/2', function(done) {
        world_bright.getTile(2, 2, 2, function(err, data) {
            if (err) throw err;
            assert.equal('84044cc921ee458cd1ece905e2682db0', md5(data));
            done();
        });
    });
});

describe('grids', function() {
    it('should fail for non-existent grid', function(done) {
        world_bright.getGrid(0, 0, 0, function(err, data) {
            assert.ok(err);
            assert.equal(err.message, 'Grid does not exist');
            done();
        });
    });

    it('should load grid 6/29/30', function(done) {
        grid_source.getGrid(6, 29, 30, function(err, data, headers) {
            if (err) throw err;
            assert.equal('4f8790dc72e204132531f1e12dea20a1', md5(JSON.stringify(data)));
            assert.ok('Content-Type' in headers);
            assert.ok('Last-Modified' in headers);
            assert.ok('ETag' in headers);
            done();
        });
    });
});

describe('tiles from bad server', function() {
    var tilejson;
    var server;

    before(function(done) {
        new TileJSON('tilejson://' + __dirname + '/fixtures/invalid.tilejson?timeout=200', function(err, source) {
            tilejson = source;
            done(err);
        });
    });

    before(function(done) {
        server = http.createServer(function (req, res) {
            if (req.url === '/tiles/1/0/0.png') {
                // Wait forever.
            } else  {
                res.writeHead(500);
                res.end();
            }
        }).listen(38923, done);
    });

    it('should load a tile from the specified tilejson source', function(done) {
        tilejson.getTile(0, 0, 0, function(err, data, headers) {
            assert.ok(err);
            assert.equal(err.message, 'Server returned HTTP 500');
            done();
        });
    });

    it('should abort when the server takes too long', function(done) {
        tilejson.getTile(1, 0, 0, function(err, data, headers) {
            assert.ok(err);
            assert.equal(err.message, 'Timed out after 200ms');
            done();
        });
    });

    after(function(done) {
        if (tilejson) tilejson.close(done); else done();
    });

    after(function() {
        server.close();
    });
});

describe('get retry', function() {
    var tilejson;
    var server;
    var connectionCount;

    beforeEach(function(done) {
        connectionCount = 0;
        new TileJSON('tilejson://' + __dirname + '/fixtures/invalid.tilejson?timeout=200', function(err, source) {
            tilejson = source;
            done(err);
        });
    });

    it('should retry on socket hangup', function(done) {
        function setupServer(callback) {
            server = http.createServer(function (req, res) {});
            server.on('connection', function(socket) {
                connectionCount++;
                socket.destroy();
            });
            server.listen(38923);
            callback();
        }

        (setupServer(function() {
            tilejson.getTile(2, 2, 2, function(err, data, headers) {
                assert.equal(err.code, 'ECONNRESET');
                assert.equal(connectionCount, 2);
                server.close();
                done();
            });
        }));
    });

    describe('http status', function(){
        before(function(done) {
            server = http.createServer(function (req, res) {
                connectionCount++;
                if (req.url === '/tiles/5/0/0.png') {
                    res.writeHead(500);
                } else  {
                    res.writeHead(400);
                }
                res.end();
            }).listen(38923, done);
        });

        it('500 should retry', function(done) {
            tilejson.getTile(5, 0, 0, function(err, data, headers) {
                assert.equal(err.status, 500);
                assert.equal(connectionCount, 2);
                done();
            });
        });

        it('400 should not retry', function(done) {
            tilejson.getTile(4, 0, 0, function(err, data, headers) {
                assert.equal(err.status, 400);
                assert.equal(connectionCount, 1);
                done();
            });
        });
    });
});
<|endoftext|>


<commit_before>(function() {
  var maps = {};

  var cartogramBinding = new Shiny.OutputBinding();
  $.extend(cartogramBinding, {
    find: function(scope) {
      return $(scope).find(".cartogram-map-output");
    },
    renderValue: function(el, data) {
      var $el = $(el);

      var map = $el.data('cartogram-map');
      if (!map) {
        // A new map was detected. Create it, initialize supporting data
        // structures, and hook up event handlers.
        var id = this.getId(el);
        maps[id] = $el;

        var svg = d3.select('#' + id + ' svg'),
          zoom = d3.behavior.zoom()
          .translate([-38, 32])
          .scale(0.94)
          .scaleExtent([0.5, 10.0])
          .on("zoom", updateZoom),
          layer = svg.append("g"),
          states = layer.append("g")
          .selectAll("path");

        updateZoom();

        function updateZoom() {
          var scale = zoom.scale();
          layer.attr("transform",
            "translate(" + zoom.translate() + ") " +
            "scale(" + [scale, scale] + ")");
        }

        var proj = d3.geo.albersUsa(),
          topology,
          geometries,
          carto = d3.cartogram()
          .projection(proj)
          .properties(function(d) {
            var data = $el.data('cartogram-data') || {};
            return data[d.id];
          })
          .value(function(d) {
            return +d.properties.scale;
          });

        $el.data('cartogram-map', {});

        d3.json($el.data('topojson'), function(topo) {
          topology = topo;
          geometries = topology.objects.states.geometries;

          var features = carto.features(topology, geometries),
            path = d3.geo.path()
            .projection(proj);

          states = states.data(features)
            .enter()
            .append("path")
            .attr("class", "state")
            .attr("id", function(d) {
              //return d.properties.NAME;
              return d.id;
            })
            .attr("fill", "#fafafa")
            .attr("d", path);

          states.append("title");

          $el.data('cartogram-map', {
            topology: topology,
            geometries: geometries,
            states: states,
            zoom: zoom,
            carto: carto,
            proj: proj
          });
        });
      }
    },
    reset: function($el) {
      var map = $el.data('cartogram-map');
      var carto = map.carto,
        topology = map.topology,
        geometries = map.geometries,
        proj = map.proj,
        states = map.states;

      if(topology === undefined || geometries === undefined) return;

      var features = carto.features(topology, geometries),
        path = d3.geo.path()
        .projection(proj);

      states.data(features)
        .transition()
        .duration(750)
        .ease("linear")
        .attr("fill", "#fafafa")
        .attr("d", path);

      states.select("title")
        .text(function(d) {
          return d.properties.name;
        });
    },
    getFormat: function(format) {
      if (typeof format === 'function') {
        return format;
      }

      var f = eval('f = ' + format);
      if (typeof f !== 'function') {
        return d3.format(f || ",");
      } else {
        return f;
      }
    },
    getColumn: function($el, key) {
      if (!key) return;

      var columns = $el.data('cartogram-columns');
      var column = {
        name: key
      };
      if (columns.hasOwnProperty(key)) {
        column = columns[key];
      }
      column.format = this.getFormat(column.format);
      return column;
    },
    getValue: function(column) {
      return function(d) {
        return +d.properties[column.name];
      };
    },
    update: function($el) {
      var map = $el.data('cartogram-map');
      var carto = map.carto,
        topology = map.topology,
        geometries = map.geometries,
        proj = map.proj,
        states = map.states;

      if(states === undefined) return;

      var scaleColumn = this.getColumn($el, $el.data('cartogram-scaleBy')),
        scaleValue = this.getValue(scaleColumn),
        colorColumn = this.getColumn($el, $el.data('cartogram-colorBy')) || scaleColumn,
        colorValue = this.getValue(colorColumn);

      var scaleValues = states.data()
        .map(scaleValue)
        .filter(function(n) {
          return isFinite(n);
        })
        .sort(d3.ascending),
        colorValues = states.data()
        .map(colorValue)
        .filter(function(n) {
          return isFinite(n);
        })
        .sort(d3.ascending),
        lo = colorValues[0],
        hi = colorValues[colorValues.length - 1];

      if(scaleValues.length * colorValues.length === 0) {
        return this.reset($el);
      }

      var color = d3.scale.linear()
        .range($el.data('colors'))
        .domain(lo < 0 ? [lo, 0, hi] : [lo, d3.mean(colorValues), hi]);

      // normalize the scale to positive numbers
      var scale = d3.scale.linear()
        .domain([scaleValues[0], scaleValues[scaleValues.length - 1]])
        .range([1, 1000]);

      // tell the cartogram to use the scaled values
      carto.value(function(d) {
        return scale(scaleValue(d));
      });

      // generate the new features, pre-projected
      var features = carto(topology, geometries).features;

      // update the data
      states.data(features)
        .select("title")
        .text(function(d) {
           var arr = [
              d.properties.name,
              scaleColumn.title + ": " + scaleColumn.format(scaleValue(d))
            ];
          if(scaleColumn !== colorColumn) {
            arr.push(colorColumn.title + ": " + scaleColumn.format(colorValue(d)));
          }
          return arr.join("\n");
        });

      states.transition()
        .duration(750)
        .ease("linear")
        .attr("fill", function(d) {
          return color(colorValue(d));
        })
        .attr("d", carto.path);
    }
  });
  Shiny.outputBindings.register(cartogramBinding, "cartogram-output-binding");

  Shiny.addCustomMessageHandler('cartogram', function(data) {
    var mapId = data.mapId;
    var map = maps[mapId];
    if (!map)
      return;

    if (methods[data.method]) {
      methods[data.method].apply(map, data.args);
    } else {
      throw new Error('Unknown method ' + data.method);
    }
  });

  var methods = {};

  methods.setView = function(lat, lng, zoom, forceReset) {
    this.data('cartogram-map').zoom.translate([lat, lng]).scale(zoom);
    if (forceRest) {
      cartogramBinding.reset(this);
    }
  };

  var nestRollup = function(data) {
      var result = {};

      data.name.forEach(function(name, i) {
        var elem = {};
        Object.keys(data).forEach(function(key) {
          elem[key] = data[key][i];
        });
        result[name] = elem;
      });

      return result;
    },
    updateAttribute = function($el, data, attribute) {
      $el.data('cartogram-' + attribute, data);
      if(attribute === 'data') cartogramBinding.reset($el);
      cartogramBinding.update($el);
    };

  methods.setData = function(data) {
    updateAttribute(this, nestRollup(data), 'data');
  };

  methods.defineColumns = function(data) {
    updateAttribute(this, nestRollup(data), 'columns');
  };

  methods.scaleBy = function(column) {
    updateAttribute(this, column, 'scaleBy');
  };

  methods.colorBy = function(column) {
    updateAttribute(this, column, 'colorBy');
  };
})();
<commit_msg>more error checking<commit_after>(function() {
  var maps = {};

  var cartogramBinding = new Shiny.OutputBinding();
  $.extend(cartogramBinding, {
    find: function(scope) {
      return $(scope).find(".cartogram-map-output");
    },
    renderValue: function(el, data) {
      var $el = $(el);

      var map = $el.data('cartogram-map');
      if (!map) {
        // A new map was detected. Create it, initialize supporting data
        // structures, and hook up event handlers.
        var id = this.getId(el);
        maps[id] = $el;

        var svg = d3.select('#' + id + ' svg'),
          zoom = d3.behavior.zoom()
          .translate([-38, 32])
          .scale(0.94)
          .scaleExtent([0.5, 10.0])
          .on("zoom", updateZoom),
          layer = svg.append("g"),
          states = layer.append("g")
          .selectAll("path");

        updateZoom();

        function updateZoom() {
          var scale = zoom.scale();
          layer.attr("transform",
            "translate(" + zoom.translate() + ") " +
            "scale(" + [scale, scale] + ")");
        }

        var proj = d3.geo.albersUsa(),
          topology,
          geometries,
          carto = d3.cartogram()
          .projection(proj)
          .properties(function(d) {
            var data = $el.data('cartogram-data') || {};
            return data[d.id];
          })
          .value(function(d) {
            return +d.properties.scale;
          });

        $el.data('cartogram-map', {});

        d3.json($el.data('topojson'), function(topo) {
          topology = topo;
          geometries = topology.objects.states.geometries;

          var features = carto.features(topology, geometries),
            path = d3.geo.path()
            .projection(proj);

          states = states.data(features)
            .enter()
            .append("path")
            .attr("class", "state")
            .attr("id", function(d) {
              //return d.properties.NAME;
              return d.id;
            })
            .attr("fill", "#fafafa")
            .attr("d", path);

          states.append("title");

          $el.data('cartogram-map', {
            topology: topology,
            geometries: geometries,
            states: states,
            zoom: zoom,
            carto: carto,
            proj: proj
          });
        });
      }
    },
    reset: function($el) {
      var map = $el.data('cartogram-map');
      var carto = map.carto,
        topology = map.topology,
        geometries = map.geometries,
        proj = map.proj,
        states = map.states;

      if(topology === undefined || geometries === undefined) return;

      var features = carto.features(topology, geometries),
        path = d3.geo.path()
        .projection(proj);

      states.data(features)
        .transition()
        .duration(750)
        .ease("linear")
        .attr("fill", "#fafafa")
        .attr("d", path);

      states.select("title")
        .text(function(d) {
          return d.properties.name;
        });
    },
    getFormat: function(format) {
      if (typeof format === 'function') {
        return format;
      }

      var f = eval('f = ' + format);
      if (typeof f !== 'function') {
        return d3.format(f || ",");
      } else {
        return f;
      }
    },
    getColumn: function($el, key) {
      if (!key || typeof key !== 'string') return;

      var columns = $el.data('cartogram-columns');
      var column = {
        name: key
      };
      if (columns.hasOwnProperty(key)) {
        column = columns[key];
      }
      column.format = this.getFormat(column.format);
      return column;
    },
    getValue: function(column) {
      return function(d) {
        return +d.properties[column.name];
      };
    },
    update: function($el) {
      var map = $el.data('cartogram-map');
      var carto = map.carto,
        topology = map.topology,
        geometries = map.geometries,
        proj = map.proj,
        states = map.states;

      if(states === undefined) return;

      var scaleColumn = this.getColumn($el, $el.data('cartogram-scaleBy')),
        scaleValue = this.getValue(scaleColumn),
        colorColumn = this.getColumn($el, $el.data('cartogram-colorBy')) || scaleColumn,
        colorValue = this.getValue(colorColumn);

      if(scaleColumn === undefined) this.reset($el);

      var scaleValues = states.data()
        .map(scaleValue)
        .filter(function(n) {
          return isFinite(n);
        })
        .sort(d3.ascending),
        colorValues = states.data()
        .map(colorValue)
        .filter(function(n) {
          return isFinite(n);
        })
        .sort(d3.ascending),
        lo = colorValues[0],
        hi = colorValues[colorValues.length - 1];

      if(scaleValues.length * colorValues.length === 0) {
        return this.reset($el);
      }

      var color = d3.scale.linear()
        .range($el.data('colors'))
        .domain(lo < 0 ? [lo, 0, hi] : [lo, d3.mean(colorValues), hi]);

      // normalize the scale to positive numbers
      var scale = d3.scale.linear()
        .domain([scaleValues[0], scaleValues[scaleValues.length - 1]])
        .range([1, 1000]);

      // tell the cartogram to use the scaled values
      carto.value(function(d) {
        return scale(scaleValue(d));
      });

      // generate the new features, pre-projected
      var features = carto(topology, geometries).features;

      // update the data
      states.data(features)
        .select("title")
        .text(function(d) {
           var arr = [
              d.properties.name,
              scaleColumn.title + ": " + scaleColumn.format(scaleValue(d))
            ];
          if(scaleColumn !== colorColumn) {
            arr.push(colorColumn.title + ": " + scaleColumn.format(colorValue(d)));
          }
          return arr.join("\n");
        });

      states.transition()
        .duration(750)
        .ease("linear")
        .attr("fill", function(d) {
          return color(colorValue(d));
        })
        .attr("d", carto.path);
    }
  });
  Shiny.outputBindings.register(cartogramBinding, "cartogram-output-binding");

  Shiny.addCustomMessageHandler('cartogram', function(data) {
    var mapId = data.mapId;
    var map = maps[mapId];
    if (!map)
      return;

    if (methods[data.method]) {
      methods[data.method].apply(map, data.args);
    } else {
      throw new Error('Unknown method ' + data.method);
    }
  });

  var methods = {};

  methods.setView = function(lat, lng, zoom, forceReset) {
    this.data('cartogram-map').zoom.translate([lat, lng]).scale(zoom);
    if (forceRest) {
      cartogramBinding.reset(this);
    }
  };

  var nestRollup = function(data) {
      var result = {};

      data.name.forEach(function(name, i) {
        var elem = {};
        Object.keys(data).forEach(function(key) {
          elem[key] = data[key][i];
        });
        result[name] = elem;
      });

      return result;
    },
    updateAttribute = function($el, data, attribute) {
      $el.data('cartogram-' + attribute, data);
      if(attribute === 'data') cartogramBinding.reset($el);
      cartogramBinding.update($el);
    };

  methods.setData = function(data) {
    updateAttribute(this, nestRollup(data), 'data');
  };

  methods.defineColumns = function(data) {
    updateAttribute(this, nestRollup(data), 'columns');
  };

  methods.scaleBy = function(column) {
    updateAttribute(this, column, 'scaleBy');
  };

  methods.colorBy = function(column) {
    updateAttribute(this, column, 'colorBy');
  };
})();
<|endoftext|>