<commit_before>'use strict';

/**
 * Pterodactyl - Daemon
 * Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
const Fs = require('fs-extra');
const Async = require('async');
const Path = require('path');
const Chokidar = require('chokidar');
const _ = require('lodash');
const Mmm = require('mmmagic');
const decompressEngine = require('decompress');
const Tar = require('tar-fs');
const RandomString = require('randomstring');
const chownr = require('chownr');

const Magic = Mmm.Magic;
const Mime = new Magic(Mmm.MAGIC_MIME_TYPE);

class FileSystem {
    constructor(server) {
        this.server = server;

        const Watcher = Chokidar.watch(this.server.configLocation, {
            persistent: true,
            awaitWriteFinish: false,
        });

        Watcher.on('change', () => {
            if (this.server.knownWrite !== true) {
                this.server.log.debug('Detected remote file change, updating JSON object correspondingly.');
                Fs.readJson(this.server.configLocation, (err, object) => {
                    if (err) {
                        // Try to overwrite those changes with the old config.
                        this.server.log.warn(err, 'An error was detected with the changed file, attempting to undo the changes.');
                        this.server.knownWrite = true;
                        Fs.writeJson(this.server.configLocation, this.server.json, writeErr => {
                            if (!writeErr) {
                                this.server.log.debug('Successfully undid those remote changes.');
                            } else {
                                this.server.log.fatal(writeErr, 'Unable to undo those changes, this could break the daemon badly.');
                            }
                        });
                    } else {
                        this.server.json = object;
                    }
                });
            }
            this.server.knownWrite = false;
        });
    }

    chown(file, next) {
        let chownTarget = file;
        if (!_.startsWith(chownTarget, this.server.path())) {
            chownTarget = this.server.path(file);
        }

        chownr(chownTarget, this.server.json.build.user, this.server.json.build.user, next);
    }

    isSelf(moveTo, moveFrom) {
        const target = this.server.path(moveTo);
        const source = this.server.path(moveFrom);

        if (!_.startsWith(target, source)) {
            return false;
        }

        const end = target.slice(source.length);
        if (!end) {
            return true;
        }

        return _.startsWith(end, '/');
    }

    write(file, data, next) {
        Async.series([
            callback => {
                this.server.knownWrite = true;
                callback();
            },
            callback => {
                Fs.outputFile(this.server.path(file), data, callback);
            },
            callback => {
                this.chown(file, callback);
            },
        ], next);
    }

    read(file, next) {
        Fs.stat(this.server.path(file), (err, stat) => {
            if (err) return next(err);
            if (!stat.isFile()) {
                return next(new Error('The file requested does not appear to be a file.'));
            }
            if (stat.size > 10000000) {
                return next(new Error('This file is too large to open.'));
            }
            Fs.readFile(this.server.path(file), 'utf8', next);
        });
    }

    readEnd(file, bytes, next) {
        if (_.isFunction(bytes)) {
            next = bytes; // eslint-disable-line
            bytes = 80000; // eslint-disable-line
        }
        Fs.stat(this.server.path(file), (err, stat) => {
            if (err) return next(err);
            if (!stat.isFile()) {
                return next(new Error('The file requested does not appear to be a file.'));
            }
            let opts = {};
            let lines = '';
            if (stat.size > bytes) {
                opts = {
                    start: (stat.size - bytes),
                    end: stat.size,
                };
            }
            const stream = Fs.createReadStream(this.server.path(file), opts);
            stream.on('data', data => {
                lines += data;
            });
            stream.on('end', () => {
                next(null, lines);
            });
        });
    }

    mkdir(path, next) {
        if (!_.isArray(path)) {
            Fs.ensureDir(this.server.path(path), err => {
                if (err) return next(err);
                this.chown(path, next);
            });
        } else {
            Async.eachOfLimit(path, 5, (value, key, callback) => {
                Fs.ensureDir(this.server.path(value), err => {
                    if (err) return callback(err);
                    this.chown(value, callback);
                });
            }, next);
        }
    }

    delete(path, next) {
        // Safety - prevent deleting the main folder.
        if (Path.resolve(this.server.path(path)) === this.server.path()) {
            return next(new Error('You cannot delete your home folder.'));
        }
        Fs.remove(this.server.path(path), next);
    }

    copy(initial, ending, opts, next) {
        if (_.isFunction(opts)) {
            next = opts; // eslint-disable-line
            opts = {}; // eslint-disable-line
        }

        if (!_.isArray(initial) && !_.isArray(ending)) {
            if (this.isSelf(ending, initial)) {
                return next(new Error('You cannot copy a folder into itself.'));
            }
            Async.series([
                callback => {
                    Fs.copy(this.server.path(initial), this.server.path(ending), {
                        clobber: opts.clobber || true,
                        preserveTimestamps: opts.timestamps || false,
                    }, callback);
                },
                callback => {
                    this.chown(ending, callback);
                },
            ], next);
        } else if (!_.isArray(initial) || !_.isArray(ending)) {
            return next(new Error('Values passed to copy function must be of the same type (string, string) or (array, array).'));
        } else {
            Async.eachOfLimit(initial, 5, (value, key, callback) => {
                if (_.isUndefined(ending[key])) {
                    return callback(new Error('The number of starting values does not match the number of ending values.'));
                }

                if (this.isSelf(ending[key], value)) {
                    return next(new Error('You cannot copy a folder into itself.'));
                }
                Fs.copy(this.server.path(value), this.server.path(ending[key]), {
                    clobber: _.get(opts, 'clobber', true),
                    preserveTimestamps: _.get(opts, 'timestamps', false),
                }, err => {
                    if (err) return callback(err);
                    this.chown(ending[key], callback);
                });
            }, next);
        }
    }

    stat(file, next) {
        Fs.stat(this.server.path(file), (err, stat) => {
            if (err) next(err);
            Mime.detectFile(this.server.path(file), (mimeErr, result) => {
                next(null, {
                    'name': (Path.parse(this.server.path(file))).base,
                    'created': stat.ctime,
                    'modified': stat.mtime,
                    'size': stat.size,
                    'directory': stat.isDirectory(),
                    'file': stat.isFile(),
                    'symlink': stat.isSymbolicLink(),
                    'mime': result || 'unknown',
                });
            });
        });
    }

    move(initial, ending, next) {
        if (!_.isArray(initial) && !_.isArray(ending)) {
            if (this.isSelf(ending, initial)) {
                return next(new Error('You cannot move a file or folder into itself.'));
            }
            Fs.move(this.server.path(initial), this.server.path(ending), { clobber: false }, err => {
                if (err && !_.startsWith(err.message, 'EEXIST:')) return next(err);
                this.chown(ending, next);
            });
        } else if (!_.isArray(initial) || !_.isArray(ending)) {
            return next(new Error('Values passed to move function must be of the same type (string, string) or (array, array).'));
        } else {
            Async.eachOfLimit(initial, 5, (value, key, callback) => {
                if (_.isUndefined(ending[key])) {
                    return callback(new Error('The number of starting values does not match the number of ending values.'));
                }

                if (this.isSelf(ending[key], value)) {
                    return next(new Error('You cannot move a file or folder into itself.'));
                }
                Fs.move(this.server.path(value), this.server.path(ending[key]), { clobber: false }, err => {
                    if (err && !_.startsWith(err.message, 'EEXIST:')) return callback(err);
                    this.chown(ending[key], callback);
                });
            }, next);
        }
    }

    decompress(files, next) {
        if (!_.isArray(files)) {
            const fromFile = this.server.path(files);
            const toDir = fromFile.substring(0, _.lastIndexOf(fromFile, '/'));
            decompressEngine(fromFile, toDir, {
                strip: 1,
            }).then(out => {
                Async.eachOfLimit(out, 10, (v, k, eachCallback) => {
                    Fs.chown(Path.join(toDir, v.path), this.server.json.build.user, this.server.json.build.user, eachCallback);
                }, err => {
                    next(err);
                });
            }).catch(next);
        } else if (_.isArray(files)) {
            Async.eachLimit(files, 1, (file, callback) => {
                const fromFile = this.server.path(file);
                const toDir = fromFile.substring(0, _.lastIndexOf(fromFile, '/'));
                decompressEngine(fromFile, toDir, {
                    strip: 1,
                }).then(out => {
                    Async.eachOfLimit(out, 10, (v, k, eachCallback) => {
                        Fs.chown(this.server.path(Path.join(toDir, v.path)), this.server.json.build.user, this.server.json.build.user, eachCallback);
                    }, err => {
                        next(err);
                    });
                }).catch(callback);
            }, next);
        } else {
            return next(new Error('Invalid datatype passed to decompression function.'));
        }
    }

    // Unlike other functions, if multiple files and folders are passed
    // they will all be combined into a single archive.
    compress(files, to, next) {
        if (!_.isString(to)) {
            return next(new Error('The to field must be a string for the folder in which the file should be saved.'));
        }

        const SaveAsName = `ptdlfm.${RandomString.generate(8)}.tar`;
        if (!_.isArray(files)) {
            if (this.isSelf(to, files)) {
                return next(new Error('Unable to compress folder into itself.'));
            }

            const Stream = Fs.createWriteStream(Path.join(this.server.path(to), SaveAsName));
            Tar.pack(this.server.path(files)).pipe(Stream);
            Stream.on('error', next);
            Stream.on('close', () => {
                next(null, SaveAsName);
            });
        } else if (_.isArray(files)) {
            const FileEntries = [];
            Async.series([
                callback => {
                    Async.eachLimit(files, 5, (file, eachCallback) => {
                        // If it is going to be inside itself, skip and move on.
                        if (this.isSelf(to, file)) {
                            return eachCallback();
                        }

                        FileEntries.push(_.replace(this.server.path(file), this.server.path(), ''));
                        eachCallback();
                    }, callback);
                },
                callback => {
                    if (_.isEmpty(FileEntries)) {
                        return next(new Error('None of the files passed to the command were valid.'));
                    }

                    const Stream = Fs.createWriteStream(Path.join(this.server.path(to), SaveAsName));
                    Tar.pack(this.server.path(), {
                        entries: FileEntries,
                    }).pipe(Stream);
                    Stream.on('error', callback);
                    Stream.on('close', callback);
                },
            ], err => {
                next(err, SaveAsName);
            });
        } else {
            return next(new Error('Invalid datatype passed to decompression function.'));
        }
    }

    directory(path, next) {
        const responseFiles = [];
        Async.waterfall([
            callback => {
                Fs.stat(this.server.path(path), (err, s) => {
                    if (err) return callback(err);
                    if (!s.isDirectory()) {
                        return callback(new Error('The path requested is not a valid directory on the system.'));
                    }
                    return callback();
                });
            },
            callback => {
                Fs.readdir(this.server.path(path), callback);
            },
            (files, callback) => {
                Async.each(files, (item, eachCallback) => {
                    Async.auto({
                        do_stat: aCallback => {
                            Fs.stat(Path.join(this.server.path(path), item), (statErr, stat) => {
                                aCallback(statErr, stat);
                            });
                        },
                        do_mime: aCallback => {
                            Mime.detectFile(Path.join(this.server.path(path), item), (mimeErr, result) => {
                                aCallback(mimeErr, result);
                            });
                        },
                        do_push: ['do_stat', 'do_mime', (results, aCallback) => {
                            responseFiles.push({
                                'name': item,
                                'created': results.do_stat.birthtime,
                                'modified': results.do_stat.mtime,
                                'size': results.do_stat.size,
                                'directory': results.do_stat.isDirectory(),
                                'file': results.do_stat.isFile(),
                                'symlink': results.do_stat.isSymbolicLink(),
                                'mime': results.do_mime || 'unknown',
                            });
                            aCallback();
                        }],
                    }, eachCallback);
                }, callback);
            },
        ], err => {
            next(err, _.sortBy(responseFiles, [(o) => { return _.lowerCase(o.name); }, 'created'])); // eslint-disable-line
        });
    }
}

module.exports = FileSystem;
<commit_msg>Move to native handling of compression/decompression and chown<commit_after>'use strict';

/**
 * Pterodactyl - Daemon
 * Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
const Fs = require('fs-extra');
const Async = require('async');
const Path = require('path');
const Chokidar = require('chokidar');
const _ = require('lodash');
const Mmm = require('mmmagic');
const decompressEngine = require('decompress');
const Tar = require('tar-fs');
const RandomString = require('randomstring');
const Process = require('child_process');
const Util = require('util');

const Magic = Mmm.Magic;
const Mime = new Magic(Mmm.MAGIC_MIME_TYPE);

class FileSystem {
    constructor(server) {
        this.server = server;

        const Watcher = Chokidar.watch(this.server.configLocation, {
            persistent: true,
            awaitWriteFinish: false,
        });

        Watcher.on('change', () => {
            if (this.server.knownWrite !== true) {
                this.server.log.debug('Detected remote file change, updating JSON object correspondingly.');
                Fs.readJson(this.server.configLocation, (err, object) => {
                    if (err) {
                        // Try to overwrite those changes with the old config.
                        this.server.log.warn(err, 'An error was detected with the changed file, attempting to undo the changes.');
                        this.server.knownWrite = true;
                        Fs.writeJson(this.server.configLocation, this.server.json, writeErr => {
                            if (!writeErr) {
                                this.server.log.debug('Successfully undid those remote changes.');
                            } else {
                                this.server.log.fatal(writeErr, 'Unable to undo those changes, this could break the daemon badly.');
                            }
                        });
                    } else {
                        this.server.json = object;
                    }
                });
            }
            this.server.knownWrite = false;
        });
    }

    chown(file, next) {
        let chownTarget = file;
        if (!_.startsWith(chownTarget, this.server.path())) {
            chownTarget = this.server.path(file);
        }

        const Exec = Process.spawn('chown', ['-R', Util.format('%d:%d', this.server.json.build.user, this.server.json.build.user), chownTarget], {});
        Exec.on('error', execErr => {
            this.server.log.error(execErr);
            return next(new Error('There was an error while attempting to set ownership of files.'));
        });
        Exec.on('exit', (code, signal) => {
            if (code !== 0) {
                return next(new Error(`Unable to set ownership of files properly, exited with code ${code} signal ${signal}.`));
            }
            return next();
        });
    }

    isSelf(moveTo, moveFrom) {
        const target = this.server.path(moveTo);
        const source = this.server.path(moveFrom);

        if (!_.startsWith(target, source)) {
            return false;
        }

        const end = target.slice(source.length);
        if (!end) {
            return true;
        }

        return _.startsWith(end, '/');
    }

    write(file, data, next) {
        Async.series([
            callback => {
                this.server.knownWrite = true;
                callback();
            },
            callback => {
                Fs.outputFile(this.server.path(file), data, callback);
            },
            callback => {
                this.chown(file, callback);
            },
        ], next);
    }

    read(file, next) {
        Fs.stat(this.server.path(file), (err, stat) => {
            if (err) return next(err);
            if (!stat.isFile()) {
                return next(new Error('The file requested does not appear to be a file.'));
            }
            if (stat.size > 10000000) {
                return next(new Error('This file is too large to open.'));
            }
            Fs.readFile(this.server.path(file), 'utf8', next);
        });
    }

    readEnd(file, bytes, next) {
        if (_.isFunction(bytes)) {
            next = bytes; // eslint-disable-line
            bytes = 80000; // eslint-disable-line
        }
        Fs.stat(this.server.path(file), (err, stat) => {
            if (err) return next(err);
            if (!stat.isFile()) {
                return next(new Error('The file requested does not appear to be a file.'));
            }
            let opts = {};
            let lines = '';
            if (stat.size > bytes) {
                opts = {
                    start: (stat.size - bytes),
                    end: stat.size,
                };
            }
            const stream = Fs.createReadStream(this.server.path(file), opts);
            stream.on('data', data => {
                lines += data;
            });
            stream.on('end', () => {
                next(null, lines);
            });
        });
    }

    mkdir(path, next) {
        if (!_.isArray(path)) {
            Fs.ensureDir(this.server.path(path), err => {
                if (err) return next(err);
                this.chown(path, next);
            });
        } else {
            Async.eachOfLimit(path, 5, (value, key, callback) => {
                Fs.ensureDir(this.server.path(value), err => {
                    if (err) return callback(err);
                    this.chown(value, callback);
                });
            }, next);
        }
    }

    delete(path, next) {
        // Safety - prevent deleting the main folder.
        if (Path.resolve(this.server.path(path)) === this.server.path()) {
            return next(new Error('You cannot delete your home folder.'));
        }
        Fs.remove(this.server.path(path), next);
    }

    copy(initial, ending, opts, next) {
        if (_.isFunction(opts)) {
            next = opts; // eslint-disable-line
            opts = {}; // eslint-disable-line
        }

        if (!_.isArray(initial) && !_.isArray(ending)) {
            if (this.isSelf(ending, initial)) {
                return next(new Error('You cannot copy a folder into itself.'));
            }
            Async.series([
                callback => {
                    Fs.copy(this.server.path(initial), this.server.path(ending), {
                        clobber: opts.clobber || true,
                        preserveTimestamps: opts.timestamps || false,
                    }, callback);
                },
                callback => {
                    this.chown(ending, callback);
                },
            ], next);
        } else if (!_.isArray(initial) || !_.isArray(ending)) {
            return next(new Error('Values passed to copy function must be of the same type (string, string) or (array, array).'));
        } else {
            Async.eachOfLimit(initial, 5, (value, key, callback) => {
                if (_.isUndefined(ending[key])) {
                    return callback(new Error('The number of starting values does not match the number of ending values.'));
                }

                if (this.isSelf(ending[key], value)) {
                    return next(new Error('You cannot copy a folder into itself.'));
                }
                Fs.copy(this.server.path(value), this.server.path(ending[key]), {
                    clobber: _.get(opts, 'clobber', true),
                    preserveTimestamps: _.get(opts, 'timestamps', false),
                }, err => {
                    if (err) return callback(err);
                    this.chown(ending[key], callback);
                });
            }, next);
        }
    }

    stat(file, next) {
        Fs.stat(this.server.path(file), (err, stat) => {
            if (err) next(err);
            Mime.detectFile(this.server.path(file), (mimeErr, result) => {
                next(null, {
                    'name': (Path.parse(this.server.path(file))).base,
                    'created': stat.ctime,
                    'modified': stat.mtime,
                    'size': stat.size,
                    'directory': stat.isDirectory(),
                    'file': stat.isFile(),
                    'symlink': stat.isSymbolicLink(),
                    'mime': result || 'unknown',
                });
            });
        });
    }

    move(initial, ending, next) {
        if (!_.isArray(initial) && !_.isArray(ending)) {
            if (this.isSelf(ending, initial)) {
                return next(new Error('You cannot move a file or folder into itself.'));
            }
            Fs.move(this.server.path(initial), this.server.path(ending), { clobber: false }, err => {
                if (err && !_.startsWith(err.message, 'EEXIST:')) return next(err);
                this.chown(ending, next);
            });
        } else if (!_.isArray(initial) || !_.isArray(ending)) {
            return next(new Error('Values passed to move function must be of the same type (string, string) or (array, array).'));
        } else {
            Async.eachOfLimit(initial, 5, (value, key, callback) => {
                if (_.isUndefined(ending[key])) {
                    return callback(new Error('The number of starting values does not match the number of ending values.'));
                }

                if (this.isSelf(ending[key], value)) {
                    return next(new Error('You cannot move a file or folder into itself.'));
                }
                Fs.move(this.server.path(value), this.server.path(ending[key]), { clobber: false }, err => {
                    if (err && !_.startsWith(err.message, 'EEXIST:')) return callback(err);
                    this.chown(ending[key], callback);
                });
            }, next);
        }
    }

    decompress(files, next) {
        if (!_.isArray(files)) {
            const fromFile = this.server.path(files);
            const toDir = fromFile.substring(0, _.lastIndexOf(fromFile, '/'));
            this.systemDecompress(fromFile, toDir, next);
        } else if (_.isArray(files)) {
            Async.eachLimit(files, 1, (file, callback) => {
                const fromFile = this.server.path(file);
                const toDir = fromFile.substring(0, _.lastIndexOf(fromFile, '/'));
                this.systemDecompress(fromFile, toDir, callback);
            }, next);
        } else {
            return next(new Error('Invalid datatype passed to decompression function.'));
        }
    }

    systemDecompress(file, to, next) {
        Mime.detectFile(file, (err, result) => {
            if (err) return next(err);

            let Exec;
            if (result === 'application/x-gzip' || result === 'application/gzip') {
                Exec = Process.spawn('tar', ['xzf', file, '-C', to], {});
            } else if (result === 'application/zip') {
                Exec = Process.spawn('unzip', [file, '-d', to], {});
            } else {
                return next(new Error(`Decompression of file failed: ${result} is not a decompessible Mimetype.`));
            }

            Exec.on('error', execErr => {
                this.server.log.error(execErr);
                return next(new Error('There was an error while attempting to decompress this file.'));
            });
            Exec.on('exit', (code, signal) => {
                if (code !== 0) {
                    return next(new Error(`Decompression of file exited with code ${code} signal ${signal}.`));
                }

                this.chown(to, next);
            });
        });
    }

    // Unlike other functions, if multiple files and folders are passed
    // they will all be combined into a single archive.
    compress(files, to, next) {
        if (!_.isString(to)) {
            return next(new Error('The to field must be a string for the folder in which the file should be saved.'));
        }

        const SaveAsName = `ptdlfm.${RandomString.generate(8)}.tar`;
        if (!_.isArray(files)) {
            if (this.isSelf(to, files)) {
                return next(new Error('Unable to compress folder into itself.'));
            }
            this.systemCompress([_.replace(this.server.path(files), `${this.server.path()}/`, '')], Path.join(this.server.path(to), SaveAsName), next);
        } else if (_.isArray(files)) {
            const FileEntries = [];
            Async.series([
                callback => {
                    Async.eachLimit(files, 5, (file, eachCallback) => {
                        // If it is going to be inside itself, skip and move on.
                        if (this.isSelf(to, file)) {
                            return eachCallback();
                        }

                        FileEntries.push(_.replace(this.server.path(file), `${this.server.path()}/`, ''));
                        eachCallback();
                    }, callback);
                },
                callback => {
                    if (_.isEmpty(FileEntries)) {
                        return next(new Error('None of the files passed to the command were valid.'));
                    }
                    this.systemCompress(FileEntries, Path.join(this.server.path(to), SaveAsName), callback);
                },
            ], err => {
                next(err, SaveAsName);
            });
        } else {
            return next(new Error('Invalid datatype passed to decompression function.'));
        }
    }

    systemCompress(files, archive, next) {
        const Exec = Process.spawn('tar', ['czf', archive, files.join(' ')], {
            cwd: this.server.path(),
        });

        Exec.on('error', execErr => {
            this.server.log.error(execErr);
            return next(new Error('There was an error while attempting to compress this folder.'));
        });
        Exec.on('exit', (code, signal) => {
            if (code !== 0) {
                return next(new Error(`Compression of files exited with code ${code} signal ${signal}.`));
            }

            this.chown(archive, err => {
                if (err) return next(err);
                return next(null, Path.basename(archive));
            });
        });
    }

    directory(path, next) {
        const responseFiles = [];
        Async.waterfall([
            callback => {
                Fs.stat(this.server.path(path), (err, s) => {
                    if (err) return callback(err);
                    if (!s.isDirectory()) {
                        return callback(new Error('The path requested is not a valid directory on the system.'));
                    }
                    return callback();
                });
            },
            callback => {
                Fs.readdir(this.server.path(path), callback);
            },
            (files, callback) => {
                Async.each(files, (item, eachCallback) => {
                    Async.auto({
                        do_stat: aCallback => {
                            Fs.stat(Path.join(this.server.path(path), item), (statErr, stat) => {
                                aCallback(statErr, stat);
                            });
                        },
                        do_mime: aCallback => {
                            Mime.detectFile(Path.join(this.server.path(path), item), (mimeErr, result) => {
                                aCallback(mimeErr, result);
                            });
                        },
                        do_push: ['do_stat', 'do_mime', (results, aCallback) => {
                            responseFiles.push({
                                'name': item,
                                'created': results.do_stat.birthtime,
                                'modified': results.do_stat.mtime,
                                'size': results.do_stat.size,
                                'directory': results.do_stat.isDirectory(),
                                'file': results.do_stat.isFile(),
                                'symlink': results.do_stat.isSymbolicLink(),
                                'mime': results.do_mime || 'unknown',
                            });
                            aCallback();
                        }],
                    }, eachCallback);
                }, callback);
            },
        ], err => {
            next(err, _.sortBy(responseFiles, [(o) => { return _.lowerCase(o.name); }, 'created'])); // eslint-disable-line
        });
    }
}

module.exports = FileSystem;
<|endoftext|>


<commit_before>(function () {
  "use strict";

  var lih = require('../core/lib/list_item_hunter');
  var Pattern = require('../core/lib/object_factory').Pattern;
  var extend = require('util')._extend;
  var pa = require('../core/lib/pattern_assembler');
  var pattern_assembler = new pa();

  // fake pattern creators
  function createFakeListPattern(customProps) {
    var inputs = {
      relPath: '01-molecules/01-lists/00-list.mustache',
      data: {}
    };
    var pattern = new Pattern(inputs.relPath);

    return extend(pattern, customProps);
  }

  function createFakePatternLab(customProps) {

    //NOTE: These listitems are faked so that pattern_assembler.combine_listitems has already clobbered them.

    var pl = {
      "listitems": {
        "1": [
          {
            "title": "Foo",
            "message": "FooM"
          }
        ],
        "2" : [
          {
            "title": "Foo",
            "message": "FooM"
          },
          {
            "title": "Bar",
            "message": "BarM"
          }
        ],
        "3": [
          {
            "title": "Foo",
            "message": "FooM"
          },
          {
            "title": "Bar",
            "message": "BarM"
          },
          {
            "title": "Baz",
            "message": "BazM"
          },
        ]
      },
      "data": {
        "link": {},
        "partials": []
      },
      "config": {
        "debug": false,
        "paths": {
          "source": {
            "patterns": "./test/files/_patterns"
          }
        },
        "outputFileSuffixes": {
          "rendered": ''
        }
      },
      "partials" : {},
      "patterns" : []
    };

    return extend(pl, customProps);
  }

  exports['list_item_hunter'] = {
    'process_list_item_partials finds and outputs basic repeating blocks': function(test){
      //arrange
      //setup current pattern from what we would have during execution
      var currentPattern = createFakeListPattern({
        "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
        "extendedTemplate": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
        "key": "test-patternName"
      });
      var patternlab = createFakePatternLab();
      var list_item_hunter = new lih();

      //act
      list_item_hunter.process_list_item_partials(currentPattern, patternlab);

      //assert
      test.equals(currentPattern.extendedTemplate, "FooBar" );

      test.done();
    },

    'process_list_item_partials listitems with lowercase name' : function(test){
      //arrange
      //setup current pattern from what we would have during execution
      var currentPattern = createFakeListPattern({
        "template": "{{#listitems.two}}{{ title }}{{/listitems.two}}",
        "extendedTemplate" : "{{#listitems.two}}{{ title }}{{/listitems.two}}",
        "key": "test-patternName"
      });
      var patternlab = createFakePatternLab();
      var list_item_hunter = new lih();

      //act
      list_item_hunter.process_list_item_partials(currentPattern, patternlab);

      //assert
      test.equals(currentPattern.extendedTemplate, "FooBar" );

      test.done();
    },

    'process_list_item_partials finds partials and outputs repeated renders': function(test){
      //arrange
      //setup current pattern from what we would have during execution
      var currentPattern = createFakeListPattern({
        "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
        "extendedTemplate": "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}",
        "key": "test-patternName"
      });

      var patternlab = createFakePatternLab({
        "patterns": [
          {
            "template": "{{ title }}",
            "extendedTemplate" : "{{ title }}",
            "patternPartial": "test-simple",
            "jsonFileData" : {}
          }

        ]
      });

      var list_item_hunter = new lih();

      //act
      list_item_hunter.process_list_item_partials(currentPattern, patternlab);

      //assert
      test.equals(currentPattern.extendedTemplate, "FooBar" );

      test.done();
    },

    'process_list_item_partials finds verbose partials and outputs repeated renders' : function(test){
      var pattern1 = createFakeListPattern({
        "template": "{{#listItems.one}}{{> 00-test/00-foo.mustache }}{{/listItems.one}}",
        "extendedTemplate" : "{{#listItems.one}}{{> 00-test/00-foo.mustache }}{{/listItems.one}}",
        "patternPartial": "test-patternName1",
        "relPath": "00-test/02-patternName1.mustache"
      });

      var pattern2 = createFakeListPattern({
        "template": "{{#listItems.two}}{{> 00-test/00-bar.mustache }}{{/listItems.two}}",
        "extendedTemplate" : "{{#listItems.two}}{{> 00-test/00-bar.mustache }}{{/listItems.two}}",
        "patternPartial": "test-patternName2",
        "relPath": "00-test/03-patternName2.mustache"
      });

      var patternlab = createFakePatternLab({
        "patterns": [
          Pattern.create('00-test/00-foo.mustache', null, {
            "template": "{{ title }}",
            "extendedTemplate": "{{ title }}",
            "relPath": "00-test/00-foo.mustache"
          }),
          Pattern.create('00-test/00-bar.mustache', null, {
            "template": "{{ title }}",
            "extendedTemplate": "{{ title }}",
            "relPath": "00-test/00-bar.mustache"
          })
        ]
      });

      // {
      //  "template": "{{ title }}",
      //  "extendedTemplate" : "{{ title }}",
      //  "subdir": "00-test",
      //  "fileName": "00-foo",
      //  "jsonFileData" : {},
      //  "patternPartial": "test-foo",
      // },
      // {
      //  "template": "{{ title }}",
      //  "extendedTemplate" : "{{ title }}",
      //  "subdir": "00-test",
      //  "fileName": "01-bar",
      //  "jsonFileData" : {},
      //  "patternPartial": "test-bar",
      // }

      var list_item_hunter = new lih();

      //act
      list_item_hunter.process_list_item_partials(pattern1, patternlab);
      list_item_hunter.process_list_item_partials(pattern2, patternlab);

      //assert
      test.equals(pattern1.extendedTemplate, "Foo" );
      test.equals(pattern2.extendedTemplate, "FooBar" );

      test.done();
    },

    'process_list_item_partials overwrites listItem property if that property is in local .listitem.json': function(test) {
      //arrange
      //setup current pattern from what we would have during execution
      var currentPattern = createFakeListPattern({
        "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
        "extendedTemplate": "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}",
        "key": "test-patternName",
        "jsonFileData": {},
        "listitems": {
          "2": [
            { "title": "One" },
            { "title": "Two" }
          ]
        }
      });
      var patternlab = createFakePatternLab({
        "patterns": [
          createFakeListPattern({
            "template": "{{ title }}",
            "extendedTemplate": "{{ title }}",
            "patternPartial": "test-simple",
            "jsonFileData": {}
          })
        ]
      });
      var list_item_hunter = new lih();

      //act
      list_item_hunter.process_list_item_partials(currentPattern, patternlab);

      //assert
      test.equals(currentPattern.extendedTemplate, "OneTwo" );

      test.done();
    },

    'process_list_item_partials keeps listItem property if that property is not in local .listitem.json' : function(test){
      //arrange
      //setup current pattern from what we would have during execution
      var currentPattern = createFakeListPattern({
        "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}",
        "extendedTemplate": "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}",
        "key": "test-patternName",
        "jsonFileData": {},
        "listitems": {
          "2": [
            { "title": "One" },
            { "title": "Two" }
          ]
        }
      });
      var patternlab = createFakePatternLab({
        "patterns": [
          createFakeListPattern({
            "template": "{{ title }}",
            "extendedTemplate": "{{ title }}",
            "patternPartial": "test-simple",
            "jsonFileData" : {}
          })
        ]
      });
      var list_item_hunter = new lih();

      //act
      list_item_hunter.process_list_item_partials(currentPattern, patternlab);

      //assert
      test.equals(currentPattern.extendedTemplate, "Foo" );

      test.done();
    },

    'process_list_item_partials uses local listItem property if that property is not set globally' : function(test){
      //arrange
      //setup current pattern from what we would have during execution
      var currentPattern = createFakeListPattern({
        "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}",
        "extendedTemplate": "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}",
        "key": "test-patternName",
        "jsonFileData": {},
        "listitems": {
          "1": [
            { "title": "One" }
          ],
          "2": [
            { "title": "One" },
            { "title": "Two" }
          ]
        }
      });

      var patternlab = createFakePatternLab({
        "patterns": [

          createFakeListPattern({
            "template": "{{ title }}",
            "extendedTemplate": "{{ title }}",
            "patternPartial": "test-simple",
            "jsonFileData": {}
          })
        ]
      });
      delete patternlab.listitems["1"]; // remove the "1" list

      var list_item_hunter = new lih();

      //act
      list_item_hunter.process_list_item_partials(currentPattern, patternlab);

      //assert
      test.equals(typeof patternlab.listitems["1"], "undefined");
      test.equals(currentPattern.extendedTemplate, "One" );

      test.done();
    },

    'process_list_item_partials - correctly ignores bookended partials without a style modifier when the same partial has a style modifier between' : function(test){
      //arrange
      var fs = require('fs-extra');
      var pa = require('../core/lib/pattern_assembler');
      var pattern_assembler = new pa();
      var list_item_hunter = new lih();
      var patterns_dir = './test/files/_patterns';

      var pl = {};
      pl.config = {};
      pl.data = {};
      pl.data.link = {};
      pl.config.debug = false;
      pl.patterns = [];
      pl.partials = {};
      pl.config.patterns = { source: patterns_dir };
      pl.listitems = {
        "1": [
           {
              "message": "Foo"
           }
        ],
        "2": [
           {
              "message": "Foo"
           },
           {
              "message": "Bar"
           }
        ]
      };

      var atomPattern = new Pattern('00-test/03-styled-atom.mustache');
      atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8');
      atomPattern.extendedTemplate = atomPattern.template;
      atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern);

      var bookendPattern = new Pattern('00-test/11-bookend-listitem.mustache');
      bookendPattern.template = fs.readFileSync(patterns_dir + '/00-test/11-bookend-listitem.mustache', 'utf8');
      bookendPattern.extendedTemplate = bookendPattern.template;
      bookendPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(bookendPattern);

      pl.patterns.push(atomPattern);
      pl.patterns.push(bookendPattern);

      //act
      list_item_hunter.process_list_item_partials(bookendPattern, pl);

      //assert. here we expect {{styleModifier}} to be replaced with an empty string or the styleModifier value from the found partial with the :styleModifier
      var expectedValue = '
  Foo   Foo   Foo   Bar   Bar   Bar
';
      test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim());
      test.done();
    },

    'process_list_item_partials - correctly ignores already processed partial that had a style modifier when the same partial no longer has one' : function(test){
      //arrange
      var fs = require('fs-extra');
      var list_item_hunter = new lih();

      var pl = createFakePatternLab();

      var atomPattern = new Pattern('00-test/03-styled-atom.mustache');
      atomPattern.template = fs.readFileSync(pl.config.paths.source.patterns + '/00-test/03-styled-atom.mustache', 'utf8');
      atomPattern.extendedTemplate = atomPattern.template;
      atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern);

      var anotherStyledAtomPattern = new Pattern('00-test/12-another-styled-atom.mustache');
      anotherStyledAtomPattern.template = fs.readFileSync(pl.config.paths.source.patterns + '/00-test/12-another-styled-atom.mustache', 'utf8');
      anotherStyledAtomPattern.extendedTemplate = anotherStyledAtomPattern.template;
      anotherStyledAtomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(anotherStyledAtomPattern);

      var listPattern = new Pattern('00-test/13-listitem.mustache');
      listPattern.template = fs.readFileSync(pl.config.paths.source.patterns + '/00-test/13-listitem.mustache', 'utf8');
      listPattern.extendedTemplate = listPattern.template;
      listPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(listPattern);

      pl.patterns.push(atomPattern);
      pl.patterns.push(anotherStyledAtomPattern);
      pl.patterns.push(listPattern);

      //act

      //might need to cal processPatternRecursive instead
      pattern_assembler.process_pattern_recursive(atomPattern.relPath, pl);
      pattern_assembler.process_pattern_recursive(anotherStyledAtomPattern.relPath, pl);
      pattern_assembler.process_pattern_recursive(listPattern.relPath, pl);

      //assert.
      var expectedValue = '
  FooM
';
      test.equals(listPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim());
      test.done();
    },

  };

})();
<commit_msg>Convert list_item_hunter.js to use tap/tape closes #503<commit_after>"use strict";

var tap = require('tap');

var lih = require('../core/lib/list_item_hunter');
var Pattern = require('../core/lib/object_factory').Pattern;
var extend = require('util')._extend;
var pa = require('../core/lib/pattern_assembler');
var pattern_assembler = new pa();

// fake pattern creators
function createFakeListPattern(customProps) {
  var inputs = {
    relPath: '01-molecules/01-lists/00-list.mustache',
    data: {}
  };
  var pattern = new Pattern(inputs.relPath);

  return extend(pattern, customProps);
}

function createFakePatternLab(customProps) {

  //NOTE: These listitems are faked so that pattern_assembler.combine_listitems has already clobbered them.

  var pl = {
    "listitems": {
      "1": [
        {
          "title": "Foo",
          "message": "FooM"
        }
      ],
      "2" : [
        {
          "title": "Foo",
          "message": "FooM"
        },
        {
          "title": "Bar",
          "message": "BarM"
        }
      ],
      "3": [
        {
          "title": "Foo",
          "message": "FooM"
        },
        {
          "title": "Bar",
          "message": "BarM"
        },
        {
          "title": "Baz",
          "message": "BazM"
        },
      ]
    },
    "data": {
      "link": {},
      "partials": []
    },
    "config": {
      "debug": false,
      "paths": {
        "source": {
          "patterns": "./test/files/_patterns"
        }
      },
      "outputFileSuffixes": {
        "rendered": ''
      }
    },
    "partials" : {},
    "patterns" : []
  };

  return extend(pl, customProps);
}

tap.test('process_list_item_partials finds and outputs basic repeating blocks', function(test){
  //arrange
  //setup current pattern from what we would have during execution
  var currentPattern = createFakeListPattern({
    "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
    "extendedTemplate": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
    "key": "test-patternName"
  });
  var patternlab = createFakePatternLab();
  var list_item_hunter = new lih();

  //act
  list_item_hunter.process_list_item_partials(currentPattern, patternlab);

  //assert
  test.equals(currentPattern.extendedTemplate, "FooBar" );

  test.end();
});

tap.test('process_list_item_partials listitems with lowercase name', function(test){
  //arrange
  //setup current pattern from what we would have during execution
  var currentPattern = createFakeListPattern({
    "template": "{{#listitems.two}}{{ title }}{{/listitems.two}}",
    "extendedTemplate" : "{{#listitems.two}}{{ title }}{{/listitems.two}}",
    "key": "test-patternName"
  });
  var patternlab = createFakePatternLab();
  var list_item_hunter = new lih();

  //act
  list_item_hunter.process_list_item_partials(currentPattern, patternlab);

  //assert
  test.equals(currentPattern.extendedTemplate, "FooBar" );

  test.end();
});

tap.test('process_list_item_partials finds partials and outputs repeated renders', function(test){
  //arrange
  //setup current pattern from what we would have during execution
  var currentPattern = createFakeListPattern({
    "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
    "extendedTemplate": "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}",
    "key": "test-patternName"
  });

  var patternlab = createFakePatternLab({
    "patterns": [
      {
        "template": "{{ title }}",
        "extendedTemplate" : "{{ title }}",
        "patternPartial": "test-simple",
        "jsonFileData" : {}
      }

    ]
  });

  var list_item_hunter = new lih();

  //act
  list_item_hunter.process_list_item_partials(currentPattern, patternlab);

  //assert
  test.equals(currentPattern.extendedTemplate, "FooBar" );

  test.end();
});

tap.test('process_list_item_partials finds verbose partials and outputs repeated renders', function(test){
  var pattern1 = createFakeListPattern({
    "template": "{{#listItems.one}}{{> 00-test/00-foo.mustache }}{{/listItems.one}}",
    "extendedTemplate" : "{{#listItems.one}}{{> 00-test/00-foo.mustache }}{{/listItems.one}}",
    "patternPartial": "test-patternName1",
    "relPath": "00-test/02-patternName1.mustache"
  });

  var pattern2 = createFakeListPattern({
    "template": "{{#listItems.two}}{{> 00-test/00-bar.mustache }}{{/listItems.two}}",
    "extendedTemplate" : "{{#listItems.two}}{{> 00-test/00-bar.mustache }}{{/listItems.two}}",
    "patternPartial": "test-patternName2",
    "relPath": "00-test/03-patternName2.mustache"
  });

  var patternlab = createFakePatternLab({
    "patterns": [
      Pattern.create('00-test/00-foo.mustache', null, {
        "template": "{{ title }}",
        "extendedTemplate": "{{ title }}",
        "relPath": "00-test/00-foo.mustache"
      }),
      Pattern.create('00-test/00-bar.mustache', null, {
        "template": "{{ title }}",
        "extendedTemplate": "{{ title }}",
        "relPath": "00-test/00-bar.mustache"
      })
    ]
  });

  // {
  //  "template": "{{ title }}",
  //  "extendedTemplate" : "{{ title }}",
  //  "subdir": "00-test",
  //  "fileName": "00-foo",
  //  "jsonFileData" : {},
  //  "patternPartial": "test-foo",
  // },
  // {
  //  "template": "{{ title }}",
  //  "extendedTemplate" : "{{ title }}",
  //  "subdir": "00-test",
  //  "fileName": "01-bar",
  //  "jsonFileData" : {},
  //  "patternPartial": "test-bar",
  // }

  var list_item_hunter = new lih();

  //act
  list_item_hunter.process_list_item_partials(pattern1, patternlab);
  list_item_hunter.process_list_item_partials(pattern2, patternlab);

  //assert
  test.equals(pattern1.extendedTemplate, "Foo" );
  test.equals(pattern2.extendedTemplate, "FooBar" );

  test.end();
});

tap.test('process_list_item_partials overwrites listItem property if that property is in local .listitem.json', function(test) {
  //arrange
  //setup current pattern from what we would have during execution
  var currentPattern = createFakeListPattern({
    "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}",
    "extendedTemplate": "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}",
    "key": "test-patternName",
    "jsonFileData": {},
    "listitems": {
      "2": [
        { "title": "One" },
        { "title": "Two" }
      ]
    }
  });
  var patternlab = createFakePatternLab({
    "patterns": [
      createFakeListPattern({
        "template": "{{ title }}",
        "extendedTemplate": "{{ title }}",
        "patternPartial": "test-simple",
        "jsonFileData": {}
      })
    ]
  });
  var list_item_hunter = new lih();

  //act
  list_item_hunter.process_list_item_partials(currentPattern, patternlab);

  //assert
  test.equals(currentPattern.extendedTemplate, "OneTwo" );

  test.end();
});

tap.test('process_list_item_partials keeps listItem property if that property is not in local .listitem.json', function(test){
  //arrange
  //setup current pattern from what we would have during execution
  var currentPattern = createFakeListPattern({
    "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}",
    "extendedTemplate": "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}",
    "key": "test-patternName",
    "jsonFileData": {},
    "listitems": {
      "2": [
        { "title": "One" },
        { "title": "Two" }
      ]
    }
  });
  var patternlab = createFakePatternLab({
    "patterns": [
      createFakeListPattern({
        "template": "{{ title }}",
        "extendedTemplate": "{{ title }}",
        "patternPartial": "test-simple",
        "jsonFileData" : {}
      })
    ]
  });
  var list_item_hunter = new lih();

  //act
  list_item_hunter.process_list_item_partials(currentPattern, patternlab);

  //assert
  test.equals(currentPattern.extendedTemplate, "Foo" );

  test.end();
});

tap.test('process_list_item_partials uses local listItem property if that property is not set globally', function(test){
  //arrange
  //setup current pattern from what we would have during execution
  var currentPattern = createFakeListPattern({
    "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}",
    "extendedTemplate": "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}",
    "key": "test-patternName",
    "jsonFileData": {},
    "listitems": {
      "1": [
        { "title": "One" }
      ],
      "2": [
        { "title": "One" },
        { "title": "Two" }
      ]
    }
  });

  var patternlab = createFakePatternLab({
    "patterns": [

      createFakeListPattern({
        "template": "{{ title }}",
        "extendedTemplate": "{{ title }}",
        "patternPartial": "test-simple",
        "jsonFileData": {}
      })
    ]
  });
  delete patternlab.listitems["1"]; // remove the "1" list

  var list_item_hunter = new lih();

  //act
  list_item_hunter.process_list_item_partials(currentPattern, patternlab);

  //assert
  test.equals(typeof patternlab.listitems["1"], "undefined");
  test.equals(currentPattern.extendedTemplate, "One" );

  test.end();
});

tap.test('process_list_item_partials - correctly ignores bookended partials without a style modifier when the same partial has a style modifier between', function(test){
  //arrange
  var fs = require('fs-extra');
  var pa = require('../core/lib/pattern_assembler');
  var pattern_assembler = new pa();
  var list_item_hunter = new lih();
  var patterns_dir = './test/files/_patterns';

  var pl = {};
  pl.config = {};
  pl.data = {};
  pl.data.link = {};
  pl.config.debug = false;
  pl.patterns = [];
  pl.partials = {};
  pl.config.patterns = { source: patterns_dir };
  pl.listitems = {
    "1": [
       {
          "message": "Foo"
       }
    ],
    "2": [
       {
          "message": "Foo"
       },
       {
          "message": "Bar"
       }
    ]
  };

  var atomPattern = new Pattern('00-test/03-styled-atom.mustache');
  atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8');
  atomPattern.extendedTemplate = atomPattern.template;
  atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern);

  var bookendPattern = new Pattern('00-test/11-bookend-listitem.mustache');
  bookendPattern.template = fs.readFileSync(patterns_dir + '/00-test/11-bookend-listitem.mustache', 'utf8');
  bookendPattern.extendedTemplate = bookendPattern.template;
  bookendPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(bookendPattern);

  pl.patterns.push(atomPattern);
  pl.patterns.push(bookendPattern);

  //act
  list_item_hunter.process_list_item_partials(bookendPattern, pl);

  //assert. here we expect {{styleModifier}} to be replaced with an empty string or the styleModifier value from the found partial with the :styleModifier
  var expectedValue = '
  Foo   Foo   Foo   Bar   Bar   Bar
';
  test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim());
  test.end();
});

tap.test('process_list_item_partials - correctly ignores already processed partial that had a style modifier when the same partial no longer has one', function(test){
  //arrange
  var fs = require('fs-extra');
  var list_item_hunter = new lih();

  var pl = createFakePatternLab();

  var atomPattern = new Pattern('00-test/03-styled-atom.mustache');
  atomPattern.template = fs.readFileSync(pl.config.paths.source.patterns + '/00-test/03-styled-atom.mustache', 'utf8');
  atomPattern.extendedTemplate = atomPattern.template;
  atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern);

  var anotherStyledAtomPattern = new Pattern('00-test/12-another-styled-atom.mustache');
  anotherStyledAtomPattern.template = fs.readFileSync(pl.config.paths.source.patterns + '/00-test/12-another-styled-atom.mustache', 'utf8');
  anotherStyledAtomPattern.extendedTemplate = anotherStyledAtomPattern.template;
  anotherStyledAtomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(anotherStyledAtomPattern);

  var listPattern = new Pattern('00-test/13-listitem.mustache');
  listPattern.template = fs.readFileSync(pl.config.paths.source.patterns + '/00-test/13-listitem.mustache', 'utf8');
  listPattern.extendedTemplate = listPattern.template;
  listPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(listPattern);

  pl.patterns.push(atomPattern);
  pl.patterns.push(anotherStyledAtomPattern);
  pl.patterns.push(listPattern);

  //act

  //might need to cal processPatternRecursive instead
  pattern_assembler.process_pattern_recursive(atomPattern.relPath, pl);
  pattern_assembler.process_pattern_recursive(anotherStyledAtomPattern.relPath, pl);
  pattern_assembler.process_pattern_recursive(listPattern.relPath, pl);

  //assert.
  var expectedValue = '
  FooM
';
  test.equals(listPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim());
  test.end();
});

<|endoftext|>


<commit_before>//@flow
import { TextureLoader } from "gl-react";
import { disposeObjectMap } from "gl-react/lib/helpers/disposable";
import type { DisposablePromise } from "gl-react/lib/helpers/disposable";

function loadImage(
  src: string,
  success: (img: Image) => void,
  failure: (e: Error) => void
) {
  let img = new window.Image();
  if (src.slice(0, 5) !== "data:") {
    img.crossOrigin = true;
  }
  img.onload = function() {
    if (img) {
      success(img);
    }
    img = null;
  };
  img.onabort = img.onerror = failure;
  img.src = src;
  return function() {
    if (img) {
      img.onload = null;
      img.onerror = null;
      img.onabort = null;
      img.src = "";
      img = null;
    }
  };
}

export default class ImageTextureLoader extends TextureLoader<string> {
  loads: { [key: string]: DisposablePromise<*> } = {};
  textures: { [key: string]: WebGLTexture } = {};
  dispose() {
    disposeObjectMap(this.loads);
    const { gl } = this;
    for (let k in this.textures) {
      gl.deleteTexture(this.textures[k]);
      delete this.textures[k];
    }
  }
  canLoad(input: any) {
    return typeof input === "string";
  }
  load(input: string) {
    const src = input;
    if (src in this.loads) {
      return this.loads[src];
    }
    let dispose = () => {};
    const promise = new Promise(
      (success, failure) => (dispose = loadImage(src, success, failure))
    ).then(img => {
      const { gl } = this;
      const texture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
      this.textures[src] = texture;
      delete this.loads[src];
      return texture;
    });
    const d = { promise, dispose };
    this.loads[src] = d;
    return d;
  }
  get(input: string) {
    return this.textures[input];
  }
  getSize(input: string) {
    const img = this.get(input);
    if (img) {
      return [img.width, img.height];
    }
  }
}
<commit_msg>fixed gl-react-dom ImageTextureLoader getSize feature<commit_after>//@flow
import { TextureLoader } from "gl-react";
import { disposeObjectMap } from "gl-react/lib/helpers/disposable";
import type { DisposablePromise } from "gl-react/lib/helpers/disposable";

function loadImage(
  src: string,
  success: (img: Image) => void,
  failure: (e: Error) => void
) {
  let img = new window.Image();
  if (src.slice(0, 5) !== "data:") {
    img.crossOrigin = true;
  }
  img.onload = function() {
    if (img) {
      success(img);
    }
    img = null;
  };
  img.onabort = img.onerror = failure;
  img.src = src;
  return function() {
    if (img) {
      img.onload = null;
      img.onerror = null;
      img.onabort = null;
      img.src = "";
      img = null;
    }
  };
}

export default class ImageTextureLoader extends TextureLoader<string> {
  loads: { [key: string]: DisposablePromise<*> } = {};
  textures: { [key: string]: WebGLTexture } = {};
  sizes: { [key: string]: [number, number] } = {};
  dispose() {
    disposeObjectMap(this.loads);
    const { gl } = this;
    for (let k in this.textures) {
      gl.deleteTexture(this.textures[k]);
      delete this.textures[k];
    }
  }
  canLoad(input: any) {
    return typeof input === "string";
  }
  load(input: string) {
    const src = input;
    if (src in this.loads) {
      return this.loads[src];
    }
    let dispose = () => {};
    const promise = new Promise(
      (success, failure) => (dispose = loadImage(src, success, failure))
    ).then(img => {
      const { gl } = this;
      const texture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
      this.textures[src] = texture;
      this.sizes[src] = [img.width, img.height];
      delete this.loads[src];
      return texture;
    });
    const d = { promise, dispose };
    this.loads[src] = d;
    return d;
  }
  get(input: string) {
    return this.textures[input];
  }
  getSize(input: string) {
    return this.sizes[input];
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import {createRenderer} from 'react-addons-test-utils';
import { assert} from 'chai';
import sd from 'skin-deep';
import Immutable from 'immutable';
import { latestRuns as data } from './latestRuns';

const pr = data.filter((run) => run.pullRequest);

import {PullRequests} from '../../main/js/components/PullRequests.jsx';

const pipeline = {
    'displayName': 'moreBeers',
    'name': 'morebeers',
    'organization': 'jenkins',
    'weatherScore': 0,
    'branchNames': ['master'],
    'numberOfFailingBranches': 1,
    'numberOfFailingPullRequests': 0,
    'numberOfSuccessfulBranches': 0,
    'numberOfSuccessfulPullRequests': 0,
    'totalNumberOfBranches': 1,
    'totalNumberOfPullRequests': 0
};

describe("PullRequests should render", () => {
  let tree = null;

  beforeEach(() => {
    tree = sd.shallowRender(<PullRequests
      data={[data]}
      back={() => {}}
      pipeline={ Immutable.fromJS(pipeline)}/>);
  });

  it("does renders the PullRequests with data", () => {
    // does data renders?
    const td = tree.subTree('td').getRenderOutput();
    assert.equal(td.props.colSpan, 5)
  });

});

describe("PullRequests should not render", () => {
  let tree = null;

  beforeEach(() => {

    tree = sd.shallowRender(<PullRequests/>);
  });

  it("does not renders the PullRequests without data", () => {
    assert.isNull(tree.getRenderOutput());
  });

});
<commit_msg>UX-200: fix regression in PullRequests spec when "Dashboard" button was removed<commit_after>import React from 'react';
import {createRenderer} from 'react-addons-test-utils';
import { assert} from 'chai';
import sd from 'skin-deep';
import Immutable from 'immutable';
import { latestRuns as data } from './latestRuns';

const pr = data.filter((run) => run.pullRequest);

import {PullRequests} from '../../main/js/components/PullRequests.jsx';

const pipeline = {
    'displayName': 'moreBeers',
    'name': 'morebeers',
    'organization': 'jenkins',
    'weatherScore': 0,
    'branchNames': ['master'],
    'numberOfFailingBranches': 1,
    'numberOfFailingPullRequests': 0,
    'numberOfSuccessfulBranches': 0,
    'numberOfSuccessfulPullRequests': 0,
    'totalNumberOfBranches': 1,
    'totalNumberOfPullRequests': 0
};

describe("PullRequests should render", () => {
  let tree = null;

  beforeEach(() => {
    tree = sd.shallowRender(<PullRequests
      data={[data]}
      back={() => {}}
      pipeline={ Immutable.fromJS(pipeline)}/>);
  });

  it("does renders the PullRequests with data", () => {
    // does data renders?
    let table = tree.dive(['main', 'article', 'Table']).getRenderOutput();
    assert.isOk(table);
    assert.equal(table.props.className, 'pr-table');
  });

});

describe("PullRequests should not render", () => {
  let tree = null;

  beforeEach(() => {

    tree = sd.shallowRender(<PullRequests/>);
  });

  it("does not renders the PullRequests without data", () => {
    assert.isNull(tree.getRenderOutput());
  });

});
<|endoftext|>


<commit_before>/*jshint node:true*/
module.exports = {
  name: 'ember-leaflet-openweathermap-tile-layer',
  description: 'openweathermap tile layer',

  normalizeEntityName: function() {
   // this prevents an error when the entityName is
   // not specified (since that doesn't actually matter
   // to us
  },
  afterInstall: function(options) {
   return this.addBowerPackageToProject('git@github.com:buche/leaflet-openweathermap#bb9272b' );
  }
};
<commit_msg>Remove deprecated usage of addBowerPackageToProject<commit_after>/*jshint node:true*/
module.exports = {
  name: 'ember-leaflet-openweathermap-tile-layer',
  description: 'openweathermap tile layer',

  normalizeEntityName: function() {
   // this prevents an error when the entityName is
   // not specified (since that doesn't actually matter
   // to us
  },
  afterInstall: function(options) {
   return this.addBowerPackageToProject('git@github.com:buche/leaflet-openweathermap.git', '#bb9272b' );
  }
};
<|endoftext|>


<commit_before>'use strict';

const _ = require(`lodash`);
const fs = require(`fs`);
const mkdirp = require(`mkdirp`);
const path = require(`path`);
const required = require(`./required`);
const request = require(`request`);

module.exports = _.curry((argv, ci, build) => {
  required(build, `build_num`);
  required(build, `project`);
  required(build, `username`);
  if (argv.noArtifacts) {
    return Promise.resolve();
  }

  return ci.getBuildArtifacts(build)
    .then((artifacts) => {
      console.log(require('util').inspect(artifacts, { depth: null }));
      return artifacts.reduce((promise, artifact) => {
        return promise.then(() => {
          return downloadArtifact(artifact)
            .catch((reason) => console.error(3, reason));
        });
      }, Promise.resolve());
    });
});

const xunit = /^.*?circle-junit.+?\//;
function downloadArtifact(artifact) {
  return new Promise((resolve, reject) => {
    let filename;
    if (artifact.path.indexOf(`cobertura`) === -1) {
      filename = `${artifact.path.replace(xunit, `reports/`)}`;
    }
    else {
      filename = `reports/cobertura-${artifact.node_index}.xml`;
    }

    if (filename.indexOf(`lerna-debug.log`) !== -1) {
      console.log(`skipping lerna-debug.log`);
      resolve();
      return;
    }

    console.log(`creating directory ${path.dirname(filename)} for artifact ${JSON.stringify(artifact, null, 2)}`);
    mkdirp.sync(path.dirname(filename));
    console.log(`fetching artifact ${artifact.pretty_path} to ${filename}`);
    // For reasons I can't explain, the pipe/writeStream pattern exits after
    // saving the first file.
    request(artifact.url, (err, res) => {
      if (err) {
        console.error(1, error)
        reject(err);
        return;
      }

      fs.writeFile(filename, res.body, (err2) => {
        if (err2) {
          console.error(2, error)
          reject(err2);
          return;
        }

        resolve();
      });
    });
  });
}
<commit_msg>chore(tooling): remove no longer needed debugging code<commit_after>'use strict';

const _ = require(`lodash`);
const fs = require(`fs`);
const mkdirp = require(`mkdirp`);
const path = require(`path`);
const required = require(`./required`);
const request = require(`request`);

module.exports = _.curry((argv, ci, build) => {
  required(build, `build_num`);
  required(build, `project`);
  required(build, `username`);
  if (argv.noArtifacts) {
    return Promise.resolve();
  }

  return ci.getBuildArtifacts(build)
    .then((artifacts) => {
      return artifacts.reduce((promise, artifact) => {
        return promise.then(() => {
          return downloadArtifact(artifact)
            .catch((reason) => console.error(`failed to download artifiact`, reason));
        });
      }, Promise.resolve());
    });
});

const xunit = /^.*?circle-junit.+?\//;
function downloadArtifact(artifact) {
  return new Promise((resolve, reject) => {
    let filename;
    if (artifact.path.indexOf(`cobertura`) === -1) {
      filename = `${artifact.path.replace(xunit, `reports/`)}`;
    }
    else {
      filename = `reports/cobertura-${artifact.node_index}.xml`;
    }

    if (filename.indexOf(`lerna-debug.log`) !== -1) {
      console.log(`skipping lerna-debug.log`);
      resolve();
      return;
    }

    console.log(`creating directory ${path.dirname(filename)} for artifact ${JSON.stringify(artifact, null, 2)}`);
    mkdirp.sync(path.dirname(filename));
    console.log(`fetching artifact ${artifact.pretty_path} to ${filename}`);
    // For reasons I can't explain, the pipe/writeStream pattern exits after
    // saving the first file.
    request(artifact.url, (err, res) => {
      if (err) {
        reject(err);
        return;
      }

      fs.writeFile(filename, res.body, (err2) => {
        if (err2) {
          reject(err2);
          return;
        }

        resolve();
      });
    });
  });
}
<|endoftext|>


<commit_before>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

const FUNC_TYPE_NAME = 'function';
const APP_LOGGER_NAME = 'Application';
const DEFAULT_ENCODING = "utf-8";

// java imports
Calendar = java.util.Calendar;
Vector = java.util.Vector;
ArrayList = java.util.ArrayList;
Logger = java.util.logging.Logger.getLogger(APP_LOGGER_NAME);
Timer = com.eas.concurrent.DeamonTimer;
Lock = java.util.concurrent.locks.ReentrantLock;
TreeMap = java.util.TreeMap;

// platypus script imports
Color = com.eas.client.scripts.ScriptColor;
Colors = java.awt.Color;
ScriptCalculator = com.eas.client.scripts.ScriptCalculator;
ScriptTask = com.eas.client.scripts.ScriptTimerTask;

// platypus misc imports
ExcelReport  = com.eas.client.reports.ExcelReport;

var formsPresent = typeof(com.eas.client.forms.FormRunner) == FUNC_TYPE_NAME;

if(formsPresent)
{
    FieldsGrid = com.eas.dbcontrols.grid.EntityFieldsGrid;
}

Style = com.eas.gui.CascadedStyle;
SqlCompiledQuery = com.eas.client.queries.SqlCompiledQuery;
Parameters = com.bearsoft.rowset.metadata.Parameters;
Parameter = com.bearsoft.rowset.metadata.Parameter;
DataTypeInfo = com.bearsoft.rowset.metadata.DataTypeInfo;

// platypus utilities imports
IDGenerator = com.bearsoft.rowset.utils.IDGenerator;
MD5Generator = com.eas.client.login.MD5Generator;
Blob = com.bearsoft.rowset.compacts.CompactBlob;
Clob = com.bearsoft.rowset.compacts.CompactClob;
CANParser = com.eas.sensors.positioning.utils.can.CANDataParser;

// platypus GIS imports
GisUtilities = com.eas.client.geo.GisUtilities;

if(formsPresent)
{
    ViewpointChangedEvent = com.eas.client.controls.geopane.events.ViewpointChangedEvent;
    MapClickedEvent = com.eas.client.controls.geopane.events.MapClickedEvent;
    MapMouseMovedEvent = com.eas.client.controls.geopane.events.MapMouseMovedEvent;
    Tools = com.eas.dbcontrols.map.mousetools.MouseTools;
    /**
     * jFreeChart
     */
    TimeSeriesChart = com.eas.client.chart.TimeSeriesChart;
    PieChart = com.eas.client.chart.PieChart;
    LineChart = com.eas.client.chart.LineChart;
}

WKTReader = com.vividsolutions.jts.io.WKTReader;
Geometry = com.vividsolutions.jts.geom.Geometry;
LineString = com.vividsolutions.jts.geom.LineString;
LinearRing = com.vividsolutions.jts.geom.LinearRing;
MultiLineString = com.vividsolutions.jts.geom.MultiLineString;
MultiPoint = com.vividsolutions.jts.geom.MultiPoint;
MultiPolygon = com.vividsolutions.jts.geom.MultiPolygon;
Point = com.vividsolutions.jts.geom.Point;
Polygon = com.vividsolutions.jts.geom.Polygon;
PointSymbol = com.eas.client.geo.PointSymbol;

//OLE Automation
ComSession = com.eas.client.scripts.ole.ComSession;
ComObject = com.eas.client.scripts.ole.ComObject;

//Resources
Resource = {};
Object.defineProperty(Resource, "load", {get : function(){
        return function(aResName, aCallback){
            var loaded = com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.load(aResName);
            if(aCallback != undefined)
                aCallback(loaded);
            return loaded;
        };
}});

Object.defineProperty(Resource, "loadText", {get : function(){
        return function(aResName, aCallbackOrEncoding, aCallback){
            if(typeof aCallbackOrEncoding == "function"){
                var _loaded = com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName);
                aCallbackOrEncoding(_loaded);
                return _loaded;
            }else if(typeof aCallback == "function"){
                var __loaded = com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName, aCallbackOrEncoding);
                aCallback(__loaded);
                return __loaded;
            }else if(aCallbackOrEncoding != undefined)
                return com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName, aCallbackOrEncoding);
            else
                return com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName);
        };
}});

function getTreadLocal(aName) {
    return com.eas.script.ScriptUtils.getThreadLocal(aName);
}
Thread = java.lang.Thread;

var THREAD_POOL_SIZE = 10;
var FixedThreadPool;

/**
 * The size of thread pool
 * @return thread pool size
 */
function setThreadPoolSize(aSize) {
    THREAD_POOL_SIZE = aSize;
}

/**
 * The size of thread pool
 */
function getThreadPoolSize() {
    return THREAD_POOL_SIZE;
}

/**
 * Thread - schedules given function in the pool thread
 */
Function.prototype.invokeBackground = function() {
    var func = this;
    var args = arguments;
    if (!FixedThreadPool) {
        FixedThreadPool = java.util.concurrent.Executors.newFixedThreadPool(THREAD_POOL_SIZE, new com.eas.concurrent.DeamonThreadFactory());
    }
    FixedThreadPool.execute(function() {
        func.apply(func, args);
    });
}

/**
 * This is a stub for dynamically loaded modules, since J2SE client
 * allways loads them dynamically and synchronously.
 */
function require(deps, aCallback) {
    if(deps != null && deps != undefined){
        for(var i = 0; i < deps.length; i++)
            com.eas.client.scripts.ScriptRunner.executeResource(deps[i]);
    }
    if(aCallback != null && aCallback != undefined)
        aCallback();
}

function readString(aFileName, aEncoding) {
    var encoding = DEFAULT_ENCODING;
    if (aEncoding) {
        encoding = aEncoding;
    }
    return com.eas.util.FileUtils.readString(new java.io.File(aFileName), encoding);
}

function writeString(aFileName, aText, aEncoding) {
    var encoding = DEFAULT_ENCODING;
    if (aEncoding) {
        encoding = aEncoding;
    }
    com.eas.util.FileUtils.writeString(new java.io.File(aFileName), aText, encoding);
}<commit_msg>Allowing to use a single string as the 1st parameter of require() <commit_after>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

const FUNC_TYPE_NAME = 'function';
const APP_LOGGER_NAME = 'Application';
const DEFAULT_ENCODING = "utf-8";

// java imports
Calendar = java.util.Calendar;
Vector = java.util.Vector;
ArrayList = java.util.ArrayList;
Logger = java.util.logging.Logger.getLogger(APP_LOGGER_NAME);
Timer = com.eas.concurrent.DeamonTimer;
Lock = java.util.concurrent.locks.ReentrantLock;
TreeMap = java.util.TreeMap;

// platypus script imports
Color = com.eas.client.scripts.ScriptColor;
Colors = java.awt.Color;
ScriptCalculator = com.eas.client.scripts.ScriptCalculator;
ScriptTask = com.eas.client.scripts.ScriptTimerTask;

// platypus misc imports
ExcelReport  = com.eas.client.reports.ExcelReport;

var formsPresent = typeof(com.eas.client.forms.FormRunner) == FUNC_TYPE_NAME;

if(formsPresent)
{
    FieldsGrid = com.eas.dbcontrols.grid.EntityFieldsGrid;
}

Style = com.eas.gui.CascadedStyle;
SqlCompiledQuery = com.eas.client.queries.SqlCompiledQuery;
Parameters = com.bearsoft.rowset.metadata.Parameters;
Parameter = com.bearsoft.rowset.metadata.Parameter;
DataTypeInfo = com.bearsoft.rowset.metadata.DataTypeInfo;

// platypus utilities imports
IDGenerator = com.bearsoft.rowset.utils.IDGenerator;
MD5Generator = com.eas.client.login.MD5Generator;
Blob = com.bearsoft.rowset.compacts.CompactBlob;
Clob = com.bearsoft.rowset.compacts.CompactClob;
CANParser = com.eas.sensors.positioning.utils.can.CANDataParser;

// platypus GIS imports
GisUtilities = com.eas.client.geo.GisUtilities;

if(formsPresent)
{
    ViewpointChangedEvent = com.eas.client.controls.geopane.events.ViewpointChangedEvent;
    MapClickedEvent = com.eas.client.controls.geopane.events.MapClickedEvent;
    MapMouseMovedEvent = com.eas.client.controls.geopane.events.MapMouseMovedEvent;
    Tools = com.eas.dbcontrols.map.mousetools.MouseTools;
    /**
     * jFreeChart
     */
    TimeSeriesChart = com.eas.client.chart.TimeSeriesChart;
    PieChart = com.eas.client.chart.PieChart;
    LineChart = com.eas.client.chart.LineChart;
}

WKTReader = com.vividsolutions.jts.io.WKTReader;
Geometry = com.vividsolutions.jts.geom.Geometry;
LineString = com.vividsolutions.jts.geom.LineString;
LinearRing = com.vividsolutions.jts.geom.LinearRing;
MultiLineString = com.vividsolutions.jts.geom.MultiLineString;
MultiPoint = com.vividsolutions.jts.geom.MultiPoint;
MultiPolygon = com.vividsolutions.jts.geom.MultiPolygon;
Point = com.vividsolutions.jts.geom.Point;
Polygon = com.vividsolutions.jts.geom.Polygon;
PointSymbol = com.eas.client.geo.PointSymbol;

//OLE Automation
ComSession = com.eas.client.scripts.ole.ComSession;
ComObject = com.eas.client.scripts.ole.ComObject;

//Resources
Resource = {};
Object.defineProperty(Resource, "load", {
    get : function(){
        return function(aResName, aCallback){
            var loaded = com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.load(aResName);
            if(aCallback != undefined)
                aCallback(loaded);
            return loaded;
        };
    }
});

Object.defineProperty(Resource, "loadText", {
    get : function(){
        return function(aResName, aCallbackOrEncoding, aCallback){
            if(typeof aCallbackOrEncoding == "function"){
                var _loaded = com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName);
                aCallbackOrEncoding(_loaded);
                return _loaded;
            }else if(typeof aCallback == "function"){
                var __loaded = com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName, aCallbackOrEncoding);
                aCallback(__loaded);
                return __loaded;
            }else if(aCallbackOrEncoding != undefined)
                return com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName, aCallbackOrEncoding);
            else
                return com.eas.client.scripts.ScriptRunner.PlatypusScriptedResource.loadText(aResName);
        };
    }
});

function getTreadLocal(aName) {
    return com.eas.script.ScriptUtils.getThreadLocal(aName);
}
Thread = java.lang.Thread;

var THREAD_POOL_SIZE = 10;
var FixedThreadPool;

/**
 * The size of thread pool
 * @return thread pool size
 */
function setThreadPoolSize(aSize) {
    THREAD_POOL_SIZE = aSize;
}

/**
 * The size of thread pool
 */
function getThreadPoolSize() {
    return THREAD_POOL_SIZE;
}

/**
 * Thread - schedules given function in the pool thread
 */
Function.prototype.invokeBackground = function() {
    var func = this;
    var args = arguments;
    if (!FixedThreadPool) {
        FixedThreadPool = java.util.concurrent.Executors.newFixedThreadPool(THREAD_POOL_SIZE, new com.eas.concurrent.DeamonThreadFactory());
    }
    FixedThreadPool.execute(function() {
        func.apply(func, args);
    });
}

/**
 * This is a stub for dynamically loaded modules, since J2SE client
 * allways loads them dynamically and synchronously.
 */
function require(deps, aCallback) {
    if(deps) {
        if (Array.isArray(deps)) {
            for(var i = 0; i < deps.length; i++) {
                com.eas.client.scripts.ScriptRunner.executeResource(deps[i]);
            }
        } else {
            com.eas.client.scripts.ScriptRunner.executeResource(deps);
        }
    }
    if(aCallback) {
        aCallback();
    }
}

function readString(aFileName, aEncoding) {
    var encoding = DEFAULT_ENCODING;
    if (aEncoding) {
        encoding = aEncoding;
    }
    return com.eas.util.FileUtils.readString(new java.io.File(aFileName), encoding);
}

function writeString(aFileName, aText, aEncoding) {
    var encoding = DEFAULT_ENCODING;
    if (aEncoding) {
        encoding = aEncoding;
    }
    com.eas.util.FileUtils.writeString(new java.io.File(aFileName), aText, encoding);
}<|endoftext|>


<commit_before>/*jslint node:true */

/*
 * examples/node-discovery.js
 * https://github.com/101100/xbee-rx
 *
 * Example that performs node discovery.
 *
 * Copyright (c) 2015-2016 Jason Heard
 * Licensed under the MIT license.
 */

"use strict";

var R = require('ramda');
var rx = require('rx');
var xbee_api = require('xbee-api');
var xbeeRx = require('../lib/xbee-rx.js');

var xbee = xbeeRx({
    serialport: '/dev/ttyUSB0',
    serialportOptions: {
        baudrate: 57600
    },
    module: "ZigBee",
    // turn on debugging to see what the library is doing
    debug: false
});

xbee
    .localCommand({
        command: 'NT',
    })
    .flatMap(function (ntResult) {
        // Fulfill promise when NT expires
        // NT is 1/10 seconds
        var timeoutMs = ntResult.readInt16BE(0) * 100;
        console.log("Got node discovery timeout:", timeoutMs, "ms");
        xbee.localCommand({ command: 'ND' }).subscribe();
        return xbee
            .allPackets
            .takeUntil(rx.Observable.timer(timeoutMs * 2))
            .where(R.propEq("type", xbee_api.constants.FRAME_TYPE.AT_COMMAND_RESPONSE))
            .where(R.propEq("command", "ND"))
            .pluck("nodeIdentification");
    })
    .subscribe(function (nodeIdentification) {
        console.log("Found node:\n", nodeIdentification);
    }, function (e) {
        console.log("Command failed:\n", e);
        xbee.close();
    }, function () {
        console.log("Timeout reached; done finding nodes");
        xbee.close();
    });

<commit_msg>Update node discovery example to finish sooner<commit_after>/*jslint node:true */

/*
 * examples/node-discovery.js
 * https://github.com/101100/xbee-rx
 *
 * Example that performs node discovery.
 *
 * Copyright (c) 2015-2016 Jason Heard
 * Licensed under the MIT license.
 */

"use strict";

var R = require('ramda');
var rx = require('rx');
var xbee_api = require('xbee-api');
var xbeeRx = require('../lib/xbee-rx.js');

var xbee = xbeeRx({
    serialport: '/dev/ttyUSB0',
    serialportOptions: {
        baudrate: 57600
    },
    module: "ZigBee",
    // turn on debugging to see what the library is doing
    debug: false
});

xbee
    .localCommand({
        command: 'NT',
    })
    .flatMap(function (ntResult) {
        // Fulfill promise when NT expires
        // NT is 1/10 seconds
        var timeoutMs = ntResult.readInt16BE(0) * 100;
        console.log("Got node discovery timeout:", timeoutMs, "ms");
        xbee.localCommand({ command: 'ND' }).subscribe();
        return xbee
            .allPackets
            .takeUntil(rx.Observable.timer(timeoutMs + 1000))
            .where(R.propEq("type", xbee_api.constants.FRAME_TYPE.AT_COMMAND_RESPONSE))
            .where(R.propEq("command", "ND"))
            .pluck("nodeIdentification");
    })
    .subscribe(function (nodeIdentification) {
        console.log("Found node:\n", nodeIdentification);
    }, function (e) {
        console.log("Command failed:\n", e);
        xbee.close();
    }, function () {
        console.log("Timeout reached; done finding nodes");
        xbee.close();
    });

<|endoftext|>


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

var winston = require('winston'); //logging
const temp = require('temp');
const spawn = require('child_process').spawn;
const exec = require('child_process').exec;
var http = require('http');
var dispatcher = require('httpdispatcher');

// this query is used to check if ROS has started
// while waiting for a permanante fix in https://github.com/realm/realm-object-server/issues/1297.
// query should return 200 with the JSON payload Ex: {"version":"1.8.1","flavor":"developer","setupRequired":true}
var options = {
  hostname: '127.0.0.1',
  port: 9080,
  path: '/api/info',
  method: 'GET'
};

function tryUntilROSStart(options, callback) {
    var req = http.request(options, function(res) {
        callback(null, res);
    });
    req.on('error', function(e) {
        tryUntilROSStart(options, callback);
    });
    req.end();
}

// Automatically track and cleanup files at exit
temp.track();

if (process. argv. length <= 2) {
    console.log("Usage: " + __filename + " somefile.log");
    process.exit(-1);
}
const logFile = process.argv[2];
winston.level = 'debug';
winston.add(winston.transports.File, { filename: logFile });

const PORT = 8888;

function handleRequest(request, response) {
    try {
        //log the request on console
        winston.log(request.url);
        //Dispatch
        dispatcher.dispatch(request, response);
    } catch(err) {
        console.log(err);
    }
}

var syncServerChildProcess = null;

function startRealmObjectServer(done) {
    stopRealmObjectServer(function(err) {
        if(err) {
          return;
        }
        temp.mkdir('ros', function(err, path) {
            if (!err) {
                winston.info("Starting sync server in ", path);
                var env = Object.create( process.env );
                winston.info(env.NODE_ENV);
                env.NODE_ENV = 'development';
                syncServerChildProcess = spawn('realm-object-server',
                        ['--root', path,
                        '--configuration', '/configuration.yml'],
                        { env: env, cwd: path});
                // local config:
                syncServerChildProcess.stdout.on('data', (data) => {
                    winston.info(`stdout: ${data}`);
                });

                syncServerChildProcess.stderr.on('data', (data) => {
                    winston.info(`stderr: ${data}`);
                });

                syncServerChildProcess.on('close', (code) => {
                    winston.info(`child process exited with code ${code}`);
                });

                tryUntilROSStart(options, function(err, resp) {
                    winston.info('>>>>>>>>>>>>>>>>>>> [ROS] server started <<<<<<<<<<<<<<<<<<<');
                    done()
                });
            }
        });
    });
}

function stopRealmObjectServer(callback) {
    if (syncServerChildProcess) {
        syncServerChildProcess.on('exit', function() {
            syncServerChildProcess = null;
            callback();
        });
        syncServerChildProcess.kill();
    } else {
        callback();
    }
}

// start sync server
dispatcher.onGet("/start", function(req, res) {
    startRealmObjectServer(() => {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('Server started');
    })
});

// stop a previously started sync server
dispatcher.onGet("/stop", function(req, res) {
    stopRealmObjectServer(function() {
      winston.info("Sync server stopped");
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Stopping the server');
    });
});

//Create and start the Http server
var server = http.createServer(handleRequest);
server.listen(PORT, function() {
    winston.info("Integration test server listening on: 127.0.0.1:%s", PORT);
});
<commit_msg>Revert "Upgrade Integration Test setup" Test if CI is better (#5174)<commit_after>#!/usr/bin/env nodejs

var winston = require('winston'); //logging
const temp = require('temp');
const spawn = require('child_process').spawn;
const exec = require('child_process').exec;
var http = require('http');
var dispatcher = require('httpdispatcher');

// Automatically track and cleanup files at exit
temp.track();

if (process. argv. length <= 2) {
    console.log("Usage: " + __filename + " somefile.log");
    process.exit(-1);
}
const logFile = process.argv[2];
winston.level = 'debug';
winston.add(winston.transports.File, { filename: logFile });

const PORT = 8888;

function handleRequest(request, response) {
    try {
        //log the request on console
        winston.log(request.url);
        //Dispatch
        dispatcher.dispatch(request, response);
    } catch(err) {
        console.log(err);
    }
}

var syncServerChildProcess = null;

function startRealmObjectServer(done) {
    // Hack for checking the ROS is fully initialized.
    // Consider the ROS is initialized fully only if log below shows twice
    // "client: Closing Realm file: /tmp/ros117521-7-1eiqt7a/internal_data/permission/__auth.realm"
    // https://github.com/realm/realm-object-server/issues/1297
    var logFindingCounter = 2

    stopRealmObjectServer(function(err) {
        if(err) {
          return;
        }
        temp.mkdir('ros', function(err, path) {
            if (!err) {
                winston.info("Starting sync server in ", path);
                var env = Object.create( process.env );
                winston.info(env.NODE_ENV);
                env.NODE_ENV = 'development';
                syncServerChildProcess = spawn('realm-object-server',
                        ['--root', path,
                        '--configuration', '/configuration.yml'],
                        { env: env, cwd: path});
                // local config:
                syncServerChildProcess.stdout.on('data', (data) => {
                    if (logFindingCounter != 0 && /client: Closing Realm file: .*__auth.realm/.test(data)) {
                        if (logFindingCounter == 1) {
                            done()
                        }
                        logFindingCounter--
                    }
                    winston.info(`stdout: ${data}`);
                });

                syncServerChildProcess.stderr.on('data', (data) => {
                    winston.info(`stderr: ${data}`);
                });

                syncServerChildProcess.on('close', (code) => {
                    winston.info(`child process exited with code ${code}`);
                });
            }
        });
    });
}

function stopRealmObjectServer(callback) {
    if (syncServerChildProcess) {
        syncServerChildProcess.on('exit', function() {
            syncServerChildProcess = null;
            callback();
        });
        syncServerChildProcess.kill();
    } else {
        callback();
    }
}

// start sync server
dispatcher.onGet("/start", function(req, res) {
    startRealmObjectServer(() => {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('Starting a server');
    })
});

// stop a previously started sync server
dispatcher.onGet("/stop", function(req, res) {
    stopRealmObjectServer(function() {
      winston.info("Sync server stopped");
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end('Stopping the server');
    });
});

//Create and start the Http server
var server = http.createServer(handleRequest);
server.listen(PORT, function() {
    winston.info("Integration test server listening on: 127.0.0.1:%s", PORT);
});
<|endoftext|>


<commit_before>'use strict';

describe('Controller: AboutCtrl', function () {

  // load the controller's module
  beforeEach(module('carRentalAppApp'));

  var AboutCtrl,
    scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    AboutCtrl = $controller('AboutCtrl', {
      $scope: scope
      // place here mocked dependencies
    });
  }));

  it('should attach a list of awesomeThings to the scope', function () {
    expect(AboutCtrl.awesomeThings.length).toBe(3);
  });
});
<commit_msg>cleanup<commit_after><|endoftext|>