text:
/* @flow */
/* eslint-env node */

/**
 *
 *  Copyright 2017 PulseShift GmbH. All rights reserved.
 *
 *  Licensed under the MIT License.
 *
 *  This file makes use of new JavaScript features.
 *  Babel handles this without us having to do anything. It just works.
 *  You can read more about the new JavaScript features here:
 *  https://babeljs.io/docs/learn-es2015/
 *
 */

import pkg from './package.json'
import gulp from 'gulp'
import gutil from 'gulp-util'
import gulpif from 'gulp-if'
import rename from 'gulp-rename'
import plumber from 'gulp-plumber'
import babel from 'gulp-babel'
import uglify from 'gulp-uglify'
import htmlmin from 'gulp-htmlmin'
import prettydata from 'gulp-pretty-data'
import imagemin from 'gulp-imagemin'
import cleanCSS from 'gulp-clean-css'
import less from 'gulp-less'
import tap from 'gulp-tap'
import order from 'gulp-order'
import touch from 'gulp-touch-cmd'
import sourcemaps from 'gulp-sourcemaps'
import ui5preload from 'gulp-ui5-preload'
import mainNpmFiles from 'gulp-main-npm-files'
import ui5Bust from 'ui5-cache-buster'
import {ui5Build, ui5CompileLessLib} from 'ui5-lib-util'
import browserify from 'browserify'
import source from 'vinyl-source-stream'
import buffer from 'vinyl-buffer'
import babelify from 'babelify'
import LessAutoprefix from 'less-plugin-autoprefix'
import ora from 'ora'
import del from 'del'
import path from 'path'
import fs from 'fs'
import handlebars from 'handlebars'
import gulpHandlebars from 'gulp-handlebars-html'
// import favicons from 'gulp-favicons'
import gzip from 'gulp-gzip'
import brotli from 'gulp-brotli'
import yargs from 'yargs'
import {hideBin} from 'yargs/helpers'

/*
 * SETUP SCRIPT RUNTIME ENVIRONMENT
 */

// parse program commands
  const options = yargs(hideBin(process.argv))
  .option('silent', {
    type: 'boolean',
    default: true,
    description: 'Run w/ reduced logging',
  })
  .option('local', {
    type: 'boolean',
    default: true,
    description: 'Start an own HTTP server, serving the build output',
  }).argv

const hdlbars = gulpHandlebars(handlebars)
const spinner = ora()

// register handlebars helper function
handlebars.registerHelper('secure', function(str) {
  return new handlebars.SafeString(str)
})

// switch between gulp log and custom log
spinner.enabled = options.silent
spinner.print = sText =>
  spinner.stopAndPersist({
    text: sText
  })

/*
 * CONFIGURATION
 */

const IS_DEV_MODE = process.env.NODE_ENV === 'development'

// path to source directory
const SRC = 'src'
// path to development directory
const DEV = 'dev'
// path to ditribution direcory
const DIST = 'dist'
// path to ui5 repository
const UI5 = IS_DEV_MODE ? 'ui5' : `${DIST}/ui5`

// create unique hash for current favicon image
// const isFaviconDefined =
//   pkg.favicon && pkg.favicon.src.length > 0 && fs.existsSync(pkg.favicon.src)
// const FAVICON_HASH = isFaviconDefined //TODO:get rid? Favicons NOT in root seesm to be an anit pattern? https://stackoverflow.com/questions/5273103/any-problems-with-favicons-in-a-subfolder
//   ? require('loader-utils')
//       .getHashDigest(
//         Buffer.concat([fs.readFileSync(pkg.favicon.src)]),
//         'sha512',
//         'base62',
//         8
//       )
//       .toLowerCase()
//   : ''

// read build settings
const BUILD = {
  cacheBuster: pkg.ui5.build && pkg.ui5.build.cacheBuster === true,
  compression: pkg.ui5.build && pkg.ui5.build.compression !== false,
  compressionGzip:
    pkg.ui5.build &&
    (pkg.ui5.build.compression === true ||
      [].concat(pkg.ui5.build.compression).includes('gzip')),
  compressionBrotli:
    pkg.ui5.build &&
    (pkg.ui5.build.compression === true ||
      [].concat(pkg.ui5.build.compression).includes('brotli'))
}

// helper array function
const noPrebuild = oModule => oModule.prebuild !== true
const mapExternalModulePath = oModule => ({
  ...oModule,
  path: oModule.path.replace(/^\/?node_modules/, `${SRC}/node_modules`)
})

// read modules
const UI5_APPS = (pkg.ui5.apps || []).map(mapExternalModulePath)
const UI5_LIBS = (pkg.ui5.libraries || []).map(mapExternalModulePath)
const UI5_THEMES = (pkg.ui5.themes || []).map(mapExternalModulePath)
const NON_UI5_ASSETS = (pkg.ui5.assets || []).map(mapExternalModulePath)
const UI5_ROOTS = []
  .concat(UI5_APPS)
  .concat(UI5_THEMES)
  .concat(UI5_LIBS)
  .concat(NON_UI5_ASSETS)

// target directory of plain npm dependencies
const NPM_MODULES_TARGET = pkg.ui5.vendor || {
  name: '',
  path: ''
}

// identify ui5 modules loaded as devDependency
const NPM_UI5_LIBS = (pkg.ui5.libraries || []).filter(oModule =>
  oModule.path.startsWith('node_modules')
)
const NPM_UI5_MODULES = []
  .concat(pkg.ui5.apps || [])
  .concat(pkg.ui5.themes || [])
  .concat(pkg.ui5.libraries || [])
  .concat(pkg.ui5.assets || [])
  .filter(oModule => oModule.path.startsWith('node_modules'))

const THEMES = [
  'base',
  'psmaterial',
  'sap_belize',
  'sap_belize_hcb',
  'sap_belize_hcw',
  'sap_belize_plus',
  'sap_bluecrystal',
  'sap_goldreflection',
  'sap_hcb'
]
const TARGET_THEME = pkg.ui5.theme || 'sap_belize'

// paths used in our app
const paths = {
  entry: {
    src: [pkg.main]
  },
  assets: {
    src: UI5_ROOTS.filter(noPrebuild)
      .reduce(
        (aSrc, oModule) =>
          aSrc.concat([
            `${oModule.path}/**/*.properties`,
            `${oModule.path}/**/*.json`,
            `${oModule.path}/**/*.{xml,html}`,
            `${oModule.path}/**/*.css`,
            `${oModule.path}/**/*.{jpg,jpeg,png,svg,ico}`,
            `${oModule.path}/**/*.{woff,woff2,eot,ttf}`
          ]),
        []
      )
      // take into account .js files only for asset roots
      .concat(NON_UI5_ASSETS.map(oAsset => `${oAsset.path}/**/*.js`))
  },
  scripts: {
    src: UI5_APPS.filter(noPrebuild)
      .concat(UI5_LIBS)
      .map(oModule => `${oModule.path}/**/*.js`)
  },
  appStyles: {
    src: UI5_APPS.concat(NON_UI5_ASSETS).map(
      oModule => `${oModule.path}/**/*.less`
    )
  },
  libStyles: {
    src: UI5_LIBS.filter(noPrebuild).map(
      oLibrary => `${oLibrary.path}/**/*.less`
    )
  },
  themeStyles: {
    src: UI5_THEMES.filter(noPrebuild).map(oTheme => `${oTheme.path}/**/*.less`)
  },
  htmlEntries: {
    // this should be the result file of task 'entryDist'
    src: [`${DIST}/index.html`]
  }
}

/**
 * Gulp 'start' task (development mode).
 * @description Call update and start file watcher.
 * @public
 */
const start = gulp.series(
  logStart,
  cleanDev,
  // favicon,
  gulp.parallel(prepareOpenUI5, loadDependencies),
  gulp.parallel(copyUi5Theme, copyUi5LibraryThemes),
  gulp.parallel(
    entry,
    assets,
    scripts,
    ui5AppStyles,
    ui5LibStyles,
    ui5ThemeStyles
  ),
  gulp.parallel(logStats, updateVersion),
  watch
)

// log start message and start spinner
function logStart(done) {
  spinner.print(' ')
  spinner.start('Start development server...')
  done()
}

// log common statistics
function logStatsCommons() {
  // const sSourceID = pkg.ui5.src
  // const oSource = pkg.ui5.srcLinks[sSourceID]
  const sUI5Version = getUI5Version()
  // const sOnlineUI5State =
  //   !oSource.isArchive && oSource.isPrebuild ? '(remote)' : ''
  // const sUI5Details = !oSource.isPrebuild ? '(custom build)' : sOnlineUI5State

  const iApps = UI5_APPS.length
  const iThemes = UI5_THEMES.length
  const iLibs = UI5_LIBS.length

  const sVendorLibsPath = NPM_MODULES_TARGET.path
  const aVendorLibs = allExplicitDependencies()

  if (aVendorLibs.length > 0) {
    spinner.print(' ')
    spinner.succeed(
      `Dependencies (vendor libraries) loaded into: ${sVendorLibsPath}`
    )

    aVendorLibs.forEach(sEntry => {
      const sModuleName = sEntry.split('/node_modules/')[1].split('/')[0]
      spinner.print(`• ${sModuleName} as ${getExposedModuleName(sModuleName)}`)
    })
  }

  // print success message
  spinner.print(' ')
  spinner.succeed(`UI5 Version: ${sUI5Version}`).print(' ')
  spinner
    .succeed('UI5 assets created:')
    .print(`• ${iApps} app${iApps !== 1 ? 's' : ''}`)
    .print(`• ${iThemes} theme${iThemes !== 1 ? 's' : ''}`)
    .print(`• ${iLibs} librar${iLibs !== 1 ? 'ies' : 'y'}`)
    .print(' ')
}

// log start statistics and stop spinner
function logStats(done) {
  // print success message
  spinner.succeed(
    'Development server started, use Ctrl+C to stop and go back to the console...'
  )
  logStatsCommons()
  done()
}
export default start

/**
 * Gulp 'build' task (distribution mode).
 * @description Build the complete app to run in production environment.
 * @public
 */
const build = gulp.series(
  logStartDist,
  cleanDist,
  // favicon,
  gulp.parallel(
    //gulp.series(prepareOpenUI5, buildOpenUI5),
    gulp.series(prepareOpenUI5),
    loadDependenciesDist
  ),
  gulp.parallel(copyUi5Theme, copyUi5LibraryThemes),
  gulp.parallel(
    entryDist,
    assetsDist,
    scriptsDist,
    ui5AppStylesDist,
    ui5LibStylesDist,
    ui5ThemeStylesDist
  ),
  gulp.parallel(ui5preloads, ui5LibPreloads),
  gulp.parallel(ui5cacheBust),
  gulp.parallel(preCompressionGzip, preCompressionBrotli),
  logStatsDist
)
const buildErrorHandler = {
  errorHandler: error => {
    // print error
    spinner.fail(error)
    // exit gulp
    throw error
  }
}

// log start build message and start spinner
function logStartDist(done) {
  spinner.print(' ')
  spinner.start('Build start...')
  done()
}

// log build statistics and stop spinner
function logStatsDist(done) {
  // print success message
  spinner
    .succeed('Build successfull.')
    .print(' ')
    .print(`Build entry: ${pkg.main}`)
    .print(`Build output: ${path.resolve(__dirname, DIST)}`)
  logStatsCommons()
  done()
}
export {build}

/* ----------------------------------------------------------- *
 * watch files for changes
 * ----------------------------------------------------------- */

// [development build]
function watch() {
  // start watchers
  gulp.watch(paths.entry.src, gulp.series(startWatchTask, entry, reload))
  gulp.watch(paths.assets.src, gulp.series(startWatchTask, assets, reload))
  gulp.watch(paths.scripts.src, gulp.series(startWatchTask, scripts, reload))
  gulp.watch(
    paths.appStyles.src,
    gulp.series(startWatchTask, ui5AppStyles, reload)
  )
  gulp.watch(
    paths.libStyles.src,
    gulp.series(startWatchTask, ui5LibStyles, reload)
  )
  gulp.watch(
    paths.themeStyles.src,
    gulp.series(startWatchTask, ui5ThemeStyles, reload)
  )

  // start HTTP server
  if (options.local) {
    startHttpServer()
  }
}

// [production build]
export function startHttpServer() {
  const sSuccessMessage =
    '\u{1F64C}  (Server started, use Ctrl+C to stop and go back to the console...)'

  // start web server
  const port = parseInt(process.env.DEV_PORT || 3000, 10)
  const express = require('express')
  const proxy = require('http-proxy-middleware')

  // proxy middleware options
  const proxyOptions = {
    target: process.env.DEV_API_PROXY,
    secure: false,
    xfwd: true,
    // autoRewrite: true,
    // changeOrigin: true,
    headers: {
      host: `localhost:${port}`,
      origin: `http://localhost:${port}`
    },
    cookieDomainRewrite: {
      '*': `localhost:${port}`
    }
  }

  // create the proxy (without context)
  const apiProxy = process.env.DEV_API_PROXY ? proxy(proxyOptions) : null

  // mount web server
  const webServer = express()

  // config and start web server
  if (process.env.DEV_API_PROXY) {
    webServer.use('/api', apiProxy)
  }
  webServer.use(express.static(IS_DEV_MODE ? `./${DEV}` : `./${DIST}`))
  webServer.use('/app', express.static(IS_DEV_MODE ? `./${DEV}` : `./${DIST}`))
  webServer.use('/ui5', express.static(`./${UI5}`))
  webServer.use('/.maps', express.static(`./.maps`))
  // if (!IS_DEV_MODE) {
  //   // TODO: get gzip middleware working again with express
  //   // gzip/brotli static middleware - serves compressed files if they exist
  //   const gzipStaticMiddleware = require('connect-gzip-static')
  //   const oneDay = 86400000
  //   webServer.use('/app', gzipStaticMiddleware(`./${DIST}`), {maxAge: oneDay})
  // }

  webServer.listen(port)

  // log success message
  gutil.log(gutil.colors.green(sSuccessMessage))
}

/* ----------------------------------------------------------- *
 * reload browser
 * ----------------------------------------------------------- */

/**
 * Helper to print the current time formatted
 * @return {string}
 */
function currentTime() {
  const now = new Date()
  const h = str_pad(now.getHours())
  const m = str_pad(now.getMinutes())
  const s = str_pad(now.getSeconds())
  return h + ':' + m + ':' + s
}

function str_pad(n) {
  return String('0' + n).slice(-2)
}

// [development build]
function reload(done) {
  if (options.silent) {
    spinner.print(
      `\u{1F435}  Update completed, ready for reload... - ${currentTime()}`
    )
  } else {
    gutil.log(`Update completed, ready for reload... - ${currentTime()}`)
  }
  done()
}

function startWatchTask(done) {
  if (options.silent) {
    spinner.print(`\u{1F440}  Watch task started ... - ${currentTime()}`)
  } else {
    gutil.log(`Watch task started ... - ${currentTime()}`)
  }
  done()
}

/* ----------------------------------------------------------- *
 * if required: download and build OpenUI5 library
 * ----------------------------------------------------------- */

// [development & production build]
// export function downloadOpenUI5() {
//   try {
//     const sSourceID = pkg.ui5.src
//     const oSource = pkg.ui5.srcLinks[sSourceID]
//     const sUI5Version = oSource.version
//     const sCompiledURL = handlebars.compile(oSource.url)(oSource)
//     const isRemoteLink = sCompiledURL.startsWith('http')

//     // if UI5 download link is marked as prebuild,
//     // we can extract it directly into '/ui5' target directory
//     const sDownloadPath = !oSource.isPrebuild
//       ? path.resolve(__dirname, './.download')
//       : path.resolve(__dirname, `./${UI5}`)
//     const isDownloadRequired =
//       oSource.isArchive &&
//       isRemoteLink &&
//       !fs.existsSync(path.join(sDownloadPath, sUI5Version))
//     const oDownloadOptions = {
//       onProgress(iStep, iTotalSteps, oStepDetails) {
//         // update spinner state
//         spinner.text = `Downloading UI5... [${iStep}/${iTotalSteps}] ${Math.round(
//           oStepDetails.progress || 0
//         )}% (${oStepDetails.name})`
//       }
//     }

//     // ensure ui5 download oath exists
//     if (!fs.existsSync(sDownloadPath)) {
//       fs.mkdirSync(sDownloadPath)
//     }

//     if (isDownloadRequired) {
//       gutil.log('Download UI5 form: ' + sCompiledURL)
//       // update spinner state
//       spinner.text =
//         'Downloading UI5... (this task can take several minutes, please be patient)'
//     }

//     // return promise
//     return isDownloadRequired
//       ? ui5Download(sCompiledURL, sDownloadPath, sUI5Version, oDownloadOptions)
//           .then(sSuccessMessage => {
//             spinner.succeed(sSuccessMessage)
//             spinner.start('')
//           })
//           .catch(sErrorMessage => {
//             spinner.fail(sErrorMessage)
//             spinner.start('')
//             throw sErrorMessage
//           })
//       : Promise.resolve()
//   } catch (error) {
//     spinner.fail(error)
//   }
// }

/**
 * Reads UI5 version from depencies
 */
function getUI5Version() {
  //Make sure to return actual semantic version number only, as this will be used in URLs
  return pkg.dependencies['@openui5/sap.ui.core'].replace(/[^0123456789.]/g, '')
}

export function prepareOpenUI5() {
  let sUI5Version = getUI5Version()
  let sUI5Path = path.resolve(__dirname, `./${UI5}`)
  const isAlreadyPresent = fs.existsSync(path.join(sUI5Path, sUI5Version))

  if (!isAlreadyPresent) {
    gutil.log(
      'Missing UI5 runtime, start building for version: ' +
        gutil.colors.bold(sUI5Version)
    )
    require('child_process').execSync(
      `yarn ui5 build --all --dest=./${UI5}/${sUI5Version} --clean-dest=true`,
      {stdio: 'inherit'}
    )

    //Build sap-ui-version.json file (e.g. to make UI5 inspector happy)
    // initialize build time
    const NOW = new Date()
    // build time stamp in format yyyyMMddhhmm
    const sBuildTime = NOW.toISOString()
      .slice(0, 16)
      .replace(/[-T:]/g, '')

    const oVersionJSON = {
      buildTimestamp: sBuildTime,
      name: 'openui5-sdk-dist-pulseshift-custom',
      version: sUI5Version,
      libraries: [] //TODO:Check if needed: Could be read from yarn/npm dependencies
    }
    const sVersionJSONString = JSON.stringify(oVersionJSON, null, 2)

    // write JSON file
    fs.writeFileSync(
      `./${UI5}/${sUI5Version}/resources/sap-ui-version.json`,
      sVersionJSONString
    )
  } else {
    gutil.log(
      'Found UI5 runtime, nothing to do - version: ' +
        gutil.colors.bold(sUI5Version)
    )
  }

  return Promise.resolve()
}

// [development & production build]
//TODO: Is this still needed?
export function buildOpenUI5() {
  //const sSourceID = pkg.ui5.src
  //const oSource = pkg.ui5.srcLinks[sSourceID]
  //const sUI5Version = oSource.version
  let sUI5Version = getUI5Version()

  //const sDownloadPath = path.resolve(__dirname, './ui5_tmp')
  const sDownloadPath = path.resolve(__dirname, `./${UI5}/${sUI5Version}`)
  const sUI5TargetPath = path.resolve(__dirname, `./${UI5}/${sUI5Version}`)
  const isBuildRequired = !fs.existsSync(sUI5TargetPath)
  const oBuildOptions = {
    onProgress(iStep, iTotalSteps, oStepDetails) {
      // update spinner state
      spinner.text = `Build UI5... [${iStep}/${iTotalSteps}] (${oStepDetails.name})`
    }
  }

  if (isBuildRequired) {
    // update spinner state
    spinner.text =
      'Build UI5... (this task can take several minutes, please be patient)'
  }

  // define build Promise
  return isBuildRequired
    ? ui5Build(
        `${sDownloadPath}/${sUI5Version}`,
        sUI5TargetPath,
        sUI5Version,
        oBuildOptions
      )
        .then(sSuccessMessage => {
          spinner.succeed(sSuccessMessage)
          spinner.start('')
        })
        .catch(sErrorMessage => {
          spinner.fail(sErrorMessage)
          spinner.start('')
        })
    : Promise.resolve()
}

/* ----------------------------------------------------------- *
 * clean development directory
 * ----------------------------------------------------------- */

// [development build]
function cleanDev() {
  const VENDOR_SRC = pkg.ui5.vendor ? `${pkg.ui5.vendor.path}/**/*` : ''
  return del(
    [`${DEV}/**/*`, `!${UI5}/**/*`].concat(
      VENDOR_SRC
    )
  )
}

// [production build]
function cleanDist() {
  const VENDOR_SRC = pkg.ui5.vendor ? `${pkg.ui5.vendor.path}/**/*` : ''
  return del(
    [`${DIST}/**/*`, `!${UI5}/**/*`].concat(
      VENDOR_SRC
    )
  )
}

// [helper function]
export function clean() {
  const VENDOR_SRC = pkg.ui5.vendor ? `${pkg.ui5.vendor.path}/**/*` : ''
  return del(
    [
      DEV,
      DIST,
      UI5,
      '.maps',
      '.download',
      'test/screenshots/**/*',
      `${SRC}/**/*.help.properties`,
      `${SRC}/**/i18n_*.properties`,
      `${SRC}/**/messagebundle_*.properties`
    ]
      .concat(
        THEMES.filter(theme => theme !== TARGET_THEME).map(
          theme => `${SRC}/resources/ui5-themes/UI5/sap/**/themes/${theme}`
        )
      )
      .concat(VENDOR_SRC)
  )
}

/* ----------------------------------------------------------- *
 * generate favicons (long runner ~ 100-200 sec)
 * ----------------------------------------------------------- */

// [production & development build]
// function favicon() {
//   const targetPath = IS_DEV_MODE ? DEV : DIST
//   const isFaviconsDirCached = fs.existsSync(
//     `${targetPath}/favicons_results.html`
//   )
//   // use content hash of master image as directory name and cashe assets in dev mode
//   return isFaviconsDirCached || !isFaviconDefined
//     ? Promise.resolve()
//     : gulp
//         .src(pkg.favicon.src)
//         .pipe(plumber(buildErrorHandler))
//         .pipe(
//           favicons({
//             appName: pkg.ui5.indexTitle,
//             appDescription: pkg.description,
//             developerName: pkg.author,
//             version: pkg.version,
//             background: '#fefefe',
//             theme_color: '#fefefe',
//             path: ``,
//             html: 'favicons_results.html',
//             online: false,
//             preferOnline: false,
//             pipeHTML: true
//           })
//         )
//         .pipe(gulp.dest(`${targetPath}`))
// }

/* ----------------------------------------------------------- *
 * optimize and compile app entry (src/index.handlebars)
 * ----------------------------------------------------------- */

// [helper function]
function getHandlebarsProps(sEntryHTMLPath) {
  const aResourceRootsSrc = []
    .concat(UI5_APPS)
    .concat(UI5_LIBS)
    .concat(NON_UI5_ASSETS)
    .concat([NPM_MODULES_TARGET])

  return {
    indexTitle: pkg.ui5.indexTitle,
    src: getRelativeUI5SrcURL(sEntryHTMLPath),
    scriptAttributes: IS_DEV_MODE ? '' : 'defer', //Add defer only in prod build as currently sap-ui-debug does not work in deferred mode
    theme: TARGET_THEME,
    // create resource roots string
    resourceroots: JSON.stringify(
      aResourceRootsSrc.reduce((oResult, oModule) => {
        const sModulePath = oModule.path.replace(
          new RegExp(`^${SRC}`),
          IS_DEV_MODE ? DEV : DIST
        )

        // create path to theme relative to entry HTML
        let sRelativePath = path.relative(
          path.parse(sEntryHTMLPath).dir,
          sModulePath
        )

        // on windows, the sRelativePath will contain backslashes. as the browser cannot handle
        // the relative path included in the script tag containing backslashes, we have to replace
        // them with normal slashes
        sRelativePath = sRelativePath.includes('\\')
          ? sRelativePath.replace(/\\/g, '/')
          : sRelativePath

        return Object.assign(oResult, {
          [oModule.name]: sRelativePath
        })
      }, {})
    ),
    // create custom theme roots string
    themeroots: JSON.stringify(
      UI5_THEMES.reduce((oResult, oTheme) => {
        const sThemePath = oTheme.path.replace(
          new RegExp(`^${SRC}`),
          IS_DEV_MODE ? DEV : DIST
        )

        // create path to theme relative to entry HTML
        let sRelativePath = path.relative(
          path.parse(sEntryHTMLPath).dir,
          `${sThemePath}/UI5`
        )

        // on windows, the sRelativePath will contain backslashes. as the browser cannot handle
        // the relative path included in the script tag containing backslashes, we have to replace
        // them with normal slashes
        sRelativePath = sRelativePath.includes('\\')
          ? sRelativePath.replace(/\\/g, '/')
          : sRelativePath

        // create path to theme relative to entry HTML
        return Object.assign(oResult, {
          [oTheme.name]: sRelativePath
        })
      }, {})
    ),
    // // create favicons
    // favicons: isFaviconDefined
    //   ? fs.readFileSync(
    //       `${IS_DEV_MODE ? DEV : DIST}/favicons_results.html`,
    //       'utf8'
    //     )
    //   : '',
    // define HOST used for API calls in case a proxy is used
    devheader: ''
    // process.env.DEV_API_PROXY && IS_DEV_MODE
    //   ? '<link id="dev-api-proxy-origin" href="https://localhost" />'
    //   : ''
  }
}

// [helper function]
function getRelativeUI5SrcURL(sEntryHTMLPath) {
  const sEntryPath = path.dirname(sEntryHTMLPath)
  //const sSourceID = pkg.ui5.src
  //const oSource = pkg.ui5.srcLinks[sSourceID]
  //const sCompiledURL = handlebars.compile(oSource.url)(oSource)
  //const isRemoteLink = sCompiledURL.startsWith('http')
  //const sLocalPath = oSource.path || ''

  // const sOpenUI5PathNaked = path.resolve(
  //   __dirname,
  //   path.join(`${UI5}/${getUI5Version()}`, 'sap-ui-core.js')
  // )
  const sOpenUI5PathWrapped = path.resolve(
    __dirname,
    path.join(`${UI5}/${getUI5Version()}/resources`, 'sap-ui-core.js')
  )

  let sRelativeUI5Path = path.relative(sEntryPath, sOpenUI5PathWrapped)

  // if (oSource.isArchive && isRemoteLink && !oSource.isPrebuild) {
  //   // ui5/version/sap-ui-core.js
  //   sRelativeUI5Path = path.relative(sEntryPath, sOpenUI5PathNaked)
  // } else if (oSource.isArchive && isRemoteLink && oSource.isPrebuild) {
  //   // ui5/version/resources/sap-ui-core.js (wrapped) OR ui5/version/sap-ui-core.js (naked)
  //   sRelativeUI5Path = path.relative(
  //     sEntryPath,
  //     fs.existsSync(sOpenUI5PathWrapped)
  //       ? sOpenUI5PathWrapped
  //       : sOpenUI5PathNaked
  //   )
  // } else if (!oSource.isArchive && isRemoteLink) {
  //   // direct remote link
  //   sRelativeUI5Path = sCompiledURL
  // } else if (!isRemoteLink) {
  //   // direct local link
  //   sRelativeUI5Path = path.relative(sEntryPath, sCompiledURL)
  // }

  // on windows, the sRelativeUI5Path will contain backslashes. as the browser cannot handle
  // the relative path included in the script tag containing backslashes, we have to replace
  // them with normal slashes
  sRelativeUI5Path = sRelativeUI5Path.includes('\\')
    ? sRelativeUI5Path.replace(/\\/g, '/')
    : sRelativeUI5Path

  return sRelativeUI5Path
}

// [development build]
function entry() {
  try {
    // update spinner state
    spinner.text = 'Compiling project resources...'

    const aEntries = paths.entry.src.map(
      sEntry =>
        new Promise((resolve, reject) =>
          gulp
            .src(
              [sEntry],
              // filter out unchanged files between runs
              {
                base: SRC,
                since: gulp.lastRun(entry)
              }
            )
            // don't exit the running watcher task on errors
            .pipe(plumber())
            // compile handlebars to HTML
            .pipe(
              hdlbars(
                getHandlebarsProps(
                  path.resolve(
                    __dirname,
                    sEntry.replace(new RegExp(`^${SRC}`), DEV)
                  )
                )
              )
            )
            .pipe(
              rename({
                extname: '.html'
              })
            )
            .pipe(gulp.dest(DEV))
            .on('error', reject)
            .on('end', resolve)
        )
    )

    return Promise.all(aEntries)
  } catch (error) {
    spinner.fail(error)
  }
}

// [production build]
function entryDist() {
  try {
    // update spinner state
    spinner.text = 'Compiling project resources...'

    const aEntries = paths.entry.src.map(
      sEntry =>
        new Promise((resolve, reject) =>
          gulp
            .src(
              [sEntry],
              // filter out unchanged files between runs
              {
                base: SRC,
                since: gulp.lastRun(entry)
              }
            )
            .pipe(plumber(buildErrorHandler))
            // compile handlebars to HTML
            .pipe(
              hdlbars(
                getHandlebarsProps(
                  path.resolve(
                    __dirname,
                    sEntry.replace(new RegExp(`^${SRC}`), DIST)
                  )
                )
              )
            )
            // minify HTML (disabled, cause data-sap-ui-theme-roots gets removed)
            // .pipe(htmlmin())
            .pipe(
              rename({
                extname: '.html'
              })
            )
            .pipe(gulp.dest(DIST))
            .on('error', reject)
            .on('end', resolve)
            .pipe(touch())
        )
    )

    return Promise.all(aEntries)
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * copy assets to destination folder (.png, .jpg, .json, ...)
 * ----------------------------------------------------------- */

// [development build]
function assets() {
  try {
    return paths.assets.src.length === 0
      ? Promise.resolve()
      : gulp
          .src(
            paths.assets.src,
            // filter out unchanged files between runs
            {
              base: SRC,
              since: gulp.lastRun(assets)
            }
          )
          // don't exit the running watcher task on errors
          .pipe(plumber())
          // do not optimize size and quality of images in dev mode

          // transpile JS: babel will run with the settings defined in `.babelrc` file
          .pipe(gulpif(/.*\.js$/, sourcemaps.init()))
          .pipe(gulpif(/.*\.js$/, babel()))
          .pipe(gulpif(/.*\.js$/, sourcemaps.write('../.maps')))
          .pipe(gulp.dest(DEV))
  } catch (error) {
    spinner.fail(error)
  }
}

// [production build]
function assetsDist() {
  try {
    return paths.assets.src.length === 0
      ? Promise.resolve()
      : gulp
          .src(paths.assets.src, {
            base: SRC
          })
          .pipe(plumber(buildErrorHandler))
          // optimize size and quality of images
          .pipe(
            gulpif(
              /.*\.(jpg|jpeg|png)$/,
              imagemin({
                progressive: true,
                interlaced: true
              })
            )
          )
          // minify XML, SVG and JSON
          .pipe(
            gulpif(
              /.*\.(xml|json|svg)$/,
              prettydata({
                type: 'minify',
                extensions: {
                  svg: 'xml'
                }
              })
            )
          )
          // minify HTML
          .pipe(gulpif(/.*\.html$/, htmlmin()))
          // minify CSS
          .pipe(
            gulpif(
              /.*\.css$/,
              cleanCSS({
                // do not resolve inline imports of assets
                inline: false,
                level: 2
              })
            )
          )
          // transpile JS: babel will run with the settings defined in `.babelrc` file
          .pipe(gulpif(/.*\.js$/, babel()))
          // minify JS
          .pipe(gulpif(/.*\.js$/, uglify()))
          .pipe(gulp.dest(DIST))
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * process scripts and transpiles ES2015 code to ES5 (.js, ...)
 * ----------------------------------------------------------- */

// [development build]
function scripts() {
  try {
    return paths.scripts.src.length === 0
      ? Promise.resolve()
      : gulp
          .src(
            paths.scripts.src,
            // filter out unchanged files between runs
            {
              base: SRC,
              since: gulp.lastRun(scripts)
            }
          )
          // don't exit the running watcher task on errors
          .pipe(plumber())
          .pipe(sourcemaps.init())
          // babel will run with the settings defined in `.babelrc` file
          .pipe(babel())
          .pipe(sourcemaps.write('../.maps'))
          .pipe(gulp.dest(DEV))
  } catch (error) {
    spinner.fail(error)
  }
}

// [production build]
function scriptsDist() {
  try {
    return paths.scripts.src.length === 0
      ? Promise.resolve()
      : gulp
          .src(paths.scripts.src, {
            base: SRC
          })
          .pipe(plumber(buildErrorHandler))
          // babel will run with the settings defined in `.babelrc` file
          .pipe(babel())
          // save non-minified copies with debug suffix
          .pipe(
            rename(path => {
              path.basename = /\.controller$/.test(path.basename)
                ? path.basename.replace(/\.controller$/, '-dbg.controller')
                : `${path.basename}-dbg`
            })
          )
          .pipe(gulp.dest(DIST))
          // process copies without suffix
          .pipe(
            rename(path => {
              path.basename = /\.controller$/.test(path.basename)
                ? path.basename.replace(/-dbg\.controller$/, '.controller')
                : path.basename.replace(/-dbg$/, '')
            })
          )
          // minify scripts
          .pipe(uglify())
          .pipe(gulp.dest(DIST))
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * compile and automatically prefix stylesheets (.less, ...)
 * ----------------------------------------------------------- */

// [development build]
function ui5AppStyles() {
  try {
    const autoprefix = new LessAutoprefix({
      // Read from .browserlistrc
      // browsers: ['last 2 versions']
    })
    return paths.appStyles.src.length === 0
      ? Promise.resolve()
      : gulp
          .src(
            paths.appStyles.src,
            // filter out unchanged files between runs
            {
              base: SRC,
              since: gulp.lastRun(ui5AppStyles)
            }
          )
          // don't exit the running watcher task on errors
          .pipe(plumber())
          .pipe(sourcemaps.init())
          // compile LESS to CSS
          .pipe(
            less({
              plugins: [autoprefix]
            })
          )
          .pipe(sourcemaps.write('../.maps'))
          .pipe(gulp.dest(DEV))
  } catch (error) {
    spinner.fail(error)
  }
}

// [production build]
function ui5AppStylesDist() {
  try {
    const autoprefix = new LessAutoprefix({
      //Read from .browserlistrc
      // browsers: ['last 2 versions']
    })
    return paths.appStyles.src.length === 0
      ? Promise.resolve()
      : gulp
          .src(paths.appStyles.src, {
            base: SRC
          })
          .pipe(plumber(buildErrorHandler))
          // compile LESS to CSS
          .pipe(
            less({
              plugins: [autoprefix]
            })
          )
          // minify CSS
          .pipe(
            cleanCSS({
              level: 2
            })
          )
          .pipe(gulp.dest(DIST))
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * bundle app resources in Component-preload.js file
 * ----------------------------------------------------------- */

// [production build]
function ui5preloads() {
  try {
    // update spinner state
    spinner.text = 'Bundling modules...'

    const aPreloadPromise = UI5_APPS.filter(noPrebuild).map(oApp => {
      const sDistAppPath = oApp.path.replace(new RegExp(`^${SRC}`), DIST)
      return new Promise(function(resolve, reject) {
        gulp
          .src([
            // bundle all app resources supported by OpenUI5
            `${sDistAppPath}/**/*.js`,
            `${sDistAppPath}/**/*.view.xml`,
            `${sDistAppPath}/**/*.fragment.xml`,
            `${sDistAppPath}/**/manifest.json`,
            // don't bundle debug resources
            `!${sDistAppPath}/**/*-dbg.js`,
            `!${sDistAppPath}/**/*-dbg.controller.js`
          ])
          .pipe(plumber(buildErrorHandler))
          .pipe(order())
          .pipe(
            ui5preload({
              base: sDistAppPath,
              namespace: oApp.name,
              isLibrary: false
            })
          )
          .pipe(gulp.dest(sDistAppPath))
          .on('error', reject)
          .on('end', resolve)
      })
    })
    return Promise.all(aPreloadPromise)
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * bundle library resources in library-preload.js file
 * ----------------------------------------------------------- */

// [production build]
function ui5LibPreloads() {
  try {
    const aPreloadPromise = UI5_LIBS.filter(noPrebuild).map(oLibrary => {
      const sDistLibraryPath = oLibrary.path.replace(
        new RegExp(`^${SRC}`),
        DIST
      )
      return new Promise(function(resolve, reject) {
        gulp
          .src([
            // bundle all library resources
            `${sDistLibraryPath}/**/*.js`,
            `${sDistLibraryPath}/**/*.json`,
            // don't bundle debug or peload resources
            `!${sDistLibraryPath}/**/*-dbg.js`,
            `!${sDistLibraryPath}/**/*-dbg.controller.js`,
            `!${sDistLibraryPath}/**/*-preload.js`
          ])
          .pipe(plumber(buildErrorHandler))
          .pipe(order())
          .pipe(
            ui5preload({
              base: sDistLibraryPath,
              namespace: oLibrary.name,
              // if set to true a library-preload.json file is emitted
              isLibrary: true
            })
          )
          // transform all library-preload.json files into library-preload.js (mandatory since OpenUI5 1.40)
          .pipe(
            gulpif(
              '**/library-preload.json',
              tap(oFile => {
                const oJSONRaw = oFile.contents.toString('utf8')
                oFile.contents = Buffer.from(
                  `jQuery.sap.registerPreloadedModules(${oJSONRaw});`
                )
                return oFile
              })
            )
          )
          .pipe(
            gulpif(
              '**/library-preload.json',
              rename({
                extname: '.js'
              })
            )
          )
          .pipe(gulp.dest(sDistLibraryPath))
          .on('error', reject)
          .on('end', resolve)
      })
    })
    return Promise.all(aPreloadPromise)
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * bundle theme styles in library.css file
 * ----------------------------------------------------------- */

// [development build]
function ui5LibStyles() {
  try {
    const mapPathToDev = sPath => sPath.replace(new RegExp(`^${SRC}`), DEV)
    const aSelectLibrarySources = UI5_LIBS.filter(noPrebuild).map(
      oLibrary => `${mapPathToDev(oLibrary.path)}/**/library.source.less`
    )

    return paths.libStyles.src.length === 0
      ? Promise.resolve()
      : new Promise((resolve, reject) => {
          // 1. copy theme resources (assets) to DEV
          gulp
            .src(paths.libStyles.src, {
              base: SRC,
              // filter out unchanged files between runs
              since: gulp.lastRun(ui5LibStyles)
            })
            // don't exit the running watcher task on errors
            .pipe(plumber())
            .pipe(gulp.dest(DEV))
            .on('error', reject)
            .on('end', resolve)
        }).then(
          () =>
            new Promise((resolve, reject) => {
              // 2. compile library.css
              gulp
                .src(aSelectLibrarySources, {
                  base: DEV,
                  read: true
                })
                // don't exit the running watcher task on errors
                .pipe(plumber())
                .pipe(
                  tap(oFile => {
                    ui5CompileLessLib(oFile)
                  })
                )
                .pipe(gulp.dest(DEV))
                .on('error', reject)
                .on('end', resolve)
            })
        )
  } catch (error) {
    spinner.fail(error)
  }
}

// [production build]
function ui5LibStylesDist() {
  try {
    const mapPathToDist = sPath => sPath.replace(new RegExp(`^${SRC}`), DIST)
    const aSelectLibrarySources = UI5_LIBS.filter(noPrebuild).map(
      oLibrary => `${mapPathToDist(oLibrary.path)}/**/library.source.less`
    )
    const aSelectLibraryBundles = UI5_LIBS.filter(noPrebuild).reduce(
      (aBundles, oLibrary) =>
        aBundles.concat([
          `${mapPathToDist(oLibrary.path)}/**/library.css`,
          `${mapPathToDist(oLibrary.path)}/**/library-RTL.css`
        ]),
      []
    )

    return paths.libStyles.src.length === 0
      ? Promise.resolve()
      : new Promise((resolve, reject) => {
          // 1. copy theme resources (assets) to DEV
          gulp
            .src(paths.libStyles.src, {
              base: SRC
            })
            .pipe(plumber(buildErrorHandler))
            .pipe(gulp.dest(DIST))
            .on('error', reject)
            .on('end', resolve)
        })
          .then(
            () =>
              new Promise((resolve, reject) => {
                // 2. compile library.css
                gulp
                  .src(aSelectLibrarySources, {
                    read: true,
                    base: DIST
                  })
                  .pipe(plumber(buildErrorHandler))
                  .pipe(
                    tap(oFile => {
                      ui5CompileLessLib(oFile)
                    })
                  )
                  .pipe(gulp.dest(DIST))
                  .on('error', reject)
                  .on('end', resolve)
              })
          )
          .then(
            () =>
              new Promise((resolve, reject) =>
                // 3. minify css after creation
                gulp
                  .src(aSelectLibraryBundles, {
                    base: DIST
                  })
                  .pipe(plumber(buildErrorHandler))
                  // minify CSS
                  .pipe(
                    cleanCSS({
                      level: 2
                    })
                  )
                  .pipe(gulp.dest(DIST))
                  .on('error', reject)
                  .on('end', resolve)
              )
          )
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * bundle theme styles in library.css file
 * ----------------------------------------------------------- */

// [development & production build]
function copyUi5Theme() {
  try {
    //const sSourceID = pkg.ui5.src
    //const oSource = pkg.ui5.srcLinks[sSourceID]
    //const sUI5Version = oSource.version
    const sUI5Version = getUI5Version()
    //const sCompiledURL = handlebars.compile(oSource.url)(oSource)
    // const isPrebuild = oSource.isPrebuild
    // const isArchive = oSource.isArchive
    // const isRemoteLibrary =
    //   sCompiledURL.startsWith('http') && !isArchive && isPrebuild
    // const sLocalPath = oSource.path || ''

    // const sOpenUI5PathNaked = path.join(`${UI5}/${sUI5Version}`, sLocalPath)
    // const sOpenUI5PathWrapped = path.join(
    //   `${UI5}/${sUI5Version}/resources`,
    //   sLocalPath
    // )

    // const sUI5Path = fs.existsSync(path.resolve(__dirname, sOpenUI5PathWrapped))
    //   ? sOpenUI5PathWrapped
    //   : sOpenUI5PathNaked

    //TODO:Wh no path joining here??
    let sUI5Path = `${UI5}/${sUI5Version}/resources`

    if (UI5_THEMES.length === 0) {
      return Promise.resolve()
    }

    // if (isRemoteLibrary) {
    //   throw 'Custom UI5 theme build can only be used with a local UI5 library (remote UI5 libs are not supported).'
    // }

    // copy UI5 theme resources to path/to/my/theme/UI5 [one-time after building ui5]
    const aThemeUpdates = UI5_THEMES.filter(noPrebuild).map(
      oTheme =>
        new Promise((resolve, reject) =>
          gulp
            .src(
              [
                `${sUI5Path}/**/*.css`,
                `${sUI5Path}/**/themes/**/*`,
                `!${sUI5Path}/**/themes/**/library.css`,
                `!${sUI5Path}/**/themes/**/library-*.css`,
                `!${sUI5Path}/**/themes/**/*.json`
              ],
              {
                base: `${sUI5Path}`
              }
            )
            .pipe(plumber(buildErrorHandler))
            .pipe(gulp.dest(path.resolve(oTheme.path, 'UI5')))
            .on('error', reject)
            .on('end', resolve)
        )
    )

    return Promise.all(aThemeUpdates)
  } catch (error) {
    spinner.fail(error)
  }
}

// [development & production build]
function copyUi5LibraryThemes() {
  try {
    // copy UI5 theme resources to path/to/my/theme/UI5 [one-time after building ui5]
    const aThemeUpdates = UI5_THEMES.filter(noPrebuild).reduce(
      (aUpdateList, oTheme) => {
        const sThemeTargetPath = oTheme.path.replace(
          new RegExp(`^${SRC}`),
          IS_DEV_MODE ? DEV : DIST
        )

        const aNpmUi5Lib = NPM_UI5_LIBS.map(oLib => {
          const sNpmUi5BasePath = oLib.path.replace(
            new RegExp(`${oLib.name.replace('.', '/')}$`),
            ''
          )
          return new Promise((resolve, reject) =>
            gulp
              .src(
                [
                  `${oLib.path}/**/themes/**/*`,
                  `!${oLib.path}/**/themes/**/*.less`
                ],
                {
                  base: sNpmUi5BasePath
                }
              )
              .pipe(plumber(buildErrorHandler))
              .pipe(gulp.dest(`${sThemeTargetPath}/UI5`))
              .on('error', reject)
              .on('end', resolve)
          )
        })
        return aUpdateList.concat(aNpmUi5Lib)
      },
      []
    )

    return Promise.all(aThemeUpdates)
  } catch (error) {
    spinner.fail(error)
  }
}

// [development build]
function ui5ThemeStyles() {
  try {
    const mapPathToDev = sPath => sPath.replace(new RegExp(`^${SRC}`), DEV)
    const aSelectLibrarySources = UI5_THEMES.filter(noPrebuild).map(
      oTheme =>
        `${mapPathToDev(
          oTheme.path
        )}/**/themes/${TARGET_THEME}/library.source.less`
    )
    const aSelectLibraryBundles = UI5_THEMES.filter(noPrebuild).reduce(
      (aBundles, oTheme) =>
        aBundles.concat([
          `${mapPathToDev(oTheme.path)}/**/themes/${TARGET_THEME}/library.css`,
          `${mapPathToDev(
            oTheme.path
          )}/**/themes/${TARGET_THEME}/library-RTL.css`
        ]),
      []
    )

    return paths.themeStyles.src.length === 0
      ? Promise.resolve()
      : new Promise((resolve, reject) => {
          // 1. copy theme resources (assets) to DEV
          gulp
            .src(paths.themeStyles.src, {
              base: SRC,
              // filter out unchanged files between runs
              since: gulp.lastRun(ui5ThemeStyles)
            })
            // don't exit the running watcher task on errors
            .pipe(plumber())
            .pipe(gulp.dest(DEV))
            .on('error', reject)
            .on('end', resolve)
        })
          .then(
            () =>
              new Promise((resolve, reject) => {
                // 2. compile library.css
                gulp
                  .src(aSelectLibrarySources, {
                    base: DEV,
                    read: true
                  })
                  // don't exit the running watcher task on errors
                  .pipe(plumber())
                  .pipe(
                    tap(oFile => {
                      ui5CompileLessLib(oFile)
                    })
                  )
                  .pipe(gulp.dest(DEV))
                  .on('error', reject)
                  .on('end', resolve)
              })
          )
          .then(
            () =>
              new Promise((resolve, reject) =>
                /**
                 * FIXME:
                 * For yet unknown reasons, the less compiler replaces some "rem" font-size units with "em".
                 * Therefore this is a hacky fix to transform all font-sizes back from "em" to "rem" without changing the values.
                 */
                gulp
                  .src(aSelectLibraryBundles, {
                    base: DEV,
                    read: true
                  })
                  .pipe(plumber())
                  .pipe(
                    tap(oFile => {
                      const sCSS = oFile.contents.toString('utf8')
                      oFile.contents = Buffer.from(
                        sCSS.replace(/(\s*\d*\.?\d+)em/gm, '$1rem')
                      )
                      return oFile
                    })
                  )
                  .pipe(gulp.dest(DEV))
                  .on('error', reject)
                  .on('end', resolve)
              )
          )
  } catch (error) {
    spinner.fail(error)
  }
}

// [production build]
function ui5ThemeStylesDist() {
  try {
    const mapPathToDist = sPath => sPath.replace(new RegExp(`^${SRC}`), DIST)
    const aSelectLibrarySources = UI5_THEMES.filter(noPrebuild).map(
      oTheme =>
        `${mapPathToDist(
          oTheme.path
        )}/**/themes/${TARGET_THEME}/library.source.less`
    )
    const aSelectLibraryBundles = UI5_THEMES.filter(noPrebuild).reduce(
      (aBundles, oTheme) =>
        aBundles.concat([
          `${mapPathToDist(oTheme.path)}/**/themes/${TARGET_THEME}/library.css`,
          `${mapPathToDist(
            oTheme.path
          )}/**/themes/${TARGET_THEME}/library-RTL.css`
        ]),
      []
    )

    return paths.themeStyles.src.length === 0
      ? Promise.resolve()
      : new Promise((resolve, reject) => {
          // 1. copy theme resources (assets) to DEV
          gulp
            .src(paths.themeStyles.src, {
              base: SRC
            })
            .pipe(plumber(buildErrorHandler))
            .pipe(gulp.dest(DIST))
            .on('error', reject)
            .on('end', resolve)
        })
          .then(
            () =>
              new Promise((resolve, reject) => {
                // 2. compile library.css
                gulp
                  .src(aSelectLibrarySources, {
                    read: true,
                    base: DIST
                  })
                  .pipe(plumber(buildErrorHandler))
                  .pipe(
                    tap(oFile => {
                      ui5CompileLessLib(oFile)
                    })
                  )
                  .pipe(gulp.dest(DIST))
                  .on('error', reject)
                  .on('end', resolve)
              })
          )
          .then(
            () =>
              new Promise((resolve, reject) =>
                // 3. minify css after creation
                gulp
                  .src(aSelectLibraryBundles, {
                    base: DIST,
                    read: true
                  })
                  .pipe(plumber(buildErrorHandler))
                  .pipe(
                    /**
                     * FIXME:
                     * For yet unknown reasons, the less compiler replaces some "rem" font-size units with "em".
                     * Therefore this is a hacky fix to transform all font-sizes back from "em" to "rem" without changing the values.
                     */
                    tap(oFile => {
                      const sCSS = oFile.contents.toString('utf8')
                      oFile.contents = Buffer.from(
                        sCSS.replace(/(\s*\d*\.?\d+)em/gm, '$1rem')
                      )
                      return oFile
                    })
                  )
                  // TODO: disable clean CSS until v4.2 is released, because svg styles are beeing removed
                  // minify CSS
                  // .pipe(
                  //   cleanCSS({
                  //     level: 2
                  //   })
                  // )
                  .pipe(gulp.dest(DIST))
                  .on('error', reject)
                  .on('end', resolve)
              )
          )
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * load dependencies
 * ----------------------------------------------------------- */

// [helper function]
function getExposedModuleName(sModule) {
  switch (sModule) {
    // define exceptions here, e.g.:
    // case 'lodash':
    //   return '_'
    default:
      // turn to camel case (e.g. "ok js", "ok-js", "ok_js", "ok/js become all "okJs")
      return sModule
        .replace(/[\s-/_.](.)/g, $1 => $1.toUpperCase())
        .replace(/[\s-/_.]/g, '')
        .replace(/^(.)/, $1 => $1.toLowerCase())
  }
}

/**
 * Helper function fo filter out all ui5 deps, as they will be considered separetly
 */
function allExplicitDependencies() {
  return mainNpmFiles().filter(sEntry => {
    const sModuleName = sEntry.split('/node_modules/')[1].split('/')[0]
    return !sModuleName.startsWith('@openui5')
  })
}

// [development build]
function loadDependencies() {
  if (!NPM_MODULES_TARGET.path || NPM_MODULES_TARGET.path.length === 0) {
    return Promise.resolve()
  }

  const aDependencies = allExplicitDependencies()
  const sVendorLibsPathSrc = pkg.ui5.vendor ? pkg.ui5.vendor.path : ''
  const sVendorLibsPathDev = sVendorLibsPathSrc.replace(
    new RegExp(`^${SRC}`),
    DEV
  )

  const aEntryBuilds = aDependencies.map(
    sEntry =>
      new Promise((resolve, reject) => {
        const sModuleName = sEntry.split('/node_modules/')[1].split('/')[0]
        const sGlobalName = getExposedModuleName(sModuleName)
        // console.log(sEntry)
        // console.log(sModuleName)
        // console.log(sGlobalName)
        return (
          browserify({
            entries: sEntry,
            // global name will never be exposed, cause we wrap the module in an ui5 define statement
            standalone: sGlobalName
          })
            // babel will run with the settings defined in `.babelrc` file
            .transform(babelify)
            .bundle()
            .pipe(source(`${sModuleName}.js`))
            .pipe(buffer())
            // wrap complete module to be compatible with ui5 loading system
            .pipe(
              tap(file => {
                file.contents = Buffer.concat([
                  Buffer.from(
                    `sap.ui.define([/* no dependencies */], function(){
                      var exports = {};
                      var module = { exports: null };`
                  ),
                  file.contents,
                  Buffer.from(`return module.exports; });`)
                ])
              })
            )
            .pipe(gulp.dest(sVendorLibsPathSrc))
            .pipe(gulp.dest(sVendorLibsPathDev))
            .on('error', reject)
            .on('end', resolve)
        )
      })
  )
  const aStyleCopy = aDependencies.map(
    sEntry =>
      new Promise((resolve, reject) => {
        const sStylesheetName = sEntry.replace(/\.js$/, '.css')
        return fs.existsSync(path.resolve(__dirname, sStylesheetName))
          ? gulp
              .src([sStylesheetName])
              .pipe(gulp.dest(sVendorLibsPathSrc))
              .pipe(gulp.dest(sVendorLibsPathDev))
              .on('error', reject)
              .on('end', resolve)
          : resolve()
      })
  )

  const oExternalModuleCopy = new Promise((resolve, reject) => {
    return NPM_UI5_MODULES.length > 0
      ? gulp
          .src(
            NPM_UI5_MODULES.map(oModule => `${oModule.path}/**/*`),
            {
              base: './'
            }
          )
          .pipe(gulp.dest(DEV))
          .on('error', reject)
          .on('end', resolve)
      : resolve()
  })

  // execute all at once
  return Promise.all(
    []
      .concat(aEntryBuilds)
      .concat(aStyleCopy)
      .concat(oExternalModuleCopy)
  )
}

// [production build]
function loadDependenciesDist() {
  try {
    if (!NPM_MODULES_TARGET.path || NPM_MODULES_TARGET.path.length === 0) {
      return Promise.resolve()
    }

    const aDependencies = allExplicitDependencies()
    const sVendorLibsPathSrc = pkg.ui5.vendor ? pkg.ui5.vendor.path : ''
    const sVendorLibsPathDist = NPM_MODULES_TARGET.path.replace(
      new RegExp(`^${SRC}`),
      DIST
    )

    const aEntryBuilds = aDependencies.map(
      sEntry =>
        new Promise((resolve, reject) => {
          const sModuleName = sEntry.split('/node_modules/')[1].split('/')[0]
          const sGlobalName = getExposedModuleName(sModuleName)
          return (
            browserify({
              entries: sEntry,
              // global name will never be exposed, cause we wrap the module in an ui5 define statement
              standalone: sGlobalName
            })
              // babel will run with the settings defined in `.babelrc` file
              .transform(babelify)
              .bundle()
              .pipe(plumber(buildErrorHandler))
              .pipe(source(`${sModuleName}.js`))
              .pipe(buffer())
              // wrap complete module to be compatible with ui5 loading system
              .pipe(
                tap(file => {
                  file.contents = Buffer.concat([
                    Buffer.from(
                      `sap.ui.define([/* no dependencies */], function(){
                      var exports = {};
                      var module = { exports: null };`
                    ),
                    file.contents,
                    Buffer.from(`return module.exports; });`)
                  ])
                })
              )
              .pipe(gulp.dest(sVendorLibsPathSrc))
              // save non-minified copies with debug suffix
              .pipe(
                rename({
                  suffix: '-dbg'
                })
              )
              .pipe(gulp.dest(sVendorLibsPathDist))
              // process copies without suffix
              .pipe(
                rename(path => {
                  path.basename = path.basename.replace(/-dbg$/, '')
                })
              )
              // minify scripts
              .pipe(uglify())
              .pipe(gulp.dest(sVendorLibsPathDist))
              .on('error', reject)
              .on('end', resolve)
          )
        })
    )

    const aStyleCopy = aDependencies.map(
      sEntry =>
        new Promise((resolve, reject) => {
          const sStylesheetName = sEntry.replace(/\.js$/, '.css')
          return fs.existsSync(path.resolve(__dirname, sStylesheetName))
            ? gulp
                .src([sStylesheetName])
                // minify CSS
                .pipe(
                  cleanCSS({
                    level: 2
                  })
                )
                .pipe(gulp.dest(sVendorLibsPathSrc))
                .pipe(gulp.dest(sVendorLibsPathDist))
                .on('error', reject)
                .on('end', resolve)
            : resolve()
        })
    )

    const oExternalModuleCopy = new Promise((resolve, reject) => {
      return NPM_UI5_MODULES.length > 0
        ? gulp
            .src(
              NPM_UI5_MODULES.map(oModule => `${oModule.path}/**/*`),
              {
                base: './'
              }
            )
            .pipe(gulp.dest(DIST))
            .on('error', reject)
            .on('end', resolve)
        : resolve()
    })

    // execute all at once
    return Promise.all(
      []
        .concat(aEntryBuilds)
        .concat(aStyleCopy)
        .concat(oExternalModuleCopy)
    )
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * hash module paths (content based) to enable cache buster
 * ----------------------------------------------------------- */

// [production build]
function ui5cacheBust() {
  try {
    // update spinner state
    spinner.text = 'Run cache buster...'

    if (BUILD.cacheBuster === false) {
      return Promise.resolve('Cache buster is deactivated.')
    }

    // console.log('paths.htmlEntries.src', paths.htmlEntries.src)
    return paths.htmlEntries.src.length === 0
      ? Promise.resolve()
      : gulp
          .src(paths.htmlEntries.src)
          .pipe(plumber(buildErrorHandler))
          // rename UI5 module (app component) paths and update index.html
          .pipe(tap(oFile => ui5Bust(oFile)))
          .pipe(gulp.dest(DIST))
  } catch (error) {
    spinner.fail(error)
  }
}

/* ----------------------------------------------------------- *
 * precompress files with gzip and brotli
 * ----------------------------------------------------------- */

// [production build]
function preCompressionGzip() {
  // update spinner state
  spinner.text = 'Run pre compression...'

  if (!BUILD.compression || !BUILD.compressionGzip) {
    return Promise.resolve()
  }

  return (
    gulp
      .src([
        `${DIST}/**/*.{js,css,html,svg,xml,json,txt,properties}`,
        `${UI5}/**/*.{js,css,html,svg,xml,json,txt,properties}`
      ])
      // create .gz (gzip) files
      .pipe(
        gzip({
          skipGrowingFiles: true
        })
      )
      .pipe(gulp.dest(DIST))
  )
}

// [production build]
function preCompressionBrotli() {
  // update spinner state
  spinner.text = 'Run pre compression...'

  if (!BUILD.compression || !BUILD.compressionBrotli) {
    return Promise.resolve()
  }

  return (
    gulp
      .src([
        `${DIST}/**/*.{js,css,html,svg,xml,json,txt,properties}`,
        `${UI5}/**/*.{js,css,html,svg,xml,json,txt,properties}`
      ])
      // create .br (brotli) files
      .pipe(
        brotli.compress({
          extension: 'br',
          skipLarger: true
        })
      )
      .pipe(gulp.dest(DIST))
  )
}

/* ----------------------------------------------------------- *
 * update sensum version info
 * ----------------------------------------------------------- */

// [development build]
function updateVersion() {
  try {
    // const sSourceID = pkg.ui5.src
    //const oSource = pkg.ui5.srcLinks[sSourceID]
    const sUI5Version = getUI5Version()
    const sCommitVersion = require('child_process')
      .execSync('git rev-parse HEAD')
      .toString()
      .trim()
    const sData = JSON.stringify(
      {
        name: pkg.name,
        description: pkg.description,
        vendor: pkg.vendor,
        version: `${pkg.version} (dev)`,
        builtAt: new Date(),
        commitVersion: sCommitVersion,
        ui5Version: sUI5Version
      },
      null,
      2
    )

    // write file
    return new Promise((resolve, reject) => {
      fs.writeFile(`${DEV}/sensum-version.json`, sData, oError => {
        return oError ? reject() : resolve()
      })
    })
  } catch (error) {
    spinner.fail(error)
  }
}

// [production build]
export function updateVersionDist() {
  try {
    const sUI5Version = getUI5Version()

    // NOTE:This code is exist nearly identical in auris repo as well
    // -> Adapt both when applying fixes

    // Our internal versioning tag pattern
    const rSemanticVersionTag = /v(\d+).(\d+).(\d+)-?(\d*)-?.*/g
    const sCommitVersion = require('child_process')
      .execSync('git rev-parse HEAD')
      .toString()
      .trim()

    //Time stamp from git in unix format
    const sCommitTimeStamp = require('child_process')
      // .execSync('git log -1 --pretty=format:%ct')
      .execSync(
        `git rev-list --format=format:'%ct' --max-count=1 ${sCommitVersion}`
      )
      .toString()
      .split('\n')[1]
      .trim()
    //Parse as js date for consistency with other timestamps
    const dCommitTimeStamp = new Date(parseInt(sCommitTimeStamp, 10) * 1000)

    const sCurrentBranch = process.env.CI_BUILD_REF_NAME || require('child_process')
      .execSync('git rev-parse --abbrev-ref HEAD')
      .toString()
      .trim()

    const sGitDescribeOutput = require('child_process')
      .execSync('git describe --abbrev=0 --tags')
      .toString()
      .trim()
    // console.log('tag: ' + sGitDescribeOutput)

    const sNoOfCommitsSinceTag = require('child_process')
      .execSync(
        `git log --ancestry-path ${sGitDescribeOutput}..HEAD --oneline | wc -l`
      )
      .toString()
      .trim()
    // console.log('NoOfCommitsSinceTag: ' + sNoOfCommitsSinceTag)

    const iNoOfCommitsSinceTag = parseInt(sNoOfCommitsSinceTag, 10)

    const [, majorVersion, minorVersion, patchVersion /* , commitDiffToTag */] =
      rSemanticVersionTag.exec(sGitDescribeOutput) || []
    // const iCommitDiff =
    //   (parseInt(commitDiffToTag, 10) || 0) - iNoOfCommitsSinceTag
    // const commitGrievanceSuffix =
    //   iCommitDiff !== 0
    //     ? `(${iCommitDiff > 0 ? '+' : '-'}${iCommitDiff} unexpected commits)`
    //     : ''

    let sSuffix = ''
    switch (sCurrentBranch) {
      case 'master':
        sSuffix = iNoOfCommitsSinceTag > 0 ? `-dev.${sNoOfCommitsSinceTag}` : ''
        break
      case 'deploy/demo':
        sSuffix = iNoOfCommitsSinceTag > 0 ? `-hf.${sNoOfCommitsSinceTag}` : ''
        break
      case 'deploy/production':
        sSuffix = iNoOfCommitsSinceTag > 0 ? `-hf.${sNoOfCommitsSinceTag}` : ''
        break
      default:
        // other branch or no branch
        sSuffix =
          iNoOfCommitsSinceTag > 0
            ? `-${sCurrentBranch}.${sNoOfCommitsSinceTag}`
            : ''
    }

    if (!majorVersion && majorVersion !== 0)
      throw 'Could not parse majorVersion: ' + majorVersion
    if (!minorVersion && minorVersion !== 0)
      throw 'Could not parse minorVersion: ' + minorVersion
    if (!patchVersion && patchVersion !== 0)
      throw 'Could not parse patchVersion: ' + patchVersion

    // concat version
    let derivedVersion = _buildVersion(
      majorVersion,
      minorVersion,
      patchVersion,
      // `${sSuffix} ${commitGrievanceSuffix}`
      `${sSuffix}`
    )

    gutil.log(
      'Software Version derived from git tag: ' +
        gutil.colors.bold(derivedVersion)
    )

    const sData = JSON.stringify(
      {
        name: pkg.name,
        description: pkg.description,
        vendor: pkg.vendor,
        version: derivedVersion,
        builtAt: new Date(),
        commitVersion: sCommitVersion,
        commitTimeStamp: dCommitTimeStamp,
        ui5Version: sUI5Version
      },
      null,
      2
    )

    // write file
    // $FlowFixMe: add proper type annotation
    return new Promise((resolve, reject) =>
      fs.writeFile(`${DIST}/sensum-version.json`, sData, oError =>
        oError ? reject() : resolve()
      )
    )
  } catch (error) {
    gutil.log(gutil.colors.red(error.message || error))
    spinner.fail(error)
  }
}

/**
 * Helper function to construct the final software version as string
 * @param majorVersion
 * @param minorVersion
 * @param patchVersion
 * @param sSuffix
 * @return {string}
 * @private
 */
function _buildVersion(majorVersion, minorVersion, patchVersion, sSuffix) {
  return `${majorVersion}.${minorVersion}.${patchVersion}${sSuffix}`
}


text:
#include <unistd.h>
#include <timing.h>
#include <kernel/alarms_internal.h>
#include <sys/time.h>

//----------------------------------------------------------
// platform-specific functions, required by alarm in HIL
//----------------------------------------------------------

static uint32_t getPcTime(void)
{
    struct timeval tv;
    if (gettimeofday(&tv, NULL)) {
        return 0; // error occurred
    } else {
        // clear biggest bits of seconds to escape overflow - we are not
        // actually interested in years, etc
        return (tv.tv_sec & 0x000fffff) * 1000 + tv.tv_usec / 1000;
    }
}

//----------------------------------------------------------
// alarm handling functions
//----------------------------------------------------------

void *alarmIntHandler(void *dummy) {
    uint32_t lastTime = getPcTime();
    while (1) {
        usleep(10000);

        uint32_t now = getPcTime();
        jiffies += now - lastTime;
        lastTime = now;

// TODO
//        mos_mutex_lock(&alarmMutex);
        alarmsProcess();
// TODO
//        mos_mutex_unlock(&alarmMutex);
    }
    return 0;
}

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Mentions de copyright</title>
    <link rel="stylesheet" type="text/css" href="csound.css" />
    <meta name="generator" content="DocBook XSL Stylesheets V1.78.1" />
    <link rel="home" href="index.html" title="Manuel de référence canonique de Csound" />
    <link rel="up" href="PrefaceTop.html" title="Préface" />
    <link rel="prev" href="PrefaceHistory.html" title="Histoire du manuel de référence canonique de Csound" />
    <link rel="next" href="PrefaceGettingStarted.html" title="Débuter avec Csound" />

          Mentions de copyright

          Précédent
          Préface
           Suivant

Mentions de copyright

    Cette version du manuel de Csound ("Le manuel canonique de Csound") est délivrée sous
    la GNU Free Documentation Licence.
    Les mentions de copyright antérieures et le crédit de leurs auteurs sont donnés ci-dessous,
    pour des raisons historiques.

Mentions de copyright antérieures

      Copyright © 1986, 1992 par le Massachusetts Institute of Technology. Tous droits réservés.

      Développé par Barry L. Vercoe au Experimental Music Studio, Media Laboratory,
      M.I.T., Cambridge, Massachusetts, avec le support partiel de la System Development Foundation et
      du National Science Foundation Grant # IRI-8704665.

Manuel

      Copyright © 2003 by Kevin Conder pour les modifications apportées au manuel de référence public de Csound.

      Il est permis de copier, distribuer et/ou modifier ce document selon les termes de la GNU Free Documentation License,
      Version 1.2 ou toute version ultérieure publiée par la Free Software Foundation ; sans aucune partie non modifiable,
      aucun texte de première de couverture et aucun texte de quatrième de couverture. Une copie de cette licence
      est disponible dans le  sous-répertoire des exemples ou à :
      www.gnu.org/licenses/fdl.txt.

      La documentation du langage Csound de ce manuel est dérivée du manuel de référence alternatif de
      Csound de Kevin Conder, qui est lui-même dérivé du manuel de référence public de
      Csound.

      Copyright © 2004-2005 par Michael Gogins pour les modifications faites au
      manuel de référence alternatif de Csound.

      Cette mention légale provient du manuel de référence public de Csound :
      « L'édition hypertexte originale
      du manuel de csound du MIT fut préparée pour le World Wide Web par Peter J. Nix
      du Department of Music at the University of Leeds et Jean Piché
      de la faculté de musique de l'université de Montréal. Une édition d'impression, en format Adobe Acrobat,
      fut ensuite maintenue par David M. Boothe. Les éditeurs reconnaissent entièrement
      les droits des auteurs de la documentation et des programmes originaux, comme décrits ci-dessus,
      et demandent en conséquence que cette mention soit citée chaque fois que ce matériel est utilisé. »

      La dernière url connue du manuel de référence public de Csound était
      http://www.lakewoodsound.com/csound/hypertext/manual.htm.

      L'url du manuel de référence alternatif de Csound, pour les copies transparentes et les copies opaques,
      est http://kevindumpscore.com/download.html#csound-manual.

      L'url du manuel de Csound et de CsoundAC est
      http://sourceforge.net/projects/csound.

Traduction française du manuel par François Pinot.

La traduction française du manuel est placée sous GNU Free Documentation License, Version 1.2 ou
    ultérieure, comme la version anglaise originale.

Csound et CsoundAC

      Csound est protégé par copyright de 1991 à 2008 par Barry Vercoe, John ffitch et les autres
      développeurs.

      CsoundAC est protégé par copyright de 2001 à 2008 par Michael Gogins.

      Csound et CsoundAC (anciennement CsoundVST) sont des logiciels libres ; vous pouvez les
      redistribuer et/ou les modifier selon les termes de la GNU Lesser General Public
      License tels que publiés par la Free Software Foundation ; soit la version
      2.1 de la License, soit (à votre choix) n'importe quelle version ultérieure.

      Csound et CsoundAC sont distribués dans l'espoir qu'il seront utiles,
      mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de la
      VALEUR COMMERCIALE ou de l'ADEQUATION A UNE UTILISATION SPECIALE. Consultez la GNU
      Lesser General Public License pour plus de détails.

      Vous devez avoir reçu une copie de la GNU Lesser General Public
      License en même temps que Csound et CsoundAC ; si ce n'est pas le cas, écrivez à la
      Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
      02111-1307 USA.

Virtual Synthesis Technology

      Virtual Synthesis Technology (VST) PlugIn technologie d'interfaçage par
      Steinberg Soft- und Hardware GmbH.

          Précédent

            Niveau supérieur

           Suivant

          Histoire du manuel de référence canonique de Csound

            Sommaire

           Débuter avec Csound


text:
foam.CLASS({
  package: 'foam.apploader',
  name: 'NodeModelFileDAO',
  extends: 'foam.dao.ProxyDAO',

  requires: [
    'foam.apploader.ModelFileDAO',
    'foam.apploader.NodeModelFileFetcher',
  ],

  properties: [
    'root',
    {
      name: 'delegate',
      factory: function() {
        return this.ModelFileDAO.create({
          fetcher: this.NodeModelFileFetcher.create({root: this.root}), });
      }
    }
  ]
});


text:
package ami

import "context"

// Monitor monitors a channel.
// This action may be used to record the audio on a specified channel.
func Monitor(ctx context.Context, client Client, actionID, channel, file, format string, mix bool) (Response, error) {
	return send(ctx, client, "Monitor", actionID, monitorData{
		Channel: channel,
		File:    file,
		Format:  format,
		Mix:     mix,
	})
}

// ChangeMonitor changes monitoring filename of a channel.
// This action may be used to change the file started by a previous 'Monitor' action.
func ChangeMonitor(ctx context.Context, client Client, actionID, channel, file string) (Response, error) {
	return send(ctx, client, "ChangeMonitor", actionID, monitorData{
		Channel: channel,
		File:    file,
	})
}

// MixMonitor record a call and mix the audio during the recording.
func MixMonitor(ctx context.Context, client Client, actionID, channel, file, options, command string) (Response, error) {
	return send(ctx, client, "MixMonitor", actionID, map[string]string{
		"Channel": channel,
		"File":    file,
		"options": options,
		"Command": command,
	})
}

// MixMonitorMute Mute / unMute a Mixmonitor recording.
// This action may be used to mute a MixMonitor recording.
func MixMonitorMute(ctx context.Context, client Client, actionID, channel, direction string, state bool) (Response, error) {
	s := map[bool]string{false: "0", true: "1"}
	return send(ctx, client, "MixMonitorMute", actionID, monitorData{
		Channel:   channel,
		Direction: direction,
		State:     s[state],
	})
}

// PauseMonitor pauses monitoring of a channel.
// This action may be used to temporarily stop the recording of a channel.
func PauseMonitor(ctx context.Context, client Client, actionID, channel string) (Response, error) {
	return send(ctx, client, "PauseMonitor", actionID, monitorData{
		Channel: channel,
	})
}

// UnpauseMonitor unpause monitoring of a channel.
// This action may be used to re-enable recording of a channel after calling PauseMonitor.
func UnpauseMonitor(ctx context.Context, client Client, actionID, channel string) (Response, error) {
	return send(ctx, client, "UnpauseMonitor", actionID, monitorData{
		Channel: channel,
	})
}

// StopMonitor stops monitoring a channel.
// This action may be used to end a previously started 'Monitor' action.
func StopMonitor(ctx context.Context, client Client, actionID, channel string) (Response, error) {
	return send(ctx, client, "StopMonitor", actionID, monitorData{
		Channel: channel,
	})
}

// StopMixMonitor stop recording a call through MixMonitor, and free the recording's file handle.
func StopMixMonitor(ctx context.Context, client Client, actionID, channel, mixMonitorID string) (Response, error) {
	return send(ctx, client, "StopMixMonitor", actionID, monitorData{
		Channel:      channel,
		MixMonitorID: mixMonitorID,
	})
}

type monitorData struct {
	Channel      string `ami:"Channel"`
	Direction    string `ami:"Direction,omitempty"`
	State        string `ami:"State,omitempty"`
	File         string `ami:"File, omitempty"`
	Format       string `ami:"Format,omitempty"`
	Mix          bool   `ami:"Mix,omitempty"`
	MixMonitorID string `ami:"MixMonitorID,omitempty"`
}


text:
<?php
/**
 * Pagination Language Lines
 * The following language lines are used by the paginator library to build
 * the simple pagination links.
 */
return [

    'previous' => '« Previous',
    'next'     => 'Next »',

];


text:
package org.pageseeder.ox.client.handler;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Load the  job ID form the document server response.
 *
 * @author Carlos Cabral
 * @since 26 Aug 2016
 */
public class JobStatusLoader extends DefaultHandler {

  /** The status. */
  private String status = null;

  /** The percentage. */
  private String percentage = null;

  /** The filelink. */
  private String filelink = null;
  /** The error message. */
  private String message = null;
  /** Indicate if the current element is message. */
  private boolean reachedmessage = Boolean.FALSE;
  /* (non-Javadoc)
   * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
   */
  @Override
  public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    if ("job-status".equals(localName)) {
      this.status = attributes.getValue("status");
      this.percentage = attributes.getValue("percentage");
    } else if ("job".equals(localName)) {
      this.filelink = attributes.getValue("path");
    } else if ("message".equalsIgnoreCase(localName)) {
      reachedmessage = Boolean.TRUE;
    }
  }

  /**
   * Receive notification of character data inside an element.
   *
   *
By default, do nothing.  Application writers may override this
   * method to take specific actions for each chunk of character data
   * (such as adding the data to a node or buffer, or printing it to
   * a file).

   *
   * @param ch The characters.
   * @param start The start position in the character array.
   * @param length The number of characters to use from the
   *               character array.
   * @exception org.xml.sax.SAXException Any SAX exception, possibly
   *            wrapping another exception.
   * @see org.xml.sax.ContentHandler#characters
   */
  public void characters (char ch[], int start, int length) throws SAXException {
    if(reachedmessage) {
      message = new String(ch, start, length);
      reachedmessage = Boolean.FALSE;
    }
  }

  /**
   * Gets the status.
   *
   * @return the status
   */
  public String getStatus() {
    return status;
  }

  /**
   * Gets the percentage.
   *
   * @return the percentage
   */
  public String getPercentage() {
    return percentage;
  }

  /**
   * Gets the filelink.
   *
   * @return the filelink
   */
  public String getFilelink() {
    return filelink;
  }

  public String getMessage() {
    return message;
  }
}


text:
<?php
/**
 * add a new question
 *
 */

gatekeeper();

$container_guid = (int) get_input('guid');
$container = get_entity($container_guid);
if (!$container) {

}

$title=elgg_echo("questions:add");
elgg_push_breadcrumb($title);
$vars = questions_prepare_form_vars();
$content = elgg_view_form('questions/save', array('id'=>'question_form'), $vars);

$body = elgg_view_layout('qq_two_column', array(

	'content' => $content,
	'title' => $title,
));

echo elgg_view_page($title, $body);


text:
<?php

namespace Imper86\AllegroApi\Rest\Model\SaleLoyaltyPromotions\Post;

use Throwable;

class TooManyPromotionOffersException extends \Exception
{
    public function __construct($message = "Offer collection can have maximum 9 offers", $code = 0, Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }
}


text:
ifdef CONFIG_HAS_CC3200SDK
ZEPHYRINCLUDE +=-I$(srctree)/ext/hal/ti/cc3200sdk
ZEPHYRINCLUDE +=-I$(srctree)/ext/hal/ti/cc3200sdk/inc
ZEPHYRINCLUDE +=-I$(srctree)/ext/hal/ti/cc3200sdk/driverlib
endif # CONFIG_HAS_CC3200SDK

ifdef CONFIG_CC3200SDK_SIMPLELINK
ZEPHYRINCLUDE +=-I$(srctree)/ext/hal/ti/cc3200sdk/simplelink
ZEPHYRINCLUDE +=-I$(srctree)/ext/hal/ti/cc3200sdk/simplelink/include
endif

ifdef CONFIG_CC3200SDK_ROM_DRIVERLIB
KBUILD_CFLAGS += -DTARGET_IS_CC3200
endif  # CONFIG_CC3200SDK_ROM_DRIVERLIB


text:
package org.consumersunion.stories.dashboard.client.event;

import org.consumersunion.stories.common.shared.model.Story;

import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HasHandlers;

public class StorySelectedEvent extends GwtEvent<StorySelectedEvent.StorySelectedHandler> {
    public interface StorySelectedHandler extends EventHandler {
        void onStorySelected(StorySelectedEvent event);
    }

    public static final Type<StorySelectedHandler> TYPE = new Type<StorySelectedHandler>();

    private final Story story;

    private StorySelectedEvent(Story story) {
        this.story = story;
    }

    public static void fire(HasHandlers source, Story story) {
        source.fireEvent(new StorySelectedEvent(story));
    }

    public Story getStory() {
        return story;
    }

    @Override
    public Type<StorySelectedHandler> getAssociatedType() {
        return TYPE;
    }

    @Override
    protected void dispatch(StorySelectedHandler handler) {
        handler.onStorySelected(this);
    }
}


text:
angularCalendar
===============


text:
<?php
/**
 * Shop System SDK:
 * - Terms of Use can be found under:
 * https://github.com/wirecard/paymentSDK-php/blob/master/_TERMS_OF_USE
 * - License can be found under:
 * https://github.com/wirecard/paymentSDK-php/blob/master/LICENSE
 */

namespace Wirecard\PaymentSdk\Constant;

use Wirecard\PaymentSdk\Entity\AccountHolder;
use Wirecard\PaymentSdk\Entity\Address;

/**
 * Contains mappable fields for the XML we build
 *
 * Class ResponseMappingXmlFields
 * @package Wirecard\PaymentSdk\Constant
 * @since 4.0.0
 */
class ResponseMappingXmlFields
{
    const PAYMENT = 'payment';
    const MERCHANT_ACCOUNT_ID = 'merchant-account-id';
    const TRANSACTION_ID = 'transaction-id';
    const TRANSACTION_STATE = 'transaction-state';
    const TRANSACTION_TYPE = 'transaction-type';
    const PAYMENT_METHODS = 'payment-methods';
    const PAYMENT_METHOD = 'payment-method';
    const REQUEST_ID = 'request-id';

    const REQUESTED_AMOUNT_CURRENCY = 'currency';
    const PAYMENT_METHOD_NAME = 'name';

    const ACS_URL = 'acs-url';
    const PAREQ = 'pareq';
    const CARDHOLDER_AUTHENTICATION_STATUS = 'cardholder-authentication-status';

    const CARD_TYPE = 'card-type';
    const EXPIRATION_MONTH = 'expiration-month';
    const EXPIRATION_YEAR = 'expiration-year';
    const TOKEN_ID = 'token-id';
    const MASKED_ACCOUNT_NUMBER = 'masked-account-number';

    const PARENT_TRANSACTION_ID = 'parent-transaction-id';
    const REQUESTED_AMOUNT = 'requested-amount';
    const THREE_D = 'three-d';
    const STATUSES = 'statuses';
    const CARD_TOKEN = 'card-token';
    const CARD = 'card';
    const STATUS = 'status';

    const ACCOUNT_HOLDER = 'account-holder';
    const ACCOUNT_HOLDER_ADDRESS = 'address';
    const ACCOUNT_HOLDER_FIRST_NAME = 'first-name';
    const ACCOUNT_HOLDER_LAST_NAME = 'last-name';
    const ACCOUNT_HOLDER_EMAIL = 'email';
    const ACCOUNT_HOLDER_PHONE = 'phone';
    const ACCOUNT_HOLDER_MOBILE_PHONE = 'mobile-phone';
    const ACCOUNT_HOLDER_WORK_PHONE = 'work-phone';
    const ACCOUNT_HOLDER_DATE_OF_BIRTH = 'date-of-birth';
    const ACCOUNT_HOLDER_CRM_ID = 'crm-id';
    const ACCOUNT_HOLDER_GENDER = 'gender';
    const ACCOUNT_HOLDER_SHIPPING_METHOD = 'shipping-method';
    const ACCOUNT_HOLDER_SOCIAL_SECURITY_NUMBER = 'social-security-number';
    const ACCOUNT_HOLDER_COUNTRY = 'country';
    const ACCOUNT_HOLDER_STATE = 'state';
    const ACCOUNT_HOLDER_CITY = 'city';
    const ACCOUNT_HOLDER_STREET_1 = 'street1';
    const ACCOUNT_HOLDER_STREET_2 = 'street2';
    const ACCOUNT_HOLDER_STREET_3 = 'street3';
    const ACCOUNT_HOLDER_POSTAL_CODE = 'postal-code';
    const ACCOUNT_HOLDER_HOUSE_EXTENSION = 'house-extension';

    /**
     * Return array of account holder fields
     * @return array
     * @since 4.0.3
     */
    public function getAccountHolderFields()
    {
        return [
            AccountHolder::KEY_FIRST_NAME             => self::ACCOUNT_HOLDER_FIRST_NAME,
            AccountHolder::KEY_LAST_NAME              => self::ACCOUNT_HOLDER_LAST_NAME,
            AccountHolder::KEY_EMAIL                  => self::ACCOUNT_HOLDER_EMAIL,
            AccountHolder::KEY_PHONE                  => self::ACCOUNT_HOLDER_PHONE,
            AccountHolder::KEY_MOBILE_PHONE           => self::ACCOUNT_HOLDER_MOBILE_PHONE,
            AccountHolder::KEY_WORK_PHONE             => self::ACCOUNT_HOLDER_WORK_PHONE,
            AccountHolder::KEY_DATE_OF_BIRTH          => self::ACCOUNT_HOLDER_DATE_OF_BIRTH,
            AccountHolder::KEY_CRM_ID                 => self::ACCOUNT_HOLDER_CRM_ID,
            AccountHolder::KEY_GENDER                 => self::ACCOUNT_HOLDER_GENDER,
            AccountHolder::KEY_SHIPPING_METHOD        => self::ACCOUNT_HOLDER_SHIPPING_METHOD,
            AccountHolder::KEY_SOCIAL_SECURITY_NUMBER => self::ACCOUNT_HOLDER_SOCIAL_SECURITY_NUMBER,
        ];
    }

    /**
     * Return array of account holder address fields
     * @return array
     * @since 4.0.3
     */
    public function getAccountHolderAddressFields()
    {
        return [
            Address::KEY_COUNTRY         => self::ACCOUNT_HOLDER_COUNTRY,
            Address::KEY_STATE           => self::ACCOUNT_HOLDER_STATE,
            Address::KEY_CITY            => self::ACCOUNT_HOLDER_CITY,
            Address::KEY_STREET_1        => self::ACCOUNT_HOLDER_STREET_1,
            Address::KEY_STREET_2        => self::ACCOUNT_HOLDER_STREET_2,
            Address::KEY_STREET_3        => self::ACCOUNT_HOLDER_STREET_3,
            Address::KEY_POSTAL_CODE     => self::ACCOUNT_HOLDER_POSTAL_CODE,
            Address::KEY_HOUSE_EXTENSION => self::ACCOUNT_HOLDER_HOUSE_EXTENSION,
        ];
    }
}


text:
from abc import ABC, abstractmethod

# Base class for policy engines
class Engine(ABC):

    @abstractmethod
    def policies(self):
        pass

    @abstractmethod
    def evaluate(self, resource):
        pass

    @abstractmethod
    def remediate(self, resource, policy_id):
        pass


text:
package be.bartgeluykens.invoice.web.pages;

import be.bartgeluykens.invoice.model.Invoice;
import be.bartgeluykens.invoice.service.InvoiceService;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.spring.injection.annot.SpringBean;

public class HomePage extends WebPage {

  @SpringBean
  InvoiceService invoiceService;

	private static final long serialVersionUID = 1L;

	public HomePage(final PageParameters parameters) {
		super(parameters);

    add(new DataView<Invoice>("invoice-table", new ListDataProvider(invoiceService.getAll()))
       {
           private static final long serialVersionUID = 1L;

         @Override
          protected void populateItem(final Item<Invoice> item)
          {
              Invoice invoice = item.getModelObject();

              item.add(new Label("shop"       , invoice.getShop()));
              item.add(new Label("invoice-date", invoice.getInvoiceDate()));
          }

       });

		// TODO Add your page's components here

    }
}


text:
<?php

namespace Magento\Payment\Test\Unit\Gateway\Validator;

use Magento\Payment\Gateway\Validator\ValidatorInterface;
use Magento\Payment\Gateway\Validator\ValidatorPool;

class ValidatorPoolTest extends \PHPUnit_Framework_TestCase
{
    public function testGet()
    {
        $commandI = $this->getMockBuilder(\Magento\Payment\Gateway\Validator\ValidatorInterface::class)
            ->getMockForAbstractClass();
        $tMap = $this->getMockBuilder(\Magento\Framework\ObjectManager\TMap::class)
            ->disableOriginalConstructor()
            ->getMock();
        $tMapFactory = $this->getMockBuilder(\Magento\Framework\ObjectManager\TMapFactory::class)
            ->disableOriginalConstructor()
            ->setMethods(['create'])
            ->getMock();

        $tMapFactory->expects(static::once())
            ->method('create')
            ->with(
                [
                    'array' => ['validator' => \Magento\Payment\Gateway\Validator\ValidatorInterface::class],
                    'type' => ValidatorInterface::class
                ]
            )
            ->willReturn($tMap);
        $tMap->expects(static::once())
            ->method('offsetExists')
            ->with('validator')
            ->willReturn(true);
        $tMap->expects(static::once())
            ->method('offsetGet')
            ->with('validator')
            ->willReturn($commandI);

        $pool = new ValidatorPool(
            $tMapFactory,
            ['validator' => \Magento\Payment\Gateway\Validator\ValidatorInterface::class]
        );

        static::assertSame($commandI, $pool->get('validator'));
    }

    public function testGetException()
    {
        $this->setExpectedException(\Magento\Framework\Exception\NotFoundException::class);

        $tMapFactory = $this->getMockBuilder(\Magento\Framework\ObjectManager\TMapFactory::class)
            ->disableOriginalConstructor()
            ->setMethods(['create'])
            ->getMock();
        $tMap = $this->getMockBuilder(\Magento\Framework\ObjectManager\TMap::class)
            ->disableOriginalConstructor()
            ->getMock();

        $tMapFactory->expects(static::once())
            ->method('create')
            ->with(
                [
                    'array' => [],
                    'type' => ValidatorInterface::class
                ]
            )
            ->willReturn($tMap);
        $tMap->expects(static::once())
            ->method('offsetExists')
            ->with('validator')
            ->willReturn(false);

        $pool = new ValidatorPool($tMapFactory, []);
        $pool->get('validator');
    }
}


text:
Co-design permanent, active, stateful, reliable cloud proxies with your web app and IoT devices.

See https://www.cafjs.com

## Application to Register IoT Devices

In order to manage a device, we need to create first a CA in this app with matching name.

See `caf_rpi/README.md` for how to on-board a Raspberry Pi.


text:
require_relative '../../spec_helper'

require 'bad_sdl/application'

describe BadSdl::Application do
  before :each do
    SDL2.init(:EVERYTHING)
    @app = BadSdl::Application.new title: example.metadata[:example_group][:full_description]
  end

  after :each do
    @app.quit()
    SDL2.quit
  end

end


text:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="3dp"
    android:background="@android:color/transparent"
    android:gravity="center_vertical"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/header"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/vision_expand_logo" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/expanderOneTitle"
        android:textColor="@android:color/white"
        android:textSize="18dp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/progressStatusText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="12dp"
        android:textColor="@android:color/white"
        android:textStyle="normal" />

    <TextView
        android:id="@+id/scoreStatusText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="7dp"
        android:textColor="@android:color/white"
        android:textStyle="normal" />

</LinearLayout>


text:
NAMESPACE_BEGIN(CryptoPP)

// more in dll.cpp
template<> const byte EMSA2HashId<RIPEMD160>::id = 0x31;
template<> const byte EMSA2HashId<RIPEMD128>::id = 0x32;
template<> const byte EMSA2HashId<Whirlpool>::id = 0x37;

#ifndef CRYPTOPP_IMPORTS

size_t PSSR_MEM_Base::MinRepresentativeBitLength(size_t hashIdentifierLength, size_t digestLength) const
{
	size_t saltLen = SaltLen(digestLength);
	size_t minPadLen = MinPadLen(digestLength);
	return 9 + 8*(minPadLen + saltLen + digestLength + hashIdentifierLength);
}

size_t PSSR_MEM_Base::MaxRecoverableLength(size_t representativeBitLength, size_t hashIdentifierLength, size_t digestLength) const
{
	if (AllowRecovery())
		return SaturatingSubtract(representativeBitLength, MinRepresentativeBitLength(hashIdentifierLength, digestLength)) / 8;
	return 0;
}

bool PSSR_MEM_Base::IsProbabilistic() const
{
	return SaltLen(1) > 0;
}

bool PSSR_MEM_Base::AllowNonrecoverablePart() const
{
	return true;
}

bool PSSR_MEM_Base::RecoverablePartFirst() const
{
	return false;
}

void PSSR_MEM_Base::ComputeMessageRepresentative(RandomNumberGenerator &rng,
	const byte *recoverableMessage, size_t recoverableMessageLength,
	HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty,
	byte *representative, size_t representativeBitLength) const
{
	CRYPTOPP_UNUSED(rng), CRYPTOPP_UNUSED(recoverableMessage), CRYPTOPP_UNUSED(recoverableMessageLength);
	CRYPTOPP_UNUSED(messageEmpty), CRYPTOPP_UNUSED(hashIdentifier);
	assert(representativeBitLength >= MinRepresentativeBitLength(hashIdentifier.second, hash.DigestSize()));

	const size_t u = hashIdentifier.second + 1;
	const size_t representativeByteLength = BitsToBytes(representativeBitLength);
	const size_t digestSize = hash.DigestSize();
	const size_t saltSize = SaltLen(digestSize);
	byte *const h = representative + representativeByteLength - u - digestSize;

	SecByteBlock digest(digestSize), salt(saltSize);
	hash.Final(digest);
	rng.GenerateBlock(salt, saltSize);

	// compute H = hash of M'
	byte c[8];
	PutWord(false, BIG_ENDIAN_ORDER, c, (word32)SafeRightShift<29>(recoverableMessageLength));
	PutWord(false, BIG_ENDIAN_ORDER, c+4, word32(recoverableMessageLength << 3));
	hash.Update(c, 8);
	hash.Update(recoverableMessage, recoverableMessageLength);
	hash.Update(digest, digestSize);
	hash.Update(salt, saltSize);
	hash.Final(h);

	// compute representative
	GetMGF().GenerateAndMask(hash, representative, representativeByteLength - u - digestSize, h, digestSize, false);
	byte *xorStart = representative + representativeByteLength - u - digestSize - salt.size() - recoverableMessageLength - 1;
	xorStart[0] ^= 1;
	if (recoverableMessage && recoverableMessageLength)
		xorbuf(xorStart + 1, recoverableMessage, recoverableMessageLength);
	xorbuf(xorStart + 1 + recoverableMessageLength, salt, salt.size());
	if (hashIdentifier.first && hashIdentifier.second)
	{
		memcpy(representative + representativeByteLength - u, hashIdentifier.first, hashIdentifier.second);
		representative[representativeByteLength - 1] = 0xcc;
	}
	else
	{
		representative[representativeByteLength - 1] = 0xbc;
	}
	if (representativeBitLength % 8 != 0)
		representative[0] = (byte)Crop(representative[0], representativeBitLength % 8);
}

DecodingResult PSSR_MEM_Base::RecoverMessageFromRepresentative(
	HashTransformation &hash, HashIdentifier hashIdentifier, bool messageEmpty,
	byte *representative, size_t representativeBitLength,
	byte *recoverableMessage) const
{
	CRYPTOPP_UNUSED(recoverableMessage), CRYPTOPP_UNUSED(messageEmpty), CRYPTOPP_UNUSED(hashIdentifier);
	assert(representativeBitLength >= MinRepresentativeBitLength(hashIdentifier.second, hash.DigestSize()));

	const size_t u = hashIdentifier.second + 1;
	const size_t representativeByteLength = BitsToBytes(representativeBitLength);
	const size_t digestSize = hash.DigestSize();
	const size_t saltSize = SaltLen(digestSize);
	const byte *const h = representative + representativeByteLength - u - digestSize;

	SecByteBlock digest(digestSize);
	hash.Final(digest);

	DecodingResult result(0);
	bool &valid = result.isValidCoding;
	size_t &recoverableMessageLength = result.messageLength;

	valid = (representative[representativeByteLength - 1] == (hashIdentifier.second ? 0xcc : 0xbc)) && valid;

	if (hashIdentifier.first && hashIdentifier.second)
		valid = VerifyBufsEqual(representative + representativeByteLength - u, hashIdentifier.first, hashIdentifier.second) && valid;

	GetMGF().GenerateAndMask(hash, representative, representativeByteLength - u - digestSize, h, digestSize);
	if (representativeBitLength % 8 != 0)
		representative[0] = (byte)Crop(representative[0], representativeBitLength % 8);

	// extract salt and recoverableMessage from DB = 00 ... || 01 || M || salt
	byte *salt = representative + representativeByteLength - u - digestSize - saltSize;
	byte *M = std::find_if(representative, salt-1, std::bind2nd(std::not_equal_to<byte>(), byte(0)));
	recoverableMessageLength = salt-M-1;
	if (*M == 0x01 &&
	   (size_t)(M - representative - (representativeBitLength % 8 != 0)) >= MinPadLen(digestSize) &&
	   recoverableMessageLength <= MaxRecoverableLength(representativeBitLength, hashIdentifier.second, digestSize))
	{
		if (recoverableMessage)
			memcpy(recoverableMessage, M+1, recoverableMessageLength);
	}
	else
	{
		recoverableMessageLength = 0;
		valid = false;
	}

	// verify H = hash of M'
	byte c[8];
	PutWord(false, BIG_ENDIAN_ORDER, c, (word32)SafeRightShift<29>(recoverableMessageLength));
	PutWord(false, BIG_ENDIAN_ORDER, c+4, word32(recoverableMessageLength << 3));
	hash.Update(c, 8);
	hash.Update(recoverableMessage, recoverableMessageLength);
	hash.Update(digest, digestSize);
	hash.Update(salt, saltSize);
	valid = hash.Verify(h) && valid;

	if (!AllowRecovery() && valid && recoverableMessageLength != 0)
		{throw NotImplemented("PSSR_MEM: message recovery disabled");}

	return result;
}

#endif

NAMESPACE_END