<commit_before>const electron = require('electron')
const os = require('os')

// Module to control application life.
const { Menu, app, shell, dialog } = electron;

const buildMenu = (mainWindow, loadInit) => {
    let menu = []

    // On Windows, both the forward slash `/` and the backward slash `\` are accepted as path delimiters.
    // The node APIs only return the backward slash, ie: `C:\\oni\\README.md`, but this causes problems
    // for VIM as it sees escape keys.
    const normalizePath = (fileName) => fileName.split("\\").join("/")

    const executeVimCommand = (command) => mainWindow.webContents.send("menu-item-click", command)

    const executeVimCommandForMultipleFiles = (command, files) => mainWindow.webContents.send("open-files", command, files)

    const executeOniCommand = (command) => mainWindow.webContents.send("execute-command", command)

    const executeVimCommandForFiles = (command, files) => {
        if (!files || !files.length)
            return

        files.forEach((fileName) => executeVimCommand(`${command} ${normalizePath(fileName)}`))
    }

    const isWindows = os.platform() == "win32";

    let preferences = {
        label: 'Preferences',
        submenu: [
            {
                label: 'Edit Oni config',
                click (item, focusedWindow) {
                    executeOniCommand("oni.config.openConfigJs")
                }
            },
        ]
    }

    if (loadInit) {
        preferences.submenu.push(
            {
                label: 'Edit Neovim config',
                click (item, focusedWindow) {
                    executeOniCommand("oni.config.openInitVim")
                }
            }
        )
    }

    let reopenWithEncoding = {
        label: 'Reopen with Encoding',
        submenu: []
    }

    // TODO: Maybe better show normal encoding name in submenu?
    // Encoding list from http://vimdoc.sourceforge.net/htmldoc/mbyte.html#encoding-values
    let encodingList = ['utf-8', 'utf-16le', 'utf-16be', 'utf-32le', 'utf-32be', 'latin1', 'koi8-r', 'koi8-u', 'macroman', 'cp437', 'cp737', 'cp775', 'cp850', 'cp852', 'cp855', 'cp857', 'cp860', 'cp861', 'cp862', 'cp863', 'cp865', 'cp866', 'cp869', 'cp874', 'cp1250', 'cp1251', 'cp1253', 'cp1254', 'cp1255', 'cp1256', 'cp1257', 'cp1258', 'cp932', 'euc-jp', 'sjis', 'cp949', 'euc-kr', 'cp936', 'euc-cn', 'cp950', 'big5', 'euc-tw'].map((val) => {
        return {
            label: val.toUpperCase(),
            click (item, focusedWindow) {
                executeVimCommand(":e! ++enc=" + val)
            }
        }
    });

    reopenWithEncoding.submenu.push(...encodingList);

    menu.push({
        label: isWindows ? 'File' : 'Oni',
        submenu: [
            {
                label: 'New File',
                click (item, focusedWindow) {
                    executeVimCommand(":enew")
                }
            },
            {
                label: 'Open File…',
                click (item, focusedWindow) {
                    dialog.showOpenDialog(mainWindow, { properties: ['openFile', 'multiSelections'] }, (files) => executeVimCommandForMultipleFiles(":tabnew ", files))
                }
            },
            {
                label: 'Open Folder…',
                click (item, focusedWindow) {
                    executeOniCommand("oni.openFolder")
                }
            },
            reopenWithEncoding,
            {
                label: 'Split Open…',
                click (item, focusedWindow) {
                    dialog.showOpenDialog(mainWindow, 'openFile', (files) => executeVimCommandForFiles(":sp", files))
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Save',
                click (item, focusedWindow) {
                    executeVimCommand(":w")
                }
            },
            {
                label: 'Save As…',
                click (item, focusedWindow) {
                    dialog.showSaveDialog(mainWindow, {}, (name) => {
                        if (name) {
                            executeVimCommand(":save " + name)
                        }
                    })
                }
            },
            {
                label: 'Save All',
                click (item, focusedWindow) {
                    executeVimCommand(":wall")
                }
            },
            {
                type: 'separator'
            },
            preferences,
            {
                type: 'separator'
            },
            {
                label: 'Close File',
                click (item, focusedWindow) {
                    executeVimCommand(":close")
                }
            },
            {
                label: 'Revert File',
                click (item, focusedWindow) {
                    executeVimCommand(":e!")
                }
            },
            {
                label: 'Close All File',
                click (item, focusedWindow) {
                    executeVimCommand(":%bw")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Exit',
                click (item, focusedWindow) {
                    app.quit()
                }
            }
        ]
    })

    // Edit menu
    menu.push({
        label: "Edit",
        submenu: [
            {
                label: 'Undo',
                click (item, focusedWindow) {
                    executeVimCommand("u")
                }
            },
            {
                label: 'Redo',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-r>")
                }
            },
            {
                label: 'Repeat',
                click (item, focusedWindow) {
                    executeVimCommand(".")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Copy',
                click (item, focusedWindow) {
                    executeVimCommand('\\"+y')
                }
            },
            {
                label: 'Cut',
                click (item, focusedWindow) {
                    executeVimCommand('\\"+x')
                }
            },
            {
                label: 'Paste',
                click (item, focusedWindow) {
                    executeVimCommand('\\"+gP')
                }
            },
            {
                label: 'Paste Line Before',
                click (item, focusedWindow) {
                    executeVimCommand("[p")
                }
            },
            {
                label: 'Paste Line After',
                click (item, focusedWindow) {
                    executeVimCommand("]p")
                }
            },
            {
                label: 'Copy File Path',
                submenu: [
                    {
                        label: 'Full Path',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:p')")
                        }
                    },
                    {
                        label: 'Full Path with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:p') . ':' . line('.')")
                        }
                    },
                    {
                        label: 'Relative Path',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%')")
                        }
                    },
                    {
                        label: 'Relative Path with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%') . ':' . line('.')")
                        }
                    },
                    {
                        label: 'File Name',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:t')")
                        }
                    },
                    {
                        label: 'File Name with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:t') . ':' . line('.')")
                        }
                    }
                ]
            },
            {
                type: 'separator'
            },
            {
                label: 'Line',
                submenu: [
                    {
                        label: 'Indent',
                        click (item, focusedWindow) {
                            executeVimCommand(">")
                        }
                    },
                    {
                        label: 'Unindent',
                        click (item, focusedWindow) {
                            executeVimCommand("<")
                        }
                    },
                    {
                        label: 'Reindent',
                        click (item, focusedWindow) {
                            executeVimCommand("=i{")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Move Up',
                        click (item, focusedWindow) {
                            executeVimCommand("m+")
                        }
                    },
                    {
                        label: 'Move Down',
                        click (item, focusedWindow) {
                            executeVimCommand("m--")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Duplicate',
                        click (item, focusedWindow) {
                            executeVimCommand("Yp")
                        }
                    },
                    {
                        label: 'Copy',
                        click (item, focusedWindow) {
                            executeVimCommand("Y")
                        }
                    },
                    {
                        label: 'Cut',
                        click (item, focusedWindow) {
                            executeVimCommand("Y^D")
                        }
                    },
                    {
                        label: 'Delete',
                        click (item, focusedWindow) {
                            executeVimCommand("dd")
                        }
                    },
                    {
                        label: 'Clear',
                        click (item, focusedWindow) {
                            executeVimCommand("^D")
                        }
                    },
                    {
                        label: 'Join',
                        click (item, focusedWindow) {
                            executeVimCommand("J")
                        }

                    }
                ]
            },
            {
                label: 'Text',
                submenu: [
                    {
                        label: 'Upper Case',
                        click (item, focusedWindow) {
                            executeVimCommand("U")
                        },
                    },
                    {
                        label: 'Lower Case',
                        click (item, focusedWindow) {
                            executeVimCommand("u")
                        },
                    },
                    {
                        label: 'Swap Case',
                        click (item, focusedWindow) {
                            executeVimCommand("~")
                        },
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Delete Inner Word',
                        click (item, focusedWindow) {
                            executeVimCommand("diw")
                        },
                    },
                    {
                        label: 'Delete Previous Word',
                        click (item, focusedWindow) {
                            executeVimCommand("bbdw")
                        },
                    },
                    {
                        label: 'Delete Next Word',
                        click (item, focusedWindow) {
                            executeVimCommand("nwdw")
                        },
                    },
                    {
                        label: 'Strip First Character',
                        click (item, focusedWindow) {
                            executeVimCommand(":%normal ^x")
                        }
                    },
                    {
                        label: 'Strip Last Character',
                        click (item, focusedWindow) {
                            executeVimCommand(":%normal $x")
                        }
                    },
                    {
                        label: 'Strip Trailings Blanks',
                        click (item, focusedWindow) {
                            executeVimCommand(":%s/^\s\+//")
                            executeVimCommand(":%s/\s\+$//")
                        }
                    },
                    {
                        label: 'Delete Line',
                        click (item, focusedWindow) {
                            executeVimCommand("dd")
                        }
                    },
                    {
                        label: 'Remove Blank Lines',
                        click (item, focusedWindow) {
                            executeVimCommand(":g/^$/d")
                        }
                    }
                ]
            },
            {
                label: 'Comment',
                submenu: [
                    {
                        label: 'Toggle Comment',
                        click (item, focusedWindow) {
                            executeVimCommand(":Commentary")
                        },
                    }
                ]
            },
            {
                label: 'Insert',
                submenu: [
                    {
                        label: 'Encoding Identifier',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =&fileencoding")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Date / Time (Short)',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =strftime('%c')")
                        }
                    },
                    {
                        label: 'Date / Time (Long)',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =strftime('%a, %d %b %Y %H:%M:%S')")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Full Path',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =expand('%:p')")
                        }
                    },
                    {
                        label: 'File Name',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =expand('%:t')")
                        }
                    },
                    {
                        label: 'File Name with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =expand('%:t') . ':' . line('.')")
                        }
                    }
                ]
            },
            {
                type: 'separator'
            },
            {
                label: 'Select All',
                click (item, focusedWindow) {
                    executeVimCommand("ggVG")
                }
            }
        ]
    })

    // Window menu
    menu.push({
        label: 'Split',
        submenu : [
            {
                label: 'New Horizontal Split',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>n")
                }
            },
            {
                label: 'Split File Horizontally',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>s")
                }
            },
            {
                label: 'Split File Vertically',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>v")
                }
            },
            {
                label: 'File Explorer Split',
                click (item, focusedWindow) {
                    executeVimCommand(":Lexplore | vertical resize 30")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Close',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>c")
                }
            },
            {
                label: 'Close Other Split(s)',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>o")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Move To',
                submenu: [
                    {
                        label: 'Top',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>K")
                        }
                    },
                    {
                        label: 'Bottom',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>J")
                        }
                    },
                    {
                        label: 'Left Side',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>H")
                        }
                    },
                    {
                        label: 'Right Side',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>L")
                        }
                    }
                ]
            },
            {
                label: 'Rotate Up',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>R")
                }
            },
            {
                label: 'Rotate Down',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>r")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Equal Size',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>=")
                }
            },
            {
                label: 'Max Height',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>_")
                }
            },
            {
                label: 'Min Height',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>1_")
                }
            },
            {
                label: 'Max Width',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>|")
                }
            },
            {
                label: 'Min Width',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>1|")
                }
            }
        ]
    })

    // Help menu
    menu.push({
        label: "Help",
        submenu: [
            {
                label: 'Learn more',
                click (item, focusedWindow) {
                    shell.openExternal('https://github.com/extr0py/oni#introduction');
                }
            },
            {
                label: 'Issues',
                click (item, focusedWindow) {
                    shell.openExternal('https://github.com/extr0py/oni/issues');
                }
            },
            {
                label: 'Github',
                sublabel: 'https://github.com/extr0py/oni',
                click (item, focusedWindow) {
                    shell.openExternal('https://github.com/extr0py/oni');
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Developer Tools',
                click (item, focusedWindow) {
                    executeOniCommand('oni.debug.openDevTools')
                },
            }
        ]
    })

    return Menu.buildFromTemplate(menu)
}

module.exports = {
    buildMenu: buildMenu
}<commit_msg>Normalize path for "Save As". (#684)<commit_after>const electron = require('electron')
const os = require('os')

// Module to control application life.
const { Menu, app, shell, dialog } = electron;

const buildMenu = (mainWindow, loadInit) => {
    let menu = []

    // On Windows, both the forward slash `/` and the backward slash `\` are accepted as path delimiters.
    // The node APIs only return the backward slash, ie: `C:\\oni\\README.md`, but this causes problems
    // for VIM as it sees escape keys.
    const normalizePath = (fileName) => fileName.split("\\").join("/")

    const executeVimCommand = (command) => mainWindow.webContents.send("menu-item-click", command)

    const executeVimCommandForMultipleFiles = (command, files) => mainWindow.webContents.send("open-files", command, files)

    const executeOniCommand = (command) => mainWindow.webContents.send("execute-command", command)

    const executeVimCommandForFiles = (command, files) => {
        if (!files || !files.length)
            return

        files.forEach((fileName) => executeVimCommand(`${command} ${normalizePath(fileName)}`))
    }

    const isWindows = os.platform() == "win32";

    let preferences = {
        label: 'Preferences',
        submenu: [
            {
                label: 'Edit Oni config',
                click (item, focusedWindow) {
                    executeOniCommand("oni.config.openConfigJs")
                }
            },
        ]
    }

    if (loadInit) {
        preferences.submenu.push(
            {
                label: 'Edit Neovim config',
                click (item, focusedWindow) {
                    executeOniCommand("oni.config.openInitVim")
                }
            }
        )
    }

    let reopenWithEncoding = {
        label: 'Reopen with Encoding',
        submenu: []
    }

    // TODO: Maybe better show normal encoding name in submenu?
    // Encoding list from http://vimdoc.sourceforge.net/htmldoc/mbyte.html#encoding-values
    let encodingList = ['utf-8', 'utf-16le', 'utf-16be', 'utf-32le', 'utf-32be', 'latin1', 'koi8-r', 'koi8-u', 'macroman', 'cp437', 'cp737', 'cp775', 'cp850', 'cp852', 'cp855', 'cp857', 'cp860', 'cp861', 'cp862', 'cp863', 'cp865', 'cp866', 'cp869', 'cp874', 'cp1250', 'cp1251', 'cp1253', 'cp1254', 'cp1255', 'cp1256', 'cp1257', 'cp1258', 'cp932', 'euc-jp', 'sjis', 'cp949', 'euc-kr', 'cp936', 'euc-cn', 'cp950', 'big5', 'euc-tw'].map((val) => {
        return {
            label: val.toUpperCase(),
            click (item, focusedWindow) {
                executeVimCommand(":e! ++enc=" + val)
            }
        }
    });

    reopenWithEncoding.submenu.push(...encodingList);

    menu.push({
        label: isWindows ? 'File' : 'Oni',
        submenu: [
            {
                label: 'New File',
                click (item, focusedWindow) {
                    executeVimCommand(":enew")
                }
            },
            {
                label: 'Open File…',
                click (item, focusedWindow) {
                    dialog.showOpenDialog(mainWindow, { properties: ['openFile', 'multiSelections'] }, (files) => executeVimCommandForMultipleFiles(":tabnew ", files))
                }
            },
            {
                label: 'Open Folder…',
                click (item, focusedWindow) {
                    executeOniCommand("oni.openFolder")
                }
            },
            reopenWithEncoding,
            {
                label: 'Split Open…',
                click (item, focusedWindow) {
                    dialog.showOpenDialog(mainWindow, 'openFile', (files) => executeVimCommandForFiles(":sp", files))
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Save',
                click (item, focusedWindow) {
                    executeVimCommand(":w")
                }
            },
            {
                label: 'Save As…',
                click (item, focusedWindow) {
                    dialog.showSaveDialog(mainWindow, {}, (name) => {
                        if (name) {
                            executeVimCommand(":save " + normalizePath(name))
                        }
                    })
                }
            },
            {
                label: 'Save All',
                click (item, focusedWindow) {
                    executeVimCommand(":wall")
                }
            },
            {
                type: 'separator'
            },
            preferences,
            {
                type: 'separator'
            },
            {
                label: 'Close File',
                click (item, focusedWindow) {
                    executeVimCommand(":close")
                }
            },
            {
                label: 'Revert File',
                click (item, focusedWindow) {
                    executeVimCommand(":e!")
                }
            },
            {
                label: 'Close All File',
                click (item, focusedWindow) {
                    executeVimCommand(":%bw")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Exit',
                click (item, focusedWindow) {
                    app.quit()
                }
            }
        ]
    })

    // Edit menu
    menu.push({
        label: "Edit",
        submenu: [
            {
                label: 'Undo',
                click (item, focusedWindow) {
                    executeVimCommand("u")
                }
            },
            {
                label: 'Redo',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-r>")
                }
            },
            {
                label: 'Repeat',
                click (item, focusedWindow) {
                    executeVimCommand(".")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Copy',
                click (item, focusedWindow) {
                    executeVimCommand('\\"+y')
                }
            },
            {
                label: 'Cut',
                click (item, focusedWindow) {
                    executeVimCommand('\\"+x')
                }
            },
            {
                label: 'Paste',
                click (item, focusedWindow) {
                    executeVimCommand('\\"+gP')
                }
            },
            {
                label: 'Paste Line Before',
                click (item, focusedWindow) {
                    executeVimCommand("[p")
                }
            },
            {
                label: 'Paste Line After',
                click (item, focusedWindow) {
                    executeVimCommand("]p")
                }
            },
            {
                label: 'Copy File Path',
                submenu: [
                    {
                        label: 'Full Path',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:p')")
                        }
                    },
                    {
                        label: 'Full Path with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:p') . ':' . line('.')")
                        }
                    },
                    {
                        label: 'Relative Path',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%')")
                        }
                    },
                    {
                        label: 'Relative Path with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%') . ':' . line('.')")
                        }
                    },
                    {
                        label: 'File Name',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:t')")
                        }
                    },
                    {
                        label: 'File Name with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":let @" + (isWindows ? '*' : '+') + "=expand('%:t') . ':' . line('.')")
                        }
                    }
                ]
            },
            {
                type: 'separator'
            },
            {
                label: 'Line',
                submenu: [
                    {
                        label: 'Indent',
                        click (item, focusedWindow) {
                            executeVimCommand(">")
                        }
                    },
                    {
                        label: 'Unindent',
                        click (item, focusedWindow) {
                            executeVimCommand("<")
                        }
                    },
                    {
                        label: 'Reindent',
                        click (item, focusedWindow) {
                            executeVimCommand("=i{")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Move Up',
                        click (item, focusedWindow) {
                            executeVimCommand("m+")
                        }
                    },
                    {
                        label: 'Move Down',
                        click (item, focusedWindow) {
                            executeVimCommand("m--")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Duplicate',
                        click (item, focusedWindow) {
                            executeVimCommand("Yp")
                        }
                    },
                    {
                        label: 'Copy',
                        click (item, focusedWindow) {
                            executeVimCommand("Y")
                        }
                    },
                    {
                        label: 'Cut',
                        click (item, focusedWindow) {
                            executeVimCommand("Y^D")
                        }
                    },
                    {
                        label: 'Delete',
                        click (item, focusedWindow) {
                            executeVimCommand("dd")
                        }
                    },
                    {
                        label: 'Clear',
                        click (item, focusedWindow) {
                            executeVimCommand("^D")
                        }
                    },
                    {
                        label: 'Join',
                        click (item, focusedWindow) {
                            executeVimCommand("J")
                        }

                    }
                ]
            },
            {
                label: 'Text',
                submenu: [
                    {
                        label: 'Upper Case',
                        click (item, focusedWindow) {
                            executeVimCommand("U")
                        },
                    },
                    {
                        label: 'Lower Case',
                        click (item, focusedWindow) {
                            executeVimCommand("u")
                        },
                    },
                    {
                        label: 'Swap Case',
                        click (item, focusedWindow) {
                            executeVimCommand("~")
                        },
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Delete Inner Word',
                        click (item, focusedWindow) {
                            executeVimCommand("diw")
                        },
                    },
                    {
                        label: 'Delete Previous Word',
                        click (item, focusedWindow) {
                            executeVimCommand("bbdw")
                        },
                    },
                    {
                        label: 'Delete Next Word',
                        click (item, focusedWindow) {
                            executeVimCommand("nwdw")
                        },
                    },
                    {
                        label: 'Strip First Character',
                        click (item, focusedWindow) {
                            executeVimCommand(":%normal ^x")
                        }
                    },
                    {
                        label: 'Strip Last Character',
                        click (item, focusedWindow) {
                            executeVimCommand(":%normal $x")
                        }
                    },
                    {
                        label: 'Strip Trailings Blanks',
                        click (item, focusedWindow) {
                            executeVimCommand(":%s/^\s\+//")
                            executeVimCommand(":%s/\s\+$//")
                        }
                    },
                    {
                        label: 'Delete Line',
                        click (item, focusedWindow) {
                            executeVimCommand("dd")
                        }
                    },
                    {
                        label: 'Remove Blank Lines',
                        click (item, focusedWindow) {
                            executeVimCommand(":g/^$/d")
                        }
                    }
                ]
            },
            {
                label: 'Comment',
                submenu: [
                    {
                        label: 'Toggle Comment',
                        click (item, focusedWindow) {
                            executeVimCommand(":Commentary")
                        },
                    }
                ]
            },
            {
                label: 'Insert',
                submenu: [
                    {
                        label: 'Encoding Identifier',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =&fileencoding")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Date / Time (Short)',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =strftime('%c')")
                        }
                    },
                    {
                        label: 'Date / Time (Long)',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =strftime('%a, %d %b %Y %H:%M:%S')")
                        }
                    },
                    {
                        type: 'separator'
                    },
                    {
                        label: 'Full Path',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =expand('%:p')")
                        }
                    },
                    {
                        label: 'File Name',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =expand('%:t')")
                        }
                    },
                    {
                        label: 'File Name with Line Number',
                        click (item, focusedWindow) {
                            executeVimCommand(":put =expand('%:t') . ':' . line('.')")
                        }
                    }
                ]
            },
            {
                type: 'separator'
            },
            {
                label: 'Select All',
                click (item, focusedWindow) {
                    executeVimCommand("ggVG")
                }
            }
        ]
    })

    // Window menu
    menu.push({
        label: 'Split',
        submenu : [
            {
                label: 'New Horizontal Split',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>n")
                }
            },
            {
                label: 'Split File Horizontally',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>s")
                }
            },
            {
                label: 'Split File Vertically',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>v")
                }
            },
            {
                label: 'File Explorer Split',
                click (item, focusedWindow) {
                    executeVimCommand(":Lexplore | vertical resize 30")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Close',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>c")
                }
            },
            {
                label: 'Close Other Split(s)',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>o")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Move To',
                submenu: [
                    {
                        label: 'Top',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>K")
                        }
                    },
                    {
                        label: 'Bottom',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>J")
                        }
                    },
                    {
                        label: 'Left Side',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>H")
                        }
                    },
                    {
                        label: 'Right Side',
                        click (item, focusedWindow) {
                            executeVimCommand("\\<C-w>L")
                        }
                    }
                ]
            },
            {
                label: 'Rotate Up',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>R")
                }
            },
            {
                label: 'Rotate Down',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>r")
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Equal Size',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>=")
                }
            },
            {
                label: 'Max Height',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>_")
                }
            },
            {
                label: 'Min Height',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>1_")
                }
            },
            {
                label: 'Max Width',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>|")
                }
            },
            {
                label: 'Min Width',
                click (item, focusedWindow) {
                    executeVimCommand("\\<C-w>1|")
                }
            }
        ]
    })

    // Help menu
    menu.push({
        label: "Help",
        submenu: [
            {
                label: 'Learn more',
                click (item, focusedWindow) {
                    shell.openExternal('https://github.com/extr0py/oni#introduction');
                }
            },
            {
                label: 'Issues',
                click (item, focusedWindow) {
                    shell.openExternal('https://github.com/extr0py/oni/issues');
                }
            },
            {
                label: 'Github',
                sublabel: 'https://github.com/extr0py/oni',
                click (item, focusedWindow) {
                    shell.openExternal('https://github.com/extr0py/oni');
                }
            },
            {
                type: 'separator'
            },
            {
                label: 'Developer Tools',
                click (item, focusedWindow) {
                    executeOniCommand('oni.debug.openDevTools')
                },
            }
        ]
    })

    return Menu.buildFromTemplate(menu)
}

module.exports = {
    buildMenu: buildMenu
}<|endoftext|>


<commit_before>/*
	[Discuz!] (C)2001-2099 Comsenz Inc.
	This is NOT a freeware, use is subject to license terms

	$Id: smilies.js 29684 2012-04-25 04:00:58Z zhangguosheng $
*/

function _smilies_show(id, smcols, seditorkey) {
	if(seditorkey && !$(seditorkey + 'sml_menu')) {
		var div = document.createElement("div");
		div.id = seditorkey + 'sml_menu';
		div.style.display = 'none';
		div.className = 'sllt';
		$('append_parent').appendChild(div);
		var div = document.createElement("div");
		div.id = id;
		div.style.overflow = 'hidden';
		$(seditorkey + 'sml_menu').appendChild(div);
	}
	if(typeof smilies_type == 'undefined') {
		var scriptNode = document.createElement("script");
		scriptNode.type = "text/javascript";
		scriptNode.charset = charset ? charset : (BROWSER.firefox ? document.characterSet : document.charset);
		scriptNode.src = 'data/cache/common_smilies_var.js?' + VERHASH;
		$('append_parent').appendChild(scriptNode);
		if(BROWSER.ie) {
			scriptNode.onreadystatechange = function() {
				smilies_onload(id, smcols, seditorkey);
			};
		} else {
			scriptNode.onload = function() {
				smilies_onload(id, smcols, seditorkey);
			};
		}
	} else {
		smilies_onload(id, smcols, seditorkey);
	}
}

function smilies_onload(id, smcols, seditorkey) {
	seditorkey = !seditorkey ? '' : seditorkey;
	smile = getcookie('smile').split('D');
	if(typeof smilies_type == 'object') {
		if(smile[0] && smilies_array[smile[0]]) {
			CURRENTSTYPE = smile[0];
		} else {
			for(i in smilies_array) {
				CURRENTSTYPE = i;break;
			}
		}
		smiliestype = '

';
		for(i in smilies_type) {
			key = i.substring(1);
			if(smilies_type[i][0]) {
				smiliestype += '
'+smilies_type[i][0]+'
';
			}
		}
		smiliestype += '

';
		$(id).innerHTML = smiliestype + '

';
		smilies_switch(id, smcols, CURRENTSTYPE, smile[1], seditorkey);
		smilies_fastdata = '';
		if(seditorkey == 'fastpost' && $('fastsmilies') && smilies_fast) {
			var j = 0;
			for(i = 0;i < smilies_fast.length; i++) {
				if(j == 0) {
					smilies_fastdata += '
';
				}
				j = ++j > 3 ? 0 : j;
				s = smilies_array[smilies_fast[i][0]][smilies_fast[i][1]][smilies_fast[i][2]];
				smilieimg = STATICURL + 'image/smiley/' + smilies_type['_' + smilies_fast[i][0]][1] + '/' + s[2];
				img[k] = new Image();
				img[k].src = smilieimg;
				smilies_fastdata += s ? '' : '';
			}
			$('fastsmilies').innerHTML = '

' + smilies_fastdata + '

';
		}
	}
}

function smilies_switch(id, smcols, type, page, seditorkey) {
	page = page ? page : 1;
	if(!smilies_array[type] || !smilies_array[type][page]) return;
	setcookie('smile', type + 'D' + page, 31536000);
	smiliesdata = '

';
	j = k = 0;
	img = [];
	for(var i = 0; i < smilies_array[type][page].length; i++) {
		if(j >= smcols) {
			smiliesdata += '
';
			j = 0;
		}
		s = smilies_array[type][page][i];
		smilieimg = STATICURL + 'image/smiley/' + smilies_type['_' + type][1] + '/' + s[2];
		var fastpost = s[1].replace(/'/, '\\\'');
		if(type == 7) {
			smilieimg = s[1];
			fastpost = "[img]" + smilieimg + "[/img]";
		}
		img[k] = new Image();
		img[k].src = smilieimg;
		smiliesdata += s && s[0] ? '' : '';
		j++;k++;
	}
	smiliesdata += '
';
	smiliespage = '';
	if(smilies_array[type].length > 2) {
		prevpage = ((prevpage = parseInt(page) - 1) < 1) ? smilies_array[type].length - 1 : prevpage;
		nextpage = ((nextpage = parseInt(page) + 1) == smilies_array[type].length) ? 1 : nextpage;
		smiliespage = '
上页' +
			'下页
' +
			page + '/' + (smilies_array[type].length - 1);
	}
	$(id + '_data').innerHTML = smiliesdata;
	$(id + '_page').innerHTML = smiliespage;
	$(id + '_tb').style.width = smcols*(16+parseInt(s[3])) + 'px';
}

function smilies_preview(seditorkey, id, obj, w) {
	var menu = $('smilies_preview');
	if(!menu) {
		menu = document.createElement('div');
		menu.id = 'smilies_preview';
		menu.className = 'sl_pv';
		menu.style.display = 'none';
		$('append_parent').appendChild(menu);
	}
	menu.innerHTML = '';
	mpos = fetchOffset($(id + '_data'));
	spos = fetchOffset(obj);
	pos = spos['left'] >= mpos['left'] + $(id + '_data').offsetWidth / 2 ? '13' : '24';
	showMenu({'ctrlid':obj.id,'showid':id + '_data','menuid':menu.id,'pos':pos,'layer':3});
}

<commit_msg>Fix panst extra emotions were not decoded<commit_after>/*
	[Discuz!] (C)2001-2099 Comsenz Inc.
	This is NOT a freeware, use is subject to license terms

	$Id: smilies.js 29684 2012-04-25 04:00:58Z zhangguosheng $
*/

function _smilies_show(id, smcols, seditorkey) {
	if(seditorkey && !$(seditorkey + 'sml_menu')) {
		var div = document.createElement("div");
		div.id = seditorkey + 'sml_menu';
		div.style.display = 'none';
		div.className = 'sllt';
		$('append_parent').appendChild(div);
		var div = document.createElement("div");
		div.id = id;
		div.style.overflow = 'hidden';
		$(seditorkey + 'sml_menu').appendChild(div);
	}
	if(typeof smilies_type == 'undefined') {
		var scriptNode = document.createElement("script");
		scriptNode.type = "text/javascript";
		scriptNode.charset = charset ? charset : (BROWSER.firefox ? document.characterSet : document.charset);
		scriptNode.src = 'data/cache/common_smilies_var.js?' + VERHASH;
		$('append_parent').appendChild(scriptNode);
		if(BROWSER.ie) {
			scriptNode.onreadystatechange = function() {
				smilies_onload(id, smcols, seditorkey);
			};
		} else {
			scriptNode.onload = function() {
				smilies_onload(id, smcols, seditorkey);
			};
		}
	} else {
		smilies_onload(id, smcols, seditorkey);
	}
}

function smilies_onload(id, smcols, seditorkey) {
	seditorkey = !seditorkey ? '' : seditorkey;
	smile = getcookie('smile').split('D');
	if(typeof smilies_type == 'object') {
		if(smile[0] && smilies_array[smile[0]]) {
			CURRENTSTYPE = smile[0];
		} else {
			for(i in smilies_array) {
				CURRENTSTYPE = i;break;
			}
		}
		smiliestype = '

';
		for(i in smilies_type) {
			key = i.substring(1);
			if(smilies_type[i][0]) {
				smiliestype += '
'+smilies_type[i][0]+'
';
			}
		}
		smiliestype += '

';
		$(id).innerHTML = smiliestype + '

';
		smilies_switch(id, smcols, CURRENTSTYPE, smile[1], seditorkey);
		smilies_fastdata = '';
		if(seditorkey == 'fastpost' && $('fastsmilies') && smilies_fast) {
			var j = 0;
			for(i = 0;i < smilies_fast.length; i++) {
				if(j == 0) {
					smilies_fastdata += '
';
				}
				j = ++j > 3 ? 0 : j;
				s = smilies_array[smilies_fast[i][0]][smilies_fast[i][1]][smilies_fast[i][2]];
				smilieimg = STATICURL + 'image/smiley/' + smilies_type['_' + smilies_fast[i][0]][1] + '/' + s[2];
				img[k] = new Image();
				img[k].src = smilieimg;
				smilies_fastdata += s ? '' : '';
			}
			$('fastsmilies').innerHTML = '

' + smilies_fastdata + '

';
		}
	}
}

function smilies_switch(id, smcols, type, page, seditorkey) {
	page = page ? page : 1;
	if(!smilies_array[type] || !smilies_array[type][page]) return;
	setcookie('smile', type + 'D' + page, 31536000);
	smiliesdata = '

';
	j = k = 0;
	img = [];
	for(var i = 0; i < smilies_array[type][page].length; i++) {
		if(j >= smcols) {
			smiliesdata += '
';
			j = 0;
		}
		s = smilies_array[type][page][i];
		smilieimg = STATICURL + 'image/smiley/' + smilies_type['_' + type][1] + '/' + s[2];
		var fastpost = s[1].replace(/'/, '\\\'');
		if(type == 7 || type == 8) {
			smilieimg = s[1];
			fastpost = "[img]" + smilieimg + "[/img]";
		}
		img[k] = new Image();
		img[k].src = smilieimg;
		smiliesdata += s && s[0] ? '' : '';
		j++;k++;
	}
	smiliesdata += '
';
	smiliespage = '';
	if(smilies_array[type].length > 2) {
		prevpage = ((prevpage = parseInt(page) - 1) < 1) ? smilies_array[type].length - 1 : prevpage;
		nextpage = ((nextpage = parseInt(page) + 1) == smilies_array[type].length) ? 1 : nextpage;
		smiliespage = '
上页' +
			'下页
' +
			page + '/' + (smilies_array[type].length - 1);
	}
	$(id + '_data').innerHTML = smiliesdata;
	$(id + '_page').innerHTML = smiliespage;
	$(id + '_tb').style.width = smcols*(16+parseInt(s[3])) + 'px';
}

function smilies_preview(seditorkey, id, obj, w) {
	var menu = $('smilies_preview');
	if(!menu) {
		menu = document.createElement('div');
		menu.id = 'smilies_preview';
		menu.className = 'sl_pv';
		menu.style.display = 'none';
		$('append_parent').appendChild(menu);
	}
	menu.innerHTML = '';
	mpos = fetchOffset($(id + '_data'));
	spos = fetchOffset(obj);
	pos = spos['left'] >= mpos['left'] + $(id + '_data').offsetWidth / 2 ? '13' : '24';
	showMenu({'ctrlid':obj.id,'showid':id + '_data','menuid':menu.id,'pos':pos,'layer':3});
}

<|endoftext|>


<commit_before>// Copyright 2014-2020, University of Colorado Boulder

/**
 * ToggleSwitch is a switch for toggling between 2 values, similar to iOS' UISwitch, used in iOS `'Settings' app.
 *
 * Interaction behavior is as follows:
 * Drag the thumb to change the value, or click anywhere to toggle the value.
 * If you click without dragging, it's a toggle.
 * If you drag but don't cross the midpoint of the track, then it's a toggle.
 * If you drag past the midpoint of the track, releasing the thumb snaps to whichever end the thumb is closest to.
 * If you drag the thumb far enough to the side (outside of the switch), it will immediately toggle the model behavior.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Jonathan Olson (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const Action = require( 'AXON/Action' );
  const Dimension2 = require( 'DOT/Dimension2' );
  const DragListener = require( 'SCENERY/listeners/DragListener' );
  const EventType = require( 'TANDEM/EventType' );
  const LinearGradient = require( 'SCENERY/util/LinearGradient' );
  const merge = require( 'PHET_CORE/merge' );
  const Node = require( 'SCENERY/nodes/Node' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Rectangle = require( 'SCENERY/nodes/Rectangle' );
  const Shape = require( 'KITE/Shape' );
  const sun = require( 'SUN/sun' );
  const Tandem = require( 'TANDEM/Tandem' );
  const ToggleSwitchIO = require( 'SUN/ToggleSwitchIO' );
  const Utils = require( 'DOT/Utils' );
  const Vector2 = require( 'DOT/Vector2' );

  // constants
  const DEFAULT_SIZE = new Dimension2( 60, 30 );

  class ToggleSwitch extends Node {

    /**
     * @param {Property.<*>} property
     * @param {*} leftValue - value when the switch is in the left position
     * @param {*} rightValue - value when the switch is in the right position
     * @param {Object} [options]
     */
    constructor( property, leftValue, rightValue, options ) {

      // PhET-iO requirements
      assert && assert( property.phetioType, 'property must have phetioType' );
      assert && assert( property.phetioType.parameterTypes, 'property.phetioType must have parameterTypes' );

      options = merge( {

        size: DEFAULT_SIZE, // if you want the thumb to be a circle, use width that is 2x height
        cursor: 'pointer',

        // controls the behavior of when model value changes occur during dragging (if any)
        // null (default: triggers model changes when thumb is dragged far enough to the side, similar to iOS)
        // true: triggers model changes whenever the thumb crosses sides
        // false: only trigger model changes until release
        toggleWhileDragging: null,

        // number of view-space units the drag needs to cover to be considered a "drag" instead of a "click/tap"
        dragThreshold: 3,

        // number of thumb-widths outside the normal range past where the model value will change
        toggleThreshold: 1,

        // thumb
        thumbFill: null, // {Color|string} thumb fill, default computed below
        thumbStroke: 'black',
        thumbTouchAreaXDilation: 8,
        thumbTouchAreaYDilation: 8,
        thumbMouseAreaXDilation: 0,
        thumbMouseAreaYDilation: 0,

        // track
        trackFillLeft: null, // {Color|string} track fill when property.value == leftValue, default computed below
        trackFillRight: null, // {Color|string} track fill when property.value == rightValue, default computed below
        trackStroke: 'black',

        // phet-io
        tandem: Tandem.REQUIRED,
        phetioType: ToggleSwitchIO,
        phetioEventType: EventType.USER,
        phetioReadOnly: PhetioObject.DEFAULT_OPTIONS.phetioReadOnly
      }, options );

      // Default track fills
      let defaultTrackFill = null;
      if ( !options.trackFillLeft || !options.trackFillRight ) {
        defaultTrackFill = new LinearGradient( 0, 0, 0, options.size.height )
          .addColorStop( 0, 'rgb( 40, 40, 40 )' )
          .addColorStop( 1, 'rgb( 200, 200, 200 )' );
      }
      options.trackFillLeft = options.trackFillLeft || defaultTrackFill;
      options.trackFillRight = options.trackFillRight || defaultTrackFill;

      // Default thumb fill
      options.thumbFill = options.thumbFill ||
                          new LinearGradient( 0, 0, 0, options.size.height )
                            .addColorStop( 0, 'white' )
                            .addColorStop( 1, 'rgb( 200, 200, 200 )' );

      super();

      const cornerRadius = options.size.height / 2;

      // track that the thumb slides in
      const trackNode = new Rectangle( 0, 0, options.size.width, options.size.height, cornerRadius, cornerRadius, {
        stroke: options.trackStroke,
        cachedPaints: [ options.trackFillLeft, options.trackFillRight ]
      } );
      this.addChild( trackNode );

      // thumb (aka knob)
      const thumbNode = new Rectangle( 0, 0, 0.5 * options.size.width, options.size.height, cornerRadius, cornerRadius, {
        fill: options.thumbFill,
        stroke: options.thumbStroke
      } );
      this.addChild( thumbNode );

      // thumb touchArea
      if ( options.thumbTouchAreaXDilation || options.thumbTouchAreaYDilation ) {
        thumbNode.touchArea = Shape.roundRect(
          -options.thumbTouchAreaXDilation, -options.thumbTouchAreaYDilation,
          ( 0.5 * options.size.width ) + ( 2 * options.thumbTouchAreaXDilation ),
          options.size.height + ( 2 * options.thumbTouchAreaYDilation ), cornerRadius, cornerRadius );
      }

      // thumb mouseArea
      if ( options.thumbMouseAreaXDilation || options.thumbMouseAreaYDilation ) {
        thumbNode.mouseArea = Shape.roundRect(
          -options.thumbMouseAreaXDilation, -options.thumbMouseAreaYDilation,
          ( 0.5 * options.size.width ) + ( 2 * options.thumbMouseAreaXDilation ),
          options.size.height + ( 2 * options.thumbMouseAreaYDilation ), cornerRadius, cornerRadius );
      }

      // move thumb and fill track
      const update = value => {
        if ( value === leftValue ) {
          thumbNode.left = 0;
          trackNode.fill = options.trackFillLeft;
        }
        else {
          thumbNode.right = options.size.width;
          trackNode.fill = options.trackFillRight;
        }
      };

      // sync with property, must be unlinked in dispose
      property.link( update );

      // thumb interactivity
      const dragThresholdSquared = options.dragThreshold * options.dragThreshold; // comparing squared magnitudes is a bit faster
      const accumulatedDelta = new Vector2( 0, 0 ); // stores how far we are from where our drag started, in our local coordinate frame
      let passedDragThreshold = false; // whether we have dragged far enough to be considered for "drag" behavior (pick closest side), or "tap" behavior (toggle)

      // Get the phetioType of the Property's value.  This is needed for the message sent by toggleAction.
      const valuePhetioType = property.phetioType.parameterTypes[ 0 ];

      // Action that is performed when the switch is toggled.
      // Toggles the Property value and sends a phet-io message with the old and new values.
      const toggleAction = new Action( ( oldValue, newValue ) => { property.value = newValue; }, {
        parameters: [
          { name: 'oldValue', phetioType: valuePhetioType },
          { name: 'newValue', phetioType: valuePhetioType }
        ],
        tandem: options.tandem.createTandem( 'toggleAction' ),
        phetioDocumentation: 'Occurs when the switch is toggled via user interaction',
        phetioReadOnly: options.phetioReadOnly,
        phetioEventType: EventType.USER
      } );

      // Gets the value that corresponds to the current thumb position.
      const thumbPositionToValue = () => ( thumbNode.centerX < trackNode.centerX ) ? leftValue : rightValue;

      const dragListener = new DragListener( {
        tandem: options.tandem.createTandem( 'dragListener' ),

        // Only touch to snag when moving the thumb (don't snag on the track itself),
        // but still support presses in the track to toggle the value.
        canStartPress: event => {
          if ( event.type === 'move' || event.type === 'enter' ) {
            return _.includes( event.trail.nodes, thumbNode );
          }
          else {
            return true;
          }
        },

        start: () => {
          // resets our state
          accumulatedDelta.setXY( 0, 0 ); // reset it mutably (less allocation)
          passedDragThreshold = false;
        },

        drag: ( event, listener ) => {

          accumulatedDelta.add( listener.modelDelta );
          passedDragThreshold = passedDragThreshold || ( accumulatedDelta.magnitudeSquared > dragThresholdSquared );

          // center the thumb on the pointer's x-coordinate if possible (but clamp to left and right ends)
          const viewPoint = listener.getCurrentTarget().globalToLocalPoint( event.pointer.point );
          const halfThumbWidth = thumbNode.width / 2;
          thumbNode.centerX = Utils.clamp( viewPoint.x, halfThumbWidth, options.size.width - halfThumbWidth );

          // whether the thumb is dragged outside of the possible range far enough beyond our threshold to potentially
          // trigger an immediate model change
          const isDraggedOutside = viewPoint.x < ( 1 - 2 * options.toggleThreshold ) * halfThumbWidth ||
                                   viewPoint.x > ( -1 + 2 * options.toggleThreshold ) * halfThumbWidth + options.size.width;

          // value that corresponds to the current thumb position
          const value = thumbPositionToValue();

          // track fill changes based on the thumb positions
          trackNode.fill = ( value === leftValue ) ? options.trackFillLeft : options.trackFillRight;

          if ( options.toggleWhileDragging === true || ( isDraggedOutside && options.toggleWhileDragging === null ) ) {

            // Only signify a change if the value actually changed to avoid duplicate messages in the PhET-iO Event
            // stream, see https://github.com/phetsims/phet-io/issues/369
            const changed = ( property.value !== value );
            if ( changed ) {
              toggleAction.execute( property.value, value );
            }
          }
        },

        end: () => {

          // if moved past the threshold, choose value based on the side, otherwise just toggle
          const oldValue = property.value;
          const toggleValue = ( oldValue === leftValue ? rightValue : leftValue );
          const newValue = passedDragThreshold ? thumbPositionToValue() : toggleValue;

          toggleAction.execute( oldValue, newValue );

          // update the thumb location (sanity check that it's here, only needs to be run if passedDragThreshold===true)
          update( newValue );
        }
      } );
      this.addInputListener( dragListener );

      this.mutate( options );

      // @private
      this.disposeToggleSwitch = () => {
        property.unlink( update );
        toggleAction.dispose();
        dragListener.dispose();
      };
    }

    /**
     * @public
     * @override
     */
    dispose() {
      this.disposeToggleSwitch();
      super.dispose();
    }
  }

  return sun.register( 'ToggleSwitch', ToggleSwitch );
} );
<commit_msg>add linked element from ToggleSwitch to its associated Property, https://github.com/phetsims/sun/issues/559<commit_after>// Copyright 2014-2020, University of Colorado Boulder

/**
 * ToggleSwitch is a switch for toggling between 2 values, similar to iOS' UISwitch, used in iOS `'Settings' app.
 *
 * Interaction behavior is as follows:
 * Drag the thumb to change the value, or click anywhere to toggle the value.
 * If you click without dragging, it's a toggle.
 * If you drag but don't cross the midpoint of the track, then it's a toggle.
 * If you drag past the midpoint of the track, releasing the thumb snaps to whichever end the thumb is closest to.
 * If you drag the thumb far enough to the side (outside of the switch), it will immediately toggle the model behavior.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Jonathan Olson (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const Action = require( 'AXON/Action' );
  const Dimension2 = require( 'DOT/Dimension2' );
  const DragListener = require( 'SCENERY/listeners/DragListener' );
  const EventType = require( 'TANDEM/EventType' );
  const LinearGradient = require( 'SCENERY/util/LinearGradient' );
  const merge = require( 'PHET_CORE/merge' );
  const Node = require( 'SCENERY/nodes/Node' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Rectangle = require( 'SCENERY/nodes/Rectangle' );
  const Shape = require( 'KITE/Shape' );
  const sun = require( 'SUN/sun' );
  const Tandem = require( 'TANDEM/Tandem' );
  const ToggleSwitchIO = require( 'SUN/ToggleSwitchIO' );
  const Utils = require( 'DOT/Utils' );
  const Vector2 = require( 'DOT/Vector2' );

  // constants
  const DEFAULT_SIZE = new Dimension2( 60, 30 );

  class ToggleSwitch extends Node {

    /**
     * @param {Property.<*>} property
     * @param {*} leftValue - value when the switch is in the left position
     * @param {*} rightValue - value when the switch is in the right position
     * @param {Object} [options]
     */
    constructor( property, leftValue, rightValue, options ) {

      // PhET-iO requirements
      assert && assert( property.phetioType, 'property must have phetioType' );
      assert && assert( property.phetioType.parameterTypes, 'property.phetioType must have parameterTypes' );

      options = merge( {

        size: DEFAULT_SIZE, // if you want the thumb to be a circle, use width that is 2x height
        cursor: 'pointer',

        // controls the behavior of when model value changes occur during dragging (if any)
        // null (default: triggers model changes when thumb is dragged far enough to the side, similar to iOS)
        // true: triggers model changes whenever the thumb crosses sides
        // false: only trigger model changes until release
        toggleWhileDragging: null,

        // number of view-space units the drag needs to cover to be considered a "drag" instead of a "click/tap"
        dragThreshold: 3,

        // number of thumb-widths outside the normal range past where the model value will change
        toggleThreshold: 1,

        // thumb
        thumbFill: null, // {Color|string} thumb fill, default computed below
        thumbStroke: 'black',
        thumbTouchAreaXDilation: 8,
        thumbTouchAreaYDilation: 8,
        thumbMouseAreaXDilation: 0,
        thumbMouseAreaYDilation: 0,

        // track
        trackFillLeft: null, // {Color|string} track fill when property.value == leftValue, default computed below
        trackFillRight: null, // {Color|string} track fill when property.value == rightValue, default computed below
        trackStroke: 'black',

        // phet-io
        tandem: Tandem.REQUIRED,
        phetioType: ToggleSwitchIO,
        phetioEventType: EventType.USER,
        phetioReadOnly: PhetioObject.DEFAULT_OPTIONS.phetioReadOnly
      }, options );

      // Default track fills
      let defaultTrackFill = null;
      if ( !options.trackFillLeft || !options.trackFillRight ) {
        defaultTrackFill = new LinearGradient( 0, 0, 0, options.size.height )
          .addColorStop( 0, 'rgb( 40, 40, 40 )' )
          .addColorStop( 1, 'rgb( 200, 200, 200 )' );
      }
      options.trackFillLeft = options.trackFillLeft || defaultTrackFill;
      options.trackFillRight = options.trackFillRight || defaultTrackFill;

      // Default thumb fill
      options.thumbFill = options.thumbFill ||
                          new LinearGradient( 0, 0, 0, options.size.height )
                            .addColorStop( 0, 'white' )
                            .addColorStop( 1, 'rgb( 200, 200, 200 )' );

      super();

      const cornerRadius = options.size.height / 2;

      // track that the thumb slides in
      const trackNode = new Rectangle( 0, 0, options.size.width, options.size.height, cornerRadius, cornerRadius, {
        stroke: options.trackStroke,
        cachedPaints: [ options.trackFillLeft, options.trackFillRight ]
      } );
      this.addChild( trackNode );

      // thumb (aka knob)
      const thumbNode = new Rectangle( 0, 0, 0.5 * options.size.width, options.size.height, cornerRadius, cornerRadius, {
        fill: options.thumbFill,
        stroke: options.thumbStroke
      } );
      this.addChild( thumbNode );

      // thumb touchArea
      if ( options.thumbTouchAreaXDilation || options.thumbTouchAreaYDilation ) {
        thumbNode.touchArea = Shape.roundRect(
          -options.thumbTouchAreaXDilation, -options.thumbTouchAreaYDilation,
          ( 0.5 * options.size.width ) + ( 2 * options.thumbTouchAreaXDilation ),
          options.size.height + ( 2 * options.thumbTouchAreaYDilation ), cornerRadius, cornerRadius );
      }

      // thumb mouseArea
      if ( options.thumbMouseAreaXDilation || options.thumbMouseAreaYDilation ) {
        thumbNode.mouseArea = Shape.roundRect(
          -options.thumbMouseAreaXDilation, -options.thumbMouseAreaYDilation,
          ( 0.5 * options.size.width ) + ( 2 * options.thumbMouseAreaXDilation ),
          options.size.height + ( 2 * options.thumbMouseAreaYDilation ), cornerRadius, cornerRadius );
      }

      // move thumb and fill track
      const update = value => {
        if ( value === leftValue ) {
          thumbNode.left = 0;
          trackNode.fill = options.trackFillLeft;
        }
        else {
          thumbNode.right = options.size.width;
          trackNode.fill = options.trackFillRight;
        }
      };

      // sync with property, must be unlinked in dispose
      property.link( update );

      // thumb interactivity
      const dragThresholdSquared = options.dragThreshold * options.dragThreshold; // comparing squared magnitudes is a bit faster
      const accumulatedDelta = new Vector2( 0, 0 ); // stores how far we are from where our drag started, in our local coordinate frame
      let passedDragThreshold = false; // whether we have dragged far enough to be considered for "drag" behavior (pick closest side), or "tap" behavior (toggle)

      // Get the phetioType of the Property's value.  This is needed for the message sent by toggleAction.
      const valuePhetioType = property.phetioType.parameterTypes[ 0 ];

      // Action that is performed when the switch is toggled.
      // Toggles the Property value and sends a phet-io message with the old and new values.
      const toggleAction = new Action( ( oldValue, newValue ) => { property.value = newValue; }, {
        parameters: [
          { name: 'oldValue', phetioType: valuePhetioType },
          { name: 'newValue', phetioType: valuePhetioType }
        ],
        tandem: options.tandem.createTandem( 'toggleAction' ),
        phetioDocumentation: 'Occurs when the switch is toggled via user interaction',
        phetioReadOnly: options.phetioReadOnly,
        phetioEventType: EventType.USER
      } );

      // Gets the value that corresponds to the current thumb position.
      const thumbPositionToValue = () => ( thumbNode.centerX < trackNode.centerX ) ? leftValue : rightValue;

      const dragListener = new DragListener( {
        tandem: options.tandem.createTandem( 'dragListener' ),

        // Only touch to snag when moving the thumb (don't snag on the track itself),
        // but still support presses in the track to toggle the value.
        canStartPress: event => {
          if ( event.type === 'move' || event.type === 'enter' ) {
            return _.includes( event.trail.nodes, thumbNode );
          }
          else {
            return true;
          }
        },

        start: () => {
          // resets our state
          accumulatedDelta.setXY( 0, 0 ); // reset it mutably (less allocation)
          passedDragThreshold = false;
        },

        drag: ( event, listener ) => {

          accumulatedDelta.add( listener.modelDelta );
          passedDragThreshold = passedDragThreshold || ( accumulatedDelta.magnitudeSquared > dragThresholdSquared );

          // center the thumb on the pointer's x-coordinate if possible (but clamp to left and right ends)
          const viewPoint = listener.getCurrentTarget().globalToLocalPoint( event.pointer.point );
          const halfThumbWidth = thumbNode.width / 2;
          thumbNode.centerX = Utils.clamp( viewPoint.x, halfThumbWidth, options.size.width - halfThumbWidth );

          // whether the thumb is dragged outside of the possible range far enough beyond our threshold to potentially
          // trigger an immediate model change
          const isDraggedOutside = viewPoint.x < ( 1 - 2 * options.toggleThreshold ) * halfThumbWidth ||
                                   viewPoint.x > ( -1 + 2 * options.toggleThreshold ) * halfThumbWidth + options.size.width;

          // value that corresponds to the current thumb position
          const value = thumbPositionToValue();

          // track fill changes based on the thumb positions
          trackNode.fill = ( value === leftValue ) ? options.trackFillLeft : options.trackFillRight;

          if ( options.toggleWhileDragging === true || ( isDraggedOutside && options.toggleWhileDragging === null ) ) {

            // Only signify a change if the value actually changed to avoid duplicate messages in the PhET-iO Event
            // stream, see https://github.com/phetsims/phet-io/issues/369
            const changed = ( property.value !== value );
            if ( changed ) {
              toggleAction.execute( property.value, value );
            }
          }
        },

        end: () => {

          // if moved past the threshold, choose value based on the side, otherwise just toggle
          const oldValue = property.value;
          const toggleValue = ( oldValue === leftValue ? rightValue : leftValue );
          const newValue = passedDragThreshold ? thumbPositionToValue() : toggleValue;

          toggleAction.execute( oldValue, newValue );

          // update the thumb location (sanity check that it's here, only needs to be run if passedDragThreshold===true)
          update( newValue );
        }
      } );
      this.addInputListener( dragListener );

      this.mutate( options );

      // Add a link to the Property that this switch controls
      this.addLinkedElement( property, {
        tandem: options.tandem.createTandem( 'property' )
      } );

      // @private
      this.disposeToggleSwitch = () => {
        property.unlink( update );
        toggleAction.dispose();
        dragListener.dispose();
      };
    }

    /**
     * @public
     * @override
     */
    dispose() {
      this.disposeToggleSwitch();
      super.dispose();
    }
  }

  return sun.register( 'ToggleSwitch', ToggleSwitch );
} );
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { LineChart, LineItem, XAxis, YAxis, Tooltip, CartesianGrid } from 'recharts';
import Highlight from 'utils/Highlight';
import './IndexView.scss';

const data = [{name: '上海', uv: 400, pv: 2400, amt: 2400},
              {name: '北京', uv: 300, pv: 4567, amt: 2400},
              {name: '杭州', uv: 300, pv: 1398, amt: 2400},
              {name: '上饶', uv: 200, pv: 9800, amt: 2400},
              {name: '台湾', uv: 278, pv: 3908, amt: 2400},
              {name: '香港', uv: 189, pv: 4800, amt: 2400}];

class IndexView extends Component {
  render() {
    const exCode = `
  <LineChart width={400} height={300} data={data}>
    <CartesianGrid stroke='#f5f5f5'/>
    <LineItem type='monotone' dataKey='uv' stroke='#ff7300'/>
  </LineChart>
    `;

    return (

Recharts = React + Charts

Re-designed charting library built with React

Install v0.1.0

            <LineChart width={540} height={300} data={data}>
              <CartesianGrid stroke='#f5f5f5' />
              <LineItem type='monotone' dataKey='uv' stroke='#ff7300' />
            </LineChart>
            <Highlight className="jsx">{exCode}</Highlight>

              Componentized

Compose your application with decoupled, reusable React components.
              Depend by React virtual DOM, you will use it more assured.

              Comfortable

Simplely deploy with configuration of props. Native SVG elements supported,
              and lightweight depend on the D3 submodules, but you don't need to write D3 code any more.

              Customizable

Separated components of each chart type,
              so you can redefine the sub components to realize your mind or use sub components to define new charts.

Recharts enables deeper integration of charts into your React application

Getting Started

    );
  }
}

export default IndexView;
<commit_msg>fix: fix the index links<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { LineChart, LineItem, XAxis, YAxis, Tooltip, CartesianGrid } from 'recharts';
import Highlight from 'utils/Highlight';
import './IndexView.scss';

const data = [{name: '上海', uv: 400, pv: 2400, amt: 2400},
              {name: '北京', uv: 300, pv: 4567, amt: 2400},
              {name: '杭州', uv: 300, pv: 1398, amt: 2400},
              {name: '上饶', uv: 200, pv: 9800, amt: 2400},
              {name: '台湾', uv: 278, pv: 3908, amt: 2400},
              {name: '香港', uv: 189, pv: 4800, amt: 2400}];

class IndexView extends Component {
  render() {
    const exCode = `
  <LineChart width={400} height={300} data={data}>
    <CartesianGrid stroke='#f5f5f5'/>
    <LineItem type='monotone' dataKey='uv' stroke='#ff7300'/>
  </LineChart>
    `;

    return (

Recharts = React + Charts

Re-designed charting library built with React

Install v0.1.0

            <LineChart width={540} height={300} data={data}>
              <CartesianGrid stroke='#f5f5f5' />
              <LineItem type='monotone' dataKey='uv' stroke='#ff7300' />
            </LineChart>
            <Highlight className="jsx">{exCode}</Highlight>

              Componentized

Compose your application with decoupled, reusable React components.
              Depend by React virtual DOM, you will use it more assured.

              Comfortable

Simplely deploy with configuration of props. Native SVG elements supported,
              and lightweight depend on the D3 submodules, but you don't need to write D3 code any more.

              Customizable

Separated components of each chart type,
              so you can redefine the sub components to realize your mind or use sub components to define new charts.

Recharts enables deeper integration of charts into your React application

Getting Started

    );
  }
}

export default IndexView;
<|endoftext|>