<commit_before>/** @babel */

import path from 'path'
import fs from 'fs-plus'

// This is loaded by atom-environment.coffee. See
// https://atom.io/docs/api/latest/Config for more information about config
// schemas.
const configSchema = {
  core: {
    type: 'object',
    properties: {
      ignoredNames: {
        type: 'array',
        default: ['.git', '.hg', '.svn', '.DS_Store', '._*', 'Thumbs.db', 'desktop.ini'],
        items: {
          type: 'string'
        },
        description: 'List of [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29). Files and directories matching these patterns will be ignored by some packages, such as the fuzzy finder and tree view. Individual packages might have additional config settings for ignoring names.'
      },
      excludeVcsIgnoredPaths: {
        type: 'boolean',
        default: true,
        title: 'Exclude VCS Ignored Paths',
        description: 'Files and directories ignored by the current project\'s VCS system will be ignored by some packages, such as the fuzzy finder and find and replace. For example, projects using Git have these paths defined in the .gitignore file. Individual packages might have additional config settings for ignoring VCS ignored files and folders.'
      },
      followSymlinks: {
        type: 'boolean',
        default: true,
        description: 'Follow symbolic links when searching files and when opening files with the fuzzy finder.'
      },
      disabledPackages: {
        type: 'array',
        default: [],

        items: {
          type: 'string'
        },

        description: 'List of names of installed packages which are not loaded at startup.'
      },
      customFileTypes: {
        type: 'object',
        default: {},
        description: 'Associates scope names (e.g. `"source.js"`) with arrays of file extensions and file names (e.g. `["Somefile", ".js2"]`)',
        additionalProperties: {
          type: 'array',
          items: {
            type: 'string'
          }
        }
      },
      themes: {
        type: 'array',
        default: ['one-dark-ui', 'one-dark-syntax'],
        items: {
          type: 'string'
        },
        description: 'Names of UI and syntax themes which will be used when Atom starts.'
      },
      projectHome: {
        type: 'string',
        default: path.join(fs.getHomeDirectory(), 'github'),
        description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.'
      },
      audioBeep: {
        type: 'boolean',
        default: true,
        description: 'Trigger the system\'s beep sound when certain actions cannot be executed or there are no results.'
      },
      destroyEmptyPanes: {
        type: 'boolean',
        default: true,
        title: 'Remove Empty Panes',
        description: 'When the last tab of a pane is closed, remove that pane as well.'
      },
      closeEmptyWindows: {
        type: 'boolean',
        default: true,
        description: 'When a window with no open tabs or panes is given the \'Close Tab\' command, close that window.'
      },
      fileEncoding: {
        description: 'Default character set encoding to use when reading and writing files.',
        type: 'string',
        default: 'utf8',
        enum: [
          'cp437',
          'cp850',
          'cp866',
          'eucjp',
          'euckr',
          'gbk',
          'iso88591',
          'iso885910',
          'iso885913',
          'iso885914',
          'iso885915',
          'iso885916',
          'iso88592',
          'iso88593',
          'iso88594',
          'iso88595',
          'iso88596',
          'iso88597',
          'iso88597',
          'iso88598',
          'koi8r',
          'koi8u',
          'macroman',
          'shiftjis',
          'utf16be',
          'utf16le',
          'utf8',
          'windows1250',
          'windows1251',
          'windows1252',
          'windows1253',
          'windows1254',
          'windows1255',
          'windows1256',
          'windows1257',
          'windows1258'
        ]
      },
      openEmptyEditorOnStart: {
        description: 'When checked opens an untitled editor when loading a blank environment (such as with _File > New Window_ or when "Restore Previous Windows On Start" is unchecked); otherwise no editor is opened when loading a blank environment. This setting has no effect when restoring a previous state.',
        type: 'boolean',
        default: true
      },
      restorePreviousWindowsOnStart: {
        description: 'When checked restores the last state of all Atom windows when started from the icon or `atom` by itself from the command line; otherwise a blank environment is loaded.',
        type: 'boolean',
        default: true
      },
      reopenProjectMenuCount: {
        description: 'How many recent projects to show in the Reopen Project menu.',
        type: 'integer',
        default: 15
      },
      automaticallyUpdate: {
        description: 'Automatically update Atom when a new release is available.',
        type: 'boolean',
        default: true
      },
      useProxySettingsWhenCallingApm: {
        title: 'Use Proxy Settings When Calling APM',
        description: 'Use detected proxy settings when calling the `apm` command-line tool.',
        type: 'boolean',
        default: true
      },
      allowPendingPaneItems: {
        description: 'Allow items to be previewed without adding them to a pane permanently, such as when single clicking files in the tree view.',
        type: 'boolean',
        default: true
      },
      telemetryConsent: {
        description: 'Allow usage statistics and exception reports to be sent to the Atom team to help improve the product.',
        title: 'Send Telemetry to the Atom Team',
        type: 'string',
        default: 'undecided',
        enum: [
          {
            value: 'limited',
            description: 'Allow limited anonymous usage stats, exception and crash reporting'
          },
          {
            value: 'no',
            description: 'Do not send any telemetry data'
          },
          {
            value: 'undecided',
            description: 'Undecided (Atom will ask again next time it is launched)'
          }
        ]
      },
      warnOnLargeFileLimit: {
        description: 'Warn before opening files larger than this number of megabytes.',
        type: 'number',
        default: 40
      }
    }
  },
  editor: {
    type: 'object',
    // These settings are used in scoped fashion only. No defaults.
    properties: {
      commentStart: {
        type: ['string', 'null']
      },
      commentEnd: {
        type: ['string', 'null']
      },
      increaseIndentPattern: {
        type: ['string', 'null']
      },
      decreaseIndentPattern: {
        type: ['string', 'null']
      },
      foldEndPattern: {
        type: ['string', 'null']
      },
      // These can be used as globals or scoped, thus defaults.
      fontFamily: {
        type: 'string',
        default: '',
        description: 'The name of the font family used for editor text.'
      },
      fontSize: {
        type: 'integer',
        default: 14,
        minimum: 1,
        maximum: 100,
        description: 'Height in pixels of editor text.'
      },
      lineHeight: {
        type: ['string', 'number'],
        default: 1.5,
        description: 'Height of editor lines, as a multiplier of font size.'
      },
      showCursorOnSelection: {
        type: 'boolean',
        'default': true,
        description: 'Show cursor while there is a selection.'
      },
      showInvisibles: {
        type: 'boolean',
        default: false,
        description: 'Render placeholders for invisible characters, such as tabs, spaces and newlines.'
      },
      showIndentGuide: {
        type: 'boolean',
        default: false,
        description: 'Show indentation indicators in the editor.'
      },
      showLineNumbers: {
        type: 'boolean',
        default: true,
        description: 'Show line numbers in the editor\'s gutter.'
      },
      atomicSoftTabs: {
        type: 'boolean',
        default: true,
        description: 'Skip over tab-length runs of leading whitespace when moving the cursor.'
      },
      autoIndent: {
        type: 'boolean',
        default: true,
        description: 'Automatically indent the cursor when inserting a newline.'
      },
      autoIndentOnPaste: {
        type: 'boolean',
        default: true,
        description: 'Automatically indent pasted text based on the indentation of the previous line.'
      },
      nonWordCharacters: {
        type: 'string',
        default: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…",
        description: 'A string of non-word characters to define word boundaries.'
      },
      preferredLineLength: {
        type: 'integer',
        default: 80,
        minimum: 1,
        description: 'Identifies the length of a line which is used when wrapping text with the `Soft Wrap At Preferred Line Length` setting enabled, in number of characters.'
      },
      tabLength: {
        type: 'integer',
        default: 2,
        minimum: 1,
        description: 'Number of spaces used to represent a tab.'
      },
      softWrap: {
        type: 'boolean',
        default: false,
        description: 'Wraps lines that exceed the width of the window. When `Soft Wrap At Preferred Line Length` is set, it will wrap to the number of characters defined by the `Preferred Line Length` setting.'
      },
      softTabs: {
        type: 'boolean',
        default: true,
        description: 'If the `Tab Type` config setting is set to "auto" and autodetection of tab type from buffer content fails, then this config setting determines whether a soft tab or a hard tab will be inserted when the Tab key is pressed.'
      },
      tabType: {
        type: 'string',
        default: 'auto',
        enum: ['auto', 'soft', 'hard'],
        description: 'Determine character inserted when Tab key is pressed. Possible values: "auto", "soft" and "hard". When set to "soft" or "hard", soft tabs (spaces) or hard tabs (tab characters) are used. When set to "auto", the editor auto-detects the tab type based on the contents of the buffer (it uses the first leading whitespace on a non-comment line), or uses the value of the Soft Tabs config setting if auto-detection fails.'
      },
      softWrapAtPreferredLineLength: {
        type: 'boolean',
        default: false,
        description: 'Instead of wrapping lines to the window\'s width, wrap lines to the number of characters defined by the `Preferred Line Length` setting. This will only take effect when the soft wrap config setting is enabled globally or for the current language. **Note:** If you want to hide the wrap guide (the vertical line) you can disable the `wrap-guide` package.'
      },
      softWrapHangingIndent: {
        type: 'integer',
        default: 0,
        minimum: 0,
        description: 'When soft wrap is enabled, defines length of additional indentation applied to wrapped lines, in number of characters.'
      },
      scrollSensitivity: {
        type: 'integer',
        default: 40,
        minimum: 10,
        maximum: 200,
        description: 'Determines how fast the editor scrolls when using a mouse or trackpad.'
      },
      scrollPastEnd: {
        type: 'boolean',
        default: false,
        description: 'Allow the editor to be scrolled past the end of the last line.'
      },
      undoGroupingInterval: {
        type: 'integer',
        default: 300,
        minimum: 0,
        description: 'Time interval in milliseconds within which text editing operations will be grouped together in the undo history.'
      },
      confirmCheckoutHeadRevision: {
        type: 'boolean',
        default: true,
        title: 'Confirm Checkout HEAD Revision',
        description: 'Show confirmation dialog when checking out the HEAD revision and discarding changes to current file since last commit.'
      },
      invisibles: {
        type: 'object',
        description: 'A hash of characters Atom will use to render whitespace characters. Keys are whitespace character types, values are rendered characters (use value false to turn off individual whitespace character types).',
        properties: {
          eol: {
            type: ['boolean', 'string'],
            default: '¬',
            maximumLength: 1,
            description: 'Character used to render newline characters (\\n) when the `Show Invisibles` setting is enabled. '
          },
          space: {
            type: ['boolean', 'string'],
            default: '·',
            maximumLength: 1,
            description: 'Character used to render leading and trailing space characters when the `Show Invisibles` setting is enabled.'
          },
          tab: {
            type: ['boolean', 'string'],
            default: '»',
            maximumLength: 1,
            description: 'Character used to render hard tab characters (\\t) when the `Show Invisibles` setting is enabled.'
          },
          cr: {
            type: ['boolean', 'string'],
            default: '¤',
            maximumLength: 1,
            description: 'Character used to render carriage return characters (for Microsoft-style line endings) when the `Show Invisibles` setting is enabled.'
          }
        }
      },
      zoomFontWhenCtrlScrolling: {
        type: 'boolean',
        default: process.platform !== 'darwin',
        description: 'Change the editor font size when pressing the Ctrl key and scrolling the mouse up/down.'
      }
    }
  }
}

if (['win32', 'linux'].includes(process.platform)) {
  configSchema.core.properties.autoHideMenuBar = {
    type: 'boolean',
    default: false,
    description: 'Automatically hide the menu bar and toggle it by pressing Alt. This is only supported on Windows & Linux.'
  }
}

if (process.platform === 'darwin') {
  configSchema.core.properties.useCustomTitleBar = {
    type: 'boolean',
    default: false,
    description: 'Use custom, theme-aware title bar.
Note: This currently does not include a proxy icon.
This setting will require a relaunch of Atom to take effect.'
  }
}

export default configSchema
<commit_msg>Added description text to the File Encoding list<commit_after>/** @babel */

import path from 'path'
import fs from 'fs-plus'

// This is loaded by atom-environment.coffee. See
// https://atom.io/docs/api/latest/Config for more information about config
// schemas.
const configSchema = {
  core: {
    type: 'object',
    properties: {
      ignoredNames: {
        type: 'array',
        default: ['.git', '.hg', '.svn', '.DS_Store', '._*', 'Thumbs.db', 'desktop.ini'],
        items: {
          type: 'string'
        },
        description: 'List of [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29). Files and directories matching these patterns will be ignored by some packages, such as the fuzzy finder and tree view. Individual packages might have additional config settings for ignoring names.'
      },
      excludeVcsIgnoredPaths: {
        type: 'boolean',
        default: true,
        title: 'Exclude VCS Ignored Paths',
        description: 'Files and directories ignored by the current project\'s VCS system will be ignored by some packages, such as the fuzzy finder and find and replace. For example, projects using Git have these paths defined in the .gitignore file. Individual packages might have additional config settings for ignoring VCS ignored files and folders.'
      },
      followSymlinks: {
        type: 'boolean',
        default: true,
        description: 'Follow symbolic links when searching files and when opening files with the fuzzy finder.'
      },
      disabledPackages: {
        type: 'array',
        default: [],

        items: {
          type: 'string'
        },

        description: 'List of names of installed packages which are not loaded at startup.'
      },
      customFileTypes: {
        type: 'object',
        default: {},
        description: 'Associates scope names (e.g. `"source.js"`) with arrays of file extensions and file names (e.g. `["Somefile", ".js2"]`)',
        additionalProperties: {
          type: 'array',
          items: {
            type: 'string'
          }
        }
      },
      themes: {
        type: 'array',
        default: ['one-dark-ui', 'one-dark-syntax'],
        items: {
          type: 'string'
        },
        description: 'Names of UI and syntax themes which will be used when Atom starts.'
      },
      projectHome: {
        type: 'string',
        default: path.join(fs.getHomeDirectory(), 'github'),
        description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.'
      },
      audioBeep: {
        type: 'boolean',
        default: true,
        description: 'Trigger the system\'s beep sound when certain actions cannot be executed or there are no results.'
      },
      destroyEmptyPanes: {
        type: 'boolean',
        default: true,
        title: 'Remove Empty Panes',
        description: 'When the last tab of a pane is closed, remove that pane as well.'
      },
      closeEmptyWindows: {
        type: 'boolean',
        default: true,
        description: 'When a window with no open tabs or panes is given the \'Close Tab\' command, close that window.'
      },
      fileEncoding: {
        description: 'Default character set encoding to use when reading and writing files.',
        type: 'string',
        default: 'utf8',
        enum: [
          {
            value: 'utf8',
            description: 'UTF-8'
          },
          {
            value: 'utf16le',
            description: 'UTF-16 LE'
          },
          {
            value: 'utf16be',
            description: 'UTF-16 BE'
          },
          {
            value: 'windows1252',
            description: 'Western (Windows 1252)'
          },
          {
            value: 'iso88591',
            description: 'Western (ISO 8859-1)'
          },
          {
            value: 'iso88593',
            description: 'Western (ISO 8859-3)'
          },
          {
            value: 'iso885915',
            description: 'Western (ISO 8859-15)'
          },
          {
            value: 'macroman',
            description: 'Western (Mac Roman)'
          },
          {
            value: 'cp437',
            description: 'DOS (CP 437)'
          },
          {
            value: 'cp850',
            description: 'DOS (CP 850)'
          },
          {
            value: 'windows1256',
            description: 'Arabic (Windows 1256)'
          },
          {
            value: 'iso88596',
            description: 'Arabic (ISO 8859-6)'
          },
          {
            value: 'windows1257',
            description: 'Baltic (Windows 1257)'
          },
          {
            value: 'iso88594',
            description: 'Baltic (ISO 8859-4)'
          },
          {
            value: 'iso885914',
            description: 'Celtic (ISO 8859-14)'
          },
          {
            value: 'windows1250',
            description: 'Central European (Windows 1250)'
          },
          {
            value: 'iso88592',
            description: 'Central European (ISO 8859-2)'
          },
          {
            value: 'windows1251',
            description: 'Cyrillic (Windows 1251)'
          },
          {
            value: 'cp866',
            description: 'Cyrillic (CP 866)'
          },
          {
            value: 'iso88595',
            description: 'Cyrillic (ISO 8859-5)'
          },
          {
            value: 'koi8r',
            description: 'Cyrillic (KOI8-R)'
          },
          {
            value: 'koi8u',
            description: 'Cyrillic (KOI8-U)'
          },
          {
            value: 'iso885913',
            description: 'Estonian (ISO 8859-13)'
          },
          {
            value: 'windows1253',
            description: 'Greek (Windows 1253)'
          },
          {
            value: 'iso88597',
            description: 'Greek (ISO 8859-7)'
          },
          {
            value: 'windows1255',
            description: 'Hebrew (Windows 1255)'
          },
          {
            value: 'iso88598',
            description: 'Hebrew (ISO 8859-8)'
          },
          {
            value: 'iso885910',
            description: 'Nordic (ISO 8859-10)'
          },
          {
            value: 'iso885916',
            description: 'Romanian (ISO 8859-16)'
          },
          {
            value: 'windows1254',
            description: 'Turkish (Windows 1254)'
          },
          {
            value: 'iso88599',
            description: 'Turkish (ISO 8859-9)'
          },
          {
            value: 'windows1258',
            description: 'Vietnamese (Windows 1254)'
          },
          {
            value: 'gbk',
            description: 'Chinese (GBK)'
          },
          {
            value: 'gb18030',
            description: 'Chinese (GB18030)'
          },
          {
            value: 'cp950',
            description: 'Traditional Chinese (Big5)'
          },
          {
            value: 'big5hkscs',
            description: 'Traditional Chinese (Big5-HKSCS)'
          },
          {
            value: 'shiftjis',
            description: 'Japanese (Shift JIS)'
          },
          {
            value: 'cp932',
            description: 'Japanese (CP 932)'
          },
          {
            value: 'eucjp',
            description: 'Japanese (EUC-JP)'
          },
          {
            value: 'euckr',
            description: 'Korean (EUC-KR)'
          }
        ]
      },
      openEmptyEditorOnStart: {
        description: 'When checked opens an untitled editor when loading a blank environment (such as with _File > New Window_ or when "Restore Previous Windows On Start" is unchecked); otherwise no editor is opened when loading a blank environment. This setting has no effect when restoring a previous state.',
        type: 'boolean',
        default: true
      },
      restorePreviousWindowsOnStart: {
        description: 'When checked restores the last state of all Atom windows when started from the icon or `atom` by itself from the command line; otherwise a blank environment is loaded.',
        type: 'boolean',
        default: true
      },
      reopenProjectMenuCount: {
        description: 'How many recent projects to show in the Reopen Project menu.',
        type: 'integer',
        default: 15
      },
      automaticallyUpdate: {
        description: 'Automatically update Atom when a new release is available.',
        type: 'boolean',
        default: true
      },
      useProxySettingsWhenCallingApm: {
        title: 'Use Proxy Settings When Calling APM',
        description: 'Use detected proxy settings when calling the `apm` command-line tool.',
        type: 'boolean',
        default: true
      },
      allowPendingPaneItems: {
        description: 'Allow items to be previewed without adding them to a pane permanently, such as when single clicking files in the tree view.',
        type: 'boolean',
        default: true
      },
      telemetryConsent: {
        description: 'Allow usage statistics and exception reports to be sent to the Atom team to help improve the product.',
        title: 'Send Telemetry to the Atom Team',
        type: 'string',
        default: 'undecided',
        enum: [
          {
            value: 'limited',
            description: 'Allow limited anonymous usage stats, exception and crash reporting'
          },
          {
            value: 'no',
            description: 'Do not send any telemetry data'
          },
          {
            value: 'undecided',
            description: 'Undecided (Atom will ask again next time it is launched)'
          }
        ]
      },
      warnOnLargeFileLimit: {
        description: 'Warn before opening files larger than this number of megabytes.',
        type: 'number',
        default: 40
      }
    }
  },
  editor: {
    type: 'object',
    // These settings are used in scoped fashion only. No defaults.
    properties: {
      commentStart: {
        type: ['string', 'null']
      },
      commentEnd: {
        type: ['string', 'null']
      },
      increaseIndentPattern: {
        type: ['string', 'null']
      },
      decreaseIndentPattern: {
        type: ['string', 'null']
      },
      foldEndPattern: {
        type: ['string', 'null']
      },
      // These can be used as globals or scoped, thus defaults.
      fontFamily: {
        type: 'string',
        default: '',
        description: 'The name of the font family used for editor text.'
      },
      fontSize: {
        type: 'integer',
        default: 14,
        minimum: 1,
        maximum: 100,
        description: 'Height in pixels of editor text.'
      },
      lineHeight: {
        type: ['string', 'number'],
        default: 1.5,
        description: 'Height of editor lines, as a multiplier of font size.'
      },
      showCursorOnSelection: {
        type: 'boolean',
        'default': true,
        description: 'Show cursor while there is a selection.'
      },
      showInvisibles: {
        type: 'boolean',
        default: false,
        description: 'Render placeholders for invisible characters, such as tabs, spaces and newlines.'
      },
      showIndentGuide: {
        type: 'boolean',
        default: false,
        description: 'Show indentation indicators in the editor.'
      },
      showLineNumbers: {
        type: 'boolean',
        default: true,
        description: 'Show line numbers in the editor\'s gutter.'
      },
      atomicSoftTabs: {
        type: 'boolean',
        default: true,
        description: 'Skip over tab-length runs of leading whitespace when moving the cursor.'
      },
      autoIndent: {
        type: 'boolean',
        default: true,
        description: 'Automatically indent the cursor when inserting a newline.'
      },
      autoIndentOnPaste: {
        type: 'boolean',
        default: true,
        description: 'Automatically indent pasted text based on the indentation of the previous line.'
      },
      nonWordCharacters: {
        type: 'string',
        default: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…",
        description: 'A string of non-word characters to define word boundaries.'
      },
      preferredLineLength: {
        type: 'integer',
        default: 80,
        minimum: 1,
        description: 'Identifies the length of a line which is used when wrapping text with the `Soft Wrap At Preferred Line Length` setting enabled, in number of characters.'
      },
      tabLength: {
        type: 'integer',
        default: 2,
        minimum: 1,
        description: 'Number of spaces used to represent a tab.'
      },
      softWrap: {
        type: 'boolean',
        default: false,
        description: 'Wraps lines that exceed the width of the window. When `Soft Wrap At Preferred Line Length` is set, it will wrap to the number of characters defined by the `Preferred Line Length` setting.'
      },
      softTabs: {
        type: 'boolean',
        default: true,
        description: 'If the `Tab Type` config setting is set to "auto" and autodetection of tab type from buffer content fails, then this config setting determines whether a soft tab or a hard tab will be inserted when the Tab key is pressed.'
      },
      tabType: {
        type: 'string',
        default: 'auto',
        enum: ['auto', 'soft', 'hard'],
        description: 'Determine character inserted when Tab key is pressed. Possible values: "auto", "soft" and "hard". When set to "soft" or "hard", soft tabs (spaces) or hard tabs (tab characters) are used. When set to "auto", the editor auto-detects the tab type based on the contents of the buffer (it uses the first leading whitespace on a non-comment line), or uses the value of the Soft Tabs config setting if auto-detection fails.'
      },
      softWrapAtPreferredLineLength: {
        type: 'boolean',
        default: false,
        description: 'Instead of wrapping lines to the window\'s width, wrap lines to the number of characters defined by the `Preferred Line Length` setting. This will only take effect when the soft wrap config setting is enabled globally or for the current language. **Note:** If you want to hide the wrap guide (the vertical line) you can disable the `wrap-guide` package.'
      },
      softWrapHangingIndent: {
        type: 'integer',
        default: 0,
        minimum: 0,
        description: 'When soft wrap is enabled, defines length of additional indentation applied to wrapped lines, in number of characters.'
      },
      scrollSensitivity: {
        type: 'integer',
        default: 40,
        minimum: 10,
        maximum: 200,
        description: 'Determines how fast the editor scrolls when using a mouse or trackpad.'
      },
      scrollPastEnd: {
        type: 'boolean',
        default: false,
        description: 'Allow the editor to be scrolled past the end of the last line.'
      },
      undoGroupingInterval: {
        type: 'integer',
        default: 300,
        minimum: 0,
        description: 'Time interval in milliseconds within which text editing operations will be grouped together in the undo history.'
      },
      confirmCheckoutHeadRevision: {
        type: 'boolean',
        default: true,
        title: 'Confirm Checkout HEAD Revision',
        description: 'Show confirmation dialog when checking out the HEAD revision and discarding changes to current file since last commit.'
      },
      invisibles: {
        type: 'object',
        description: 'A hash of characters Atom will use to render whitespace characters. Keys are whitespace character types, values are rendered characters (use value false to turn off individual whitespace character types).',
        properties: {
          eol: {
            type: ['boolean', 'string'],
            default: '¬',
            maximumLength: 1,
            description: 'Character used to render newline characters (\\n) when the `Show Invisibles` setting is enabled. '
          },
          space: {
            type: ['boolean', 'string'],
            default: '·',
            maximumLength: 1,
            description: 'Character used to render leading and trailing space characters when the `Show Invisibles` setting is enabled.'
          },
          tab: {
            type: ['boolean', 'string'],
            default: '»',
            maximumLength: 1,
            description: 'Character used to render hard tab characters (\\t) when the `Show Invisibles` setting is enabled.'
          },
          cr: {
            type: ['boolean', 'string'],
            default: '¤',
            maximumLength: 1,
            description: 'Character used to render carriage return characters (for Microsoft-style line endings) when the `Show Invisibles` setting is enabled.'
          }
        }
      },
      zoomFontWhenCtrlScrolling: {
        type: 'boolean',
        default: process.platform !== 'darwin',
        description: 'Change the editor font size when pressing the Ctrl key and scrolling the mouse up/down.'
      }
    }
  }
}

if (['win32', 'linux'].includes(process.platform)) {
  configSchema.core.properties.autoHideMenuBar = {
    type: 'boolean',
    default: false,
    description: 'Automatically hide the menu bar and toggle it by pressing Alt. This is only supported on Windows & Linux.'
  }
}

if (process.platform === 'darwin') {
  configSchema.core.properties.useCustomTitleBar = {
    type: 'boolean',
    default: false,
    description: 'Use custom, theme-aware title bar.
Note: This currently does not include a proxy icon.
This setting will require a relaunch of Atom to take effect.'
  }
}

export default configSchema
<|endoftext|>


<commit_before>const check = require("check-types");
const debug = require("debug")("bow:OutboundServer");
const SocketIO = require("socket.io");

const assert = require("./utils/assert");
const normalizeVersion = require("./utils/normalizeVersion");

function getMatchingVersionOutbound(version) {
  return this.outbounds
    .filter((outbound) => version === outbound.version)
    [0];
}

function checkVersion(socket, next) {
  const version = normalizeVersion(socket.handshake.query.v);
  if (check.nonEmptyString(version)) {
    const outbound = getMatchingVersionOutbound.call(this, version);
    if (check.assigned(outbound)) {
      socket.outbound = outbound;
      next();
    } else {
      next(new Error(`Version '${version}' not supported`));
    }
  } else {
    next(new Error("Version not found in handshake request, expected query parameter 'v'"));
  }
}

const authenticate = (socket, timeout) => async function (token) {
  try {
    clearTimeout(timeout);
    let disconnected = false;
    socket.on("disconnect", () => {
      disconnected = true;
      this.middlewareServer.remove(socket);
    });
    socket.userId = await socket.outbound.getUserIdByToken(token);
    assert.assigned(socket.userId, "user id retrieved from token");
    await this.middlewareServer.register(socket);
    if (!disconnected) {
      socket.emit("welcome");
    }
  } catch (error) {
    socket.emit("alert", error instanceof Error ? error.message : error);
    socket.disconnect();
  }
};

function onConnection(socket) {
  const timeout = setTimeout(() => {
    debug("Connection timeout reached (%dms)", this.config.timeout);
    socket.emit("alert", "Authentication timeout reached");
    socket.disconnect();
  }, this.config.timeout);
  socket
    .on("error", (error) => debug("Socket received an error: %s", error instanceof Error ? error.message : error))
    .on("authenticate", authenticate(socket, timeout).bind(this));
}

module.exports = class OutboundServer {

  constructor(outbounds, config, middlewareServer) {
    this.outbounds = outbounds;
    this.config = config;
    this.middlewareServer = middlewareServer;
    this.io = undefined;
    Object.seal(this);
  }

  async start(server) {
    this.io = new SocketIO(server)
      .use(checkVersion.bind(this))
      .on("connection", onConnection.bind(this));
  }

  async stop() {
    await new Promise((resolve) => this.io.server.close(resolve));
  }

};
<commit_msg>[1.0.0] Use of once instead of on for one-time events<commit_after>const check = require("check-types");
const debug = require("debug")("bow:OutboundServer");
const SocketIO = require("socket.io");

const assert = require("./utils/assert");
const normalizeVersion = require("./utils/normalizeVersion");

function getMatchingVersionOutbound(version) {
  return this.outbounds
    .filter((outbound) => version === outbound.version)
    [0];
}

function checkVersion(socket, next) {
  const version = normalizeVersion(socket.handshake.query.v);
  if (check.nonEmptyString(version)) {
    const outbound = getMatchingVersionOutbound.call(this, version);
    if (check.assigned(outbound)) {
      socket.outbound = outbound;
      next();
    } else {
      next(new Error(`Version '${version}' not supported`));
    }
  } else {
    next(new Error("Version not found in handshake request, expected query parameter 'v'"));
  }
}

const authenticate = (socket, timeout) => async function (token) {
  try {
    clearTimeout(timeout);
    let disconnected = false;
    socket.once("disconnect", () => {
      disconnected = true;
      this.middlewareServer.remove(socket);
    });
    socket.userId = await socket.outbound.getUserIdByToken(token);
    assert.assigned(socket.userId, "user id retrieved from token");
    await this.middlewareServer.register(socket);
    if (!disconnected) {
      socket.emit("welcome");
    }
  } catch (error) {
    socket.emit("alert", error instanceof Error ? error.message : error);
    socket.disconnect();
  }
};

function onConnection(socket) {
  const timeout = setTimeout(() => {
    debug("Connection timeout reached (%dms)", this.config.timeout);
    socket.emit("alert", "Authentication timeout reached");
    socket.disconnect();
  }, this.config.timeout);
  socket
    .on("error", (error) => debug("Socket received an error: %s", error instanceof Error ? error.message : error))
    .once("authenticate", authenticate(socket, timeout).bind(this));
}

module.exports = class OutboundServer {

  constructor(outbounds, config, middlewareServer) {
    this.outbounds = outbounds;
    this.config = config;
    this.middlewareServer = middlewareServer;
    this.io = undefined;
    Object.seal(this);
  }

  async start(server) {
    this.io = new SocketIO(server)
      .use(checkVersion.bind(this))
      .on("connection", onConnection.bind(this));
  }

  async stop() {
    await new Promise((resolve) => this.io.server.close(resolve));
  }

};
<|endoftext|>


<commit_before>/*
 * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

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

const path = require('path')
const express = require('express')
const debug = require('debug')('signalk-server:config')
const _ = require('lodash')
const fs = require('fs')
const uuidv4 = require('uuid/v4')
const semver = require('semver')

let disableWriteSettings = false

function load(app) {
  app.__argv = process.argv.slice(2)
  app.argv = require('minimist')(app.__argv)

  const config = (app.config = app.config || {})
  const env = (app.env = process.env)

  config.getExternalHostname = getExternalHostname.bind(config, config)
  config.getExternalPort = getExternalPort.bind(config, config)

  config.appPath = config.appPath || path.normalize(__dirname + '/../../')
  debug('appPath:' + config.appPath)

  try {
    const pkg = require('../../package.json')
    config.name = pkg.name
    config.author = pkg.author
    config.contributors = pkg.contributors
    config.version = pkg.version
    config.description = pkg.description

    checkPackageVersion('@signalk/server-admin-ui', pkg, app.config.appPath)
  } catch (err) {
    console.error('error parsing package.json', err)
    process.exit(1)
  }

  setConfigDirectory(app)
  if (_.isObject(app.config.settings)) {
    debug('Using settings from constructor call, not reading defaults')
    disableWriteSettings = true
  } else {
    readSettingsFile(app)
    setFullDefaults(app)
  }
  setSelfSettings(app)

  if (app.argv['sample-nmea0183-data']) {
    const sample = path.join(app.config.appPath, 'samples/plaka.log')
    console.log(`Using sample data from ${sample}`)
    app.config.settings.pipedProviders.push({
      id: 'nmea0183-sample-data',
      pipeElements: [
        {
          type: 'providers/simple',
          options: {
            logging: false,
            type: 'FileStream',
            subOptions: {
              dataType: 'NMEA0183',
              filename: sample
            }
          }
        }
      ],
      enabled: true
    })
    if (app.argv['override-timestamps']) {
      app.config.overrideTimestampWithNow = true
    }
  }

  if (app.argv['sample-n2k-data']) {
    const sample = path.join(app.config.appPath, 'samples/aava-n2k.data')
    console.log(`Using sample data from ${sample}`)
    app.config.settings.pipedProviders.push({
      id: 'n2k-sample-data',
      pipeElements: [
        {
          type: 'providers/simple',
          options: {
            logging: false,
            type: 'FileStream',
            subOptions: {
              dataType: 'NMEA2000JS',
              filename: sample
            }
          }
        }
      ],
      enabled: true
    })
    if (app.argv['override-timestamps']) {
      app.config.overrideTimestampWithNow = true
    }
  }

  if (app.argv.securityenabled && !app.config.security) {
    app.config.settings.security = { strategy: './tokensecurity' }
  }

  if (env.SSLPORT) {
    config.settings.ssl = true
  }

  if (!_.isUndefined(app.env.WSCOMPRESSION)) {
    config.settings.wsCompression =
      app.env.WSCOMPRESSION.toLowerCase() === 'true'
  }

  require('./development')(app)
  require('./production')(app)
}

function checkPackageVersion(name, pkg, appPath) {
  const expected = pkg.dependencies[name]
  let modulePackageJsonPath = path.join(
    appPath,
    'node_modules',
    name,
    'package.json'
  )
  if (!fs.existsSync(modulePackageJsonPath)) {
    modulePackageJsonPath = path.join(appPath, '..', name, 'package.json')
  }
  const installed = require(modulePackageJsonPath)

  if (!semver.satisfies(installed.version, expected)) {
    console.error(
      `invalid version of the ${name} package is installed ${installed.version} !== ${expected}`
    )
    process.exit(1)
  }
}

// Establish what the config directory path is.
function getConfigDirectory({ argv, config, env }) {
  // Possible paths in order of priority.
  const configPaths = [
    env.SIGNALK_NODE_CONDFIG_DIR,
    env.SIGNALK_NODE_CONFIG_DIR,
    config.configPath,
    argv.c,
    argv.s && config.appPath,
    env.HOME && path.join(env.HOME, '.signalk'),
    config.appPath
  ]
  // Find first config directory path that has a truthy value.
  const configPath = path.resolve(_.find(configPaths))
  debug('configDirPath: ' + configPath)
  return configPath
}

// Create directories and set app.config.configPath.
function setConfigDirectory(app) {
  app.config.configPath = getConfigDirectory(app)
  if (app.config.configPath !== app.config.appPath) {
    if (!fs.existsSync(app.config.configPath)) {
      fs.mkdirSync(app.config.configPath)
      debug(`configDir Created: ${app.config.configPath}`)
    }
    const configPackage = path.join(app.config.configPath, 'package.json')
    if (!fs.existsSync(configPackage)) {
      fs.writeFileSync(
        configPackage,
        JSON.stringify(pluginsPackageJsonTemplate, null, 2)
      )
    }
    const npmrcPath = path.join(app.config.configPath, '.npmrc')
    if (!fs.existsSync(npmrcPath)) {
      fs.writeFileSync(npmrcPath, 'package-lock=false\n')
    } else {
      const contents = fs.readFileSync(npmrcPath)
      if (contents.indexOf('package-lock=') === -1) {
        fs.appendFileSync(npmrcPath, '\npackage-lock=false\n')
      }
    }
  }
}

function getDefaultsPath(app) {
  const defaultsFile =
    app.config.configPath !== app.config.appPath
      ? 'defaults.json'
      : 'settings/defaults.json'
  return path.join(app.config.configPath, defaultsFile)
}

function readDefaultsFile(app) {
  const defaultsPath = getDefaultsPath(app)
  // return require(defaultsPath)
  const data = fs.readFileSync(defaultsPath)
  return JSON.parse(data)
}

function setFullDefaults(app) {
  const defaultsPath = getDefaultsPath(app)
  try {
    app.config.defaults = readDefaultsFile(app)
    debug(`Found defaults at ${defaultsPath.toString()}`)
  } catch (e) {
    if (e.code && e.code === 'ENOENT') {
      debug(`No defaults found at ${defaultsPath.toString()}`)
    } else {
      console.log(e)
    }
    app.config.defaults = { vessels: { self: {} } }
  }
}

function writeDefaultsFile(app, defaults, cb) {
  fs.writeFile(getDefaultsPath(app), JSON.stringify(defaults, null, 2), cb)
}

function setSelfSettings(app) {
  let name = _.get(app.config.defaults, 'vessels.self.name')
  let mmsi = _.get(app.config.defaults, 'vessels.self.mmsi')
  let uuid = _.get(app.config.defaults, 'vessels.self.uuid')

  if (app.config.settings.vessel) {
    // backwards compatibility for settings files with 'vessel'
    if (!mmsi && !uuid) {
      mmsi = app.config.settings.vessel.mmsi
      uuid = app.config.settings.vessel.uuid
      if (mmsi) {
        app.config.defaults.vessels.self.mmsi = mmsi
      }
      if (uuid) {
        app.config.defaults.vessels.self.uuid = uuid
      }
    }
    if (!name) {
      name = app.config.settings.vessel.name
      app.config.defaults.vessels.self.name = name
    }
  }

  if (mmsi && !_.isString(mmsi)) {
    throw new Error(`invalid mmsi: ${mmsi}`)
  }

  if (uuid && !_.isString(uuid)) {
    throw new Error(`invalid uuid: ${uuid}`)
  }

  if (_.isUndefined(mmsi) && _.isUndefined(uuid)) {
    uuid = 'urn:mrn:signalk:uuid:' + uuidv4()
    _.set(app.config.defaults, 'vessels.self.uuid', uuid)
    if (!disableWriteSettings) {
      writeDefaultsFile(app, app.config.defaults, err => {
        if (err) {
          console.error(`unable to write defaults file: ${err}`)
        }
      })
    }
  }

  if (mmsi) {
    app.selfType = 'mmsi'
    app.selfId = 'urn:mrn:imo:mmsi:' + mmsi
  } else if (uuid) {
    app.selfType = 'uuid'
    app.selfId = uuid
  }
  if (app.selfType) {
    debug(app.selfType.toUpperCase() + ': ' + app.selfId)
  }
  app.selfContext = 'vessels.' + app.selfId
}

function readSettingsFile(app) {
  const settings = getSettingsFilename(app)
  if (!app.argv.s && !fs.existsSync(settings)) {
    console.log('Settings file does not exist, using empty settings')
    app.config.settings = {}
  } else {
    debug('Using settings file: ' + settings)
    app.config.settings = require(settings)
  }
  if (_.isUndefined(app.config.settings.pipedProviders)) {
    app.config.settings.pipedProviders = []
  }
  if (_.isUndefined(app.config.settings.interfaces)) {
    app.config.settings.interfaces = {}
  }
}

function writeSettingsFile(app, settings, cb) {
  if (!disableWriteSettings) {
    const settingsPath = getSettingsFilename(app)
    fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), cb)
  } else {
    cb()
  }
}

function getSettingsFilename(app) {
  if (process.env.SIGNALK_NODE_SETTINGS) {
    debug(
      'Settings filename was set in environment SIGNALK_NODE_SETTINGS, overriding all other options'
    )
    return path.resolve(process.env.SIGNALK_NODE_SETTINGS)
  }

  const settingsFile = app.argv.s || 'settings.json'
  return path.join(app.config.configPath, settingsFile)
}

function getExternalHostname(config) {
  if (process.env.EXTERNALHOST) {
    return process.env.EXTERNALHOST
  }
  if (config.settings.proxy_host) {
    return config.settings.proxy_host
  } else if (config.settings.hostname) {
    return config.settings.hostname
  }
  try {
    return require('os').hostname()
  } catch (ex) {
    return 'hostname_not_available'
  }
}

function getExternalPort(config) {
  if (process.env.EXTERNALPORT) {
    return process.env.EXTERNALPORT
  }
  if (config.settings.proxy_port) {
    return config.settings.proxy_port
  } else if (config.port) {
    return config.port
  }
  return ''
}

const pluginsPackageJsonTemplate = {
  name: 'signalk-server-config',
  version: '0.0.1',
  description: 'This file is here to track your plugin and webapp installs.',
  repository: {},
  license: 'Apache-2.0'
}

module.exports = {
  load,
  getConfigDirectory,
  writeSettingsFile,
  writeDefaultsFile,
  readDefaultsFile
}
<commit_msg>fix: invalid defaults.json gets ignored and overwritten (#932)<commit_after>/*
 * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

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

const path = require('path')
const express = require('express')
const debug = require('debug')('signalk-server:config')
const _ = require('lodash')
const fs = require('fs')
const uuidv4 = require('uuid/v4')
const semver = require('semver')

let disableWriteSettings = false

function load(app) {
  app.__argv = process.argv.slice(2)
  app.argv = require('minimist')(app.__argv)

  const config = (app.config = app.config || {})
  const env = (app.env = process.env)

  config.getExternalHostname = getExternalHostname.bind(config, config)
  config.getExternalPort = getExternalPort.bind(config, config)

  config.appPath = config.appPath || path.normalize(__dirname + '/../../')
  debug('appPath:' + config.appPath)

  try {
    const pkg = require('../../package.json')
    config.name = pkg.name
    config.author = pkg.author
    config.contributors = pkg.contributors
    config.version = pkg.version
    config.description = pkg.description

    checkPackageVersion('@signalk/server-admin-ui', pkg, app.config.appPath)
  } catch (err) {
    console.error('error parsing package.json', err)
    process.exit(1)
  }

  setConfigDirectory(app)
  if (_.isObject(app.config.settings)) {
    debug('Using settings from constructor call, not reading defaults')
    disableWriteSettings = true
  } else {
    readSettingsFile(app)
    setFullDefaults(app)
  }
  setSelfSettings(app)

  if (app.argv['sample-nmea0183-data']) {
    const sample = path.join(app.config.appPath, 'samples/plaka.log')
    console.log(`Using sample data from ${sample}`)
    app.config.settings.pipedProviders.push({
      id: 'nmea0183-sample-data',
      pipeElements: [
        {
          type: 'providers/simple',
          options: {
            logging: false,
            type: 'FileStream',
            subOptions: {
              dataType: 'NMEA0183',
              filename: sample
            }
          }
        }
      ],
      enabled: true
    })
    if (app.argv['override-timestamps']) {
      app.config.overrideTimestampWithNow = true
    }
  }

  if (app.argv['sample-n2k-data']) {
    const sample = path.join(app.config.appPath, 'samples/aava-n2k.data')
    console.log(`Using sample data from ${sample}`)
    app.config.settings.pipedProviders.push({
      id: 'n2k-sample-data',
      pipeElements: [
        {
          type: 'providers/simple',
          options: {
            logging: false,
            type: 'FileStream',
            subOptions: {
              dataType: 'NMEA2000JS',
              filename: sample
            }
          }
        }
      ],
      enabled: true
    })
    if (app.argv['override-timestamps']) {
      app.config.overrideTimestampWithNow = true
    }
  }

  if (app.argv.securityenabled && !app.config.security) {
    app.config.settings.security = { strategy: './tokensecurity' }
  }

  if (env.SSLPORT) {
    config.settings.ssl = true
  }

  if (!_.isUndefined(app.env.WSCOMPRESSION)) {
    config.settings.wsCompression =
      app.env.WSCOMPRESSION.toLowerCase() === 'true'
  }

  require('./development')(app)
  require('./production')(app)
}

function checkPackageVersion(name, pkg, appPath) {
  const expected = pkg.dependencies[name]
  let modulePackageJsonPath = path.join(
    appPath,
    'node_modules',
    name,
    'package.json'
  )
  if (!fs.existsSync(modulePackageJsonPath)) {
    modulePackageJsonPath = path.join(appPath, '..', name, 'package.json')
  }
  const installed = require(modulePackageJsonPath)

  if (!semver.satisfies(installed.version, expected)) {
    console.error(
      `invalid version of the ${name} package is installed ${installed.version} !== ${expected}`
    )
    process.exit(1)
  }
}

// Establish what the config directory path is.
function getConfigDirectory({ argv, config, env }) {
  // Possible paths in order of priority.
  const configPaths = [
    env.SIGNALK_NODE_CONDFIG_DIR,
    env.SIGNALK_NODE_CONFIG_DIR,
    config.configPath,
    argv.c,
    argv.s && config.appPath,
    env.HOME && path.join(env.HOME, '.signalk'),
    config.appPath
  ]
  // Find first config directory path that has a truthy value.
  const configPath = path.resolve(_.find(configPaths))
  debug('configDirPath: ' + configPath)
  return configPath
}

// Create directories and set app.config.configPath.
function setConfigDirectory(app) {
  app.config.configPath = getConfigDirectory(app)
  if (app.config.configPath !== app.config.appPath) {
    if (!fs.existsSync(app.config.configPath)) {
      fs.mkdirSync(app.config.configPath)
      debug(`configDir Created: ${app.config.configPath}`)
    }
    const configPackage = path.join(app.config.configPath, 'package.json')
    if (!fs.existsSync(configPackage)) {
      fs.writeFileSync(
        configPackage,
        JSON.stringify(pluginsPackageJsonTemplate, null, 2)
      )
    }
    const npmrcPath = path.join(app.config.configPath, '.npmrc')
    if (!fs.existsSync(npmrcPath)) {
      fs.writeFileSync(npmrcPath, 'package-lock=false\n')
    } else {
      const contents = fs.readFileSync(npmrcPath)
      if (contents.indexOf('package-lock=') === -1) {
        fs.appendFileSync(npmrcPath, '\npackage-lock=false\n')
      }
    }
  }
}

function getDefaultsPath(app) {
  const defaultsFile =
    app.config.configPath !== app.config.appPath
      ? 'defaults.json'
      : 'settings/defaults.json'
  return path.join(app.config.configPath, defaultsFile)
}

function readDefaultsFile(app) {
  const defaultsPath = getDefaultsPath(app)
  // return require(defaultsPath)
  const data = fs.readFileSync(defaultsPath)
  return JSON.parse(data)
}

function setFullDefaults(app) {
  const defaultsPath = getDefaultsPath(app)
  try {
    app.config.defaults = readDefaultsFile(app)
    debug(`Found defaults at ${defaultsPath.toString()}`)
  } catch (e) {
    if (e.code && e.code === 'ENOENT') {
      debug(`No defaults found at ${defaultsPath.toString()}`)
    } else {
      console.error(`unable to parse ${defaultsPath.toString()}`)
      console.error(e)
      process.exit(1)
    }
    app.config.defaults = { vessels: { self: {} } }
  }
}

function writeDefaultsFile(app, defaults, cb) {
  fs.writeFile(getDefaultsPath(app), JSON.stringify(defaults, null, 2), cb)
}

function setSelfSettings(app) {
  let name = _.get(app.config.defaults, 'vessels.self.name')
  let mmsi = _.get(app.config.defaults, 'vessels.self.mmsi')
  let uuid = _.get(app.config.defaults, 'vessels.self.uuid')

  if (app.config.settings.vessel) {
    // backwards compatibility for settings files with 'vessel'
    if (!mmsi && !uuid) {
      mmsi = app.config.settings.vessel.mmsi
      uuid = app.config.settings.vessel.uuid
      if (mmsi) {
        app.config.defaults.vessels.self.mmsi = mmsi
      }
      if (uuid) {
        app.config.defaults.vessels.self.uuid = uuid
      }
    }
    if (!name) {
      name = app.config.settings.vessel.name
      app.config.defaults.vessels.self.name = name
    }
  }

  if (mmsi && !_.isString(mmsi)) {
    throw new Error(`invalid mmsi: ${mmsi}`)
  }

  if (uuid && !_.isString(uuid)) {
    throw new Error(`invalid uuid: ${uuid}`)
  }

  if (_.isUndefined(mmsi) && _.isUndefined(uuid)) {
    uuid = 'urn:mrn:signalk:uuid:' + uuidv4()
    _.set(app.config.defaults, 'vessels.self.uuid', uuid)
    if (!disableWriteSettings) {
      writeDefaultsFile(app, app.config.defaults, err => {
        if (err) {
          console.error(`unable to write defaults file: ${err}`)
        }
      })
    }
  }

  if (mmsi) {
    app.selfType = 'mmsi'
    app.selfId = 'urn:mrn:imo:mmsi:' + mmsi
  } else if (uuid) {
    app.selfType = 'uuid'
    app.selfId = uuid
  }
  if (app.selfType) {
    debug(app.selfType.toUpperCase() + ': ' + app.selfId)
  }
  app.selfContext = 'vessels.' + app.selfId
}

function readSettingsFile(app) {
  const settings = getSettingsFilename(app)
  if (!app.argv.s && !fs.existsSync(settings)) {
    console.log('Settings file does not exist, using empty settings')
    app.config.settings = {}
  } else {
    debug('Using settings file: ' + settings)
    app.config.settings = require(settings)
  }
  if (_.isUndefined(app.config.settings.pipedProviders)) {
    app.config.settings.pipedProviders = []
  }
  if (_.isUndefined(app.config.settings.interfaces)) {
    app.config.settings.interfaces = {}
  }
}

function writeSettingsFile(app, settings, cb) {
  if (!disableWriteSettings) {
    const settingsPath = getSettingsFilename(app)
    fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), cb)
  } else {
    cb()
  }
}

function getSettingsFilename(app) {
  if (process.env.SIGNALK_NODE_SETTINGS) {
    debug(
      'Settings filename was set in environment SIGNALK_NODE_SETTINGS, overriding all other options'
    )
    return path.resolve(process.env.SIGNALK_NODE_SETTINGS)
  }

  const settingsFile = app.argv.s || 'settings.json'
  return path.join(app.config.configPath, settingsFile)
}

function getExternalHostname(config) {
  if (process.env.EXTERNALHOST) {
    return process.env.EXTERNALHOST
  }
  if (config.settings.proxy_host) {
    return config.settings.proxy_host
  } else if (config.settings.hostname) {
    return config.settings.hostname
  }
  try {
    return require('os').hostname()
  } catch (ex) {
    return 'hostname_not_available'
  }
}

function getExternalPort(config) {
  if (process.env.EXTERNALPORT) {
    return process.env.EXTERNALPORT
  }
  if (config.settings.proxy_port) {
    return config.settings.proxy_port
  } else if (config.port) {
    return config.port
  }
  return ''
}

const pluginsPackageJsonTemplate = {
  name: 'signalk-server-config',
  version: '0.0.1',
  description: 'This file is here to track your plugin and webapp installs.',
  repository: {},
  license: 'Apache-2.0'
}

module.exports = {
  load,
  getConfigDirectory,
  writeSettingsFile,
  writeDefaultsFile,
  readDefaultsFile
}
<|endoftext|>


<commit_before>// ContentNode
// -----------------------------------------------------------------------------

var ContentNode = function(g, key, data) {
  uv.Node.call(this);

  this.g = g;
  this.key = key;

  // Node is expectd to have a type property
  this.type = data.type;

  // Memoize raw data for the build process
  this.data = data;
};

ContentNode.prototype = uv.inherit(uv.Node);

ContentNode.prototype.build = function() {
  var that = this;

  // Init children relationship
  this.replace('children', new uv.SortedHash());

  if (that.data.children) {
    // Register children if there are some
    uv.each(that.data.children, function(nodeId) {
      // Find referenced node
      var n = that.g.get('nodes', nodeId);

      if (!n) {
        throw 'node '+key+' not found at document';
      }

      that.set('children', nodeId, n);
      // Register parent
      n.parent = that;
    });
  }
};

ContentNode.prototype.serialize = function() {
  return this.data;
};

// Add child either to the end of the children list or after
// a given reference node
ContentNode.prototype.addChild = function(node, referenceNode) {
  var that = this;
  if (referenceNode) { // insert after referenceNode
    that.all('children').set(node.key, node, that.all('children').index(referenceNode.key)+1);
  } else { // insert at the end
    that.set('children', node.key, node);
  }

  // The new node needs to be attached to ContentGraph's node map as well
  that.g.set('nodes', node.key, node);
};

ContentNode.prototype.removeChild = function(key) {
  this.all('children').del(key);

  // TODO: Also remove from g.all('nodes') when no longer referenced
};

// ContentGraph
// -----------------------------------------------------------------------------

var ContentGraph = function(g) {
  var that = this;
  uv.Node.call(this);

  this.type = 'document';
  this.data = g;
  this.g = g;

  this.key = 'root';

  this.nodeCount = 1000;

  // Register ContentNodes
  uv.each(g.nodes, function(node, key) {
    that.set('nodes', key, new ContentNode(that, key, node));
  });

  // Register direct children
  uv.each(g.children, function(nodeId) {
    // Get referenced node
    var n = that.get('nodes', nodeId);

    if (!n) {
      throw 'node '+key+' not found at document';
    }
    that.set('children', nodeId, n);

    n.parent = that;
  });

  // Now that all nodes are registered we can build them
  this.all('nodes').each(function(r, key, index) {
    r.build();
  });
};

ContentGraph.prototype = uv.inherit(ContentNode);

// Generates a unique id for new nodes
ContentGraph.prototype.generateId = function () {
  return this.nodeCount += 1;
};

ContentGraph.prototype.addChild = function(node, referenceNode) {
  var that = this;
  if (referenceNode) { // insert after referenceNode
    that.all('children').set(node.key, node, that.all('children').index(referenceNode.key)+1);
  } else { // insert at the end
    that.set('children', node.key, node);
  }

  // The new node needs to be attached to ContentGraph's node map as well
  that.set('nodes', node.key, node);
};

// Serializes the current state to a JSON representation
ContentGraph.prototype.serialize = function() {
  var result = {
    title: this.data.title,
    nodes: {},
    nodeCount: this.nodeCount
  };

  this.all('nodes').each(function(node, key, index) {
    result.nodes[key] = node.serialize();
  });

  return result;
};

// HTMLRenderer
// =============================================================================
// This is just a stub, eventually we'll use a template engine here.

var HTMLRenderer = function(root) {

  // Implement node types
  var renderers = {
    document: function(node) {
      var str = '

Document

' + node.data.title + '
';

      node.all('children').each(function(node, key, index) {
        str += renderers[node.type](node);
      });

      str += '
';
      return str;
    },
    section: function(node) {
      // var str = '<h'+(this.level+1)+' id="section_'+this.sectionId+'">'+this.val+'</h'+(this.level+1)+'>';
      var str = '

Section

'+node.data.name+'
';

      node.all('children').each(function(node, key, index) {
        str += renderers[node.type](node); // node.render();
      });

      str += "
";
      return str;
    },
    paragraph: function(node) {
      var str = '

Paragraph
';

      var converter = new Showdown.converter();

      str += converter.makeHtml(node.data.content);
      // node.all('children').each(function(node, key, index) {
      //   str += renderers[node.type](node); // node.render();
      // });

      str += '
';
      return str;
    },
    image: function(node) {
      var str = '

Image
';

      if (node.data.url.length > 0) {
        str += '';
      } else {
        str += "image placeholder ...";
      }

      str += "
";
      return str;
    }
  };

  return {
    render: function() {
      // Traverse the document
      return renderers[root.type](root);
    }
  };
};

<commit_msg>Bugfix regarding ContentGraph serialization.<commit_after>// ContentNode
// -----------------------------------------------------------------------------

var ContentNode = function(g, key, data) {
  uv.Node.call(this);

  this.g = g;
  this.key = key;

  // Node is expectd to have a type property
  this.type = data.type;

  // Memoize raw data for the build process
  this.data = data;
};

ContentNode.prototype = uv.inherit(uv.Node);

ContentNode.prototype.build = function() {
  var that = this;

  // Init children relationship
  this.replace('children', new uv.SortedHash());

  if (that.data.children) {
    // Register children if there are some
    uv.each(that.data.children, function(nodeId) {
      // Find referenced node
      var n = that.g.get('nodes', nodeId);

      if (!n) {
        throw 'node '+key+' not found at document';
      }

      that.set('children', nodeId, n);
      // Register parent
      n.parent = that;
    });
  }
};

ContentNode.prototype.serialize = function() {
  var result = _.extend({}, this.data);
  result.children = [];

  this.all('children').each(function(node, key, index) {
    result.children.push(key);
  });

  return result;
};

// Add child either to the end of the children list or after
// a given reference node
ContentNode.prototype.addChild = function(node, referenceNode) {
  var that = this;
  if (referenceNode) { // insert after referenceNode
    that.all('children').set(node.key, node, that.all('children').index(referenceNode.key)+1);
  } else { // insert at the end
    that.set('children', node.key, node);
  }

  // The new node needs to be attached to ContentGraph's node map as well
  that.g.set('nodes', node.key, node);
};

ContentNode.prototype.removeChild = function(key) {
  this.all('children').del(key);

  // TODO: Also remove from g.all('nodes') when no longer referenced
};

// ContentGraph
// -----------------------------------------------------------------------------

var ContentGraph = function(g) {
  var that = this;
  uv.Node.call(this);

  this.type = 'document';
  this.data = g;
  this.g = this;

  this.key = 'root';

  this.nodeCount = g.nodeCount || 1000;

  // Register ContentNodes
  uv.each(g.nodes, function(node, key) {
    that.set('nodes', key, new ContentNode(that, key, node));
  });

  // Register direct children
  uv.each(g.children, function(nodeId) {
    // Get referenced node
    var n = that.get('nodes', nodeId);

    if (!n) {
      throw 'node '+key+' not found at document';
    }
    that.set('children', nodeId, n);

    n.parent = that;
  });

  // Now that all nodes are registered we can build them
  this.all('nodes').each(function(r, key, index) {
    r.build();
  });
};

ContentGraph.prototype = uv.inherit(ContentNode);

// Generates a unique id for new nodes
ContentGraph.prototype.generateId = function () {
  return this.nodeCount += 1;
};

ContentGraph.prototype.addChild = function(node, referenceNode) {
  var that = this;
  if (referenceNode) { // insert after referenceNode
    that.all('children').set(node.key, node, that.all('children').index(referenceNode.key)+1);
  } else { // insert at the end
    that.set('children', node.key, node);
  }

  // The new node needs to be attached to ContentGraph's node map as well
  that.set('nodes', node.key, node);
};

// Serializes the current state to a JSON representation
ContentGraph.prototype.serialize = function() {
  var result = {
    title: this.data.title,
    nodes: {},
    children: [],
    nodeCount: this.nodeCount
  };

  this.all('children').each(function(node, key, index) {
    result.children.push(key);
  });

  this.all('nodes').each(function(node, key, index) {

    console.log(key);

    result.nodes[key] = node.serialize();
  });

  return result;
};

// HTMLRenderer
// =============================================================================
// This is just a stub, eventually we'll use a template engine here.

var HTMLRenderer = function(root) {

  // Implement node types
  var renderers = {
    document: function(node) {
      var str = '

Document

' + node.data.title + '
';

      node.all('children').each(function(node, key, index) {
        str += renderers[node.type](node);
      });

      str += '
';
      return str;
    },
    section: function(node) {
      // var str = '<h'+(this.level+1)+' id="section_'+this.sectionId+'">'+this.val+'</h'+(this.level+1)+'>';
      var str = '

Section

'+node.data.name+'
';

      node.all('children').each(function(node, key, index) {
        str += renderers[node.type](node); // node.render();
      });

      str += "
";
      return str;
    },
    paragraph: function(node) {
      var str = '

Paragraph
';

      var converter = new Showdown.converter();

      str += converter.makeHtml(node.data.content);
      // node.all('children').each(function(node, key, index) {
      //   str += renderers[node.type](node); // node.render();
      // });

      str += '
';
      return str;
    },
    image: function(node) {
      var str = '

Image
';

      if (node.data.url.length > 0) {
        str += '';
      } else {
        str += "image placeholder ...";
      }

      str += "
";
      return str;
    }
  };

  return {
    render: function() {
      // Traverse the document
      return renderers[root.type](root);
    }
  };
};

<|endoftext|>


<commit_before>import events from 'events';

import Keyboard from '../interactions/keyboard';
import LayerTimeContext from './layer-time-context';
import Surface from '../interactions/surface';
import TimelineTimeContext from './timeline-time-context';
import Track from './track';
import TrackCollection from './track-collection';

/**
 * The `timeline` is the main entry point of a temporal visualization.
 *
 * it:
 * - contains factories to manage its `tracks` and `layers`,
 * - get or set the view window overs its `tracks` through `offset`, `zoom`, `pixelsPerSecond`, `visibleWidth`,
 * - is the central hub for all user interaction events (keyboard, mouse),
 * - holds the current interaction `state` which defines how the different timeline elements (tracks, layers, shapes) respond to user interactions.
 *
 * ```js
 * const visibleWidth = 500;  // default width for all created `Track`
 * const duration = 10;       // the timeline should dislay 10 seconds of data
 * const pixelsPerSeconds = width / duration;
 * const timeline = new ui.core.Timeline(pixelsPerSecond, width);
 * ```
 */
export default class Timeline extends events.EventEmitter {
  /**
   * Creates a new `Timeline` instance
   * @param {Number} [pixelsPerSecond=100] - the number of pixels per seconds the timeline should display
   * @param {Number} [visibleWidth=1000] - the default visible width for all the tracks
   */
  constructor(pixelsPerSecond = 100, visibleWidth = 1000, {
    registerKeyboard = true
  } = {}) {

    super();

    this._tracks = new TrackCollection(this);
    this._state = null;

    // default interactions
    this._surfaceCtor = Surface;

    if (registerKeyboard) {
      this.createInteraction(Keyboard, document);
    }

    // stores
    this._trackById = {};
    this._groupedLayers = {};

    /**
     * @this Timeline
     * @attribute {TimelineTimeContext} - master time context of the graph
     */
    this.timeContext = new TimelineTimeContext(pixelsPerSecond, visibleWidth);
  }

  /**
   * updates `TimeContext`'s offset
   * @attribute {Number} [offset=0]
   */
  get offset() {
    return this.timeContext.offset;
  }

  set offset(value) {
    this.timeContext.offset = value;
  }

  get zoom() {
    return this.timeContext.zoom;
  }

  set zoom(value) {
    this.timeContext.zoom = value;
  }

  get pixelsPerSecond() {
    return this.timeContext.pixelsPerSecond;
  }

  set pixelsPerSecond(value) {
    this.timeContext.pixelsPerSecond = value;
  }

  get visibleWidth() {
    return this.timeContext.visibleWidth;
  }

  set visibleWidth(value) {
    this.timeContext.visibleWidth = value;
  }

  get timeToPixel() {
    return this.timeContext.timeToPixel;
  }

  /**
   *  @readonly
   */
  get visibleDuration() {
    return this.timeContext.visibleDuration;
  }

  // @NOTE maybe expose as public instead of get/set for nothing...
  set maintainVisibleDuration(bool) {
    this.timeContext.maintainVisibleDuration = bool;
  }

  get maintainVisibleDuration() {
    return this.timeContext.maintainVisibleDuration;
  }

  // @readonly - used in track collection
  get groupedLayers() {
    return this._groupedLayers;
  }

  /**
   *  Override the default Surface that is instanciated on each
   *  @param {EventSource} ctor - the constructor to use to build surfaces
   */
  configureSurface(ctor) {
    this._surfaceCtor = ctor;
  }

  /**
   * Factory method to add interaction modules the timeline should listen to.
   * By default, the timeline listen to Keyboard, and instanciate a `Surface` on each container.
   * Can be used to install any interaction implementing the `EventSource` interface
   * @param {EventSource} ctor - the contructor of the interaction module to instanciate
   * @param el {DOMElement} the DOM element to bind to the EventSource module
   * @param options {Object} options to be applied to the ctor (defaults to `{}`)
   */
  createInteraction(ctor, el, options = {}) {
    const interaction = new ctor(el, options);
    interaction.on('event', (e) => this._handleEvent(e));
  }

  /**
   * returns an array of the layers which positions
   * and sizes matches a pointer Event
   * @param {WavesEvent} e - the event from the Surface
   * @return {Array} - matched layers
   */
  getHitLayers(e) {
    const clientX = e.originalEvent.clientX;
    const clientY = e.originalEvent.clientY;
    let layers = [];

    this.layers.forEach((layer) => {
      if (!layer.params.hittable) { return; }
      const br = layer.$el.getBoundingClientRect();

      if (
        clientX > br.left && clientX < br.right &&
        clientY > br.top && clientY < br.bottom
      ) {
        layers.push(layer);
      }
    });

    return layers;
  }

  /**
   * The callback that is used to listen to interactions modules
   * @params {Event} e - a custom event generated by interaction modules
   */
  _handleEvent(e) {
    const hitLayers = (e.source === 'surface') ?
      this.getHitLayers(e) : null;
    // emit event as a middleware
    this.emit('event', e, hitLayers);
    // propagate to the state
    if (!this._state) { return; }
    this._state.handleEvent(e, hitLayers);
  }

  /**
   * Changes the state of the timeline
   * @param {BaseState} - the state in which the timeline must be setted
   */
  set state(state) {
    if (this._state) { this._state.exit(); }
    this._state = state;
    if (this._state) { this._state.enter(); }
  }

  get state() {
    return this._state;
  }

  /**
   *  Shortcut to access the Track collection
   *  @return {TrackCollection}
   */
  get tracks() {
    return this._tracks;
  }

  /**
   * Shortcut to access the Layer list
   * @return {Array}
   */
  get layers() {
    return this._tracks.layers;
  }

  /**
   * Adds a track to the timeline
   * Tracks display a view window on the timeline in theirs own SVG element.
   * @param {Track} track
   */
  add(track) {
    if (this.tracks.indexOf(track) !== -1) {
      throw new Error('track already added to the timeline');
    }

    track.configure(this.timeContext);

    this.tracks.push(track);
    this.createInteraction(this._surfaceCtor, track.$el);
  }

  /**
   *  Removes a track from the timeline
   *  @TODO
   */
  remove(track) {
    // should destroy interaction too, avoid ghost eventListeners
  }

  /**
   *  Creates a new track from the configuration define in `configureTracks`
   *  @param {DOMElement} $el - the element to insert the track inside
   *  @param {Object} options - override the defaults options if necessary
   *  @param {String} [trackId=null] - optionnal id to give to the track, only exists in timeline's context
   *  @return {Track}
   */
  createTrack($el, trackHeight = 100, trackId = null) {
    const track = new Track($el, trackHeight);

    if (trackId !== null) {
      if (this._trackById[trackId] !== undefined) {
        throw new Error(`trackId: "${trackId}" is already used`);
      }

      this._trackById[trackId] = track;
    }

    // Add track to the timeline
    this.add(track);
    track.render();
    track.update();

    return track;
  }

  /**
   * Adds a layer to a track, allow to group track arbitrarily inside groups. Basically a wrapper for `track.add(layer)`
   * @param {Layer} layer - the layer to add
   * @param {Track} track - the track to the insert the layer in
   * @param {String} [groupId='default'] - the group in which associate the layer
   */
  addLayer(layer, trackOrTrackId, groupId = 'default', isAxis = false) {
    let track = trackOrTrackId;

    if (typeof trackOrTrackId === 'string') {
      track = this.getTrackById(trackOrTrackId);
    }

    // creates the `LayerTimeContext` if not present
    if (!layer.timeContext) {
      const timeContext = isAxis ?
        this.timeContext : new LayerTimeContext(this.timeContext);

      layer.setTimeContext(timeContext);
    }

    // we should have a Track instance at this point
    track.add(layer);

    if (!this._groupedLayers[groupId]) {
      this._groupedLayers[groupId] = [];
    }

    this._groupedLayers[groupId].push(layer);

    layer.render();
    layer.update();
  }

  /**
   *  Removes a layer from its track (the layer is detatched from the DOM but can still be reused)
   *  @param {Layer} layer - the layer to remove
   */
  removeLayer(layer) {
    this.tracks.forEach(function(track) {
      const index = track.layers.indexOf(layer);
      if (index !== -1) { track.remove(layer); }
    });

    // clean references in helpers
    for (let groupId in this._groupedLayers) {
      const group = this._groupedLayers[groupId];
      const index = group.indexOf(layer);

      if (index !== -1) { group.splice(layer, 1); }

      if (!group.length) {
        delete this._groupedLayers[groupId];
      }
    }
  }

  /**
   *  Returns a track from it's id
   *  @param {String} trackId
   *  @return {Track}
   */
  getTrackById(trackId) {
    return this._trackById[trackId];
  }

  /**
   *  Returns the track containing a given DOM Element, if no match found return null
   *  @param {DOMElement} $el
   *  @return {Track}
   */
  getTrackFromDOMElement($el) {
    let $svg = null;
    let track = null;
    // find the closest `.track` element
    do {
      if ($el.classList.contains('track')) {
        $svg = $el;
      }
      $el = $el.parentNode;
    } while ($svg === null);
    // find the related `Track`
    this.tracks.forEach(function(_track) {
      if (_track.$svg === $svg) { track = _track; }
    });

    return track;
  }

  /**
   * Returns an array of layers from their group Id
   * @param {String} groupId
   * @return {Array}
   */
  getLayersByGroup(groupId) {
    return this._groupedLayers[groupId];
  }

  *[Symbol.iterator]() {
    yield* this.tracks[Symbol.iterator]();
  }
}
<commit_msg>updated Timeline documentation<commit_after>import events from 'events';

import Keyboard from '../interactions/keyboard';
import LayerTimeContext from './layer-time-context';
import Surface from '../interactions/surface';
import TimelineTimeContext from './timeline-time-context';
import Track from './track';
import TrackCollection from './track-collection';

/**
 * Is the main entry point to create a temporal visualization.
 *
 * A `timeline` instance mainly provides the context for any visualization of temporal data and maintains the hierarchy of `Track`, `Layer` and `Shape` over the entiere visualisation.
 * Its main responsabilites are:
 * - maintaining the temporal consistency accross the visualisation through its `timeContext` property (instance of `TimelineTimeContext`).
 * - handling interactions to its current state (acting here as a simple state machine).
 *
 * @TODO insert figure
 *
 * It also contains a reference to all the register track allowing to `render` or `update` all the layer from a single entry point.
 *
 * ## Example Usage
 *
 * ```js
 * const visibleWidth = 500; // default width in pixels for all created `Track`
 * const duration = 10; // the visible area represents 10 seconds
 * const pixelsPerSeconds = visibleWidth / duration;
 * const timeline = new ui.core.Timeline(pixelsPerSecond, width);
 * ```
 */
export default class Timeline extends events.EventEmitter {
  /**
   * @param {Number} [pixelsPerSecond=100] - the default scaling between time and pixels.
   * @param {Number} [visibleWidth=1000] - the default visible area for all registered tracks.
   */
  constructor(pixelsPerSecond = 100, visibleWidth = 1000, {
    registerKeyboard = true
  } = {}) {

    super();

    this._tracks = new TrackCollection(this);
    this._state = null;

    // default interactions
    this._surfaceCtor = Surface;

    if (registerKeyboard) {
      this.createInteraction(Keyboard, document);
    }

    // stores
    this._trackById = {};
    this._groupedLayers = {};

    /** @type {TimelineTimeContext} - master time context for the visualization. */
    this.timeContext = new TimelineTimeContext(pixelsPerSecond, visibleWidth);
  }

  /**
   * Returns `TimelineTimeContext`'s `offset` time domain value.
   * @type {Number} [offset=0]
   */
  get offset() {
    return this.timeContext.offset;
  }

  /**
   * Updates `TimelineTimeContext`'s `offset` time domain value.
   * @type {Number} [offset=0]
   */
  set offset(value) {
    this.timeContext.offset = value;
  }

  /**
   * Returns the `TimelineTimeContext`'s `zoom` value.
   * @type {Number} [offset=0]
   */
  get zoom() {
    return this.timeContext.zoom;
  }

  /**
   * Updates the `TimelineTimeContext`'s `zoom` value.
   * @type {Number} [offset=0]
   */
  set zoom(value) {
    this.timeContext.zoom = value;
  }

  /**
   * Returns the `TimelineTimeContext`'s `pixelsPerSecond` ratio.
   * @type {Number} [offset=0]
   */
  get pixelsPerSecond() {
    return this.timeContext.pixelsPerSecond;
  }

  /**
   * Updates the `TimelineTimeContext`'s `pixelsPerSecond` ratio.
   * @type {Number} [offset=0]
   */
  set pixelsPerSecond(value) {
    this.timeContext.pixelsPerSecond = value;
  }

  /**
   * Returns the `TimelineTimeContext`'s `visibleWidth` pixel domain value.
   * @type {Number} [offset=0]
   */
  get visibleWidth() {
    return this.timeContext.visibleWidth;
  }

  /**
   * Updates the `TimelineTimeContext`'s `visibleWidth` pixel domain value.
   * @type {Number} [offset=0]
   */
  set visibleWidth(value) {
    this.timeContext.visibleWidth = value;
  }

  /**
   * Returns `TimelineTimeContext`'s `timeToPixel` transfert function.
   * @type {Function}
   */
  get timeToPixel() {
    return this.timeContext.timeToPixel;
  }

  /**
   * Returns `TimelineTimeContext`'s `visibleDuration` helper value.
   * @type {Number}
   */
  get visibleDuration() {
    return this.timeContext.visibleDuration;
  }

  /**
   * Updates the `TimelineTimeContext`'s `maintainVisibleDuration` value.
   * Defines if the duration of the visible area should be maintain when the `visibleWidth` attribute is updated.
   * @type {Boolean}
   */
  set maintainVisibleDuration(bool) {
    this.timeContext.maintainVisibleDuration = bool;
  }

  /**
   * Returns `TimelineTimeContext`'s `maintainVisibleDuration` current value.
   * @type {Boolean}
   */
  get maintainVisibleDuration() {
    return this.timeContext.maintainVisibleDuration;
  }

  /**
   * Object maintaining arrays of `Layer` instances ordered by their `groupId`. Is used internally by the `TrackCollection` instance.
   * @type {Object}
   */
  get groupedLayers() {
    return this._groupedLayers;
  }

  /**
   * Overrides the default `Surface` that is instanciated on each `Track` instance. This methos should be called before adding any `Track` instance to the current `timeline`.
   *
   * @param {EventSource} ctor - The constructor to use in order to catch mouse events on each `Track` instances.
   */
  configureSurface(ctor) {
    this._surfaceCtor = ctor;
  }

  /**
   * Factory method to add interaction modules the timeline should listen to.
   * By default, the timeline instanciate a global `Keyboard` instance and a `Surface` instance on each container.
   * Should be used to install new interactions implementing the `EventSource` interface.
   *
   * @param {EventSource} ctor - The contructor of the interaction module to instanciate.
   * @param {DOMElement} $el - The DOM element which will be binded to the `EventSource` module.
   * @param {Object} [options={}] - Options to be applied to the `ctor`.
   */
  createInteraction(ctor, $el, options = {}) {
    const interaction = new ctor($el, options);
    interaction.on('event', (e) => this._handleEvent(e));
  }

  /**
   * Returns a list of the layers situated under the position of a `WaveEvent`.
   *
   * @param {WavesEvent} e - An event triggered by a `WaveEvent`
   * @return {Array} - Matched layers
   */
  getHitLayers(e) {
    const clientX = e.originalEvent.clientX;
    const clientY = e.originalEvent.clientY;
    let layers = [];

    this.layers.forEach((layer) => {
      if (!layer.params.hittable) { return; }
      const br = layer.$el.getBoundingClientRect();

      if (
        clientX > br.left && clientX < br.right &&
        clientY > br.top && clientY < br.bottom
      ) {
        layers.push(layer);
      }
    });

    return layers;
  }

  /**
   * The callback that is used to listen to interactions modules.
   *
   * @param {WaveEvent} e - An event generated by an interaction modules (`EventSource`).
   */
  _handleEvent(e) {
    const hitLayers = (e.source === 'surface') ?
      this.getHitLayers(e) : null;
    // emit event as a middleware
    this.emit('event', e, hitLayers);
    // propagate to the state
    if (!this._state) { return; }
    this._state.handleEvent(e, hitLayers);
  }

  /**
   * Updates the state of the timeline.
   * @type {BaseState}
   */
  set state(state) {
    if (this._state) { this._state.exit(); }
    this._state = state;
    if (this._state) { this._state.enter(); }
  }

  /**
   * Returns the current state of the timeline.
   * @type {BaseState}
   */
  get state() {
    return this._state;
  }

  /**
   * Returns the `TrackCollection` instance
   * @type {TrackCollection}
   */
  get tracks() {
    return this._tracks;
  }

  /**
   * Returns the list of all registered layers.
   * @type {Array}
   */
  get layers() {
    return this._tracks.layers;
  }

  /**
   * Adds a new track to the timeline.
   * @todo add the `trackId` parameter.
   * @param {Track} track - The new track to be registered in the timeline.
   */
  add(track) {
    if (this.tracks.indexOf(track) !== -1) {
      throw new Error('track already added to the timeline');
    }

    track.configure(this.timeContext);

    this.tracks.push(track);
    this.createInteraction(this._surfaceCtor, track.$el);
  }

  /**
   * @todo must be implemented.
   * Removes a track from the timeline.
   */
  remove(track) {
    // should destroy interaction too, avoid ghost eventListeners
  }

  /**
   * Helper to create a new `Track` instance. The `track` is added, rendered and updated before being returned.
   *
   * @param {DOMElement} $el - The DOM element where the track should be inserted.
   * @param {Number} trackHeight - The height of the newly created track.
   * @param {String} [trackId=null] - Optionnal unique id to associate with the track, this id only exists in timeline's context and should be used in conjonction with `addLayer` method.
   * @return {Track}
   */
  createTrack($el, trackHeight = 100, trackId = null) {
    const track = new Track($el, trackHeight);

    if (trackId !== null) {
      if (this._trackById[trackId] !== undefined) {
        throw new Error(`trackId: "${trackId}" is already used`);
      }

      this._trackById[trackId] = track;
    }

    // Add track to the timeline
    this.add(track);
    track.render();
    track.update();

    return track;
  }

  /**
   * Helper to add a `Layer` instance into a given `Track`. Is designed to used in conjonction with the `Timeline~getLayersByGroup` method. The layer is internally rendered and updated.
   *
   * @param {Layer} layer - The `Layer` instance to add into the visualization.
   * @param {(Track|String)} trackOrTrackId - The `Track` instance (or its `id` as defined in the `createTrack` method) where the `Layer` instance should be inserted.
   * @param {String} [groupId='default'] - An optionnal group id in which the `Layer` should be inserted.
   * @param {Boolean} [isAxis] - Set to `true` if the added `layer` is an instance of `AxisLayer` (these layers shares the `TimlineTimeContext` instance of the timeline).
   */
  addLayer(layer, trackOrTrackId, groupId = 'default', isAxis = false) {
    let track = trackOrTrackId;

    if (typeof trackOrTrackId === 'string') {
      track = this.getTrackById(trackOrTrackId);
    }

    // creates the `LayerTimeContext` if not present
    if (!layer.timeContext) {
      const timeContext = isAxis ?
        this.timeContext : new LayerTimeContext(this.timeContext);

      layer.setTimeContext(timeContext);
    }

    // we should have a Track instance at this point
    track.add(layer);

    if (!this._groupedLayers[groupId]) {
      this._groupedLayers[groupId] = [];
    }

    this._groupedLayers[groupId].push(layer);

    layer.render();
    layer.update();
  }

  /**
   * Removes a layer from its track. The layer is detatched from the DOM but can still be reused later.
   *
   * @param {Layer} layer - The layer to remove.
   */
  removeLayer(layer) {
    this.tracks.forEach(function(track) {
      const index = track.layers.indexOf(layer);
      if (index !== -1) { track.remove(layer); }
    });

    // clean references in helpers
    for (let groupId in this._groupedLayers) {
      const group = this._groupedLayers[groupId];
      const index = group.indexOf(layer);

      if (index !== -1) { group.splice(layer, 1); }

      if (!group.length) {
        delete this._groupedLayers[groupId];
      }
    }
  }

  /**
   * Returns a `Track` instance from it's given id.
   *
   * @param {String} trackId
   * @return {Track}
   */
  getTrackById(trackId) {
    return this._trackById[trackId];
  }

  /**
   * Returns the track containing a given DOM Element, returns null if no match found.
   *
   * @param {DOMElement} $el - The DOM Element to be tested.
   * @return {Track}
   */
  getTrackFromDOMElement($el) {
    let $svg = null;
    let track = null;
    // find the closest `.track` element
    do {
      if ($el.classList.contains('track')) {
        $svg = $el;
      }
      $el = $el.parentNode;
    } while ($svg === null);
    // find the related `Track`
    this.tracks.forEach(function(_track) {
      if (_track.$svg === $svg) { track = _track; }
    });

    return track;
  }

  /**
   * Returns an array of layers from their given group id.
   *
   * @param {String} groupId - The id of the group as defined in `addLayer`.
   * @return {(Array|undefined)}
   */
  getLayersByGroup(groupId) {
    return this._groupedLayers[groupId];
  }

  /** @private */
  *[Symbol.iterator]() {
    yield* this.tracks[Symbol.iterator]();
  }
}
<|endoftext|>


<commit_before>var content = `

  var showSignaturePad = function (signaturePadCanvas, bodyWidth, bodyHeight) {
    /*We're rotating by 90% -> Flip X and Y*/
    /*var width = bodyHeight;
    var height = bodyWidth;*/

    var width = bodyWidth;
    var height = bodyHeight;

    var sizeSignaturePad = function () {
      var devicePixelRatio = 1; /*window.devicePixelRatio || 1;*/
      var canvasWidth = width * devicePixelRatio;
      var canvasHeight = height * devicePixelRatio;
      signaturePadCanvas.width = canvasWidth;
      signaturePadCanvas.height = canvasHeight;
      signaturePadCanvas.getContext('2d').scale(devicePixelRatio, devicePixelRatio);
    };

    var finishedStroke = function(base64DataUrl) {
       executeNativeFunction('finishedStroke', {base64DataUrl: base64DataUrl});
    };

    var enableSignaturePadFunctionality = function () {
      var signaturePad = new SignaturePad(signaturePadCanvas, {onEnd: function() { finishedStroke(signaturePad.toDataURL()); }});
      /* signaturePad.translateMouseCoordinates = function (point) {
        var translatedY = point.x;
        var translatedX = width - point.y;
        point.x = translatedX;
        point.y = translatedY;
      }; */
      signaturePad.minWidth = 1;
      signaturePad.maxWidth = 4;
    };

    sizeSignaturePad();
    enableSignaturePadFunctionality();
  };

  var bodyWidth = document.body.clientWidth;
  var bodyHeight = document.body.clientHeight;
  var canvasElement = document.querySelector('canvas');
  showSignaturePad(canvasElement, bodyWidth, bodyHeight);
`;

export default content;<commit_msg>Fix to work in Genymotion simulator<commit_after>var content = `

  var showSignaturePad = function (signaturePadCanvas, bodyWidth, bodyHeight) {
    /*We're rotating by 90% -> Flip X and Y*/
    /*var width = bodyHeight;
    var height = bodyWidth;*/

    var width = bodyWidth;
    var height = bodyHeight;

    var sizeSignaturePad = function () {
      var devicePixelRatio = 1; /*window.devicePixelRatio || 1;*/
      var canvasWidth = width * devicePixelRatio;
      var canvasHeight = height * devicePixelRatio;
      signaturePadCanvas.width = canvasWidth;
      signaturePadCanvas.height = canvasHeight;
      signaturePadCanvas.getContext('2d').scale(devicePixelRatio, devicePixelRatio);
    };

    var finishedStroke = function(base64DataUrl) {
       executeNativeFunction('finishedStroke', {base64DataUrl: base64DataUrl});
    };

    var enableSignaturePadFunctionality = function () {
      var signaturePad = new SignaturePad(signaturePadCanvas, {onEnd: function() { finishedStroke(signaturePad.toDataURL()); }});
      /* signaturePad.translateMouseCoordinates = function (point) {
        var translatedY = point.x;
        var translatedX = width - point.y;
        point.x = translatedX;
        point.y = translatedY;
      }; */
      signaturePad.minWidth = 1;
      signaturePad.maxWidth = 4;
    };

    sizeSignaturePad();
    enableSignaturePadFunctionality();
  };

  var bodyWidth = document.body.clientWidth;
  var bodyHeight = document.body.clientHeight;
  if(!bodyWidth) {
    bodyWidth = window.innerWidth;
  }
  if(!bodyHeight) {
    bodyHeight = window.innerHeight;
  }

  var canvasElement = document.querySelector('canvas');
  showSignaturePad(canvasElement, bodyWidth, bodyHeight);
`;

export default content;
<|endoftext|>


<commit_before>"use strict";

module.exports = {
  "japan":"http://blog.openstreetmap.de/wp-uploads//2016/02/jp.svg",
  "france":"http://blog.openstreetmap.de/wp-uploads//2016/02/fr.svg",
  "spain":"http://blog.openstreetmap.de/wp-uploads//2015/01/es.svg",
  "czech republic":"http://blog.openstreetmap.de/wp-uploads//2016/03/cz.svg",
  "russia":"http://blog.openstreetmap.de/wp-uploads//2016/01/ru.svg",
  "romania":"http://blog.openstreetmap.de/wp-uploads//2015/10/ro.svg",
  "netherland":"http://blog.openstreetmap.de/wp-uploads//2015/07/nl.svg",
  "brasil":"http://blog.openstreetmap.de/wp-uploads//2015/01/pt-br.svg",
  "poland":"http://blog.openstreetmap.de/wp-uploads//2015/10/pl.svg",
  "taiwan":"http://blog.openstreetmap.de/wp-uploads//2016/01/tw.svg",
  "switzerland":"http://blog.openstreetmap.de/wp-uploads//2016/02/ch.svg",
  "schweiz":"http://blog.openstreetmap.de/wp-uploads//2016/01/ch.svg",
  "italy":"http://blog.openstreetmap.de/wp-uploads//2016/02/it.svg",
  "us":"http://blog.openstreetmap.de/wp-uploads//2016/01/us.svg",
  "united states":"http://blog.openstreetmap.de/wp-uploads//2016/01/us.svg",
  "austria":"http://blog.openstreetmap.de/wp-uploads//2016/02/at.svg",
  "österreich":"http://blog.openstreetmap.de/wp-uploads//2016/02/at.svg",
  "philippines":"http://blog.openstreetmap.de/wp-uploads//2016/01/ph.svg",
  "belgium":"http://blog.openstreetmap.de/wp-uploads//2016/01/be-1.svg",
  "argentina":"http://blog.openstreetmap.de/wp-uploads//2016/01/ar.svg",
  "england":"http://blog.openstreetmap.de/wp-uploads//2016/03/en.svg",
  "uk":"http://blog.openstreetmap.de/wp-uploads//2016/03/en.svg",
  "germany":"http://blog.openstreetmap.de/wp-uploads//2016/01/de.svg",
  "deutschland":"http://blog.openstreetmap.de/wp-uploads//2016/01/de.svg",
  "bolivia":"http://blog.openstreetmap.de/wp-uploads//2016/03/bo.svg",
  "chile":"http://blog.openstreetmap.de/wp-uploads//2016/03/cl.svg",
  "canada":"http://blog.openstreetmap.de/wp-uploads//2016/01/ca.svg",
  "hungary":"http://blog.openstreetmap.de/wp-uploads//2016/02/hu.svg",
  "india":"http://blog.openstreetmap.de/wp-uploads//2016/01/in.svg",
  "saint lucia":"http://blog.openstreetmap.de/wp-uploads//2016/02/lc.svg",
  "finland":"http://blog.openstreetmap.de/wp-uploads//2016/02/fi.svg",
  "haiti":"http://blog.openstreetmap.de/wp-uploads//2016/02/ht.svg",
  "mexico":"http://blog.openstreetmap.de/wp-uploads//2016/02/mx.svg",
  "colombia":"http://blog.openstreetmap.de/wp-uploads//2016/02/co.svg"

};
<commit_msg>UK - England - Scotland Flag<commit_after>"use strict";

module.exports = {
  "japan":"http://blog.openstreetmap.de/wp-uploads//2016/02/jp.svg",
  "france":"http://blog.openstreetmap.de/wp-uploads//2016/02/fr.svg",
  "spain":"http://blog.openstreetmap.de/wp-uploads//2015/01/es.svg",
  "czech republic":"http://blog.openstreetmap.de/wp-uploads//2016/03/cz.svg",
  "russia":"http://blog.openstreetmap.de/wp-uploads//2016/01/ru.svg",
  "romania":"http://blog.openstreetmap.de/wp-uploads//2015/10/ro.svg",
  "netherland":"http://blog.openstreetmap.de/wp-uploads//2015/07/nl.svg",
  "brasil":"http://blog.openstreetmap.de/wp-uploads//2015/01/pt-br.svg",
  "poland":"http://blog.openstreetmap.de/wp-uploads//2015/10/pl.svg",
  "taiwan":"http://blog.openstreetmap.de/wp-uploads//2016/01/tw.svg",
  "switzerland":"http://blog.openstreetmap.de/wp-uploads//2016/02/ch.svg",
  "schweiz":"http://blog.openstreetmap.de/wp-uploads//2016/01/ch.svg",
  "italy":"http://blog.openstreetmap.de/wp-uploads//2016/02/it.svg",
  "us":"http://blog.openstreetmap.de/wp-uploads//2016/01/us.svg",
  "united states":"http://blog.openstreetmap.de/wp-uploads//2016/01/us.svg",
  "austria":"http://blog.openstreetmap.de/wp-uploads//2016/02/at.svg",
  "österreich":"http://blog.openstreetmap.de/wp-uploads//2016/02/at.svg",
  "philippines":"http://blog.openstreetmap.de/wp-uploads//2016/01/ph.svg",
  "belgium":"http://blog.openstreetmap.de/wp-uploads//2016/01/be-1.svg",
  "argentina":"http://blog.openstreetmap.de/wp-uploads//2016/01/ar.svg",
  "england":"http://blog.openstreetmap.de/wp-uploads//2016/03/en.svg",
  "uk":"http://blog.openstreetmap.de/wp-uploads//2016/03/en.svg",
  "scotland":"http://blog.openstreetmap.de/wp-uploads//2016/03/en.svg",
  "germany":"http://blog.openstreetmap.de/wp-uploads//2016/01/de.svg",
  "deutschland":"http://blog.openstreetmap.de/wp-uploads//2016/01/de.svg",
  "bolivia":"http://blog.openstreetmap.de/wp-uploads//2016/03/bo.svg",
  "chile":"http://blog.openstreetmap.de/wp-uploads//2016/03/cl.svg",
  "canada":"http://blog.openstreetmap.de/wp-uploads//2016/01/ca.svg",
  "hungary":"http://blog.openstreetmap.de/wp-uploads//2016/02/hu.svg",
  "india":"http://blog.openstreetmap.de/wp-uploads//2016/01/in.svg",
  "saint lucia":"http://blog.openstreetmap.de/wp-uploads//2016/02/lc.svg",
  "finland":"http://blog.openstreetmap.de/wp-uploads//2016/02/fi.svg",
  "haiti":"http://blog.openstreetmap.de/wp-uploads//2016/02/ht.svg",
  "mexico":"http://blog.openstreetmap.de/wp-uploads//2016/02/mx.svg",
  "colombia":"http://blog.openstreetmap.de/wp-uploads//2016/02/co.svg"

};
<|endoftext|>