<commit_before>'use strict'; // eslint-disable-line

const http = require('http');
const https = require('https');
const assert = require('assert');
const dhparam = require('../../https/dh2048').dhparam;
const ciphers = require('../../https/ciphers').ciphers;
const errors = require('../../errors');
const { checkSupportIPv6 } = require('./utils');

class Server {

    /**
     * @constructor
     *
     * @param {number} port - Port to listen into
     * @param {werelogs.Logger} logger - Logger object
     */
    constructor(port, logger) {
        assert.strictEqual(typeof port, 'number', 'Port must be a number');
        this._noDelay = true;
        this._cbOnListening = () => {};
        this._cbOnRequest = (req, res) => this._noHandlerCb(req, res);
        this._cbOnCheckContinue = (req, res) => {
            res.writeContinue();
            this._cbOnRequest(req, res);
        };
        // AWS S3 does not respond with 417 Expectation Failed or any error
        // when Expect header is received and the value is not 100-continue
        this._cbOnCheckExpectation = (req, res) => this._cbOnRequest(req, res);
        this._cbOnError = () => false;
        this._cbOnStop = () => {};
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        this._port = port;
        this._address = checkSupportIPv6() ? '::' : '0.0.0.0';
        this._server = null;
        this._logger = logger;
    }

    /**
     * Setter to noDelay, this disable the nagle tcp algorithm, reducing
     * latency for each request
     *
     * @param {boolean} value - { true: Disable, false: Enable }
     * @return {Server} itself
     */
    setNoDelay(value) {
        this._noDelay = value;
        return this;
    }

    /**
     * Getter to access to the http/https server
     *
     * @return {http.Server|https.Server} http/https server
     */
    getServer() {
        return this._server;
    }

    /**
     * Getter to access to the current authority certificate
     *
     * @return {string} Authority certificate
     */
    getAuthorityCertificate() {
        return this._https.ca;
    }

    /**
     * Setter to the listening port
     *
     * @param {number} port - Port to listen into
     * @return {undefined}
     */
    setPort(port) {
        this._port = port;
    }

    /**
     * Getter to access to the listening port
     *
     * @return {number} listening port
     */
    getPort() {
        return this._port;
    }

    /**
     * Setter to the bind address
     *
     * @param {String} address - address bound to the socket
     * @return {undefined}
     */
    setBindAddress(address) {
        this._address = address;
    }

    /**
     * Getter to access the bind address
     *
     * @return {String} address bound to the socket
     */
    getBindAddress() {
        return this._address;
    }

    /**
     * Getter to access to the noDelay (nagle algorithm) configuration
     *
     * @return {boolean} { true: Disable, false: Enable }
     */
    isNoDelay() {
        return this._noDelay;
    }

    /**
     * Getter to know if the server run under https or http
     *
     * @return {boolean} { true: Https server, false: http server }
     */
    isHttps() {
        return !!this._https.cert && !!this._https.key;
    }

    /**
     * Setter for the https configuration
     *
     * @param {string} [cert] - Content of the certificate
     * @param {string} [key] - Content of the key
     * @param {string} [ca] - Content of the authority certificate
     * @param {boolean} [twoWay] - Enable the two way exchange, which means
     *   each client needs to set up an ssl certificate
     * @return {Server} itself
     */
    setHttps(cert, key, ca, twoWay) {
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        if (cert && key) {
            assert.strictEqual(typeof cert, 'string');
            assert.strictEqual(typeof key, 'string');
            this._https.cert = cert;
            this._https.key = key;
        }
        if (ca) {
            assert.strictEqual(typeof ca, 'string');
            this._https.ca = [ca];
        }
        if (twoWay) {
            assert.strictEqual(typeof twoWay, 'boolean');
            this._https.requestCert = twoWay;
        }
        return this;
    }

    /**
     * Function called when no handler specified in the server
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _noHandlerCb(req, res) {
        // if no handler on the Server, send back an internal error
        const err = errors.InternalError;
        const msg = `${err.message}: No handler in Server`;
        res.writeHead(err.code, {
            'Content-Type': 'text/plain',
            'Content-Length': msg.length,
        });
        return res.end(msg);
    }

    /**
     * Function called when request received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onRequest(req, res) {
        return this._cbOnRequest(req, res);
    }

    /**
     * Function called when the Server is listening
     *
     * @return {undefined}
     */
    _onListening() {
        this._logger.info('Server is listening', {
            method: 'arsenal.network.Server._onListening',
            address: this._server.address(),
        });
        this._cbOnListening();
    }

    /**
     * Function called when the Server sends back an error
     *
     * @param {Error} err - Error to be sent back
     * @return {undefined}
     */
    _onError(err) {
        this._logger.error('Server error', {
            method: 'arsenal.network.Server._onError',
            port: this._port,
            error: err.stack || err,
        });
        if (this._cbOnError) {
            if (this._cbOnError(err) === true) {
                process.nextTick(() => this.start());
            }
        }
    }

    /**
     * Function called when the Server is stopped
     *
     * @return {undefined}
     */
    _onClose() {
        if (this._server.listening) {
            this._logger.info('Server is stopped', {
                address: this._server.address(),
            });
        }
        this._server = null;
        this._cbOnStop();
    }

    /**
     * Set the listening callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onListening(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnListening = cb;
        return this;
    }

    /**
     * Set the request handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onRequest(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnRequest = cb;
        return this;
    }

    /**
     * Set the checkExpectation handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckExpectation(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckExpectation = cb;
        return this;
    }

    /**
     * Set the checkContinue handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckContinue(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckContinue = cb;
        return this;
    }

    /**
     * Set the error handler callback, if this handler returns true when an
     * error is triggered, the server will restart
     *
     * @param {function} cb - Callback(err)
     * @return {Server} itself
     */
    onError(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnError = cb;
        return this;
    }

    /**
     * Set the stop handler callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onStop(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnStop = cb;
        return this;
    }

    /**
     * Function called when a secure connection is etablished
     *
     * @param {tls.TlsSocket} sock - socket
     * @return {undefined}
     */
    _onSecureConnection(sock) {
        if (!sock.authorized) {
            this._logger.error('rejected secure connection', {
                address: sock.address(),
                authorized: false,
                error: sock.authorizationError,
            });
        }
    }

    /**
     * function called when an error came from the client request
     *
     * @param {Error} err - Error
     * @param {net.Socket|tls.TlsSocket} sock - Socket
     * @return {undefined}
     */
    _onClientError(err, sock) {
        this._logger.error('client error', {
            method: 'arsenal.network.Server._onClientError',
            error: err.stack || err,
            address: sock.address(),
        });
    }

    /**
     * Function called when request with an HTTP Expect header is received,
     * where the value is not 100-continue
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckExpectation(req, res) {
        return this._cbOnCheckExpectation(req, res);
    }

    /**
     * Function called when request with an HTTP Expect: 100-continue
     * is received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckContinue(req, res) {
        return this._cbOnCheckContinue(req, res);
    }

    /**
     * Function to start the Server
     *
     * @return {Server} itself
     */
    start() {
        if (!this._server) {
            if (this.isHttps()) {
                this._logger.info('starting Server under https', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._https.agent = new https.Agent(this._https);
                this._server = https.createServer(this._https,
                    (req, res) => this._onRequest(req, res));
            } else {
                this._logger.info('starting Server under http', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._server = http.createServer(
                    (req, res) => this._onRequest(req, res));
            }

            this._server.on('error', err => this._onError(err));
            this._server.on('secureConnection',
                sock => this._onSecureConnection(sock));
            this._server.on('connection', sock => {
                // Setting no delay of the socket to the value configured
                sock.setNoDelay(this.isNoDelay());
                sock.on('error', err => this._logger.info(
                 'socket error - request rejected', { error: err }));
            });
            this._server.on('tlsClientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('clientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('checkContinue', (req, res) =>
                    this._onCheckContinue(req, res));
            this._server.on('checkExpectation', (req, res) =>
                    this._onCheckExpectation(req, res));
            this._server.on('listening', () => this._onListening());
        }
        this._server.listen(this._port, this._address);
        return this;
    }

    /**
     * Function to stop the Server
     *
     * @return {Server} itself
     */
    stop() {
        if (this._server) {
            this._server.close(() => this._onClose());
        }
        return this;
    }
}

module.exports = Server;
<commit_msg>bugfix: S3C-3402 remove wrong error log<commit_after>'use strict'; // eslint-disable-line

const http = require('http');
const https = require('https');
const assert = require('assert');
const dhparam = require('../../https/dh2048').dhparam;
const ciphers = require('../../https/ciphers').ciphers;
const errors = require('../../errors');
const { checkSupportIPv6 } = require('./utils');

class Server {

    /**
     * @constructor
     *
     * @param {number} port - Port to listen into
     * @param {werelogs.Logger} logger - Logger object
     */
    constructor(port, logger) {
        assert.strictEqual(typeof port, 'number', 'Port must be a number');
        this._noDelay = true;
        this._cbOnListening = () => {};
        this._cbOnRequest = (req, res) => this._noHandlerCb(req, res);
        this._cbOnCheckContinue = (req, res) => {
            res.writeContinue();
            this._cbOnRequest(req, res);
        };
        // AWS S3 does not respond with 417 Expectation Failed or any error
        // when Expect header is received and the value is not 100-continue
        this._cbOnCheckExpectation = (req, res) => this._cbOnRequest(req, res);
        this._cbOnError = () => false;
        this._cbOnStop = () => {};
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        this._port = port;
        this._address = checkSupportIPv6() ? '::' : '0.0.0.0';
        this._server = null;
        this._logger = logger;
    }

    /**
     * Setter to noDelay, this disable the nagle tcp algorithm, reducing
     * latency for each request
     *
     * @param {boolean} value - { true: Disable, false: Enable }
     * @return {Server} itself
     */
    setNoDelay(value) {
        this._noDelay = value;
        return this;
    }

    /**
     * Getter to access to the http/https server
     *
     * @return {http.Server|https.Server} http/https server
     */
    getServer() {
        return this._server;
    }

    /**
     * Getter to access to the current authority certificate
     *
     * @return {string} Authority certificate
     */
    getAuthorityCertificate() {
        return this._https.ca;
    }

    /**
     * Setter to the listening port
     *
     * @param {number} port - Port to listen into
     * @return {undefined}
     */
    setPort(port) {
        this._port = port;
    }

    /**
     * Getter to access to the listening port
     *
     * @return {number} listening port
     */
    getPort() {
        return this._port;
    }

    /**
     * Setter to the bind address
     *
     * @param {String} address - address bound to the socket
     * @return {undefined}
     */
    setBindAddress(address) {
        this._address = address;
    }

    /**
     * Getter to access the bind address
     *
     * @return {String} address bound to the socket
     */
    getBindAddress() {
        return this._address;
    }

    /**
     * Getter to access to the noDelay (nagle algorithm) configuration
     *
     * @return {boolean} { true: Disable, false: Enable }
     */
    isNoDelay() {
        return this._noDelay;
    }

    /**
     * Getter to know if the server run under https or http
     *
     * @return {boolean} { true: Https server, false: http server }
     */
    isHttps() {
        return !!this._https.cert && !!this._https.key;
    }

    /**
     * Setter for the https configuration
     *
     * @param {string} [cert] - Content of the certificate
     * @param {string} [key] - Content of the key
     * @param {string} [ca] - Content of the authority certificate
     * @param {boolean} [twoWay] - Enable the two way exchange, which means
     *   each client needs to set up an ssl certificate
     * @return {Server} itself
     */
    setHttps(cert, key, ca, twoWay) {
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        if (cert && key) {
            assert.strictEqual(typeof cert, 'string');
            assert.strictEqual(typeof key, 'string');
            this._https.cert = cert;
            this._https.key = key;
        }
        if (ca) {
            assert.strictEqual(typeof ca, 'string');
            this._https.ca = [ca];
        }
        if (twoWay) {
            assert.strictEqual(typeof twoWay, 'boolean');
            this._https.requestCert = twoWay;
        }
        return this;
    }

    /**
     * Function called when no handler specified in the server
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _noHandlerCb(req, res) {
        // if no handler on the Server, send back an internal error
        const err = errors.InternalError;
        const msg = `${err.message}: No handler in Server`;
        res.writeHead(err.code, {
            'Content-Type': 'text/plain',
            'Content-Length': msg.length,
        });
        return res.end(msg);
    }

    /**
     * Function called when request received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onRequest(req, res) {
        return this._cbOnRequest(req, res);
    }

    /**
     * Function called when the Server is listening
     *
     * @return {undefined}
     */
    _onListening() {
        this._logger.info('Server is listening', {
            method: 'arsenal.network.Server._onListening',
            address: this._server.address(),
        });
        this._cbOnListening();
    }

    /**
     * Function called when the Server sends back an error
     *
     * @param {Error} err - Error to be sent back
     * @return {undefined}
     */
    _onError(err) {
        this._logger.error('Server error', {
            method: 'arsenal.network.Server._onError',
            port: this._port,
            error: err.stack || err,
        });
        if (this._cbOnError) {
            if (this._cbOnError(err) === true) {
                process.nextTick(() => this.start());
            }
        }
    }

    /**
     * Function called when the Server is stopped
     *
     * @return {undefined}
     */
    _onClose() {
        if (this._server.listening) {
            this._logger.info('Server is stopped', {
                address: this._server.address(),
            });
        }
        this._server = null;
        this._cbOnStop();
    }

    /**
     * Set the listening callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onListening(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnListening = cb;
        return this;
    }

    /**
     * Set the request handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onRequest(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnRequest = cb;
        return this;
    }

    /**
     * Set the checkExpectation handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckExpectation(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckExpectation = cb;
        return this;
    }

    /**
     * Set the checkContinue handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckContinue(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckContinue = cb;
        return this;
    }

    /**
     * Set the error handler callback, if this handler returns true when an
     * error is triggered, the server will restart
     *
     * @param {function} cb - Callback(err)
     * @return {Server} itself
     */
    onError(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnError = cb;
        return this;
    }

    /**
     * Set the stop handler callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onStop(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnStop = cb;
        return this;
    }

    /**
     * Function called when a secure connection is etablished
     *
     * @param {tls.TlsSocket} sock - socket
     * @return {undefined}
     */
    _onSecureConnection(sock) {
        if (this._https.requestCert && !sock.authorized) {
            this._logger.error('rejected secure connection', {
                address: sock.address(),
                authorized: false,
                error: sock.authorizationError,
            });
        }
    }

    /**
     * function called when an error came from the client request
     *
     * @param {Error} err - Error
     * @param {net.Socket|tls.TlsSocket} sock - Socket
     * @return {undefined}
     */
    _onClientError(err, sock) {
        this._logger.error('client error', {
            method: 'arsenal.network.Server._onClientError',
            error: err.stack || err,
            address: sock.address(),
        });
    }

    /**
     * Function called when request with an HTTP Expect header is received,
     * where the value is not 100-continue
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckExpectation(req, res) {
        return this._cbOnCheckExpectation(req, res);
    }

    /**
     * Function called when request with an HTTP Expect: 100-continue
     * is received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckContinue(req, res) {
        return this._cbOnCheckContinue(req, res);
    }

    /**
     * Function to start the Server
     *
     * @return {Server} itself
     */
    start() {
        if (!this._server) {
            if (this.isHttps()) {
                this._logger.info('starting Server under https', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._https.agent = new https.Agent(this._https);
                this._server = https.createServer(this._https,
                    (req, res) => this._onRequest(req, res));
            } else {
                this._logger.info('starting Server under http', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._server = http.createServer(
                    (req, res) => this._onRequest(req, res));
            }

            this._server.on('error', err => this._onError(err));
            this._server.on('secureConnection',
                sock => this._onSecureConnection(sock));
            this._server.on('connection', sock => {
                // Setting no delay of the socket to the value configured
                sock.setNoDelay(this.isNoDelay());
                sock.on('error', err => this._logger.info(
                 'socket error - request rejected', { error: err }));
            });
            this._server.on('tlsClientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('clientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('checkContinue', (req, res) =>
                    this._onCheckContinue(req, res));
            this._server.on('checkExpectation', (req, res) =>
                    this._onCheckExpectation(req, res));
            this._server.on('listening', () => this._onListening());
        }
        this._server.listen(this._port, this._address);
        return this;
    }

    /**
     * Function to stop the Server
     *
     * @return {Server} itself
     */
    stop() {
        if (this._server) {
            this._server.close(() => this._onClose());
        }
        return this;
    }
}

module.exports = Server;
<|endoftext|>


<commit_before>import path from 'path';
import fs from 'fs.promised';
import ip from 'ip';
import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server';
import chalk from 'chalk';
import getPort from 'get-port';
import clientConfig from './webpack-client-config';
import serverConfig from './webpack-server-config';
import transformConfig from './transform-config';

export default async (watch=false, env, onprogress) => {
	if (watch) {
		return await devBuild(env, onprogress);
	}

	return await prodBuild(env);
};

const devBuild = async (env, onprogress) => {
	let config = clientConfig(env);
	await transformConfig(env, config);

	let userPort = parseInt(process.env.PORT || config.devServer.port, 10) || 8080;
	let port = await getPort(userPort);

	let compiler = webpack(config);
	return await new Promise((resolve, reject) => {

		compiler.plugin('emit', (compilation, callback) => {
			var missingDeps = compilation.missingDependencies;
			var nodeModulesPath = path.resolve(__dirname, '../../../node_modules');

			if (missingDeps.some(file => file.indexOf(nodeModulesPath) !== -1)) {
				// ...tell webpack to watch node_modules recursively until they appear.
				compilation.contextDependencies.push(nodeModulesPath);
			}

			callback();
		});

		compiler.plugin('done', stats => {
			let devServer = config.devServer;

			let protocol = devServer.https ? 'https' : 'http';
			let host = process.env.HOST || devServer.host || 'localhost';

			let serverAddr = `${protocol}://${host}:${chalk.bold(port)}`;
			let localIpAddr = `${protocol}://${ip.address()}:${chalk.bold(port)}`;

			clearConsole();
			if (stats.hasErrors()) {
				process.stdout.write(chalk.red('\Build failed!\n\n'));
			} else {
				process.stdout.write(chalk.green('\nCompiled successfully!\n\n'));

				if (userPort !== port) {
					process.stdout.write(`Port ${chalk.bold(userPort)} is in use, using ${chalk.bold(port)} instead\n\n`);
				}
				process.stdout.write('You can view the application in browser.\n\n');
				process.stdout.write(`${chalk.bold('Local:')}            ${serverAddr}\n`);
				process.stdout.write(`${chalk.bold('On Your Network:')}  ${localIpAddr}\n`);
			}

			if (onprogress) onprogress(stats);
		});
		compiler.plugin('failed', reject);

		let server = new WebpackDevServer(compiler, config.devServer);
		server.listen(port);
	});
};

const prodBuild = async (env) => {
	let config = clientConfig(env);

	await transformConfig(env, config);
	let serverCompiler, clientCompiler = webpack(config);

	if (env.prerender) {
		let ssrConfig = serverConfig(env);
		await transformConfig(env, ssrConfig, true);
		serverCompiler = webpack(ssrConfig);
		await runCompiler(serverCompiler);
	}

	let stats = await runCompiler(clientCompiler);

	// Timeout for plugins that work on `after-emit` event of webpack
	await new Promise(r => setTimeout(()=>	r(), 20));

	return stats;
};

const runCompiler = compiler => new Promise((resolve, reject) => {
	compiler.run((err, stats) => {
		if (err || stats.hasErrors()) {
			showStats(stats);
			reject(chalk.red('Build failed!'));
		}

		resolve(stats);
	});
});

export function showStats(stats) {
	let info = stats.toJson("errors-only");

	if (stats.hasErrors()) {
		info.errors.map(stripBabelLoaderPrefix).forEach( message => {
			process.stderr.write(chalk.red(message)+'\n');
		});
	}

	if (stats.hasWarnings()) {
		info.warnings.map(stripBabelLoaderPrefix).forEach( message => {
			process.stderr.write(chalk.yellow(message)+'\n');
		});
	}

	return stats;
}

export function writeJsonStats(stats) {
	let outputPath = path.resolve(process.cwd(), 'stats.json');
	let jsonStats = stats.toJson({
		json: true,
		chunkModules: true,
		source: false,
	});

	jsonStats = (jsonStats.children && jsonStats.children[0]) || jsonStats;

	jsonStats.modules.forEach(stripBabelLoaderFromModuleNames);
	jsonStats.chunks.forEach(c => c.modules.forEach(stripBabelLoaderFromModuleNames));

	return fs.writeFile(outputPath, JSON.stringify(jsonStats))
		.then(() => {
			process.stdout.write('\nWebpack output stats generated.\n\n');
			process.stdout.write('You can upload your stats.json to:\n');
			process.stdout.write('- https://chrisbateman.github.io/webpack-visualizer/\n');
			process.stdout.write('- https://webpack.github.io/analyse/\n');
		});
}

const clearConsole = () => {
	process.stdout.write(
		process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'
	);
};

const stripBabelLoaderFromModuleNames = m => {
	const keysToNormalize = ['identifier', 'name', 'module', 'moduleName', 'moduleIdentifier'];

	keysToNormalize.forEach(key => {
		if (key in m) {
			m[key] = stripBabelLoaderPrefix(m[key]);
		}
	});

	if (m.reasons) {
		m.reasons.forEach(stripBabelLoaderFromModuleNames);
	}

	return m;
};

const stripBabelLoaderPrefix = log => log.replace(/@?\s*(\.\/~\/babel-loader\/lib\?{[\s\S]*?}!)/g, '');
<commit_msg>Resolve 0.0.0.0 to windows in localhost<commit_after>import path from 'path';
import fs from 'fs.promised';
import ip from 'ip';
import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server';
import chalk from 'chalk';
import getPort from 'get-port';
import clientConfig from './webpack-client-config';
import serverConfig from './webpack-server-config';
import transformConfig from './transform-config';

export default async (watch=false, env, onprogress) => {
	if (watch) {
		return await devBuild(env, onprogress);
	}

	return await prodBuild(env);
};

const devBuild = async (env, onprogress) => {
	let config = clientConfig(env);
	await transformConfig(env, config);

	let userPort = parseInt(process.env.PORT || config.devServer.port, 10) || 8080;
	let port = await getPort(userPort);

	let compiler = webpack(config);
	return await new Promise((resolve, reject) => {

		compiler.plugin('emit', (compilation, callback) => {
			var missingDeps = compilation.missingDependencies;
			var nodeModulesPath = path.resolve(__dirname, '../../../node_modules');

			if (missingDeps.some(file => file.indexOf(nodeModulesPath) !== -1)) {
				// ...tell webpack to watch node_modules recursively until they appear.
				compilation.contextDependencies.push(nodeModulesPath);
			}

			callback();
		});

		compiler.plugin('done', stats => {
			let devServer = config.devServer;
			let protocol = devServer.https ? 'https' : 'http';
			let host = process.env.HOST || 'localhost';

			let serverAddr = `${protocol}://${host}:${chalk.bold(port)}`;
			let localIpAddr = `${protocol}://${ip.address()}:${chalk.bold(port)}`;

			clearConsole();
			if (stats.hasErrors()) {
				process.stdout.write(chalk.red('\Build failed!\n\n'));
			} else {
				process.stdout.write(chalk.green('Compiled successfully!\n\n'));

				if (userPort !== port) {
					process.stdout.write(`Port ${chalk.bold(userPort)} is in use, using ${chalk.bold(port)} instead\n\n`);
				}
				process.stdout.write('You can view the application in browser.\n\n');
				process.stdout.write(`${chalk.bold('Local:')}            ${serverAddr}\n`);
				process.stdout.write(`${chalk.bold('On Your Network:')}  ${localIpAddr}\n`);
			}

			if (onprogress) onprogress(stats);
		});
		compiler.plugin('failed', reject);

		let server = new WebpackDevServer(compiler, config.devServer);
		server.listen(port);
	});
};

const prodBuild = async (env) => {
	let config = clientConfig(env);

	await transformConfig(env, config);
	let serverCompiler, clientCompiler = webpack(config);

	if (env.prerender) {
		let ssrConfig = serverConfig(env);
		await transformConfig(env, ssrConfig, true);
		serverCompiler = webpack(ssrConfig);
		await runCompiler(serverCompiler);
	}

	let stats = await runCompiler(clientCompiler);

	// Timeout for plugins that work on `after-emit` event of webpack
	await new Promise(r => setTimeout(()=>	r(), 20));

	return stats;
};

const runCompiler = compiler => new Promise((resolve, reject) => {
	compiler.run((err, stats) => {
		if (err || stats.hasErrors()) {
			showStats(stats);
			reject(chalk.red('Build failed!'));
		}

		resolve(stats);
	});
});

export function showStats(stats) {
	let info = stats.toJson("errors-only");

	if (stats.hasErrors()) {
		info.errors.map(stripBabelLoaderPrefix).forEach( message => {
			process.stderr.write(chalk.red(message)+'\n');
		});
	}

	if (stats.hasWarnings()) {
		info.warnings.map(stripBabelLoaderPrefix).forEach( message => {
			process.stderr.write(chalk.yellow(message)+'\n');
		});
	}

	return stats;
}

export function writeJsonStats(stats) {
	let outputPath = path.resolve(process.cwd(), 'stats.json');
	let jsonStats = stats.toJson({
		json: true,
		chunkModules: true,
		source: false,
	});

	jsonStats = (jsonStats.children && jsonStats.children[0]) || jsonStats;

	jsonStats.modules.forEach(stripBabelLoaderFromModuleNames);
	jsonStats.chunks.forEach(c => c.modules.forEach(stripBabelLoaderFromModuleNames));

	return fs.writeFile(outputPath, JSON.stringify(jsonStats))
		.then(() => {
			process.stdout.write('\nWebpack output stats generated.\n\n');
			process.stdout.write('You can upload your stats.json to:\n');
			process.stdout.write('- https://chrisbateman.github.io/webpack-visualizer/\n');
			process.stdout.write('- https://webpack.github.io/analyse/\n');
		});
}

const clearConsole = () => {
	process.stdout.write(
		process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'
	);
};

const stripBabelLoaderFromModuleNames = m => {
	const keysToNormalize = ['identifier', 'name', 'module', 'moduleName', 'moduleIdentifier'];

	keysToNormalize.forEach(key => {
		if (key in m) {
			m[key] = stripBabelLoaderPrefix(m[key]);
		}
	});

	if (m.reasons) {
		m.reasons.forEach(stripBabelLoaderFromModuleNames);
	}

	return m;
};

const stripBabelLoaderPrefix = log => log.replace(/@?\s*(\.\/~\/babel-loader\/lib\?{[\s\S]*?}!)/g, '');
<|endoftext|>


<commit_before>var AppModule   = global.autodafe.AppModule;

module.exports = Route.inherits( AppModule );

function Route( params ) {
  this._init( params );
}

Route.prototype._init = function( params ) {
  Route.parent._init.call( this, params );

  if ( typeof params.path != 'string' )
      throw new Error( 'Route path should be string. Type of `%s` is %s'.format( params.path, typeof params.path ) );

    if ( !params.router ) throw new Error( 'Link to router is required for Route creation' );

  this.controller       = null;
  this.action           = null;
  this.connection_types = [];
  this._.router         = params.router;

  this._.path           = '';
  this._.path.get       = function(){
    var controller = this.controller || this.app.default_controller;
    // todo: контроллера может не быть
    var action     = this.action     || this.router.get_controller( controller ).default_action;
    return controller + '.' + action;
  }

  var self = this;
  this.rule_params = [];
  this.rule_re     = {};

  this._filters     = {};
  this._source_rule = null;
  this.rule         = null;

  // valid : ' controller . action | post ', 'controller.action', 'controller|ws', ' controller', '', '|get'
  // pockets : 2 - controller, 4 - action, 5 - connection types: " | post, delete"
  this._re = /^\s*((\w+)\s*(\.\s*(\w+)\s*)?)?(([|,]\s*(post|get|delete|http|ws)\s*)*)$/i;

  this._parse_rule( params.rule );
  this._parse_path( params.path );
};

Route.prototype._parse_rule = function ( rule ) {
  var parts = ( rule || '' ).split(' ');
  while( parts.length > 1 ) {
    var filter = parts.shift() || '';
    var colon  = filter.indexOf( ':' );
    if ( ~colon ) this._filters[ filter.substr( 0, colon ) ] = filter.substr( colon + 1 );
    else          this._filters[ 'hostname' ]                = filter;
  }

  this._source_rule = parts[0].charAt(0) == '/' ? parts[0].substr(1) : parts[0];

  var self = this;
  var re_text = this._source_rule.replace( /<(\w+):(.+?)>/g, function( all, $1, $2 ){
    self.rule_params.push( $1 );
    self.rule_re[ $1 ] = new RegExp( '^' + $2 + '$' );
    return '(' + $2 + ')';
  } );

  this.rule = new RegExp( '^\/?' + re_text + '\/?$' );
};

Route.prototype._parse_path = function ( route_path ) {
  var matches = this._re.exec( route_path );
  if ( !matches ) throw new Error( 'Route path `%s` has bad format'.format( route_path ) );

  this.controller       =   matches[2] || null;
  this.action           =   matches[4] || null;
  this.connection_types = ( matches[5] || '' ).split( /[\s|,]+/ ).filter( Boolean );
  if ( ~this.connection_types.indexOf( 'http' ) ) this.connection_types.push( 'post', 'get', 'delete' );
};

Route.prototype.is_suitable_for = function ( query, extend_params_on_success ) {
  if ( !this.is_allowed_con_type( query.connection_type ) ) return false;
  if ( !this.check_filters( query.parsed_url ) ) return false;

  var matches = this.rule.exec( query.action );
  if ( matches && extend_params_on_success ) {
    query.route = this;

    this.rule_params.forEach( function( param, i ){
      query.params[ param ] = matches[ i+1 ];
    } );
  }

  return matches;
};

Route.prototype.check_filters = function ( parsed_url ) {
  for( var filter_name in this._filters )
    if ( this._filters[ filter_name ] != parsed_url[ filter_name ] )
      return false;

  return true;
};

Route.prototype.is_allowed_con_type = function ( connection_type ) {
  return !this.connection_types.length || ~this.connection_types.indexOf( connection_type );
};

Route.prototype.has_params = function ( params ) {
  params = params || {};

  return this.rule_params.every( function( param ) {
    return param in params && this.rule_re[ param ].test( params[ param ] );
  }, this );
};

Route.prototype.get_rule = function ( params ) {
  return this._source_rule.replace( /<(\w+):(.+?)>/g, function( all, $1, $2 ){
    var param = params[ $1 ];
    delete params[ $1 ];
    return param;
  } );
};<commit_msg>encode and decode uri<commit_after>var AppModule   = global.autodafe.AppModule;

module.exports = Route.inherits( AppModule );

function Route( params ) {
  this._init( params );
}

Route.prototype._init = function( params ) {
  Route.parent._init.call( this, params );

  if ( typeof params.path != 'string' )
      throw new Error( 'Route path should be string. Type of `%s` is %s'.format( params.path, typeof params.path ) );

    if ( !params.router ) throw new Error( 'Link to router is required for Route creation' );

  this.controller       = null;
  this.action           = null;
  this.connection_types = [];
  this._.router         = params.router;

  this._.path           = '';
  this._.path.get       = function(){
    var controller = this.controller || this.app.default_controller;
    // todo: контроллера может не быть
    var action     = this.action     || this.router.get_controller( controller ).default_action;
    return controller + '.' + action;
  }

  var self = this;
  this.rule_params = [];
  this.rule_re     = {};

  this._filters     = {};
  this._source_rule = null;
  this.rule         = null;

  // valid : ' controller . action | post ', 'controller.action', 'controller|ws', ' controller', '', '|get'
  // pockets : 2 - controller, 4 - action, 5 - connection types: " | post, delete"
  this._re = /^\s*((\w+)\s*(\.\s*(\w+)\s*)?)?(([|,]\s*(post|get|delete|http|ws)\s*)*)$/i;

  this._parse_rule( params.rule );
  this._parse_path( params.path );
};

Route.prototype._parse_rule = function ( rule ) {
  var parts = ( rule || '' ).split(' ');
  while( parts.length > 1 ) {
    var filter = parts.shift() || '';
    var colon  = filter.indexOf( ':' );
    if ( ~colon ) this._filters[ filter.substr( 0, colon ) ] = filter.substr( colon + 1 );
    else          this._filters[ 'hostname' ]                = filter;
  }

  this._source_rule = parts[0].charAt(0) == '/' ? parts[0].substr(1) : parts[0];

  var self = this;
  var re_text = this._source_rule.replace( /<(\w+):(.+?)>/g, function( all, $1, $2 ){
    self.rule_params.push( $1 );
    self.rule_re[ $1 ] = new RegExp( '^' + $2 + '$' );
    return '(' + $2 + ')';
  } );

  this.rule = new RegExp( '^\/?' + re_text + '\/?$' );
};

Route.prototype._parse_path = function ( route_path ) {
  var matches = this._re.exec( route_path );
  if ( !matches ) throw new Error( 'Route path `%s` has bad format'.format( route_path ) );

  this.controller       =   matches[2] || null;
  this.action           =   matches[4] || null;
  this.connection_types = ( matches[5] || '' ).split( /[\s|,]+/ ).filter( Boolean );
  if ( ~this.connection_types.indexOf( 'http' ) ) this.connection_types.push( 'post', 'get', 'delete' );
};

Route.prototype.is_suitable_for = function ( query, extend_params_on_success ) {
  if ( !this.is_allowed_con_type( query.connection_type ) ) return false;
  if ( !this.check_filters( query.parsed_url ) ) return false;

  var matches = this.rule.exec( query.action );
  if ( matches && extend_params_on_success ) {
    query.route = this;

    this.rule_params.forEach( function( param, i ){
      query.params[ param ] = decodeURI( matches[ i+1 ] );
    } );
  }

  return matches;
};

Route.prototype.check_filters = function ( parsed_url ) {
  for( var filter_name in this._filters )
    if ( this._filters[ filter_name ] != parsed_url[ filter_name ] )
      return false;

  return true;
};

Route.prototype.is_allowed_con_type = function ( connection_type ) {
  return !this.connection_types.length || ~this.connection_types.indexOf( connection_type );
};

Route.prototype.has_params = function ( params ) {
  params = params || {};

  return this.rule_params.every( function( param ) {
    return param in params && this.rule_re[ param ].test( params[ param ] );
  }, this );
};

Route.prototype.get_rule = function ( params ) {
  return this._source_rule.replace( /<(\w+):(.+?)>/g, function( all, $1, $2 ){
    var param = params[ $1 ];
    delete params[ $1 ];
    return encodeURI( param );
  } );
};<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');

const swagenCore = require('swagen-core');
const ts = require('swagen-typescript-language');

module.exports = class Generator {
    constructor(definition, profile) {
        this.definition = definition;
        this.profile = profile;
        this.transform = new swagenCore.transformer.Transformer(profile);
        this.transform.transformDefinition(this.definition);
        this.options = {
            baseUrlToken: profile.options.baseUrlToken || 'API_BASE_URL',
            serviceClassSuffix: profile.options.serviceClassSuffix || 'Client'
        };
    }

    generate() {
        this.code = new swagenCore.CodeBuilder();
        this.generateInitialCode();
        this.code.blank();
        this.generateImplementations();
        this.code.blank();
        this.generateBuildServiceUrlFunction();
        this.code.blank();
        this.generateSwaggerExceptionClass();
        this.code.blank();
        this.generateThrowExceptionFunction();
        this.code.blank();
        this.generateModels();
        return this.code.toCode();
    }

    generateInitialCode() {
        this.code.line(...ts.buildHeader(this.profile, this.definition));
        this.code.blank();
        this.code.line(
            `import 'rxjs/add/observable/fromPromise';`,
            `import 'rxjs/add/observable/of';`,
            `import 'rxjs/add/observable/throw';`,
            `import 'rxjs/add/operator/map';`,
            `import 'rxjs/add/operator/mergeMap';`,
            `import 'rxjs/add/operator/toPromise';`,
            `import 'rxjs/add/operator/catch';`,
            ``,
            `import { Observable } from 'rxjs/Observable';`,
            `import { Injectable, Inject, Optional, OpaqueToken } from '@angular/core';`,
            `import { Http, Headers, ResponseContentType, Response } from '@angular/http';`,
            ``,
            `export const API_BASE_URL = new OpaqueToken('${this.options.baseUrlToken}');`,
        );
    }

    generateImplementations() {
        let sortedKeys = _.keys(this.definition.services).sort();
        for (let i = 0; i < sortedKeys.length; i++) {
            this.code.blank(i > 0);
            let serviceName = sortedKeys[i];
            let service = this.definition.services[serviceName]
            this.generateImplementation(serviceName, service);
        }
    }

    generateImplementation(serviceName, service) {
        this.code.line(
            `@Injectable()`,
            `export class ${serviceName}${this.options.serviceClassSuffix} {`,
        ).indent();

        this.code.line(
            `private http: Http;`,
            `private baseUrl: string;`,
            `public jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;`
        );

        this.code.blank();

        this.code.line(
            `constructor(@Inject(Http) http: Http, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {`
        )
            .indent()
            .line(
                `this.http = http;`,
                `this.baseUrl = baseUrl || '${this.definition.metadata.baseUrl}';`,
            )
            .unindent('}');

        for (let operationName in service) {
            this.code.blank();
            let operation = service[operationName];
            this.code.line(...ts.buildOperationDocComments(operation));
            this.generateOperation(operationName, operation);
        }

        this.code.unindent('}')
    }

    generateOperation(operationName, operation) {
        let options = {
            modelNs: '',
            returnTypeTransformer: rt => `Observable<${rt}>`
        };
        this.code.inline('public ').inline(ts.getMethodSignature(operationName, operation, options)).inline(' {').done().indent();

        //Check required parameters
        let requiredParams = (operation.parameters || []).filter(p => !!p.required);
        for (let i = 0; i < requiredParams.length; i++) {
            let requiredParam = requiredParams[i];
            this.code
                .line(`if (${requiredParam.name} == undefined || ${requiredParam.name} == null) {`)
                .indent(`throw new Error(\`The parameter '${requiredParam.name}' must be defined.\`);`)
                .unindent(`}`);
        }

        //Resolve path parameters in relative URL.
        let pathParams = (operation.parameters || []).filter(p => p.type === 'path');
        let hasPathParams = pathParams.length > 0;
        this.code
            .inline(`let resourceUrl: string = '${operation.path}'`)
            .inline(`;`, !hasPathParams)
            .done();
        this.code.indent();
        for (let i = 0; i < pathParams.length; i++) {
            this.code
                .inline(`.replace('{${pathParams[i].name}}', encodeURIComponent('' + ${pathParams[i].name}))`)
                .inline(`;`, i === pathParams.length - 1)
                .done();
        }
        this.code.unindent();

        //Query parameters
        let queryParams = (operation.parameters || []).filter(p => p.type === 'query');
        if (queryParams.length > 0) {
            this.code
                .line(`let queryParams: {[key: string]: string} = {`)
                .indent();
            for (let i = 0; i < queryParams.length; i++) {
                this.code
                    .inline(`${queryParams[i].name}: encodeURIComponent('' + ${queryParams[i].name})`)
                    .inline(`,`, i < queryParams.length - 1)
                    .done();
            }
            this.code.unindent('};')
        }

        this.code
            .inline(`let url = buildServiceUrl(this.baseUrl, resourceUrl`)
            .inline(`, queryParams`, queryParams.length > 0)
            .inline(`);`)
            .done();

        //Options
        let bodyParam = (operation.parameters || []).find(p => p.type === 'body');
        let formDataParams = (operation.parameters || []).filter(p => p.type === 'formData');
        let headerParams = (operation.parameters || []).filter(p => p.type === 'header');
        let returnType = ts.getReturnType(operation, { modelNs: '' });

        //Create content variable
        if (formDataParams.length > 0) {
            this.code.line(`const content = new FormData();`);
            for (let i = 0; i < formDataParams.length; i++) {
                let paramName = formDataParams[i].name;
                this.code
                    .line(`if (${paramName} != null && ${paramName} != undefined) {`)
                    .indent()
                        .line(`content.append('${paramName}', ${paramName}.toString());`)
                    .unindent(`}`);
            }
        } else if (bodyParam) {
            this.code.line(`const content = JSON.stringify(${bodyParam.name});`);
        } else {
            this.code.line(`const content = '';`)
        }

        //Create HTTP request options variable
        this.code.line(`let options = {`)
            .indent()
                .line(`body: content,`);
        this.code.line(`headers: new Headers({`)
            .indent();
        for (let i = 0; i < headerParams.length; i++) {
            this.code.line(`'${headerParams[i].name}': ${headerParams[i].name},`);
        }
        if (bodyParam) {
            this.code.line(`'Content-Type': 'application/json',`);
        }
        this.code.line(`'Accept': 'application/json'`);
        this.code.unindent('}),');
        this.code.line(`method: '${operation.verb.toUpperCase()}'`)
        this.code.unindent('};');

        const processMethodName = `process${_.upperFirst(_.camelCase(operationName))}`;
        this.code
            .line(`return this.http.request(url, options).flatMap(response =>`)
            .indent()
                .line(`this.${processMethodName}(response)`)
            .unindent(`).catch(response => {`)
            .indent()
                .line(`if (response instanceof Response) {`)
                .indent()
                    .line(`try {`)
                    .indent()
                        .line(`return this.${processMethodName}(response);`)
                    .unindent(`} catch (e) {`)
                    .indent()
                        .line(`return <Observable<${returnType}>><any>Observable.throw(response);`)
                    .unindent(`}`)
                .unindent('}')
            .unindent(`});`);

        this.code.unindent('}');

        this.code.blank();

        //Process method
        this.code.line(
            `private ${processMethodName}(response: Response): Observable<${returnType}> {`
        ).indent();
        this.code
            .line(`const status = +response.status;`)
            .line(`const responseText = response.text();`)
            .line(`switch (status) {`)
            .indent();
        for (let statusCode in operation.responses) {
            let status = +statusCode;
            if (!status) {
                continue;
            }
            this.code.line(`case ${statusCode}:`)
                .indent();
            if (status >= 200 && status < 400) {
                this.code
                    .line(`let result: string = !responseText ? null : <string>JSON.parse(responseText, this.jsonParseReviver);`)
                    .line(`return Observable.of(result);`)
            } else if (status >= 400) {
                this.code
                    .line(`return throwException(status, responseText);`);
            }
            this.code.unindent();
        }
        this.code.line(`default:`)
            .indent();
        this.code.line(`throw new Error(\`Unexpected status code \${status}\`);`);
        this.code.unindent();

        this.code.unindent('}');

        this.code.unindent(`}`);
    }

    generateModels() {
        let sortedKeys = _.keys(this.definition.models).sort((x, y) => x.toLowerCase().localeCompare(y.toLowerCase()));

        for (let i = 0; i < sortedKeys.length; i++) {
            let modelName = sortedKeys[i];
            let model = this.definition.models[modelName];
            this.code.blank(i > 0);
            this.code.line(
                `export interface ${modelName} {`
            ).indent();

            for (let propertyName in model) {
                let property = model[propertyName];
                this.code
                    .inline(propertyName)
                    .inline('?', !property.required)
                    .inline(': ')
                    .inline(ts.getDataType(property))
                    .inline(` | undefined`, !property.required)
                    .inline(';')
                    .done();
            }

            this.code.unindent('}');
        }

        let enumKeys = _.keys(this.definition.enums).sort((x, y) => x.toLowerCase().localeCompare(y.toLowerCase()));

        for (let i = 0; i < enumKeys.length; i++) {
            let enumName = enumKeys[i];
            let enumValues = this.definition.enums[enumName]
                .map(e => `"${e}"`)
                .join(' | ');
            this.code.blank(sortedKeys.length > 0 || i > 0);
            this.code.line(`export type ${enumName} = ${enumValues};`);
        }

        this.code.blank(sortedKeys.length > 0 || enumKeys.length > 0);
    }

    generateBuildServiceUrlFunction() {
        this.code
            .line(`function buildServiceUrl(baseUrl: string, resourceUrl: string, queryParams?: {[key: string]: string}): string {`)
            .indent()
                .line(`let url: string = baseUrl;`)
                .line(`let baseUrlSlash: boolean = url[url.length - 1] === '/';`)
                .line(`let resourceUrlSlash: boolean = resourceUrl[0] === '/';`)
                .line(`if (!baseUrlSlash && !resourceUrlSlash) {`)
                .indent()
                    .line(`url += '/';`)
                .unindent(`} else if (baseUrlSlash && resourceUrlSlash) {`)
                .indent()
                    .line(`url = url.substr(0, url.length - 1);`)
                .unindent(`}`)
                .line(`url += resourceUrl;`)
                .blank()
                .line(`if (queryParams) {`)
                .indent()
                    .line(`let isFirst: boolean = true;`)
                    .line(`for (let p in queryParams) {`)
                    .indent()
                        .line(`if (queryParams.hasOwnProperty(p) && queryParams[p]) {`)
                        .indent()
                            .line(`let separator: string = isFirst ? '?' : '&';`)
                            .line(`url += \`\${separator}\${p}=\${queryParams[p]}\`;`)
                            .line(`isFirst = false;`)
                        .unindent(`}`)
                    .unindent(`}`)
                .unindent(`}`)
                .line(`return url;`)
            .unindent(`}`);
    }

    generateSwaggerExceptionClass() {
        this.code
            .line(`export class SwaggerException extends Error {`)
            .indent()
                .line(`constructor(public status: number, public response: string, public result: string) {`)
                .indent()
                    .line(`super();`)
                .unindent(`}`)
            .unindent(`}`);
    }

    generateThrowExceptionFunction() {
        this.code
            .line(`function throwException(status: number, response: string, result?: string): Observable<any> {`)
            .indent()
                .line(`return !!result ? Observable.throw(result) : Observable.throw(new SwaggerException(status, response, null));`)
            .unindent(`}`);
    }
}
<commit_msg>Fixed compile error and code formatting for the generated code.<commit_after>'use strict';

const _ = require('lodash');

const swagenCore = require('swagen-core');
const ts = require('swagen-typescript-language');

module.exports = class Generator {
    constructor(definition, profile) {
        this.definition = definition;
        this.profile = profile;
        this.transform = new swagenCore.transformer.Transformer(profile);
        this.transform.transformDefinition(this.definition);
        this.options = {
            baseUrlToken: profile.options.baseUrlToken || 'API_BASE_URL',
            serviceClassSuffix: profile.options.serviceClassSuffix || 'Client'
        };
    }

    generate() {
        this.code = new swagenCore.CodeBuilder();
        this.generateInitialCode();
        this.code.blank();
        this.generateImplementations();
        this.code.blank();
        this.generateBuildServiceUrlFunction();
        this.code.blank();
        this.generateSwaggerExceptionClass();
        this.code.blank();
        this.generateThrowExceptionFunction();
        this.code.blank();
        this.generateModels();
        return this.code.toCode();
    }

    generateInitialCode() {
        this.code.line(...ts.buildHeader(this.profile, this.definition));
        this.code.blank();
        this.code.line(
            `import 'rxjs/add/observable/fromPromise';`,
            `import 'rxjs/add/observable/of';`,
            `import 'rxjs/add/observable/throw';`,
            `import 'rxjs/add/operator/map';`,
            `import 'rxjs/add/operator/mergeMap';`,
            `import 'rxjs/add/operator/toPromise';`,
            `import 'rxjs/add/operator/catch';`,
            ``,
            `import { Observable } from 'rxjs/Observable';`,
            `import { Injectable, Inject, Optional, OpaqueToken } from '@angular/core';`,
            `import { Http, Headers, ResponseContentType, Response } from '@angular/http';`,
            ``,
            `export const API_BASE_URL = new OpaqueToken('${this.options.baseUrlToken}');`,
        );
    }

    generateImplementations() {
        let sortedKeys = _.keys(this.definition.services).sort();
        for (let i = 0; i < sortedKeys.length; i++) {
            this.code.blank(i > 0);
            let serviceName = sortedKeys[i];
            let service = this.definition.services[serviceName]
            this.generateImplementation(serviceName, service);
        }
    }

    generateImplementation(serviceName, service) {
        this.code.line(
            `@Injectable()`,
            `export class ${serviceName}${this.options.serviceClassSuffix} {`,
        ).indent();

        this.code.line(
            `private http: Http;`,
            `private baseUrl: string;`,
            `public jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;`
        );

        this.code.blank();

        this.code.line(
            `constructor(@Inject(Http) http: Http, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {`
        )
            .indent()
            .line(
                `this.http = http;`,
                `this.baseUrl = baseUrl || '${this.definition.metadata.baseUrl}';`,
            )
            .unindent('}');

        for (let operationName in service) {
            this.code.blank();
            let operation = service[operationName];
            this.code.line(...ts.buildOperationDocComments(operation));
            this.generateOperation(operationName, operation);
        }

        this.code.unindent('}')
    }

    generateOperation(operationName, operation) {
        let options = {
            modelNs: '',
            returnTypeTransformer: rt => `Observable<${rt}>`
        };
        this.code.inline('public ').inline(ts.getMethodSignature(operationName, operation, options)).inline(' {').done().indent();

        //Check required parameters
        let requiredParams = (operation.parameters || []).filter(p => !!p.required);
        for (let i = 0; i < requiredParams.length; i++) {
            let requiredParam = requiredParams[i];
            this.code
                .line(`if (${requiredParam.name} == undefined || ${requiredParam.name} == null) {`)
                .indent(`throw new Error(\`The parameter '${requiredParam.name}' must be defined.\`);`)
                .unindent(`}`);
        }

        //Resolve path parameters in relative URL.
        let pathParams = (operation.parameters || []).filter(p => p.type === 'path');
        let hasPathParams = pathParams.length > 0;
        this.code
            .inline(`let resourceUrl: string = '${operation.path}'`)
            .inline(`;`, !hasPathParams)
            .done();
        this.code.indent();
        for (let i = 0; i < pathParams.length; i++) {
            this.code
                .inline(`.replace('{${pathParams[i].name}}', encodeURIComponent('' + ${pathParams[i].name}))`)
                .inline(`;`, i === pathParams.length - 1)
                .done();
        }
        this.code.unindent();

        //Query parameters
        let queryParams = (operation.parameters || []).filter(p => p.type === 'query');
        if (queryParams.length > 0) {
            this.code
                .line(`let queryParams: {[key: string]: string} = {`)
                .indent();
            for (let i = 0; i < queryParams.length; i++) {
                this.code
                    .inline(`${queryParams[i].name}: encodeURIComponent('' + ${queryParams[i].name})`)
                    .inline(`,`, i < queryParams.length - 1)
                    .done();
            }
            this.code.unindent('};')
        }

        this.code
            .inline(`let url = buildServiceUrl(this.baseUrl, resourceUrl`)
            .inline(`, queryParams`, queryParams.length > 0)
            .inline(`);`)
            .done();

        this.code.blank();

        //Options
        let bodyParam = (operation.parameters || []).find(p => p.type === 'body');
        let formDataParams = (operation.parameters || []).filter(p => p.type === 'formData');
        let headerParams = (operation.parameters || []).filter(p => p.type === 'header');
        let returnType = ts.getReturnType(operation, { modelNs: '' });

        //Create content variable
        if (formDataParams.length > 0) {
            this.code.line(`const content = new FormData();`);
            for (let i = 0; i < formDataParams.length; i++) {
                let paramName = formDataParams[i].name;
                this.code
                    .line(`if (${paramName} != null && ${paramName} != undefined) {`)
                    .indent()
                        .line(`content.append('${paramName}', ${paramName}.toString());`)
                    .unindent(`}`);
            }
        } else if (bodyParam) {
            this.code.line(`const content = JSON.stringify(${bodyParam.name});`);
        } else {
            this.code.line(`const content = '';`)
        }

        this.code.blank();

        //Create HTTP request options variable
        this.code.line(`let options = {`)
            .indent()
                .line(`body: content,`);
        this.code.line(`headers: new Headers({`)
            .indent();
        for (let i = 0; i < headerParams.length; i++) {
            this.code.line(`'${headerParams[i].name}': ${headerParams[i].name},`);
        }
        if (bodyParam) {
            this.code.line(`'Content-Type': 'application/json',`);
        }
        this.code.line(`'Accept': 'application/json'`);
        this.code.unindent('}),');
        this.code.line(`method: '${operation.verb.toUpperCase()}'`)
        this.code.unindent('};');

        this.code.blank();

        const processMethodName = `process${_.upperFirst(_.camelCase(operationName))}`;
        this.code
            .line(`return this.http.request(url, options).flatMap(response =>`)
            .indent()
                .line(`this.${processMethodName}(response)`)
            .unindent(`).catch(response => {`)
            .indent()
                .line(`if (response instanceof Response) {`)
                .indent()
                    .line(`try {`)
                    .indent()
                        .line(`return this.${processMethodName}(response);`)
                    .unindent(`} catch (e) {`)
                    .indent()
                        .line(`return <Observable<${returnType}>><any>Observable.throw(response);`)
                    .unindent(`}`)
                .unindent('}')
            .unindent(`});`);

        this.code.unindent('}');

        this.code.blank();

        //Process method
        this.code.line(
            `private ${processMethodName}(response: Response): Observable<${returnType}> {`
        ).indent();
        this.code
            .line(`const status = +response.status;`)
            .line(`const responseText = response.text();`)
            .line(`switch (status) {`)
            .indent();
        for (let statusCode in operation.responses) {
            let status = +statusCode;
            if (!status) {
                continue;
            }
            this.code.line(`case ${statusCode}:`)
                .indent();
            if (status >= 200 && status < 400) {
                this.code
                    .line(`let result: ${returnType} = !responseText ? null : <${returnType}>JSON.parse(responseText, this.jsonParseReviver);`)
                    .line(`return Observable.of(result);`)
            } else if (status >= 400) {
                this.code
                    .line(`return throwException(status, responseText);`);
            }
            this.code.unindent();
        }
        this.code.line(`default:`)
            .indent();
        this.code.line(`throw new Error(\`Unexpected status code \${status}\`);`);
        this.code.unindent();

        this.code.unindent('}');

        this.code.unindent(`}`);
    }

    generateModels() {
        let sortedKeys = _.keys(this.definition.models).sort((x, y) => x.toLowerCase().localeCompare(y.toLowerCase()));

        for (let i = 0; i < sortedKeys.length; i++) {
            let modelName = sortedKeys[i];
            let model = this.definition.models[modelName];
            this.code.blank(i > 0);
            this.code.line(
                `export interface ${modelName} {`
            ).indent();

            for (let propertyName in model) {
                let property = model[propertyName];
                this.code
                    .inline(propertyName)
                    .inline('?', !property.required)
                    .inline(': ')
                    .inline(ts.getDataType(property))
                    .inline(` | undefined`, !property.required)
                    .inline(';')
                    .done();
            }

            this.code.unindent('}');
        }

        let enumKeys = _.keys(this.definition.enums).sort((x, y) => x.toLowerCase().localeCompare(y.toLowerCase()));

        for (let i = 0; i < enumKeys.length; i++) {
            let enumName = enumKeys[i];
            let enumValues = this.definition.enums[enumName]
                .map(e => `"${e}"`)
                .join(' | ');
            this.code.blank(sortedKeys.length > 0 || i > 0);
            this.code.line(`export type ${enumName} = ${enumValues};`);
        }

        this.code.blank(sortedKeys.length > 0 || enumKeys.length > 0);
    }

    generateBuildServiceUrlFunction() {
        this.code
            .line(`function buildServiceUrl(baseUrl: string, resourceUrl: string, queryParams?: {[key: string]: string}): string {`)
            .indent()
                .line(`let url: string = baseUrl;`)
                .line(`let baseUrlSlash: boolean = url[url.length - 1] === '/';`)
                .line(`let resourceUrlSlash: boolean = resourceUrl[0] === '/';`)
                .line(`if (!baseUrlSlash && !resourceUrlSlash) {`)
                .indent()
                    .line(`url += '/';`)
                .unindent(`} else if (baseUrlSlash && resourceUrlSlash) {`)
                .indent()
                    .line(`url = url.substr(0, url.length - 1);`)
                .unindent(`}`)
                .line(`url += resourceUrl;`)
                .blank()
                .line(`if (queryParams) {`)
                .indent()
                    .line(`let isFirst: boolean = true;`)
                    .line(`for (let p in queryParams) {`)
                    .indent()
                        .line(`if (queryParams.hasOwnProperty(p) && queryParams[p]) {`)
                        .indent()
                            .line(`let separator: string = isFirst ? '?' : '&';`)
                            .line(`url += \`\${separator}\${p}=\${queryParams[p]}\`;`)
                            .line(`isFirst = false;`)
                        .unindent(`}`)
                    .unindent(`}`)
                .unindent(`}`)
                .line(`return url;`)
            .unindent(`}`);
    }

    generateSwaggerExceptionClass() {
        this.code
            .line(`export class SwaggerException extends Error {`)
            .indent()
                .line(`constructor(public status: number, public response: string, public result: string) {`)
                .indent()
                    .line(`super();`)
                .unindent(`}`)
            .unindent(`}`);
    }

    generateThrowExceptionFunction() {
        this.code
            .line(`function throwException(status: number, response: string, result?: string): Observable<any> {`)
            .indent()
                .line(`return !!result ? Observable.throw(result) : Observable.throw(new SwaggerException(status, response, null));`)
            .unindent(`}`);
    }
}
<|endoftext|>


<commit_before>/** @babel */
/** @jsx etch.dom */

import path from 'path'
import _ from 'underscore-plus'
import {Disposable, CompositeDisposable} from 'atom'
import etch from 'etch'
import KeybindingsPanel from './keybindings-panel'

// Displays the keybindings for a package namespace
export default class PackageKeymapView {
  constructor (pack) {
    this.pack = pack
    this.otherPlatformPattern = new RegExp(`\\.platform-(?!${_.escapeRegExp(process.platform)}\\b)`)
    this.namespace = this.pack.name
    this.disposables = new CompositeDisposable()
    etch.initialize(this)

    const packagesWithKeymapsDisabled = atom.config.get('core.packagesWithKeymapsDisabled') || []
    this.refs.keybindingToggle.checked = !packagesWithKeymapsDisabled.includes(this.namespace)

    const changeHandler = (event) => {
      event.stopPropagation()
      const value = this.refs.keybindingToggle.checked
      if (value) {
        atom.config.removeAtKeyPath('core.packagesWithKeymapsDisabled', this.namespace)
      } else {
        atom.config.pushAtKeyPath('core.packagesWithKeymapsDisabled', this.namespace)
      }

      this.updateKeyBindingView()
    }
    this.refs.keybindingToggle.addEventListener('change', changeHandler)
    this.disposables.add(new Disposable(() => { this.refs.keybindingToggle.removeEventListener('change', changeHandler) }))

    const copyIconClickHandler = (event) => {
      const target = event.target.closest('.copy-icon')
      if (target) {
        event.preventDefault()
        event.stopPropagation()
        this.writeKeyBindingToClipboard(target.closest('tr').dataset)
      }
    }
    this.element.addEventListener('click', copyIconClickHandler)
    this.disposables.add(new Disposable(() => { this.element.removeEventListener('click', copyIconClickHandler) }))

    this.updateKeyBindingView()

    let hasKeymaps = false
    for (let [packageKeymapsPath, map] of atom.packages.getLoadedPackage(this.namespace).keymaps) {
      if (map.length > 0) {
        hasKeymaps = true
        break
      }
    }

    if (this.refs.keybindingItems.children.length === 0 && !hasKeymaps) {
      this.element.style.display = 'none'
    }
  }

  update () {}

  destroy () {
    this.disposables.dispose()
    return etch.destroy(this)
  }

  render () {
    return (

Keybindings

            <input id='toggleKeybindings' className='input-checkbox' type='checkbox' ref='keybindingToggle'></input>

Enable

            {"Disable this if you want to bind your own keystrokes for this package's commands in your keymap."}

              Keystroke
              Command
              Selector
              Source

    )
  }

  updateKeyBindingView () {
    this.refs.keybindingItems.innerHTML = ''

    const packagesWithKeymapsDisabled = atom.config.get('core.packagesWithKeymapsDisabled') || []
    const keybindingsDisabled = packagesWithKeymapsDisabled.includes(this.namespace)
    if (keybindingsDisabled) {
      this.refs.keybindingItems.classList.add('text-subtle')
    } else {
      this.refs.keybindingItems.classList.remove('text-subtle')
    }

    const keyBindings = []
    if (atom.keymaps.build) {
      for (const [keymapPath, keymap] of atom.packages.getLoadedPackage(this.namespace).keymaps) {
        keyBindings.push(...atom.keymaps.build(this.namespace, keymap, 0, false))
      }
    } else {
      // Backwards compatibility for Atom <= 1.19
      for (const keyBinding of atom.keymaps.getKeyBindings()) {
        const {command} = keyBinding
        if (command && command.indexOf && command.indexOf(`${this.namespace}` === 0)) {
          keyBindings.push(keyBinding)
        }
      }
    }

    for (const keyBinding of keyBindings) {
      const {command, keystrokes, selector, source} = keyBinding
      if (!command) {
        continue
      }

      if (this.otherPlatformPattern.test(selector)) {
        continue
      }

      const keyBindingRow = document.createElement('tr')
      keyBindingRow.dataset.selector = selector
      keyBindingRow.dataset.keystrokes = keystrokes
      keyBindingRow.dataset.command = command

      const keystrokesTd = document.createElement('td')

      const copyIconSpan = document.createElement('span')
      copyIconSpan.classList.add('icon', 'icon-clippy', 'copy-icon')
      keystrokesTd.appendChild(copyIconSpan)

      const keystrokesSpan = document.createElement('span')
      keystrokesSpan.textContent = keystrokes
      keystrokesTd.appendChild(keystrokesSpan)

      keyBindingRow.appendChild(keystrokesTd)

      const commandTd = document.createElement('td')
      commandTd.textContent = command
      keyBindingRow.appendChild(commandTd)

      const selectorTd = document.createElement('td')
      selectorTd.textContent = selector
      keyBindingRow.appendChild(selectorTd)

      const sourceTd = document.createElement('td')
      sourceTd.textContent = KeybindingsPanel.determineSource(source)
      keyBindingRow.appendChild(sourceTd)

      this.refs.keybindingItems.appendChild(keyBindingRow)
    }
  }

  writeKeyBindingToClipboard ({selector, keystrokes, command}) {
    let content
    const keymapExtension = path.extname(atom.keymaps.getUserKeymapPath())
    if (keymapExtension === '.cson') {
      content = `\
'${selector}':
  '${keystrokes}': '${command}'\
`
    } else {
      content = `\
"${selector}": {
  "${keystrokes}": "${command}"
}\
`
    }

    atom.clipboard.write(content)
  }
}
<commit_msg>Forgot a :<commit_after>/** @babel */
/** @jsx etch.dom */

import path from 'path'
import _ from 'underscore-plus'
import {Disposable, CompositeDisposable} from 'atom'
import etch from 'etch'
import KeybindingsPanel from './keybindings-panel'

// Displays the keybindings for a package namespace
export default class PackageKeymapView {
  constructor (pack) {
    this.pack = pack
    this.otherPlatformPattern = new RegExp(`\\.platform-(?!${_.escapeRegExp(process.platform)}\\b)`)
    this.namespace = this.pack.name
    this.disposables = new CompositeDisposable()
    etch.initialize(this)

    const packagesWithKeymapsDisabled = atom.config.get('core.packagesWithKeymapsDisabled') || []
    this.refs.keybindingToggle.checked = !packagesWithKeymapsDisabled.includes(this.namespace)

    const changeHandler = (event) => {
      event.stopPropagation()
      const value = this.refs.keybindingToggle.checked
      if (value) {
        atom.config.removeAtKeyPath('core.packagesWithKeymapsDisabled', this.namespace)
      } else {
        atom.config.pushAtKeyPath('core.packagesWithKeymapsDisabled', this.namespace)
      }

      this.updateKeyBindingView()
    }
    this.refs.keybindingToggle.addEventListener('change', changeHandler)
    this.disposables.add(new Disposable(() => { this.refs.keybindingToggle.removeEventListener('change', changeHandler) }))

    const copyIconClickHandler = (event) => {
      const target = event.target.closest('.copy-icon')
      if (target) {
        event.preventDefault()
        event.stopPropagation()
        this.writeKeyBindingToClipboard(target.closest('tr').dataset)
      }
    }
    this.element.addEventListener('click', copyIconClickHandler)
    this.disposables.add(new Disposable(() => { this.element.removeEventListener('click', copyIconClickHandler) }))

    this.updateKeyBindingView()

    let hasKeymaps = false
    for (let [packageKeymapsPath, map] of atom.packages.getLoadedPackage(this.namespace).keymaps) {
      if (map.length > 0) {
        hasKeymaps = true
        break
      }
    }

    if (this.refs.keybindingItems.children.length === 0 && !hasKeymaps) {
      this.element.style.display = 'none'
    }
  }

  update () {}

  destroy () {
    this.disposables.dispose()
    return etch.destroy(this)
  }

  render () {
    return (

Keybindings

            <input id='toggleKeybindings' className='input-checkbox' type='checkbox' ref='keybindingToggle'></input>

Enable

            {"Disable this if you want to bind your own keystrokes for this package's commands in your keymap."}

              Keystroke
              Command
              Selector
              Source

    )
  }

  updateKeyBindingView () {
    this.refs.keybindingItems.innerHTML = ''

    const packagesWithKeymapsDisabled = atom.config.get('core.packagesWithKeymapsDisabled') || []
    const keybindingsDisabled = packagesWithKeymapsDisabled.includes(this.namespace)
    if (keybindingsDisabled) {
      this.refs.keybindingItems.classList.add('text-subtle')
    } else {
      this.refs.keybindingItems.classList.remove('text-subtle')
    }

    const keyBindings = []
    if (atom.keymaps.build) {
      for (const [keymapPath, keymap] of atom.packages.getLoadedPackage(this.namespace).keymaps) {
        keyBindings.push(...atom.keymaps.build(this.namespace, keymap, 0, false))
      }
    } else {
      // Backwards compatibility for Atom <= 1.19
      for (const keyBinding of atom.keymaps.getKeyBindings()) {
        const {command} = keyBinding
        if (command && command.indexOf && command.indexOf(`${this.namespace}:` === 0)) {
          keyBindings.push(keyBinding)
        }
      }
    }

    for (const keyBinding of keyBindings) {
      const {command, keystrokes, selector, source} = keyBinding
      if (!command) {
        continue
      }

      if (this.otherPlatformPattern.test(selector)) {
        continue
      }

      const keyBindingRow = document.createElement('tr')
      keyBindingRow.dataset.selector = selector
      keyBindingRow.dataset.keystrokes = keystrokes
      keyBindingRow.dataset.command = command

      const keystrokesTd = document.createElement('td')

      const copyIconSpan = document.createElement('span')
      copyIconSpan.classList.add('icon', 'icon-clippy', 'copy-icon')
      keystrokesTd.appendChild(copyIconSpan)

      const keystrokesSpan = document.createElement('span')
      keystrokesSpan.textContent = keystrokes
      keystrokesTd.appendChild(keystrokesSpan)

      keyBindingRow.appendChild(keystrokesTd)

      const commandTd = document.createElement('td')
      commandTd.textContent = command
      keyBindingRow.appendChild(commandTd)

      const selectorTd = document.createElement('td')
      selectorTd.textContent = selector
      keyBindingRow.appendChild(selectorTd)

      const sourceTd = document.createElement('td')
      sourceTd.textContent = KeybindingsPanel.determineSource(source)
      keyBindingRow.appendChild(sourceTd)

      this.refs.keybindingItems.appendChild(keyBindingRow)
    }
  }

  writeKeyBindingToClipboard ({selector, keystrokes, command}) {
    let content
    const keymapExtension = path.extname(atom.keymaps.getUserKeymapPath())
    if (keymapExtension === '.cson') {
      content = `\
'${selector}':
  '${keystrokes}': '${command}'\
`
    } else {
      content = `\
"${selector}": {
  "${keystrokes}": "${command}"
}\
`
    }

    atom.clipboard.write(content)
  }
}
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================
/** @class

  Used to display an iframe. The source, (specified by the value property) of
  the iFrame should be from the same domain. (i.e. the src / value should be
  from the same domain) if you want to access the contents of the iframe.

  @extends SC.View
  @extends SC.Control
  @since SproutCore 1.0
*/
SC.WebView = SC.View.extend(SC.Control, {

  classNames: 'sc-web-view',
  displayProperties: ['value', 'shouldAutoResize'],

  /**
  The content of the iframe can be bigger than the size specifed when creating
  the view. If you want the view to be auto-resized to the dimensions of the
  iframe, then set the value of this property to YES.
  The web view can be auto resized only if the contents are from the same
  domain as the parent domain.
  @property{Boolean}
  */
  shouldAutoResize: NO,

  render: function(context, firstTime) {
    var src = this.get('value');
    if (firstTime) {
      context.push('');
    } else {
      var iframe = this.$('iframe');
      // clear out the previous src, to force a reload
      iframe.attr('src', 'javascript:;');
      iframe.attr('src', src);
    }
  },

  /**
  Called when the layer gets created.
  */
  didCreateLayer: function() {
    var f = this.$('iframe');
    // Attach an onload event to the iframe.
    SC.Event.add(f, 'load', this, this.iframeDidLoad);
  },

  /**
  Called when iframe onload event is fired.
  1. Resizes the view to fit the contents of the iframe using the
  scroll width and scroll height of the contents of the iframe

  The iframe contents can be accessed only when the src is from the same
  domain as the parent document
  @returns {void}
  */
  iframeDidLoad: function() {
    //fit the iframe to size of the contents.
    if (this.get('shouldAutoResize') === YES){
      var contentWindow;
      var iframeElt = this.$('iframe')[0];
      if(iframeElt && iframeElt.contentWindow){
        contentWindow = iframeElt.contentWindow;
        if(contentWindow && contentWindow.document && contentWindow.document.documentElement){
          var docElement = contentWindow.document.documentElement;
          // setting the width before the height gives more accurate results..
          // atleast for the test iframe content i'm using.
          //TODO: try out document flows other than top to bottom.
          if (!SC.browser.isIE){
            this.$().width(docElement.scrollWidth);
            this.$().height(docElement.scrollHeight);
          } else {
            this.$().width(docElement.scrollWidth + 12);
            this.$().height(docElement.scrollHeight + 5);
          }
        }
      }
    }
  }
});
<commit_msg>Add scope declaration to WebView.<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================
/** @class

  Used to display an iframe. The source, (specified by the value property) of
  the iFrame should be from the same domain. (i.e. the src / value should be
  from the same domain) if you want to access the contents of the iframe.

  @extends SC.View
  @extends SC.Control
  @since SproutCore 1.0
*/
SC.WebView = SC.View.extend(SC.Control, {
/** @scope SC.WebView.prototype */
  classNames: 'sc-web-view',
  displayProperties: ['value', 'shouldAutoResize'],

  /**
  The content of the iframe can be bigger than the size specifed when creating
  the view. If you want the view to be auto-resized to the dimensions of the
  iframe, then set the value of this property to YES.
  The web view can be auto resized only if the contents are from the same
  domain as the parent domain.
  @property{Boolean}
  */
  shouldAutoResize: NO,

  render: function(context, firstTime) {
    var src = this.get('value');
    if (firstTime) {
      context.push('');
    } else {
      var iframe = this.$('iframe');
      // clear out the previous src, to force a reload
      iframe.attr('src', 'javascript:;');
      iframe.attr('src', src);
    }
  },

  /**
  Called when the layer gets created.
  */
  didCreateLayer: function() {
    var f = this.$('iframe');
    // Attach an onload event to the iframe.
    SC.Event.add(f, 'load', this, this.iframeDidLoad);
  },

  /**
  Called when iframe onload event is fired.
  1. Resizes the view to fit the contents of the iframe using the
  scroll width and scroll height of the contents of the iframe

  The iframe contents can be accessed only when the src is from the same
  domain as the parent document
  @returns {void}
  */
  iframeDidLoad: function() {
    //fit the iframe to size of the contents.
    if (this.get('shouldAutoResize') === YES){
      var contentWindow;
      var iframeElt = this.$('iframe')[0];
      if(iframeElt && iframeElt.contentWindow){
        contentWindow = iframeElt.contentWindow;
        if(contentWindow && contentWindow.document && contentWindow.document.documentElement){
          var docElement = contentWindow.document.documentElement;
          // setting the width before the height gives more accurate results..
          // atleast for the test iframe content i'm using.
          //TODO: try out document flows other than top to bottom.
          if (!SC.browser.isIE){
            this.$().width(docElement.scrollWidth);
            this.$().height(docElement.scrollHeight);
          } else {
            this.$().width(docElement.scrollWidth + 12);
            this.$().height(docElement.scrollHeight + 5);
          }
        }
      }
    }
  }
});
<|endoftext|>