<commit_before>import router from 'router'
import http from 'http'
import mapSeries from '../helpers/map-series'
import asynchronous from './middleware/asynchronous'
import finalhandler from './finalhandler'
import middleware from './middleware'
import route from './route'
import logger from '../helpers/logger'
import Emitter from './emitter'

export default (options = {}) => {
  // Prepare internals
  const internals = {
    // Prepare router
    router: router(),

    // Prepare server
    server: (options.server || http).createServer((req, res) => {
      internals.router(req, res, finalhandler(req, res))
    }),

    // Prepare event emitter
    emitter: new Emitter(),

    // Prepare store
    store: {},

    // Prepare routes array
    routes: []
  }

  // Apply middleware
  middleware(internals.router)

  // Pleasant...
  class Pleasant {
    constructor() {
      this.listener = internals.server
    }

    // Register plugins
    async register(...args) {
      // Get arguments
      const prefix = typeof args[0] === 'string' ? args.shift() : null
      const plugins = !Array.isArray(args[0]) ? [args[0]] : args[0]
      const config = args[1] || {}

      // Save reference to application router
      const app = internals.router

      // Prefix enabled?
      if (prefix) {
        // Attach temporary router
        internals.router = router()

        // State that router is prefixed
        internals.router.prefix = prefix
      }

      // Register plugins
      await mapSeries(plugins, async plugin => {
        const fn = plugin.default || plugin

        if (typeof fn !== 'function') {
          return
        }

        await fn(this, config).catch(err => {
          logger.error(err)
          process.exit()
        })
      })

      // Prefix enabled?
      if (prefix) {
        // Add temporary router as prefixed middleware to application router
        app.use(prefix, internals.router)

        // Change back to application router
        internals.router = app
      }
    }

    // Add event handler
    on(...args) {
      return internals.emitter.on(...args)
    }

    // Remove event handler
    off(...args) {
      return internals.emitter.off(...args)
    }

    // Emit event
    emit(...args) {
      return internals.emitter.emit(...args)
    }

    // Start listeningen
    async listen(...args) {
      await this.emit('ready')
      return internals.server.listen(...args)
    }

    // Close server
    close(...args) {
      return internals.server.close(...args)
    }

    // Add route
    route(config) {
      route(internals.router, config)

      if (internals.router.prefix) {
        config.url = `${internals.router.prefix}${config.url}`
      }

      internals.routes.push(config)
    }

    // Return list of routes
    routes() {
      return internals.routes
    }

    // Apply middleware
    use(...args) {
      // Wrap in asynchronous handler
      args = args.map(
        arg => (typeof arg === 'function' ? asynchronous(arg) : arg)
      )

      // Apply middleware
      internals.router.use(...args)

      // Make use statements chainable
      return this
    }

    // Set store value
    set(key, value) {
      internals.store[key] = value
    }

    // Get store value
    get(key) {
      return key ? internals.store[key] : internals.store
    }
  }

  // Return instance
  return new Pleasant()
}
<commit_msg>Make sure nested prefixed routes are logged correctly<commit_after>import router from 'router'
import http from 'http'
import mapSeries from '../helpers/map-series'
import asynchronous from './middleware/asynchronous'
import finalhandler from './finalhandler'
import middleware from './middleware'
import route from './route'
import logger from '../helpers/logger'
import Emitter from './emitter'

export default (options = {}) => {
  // Prepare internals
  const internals = {
    // Prepare router
    router: router(),

    // Prepare server
    server: (options.server || http).createServer((req, res) => {
      internals.router(req, res, finalhandler(req, res))
    }),

    // Prepare event emitter
    emitter: new Emitter(),

    // Prepare store
    store: {},

    // Prepare routes array
    routes: []
  }

  // Apply middleware
  middleware(internals.router)

  // Pleasant...
  class Pleasant {
    constructor() {
      this.listener = internals.server
    }

    // Register plugins
    async register(...args) {
      // Get arguments
      const prefix = typeof args[0] === 'string' ? args.shift() : null
      const plugins = !Array.isArray(args[0]) ? [args[0]] : args[0]
      const config = args[1] || {}

      // Save reference to application router
      const app = internals.router

      // Prefix enabled?
      if (prefix) {
        // Attach temporary router
        internals.router = router()

        // State that router is prefixed
        internals.router.prefix = `${app.prefix || ''}${prefix}`
      }

      // Register plugins
      await mapSeries(plugins, async plugin => {
        const fn = plugin.default || plugin

        if (typeof fn !== 'function') {
          return
        }

        await fn(this, config).catch(err => {
          logger.error(err)
          process.exit()
        })
      })

      // Prefix enabled?
      if (prefix) {
        // Add temporary router as prefixed middleware to application router
        app.use(prefix, internals.router)

        // Change back to application router
        internals.router = app
      }
    }

    // Add event handler
    on(...args) {
      return internals.emitter.on(...args)
    }

    // Remove event handler
    off(...args) {
      return internals.emitter.off(...args)
    }

    // Emit event
    emit(...args) {
      return internals.emitter.emit(...args)
    }

    // Start listeningen
    async listen(...args) {
      await this.emit('ready')
      return internals.server.listen(...args)
    }

    // Close server
    close(...args) {
      return internals.server.close(...args)
    }

    // Add route
    route(config) {
      route(internals.router, config)

      if (internals.router.prefix) {
        config.url = `${internals.router.prefix}${config.url}`
      }

      internals.routes.push(config)
    }

    // Return list of routes
    routes() {
      return internals.routes
    }

    // Apply middleware
    use(...args) {
      // Wrap in asynchronous handler
      args = args.map(
        arg => (typeof arg === 'function' ? asynchronous(arg) : arg)
      )

      // Apply middleware
      internals.router.use(...args)

      // Make use statements chainable
      return this
    }

    // Set store value
    set(key, value) {
      internals.store[key] = value
    }

    // Get store value
    get(key) {
      return key ? internals.store[key] : internals.store
    }
  }

  // Return instance
  return new Pleasant()
}
<|endoftext|>


<commit_before>'use strict';

var DIR         = __dirname + '/../../',
    DIR_TMPL    = DIR       + 'tmpl/',
    DIR_HTML    = DIR       + 'html/',
    DIR_COMMON  = DIR       + 'common/',
    DIR_JSON    = DIR       + 'json/',
    DIR_SERVER  = __dirname + '/',
    DIR_FS      = DIR_TMPL  + 'fs/',

    fs          = require('fs'),

    flop        = require('flop'),
    ponse       = require('ponse'),
    files       = require('files-io'),
    rendy       = require('rendy'),
    exec        = require('execon'),

    minify      = require('minify'),
    format      = require('format-io'),
    squad       = require('squad'),
    apart       = require('apart'),

    config      = require(DIR_SERVER + 'config'),
    root        = require(DIR_SERVER + 'root'),
    prefixer    = require(DIR_SERVER + 'prefixer'),
    prefix      = squad(prefixer, apart(config, 'prefix')),

    CloudFunc   = require(DIR_COMMON + 'cloudfunc'),

    PATH_INDEX  = DIR_HTML   + 'index.html',

    TMPL_PATH   = [
        'file',
        'panel',
        'path',
        'pathLink',
        'link'
    ],

    Template    = {},

    FS          = CloudFunc.FS,

    CSS_URL     = require(DIR_JSON + 'css.json')
        .map(function(name) {
            return 'css/' + name + '.css';
        }).join(':');

module.exports = function(req, res, next) {
    check(req, res, next);

    readFiles(function() {
        route(req, res, next);
    });
};

/**
 * additional processing of index file
 */
function indexProcessing(options) {
    var from, to,
        left        = '',
        right       = '',
        keysPanel   = '<div id="js-keyspanel" class="{{ className }}',
        isOnePanel  = config('onePanelMode'),
        noConfig    = !config('configDialog'),
        noConsole   = !config('console'),
        data        = options.data,
        panel       = options.panel;

    if (!config('showKeysPanel')) {
        from        = rendy(keysPanel, {
            className: 'keyspanel'
        });

        to          = rendy(keysPanel, {
            className: 'keyspanel hidden'
        });

        data        = data.replace(from, to);
    }

    if (isOnePanel)
        data = data
            .replace('icon-move', 'icon-move none')
            .replace('icon-copy', 'icon-copy none');

   if (noConfig)
        data = data
            .replace('icon-config', 'icon-config none');

   if (noConsole)
        data = data
            .replace('icon-console', 'icon-console none');

    left = rendy(Template.panel, {
        side        : 'left',
        content     : panel,
        className   : !isOnePanel ? '' : 'panel-single'
    });

    if (!isOnePanel)
        right = rendy(Template.panel, {
            side        : 'right',
            content     : panel,
            className   : ''
        });

    data = rendy(data, {
        title: CloudFunc.getTitle(),
        fm: left + right,
        prefix: prefix(),
        css: CSS_URL
    });

    return data;
}

function readFiles(callback) {
    var filesList,
        paths       = {},

        lengthTmpl  = Object.keys(Template).length,
        lenthPath   = TMPL_PATH.length,
        isRead      = lengthTmpl === lenthPath;

    if (typeof callback !== 'function')
        throw Error('callback should be function!');

    if (isRead) {
        callback();
    } else {
        filesList   = TMPL_PATH.map(function(name) {
            var path = DIR_FS + name + '.hbs';

            paths[path] = name;

            return path;
        });

        files.read(filesList, 'utf8', function(error, files) {
            if (error)
                throw error;
            else
                Object.keys(files).forEach(function(path) {
                    var name = paths[path];

                    Template[name] = files[path];
                });

            callback();
        });
    }
}

/**
 * routing of server queries
 */
function route(request, response, callback) {
    var name, p, isAuth, isFS, fullPath;

    name    = ponse.getPathName(request);

    isAuth  = RegExp('^(/auth|/auth/github)$').test(name);
    isFS    = RegExp('^/$|^' + FS).test(name);

    p       = {
        request     : request,
        response    : response,
        gzip        : true,
        name        : name
    };

    if (!isAuth && !isFS)
        callback();
    else if (isAuth) {
        p.name = DIR_HTML + name + '.html';
        ponse.sendFile(p);
    } else if (isFS) {
        name        = name.replace(CloudFunc.FS, '') || '/';
        fullPath    = root(name);

        flop.read(fullPath, function(error, dir) {
            if (dir)
                dir.path = format.addSlashToEnd(name);

            if (!error)
                buildIndex(dir, function(error, data) {
                    p.name = PATH_INDEX;

                    if (error)
                        ponse.sendError(error, p);
                    else
                        ponse.send(data, p);
                });
            else if (error.code !== 'ENOTDIR')
                ponse.sendError(error, p);
            else
                fs.realpath(fullPath, function(error, pathReal) {
                    if (!error)
                        p.name = pathReal;
                    else
                        p.name = name;

                    p.gzip = false;
                    ponse.sendFile(p);
                });
        });
    }
}

function buildIndex(json, callback) {
    var isMinify = config('minify');

    exec.if(!isMinify, function(error, name) {
        fs.readFile(name || PATH_INDEX, 'utf8', function(error, template) {
            var panel, data;

            if (!error) {
                panel   = CloudFunc.buildFromJSON({
                    data        : json,
                    prefix      : prefix(),
                    template    : Template
                }),

                data    = indexProcessing({
                    panel   : panel,
                    data    : template
                });
            }

            callback(error, data);
        });
    },  function(callback) {
            minify(PATH_INDEX, 'name', callback);
    });
}

function check(req, res, next) {
    if (!req)
        throw Error('req could not be empty!');

    if (!res)
        throw Error('res could not be empty!');

    if (typeof next !== 'function')
        throw Error('next should be function!');
}
<commit_msg>chore(routes) es2015-ify<commit_after>'use strict';

const DIR = __dirname + '/../../';
const DIR_TMPL = DIR + 'tmpl/';
const DIR_HTML = DIR + 'html/';
const DIR_COMMON = DIR + 'common/';
const DIR_JSON = DIR + 'json/';
const DIR_SERVER = __dirname + '/';
const DIR_FS = DIR_TMPL + 'fs/';

const fs = require('fs');

const flop = require('flop');
const ponse = require('ponse');
const files = require('files-io');
const rendy = require('rendy');
const exec = require('execon');
const minify = require('minify');
const format = require('format-io');
const squad = require('squad');
const apart = require('apart');

const config = require(DIR_SERVER + 'config');
const root = require(DIR_SERVER + 'root');
const prefixer = require(DIR_SERVER + 'prefixer');
const prefix = squad(prefixer, apart(config, 'prefix'));
const CloudFunc = require(DIR_COMMON + 'cloudfunc');

const PATH_INDEX = DIR_HTML + 'index.html';

const TMPL_PATH   = [
    'file',
    'panel',
    'path',
    'pathLink',
    'link'
];

const Template = {};
const FS = CloudFunc.FS;

const CSS_URL = require(DIR_JSON + 'css.json')
    .map((name) => {
        return 'css/' + name + '.css'
    }).join(':');

module.exports = (req, res, next) => {
    check(req, res, next);

    readFiles(() => {
        route(req, res, next);
    });
};

/**
 * additional processing of index file
 */
function indexProcessing(options) {
    let from;
    let to;
    let left = '';
    let right = '';
    const keysPanel = '<div id="js-keyspanel" class="{{ className }}';
    const isOnePanel = config('onePanelMode');
    const noConfig = !config('configDialog');
    const noConsole = !config('console');
    const panel = options.panel;

    let data = options.data;

    if (!config('showKeysPanel')) {
        from = rendy(keysPanel, {
            className: 'keyspanel'
        });

        to = rendy(keysPanel, {
            className: 'keyspanel hidden'
        });

        data = data.replace(from, to);
    }

    if (isOnePanel)
        data = data
            .replace('icon-move', 'icon-move none')
            .replace('icon-copy', 'icon-copy none');

    if (noConfig)
         data = data
             .replace('icon-config', 'icon-config none');

    if (noConsole)
         data = data
             .replace('icon-console', 'icon-console none');

    left = rendy(Template.panel, {
        side        : 'left',
        content     : panel,
        className   : !isOnePanel ? '' : 'panel-single'
    });

    if (!isOnePanel)
        right = rendy(Template.panel, {
            side        : 'right',
            content     : panel,
            className   : ''
        });

    data = rendy(data, {
        title: CloudFunc.getTitle(),
        fm: left + right,
        prefix: prefix(),
        css: CSS_URL
    });

    return data;
}

function readFiles(callback) {
    const paths = {};
    const lengthTmpl = Object.keys(Template).length;
    const lenthPath = TMPL_PATH.length;
    const isRead = lengthTmpl === lenthPath;

    if (typeof callback !== 'function')
        throw Error('callback should be a function!');

    if (isRead)
        return callback();

    const filesList = TMPL_PATH.map((name) => {
        const path = DIR_FS + name + '.hbs';

        paths[path] = name;

        return path;
    });

    files.read(filesList, 'utf8', (error, files) => {
        if (error)
            throw error;

        Object.keys(files).forEach((path) => {
            const name = paths[path];

            Template[name] = files[path];
        });

        callback();
    });
}

/**
 * routing of server queries
 */
function route(request, response, callback) {
    let name = ponse.getPathName(request);

    const isAuth = RegExp('^(/auth|/auth/github)$').test(name);
    const isFS = RegExp('^/$|^' + FS).test(name);
    const p = {
        request     : request,
        response    : response,
        gzip        : true,
        name        : name
    };

    if (!isAuth && !isFS)
        return callback();

    if (isAuth) {
        p.name = DIR_HTML + name + '.html';
        ponse.sendFile(p);
        return;
    }

    if (!isFS)
        return;

    name = name.replace(CloudFunc.FS, '') || '/';
    const fullPath = root(name);

    flop.read(fullPath, (error, dir) => {
        if (dir)
            dir.path = format.addSlashToEnd(name);

        if (!error)
            return buildIndex(dir, (error, data) => {
                p.name = PATH_INDEX;

                if (error)
                    return ponse.sendError(error, p);

                ponse.send(data, p);
            });

        if (error.code !== 'ENOTDIR')
            return ponse.sendError(error, p);

        fs.realpath(fullPath, (error, pathReal) => {
            if (!error)
                p.name = pathReal;
            else
                p.name = name;

            p.gzip = false;
            ponse.sendFile(p);
        });
    });
}

function buildIndex(json, callback) {
    const isMinify = config('minify');

    exec.if(!isMinify, (error, name) => {
        fs.readFile(name || PATH_INDEX, 'utf8', (error, template) => {
            if (error)
                return;

            const panel = CloudFunc.buildFromJSON({
                data        : json,
                prefix      : prefix(),
                template    : Template
            });

            const data = indexProcessing({
                panel   : panel,
                data    : template
            });

            callback(error, data);
        });
    },  (callback) => {
            minify(PATH_INDEX, 'name', callback);
    });
}

function check(req, res, next) {
    if (!req)
        throw Error('req could not be empty!');

    if (!res)
        throw Error('res could not be empty!');

    if (typeof next !== 'function')
        throw Error('next should be function!');
}

<|endoftext|>


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

    var DIR         = __dirname + '/../../',
        DIR_HTML    = DIR       + 'html/',
        DIR_LIB     = DIR       + 'lib/',
        DIR_SERVER  = __dirname + '/',
        DIR_FS      = DIR_HTML  + 'fs/',

        fs          = require('fs'),

        mellow      = require('mellow'),
        ponse       = require('ponse'),
        files       = require('files-io'),
        rendy       = require('rendy'),
        exec        = require('execon'),

        minify      = require('minify'),
        format      = require('format-io'),

        config      = require(DIR_SERVER + 'config'),

        CloudFunc   = require(DIR_LIB + 'cloudfunc'),

        PATH_INDEX  = DIR_FS   + 'index.html',

        TMPL_PATH   = [
            'file',
            'panel',
            'path',
            'pathLink',
            'link'
        ],

        Template    = {},

        FS          = CloudFunc.FS;

    module.exports  = function(req, res, next) {
        readFiles(function() {
            route(req, res, next);
        });
    };

    /**
     * additional processing of index file
     */
    function indexProcessing(options) {
        var left, right, from, to,
            keysPanel   = '<div id="js-keyspanel" class="{{ className }}',
            LEFT        = CloudFunc.PANEL_LEFT,
            RIGHT       = CloudFunc.PANEL_RIGHT,
            data        = options.data,
            panel       = options.panel;

        if (!config('showKeysPanel')) {
            from        = rendy(keysPanel, {
                className: 'keyspanel'
            });

            to          = rendy(keysPanel, {
                className: 'keyspanel hidden'
            });

            data        = data.replace(from, to);
        }

        left = rendy(Template.panel, {
            id      : LEFT,
            side    : 'left',
            content : panel
        });

        right = rendy(Template.panel, {
            id      : RIGHT,
            side    : 'right',
            content : panel
        });

        data = rendy(data, {
            title   : CloudFunc.getTitle(),
            fm      : left + right
        });

        return data;
    }

    function readFiles(callback) {
        var filesList,
            paths       = {},

            lengthTmpl  = Object.keys(Template).length,
            lenthPath   = TMPL_PATH.length,
            isRead      = lengthTmpl === lenthPath;

        if (typeof callback !== 'function')
            throw Error('callback should be function!');

        if (isRead) {
            callback();
        } else {
            filesList   = TMPL_PATH.map(function(name) {
                var path = DIR_FS + name + '.html';

                paths[path] = name;

                return path;
            });

            files.read(filesList, 'utf8', function(error, files) {
                if (error)
                    throw error;
                else
                    Object.keys(files).forEach(function(path) {
                        var name = paths[path];

                        Template[name] = files[path];
                    });

                callback();
            });
        }
    }

    /**
     * routing of server queries
     */
    function route(request, response, callback) {
        var name, p, isAuth, isFS, path;

        if (!request)
            throw Error('request could not be empty!');

        if (!response)
            throw Error('response could not be empty!');

        if (typeof callback !== 'function')
            throw Error('callback should be function!');

        name    = ponse.getPathName(request);
        isAuth  = RegExp('^(/auth|/auth/github)$').test(name);
        isFS    = RegExp('^/$|^' + FS).test(name);

        p       = {
            request     : request,
            response    : response,
            gzip        : true,
            name        : name
        };

        if (!isAuth && !isFS)
            callback();
        else if (isAuth) {
            console.log('* Routing' + '-> ' + name);

            p.name = DIR_HTML + name + '.html';
            ponse.sendFile(p);
        } else if (isFS) {
            name    = name.replace(CloudFunc.FS, '') || '/';
            path    = mellow.pathToWin(name);

            mellow.read(path, function(error, dir) {
                if (dir)
                    dir.path = format.addSlashToEnd(name);

                if (!error)
                    buildIndex(dir, function(error, data) {
                        p.name = PATH_INDEX;

                        if (error)
                            ponse.sendError(error, p);
                        else
                            ponse.send(data, p);
                    });
                else if (error.code !== 'ENOTDIR')
                    ponse.sendError(error, p);
                else
                    fs.realpath(path, function(error, pathReal) {
                        if (!error)
                            p.name = pathReal;
                        else
                            p.name = path;

                        p.gzip = false;
                        ponse.sendFile(p);
                    });
            });
        }
    }

    function buildIndex(json, callback) {
        var isMinify = config('minify');

        exec.if(!isMinify, function(error, name) {
            fs.readFile(name || PATH_INDEX, 'utf8', function(error, template) {
                var panel, data;

                if (!error) {
                    panel   = CloudFunc.buildFromJSON({
                        data        : json,
                        template    : Template
                    }),

                    data    = indexProcessing({
                        panel   : panel,
                        data    : template
                    });
                }

                exec(callback, error, data);
            });
        },  function(callback) {
                minify(PATH_INDEX, 'name', callback);
        });
    }
})();
<commit_msg>chore(route) add "\n"<commit_after>(function() {
    'use strict';

    var DIR         = __dirname + '/../../',
        DIR_HTML    = DIR       + 'html/',
        DIR_LIB     = DIR       + 'lib/',
        DIR_SERVER  = __dirname + '/',
        DIR_FS      = DIR_HTML  + 'fs/',

        fs          = require('fs'),

        mellow      = require('mellow'),
        ponse       = require('ponse'),
        files       = require('files-io'),
        rendy       = require('rendy'),
        exec        = require('execon'),

        minify      = require('minify'),
        format      = require('format-io'),

        config      = require(DIR_SERVER + 'config'),

        CloudFunc   = require(DIR_LIB + 'cloudfunc'),

        PATH_INDEX  = DIR_FS   + 'index.html',

        TMPL_PATH   = [
            'file',
            'panel',
            'path',
            'pathLink',
            'link'
        ],

        Template    = {},

        FS          = CloudFunc.FS;

    module.exports  = function(req, res, next) {
        readFiles(function() {
            route(req, res, next);
        });
    };

    /**
     * additional processing of index file
     */
    function indexProcessing(options) {
        var left, right, from, to,
            keysPanel   = '<div id="js-keyspanel" class="{{ className }}',
            LEFT        = CloudFunc.PANEL_LEFT,
            RIGHT       = CloudFunc.PANEL_RIGHT,
            data        = options.data,
            panel       = options.panel;

        if (!config('showKeysPanel')) {
            from        = rendy(keysPanel, {
                className: 'keyspanel'
            });

            to          = rendy(keysPanel, {
                className: 'keyspanel hidden'
            });

            data        = data.replace(from, to);
        }

        left = rendy(Template.panel, {
            id      : LEFT,
            side    : 'left',
            content : panel
        });

        right = rendy(Template.panel, {
            id      : RIGHT,
            side    : 'right',
            content : panel
        });

        data = rendy(data, {
            title   : CloudFunc.getTitle(),
            fm      : left + right
        });

        return data;
    }

    function readFiles(callback) {
        var filesList,
            paths       = {},

            lengthTmpl  = Object.keys(Template).length,
            lenthPath   = TMPL_PATH.length,
            isRead      = lengthTmpl === lenthPath;

        if (typeof callback !== 'function')
            throw Error('callback should be function!');

        if (isRead) {
            callback();
        } else {
            filesList   = TMPL_PATH.map(function(name) {
                var path = DIR_FS + name + '.html';

                paths[path] = name;

                return path;
            });

            files.read(filesList, 'utf8', function(error, files) {
                if (error)
                    throw error;
                else
                    Object.keys(files).forEach(function(path) {
                        var name = paths[path];

                        Template[name] = files[path];
                    });

                callback();
            });
        }
    }

    /**
     * routing of server queries
     */
    function route(request, response, callback) {
        var name, p, isAuth, isFS, path;

        if (!request)
            throw Error('request could not be empty!');

        if (!response)
            throw Error('response could not be empty!');

        if (typeof callback !== 'function')
            throw Error('callback should be function!');

        name    = ponse.getPathName(request);

        isAuth  = RegExp('^(/auth|/auth/github)$').test(name);
        isFS    = RegExp('^/$|^' + FS).test(name);

        p       = {
            request     : request,
            response    : response,
            gzip        : true,
            name        : name
        };

        if (!isAuth && !isFS)
            callback();
        else if (isAuth) {
            console.log('* Routing' + '-> ' + name);

            p.name = DIR_HTML + name + '.html';
            ponse.sendFile(p);
        } else if (isFS) {
            name    = name.replace(CloudFunc.FS, '') || '/';
            path    = mellow.pathToWin(name);

            mellow.read(path, function(error, dir) {
                if (dir)
                    dir.path = format.addSlashToEnd(name);

                if (!error)
                    buildIndex(dir, function(error, data) {
                        p.name = PATH_INDEX;

                        if (error)
                            ponse.sendError(error, p);
                        else
                            ponse.send(data, p);
                    });
                else if (error.code !== 'ENOTDIR')
                    ponse.sendError(error, p);
                else
                    fs.realpath(path, function(error, pathReal) {
                        if (!error)
                            p.name = pathReal;
                        else
                            p.name = path;

                        p.gzip = false;
                        ponse.sendFile(p);
                    });
            });
        }
    }

    function buildIndex(json, callback) {
        var isMinify = config('minify');

        exec.if(!isMinify, function(error, name) {
            fs.readFile(name || PATH_INDEX, 'utf8', function(error, template) {
                var panel, data;

                if (!error) {
                    panel   = CloudFunc.buildFromJSON({
                        data        : json,
                        template    : Template
                    }),

                    data    = indexProcessing({
                        panel   : panel,
                        data    : template
                    });
                }

                exec(callback, error, data);
            });
        },  function(callback) {
                minify(PATH_INDEX, 'name', callback);
        });
    }
})();
<|endoftext|>


<commit_before>var path = require('path'),
    fs = require('fs'),
    semver = require('./semver.js');

var existsSync = fs.existsSync || path.existsSync;

function flushAndExit(code) {
    var exiting;
    process.on('exit', function () {
        if (exiting) {
            return;
        }
        exiting = true;
        process.exit(code);
    });
}

function resolveNpmPath(npmRootPath, npmVersion) {
    if (!npmVersion) {
        return;
    }

    var npmPath = path.resolve(npmRootPath, npmVersion, 'node_modules', 'npm', 'bin', 'npm-cli.js');
    if (!existsSync(npmPath)) {
        // Try resolving it using the old npm layout
        npmPath = path.resolve(npmRootPath, npmVersion, 'bin', 'npm-cli.js');
        if (!existsSync(npmPath)) {
            throw new Error('Unable to locate npm version ' + npmVersion);
        }
    }

    return npmPath;
}

function getDefaultNpmVersion(nodeVersionPath) {
    var npmLinkPath = path.resolve(nodeVersionPath, 'npm.txt');
    // Determine if there's a link to npm at the node path
    if (!existsSync(npmLinkPath)) {
        return;
    }
    var npmVersion = fs.readFileSync(npmLinkPath, 'utf8').trim();
    return npmVersion;
}

function getNpmVersionFromJson(npmRootPath, json) {
    if (typeof json.engines.npm !== 'string') {
        return;
    }

    var versions = [];
    fs.readdirSync(npmRootPath).forEach(function (dir) {
        versions.push(dir);
    });

    var npmVersion = semver.maxSatisfying(versions, json.engines.npm);
    if (!npmVersion) {
        var errorMsg = 'No available npm version matches application\'s version constraint of \''
                        + json.engines.npm + '\'. Use package.json to choose one of the following versions: '
                        + versions.join(', ') + '.';
        throw new Error(errorMsg);
    }
    return npmVersion;
}

function saveNodePaths(tempDir, nodeExePath, npmPath) {
    if (!tempDir) {
        return;
    }
    var nodeTmpFile = path.resolve(tempDir, '__nodeVersion.tmp'),
        npmTempFile = path.resolve(tempDir, '__npmVersion.tmp');

    fs.writeFileSync(nodeTmpFile, nodeExePath);
    if (npmPath) {
        fs.writeFileSync(npmTempFile, npmPath);
    }
}

function getNodeDefaultStartFile(sitePath) {
    var nodeStartFiles = ['server.js', 'app.js'];

    for (var i = 0; i < nodeStartFiles.length; i++) {
        var nodeStartFilePath = path.join(sitePath, nodeStartFiles[i]);
        if (existsSync(nodeStartFilePath)) {
            return nodeStartFiles[i];
        }
    }

    return null;
}

// Determine the installation location of node.js and iisnode
var programFilesDir = process.env['programfiles(x86)'] || process.env.programfiles,
    nodejsDir = path.resolve(programFilesDir, 'nodejs'),
    npmRootPath = path.resolve(programFilesDir, 'npm');

if (!existsSync(nodejsDir)) {
    throw new Error('Unable to locate node.js installation directory at ' + nodejsDir);
}

var interceptorJs = path.resolve(process.env['programfiles(x86)'], 'iisnode', 'interceptor.js');
if (!existsSync(interceptorJs)) {
    interceptorJs = path.resolve(process.env.programfiles, 'iisnode', 'interceptor.js');
    if (!existsSync(interceptorJs)) {
        throw new Error('Unable to locate iisnode installation directory with interceptor.js file');
    }
}

// Validate input parameters

var repo = process.argv[2];
var wwwroot = process.argv[3];
var tempDir = process.argv[4];
if (!existsSync(wwwroot) || !existsSync(repo) || (tempDir && !existsSync(tempDir))) {
    throw new Error('Usage: node.exe selectNodeVersion.js <path_to_repo> <path_to_wwwroot> [path_to_temp]');
}

var packageJson = path.resolve(repo, 'package.json'),
    json = existsSync(packageJson) && JSON.parse(fs.readFileSync(packageJson, 'utf8'));

// If the web.config file does not exist in the repo, use a default one that is specific for node on IIS in Azure,
// and generate it in 'wwwroot'
// Obtain the start script from package.json or seach for app.js/server.js at the root of the repository.
var nodeStartFilePath = (function createIisNodeWebConfigIfNeeded() {
    var webConfigRepoPath = path.join(repo, 'web.config'),
        webConfigWwwRootPath = path.join(wwwroot, 'web.config'),
        nodeStartFilePath = null;

    if (!existsSync(webConfigRepoPath)) {
        // Check for {"scripts": {"start": < startupCommand > } } exists
        if (typeof json === 'object' && typeof json.scripts === 'object' && typeof json.scripts.start === 'string') {
            var startupCommand = json.scripts.start;
            var defaultNode = "node ";
            if (startupCommand.length > defaultNode.length && startupCommand.slice(0, defaultNode.length) === defaultNode) {
                nodeStartFilePath = startupCommand.slice(defaultNode.length);
                // For the path to be read by iisnode handler
                if (nodeStartFilePath.slice(0, 2) === "./") {
                    nodeStartFilePath = nodeStartFilePath.slice(2);
                }
                console.log('Using start-up script ' + nodeStartFilePath + ' specified in package.json.');
            } else {
                console.error('Invalid start-up command in package.json. Please use the format "node <script relative path>".');
            }
        }

        if (!nodeStartFilePath) {
            console.log('Looking for app.js/server.js under site root.');
            nodeStartFilePath = getNodeDefaultStartFile(repo);
            if (!nodeStartFilePath) {
                console.error('Missing server.js/app.js files, web.config is not generated');
                return nodeStartFilePath;
            }
            console.log('Using start-up script ' + nodeStartFilePath);
        }

        var iisNodeConfigTemplatePath = path.join(__dirname, 'iisnode.config.template');
        var webConfigContent = fs.readFileSync(iisNodeConfigTemplatePath, 'utf8');
        webConfigContent = webConfigContent.replace(/\{NodeStartFile\}/g, nodeStartFilePath);

        fs.writeFileSync(webConfigWwwRootPath, webConfigContent, 'utf8');

        console.log('Generated web.config.');
    }

    return nodeStartFilePath;
})();

// The directory of the start up script.
var nodeStartDirectory = (function () {
    if (!nodeStartFilePath) {
        return "";
    }

    var index = nodeStartFilePath.lastIndexOf("/");
    if (index === -1) {
        return "";
    } else {
        return nodeStartFilePath.slice(0, index);
    }
})();

// If the iinode.yml file does not exist in the repo but exists in wwwroot, remove it from wwwroot
// to prevent side-effects of previous deployments
var repoIisnodeYml = path.resolve(repo, nodeStartDirectory, 'iisnode.yml');
var siteIisnodeYml = path.resolve(wwwroot, nodeStartDirectory, 'iisnode.yml');
if (!existsSync(repoIisnodeYml) && existsSync(siteIisnodeYml)) {
    fs.unlinkSync(siteIisnodeYml);
}

try {
    var nodeVersion = process.env.WEBSITE_NODE_DEFAULT_VERSION || process.versions.node,
        npmVersion = null,
        yml = existsSync(repoIisnodeYml) ? fs.readFileSync(repoIisnodeYml, 'utf8') : '',
        shouldUpdateIisNodeYml = false;

    if (yml.match(/^ *nodeProcessCommandLine *:/m)) {
        // If the iisnode.yml included with the application explicitly specifies the
        // nodeProcessCommandLine, exit this script. The presence of nodeProcessCommandLine
        // deactivates automatic version selection.
        console.log('The iisnode.yml file explicitly sets nodeProcessCommandLine. ' +
                    'Automatic node.js version selection is turned off.');
    } else {
        // If the package.json file is not included with the application
        // or if it does not specify node.js version constraints, use WEBSITE_NODE_DEFAULT_VERSION.
        if (typeof json !== 'object' || typeof json.engines !== 'object' || typeof json.engines.node !== 'string') {
            // Attempt to read the pinned node version or fallback to the version of the executing node.exe.
            console.log('The package.json file does not specify node.js engine version constraints.');
            console.log('The node.js application will run with the default node.js version '
                + nodeVersion + '.');
        } else {
            // Determine the set of node.js versions available on the platform
            var versions = [];
            fs.readdirSync(nodejsDir).forEach(function (dir) {
                if (dir.match(/^\d+\.\d+\.\d+$/) && existsSync(path.resolve(nodejsDir, dir, 'node.exe'))) {
                    versions.push(dir);
                }
            });

            console.log('Node.js versions available on the platform are: ' + versions.sort(semver.compare).join(', ') + '.');

            // Calculate actual node.js version to use for the application as the maximum available version
            // that satisfies the version constraints from package.json.
            nodeVersion = semver.maxSatisfying(versions, json.engines.node);
            if (!nodeVersion) {
                throw new Error('No available node.js version matches application\'s version constraint of \''
                    + json.engines.node + '\'. Use package.json to choose one of the available versions.');
            }

            console.log('Selected node.js version ' + nodeVersion + '. Use package.json file to choose a different version.');
            npmVersion = getNpmVersionFromJson(npmRootPath, json);
            shouldUpdateIisNodeYml = true;
        }
    }

    var nodeVersionPath = path.resolve(nodejsDir, nodeVersion),
        nodeExePath = path.resolve(nodeVersionPath, 'node.exe'),
        npmPath = resolveNpmPath(npmRootPath, npmVersion || getDefaultNpmVersion(nodeVersionPath));

    // Save the node version in a temporary path for kudu service usage
    saveNodePaths(tempDir, nodeExePath, npmPath);

    if (shouldUpdateIisNodeYml) {
        // Save the version information to iisnode.yml in the start script directory

        if (yml !== '') {
            yml += '\r\n';
        }

        yml += 'nodeProcessCommandLine: "' + nodeExePath + '"';

        console.log('Updating iisnode.yml at ' + siteIisnodeYml);

        fs.writeFileSync(siteIisnodeYml, yml);
    }

} catch (ex) {
    console.error(ex.message);
    flushAndExit(-1);
}
<commit_msg>Use server.js/app.js when the start script in package.json is invalid.<commit_after>var path = require('path'),
    fs = require('fs'),
    semver = require('./semver.js');

var existsSync = fs.existsSync || path.existsSync;

function flushAndExit(code) {
    var exiting;
    process.on('exit', function () {
        if (exiting) {
            return;
        }
        exiting = true;
        process.exit(code);
    });
}

function resolveNpmPath(npmRootPath, npmVersion) {
    if (!npmVersion) {
        return;
    }

    var npmPath = path.resolve(npmRootPath, npmVersion, 'node_modules', 'npm', 'bin', 'npm-cli.js');
    if (!existsSync(npmPath)) {
        // Try resolving it using the old npm layout
        npmPath = path.resolve(npmRootPath, npmVersion, 'bin', 'npm-cli.js');
        if (!existsSync(npmPath)) {
            throw new Error('Unable to locate npm version ' + npmVersion);
        }
    }

    return npmPath;
}

function getDefaultNpmVersion(nodeVersionPath) {
    var npmLinkPath = path.resolve(nodeVersionPath, 'npm.txt');
    // Determine if there's a link to npm at the node path
    if (!existsSync(npmLinkPath)) {
        return;
    }
    var npmVersion = fs.readFileSync(npmLinkPath, 'utf8').trim();
    return npmVersion;
}

function getNpmVersionFromJson(npmRootPath, json) {
    if (typeof json.engines.npm !== 'string') {
        return;
    }

    var versions = [];
    fs.readdirSync(npmRootPath).forEach(function (dir) {
        versions.push(dir);
    });

    var npmVersion = semver.maxSatisfying(versions, json.engines.npm);
    if (!npmVersion) {
        var errorMsg = 'No available npm version matches application\'s version constraint of \''
                        + json.engines.npm + '\'. Use package.json to choose one of the following versions: '
                        + versions.join(', ') + '.';
        throw new Error(errorMsg);
    }
    return npmVersion;
}

function saveNodePaths(tempDir, nodeExePath, npmPath) {
    if (!tempDir) {
        return;
    }
    var nodeTmpFile = path.resolve(tempDir, '__nodeVersion.tmp'),
        npmTempFile = path.resolve(tempDir, '__npmVersion.tmp');

    fs.writeFileSync(nodeTmpFile, nodeExePath);
    if (npmPath) {
        fs.writeFileSync(npmTempFile, npmPath);
    }
}

function getNodeDefaultStartFile(sitePath) {
    var nodeStartFiles = ['server.js', 'app.js'];

    for (var i = 0; i < nodeStartFiles.length; i++) {
        var nodeStartFilePath = path.join(sitePath, nodeStartFiles[i]);
        if (existsSync(nodeStartFilePath)) {
            return nodeStartFiles[i];
        }
    }

    return null;
}

// Determine the installation location of node.js and iisnode
var programFilesDir = process.env['programfiles(x86)'] || process.env.programfiles,
    nodejsDir = path.resolve(programFilesDir, 'nodejs'),
    npmRootPath = path.resolve(programFilesDir, 'npm');

if (!existsSync(nodejsDir)) {
    throw new Error('Unable to locate node.js installation directory at ' + nodejsDir);
}

var interceptorJs = path.resolve(process.env['programfiles(x86)'], 'iisnode', 'interceptor.js');
if (!existsSync(interceptorJs)) {
    interceptorJs = path.resolve(process.env.programfiles, 'iisnode', 'interceptor.js');
    if (!existsSync(interceptorJs)) {
        throw new Error('Unable to locate iisnode installation directory with interceptor.js file');
    }
}

// Validate input parameters

var repo = process.argv[2];
var wwwroot = process.argv[3];
var tempDir = process.argv[4];
if (!existsSync(wwwroot) || !existsSync(repo) || (tempDir && !existsSync(tempDir))) {
    throw new Error('Usage: node.exe selectNodeVersion.js <path_to_repo> <path_to_wwwroot> [path_to_temp]');
}

var packageJson = path.resolve(repo, 'package.json'),
    json = existsSync(packageJson) && JSON.parse(fs.readFileSync(packageJson, 'utf8'));

// If the web.config file does not exist in the repo, use a default one that is specific for node on IIS in Azure,
// and generate it in 'wwwroot'
// Obtain the start script from package.json or seach for app.js/server.js at the root of the repository.
var nodeStartFilePath = (function createIisNodeWebConfigIfNeeded() {
    var webConfigRepoPath = path.join(repo, 'web.config'),
        webConfigWwwRootPath = path.join(wwwroot, 'web.config'),
        nodeStartFilePath = null;

    if (!existsSync(webConfigRepoPath)) {
        // Check for {"scripts": {"start": < startupCommand > } } exists
        if (typeof json === 'object' && typeof json.scripts === 'object' && typeof json.scripts.start === 'string') {
            var startupCommand = json.scripts.start;
            var defaultNode = "node ";
            if (startupCommand.length > defaultNode.length && startupCommand.slice(0, defaultNode.length) === defaultNode) {
                var startFile = path.resolve(repo, startupCommand.slice(defaultNode.length));
                var startFileJs = path.resolve(repo, startupCommand.slice(defaultNode.length) + ".js");
                if (existsSync(startFile)) {
                    nodeStartFilePath = path.relative(repo, startFile);
                } else if (existsSync(startFileJs)) {
                    nodeStartFilePath = path.relative(repo, startFileJs);
                }
                if (nodeStartFilePath) {
                    // iisnode requires forward-slash in paths
                    nodeStartFilePath = nodeStartFilePath.replace(/\\/g, '/');
                    console.log('Using start-up script ' + nodeStartFilePath + ' from package.json.');
                } else {
                    console.log('Start script "' + startupCommand.slice(defaultNode.length) + '" from package.json is not found.');
                }
            } else {
                console.error('Invalid start-up command "' + startupCommand + '" in package.json. Please use the format "node <script relative path>".');
            }
        }

        if (!nodeStartFilePath) {
            console.log('Looking for app.js/server.js under site root.');
            nodeStartFilePath = getNodeDefaultStartFile(repo);
            if (!nodeStartFilePath) {
                console.error('Missing server.js/app.js files, web.config is not generated');
                return nodeStartFilePath;
            }
            console.log('Using start-up script ' + nodeStartFilePath);
        }

        var iisNodeConfigTemplatePath = path.join(__dirname, 'iisnode.config.template');
        var webConfigContent = fs.readFileSync(iisNodeConfigTemplatePath, 'utf8');
        webConfigContent = webConfigContent.replace(/\{NodeStartFile\}/g, nodeStartFilePath);

        fs.writeFileSync(webConfigWwwRootPath, webConfigContent, 'utf8');

        console.log('Generated web.config.');
    }

    return nodeStartFilePath;
})();

// The directory of the start up script.
var nodeStartDirectory = (function () {
    if (!nodeStartFilePath) {
        return "";
    }

    var index = nodeStartFilePath.lastIndexOf("/");
    if (index === -1) {
        return "";
    } else {
        return nodeStartFilePath.slice(0, index);
    }
})();

// If the iinode.yml file does not exist in the repo but exists in wwwroot, remove it from wwwroot
// to prevent side-effects of previous deployments
var repoIisnodeYml = path.resolve(repo, nodeStartDirectory, 'iisnode.yml');
var siteIisnodeYml = path.resolve(wwwroot, nodeStartDirectory, 'iisnode.yml');
if (!existsSync(repoIisnodeYml) && existsSync(siteIisnodeYml)) {
    fs.unlinkSync(siteIisnodeYml);
}

try {
    var nodeVersion = process.env.WEBSITE_NODE_DEFAULT_VERSION || process.versions.node,
        npmVersion = null,
        yml = existsSync(repoIisnodeYml) ? fs.readFileSync(repoIisnodeYml, 'utf8') : '',
        shouldUpdateIisNodeYml = false;

    if (yml.match(/^ *nodeProcessCommandLine *:/m)) {
        // If the iisnode.yml included with the application explicitly specifies the
        // nodeProcessCommandLine, exit this script. The presence of nodeProcessCommandLine
        // deactivates automatic version selection.
        console.log('The iisnode.yml file explicitly sets nodeProcessCommandLine. ' +
                    'Automatic node.js version selection is turned off.');
    } else {
        // If the package.json file is not included with the application
        // or if it does not specify node.js version constraints, use WEBSITE_NODE_DEFAULT_VERSION.
        if (typeof json !== 'object' || typeof json.engines !== 'object' || typeof json.engines.node !== 'string') {
            // Attempt to read the pinned node version or fallback to the version of the executing node.exe.
            console.log('The package.json file does not specify node.js engine version constraints.');
            console.log('The node.js application will run with the default node.js version '
                + nodeVersion + '.');
        } else {
            // Determine the set of node.js versions available on the platform
            var versions = [];
            fs.readdirSync(nodejsDir).forEach(function (dir) {
                if (dir.match(/^\d+\.\d+\.\d+$/) && existsSync(path.resolve(nodejsDir, dir, 'node.exe'))) {
                    versions.push(dir);
                }
            });

            console.log('Node.js versions available on the platform are: ' + versions.sort(semver.compare).join(', ') + '.');

            // Calculate actual node.js version to use for the application as the maximum available version
            // that satisfies the version constraints from package.json.
            nodeVersion = semver.maxSatisfying(versions, json.engines.node);
            if (!nodeVersion) {
                throw new Error('No available node.js version matches application\'s version constraint of \''
                    + json.engines.node + '\'. Use package.json to choose one of the available versions.');
            }

            console.log('Selected node.js version ' + nodeVersion + '. Use package.json file to choose a different version.');
            npmVersion = getNpmVersionFromJson(npmRootPath, json);
            shouldUpdateIisNodeYml = true;
        }
    }

    var nodeVersionPath = path.resolve(nodejsDir, nodeVersion),
        nodeExePath = path.resolve(nodeVersionPath, 'node.exe'),
        npmPath = resolveNpmPath(npmRootPath, npmVersion || getDefaultNpmVersion(nodeVersionPath));

    // Save the node version in a temporary path for kudu service usage
    saveNodePaths(tempDir, nodeExePath, npmPath);

    if (shouldUpdateIisNodeYml) {
        // Save the version information to iisnode.yml in the start script directory

        if (yml !== '') {
            yml += '\r\n';
        }

        yml += 'nodeProcessCommandLine: "' + nodeExePath + '"';

        console.log('Updating iisnode.yml at ' + siteIisnodeYml);

        fs.writeFileSync(siteIisnodeYml, yml);
    }

} catch (ex) {
    console.error(ex.message);
    flushAndExit(-1);
}
<|endoftext|>


<commit_before>// This file defines utility functions that are available as methods of
// `this` inside setter functions.

exports.assign = Object.assign || function (obj) {
  var argc = arguments.length;
  for (var i = 1; i < argc; ++i) {
    var arg = arguments[i];
    if (arg && typeof arg === "object") {
      var keys = Object.keys(arg);
      for (var k = 0; k < keys.length; ++k) {
        obj[k] = arg[k];
      }
    }
  }
  return obj;
};
<commit_msg>Fix silly mistake in the polyfill for Object.assign.<commit_after>// This file defines utility functions that are available as methods of
// `this` inside setter functions.

exports.assign = Object.assign || function (obj) {
  var argc = arguments.length;
  for (var i = 1; i < argc; ++i) {
    var arg = arguments[i];
    if (arg && typeof arg === "object") {
      var keys = Object.keys(arg);
      for (var k = 0; k < keys.length; ++k) {
        var key = keys[k];
        obj[key] = arg[key];
      }
    }
  }
  return obj;
};
<|endoftext|>


<commit_before>import colors from '../settings/colors';
import darken from '../internal/darken';

const styleSheet = {
  wrapper: {
    width: '100vw',
    height: '1px',
    position: 'absolute',
    top: '0',
    left: '0',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'center'
  },
  banner: {
    position: 'relative',
    backgroundColor: colors.white,
    borderRadius: '3px',
    boxShadow: `${colors.boxShadow} 0px 1px 6px, ${colors.boxShadow} 0px 1px 4px`,
    overflow: 'hidden'
  },
  desktop: {
    width: '728px',
    height: '90px'
  },
  mobile: {
    width: '320px',
    height: '50px'
  },
  button: {
    position: 'absolute',
    top: '4px',
    right: '4px',
    boxShadow: `${colors.boxShadow} 0px 1px 6px, ${colors.boxShadow} 0px 1px 4px`,
    background: colors.primaryText,
    ':hover': {
      background: darken(colors.primaryText, 0.15)
    },
    ':active': {
      background: darken(colors.primaryText, 0.25)
    }
  }
};

export default styleSheet;
<commit_msg>:bug: Changed to percentage<commit_after>import colors from '../settings/colors';
import darken from '../internal/darken';

const styleSheet = {
  wrapper: {
    width: '100%',
    height: '1px',
    position: 'absolute',
    top: '0',
    left: '0',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'center'
  },
  banner: {
    position: 'relative',
    backgroundColor: colors.white,
    borderRadius: '3px',
    boxShadow: `${colors.boxShadow} 0px 1px 6px, ${colors.boxShadow} 0px 1px 4px`,
    overflow: 'hidden'
  },
  desktop: {
    width: '728px',
    height: '90px'
  },
  mobile: {
    width: '320px',
    height: '50px'
  },
  button: {
    position: 'absolute',
    top: '4px',
    right: '4px',
    boxShadow: `${colors.boxShadow} 0px 1px 6px, ${colors.boxShadow} 0px 1px 4px`,
    background: colors.primaryText,
    ':hover': {
      background: darken(colors.primaryText, 0.15)
    },
    ':active': {
      background: darken(colors.primaryText, 0.25)
    }
  }
};

export default styleSheet;
<|endoftext|>


<commit_before>;(function() {

    var container = null;
    var markers = null;

    var mousewheel = "onwheel" in document.createElement("div") ? "wheel" :
            document.onmousewheel !== undefined ? "mousewheel" :
            "DOMMouseScroll";

    function panZoom(opt_options) {
        var pz = {
            pan: {}
        };
        var self = this;
        opt_options = Object(opt_options);
        opt_options.zoom = opt_options.zoom || [];
        pz.elm = self;
        var svg = self;
        while ((svg = self.parent).node.tagName.toUpperCase() !== "SVG") {}
        var rect = new SVG(document.createDocumentFragment()).rect().attr({
            width: svg.width(),
            height: svg.height(),
            fill: "none"
        }).style("pointer-events", "all");
        self.parent.node.insertBefore(rect.node, self.node)

        function updateMatrix() {
            self.matrix([
                pz.transform.scaleX, 0, 0,
                pz.transform.scaleY, pz.transform.x, pz.transform.y
            ].join(","));
        }

        function pan(e) {
            if (!pz.pan.mousedown) {
                return;
            }
            var tr = pz.transform = self.transform();
            var diffX = (pz.pan.fPos.x - pz.pan.iPos.x) / (tr.scaleX + 1);
            var diffY = (pz.pan.fPos.y - pz.pan.iPos.y) / (tr.scaleY + 1);
            tr.x += diffX;
            tr.y += diffY;
            pz.pan.iPos = pz.pan.fPos;
            self.node.dispatchEvent(new CustomEvent("pan", e, tr));
            updateMatrix();
        }

        function zoom (e) {
            var tr = pz.transform = self.transform();
            var d = e.deltaY / 1000
            var scale = tr.scaleX + d;

            var oX = e.clientX;
            var oY = e.clientY;

            var scaleD = scale / tr.scaleX;
            var currentX = tr.x;
            var currentY = tr.y;
            var x = scaleD * (currentX - oX) + oX;
            var y = scaleD * (currentY - oY) + oY;

            if (scale > opt_options.zoom[1]) {
                scale = opt_options.zoom[1];
                return;
            }

            if (scale < opt_options.zoom[0]) {
                scale = opt_options.zoom[0];
                return;
            }

            tr.scaleY = tr.scaleX = scale;
            tr.x = x;
            tr.y = y;

            self.node.dispatchEvent(new CustomEvent("zoom", e, tr));
            updateMatrix();
        }

        var EventListeners = {
            mouse_down: function (e) {
                pz.pan.mousedown = true;
                pz.pan.iPos = {
                    x: e.clientX || e.touches[0].pageX,
                    y: e.clientY || e.touches[0].pageY
                };
            },
            mouse_up: function (e) {
                pz.pan.mousedown = false;
                pz.pan.fPos = {
                    x: e.clientX || e.touches[0].pageX,
                    y: e.clientY || e.touches[0].pageY
                };
                pan();
            },
            mouse_move: function (e) {
                pz.pan.fPos = {
                    x: e.clientX || e.touches[0].pageX,
                    y: e.clientY || e.touches[0].pageY
                };
                pan();
            },
            mouse_leave: function (e) {
                pz.pan.mousedown = false;
            }
        };

        rect
          .on(mousewheel, zoom)
          .on("mousedown", EventListeners.mouse_down)
          .on("touchstart", EventListeners.mouse_down)
          .on("mousemove", EventListeners.mouse_move)
          .on("touchmove", EventListeners.mouse_move)
          .on("mouseup", EventListeners.mouse_up)
          .on("touchup", EventListeners.mouse_up)
          .on("mouseleave", EventListeners.mouse_leave)
          ;

        self.on(mousewheel, zoom);
    }

    SVG.extend(SVG.Element, {
        panZoom: panZoom
    });
}).call(this);
<commit_msg>JSDoc comments<commit_after>/*!
 * SVG.js Pan Zoom Plugin
 * ======================
 *
 * A JavaScript library for pan and zoom SVG things.
 * Created with <3 and JavaScript by the jillix developers.
 *
 * svg.pan-zoom.js 1.0.0
 * Licensed under the MIT license.
 * */
;(function() {

    var container = null;
    var markers = null;

    var mousewheel = "onwheel" in document.createElement("div") ? "wheel" :
            document.onmousewheel !== undefined ? "mousewheel" :
            "DOMMouseScroll";

    /**
     * panZoom
     * The pan-zoom contructor.
     *
     * @name panZoom
     * @function
     * @param {Object|undefined} opt_options An optional object containing the following fields:
     *
     *  - `zoom` (Array): An array of two float values: the minimum and maximum zoom values (default: `undefined`).
     *
     * @return {PanZoom} The PanZoom object containing the following fields:
     *
     *  - `elm` (SVG): The selected element.
     *  - `pan` (Object): An object containing pan values.
     *  - `transform` (Object): An object containing the transform data (`scaleX`, `scaleY`, `x` and `y`).
     *
     */
    function panZoom(opt_options) {

        // Selected element
        var self = this;

        // Pan zoom object
        var pz = {
            pan: {},
            elm = self
        };

        // Set options
        opt_options = Object(opt_options);
        opt_options.zoom = opt_options.zoom || [];

        // Get the svg document
        var svg = self;
        while ((svg = self.parent).node.tagName.toUpperCase() !== "SVG") {}

        // Create the rectangle
        var rect = new SVG(document.createDocumentFragment()).rect().attr({
            width: svg.width(),
            height: svg.height(),
            fill: "none"
        }).style("pointer-events", "all");

        // Insert the rectangle
        self.parent.node.insertBefore(rect.node, self.node)

        /**
         * updateMatrix
         * An internal function called to update the svg matrix.
         *
         * @name updateMatrix
         * @function
         * @return {undefined}
         */
        function updateMatrix() {
            self.matrix([
                pz.transform.scaleX, 0, 0,
                pz.transform.scaleY, pz.transform.x, pz.transform.y
            ].join(","));
        }

        /**
         * pan
         * The internal function called for panning.
         *
         * @name pan
         * @function
         * @param {Event} e The internal listener event.
         * @return {undefined}
         */
        function pan(e) {
            if (!pz.pan.mousedown) {
                return;
            }
            var tr = pz.transform = self.transform();
            var diffX = (pz.pan.fPos.x - pz.pan.iPos.x) / (tr.scaleX + 1);
            var diffY = (pz.pan.fPos.y - pz.pan.iPos.y) / (tr.scaleY + 1);
            tr.x += diffX;
            tr.y += diffY;
            pz.pan.iPos = pz.pan.fPos;
            self.node.dispatchEvent(new CustomEvent("pan", e, tr));
            updateMatrix();
        }

        /*!
         * zoom
         * The internal function called for zooming.
         *
         * @name zoom
         * @function
         * @param {Event} e The internal listener event.
         * @return {undefined}
         */
        function zoom (e) {
            var tr = pz.transform = self.transform();
            var d = e.deltaY / 1000
            var scale = tr.scaleX + d;

            var oX = e.clientX;
            var oY = e.clientY;

            var scaleD = scale / tr.scaleX;
            var currentX = tr.x;
            var currentY = tr.y;
            var x = scaleD * (currentX - oX) + oX;
            var y = scaleD * (currentY - oY) + oY;

            if (scale > opt_options.zoom[1]) {
                scale = opt_options.zoom[1];
                return;
            }

            if (scale < opt_options.zoom[0]) {
                scale = opt_options.zoom[0];
                return;
            }

            tr.scaleY = tr.scaleX = scale;
            tr.x = x;
            tr.y = y;

            self.node.dispatchEvent(new CustomEvent("zoom", e, tr));
            updateMatrix();
        }

        // The event listeners
        var EventListeners = {
            mouse_down: function (e) {
                pz.pan.mousedown = true;
                pz.pan.iPos = {
                    x: e.clientX || e.touches[0].pageX,
                    y: e.clientY || e.touches[0].pageY
                };
            },
            mouse_up: function (e) {
                pz.pan.mousedown = false;
                pz.pan.fPos = {
                    x: e.clientX || e.touches[0].pageX,
                    y: e.clientY || e.touches[0].pageY
                };
                pan();
            },
            mouse_move: function (e) {
                pz.pan.fPos = {
                    x: e.clientX || e.touches[0].pageX,
                    y: e.clientY || e.touches[0].pageY
                };
                pan();
            },
            mouse_leave: function (e) {
                pz.pan.mousedown = false;
            }
        };

        // Add event listeners
        rect
          .on(mousewheel, zoom)
          .on("mousedown", EventListeners.mouse_down)
          .on("touchstart", EventListeners.mouse_down)
          .on("mousemove", EventListeners.mouse_move)
          .on("touchmove", EventListeners.mouse_move)
          .on("mouseup", EventListeners.mouse_up)
          .on("touchup", EventListeners.mouse_up)
          .on("mouseleave", EventListeners.mouse_leave)
          ;

        self.on(mousewheel, zoom);
    }

    // Extend the SVG.Element with the new function
    SVG.extend(SVG.Element, {
        panZoom: panZoom
    });
}).call(this);
<|endoftext|>


<commit_before>function renderDisplayObject(child, ctx, transform, cxform, clip) {
  var m = transform;
  if (m.a * m.d == m.b * m.c) {
    // Workaround for bug 844184 -- the object is invisible
    ctx.closePath();
    ctx.rect(0, 0, 0, 0);
    ctx.clip();
  } else {
    ctx.transform(m.a, m.b, m.c, m.d, m.tx, m.ty);
  }

  if (cxform) {
    // We only support alpha channel transformation for now
    ctx.globalAlpha = (ctx.globalAlpha * cxform.alphaMultiplier + cxform.alphaOffset) / 256;
  }
  if (child._alpha !== 1) {
    ctx.globalAlpha *= child._alpha;
  }

  if (child._graphics) {
    var graphics = child._graphics;

    if (graphics._bitmap) {
      ctx.translate(child._bbox.left, child._bbox.top);
      ctx.drawImage(graphics._bitmap, 0, 0);
    } else {
      var scale = graphics._scale;
      if (scale !== 1)
        ctx.scale(scale, scale);

      var subpaths = graphics._subpaths;
      for (var j = 0, o = subpaths.length; j < o; j++) {
        var pathTracker = subpaths[j], path = pathTracker.target;
        if (clip) {
          ctx.beginPath();
          ctx.__draw__(path);
          ctx.closePath();
        } else {
          if (path.fillStyle) {
            ctx.fillStyle = path.fillStyle;
            if (path.fillTransform) {
              var m = path.fillTransform;
              ctx.beginPath();
              ctx.__draw__(path);
              ctx.save();
              ctx.transform(m.a, m.b, m.c, m.d, m.tx, m.ty);
              ctx.fill();
              ctx.restore();
            } else {
              ctx.fill(path);
            }
          }
          if (path.strokeStyle) {
            ctx.strokeStyle = path.strokeStyle;
            var drawingStyles = pathTracker.drawingStyles;
            for (var prop in drawingStyles)
              ctx[prop] = drawingStyles[prop];
            ctx.stroke(path);
          }
        }
      }
    }
  }

  if (child.draw)
    child.draw(ctx, child.ratio);
}

function renderStage(stage, ctx, onBeforeFrame, onAfterFrame) {
  var frameWidth = ctx.canvas.width;
  var frameHeight = ctx.canvas.height;

  var scaleX = frameWidth / stage._stageWidth;
  var scaleY = frameHeight / stage._stageHeight;

  var scale = Math.min(scaleX, scaleY);
  var offsetX = (frameWidth - scale * stage.stageWidth) / 2;
  var offsetY = (frameHeight - scale * stage.stageHeight) / 2;

  ctx.translate(offsetX, offsetY);
  ctx.scale(scale, scale);

  // All the visitors close over this class to do instance testing.
  var MovieClipClass = avm2.systemDomain.getClass("flash.display.MovieClip");
  var ContainerClass = avm2.systemDomain.getClass("flash.display.DisplayObjectContainer");
  var SimpleButtonClass = avm2.systemDomain.getClass("flash.display.SimpleButton");
  var InteractiveClass = avm2.systemDomain.getClass("flash.display.InteractiveObject");

  function roundForClipping(bounds) {
    var x = (Math.floor(bounds.x * scale + offsetX) - offsetX) / scale;
    var y = (Math.floor(bounds.y * scale + offsetY) - offsetY) / scale;
    var x2 = (Math.ceil((bounds.x + bounds.width) * scale + offsetX) - offsetX) / scale;
    var y2 = (Math.ceil((bounds.y + bounds.height) * scale + offsetY) - offsetY) / scale;
    return { x: x, y: y, width: x2 - x, height: y2 - y };
  }

  function visitContainer(container, visitor) {
    var children = container._children;
    var dirty = false;

    visitor.childrenStart(container);

    for (var i = 0, n = children.length; i < n; i++) {
      var child = children[i];
      if (child && child._visible) {
        var isContainer = ContainerClass.isInstanceOf(child) ||
                          SimpleButtonClass.isInstanceOf(child);

        visitor.visit(child, isContainer);

        if (isContainer)
          visitContainer(child, visitor);

        if (child._dirtyArea)
          dirty = true;
      }
    }

    visitor.childrenEnd(container);

    if (dirty)
      container._bounds = null;
  }

  function PreVisitor(ctx) {
    this.ctx = ctx;
    this.enterFrameEvt = new flash.events.Event("enterFrame");
  }
  PreVisitor.prototype = {
    childrenStart: function() {},
    childrenEnd: function() {},
    visit: function (child, isContainer) {
      if (MovieClipClass.isInstanceOf(child) && child.isPlaying()) {
        child._renderNextFrame();
        flushPendingScripts();
      }

      child.dispatchEvent(this.enterFrameEvt);

      if (child._refreshAS2Variables) {
        child._refreshAS2Variables();
      }

      if (child._dirtyArea) {
        var b1 = roundForClipping(child._dirtyArea);
        var b2 = roundForClipping(child.getBounds());
        this.ctx.rect(b1.x, b1.y, b1.width, b1.height);
        this.ctx.rect(b2.x, b2.y, b2.width, b2.height);
      } else if (child._graphics && (child._graphics._revision !== child._revision)) {
        child._revision = child._graphics._revision;
        child._markAsDirty();
        // redraw entire stage till we calculate bounding boxes for dynamic graphics
        this.ctx.rect(0, 0, frameWidth, frameHeight);
      }
    }
  };

  function MouseVisitor() {
    this.interactiveParent = stage;
    this.parentsStack = [stage];
    this.mouseOverEvt = new flash.events.Event("mouseOver");
    this.mouseOutEvt = new flash.events.Event("mouseOut");
    this.mouseMoveEvt = new flash.events.Event("mouseMove");

    this.mouseOverTargets = [stage._mouseOver ? stage : null];
    this.oldMouseOverTargets = [];
    if (stage._mouseJustLeft) {
      this.oldMouseOverTargets.push(stage);
      stage._mouseJustLeft = false;
    }
  }
  MouseVisitor.prototype = {
    childrenStart: function() {},
    childrenEnd: function(container) {
      this.interactiveParent = this.parentsStack.pop();

      if (container === stage) {
        var newMouseOverTargets = [];
        var oldMouseOverTargets = this.oldMouseOverTargets;
        var target = this.mouseOverTargets.pop();
        stage._clickTarget = target;
        if (target) {
          // removing duplicates from this.mouseOverTargets and removing symbols
          // from this.oldMouseOverTargets if they are in "mouseOver" state
          do {
            var i = oldMouseOverTargets.indexOf(target);
            if (i >= 0) {
              oldMouseOverTargets[i] = null;
            }
            if (!target._mouseOver) {
              newMouseOverTargets.push(target);
            }
            var prev = target;
            do {
              target = this.mouseOverTargets.pop();
            } while (prev === target);
          } while(target);
        }
        // generating mouseOut events for non-processed oldMouseOverTargets
        while (oldMouseOverTargets.length > 0) {
          target = oldMouseOverTargets.pop();
          if (!target) {
            continue;
          }
          target._mouseOver = false;
          target.dispatchEvent(this.mouseOutEvt);

          if (TRACE_SYMBOLS_INFO && target._control) {
            delete target._control.dataset.mouseOver;
          }
        }
        // generating mouseOver events for new "mouseOver" symbols
        while (newMouseOverTargets.length > 0) {
          target = newMouseOverTargets.pop();
          target._mouseOver = true;
          target.dispatchEvent(this.mouseOverEvt);

          if (TRACE_SYMBOLS_INFO && target._control) {
            target._control.dataset.mouseOver = true;
          }
        }
      }
    },
    visit: function (child, isContainer) {
      var interactiveParent = this.interactiveParent;
      if (InteractiveClass.isInstanceOf(child) && child._mouseEnabled &&
          interactiveParent._mouseChildren) {
        interactiveParent = child;
      }

      if (child._mouseOver) {
        // remembering all symbols in "mouseOver" state
        this.oldMouseOverTargets.push(child);
      }

      var mouseMoved = false;

      var parent = child._parent;
      var pt = { x: parent._mouseX, y: parent._mouseY };
      child._applyCurrentInverseTransform(pt, parent);

      if (pt.x !== child._mouseX || pt.y !== child._mouseY) {
        mouseMoved = true;
      }

      child._mouseX = pt.x;
      child._mouseY = pt.y;

      var hitArea = child._hitArea || child;
      if (stage._mouseOver &&
          hitArea._hitTest(true, stage._mouseX, stage._mouseY, true, null, true)) {
        if (mouseMoved) {
          interactiveParent.dispatchEvent(this.mouseMoveEvt);
        }
        // saving the current interactive symbol and whole stack of
        // its parents (including duplicates)
        this.mouseOverTargets = this.parentsStack.concat([interactiveParent]);
      }

      if (isContainer) {
        this.parentsStack.push(this.interactiveParent);
        this.interactiveParent = interactiveParent;
      }
    }
  };

  function PostVisitor() {
    this.exitFrameEvt = new flash.events.Event("exitFrame");
  }
  PostVisitor.prototype = {
    childrenStart: function() {},
    childrenEnd: function() {},
    visit: function (child) {
      //if (MovieClipClass.isInstanceOf(child))
        child.dispatchEvent(this.exitFrameEvt);
    }
  };

  function RenderVisitor(ctx) {
    this.ctx = ctx;
    this.depth = 0;

    this.clipDepth = null;
    this.clipStack = null;
  }
  RenderVisitor.prototype = {
    childrenStart: function(parent) {
      if (this.depth == 0) {
        var ctx = this.ctx;

        ctx.save();

        ctx.clip();

        ctx.clearRect(0, 0, frameWidth, frameHeight);

        ctx.mozFillRule = 'evenodd';

        stage._canvasState = {
          canvas: ctx.canvas,
          scale: scale,
          offsetX: offsetX,
          offsetY: offsetY
        };
      }
      this.depth++;

      if (this.clipDepth && this.clipDepth.length > 0) {
        // saving the parent clipping state
        this.clipStack = {
          depth: this.depth,
          clip: this.clipDepth,
          next: this.clipStack
        };
        this.clipDepth = null;
      }
    },
    childrenEnd: function(parent) {
      if (this.clipDepth) {
        // removing existing clippings
        while (this.clipDepth.length > 0) {
          this.clipDepth.pop();
          this.ctx.restore();
        }
        this.clipDepth = null;
      }
      // checking if we saved the parent clipping state
      if (this.clipStack && this.clipStack.depth === this.depth) {
        this.clipDepth = this.clipStack.clip;
        this.clipStack = this.clipStack.next;
      }

      this.depth--;
      this.ctx.restore();
    },
    visit: function (child, isContainer) {
      var ctx = this.ctx;

      var clippingMask = false;
      // removing clipping if the required character depth is achived
      while (this.clipDepth && this.clipDepth.length > 0 &&
          child._depth > this.clipDepth[0]) {
        this.clipDepth.shift();
        ctx.restore();
      }
      // TODO: handle container as a clipping mask
      if (child._clipDepth && !isContainer) {
        if (!this.clipDepth) {
          this.clipDepth = [];
        }
        clippingMask = true;
        // saving clipping until certain character depth
        this.clipDepth.unshift(child._clipDepth);
        ctx.save();
      }

      ctx.save();

      renderDisplayObject(child, ctx, child._currentTransform, child._cxform, clippingMask);

      if (!isContainer) {
        // letting the container to restore transforms after all children are painted
        ctx.restore();
      }

      if (clippingMask) {
        ctx.clip();
      }

      if (stage._showRedrawRegions && child._dirtyArea) {
        var b = child._dirtyArea;
        ctx.save();
        ctx.strokeStyle = '#f00';
        ctx.lineWidth = 1;
        ctx.strokeRect(b.x, b.y, b.width, b.height);
        ctx.restore();
      }

      child._dirtyArea = null;
    }
  };

  var frameTime = 0;
  var maxDelay = 1000 / stage.frameRate;
  var nextRenderAt = Date.now();

  var requestAnimationFrame = window.requestAnimationFrame ||
                              window.mozRequestAnimationFrame ||
                              window.webkitRequestAnimationFrame ||
                              window.oRequestAnimationFrame ||
                              window.msRequestAnimationFrame ||
                              window.setTimeout;

  var renderDummyBalls;

  var dummyBalls;
  if (typeof FirefoxCom !== 'undefined' &&
    FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false})) {
    var radius = 10;
    var speed = 1;
    dummyBalls = [];
    for (var i = 0; i < 10; i++) {
      dummyBalls.push({
        position: {
          x: radius + Math.random() * ((ctx.canvas.width - 2 * radius) / scale),
          y: radius + Math.random() * ((ctx.canvas.height - 2 * radius) / scale)
        },
        velocity: {x: speed * (Math.random() - 0.5), y: speed * (Math.random() - 0.5)}
      });
    }
    ctx.fillStyle = "black";
    ctx.lineWidth = 2;
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    renderDummyBalls = function () {
      ctx.fillStyle = "black";
      ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      ctx.strokeStyle = "green";
      dummyBalls.forEach(function (ball) {
        var position = ball.position;
        var velocity = ball.velocity;
        ctx.beginPath();
        ctx.arc(position.x, position.y, radius, 0, Math.PI * 2, true);
        ctx.stroke();
        var x = (position.x + velocity.x);
        var y = (position.y + velocity.y);
        if (x < radius || x > ctx.canvas.width / scale - radius) {
          velocity.x *= -1;
        }
        if (y < radius || y > ctx.canvas.height / scale - radius) {
          velocity.y *= -1;
        }
        position.x += velocity.x;
        position.y += velocity.y;
      });
    }
  }

  function flushPendingScripts() {
    while (stage._pendingScripts.length > 0) {
      var fn = stage._pendingScripts.shift();
      fn();
    }
  }

  console.timeEnd("Initialize Renderer");
  console.timeEnd("Total");

  (function draw() {
    var now = Date.now();
    var renderFrame;
    var renderFrame = now >= nextRenderAt;
    if (renderFrame && onBeforeFrame) {
      var e = { cancel: false };
      onBeforeFrame(e);
      renderFrame = !e.cancel;
    }
    if (renderFrame) {
      frameTime = now;
      nextRenderAt = frameTime + maxDelay;

      if (renderDummyBalls) {
        renderDummyBalls();
      } else {
        visitContainer(stage, new MouseVisitor());
        flushPendingScripts();
        ctx.beginPath();
        visitContainer(stage, new PreVisitor(ctx));
        visitContainer(stage, new RenderVisitor(ctx));
        visitContainer(stage, new PostVisitor());
        stage._syncCursor();

        if (onAfterFrame) {
          onAfterFrame();
        }
      }
    }
    requestAnimationFrame(draw);
  })();
}
<commit_msg>Limits amount of pending scripts during rendering<commit_after>function renderDisplayObject(child, ctx, transform, cxform, clip) {
  var m = transform;
  if (m.a * m.d == m.b * m.c) {
    // Workaround for bug 844184 -- the object is invisible
    ctx.closePath();
    ctx.rect(0, 0, 0, 0);
    ctx.clip();
  } else {
    ctx.transform(m.a, m.b, m.c, m.d, m.tx, m.ty);
  }

  if (cxform) {
    // We only support alpha channel transformation for now
    ctx.globalAlpha = (ctx.globalAlpha * cxform.alphaMultiplier + cxform.alphaOffset) / 256;
  }
  if (child._alpha !== 1) {
    ctx.globalAlpha *= child._alpha;
  }

  if (child._graphics) {
    var graphics = child._graphics;

    if (graphics._bitmap) {
      ctx.translate(child._bbox.left, child._bbox.top);
      ctx.drawImage(graphics._bitmap, 0, 0);
    } else {
      var scale = graphics._scale;
      if (scale !== 1)
        ctx.scale(scale, scale);

      var subpaths = graphics._subpaths;
      for (var j = 0, o = subpaths.length; j < o; j++) {
        var pathTracker = subpaths[j], path = pathTracker.target;
        if (clip) {
          ctx.beginPath();
          ctx.__draw__(path);
          ctx.closePath();
        } else {
          if (path.fillStyle) {
            ctx.fillStyle = path.fillStyle;
            if (path.fillTransform) {
              var m = path.fillTransform;
              ctx.beginPath();
              ctx.__draw__(path);
              ctx.save();
              ctx.transform(m.a, m.b, m.c, m.d, m.tx, m.ty);
              ctx.fill();
              ctx.restore();
            } else {
              ctx.fill(path);
            }
          }
          if (path.strokeStyle) {
            ctx.strokeStyle = path.strokeStyle;
            var drawingStyles = pathTracker.drawingStyles;
            for (var prop in drawingStyles)
              ctx[prop] = drawingStyles[prop];
            ctx.stroke(path);
          }
        }
      }
    }
  }

  if (child.draw)
    child.draw(ctx, child.ratio);
}

function renderStage(stage, ctx, onBeforeFrame, onAfterFrame) {
  var frameWidth = ctx.canvas.width;
  var frameHeight = ctx.canvas.height;

  var scaleX = frameWidth / stage._stageWidth;
  var scaleY = frameHeight / stage._stageHeight;

  var scale = Math.min(scaleX, scaleY);
  var offsetX = (frameWidth - scale * stage.stageWidth) / 2;
  var offsetY = (frameHeight - scale * stage.stageHeight) / 2;

  ctx.translate(offsetX, offsetY);
  ctx.scale(scale, scale);

  // All the visitors close over this class to do instance testing.
  var MovieClipClass = avm2.systemDomain.getClass("flash.display.MovieClip");
  var ContainerClass = avm2.systemDomain.getClass("flash.display.DisplayObjectContainer");
  var SimpleButtonClass = avm2.systemDomain.getClass("flash.display.SimpleButton");
  var InteractiveClass = avm2.systemDomain.getClass("flash.display.InteractiveObject");

  function roundForClipping(bounds) {
    var x = (Math.floor(bounds.x * scale + offsetX) - offsetX) / scale;
    var y = (Math.floor(bounds.y * scale + offsetY) - offsetY) / scale;
    var x2 = (Math.ceil((bounds.x + bounds.width) * scale + offsetX) - offsetX) / scale;
    var y2 = (Math.ceil((bounds.y + bounds.height) * scale + offsetY) - offsetY) / scale;
    return { x: x, y: y, width: x2 - x, height: y2 - y };
  }

  function visitContainer(container, visitor) {
    var children = container._children;
    var dirty = false;

    visitor.childrenStart(container);

    for (var i = 0, n = children.length; i < n; i++) {
      var child = children[i];
      if (child && child._visible) {
        var isContainer = ContainerClass.isInstanceOf(child) ||
                          SimpleButtonClass.isInstanceOf(child);

        visitor.visit(child, isContainer);

        if (isContainer)
          visitContainer(child, visitor);

        if (child._dirtyArea)
          dirty = true;
      }
    }

    visitor.childrenEnd(container);

    if (dirty)
      container._bounds = null;
  }

  function PreVisitor(ctx) {
    this.ctx = ctx;
    this.enterFrameEvt = new flash.events.Event("enterFrame");
  }
  PreVisitor.prototype = {
    childrenStart: function() {},
    childrenEnd: function() {},
    visit: function (child, isContainer) {
      if (MovieClipClass.isInstanceOf(child) && child.isPlaying()) {
        child._renderNextFrame();
        flushPendingScripts();
      }

      child.dispatchEvent(this.enterFrameEvt);

      if (child._refreshAS2Variables) {
        child._refreshAS2Variables();
      }

      if (child._dirtyArea) {
        var b1 = roundForClipping(child._dirtyArea);
        var b2 = roundForClipping(child.getBounds());
        this.ctx.rect(b1.x, b1.y, b1.width, b1.height);
        this.ctx.rect(b2.x, b2.y, b2.width, b2.height);
      } else if (child._graphics && (child._graphics._revision !== child._revision)) {
        child._revision = child._graphics._revision;
        child._markAsDirty();
        // redraw entire stage till we calculate bounding boxes for dynamic graphics
        this.ctx.rect(0, 0, frameWidth, frameHeight);
      }
    }
  };

  function MouseVisitor() {
    this.interactiveParent = stage;
    this.parentsStack = [stage];
    this.mouseOverEvt = new flash.events.Event("mouseOver");
    this.mouseOutEvt = new flash.events.Event("mouseOut");
    this.mouseMoveEvt = new flash.events.Event("mouseMove");

    this.mouseOverTargets = [stage._mouseOver ? stage : null];
    this.oldMouseOverTargets = [];
    if (stage._mouseJustLeft) {
      this.oldMouseOverTargets.push(stage);
      stage._mouseJustLeft = false;
    }
  }
  MouseVisitor.prototype = {
    childrenStart: function() {},
    childrenEnd: function(container) {
      this.interactiveParent = this.parentsStack.pop();

      if (container === stage) {
        var newMouseOverTargets = [];
        var oldMouseOverTargets = this.oldMouseOverTargets;
        var target = this.mouseOverTargets.pop();
        stage._clickTarget = target;
        if (target) {
          // removing duplicates from this.mouseOverTargets and removing symbols
          // from this.oldMouseOverTargets if they are in "mouseOver" state
          do {
            var i = oldMouseOverTargets.indexOf(target);
            if (i >= 0) {
              oldMouseOverTargets[i] = null;
            }
            if (!target._mouseOver) {
              newMouseOverTargets.push(target);
            }
            var prev = target;
            do {
              target = this.mouseOverTargets.pop();
            } while (prev === target);
          } while(target);
        }
        // generating mouseOut events for non-processed oldMouseOverTargets
        while (oldMouseOverTargets.length > 0) {
          target = oldMouseOverTargets.pop();
          if (!target) {
            continue;
          }
          target._mouseOver = false;
          target.dispatchEvent(this.mouseOutEvt);

          if (TRACE_SYMBOLS_INFO && target._control) {
            delete target._control.dataset.mouseOver;
          }
        }
        // generating mouseOver events for new "mouseOver" symbols
        while (newMouseOverTargets.length > 0) {
          target = newMouseOverTargets.pop();
          target._mouseOver = true;
          target.dispatchEvent(this.mouseOverEvt);

          if (TRACE_SYMBOLS_INFO && target._control) {
            target._control.dataset.mouseOver = true;
          }
        }
      }
    },
    visit: function (child, isContainer) {
      var interactiveParent = this.interactiveParent;
      if (InteractiveClass.isInstanceOf(child) && child._mouseEnabled &&
          interactiveParent._mouseChildren) {
        interactiveParent = child;
      }

      if (child._mouseOver) {
        // remembering all symbols in "mouseOver" state
        this.oldMouseOverTargets.push(child);
      }

      var mouseMoved = false;

      var parent = child._parent;
      var pt = { x: parent._mouseX, y: parent._mouseY };
      child._applyCurrentInverseTransform(pt, parent);

      if (pt.x !== child._mouseX || pt.y !== child._mouseY) {
        mouseMoved = true;
      }

      child._mouseX = pt.x;
      child._mouseY = pt.y;

      var hitArea = child._hitArea || child;
      if (stage._mouseOver &&
          hitArea._hitTest(true, stage._mouseX, stage._mouseY, true, null, true)) {
        if (mouseMoved) {
          interactiveParent.dispatchEvent(this.mouseMoveEvt);
        }
        // saving the current interactive symbol and whole stack of
        // its parents (including duplicates)
        this.mouseOverTargets = this.parentsStack.concat([interactiveParent]);
      }

      if (isContainer) {
        this.parentsStack.push(this.interactiveParent);
        this.interactiveParent = interactiveParent;
      }
    }
  };

  function PostVisitor() {
    this.exitFrameEvt = new flash.events.Event("exitFrame");
  }
  PostVisitor.prototype = {
    childrenStart: function() {},
    childrenEnd: function() {},
    visit: function (child) {
      //if (MovieClipClass.isInstanceOf(child))
        child.dispatchEvent(this.exitFrameEvt);
    }
  };

  function RenderVisitor(ctx) {
    this.ctx = ctx;
    this.depth = 0;

    this.clipDepth = null;
    this.clipStack = null;
  }
  RenderVisitor.prototype = {
    childrenStart: function(parent) {
      if (this.depth == 0) {
        var ctx = this.ctx;

        ctx.save();

        ctx.clip();

        ctx.clearRect(0, 0, frameWidth, frameHeight);

        ctx.mozFillRule = 'evenodd';

        stage._canvasState = {
          canvas: ctx.canvas,
          scale: scale,
          offsetX: offsetX,
          offsetY: offsetY
        };
      }
      this.depth++;

      if (this.clipDepth && this.clipDepth.length > 0) {
        // saving the parent clipping state
        this.clipStack = {
          depth: this.depth,
          clip: this.clipDepth,
          next: this.clipStack
        };
        this.clipDepth = null;
      }
    },
    childrenEnd: function(parent) {
      if (this.clipDepth) {
        // removing existing clippings
        while (this.clipDepth.length > 0) {
          this.clipDepth.pop();
          this.ctx.restore();
        }
        this.clipDepth = null;
      }
      // checking if we saved the parent clipping state
      if (this.clipStack && this.clipStack.depth === this.depth) {
        this.clipDepth = this.clipStack.clip;
        this.clipStack = this.clipStack.next;
      }

      this.depth--;
      this.ctx.restore();
    },
    visit: function (child, isContainer) {
      var ctx = this.ctx;

      var clippingMask = false;
      // removing clipping if the required character depth is achived
      while (this.clipDepth && this.clipDepth.length > 0 &&
          child._depth > this.clipDepth[0]) {
        this.clipDepth.shift();
        ctx.restore();
      }
      // TODO: handle container as a clipping mask
      if (child._clipDepth && !isContainer) {
        if (!this.clipDepth) {
          this.clipDepth = [];
        }
        clippingMask = true;
        // saving clipping until certain character depth
        this.clipDepth.unshift(child._clipDepth);
        ctx.save();
      }

      ctx.save();

      renderDisplayObject(child, ctx, child._currentTransform, child._cxform, clippingMask);

      if (!isContainer) {
        // letting the container to restore transforms after all children are painted
        ctx.restore();
      }

      if (clippingMask) {
        ctx.clip();
      }

      if (stage._showRedrawRegions && child._dirtyArea) {
        var b = child._dirtyArea;
        ctx.save();
        ctx.strokeStyle = '#f00';
        ctx.lineWidth = 1;
        ctx.strokeRect(b.x, b.y, b.width, b.height);
        ctx.restore();
      }

      child._dirtyArea = null;
    }
  };

  var frameTime = 0;
  var maxDelay = 1000 / stage.frameRate;
  var nextRenderAt = Date.now();

  var requestAnimationFrame = window.requestAnimationFrame ||
                              window.mozRequestAnimationFrame ||
                              window.webkitRequestAnimationFrame ||
                              window.oRequestAnimationFrame ||
                              window.msRequestAnimationFrame ||
                              window.setTimeout;

  var renderDummyBalls;

  var dummyBalls;
  if (typeof FirefoxCom !== 'undefined' &&
    FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false})) {
    var radius = 10;
    var speed = 1;
    dummyBalls = [];
    for (var i = 0; i < 10; i++) {
      dummyBalls.push({
        position: {
          x: radius + Math.random() * ((ctx.canvas.width - 2 * radius) / scale),
          y: radius + Math.random() * ((ctx.canvas.height - 2 * radius) / scale)
        },
        velocity: {x: speed * (Math.random() - 0.5), y: speed * (Math.random() - 0.5)}
      });
    }
    ctx.fillStyle = "black";
    ctx.lineWidth = 2;
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    renderDummyBalls = function () {
      ctx.fillStyle = "black";
      ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      ctx.strokeStyle = "green";
      dummyBalls.forEach(function (ball) {
        var position = ball.position;
        var velocity = ball.velocity;
        ctx.beginPath();
        ctx.arc(position.x, position.y, radius, 0, Math.PI * 2, true);
        ctx.stroke();
        var x = (position.x + velocity.x);
        var y = (position.y + velocity.y);
        if (x < radius || x > ctx.canvas.width / scale - radius) {
          velocity.x *= -1;
        }
        if (y < radius || y > ctx.canvas.height / scale - radius) {
          velocity.y *= -1;
        }
        position.x += velocity.x;
        position.y += velocity.y;
      });
    }
  }

  function flushPendingScripts() {
    var MAX_PENDING_SCRIPTS_EXECUTED = 100;
    var executed = 0;
    while (stage._pendingScripts.length > 0) {
      var fn = stage._pendingScripts.shift();
      fn();
      if (++executed > MAX_PENDING_SCRIPTS_EXECUTED) {
        console.error('ERROR: pending script limit was reached');
        stage._pendingScripts = [];
        return;
      }
    }
  }

  console.timeEnd("Initialize Renderer");
  console.timeEnd("Total");

  (function draw() {
    var now = Date.now();
    var renderFrame;
    var renderFrame = now >= nextRenderAt;
    if (renderFrame && onBeforeFrame) {
      var e = { cancel: false };
      onBeforeFrame(e);
      renderFrame = !e.cancel;
    }
    if (renderFrame) {
      frameTime = now;
      nextRenderAt = frameTime + maxDelay;

      if (renderDummyBalls) {
        renderDummyBalls();
      } else {
        visitContainer(stage, new MouseVisitor());
        flushPendingScripts();
        ctx.beginPath();
        visitContainer(stage, new PreVisitor(ctx));
        visitContainer(stage, new RenderVisitor(ctx));
        visitContainer(stage, new PostVisitor());
        stage._syncCursor();

        if (onAfterFrame) {
          onAfterFrame();
        }
      }
    }
    requestAnimationFrame(draw);
  })();
}
<|endoftext|>