<commit_before>const autoprefixer    = require('autoprefixer')
const babelify        = require('babelify')
const brfs            = require('brfs')
const browserify      = require('browserify')
const coffee_reactify = require('coffee-reactify')
const crypto          = require('crypto')
const envify          = require('envify/custom')
const fs              = require('fs-extra')
const path            = require('path')
const postcss         = require('postcss')
const Sass            = require('node-sass')
const SDKError        = require('./SDKError')
const sqwish          = require('sqwish')
const sys             = require('sys')
const UglifyJS        = require('uglify-es')
const walkSync        = require('./walkSync')
const { formatProjectPath } = SDKError

const ERROR_STYLES = 'display: block; font-family: monospace; border-bottom: 3px solid red; padding: 24px; background-color: white; white-space: pre;'

function compileCoffee (source_path, dest_path, project_directory, cb) {
    SDKError.log(SDKError.colors.grey(`Compiling (coffee): ${ source_path.replace(project_directory, '') }`))
    const b = browserify([source_path])
    const compiled = b.transform(
        coffee_reactify
    ).transform(
        envify({ NODE_ENV: process.env.NODE_ENV })
    ).transform(brfs).bundle( (err, compiled) => {
        let compilation_error = null
        if (err) {
            console.error(err)
            compilation_error = err
            // console.error(err)
            compilation_error.formatted = `CoffeeScript compilation error:
file: ${ err.filename }

${ err.toString() }`
            compiled = `document.body.innerHTML = '<style>body { ${ ERROR_STYLES } }</style><p>' + decodeURIComponent("${ encodeURIComponent(compilation_error.formatted) }") + '</p>'`
        }
        if ('production' === process.env.NODE_ENV) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source_path.replace(project_directory,'') }`))
            compiled = UglifyJS.minify(compiled.toString()).code
        }
        fs.writeFile(dest_path, compiled, (err) => {
            if (err) {
                throw err
            }
            cb(compilation_error)
        })
    })
}

function compileSass (source_path, dest_path, project_directory, cb) {
    SDKError.log(SDKError.colors.grey(`Compiling (sass): ${ source_path.replace(project_directory, '') }`))
    Sass.render({
        file: source_path,
        includePaths: [
            project_directory,
            path.join(project_directory, 'node_modules', 'proof-sdk', 'stylesheets'),
            path.join(project_directory, 'node_modules', 'proof-contrib', 'stylesheets'),
        ],
    } , (err, compiled) => {
            let output
            let compilation_error = null
            if (err) {
                compilation_error = err
                let _err_message = err.toString()
                _err_message += '\\A    file: ' + err.file.replace(project_directory, '')
                _err_message += '\\A    line: ' + err.line.toString() + ', column: ' + err.column.toString()
                output = `body::before{ content: '${ _err_message }'; ${ ERROR_STYLES }}`
            } else {
                output = compiled.css
            }
            const _prefixing = postcss([autoprefixer]).process(output)
            _prefixing.then( (result) => {
                compiled = result.css
                if ('production' === process.env.NODE_ENV) {
                    SDKError.log(SDKError.colors.grey(`Minifying ${ source_path.replace(project_directory,'') }`))
                    compiled = sqwish.minify(compiled.toString())
                }
                fs.writeFile(dest_path, compiled, (err) => {
                    if (err) {
                        throw err
                    }
                    cb(compilation_error)
                })
            }).catch( (err) => {
                if (err) {
                    throw err
                }
            })
    })
}

function compileJS (source_path, dest_path, project_directory, cb) {
    SDKError.log(SDKError.colors.grey(`Compiling (js/x): ${ source_path.replace(project_directory, '') }`))
    const b = browserify([source_path], { extensions: ['.js', '.jsx', '.es', '.es6']})
    const compiled = b.transform(
        babelify,
        {
            presets: [
                // Require these directly so they can be properly
                // discovered by babel.
                require('babel-preset-react'),
                require('babel-preset-env'),
            ]
        }
    ).transform(
        { global: true },
        envify({ NODE_ENV: process.env.NODE_ENV })
    ).transform(brfs).bundle( (err, compiled) => {
        let compilation_error = null
        if (err) {
            console.error(err)
            compilation_error = err
            // console.error(err)
            compilation_error.formatted = `JS compilation error:
file: ${ err.filename ? err.filename : '?' }
line: ${ err.loc ? err.loc.line : '?' }, column: ${ err.loc ? err.loc.column : '?' }

${ err.toString() }`
            compiled = `document.body.innerHTML = '<style>body { ${ ERROR_STYLES } }</style><p>' + decodeURIComponent("${ encodeURIComponent(compilation_error.formatted) }") + '</p>'`
        }
        let file_data = compiled.toString()

        if ('production' === process.env.NODE_ENV && null == compilation_error) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source_path.replace(project_directory,'') }`))
            file_data = UglifyJS.minify(file_data)
            if (null != file_data.error) {
                console.error(err)
                err = file_data.error
                compilation_error = file_data.error
                compilation_error.formatted = `JS compilation error:
file: ${ err.filename ? err.filename : '?' }
line: ${ err.loc ? err.loc.line : '?' }, column: ${ err.loc ? err.loc.column : '?' }

${ err.toString() }`
                file_data = `document.body.innerHTML = '<style>body { ${ ERROR_STYLES } }</style><p>' + decodeURIComponent("${ encodeURIComponent(compilation_error.formatted) }") + '</p>'`
            } else {
                file_data = file_data.code
            }
        }
        fs.writeFile(dest_path, file_data, (_err) => {
            if (_err) {
                throw _err
            }
            cb(compilation_error)
        })
    })
}

function copyAndMinifyJS (source, destination, project_directory, callback) {
    fs.readFile(source, (err, file_data) => {
        if (err) {
            throw err
        }
        if ('production' === process.env.NODE_ENV) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source.replace(project_directory,'') }`))
            file_data = UglifyJS.minify(file_data.toString()).code
        }
        fs.writeFile(destination, file_data, (err) => {
            if (err) {
                throw err
            }
            callback()
        })
    })
}

function copyAndMinifyCSS (source, destination, project_directory, callback) {
    fs.readFile(source, (err, file_data) => {
        if (err) {
            throw err
        }
        if ('production' === process.env.NODE_ENV) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source.replace(project_directory,'') }`))
            file_data = sqwish.minify(file_data.toString())
        }
        fs.writeFile(destination, file_data, (err) => {
            if (err) {
                throw err
            }
            callback()
        })
    })
}

function copyAsset (source, destination, project_directory, callback) {
    SDKError.log(SDKError.colors.grey(`Copying ${ source.replace(project_directory,'') }`))
    fs.copy(source, destination, () => {
        callback()
    })
}

function processAsset (opts) {
    SDKError.log(`Processing asset: ${ formatProjectPath(opts.project_directory, opts.asset) }`)
    let dest_path = opts.asset.replace(opts.asset_source_dir, opts.asset_cache_dir)
    const path_parts = dest_path.split('.')
    switch (path_parts.pop()) {
        case 'coffee':
            path_parts.push('js')
            dest_path = path_parts.join('.')
            compileCoffee(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'sass':
        case 'scss':
            path_parts.push('css')
            dest_path = path_parts.join('.')
            compileSass(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'jsx':
            path_parts.push('js')
            dest_path = path_parts.join('.')
            compileJS(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'js':
            compileJS(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'css':
            copyAndMinifyCSS(opts.asset, dest_path, opts.project_directory, () => {
                opts.callback()
            })
            break
        default:
            copyAsset(opts.asset, dest_path, opts.project_directory, () => {
                opts.callback()
            })
    }
}

function copyAssetsToBuild (project_directory, asset_cache_dir, asset_dest_dir) {
    const _to_copy = []
    const _names = ['script.js', 'style.css']
    walkSync(asset_cache_dir).forEach( (f) => {
        // The file is script.js, style.css, or a non-script/-style asset.
        if (['js', 'css'].indexOf(f.split('.').pop()) === -1 || _names.indexOf(f.split('/').pop()) > -1) {
            const dest_path = f.replace(asset_cache_dir, asset_dest_dir)
            _to_copy.push({ source: f, destination: dest_path })
        }
    })

    SDKError.log(`Copying ${ SDKError.colors.green(_to_copy.length) } assets to build: ${ formatProjectPath(project_directory, asset_dest_dir) }`)

    _to_copy.forEach( (f) => {
        fs.copySync(f.source, f.destination)
        compileAssets.files_emitted.push(f.destination)
    })
}

// All assets get compiled and placed into .asset-cache. This allows <Asset>
// tags in the compiler to inline certain files. However, only script.js and
// style.css (and any non-js/-css) get copied into the build output folder.
function compileAssets (opts) {
    // Reset the count each run.
    compileAssets.files_emitted = []

    const {
        project_directory,
        build_directory,
        callback,
        hash_files,
        project_config,
        build_cache,
    } = opts

    const { allow_asset_errors } = opts.command_options

    if (build_cache && build_cache.get('assets/*')) {
        callback(build_cache.get('assets/*'))
        return
    }

    const asset_source_dir    = path.join(project_directory, 'assets')
    const asset_cache_dir     = path.join(project_directory, '.asset-cache')
    if (project_config.ROOT_PREFIX) {
        asset_dest_dir = path.join(build_directory, project_config.ROOT_PREFIX, 'assets')
    } else {
        asset_dest_dir = path.join(build_directory, 'assets')
    }

    // Asset folder is not strictly required, so only warn if it doesn't exist.
    if (!fs.existsSync(asset_source_dir)) {
        SDKError.warn('assets', 'No project ./assets/ folder found.')
        callback && callback(null)
        return
    }

    let asset_hash = null
    if (hash_files) {
        // Hash all the compiled assets, not just the auto ones.
        const compiled_assets = walkSync(asset_source_dir)
        const hash = crypto.createHash('md5')
        compiled_assets.sort()
        compiled_assets.forEach( (asset_path) => {
            const _source_content = fs.readFileSync(asset_path)
            hash.update(_source_content, 'binary')
        })
        asset_hash = hash.digest('hex')
        asset_dest_dir = path.join(asset_dest_dir, asset_hash)
    }

    build_cache && build_cache.set('assets/*', asset_hash)

    fs.ensureDirSync(asset_cache_dir)
    fs.ensureDirSync(asset_dest_dir)

    // Ignore _ prefixed files to prevent trying to compile imported sass files
    // on their own.
    const assets = walkSync(asset_source_dir, ['_','.'])
    let to_process = assets.length

    assets.forEach((asset) => {
        processAsset({
            asset_source_dir    : asset_source_dir,
            asset_cache_dir     : asset_cache_dir,
            asset_dest_dir      : asset_dest_dir,
            asset               : asset,
            project_directory   : project_directory,
            callback: (compilation_error) => {
                if (null != compilation_error) {
                    console.log('\n')
                    console.error(compilation_error.formatted || compilation_error)
                    console.log('\n')
                    if (!allow_asset_errors) {
                        process.exit(1)
                    }
                }
                to_process -= 1
                if (0 === to_process) {
                    copyAssetsToBuild(project_directory, asset_cache_dir, asset_dest_dir)
                    callback && callback(asset_hash)
                }
            },
        })
    })
}

compileAssets.files_emitted = []

// Export processAsset so it can be used directly, bypassing path definitions.
compileAssets.processAsset = processAsset

compileAssets.includeAssets = (opts) => {
    const {
        project_directory,
        build_directory,
        assets,
        asset_hash,
    } = opts

    assets.forEach( (f) => {
        let _f = f.split('.')
        const _ext = _f.pop()
        switch (_ext) {
            case 'coffee':
            case 'cjsx':
            case 'jsx':
                _f.push('js')
                break
            case 'sass':
            case 'scss':
                _f.push('css')
                break
            default:
                _f.push(_ext)
        }
        _f = _f.join('.')
        const _source = path.join(project_directory, '.asset-cache', _f)
        let _dest
        if (asset_hash) {
            _dest = path.join(build_directory, 'assets', asset_hash, _f)
        } else {
            _dest = path.join(build_directory, 'assets', _f)
        }
        if (!fs.existsSync(_source)) {
            throw new SDKError('assets.emitAssets', `Asset specified by emitAssets not found: ${ f }`)
        }
        SDKError.log(SDKError.colors.grey(`Including asset: ${ f }`))
        fs.copySync(_source, _dest)
        compileAssets.files_emitted.push(_dest)
    })
}
module.exports = compileAssets<commit_msg>Add flow transform to asset compilation.<commit_after>const autoprefixer    = require('autoprefixer')
const babelify        = require('babelify')
const brfs            = require('brfs')
const browserify      = require('browserify')
const coffee_reactify = require('coffee-reactify')
const crypto          = require('crypto')
const envify          = require('envify/custom')
const fs              = require('fs-extra')
const path            = require('path')
const postcss         = require('postcss')
const Sass            = require('node-sass')
const SDKError        = require('./SDKError')
const sqwish          = require('sqwish')
const sys             = require('sys')
const UglifyJS        = require('uglify-es')
const walkSync        = require('./walkSync')
const { formatProjectPath } = SDKError

const ERROR_STYLES = 'display: block; font-family: monospace; border-bottom: 3px solid red; padding: 24px; background-color: white; white-space: pre;'

function compileCoffee (source_path, dest_path, project_directory, cb) {
    SDKError.log(SDKError.colors.grey(`Compiling (coffee): ${ source_path.replace(project_directory, '') }`))
    const b = browserify([source_path])
    const compiled = b.transform(
        coffee_reactify
    ).transform(
        envify({ NODE_ENV: process.env.NODE_ENV })
    ).transform(brfs).bundle( (err, compiled) => {
        let compilation_error = null
        if (err) {
            console.error(err)
            compilation_error = err
            // console.error(err)
            compilation_error.formatted = `CoffeeScript compilation error:
file: ${ err.filename }

${ err.toString() }`
            compiled = `document.body.innerHTML = '<style>body { ${ ERROR_STYLES } }</style><p>' + decodeURIComponent("${ encodeURIComponent(compilation_error.formatted) }") + '</p>'`
        }
        if ('production' === process.env.NODE_ENV) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source_path.replace(project_directory,'') }`))
            compiled = UglifyJS.minify(compiled.toString()).code
        }
        fs.writeFile(dest_path, compiled, (err) => {
            if (err) {
                throw err
            }
            cb(compilation_error)
        })
    })
}

function compileSass (source_path, dest_path, project_directory, cb) {
    SDKError.log(SDKError.colors.grey(`Compiling (sass): ${ source_path.replace(project_directory, '') }`))
    Sass.render({
        file: source_path,
        includePaths: [
            project_directory,
            path.join(project_directory, 'node_modules', 'proof-sdk', 'stylesheets'),
            path.join(project_directory, 'node_modules', 'proof-contrib', 'stylesheets'),
        ],
    } , (err, compiled) => {
            let output
            let compilation_error = null
            if (err) {
                compilation_error = err
                let _err_message = err.toString()
                _err_message += '\\A    file: ' + err.file.replace(project_directory, '')
                _err_message += '\\A    line: ' + err.line.toString() + ', column: ' + err.column.toString()
                output = `body::before{ content: '${ _err_message }'; ${ ERROR_STYLES }}`
            } else {
                output = compiled.css
            }
            const _prefixing = postcss([autoprefixer]).process(output)
            _prefixing.then( (result) => {
                compiled = result.css
                if ('production' === process.env.NODE_ENV) {
                    SDKError.log(SDKError.colors.grey(`Minifying ${ source_path.replace(project_directory,'') }`))
                    compiled = sqwish.minify(compiled.toString())
                }
                fs.writeFile(dest_path, compiled, (err) => {
                    if (err) {
                        throw err
                    }
                    cb(compilation_error)
                })
            }).catch( (err) => {
                if (err) {
                    throw err
                }
            })
    })
}

function compileJS (source_path, dest_path, project_directory, cb) {
    SDKError.log(SDKError.colors.grey(`Compiling (js/x): ${ source_path.replace(project_directory, '') }`))
    const b = browserify([source_path], { extensions: ['.js', '.jsx', '.es', '.es6']})
    const compiled = b.transform(
        babelify,
        {
            presets: [
                // Require these directly so they can be properly
                // discovered by babel.
                require('babel-preset-flow'),
                require('babel-preset-react'),
                require('babel-preset-env'),
            ]
        }
    ).transform(
        { global: true },
        envify({ NODE_ENV: process.env.NODE_ENV })
    ).transform(brfs).bundle( (err, compiled) => {
        let compilation_error = null
        if (err) {
            console.error(err)
            compilation_error = err
            // console.error(err)
            compilation_error.formatted = `JS compilation error:
file: ${ err.filename ? err.filename : '?' }
line: ${ err.loc ? err.loc.line : '?' }, column: ${ err.loc ? err.loc.column : '?' }

${ err.toString() }`
            compiled = `document.body.innerHTML = '<style>body { ${ ERROR_STYLES } }</style><p>' + decodeURIComponent("${ encodeURIComponent(compilation_error.formatted) }") + '</p>'`
        }
        let file_data = compiled.toString()

        if ('production' === process.env.NODE_ENV && null == compilation_error) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source_path.replace(project_directory,'') }`))
            file_data = UglifyJS.minify(file_data)
            if (null != file_data.error) {
                console.error(err)
                err = file_data.error
                compilation_error = file_data.error
                compilation_error.formatted = `JS compilation error:
file: ${ err.filename ? err.filename : '?' }
line: ${ err.loc ? err.loc.line : '?' }, column: ${ err.loc ? err.loc.column : '?' }

${ err.toString() }`
                file_data = `document.body.innerHTML = '<style>body { ${ ERROR_STYLES } }</style><p>' + decodeURIComponent("${ encodeURIComponent(compilation_error.formatted) }") + '</p>'`
            } else {
                file_data = file_data.code
            }
        }
        fs.writeFile(dest_path, file_data, (_err) => {
            if (_err) {
                throw _err
            }
            cb(compilation_error)
        })
    })
}

function copyAndMinifyJS (source, destination, project_directory, callback) {
    fs.readFile(source, (err, file_data) => {
        if (err) {
            throw err
        }
        if ('production' === process.env.NODE_ENV) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source.replace(project_directory,'') }`))
            file_data = UglifyJS.minify(file_data.toString()).code
        }
        fs.writeFile(destination, file_data, (err) => {
            if (err) {
                throw err
            }
            callback()
        })
    })
}

function copyAndMinifyCSS (source, destination, project_directory, callback) {
    fs.readFile(source, (err, file_data) => {
        if (err) {
            throw err
        }
        if ('production' === process.env.NODE_ENV) {
            SDKError.log(SDKError.colors.grey(`Minifying ${ source.replace(project_directory,'') }`))
            file_data = sqwish.minify(file_data.toString())
        }
        fs.writeFile(destination, file_data, (err) => {
            if (err) {
                throw err
            }
            callback()
        })
    })
}

function copyAsset (source, destination, project_directory, callback) {
    SDKError.log(SDKError.colors.grey(`Copying ${ source.replace(project_directory,'') }`))
    fs.copy(source, destination, () => {
        callback()
    })
}

function processAsset (opts) {
    SDKError.log(`Processing asset: ${ formatProjectPath(opts.project_directory, opts.asset) }`)
    let dest_path = opts.asset.replace(opts.asset_source_dir, opts.asset_cache_dir)
    const path_parts = dest_path.split('.')
    switch (path_parts.pop()) {
        case 'coffee':
            path_parts.push('js')
            dest_path = path_parts.join('.')
            compileCoffee(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'sass':
        case 'scss':
            path_parts.push('css')
            dest_path = path_parts.join('.')
            compileSass(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'jsx':
            path_parts.push('js')
            dest_path = path_parts.join('.')
            compileJS(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'js':
            compileJS(opts.asset, dest_path, opts.project_directory, (err) => {
                opts.callback(err)
            })
            break
        case 'css':
            copyAndMinifyCSS(opts.asset, dest_path, opts.project_directory, () => {
                opts.callback()
            })
            break
        default:
            copyAsset(opts.asset, dest_path, opts.project_directory, () => {
                opts.callback()
            })
    }
}

function copyAssetsToBuild (project_directory, asset_cache_dir, asset_dest_dir) {
    const _to_copy = []
    const _names = ['script.js', 'style.css']
    walkSync(asset_cache_dir).forEach( (f) => {
        // The file is script.js, style.css, or a non-script/-style asset.
        if (['js', 'css'].indexOf(f.split('.').pop()) === -1 || _names.indexOf(f.split('/').pop()) > -1) {
            const dest_path = f.replace(asset_cache_dir, asset_dest_dir)
            _to_copy.push({ source: f, destination: dest_path })
        }
    })

    SDKError.log(`Copying ${ SDKError.colors.green(_to_copy.length) } assets to build: ${ formatProjectPath(project_directory, asset_dest_dir) }`)

    _to_copy.forEach( (f) => {
        fs.copySync(f.source, f.destination)
        compileAssets.files_emitted.push(f.destination)
    })
}

// All assets get compiled and placed into .asset-cache. This allows <Asset>
// tags in the compiler to inline certain files. However, only script.js and
// style.css (and any non-js/-css) get copied into the build output folder.
function compileAssets (opts) {
    // Reset the count each run.
    compileAssets.files_emitted = []

    const {
        project_directory,
        build_directory,
        callback,
        hash_files,
        project_config,
        build_cache,
    } = opts

    const { allow_asset_errors } = opts.command_options

    if (build_cache && build_cache.get('assets/*')) {
        callback(build_cache.get('assets/*'))
        return
    }

    const asset_source_dir    = path.join(project_directory, 'assets')
    const asset_cache_dir     = path.join(project_directory, '.asset-cache')
    if (project_config.ROOT_PREFIX) {
        asset_dest_dir = path.join(build_directory, project_config.ROOT_PREFIX, 'assets')
    } else {
        asset_dest_dir = path.join(build_directory, 'assets')
    }

    // Asset folder is not strictly required, so only warn if it doesn't exist.
    if (!fs.existsSync(asset_source_dir)) {
        SDKError.warn('assets', 'No project ./assets/ folder found.')
        callback && callback(null)
        return
    }

    let asset_hash = null
    if (hash_files) {
        // Hash all the compiled assets, not just the auto ones.
        const compiled_assets = walkSync(asset_source_dir)
        const hash = crypto.createHash('md5')
        compiled_assets.sort()
        compiled_assets.forEach( (asset_path) => {
            const _source_content = fs.readFileSync(asset_path)
            hash.update(_source_content, 'binary')
        })
        asset_hash = hash.digest('hex')
        asset_dest_dir = path.join(asset_dest_dir, asset_hash)
    }

    build_cache && build_cache.set('assets/*', asset_hash)

    fs.ensureDirSync(asset_cache_dir)
    fs.ensureDirSync(asset_dest_dir)

    // Ignore _ prefixed files to prevent trying to compile imported sass files
    // on their own.
    const assets = walkSync(asset_source_dir, ['_','.'])
    let to_process = assets.length

    assets.forEach((asset) => {
        processAsset({
            asset_source_dir    : asset_source_dir,
            asset_cache_dir     : asset_cache_dir,
            asset_dest_dir      : asset_dest_dir,
            asset               : asset,
            project_directory   : project_directory,
            callback: (compilation_error) => {
                if (null != compilation_error) {
                    console.log('\n')
                    console.error(compilation_error.formatted || compilation_error)
                    console.log('\n')
                    if (!allow_asset_errors) {
                        process.exit(1)
                    }
                }
                to_process -= 1
                if (0 === to_process) {
                    copyAssetsToBuild(project_directory, asset_cache_dir, asset_dest_dir)
                    callback && callback(asset_hash)
                }
            },
        })
    })
}

compileAssets.files_emitted = []

// Export processAsset so it can be used directly, bypassing path definitions.
compileAssets.processAsset = processAsset

compileAssets.includeAssets = (opts) => {
    const {
        project_directory,
        build_directory,
        assets,
        asset_hash,
    } = opts

    assets.forEach( (f) => {
        let _f = f.split('.')
        const _ext = _f.pop()
        switch (_ext) {
            case 'coffee':
            case 'cjsx':
            case 'jsx':
                _f.push('js')
                break
            case 'sass':
            case 'scss':
                _f.push('css')
                break
            default:
                _f.push(_ext)
        }
        _f = _f.join('.')
        const _source = path.join(project_directory, '.asset-cache', _f)
        let _dest
        if (asset_hash) {
            _dest = path.join(build_directory, 'assets', asset_hash, _f)
        } else {
            _dest = path.join(build_directory, 'assets', _f)
        }
        if (!fs.existsSync(_source)) {
            throw new SDKError('assets.emitAssets', `Asset specified by emitAssets not found: ${ f }`)
        }
        SDKError.log(SDKError.colors.grey(`Including asset: ${ f }`))
        fs.copySync(_source, _dest)
        compileAssets.files_emitted.push(_dest)
    })
}
module.exports = compileAssets<|endoftext|>


<commit_before>
'use strict';

(function() {

  // Pseudo-constants:
  var VERSION             = '0.2.0';
  var BASE_URL            = 'https://api-test.w3.org/';
  var APIARY_PLACEHOLDER  = /[\^\ ]apiary-([^\ ]+)/g;
  var TYPE_DOMAIN_PAGE    = 1;
  var TYPE_GROUP_PAGE     = 2;
  var TYPE_USER_PAGE      = 3;
  var TYPE_ACTIVITIES     = 4;
  var TYPE_CHAIRS         = 5;
  var TYPE_SPECIFICATIONS = 6;

  // “Global” variables:
  var apiKey;
  var type;
  var id;
  var placeholders = {};
  var data         = {};

  /**
   * Main function.
   *
   * I know everything you need to know, baby.
   */

  $(document).ready(function() {

    inferTypeAndId();

    if (apiKey && type && id) {
      findPlaceholders();
      getDataForType(type, id, injectValues);
    } else {
      window.alert('Apiary ' + VERSION + '\n' +
        'ERROR: could not get all necessary metadata.\n' +
        'apiKey: “' + apiKey + '”\n' +
        'type: “' + type + '”\n' +
        'id: “' + id + '”');
    }

  });

  /**
   * Infer the type of page (domain, group…) and the ID of the corresponding entity; resolve API key.
   *
   * After this function is done, variables “apiKey”, “type” and “id” should have their right values set.
   */

  var inferTypeAndId = function() {

    if (1 === $('html[data-api-key]').length) {
      apiKey = $('html[data-api-key]').data('api-key');
    }

    if ($('[data-domain-id]').length > 0) {
      type = TYPE_DOMAIN_PAGE;
      id = $('[data-domain-id]').data('domain-id');
    } else if ($('[data-group-id]').length > 0) {
      type = TYPE_GROUP_PAGE;
      id = $('[data-group-id]').data('group-id');
    } else if ($('[data-user-id]').length > 0) {
      type = TYPE_USER_PAGE;
      id = $('[data-user-id]').data('user-id');
    }

  };

  /**
   * Traverse the DOM in search of all elements with class “apiary-*”.
   *
   * After this function is done, “placeholders” should be an object containing all keys found in the DOM;
   * and for every key, an array of all elements mentioning that key.
   * For example:
   * {
   *   name: [
   *     <title> element,
   *
 element
   *   ],
   *   lead: [
 element],
   *   activities: [
 element]
   * }
   */

  var findPlaceholders = function() {
    var candidates = $('[class^="apiary"]');
    var cand, match;
    for (var c = 0; c < candidates.length; c ++) {
      cand = $(candidates[c]);
      match = APIARY_PLACEHOLDER.exec(cand.attr('class'));
      while (match) {
        if (!placeholders[match[1]]) {
          placeholders[match[1]] = [];
        }
        placeholders[match[1]].push(cand);
        match = APIARY_PLACEHOLDER.exec(cand.attr('class'));
      }
    }
  };

  /**
   * Get basic data for a particular entity from the W3C API, given a type of item and its value.
   *
   * @param {TYPE}     item     type of entity, eg TYPE_DOMAIN_PAGE
   * @param {Object}   value    ID of the entity, eg “1234”
   * @param {Function} callback eg injectValues
   */

  var getDataForType = function(item, value, callback) {
    if (Object.keys(placeholders).length > 0) {
      if (TYPE_DOMAIN_PAGE === item) {
        get(BASE_URL + 'domains/' + value, function(json) {
          data.name = json.name;
          data.lead = json._links.lead.title;
          if (placeholders.activities) {
            digDownData(TYPE_ACTIVITIES, json._links.activities.href, callback);
          } else if (callback) {
            callback.call();
          }
        });
      } else if (TYPE_GROUP_PAGE === item) {
        get(BASE_URL + 'groups/' + value, function(json) {
          data.name = json.name;
          data.type = json.type;
          data.description = json.description;
          if (placeholders.chairs) {
            digDownData(TYPE_CHAIRS, json._links.chairs.href, callback);
          } else if (callback) {
            callback.call();
          }
        });
      } else if (TYPE_USER_PAGE === item) {
        get(BASE_URL + 'users/' + value, function(json) {
          data.name = json.name;
          data.family = json.family;
          data.given = json.given;
          data.photo = '';
          if (placeholders.specifications) {
            digDownData(TYPE_SPECIFICATIONS, json._links.specifications.href, callback);
          } else if (callback) {
            callback.call();
          }
        });
      } else {
        if (callback) {
          callback.call();
        }
      }
    } else {
      if (callback) {
        callback.call();
      }
    }
  };

  /**
   * Get data recursively, given a URL and the type of data.
   *
   * @param {TYPE}     item     eg TYPE_ACTIVITIES
   * @param {String}   url      eg “https://api-test.w3.org/domains/109/activities”
   * @param {Function} callback eg injectValues
   */

  var digDownData = function(item, url, callback) {
    var list, i;
    get(url, function(json) {
      if (TYPE_ACTIVITIES === item) {
        list = '
';
        for (i = 0; i < json._links.activities.length; i ++) {
          list += '
' + json._links.activities[i].title + '
';
        }
        list += '
';
        data.activities = list;
      } else if (TYPE_CHAIRS === item) {
        list = '
';
        for (i = 0; i < json._links.chairs.length; i ++) {
          list += '
' + json._links.chairs[i].title + '
';
        }
        list += '
';
        data.chairs = list;
      } else if (TYPE_SPECIFICATIONS === item) {
        list = '
';
        for (i = 0; i < json._links.specifications.length; i ++) {
          list += '
' + json._links.specifications[i].title + '
';
        }
        list += '
';
        data.specifications = list;
      }
      if (callback) {
        callback.call();
      }
    });
  };

  /**
   * Inject values retrieved from the API into the relevant elements of the DOM.
   * While dynamically writing content, set aria-busy attribute to “true” for screen-readers.
   */

  var injectValues = function() {
    var live = $('[aria-live]');
    if (live) {
      live.attr('aria-busy', 'true');
    }
    for (var i in placeholders) {
      if (placeholders.hasOwnProperty(i)) {
        for (var j in placeholders[i]) {
          placeholders[i][j].html(data[i]);
        }
      }
    }
    if (live) {
      live.attr('aria-busy', 'false');
    }
  };

  /**
   * Find the largest photo available from an array of them.
   *
   * @param {String}   url      target URL, including base URL and parameters, but not an API key.
   * @param {Function} callback function(json){}
   */

  var get = function(url, callback) {

    $.ajax({
      headers: {'Authorization': 'W3C-API apikey="' + apiKey + '"'},
      url:     url,
      success: callback
    });

  };

  /**
   * Find the largest photo available from an array of them.
   *
   * @param {Array} photos eg, [{name: 'large', href: 'size-L.jpg'}, {name: 'medium', href: 'size-M.jpg'}]
   */

  var getLargestPhotoUrl = function(photos) {
    var VALUE = {large: 2, thumbnail: 1, tiny: 0};
    var result;
    for (var i = 0; i < photos.length; i ++) {
      if (!result || VALUE[photos[i].name] > VALUE[result.name]) {
        result = photos[i];
      }
    }
    return result.href;
  };

})();

<commit_msg>Removed dynamic aria-busy toggle<commit_after>
'use strict';

(function() {

  // Pseudo-constants:
  var VERSION             = '0.2.0';
  var BASE_URL            = 'https://api-test.w3.org/';
  var APIARY_PLACEHOLDER  = /[\^\ ]apiary-([^\ ]+)/g;
  var TYPE_DOMAIN_PAGE    = 1;
  var TYPE_GROUP_PAGE     = 2;
  var TYPE_USER_PAGE      = 3;
  var TYPE_ACTIVITIES     = 4;
  var TYPE_CHAIRS         = 5;
  var TYPE_SPECIFICATIONS = 6;

  // “Global” variables:
  var apiKey;
  var type;
  var id;
  var placeholders = {};
  var data         = {};

  /**
   * Main function.
   *
   * I know everything you need to know, baby.
   */

  $(document).ready(function() {

    inferTypeAndId();

    if (apiKey && type && id) {
      findPlaceholders();
      getDataForType(type, id, injectValues);
    } else {
      window.alert('Apiary ' + VERSION + '\n' +
        'ERROR: could not get all necessary metadata.\n' +
        'apiKey: “' + apiKey + '”\n' +
        'type: “' + type + '”\n' +
        'id: “' + id + '”');
    }

  });

  /**
   * Infer the type of page (domain, group…) and the ID of the corresponding entity; resolve API key.
   *
   * After this function is done, variables “apiKey”, “type” and “id” should have their right values set.
   */

  var inferTypeAndId = function() {

    if (1 === $('html[data-api-key]').length) {
      apiKey = $('html[data-api-key]').data('api-key');
    }

    if ($('[data-domain-id]').length > 0) {
      type = TYPE_DOMAIN_PAGE;
      id = $('[data-domain-id]').data('domain-id');
    } else if ($('[data-group-id]').length > 0) {
      type = TYPE_GROUP_PAGE;
      id = $('[data-group-id]').data('group-id');
    } else if ($('[data-user-id]').length > 0) {
      type = TYPE_USER_PAGE;
      id = $('[data-user-id]').data('user-id');
    }

  };

  /**
   * Traverse the DOM in search of all elements with class “apiary-*”.
   *
   * After this function is done, “placeholders” should be an object containing all keys found in the DOM;
   * and for every key, an array of all elements mentioning that key.
   * For example:
   * {
   *   name: [
   *     <title> element,
   *
 element
   *   ],
   *   lead: [
 element],
   *   activities: [
 element]
   * }
   */

  var findPlaceholders = function() {
    var candidates = $('[class^="apiary"]');
    var cand, match;
    for (var c = 0; c < candidates.length; c ++) {
      cand = $(candidates[c]);
      match = APIARY_PLACEHOLDER.exec(cand.attr('class'));
      while (match) {
        if (!placeholders[match[1]]) {
          placeholders[match[1]] = [];
        }
        placeholders[match[1]].push(cand);
        match = APIARY_PLACEHOLDER.exec(cand.attr('class'));
      }
    }
  };

  /**
   * Get basic data for a particular entity from the W3C API, given a type of item and its value.
   *
   * @param {TYPE}     item     type of entity, eg TYPE_DOMAIN_PAGE
   * @param {Object}   value    ID of the entity, eg “1234”
   * @param {Function} callback eg injectValues
   */

  var getDataForType = function(item, value, callback) {
    if (Object.keys(placeholders).length > 0) {
      if (TYPE_DOMAIN_PAGE === item) {
        get(BASE_URL + 'domains/' + value, function(json) {
          data.name = json.name;
          data.lead = json._links.lead.title;
          if (placeholders.activities) {
            digDownData(TYPE_ACTIVITIES, json._links.activities.href, callback);
          } else if (callback) {
            callback.call();
          }
        });
      } else if (TYPE_GROUP_PAGE === item) {
        get(BASE_URL + 'groups/' + value, function(json) {
          data.name = json.name;
          data.type = json.type;
          data.description = json.description;
          if (placeholders.chairs) {
            digDownData(TYPE_CHAIRS, json._links.chairs.href, callback);
          } else if (callback) {
            callback.call();
          }
        });
      } else if (TYPE_USER_PAGE === item) {
        get(BASE_URL + 'users/' + value, function(json) {
          data.name = json.name;
          data.family = json.family;
          data.given = json.given;
          data.photo = '';
          if (placeholders.specifications) {
            digDownData(TYPE_SPECIFICATIONS, json._links.specifications.href, callback);
          } else if (callback) {
            callback.call();
          }
        });
      } else {
        if (callback) {
          callback.call();
        }
      }
    } else {
      if (callback) {
        callback.call();
      }
    }
  };

  /**
   * Get data recursively, given a URL and the type of data.
   *
   * @param {TYPE}     item     eg TYPE_ACTIVITIES
   * @param {String}   url      eg “https://api-test.w3.org/domains/109/activities”
   * @param {Function} callback eg injectValues
   */

  var digDownData = function(item, url, callback) {
    var list, i;
    get(url, function(json) {
      if (TYPE_ACTIVITIES === item) {
        list = '
';
        for (i = 0; i < json._links.activities.length; i ++) {
          list += '
' + json._links.activities[i].title + '
';
        }
        list += '
';
        data.activities = list;
      } else if (TYPE_CHAIRS === item) {
        list = '
';
        for (i = 0; i < json._links.chairs.length; i ++) {
          list += '
' + json._links.chairs[i].title + '
';
        }
        list += '
';
        data.chairs = list;
      } else if (TYPE_SPECIFICATIONS === item) {
        list = '
';
        for (i = 0; i < json._links.specifications.length; i ++) {
          list += '
' + json._links.specifications[i].title + '
';
        }
        list += '
';
        data.specifications = list;
      }
      if (callback) {
        callback.call();
      }
    });
  };

  /**
   * Inject values retrieved from the API into the relevant elements of the DOM.
   */

  var injectValues = function() {
    for (var i in placeholders) {
      if (placeholders.hasOwnProperty(i)) {
        for (var j in placeholders[i]) {
          placeholders[i][j].html(data[i]);
        }
      }
    }
  };

  /**
   * Find the largest photo available from an array of them.
   *
   * @param {String}   url      target URL, including base URL and parameters, but not an API key.
   * @param {Function} callback function(json){}
   */

  var get = function(url, callback) {

    $.ajax({
      headers: {'Authorization': 'W3C-API apikey="' + apiKey + '"'},
      url:     url,
      success: callback
    });

  };

  /**
   * Find the largest photo available from an array of them.
   *
   * @param {Array} photos eg, [{name: 'large', href: 'size-L.jpg'}, {name: 'medium', href: 'size-M.jpg'}]
   */

  var getLargestPhotoUrl = function(photos) {
    var VALUE = {large: 2, thumbnail: 1, tiny: 0};
    var result;
    for (var i = 0; i < photos.length; i ++) {
      if (!result || VALUE[photos[i].name] > VALUE[result.name]) {
        result = photos[i];
      }
    }
    return result.href;
  };

})();

<|endoftext|>


<commit_before>console.log('Service Worker started: ', self);

var urlsToCache = [
    '/',
    '/css/style.min.css',
    '/js/main.min.js',
    '/images/hand-of-god.png',
    '/images/just-the-tip.png',
    '/images/polka-pattern.png',
    '/images/ronco.png',
    '/sounds/gagging.mp3',
    '/sounds/pop.mp3',
    // '/sounds/ah-ma-e-ronco.mp3'
];

self.addEventListener('install', function(event) {
    console.log('Installed: ', event);

    event.waitUntil(
        caches.open('ronco').then(function(cache) {
            console.log('Opened cache: ', cache);

            return cache.addAll(urlsToCache).then(function() {
                return self.skipWaiting();
            });

        })
    );
});

self.addEventListener('activate', function(event) {
    console.log('Activated: ', event);

    // Sets itself as main Service Worker
    event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', function(event) {
    console.log('Fetch: ', event);

    event.respondWith(
        caches.match(event.request).then(function(response) {
            // Cache hit - return response, otherwhise make a new request
            return response || fetch(event.request);

        })
    );
});<commit_msg>added cache versioning to sw<commit_after>console.log('Service Worker started: ', self);

var CACHE_VERSION = 'ronco-v1.0.0';
var urlsToCache = [
    '/',
    '/css/style.min.css',
    '/js/main.min.js',
    '/images/hand-of-god.png',
    '/images/just-the-tip.png',
    '/images/polka-pattern.png',
    '/images/ronco.png',
    '/sounds/gagging.mp3',
    '/sounds/pop.mp3',
    // '/sounds/ah-ma-e-ronco.mp3'
];

self.addEventListener('install', function(event) {
    console.log('Installed: ', event);

    event.waitUntil(
        caches.open(CACHE_VERSION).then(function(cache) {
            console.log('Opened cache: ', cache);

            return cache.addAll(urlsToCache).then(function() {
                return self.skipWaiting();
            });

        })
    );
});

self.addEventListener('activate', function(event) {
    console.log('Activated: ', event);

    event.waitUntil(
        caches.keys().then(function(cacheNames) {
            return Promise.all(cacheNames.map(function(cacheName) {
                if (cacheName !== CACHE_VERSION) {
                    return caches.delete(cacheName);
                }
            })).then(function() {
                // Sets itself as main Service Worker
                return self.clients.claim();
            });
        })
    );
});

self.addEventListener('fetch', function(event) {
    console.log('Fetch: ', event);

    event.respondWith(
        caches.match(event.request).then(function(response) {
            // Cache hit - return response, otherwhise make a new request
            return response || fetch(event.request);
        })
    );
});<|endoftext|>


<commit_before>/**
 * TODO: #timeline -> event page -> history back (and reverse), no view data is cached
 * TODO: which table columns are always shown (how to render various event types)
 * TODO: Add file logging for 404/500 (http://expressjs.com/guide.html#error-handling)
 */

var _ = require('underscore');
var express = require('express');
var app = express.createServer();
var storage = require(__dirname + '/modules/storage/mongodb.js');
var config = require(__dirname + '/../config/config.js').read();

// http://stackoverflow.com/questions/6825325/example-of-node-js-express-registering-underscore-js-as-view-engine
app.register('.html', {
  compile: function (str, options) {
    var template = _.template(str);
    return function (locals) {
      return template(locals);
    };
  }
});

// http://japhr.blogspot.com/2011/10/underscorejs-templates-in-backbonejs.html
_.templateSettings = {
  evaluate : /\{\[([\s\S]+?)\]\}/g,
  interpolate : /\{\{([\s\S]+?)\}\}/g
};

// NODE_ENV=development node app/ui.js
app.configure('development', function() {
  console.log('environment: dev');
  var path = require('path');
  var dust = require('dust');
  var fs = require('fs');
  var templates = fs.readdirSync(__dirname + '/views');
  var fd = fs.openSync(__dirname + '/../public/templates/compiled.js', 'a');
  _.each(templates, function(template) {
    console.log('template: ' + template);
    var templateName = path.basename(template, '.html');
    var compiled = dust.compile(fs.readFileSync(__dirname + '/views/' + template, 'UTF-8'), templateName);
    dust.loadSource(compiled);
    fs.writeSync(fd, compiled, null, 'utf8');
    //fs.writeFileSync(path.join(__dirname + '/../public/templates', templateName + '.js'), compiled);
    //fs.writeFileSync(path.join(__dirname + '/../public/templates/compiled.js'), compiled);
  });
});

app.use(express.static(__dirname + '/../public'));
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
app.set('view options', { layout: false });

app.get('/', function(req, res) {
  res.render('index');
});

app.get('/timeline', function(req, res) {
  storage.get_timeline(req.query, function(err, documents) {
    res.send(documents);
  });
});

app.get('/event/:id', function(req, res) {
  if (!req.params.id.match(/^[a-z0-9]{24}$/)) {
    res.send(null);
  }
  storage.get_log(req.params.id, function(err, document) {
    res.send(document);
  });
});

app.listen(8080, '127.0.0.1');
<commit_msg>Add todos<commit_after>/**
 * TODO: import.js hangs on /tmp/php.log -- need way to mass-insert async
 * TODO: which table columns are always shown (how to render various event types)
 * TODO: Add file logging for 404/500 (http://expressjs.com/guide.html#error-handling)
 * TODO: Add 'err' handling to mongodb.js
 */

var _ = require('underscore');
var express = require('express');
var app = express.createServer();
var storage = require(__dirname + '/modules/storage/mongodb.js');
var config = require(__dirname + '/../config/config.js').read();

// http://stackoverflow.com/questions/6825325/example-of-node-js-express-registering-underscore-js-as-view-engine
app.register('.html', {
  compile: function (str, options) {
    var template = _.template(str);
    return function (locals) {
      return template(locals);
    };
  }
});

// http://japhr.blogspot.com/2011/10/underscorejs-templates-in-backbonejs.html
_.templateSettings = {
  evaluate : /\{\[([\s\S]+?)\]\}/g,
  interpolate : /\{\{([\s\S]+?)\}\}/g
};

// NODE_ENV=development node app/ui.js
app.configure('development', function() {
  console.log('environment: dev');
  var path = require('path');
  var dust = require('dust');
  var fs = require('fs');
  var templates = fs.readdirSync(__dirname + '/views');
  var fd = fs.openSync(__dirname + '/../public/templates/compiled.js', 'a');
  _.each(templates, function(template) {
    console.log('template: ' + template);
    var templateName = path.basename(template, '.html');
    var compiled = dust.compile(fs.readFileSync(__dirname + '/views/' + template, 'UTF-8'), templateName);
    dust.loadSource(compiled);
    fs.writeSync(fd, compiled, null, 'utf8');
    //fs.writeFileSync(path.join(__dirname + '/../public/templates', templateName + '.js'), compiled);
    //fs.writeFileSync(path.join(__dirname + '/../public/templates/compiled.js'), compiled);
  });
});

app.use(express.static(__dirname + '/../public'));
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
app.set('view options', { layout: false });

app.get('/', function(req, res) {
  res.render('index');
});

app.get('/timeline', function(req, res) {
  storage.get_timeline(req.query, function(err, documents) {
    res.send(documents);
  });
});

app.get('/event/:id', function(req, res) {
  if (!req.params.id.match(/^[a-z0-9]{24}$/)) {
    res.send(null);
  }
  storage.get_log(req.params.id, function(err, document) {
    res.send(document);
  });
});

app.listen(8080, '127.0.0.1');
<|endoftext|>


<commit_before>define([
	'./properties'
], function (properties) {
	'use strict';

	// module:
	//		dojo/aspect

	var undefined,
		getDescriptor = properties.getDescriptor,
		defineProperty = Object.defineProperty,
		nextId = 0;

	function advise(dispatcher, type, advice, receiveArguments) {
		var previous = dispatcher[type];
		var around = type === 'around';
		var signal;
		if (around) {
			var advised = advice(function advice() {
				return previous.advice(this, arguments);
			});
			signal = {
				remove: function () {
					signal.cancelled = true;
				},
				advice: function (target, args) {
					return signal.cancelled ?
						previous.advice(target, args) : // cancelled, skip to next one
						advised.apply(target, args);	// called the advised function
				}
			};
		}
		else {
			// create the remove handler
			signal = {
				remove: function () {
					if (signal.advice) {
						var previous = signal.previous,
							next = signal.next;

						if (!next && !previous) {
							delete dispatcher[type];
						}
						else {
							if (previous) {
								previous.next = next;
							}
							else {
								dispatcher[type] = next;
							}
							if (next) {
								next.previous = previous;
							}
						}

						// remove the advice to signal that this signal has been removed
						dispatcher = advice = signal.advice = null;
					}
				},
				id: nextId++,
				advice: advice,
				receiveArguments: receiveArguments
			};
		}
		if (previous && !around) {
			if (type === 'after') {
				// add the listener to the end of the list
				// note that we had to change this loop a little bit to workaround a bizarre IE10 JIT bug
				while (previous.next && (previous = previous.next)) {}
				previous.next = signal;
				signal.previous = previous;
			}
			else if (type === 'before') {
				// add to beginning
				dispatcher[type] = signal;
				signal.next = previous;
				previous.previous = signal;
			}
		}
		else {
			// around or first one just replaces
			dispatcher[type] = signal;
		}
		return signal;
	}

	function aspect(type) {
		return function (target, methodName, advice, receiveArguments) {
			var existing = target[methodName],
				descriptor = getDescriptor(target, methodName),
				dispatcher;
			if (!existing || existing.target !== target) {
				// no dispatcher in place
				defineProperty(target, methodName, {
					value: (dispatcher = function () {
						var executionId = nextId,
						// before advice
							args = arguments,
							before = dispatcher.before;

						while (before) {
							args = before.advice.apply(this, args) || args;
							before = before.next;
						}
						// around advice
						if (dispatcher.around) {
							var results = dispatcher.around.advice(this, args);
						}
						// after advice
						var after = dispatcher.after;
						while (after && after.id < executionId) {
							if (after.receiveArguments) {
								var newResults = after.advice.apply(this, args);
								// change the return value only if a new value was returned
								results = newResults === undefined ? results : newResults;
							}
							else {
								results = after.advice.call(this, results, args);
							}
							after = after.next;
						}
						return results;
					}),
					writable: true,
					enumerable: descriptor ? descriptor.enumerable : true,
					configurable: true
				});
				if (existing) {
					dispatcher.around = { advice: function (target, args) {
						return existing.apply(target, args);
					}};
				}
				dispatcher.target = target;
			}
			var results = advise((dispatcher || existing), type, advice, receiveArguments);
			advice = null;
			return results;
		};
	}

	// TODOC: after/before/around return object

	var after = aspect('after');
	/*=====
	after = function(target, methodName, advice, receiveArguments) {
		// summary:
		//		The 'after' export of the aspect module is a function that can be used to attach
		//		'after' advice to a method. This function will be executed after the original method
		//		is executed. By default the function will be called with a single argument, the return
		//		value of the original method, or the the return value of the last executed advice (if a previous one exists).
		//		The fourth (optional) argument can be set to true to so the function receives the original
		//		arguments (from when the original method was called) rather than the return value.
		//		If there are multiple 'after' advisors, they are executed in the order they were registered.
		// target: Object
		//		This is the target object
		// methodName: String
		//		This is the name of the method to attach to.
		// advice: Function
		//		This is function to be called after the original method
		// receiveArguments: Boolean?
		//		If this is set to true, the advice function receives the original arguments (from when the original mehtod
		//		was called) rather than the return value of the original/previous method.
		// returns:
		//		A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
		//		stop the advice function from being executed.
	};
	=====*/

	var before = aspect('before');
	/*=====
	before = function(target, methodName, advice) {
		// summary:
		//		The 'before' export of the aspect module is a function that can be used to attach
		//		'before' advice to a method. This function will be executed before the original method
		//		is executed. This function will be called with the arguments used to call the method.
		//		This function may optionally return an array as the new arguments to use to call
		//		the original method (or the previous, next-to-execute before advice, if one exists).
		//		If the before method doesn't return anything (returns undefined) the original arguments
		//		will be preserved.
		//		If there are multiple 'before' advisors, they are executed in the reverse order they were registered.
		// target: Object
		//		This is the target object
		// methodName: String
		//		This is the name of the method to attach to.
		// advice: Function
		//		This is function to be called before the original method
	};
	=====*/

	var around = aspect('around');
	/*=====
	 around = function(target, methodName, advice) {
		// summary:
		//		The 'around' export of the aspect module is a function that can be used to attach
		//		'around' advice to a method. The advisor function is immediately executed when
		//		the around() is called, is passed a single argument that is a function that can be
		//		called to continue execution of the original method (or the next around advisor).
		//		The advisor function should return a function, and this function will be called whenever
		//		the method is called. It will be called with the arguments used to call the method.
		//		Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
		// example:
		//		If there are multiple 'around' advisors, the most recent one is executed first,
		//		which can then delegate to the next one and so on. For example:
		//		|	around(obj, 'foo', function(originalFoo) {
		//		|		return function() {
		//		|			var start = new Date().getTime();
		//		|			var results = originalFoo.apply(this, arguments); // call the original
		//		|			var end = new Date().getTime();
		//		|			console.log('foo execution took ' + (end - start) + ' ms');
		//		|			return results;
		//		|		};
		//		|	});
		// target: Object
		//		This is the target object
		// methodName: String
		//		This is the name of the method to attach to.
		// advice: Function
		//		This is function to be called around the original method
	};
	=====*/

	return {
		// summary:
		//		provides aspect oriented programming functionality, allowing for
		//		one to add before, around, or after advice on existing methods.
		// example:
		//	|	define(['dojo/aspect'], function(aspect) {
		//	|		var signal = aspect.after(targetObject, 'methodName', function(someArgument) {
		//	|			this will be called when targetObject.methodName() is called, after the original function is called
		//	|		});
		//
		// example:
		//	The returned signal object can be used to cancel the advice.
		//	|	signal.remove(); // this will stop the advice from being executed anymore
		//	|	aspect.before(targetObject, 'methodName', function(someArgument) {
		//	|		// this will be called when targetObject.methodName() is called, before the original function is called
		//	|	 });

		before: before,
		around: around,
		after: after
	};
});<commit_msg>Remove silly `undefined`<commit_after>define([
	'./properties'
], function (properties) {
	'use strict';

	// module:
	//		dojo/aspect

	var getDescriptor = properties.getDescriptor,
		defineProperty = Object.defineProperty,
		nextId = 0;

	function advise(dispatcher, type, advice, receiveArguments) {
		var previous = dispatcher[type];
		var around = type === 'around';
		var signal;
		if (around) {
			var advised = advice(function advice() {
				return previous.advice(this, arguments);
			});
			signal = {
				remove: function () {
					signal.cancelled = true;
				},
				advice: function (target, args) {
					return signal.cancelled ?
						previous.advice(target, args) : // cancelled, skip to next one
						advised.apply(target, args);	// called the advised function
				}
			};
		}
		else {
			// create the remove handler
			signal = {
				remove: function () {
					if (signal.advice) {
						var previous = signal.previous,
							next = signal.next;

						if (!next && !previous) {
							delete dispatcher[type];
						}
						else {
							if (previous) {
								previous.next = next;
							}
							else {
								dispatcher[type] = next;
							}
							if (next) {
								next.previous = previous;
							}
						}

						// remove the advice to signal that this signal has been removed
						dispatcher = advice = signal.advice = null;
					}
				},
				id: nextId++,
				advice: advice,
				receiveArguments: receiveArguments
			};
		}
		if (previous && !around) {
			if (type === 'after') {
				// add the listener to the end of the list
				// note that we had to change this loop a little bit to workaround a bizarre IE10 JIT bug
				while (previous.next && (previous = previous.next)) {}
				previous.next = signal;
				signal.previous = previous;
			}
			else if (type === 'before') {
				// add to beginning
				dispatcher[type] = signal;
				signal.next = previous;
				previous.previous = signal;
			}
		}
		else {
			// around or first one just replaces
			dispatcher[type] = signal;
		}
		return signal;
	}

	function aspect(type) {
		return function (target, methodName, advice, receiveArguments) {
			var existing = target[methodName],
				descriptor = getDescriptor(target, methodName),
				dispatcher;
			if (!existing || existing.target !== target) {
				// no dispatcher in place
				defineProperty(target, methodName, {
					value: (dispatcher = function () {
						var executionId = nextId,
						// before advice
							args = arguments,
							before = dispatcher.before;

						while (before) {
							args = before.advice.apply(this, args) || args;
							before = before.next;
						}
						// around advice
						if (dispatcher.around) {
							var results = dispatcher.around.advice(this, args);
						}
						// after advice
						var after = dispatcher.after;
						while (after && after.id < executionId) {
							if (after.receiveArguments) {
								var newResults = after.advice.apply(this, args);
								// change the return value only if a new value was returned
								results = newResults === undefined ? results : newResults;
							}
							else {
								results = after.advice.call(this, results, args);
							}
							after = after.next;
						}
						return results;
					}),
					writable: true,
					enumerable: descriptor ? descriptor.enumerable : true,
					configurable: true
				});
				if (existing) {
					dispatcher.around = { advice: function (target, args) {
						return existing.apply(target, args);
					}};
				}
				dispatcher.target = target;
			}
			var results = advise((dispatcher || existing), type, advice, receiveArguments);
			advice = null;
			return results;
		};
	}

	// TODOC: after/before/around return object

	var after = aspect('after');
	/*=====
	after = function(target, methodName, advice, receiveArguments) {
		// summary:
		//		The 'after' export of the aspect module is a function that can be used to attach
		//		'after' advice to a method. This function will be executed after the original method
		//		is executed. By default the function will be called with a single argument, the return
		//		value of the original method, or the the return value of the last executed advice (if a previous one exists).
		//		The fourth (optional) argument can be set to true to so the function receives the original
		//		arguments (from when the original method was called) rather than the return value.
		//		If there are multiple 'after' advisors, they are executed in the order they were registered.
		// target: Object
		//		This is the target object
		// methodName: String
		//		This is the name of the method to attach to.
		// advice: Function
		//		This is function to be called after the original method
		// receiveArguments: Boolean?
		//		If this is set to true, the advice function receives the original arguments (from when the original mehtod
		//		was called) rather than the return value of the original/previous method.
		// returns:
		//		A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
		//		stop the advice function from being executed.
	};
	=====*/

	var before = aspect('before');
	/*=====
	before = function(target, methodName, advice) {
		// summary:
		//		The 'before' export of the aspect module is a function that can be used to attach
		//		'before' advice to a method. This function will be executed before the original method
		//		is executed. This function will be called with the arguments used to call the method.
		//		This function may optionally return an array as the new arguments to use to call
		//		the original method (or the previous, next-to-execute before advice, if one exists).
		//		If the before method doesn't return anything (returns undefined) the original arguments
		//		will be preserved.
		//		If there are multiple 'before' advisors, they are executed in the reverse order they were registered.
		// target: Object
		//		This is the target object
		// methodName: String
		//		This is the name of the method to attach to.
		// advice: Function
		//		This is function to be called before the original method
	};
	=====*/

	var around = aspect('around');
	/*=====
	 around = function(target, methodName, advice) {
		// summary:
		//		The 'around' export of the aspect module is a function that can be used to attach
		//		'around' advice to a method. The advisor function is immediately executed when
		//		the around() is called, is passed a single argument that is a function that can be
		//		called to continue execution of the original method (or the next around advisor).
		//		The advisor function should return a function, and this function will be called whenever
		//		the method is called. It will be called with the arguments used to call the method.
		//		Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
		// example:
		//		If there are multiple 'around' advisors, the most recent one is executed first,
		//		which can then delegate to the next one and so on. For example:
		//		|	around(obj, 'foo', function(originalFoo) {
		//		|		return function() {
		//		|			var start = new Date().getTime();
		//		|			var results = originalFoo.apply(this, arguments); // call the original
		//		|			var end = new Date().getTime();
		//		|			console.log('foo execution took ' + (end - start) + ' ms');
		//		|			return results;
		//		|		};
		//		|	});
		// target: Object
		//		This is the target object
		// methodName: String
		//		This is the name of the method to attach to.
		// advice: Function
		//		This is function to be called around the original method
	};
	=====*/

	return {
		// summary:
		//		provides aspect oriented programming functionality, allowing for
		//		one to add before, around, or after advice on existing methods.
		// example:
		//	|	define(['dojo/aspect'], function(aspect) {
		//	|		var signal = aspect.after(targetObject, 'methodName', function(someArgument) {
		//	|			this will be called when targetObject.methodName() is called, after the original function is called
		//	|		});
		//
		// example:
		//	The returned signal object can be used to cancel the advice.
		//	|	signal.remove(); // this will stop the advice from being executed anymore
		//	|	aspect.before(targetObject, 'methodName', function(someArgument) {
		//	|		// this will be called when targetObject.methodName() is called, before the original function is called
		//	|	 });

		before: before,
		around: around,
		after: after
	};
});<|endoftext|>


<commit_before>// assets to be used by the 'hapi-assets' module based on process.env.NODE_ENV
module.exports = {
    development: {
        js: ['js/test-one.js', 'js/test-two.js'],
        css: ['css/test-one.css', 'css/test-two.css']
    },
    production: {
        js: ['js/scripts.js'],
        css: ['css/styles.css']
    }
};
<commit_msg>remove assets.js file<commit_after><|endoftext|>


<commit_before>import React, { Component } from 'react';
import { View, Text, Navigator } from 'react-native';

export default class MyScene extends Component {
  render() {
    return (
      <View>
        <Text>Hi! My name is kevin</Text>
      </View>
    )
  }
}
<commit_msg>a testing component<commit_after>import React, { Component } from 'react';
import { View, Text, Navigator, Button, Icon} from 'react-native';

export default class MyScene extends Component {

  handlePress() {
    console.log('Pressed!');
  }

  render() {
    return (
      <View>
        <Button
            style={{borderWidth: 1, borderColor: 'blue'}}
            onPress={this.handlePress}
            title={'Press Me!'}>
        </Button>
      </View>
    )
  }
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) Codiad & Kent Safranski (codiad.com), distributed
 *  as-is and without warranty under the MIT License. See
 *  [root]/license.txt for more. This information must remain intact.
 */

var VirtualRenderer = require('ace/virtual_renderer').VirtualRenderer;
var Editor = require('ace/editor').Editor;

editor_modes = {}; // Loaded modes
cursorpoll = null;

var editor = {

    // Array of all instances - there is one editor instance
    // corresponding to each split.
    instances: [],

    active_instance: null,

    // Default Editor-wide settings
    settings: {
        theme: 'twilight',
        font_size: '13px',
        print_margin: false,
        highlight_line: true,
        indent_guides: true,
        wrap_mode: false
    },

    get_settings : function(){
        var bool_val = null;
        var theme = localStorage.getItem('theme'); if(theme!==null){ this.settings.theme = theme; }
        var font_size = localStorage.getItem('font-size'); if(font_size!==null){ this.settings.font_size = font_size; }
        var print_margin = localStorage.getItem('print-margin'); if(print_margin!==null){
            bool_val = (print_margin == "true");
            this.settings.print_margin = bool_val;
        }
        var highlight_line = localStorage.getItem('highlight-line'); if(highlight_line!==null){
            bool_val = (highlight_line == "true");
            this.settings.highlight_line = bool_val;
        }
        var indent_guides = localStorage.getItem('indent-guides'); if(indent_guides!==null){
            bool_val = (indent_guides == "true");
            this.settings.indent_guides = bool_val;
        }
        var wrap_mode = localStorage.getItem('wrap-mode'); if(wrap_mode!==null){
            bool_val = (wrap_mode == "true");
            this.settings.wrap_mode = bool_val;
        }
    },

    add_instance: function(session){
        var i  = ace.edit('editor');

        // Check user-specified settings
        this.get_settings();

        // Apply the current configuration settings:
        i.setTheme('ace/theme/' + this.settings.theme);
        i.setFontSize(this.settings.font_size);
        i.setShowPrintMargin(this.settings.print_margin);
        i.setHighlightActiveLine(this.settings.highlight_line);
        i.setDisplayIndentGuides(this.settings.indent_guides);
        i.getSession().setUseWrapMode(this.settings.wrap_mode);

        this.change_listener(i);
        this.cursor_tracking(i);
        this.bind_keys(i);

        this.instances.push(i);
        return i;
    },

    exterminate: function(){
        $('#editor-region').html('').append($("<div>").attr('id', 'editor'));
        this.instances = [];
    },

    remove_session: function(session, replacement_session){
        for (var k = 0; k < this.instances.length; k++) {
            if (this.instances[k].getSession() === session) {
                this.instances[k].setSession(replacement_session);
            }
        }
    },

    for_each: function(fn){
        for (var k = 0; k < this.instances.length; k++) {
            fn.call(this, this.instances[k]);
        }
    },

    //////////////////////////////////////////////////////////////////
    // Get the currently active Editor
    //////////////////////////////////////////////////////////////////

    get_active: function() {
	return this.active_instance;
    },

    set_active: function(i) {
	if (! i) return;
	this.active_instance = i;
	$('#current-file').text(i.getSession().path);
    },

    set_session: function(session, i) {
        i = i || this.get_active();
        if (! i) i = this.add_instance(session);
        i.setSession(session);
	this.set_active(i);
    },

    //////////////////////////////////////////////////////////////////
    // Select mode from extension
    //////////////////////////////////////////////////////////////////

    select_mode: function(e) {
        switch (e) {
        case 'html':
        case 'htm':
        case 'tpl':
            return 'html';
        case 'js':
            return 'javascript';
        case 'css':
            return 'css';
        case 'scss':
        case 'sass':
            return 'scss';
        case 'less':
            return 'less';
        case 'php':
        case 'php5':
            return 'php';
        case 'json':
            return 'json';
        case 'xml':
            return 'xml';
        case 'sql':
            return 'sql';
        case 'md':
            return 'markdown';
        default:
            return 'text';
        }
    },

    //////////////////////////////////////////////////////////////////
    // Set editor mode/language
    //////////////////////////////////////////////////////////////////

    set_mode: function(m, i) {
        i = i || this.get_active();
        if (!editor_modes[m]) { // Check if mode is already loaded
            $.loadScript("components/editor/ace-editor/mode-" + m + ".js", function() {
                editor_modes[m] = true; // Mark to not load again
                var EditorMode = require("ace/mode/" + m)
                    .Mode;
                i.getSession()
                    .setMode(new EditorMode());
            }, true);
        } else {
            var EditorMode = require("ace/mode/" + m)
                .Mode;
            i.getSession()
                .setMode(new EditorMode());
        }
    },

    //////////////////////////////////////////////////////////////////
    // Set editor theme
    //////////////////////////////////////////////////////////////////

    set_theme: function(t, i) {
        if (i) {
            // If a specific instance is specified, change the theme for
            // this instance
            i.setTheme("ace/theme/"+t);
        } else {
            // Change the theme for the existing editor instances
            // and make it the default for new instances
            this.settings.theme = t;
            for (var k = 0; k < this.instances.length; k++) {
                this.instances[k].setTheme("ace/theme/"+t);
            }
        }
        // LocalStorage
        localStorage.setItem('theme',t);
    },

    //////////////////////////////////////////////////////////////////
    // Set content of editor
    //////////////////////////////////////////////////////////////////

    set_content: function(c, i) {
        i = i || this.get_active();
        i.getSession().setValue(c);
    },

    //////////////////////////////////////////////////////////////////
    // Set Font Size
    //////////////////////////////////////////////////////////////////

    set_font_size: function(s, i) {
        if (i) {
            i.setFontSize(s);
        } else {
            this.settings.font_size = s;
            this.for_each(function(i) {
                i.setFontSize(s);
            });
        }
        // LocalStorage
        localStorage.setItem('font-size',s);
    },

    //////////////////////////////////////////////////////////////////
    // Highlight active line
    //////////////////////////////////////////////////////////////////

    set_highlight_line: function(h, i) {
        if (i) {
            i.setHighlightActiveLine(h);
        } else {
            this.settings.highlight_line = h;
            this.for_each(function(i) {
                i.setHighlightActiveLine(h);
            });
        }
        // LocalStorage
        localStorage.setItem('highlight-line',h);
    },

    //////////////////////////////////////////////////////////////////
    // Show/Hide print margin indicator
    //////////////////////////////////////////////////////////////////

    set_print_margin: function(p, i) {
        if (i) {
            i.setShowPrintMargin(p);
        } else {
            this.settings.print_margin = p;
            this.for_each(function(i) {
                i.setShowPrintMargin(p);
            });
        }
        // LocalStorage
        localStorage.setItem('print-margin',p);
    },

    //////////////////////////////////////////////////////////////////
    // Show/Hide indent guides
    //////////////////////////////////////////////////////////////////

    set_indent_guides: function(g, i) {
        if (i) {
            i.setDisplayIndentGuides(g);
        } else {
            this.settings.indent_guides = g;
            this.for_each(function(i) {
                i.setDisplayIndentGuides(g);
            });
        }
        // LocalStorage
        localStorage.setItem('indent-guides',g);
    },

    //////////////////////////////////////////////////////////////////
    // Code Folding
    //////////////////////////////////////////////////////////////////

    set_code_folding: function(f, i) {
        if (i) {
            i.setFoldStyle(f);
        } else {
            this.for_each(function(i){
                i.setFoldStyle(f);
            });
        }
    },

    //////////////////////////////////////////////////////////////////
    // Set Line Wrapping
    //////////////////////////////////////////////////////////////////

    set_wrap_mode: function(w, i) {
        if (i) {
            i.getSession().setUseWrapMode(w);
        } else {
            this.for_each(function(i){
                i.getSession().setUseWrapMode(w);
            });
        }
        // LocalStorage
        localStorage.setItem('wrap-mode',w);
    },

    //////////////////////////////////////////////////////////////////
    // Get content from editor by ID
    //////////////////////////////////////////////////////////////////

    get_content: function(i) {
        i = i || this.get_active();
        if (! i) return;
        var content = i.getSession().getValue();
        if (!content) {
            content = ' ';
        } // Pass something through
        return content;
    },

    //////////////////////////////////////////////////////////////////
    // Resize
    //////////////////////////////////////////////////////////////////

    resize: function(i) {
        i = i || this.get_active();
        if (! i) return;
        i.resize();
    },

    //////////////////////////////////////////////////////////////////
    // Change Listener
    //////////////////////////////////////////////////////////////////

    change_listener: function(i) {
        var _this = this;
        i.on('change', function() {
            active.mark_changed(_this.get_active().getSession().path);
        });
    },

    //////////////////////////////////////////////////////////////////
    // Get Selected Text
    //////////////////////////////////////////////////////////////////

    get_selected_text: function(i) {
        i = i || this.get_active();
        if (! i) return;
        return i.getCopyText();
    },

    //////////////////////////////////////////////////////////////////
    // Insert text
    //////////////////////////////////////////////////////////////////

    insert_text: function(val, i) {
        i = i || this.get_active();
        if (! i) return;
        i.insert(val);
    },

    //////////////////////////////////////////////////////////////////
    // Goto Line
    //////////////////////////////////////////////////////////////////

    goto_line: function(line, i) {
        i = i || this.get_active();
        if (! i) return;
        i.gotoLine(line, 0, true);
    },

    //////////////////////////////////////////////////////////////////
    // Focus
    //////////////////////////////////////////////////////////////////

    focus: function(i) {
        i = i || this.get_active();
        if (! i) return;
        i.focus();
    },

    //////////////////////////////////////////////////////////////////
    // Cursor Tracking
    //////////////////////////////////////////////////////////////////

    cursor_tracking: function(i) {
        i = i || this.get_active();
        if (! i) return;
        clearInterval(cursorpoll);
        cursorpoll = setInterval(function() {
            $('#cursor-position')
                .html('Ln: '
                      + (i.getCursorPosition().row + 1)
                      + ' · Col: '
                      + i.getCursorPosition().column
                     );
        }, 100);
    },

    //////////////////////////////////////////////////////////////////
    // Bind Keys
    //////////////////////////////////////////////////////////////////

    bind_keys: function(i) {

        // Find
        i.commands.addCommand({
            name: 'Find',
            bindKey: {
                win: 'Ctrl-F',
                mac: 'Command-F'
            },
            exec: function(e) {
                editor.open_search('find');
            }
        });

        // Find + Replace
        i.commands.addCommand({
            name: 'Replace',
            bindKey: {
                win: 'Ctrl-R',
                mac: 'Command-R'
            },
            exec: function(e) {
                editor.open_search('replace');
            }
        });

        i.commands.addCommand({
            name: 'Move Up',
            bindKey: {
                win: 'Ctrl-up',
                mac: 'Command-up'
            },
            exec: function(e) {
                active.move('up');
            }
        });

        i.commands.addCommand({
            name: 'Move Down',
            bindKey: {
                win: 'Ctrl-down',
                mac: 'Command-up'
            },
            exec: function(e) {
                active.move('down');
            }
        });

    },

    //////////////////////////////////////////////////////////////////
    // Search (Find + Replace)
    //////////////////////////////////////////////////////////////////

    open_search: function(type) {
        if (this.get_active()) {
            modal.load(400, 'components/editor/dialog.php?action=search&type=' + type);
            modal.hide_overlay();
        } else {
            message.error('No Open Files');
        }
    },

    search: function(action, i) {
        i = i || this.get_active();
        if (! i) return;
        var find = $('#modal input[name="find"]')
            .val();
        var replace = $('#modal input[name="replace"]')
            .val();
        switch (action) {
        case 'find':

            i.find(find, {
                backwards: false,
                wrap: true,
                caseSensitive: false,
                wholeWord: false,
                regExp: false
            });

            break;

        case 'replace':

            i.find(find, {
                backwards: false,
                wrap: true,
                caseSensitive: false,
                wholeWord: false,
                regExp: false
            });
            i.replace(replace);

            break;

        case 'replace_all':

            i.find(find, {
                backwards: false,
                wrap: true,
                caseSensitive: false,
                wholeWord: false,
                regExp: false
            });
            i.replaceAll(replace);

            break;
        }
    }

};
<commit_msg>whitespace cleanup<commit_after>/*
 *  Copyright (c) Codiad & Kent Safranski (codiad.com), distributed
 *  as-is and without warranty under the MIT License. See
 *  [root]/license.txt for more. This information must remain intact.
 */

var VirtualRenderer = require('ace/virtual_renderer').VirtualRenderer;
var Editor = require('ace/editor').Editor;

editor_modes = {}; // Loaded modes
cursorpoll = null;

var editor = {

    // Array of all instances - there is one editor instance
    // corresponding to each split.
    instances: [],

    active_instance: null,

    // Default Editor-wide settings
    settings: {
        theme: 'twilight',
        font_size: '13px',
        print_margin: false,
        highlight_line: true,
        indent_guides: true,
        wrap_mode: false
    },

    get_settings : function(){
        var bool_val = null;
        var theme = localStorage.getItem('theme'); if(theme!==null){ this.settings.theme = theme; }
        var font_size = localStorage.getItem('font-size'); if(font_size!==null){ this.settings.font_size = font_size; }
        var print_margin = localStorage.getItem('print-margin'); if(print_margin!==null){
            bool_val = (print_margin == "true");
            this.settings.print_margin = bool_val;
        }
        var highlight_line = localStorage.getItem('highlight-line'); if(highlight_line!==null){
            bool_val = (highlight_line == "true");
            this.settings.highlight_line = bool_val;
        }
        var indent_guides = localStorage.getItem('indent-guides'); if(indent_guides!==null){
            bool_val = (indent_guides == "true");
            this.settings.indent_guides = bool_val;
        }
        var wrap_mode = localStorage.getItem('wrap-mode'); if(wrap_mode!==null){
            bool_val = (wrap_mode == "true");
            this.settings.wrap_mode = bool_val;
        }
    },

    add_instance: function(session){
        var i  = ace.edit('editor');

        // Check user-specified settings
        this.get_settings();

        // Apply the current configuration settings:
        i.setTheme('ace/theme/' + this.settings.theme);
        i.setFontSize(this.settings.font_size);
        i.setShowPrintMargin(this.settings.print_margin);
        i.setHighlightActiveLine(this.settings.highlight_line);
        i.setDisplayIndentGuides(this.settings.indent_guides);
        i.getSession().setUseWrapMode(this.settings.wrap_mode);

        this.change_listener(i);
        this.cursor_tracking(i);
        this.bind_keys(i);

        this.instances.push(i);
        return i;
    },

    exterminate: function(){
        $('#editor-region').html('').append($("<div>").attr('id', 'editor'));
        this.instances = [];
    },

    remove_session: function(session, replacement_session){
        for (var k = 0; k < this.instances.length; k++) {
            if (this.instances[k].getSession() === session) {
                this.instances[k].setSession(replacement_session);
            }
        }
    },

    for_each: function(fn){
        for (var k = 0; k < this.instances.length; k++) {
            fn.call(this, this.instances[k]);
        }
    },

    //////////////////////////////////////////////////////////////////
    // Get the currently active Editor
    //////////////////////////////////////////////////////////////////

    get_active: function() {
        return this.active_instance;
    },

    set_active: function(i) {
        if (! i) return;
        this.active_instance = i;
        $('#current-file').text(i.getSession().path);
    },

    set_session: function(session, i) {
        i = i || this.get_active();
        if (! i) i = this.add_instance(session);
        i.setSession(session);
        this.set_active(i);
    },

    //////////////////////////////////////////////////////////////////
    // Select mode from extension
    //////////////////////////////////////////////////////////////////

    select_mode: function(e) {
        switch (e) {
        case 'html':
        case 'htm':
        case 'tpl':
            return 'html';
        case 'js':
            return 'javascript';
        case 'css':
            return 'css';
        case 'scss':
        case 'sass':
            return 'scss';
        case 'less':
            return 'less';
        case 'php':
        case 'php5':
            return 'php';
        case 'json':
            return 'json';
        case 'xml':
            return 'xml';
        case 'sql':
            return 'sql';
        case 'md':
            return 'markdown';
        default:
            return 'text';
        }
    },

    //////////////////////////////////////////////////////////////////
    // Set editor mode/language
    //////////////////////////////////////////////////////////////////

    set_mode: function(m, i) {
        i = i || this.get_active();
        if (!editor_modes[m]) { // Check if mode is already loaded
            $.loadScript("components/editor/ace-editor/mode-" + m + ".js", function() {
                editor_modes[m] = true; // Mark to not load again
                var EditorMode = require("ace/mode/" + m)
                    .Mode;
                i.getSession()
                    .setMode(new EditorMode());
            }, true);
        } else {
            var EditorMode = require("ace/mode/" + m)
                .Mode;
            i.getSession()
                .setMode(new EditorMode());
        }
    },

    //////////////////////////////////////////////////////////////////
    // Set editor theme
    //////////////////////////////////////////////////////////////////

    set_theme: function(t, i) {
        if (i) {
            // If a specific instance is specified, change the theme for
            // this instance
            i.setTheme("ace/theme/"+t);
        } else {
            // Change the theme for the existing editor instances
            // and make it the default for new instances
            this.settings.theme = t;
            for (var k = 0; k < this.instances.length; k++) {
                this.instances[k].setTheme("ace/theme/"+t);
            }
        }
        // LocalStorage
        localStorage.setItem('theme',t);
    },

    //////////////////////////////////////////////////////////////////
    // Set content of editor
    //////////////////////////////////////////////////////////////////

    set_content: function(c, i) {
        i = i || this.get_active();
        i.getSession().setValue(c);
    },

    //////////////////////////////////////////////////////////////////
    // Set Font Size
    //////////////////////////////////////////////////////////////////

    set_font_size: function(s, i) {
        if (i) {
            i.setFontSize(s);
        } else {
            this.settings.font_size = s;
            this.for_each(function(i) {
                i.setFontSize(s);
            });
        }
        // LocalStorage
        localStorage.setItem('font-size',s);
    },

    //////////////////////////////////////////////////////////////////
    // Highlight active line
    //////////////////////////////////////////////////////////////////

    set_highlight_line: function(h, i) {
        if (i) {
            i.setHighlightActiveLine(h);
        } else {
            this.settings.highlight_line = h;
            this.for_each(function(i) {
                i.setHighlightActiveLine(h);
            });
        }
        // LocalStorage
        localStorage.setItem('highlight-line',h);
    },

    //////////////////////////////////////////////////////////////////
    // Show/Hide print margin indicator
    //////////////////////////////////////////////////////////////////

    set_print_margin: function(p, i) {
        if (i) {
            i.setShowPrintMargin(p);
        } else {
            this.settings.print_margin = p;
            this.for_each(function(i) {
                i.setShowPrintMargin(p);
            });
        }
        // LocalStorage
        localStorage.setItem('print-margin',p);
    },

    //////////////////////////////////////////////////////////////////
    // Show/Hide indent guides
    //////////////////////////////////////////////////////////////////

    set_indent_guides: function(g, i) {
        if (i) {
            i.setDisplayIndentGuides(g);
        } else {
            this.settings.indent_guides = g;
            this.for_each(function(i) {
                i.setDisplayIndentGuides(g);
            });
        }
        // LocalStorage
        localStorage.setItem('indent-guides',g);
    },

    //////////////////////////////////////////////////////////////////
    // Code Folding
    //////////////////////////////////////////////////////////////////

    set_code_folding: function(f, i) {
        if (i) {
            i.setFoldStyle(f);
        } else {
            this.for_each(function(i){
                i.setFoldStyle(f);
            });
        }
    },

    //////////////////////////////////////////////////////////////////
    // Set Line Wrapping
    //////////////////////////////////////////////////////////////////

    set_wrap_mode: function(w, i) {
        if (i) {
            i.getSession().setUseWrapMode(w);
        } else {
            this.for_each(function(i){
                i.getSession().setUseWrapMode(w);
            });
        }
        // LocalStorage
        localStorage.setItem('wrap-mode',w);
    },

    //////////////////////////////////////////////////////////////////
    // Get content from editor by ID
    //////////////////////////////////////////////////////////////////

    get_content: function(i) {
        i = i || this.get_active();
        if (! i) return;
        var content = i.getSession().getValue();
        if (!content) {
            content = ' ';
        } // Pass something through
        return content;
    },

    //////////////////////////////////////////////////////////////////
    // Resize
    //////////////////////////////////////////////////////////////////

    resize: function(i) {
        i = i || this.get_active();
        if (! i) return;
        i.resize();
    },

    //////////////////////////////////////////////////////////////////
    // Change Listener
    //////////////////////////////////////////////////////////////////

    change_listener: function(i) {
        var _this = this;
        i.on('change', function() {
            active.mark_changed(_this.get_active().getSession().path);
        });
    },

    //////////////////////////////////////////////////////////////////
    // Get Selected Text
    //////////////////////////////////////////////////////////////////

    get_selected_text: function(i) {
        i = i || this.get_active();
        if (! i) return;
        return i.getCopyText();
    },

    //////////////////////////////////////////////////////////////////
    // Insert text
    //////////////////////////////////////////////////////////////////

    insert_text: function(val, i) {
        i = i || this.get_active();
        if (! i) return;
        i.insert(val);
    },

    //////////////////////////////////////////////////////////////////
    // Goto Line
    //////////////////////////////////////////////////////////////////

    goto_line: function(line, i) {
        i = i || this.get_active();
        if (! i) return;
        i.gotoLine(line, 0, true);
    },

    //////////////////////////////////////////////////////////////////
    // Focus
    //////////////////////////////////////////////////////////////////

    focus: function(i) {
        i = i || this.get_active();
        if (! i) return;
        i.focus();
    },

    //////////////////////////////////////////////////////////////////
    // Cursor Tracking
    //////////////////////////////////////////////////////////////////

    cursor_tracking: function(i) {
        i = i || this.get_active();
        if (! i) return;
        clearInterval(cursorpoll);
        cursorpoll = setInterval(function() {
            $('#cursor-position')
                .html('Ln: '
                      + (i.getCursorPosition().row + 1)
                      + ' · Col: '
                      + i.getCursorPosition().column
                     );
        }, 100);
    },

    //////////////////////////////////////////////////////////////////
    // Bind Keys
    //////////////////////////////////////////////////////////////////

    bind_keys: function(i) {

        // Find
        i.commands.addCommand({
            name: 'Find',
            bindKey: {
                win: 'Ctrl-F',
                mac: 'Command-F'
            },
            exec: function(e) {
                editor.open_search('find');
            }
        });

        // Find + Replace
        i.commands.addCommand({
            name: 'Replace',
            bindKey: {
                win: 'Ctrl-R',
                mac: 'Command-R'
            },
            exec: function(e) {
                editor.open_search('replace');
            }
        });

        i.commands.addCommand({
            name: 'Move Up',
            bindKey: {
                win: 'Ctrl-up',
                mac: 'Command-up'
            },
            exec: function(e) {
                active.move('up');
            }
        });

        i.commands.addCommand({
            name: 'Move Down',
            bindKey: {
                win: 'Ctrl-down',
                mac: 'Command-up'
            },
            exec: function(e) {
                active.move('down');
            }
        });

    },

    //////////////////////////////////////////////////////////////////
    // Search (Find + Replace)
    //////////////////////////////////////////////////////////////////

    open_search: function(type) {
        if (this.get_active()) {
            modal.load(400, 'components/editor/dialog.php?action=search&type=' + type);
            modal.hide_overlay();
        } else {
            message.error('No Open Files');
        }
    },

    search: function(action, i) {
        i = i || this.get_active();
        if (! i) return;
        var find = $('#modal input[name="find"]')
            .val();
        var replace = $('#modal input[name="replace"]')
            .val();
        switch (action) {
        case 'find':

            i.find(find, {
                backwards: false,
                wrap: true,
                caseSensitive: false,
                wholeWord: false,
                regExp: false
            });

            break;

        case 'replace':

            i.find(find, {
                backwards: false,
                wrap: true,
                caseSensitive: false,
                wholeWord: false,
                regExp: false
            });
            i.replace(replace);

            break;

        case 'replace_all':

            i.find(find, {
                backwards: false,
                wrap: true,
                caseSensitive: false,
                wholeWord: false,
                regExp: false
            });
            i.replaceAll(replace);

            break;
        }
    }

};
<|endoftext|>