<commit_before>import $ from 'blingblingjs'
import hotkeys from 'hotkeys-js'
import { getStyle, showHideSelected } from '../utilities/'

const key_events = 'up,down,left,right'
  .split(',')
  .reduce((events, event) =>
    `${events},${event},shift+${event}`
  , '')
  .substring(1)

const command_events = 'cmd+up,cmd+down'

export function Font(selector) {
  hotkeys(key_events, (e, handler) => {
    if (e.cancelBubble) return

    e.preventDefault()

    let selectedNodes = $(selector)
      , keys = handler.key.split('+')

    if (keys.includes('left') || keys.includes('right'))
      keys.includes('shift')
        ? changeKerning(selectedNodes, handler.key)
        : changeAlignment(selectedNodes, handler.key)
    else
      keys.includes('shift')
        ? changeLeading(selectedNodes, handler.key)
        : changeFontSize(selectedNodes, handler.key)
  })

  hotkeys(command_events, (e, handler) => {
    e.preventDefault()
    let keys = handler.key.split('+')
    changeFontWeight($(selector), keys.includes('up') ? 'up' : 'down')
  })

  hotkeys('cmd+b', e => {
    $(selector).forEach(el =>
      el.style.fontWeight =
        el.style.fontWeight == 'bold'
          ? null
          : 'bold')
  })

  hotkeys('cmd+i', e => {
    $(selector).forEach(el =>
      el.style.fontStyle =
        el.style.fontStyle == 'italic'
          ? null
          : 'italic')
  })

  return () => {
    hotkeys.unbind(key_events)
    hotkeys.unbind(command_events)
    hotkeys.unbind('cmd+b,cmd+i')
    hotkeys.unbind('up,down,left,right')
  }
}

export function changeLeading(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'lineHeight',
      current:  parseInt(getStyle(el, 'lineHeight')),
      amount:   1,
      negative: direction.split('+').includes('down'),
    }))
    .map(payload =>
      Object.assign(payload, {
        current: payload.current == 'normal' || isNaN(payload.current)
          ? 1.14 * parseInt(getStyle(payload.el, 'fontSize')) // document this choice
          : payload.current
      }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.negative
          ? payload.current - payload.amount
          : payload.current + payload.amount
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = `${value}px`)
}

export function changeKerning(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'letterSpacing',
      current:  parseFloat(getStyle(el, 'letterSpacing')),
      amount:   .1,
      negative: direction.split('+').includes('left'),
    }))
    .map(payload =>
      Object.assign(payload, {
        current: payload.current == 'normal' || isNaN(payload.current)
          ? 0
          : payload.current
      }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.negative
          ? payload.current - payload.amount
          : payload.current + payload.amount
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = `${value <= -2 ? -2 : value}px`)
}

export function changeFontSize(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'fontSize',
      current:  parseInt(getStyle(el, 'fontSize')),
      amount:   direction.split('+').includes('shift') ? 10 : 1,
      negative: direction.split('+').includes('down'),
    }))
    .map(payload =>
      Object.assign(payload, {
        font_size: payload.negative
          ? payload.current - payload.amount
          : payload.current + payload.amount
      }))
    .forEach(({el, style, font_size}) =>
      el.style[style] = `${font_size <= 6 ? 6 : font_size}px`)
}

const weightMap = {
  normal: 2,
  bold:   5,
  light:  0,
  "": 2,
  "100":0,"200":1,"300":2,"400":3,"500":4,"600":5,"700":6,"800":7,"900":8
}
const weightOptions = [100,200,300,400,500,600,700,800,900]

export function changeFontWeight(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'fontWeight',
      current:  getStyle(el, 'fontWeight'),
      direction: direction.split('+').includes('down'),
    }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.direction
          ? weightMap[payload.current] - 1
          : weightMap[payload.current] + 1
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = weightOptions[value < 0 ? 0 : value >= weightOptions.length
        ? weightOptions.length
        : value
      ])
}

const alignMap = {
  start: 0,
  left: 0,
  center: 1,
  right: 2,
}
const alignOptions = ['left','center','right']

export function changeAlignment(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'textAlign',
      current:  getStyle(el, 'textAlign'),
      direction: direction.split('+').includes('left'),
    }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.direction
          ? alignMap[payload.current] - 1
          : alignMap[payload.current] + 1
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = alignOptions[value < 0 ? 0 : value >= 2 ? 2: value])
}
<commit_msg>fixes letter spacing going into a large float<commit_after>import $ from 'blingblingjs'
import hotkeys from 'hotkeys-js'
import { getStyle, showHideSelected } from '../utilities/'

const key_events = 'up,down,left,right'
  .split(',')
  .reduce((events, event) =>
    `${events},${event},shift+${event}`
  , '')
  .substring(1)

const command_events = 'cmd+up,cmd+down'

export function Font(selector) {
  hotkeys(key_events, (e, handler) => {
    if (e.cancelBubble) return

    e.preventDefault()

    let selectedNodes = $(selector)
      , keys = handler.key.split('+')

    if (keys.includes('left') || keys.includes('right'))
      keys.includes('shift')
        ? changeKerning(selectedNodes, handler.key)
        : changeAlignment(selectedNodes, handler.key)
    else
      keys.includes('shift')
        ? changeLeading(selectedNodes, handler.key)
        : changeFontSize(selectedNodes, handler.key)
  })

  hotkeys(command_events, (e, handler) => {
    e.preventDefault()
    let keys = handler.key.split('+')
    changeFontWeight($(selector), keys.includes('up') ? 'up' : 'down')
  })

  hotkeys('cmd+b', e => {
    $(selector).forEach(el =>
      el.style.fontWeight =
        el.style.fontWeight == 'bold'
          ? null
          : 'bold')
  })

  hotkeys('cmd+i', e => {
    $(selector).forEach(el =>
      el.style.fontStyle =
        el.style.fontStyle == 'italic'
          ? null
          : 'italic')
  })

  return () => {
    hotkeys.unbind(key_events)
    hotkeys.unbind(command_events)
    hotkeys.unbind('cmd+b,cmd+i')
    hotkeys.unbind('up,down,left,right')
  }
}

export function changeLeading(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'lineHeight',
      current:  parseInt(getStyle(el, 'lineHeight')),
      amount:   1,
      negative: direction.split('+').includes('down'),
    }))
    .map(payload =>
      Object.assign(payload, {
        current: payload.current == 'normal' || isNaN(payload.current)
          ? 1.14 * parseInt(getStyle(payload.el, 'fontSize')) // document this choice
          : payload.current
      }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.negative
          ? payload.current - payload.amount
          : payload.current + payload.amount
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = `${value}px`)
}

export function changeKerning(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'letterSpacing',
      current:  parseFloat(getStyle(el, 'letterSpacing')),
      amount:   .1,
      negative: direction.split('+').includes('left'),
    }))
    .map(payload =>
      Object.assign(payload, {
        current: payload.current == 'normal' || isNaN(payload.current)
          ? 0
          : payload.current
      }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.negative
          ? (payload.current - payload.amount).toFixed(2)
          : (payload.current + payload.amount).toFixed(2)
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = `${value <= -2 ? -2 : value}px`)
}

export function changeFontSize(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'fontSize',
      current:  parseInt(getStyle(el, 'fontSize')),
      amount:   direction.split('+').includes('shift') ? 10 : 1,
      negative: direction.split('+').includes('down'),
    }))
    .map(payload =>
      Object.assign(payload, {
        font_size: payload.negative
          ? payload.current - payload.amount
          : payload.current + payload.amount
      }))
    .forEach(({el, style, font_size}) =>
      el.style[style] = `${font_size <= 6 ? 6 : font_size}px`)
}

const weightMap = {
  normal: 2,
  bold:   5,
  light:  0,
  "": 2,
  "100":0,"200":1,"300":2,"400":3,"500":4,"600":5,"700":6,"800":7,"900":8
}
const weightOptions = [100,200,300,400,500,600,700,800,900]

export function changeFontWeight(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'fontWeight',
      current:  getStyle(el, 'fontWeight'),
      direction: direction.split('+').includes('down'),
    }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.direction
          ? weightMap[payload.current] - 1
          : weightMap[payload.current] + 1
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = weightOptions[value < 0 ? 0 : value >= weightOptions.length
        ? weightOptions.length
        : value
      ])
}

const alignMap = {
  start: 0,
  left: 0,
  center: 1,
  right: 2,
}
const alignOptions = ['left','center','right']

export function changeAlignment(els, direction) {
  els
    .map(el => showHideSelected(el))
    .map(el => ({
      el,
      style:    'textAlign',
      current:  getStyle(el, 'textAlign'),
      direction: direction.split('+').includes('left'),
    }))
    .map(payload =>
      Object.assign(payload, {
        value: payload.direction
          ? alignMap[payload.current] - 1
          : alignMap[payload.current] + 1
      }))
    .forEach(({el, style, value}) =>
      el.style[style] = alignOptions[value < 0 ? 0 : value >= 2 ? 2: value])
}
<|endoftext|>


<commit_before>var fs = require( 'fs' )
var MBR = require( 'mbr' )
var GPT = require( '..' )
var inspect = require( '../test/inspect' )

var argv = process.argv.slice( 2 )
var devicePath = argv.shift()

if( !devicePath ) {
  console.error(`
  Usage: node example/verify <device>

  Examples:
    - Linux / Mac OS: node example/verify /dev/rdisk2
    - Windows: node example/verify \\.\PhysicalDrive0

  NOTE: This assumes a block size of 512 bytes
`)
  process.exit(1)
}

console.log( '' )

var blockSize = 512
var fd = null

try {
  fd = fs.openSync( devicePath, 'r' )
} catch( error ) {
  console.error( 'Couldn\'t open device for reading:\n', error.message )
  process.exit( 1 )
}

function readMBR() {
  var buffer = Buffer.alloc( 512 )
  fs.readSync( fd, buffer, 0, buffer.length, 0 )
  return MBR.parse( buffer )
}

function readPrimaryGPT(efiPart) {

  // NOTE: You'll need to know / determine the logical block size of the storage device;
  // For the sake of brevity, we'll just go with the still most common 512 bytes
  var gpt = new GPT({ blockSize: 512 })
  var offset = efiPart.firstLBA * gpt.blockSize
  // The default GPT is 33 blocks in length (1 block header, 32 block table)
  var buffer = Buffer.alloc( 33 * gpt.blockSize )

  fs.readSync( fd, buffer, 0, buffer.length, offset )

  return GPT.parse( buffer )

}

// function readPrimaryGPT(efiPart) {

//   // NOTE: You'll need to know / determine the logical block size of the storage device;
//   // For the sake of brevity, we'll just go with the still most common 512 bytes
//   var gpt = new GPT({ blockSize: 512 })

//   // First, we need to read & parse the GPT header, which will declare various
//   // sizes and offsets for us to calculate where & how long the table and backup are
//   var offset = efiPart.firstLBA * gpt.blockSize
//   var headerBuffer = Buffer.alloc( gpt.blockSize )

//   fs.readSync( fd, headerBuffer, 0, headerBuffer.length, offset )
//   gpt.parseHeader( headerBuffer )

//   // Now on to reading the actual partition table
//   var tableBuffer = Buffer.alloc( gpt.tableSize )
//   var tableOffset = gpt.tableOffset * gpt.blockSize
//   fs.readSync( fd, tableBuffer, 0, tableBuffer.length, tableOffset )

//   // We need to parse the first 4 partition entries & the rest separately
//   // as the first 4 table entries always occupy one block,
//   // with the rest following in subsequent blocks
//   gpt.parseTable( tableBuffer, 0, gpt.blockSize )
//   gpt.parseTable( tableBuffer, gpt.blockSize, gpt.tableSize )

//   return gpt

// }

function readBackupGPT(primaryGPT) {

  var backupGPT = new GPT({ blockSize: primaryGPT.blockSize })
  var buffer = Buffer.alloc( 33 * primaryGPT.blockSize )
  var offset = ( ( primaryGPT.backupLBA - 32 ) * blockSize )

  fs.readSync( fd, buffer, 0, buffer.length, offset )
  backupGPT.parseBackup( buffer )

  return backupGPT

}

var mbr = readMBR()

console.log( 'Master Boot Record:', inspect( mbr ) )
console.log( '' )

var efiPart = mbr.getEFIPart()

if( efiPart == null ) {
  return console.error( 'No EFI partition found' )
}

console.log( 'EFI Parition:', inspect( efiPart ) )
console.log( '' )

var primaryGPT = readPrimaryGPT(efiPart)

console.log( 'Primary:', inspect( primaryGPT ) )
console.log( '' )

var backupGPT = readBackupGPT(primaryGPT)

console.log( 'Backup:', inspect( backupGPT ) )
console.log( '' )

// Check header & table checksums for primary and backup GPT
var isPrimaryValid = primaryGPT.verify() // true
var isBackupValid = backupGPT.verify() // true

// Compare primary & backup GPT table checksums to
// verify both are in the same state
// NOTE: If there's a `gpt.headerChecksum` match, something's wrong,
// as the primary and backup should refer to each other in offsets
var checksumsMatch = primaryGPT.tableChecksum === backupGPT.tableChecksum &&
  primaryGPT.headerChecksum !== backupGPT.headerChecksum

if( isPrimaryValid && isBackupValid && checksumsMatch ) {
  return console.log('[OK]')
}

if( !isPrimaryValid ) {
  console.error( `[ERROR]: Primary GPT checksum mismatch` )
  process.exit( 1 )
}

if( !isBackupValid ) {
  console.error( `[ERROR]: Backup GPT checksum mismatch` )
  process.exit( 1 )
}

if( !checksumsMatch ) {
  console.error( `[ERROR]: Primary & Backup GPT mismatch` )
  process.exit( 1 )
}

fs.closeSync( fd )
<commit_msg>verify: Use blocksize consistently<commit_after>var fs = require( 'fs' )
var MBR = require( 'mbr' )
var GPT = require( '..' )
var inspect = require( '../test/inspect' )

var argv = process.argv.slice( 2 )
var devicePath = argv.shift()

if( !devicePath ) {
  console.error(`
  Usage: node example/verify <device>

  Examples:
    - Linux / Mac OS: node example/verify /dev/rdisk2
    - Windows: node example/verify \\.\PhysicalDrive0

  NOTE: This assumes a block size of 512 bytes
`)
  process.exit(1)
}

console.log( '' )

var blockSize = 512
var fd = null

try {
  fd = fs.openSync( devicePath, 'r' )
} catch( error ) {
  console.error( 'Couldn\'t open device for reading:\n', error.message )
  process.exit( 1 )
}

function readMBR() {
  var buffer = Buffer.alloc( blockSize )
  fs.readSync( fd, buffer, 0, buffer.length, 0 )
  return MBR.parse( buffer )
}

function readPrimaryGPT(efiPart) {

  // NOTE: You'll need to know / determine the logical block size of the storage device;
  // For the sake of brevity, we'll just go with the still most common 512 bytes
  var gpt = new GPT({ blockSize: blockSize })
  var offset = efiPart.firstLBA * gpt.blockSize
  // The default GPT is 33 blocks in length (1 block header, 32 block table)
  var buffer = Buffer.alloc( 33 * gpt.blockSize )

  fs.readSync( fd, buffer, 0, buffer.length, offset )

  return GPT.parse( buffer )

}

// function readPrimaryGPT(efiPart) {

//   // NOTE: You'll need to know / determine the logical block size of the storage device;
//   // For the sake of brevity, we'll just go with the still most common 512 bytes
//   var gpt = new GPT({ blockSize: 512 })

//   // First, we need to read & parse the GPT header, which will declare various
//   // sizes and offsets for us to calculate where & how long the table and backup are
//   var offset = efiPart.firstLBA * gpt.blockSize
//   var headerBuffer = Buffer.alloc( gpt.blockSize )

//   fs.readSync( fd, headerBuffer, 0, headerBuffer.length, offset )
//   gpt.parseHeader( headerBuffer )

//   // Now on to reading the actual partition table
//   var tableBuffer = Buffer.alloc( gpt.tableSize )
//   var tableOffset = gpt.tableOffset * gpt.blockSize
//   fs.readSync( fd, tableBuffer, 0, tableBuffer.length, tableOffset )

//   // We need to parse the first 4 partition entries & the rest separately
//   // as the first 4 table entries always occupy one block,
//   // with the rest following in subsequent blocks
//   gpt.parseTable( tableBuffer, 0, gpt.blockSize )
//   gpt.parseTable( tableBuffer, gpt.blockSize, gpt.tableSize )

//   return gpt

// }

function readBackupGPT(primaryGPT) {

  var backupGPT = new GPT({ blockSize: primaryGPT.blockSize })
  var buffer = Buffer.alloc( 33 * primaryGPT.blockSize )
  var offset = ( primaryGPT.backupLBA - 32 ) * primaryGPT.blockSize

  fs.readSync( fd, buffer, 0, buffer.length, offset )
  backupGPT.parseBackup( buffer )

  return backupGPT

}

var mbr = readMBR()

console.log( 'Master Boot Record:', inspect( mbr ) )
console.log( '' )

var efiPart = mbr.getEFIPart()

if( efiPart == null ) {
  return console.error( 'No EFI partition found' )
}

console.log( 'EFI Parition:', inspect( efiPart ) )
console.log( '' )

var primaryGPT = readPrimaryGPT(efiPart)

console.log( 'Primary:', inspect( primaryGPT ) )
console.log( '' )

var backupGPT = readBackupGPT(primaryGPT)

console.log( 'Backup:', inspect( backupGPT ) )
console.log( '' )

// Check header & table checksums for primary and backup GPT
var isPrimaryValid = primaryGPT.verify() // true
var isBackupValid = backupGPT.verify() // true

// Compare primary & backup GPT table checksums to
// verify both are in the same state
// NOTE: If there's a `gpt.headerChecksum` match, something's wrong,
// as the primary and backup should refer to each other in offsets
var checksumsMatch = primaryGPT.tableChecksum === backupGPT.tableChecksum &&
  primaryGPT.headerChecksum !== backupGPT.headerChecksum

if( isPrimaryValid && isBackupValid && checksumsMatch ) {
  return console.log('[OK]')
}

if( !isPrimaryValid ) {
  console.error( `[ERROR]: Primary GPT checksum mismatch` )
  process.exit( 1 )
}

if( !isBackupValid ) {
  console.error( `[ERROR]: Backup GPT checksum mismatch` )
  process.exit( 1 )
}

if( !checksumsMatch ) {
  console.error( `[ERROR]: Primary & Backup GPT mismatch` )
  process.exit( 1 )
}

fs.closeSync( fd )
<|endoftext|>


<commit_before>(function () {
    var $$ = {
        on: function(root, eventName, selector, fn) {
            root.addEventListener(eventName, function(event) {
                var target = event.target.closest(selector);
                if (root.contains(target)) {
                    fn.call(target, event);
                }
            });
        },
        show: function(element) {
            element.style.display = 'block';
        },
        hide: function(element) {
            element.style.display = 'none';
        },
        toggle: function(element, value) {
            if (value) {
                $$.show(element);
            } else {
                $$.hide(element);
            }
        },
        visible: function(element) {
            var style = getComputedStyle(element);
            return style.display !== 'none';
        },
        executeScripts: function(root) {
            root.querySelectorAll('script').forEach(function(e) {
                var clone = document.createElement('script');
                clone.src = e.src;
                root.appendChild(clone);
            });
        },
    };

    var onKeyDown = function(event) {
        if (event.keyCode === 27) {
            djdt.hide_one_level();
        }
    };

    var ajax = function(url, init) {
        init = Object.assign({credentials: 'same-origin'}, init);
        return fetch(url, init).then(function(response) {
            if (response.ok) {
                return response.text();
            } else {
                var win = document.querySelector('#djDebugWindow');
                win.innerHTML = '
»
'+response.status+': '+response.statusText+'

';
                $$.show(win);
                return Promise.reject();
            }
        });
    };

    var djdt = {
        handleDragged: false,
        init: function() {
            var djDebug = document.querySelector('#djDebug');
            $$.show(djDebug);
            $$.on(djDebug.querySelector('#djDebugPanelList'), 'click', 'li a', function(event) {
                event.preventDefault();
                if (!this.className) {
                    return;
                }
                var current = djDebug.querySelector('#' + this.className);
                if ($$.visible(current)) {
                    djdt.hide_panels();
                } else {
                    djdt.hide_panels();

                    $$.show(current);
                    this.parentElement.classList.add('djdt-active');

                    var inner = current.querySelector('.djDebugPanelContent .djdt-scroll'),
                        store_id = djDebug.dataset.storeId;
                    if (store_id && inner.children.length === 0) {
                        var url = djDebug.dataset.renderPanelUrl;
                        var url_params = new URLSearchParams();
                        url_params.append('store_id', store_id);
                        url_params.append('panel_id', this.className);
                        url += '?' + url_params.toString();
                        ajax(url).then(function(body) {
                            inner.previousElementSibling.remove();  // Remove AJAX loader
                            inner.innerHTML = body;
                            $$.executeScripts(inner);
                        });
                    }
                }
            });
            $$.on(djDebug, 'click', 'a.djDebugClose', function(event) {
                event.preventDefault();
                djdt.hide_one_level();
            });
            $$.on(djDebug, 'click', '.djDebugPanelButton input[type=checkbox]', function() {
                djdt.cookie.set(this.dataset.cookie, this.checked ? 'on' : 'off', {
                    path: '/',
                    expires: 10
                });
            });

            // Used by the SQL and template panels
            $$.on(djDebug, 'click', '.remoteCall', function(event) {
                event.preventDefault();

                var ajax_data = {};

                if (this.tagName === 'BUTTON') {
                    var form = this.closest('form');
                    ajax_data.url = this.getAttribute('formaction');

                    if (form) {
                        ajax_data.body = new FormData(form);
                        ajax_data.method = form.getAttribute('method') || 'POST';
                    }
                }

                if (this.tagName === 'A') {
                    ajax_data.url = this.getAttribute('href');
                }

                ajax(ajax_data.url, ajax_data).then(function(body) {
                    var win = djDebug.querySelector('#djDebugWindow');
                    win.innerHTML = body;
                    $$.executeScripts(win);
                    $$.show(win);
                });
            });

            // Used by the cache, profiling and SQL panels
            $$.on(djDebug, 'click', 'a.djToggleSwitch', function(event) {
                event.preventDefault();
                var self = this;
                var id = this.dataset.toggleId;
                var open_me = this.textContent === this.dataset.toggleOpen;
                var name = this.dataset.toggleName;
                var container = this.closest('.djDebugPanelContent').querySelector('#' + name + '_' + id);
                container.querySelectorAll('.djDebugCollapsed').forEach(function(e) {
                    $$.toggle(e, open_me);
                });
                container.querySelectorAll('.djDebugUncollapsed').forEach(function(e) {
                    $$.toggle(e, !open_me);
                });
                this.closest('.djDebugPanelContent').querySelectorAll('.djToggleDetails_' + id).forEach(function(e) {
                    if (open_me) {
                        e.classList.add('djSelected');
                        e.classList.remove('djUnselected');
                        self.textContent = self.dataset.toggleClose;
                    } else {
                        e.classList.remove('djSelected');
                        e.classList.add('djUnselected');
                        self.textContent = self.dataset.toggleOpen;
                    }
                    var switch_ = e.querySelector('.djToggleSwitch')
                    if (switch_) switch_.textContent = self.textContent;
                });
            });

            djDebug.querySelector('#djHideToolBarButton').addEventListener('click', function(event) {
                event.preventDefault();
                djdt.hide_toolbar(true);
            });
            djDebug.querySelector('#djShowToolBarButton').addEventListener('click', function(event) {
                event.preventDefault();
                if (!djdt.handleDragged) {
                    djdt.show_toolbar();
                }
            });
            var startPageY, baseY;
            var handle = document.querySelector('#djDebugToolbarHandle');
            var onHandleMove = function(event) {
                // Chrome can send spurious mousemove events, so don't do anything unless the
                // cursor really moved.  Otherwise, it will be impossible to expand the toolbar
                // due to djdt.handleDragged being set to true.
                if (djdt.handleDragged || event.pageY !== startPageY) {
                    var top = baseY + event.pageY;

                    if (top < 0) {
                        top = 0;
                    } else if (top + handle.offsetHeight > window.innerHeight) {
                        top = window.innerHeight - handle.offsetHeight;
                    }

                    handle.style.top = top + 'px';
                    djdt.handleDragged = true;
                }
            };
            djDebug.querySelector('#djShowToolBarButton').addEventListener('mousedown', function(event) {
                event.preventDefault();
                startPageY = event.pageY;
                baseY = handle.offsetTop - startPageY;
                document.addEventListener('mousemove', onHandleMove);
            });
            document.addEventListener('mouseup', function (event) {
                document.removeEventListener('mousemove', onHandleMove);
                if (djdt.handleDragged) {
                    event.preventDefault();
                    djdt.cookie.set('djdttop', handle.offsetTop, {
                        path: '/',
                        expires: 10
                    });
                    setTimeout(function () {
                        djdt.handleDragged = false;
                    }, 10);
                }
            });
            if (djdt.cookie.get('djdt') === 'hide') {
                djdt.hide_toolbar(false);
            } else {
                djdt.show_toolbar();
            }
        },
        hide_panels: function() {
            var djDebug = document.getElementBy('djDebug');
            $$.hide(djDebug.querySelector('#djDebugWindow'));
            djDebug.querySelectorAll('.djdt-panelContent').forEach(function(e) {
                $$.hide(e);
            });
            djDebug.querySelectorAll('#djDebugToolbar li').forEach(function(e) {
                e.classList.remove('djdt-active');
            });
        },
        hide_toolbar: function(setCookie) {
            djdt.hide_panels();

            var djDebug = document.getElementBy('djDebug');
            $$.hide(djDebug.querySelector('#djDebugToolbar'));

            var handle = document.querySelector('#djDebugToolbarHandle');
            $$.show(handle);
            // set handle position
            var handleTop = djdt.cookie.get('djdttop');
            if (handleTop) {
                handleTop = Math.min(handleTop, window.innerHeight - handle.offsetHeight);
                handle.style.top = handleTop + 'px';
            }

            document.removeEventListener('keydown', onKeyDown);

            if (setCookie) {
                djdt.cookie.set('djdt', 'hide', {
                    path: '/',
                    expires: 10
                });
            }
        },
        hide_one_level: function() {
            var djDebug = document.getElementBy('djDebug');
            if ($$.visible(djDebug.querySelector('#djDebugWindow'))) {
                $$.hide(djDebug.querySelector('#djDebugWindow'));
            } else if (djDebug.querySelector('#djDebugToolbar li.djdt-active')) {
                djdt.hide_panels();
            } else {
                djdt.hide_toolbar(true);
            }
        },
        show_toolbar: function() {
            document.addEventListener('keydown', onKeyDown);
            var djDebug = document.getElementBy('djDebug');
            $$.hide(djDebug.querySelector('#djDebugToolbarHandle'));
            $$.show(djDebug.querySelector('#djDebugToolbar'));
            djdt.cookie.set('djdt', 'show', {
                path: '/',
                expires: 10
            });
        },
        cookie: {
            get: function(key){
                if (document.cookie.indexOf(key) === -1) return null;

                var cookieArray = document.cookie.split('; '),
                    cookies = {};

                cookieArray.forEach(function(e){
                    var parts = e.split('=');
                    cookies[ parts[0] ] = parts[1];
                });

                return cookies[ key ];
            },
            set: function(key, value, options){
                options = options || {};

                if (typeof options.expires === 'number') {
                    var days = options.expires, t = options.expires = new Date();
                    t.setDate(t.getDate() + days);
                }

                document.cookie = [
                    encodeURIComponent(key) + '=' + String(value),
                    options.expires ? '; expires=' + options.expires.toUTCString() : '',
                    options.path    ? '; path=' + options.path : '',
                    options.domain  ? '; domain=' + options.domain : '',
                    options.secure  ? '; secure' : ''
                ].join('');

                return value;
            }
        },
    };
    window.djdt = {
        show_toolbar: djdt.show_toolbar,
        hide_toolbar: djdt.hide_toolbar,
        init: djdt.init,
        close: djdt.hide_one_level,
        cookie: djdt.cookie,
    };
    document.addEventListener('DOMContentLoaded', djdt.init);
})();
<commit_msg>Fix typo: getElementBy → getElementById<commit_after>(function () {
    var $$ = {
        on: function(root, eventName, selector, fn) {
            root.addEventListener(eventName, function(event) {
                var target = event.target.closest(selector);
                if (root.contains(target)) {
                    fn.call(target, event);
                }
            });
        },
        show: function(element) {
            element.style.display = 'block';
        },
        hide: function(element) {
            element.style.display = 'none';
        },
        toggle: function(element, value) {
            if (value) {
                $$.show(element);
            } else {
                $$.hide(element);
            }
        },
        visible: function(element) {
            var style = getComputedStyle(element);
            return style.display !== 'none';
        },
        executeScripts: function(root) {
            root.querySelectorAll('script').forEach(function(e) {
                var clone = document.createElement('script');
                clone.src = e.src;
                root.appendChild(clone);
            });
        },
    };

    var onKeyDown = function(event) {
        if (event.keyCode === 27) {
            djdt.hide_one_level();
        }
    };

    var ajax = function(url, init) {
        init = Object.assign({credentials: 'same-origin'}, init);
        return fetch(url, init).then(function(response) {
            if (response.ok) {
                return response.text();
            } else {
                var win = document.querySelector('#djDebugWindow');
                win.innerHTML = '
»
'+response.status+': '+response.statusText+'

';
                $$.show(win);
                return Promise.reject();
            }
        });
    };

    var djdt = {
        handleDragged: false,
        init: function() {
            var djDebug = document.querySelector('#djDebug');
            $$.show(djDebug);
            $$.on(djDebug.querySelector('#djDebugPanelList'), 'click', 'li a', function(event) {
                event.preventDefault();
                if (!this.className) {
                    return;
                }
                var current = djDebug.querySelector('#' + this.className);
                if ($$.visible(current)) {
                    djdt.hide_panels();
                } else {
                    djdt.hide_panels();

                    $$.show(current);
                    this.parentElement.classList.add('djdt-active');

                    var inner = current.querySelector('.djDebugPanelContent .djdt-scroll'),
                        store_id = djDebug.dataset.storeId;
                    if (store_id && inner.children.length === 0) {
                        var url = djDebug.dataset.renderPanelUrl;
                        var url_params = new URLSearchParams();
                        url_params.append('store_id', store_id);
                        url_params.append('panel_id', this.className);
                        url += '?' + url_params.toString();
                        ajax(url).then(function(body) {
                            inner.previousElementSibling.remove();  // Remove AJAX loader
                            inner.innerHTML = body;
                            $$.executeScripts(inner);
                        });
                    }
                }
            });
            $$.on(djDebug, 'click', 'a.djDebugClose', function(event) {
                event.preventDefault();
                djdt.hide_one_level();
            });
            $$.on(djDebug, 'click', '.djDebugPanelButton input[type=checkbox]', function() {
                djdt.cookie.set(this.dataset.cookie, this.checked ? 'on' : 'off', {
                    path: '/',
                    expires: 10
                });
            });

            // Used by the SQL and template panels
            $$.on(djDebug, 'click', '.remoteCall', function(event) {
                event.preventDefault();

                var ajax_data = {};

                if (this.tagName === 'BUTTON') {
                    var form = this.closest('form');
                    ajax_data.url = this.getAttribute('formaction');

                    if (form) {
                        ajax_data.body = new FormData(form);
                        ajax_data.method = form.getAttribute('method') || 'POST';
                    }
                }

                if (this.tagName === 'A') {
                    ajax_data.url = this.getAttribute('href');
                }

                ajax(ajax_data.url, ajax_data).then(function(body) {
                    var win = djDebug.querySelector('#djDebugWindow');
                    win.innerHTML = body;
                    $$.executeScripts(win);
                    $$.show(win);
                });
            });

            // Used by the cache, profiling and SQL panels
            $$.on(djDebug, 'click', 'a.djToggleSwitch', function(event) {
                event.preventDefault();
                var self = this;
                var id = this.dataset.toggleId;
                var open_me = this.textContent === this.dataset.toggleOpen;
                var name = this.dataset.toggleName;
                var container = this.closest('.djDebugPanelContent').querySelector('#' + name + '_' + id);
                container.querySelectorAll('.djDebugCollapsed').forEach(function(e) {
                    $$.toggle(e, open_me);
                });
                container.querySelectorAll('.djDebugUncollapsed').forEach(function(e) {
                    $$.toggle(e, !open_me);
                });
                this.closest('.djDebugPanelContent').querySelectorAll('.djToggleDetails_' + id).forEach(function(e) {
                    if (open_me) {
                        e.classList.add('djSelected');
                        e.classList.remove('djUnselected');
                        self.textContent = self.dataset.toggleClose;
                    } else {
                        e.classList.remove('djSelected');
                        e.classList.add('djUnselected');
                        self.textContent = self.dataset.toggleOpen;
                    }
                    var switch_ = e.querySelector('.djToggleSwitch')
                    if (switch_) switch_.textContent = self.textContent;
                });
            });

            djDebug.querySelector('#djHideToolBarButton').addEventListener('click', function(event) {
                event.preventDefault();
                djdt.hide_toolbar(true);
            });
            djDebug.querySelector('#djShowToolBarButton').addEventListener('click', function(event) {
                event.preventDefault();
                if (!djdt.handleDragged) {
                    djdt.show_toolbar();
                }
            });
            var startPageY, baseY;
            var handle = document.querySelector('#djDebugToolbarHandle');
            var onHandleMove = function(event) {
                // Chrome can send spurious mousemove events, so don't do anything unless the
                // cursor really moved.  Otherwise, it will be impossible to expand the toolbar
                // due to djdt.handleDragged being set to true.
                if (djdt.handleDragged || event.pageY !== startPageY) {
                    var top = baseY + event.pageY;

                    if (top < 0) {
                        top = 0;
                    } else if (top + handle.offsetHeight > window.innerHeight) {
                        top = window.innerHeight - handle.offsetHeight;
                    }

                    handle.style.top = top + 'px';
                    djdt.handleDragged = true;
                }
            };
            djDebug.querySelector('#djShowToolBarButton').addEventListener('mousedown', function(event) {
                event.preventDefault();
                startPageY = event.pageY;
                baseY = handle.offsetTop - startPageY;
                document.addEventListener('mousemove', onHandleMove);
            });
            document.addEventListener('mouseup', function (event) {
                document.removeEventListener('mousemove', onHandleMove);
                if (djdt.handleDragged) {
                    event.preventDefault();
                    djdt.cookie.set('djdttop', handle.offsetTop, {
                        path: '/',
                        expires: 10
                    });
                    setTimeout(function () {
                        djdt.handleDragged = false;
                    }, 10);
                }
            });
            if (djdt.cookie.get('djdt') === 'hide') {
                djdt.hide_toolbar(false);
            } else {
                djdt.show_toolbar();
            }
        },
        hide_panels: function() {
            var djDebug = document.getElementById('djDebug');
            $$.hide(djDebug.querySelector('#djDebugWindow'));
            djDebug.querySelectorAll('.djdt-panelContent').forEach(function(e) {
                $$.hide(e);
            });
            djDebug.querySelectorAll('#djDebugToolbar li').forEach(function(e) {
                e.classList.remove('djdt-active');
            });
        },
        hide_toolbar: function(setCookie) {
            djdt.hide_panels();

            var djDebug = document.getElementById('djDebug');
            $$.hide(djDebug.querySelector('#djDebugToolbar'));

            var handle = document.querySelector('#djDebugToolbarHandle');
            $$.show(handle);
            // set handle position
            var handleTop = djdt.cookie.get('djdttop');
            if (handleTop) {
                handleTop = Math.min(handleTop, window.innerHeight - handle.offsetHeight);
                handle.style.top = handleTop + 'px';
            }

            document.removeEventListener('keydown', onKeyDown);

            if (setCookie) {
                djdt.cookie.set('djdt', 'hide', {
                    path: '/',
                    expires: 10
                });
            }
        },
        hide_one_level: function() {
            var djDebug = document.getElementById('djDebug');
            if ($$.visible(djDebug.querySelector('#djDebugWindow'))) {
                $$.hide(djDebug.querySelector('#djDebugWindow'));
            } else if (djDebug.querySelector('#djDebugToolbar li.djdt-active')) {
                djdt.hide_panels();
            } else {
                djdt.hide_toolbar(true);
            }
        },
        show_toolbar: function() {
            document.addEventListener('keydown', onKeyDown);
            var djDebug = document.getElementById('djDebug');
            $$.hide(djDebug.querySelector('#djDebugToolbarHandle'));
            $$.show(djDebug.querySelector('#djDebugToolbar'));
            djdt.cookie.set('djdt', 'show', {
                path: '/',
                expires: 10
            });
        },
        cookie: {
            get: function(key){
                if (document.cookie.indexOf(key) === -1) return null;

                var cookieArray = document.cookie.split('; '),
                    cookies = {};

                cookieArray.forEach(function(e){
                    var parts = e.split('=');
                    cookies[ parts[0] ] = parts[1];
                });

                return cookies[ key ];
            },
            set: function(key, value, options){
                options = options || {};

                if (typeof options.expires === 'number') {
                    var days = options.expires, t = options.expires = new Date();
                    t.setDate(t.getDate() + days);
                }

                document.cookie = [
                    encodeURIComponent(key) + '=' + String(value),
                    options.expires ? '; expires=' + options.expires.toUTCString() : '',
                    options.path    ? '; path=' + options.path : '',
                    options.domain  ? '; domain=' + options.domain : '',
                    options.secure  ? '; secure' : ''
                ].join('');

                return value;
            }
        },
    };
    window.djdt = {
        show_toolbar: djdt.show_toolbar,
        hide_toolbar: djdt.hide_toolbar,
        init: djdt.init,
        close: djdt.hide_one_level,
        cookie: djdt.cookie,
    };
    document.addEventListener('DOMContentLoaded', djdt.init);
})();
<|endoftext|>


<commit_before>/* global moment */
import {formatDate} from 'ghost/utils/date-formatting';

var PostSettingsMenuView = Ember.View.extend({
    templateName: 'post-settings-menu',

    publishedAtBinding: Ember.Binding.oneWay('controller.publishedAt'),

    datePlaceholder: Ember.computed('controller.publishedAt', function () {
        return formatDate(moment());
    })
});

export default PostSettingsMenuView;
<commit_msg>Remove PostSettingsMenuView.<commit_after><|endoftext|>


<commit_before>var fs = require('fs')
  , path = require('path')
  , kerouac = require('kerouac')
  , utils = require('../utils');

exports = module.exports = function(postsDB) {
  var dir = 'blog'
    , layout;

  var exts = [ '.md' ];

  function loadPost(page, next) {
    console.log('LOAD!')
    console.log(page);
    console.log(page.params);

    var q = {
      slug: page.params.slug,
      year: page.params.year,
      month: page.params.month,
      day: page.params.day
    }

    postsDB.read(q, function(err, post) {
      console.log(err);
      console.log(post);

      next();
    });
  }

  function findFile(page, next) {
    var file, ext
      , i, len;

    for (i = 0, len = exts.length; i < len; ++i) {
      ext = exts[i];
      file = path.resolve(dir, page.params.slug + ext);
      if (fs.existsSync(file)) {
        page.inputPath = file;
        return next();
      }
    }

    return next('route');
  }

  function meta(page, next) {
    page.locals.createdAt = page.createdAt;

    page.post = true;
    page.title = page.locals.title;
    page.tags = utils.tarray(page.locals.tags);
    next();
  }

  return [
    kerouac.manifest(),
    kerouac.canonicalURL(),
    loadPost,
    findFile,
    kerouac.timestamps(),
    kerouac.layout(layout),
    kerouac.loadContent(),
    meta,
    kerouac.render()
  ];
};

exports['@require'] = [
  'http://i.kerouacjs.org/blog/PostsDatabase'
];
<commit_msg>Use loadPost rather than findFile.<commit_after>var fs = require('fs')
  , path = require('path')
  , kerouac = require('kerouac')
  , utils = require('../utils');

exports = module.exports = function(postsDB) {
  var dir = 'blog'
    , layout;

  var exts = [ '.md' ];

  function loadPost(page, next) {
    var q = {
      slug: page.params.slug,
      year: page.params.year,
      month: page.params.month,
      day: page.params.day
    }

    postsDB.read(q, function(err, post) {
      page.locals.title = post.title;
      page.locals.createdAt = post.createdAt;
      page.locals.modifiedAt = post.modifiedAt;
      page.content = post.content;

      next();
    });
  }

  function findFile(page, next) {
    var file, ext
      , i, len;

    for (i = 0, len = exts.length; i < len; ++i) {
      ext = exts[i];
      file = path.resolve(dir, page.params.slug + ext);
      if (fs.existsSync(file)) {
        page.inputPath = file;
        return next();
      }
    }

    return next('route');
  }

  function meta(page, next) {
    page.locals.createdAt = page.createdAt;

    page.post = true;
    page.title = page.locals.title;
    page.tags = utils.tarray(page.locals.tags);
    next();
  }

  return [
    kerouac.manifest(),
    kerouac.canonicalURL(),
    loadPost,
    //findFile,
    //kerouac.timestamps(),
    kerouac.layout(layout),
    //kerouac.loadContent(),
    meta,
    kerouac.render()
  ];
};

exports['@require'] = [
  'http://i.kerouacjs.org/blog/PostsDatabase'
];
<|endoftext|>


<commit_before>/* global APP, $, JitsiMeetJS */

/**
 * Initialise global shortcuts.
 * Global shortcuts are shortcuts for features that don't have a button or
 * link associated with the action. In other words they represent actions
 * triggered _only_ with a shortcut.
 */
function initGlobalShortcuts() {

    KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
        APP.UI.showKeyboardShortcutsPanel(false);
    });

    KeyboardShortcut.registerShortcut("?", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.shortcut.help");
        APP.UI.toggleKeyboardShortcutsPanel();
    }, "keyboardShortcuts.toggleShortcuts");

    KeyboardShortcut.registerShortcut("R", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.raisedhand.toggled");
        APP.conference.maybeToggleRaisedHand();
    }, "keyboardShortcuts.raiseHand");

    KeyboardShortcut.registerShortcut("T", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
        APP.conference.muteAudio(true);
    }, "keyboardShortcuts.pushToTalk");

    /**
     * FIXME: Currently focus keys are directly implemented below in onkeyup.
     * They should be moved to the SmallVideo instead.
     */
    KeyboardShortcut._addShortcutToHelp("0", "keyboardShortcuts.focusLocal");
    KeyboardShortcut._addShortcutToHelp("1-9", "keyboardShortcuts.focusRemote");
}

/**
 * Map of shortcuts. When a shortcut is registered it enters the mapping.
 * @type {{}}
 */
let _shortcuts = {};

/**
 * Maps keycode to character, id of popover for given function and function.
 */
var KeyboardShortcut = {
    init: function () {
        initGlobalShortcuts();

        var self = this;
        window.onkeyup = function(e) {
            var key = self._getKeyboardKey(e).toUpperCase();
            var num = parseInt(key, 10);
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                if (_shortcuts.hasOwnProperty(key)) {
                    _shortcuts[key].function(e);
                }
                else if (!isNaN(num) && num >= 0 && num <= 9) {
                    APP.UI.clickOnVideo(num + 1);
                }
            //esc while the smileys are visible hides them
            } else if (key === "ESCAPE" &&
                $('#smileysContainer').is(':visible')) {
                APP.UI.toggleSmileys();
            }
        };

        window.onkeydown = function(e) {
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                var key = self._getKeyboardKey(e).toUpperCase();
                if(key === "T") {
                    if(APP.conference.isLocalAudioMuted())
                        APP.conference.muteAudio(false);
                }
            }
        };
        $('body').popover({ selector: '[data-toggle=popover]',
            trigger: 'click hover',
            content: function() {
                return this.getAttribute("content") +
                    self._getShortcut(this.getAttribute("shortcut"));
            }
        });
    },

    /**
     * Registers a new shortcut.
     *
     * @param shortcutChar the shortcut character triggering the action
     * @param shortcutAttr the "shortcut" html element attribute mappring an
     * element to this shortcut and used to show the shortcut character on the
     * element tooltip
     * @param exec the function to be executed when the shortcut is pressed
     * @param helpDescription the description of the shortcut that would appear
     * in the help menu
     */
    registerShortcut: function( shortcutChar,
                                shortcutAttr,
                                exec,
                                helpDescription) {
        _shortcuts[shortcutChar] = {
            character: shortcutChar,
            shortcutAttr: shortcutAttr,
            function: exec
        };

        if (helpDescription)
            this._addShortcutToHelp(shortcutChar, helpDescription);
    },

    /**
     * Unregisters a shortcut.
     *
     * @param shortcutChar unregisters the given shortcut, which means it will
     * no longer be usable
     */
    unregisterShortcut: function(shortcutChar) {
        _shortcuts.remove(shortcutChar);

        this._removeShortcutFromHelp(shortcutChar);
    },

    /**
     *
     * @param id indicates the popover associated with the shortcut
     * @returns {string} the keyboard shortcut used for the id given
     */
    _getShortcut: function (id) {
        for (var key in _shortcuts) {
            if (_shortcuts.hasOwnProperty(key)) {
                if (_shortcuts[key].shortcutAttr === id) {
                    return " (" + _shortcuts[key].character + ")";
                }
            }
        }
        return "";
    },
    /**
     * @param e a KeyboardEvent
     * @returns {string} e.key or something close if not supported
     */
    _getKeyboardKey: function (e) {
        if (typeof e.key === "string") {
            return e.key;
        }
        if (e.type === "keypress" && (
                (e.which >= 32 && e.which <= 126) ||
                (e.which >= 160 && e.which <= 255) )) {
            return String.fromCharCode(e.which);
        }
        // try to fallback (0-9A-Za-z and QWERTY keyboard)
        switch (e.which) {
        case 27:
            return "Escape";
        case 191:
            return e.shiftKey ? "?" : "/";
        }
        if (e.shiftKey || e.type === "keypress") {
            return String.fromCharCode(e.which);
        } else {
            return String.fromCharCode(e.which).toLowerCase();
        }
    },

    /**
     * Adds the given shortcut to the help dialog.
     *
     * @param shortcutChar the shortcut character
     * @param shortcutDescriptionKey the description of the shortcut
     * @private
     */
    _addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {

        var listElement = document.createElement("li");
        listElement.id = shortcutChar;

        var spanElement = document.createElement("span");
        spanElement.className = "item-action";

        var kbdElement = document.createElement("kbd");
        kbdElement.className = "regular-key";
        kbdElement.innerHTML = shortcutChar;
        spanElement.appendChild(kbdElement);

        var descriptionElement = document.createElement("span");
        descriptionElement.className = "item-description";
        descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
        descriptionElement.innerHTML
            = APP.translation.translateString(shortcutDescriptionKey);

        listElement.appendChild(spanElement);
        listElement.appendChild(descriptionElement);

        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        if (parentListElement)
            parentListElement.appendChild(listElement);
    },

    /**
     * Removes the list element corresponding to the given shortcut from the
     * help dialog
     * @private
     */
    _removeShortcutFromHelp: function (shortcutChar) {
        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        var shortcutElement = document.getElementById(shortcutChar);

        if (shortcutElement)
            parentListElement.removeChild(shortcutElement);
    }
};

module.exports = KeyboardShortcut;
<commit_msg>Fix shortcut appearing in tooltip of wrong buttons<commit_after>/* global APP, $, JitsiMeetJS */

/**
 * Initialise global shortcuts.
 * Global shortcuts are shortcuts for features that don't have a button or
 * link associated with the action. In other words they represent actions
 * triggered _only_ with a shortcut.
 */
function initGlobalShortcuts() {

    KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
        APP.UI.showKeyboardShortcutsPanel(false);
    });

    KeyboardShortcut.registerShortcut("?", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.shortcut.help");
        APP.UI.toggleKeyboardShortcutsPanel();
    }, "keyboardShortcuts.toggleShortcuts");

    KeyboardShortcut.registerShortcut("R", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.raisedhand.toggled");
        APP.conference.maybeToggleRaisedHand();
    }, "keyboardShortcuts.raiseHand");

    KeyboardShortcut.registerShortcut("T", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
        APP.conference.muteAudio(true);
    }, "keyboardShortcuts.pushToTalk");

    /**
     * FIXME: Currently focus keys are directly implemented below in onkeyup.
     * They should be moved to the SmallVideo instead.
     */
    KeyboardShortcut._addShortcutToHelp("0", "keyboardShortcuts.focusLocal");
    KeyboardShortcut._addShortcutToHelp("1-9", "keyboardShortcuts.focusRemote");
}

/**
 * Map of shortcuts. When a shortcut is registered it enters the mapping.
 * @type {{}}
 */
let _shortcuts = {};

/**
 * Maps keycode to character, id of popover for given function and function.
 */
var KeyboardShortcut = {
    init: function () {
        initGlobalShortcuts();

        var self = this;
        window.onkeyup = function(e) {
            var key = self._getKeyboardKey(e).toUpperCase();
            var num = parseInt(key, 10);
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                if (_shortcuts.hasOwnProperty(key)) {
                    _shortcuts[key].function(e);
                }
                else if (!isNaN(num) && num >= 0 && num <= 9) {
                    APP.UI.clickOnVideo(num + 1);
                }
            //esc while the smileys are visible hides them
            } else if (key === "ESCAPE" &&
                $('#smileysContainer').is(':visible')) {
                APP.UI.toggleSmileys();
            }
        };

        window.onkeydown = function(e) {
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                var key = self._getKeyboardKey(e).toUpperCase();
                if(key === "T") {
                    if(APP.conference.isLocalAudioMuted())
                        APP.conference.muteAudio(false);
                }
            }
        };
        $('body').popover({ selector: '[data-toggle=popover]',
            trigger: 'click hover',
            content: function() {
                var shortcutAttr = this.getAttribute("shortcut");

                var shortcutString = (shortcutAttr)
                                        ? self._getShortcutTooltip(shortcutAttr)
                                        : "";
                return this.getAttribute("content") + shortcutString;
            }
        });
    },

    /**
     * Registers a new shortcut.
     *
     * @param shortcutChar the shortcut character triggering the action
     * @param shortcutAttr the "shortcut" html element attribute mappring an
     * element to this shortcut and used to show the shortcut character on the
     * element tooltip
     * @param exec the function to be executed when the shortcut is pressed
     * @param helpDescription the description of the shortcut that would appear
     * in the help menu
     */
    registerShortcut: function( shortcutChar,
                                shortcutAttr,
                                exec,
                                helpDescription) {
        _shortcuts[shortcutChar] = {
            character: shortcutChar,
            shortcutAttr: shortcutAttr,
            function: exec
        };

        if (helpDescription)
            this._addShortcutToHelp(shortcutChar, helpDescription);
    },

    /**
     * Unregisters a shortcut.
     *
     * @param shortcutChar unregisters the given shortcut, which means it will
     * no longer be usable
     */
    unregisterShortcut: function(shortcutChar) {
        _shortcuts.remove(shortcutChar);

        this._removeShortcutFromHelp(shortcutChar);
    },

    /**
     *
     * @param id indicates the popover associated with the shortcut
     * @returns {string} the keyboard shortcut used for the id given
     */
    _getShortcutTooltip: function (id) {
        if (!id || id.length <= 0)
            return "";

        for (var key in _shortcuts) {
            if (_shortcuts.hasOwnProperty(key)) {
                if (_shortcuts[key].shortcutAttr
                    && _shortcuts[key].shortcutAttr === id) {
                    return " (" + _shortcuts[key].character + ")";
                }
            }
        }
        return "";
    },
    /**
     * @param e a KeyboardEvent
     * @returns {string} e.key or something close if not supported
     */
    _getKeyboardKey: function (e) {
        if (typeof e.key === "string") {
            return e.key;
        }
        if (e.type === "keypress" && (
                (e.which >= 32 && e.which <= 126) ||
                (e.which >= 160 && e.which <= 255) )) {
            return String.fromCharCode(e.which);
        }
        // try to fallback (0-9A-Za-z and QWERTY keyboard)
        switch (e.which) {
        case 27:
            return "Escape";
        case 191:
            return e.shiftKey ? "?" : "/";
        }
        if (e.shiftKey || e.type === "keypress") {
            return String.fromCharCode(e.which);
        } else {
            return String.fromCharCode(e.which).toLowerCase();
        }
    },

    /**
     * Adds the given shortcut to the help dialog.
     *
     * @param shortcutChar the shortcut character
     * @param shortcutDescriptionKey the description of the shortcut
     * @private
     */
    _addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {

        var listElement = document.createElement("li");
        listElement.id = shortcutChar;

        var spanElement = document.createElement("span");
        spanElement.className = "item-action";

        var kbdElement = document.createElement("kbd");
        kbdElement.className = "regular-key";
        kbdElement.innerHTML = shortcutChar;
        spanElement.appendChild(kbdElement);

        var descriptionElement = document.createElement("span");
        descriptionElement.className = "item-description";
        descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
        descriptionElement.innerHTML
            = APP.translation.translateString(shortcutDescriptionKey);

        listElement.appendChild(spanElement);
        listElement.appendChild(descriptionElement);

        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        if (parentListElement)
            parentListElement.appendChild(listElement);
    },

    /**
     * Removes the list element corresponding to the given shortcut from the
     * help dialog
     * @private
     */
    _removeShortcutFromHelp: function (shortcutChar) {
        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        var shortcutElement = document.getElementById(shortcutChar);

        if (shortcutElement)
            parentListElement.removeChild(shortcutElement);
    }
};

module.exports = KeyboardShortcut;
<|endoftext|>


<commit_before>// session event hooks

export const start = async (params, request) => {
  let cookies   = {},
      session   = {},
      active    ,
      ip        = app.toolbox.helpers.ip(request),
      bannedIP  = false

  // Check banned IP addresses
  let bannedIPs = await app.models.user.bannedIPs()

  // Check remote IP against banned IPs
  if ( bannedIPs.indexOf(ip) >= 0 ) {
    bannedIP = ip
  }

  // Proceed if IP isn't banned
  if ( !bannedIP ) {
    // If the user has cookies from a previous session, authenticate and start a new session.
    if ( params.cookie.comitium_id ) {
      let authenticate  = await app.models.user.authenticate({ usernameHash: params.cookie.comitium_id }),
          user          = authenticate.user

      if ( authenticate.success ) {
        user.user_id = authenticate.user.id
        user.ip = ip
        delete user.id

        app.models.user.log({
          userID: user.user_id,
          action: 'Session start (cookied)',
          ip: ip
        })

        return {
          cookies: {
            comitium_active: {
              expires: 'now'
            }
          },
          session: user
        }
      } else {
        return {
          cookies: {
            comitium_id: {
              expires: 'now'
            },
            comitium_active: {
              expires: 'now'
            }
          },
          session: {
            group_id: 1
          }
        }
      }
    } else {
      session.group_id = 1
      session.ip = ip
      active = app.toolbox.helpers.isoDate()
      cookies.comitium_active = {
        value: active,
        expires: 'never'
      }

      if ( params.cookie.comitium_active ) {
        session.last_activity = params.cookie.comitium_active
      } else {
        session.last_activity = active
      }

      return {
        cookies: cookies,
        session: session
      }
    }
  // Throw an exception if the remote IP is banned
  // TODO: Create a banned
  } else {
    let err = new Error('Your IP address (' + bannedIP + ') has been banned.')
    err.statusCode = 403
    throw err
  }
}
<commit_msg>Added date format for public<commit_after>// session event hooks

export const start = async (params, request) => {
  let cookies   = {},
      session   = {},
      active    ,
      ip        = app.toolbox.helpers.ip(request),
      bannedIP  = false

  // Check banned IP addresses
  let bannedIPs = await app.models.user.bannedIPs()

  // Check remote IP against banned IPs
  if ( bannedIPs.indexOf(ip) >= 0 ) {
    bannedIP = ip
  }

  // Proceed if IP isn't banned
  if ( !bannedIP ) {
    // If the user has cookies from a previous session, authenticate and start a new session.
    if ( params.cookie.comitium_id ) {
      let authenticate  = await app.models.user.authenticate({ usernameHash: params.cookie.comitium_id }),
          user          = authenticate.user

      if ( authenticate.success ) {
        user.user_id = authenticate.user.id
        user.ip = ip
        delete user.id

        app.models.user.log({
          userID: user.user_id,
          action: 'Session start (cookied)',
          ip: ip
        })

        return {
          cookies: {
            comitium_active: {
              expires: 'now'
            }
          },
          session: user
        }
      } else {
        return {
          cookies: {
            comitium_id: {
              expires: 'now'
            },
            comitium_active: {
              expires: 'now'
            }
          },
          session: {
            group_id: 1
          }
        }
      }
    } else {
      session.group_id = 1
      session.ip = ip
      active = app.toolbox.helpers.isoDate()
      cookies.comitium_active = {
        value: active,
        expires: 'never'
      }

      if ( params.cookie.comitium_active ) {
        session.last_activity = params.cookie.comitium_active
      } else {
        session.last_activity = active
      }

      session.date_format = 'MMMM D, YYYY'

      return {
        cookies: cookies,
        session: session
      }
    }
  // Throw an exception if the remote IP is banned
  // TODO: Create a banned
  } else {
    let err = new Error('Your IP address (' + bannedIP + ') has been banned.')
    err.statusCode = 403
    throw err
  }
}
<|endoftext|>


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

/*global paths*/
dembeloAdmin = (function () {

    return {
        init: function () {
            $$("mainnav").attachEvent("onAfterSelect", function (id){
                if (id == 1) {
                    $$('usergrid').load(paths.adminUsers);
                    $$('userstuff').show();
                } else if (id == 2) {
                    $$('licenseegrid').load(paths.adminLicensees);
                    $$('licenseestuff').show();
                } else if (id == 3) {
                    $$('topicgrid').load(paths.adminTopics);
                    $$('topicgrid').show();
                } else if (id == 4) {
                    $$('storygrid').load(paths.adminStories);
                    $$('storygrid').show();
                }
            });

            $$("mainnav").select(1);

            $$('userform').bind($$('usergrid'));
            $$('userformrole').attachEvent('onChange', function (newValue) {
                if (newValue == 'ROLE_LICENSEE') {
                    $$('userformlicensee').enable()
                } else {
                    $$('userformlicensee').setValue('');
                    $$('userformlicensee').disable()
                }
            });
            $$('userformstatus').attachEvent('onChange', function (newValue) {
                if (newValue === 'inaktiv') {
                    $$('userformactivation').enable();
                } else {
                    $$('userformactivation').disable();
                }
            });

            $$('licenseeform').bind($$('licenseegrid'));
        },
        formsave: function (type) {
            var id = type + "form",
                values = $$(id).getValues();
            values['formtype'] = type;

            if (!$$(id).validate()) {
                return;
            }

            webix.ajax().post(paths.adminFormSave, values, function (text) {
                var params = JSON.parse(text);
                if (params['error'] === false) {
                    $$(id).save();
                    if (params['newId']) {
                        $$(type + 'grid').getSelectedItem().id = params['newId'];
                    }
                } else {
                    webix.modalbox({
                        title: "Fehler",
                        buttons: ["Ok"],
                        text: "Das Speichern ist leider fehlgeschlagen..."
                    });
                }
            });
        },

        delItem: function (type) {
            var id = type + "grid",
                values = {};
            values['id'] = $$(id).getSelectedId().row;
            values['formtype'] = type;

            if (values['id'] === undefined) {
                webix.modalbox({
                    title: "Fehler",
                    buttons: ["Ok"],
                    text: "Keine Zeile zum Löschen ausgewählt."
                });
                return;
            }

            webix.ajax().post(paths.adminFormDel, values, function (text) {
                var params = JSON.parse(text);
                if (params['error'] === false) {
                    $$(id).remove($$(id).getSelectedId());
                } else {
                    webix.modalbox({
                        title: "Fehler",
                        buttons: ["Ok"],
                        text: "Das Löschen ist leider fehlgeschlagen..."
                    });
                }
            });
        },

        getToolbar: function(type) {

            var clickString;

            switch(type) {
                case 'user':
                    clickString = "$$('usergrid').add({id: 'new', email: '', roles: 'ROLE_USER'})";
                    break;
                case 'licensee':
                    clickString = "$$('licenseegrid').add({id: 'new', name: ''})";
                    break;
            }

            return {
                view: "toolbar",
                    cols: [
                {
                    id: "newBtn" + type,
                    view: "button",
                    value: "Neu",
                    type: "form",
                    click: clickString
                },
                {
                    id: "deleteBtn" + type,
                    view: "button",
                    value: "Löschen",
                    type: "danger",
                    click: "dembeloAdmin.delItem('" + type + "')"
                }
            ]
            };
        },

        sendActivationMail: function () {
            var userId = $$('usergrid').getSelectedId().id;

            webix.ajax().post(paths.adminUserActivationMail, {userId: userId}, function (text) {
                var params = JSON.parse(text);
                if (params['error'] === false) {
                    webix.modalbox({
                        title: "Aktivierungsmail versandt",
                        buttons: ["Ok"],
                        text: "Die Email zur Aktivierung wurde erfolgreich versandt."
                    });
                } else {
                    webix.modalbox({
                        title: "Fehler",
                        buttons: ["Ok"],
                        text: "Der Mailversandt ist leider fehlgeschlagen..."
                    });
                }
            });
        }

    };

}());<commit_msg>issue #225, always reload data when switching backend area<commit_after>/* Copyright (C) 2015 Michael Giesler
 *
 * This file is part of Dembelo.
 *
 * Dembelo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Dembelo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License 3 for more details.
 *
 * You should have received a copy of the GNU Affero General Public License 3
 * along with Dembelo. If not, see <http://www.gnu.org/licenses/>.
 */

/*global paths*/
dembeloAdmin = (function () {

    return {
        init: function () {
            $$("mainnav").attachEvent("onAfterSelect", function (id){
                if (id == 1) {
                    $$('usergrid').clearAll();
                    $$('usergrid').load(paths.adminUsers);
                    $$('userstuff').show();
                } else if (id == 2) {
                    $$('licenseegrid').clearAll();
                    $$('licenseegrid').load(paths.adminLicensees);
                    $$('licenseestuff').show();
                } else if (id == 3) {
                    $$('topicgrid').clearAll();
                    $$('topicgrid').load(paths.adminTopics);
                    $$('topicgrid').show();
                } else if (id == 4) {
                    $$('storygrid').clearAll();
                    $$('storygrid').load(paths.adminStories);
                    $$('storygrid').show();
                }
            });

            $$("mainnav").select(1);

            $$('userform').bind($$('usergrid'));
            $$('userformrole').attachEvent('onChange', function (newValue) {
                if (newValue == 'ROLE_LICENSEE') {
                    $$('userformlicensee').enable()
                } else {
                    $$('userformlicensee').setValue('');
                    $$('userformlicensee').disable()
                }
            });
            $$('userformstatus').attachEvent('onChange', function (newValue) {
                if (newValue === 'inaktiv') {
                    $$('userformactivation').enable();
                } else {
                    $$('userformactivation').disable();
                }
            });

            $$('licenseeform').bind($$('licenseegrid'));
        },
        formsave: function (type) {
            var id = type + "form",
                values = $$(id).getValues();
            values['formtype'] = type;

            if (!$$(id).validate()) {
                return;
            }

            webix.ajax().post(paths.adminFormSave, values, function (text) {
                var params = JSON.parse(text);
                if (params['error'] === false) {
                    $$(id).save();
                    if (params['newId']) {
                        $$(type + 'grid').getSelectedItem().id = params['newId'];
                    }
                } else {
                    webix.modalbox({
                        title: "Fehler",
                        buttons: ["Ok"],
                        text: "Das Speichern ist leider fehlgeschlagen..."
                    });
                }
            });
        },

        delItem: function (type) {
            var id = type + "grid",
                values = {};
            values['id'] = $$(id).getSelectedId().row;
            values['formtype'] = type;

            if (values['id'] === undefined) {
                webix.modalbox({
                    title: "Fehler",
                    buttons: ["Ok"],
                    text: "Keine Zeile zum Löschen ausgewählt."
                });
                return;
            }

            webix.ajax().post(paths.adminFormDel, values, function (text) {
                var params = JSON.parse(text);
                if (params['error'] === false) {
                    $$(id).remove($$(id).getSelectedId());
                } else {
                    webix.modalbox({
                        title: "Fehler",
                        buttons: ["Ok"],
                        text: "Das Löschen ist leider fehlgeschlagen..."
                    });
                }
            });
        },

        getToolbar: function(type) {

            var clickString;

            switch(type) {
                case 'user':
                    clickString = "$$('usergrid').add({id: 'new', email: '', roles: 'ROLE_USER'})";
                    break;
                case 'licensee':
                    clickString = "$$('licenseegrid').add({id: 'new', name: ''})";
                    break;
            }

            return {
                view: "toolbar",
                    cols: [
                {
                    id: "newBtn" + type,
                    view: "button",
                    value: "Neu",
                    type: "form",
                    click: clickString
                },
                {
                    id: "deleteBtn" + type,
                    view: "button",
                    value: "Löschen",
                    type: "danger",
                    click: "dembeloAdmin.delItem('" + type + "')"
                }
            ]
            };
        },

        sendActivationMail: function () {
            var userId = $$('usergrid').getSelectedId().id;

            webix.ajax().post(paths.adminUserActivationMail, {userId: userId}, function (text) {
                var params = JSON.parse(text);
                if (params['error'] === false) {
                    webix.modalbox({
                        title: "Aktivierungsmail versandt",
                        buttons: ["Ok"],
                        text: "Die Email zur Aktivierung wurde erfolgreich versandt."
                    });
                } else {
                    webix.modalbox({
                        title: "Fehler",
                        buttons: ["Ok"],
                        text: "Der Mailversandt ist leider fehlgeschlagen..."
                    });
                }
            });
        }

    };

}());<|endoftext|>


<commit_before>/* global APP, $, JitsiMeetJS */

/**
 * Initialise global shortcuts.
 * Global shortcuts are shortcuts for features that don't have a button or
 * link associated with the action. In other words they represent actions
 * triggered _only_ with a shortcut.
 */
function initGlobalShortcuts() {

    KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
        APP.UI.showKeyboardShortcutsPanel(false);
    });

    KeyboardShortcut.registerShortcut("?", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.shortcut.help");
        APP.UI.toggleKeyboardShortcutsPanel();
    }, "keyboardShortcuts.toggleShortcuts");

    KeyboardShortcut.registerShortcut("T", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
        APP.conference.muteAudio(true);
    }, "keyboardShortcuts.pushToTalk");

    /**
     * FIXME: Currently focus keys are directly implemented below in onkeyup.
     * They should be moved to the SmallVideo instead.
     */
    KeyboardShortcut._addShortcutToHelp("0", "keyboardShortcuts.focusLocal");
    KeyboardShortcut._addShortcutToHelp("1-9", "keyboardShortcuts.focusRemote");
}

/**
 * Map of shortcuts. When a shortcut is registered it enters the mapping.
 * @type {{}}
 */
let _shortcuts = {};

/**
 * Maps keycode to character, id of popover for given function and function.
 */
var KeyboardShortcut = {
    init: function () {
        initGlobalShortcuts();

        var self = this;
        window.onkeyup = function(e) {
            var key = self._getKeyboardKey(e).toUpperCase();
            var num = parseInt(key, 10);
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                if (_shortcuts.hasOwnProperty(key)) {
                    _shortcuts[key].function(e);
                }
                else if (!isNaN(num) && num >= 0 && num <= 9) {
                    APP.UI.clickOnVideo(num + 1);
                }
            //esc while the smileys are visible hides them
            } else if (key === "ESCAPE" &&
                $('#smileysContainer').is(':visible')) {
                APP.UI.toggleSmileys();
            }
        };

        window.onkeydown = function(e) {
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                var key = self._getKeyboardKey(e).toUpperCase();
                if(key === "T") {
                    if(APP.conference.isLocalAudioMuted())
                        APP.conference.muteAudio(false);
                }
            }
        };
    },

    /**
     * Registers a new shortcut.
     *
     * @param shortcutChar the shortcut character triggering the action
     * @param shortcutAttr the "shortcut" html element attribute mappring an
     * element to this shortcut and used to show the shortcut character on the
     * element tooltip
     * @param exec the function to be executed when the shortcut is pressed
     * @param helpDescription the description of the shortcut that would appear
     * in the help menu
     */
    registerShortcut: function( shortcutChar,
                                shortcutAttr,
                                exec,
                                helpDescription) {
        _shortcuts[shortcutChar] = {
            character: shortcutChar,
            shortcutAttr: shortcutAttr,
            function: exec
        };

        if (helpDescription)
            this._addShortcutToHelp(shortcutChar, helpDescription);
    },

    /**
     * Unregisters a shortcut.
     *
     * @param shortcutChar unregisters the given shortcut, which means it will
     * no longer be usable
     */
    unregisterShortcut: function(shortcutChar) {
        _shortcuts.remove(shortcutChar);

        this._removeShortcutFromHelp(shortcutChar);
    },

    /**
     * Returns the tooltip string for the given shortcut attribute.
     *
     * @param shortcutAttr indicates the popover associated with the shortcut
     * @returns {string} the tooltip string to add to the given shortcut popover
     * or an empty string if the shortcutAttr is null, an empty string or not
     * found in the shortcut mapping
     */
    getShortcutTooltip: function (shortcutAttr) {
        if (typeof shortcutAttr === "string" && shortcutAttr.length > 0) {
            for (var key in _shortcuts) {
                if (_shortcuts.hasOwnProperty(key)
                    && _shortcuts[key].shortcutAttr
                    && _shortcuts[key].shortcutAttr === shortcutAttr) {
                    return " (" + _shortcuts[key].character + ")";
                }
            }
        }

        return "";
    },
    /**
     * @param e a KeyboardEvent
     * @returns {string} e.key or something close if not supported
     */
    _getKeyboardKey: function (e) {
        if (typeof e.key === "string") {
            return e.key;
        }
        if (e.type === "keypress" && (
                (e.which >= 32 && e.which <= 126) ||
                (e.which >= 160 && e.which <= 255) )) {
            return String.fromCharCode(e.which);
        }
        // try to fallback (0-9A-Za-z and QWERTY keyboard)
        switch (e.which) {
        case 27:
            return "Escape";
        case 191:
            return e.shiftKey ? "?" : "/";
        }
        if (e.shiftKey || e.type === "keypress") {
            return String.fromCharCode(e.which);
        } else {
            return String.fromCharCode(e.which).toLowerCase();
        }
    },

    /**
     * Adds the given shortcut to the help dialog.
     *
     * @param shortcutChar the shortcut character
     * @param shortcutDescriptionKey the description of the shortcut
     * @private
     */
    _addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {

        var listElement = document.createElement("li");
        listElement.id = shortcutChar;

        var spanElement = document.createElement("span");
        spanElement.className = "item-action";

        var kbdElement = document.createElement("kbd");
        kbdElement.className = "regular-key";
        kbdElement.innerHTML = shortcutChar;
        spanElement.appendChild(kbdElement);

        var descriptionElement = document.createElement("span");
        descriptionElement.className = "item-description";
        descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
        descriptionElement.innerHTML
            = APP.translation.translateString(shortcutDescriptionKey);

        listElement.appendChild(spanElement);
        listElement.appendChild(descriptionElement);

        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        if (parentListElement)
            parentListElement.appendChild(listElement);
    },

    /**
     * Removes the list element corresponding to the given shortcut from the
     * help dialog
     * @private
     */
    _removeShortcutFromHelp: function (shortcutChar) {
        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        var shortcutElement = document.getElementById(shortcutChar);

        if (shortcutElement)
            parentListElement.removeChild(shortcutElement);
    }
};

module.exports = KeyboardShortcut;
<commit_msg>Spacebar is used to activate Push-To-Talk<commit_after>/* global APP, $, JitsiMeetJS */

/**
 * Initialise global shortcuts.
 * Global shortcuts are shortcuts for features that don't have a button or
 * link associated with the action. In other words they represent actions
 * triggered _only_ with a shortcut.
 */
function initGlobalShortcuts() {

    KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
        APP.UI.showKeyboardShortcutsPanel(false);
    });

    KeyboardShortcut.registerShortcut("?", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.shortcut.help");
        APP.UI.toggleKeyboardShortcutsPanel();
    }, "keyboardShortcuts.toggleShortcuts");

    KeyboardShortcut.registerShortcut("SPACE", null, function() {
        JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
        APP.conference.muteAudio(true);
    }, "keyboardShortcuts.pushToTalk");

    /**
     * FIXME: Currently focus keys are directly implemented below in onkeyup.
     * They should be moved to the SmallVideo instead.
     */
    KeyboardShortcut._addShortcutToHelp("0", "keyboardShortcuts.focusLocal");
    KeyboardShortcut._addShortcutToHelp("1-9", "keyboardShortcuts.focusRemote");
}

/**
 * Map of shortcuts. When a shortcut is registered it enters the mapping.
 * @type {{}}
 */
let _shortcuts = {};

/**
 * Maps keycode to character, id of popover for given function and function.
 */
var KeyboardShortcut = {
    init: function () {
        initGlobalShortcuts();

        var self = this;
        window.onkeyup = function(e) {
            var key = self._getKeyboardKey(e).toUpperCase();
            var num = parseInt(key, 10);
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                if (_shortcuts.hasOwnProperty(key)) {
                    _shortcuts[key].function(e);
                }
                else if (!isNaN(num) && num >= 0 && num <= 9) {
                    APP.UI.clickOnVideo(num + 1);
                }
            //esc while the smileys are visible hides them
            } else if (key === "ESCAPE" &&
                $('#smileysContainer').is(':visible')) {
                APP.UI.toggleSmileys();
            }
        };

        window.onkeydown = function(e) {
            if(!($(":focus").is("input[type=text]") ||
                $(":focus").is("input[type=password]") ||
                $(":focus").is("textarea"))) {
                var key = self._getKeyboardKey(e).toUpperCase();
                if(key === "SPACE") {
                    if(APP.conference.isLocalAudioMuted())
                        APP.conference.muteAudio(false);
                }
            }
        };
    },

    /**
     * Registers a new shortcut.
     *
     * @param shortcutChar the shortcut character triggering the action
     * @param shortcutAttr the "shortcut" html element attribute mappring an
     * element to this shortcut and used to show the shortcut character on the
     * element tooltip
     * @param exec the function to be executed when the shortcut is pressed
     * @param helpDescription the description of the shortcut that would appear
     * in the help menu
     */
    registerShortcut: function( shortcutChar,
                                shortcutAttr,
                                exec,
                                helpDescription) {
        _shortcuts[shortcutChar] = {
            character: shortcutChar,
            shortcutAttr: shortcutAttr,
            function: exec
        };

        if (helpDescription)
            this._addShortcutToHelp(shortcutChar, helpDescription);
    },

    /**
     * Unregisters a shortcut.
     *
     * @param shortcutChar unregisters the given shortcut, which means it will
     * no longer be usable
     */
    unregisterShortcut: function(shortcutChar) {
        _shortcuts.remove(shortcutChar);

        this._removeShortcutFromHelp(shortcutChar);
    },

    /**
     * Returns the tooltip string for the given shortcut attribute.
     *
     * @param shortcutAttr indicates the popover associated with the shortcut
     * @returns {string} the tooltip string to add to the given shortcut popover
     * or an empty string if the shortcutAttr is null, an empty string or not
     * found in the shortcut mapping
     */
    getShortcutTooltip: function (shortcutAttr) {
        if (typeof shortcutAttr === "string" && shortcutAttr.length > 0) {
            for (var key in _shortcuts) {
                if (_shortcuts.hasOwnProperty(key)
                    && _shortcuts[key].shortcutAttr
                    && _shortcuts[key].shortcutAttr === shortcutAttr) {
                    return " (" + _shortcuts[key].character + ")";
                }
            }
        }

        return "";
    },
    /**
     * @param e a KeyboardEvent
     * @returns {string} e.key or something close if not supported
     */
    _getKeyboardKey: function (e) {
        if (e.which === 32) {
            return 'SPACE';
        }
        if (typeof e.key === "string") {
            return e.key;
        }
        if (e.type === "keypress" && (
                (e.which >= 32 && e.which <= 126) ||
                (e.which >= 160 && e.which <= 255) )) {
            return String.fromCharCode(e.which);
        }
        // try to fallback (0-9A-Za-z and QWERTY keyboard)
        switch (e.which) {
        case 27:
            return "Escape";
        case 191:
            return e.shiftKey ? "?" : "/";
        }
        if (e.shiftKey || e.type === "keypress") {
            return String.fromCharCode(e.which);
        } else {
            return String.fromCharCode(e.which).toLowerCase();
        }
    },

    /**
     * Adds the given shortcut to the help dialog.
     *
     * @param shortcutChar the shortcut character
     * @param shortcutDescriptionKey the description of the shortcut
     * @private
     */
    _addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {

        var listElement = document.createElement("li");
        listElement.id = shortcutChar;

        var spanElement = document.createElement("span");
        spanElement.className = "item-action";

        var kbdElement = document.createElement("kbd");
        kbdElement.className = "regular-key";
        kbdElement.innerHTML = shortcutChar;
        spanElement.appendChild(kbdElement);

        var descriptionElement = document.createElement("span");
        descriptionElement.className = "item-description";
        descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
        descriptionElement.innerHTML
            = APP.translation.translateString(shortcutDescriptionKey);

        listElement.appendChild(spanElement);
        listElement.appendChild(descriptionElement);

        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        if (parentListElement)
            parentListElement.appendChild(listElement);
    },

    /**
     * Removes the list element corresponding to the given shortcut from the
     * help dialog
     * @private
     */
    _removeShortcutFromHelp: function (shortcutChar) {
        var parentListElement
            = document.getElementById("keyboard-shortcuts-list");

        var shortcutElement = document.getElementById(shortcutChar);

        if (shortcutElement)
            parentListElement.removeChild(shortcutElement);
    }
};

module.exports = KeyboardShortcut;
<|endoftext|>