<commit_before>// Copyright (c) 2012-2017, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var child_process = require('child_process'),
    temp = require('temp'),
    httpProxy = require('http-proxy'),
    Promise = require('promise'), // jshint ignore:line
    quote = require('shell-quote'),
    _ = require('underscore-node'),
    logger = require('./logger').logger,
    CompilationEnvironment = require('./compilation-env').CompilationEnvironment;

temp.track();

var oneTimeInit = false;
function initialise(gccProps, compilerEnv) {
    if (oneTimeInit) return;
    oneTimeInit = true;
    var tempDirCleanupSecs = gccProps("tempDirCleanupSecs", 600);
    logger.info("Cleaning temp dirs every " + tempDirCleanupSecs + " secs");
    setInterval(function () {
        if (compilerEnv.isBusy()) {
            logger.warn("Skipping temporary file clean up as compiler environment is busy");
            return;
        }
        temp.cleanup(function (err, stats) {
            if (err) logger.error("Error cleaning directories: ", err);
            if (stats) logger.debug("Directory cleanup stats:", stats);
        });
    }, tempDirCleanupSecs * 1000);
}

function CompileHandler(gccProps, compilerProps) {
    this.compilersById = {};
    this.compilerEnv = new CompilationEnvironment(gccProps, compilerProps);
    initialise(gccProps, this.compilerEnv);
    this.factories = {};

    this.create = function (compiler) {
        var type = compiler.compilerType || "default";
        if (this.factories[type] === undefined) {
            var path = './compilers/' + type;
            logger.info("Loading compiler from", path);
            this.factories[type] = require(path);
        }
        return this.factories[type](compiler, this.compilerEnv);
    };

    this.setCompilers = function (compilers) {
        var initPromises = _.map(compilers, this.create, this);
        return Promise.all(initPromises)
            .then(function (compilers) {
                return _.filter(compilers, _.identity);
            })
            .then(_.bind(function (compilers) {
                _.each(compilers, function (compiler) {
                    this.compilersById[compiler.compiler.id] = compiler;
                }, this);
                return _.map(compilers, function (compiler) {
                    return compiler.getInfo();
                });
            }, this)).catch(function (err) {
                logger.error(err);
            });
    };
    var proxy = httpProxy.createProxyServer({});
    var textBanner = compilerProps('textBanner');

    this.handler = _.bind(function compile(req, res, next) {
        var source, options, filters, compiler;
        if (req.is('json')) {
            // JSON-style request
            compiler = this.compilersById[req.compiler || req.body.compiler];
            if (!compiler) return next();
            source = req.body.source;
            options = req.body.options;
            filters = req.body.filters || compiler.getDefaultFilters();
        } else {
            // API-style
            compiler = this.compilersById[req.compiler];
            if (!compiler) return next();
            source = req.body;
            options = req.query.options;
            // By default we get the default filters.
            filters = compiler.getDefaultFilters();
            // If specified exactly, we'll take that with ?filters=a,b,c
            if (req.query.filters) {
                filters = _.object(_.map(req.query.filters.split(","), function (filter) {
                    return [filter, true];
                }));
            }
            // Add a filter. ?addFilters=binary
            _.each((req.query.addFilters || "").split(","), function (filter) {
                filters[filter] = true;
            });
            // Remove a filter. ?removeFilter=intel
            _.each((req.query.removeFilters || "").split(","), function (filter) {
                delete filters[filter];
            });
        }
        var remote = compiler.getRemote();
        if (remote) {
            proxy.web(req, res, {target: remote}, function (e) {
                logger.error("Proxy error: ", e);
                next(e);
            });
            return;
        }

        if (source === undefined) {
            return next(new Error("Bad request"));
        }
        options = _.chain(quote.parse(options || '')
            .map(function (x) {
                if (typeof(x) == "string") return x;
                return x.pattern;
            }))
            .filter(_.identity)
            .value();
        function textify(array) {
            return _.pluck(array || [], 'text').join("\n");
        }

        compiler.compile(source, options, filters).then(
            function (result) {
                if (req.accepts(['text', 'json']) === 'json') {
                    res.set('Content-Type', 'application/json');
                    res.end(JSON.stringify(result));
                } else {
                    res.set('Content-Type', 'text/plain');
                    try {
                        if (!_.isEmpty(textBanner)) res.write('# ' + textBanner + "\n");
                        res.write(textify(result.asm));
                        if (result.code !== 0) res.write("\n# Compiler exited with result code " + result.code);
                        if (!_.isEmpty(result.stdout)) res.write("\nStandard out:\n" + textify(result.stdout));
                        if (!_.isEmpty(result.stderr)) res.write("\nStandard error:\n" + textify(result.stderr));
                    } catch (ex) {
                        re.write("Error handling request: " + ex);
                    }
                    res.end('\n');
                }
            },
            function (error) {
                logger.error("Error: " + error);
                if (typeof(error) !== "string") {
                    error = "Internal GCC explorer error: " + error.toString();
                }
                res.end(JSON.stringify({code: -1, stderr: [{text: error}]}));
            }
        );
    }, this);
}

module.exports = {
    CompileHandler: CompileHandler
};
<commit_msg>Fix proxying of requests. Fixes #239<commit_after>// Copyright (c) 2012-2017, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var child_process = require('child_process'),
    temp = require('temp'),
    httpProxy = require('http-proxy'),
    Promise = require('promise'), // jshint ignore:line
    quote = require('shell-quote'),
    _ = require('underscore-node'),
    logger = require('./logger').logger,
    CompilationEnvironment = require('./compilation-env').CompilationEnvironment;

temp.track();

var oneTimeInit = false;
function initialise(gccProps, compilerEnv) {
    if (oneTimeInit) return;
    oneTimeInit = true;
    var tempDirCleanupSecs = gccProps("tempDirCleanupSecs", 600);
    logger.info("Cleaning temp dirs every " + tempDirCleanupSecs + " secs");
    setInterval(function () {
        if (compilerEnv.isBusy()) {
            logger.warn("Skipping temporary file clean up as compiler environment is busy");
            return;
        }
        temp.cleanup(function (err, stats) {
            if (err) logger.error("Error cleaning directories: ", err);
            if (stats) logger.debug("Directory cleanup stats:", stats);
        });
    }, tempDirCleanupSecs * 1000);
}

function CompileHandler(gccProps, compilerProps) {
    this.compilersById = {};
    this.compilerEnv = new CompilationEnvironment(gccProps, compilerProps);
    initialise(gccProps, this.compilerEnv);
    this.factories = {};

    this.create = function (compiler) {
        var type = compiler.compilerType || "default";
        if (this.factories[type] === undefined) {
            var path = './compilers/' + type;
            logger.info("Loading compiler from", path);
            this.factories[type] = require(path);
        }
        return this.factories[type](compiler, this.compilerEnv);
    };

    this.setCompilers = function (compilers) {
        var initPromises = _.map(compilers, this.create, this);
        return Promise.all(initPromises)
            .then(function (compilers) {
                return _.filter(compilers, _.identity);
            })
            .then(_.bind(function (compilers) {
                _.each(compilers, function (compiler) {
                    this.compilersById[compiler.compiler.id] = compiler;
                }, this);
                return _.map(compilers, function (compiler) {
                    return compiler.getInfo();
                });
            }, this)).catch(function (err) {
                logger.error(err);
            });
    };
    var proxy = httpProxy.createProxyServer({});
    var textBanner = compilerProps('textBanner');

    this.handler = _.bind(function compile(req, res, next) {
        var source, options, filters, compiler;
        if (req.is('json')) {
            // JSON-style request
            compiler = this.compilersById[req.compiler || req.body.compiler];
            if (!compiler) return next();
            source = req.body.source;
            options = req.body.options;
            filters = req.body.filters || compiler.getDefaultFilters();
        } else {
            // API-style
            compiler = this.compilersById[req.compiler];
            if (!compiler) return next();
            source = req.body;
            options = req.query.options;
            // By default we get the default filters.
            filters = compiler.getDefaultFilters();
            // If specified exactly, we'll take that with ?filters=a,b,c
            if (req.query.filters) {
                filters = _.object(_.map(req.query.filters.split(","), function (filter) {
                    return [filter, true];
                }));
            }
            // Add a filter. ?addFilters=binary
            _.each((req.query.addFilters || "").split(","), function (filter) {
                filters[filter] = true;
            });
            // Remove a filter. ?removeFilter=intel
            _.each((req.query.removeFilters || "").split(","), function (filter) {
                delete filters[filter];
            });
        }
        var remote = compiler.getRemote();
        if (remote) {
            req.url = req.originalUrl;  // Undo any routing that was done to get here (i.e. /api/* path has been removed)
            proxy.web(req, res, {target: remote}, function (e) {
                logger.error("Proxy error: ", e);
                next(e);
            });
            return;
        }

        if (source === undefined) {
            return next(new Error("Bad request"));
        }
        options = _.chain(quote.parse(options || '')
            .map(function (x) {
                if (typeof(x) == "string") return x;
                return x.pattern;
            }))
            .filter(_.identity)
            .value();
        function textify(array) {
            return _.pluck(array || [], 'text').join("\n");
        }

        compiler.compile(source, options, filters).then(
            function (result) {
                if (req.accepts(['text', 'json']) === 'json') {
                    res.set('Content-Type', 'application/json');
                    res.end(JSON.stringify(result));
                } else {
                    res.set('Content-Type', 'text/plain');
                    try {
                        if (!_.isEmpty(textBanner)) res.write('# ' + textBanner + "\n");
                        res.write(textify(result.asm));
                        if (result.code !== 0) res.write("\n# Compiler exited with result code " + result.code);
                        if (!_.isEmpty(result.stdout)) res.write("\nStandard out:\n" + textify(result.stdout));
                        if (!_.isEmpty(result.stderr)) res.write("\nStandard error:\n" + textify(result.stderr));
                    } catch (ex) {
                        re.write("Error handling request: " + ex);
                    }
                    res.end('\n');
                }
            },
            function (error) {
                logger.error("Error: " + error);
                if (typeof(error) !== "string") {
                    error = "Internal GCC explorer error: " + error.toString();
                }
                res.end(JSON.stringify({code: -1, stderr: [{text: error}]}));
            }
        );
    }, this);
}

module.exports = {
    CompileHandler: CompileHandler
};
<|endoftext|>


<commit_before>/* global Blob,URL,requestAnimationFrame */
import React from 'react';
import Color from 'color';
import uuid from 'uuid';
import hterm from '../hterm';
import Component from '../component';
import getColorList from '../utils/colors';
import notify from '../utils/notify';

export default class Term extends Component {

  constructor(props) {
    super(props);
    this.handleWheel = this.handleWheel.bind(this);
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseUp = this.handleMouseUp.bind(this);
    this.handleScrollEnter = this.handleScrollEnter.bind(this);
    this.handleScrollLeave = this.handleScrollLeave.bind(this);
    this.onHyperCaret = this.onHyperCaret.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    props.ref_(this);
  }

  componentDidMount() {
    const {props} = this;
    this.term = props.term || new hterm.Terminal(uuid.v4());
    this.term.onHyperCaret(this.hyperCaret);

    // the first term that's created has unknown size
    // subsequent new tabs have size
    if (props.cols && props.rows) {
      this.term.realizeSize_(props.cols, props.rows);
    }

    const prefs = this.term.getPrefs();

    prefs.set('font-family', props.fontFamily);
    prefs.set('font-size', props.fontSize);
    prefs.set('font-smoothing', props.fontSmoothing);
    prefs.set('cursor-color', this.validateColor(props.cursorColor, 'rgba(255,255,255,0.5)'));
    prefs.set('enable-clipboard-notice', false);
    prefs.set('foreground-color', props.foregroundColor);

    // hterm.ScrollPort.prototype.setBackgroundColor is overriden
    // to make hterm's background transparent. we still need to set
    // background-color for proper text rendering
    prefs.set('background-color', props.backgroundColor);
    prefs.set('color-palette-overrides', getColorList(props.colors));
    prefs.set('user-css', this.getStylesheet(props.customCSS));
    prefs.set('scrollbar-visible', false);
    prefs.set('receive-encoding', 'raw');
    prefs.set('send-encoding', 'raw');
    prefs.set('alt-sends-what', 'browser-key');

    if (props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }

    this.term.onTerminalReady = () => {
      const io = this.term.io.push();
      io.onVTKeystroke = io.sendString = props.onData;
      io.onTerminalResize = (cols, rows) => {
        if (cols !== this.props.cols || rows !== this.props.rows) {
          props.onResize(cols, rows);
        }
      };

      this.term.modifierKeys = props.modifierKeys;
      // this.term.CursorNode_ is available at this point.
      this.term.setCursorShape(props.cursorShape);

      // emit onTitle event when hterm instance
      // wants to set the title of its tab
      this.term.setWindowTitle = props.onTitle;
      this.term.focusHyperCaret();
    };
    this.term.decorate(this.termRef);
    this.term.installKeyboard();
    if (this.props.onTerminal) {
      this.props.onTerminal(this.term);
    }

    const iframeWindow = this.getTermDocument().defaultView;
    iframeWindow.addEventListener('wheel', this.handleWheel);

    this.getScreenNode().addEventListener('mouseup', this.handleMouseUp);
  }

  handleWheel(e) {
    if (this.props.onWheel) {
      this.props.onWheel(e);
    }
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    clearTimeout(this.scrollbarsHideTimer);
    if (!this.scrollMouseEnter) {
      this.scrollbarsHideTimer = setTimeout(() => {
        prefs.set('scrollbar-visible', false);
      }, 1000);
    }
  }

  handleScrollEnter() {
    clearTimeout(this.scrollbarsHideTimer);
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    this.scrollMouseEnter = true;
  }

  handleScrollLeave() {
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', false);
    this.scrollMouseEnter = false;
  }

  handleMouseUp() {
    this.props.onActive();
    // this makes sure that we focus the hyper caret only
    // if a click on the term does not result in a selection
    // otherwise, if we focus without such check, it'd be
    // impossible to select a piece of text
    if (this.term.document_.getSelection().type !== 'Range') {
      this.term.focusHyperCaret();
    }
  }
  handleFocus() {
    // This will in turn result in `this.focus()` being
    // called, which is unecessary.
    // Should investigate if it matters.
    this.props.onActive();
  }

  handleKeyDown(e) {
    if (e.ctrlKey && e.key === 'c') {
      this.props.onURLAbort();
    }
  }

  onHyperCaret(caret) {
    this.hyperCaret = caret;
  }

  write(data) {
    // sometimes the preference set above for
    // `receive-encoding` is not known by the vt
    // before we type to write (since the preference
    // manager is asynchronous), so we force it to
    // avoid buffering
    // this fixes a race condition where sometimes
    // opening new sessions results in broken
    // output due to the term attempting to decode
    // as `utf-8` instead of `raw`
    if (this.term.vt.characterEncoding !== 'raw') {
      this.term.vt.characterEncoding = 'raw';
    }

    this.term.io.writeUTF8(data);
  }

  focus() {
    this.term.focusHyperCaret();
  }

  clear() {
    this.term.clearPreserveCursorRow();

    // If cursor is still not at the top, a command is probably
    // running and we'd like to delete the whole screen.
    // Move cursor to top
    if (this.term.getCursorRow() !== 0) {
      this.write('\x1B[0;0H\x1B[2J');
    }
  }

  moveWordLeft() {
    this.term.onVTKeystroke('\x1bb');
  }

  moveWordRight() {
    this.term.onVTKeystroke('\x1bf');
  }

  deleteWordLeft() {
    this.term.onVTKeystroke('\x1b\x7f');
  }

  deleteWordRight() {
    this.term.onVTKeystroke('\x1bd');
  }

  deleteLine() {
    this.term.onVTKeystroke('\x1bw');
  }

  moveToStart() {
    this.term.onVTKeystroke('\x01');
  }

  moveToEnd() {
    this.term.onVTKeystroke('\x05');
  }

  selectAll() {
    this.term.selectAll();
  }

  getScreenNode() {
    return this.term.scrollPort_.getScreenNode();
  }

  getTermDocument() {
    return this.term.document_;
  }

  getStylesheet(css) {
    const hyperCaret = `
      .hyper-caret {
        outline: none;
        display: inline-block;
        color: transparent;
        text-shadow: 0 0 0 black;
        font-family: ${this.props.fontFamily};
        font-size: ${this.props.fontSize}px;
      }
    `;
    let osSpecificCss = '';
    if (process.platform === 'win32') {
      osSpecificCss = `::-webkit-scrollbar {
        width: 5px;
      }
      ::-webkit-scrollbar-thumb {
        -webkit-border-radius: 10px;
        border-radius: 10px;
        background: ${this.props.borderColor};
      }
      ::-webkit-scrollbar-thumb:window-inactive {
        background: ${this.props.borderColor};
      }`;
    } else if (process.platform === 'darwin') {
      osSpecificCss = `::-webkit-scrollbar {
        width: 9px;
        background: transparent;
       }
       ::-webkit-scrollbar-thumb {
        -webkit-border-radius: 10px;
        border-radius: 10px;
        -webkit-box-shadow: inset 0 0 0 1px ${this.props.borderColor};
       }`;
    }
    return URL.createObjectURL(new Blob([`
      .cursor-node[focus="false"] {
         border-width: 1px !important;
      }
      ${hyperCaret}
      ${osSpecificCss}
      ${css}
    `], {type: 'text/css'}));
  }

  validateColor(color, alternative = 'rgb(255,255,255)') {
    try {
      return Color(color).rgbString();
    } catch (err) {
      notify(`color "${color}" is invalid`);
    }
    return alternative;
  }

  handleMouseDown(ev) {
    // we prevent losing focus when clicking the boundary
    // wrappers of the main terminal element
    if (ev.target === this.termWrapperRef ||
        ev.target === this.termRef) {
      ev.preventDefault();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.url !== nextProps.url) {
      // when the url prop changes, we make sure
      // the terminal starts or stops ignoring
      // key input so that it doesn't conflict
      // with the <webview>
      if (nextProps.url) {
        this.term.io.push();
        window.addEventListener('keydown', this.handleKeyDown);
      } else {
        window.removeEventListener('keydown', this.handleKeyDown);
        this.term.io.pop();
      }
    }

    if (!this.props.cleared && nextProps.cleared) {
      this.clear();
    }

    const prefs = this.term.getPrefs();

    if (this.props.fontSize !== nextProps.fontSize) {
      prefs.set('font-size', nextProps.fontSize);
      this.hyperCaret.style.fontSize = nextProps.fontSize + 'px';
    }

    if (this.props.foregroundColor !== nextProps.foregroundColor) {
      prefs.set('foreground-color', nextProps.foregroundColor);
    }

    if (this.props.fontFamily !== nextProps.fontFamily) {
      prefs.set('font-family', nextProps.fontFamily);
      this.hyperCaret.style.fontFamily = nextProps.fontFamily;
    }

    if (this.props.fontSmoothing !== nextProps.fontSmoothing) {
      prefs.set('font-smoothing', nextProps.fontSmoothing);
    }

    if (this.props.cursorColor !== nextProps.cursorColor) {
      prefs.set('cursor-color', this.validateColor(nextProps.cursorColor, 'rgba(255,255,255,0.5)'));
    }

    if (this.props.cursorShape !== nextProps.cursorShape) {
      this.term.setCursorShape(nextProps.cursorShape);
    }

    if (this.props.colors !== nextProps.colors) {
      prefs.set('color-palette-overrides', getColorList(nextProps.colors));
    }

    if (this.props.customCSS !== nextProps.customCSS) {
      prefs.set('user-css', this.getStylesheet(nextProps.customCSS));
    }

    if (this.props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (this.props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.scrollbarsHideTimer);
    this.props.ref_(null);
  }

  template(css) {
    return (<div
      ref={component => {
        this.termWrapperRef = component;
      }}
      className={css('fit', this.props.isTermActive && 'active')}
      onMouseDown={this.handleMouseDown}
      style={{padding: this.props.padding}}
      >
      { this.props.customChildrenBefore }
      <div
        ref={component => {
          this.termRef = component;
        }}
        className={css('fit', 'term')}
        />
      { this.props.url ?
        <webview
          key="hyper-webview"
          src={this.props.url}
          onFocus={this.handleFocus}
          style={{
            background: '#fff',
            position: 'absolute',
            top: 0,
            left: 0,
            display: 'inline-flex',
            width: '100%',
            height: '100%'
          }}
          /> :
        <div // eslint-disable-line react/jsx-indent
          key="scrollbar"
          className={css('scrollbarShim')}
          onMouseEnter={this.handleScrollEnter}
          onMouseLeave={this.handleScrollLeave}
          />
      }
      <div key="hyper-caret" contentEditable className="hyper-caret" ref={this.onHyperCaret}/>
      { this.props.customChildren }
    </div>);
  }

  styles() {
    return {
      fit: {
        display: 'block',
        width: '100%',
        height: '100%'
      },

      term: {
        position: 'relative'
      },

      scrollbarShim: {
        position: 'fixed',
        right: 0,
        width: '50px',
        top: 0,
        bottom: 0,
        pointerEvents: 'none'
      }
    };
  }
}
<commit_msg>Define widths for BEAM and UNDERLINE cursors (#1389)<commit_after>/* global Blob,URL,requestAnimationFrame */
import React from 'react';
import Color from 'color';
import uuid from 'uuid';
import hterm from '../hterm';
import Component from '../component';
import getColorList from '../utils/colors';
import notify from '../utils/notify';

export default class Term extends Component {

  constructor(props) {
    super(props);
    this.handleWheel = this.handleWheel.bind(this);
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseUp = this.handleMouseUp.bind(this);
    this.handleScrollEnter = this.handleScrollEnter.bind(this);
    this.handleScrollLeave = this.handleScrollLeave.bind(this);
    this.onHyperCaret = this.onHyperCaret.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    props.ref_(this);
  }

  componentDidMount() {
    const {props} = this;
    this.term = props.term || new hterm.Terminal(uuid.v4());
    this.term.onHyperCaret(this.hyperCaret);

    // the first term that's created has unknown size
    // subsequent new tabs have size
    if (props.cols && props.rows) {
      this.term.realizeSize_(props.cols, props.rows);
    }

    const prefs = this.term.getPrefs();

    prefs.set('font-family', props.fontFamily);
    prefs.set('font-size', props.fontSize);
    prefs.set('font-smoothing', props.fontSmoothing);
    prefs.set('cursor-color', this.validateColor(props.cursorColor, 'rgba(255,255,255,0.5)'));
    prefs.set('enable-clipboard-notice', false);
    prefs.set('foreground-color', props.foregroundColor);

    // hterm.ScrollPort.prototype.setBackgroundColor is overriden
    // to make hterm's background transparent. we still need to set
    // background-color for proper text rendering
    prefs.set('background-color', props.backgroundColor);
    prefs.set('color-palette-overrides', getColorList(props.colors));
    prefs.set('user-css', this.getStylesheet(props.customCSS));
    prefs.set('scrollbar-visible', false);
    prefs.set('receive-encoding', 'raw');
    prefs.set('send-encoding', 'raw');
    prefs.set('alt-sends-what', 'browser-key');

    if (props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }

    this.term.onTerminalReady = () => {
      const io = this.term.io.push();
      io.onVTKeystroke = io.sendString = props.onData;
      io.onTerminalResize = (cols, rows) => {
        if (cols !== this.props.cols || rows !== this.props.rows) {
          props.onResize(cols, rows);
        }
      };

      this.term.modifierKeys = props.modifierKeys;
      // this.term.CursorNode_ is available at this point.
      this.term.setCursorShape(props.cursorShape);

      // emit onTitle event when hterm instance
      // wants to set the title of its tab
      this.term.setWindowTitle = props.onTitle;
      this.term.focusHyperCaret();
    };
    this.term.decorate(this.termRef);
    this.term.installKeyboard();
    if (this.props.onTerminal) {
      this.props.onTerminal(this.term);
    }

    const iframeWindow = this.getTermDocument().defaultView;
    iframeWindow.addEventListener('wheel', this.handleWheel);

    this.getScreenNode().addEventListener('mouseup', this.handleMouseUp);
  }

  handleWheel(e) {
    if (this.props.onWheel) {
      this.props.onWheel(e);
    }
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    clearTimeout(this.scrollbarsHideTimer);
    if (!this.scrollMouseEnter) {
      this.scrollbarsHideTimer = setTimeout(() => {
        prefs.set('scrollbar-visible', false);
      }, 1000);
    }
  }

  handleScrollEnter() {
    clearTimeout(this.scrollbarsHideTimer);
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    this.scrollMouseEnter = true;
  }

  handleScrollLeave() {
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', false);
    this.scrollMouseEnter = false;
  }

  handleMouseUp() {
    this.props.onActive();
    // this makes sure that we focus the hyper caret only
    // if a click on the term does not result in a selection
    // otherwise, if we focus without such check, it'd be
    // impossible to select a piece of text
    if (this.term.document_.getSelection().type !== 'Range') {
      this.term.focusHyperCaret();
    }
  }
  handleFocus() {
    // This will in turn result in `this.focus()` being
    // called, which is unecessary.
    // Should investigate if it matters.
    this.props.onActive();
  }

  handleKeyDown(e) {
    if (e.ctrlKey && e.key === 'c') {
      this.props.onURLAbort();
    }
  }

  onHyperCaret(caret) {
    this.hyperCaret = caret;
  }

  write(data) {
    // sometimes the preference set above for
    // `receive-encoding` is not known by the vt
    // before we type to write (since the preference
    // manager is asynchronous), so we force it to
    // avoid buffering
    // this fixes a race condition where sometimes
    // opening new sessions results in broken
    // output due to the term attempting to decode
    // as `utf-8` instead of `raw`
    if (this.term.vt.characterEncoding !== 'raw') {
      this.term.vt.characterEncoding = 'raw';
    }

    this.term.io.writeUTF8(data);
  }

  focus() {
    this.term.focusHyperCaret();
  }

  clear() {
    this.term.clearPreserveCursorRow();

    // If cursor is still not at the top, a command is probably
    // running and we'd like to delete the whole screen.
    // Move cursor to top
    if (this.term.getCursorRow() !== 0) {
      this.write('\x1B[0;0H\x1B[2J');
    }
  }

  moveWordLeft() {
    this.term.onVTKeystroke('\x1bb');
  }

  moveWordRight() {
    this.term.onVTKeystroke('\x1bf');
  }

  deleteWordLeft() {
    this.term.onVTKeystroke('\x1b\x7f');
  }

  deleteWordRight() {
    this.term.onVTKeystroke('\x1bd');
  }

  deleteLine() {
    this.term.onVTKeystroke('\x1bw');
  }

  moveToStart() {
    this.term.onVTKeystroke('\x01');
  }

  moveToEnd() {
    this.term.onVTKeystroke('\x05');
  }

  selectAll() {
    this.term.selectAll();
  }

  getScreenNode() {
    return this.term.scrollPort_.getScreenNode();
  }

  getTermDocument() {
    return this.term.document_;
  }

  getStylesheet(css) {
    const hyperCaret = `
      .hyper-caret {
        outline: none;
        display: inline-block;
        color: transparent;
        text-shadow: 0 0 0 black;
        font-family: ${this.props.fontFamily};
        font-size: ${this.props.fontSize}px;
      }
    `;
    let osSpecificCss = '';
    if (process.platform === 'win32') {
      osSpecificCss = `::-webkit-scrollbar {
        width: 5px;
      }
      ::-webkit-scrollbar-thumb {
        -webkit-border-radius: 10px;
        border-radius: 10px;
        background: ${this.props.borderColor};
      }
      ::-webkit-scrollbar-thumb:window-inactive {
        background: ${this.props.borderColor};
      }`;
    } else if (process.platform === 'darwin') {
      osSpecificCss = `::-webkit-scrollbar {
        width: 9px;
        background: transparent;
       }
       ::-webkit-scrollbar-thumb {
        -webkit-border-radius: 10px;
        border-radius: 10px;
        -webkit-box-shadow: inset 0 0 0 1px ${this.props.borderColor};
       }`;
    }
    return URL.createObjectURL(new Blob([`
      .cursor-node[focus="false"] {
         border-width: 1px !important;
      }
      .cursor-node[focus="true"] {
        border-left-width: 1px;
        border-bottom-width: 2px;
      }
      ${hyperCaret}
      ${osSpecificCss}
      ${css}
    `], {type: 'text/css'}));
  }

  validateColor(color, alternative = 'rgb(255,255,255)') {
    try {
      return Color(color).rgbString();
    } catch (err) {
      notify(`color "${color}" is invalid`);
    }
    return alternative;
  }

  handleMouseDown(ev) {
    // we prevent losing focus when clicking the boundary
    // wrappers of the main terminal element
    if (ev.target === this.termWrapperRef ||
        ev.target === this.termRef) {
      ev.preventDefault();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.url !== nextProps.url) {
      // when the url prop changes, we make sure
      // the terminal starts or stops ignoring
      // key input so that it doesn't conflict
      // with the <webview>
      if (nextProps.url) {
        this.term.io.push();
        window.addEventListener('keydown', this.handleKeyDown);
      } else {
        window.removeEventListener('keydown', this.handleKeyDown);
        this.term.io.pop();
      }
    }

    if (!this.props.cleared && nextProps.cleared) {
      this.clear();
    }

    const prefs = this.term.getPrefs();

    if (this.props.fontSize !== nextProps.fontSize) {
      prefs.set('font-size', nextProps.fontSize);
      this.hyperCaret.style.fontSize = nextProps.fontSize + 'px';
    }

    if (this.props.foregroundColor !== nextProps.foregroundColor) {
      prefs.set('foreground-color', nextProps.foregroundColor);
    }

    if (this.props.fontFamily !== nextProps.fontFamily) {
      prefs.set('font-family', nextProps.fontFamily);
      this.hyperCaret.style.fontFamily = nextProps.fontFamily;
    }

    if (this.props.fontSmoothing !== nextProps.fontSmoothing) {
      prefs.set('font-smoothing', nextProps.fontSmoothing);
    }

    if (this.props.cursorColor !== nextProps.cursorColor) {
      prefs.set('cursor-color', this.validateColor(nextProps.cursorColor, 'rgba(255,255,255,0.5)'));
    }

    if (this.props.cursorShape !== nextProps.cursorShape) {
      this.term.setCursorShape(nextProps.cursorShape);
    }

    if (this.props.colors !== nextProps.colors) {
      prefs.set('color-palette-overrides', getColorList(nextProps.colors));
    }

    if (this.props.customCSS !== nextProps.customCSS) {
      prefs.set('user-css', this.getStylesheet(nextProps.customCSS));
    }

    if (this.props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (this.props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.scrollbarsHideTimer);
    this.props.ref_(null);
  }

  template(css) {
    return (<div
      ref={component => {
        this.termWrapperRef = component;
      }}
      className={css('fit', this.props.isTermActive && 'active')}
      onMouseDown={this.handleMouseDown}
      style={{padding: this.props.padding}}
      >
      { this.props.customChildrenBefore }
      <div
        ref={component => {
          this.termRef = component;
        }}
        className={css('fit', 'term')}
        />
      { this.props.url ?
        <webview
          key="hyper-webview"
          src={this.props.url}
          onFocus={this.handleFocus}
          style={{
            background: '#fff',
            position: 'absolute',
            top: 0,
            left: 0,
            display: 'inline-flex',
            width: '100%',
            height: '100%'
          }}
          /> :
        <div // eslint-disable-line react/jsx-indent
          key="scrollbar"
          className={css('scrollbarShim')}
          onMouseEnter={this.handleScrollEnter}
          onMouseLeave={this.handleScrollLeave}
          />
      }
      <div key="hyper-caret" contentEditable className="hyper-caret" ref={this.onHyperCaret}/>
      { this.props.customChildren }
    </div>);
  }

  styles() {
    return {
      fit: {
        display: 'block',
        width: '100%',
        height: '100%'
      },

      term: {
        position: 'relative'
      },

      scrollbarShim: {
        position: 'fixed',
        right: 0,
        width: '50px',
        top: 0,
        bottom: 0,
        pointerEvents: 'none'
      }
    };
  }
}
<|endoftext|>


<commit_before>/* Python built-in exceptions */

py_builtins.__exceptions__ = [
    'NotImplementedError',
    'ZeroDivisionError',
    'AssertionError',
    'AttributeError',
    'RuntimeError',
    'ImportError',
    'TypeError',
    'ValueError',
    'NameError',
    'IndexError',
    'KeyError',
    'StopIteration'
];

for (var i in py_builtins.__exceptions__) {
    var name = py_builtins.__exceptions__[i];

    py_builtins[name] = function() {
        return function(message) {
            this.message = defined(message) ? message : "";
        };
    }();

    py_builtins[name].__name__ = name;
    py_builtins[name].prototype.__class__ = py_builtins[name];

    py_builtins[name].prototype.__str__ = function() {
        return str.__call__(js(this.__class__.__name__) + ": " + js(this.message));
    };

    py_builtins[name].prototype.toString = function() {
        return js(this.__str__());
    };
}

/* Python built-in functions */

function hasattr(obj, name) {
    return defined(obj[name]);
}

function getattr(obj, name, value) {
    var _value = obj[name];

    if (defined(_value)) {
        return _value;
    } else {
        if (defined(value)) {
            return value;
        } else {
            throw new py_builtins.AttributeError(obj, name);
        }
    }
}

function setattr(obj, name, value) {
    obj[name] = value;
}

function hash(obj) {
    if (hasattr(obj, '__hash__')) {
        return obj.__hash__();
    } else if (typeof(obj) == 'number') {
        return obj == -1 ? -2 : obj;
    } else {
        throw new py_builtins.AttributeError(obj, '__hash__');
    }
}

function len(obj) {
    if (hasattr(obj, '__len__')) {
        return obj.__len__();
    } else {
        throw new py_builtins.AttributeError(obj, '__name__');
    }
}

function range(start, end, step) {
    if (!defined(end)) {
        end = start;
        start = 0;
    }

    if (!defined(step)) {
        step = 1;
    }

    var seq = [];

    for (var i = start; i < end; i += step) {
        seq.push(i);
    }

    if (py_builtins.__python3__)
        return iter.__call__(seq);
    else
        return list(seq);
}

function xrange(start, end, step) {
    return iter.__call__(range(start, end, step));
}

function map() {
    if (arguments.length < 2) {
        throw new py_builtins.TypeError("map() requires at least two args");
    }

    if (arguments.length > 2) {
        throw new py_builtins.NotImplementedError("only one sequence allowed in map()");
    }

    var func = arguments[0];
    var seq = iter.__call__(arguments[1]);

    var items = list();

    iterate(seq, function(item) {
        items.append(func(item));
    });

    if (py_builtins.__python3__)
        return iter.__call__(items);
    else
        return items;
}

function zip() {
    if (!arguments.length) {
        return list();
    }

    var iters = list();
    var i;

    for (i = 0; i < arguments.length; i++) {
        iters.append(iter.__call__(arguments[i]));
    }

    var items = list();

    while (true) {
        var item = list();

        for (i = 0; i < arguments.length; i++) {
            try {
                var value = iters.__getitem__(i).next();
            } catch (exc) {
                if (isinstance(exc, py_builtins.StopIteration)) {
                    return items;
                } else {
                    throw exc;
                }
            }

            item.append(value);
        }

        items.append(tuple(item));
    }
}

function isinstance(obj, cls) {
    if (cls instanceof _tuple) {
        var length = cls.__len__();

        if (length == 0) {
            return false;
        }

        for (var i = 0; i < length; i++) {
            var _cls = cls.__getitem__(i);

            if (isinstance(obj, _cls)) {
                return true;
            }
        }

        return false;
    } else {
        if (defined(obj.__class__) && defined(cls.__name__)) {
            return obj.__class__ == cls;
        } else {
            if (defined(cls.__call__)) {
                return obj instanceof cls.__call__;
            } else {
                return obj instanceof cls;
            }
        }
    }
}

py_builtins.bool = function(a) {
    if ((a != null) && defined(a.__bool__))
        return a.__bool__();
    else {
        if (a)
            return true;
        else
            return false;
    }
};

py_builtins.eq = function(a, b) {
    if ((a != null) && defined(a.__eq__))
        return a.__eq__(b);
    else if ((b != null) && defined(b.__eq__))
        return b.__eq__(a);
    else
        return a == b;
};

py_builtins._int = Function(function(value) {
    return value;
});

py_builtins._float = Function(function(value) {
    return value;
});

py_builtins.max = function(list) {
    if (len(list) == 0)
        throw new py_builtins.ValueError("max() arg is an empty sequence");
    else {
        var result = null;

        iterate(iter.__call__(list), function(item) {
                if ((result == null) || (item > result))
                    result = item;
        });

        return result;
    }
};

py_builtins.min = function(list) {
    if (len(list) == 0)
        throw new py_builtins.ValueError("min() arg is an empty sequence");
    else {
        var result = null;

        iterate(iter.__call__(list), function(item) {
                if ((result == null) || (item < result))
                    result = item;
        });

        return result;
    }
};

py_builtins.sum = function(list) {
    var result = 0;

    iterate(iter.__call__(list), function(item) {
        result += item;
    });

    return result;
};

py_builtins.print = function(s) {
    if (typeof(console) != "undefined" && defined(console.log))
        console.log(js(str.__call__(s)));
    else {
        if (arguments.length <= 1) {
            if (defined(s))
                print(s);
            else
                print("");
        } else {
            var args = tuple(to_array(arguments));
            print(str.__call__(" ").join(args));
        }
    }
};

<commit_msg> * Updated 10-builtin.js to use list.__call__() instead of list()<commit_after>/* Python built-in exceptions */

py_builtins.__exceptions__ = [
    'NotImplementedError',
    'ZeroDivisionError',
    'AssertionError',
    'AttributeError',
    'RuntimeError',
    'ImportError',
    'TypeError',
    'ValueError',
    'NameError',
    'IndexError',
    'KeyError',
    'StopIteration'
];

for (var i in py_builtins.__exceptions__) {
    var name = py_builtins.__exceptions__[i];

    py_builtins[name] = function() {
        return function(message) {
            this.message = defined(message) ? message : "";
        };
    }();

    py_builtins[name].__name__ = name;
    py_builtins[name].prototype.__class__ = py_builtins[name];

    py_builtins[name].prototype.__str__ = function() {
        return str.__call__(js(this.__class__.__name__) + ": " + js(this.message));
    };

    py_builtins[name].prototype.toString = function() {
        return js(this.__str__());
    };
}

/* Python built-in functions */

function hasattr(obj, name) {
    return defined(obj[name]);
}

function getattr(obj, name, value) {
    var _value = obj[name];

    if (defined(_value)) {
        return _value;
    } else {
        if (defined(value)) {
            return value;
        } else {
            throw new py_builtins.AttributeError(obj, name);
        }
    }
}

function setattr(obj, name, value) {
    obj[name] = value;
}

function hash(obj) {
    if (hasattr(obj, '__hash__')) {
        return obj.__hash__();
    } else if (typeof(obj) == 'number') {
        return obj == -1 ? -2 : obj;
    } else {
        throw new py_builtins.AttributeError(obj, '__hash__');
    }
}

function len(obj) {
    if (hasattr(obj, '__len__')) {
        return obj.__len__();
    } else {
        throw new py_builtins.AttributeError(obj, '__name__');
    }
}

function range(start, end, step) {
    if (!defined(end)) {
        end = start;
        start = 0;
    }

    if (!defined(step)) {
        step = 1;
    }

    var seq = [];

    for (var i = start; i < end; i += step) {
        seq.push(i);
    }

    if (py_builtins.__python3__)
        return iter.__call__(seq);
    else
        return list.__call__(seq);
}

function xrange(start, end, step) {
    return iter.__call__(range(start, end, step));
}

function map() {
    if (arguments.length < 2) {
        throw new py_builtins.TypeError("map() requires at least two args");
    }

    if (arguments.length > 2) {
        throw new py_builtins.NotImplementedError("only one sequence allowed in map()");
    }

    var func = arguments[0];
    var seq = iter.__call__(arguments[1]);

    var items = list.__call__();

    iterate(seq, function(item) {
        items.append(func(item));
    });

    if (py_builtins.__python3__)
        return iter.__call__(items);
    else
        return items;
}

function zip() {
    if (!arguments.length) {
        return list.__call__();
    }

    var iters = list.__call__();
    var i;

    for (i = 0; i < arguments.length; i++) {
        iters.append(iter.__call__(arguments[i]));
    }

    var items = list.__call__();

    while (true) {
        var item = list.__call__();

        for (i = 0; i < arguments.length; i++) {
            try {
                var value = iters.__getitem__(i).next();
            } catch (exc) {
                if (isinstance(exc, py_builtins.StopIteration)) {
                    return items;
                } else {
                    throw exc;
                }
            }

            item.append(value);
        }

        items.append(tuple(item));
    }
}

function isinstance(obj, cls) {
    if (cls instanceof _tuple) {
        var length = cls.__len__();

        if (length == 0) {
            return false;
        }

        for (var i = 0; i < length; i++) {
            var _cls = cls.__getitem__(i);

            if (isinstance(obj, _cls)) {
                return true;
            }
        }

        return false;
    } else {
        if (defined(obj.__class__) && defined(cls.__name__)) {
            return obj.__class__ == cls;
        } else {
            if (defined(cls.__call__)) {
                return obj instanceof cls.__call__;
            } else {
                return obj instanceof cls;
            }
        }
    }
}

py_builtins.bool = function(a) {
    if ((a != null) && defined(a.__bool__))
        return a.__bool__();
    else {
        if (a)
            return true;
        else
            return false;
    }
};

py_builtins.eq = function(a, b) {
    if ((a != null) && defined(a.__eq__))
        return a.__eq__(b);
    else if ((b != null) && defined(b.__eq__))
        return b.__eq__(a);
    else
        return a == b;
};

py_builtins._int = Function(function(value) {
    return value;
});

py_builtins._float = Function(function(value) {
    return value;
});

py_builtins.max = function(list) {
    if (len(list) == 0)
        throw new py_builtins.ValueError("max() arg is an empty sequence");
    else {
        var result = null;

        iterate(iter.__call__(list), function(item) {
                if ((result == null) || (item > result))
                    result = item;
        });

        return result;
    }
};

py_builtins.min = function(list) {
    if (len(list) == 0)
        throw new py_builtins.ValueError("min() arg is an empty sequence");
    else {
        var result = null;

        iterate(iter.__call__(list), function(item) {
                if ((result == null) || (item < result))
                    result = item;
        });

        return result;
    }
};

py_builtins.sum = function(list) {
    var result = 0;

    iterate(iter.__call__(list), function(item) {
        result += item;
    });

    return result;
};

py_builtins.print = function(s) {
    if (typeof(console) != "undefined" && defined(console.log))
        console.log(js(str.__call__(s)));
    else {
        if (arguments.length <= 1) {
            if (defined(s))
                print(s);
            else
                print("");
        } else {
            var args = tuple(to_array(arguments));
            print(str.__call__(" ").join(args));
        }
    }
};

<|endoftext|>


<commit_before>var Serializers = require('./serializers'),
    Deserializers = require('./deserializers');

/**
 * No-Operation
 */
var NOOP = function(){};

var TYPES = {
  'BytesType': { ser:Serializers.encodeBinary, de:Deserializers.decodeBinary },
  'LongType': { ser:Serializers.encodeLong, de:Deserializers.decodeLong },
  'IntegerType': { ser:Serializers.encodeInt32, de:Deserializers.decodeInt32 },
  'UTF8Type': { ser:Serializers.encodeUTF8, de:Deserializers.decodeUTF8 },
  'AsciiType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii },
  'LexicalUUIDType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii }, //TODO: This may not be right
  'TimeUUIDType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii },    //TODO: This may not be right
  'CounterColumnType': { ser:NOOP, de:NOOP },
  'FloatType': { ser:Serializers.encodeFloat, de:Deserializers.decodeFloat },
  'DoubleType':{ ser:Serializers.encodeDouble, de:Deserializers.decodeDouble },
  'DateType':{ ser:Serializers.encodeDate, de:Deserializers.decodeDate },
  'BooleanType':{ ser:Serializers.encodeBoolean, de:Deserializers.decodeBoolean },
  'UUIDType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii }        //TODO: This may not be right
};
/**
 * Given a string like org.apache.cassandra.db.marshal.UTF8Type
 * return a string of UTF8Type
 * @return {string} TypeName
 * @private
 */
function getType(str){
  var classes = str.split('.');
  return classes[classes.length - 1];
}

/**
 * Returns the type string inside of the parentheses
 * @private
 */
function getInnerType(str){
  return str.substring(str.indexOf('(') + 1, str.length - 1);
}

/**
 * Returns an array of types for composite columns
 * @private
 */
function getCompositeTypes(str){
  var types = getInnerType(str).split(','),
      i = 0, ret = [], typeLength = types.length;

  for(; i < typeLength; i += 1){
    ret.push( getType(types[i]) );
  }

  return ret;
}

/**
 * Parses the type string and decides what types to return
 * @private
 */
function parseTypeString(type){
  if (type.indexOf('CompositeType') > -1){
    return getCompositeTypes(type);
  } else if(type.indexOf('ReversedType') > -1){
    return getInnerType(type);
  } else if(type === null || type === undefined) {
    return 'BytesType';
  } else {
    return getType(type);
  }
}

/**
 * Creates a serializer for composite types
 * @private
 */
function compositeSerializer(serializers){
  return function(vals){
    var i = 0, buffers = [], totalLength = 0,
        valLength = vals.length, val;

    if(!Array.isArray(vals)){
      vals = [vals];
    }

    for(; i < valLength; i += 1){
      val = serializers[i](vals[i]);
      buffers.push(val);
      totalLength += val.length + 3;
    }

    var buf = new Buffer(totalLength),
        buffersLength = buffers.length,
        writtenLength = 0;

    i = 0;
    for(; i < buffersLength; i += 1){
      val = buffers[i];
      buf.writeUInt16BE(val.length, writtenLength);
      writtenLength += 2;
      val.copy(buf, writtenLength, 0);
      writtenLength += val.length;
      buf.write('\x00', writtenLength);
      writtenLength += 1;
    }

    return buf;
  };
}

/**
 * Creates a deserializer for composite types
 * @private
 */
function compositeDeserializer(serializers){
  return function(vals){
    throw('Not Implemented');
  };
}

/**
 * Gets the serializer(s) for a specific type
 * @private
 */
function getSerializer(type){
  if (Array.isArray(type)){
    var i = 0, typeLength = type.length, serializers = [];
    for(; i < typeLength; i += 1){
      serializers.push( getSerializer(type[i]));
    }

    return compositeSerializer(serializers);
  } else {
    return TYPES[type].ser;
  }
}

/**
 * Gets the deserializer(s) for a specific type
 * @private
 */
function getDeserializer(type){
  if (Array.isArray(type)){
    var i = 0, typeLength = type.length, deserializers = [];
    for(; i < typeLength; i += 1){
      deserializers.push( getDeserializer(type[i]));
    }

    return compositeDeserializer(deserializers);
  } else {
    return TYPES[type].de;
  }
}

/**
 * Creates a SerDe object for a column family
 */
var Marshal = function(type){
  var parsedType = parseTypeString(type);

  if(Array.isArray(parsedType)){
    this.isComposite = true;
  } else {
    this.isComposite = false;
  }

  this.serialize = getSerializer(parsedType);
  this.deserialize = getDeserializer(parsedType);
};

module.exports = Marshal;<commit_msg>reget val length so as to not get string length<commit_after>var Serializers = require('./serializers'),
    Deserializers = require('./deserializers');

/**
 * No-Operation
 */
var NOOP = function(){};

var TYPES = {
  'BytesType': { ser:Serializers.encodeBinary, de:Deserializers.decodeBinary },
  'LongType': { ser:Serializers.encodeLong, de:Deserializers.decodeLong },
  'IntegerType': { ser:Serializers.encodeInt32, de:Deserializers.decodeInt32 },
  'UTF8Type': { ser:Serializers.encodeUTF8, de:Deserializers.decodeUTF8 },
  'AsciiType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii },
  'LexicalUUIDType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii }, //TODO: This may not be right
  'TimeUUIDType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii },    //TODO: This may not be right
  'CounterColumnType': { ser:NOOP, de:NOOP },
  'FloatType': { ser:Serializers.encodeFloat, de:Deserializers.decodeFloat },
  'DoubleType':{ ser:Serializers.encodeDouble, de:Deserializers.decodeDouble },
  'DateType':{ ser:Serializers.encodeDate, de:Deserializers.decodeDate },
  'BooleanType':{ ser:Serializers.encodeBoolean, de:Deserializers.decodeBoolean },
  'UUIDType': { ser:Serializers.encodeAscii, de:Deserializers.decodeAscii }        //TODO: This may not be right
};
/**
 * Given a string like org.apache.cassandra.db.marshal.UTF8Type
 * return a string of UTF8Type
 * @return {string} TypeName
 * @private
 */
function getType(str){
  var classes = str.split('.');
  return classes[classes.length - 1];
}

/**
 * Returns the type string inside of the parentheses
 * @private
 */
function getInnerType(str){
  return str.substring(str.indexOf('(') + 1, str.length - 1);
}

/**
 * Returns an array of types for composite columns
 * @private
 */
function getCompositeTypes(str){
  var types = getInnerType(str).split(','),
      i = 0, ret = [], typeLength = types.length;

  for(; i < typeLength; i += 1){
    ret.push( getType(types[i]) );
  }

  return ret;
}

/**
 * Parses the type string and decides what types to return
 * @private
 */
function parseTypeString(type){
  if (type.indexOf('CompositeType') > -1){
    return getCompositeTypes(type);
  } else if(type.indexOf('ReversedType') > -1){
    return getInnerType(type);
  } else if(type === null || type === undefined) {
    return 'BytesType';
  } else {
    return getType(type);
  }
}

/**
 * Creates a serializer for composite types
 * @private
 */
function compositeSerializer(serializers){
  return function(vals){
    var i = 0, buffers = [], totalLength = 0,
        valLength = vals.length, val;

    if(!Array.isArray(vals)){
      vals = [vals];
      valLength = vals.length;
    }

    for(; i < valLength; i += 1){
      val = serializers[i](vals[i]);
      buffers.push(val);
      totalLength += val.length + 3;
    }

    var buf = new Buffer(totalLength),
        buffersLength = buffers.length,
        writtenLength = 0;

    i = 0;
    for(; i < buffersLength; i += 1){
      val = buffers[i];
      buf.writeUInt16BE(val.length, writtenLength);
      writtenLength += 2;
      val.copy(buf, writtenLength, 0);
      writtenLength += val.length;
      buf.write('\x00', writtenLength);
      writtenLength += 1;
    }

    return buf;
  };
}

/**
 * Creates a deserializer for composite types
 * @private
 */
function compositeDeserializer(serializers){
  return function(vals){
    throw('Not Implemented');
  };
}

/**
 * Gets the serializer(s) for a specific type
 * @private
 */
function getSerializer(type){
  if (Array.isArray(type)){
    var i = 0, typeLength = type.length, serializers = [];
    for(; i < typeLength; i += 1){
      serializers.push( getSerializer(type[i]));
    }

    return compositeSerializer(serializers);
  } else {
    return TYPES[type].ser;
  }
}

/**
 * Gets the deserializer(s) for a specific type
 * @private
 */
function getDeserializer(type){
  if (Array.isArray(type)){
    var i = 0, typeLength = type.length, deserializers = [];
    for(; i < typeLength; i += 1){
      deserializers.push( getDeserializer(type[i]));
    }

    return compositeDeserializer(deserializers);
  } else {
    return TYPES[type].de;
  }
}

/**
 * Creates a SerDe object for a column family
 */
var Marshal = function(type){
  var parsedType = parseTypeString(type);

  if(Array.isArray(parsedType)){
    this.isComposite = true;
  } else {
    this.isComposite = false;
  }

  this.serialize = getSerializer(parsedType);
  this.deserialize = getDeserializer(parsedType);
};

module.exports = Marshal;<|endoftext|>


<commit_before>var path = require('path'),
    fs = require('fs'),
    External = require('./external'),
    Step = require('step'),
    _ = require('underscore')._,
    sys = require('sys'),
    mess = require('mess');

require.paths.unshift(path.join(__dirname, '..', 'lib'));

/**
 * Rendering circuitry for JSON map manifests.
 *
 * This is node-only for the time being.
 */

/**
 * Convert a two-element-per-item Array
 * into an object, for the purpose of checking membership
 * and replacing stuff.
 * @param {Array} list a list.
 */
var to = function(list) {
    return list && list[0] && _.reduce(list, function(m, r) {
        if (r && r.length > 1) {
          m[r[0]] = r[1];
        }
        return m;
    }, {});
};

mess.Renderer = function Renderer(env) {
    var env = {
        data_dir: env.data_dir || '/tmp/',
        local_data_dir: env.local_data_dir || '',
        validation_data: env.validation_data || false
    };
    return {
        /**
         * Keep a copy of passed-in environment variables
         */
        env: env,
        /**
         * Wrapper for downloading externals: likely removable
         *
         * @param {String} uri the URI of the resource.
         * @param {Function} callback
         */
        grab: function(uri, callback) {
            new(External)(this.env).process(uri, callback);
        },

        /**
         * Download any file-based remote datsources.
         *
         * Usable as an entry point: does not expect any modification to
         * the map object beyond JSON parsing.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeExternals: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Layer.forEach(function(l) {
                        if (l.Datasource.file) {
                            that.grab(l.Datasource.file, group());
                        }
                    });
                    if (m.Layer.length == 0) group()();
                },
                function(err, results) {
                    var result_map = to(results);
                    m.Layer = _.map(_.filter(m.Layer,
                        function(l) {
                            return l.Datasource.file &&
                                result_map[l.Datasource.file];
                        }),
                        function(l) {
                            l.Datasource.file = result_map[l.Datasource.file];
                            return l;
                        }
                    );
                    callback(err, m);
                }
            );
        },

        /**
         * Download any remote stylesheets
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeStyle: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (!s.id) {
                            that.grab(s, group());
                        }
                    });
                    group()();
                },
                function(err, results) {
                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        }
                    }
                    callback(err, m);
                }
            );
        },

        /**
         * Compile (already downloaded) styles with mess.js,
         * calling callback with an array of [map object, [stylesheet objects]]
         *
         * Called with the results of localizeStyle or localizeExternals:
         * expects not to handle downloading.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        style: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (s.id) {
                            group()(null, [s.id, s.data]);
                        } else {
                            fs.readFile(s, 'utf-8', function(err, data) {
                                group()(err, [s, data]);
                            });
                        }
                    });
                },
                function(e, results) {
                    var options = {},
                        group = this.group();
                    for (var i = 0, l = results.length; i < l; i++) {
                        new(mess.Parser)(_.extend(_.extend({
                            filename: s
                        }, that.env), this.env)).parse(results[i][1],
                            function(err, tree) {
                            if (err) {
                                mess.writeError(err, options);
                                throw err;
                            } else {
                                try {
                                    group()(err, [
                                        results[i][0],
                                        tree.toCSS({ compress: false }),
                                        tree]);
                                    return;
                                } catch (e) {
                                    throw e;
                                    return;
                                }
                            }
                        });
                    }
                },
                function(err, results) {
                    if (err) {
                        callback(err);
                        return;
                    }

                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        } else {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s].id];
                        }
                    }
                    callback(err, [m, results]);
                }
            );
        },

        split_symbolizers: function(definitions) {
            var by_symbolizer = {};
            for (var i = 0; i < definitions.length; i++) {
                definitions[i].symbolizers().forEach(function(sym) {
                    if(!by_symbolizer[sym]) {
                        by_symbolizer[sym] = [];
                    }
                    by_symbolizer[sym].push(
                        definitions[i].filter_symbolizer(sym));
                });
            }
            return by_symbolizer;
        },

        /**
         * Pick the 'winners' - all elements that select
         * properly. Apply inherited styles from their
         * ancestors to them.
         */
        process_chain: function(definitions) {
            // definitions are ordered in specificity,
            // high to low
            //
            // basically if 'this level' has
            // a filter, then keep going, otherwise
            // this is the final selector.
            var winners = [];
            var ancestors = [];
            var below_threshold = false;

            while (def = definitions.shift()) {
                if (below_threshold) {
                    ancestors.push(def)
                } else if (def.selector.specificity()[2] > 0) {
                    winners.push(def);
                } else {
                    winners.push(def);
                    // nothing below this level will win
                    below_threshold = true;
                }
            }

            // iterate in reverse - low to high specificity
            for (var i = ancestors.length - 1; i >= 0; i--) {
                for (var j = 0; j < winners.length; j++) {
                    winners[j].inherit_from(ancestors[i]);
                }
            }

            winners = this.resolve_filters(winners);

            return winners;
        },

        // TODO: don't add negations to the unfiltered selectors
        resolve_filters: function(definitions) {
            var negated_filters = [];
            for (var i = 0; i < definitions.length; i++) {
                if (definitions[i].selector.filters.length) {
                    // array of the negation of this definition's filters
                    var negation = definitions[i].selector.filters.map(
                        function(f) { return f.negate(); });

                    // add in existing negations
                    definitions[i].selector.filters =
                        definitions[i].selector.filters.concat(negated_filters);

                    // add this definition's filter's negations to the list
                    negated_filters = negated_filters.concat(negation);
                }
            }
            return definitions;
        },

        /**
         * Prepare full XML map output. Called with the results
         * of this.style
         *
         * @param {Array} res array of [map object, stylesheets].
         * @param {Function} callback
         */
        template: function(err, res, callback) {
            if (err) {
                callback(err);
                return;
            }

            var that = this,
                m = res[0],
                stylesheets = res[1],

                entities = _.template('<?xml version="1.0" ' +
                    'encoding="utf-8"?>\n' +
                    '<!DOCTYPE Map[\n' +
                    '<% for (ref in externals) { %>' +
                    '  <!ENTITY <%= ref %> SYSTEM "<%= externals[ref] %>">\n' +
                    '<% } %>]>\n'),

                styles = _.template('<StyleName><%= name %></StyleName>'),

                layer = _.template('\n  <Layer\n   id="<%= id %>"\n' +
                    '   name="<%= name %>"\n' +
                    '   srs="<%= srs %>">\n' +
                    '<% for (s in styles) { %>' +
                    '    <StyleName><%= styles[s] %></StyleName>\n' +
                    '<% } %>' +
                    '    <Datasource>\n<% for (n in Datasource) { %>' +
                    '      <Parameter name="<%= n %>"><%= Datasource[n] %>' +
                    '</Parameter>\n' +
                    '<% } %>    </Datasource>\n' +
                    '  </Layer>\n'),

                stylesheet_tmpl = _.template('  <% for (i in Stylesheet) { %>' +
                    '<%= Stylesheet[i] %>\n' +
                    '<% } %>\n'),

                // referencing externals in the body of the map
                references = _.template('<% for (ref in externals) { %>' +
                    '&<%= ref %>;\n' +
                    '<% } %>'),

                // for the Map[] doctype: XML entities to
                // organize code
                entity_list = { externals: [], symbols: [] };

            /**
             * Index a hash of the CSS rules in all stylesheets
             * This is performance-critical: necessary to test
             * at some point
             *
             * @param {Array} stylesheets
             * @return {Array} list of selectors.
             */

            // var findBackground = function(stylesheets) {
            //     var b = '#FFFFFF';
            //     _.each(stylesheets, function(s) {
            //         _.each(s[2].rules, function(r) {
            //             if (r && r.selectors && r.selectors[0]._css &&
            //                 r.selectors[0]._css === 'Map') {
            //                 var bg = _.detect(r.rules, function(rule) {
            //                     return rule.name == 'background-color';
            //                 });
            //                 if (bg) {
            //                     b = bg.value.toCSS({ compress: false });
            //                 }
            //             }
            //         });
            //     });
            //     return b;
            // };

            /**
             * Add styles to a layer object
             *
             * @param {Object} layer the layer object.
             * @param {Array} stylesheets the results of ruleHash().
             * @return Formalized layer definition with .styles = [].
             */

            var rulesets = _.flatten(stylesheets.map(function(rulesets) {
                return rulesets[2].toMSS();
            }));

            var output = [entities(entity_list)];
            // TODO: must change when background colors are available
            output.push('<Map background-color="'
                    // findBackground(stylesheets)
                    + '" srs="+proj=merc +a=6378137 +b=6378137 '
                    + '+lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m '
                    + '+nadgrids=@null +no_defs">\n');

            m.Layer.forEach(function(l) {
                l.styles = [];
                var classes = (l['class'] || '').split(/\s+/g);

                var matching = rulesets.filter(function(ruleset) {
                    return ruleset.selector.matches(l.id, classes);
                });

                // matching is an array of matching selectors,
                // in order from high specificity to low.
                var by_symbolizer = that.split_symbolizers(matching);

                var complete_chains = {};
                for (sym in by_symbolizer) {
                    var new_style = new(mess.tree.Style)(
                        l.id,
                        sym,
                        that.process_chain(by_symbolizer[sym]));
                    var n = new_style.name();
                    l.styles.push(n);
                    output.push(new_style.toXML());
                }
                var nl = new(mess.tree.Layer)(l);
                output.push(nl.toXML());
                console.log('Layer ' + l.id);
                console.log(sys.inspect(matching, false, null));
                console.log('______');
            });

            // output.push(references(entity_list));
            // output.push(stylesheet_tmpl(m));
            output.push('</Map>');

            callback(null, output.join('\n'));
        },

        /**
         * Prepare a JML document (given as a string) into a
         * fully-localized XML file ready for Mapnik2 consumption
         *
         * @param {String} str the JSON file as a string.
         * @param {Function} callback to be called with err, XML representation.
         */
        render: function(str, callback) {
            var m = JSON.parse(str),
                that = this;
            this.localizeExternals(m, function(err, res) {
                that.localizeStyle(res, function(err, res) {
                    that.style(res, function(err, res) {
                        that.template(err, res, function(err, res) {
                            callback(err, res);
                        });
                    });
                });
            });
        }
    };
};

module.exports = mess;
<commit_msg>Removing debugging so that one can pipe to nik2img<commit_after>var path = require('path'),
    fs = require('fs'),
    External = require('./external'),
    Step = require('step'),
    _ = require('underscore')._,
    sys = require('sys'),
    mess = require('mess');

require.paths.unshift(path.join(__dirname, '..', 'lib'));

/**
 * Rendering circuitry for JSON map manifests.
 *
 * This is node-only for the time being.
 */

/**
 * Convert a two-element-per-item Array
 * into an object, for the purpose of checking membership
 * and replacing stuff.
 * @param {Array} list a list.
 */
var to = function(list) {
    return list && list[0] && _.reduce(list, function(m, r) {
        if (r && r.length > 1) {
          m[r[0]] = r[1];
        }
        return m;
    }, {});
};

mess.Renderer = function Renderer(env) {
    var env = {
        data_dir: env.data_dir || '/tmp/',
        local_data_dir: env.local_data_dir || '',
        validation_data: env.validation_data || false
    };
    return {
        /**
         * Keep a copy of passed-in environment variables
         */
        env: env,
        /**
         * Wrapper for downloading externals: likely removable
         *
         * @param {String} uri the URI of the resource.
         * @param {Function} callback
         */
        grab: function(uri, callback) {
            new(External)(this.env).process(uri, callback);
        },

        /**
         * Download any file-based remote datsources.
         *
         * Usable as an entry point: does not expect any modification to
         * the map object beyond JSON parsing.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeExternals: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Layer.forEach(function(l) {
                        if (l.Datasource.file) {
                            that.grab(l.Datasource.file, group());
                        }
                    });
                    if (m.Layer.length == 0) group()();
                },
                function(err, results) {
                    var result_map = to(results);
                    m.Layer = _.map(_.filter(m.Layer,
                        function(l) {
                            return l.Datasource.file &&
                                result_map[l.Datasource.file];
                        }),
                        function(l) {
                            l.Datasource.file = result_map[l.Datasource.file];
                            return l;
                        }
                    );
                    callback(err, m);
                }
            );
        },

        /**
         * Download any remote stylesheets
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        localizeStyle: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (!s.id) {
                            that.grab(s, group());
                        }
                    });
                    group()();
                },
                function(err, results) {
                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        }
                    }
                    callback(err, m);
                }
            );
        },

        /**
         * Compile (already downloaded) styles with mess.js,
         * calling callback with an array of [map object, [stylesheet objects]]
         *
         * Called with the results of localizeStyle or localizeExternals:
         * expects not to handle downloading.
         *
         * @param {Object} m map object.
         * @param {Function} callback
         */
        style: function(m, callback) {
            var that = this;
            Step(
                function() {
                    var group = this.group();
                    m.Stylesheet.forEach(function(s) {
                        if (s.id) {
                            group()(null, [s.id, s.data]);
                        } else {
                            fs.readFile(s, 'utf-8', function(err, data) {
                                group()(err, [s, data]);
                            });
                        }
                    });
                },
                function(e, results) {
                    var options = {},
                        group = this.group();
                    for (var i = 0, l = results.length; i < l; i++) {
                        new(mess.Parser)(_.extend(_.extend({
                            filename: s
                        }, that.env), this.env)).parse(results[i][1],
                            function(err, tree) {
                            if (err) {
                                mess.writeError(err, options);
                                throw err;
                            } else {
                                try {
                                    group()(err, [
                                        results[i][0],
                                        tree.toCSS({ compress: false }),
                                        tree]);
                                    return;
                                } catch (e) {
                                    throw e;
                                    return;
                                }
                            }
                        });
                    }
                },
                function(err, results) {
                    if (err) {
                        callback(err);
                        return;
                    }

                    var result_map = to(results);
                    for (s in m.Stylesheet) {
                        if (!m.Stylesheet[s].id) {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s]];
                        } else {
                            m.Stylesheet[s] = result_map[m.Stylesheet[s].id];
                        }
                    }
                    callback(err, [m, results]);
                }
            );
        },

        split_symbolizers: function(definitions) {
            var by_symbolizer = {};
            for (var i = 0; i < definitions.length; i++) {
                definitions[i].symbolizers().forEach(function(sym) {
                    if(!by_symbolizer[sym]) {
                        by_symbolizer[sym] = [];
                    }
                    by_symbolizer[sym].push(
                        definitions[i].filter_symbolizer(sym));
                });
            }
            return by_symbolizer;
        },

        /**
         * Pick the 'winners' - all elements that select
         * properly. Apply inherited styles from their
         * ancestors to them.
         */
        process_chain: function(definitions) {
            // definitions are ordered in specificity,
            // high to low
            //
            // basically if 'this level' has
            // a filter, then keep going, otherwise
            // this is the final selector.
            var winners = [];
            var ancestors = [];
            var below_threshold = false;

            while (def = definitions.shift()) {
                if (below_threshold) {
                    ancestors.push(def)
                } else if (def.selector.specificity()[2] > 0) {
                    winners.push(def);
                } else {
                    winners.push(def);
                    // nothing below this level will win
                    below_threshold = true;
                }
            }

            // iterate in reverse - low to high specificity
            for (var i = ancestors.length - 1; i >= 0; i--) {
                for (var j = 0; j < winners.length; j++) {
                    winners[j].inherit_from(ancestors[i]);
                }
            }

            winners = this.resolve_filters(winners);

            return winners;
        },

        // TODO: don't add negations to the unfiltered selectors
        resolve_filters: function(definitions) {
            var negated_filters = [];
            for (var i = 0; i < definitions.length; i++) {
                if (definitions[i].selector.filters.length) {
                    // array of the negation of this definition's filters
                    var negation = definitions[i].selector.filters.map(
                        function(f) { return f.negate(); });

                    // add in existing negations
                    definitions[i].selector.filters =
                        definitions[i].selector.filters.concat(negated_filters);

                    // add this definition's filter's negations to the list
                    negated_filters = negated_filters.concat(negation);
                }
            }
            return definitions;
        },

        /**
         * Prepare full XML map output. Called with the results
         * of this.style
         *
         * @param {Array} res array of [map object, stylesheets].
         * @param {Function} callback
         */
        template: function(err, res, callback) {
            if (err) {
                callback(err);
                return;
            }

            var that = this,
                m = res[0],
                stylesheets = res[1],

                entities = _.template('<?xml version="1.0" ' +
                    'encoding="utf-8"?>\n' +
                    '<!DOCTYPE Map[\n' +
                    '<% for (ref in externals) { %>' +
                    '  <!ENTITY <%= ref %> SYSTEM "<%= externals[ref] %>">\n' +
                    '<% } %>]>\n'),

                styles = _.template('<StyleName><%= name %></StyleName>'),

                layer = _.template('\n  <Layer\n   id="<%= id %>"\n' +
                    '   name="<%= name %>"\n' +
                    '   srs="<%= srs %>">\n' +
                    '<% for (s in styles) { %>' +
                    '    <StyleName><%= styles[s] %></StyleName>\n' +
                    '<% } %>' +
                    '    <Datasource>\n<% for (n in Datasource) { %>' +
                    '      <Parameter name="<%= n %>"><%= Datasource[n] %>' +
                    '</Parameter>\n' +
                    '<% } %>    </Datasource>\n' +
                    '  </Layer>\n'),

                stylesheet_tmpl = _.template('  <% for (i in Stylesheet) { %>' +
                    '<%= Stylesheet[i] %>\n' +
                    '<% } %>\n'),

                // referencing externals in the body of the map
                references = _.template('<% for (ref in externals) { %>' +
                    '&<%= ref %>;\n' +
                    '<% } %>'),

                // for the Map[] doctype: XML entities to
                // organize code
                entity_list = { externals: [], symbols: [] };

            /**
             * Index a hash of the CSS rules in all stylesheets
             * This is performance-critical: necessary to test
             * at some point
             *
             * @param {Array} stylesheets
             * @return {Array} list of selectors.
             */

            // var findBackground = function(stylesheets) {
            //     var b = '#FFFFFF';
            //     _.each(stylesheets, function(s) {
            //         _.each(s[2].rules, function(r) {
            //             if (r && r.selectors && r.selectors[0]._css &&
            //                 r.selectors[0]._css === 'Map') {
            //                 var bg = _.detect(r.rules, function(rule) {
            //                     return rule.name == 'background-color';
            //                 });
            //                 if (bg) {
            //                     b = bg.value.toCSS({ compress: false });
            //                 }
            //             }
            //         });
            //     });
            //     return b;
            // };

            /**
             * Add styles to a layer object
             *
             * @param {Object} layer the layer object.
             * @param {Array} stylesheets the results of ruleHash().
             * @return Formalized layer definition with .styles = [].
             */

            var rulesets = _.flatten(stylesheets.map(function(rulesets) {
                return rulesets[2].toMSS();
            }));

            var output = [entities(entity_list)];
            // TODO: must change when background colors are available
            output.push('<Map background-color="'
                    + "#ffffff"
                    // findBackground(stylesheets)
                    + '" srs="+proj=merc +a=6378137 +b=6378137 '
                    + '+lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m '
                    + '+nadgrids=@null +no_defs">\n');

            m.Layer.forEach(function(l) {
                l.styles = [];
                var classes = (l['class'] || '').split(/\s+/g);

                var matching = rulesets.filter(function(ruleset) {
                    return ruleset.selector.matches(l.id, classes);
                });

                // matching is an array of matching selectors,
                // in order from high specificity to low.
                var by_symbolizer = that.split_symbolizers(matching);

                var complete_chains = {};
                for (sym in by_symbolizer) {
                    var new_style = new(mess.tree.Style)(
                        l.id,
                        sym,
                        that.process_chain(by_symbolizer[sym]));
                    var n = new_style.name();
                    l.styles.push(n);
                    output.push(new_style.toXML());
                }
                var nl = new(mess.tree.Layer)(l);
                output.push(nl.toXML());
            });

            // output.push(references(entity_list));
            // output.push(stylesheet_tmpl(m));
            output.push('</Map>');

            callback(null, output.join('\n'));
        },

        /**
         * Prepare a JML document (given as a string) into a
         * fully-localized XML file ready for Mapnik2 consumption
         *
         * @param {String} str the JSON file as a string.
         * @param {Function} callback to be called with err, XML representation.
         */
        render: function(str, callback) {
            var m = JSON.parse(str),
                that = this;
            this.localizeExternals(m, function(err, res) {
                that.localizeStyle(res, function(err, res) {
                    that.style(res, function(err, res) {
                        that.template(err, res, function(err, res) {
                            callback(err, res);
                        });
                    });
                });
            });
        }
    };
};

module.exports = mess;
<|endoftext|>