<commit_before>import get from 'lodash/get'
import { createSelector } from 'reselect'
import { grpc } from 'workers'
import { getIntl } from '@zap/i18n'
import { openModal, closeModal } from './modal'
import { settingsSelectors } from './settings'
import { showError } from './notification'
import createReducer from './utils/createReducer'
import messages from './messages'
// ------------------------------------
// Initial State
// ------------------------------------

const initialState = {
  isAddressLoading: false,
  fetchAddressError: null,
  addressesLoading: {
    np2wkh: false,
    p2wkh: false,
  },
  addresses: {
    np2wkh: null,
    p2wkh: null,
  },
}

// ------------------------------------
// Constants
// ------------------------------------

export const FETCH_ADDRESSES = 'FETCH_ADDRESSES'
export const FETCH_ADDRESSES_SUCCESS = 'FETCH_ADDRESSES_SUCCESS'
export const FETCH_ADDRESSES_FAILURE = 'FETCH_ADDRESSES_FAILURE'
export const NEW_ADDRESS = 'NEW_ADDRESS'
export const NEW_ADDRESS_SUCCESS = 'NEW_ADDRESS_SUCCESS'
export const NEW_ADDRESS_FAILURE = 'NEW_ADDRESS_FAILURE'

// LND expects types to be sent as int, so this object will allow mapping from string to int
const ADDRESS_TYPES = {
  p2wkh: 0,
  np2wkh: 1,
}

// ------------------------------------
// Actions
// ------------------------------------

export const openWalletModal = () => dispatch => dispatch(openModal('RECEIVE_MODAL'))
export const closeWalletModal = () => dispatch => dispatch(closeModal('RECEIVE_MODAL'))

/**
 * initAddresses - Initialise addresses.
 *
 * @returns {Function} Thunk
 */
export const initAddresses = () => async (dispatch, getState) => {
  try {
    dispatch({ type: FETCH_ADDRESSES })

    const state = getState()

    // Get node information (addresses are keyed under the node pubkey).
    const pubKey = state.info.data.identity_pubkey
    const node = await window.db.nodes.get({ id: pubKey })

    // Get existing addresses for the node.
    const addresses = get(node, 'addresses', {})

    // Ensure that we have an address for all supported address types.
    await Promise.all(
      Object.keys(ADDRESS_TYPES).map(async type => {
        if (!addresses[type]) {
          return dispatch(newAddress(type))
        }
        return null
      })
    )
    dispatch({ type: FETCH_ADDRESSES_SUCCESS, addresses })
  } catch (error) {
    dispatch({ type: FETCH_ADDRESSES_FAILURE, error: error.message })
  }
}

/**
 * newAddress - Generate a new address and sets it as wallet current.
 *
 * @param {('p2wkh'|'np2wkh')} addressType Address type
 * @returns {Function} Thunk
 */
export const newAddress = addressType => async dispatch => {
  dispatch({ type: NEW_ADDRESS, addressType })
  try {
    const address = await dispatch(createNewAddress(addressType))
    await dispatch(newAddressSuccess(addressType, address))
  } catch (error) {
    dispatch(newAddressFailure(addressType, error.message))
  }
}

/**
 * createNewAddress - Creates a new address.
 *
 * @param {('p2wkh'|'np2wkh'|null)} addressType Address type. If not specified uses current saved config setting.
 * @returns {Promise<string>} Generated address
 */
export const createNewAddress = addressType => async (dispatch, getState) => {
  const getConfigAddressType = () => {
    const settings = settingsSelectors.currentConfig(getState())
    return settings && settings.address
  }
  const type = addressType || getConfigAddressType()
  const data = await grpc.services.Lightning.newAddress({ type: ADDRESS_TYPES[type] })
  return data.address
}

/**
 * newAddressSuccess - Generate new address success callback.
 *
 * @param {string} addressType Address type
 * @param {string} address Address
 * @returns {Function} Thunk
 */
export const newAddressSuccess = (addressType, address) => async (dispatch, getState) => {
  const state = getState()
  const pubKey = state.info.data.identity_pubkey

  // If we know the node's public key, store the address for reuse.
  if (pubKey) {
    const node = await window.db.nodes.get(pubKey)
    if (node) {
      node.setCurrentAddress(addressType, address)
      await window.db.nodes.put(node)
    } else {
      await window.db.nodes.put({ id: pubKey, addresses: { [addressType]: address } })
    }
  }

  dispatch({ type: NEW_ADDRESS_SUCCESS, addressType, address })
}

/**
 * newAddressFailure - Generate new address failure callback.
 *
 * @param {string} addressType Address type
 * @param {string} error Error message
 * @returns {Function} Thunk
 */
export const newAddressFailure = (addressType, error) => dispatch => {
  const intl = getIntl()
  dispatch(
    showError(intl.formatMessage(messages.address_new_address_error, { addressType, error }))
  )
  dispatch({
    type: NEW_ADDRESS_FAILURE,
    addressType,
    error,
  })
}

// ------------------------------------
// Action Handlers
// ------------------------------------

const ACTION_HANDLERS = {
  [FETCH_ADDRESSES]: state => {
    state.isAddressLoading = true
  },
  [FETCH_ADDRESSES_SUCCESS]: (state, { addresses }) => {
    state.addresses = addresses
    state.isAddressLoading = false
    state.fetchAddressError = null
  },
  [FETCH_ADDRESSES_FAILURE]: (state, { error }) => {
    state.isAddressLoading = false
    state.fetchAddressError = error
  },
  [NEW_ADDRESS]: (state, { addressType }) => {
    state.addressesLoading[addressType] = true
  },
  [NEW_ADDRESS_SUCCESS]: (state, { addressType, address }) => {
    state.addressesLoading[addressType] = false
    state.addresses[addressType] = address
  },
  [NEW_ADDRESS_FAILURE]: (state, { addressType }) => {
    state.addressesLoading[addressType] = false
  },
}

// ------------------------------------
// Selectors
// ------------------------------------

const addressSelectors = {}

addressSelectors.addressesLoading = state => state.address.addressesLoading
addressSelectors.currentAddresses = state => state.address.addresses
addressSelectors.currentConfig = state => settingsSelectors.currentConfig(state)
addressSelectors.currentAddress = createSelector(
  addressSelectors.currentAddresses,
  addressSelectors.currentConfig,
  (currentAddresses, currentConfig) => currentAddresses[currentConfig.address]
)
addressSelectors.isAddressLoading = createSelector(
  addressSelectors.addressesLoading,
  addressSelectors.currentConfig,
  (addressesLoading, currentConfig) => addressesLoading[currentConfig.address]
)

export { addressSelectors }

export default createReducer(initialState, ACTION_HANDLERS)
<commit_msg>fix(onboarding): retain wallet addresses in state<commit_after>import get from 'lodash/get'
import { createSelector } from 'reselect'
import { grpc } from 'workers'
import { getIntl } from '@zap/i18n'
import { openModal, closeModal } from './modal'
import { settingsSelectors } from './settings'
import { showError } from './notification'
import createReducer from './utils/createReducer'
import messages from './messages'
// ------------------------------------
// Initial State
// ------------------------------------

const initialState = {
  isAddressLoading: false,
  fetchAddressError: null,
  addressesLoading: {
    np2wkh: false,
    p2wkh: false,
  },
  addresses: {
    np2wkh: null,
    p2wkh: null,
  },
}

// ------------------------------------
// Constants
// ------------------------------------

export const FETCH_ADDRESSES = 'FETCH_ADDRESSES'
export const FETCH_ADDRESSES_SUCCESS = 'FETCH_ADDRESSES_SUCCESS'
export const FETCH_ADDRESSES_FAILURE = 'FETCH_ADDRESSES_FAILURE'
export const NEW_ADDRESS = 'NEW_ADDRESS'
export const NEW_ADDRESS_SUCCESS = 'NEW_ADDRESS_SUCCESS'
export const NEW_ADDRESS_FAILURE = 'NEW_ADDRESS_FAILURE'

// LND expects types to be sent as int, so this object will allow mapping from string to int
const ADDRESS_TYPES = {
  p2wkh: 0,
  np2wkh: 1,
}

// ------------------------------------
// Actions
// ------------------------------------

export const openWalletModal = () => dispatch => dispatch(openModal('RECEIVE_MODAL'))
export const closeWalletModal = () => dispatch => dispatch(closeModal('RECEIVE_MODAL'))

/**
 * initAddresses - Initialise addresses.
 *
 * @returns {Function} Thunk
 */
export const initAddresses = () => async (dispatch, getState) => {
  try {
    dispatch({ type: FETCH_ADDRESSES })

    const state = getState()

    // Get node information (addresses are keyed under the node pubkey).
    const pubKey = state.info.data.identity_pubkey
    const node = await window.db.nodes.get({ id: pubKey })

    // Get existing addresses for the node.
    const addresses = get(node, 'addresses', {})

    // Ensure that we have an address for all supported address types.
    const fetchAddresses = async types => {
      const data = {}
      await Promise.all(
        types.map(async type => {
          data[type] = addresses[type] || (await dispatch(newAddress(type)))
        })
      )
      return data
    }

    const allAddresses = await fetchAddresses(Object.keys(ADDRESS_TYPES))

    dispatch({ type: FETCH_ADDRESSES_SUCCESS, addresses: allAddresses })
  } catch (error) {
    dispatch({ type: FETCH_ADDRESSES_FAILURE, error: error.message })
  }
}

/**
 * newAddress - Generate a new address and sets it as wallet current.
 *
 * @param {('p2wkh'|'np2wkh')} addressType Address type
 * @returns {Function} Thunk
 */
export const newAddress = addressType => async dispatch => {
  dispatch({ type: NEW_ADDRESS, addressType })
  try {
    const address = await dispatch(createNewAddress(addressType))
    await dispatch(newAddressSuccess(addressType, address))
    return address
  } catch (error) {
    dispatch(newAddressFailure(addressType, error.message))
    return null
  }
}

/**
 * createNewAddress - Creates a new address.
 *
 * @param {('p2wkh'|'np2wkh'|null)} addressType Address type. If not specified uses current saved config setting.
 * @returns {Promise<string>} Generated address
 */
export const createNewAddress = addressType => async (dispatch, getState) => {
  const getConfigAddressType = () => {
    const settings = settingsSelectors.currentConfig(getState())
    return settings && settings.address
  }
  const type = addressType || getConfigAddressType()
  const data = await grpc.services.Lightning.newAddress({ type: ADDRESS_TYPES[type] })
  return data.address
}

/**
 * newAddressSuccess - Generate new address success callback.
 *
 * @param {string} addressType Address type
 * @param {string} address Address
 * @returns {Function} Thunk
 */
export const newAddressSuccess = (addressType, address) => async (dispatch, getState) => {
  const state = getState()
  const pubKey = state.info.data.identity_pubkey

  // If we know the node's public key, store the address for reuse.
  if (pubKey) {
    const node = await window.db.nodes.get(pubKey)
    if (node) {
      node.setCurrentAddress(addressType, address)
      await window.db.nodes.put(node)
    } else {
      await window.db.nodes.put({ id: pubKey, addresses: { [addressType]: address } })
    }
  }

  dispatch({ type: NEW_ADDRESS_SUCCESS, addressType, address })
}

/**
 * newAddressFailure - Generate new address failure callback.
 *
 * @param {string} addressType Address type
 * @param {string} error Error message
 * @returns {Function} Thunk
 */
export const newAddressFailure = (addressType, error) => dispatch => {
  const intl = getIntl()
  dispatch(
    showError(intl.formatMessage(messages.address_new_address_error, { addressType, error }))
  )
  dispatch({
    type: NEW_ADDRESS_FAILURE,
    addressType,
    error,
  })
}

// ------------------------------------
// Action Handlers
// ------------------------------------

const ACTION_HANDLERS = {
  [FETCH_ADDRESSES]: state => {
    state.isAddressLoading = true
  },
  [FETCH_ADDRESSES_SUCCESS]: (state, { addresses }) => {
    state.addresses = addresses
    state.isAddressLoading = false
    state.fetchAddressError = null
  },
  [FETCH_ADDRESSES_FAILURE]: (state, { error }) => {
    state.isAddressLoading = false
    state.fetchAddressError = error
  },
  [NEW_ADDRESS]: (state, { addressType }) => {
    state.addressesLoading[addressType] = true
  },
  [NEW_ADDRESS_SUCCESS]: (state, { addressType, address }) => {
    state.addressesLoading[addressType] = false
    state.addresses[addressType] = address
  },
  [NEW_ADDRESS_FAILURE]: (state, { addressType }) => {
    state.addressesLoading[addressType] = false
  },
}

// ------------------------------------
// Selectors
// ------------------------------------

const addressSelectors = {}

addressSelectors.addressesLoading = state => state.address.addressesLoading
addressSelectors.currentAddresses = state => state.address.addresses
addressSelectors.currentConfig = state => settingsSelectors.currentConfig(state)
addressSelectors.currentAddress = createSelector(
  addressSelectors.currentAddresses,
  addressSelectors.currentConfig,
  (currentAddresses, currentConfig) => currentAddresses[currentConfig.address]
)
addressSelectors.isAddressLoading = createSelector(
  addressSelectors.addressesLoading,
  addressSelectors.currentConfig,
  (addressesLoading, currentConfig) => addressesLoading[currentConfig.address]
)

export { addressSelectors }

export default createReducer(initialState, ACTION_HANDLERS)
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * @author Toru Nagashima
 * @copyright 2016 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
"use strict"

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const fs = require("fs")
const path = require("path")
const chokidar = require("chokidar")
const minimist = require("minimist")
const mkdir = require("mkdirp")
const createAppcacheStream = require("../lib/generate")
const Queue = require("../lib/queue")

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const OPTIONS = [
  {name: "output", alias: "o", type: "string"},
  {name: "prefix", alias: "p", type: "string"},
  {name: "postfile", alias: "P", type: "string"},
  {name: "help", alias: "h", type: "boolean"},
  {name: "verbose", alias: "v", type: "boolean"},
  {name: "version", alias: "V", type: "boolean"},
  {name: "watch", alias: "w", type: "boolean"},
  {name: "network-star", alias: "networkStar", type: "boolean"},
  {name: "stamp", alias: "s", type: "boolean"},
]

/**
 * The reduce handler to make alias object.
 *
 * @param {object} obj - The alias object.
 * @param {{name: string, alias: string}} option - The option object.
 * @returns {object} `obj`.
 * @private
 */
function mergeAlias(obj, option) {
    obj[option.alias] = option.name
    return obj
}

/**
 * Print the help text of this CLI command.
 *
 * @returns {void}
 * @private
 */
function printHelp() {
    console.log(`
Usage: appcache-manifest [OPTIONS] [FILES...]

    FILES: File globs that includes into manifest.

Options:
    -o, --output <FILE_PATH>    The path of an output file.
                                Prints to stdout by default.
    -s, --stamp                 Stamps the output with the date/time instead
                                of an md5 hash.
    -p, --prefix <PATH>         The prefix part of paths in cache section.
    -P, --postfile <FILE_PATH>  The path of a postfix file.  The file will be
                                concatinated after generated contents.
    -h, --help                  Prints this text.
    -v, --verbose               Prints detail messages in progress. This option
                                must be used together with '--output' option.
    -V, --version               Prints version number.
    -w, --watch                 This command would be watching target files and
                                directories if --watch option is specified.
                                Then it regenerates the manifest file when those
                                files are added, removed, or changed.
    --network-star              Adds "NETWORK:\n*" after generated contents.

See Also:
    https://github.com/mysticatea/appcache-manifest
`)
}

/**
 * Print the version number of this CLI command.
 *
 * @returns {void}
 * @private
 */
function printVersion() {
    console.log(`v${require("../package.json").version}`)
}

//------------------------------------------------------------------------------
const generationQueue = new Queue()

/**
 * Generate appcache manifest.
 *
 * @param {string[]} globs - The globs which specifies the target files.
 * @param {object} options - The option object.
 * @param {function} callback - The callback function.
 * @returns {void}
 * @private
 */
function generate(globs, options, callback) {
    if (generationQueue.size > 0) {
        // There is a process not started.
        return
    }
    if (typeof options.delay === "number") {
        generationQueue.push(next => setTimeout(next, options.delay))
    }
    generationQueue.push(next => {
        //eslint-disable-next-line require-jsdoc
        function done(err) {
            if (err != null) {
                process.exitCode = 1
                console.error(`  ERROR: ${err.message}`)
            }
            if (options.verbose && err == null) {
                console.log("  Done.")
            }

            next()

            if (callback != null) {
                callback(err)
            }
        }

        const appcacheStream = createAppcacheStream(globs, options)
        if (options.verbose) {
            console.log(`Generate: ${options.output}`)
            appcacheStream
                .on("addpath", (e) => console.log(`  Add: ${e.path}`))
                .on("addhash", (e) => console.log(`  Add fingerprint: ${e.digest}`))
        }

        // Pipe to output.
        if (options.output) {
            const dir = path.dirname(path.resolve(options.output))
            mkdir(dir, (err) => {
                if (err != null) {
                    done(err)
                }
                else {
                    appcacheStream
                        .pipe(fs.createWriteStream(options.output))
                        .on("finish", done)
                        .on("error", done)
                }
            })
        }
        else {
            appcacheStream
                .on("end", done)
                .pipe(process.stdout)
        }
    })
}

/**
 * Generate appcache manifest for each change.
 *
 * @param {string[]} globs - The globs which specifies the target files.
 * @param {object} options - The option object.
 * @returns {void}
 * @private
 */
function watch(globs, options) {
    options.delay = 1000

    chokidar
        .watch(globs, {persistent: true, ignoreInitial: true})
        .on("add", () => generate(globs, options))
        .on("unlink", () => generate(globs, options))
        .on("change", () => generate(globs, options))
        .on("error", (err) => console.error(`ERROR: ${err.message}`))
        .on("ready", () => {
            if (options.verbose) {
                console.log(`Be watching ${globs.join(", ")}`)
            }
        })

    // In order to kill by the test harness.
    process.on("message", () => {
        process.exit(0)
    })
}

/**
 * Validate options.
 *
 * @param {string[]} globs - The globs which specifies the target files.
 * @param {object} options - The option object.
 * @returns {boolean} `true` if the option object is valid.
 * @private
 */
function validate(globs, options) {
    let hasError = false

    if (globs == null || globs.length === 0) {
        console.error("ERROR: requires file globs.")
        hasError = true
    }
    if (options.output != null && Array.isArray(options.output)) {
        console.error("ERROR: --output option should not be multiple.")
        hasError = true
    }
    if (options.prefix != null) {
        if (Array.isArray(options.prefix)) {
            console.error("ERROR: --prefix option should not be multiple.")
            hasError = true
        }
        else if (options.prefix[0] !== "/") {
            console.error("ERROR: --prefix option should be started with '/'.")
            hasError = true
        }
    }
    if (options.verbose && !options.output) {
        console.error("ERROR: --verbose option should be used together with --output option.")
        hasError = true
    }
    if (options.watch && !options.output) {
        console.error("ERROR: --watch option should be used together with --output option.")
        hasError = true
    }

    return !hasError
}

//------------------------------------------------------------------------------
// Main
//------------------------------------------------------------------------------

// Parse arguments.
let hasUnknownOptions = false
const args = process.argv.slice(2)
const options = minimist(args, {
    string: OPTIONS.filter(o => o.type === "string").map(o => o.name),
    boolean: OPTIONS.filter(o => o.type === "boolean").map(o => o.name),
    alias: OPTIONS.filter(o => o.alias != null).reduce(mergeAlias, {}),
    unknown: (arg) => {
        if (arg[0] === "-") {
            console.error(`ERROR: ${arg} is unknown option.`)
            hasUnknownOptions = true
        }
    },
})
const globs = options._

// Help/Version.
if (options.help || args.length === 0) {
    printHelp()
    return
}
if (options.version || (args.length === 1 && args[0] === "-v")) {
    printVersion()
    return
}

// Validate.
if (!validate(globs, options) || hasUnknownOptions) {
    process.exitCode = 1
    return
}

// Main.
generate(globs, options, (err) => {
    if (options.watch && err == null) {
        watch(globs, options)
    }
})
<commit_msg>Chore: trivial fix<commit_after>#!/usr/bin/env node

/**
 * @author Toru Nagashima
 * @copyright 2016 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
"use strict"

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const fs = require("fs")
const path = require("path")
const chokidar = require("chokidar")
const minimist = require("minimist")
const mkdir = require("mkdirp")
const createAppcacheStream = require("../lib/generate")
const Queue = require("../lib/queue")

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const OPTIONS = [
  {name: "output", alias: "o", type: "string"},
  {name: "prefix", alias: "p", type: "string"},
  {name: "postfile", alias: "P", type: "string"},
  {name: "help", alias: "h", type: "boolean"},
  {name: "verbose", alias: "v", type: "boolean"},
  {name: "version", alias: "V", type: "boolean"},
  {name: "watch", alias: "w", type: "boolean"},
  {name: "network-star", alias: "networkStar", type: "boolean"},
  {name: "stamp", alias: "s", type: "boolean"},
]
const generationQueue = new Queue()

/**
 * The reduce handler to make alias object.
 *
 * @param {object} obj - The alias object.
 * @param {{name: string, alias: string}} option - The option object.
 * @returns {object} `obj`.
 * @private
 */
function mergeAlias(obj, option) {
    obj[option.alias] = option.name
    return obj
}

/**
 * Print the help text of this CLI command.
 *
 * @returns {void}
 * @private
 */
function printHelp() {
    console.log(`
Usage: appcache-manifest [OPTIONS] [FILES...]

    FILES: File globs that includes into manifest.

Options:
    -o, --output <FILE_PATH>    The path of an output file.
                                Prints to stdout by default.
    -s, --stamp                 Stamps the output with the date/time instead
                                of an md5 hash.
    -p, --prefix <PATH>         The prefix part of paths in cache section.
    -P, --postfile <FILE_PATH>  The path of a postfix file.  The file will be
                                concatinated after generated contents.
    -h, --help                  Prints this text.
    -v, --verbose               Prints detail messages in progress. This option
                                must be used together with '--output' option.
    -V, --version               Prints version number.
    -w, --watch                 This command would be watching target files and
                                directories if --watch option is specified.
                                Then it regenerates the manifest file when those
                                files are added, removed, or changed.
    --network-star              Adds "NETWORK:\n*" after generated contents.

See Also:
    https://github.com/mysticatea/appcache-manifest
`)
}

/**
 * Print the version number of this CLI command.
 *
 * @returns {void}
 * @private
 */
function printVersion() {
    console.log(`v${require("../package.json").version}`)
}

/**
 * Validate options.
 *
 * @param {string[]} globs - The globs which specifies the target files.
 * @param {object} options - The option object.
 * @returns {boolean} `true` if the option object is valid.
 * @private
 */
function validate(globs, options) {
    let hasError = false

    if (globs == null || globs.length === 0) {
        console.error("ERROR: requires file globs.")
        hasError = true
    }
    if (options.output != null && Array.isArray(options.output)) {
        console.error("ERROR: --output option should not be multiple.")
        hasError = true
    }
    if (options.prefix != null) {
        if (Array.isArray(options.prefix)) {
            console.error("ERROR: --prefix option should not be multiple.")
            hasError = true
        }
        else if (options.prefix[0] !== "/") {
            console.error("ERROR: --prefix option should be started with '/'.")
            hasError = true
        }
    }
    if (options.verbose && !options.output) {
        console.error("ERROR: --verbose option should be used together with --output option.")
        hasError = true
    }
    if (options.watch && !options.output) {
        console.error("ERROR: --watch option should be used together with --output option.")
        hasError = true
    }

    return !hasError
}

/**
 * Generate appcache manifest.
 *
 * @param {string[]} globs - The globs which specifies the target files.
 * @param {object} options - The option object.
 * @param {function} callback - The callback function.
 * @returns {void}
 * @private
 */
function generate(globs, options, callback) {
    if (generationQueue.size > 0) {
        // There is a process not started.
        return
    }
    if (typeof options.delay === "number") {
        generationQueue.push(next => setTimeout(next, options.delay))
    }
    generationQueue.push(next => {
        //eslint-disable-next-line require-jsdoc
        function done(err) {
            if (err != null) {
                process.exitCode = 1
                console.error(`  ERROR: ${err.message}`)
            }
            if (options.verbose && err == null) {
                console.log("  Done.")
            }

            next()

            if (callback != null) {
                callback(err)
            }
        }

        const appcacheStream = createAppcacheStream(globs, options)
        if (options.verbose) {
            console.log(`Generate: ${options.output}`)
            appcacheStream
                .on("addpath", (e) => console.log(`  Add: ${e.path}`))
                .on("addhash", (e) => console.log(`  Add fingerprint: ${e.digest}`))
        }

        // Pipe to output.
        if (options.output) {
            const dir = path.dirname(path.resolve(options.output))
            mkdir(dir, (err) => {
                if (err != null) {
                    done(err)
                }
                else {
                    appcacheStream
                        .pipe(fs.createWriteStream(options.output))
                        .on("finish", done)
                        .on("error", done)
                }
            })
        }
        else {
            appcacheStream
                .on("end", done)
                .pipe(process.stdout)
        }
    })
}

/**
 * Generate appcache manifest for each change.
 *
 * @param {string[]} globs - The globs which specifies the target files.
 * @param {object} options - The option object.
 * @returns {void}
 * @private
 */
function watch(globs, options) {
    options.delay = 1000

    chokidar
        .watch(globs, {persistent: true, ignoreInitial: true})
        .on("add", () => generate(globs, options))
        .on("unlink", () => generate(globs, options))
        .on("change", () => generate(globs, options))
        .on("error", (err) => console.error(`ERROR: ${err.message}`))
        .on("ready", () => {
            if (options.verbose) {
                console.log(`Be watching ${globs.join(", ")}`)
            }
        })

    // In order to kill by the test harness.
    process.on("message", () => {
        process.exit(0)
    })
}

//------------------------------------------------------------------------------
// Main
//------------------------------------------------------------------------------

// Parse arguments.
let hasUnknownOptions = false
const args = process.argv.slice(2)
const options = minimist(args, {
    string: OPTIONS.filter(o => o.type === "string").map(o => o.name),
    boolean: OPTIONS.filter(o => o.type === "boolean").map(o => o.name),
    alias: OPTIONS.filter(o => o.alias != null).reduce(mergeAlias, {}),
    unknown: (arg) => {
        if (arg[0] === "-") {
            console.error(`ERROR: ${arg} is unknown option.`)
            hasUnknownOptions = true
        }
    },
})
const globs = options._

// Help/Version.
if (options.help || args.length === 0) {
    printHelp()
    return
}
if (options.version || (args.length === 1 && args[0] === "-v")) {
    printVersion()
    return
}

// Validate.
if (!validate(globs, options) || hasUnknownOptions) {
    process.exitCode = 1
    return
}

// Main.
generate(globs, options, (err) => {
    if (options.watch && err == null) {
        watch(globs, options)
    }
})
<|endoftext|>


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

// toolbar cite
(function (jQuery) {
    return jQuery.widget("IKS.toolbarcite", {
        options: {
            editable: null,
            uuid: "cite",
            link: true,
            image: true,
            dialogOpts: {
                autoOpen: false,
                width: 600,
                height: 'auto',
                title: gettext('Configure citation'),
                modal: true,
                resizable: false,
                draggable: false,
                dialogClass: 'hallocite-dialog',

            },
            butonCssClass: null
        },

        populateToolbar: function (toolbar) {
            var buttonize,
                buttonset,
                button,
                dialog,
                dialogSubmitCb,
                bibEntryStart,
                bibFormatStart,
                bibBeforeStart,
                bibPageStart,
                widget,
                clickedNode,
                citationNode,
                citeSpan,
                range,
                selection,
                _this = widget = this,
                dialogId = "" + this.options.uuid + "-dialog",
                cite_source_table;

            urlInput = jQuery('input[name=url]', dialog).focus(function (e) {
                return this.select();
            });

            dialogSubmitCb = function () {
                var cite_items = jQuery('#selected-cite-source-table .fw-cite-parts-table'),
                    cite_ids = [],
                    cite_prefixes = [],
                    cite_pages = [],
                    bibFormat,
                    bibEntry,
                    bibPage,
                    bibBefore,
                    emptySpaceNode;

                if (0 === cite_items.size()) {
                    alert(gettext('Please select at least one citation source!'));
                    return false;
                }

                cite_items.each(function () {
                    cite_ids.push(jQuery(this).find('.delete').data('id'));
                    cite_pages.push(jQuery(this).find('.fw-cite-page').val());
                    cite_prefixes.push(jQuery(this).find('.fw-cite-text').val());
                });

                bibFormat = jQuery('#citation-style-label').data('style');
                bibEntry = cite_ids.join(',');
                bibPage = cite_pages.join(',,,');
                bibBefore = cite_prefixes.join(',,,');

                if (bibEntry === bibEntryStart
                    && bibBefore === bibBeforeStart
                    && bibPage == bibPageStart
                    && bibFormat == bibFormatStart) {
                    // Nothing has been changed, so we just close the dialog again
                    return true;
                }

                if (citationNode) {
                    emptySpaceNode = document.createTextNode('\u180e');
                    citationNode.parentNode.insertBefore(emptySpaceNode, citationNode.nextSibling);
                    range.selectNode(emptySpaceNode);
                    range.collapse(true);
                    manualEdits.remove(citationNode, false);
                    citationNode = false;
                }

                citeSpan = nodeConverter.createCiteView();
                citeSpan.setAttribute('data-bib-entry', bibEntry);
                citeSpan.setAttribute('data-bib-before', bibBefore);
                citeSpan.setAttribute('data-bib-page', bibPage);
                citeSpan.setAttribute('data-bib-format', bibFormat);

                // Make sure to get out of any track changes node if tracking is disabled.
                range = dom.noTrackIfDisabled(range);
                // Make sure to get out of any citation node.
                range = dom.noCitationOrLinkNode(range);
                // Insert the citation
                manualEdits.insert(citeSpan, range);

                if (emptySpaceNode) {
                    jQuery(emptySpaceNode).remove();
                }

                citationHelpers.formatCitationsInDoc();
                return true;
            };

            //jQuery(document).on('click', '#addBibEntryButton', dialogSubmitCb);
            //dialog.find("form").submit(dialogSubmitCb);
            buttonset = $.Fidus.buttonset.prototype.createButtonset.call(
                this,
                widget.widgetName,
                1
            );

            buttonize = function (type) {
                var id = _this.options.uuid + "-" + type;

                button = jQuery('<button></button>');
                button.makebutton({
                    label: 'Cite',
                    icon: 'icon-quote-right',
                    editable: _this.options.editable,
                    queryState: false,
                    uuid: _this.options.uuid,
                    cssClass: _this.options.buttonCssClass
                });

                button.attr('class', 'fw-button fw-light fw-large fw-square disabled');
                buttonset.append(button);

                function openDialog(event) {
                    var ids;
                    selection = rangy.getSelection();
                    range = selection.getRangeAt(0);

                    clickedNode = range.startContainer;
                    citationNode = jQuery(clickedNode).closest('.citation')[0];
                    bibEntryStart = jQuery(citationNode).attr('data-bib-entry');
                    bibFormatStart = 'autocite';
                    bibBeforeStart = jQuery(citationNode).attr('data-bib-before');
                    bibPageStart = jQuery(citationNode).attr('data-bib-page');

                    var books = '',
                        cited_books = '',
                        cited_ids = [],
                        cited_prefixes,
                        cited_pages;

                    if(citationNode) {
                        cited_ids = bibEntryStart.split(',');
                        cited_prefixes = bibBeforeStart.split(',,,');
                        cited_pages = bibPageStart.split(',,,');
                        bibFormatStart = jQuery(citationNode).attr('data-bib-format');
                    }

                    _.each(BibDB, function(bibs, index) {
                        var this_book = {
                            'id': index,
                            'type': bibs.entry_type,
                            'title': bibs.title || '',
                            'author': bibs.author || bibs.editor || ''
                        };

                        this_book.title = this_book.title.replace(/[{}]/g, '');
                        this_book.author = this_book.author.replace(/[{}]/g, '');
                        books += tmp_citation_book(this_book);

                        var cited_id = _.indexOf(cited_ids, index);
                        if(0 <= cited_id) {
                            this_book.prefix = cited_prefixes[cited_id];
                            this_book.page = cited_pages[cited_id];
                            cited_books += tmp_selected_citation(this_book);
                        }
                    });

                    var submit_button_text = citationNode ? 'Update' : 'Insert';

                    dialog = jQuery(
                        tmp_configure_citation({
                            'dialogId': dialogId,
                            'books': books,
                            'selectedbooks': cited_books,
                            'citeformat': bibFormatStart
                        })
                    );

                    jQuery('body').append(dialog);

                    var diaButtons = {};
                    diaButtons[gettext('Register new source')] = function () {
                        bibliographyHelpers.createBibEntryDialog();
                    };

                    diaButtons[gettext(submit_button_text)] = function () {
                        if(dialogSubmitCb()) {
                            dialog.dialog('close');
                        }
                    };

                    diaButtons[gettext('Cancel')] = function () {
                        dialog.dialog('close');
                    };

                    jQuery('#cite-source-table').bind('update', function () {
                        if (jQuery(this).hasClass('dataTable')) {
                            jQuery(this).dataTable({
                                "bRetrieve": true,
                            });
                        } else {
                            jQuery(this).dataTable({
                                "bPaginate": false,
                                "bLengthChange": false,
                                "bFilter": true,
                                "bInfo": false,
                                "bAutoWidth": false,
                                "oLanguage": {
                                    "sSearch": ''
                                },
                            });
                        }
                        jQuery('#cite-source-table_filter input').attr('placeholder', gettext('Search for Bibliography'));

                        var autocomplete_tags = [];
                        jQuery('#cite-source-table .fw-searchable').each(function() {
                            autocomplete_tags.push(this.innerText);
                        });
                        autocomplete_tags = _.uniq(autocomplete_tags);
                        jQuery("#cite-source-table_filter input").autocomplete({
                            source: autocomplete_tags
                        });
                    });

                    //widget.options.editable.keepActivated(true);
                    dialog.dialog({
                        draggable : false,
                        resizable : false,
                        top: 10,
                        width : 820,
                        height : 540,
                        modal : true,
                        buttons : diaButtons,
                        create : function () {
                            var $the_dialog = jQuery(this).closest(".ui-dialog");
                            $the_dialog.find(".ui-dialog-buttonset .ui-button:eq(0)").addClass("fw-button fw-light fw-add-button");
                            $the_dialog.find(".ui-dialog-buttonset .ui-button:eq(1)").addClass("fw-button fw-dark");
                            $the_dialog.find(".ui-dialog-buttonset .ui-button:eq(2)").addClass("fw-button fw-orange");

                            jQuery('#cite-source-table').trigger('update');

                            $.addDropdownBox(jQuery('#citation-style-label'), jQuery('#citation-style-pulldown'));
                            jQuery(document).on('click','#citation-style-pulldown .fw-pulldown-item', function () {
                                jQuery('#citation-style-label label').html(jQuery(this).html());
                                jQuery('#citation-style-label').attr('data-style', jQuery(this).data('style'));
                            });

                            jQuery(document).on('click', '#add-cite-book', function() {
                                var selected_sources = jQuery('#cite-source-table .fw-checkable.checked'),
                                    selected_books = [];
                                selected_sources.each(function() {
                                    var id = jQuery(this).data('id');
                                    if(jQuery('#selected-source-' + id).size()) {
                                        return;
                                    }
                                    selected_books.push({
                                        'id': id,
                                        'type': jQuery(this).data('type'),
                                        'title': jQuery(this).data('title'),
                                        'author': jQuery(this).data('author')
                                    });
                                });
                                selected_sources.removeClass('checked');
                                citationHelpers.appendToCitedBooks(selected_books);
                            });

                            jQuery(document).on('click', '.selected-source .delete', function() {
                                var source_wrapper_id = '#selected-source-' + jQuery(this).data('id');
                                jQuery(source_wrapper_id).remove();
                            });
                        },

                        close : function() {
                            jQuery('label', button).removeClass('ui-state-active');
                            if (citeSpan) {
                                range.selectNode(citeSpan);
                            } else if (citationNode) {
                                range.selectNode(citationNode);
                            }
                            range.collapse();
                            selection.removeAllRanges();
                            selection.addRange(range);
                            widget.options.editable.element.focus();
                            widget.options.editable.keepActivated(false);
                            jQuery(this).dialog('destroy').remove();
                        }
                    });

                    jQuery('.fw-checkable').bind('click', function() { $.setCheckableLabel(jQuery(this)); });
                }

                function enableCitations() {
                    button.bind("click", openDialog);
                    button.removeClass('disabled');

                    citationHelpers.bindEvents(openDialog);
                    // Enable the exporter and citation style menus.
                    jQuery('.citationstyle-menu, .exporter-menu').each(function () {
                        jQuery.addDropdownBox(jQuery(this), jQuery(this).siblings('.fw-pulldown'));
                        jQuery(this).removeClass('disabled');
                        jQuery(this).removeClass('header-nav-item-disabled');
                        jQuery(this).addClass('header-nav-item');
                    });
                    citationHelpers.formatCitationsInDoc();
                    editorHelpers.documentHasChanged();
                }

                if(jQuery.isEmptyObject(BibDB)) {
                    //Enable the various bibliography options once the bibliography data has been loaded.
                    jQuery('body').bind("bibliography_ready", function (event) {
                        //Enable the citation dialog now that the bibliography database is available.
                        enableCitations();
                    });
                } else {
                    enableCitations();
                }

                return _this.element.bind("keyup paste change mouseup", function (event) {
                    var nodeName, start;
                    start = jQuery(widget.options.editable.getSelection().startContainer);
                    nodeName = start.prop('nodeName') ? start.prop('nodeName') : start.parent().prop('nodeName');
                    if (nodeName && nodeName.toUpperCase() === "A") {
                        jQuery('label', button).addClass('ui-state-active');
                        return;
                    }
                    return jQuery('label', button).removeClass('ui-state-active');
                });
            };

            if (this.options.link) {
                buttonize("A");
            }
            if (this.options.link) {
                buttonset.buttonset();
                toolbar.append(buttonset);
            }
        },

        _init: function () {}
    });
})(jQuery);<commit_msg>add citation removal button<commit_after>/**
 * @copyright This file is part of Fidus Writer.
 *
 * Copyright (C) 2013 Takuto Kojima, Johannes Wilm.
 *
 * @license This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 */

// toolbar cite
(function (jQuery) {
    return jQuery.widget("IKS.toolbarcite", {
        options: {
            editable: null,
            uuid: "cite",
            link: true,
            image: true,
            dialogOpts: {
                autoOpen: false,
                width: 600,
                height: 'auto',
                title: gettext('Configure citation'),
                modal: true,
                resizable: false,
                draggable: false,
                dialogClass: 'hallocite-dialog',

            },
            butonCssClass: null
        },

        populateToolbar: function (toolbar) {
            var buttonize,
                buttonset,
                button,
                dialog,
                dialogSubmitCb,
                bibEntryStart,
                bibFormatStart,
                bibBeforeStart,
                bibPageStart,
                widget,
                clickedNode,
                citationNode,
                citeSpan,
                range,
                selection,
                _this = widget = this,
                dialogId = "" + this.options.uuid + "-dialog",
                cite_source_table;

            urlInput = jQuery('input[name=url]', dialog).focus(function (e) {
                return this.select();
            });

            dialogSubmitCb = function () {
                var cite_items = jQuery('#selected-cite-source-table .fw-cite-parts-table'),
                    cite_ids = [],
                    cite_prefixes = [],
                    cite_pages = [],
                    bibFormat,
                    bibEntry,
                    bibPage,
                    bibBefore,
                    emptySpaceNode;

                if (0 === cite_items.size()) {
                    alert(gettext('Please select at least one citation source!'));
                    return false;
                }

                cite_items.each(function () {
                    cite_ids.push(jQuery(this).find('.delete').data('id'));
                    cite_pages.push(jQuery(this).find('.fw-cite-page').val());
                    cite_prefixes.push(jQuery(this).find('.fw-cite-text').val());
                });

                bibFormat = jQuery('#citation-style-label').data('style');
                bibEntry = cite_ids.join(',');
                bibPage = cite_pages.join(',,,');
                bibBefore = cite_prefixes.join(',,,');

                if (bibEntry === bibEntryStart
                    && bibBefore === bibBeforeStart
                    && bibPage == bibPageStart
                    && bibFormat == bibFormatStart) {
                    // Nothing has been changed, so we just close the dialog again
                    return true;
                }

                if (citationNode) {
                    emptySpaceNode = document.createTextNode('\u180e');
                    citationNode.parentNode.insertBefore(emptySpaceNode, citationNode.nextSibling);
                    range.selectNode(emptySpaceNode);
                    range.collapse(true);
                    manualEdits.remove(citationNode, false);
                    citationNode = false;
                }

                citeSpan = nodeConverter.createCiteView();
                citeSpan.setAttribute('data-bib-entry', bibEntry);
                citeSpan.setAttribute('data-bib-before', bibBefore);
                citeSpan.setAttribute('data-bib-page', bibPage);
                citeSpan.setAttribute('data-bib-format', bibFormat);

                // Make sure to get out of any track changes node if tracking is disabled.
                range = dom.noTrackIfDisabled(range);
                // Make sure to get out of any citation node.
                range = dom.noCitationOrLinkNode(range);
                // Insert the citation
                manualEdits.insert(citeSpan, range);

                if (emptySpaceNode) {
                    jQuery(emptySpaceNode).remove();
                }

                citationHelpers.formatCitationsInDoc();
                return true;
            };

            //jQuery(document).on('click', '#addBibEntryButton', dialogSubmitCb);
            //dialog.find("form").submit(dialogSubmitCb);
            buttonset = $.Fidus.buttonset.prototype.createButtonset.call(
                this,
                widget.widgetName,
                1
            );

            buttonize = function (type) {
                var id = _this.options.uuid + "-" + type;

                button = jQuery('<button></button>');
                button.makebutton({
                    label: 'Cite',
                    icon: 'icon-quote-right',
                    editable: _this.options.editable,
                    queryState: false,
                    uuid: _this.options.uuid,
                    cssClass: _this.options.buttonCssClass
                });

                button.attr('class', 'fw-button fw-light fw-large fw-square disabled');
                buttonset.append(button);

                function openDialog(event) {
                    var ids;
                    selection = rangy.getSelection();
                    range = selection.getRangeAt(0);

                    clickedNode = range.startContainer;
                    citationNode = jQuery(clickedNode).closest('.citation')[0];
                    bibEntryStart = jQuery(citationNode).attr('data-bib-entry');
                    bibFormatStart = 'autocite';
                    bibBeforeStart = jQuery(citationNode).attr('data-bib-before');
                    bibPageStart = jQuery(citationNode).attr('data-bib-page');

                    var books = '',
                        cited_books = '',
                        cited_ids = [],
                        cited_prefixes,
                        cited_pages;

                    if(citationNode) {
                        cited_ids = bibEntryStart.split(',');
                        cited_prefixes = bibBeforeStart.split(',,,');
                        cited_pages = bibPageStart.split(',,,');
                        bibFormatStart = jQuery(citationNode).attr('data-bib-format');
                    }

                    _.each(BibDB, function(bibs, index) {
                        var this_book = {
                            'id': index,
                            'type': bibs.entry_type,
                            'title': bibs.title || '',
                            'author': bibs.author || bibs.editor || ''
                        };

                        this_book.title = this_book.title.replace(/[{}]/g, '');
                        this_book.author = this_book.author.replace(/[{}]/g, '');
                        books += tmp_citation_book(this_book);

                        var cited_id = _.indexOf(cited_ids, index);
                        if(0 <= cited_id) {
                            this_book.prefix = cited_prefixes[cited_id];
                            this_book.page = cited_pages[cited_id];
                            cited_books += tmp_selected_citation(this_book);
                        }
                    });

                    dialog = jQuery(
                        tmp_configure_citation({
                            'dialogId': dialogId,
                            'books': books,
                            'selectedbooks': cited_books,
                            'citeformat': bibFormatStart
                        })
                    );

                    jQuery('body').append(dialog);

                    var diaButtons = [];
                    diaButtons.push({text: gettext('Register new source'), click: function () {
                        bibliographyHelpers.createBibEntryDialog();
                    }, class: 'fw-button fw-light fw-add-button'});

                    if (citationNode) {
                        diaButtons.push({text: gettext('Remove'), click: function () {
                            citationNode.parentNode.removeChild(citationNode);
                            citationNode = false;
                            citeSpan = false;
                            dialog.dialog('close');
                        }, class: 'fw-button fw-orange'});
                    }

                    var submit_button_text = citationNode ? 'Update' : 'Insert';

                    diaButtons.push({text: gettext(submit_button_text), click: function () {
                        if(dialogSubmitCb()) {
                            dialog.dialog('close');
                        }
                    }, class: "fw-button fw-dark"});

                    diaButtons.push({text: gettext('Cancel'), click: function () {
                        dialog.dialog('close');
                    }, class: 'fw-button fw-orange'});

                    jQuery('#cite-source-table').bind('update', function () {
                        if (jQuery(this).hasClass('dataTable')) {
                            jQuery(this).dataTable({
                                "bRetrieve": true,
                            });
                        } else {
                            jQuery(this).dataTable({
                                "bPaginate": false,
                                "bLengthChange": false,
                                "bFilter": true,
                                "bInfo": false,
                                "bAutoWidth": false,
                                "oLanguage": {
                                    "sSearch": ''
                                },
                            });
                        }
                        jQuery('#cite-source-table_filter input').attr('placeholder', gettext('Search for Bibliography'));

                        var autocomplete_tags = [];
                        jQuery('#cite-source-table .fw-searchable').each(function() {
                            autocomplete_tags.push(this.innerText);
                        });
                        autocomplete_tags = _.uniq(autocomplete_tags);
                        jQuery("#cite-source-table_filter input").autocomplete({
                            source: autocomplete_tags
                        });
                    });

                    //widget.options.editable.keepActivated(true);
                    dialog.dialog({
                        draggable : false,
                        resizable : false,
                        top: 10,
                        width : 820,
                        height : 540,
                        modal : true,
                        buttons : diaButtons,
                        create : function () {
                            var $the_dialog = jQuery(this).closest(".ui-dialog");

                            jQuery('#cite-source-table').trigger('update');

                            $.addDropdownBox(jQuery('#citation-style-label'), jQuery('#citation-style-pulldown'));
                            jQuery(document).on('click','#citation-style-pulldown .fw-pulldown-item', function () {
                                jQuery('#citation-style-label label').html(jQuery(this).html());
                                jQuery('#citation-style-label').attr('data-style', jQuery(this).data('style'));
                            });

                            jQuery(document).on('click', '#add-cite-book', function() {
                                var selected_sources = jQuery('#cite-source-table .fw-checkable.checked'),
                                    selected_books = [];
                                selected_sources.each(function() {
                                    var id = jQuery(this).data('id');
                                    if(jQuery('#selected-source-' + id).size()) {
                                        return;
                                    }
                                    selected_books.push({
                                        'id': id,
                                        'type': jQuery(this).data('type'),
                                        'title': jQuery(this).data('title'),
                                        'author': jQuery(this).data('author')
                                    });
                                });
                                selected_sources.removeClass('checked');
                                citationHelpers.appendToCitedBooks(selected_books);
                            });

                            jQuery(document).on('click', '.selected-source .delete', function() {
                                var source_wrapper_id = '#selected-source-' + jQuery(this).data('id');
                                jQuery(source_wrapper_id).remove();
                            });
                        },

                        close : function() {
                            jQuery('label', button).removeClass('ui-state-active');
                            if (citeSpan) {
                                range.selectNode(citeSpan);
                            } else if (citationNode) {
                                range.selectNode(citationNode);
                            }
                            range.collapse();
                            selection.removeAllRanges();
                            selection.addRange(range);
                            widget.options.editable.element.focus();
                            widget.options.editable.keepActivated(false);
                            jQuery(this).dialog('destroy').remove();
                        }
                    });

                    jQuery('.fw-checkable').bind('click', function() { $.setCheckableLabel(jQuery(this)); });
                }

                function enableCitations() {
                    button.bind("click", openDialog);
                    button.removeClass('disabled');

                    citationHelpers.bindEvents(openDialog);
                    // Enable the exporter and citation style menus.
                    jQuery('.citationstyle-menu, .exporter-menu').each(function () {
                        jQuery.addDropdownBox(jQuery(this), jQuery(this).siblings('.fw-pulldown'));
                        jQuery(this).removeClass('disabled');
                        jQuery(this).removeClass('header-nav-item-disabled');
                        jQuery(this).addClass('header-nav-item');
                    });
                    citationHelpers.formatCitationsInDoc();
                    editorHelpers.documentHasChanged();
                }

                if(jQuery.isEmptyObject(BibDB)) {
                    //Enable the various bibliography options once the bibliography data has been loaded.
                    jQuery('body').bind("bibliography_ready", function (event) {
                        //Enable the citation dialog now that the bibliography database is available.
                        enableCitations();
                    });
                } else {
                    enableCitations();
                }

                return _this.element.bind("keyup paste change mouseup", function (event) {
                    var nodeName, start;
                    start = jQuery(widget.options.editable.getSelection().startContainer);
                    nodeName = start.prop('nodeName') ? start.prop('nodeName') : start.parent().prop('nodeName');
                    if (nodeName && nodeName.toUpperCase() === "A") {
                        jQuery('label', button).addClass('ui-state-active');
                        return;
                    }
                    return jQuery('label', button).removeClass('ui-state-active');
                });
            };

            if (this.options.link) {
                buttonize("A");
            }
            if (this.options.link) {
                buttonset.buttonset();
                toolbar.append(buttonset);
            }
        },

        _init: function () {}
    });
})(jQuery);<|endoftext|>


<commit_before>// https://developer.chrome.com/extensions/manifest
const chrome = {
  required: [
    ['manifest_version', {value: 2}],
    'name',
    'version'
  ],
  optional: [
    'author',
    'automation',
    ['background', {recommend: {persistent: false}}],
    'background_page',
    'chrome_settings_overrides',
    'chrome_ui_overrides',
    'chrome_url_overrides',
    'commands',
    'content_capabilities',
    'content_scripts',
    'content_security_policy',
    'converted_from_user_script',
    'current_locale',
    'devtools_page',
    'event_rules',
    'externally_connectable',
    'file_browser_handlers',
    'file_system_provider_capabilities',
    'homepage_url',
    'import',
    'incognito',
    'input_components',
    'key',
    'minimum_chrome_version',
    'nacl_modules',
    'oauth2',
    'offline_enabled',
    'omnibox',
    'optional_permissions',
    'options_page',
    'options_ui',
    ['permissions', {
      supportValues: [
        'activeTab',
        'alarms',
        'background',
        'bookmarks',
        'browsingData',
        'certificateProvider',
        'clipboardRead',
        'clipboardWrite',
        'contentSettings',
        'contextMenus',
        'cookies',
        'debugger',
        'declarativeContent',
        'declarativeWebRequest',
        'desktopCapture',
        'displaySource',
        'dns',
        'documentScan',
        'downloads',
        'enterprise.deviceAttributes',
        'enterprise.platformKeys',
        'experimental',
        'fileBrowserHandler',
        'fileSystemProvider',
        'fontSettings',
        'gcm',
        'geolocation',
        'history',
        'identity',
        'idle',
        'idltest',
        'management',
        'nativeMessaging',
        'networking.config',
        'notifications',
        'pageCapture',
        'platformKeys',
        'power',
        'printerProvider',
        'privacy',
        'processes',
        'proxy',
        'sessions',
        'signedInDevices',
        'storage',
        'system.cpu',
        'system.display',
        'system.memory',
        'system.storage',
        'tabCapture',
        'tabs',
        'topSites',
        'tts',
        'ttsEngine',
        'unlimitedStorage',
        'vpnProvider',
        'wallpaper',
        'webNavigation',
        'webRequest',
        'webRequestBlocking'
      ]
    }],
    'platforms',
    'plugins',
    'requirements',
    'sandbox',
    'short_name',
    'signature',
    'spellcheck',
    'storage',
    'system_indicator',
    'tts_engine',
    'update_url',
    'version_name',
    'web_accessible_resources'
  ],
  recommend: ['default_locale', 'description', 'icons'],
  pickOneOrNone: ['browser_action', 'page_action']
}

// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json
const firefox = {
  required: [
    'applications',
    'version',
    'name',
    ['manifest_version', {value: 2}]
  ],
  optional: [
    'author',
    ['background', {unsupportProps: ['persistent']}],
    'browser_action',
    'chrome_url_overrides',
    'commands',
    'content_scripts',
    'content_security_policy',
    'default_locale',
    'description',
    'developer',
    'homepage_url',
    'icons',
    'omnibox',
    ['options_ui', {unsupportProps: ['chrome_style']}],
    'page_action',
    ['permissions', {
      supportValues: [
        'activeTab',
        'alarms',
        'bookmarks',
        'browsingData',
        'contextMenus',
        'contextualIdentities',
        'cookies',
        'downloads',
        'downloads.open',
        'history',
        'identity',
        'idle',
        'management',
        'nativeMessaging',
        'notifications',
        'sessions',
        'storage',
        'tabs',
        'topSites',
        'webNavigation',
        'webRequest',
        'webRequestBlocking'
      ]
    }],
    'short_name',
    'sidebar_action',
    'web_accessible_resources'
  ],
  recommend: 'inherit',
  pickOneOrNone: 'inherit'
}

// https://docs.microsoft.com/en-us/microsoft-edge/extensions/api-support/supported-manifest-keys

const edge = {
  required: ['author', 'name', 'version'],
  optional: [
    'background',
    'content_scripts',
    'content_security_policy',
    'key',
    'options_page',
    ['permissions', {
      supportValues: [
        'contextMenus',
        'cookies',
        'geolocation',
        'idle',
        'storage',
        'tabs',
        'unlimitedStorage',
        'webNavigation',
        'webRequest',
        'webRequestBlocking'
      ]
    }],
    'short_name',
    'web_accessible_resources'
  ],
  recommend: ['description', 'manifest_version', 'icons'],
  pickOneOrNone: 'inherit'
}

module.exports = {chrome, firefox, edge}
<commit_msg>Edge support default_locale and minimum_edge_version but documented...<commit_after>// https://developer.chrome.com/extensions/manifest
const chrome = {
  required: [
    ['manifest_version', {value: 2}],
    'name',
    'version'
  ],
  optional: [
    'author',
    'automation',
    ['background', {recommend: {persistent: false}}],
    'background_page',
    'chrome_settings_overrides',
    'chrome_ui_overrides',
    'chrome_url_overrides',
    'commands',
    'content_capabilities',
    'content_scripts',
    'content_security_policy',
    'converted_from_user_script',
    'current_locale',
    'devtools_page',
    'event_rules',
    'externally_connectable',
    'file_browser_handlers',
    'file_system_provider_capabilities',
    'homepage_url',
    'import',
    'incognito',
    'input_components',
    'key',
    'minimum_chrome_version',
    'nacl_modules',
    'oauth2',
    'offline_enabled',
    'omnibox',
    'optional_permissions',
    'options_page',
    'options_ui',
    ['permissions', {
      supportValues: [
        'activeTab',
        'alarms',
        'background',
        'bookmarks',
        'browsingData',
        'certificateProvider',
        'clipboardRead',
        'clipboardWrite',
        'contentSettings',
        'contextMenus',
        'cookies',
        'debugger',
        'declarativeContent',
        'declarativeWebRequest',
        'desktopCapture',
        'displaySource',
        'dns',
        'documentScan',
        'downloads',
        'enterprise.deviceAttributes',
        'enterprise.platformKeys',
        'experimental',
        'fileBrowserHandler',
        'fileSystemProvider',
        'fontSettings',
        'gcm',
        'geolocation',
        'history',
        'identity',
        'idle',
        'idltest',
        'management',
        'nativeMessaging',
        'networking.config',
        'notifications',
        'pageCapture',
        'platformKeys',
        'power',
        'printerProvider',
        'privacy',
        'processes',
        'proxy',
        'sessions',
        'signedInDevices',
        'storage',
        'system.cpu',
        'system.display',
        'system.memory',
        'system.storage',
        'tabCapture',
        'tabs',
        'topSites',
        'tts',
        'ttsEngine',
        'unlimitedStorage',
        'vpnProvider',
        'wallpaper',
        'webNavigation',
        'webRequest',
        'webRequestBlocking'
      ]
    }],
    'platforms',
    'plugins',
    'requirements',
    'sandbox',
    'short_name',
    'signature',
    'spellcheck',
    'storage',
    'system_indicator',
    'tts_engine',
    'update_url',
    'version_name',
    'web_accessible_resources'
  ],
  recommend: ['default_locale', 'description', 'icons'],
  pickOneOrNone: ['browser_action', 'page_action']
}

// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json
const firefox = {
  required: [
    'applications',
    'version',
    'name',
    ['manifest_version', {value: 2}]
  ],
  optional: [
    'author',
    ['background', {unsupportProps: ['persistent']}],
    'browser_action',
    'chrome_url_overrides',
    'commands',
    'content_scripts',
    'content_security_policy',
    'default_locale',
    'description',
    'developer',
    'homepage_url',
    'icons',
    'omnibox',
    ['options_ui', {unsupportProps: ['chrome_style']}],
    'page_action',
    ['permissions', {
      supportValues: [
        'activeTab',
        'alarms',
        'bookmarks',
        'browsingData',
        'contextMenus',
        'contextualIdentities',
        'cookies',
        'downloads',
        'downloads.open',
        'history',
        'identity',
        'idle',
        'management',
        'nativeMessaging',
        'notifications',
        'sessions',
        'storage',
        'tabs',
        'topSites',
        'webNavigation',
        'webRequest',
        'webRequestBlocking'
      ]
    }],
    'short_name',
    'sidebar_action',
    'web_accessible_resources'
  ],
  recommend: 'inherit',
  pickOneOrNone: 'inherit'
}

// https://docs.microsoft.com/en-us/microsoft-edge/extensions/api-support/supported-manifest-keys

const edge = {
  required: ['author', 'name', 'version'],
  optional: [
    'background',
    'content_scripts',
    'content_security_policy',
    'default_locale',
    'minimum_edge_version',
    'key',
    'options_page',
    ['permissions', {
      supportValues: [
        'contextMenus',
        'cookies',
        'geolocation',
        'idle',
        'storage',
        'tabs',
        'unlimitedStorage',
        'webNavigation',
        'webRequest',
        'webRequestBlocking'
      ]
    }],
    'short_name',
    'web_accessible_resources'
  ],
  recommend: ['description', 'manifest_version', 'icons'],
  pickOneOrNone: 'inherit'
}

module.exports = {chrome, firefox, edge}
<|endoftext|>


<commit_before>const { create, all } = require('../../../main/es5/index.js')
const workerpool = require('workerpool')
const math = create(all)

// disable the import function so the math.js instance cannot be changed
function noImport () {
  throw new Error('function import is disabled.')
}
math.import({ import: noImport }, { override: true })

/**
 * Evaluate an expression
 * @param {string} expr
 * @return {string} result
 */
function evaluate (expr) {
  const ans = math.evaluate(expr)
  return math.format(ans)
}

// create a worker and register public functions
workerpool.worker({
  evaluate: evaluate
})
<commit_msg>Simplify require url in math_worker example<commit_after>const { create, all } = require('../../..')
const workerpool = require('workerpool')
const math = create(all)

// disable the import function so the math.js instance cannot be changed
function noImport () {
  throw new Error('function import is disabled.')
}
math.import({ import: noImport }, { override: true })

/**
 * Evaluate an expression
 * @param {string} expr
 * @return {string} result
 */
function evaluate (expr) {
  const ans = math.evaluate(expr)
  return math.format(ans)
}

// create a worker and register public functions
workerpool.worker({
  evaluate: evaluate
})
<|endoftext|>


<commit_before>'use strict';

const angular = require('angular');

import { ACCOUNT_SERVICE, CONFIRMATION_MODAL_SERVICE, LOAD_BALANCER_WRITE_SERVICE } from '@spinnaker/core';

module.exports = angular.module('spinnaker.loadBalancer.openstack.details.controller', [
    require('@uirouter/angularjs').default,
    ACCOUNT_SERVICE,
    CONFIRMATION_MODAL_SERVICE,
    LOAD_BALANCER_WRITE_SERVICE,
  ])
  .controller('openstackLoadBalancerDetailsController', function ($scope, $state, $uibModal, loadBalancer, app,
                                                                  confirmationModalService, accountService, loadBalancerWriter, subnetReader, $q) {

      let application = app;

      $scope.state = {
        loading: true
      };

      function extractLoadBalancer() {
        let appLoadBalancer = app.loadBalancers.data.find(function (test) {
          return test.name === loadBalancer.name &&
            test.region === loadBalancer.region &&
            test.account === loadBalancer.accountId;
        });

        if (appLoadBalancer) {
          $scope.loadBalancer = appLoadBalancer;
          $scope.state.loading = false;

          $scope.lbLinks = [];

          angular.forEach($scope.loadBalancer.listeners, (value) => {
            if (value.externalProtocol.substring(0, 4).toLowerCase() === 'http') {
              $scope.lbLinks.push(value.externalProtocol.toLowerCase() + '://' + $scope.loadBalancer.ip + ':' + value.externalPort + '/');
            }
          });

        } else {
          autoClose();
        }

        return $q.when(null);
      }

      function autoClose() {
        if ($scope.$$destroyed) {
          return;
        }
        $state.params.allowModalToStayOpen = true;
        $state.go('^', null, {location: 'replace'});
      }

      extractLoadBalancer().then(() => {
        // If the user navigates away from the view before the initial extractLoadBalancer call completes,
        // do not bother subscribing to the refresh
        if (!$scope.$$destroyed) {
          app.loadBalancers.onRefresh($scope, extractLoadBalancer);
        }
      });

      this.editLoadBalancer = function editLoadBalancer() {
        $uibModal.open({
          templateUrl: require('../configure/wizard/editWizard.html'),
          controller: 'openstackUpsertLoadBalancerController as ctrl',
          size: 'lg',
          resolve: {
            application: function() { return application; },
            loadBalancer: function() { return angular.copy($scope.loadBalancer); },
            isNew: function() { return false; }
          }
        });
      };

      this.deleteLoadBalancer = function deleteLoadBalancer() {
        if ($scope.loadBalancer.instances && $scope.loadBalancer.instances.length) {
          return;
        }

        const taskMonitor = {
          application: application,
          title: 'Deleting ' + loadBalancer.name,
        };

        const command = {
          cloudProvider: 'openstack',
          loadBalancerName: $scope.loadBalancer.name,
          id: $scope.loadBalancer.name,
          region: $scope.loadBalancer.region,
          account: $scope.loadBalancer.account,
          credentials: $scope.loadBalancer.account,
        };

        const submitMethod = () => loadBalancerWriter.deleteLoadBalancer(command, application);

        confirmationModalService.confirm({
          header: 'Really delete ' + loadBalancer.name + '?',
          buttonText: 'Delete ' + loadBalancer.name,
          provider: 'openstack',
          account: loadBalancer.accountId,
          applicationName: application.name,
          taskMonitorConfig: taskMonitor,
          submitMethod: submitMethod
        });
      };
    }
  );
<commit_msg>fix(provider/openstack) - Pass loadbalancer UI to delete function. (#4408)<commit_after>'use strict';

const angular = require('angular');

import { ACCOUNT_SERVICE, CONFIRMATION_MODAL_SERVICE, LOAD_BALANCER_WRITE_SERVICE } from '@spinnaker/core';

module.exports = angular.module('spinnaker.loadBalancer.openstack.details.controller', [
    require('@uirouter/angularjs').default,
    ACCOUNT_SERVICE,
    CONFIRMATION_MODAL_SERVICE,
    LOAD_BALANCER_WRITE_SERVICE,
  ])
  .controller('openstackLoadBalancerDetailsController', function ($scope, $state, $uibModal, loadBalancer, app,
                                                                  confirmationModalService, accountService, loadBalancerWriter, subnetReader, $q) {

      let application = app;

      $scope.state = {
        loading: true
      };

      function extractLoadBalancer() {
        let appLoadBalancer = app.loadBalancers.data.find(function (test) {
          return test.name === loadBalancer.name &&
            test.region === loadBalancer.region &&
            test.account === loadBalancer.accountId;
        });

        if (appLoadBalancer) {
          $scope.loadBalancer = appLoadBalancer;
          $scope.state.loading = false;

          $scope.lbLinks = [];

          angular.forEach($scope.loadBalancer.listeners, (value) => {
            if (value.externalProtocol.substring(0, 4).toLowerCase() === 'http') {
              $scope.lbLinks.push(value.externalProtocol.toLowerCase() + '://' + $scope.loadBalancer.ip + ':' + value.externalPort + '/');
            }
          });

        } else {
          autoClose();
        }

        return $q.when(null);
      }

      function autoClose() {
        if ($scope.$$destroyed) {
          return;
        }
        $state.params.allowModalToStayOpen = true;
        $state.go('^', null, {location: 'replace'});
      }

      extractLoadBalancer().then(() => {
        // If the user navigates away from the view before the initial extractLoadBalancer call completes,
        // do not bother subscribing to the refresh
        if (!$scope.$$destroyed) {
          app.loadBalancers.onRefresh($scope, extractLoadBalancer);
        }
      });

      this.editLoadBalancer = function editLoadBalancer() {
        $uibModal.open({
          templateUrl: require('../configure/wizard/editWizard.html'),
          controller: 'openstackUpsertLoadBalancerController as ctrl',
          size: 'lg',
          resolve: {
            application: function() { return application; },
            loadBalancer: function() { return angular.copy($scope.loadBalancer); },
            isNew: function() { return false; }
          }
        });
      };

      this.deleteLoadBalancer = function deleteLoadBalancer() {
        if ($scope.loadBalancer.instances && $scope.loadBalancer.instances.length) {
          return;
        }

        const taskMonitor = {
          application: application,
          title: 'Deleting ' + loadBalancer.name,
        };

        const command = {
          cloudProvider: 'openstack',
          loadBalancerName: $scope.loadBalancer.name,
          id: $scope.loadBalancer.id,
          region: $scope.loadBalancer.region,
          account: $scope.loadBalancer.account,
          credentials: $scope.loadBalancer.account,
        };

        const submitMethod = () => loadBalancerWriter.deleteLoadBalancer(command, application);

        confirmationModalService.confirm({
          header: 'Really delete ' + loadBalancer.name + '?',
          buttonText: 'Delete ' + loadBalancer.name,
          provider: 'openstack',
          account: loadBalancer.accountId,
          applicationName: application.name,
          taskMonitorConfig: taskMonitor,
          submitMethod: submitMethod
        });
      };
    }
  );
<|endoftext|>