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

var player = require('chromecast-player')();
var opts = require('minimist')(process.argv.slice(2));
var chalk = require('chalk');
var keypress = require('keypress');
var ui = require('playerui')();
var circulate = require('array-loop');
var xtend = require('xtend');
var shuffle = require('array-shuffle');
var unformatTime = require('./utils/unformat-time');
var debug = require('debug')('castnow');
var debouncedSeeker = require('debounced-seeker');
var noop = function() {};

// plugins
var directories = require('./plugins/directories');
var xspf = require('./plugins/xspf');
var localfile = require('./plugins/localfile');
var torrent = require('./plugins/torrent');
var transcode = require('./plugins/transcode');
var subtitles = require('./plugins/subtitles');
var stdin = require('./plugins/stdin');

if (opts.help) {
  return console.log([
    '',
    'Usage: castnow [<media>, <media>, ...] [OPTIONS]',
    '',
    'Option                   Meaning',
    '--tomp4                  Convert file to mp4 during playback',
    '--device <name>          The name of the Chromecast device that should be used',
    '--address <ip>           The IP address or hostname of your Chromecast device',
    '--subtitles <path/url>   Path or URL to an SRT or VTT file',
    '--subtitle-scale <scale> Subtitle font scale',
    '--subtitle-color <color> Subtitle font RGBA color',
    '--subtitle-port <port>   Specify the port to be used for serving subtitles',
    '--myip <ip>              Your local IP address',
    '--quiet                  No output',
    '--peerflix-* <value>     Pass options to peerflix',
    '--ffmpeg-* <value>       Pass options to ffmpeg',
    '--type <type>            Explicitly set the mime-type (e.g. "video/mp4")',
    '--bypass-srt-encoding    Disable automatic UTF-8 encoding of SRT subtitles',
    '--seek <hh:mm:ss>        Seek to the specified time on start using the format hh:mm:ss or mm:ss',
    '--loop                   Loop over playlist, or file, forever',
    '--shuffle                Play in random order',
    '--recursive              List all files in directories recursively',
    '--volume-step <step>     Step at which the volume changes. Helpful for speakers that are softer or louder than normal. Value ranges from 0 to 1 (e.g. ".05")',
    '--localfile-port <port>  Specify the port to be used for serving a local file',
    '--transcode-port <port>  Specify the port to be used for serving a transcoded file',
    '--torrent-port <port>    Specify the port to be used for serving a torrented file',
    '--stdin-port <port>      Specify the port to be used for serving a file read from stdin',
    '--command <key1>,<key2>  Execute key command(s) (where each <key> is one of the keys listed below)',
    '--exit                   Exit when playback begins or --command completes',

    '--help                   This help screen',
    '',
    'Player controls',
    '',
    'Key                      Action',
    'space                    Toggle between play and pause',
    'm                        Toggle mute',
    't                        Toggle subtitles',
    'up                       Volume Up',
    'down                     Volume Down',
    'left                     Seek backward',
    'right                    Seek forward',
    'n                        Next in playlist',
    's                        Stop playback',
    'quit                     Quit',
    ''
  ].join('\n'));
}

if (opts._.length) {
  opts.playlist = opts._.map(function(item) {
    return {
      path: item
    };
  });
}

delete opts._;

if (opts.quiet || opts.exit || process.env.DEBUG) {
  ui.hide();
}

var volumeStep = 0.05;
var stepOption = opts['volume-step'];

if (stepOption) {
  var parsed = parseFloat(stepOption);

  if (isNaN(parsed)) {
    fatalError('invalid --volume-step');
  }

  if (parsed < 0 || parsed > 1) {
    fatalError('--volume-step must be between 0 and 1');
  }

  volumeStep = parsed;
}

debug('volume step: %s', volumeStep);

ui.showLabels('state');

function fatalError(err) {
  ui.hide(err);
  debug(err);
  console.log(chalk.red(err));
  process.exit();
}

var last = function(fn, l) {
  return function() {
    var args = Array.prototype.slice.call(arguments);
    args.push(l);
    l = fn.apply(null, args);
    return l;
  };
};

var ctrl = function(err, p, ctx) {
  if (err) {
    ui.hide();
    debug('player error: %o', err);
    console.log(chalk.red(err));
    process.exit();
  }

  var playlist = ctx.options.playlist;
  var volume;
  var is_keyboard_interactive = process.stdin.isTTY || false;

  if (is_keyboard_interactive) {
    keypress(process.stdin);
    process.stdin.setRawMode(true);
    process.stdin.resume();
  }

  ctx.once('closed', function() {
    ui.hide();
    console.log(chalk.red('lost connection'));
    process.exit();
  });

  // get initial volume
  p.getVolume(function(err, status) {
    volume = status;
  });

  if (!ctx.options.disableTimeline) {
    p.on('position', function(pos) {
      ui.setProgress(pos.percent);
      ui.render();
    });
  }

  var seekImmediate = function(offset) {
    if (ctx.options.disableSeek || offset === 0) return;
    var seconds = Math.max(0, (p.getPosition() / 1000) + offset);
    debug('seeking to %s', seconds);
    p.seek(seconds);
  };

  if (opts.exit) {
    // cannot debounce or seek never executes before we exit
    var seek = seekImmediate;
  } else {
    var seek = debouncedSeeker(function(offset) {
      // handles seeking offset = seconds
      seekImmediate(offset);
    }, 500);
  }

  var updateTitle = function() {
    p.getStatus(function(err, status) {
      if (!status || !status.media ||
          !status.media.metadata ||
          !status.media.metadata.title) return;

      var metadata = status.media.metadata;
      var title;
      if (metadata.artist) {
        title = metadata.artist + ' - ' + metadata.title;
      } else {
        title = metadata.title;
      }
      ui.setLabel('source', 'Source', title);
      ui.showLabels('state', 'source');
      ui.render();
    });
  };

  var initialSeek = function() {
    var seconds = unformatTime(ctx.options.seek);
    debug('seeking to %s', seconds);
    p.seek(seconds);
  };

  p.on('playing', updateTitle);

  if (!ctx.options.disableSeek && ctx.options.seek) {
    p.once('playing', initialSeek);
  }

  updateTitle();

  var nextInPlaylist = function() {
    if (ctx.mode !== 'launch') return;
    if (!playlist.length) return process.exit();
    p.stop(function() {
      ui.showLabels('state');
      debug('loading next in playlist: %o', playlist[0]);
      p.load(playlist[0], noop);
      var file = playlist.shift();
      if (ctx.options.loop) playlist.push(file)
    });
  };

  p.on('status', last(function(status, memo) {
    if (opts.exit && status.playerState == 'PLAYING') process.exit();
    if (status.playerState !== 'IDLE') return;
    if (status.idleReason !== 'FINISHED') return;
    if (memo && memo.playerState === 'IDLE') return;
    nextInPlaylist();
    return status;
  }));

  var keyMappings = {

    // toggle between play / pause
    space: function() {
      if (p.currentSession.playerState === 'PLAYING') {
        p.pause();
      } else if (p.currentSession.playerState === 'PAUSED') {
        p.play();
      }
    },

    // toggle between mute / unmute
    m: function() {
      if(!volume) {
        return;
      } else if (volume.muted) {
        p.unmute(function(err, status) {
          if (err) return;
          volume = status;
        });
      } else {
        p.mute(function(err, status) {
          if (err) return;
          volume = status;
        });
      }
    },

    t: function() {
      if (!p.currentSession.media.tracks) { return }
      var sessionRequestBody = {
        type: 'EDIT_TRACKS_INFO'
      }
      sessionRequestBody.activeTrackIds = p.currentSession.activeTrackIds ? [] : [1];
      p.sessionRequest(sessionRequestBody);
    },

    // volume up
    up: function() {
      if (!volume || volume.level >= 1) {
        return;
      }

      var newVolume = Math.min(volume.level + volumeStep, 1);

      p.setVolume(newVolume, function(err, status) {
        if (err) {
          return;
        }

        debug("volume up: %s", status.level);

        volume = status;
      });
    },

    // volume down
    down: function() {
      if (!volume || volume.level <= 0) {
        return;
      }

      var newVolume = Math.max(volume.level - volumeStep, 0);

      p.setVolume(newVolume, function(err, status) {
        if (err) {
          return;
        }

        debug("volume down: %s", status.level);

        volume = status;
      });
    },

    // next item in playlist
    n: function() {
      nextInPlaylist();
    },

    // stop playback
    s: function() {
      p.stop();
    },

    // quit
    q: function() {
      process.exit();
    },

    // Rewind, one "seekCount" per press
    left: function() {
      seek(-30);
    },

    // Forward, one "seekCount" per press
    right: function() {
      seek(30);
    }
  };

  if (is_keyboard_interactive) {
    process.stdin.on('keypress', function(ch, key) {
      if (key && key.name && keyMappings[key.name]) {
        debug('key pressed: %s', key.name);
        keyMappings[key.name]();
      }
      if (key && key.ctrl && key.name == 'c') {
        process.exit();
      }
    });
  }

  if (opts.command) {
    var commands = opts.command.split(",");
    commands.forEach(function(command) {
      if (!keyMappings[command]) {
        fatalError('invalid --command: ' + command);
      }
    });

    var index = 0;
    function run_commands() {
      if (index < commands.length) {
        var command = commands[index++];
        keyMappings[command]();
        p.getStatus(run_commands);
      } else {
        if (opts.exit) {
          process.exit();
        }
      }
    }

    p.getStatus(run_commands);
  }
};

var capitalize = function(str) {
  return str.substr(0, 1).toUpperCase() + str.substr(1);
};

var logState = (function() {
  var inter;
  var dots = circulate(['.', '..', '...', '....']);
  return function(status) {
    if (inter) clearInterval(inter);
    debug('player status: %s', status);
    inter = setInterval(function() {
      ui.setLabel('state', 'State', capitalize(status) + dots());
      ui.render();
    }, 300);
  };
})();

player.use(function(ctx, next) {
  ctx.on('status', logState);
  next();
});

player.use(stdin);
player.use(directories);
player.use(torrent);
player.use(xspf);
player.use(localfile);
player.use(transcode);
player.use(subtitles);

player.use(function(ctx, next) {
  if (ctx.mode !== 'launch') return next();
  if (ctx.options.shuffle)
    ctx.options.playlist = shuffle(ctx.options.playlist);
  ctx.options = xtend(ctx.options, ctx.options.playlist[0]);
  var file = ctx.options.playlist.shift();
  if (ctx.options.loop) ctx.options.playlist.push(file);
  next();
});

if (!opts.playlist) {
  debug('attaching...');
  player.attach(opts, ctrl);
} else {
  debug('launching...');
  player.launch(opts, ctrl);
}

process.on('SIGINT', function() {
  process.exit();
});

process.on('exit', function() {
  ui.hide();
});

module.exports = player;
<commit_msg>Add a "previous" command (like "next").<commit_after>#!/usr/bin/env node

var player = require('chromecast-player')();
var opts = require('minimist')(process.argv.slice(2));
var chalk = require('chalk');
var keypress = require('keypress');
var ui = require('playerui')();
var circulate = require('array-loop');
var xtend = require('xtend');
var shuffle = require('array-shuffle');
var unformatTime = require('./utils/unformat-time');
var debug = require('debug')('castnow');
var debouncedSeeker = require('debounced-seeker');
var noop = function() {};

// plugins
var directories = require('./plugins/directories');
var xspf = require('./plugins/xspf');
var localfile = require('./plugins/localfile');
var torrent = require('./plugins/torrent');
var transcode = require('./plugins/transcode');
var subtitles = require('./plugins/subtitles');
var stdin = require('./plugins/stdin');

if (opts.help) {
  return console.log([
    '',
    'Usage: castnow [<media>, <media>, ...] [OPTIONS]',
    '',
    'Option                   Meaning',
    '--tomp4                  Convert file to mp4 during playback',
    '--device <name>          The name of the Chromecast device that should be used',
    '--address <ip>           The IP address or hostname of your Chromecast device',
    '--subtitles <path/url>   Path or URL to an SRT or VTT file',
    '--subtitle-scale <scale> Subtitle font scale',
    '--subtitle-color <color> Subtitle font RGBA color',
    '--subtitle-port <port>   Specify the port to be used for serving subtitles',
    '--myip <ip>              Your local IP address',
    '--quiet                  No output',
    '--peerflix-* <value>     Pass options to peerflix',
    '--ffmpeg-* <value>       Pass options to ffmpeg',
    '--type <type>            Explicitly set the mime-type (e.g. "video/mp4")',
    '--bypass-srt-encoding    Disable automatic UTF-8 encoding of SRT subtitles',
    '--seek <hh:mm:ss>        Seek to the specified time on start using the format hh:mm:ss or mm:ss',
    '--loop                   Loop over playlist, or file, forever',
    '--shuffle                Play in random order',
    '--recursive              List all files in directories recursively',
    '--volume-step <step>     Step at which the volume changes. Helpful for speakers that are softer or louder than normal. Value ranges from 0 to 1 (e.g. ".05")',
    '--localfile-port <port>  Specify the port to be used for serving a local file',
    '--transcode-port <port>  Specify the port to be used for serving a transcoded file',
    '--torrent-port <port>    Specify the port to be used for serving a torrented file',
    '--stdin-port <port>      Specify the port to be used for serving a file read from stdin',
    '--command <key1>,<key2>  Execute key command(s) (where each <key> is one of the keys listed below)',
    '--exit                   Exit when playback begins or --command completes',

    '--help                   This help screen',
    '',
    'Player controls',
    '',
    'Key                      Action',
    'space                    Toggle between play and pause',
    'm                        Toggle mute',
    't                        Toggle subtitles',
    'up                       Volume Up',
    'down                     Volume Down',
    'left                     Seek backward',
    'right                    Seek forward',
    'p                        Previous in playlist',
    'n                        Next in playlist',
    's                        Stop playback',
    'quit                     Quit',
    ''
  ].join('\n'));
}

if (opts._.length) {
  opts.playlist = opts._.map(function(item) {
    return {
      path: item
    };
  });
}

delete opts._;

if (opts.quiet || opts.exit || process.env.DEBUG) {
  ui.hide();
}

var volumeStep = 0.05;
var stepOption = opts['volume-step'];

if (stepOption) {
  var parsed = parseFloat(stepOption);

  if (isNaN(parsed)) {
    fatalError('invalid --volume-step');
  }

  if (parsed < 0 || parsed > 1) {
    fatalError('--volume-step must be between 0 and 1');
  }

  volumeStep = parsed;
}

debug('volume step: %s', volumeStep);

ui.showLabels('state');

function fatalError(err) {
  ui.hide(err);
  debug(err);
  console.log(chalk.red(err));
  process.exit();
}

var last = function(fn, l) {
  return function() {
    var args = Array.prototype.slice.call(arguments);
    args.push(l);
    l = fn.apply(null, args);
    return l;
  };
};

var ctrl = function(err, p, ctx) {
  if (err) {
    ui.hide();
    debug('player error: %o', err);
    console.log(chalk.red(err));
    process.exit();
  }

  var playlist = ctx.options.playlist;
  var playlist_history = ctx.options.playlist_history;
  var volume;
  var is_keyboard_interactive = process.stdin.isTTY || false;

  if (is_keyboard_interactive) {
    keypress(process.stdin);
    process.stdin.setRawMode(true);
    process.stdin.resume();
  }

  ctx.once('closed', function() {
    ui.hide();
    console.log(chalk.red('lost connection'));
    process.exit();
  });

  // get initial volume
  p.getVolume(function(err, status) {
    volume = status;
  });

  if (!ctx.options.disableTimeline) {
    p.on('position', function(pos) {
      ui.setProgress(pos.percent);
      ui.render();
    });
  }

  var seekImmediate = function(offset) {
    if (ctx.options.disableSeek || offset === 0) return;
    var seconds = Math.max(0, (p.getPosition() / 1000) + offset);
    debug('seeking to %s', seconds);
    p.seek(seconds);
  };

  if (opts.exit) {
    // cannot debounce or seek never executes before we exit
    var seek = seekImmediate;
  } else {
    var seek = debouncedSeeker(function(offset) {
      // handles seeking offset = seconds
      seekImmediate(offset);
    }, 500);
  }

  var updateTitle = function() {
    p.getStatus(function(err, status) {
      if (!status || !status.media ||
          !status.media.metadata ||
          !status.media.metadata.title) return;

      var metadata = status.media.metadata;
      var title;
      if (metadata.artist) {
        title = metadata.artist + ' - ' + metadata.title;
      } else {
        title = metadata.title;
      }
      ui.setLabel('source', 'Source', title);
      ui.showLabels('state', 'source');
      ui.render();
    });
  };

  var initialSeek = function() {
    var seconds = unformatTime(ctx.options.seek);
    debug('seeking to %s', seconds);
    p.seek(seconds);
  };

  p.on('playing', updateTitle);

  if (!ctx.options.disableSeek && ctx.options.seek) {
    p.once('playing', initialSeek);
  }

  updateTitle();

  var nextInPlaylist = function() {
    if (ctx.mode !== 'launch') return;
    if (!playlist.length) return process.exit();
    p.stop(function() {
      ui.showLabels('state');
      debug('loading next in playlist: %o', playlist[0]);
      p.load(playlist[0], noop);
      var file = playlist.shift();
      if (ctx.options.loop)
        playlist.push(file);
      else
        playlist_history.push(file);
    });
  };

  var previousInPlaylist = function() {
    if (ctx.options.loop) {
      playlist.unshift(playlist.pop());
      playlist.unshift(playlist.pop());
      nextInPlaylist();
    }
    else if (0 < playlist_history.length) {
      playlist.unshift(playlist_history.pop());
      if (0 < playlist_history.length) playlist.unshift(playlist_history.pop());
      nextInPlaylist();
    }
  };

  p.on('status', last(function(status, memo) {
    if (opts.exit && status.playerState == 'PLAYING') process.exit();
    if (status.playerState !== 'IDLE') return;
    if (status.idleReason !== 'FINISHED') return;
    if (memo && memo.playerState === 'IDLE') return;
    nextInPlaylist();
    return status;
  }));

  var keyMappings = {

    // toggle between play / pause
    space: function() {
      if (p.currentSession.playerState === 'PLAYING') {
        p.pause();
      } else if (p.currentSession.playerState === 'PAUSED') {
        p.play();
      }
    },

    // toggle between mute / unmute
    m: function() {
      if(!volume) {
        return;
      } else if (volume.muted) {
        p.unmute(function(err, status) {
          if (err) return;
          volume = status;
        });
      } else {
        p.mute(function(err, status) {
          if (err) return;
          volume = status;
        });
      }
    },

    t: function() {
      if (!p.currentSession.media.tracks) { return }
      var sessionRequestBody = {
        type: 'EDIT_TRACKS_INFO'
      }
      sessionRequestBody.activeTrackIds = p.currentSession.activeTrackIds ? [] : [1];
      p.sessionRequest(sessionRequestBody);
    },

    // volume up
    up: function() {
      if (!volume || volume.level >= 1) {
        return;
      }

      var newVolume = Math.min(volume.level + volumeStep, 1);

      p.setVolume(newVolume, function(err, status) {
        if (err) {
          return;
        }

        debug("volume up: %s", status.level);

        volume = status;
      });
    },

    // volume down
    down: function() {
      if (!volume || volume.level <= 0) {
        return;
      }

      var newVolume = Math.max(volume.level - volumeStep, 0);

      p.setVolume(newVolume, function(err, status) {
        if (err) {
          return;
        }

        debug("volume down: %s", status.level);

        volume = status;
      });
    },

    // next item in playlist
    n: function() {
      nextInPlaylist();
    },

    // previous item in playlist
    p: function() {
      previousInPlaylist();
    },

    // stop playback
    s: function() {
      p.stop();
    },

    // quit
    q: function() {
      process.exit();
    },

    // Rewind, one "seekCount" per press
    left: function() {
      seek(-30);
    },

    // Forward, one "seekCount" per press
    right: function() {
      seek(30);
    }
  };

  if (is_keyboard_interactive) {
    process.stdin.on('keypress', function(ch, key) {
      if (key && key.name && keyMappings[key.name]) {
        debug('key pressed: %s', key.name);
        keyMappings[key.name]();
      }
      if (key && key.ctrl && key.name == 'c') {
        process.exit();
      }
    });
  }

  if (opts.command) {
    var commands = opts.command.split(",");
    commands.forEach(function(command) {
      if (!keyMappings[command]) {
        fatalError('invalid --command: ' + command);
      }
    });

    var index = 0;
    function run_commands() {
      if (index < commands.length) {
        var command = commands[index++];
        keyMappings[command]();
        p.getStatus(run_commands);
      } else {
        if (opts.exit) {
          process.exit();
        }
      }
    }

    p.getStatus(run_commands);
  }
};

var capitalize = function(str) {
  return str.substr(0, 1).toUpperCase() + str.substr(1);
};

var logState = (function() {
  var inter;
  var dots = circulate(['.', '..', '...', '....']);
  return function(status) {
    if (inter) clearInterval(inter);
    debug('player status: %s', status);
    inter = setInterval(function() {
      ui.setLabel('state', 'State', capitalize(status) + dots());
      ui.render();
    }, 300);
  };
})();

player.use(function(ctx, next) {
  ctx.on('status', logState);
  next();
});

player.use(stdin);
player.use(directories);
player.use(torrent);
player.use(xspf);
player.use(localfile);
player.use(transcode);
player.use(subtitles);

player.use(function(ctx, next) {
  if (ctx.mode !== 'launch') return next();
  if (ctx.options.shuffle)
    ctx.options.playlist = shuffle(ctx.options.playlist);
  ctx.options = xtend(ctx.options, ctx.options.playlist[0]);
  var file = ctx.options.playlist.shift();
  if (ctx.options.loop)
    ctx.options.playlist.push(file);
  else
    ctx.options.playlist_history = [file];
  next();
});

if (!opts.playlist) {
  debug('attaching...');
  player.attach(opts, ctrl);
} else {
  debug('launching...');
  player.launch(opts, ctrl);
}

process.on('SIGINT', function() {
  process.exit();
});

process.on('exit', function() {
  ui.hide();
});

module.exports = player;
<|endoftext|>


<commit_before>var Grid = require('gridfs-stream');
var stream = require('stream');
var fs = require('fs');

function streamToBuffer(source, cb) {
  var chunks = [];
  var buffer = new stream.Writable();

  buffer._write = function (chunk, enc, cb) {
    chunks.push(chunk);
    cb();
  };

  source.on('end', function () {
    cb(null, Buffer.concat(chunks));
  });

  source.on('error', function (err) {
    cb(err);
  });

  source.pipe(buffer);
}

Grid.prototype.fromFile = function (options, source) {
  var ws = this.createWriteStream(options);
  var rs = typeof source === 'string' ? fs.createReadStream(source) : source;

  return {
    id: ws.id,
    save: function (cb) {
      ws.on('close', function (file) {
        return cb(null, file);
      });

      rs.pipe(ws);
    }
  };
};

Grid.prototype.toFile = function (options, target, cb) {
  var rs = this.createReadStream(options);
  var ws = typeof target === 'string' ? fs.createWriteStream(target) : target;

  rs.on('end', function () {
    cb(null);
  });

  rs.pipe(ws);
};

Grid.prototype.readFile = function (options, cb) {
  streamToBuffer(this.createReadStream(options), cb);
};

Grid.prototype.writeFile = function (options, data, cb) {
  data = typeof data === 'object' ? data.toString() : data;
  var ws = this.createWriteStream(options);

  ws.on('close', function (file) {
    cb(null, file);
  });

  ws.end(data);
};

module.exports = exports = Grid;
<commit_msg>add error listeners<commit_after>var Grid = require('gridfs-stream');
var stream = require('stream');
var fs = require('fs');

function streamToBuffer(source, cb) {
  var chunks = [];
  var buffer = new stream.Writable();

  buffer._write = function (chunk, enc, cb) {
    chunks.push(chunk);
    cb();
  };

  source.on('end', function () {
    cb(null, Buffer.concat(chunks));
  });

  source.on('error', function (err) {
    cb(err);
  });

  source.pipe(buffer);
}

Grid.prototype.fromFile = function (options, source) {
  var ws = this.createWriteStream(options);
  var rs = typeof source === 'string' ? fs.createReadStream(source) : source;

  return {
    id: ws.id,
    save: function (cb) {
      ws.on('close', function (file) {
        return cb(null, file);
      });

      ws.on('error', function (err) {
        cb(err);
      });

      rs.pipe(ws);
    }
  };
};

Grid.prototype.toFile = function (options, target, cb) {
  var rs = this.createReadStream(options);
  var ws = typeof target === 'string' ? fs.createWriteStream(target) : target;

  rs.on('end', function () {
    cb(null);
  });

  rs.pipe(ws);
};

Grid.prototype.readFile = function (options, cb) {
  streamToBuffer(this.createReadStream(options), cb);
};

Grid.prototype.writeFile = function (options, data, cb) {
  data = typeof data === 'object' ? data.toString() : data;
  var ws = this.createWriteStream(options);

  ws.on('error', function (err) {
    cb(err);
  });

  ws.on('close', function (file) {
    cb(null, file);
  });

  ws.end(data);
};

module.exports = exports = Grid;
<|endoftext|>


<commit_before>// Error constants
const CONFIG_REQUIRED = 'Configuration Object Required'
const PROCESS_REQUIRED = 'required paramenter [process] must be a function'
const SOURCE_REQUIRED = 'Source is required to be a function, promise or array'
const TYPE_PROCEED_ON_ERROR = 'parameter stopOnError must be a boolean'
const TYPE_EVENT_HANDLER = 'Event handlers must be functions'
const POOLING_REQUIRES_FUNCTION_SOURCE = 'Only Function source can be used with pooling'

class JobQueuer {
  constructor(config) {
    if (!config) throw new Error(CONFIG_REQUIRED)
    if (!config.process || typeof config.process !== 'function') throw new Error(PROCESS_REQUIRED)
    if (
      !config.source || (
        typeof config.source !== 'function' &&
        !Array.isArray(config.source) &&
        !config.source.then
      )
    ) throw new Error(SOURCE_REQUIRED)
    if (config.stopOnError && typeof config.stopOnError !== 'boolean') throw new Error(TYPE_PROCEED_ON_ERROR)
    this.events = {}
    this.debug = config.debug
    this.maxProceses = config.maxProceses >= 0 ? config.maxProceses : 1
    this.process = config.process
    this.stopOnError = config.stopOnError || false
    this.sourceType = Array.isArray(config.source) ? 'array' : config.source.then ? 'promise' : 'function'
    if (this.sourceType === 'array') {
      this.source = config.source.slice(0)
    } else {
      this.source = config.source
    }
    this.running = 0
    this.jobsFinished = 0
    this.jobErrors = 0
    this.fillingJobs = false
    this.autoincrementId = 0
    this.status = 'stoped'
    this.paused = false
    this.poolingInterval = config.pooling >= 0 ? config.pooling : false
    if (this.sourceType === 'array' && this.poolingInterval !== false) throw(new Error(POOLING_REQUIRES_FUNCTION_SOURCE))
  }

  data (data) {
    let obj = {
      startTime: this.startTime,
      processed: this.jobsFinished,
      errors: this.jobErrors,
      maxProceses: this.maxProceses,
      stopOnError: this.stopOnError,
      sourceType: this.sourceType,
      status: this.status
    }
    if (data) {
      for (let key in data) {
        if (data.hasOwnProperty(key)) obj[key] = data[key]
      }
    }
    return obj
  }

  start() {
    this.status = 'running'
    this.startTime = new Date()
    this.emit('start', this.data())
    this.init()
  }

  pause () {
    if (this.status === 'running' || this.status === 'pooling') {
      this.paused = true
      this.status = 'paused'
      this.emit('pause', this.data())
    }
  }

  resume () {
    if (this.status !== 'running') {
      this.paused = false
      this.status = 'running'
      this.emit('resume', this.data())
      this.fillJobs()
    }
  }

  init () {
    if (this.sourceType === 'promise') {
      this.source.then((data) => {
        this.sourceType = Array.isArray(data) ? 'array' : data.then ? 'promise' : 'function'
        if (this.sourceType === 'array') {
          if (this.poolingInterval !== false) throw(new Error(POOLING_REQUIRES_FUNCTION_SOURCE))
          this.source = data.slice(0)
        } else {
          this.source = data
        }
        this.init()
      }).catch((err) => {
        this.processFinish(err)
      })
    } else {
      this.fillJobs()
    }
  }

  processFinish (err) {
    if (err) {
      this.emit('error', err)
      this.status = 'error'
    } else {
      this.status = 'finished'
    }
    if (this.poolingInterval === false) {
      this.emit('processFinish', this.data({endTime: new Date()}))
    } else {
      this.status = 'pooling'
      this.empty = false
      this.emit('pooling', this.data())
      setTimeout(() => {
        this.fillJobs()
      }, this.poolingInterval)
    }
  }

  on(event, handler) {
    this.events[event] = handler
    return this
  }

  emit(event, payload) {
    if (event === 'error' && this.stopOnError) this.status = 'error'
    if (this.debug && console) console.log(`[${new Date()}][${event}]`, payload)
    if (this.events[event]) this.events[event](payload)
  }

  runningJobsCount() {
    return this.running
  }

  runJob(jobPromise) {
    this.running++
    let jobId = ++this.autoincrementId
    this.emit('jobRun', jobId)
    let next = () => {
      let runningCount = --this.running
      if ((!runningCount && this.status === 'empty') || this.status === 'error') {
        this.status = 'finished'
        return this.processFinish()
      }
      this.fillJobs()
    }

    let jobStartTime = new Date()
    jobPromise((err, result) => {
      if (err) {
        this.emit('error', err)
        this.jobErrors ++
      } else if (result) {
        let jobEndTime = new Date()
        this.emit('jobFinish', {
          jobId,
          jobStartTime,
          jobEndTime,
          result,
          jobsRunning: this.running
        })
        this.jobsFinished ++
      }
      next()
    })
  }

  fillJobs () {
    if (this.fillingJobs) return
    this.fillingJobs = true

    const resolveJobValue = (jobValue, done) => {
      try {
        let resolved = false
        if (jobValue !== null) {
          let jobPromise = this.process(jobValue, (err, value) => {
            if (!resolved) done(err, value)
          })
          if (jobPromise) {
            resolved = true
            if (typeof jobPromise.then === 'function') {
              return jobPromise.then((data) => {
                done(null, data)
              }).catch(done)
            }
            done(null, jobPromise)
          }
        } else {
          this.status = 'empty'
          done()
        }
      } catch (e) {
        done(e)
      }
    }

    while (
      !this.paused &&
      (this.maxProceses === 0 || this.running < this.maxProceses)
      && this.status === 'running'
      && ((this.sourceType === 'array' && this.source.length) || (this.sourceType !== 'array'))
    ) {
      this.emit('jobFetch', {
        jobsRunning: this.running
      })

      const job = (done) => {
        let item
        let resolved = false
        if (this.sourceType === 'array') {
          item = this.source.splice(0, 1)[0]
          if (!this.source.length) this.status = 'empty'
        } else {
          item = this.source((err, jobValue) => {
            if (!resolved) {
              if (err) return done(err)
              resolveJobValue(jobValue, done)
            }
          })
        }
        if (undefined !== item) {
          if (item && item.then && typeof item.then === 'function') {
            item.then((jobValue) => {
              resolveJobValue(jobValue, done)
            }).catch(done)
          } else {
            if (item !== null) {
              resolved = true
              resolveJobValue(item, done)
            } else {
              this.status = 'empty'
              resolved = true
              done()
            }
          }
        }
      }
      this.runJob(job)
    }
    this.fillingJobs = false
  }
}

class JobQ {
  constructor (options) {
    this.instance = new JobQueuer(options)
  }

  on (event, handler) {
    this.instance.on(event, handler)
    return this
  }

  start () {
    this.instance.start()
    return this
  }

  pause () {
    this.instance.pause()
    return this
  }

  resume () {
    this.instance.resume()
    return this
  }

  runningJobsCount () {
    return this.instance.runningJobsCount()
  }
}

module.exports = JobQ

<commit_msg>pooling fix<commit_after>// Error constants
const CONFIG_REQUIRED = 'Configuration Object Required'
const PROCESS_REQUIRED = 'required paramenter [process] must be a function'
const SOURCE_REQUIRED = 'Source is required to be a function, promise or array'
const TYPE_PROCEED_ON_ERROR = 'parameter stopOnError must be a boolean'
const TYPE_EVENT_HANDLER = 'Event handlers must be functions'
const POOLING_REQUIRES_FUNCTION_SOURCE = 'Only Function source can be used with pooling'

class JobQueuer {
  constructor(config) {
    if (!config) throw new Error(CONFIG_REQUIRED)
    if (!config.process || typeof config.process !== 'function') throw new Error(PROCESS_REQUIRED)
    if (
      !config.source || (
        typeof config.source !== 'function' &&
        !Array.isArray(config.source) &&
        !config.source.then
      )
    ) throw new Error(SOURCE_REQUIRED)
    if (config.stopOnError && typeof config.stopOnError !== 'boolean') throw new Error(TYPE_PROCEED_ON_ERROR)
    this.events = {}
    this.debug = config.debug
    this.maxProceses = config.maxProceses >= 0 ? config.maxProceses : 1
    this.process = config.process
    this.stopOnError = config.stopOnError || false
    this.sourceType = Array.isArray(config.source) ? 'array' : config.source.then ? 'promise' : 'function'
    if (this.sourceType === 'array') {
      this.source = config.source.slice(0)
    } else {
      this.source = config.source
    }
    this.running = 0
    this.jobsFinished = 0
    this.jobErrors = 0
    this.fillingJobs = false
    this.autoincrementId = 0
    this.status = 'stoped'
    this.paused = false
    this.poolingInterval = config.pooling >= 0 ? config.pooling : false
    if (this.sourceType === 'array' && this.poolingInterval !== false) throw(new Error(POOLING_REQUIRES_FUNCTION_SOURCE))
  }

  data (data) {
    let obj = {
      startTime: this.startTime,
      processed: this.jobsFinished,
      errors: this.jobErrors,
      maxProceses: this.maxProceses,
      stopOnError: this.stopOnError,
      sourceType: this.sourceType,
      status: this.status
    }
    if (data) {
      for (let key in data) {
        if (data.hasOwnProperty(key)) obj[key] = data[key]
      }
    }
    return obj
  }

  start() {
    this.status = 'running'
    this.startTime = new Date()
    this.emit('start', this.data())
    this.init()
  }

  pause () {
    if (this.status === 'running' || this.status === 'pooling') {
      this.paused = true
      this.status = 'paused'
      this.emit('pause', this.data())
    }
  }

  resume () {
    if (this.status !== 'running') {
      this.paused = false
      this.status = 'running'
      this.emit('resume', this.data())
      this.fillJobs()
    }
  }

  init () {
    if (this.sourceType === 'promise') {
      this.source.then((data) => {
        this.sourceType = Array.isArray(data) ? 'array' : data.then ? 'promise' : 'function'
        if (this.sourceType === 'array') {
          if (this.poolingInterval !== false) throw(new Error(POOLING_REQUIRES_FUNCTION_SOURCE))
          this.source = data.slice(0)
        } else {
          this.source = data
        }
        this.init()
      }).catch((err) => {
        this.processFinish(err)
      })
    } else {
      this.fillJobs()
    }
  }

  processFinish (err) {
    if (err) {
      this.emit('error', err)
      this.status = 'error'
    } else {
      this.status = 'finished'
    }
    if (this.poolingInterval === false) {
      this.emit('processFinish', this.data({endTime: new Date()}))
    } else {
      this.status = 'pooling'
      this.empty = false
      this.emit('pooling', this.data())
      setTimeout(() => {
        this.status = 'running'
        this.fillJobs()
      }, this.poolingInterval)
    }
  }

  on(event, handler) {
    this.events[event] = handler
    return this
  }

  emit(event, payload) {
    if (event === 'error' && this.stopOnError) this.status = 'error'
    if (this.debug && console) console.log(`[${new Date()}][${event}]`, payload)
    if (this.events[event]) this.events[event](payload)
  }

  runningJobsCount() {
    return this.running
  }

  runJob(jobPromise) {
    this.running++
    let jobId = ++this.autoincrementId
    this.emit('jobRun', jobId)
    let next = () => {
      let runningCount = --this.running
      if ((!runningCount && this.status === 'empty') || this.status === 'error') {
        this.status = 'finished'
        return this.processFinish()
      }
      this.fillJobs()
    }

    let jobStartTime = new Date()
    jobPromise((err, result) => {
      if (err) {
        this.emit('error', err)
        this.jobErrors ++
      } else if (result) {
        let jobEndTime = new Date()
        this.emit('jobFinish', {
          jobId,
          jobStartTime,
          jobEndTime,
          result,
          jobsRunning: this.running
        })
        this.jobsFinished ++
      }
      next()
    })
  }

  fillJobs () {
    if (this.fillingJobs) return
    this.fillingJobs = true

    const resolveJobValue = (jobValue, done) => {
      try {
        let resolved = false
        if (jobValue !== null) {
          let jobPromise = this.process(jobValue, (err, value) => {
            if (!resolved) done(err, value)
          })
          if (jobPromise) {
            resolved = true
            if (typeof jobPromise.then === 'function') {
              return jobPromise.then((data) => {
                done(null, data)
              }).catch(done)
            }
            done(null, jobPromise)
          }
        } else {
          this.status = 'empty'
          done()
        }
      } catch (e) {
        done(e)
      }
    }

    while (
      !this.paused &&
      (this.maxProceses === 0 || this.running < this.maxProceses)
      && this.status === 'running'
      && ((this.sourceType === 'array' && this.source.length) || (this.sourceType !== 'array'))
    ) {
      this.emit('jobFetch', {
        jobsRunning: this.running
      })

      const job = (done) => {
        let item
        let resolved = false
        if (this.sourceType === 'array') {
          item = this.source.splice(0, 1)[0]
          if (!this.source.length) this.status = 'empty'
        } else {
          item = this.source((err, jobValue) => {
            if (!resolved) {
              if (err) return done(err)
              resolveJobValue(jobValue, done)
            }
          })
        }
        if (undefined !== item) {
          if (item && item.then && typeof item.then === 'function') {
            item.then((jobValue) => {
              resolveJobValue(jobValue, done)
            }).catch(done)
          } else {
            if (item !== null) {
              resolved = true
              resolveJobValue(item, done)
            } else {
              this.status = 'empty'
              resolved = true
              done()
            }
          }
        }
      }
      this.runJob(job)
    }
    this.fillingJobs = false
  }
}

class JobQ {
  constructor (options) {
    this.instance = new JobQueuer(options)
  }

  on (event, handler) {
    this.instance.on(event, handler)
    return this
  }

  start () {
    this.instance.start()
    return this
  }

  pause () {
    this.instance.pause()
    return this
  }

  resume () {
    this.instance.resume()
    return this
  }

  runningJobsCount () {
    return this.instance.runningJobsCount()
  }
}

module.exports = JobQ

<|endoftext|>


<commit_before>var fs = require('fs');

if(process.env.EXIT_ON_CHANGES) {
	console.log("In development mode, will exit on file changes in cwd");
	fs.watch('.', {}, function(ev, filename) {
		if(/\.js$/.test(filename)) {
			console.log("Exiting because:", ev, filename);
			process.exit(0);
		}
	})
}
<commit_msg>Fix line endings; watch more file extensions; disable on EXIT_ON_CHANGES=0<commit_after>var fs = require('fs');

if(Number(process.env.EXIT_ON_CHANGES)) {
	console.log("[exit-on-changes] EXIT_ON_CHANGES enabled");
	fs.watch('.', {}, function(ev, filename) {
		if(/\.(jsx?|es6?)$/.test(filename)) {
			console.log("[exit-on-changes] Exiting after:", ev, filename);
			process.exit(0);
		}
	})
}
<|endoftext|>


<commit_before>var THREE = require("three");
var OrbitControls = require('./orbitcontrols');
var cgaparser = require("./cgaparser");
var cgaprocessor = require("./cgaprocessor");

require("./format");

function $(sel) {
  return document.querySelector(sel);
}

$.create = function(n, v, c) {
  n = document.createElement(n);
  for (var p in v)
    n.setAttribute(p, v[p]);
  if (c) n.innerHTML = c;
  return n;
};

var examples = {
  basic: 'Lot --> extrude(2) split(y) { 0.2 : r(0, rand(0,30), 0) Floor }*',
  house: "Lot --> extrude(1) comp(f) { front: Front | side: Side | top: Roof | back : Back }\n\
\n\
Roof --> color(\"red\") taper(1) \n\
\n\
Front --> color(\"white\") split(x) { '0.3: Wall | '0.4 : DoorWall | '0.3: Wall }\n\
\n\
DoorWall --> split(y) { '0.8: Door | '0.2 : Wall }\n\
\n\
Door --> color(\"white\")"
};

function setup() {
  var canvas = $('canvas');

  var lot = 'triangle';
  var grammar;
  var last;

  $('.lot-selector').addEventListener('change', e => {
    lot=e.target.value;
    update();
  });

  $('#updateBtn').addEventListener('click', update);

  Object.keys(examples).forEach(e => $('.example-selector').appendChild( $.create('option', null, e) ) );
  $('.example-selector').addEventListener('change', e => {
    if (!e.target.value) return;
    $('textarea').value = examples[e.target.value];
    parse();
  });

  var grammarText = localStorage.getItem('grammar');
  if (grammarText) $('textarea').value = grammarText;

  var scene = new THREE.Scene();
  scene.fog = new THREE.FogExp2( 0x112244, 0.06 );

  var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000 );
  camera.position.z = 5;
  camera.position.y = 3;
  camera.rotation.x = -0.5;
  camera.aspect = canvas.clientWidth / canvas.clientHeight;

  var controls = new OrbitControls( camera, canvas );
  controls.autoRotate = true;
  controls.enableZoom = true;
  controls.maxPolarAngle = Math.PI/2-0.01;

  var renderer = new THREE.WebGLRenderer({ antialias: true, canvas: canvas });
  renderer.setSize( canvas.clientWidth, canvas.clientHeight );
  renderer.setClearColor( scene.fog.color );
  renderer.shadowMap.soft = true;
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap

  var default_material = new THREE.MeshLambertMaterial( { color: 0x6699ff } );
  var wire_material = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 2 } );
  var grass_material = new THREE.MeshPhongMaterial( { color: 0x44bb55 } );

  var group = new THREE.Group();
  group.castShadow=true;
  scene.add( group );

  var ground = new THREE.Mesh(new THREE.PlaneGeometry( 150, 150 ), grass_material);
  ground.receiveShadow = true;
  ground.rotateX(-Math.PI/2);
  ground.position.y = -0.01;
  ground.scale.set(100,100,100);
  scene.add(ground);

  //Create a DirectionalLight and turn on shadows for the light
  var light = new THREE.DirectionalLight( 0xffffff, 1, 100 );
  light.position.set( 3, 2, 3); 			//default; light shining from top

  light.castShadow = true;            // default false
  //Set up shadow properties for the light
  // light.shadow.mapSize.width = 1024;
  // light.shadow.mapSize.height = 1024;

  scene.add( light );

  // //Create a helper for the shadow camera (optional)
  // var helper = new THREE.CameraHelper( light.shadow.camera );
  // scene.add( helper );
  scene.add(new THREE.AmbientLight( 0x404040 )); // soft white light

  window.addEventListener( 'resize', onWindowResize, false );

  function onWindowResize() {
	camera.aspect = canvas.parentElement.clientWidth / canvas.parentElement.clientHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( canvas.parentElement.clientWidth-30, canvas.parentElement.clientHeight );
  }

  function render() {
    controls.update();
	requestAnimationFrame( render );
	renderer.render( scene, camera );
  }
  render();

  $('textarea').addEventListener('keyup', parse);

  function parse() {
    if ($('textarea').value == last) return;
    $('#error').innerHTML = '';
    try {
      last = $('textarea').value;
      grammar = cgaparser.parse(last);
      $('#out').innerHTML = String(grammar);

      localStorage.setItem('grammar', last);
      update();

    } catch (e) {
      console.log(e);
      $('#error').innerHTML=e;
      return ;
    }
  }

  function update() {
    var lotGeom = new THREE.Geometry();

    if ( lot == 'triangle') {
      lotGeom.vertices.push( new THREE.Vector3( -1, 0, 0 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1, 0, 0 ) );
      lotGeom.vertices.push( new THREE.Vector3(  0, 0, 2*Math.sqrt(3)/2 ) );

      lotGeom.faces.push( new THREE.Face3( 0, 2, 1 ) );
    } else if ( lot == 'square' ) {
      lotGeom.vertices.push( new THREE.Vector3( -1, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1, 0, 1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  -1, 0, 1 ) );

      lotGeom.faces.push( new THREE.Face3( 0, 2, 1 ) );
      lotGeom.faces.push( new THREE.Face3( 2, 0, 3 ) );
    } else if (lot == 'rectangle') {
      lotGeom.vertices.push( new THREE.Vector3( -1.5, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1.5, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1.5, 0, 1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  -1.5, 0, 1 ) );

      lotGeom.faces.push( new THREE.Face3( 0, 2, 1 ) );
      lotGeom.faces.push( new THREE.Face3( 2, 0, 3 ) );

    }
    lotGeom.computeFaceNormals();
    lotGeom.computeVertexNormals();

    for( var i = group.children.length - 1; i >= 0; i--) {
      var e = group.children[i];
      group.remove(e);
      e.geometry.dispose();

    }

    var proc = new cgaprocessor.Processor(grammar);
    var res = proc.process(lotGeom);
    console.log(res);

    res.forEach(r => {
      r.computeFaceNormals();
      r.computeVertexNormals();

      var material = default_material;

      var attrs = r.attrs;
      if ( attrs && attrs.material && attrs.material.color ) material = new THREE.MeshLambertMaterial({ color: attrs.material.color });

      var mesh = new THREE.Mesh(new THREE.BufferGeometry().fromGeometry(r), material);
      mesh.castShadow = true;
      group.add(mesh);

      var wireframe = new THREE.LineSegments( new THREE.EdgesGeometry(r), wire_material );
      //var wireframe = new THREE.LineSegments( new THREE.WireframeGeometry(r), wire_material );
      group.add( wireframe );

    });

  }

  parse();
}

setup();
<commit_msg>center triangle lot on 0,0<commit_after>var THREE = require("three");
var OrbitControls = require('./orbitcontrols');
var cgaparser = require("./cgaparser");
var cgaprocessor = require("./cgaprocessor");

require("./format");

function $(sel) {
  return document.querySelector(sel);
}

$.create = function(n, v, c) {
  n = document.createElement(n);
  for (var p in v)
    n.setAttribute(p, v[p]);
  if (c) n.innerHTML = c;
  return n;
};

var examples = {
  basic: 'Lot --> extrude(2) split(y) { 0.2 : r(0, rand(0,30), 0) Floor }*',
  house: "Lot --> extrude(1) comp(f) { front: Front | side: Side | top: Roof | back : Back }\n\
\n\
Roof --> color(\"red\") taper(1) \n\
\n\
Front --> color(\"white\") split(x) { '0.3: Wall | '0.4 : DoorWall | '0.3: Wall }\n\
\n\
DoorWall --> split(y) { '0.8: Door | '0.2 : Wall }\n\
\n\
Door --> color(\"white\")"
};

function setup() {
  var canvas = $('canvas');

  var lot = 'triangle';
  var grammar;
  var last;

  $('.lot-selector').addEventListener('change', e => {
    lot=e.target.value;
    update();
  });

  $('#updateBtn').addEventListener('click', update);

  Object.keys(examples).forEach(e => $('.example-selector').appendChild( $.create('option', null, e) ) );
  $('.example-selector').addEventListener('change', e => {
    if (!e.target.value) return;
    $('textarea').value = examples[e.target.value];
    parse();
  });

  var grammarText = localStorage.getItem('grammar');
  if (grammarText) $('textarea').value = grammarText;

  var scene = new THREE.Scene();
  scene.fog = new THREE.FogExp2( 0x112244, 0.06 );

  var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000 );
  camera.position.z = 5;
  camera.position.y = 3;
  camera.rotation.x = -0.5;
  camera.aspect = canvas.clientWidth / canvas.clientHeight;

  var controls = new OrbitControls( camera, canvas );
  controls.autoRotate = true;
  controls.enableZoom = true;
  controls.maxPolarAngle = Math.PI/2-0.01;

  var renderer = new THREE.WebGLRenderer({ antialias: true, canvas: canvas });
  renderer.setSize( canvas.clientWidth, canvas.clientHeight );
  renderer.setClearColor( scene.fog.color );
  renderer.shadowMap.soft = true;
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap

  var default_material = new THREE.MeshLambertMaterial( { color: 0x6699ff } );
  var wire_material = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 2 } );
  var grass_material = new THREE.MeshPhongMaterial( { color: 0x44bb55 } );

  var group = new THREE.Group();
  group.castShadow=true;
  scene.add( group );

  var ground = new THREE.Mesh(new THREE.PlaneGeometry( 150, 150 ), grass_material);
  ground.receiveShadow = true;
  ground.rotateX(-Math.PI/2);
  ground.position.y = -0.01;
  ground.scale.set(100,100,100);
  scene.add(ground);

  //Create a DirectionalLight and turn on shadows for the light
  var light = new THREE.DirectionalLight( 0xffffff, 1, 100 );
  light.position.set( 3, 2, 3); 			//default; light shining from top

  light.castShadow = true;            // default false
  //Set up shadow properties for the light
  // light.shadow.mapSize.width = 1024;
  // light.shadow.mapSize.height = 1024;

  scene.add( light );

  // //Create a helper for the shadow camera (optional)
  // var helper = new THREE.CameraHelper( light.shadow.camera );
  // scene.add( helper );
  scene.add(new THREE.AmbientLight( 0x404040 )); // soft white light

  window.addEventListener( 'resize', onWindowResize, false );

  function onWindowResize() {
	camera.aspect = canvas.parentElement.clientWidth / canvas.parentElement.clientHeight;
	camera.updateProjectionMatrix();
	renderer.setSize( canvas.parentElement.clientWidth-30, canvas.parentElement.clientHeight );
  }

  function render() {
    controls.update();
	requestAnimationFrame( render );
	renderer.render( scene, camera );
  }
  render();

  $('textarea').addEventListener('keyup', parse);

  function parse() {
    if ($('textarea').value == last) return;
    $('#error').innerHTML = '';
    try {
      last = $('textarea').value;
      grammar = cgaparser.parse(last);
      $('#out').innerHTML = String(grammar);

      localStorage.setItem('grammar', last);
      update();

    } catch (e) {
      console.log(e);
      $('#error').innerHTML=e;
      return ;
    }
  }

  function update() {
    var lotGeom = new THREE.Geometry();

    if ( lot == 'triangle') {
      var h = Math.sqrt(3);
      lotGeom.vertices.push( new THREE.Vector3( -1, 0,  h/2 ) );
      lotGeom.vertices.push( new THREE.Vector3(  0, 0, -h/2 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1, 0,  h/2 ) );

      lotGeom.faces.push( new THREE.Face3( 0, 2, 1 ) );
    } else if ( lot == 'square' ) {
      lotGeom.vertices.push( new THREE.Vector3( -1, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1, 0, 1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  -1, 0, 1 ) );

      lotGeom.faces.push( new THREE.Face3( 0, 2, 1 ) );
      lotGeom.faces.push( new THREE.Face3( 2, 0, 3 ) );
    } else if (lot == 'rectangle') {
      lotGeom.vertices.push( new THREE.Vector3( -1.5, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1.5, 0, -1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  1.5, 0, 1 ) );
      lotGeom.vertices.push( new THREE.Vector3(  -1.5, 0, 1 ) );

      lotGeom.faces.push( new THREE.Face3( 0, 2, 1 ) );
      lotGeom.faces.push( new THREE.Face3( 2, 0, 3 ) );

    }
    lotGeom.computeFaceNormals();
    lotGeom.computeVertexNormals();

    for( var i = group.children.length - 1; i >= 0; i--) {
      var e = group.children[i];
      group.remove(e);
      e.geometry.dispose();

    }

    var proc = new cgaprocessor.Processor(grammar);
    var res = proc.process(lotGeom);
    console.log(res);

    res.forEach(r => {
      r.computeFaceNormals();
      r.computeVertexNormals();

      var material = default_material;

      var attrs = r.attrs;
      if ( attrs && attrs.material && attrs.material.color ) material = new THREE.MeshLambertMaterial({ color: attrs.material.color });

      var mesh = new THREE.Mesh(new THREE.BufferGeometry().fromGeometry(r), material);
      mesh.castShadow = true;
      group.add(mesh);

      var wireframe = new THREE.LineSegments( new THREE.EdgesGeometry(r), wire_material );
      //var wireframe = new THREE.LineSegments( new THREE.WireframeGeometry(r), wire_material );
      group.add( wireframe );

    });

  }

  parse();
}

setup();
<|endoftext|>


<commit_before>// Import Dependencies.
var gulp = require("gulp");
var concat = require("gulp-concat");
var replace = require("gulp-replace");

// Read the full config.
var config = require("../config.json");

/**
 * Process the .d.ts files or Playground intellisense.
 */
gulp.task("intellisense", function() {
    return gulp.src(config.build.intellisenseSources)
        .pipe(concat(config.build.intellisenseFile))
        .pipe(replace(/^\s+_.*?;/gm, ""))
        .pipe(replace(/^\s+_[\S\s]*?}/gm, ""))
        .pipe(replace(/^\s*readonly _/gm, "protected readonly _"))
        .pipe(replace(/^\s*static _/gm, "private static _"))
        .pipe(replace(/^\s*abstract _/gm, ""))
        .pipe(gulp.dest(config.build.playgroundDirectory));
});<commit_msg>Fix intellisense generation<commit_after>// Import Dependencies.
var gulp = require("gulp");
var concat = require("gulp-concat");
var replace = require("gulp-replace");

// Read the full config.
var config = require("../config.json");

/**
 * Process the .d.ts files or Playground intellisense.
 */
gulp.task("intellisense", function() {
    return gulp.src(config.build.intellisenseSources)
        .pipe(concat(config.build.intellisenseFile))
        .pipe(replace(/^\s*readonly _/gm, "protected readonly _"))
        .pipe(replace(/^\s*static _/gm, "private static _"))
        .pipe(replace(/^\s*class _/gm, "private class _"))
        .pipe(replace(/^\s* _/gm, ""))
        .pipe(replace(/^\s*_/gm, "private _"))
        .pipe(gulp.dest(config.build.playgroundDirectory));
});<|endoftext|>


<commit_before>dc.ui.Toolbar = dc.View.extend({

  id : 'toolbar',

  callbacks : {
    '#open_viewers.click'            : '_openViewers',
    '#toolbar_upload.click'          : '_openUpload'
  },

  MENUS : ['project', 'edit', 'publish', 'analyze'],

  constructor : function(options) {
    this._floating = false;
    this.base(options);
    _.bindAll(this, '_updateSelectedDocuments',
      '_deleteSelectedDocuments', 'editTitle', 'editSource', 'editDescription',
      'editRelatedArticle', 'editAccess', 'displayEmbedSnippet', 'checkFloat',
      '_openTimeline', '_viewEntities');
    this.editMenu         = this._createEditMenu();
    this.publishMenu      = this._createPublishMenu();
    this.analyzeMenu      = this._createAnalyzeMenu();
    this.projectMenu      = new dc.ui.ProjectMenu({onClick : this._updateSelectedDocuments});
  },

  render : function() {
    var el = $(this.el);
    el.html(JST['workspace/toolbar']({}));
    _.each(this.MENUS, _.bind(function(menu){
      $('.' + menu + '_menu_container', el).append(this[menu + 'Menu'].render().el);
    }, this));
    this.openButton              = $('#open_viewers', this.el);
    this.floatEl                 = $('#floating_toolbar', this.el);
    $(window).scroll(this.checkFloat);
    this.setCallbacks();
    return this;
  },

  notifyProjectChange : function(projectName, numDocs, removal) {
    var prefix = removal ? 'Removed ' : 'Added ';
    var prep   = removal ? ' from "'  : ' to "';
    var notification = prefix + numDocs + ' ' + Inflector.pluralize('document', numDocs) + prep + projectName + '"';
    dc.ui.notifier.show({mode : 'info', text : notification});
  },

  // Wrapper function for safely editing an attribute of a specific document.
  edit : function(callback, message) {
    if (!Documents.allowedToEditSelected(message)) return;
    return callback.call(this, Documents.selected());
  },

  editTitle : function() {
    this.edit(function(docs) {
      var doc = docs[0];
      dc.ui.Dialog.prompt('Title', doc.get('title'), function(title) {
        Documents.update(doc, {title : title});
        return true;
      }, {mode : 'short_prompt'});
    });
  },

  editDescription : function() {
    this.edit(function(docs) {
      var doc = docs[0];
      dc.ui.Dialog.prompt('Description', doc.get('description'), function(description) {
        Documents.update(doc, {description : description});
        return true;
      });
    });
  },

  editSource : function() {
    this.edit(function(docs) {
      var current = Documents.sharedAttribute(docs, 'source') || '';
      dc.ui.Dialog.prompt('Source', current, function(source) {
        _.each(docs, function(doc) { Documents.update(doc, {source : source}); });
        return true;
      }, {mode : 'short_prompt', information : this._subtitle(docs.length)});
    });
  },

  editRelatedArticle : function() {
    this.edit(function(docs) {
      var current = Documents.sharedAttribute(docs, 'related_article') || '';
      dc.ui.Dialog.prompt('Related Article', current, function(rel) {
        _.each(docs, function(doc) { Documents.update(doc, {related_article : rel}); });
        return true;
      }, {mode : 'short_prompt', information : this._subtitle(docs.length)});
    });
  },

  editAccess : function() {
    if (!Documents.allowedToEditSelected()) return;
    var docs    = Documents.selected();
    var current = Documents.sharedAttribute(docs, 'access') || dc.access.PRIVATE;
    dc.ui.Dialog.choose('Access Level', [
      {text : 'Public Access',  description : 'Anyone on the internet can search for and view the document.', value : dc.access.PUBLIC, selected : current == dc.access.PUBLIC},
      {text : 'Private Access', description : 'Only people explicitly granted permission (via collaboration) may access.', value : dc.access.PRIVATE, selected : current == dc.access.PRIVATE},
      {text : 'Private to ' + dc.app.organization.name, description : 'Only the people in your organization may view the document.', value : dc.access.ORGANIZATION, selected : current == dc.access.ORGANIZATION}
    ], _.bind(function(access) {
      _.each(docs, function(doc) { Documents.update(doc, {access : parseInt(access, 10)}); });
      var notification = 'Access updated for ' + docs.length + ' ' + Inflector.pluralize('document', docs.length);
      dc.ui.notifier.show({mode : 'info', text : notification});
      return true;
    }, this), {information : this._subtitle(docs.length)});
  },

  displayEmbedSnippet : function() {
    if (dc.app.organization.demo) return dc.ui.Dialog.alert('Demo accounts are not allowed to embed document viewers.');
    this.edit(function(docs) {
      new dc.ui.EmbedDialog(docs[0]);
    }, 'At this stage in the DocumentCloud beta, you can\'t yet embed documents that you haven\'t uploaded yourself.');
  },

  checkFloat : function() {
    var floating = $(window).scrollTop() > $(this.el).offset().top - 30;
    if (this._floating == floating) return;
    $(document.body).toggleClass('floating_toolbar', this._floating = floating);
  },

  _subtitle : function(count) {
    return count > 1 ? count + ' Documents' : '';
  },

  _updateSelectedDocuments : function(project) {
    var docs = Documents.selected();
    var removal = project.containsAny(docs);
    removal ? project.removeDocuments(docs) : project.addDocuments(docs);
    this.notifyProjectChange(project.get('title'), docs.length, removal);
  },

  _deleteSelectedDocuments : function() {
    Documents.destroySelected();
  },

  _openTimeline : function() {
    var docs = Documents.selected();
    if (!docs.length) return dc.ui.Dialog.alert("In order to view a timeline, please select some documents.");
    new dc.ui.TimelineDialog(docs);
  },

  _openRelatedDocuments : function() {
    var docs = Documents.selected();
    if (docs.length != 1) return dc.ui.Dialog.alert("Please select a single document, in order to view related documents.");
    dc.app.searcher.search('related: ' + docs[0].id + '-' + docs[0].attributes().slug);
  },

  _viewEntities : function() {
    var docs = Documents.selected();
    if (!docs.length) return dc.app.navigation.open('entities');
    dc.app.navigation.open('entities', true);
    dc.app.searcher.search(_.map(docs, function(doc){ return 'docid: ' + doc.canonicalId(); }).join(' '));
  },

  _openUpload : function() {
    dc.app.uploader.open();
  },

  _panel : function() {
    return this._panelEl = this._panelEl || $(this.el).parents('.panel_content')[0];
  },

  _enableMenuItems : function(menu) {
    $('.menu_item:not(.plus,.always)', menu.content)
      .toggleClass('disabled', !Documents.selectedCount)
      .attr('title', Documents.selectedCount ? '' : 'No documents selected');
    $('.singular', menu.content).toggleClass('disabled', !(Documents.selectedCount == 1));
  },

  _createPublishMenu : function() {
    return new dc.ui.Menu({
      label   : 'Publish',
      onOpen  : this._enableMenuItems,
      items   : [
        {title : 'Embed Document Viewer',    onClick : this.displayEmbedSnippet},
        {title : 'Download Document Viewer', onClick : Documents.downloadSelectedViewers},
        {title : 'Download Original PDF',    onClick : Documents.downloadSelectedPDF},
        {title : 'Download Full Text',       onClick : Documents.downloadSelectedFullText}
      ]
    });
  },

  _createEditMenu : function() {
    return new dc.ui.Menu({
      label   : 'Edit',
      onOpen  : this._enableMenuItems,
      items   : [
        {title : 'New Document',         attrs: {'class' : 'plus'},            onClick : function(){ dc.app.uploader.open(); }},
        {title : 'Edit All Fields',      attrs: {'class' : 'multiple'},        onClick : function(){ dc.ui.DocumentDialog.open(); }},
        {title : 'Edit Title',           attrs: {'class' : 'singular indent'}, onClick : this.editTitle},
        {title : 'Edit Description',     attrs: {'class' : 'singular indent'}, onClick : this.editDescription},
        {title : 'Edit Source',          attrs: {'class' : 'multiple indent'}, onClick : this.editSource},
        {title : 'Edit Related Article', attrs: {'class' : 'multiple indent'}, onClick : this.editRelatedArticle},
        {title : 'Edit Access Level',    attrs: {'class' : 'multiple indent'}, onClick : this.editAccess},
        {title : 'Delete Documents',     attrs: {'class' : 'multiple warn'},   onClick : this._deleteSelectedDocuments}
      ]
    });
  },

  _createAnalyzeMenu : function() {
    return new dc.ui.Menu({
      label   : 'Analyze',
      onOpen  : this._enableMenuItems,
      items   : [
        {title: 'View Entities',          attrs: {'class' : 'always'},   onClick : this._viewEntities},
        {title: 'View Timeline',                                         onClick : this._openTimeline},
        {title: 'Find Related Documents', attrs: {'class' : 'singular'}, onClick : this._openRelatedDocuments}
      ]
    });
  },

  _openViewers : function() {
    if (!Documents.selectedCount) return dc.ui.Dialog.alert('Please select a document to open.');
    _.each(Documents.selected(), function(doc){ doc.openViewer(); });
  }

});<commit_msg>fixing help toolbar menu<commit_after>dc.ui.Toolbar = dc.View.extend({

  id : 'toolbar',

  callbacks : {
    '#open_viewers.click'            : '_openViewers',
    '#toolbar_upload.click'          : '_openUpload'
  },

  MENUS : ['project', 'edit', 'publish', 'analyze'],

  constructor : function(options) {
    this._floating = false;
    this.base(options);
    _.bindAll(this, '_updateSelectedDocuments',
      '_deleteSelectedDocuments', 'editTitle', 'editSource', 'editDescription',
      'editRelatedArticle', 'editAccess', 'displayEmbedSnippet', 'checkFloat',
      '_openTimeline', '_viewEntities');
    this.editMenu         = this._createEditMenu();
    this.publishMenu      = this._createPublishMenu();
    this.analyzeMenu      = this._createAnalyzeMenu();
    this.projectMenu      = new dc.ui.ProjectMenu({onClick : this._updateSelectedDocuments});
  },

  render : function() {
    var el = $(this.el);
    el.html(JST['workspace/toolbar']({}));
    _.each(this.MENUS, _.bind(function(menu){
      $('.' + menu + '_menu_container', el).append(this[menu + 'Menu'].render().el);
    }, this));
    this.openButton              = $('#open_viewers', this.el);
    this.floatEl                 = $('#floating_toolbar', this.el);
    $(window).scroll(this.checkFloat);
    this.setCallbacks();
    return this;
  },

  notifyProjectChange : function(projectName, numDocs, removal) {
    var prefix = removal ? 'Removed ' : 'Added ';
    var prep   = removal ? ' from "'  : ' to "';
    var notification = prefix + numDocs + ' ' + Inflector.pluralize('document', numDocs) + prep + projectName + '"';
    dc.ui.notifier.show({mode : 'info', text : notification});
  },

  // Wrapper function for safely editing an attribute of a specific document.
  edit : function(callback, message) {
    if (!Documents.allowedToEditSelected(message)) return;
    return callback.call(this, Documents.selected());
  },

  editTitle : function() {
    this.edit(function(docs) {
      var doc = docs[0];
      dc.ui.Dialog.prompt('Title', doc.get('title'), function(title) {
        Documents.update(doc, {title : title});
        return true;
      }, {mode : 'short_prompt'});
    });
  },

  editDescription : function() {
    this.edit(function(docs) {
      var doc = docs[0];
      dc.ui.Dialog.prompt('Description', doc.get('description'), function(description) {
        Documents.update(doc, {description : description});
        return true;
      });
    });
  },

  editSource : function() {
    this.edit(function(docs) {
      var current = Documents.sharedAttribute(docs, 'source') || '';
      dc.ui.Dialog.prompt('Source', current, function(source) {
        _.each(docs, function(doc) { Documents.update(doc, {source : source}); });
        return true;
      }, {mode : 'short_prompt', information : this._subtitle(docs.length)});
    });
  },

  editRelatedArticle : function() {
    this.edit(function(docs) {
      var current = Documents.sharedAttribute(docs, 'related_article') || '';
      dc.ui.Dialog.prompt('Related Article', current, function(rel) {
        _.each(docs, function(doc) { Documents.update(doc, {related_article : rel}); });
        return true;
      }, {mode : 'short_prompt', information : this._subtitle(docs.length)});
    });
  },

  editAccess : function() {
    if (!Documents.allowedToEditSelected()) return;
    var docs    = Documents.selected();
    var current = Documents.sharedAttribute(docs, 'access') || dc.access.PRIVATE;
    dc.ui.Dialog.choose('Access Level', [
      {text : 'Public Access',  description : 'Anyone on the internet can search for and view the document.', value : dc.access.PUBLIC, selected : current == dc.access.PUBLIC},
      {text : 'Private Access', description : 'Only people explicitly granted permission (via collaboration) may access.', value : dc.access.PRIVATE, selected : current == dc.access.PRIVATE},
      {text : 'Private to ' + dc.app.organization.name, description : 'Only the people in your organization may view the document.', value : dc.access.ORGANIZATION, selected : current == dc.access.ORGANIZATION}
    ], _.bind(function(access) {
      _.each(docs, function(doc) { Documents.update(doc, {access : parseInt(access, 10)}); });
      var notification = 'Access updated for ' + docs.length + ' ' + Inflector.pluralize('document', docs.length);
      dc.ui.notifier.show({mode : 'info', text : notification});
      return true;
    }, this), {information : this._subtitle(docs.length)});
  },

  displayEmbedSnippet : function() {
    if (dc.app.organization.demo) return dc.ui.Dialog.alert('Demo accounts are not allowed to embed document viewers.');
    this.edit(function(docs) {
      new dc.ui.EmbedDialog(docs[0]);
    }, 'At this stage in the DocumentCloud beta, you can\'t yet embed documents that you haven\'t uploaded yourself.');
  },

  checkFloat : function() {
    var open = dc.app.navigation.isOpen('documents');
    var floating = open && ($(window).scrollTop() > $(this.el).offset().top - 30);
    if (this._floating == floating) return;
    $(document.body).toggleClass('floating_toolbar', this._floating = floating);
  },

  _subtitle : function(count) {
    return count > 1 ? count + ' Documents' : '';
  },

  _updateSelectedDocuments : function(project) {
    var docs = Documents.selected();
    var removal = project.containsAny(docs);
    removal ? project.removeDocuments(docs) : project.addDocuments(docs);
    this.notifyProjectChange(project.get('title'), docs.length, removal);
  },

  _deleteSelectedDocuments : function() {
    Documents.destroySelected();
  },

  _openTimeline : function() {
    var docs = Documents.selected();
    if (!docs.length) return dc.ui.Dialog.alert("In order to view a timeline, please select some documents.");
    new dc.ui.TimelineDialog(docs);
  },

  _openRelatedDocuments : function() {
    var docs = Documents.selected();
    if (docs.length != 1) return dc.ui.Dialog.alert("Please select a single document, in order to view related documents.");
    dc.app.searcher.search('related: ' + docs[0].id + '-' + docs[0].attributes().slug);
  },

  _viewEntities : function() {
    var docs = Documents.selected();
    if (!docs.length) return dc.app.navigation.open('entities');
    dc.app.navigation.open('entities', true);
    dc.app.searcher.search(_.map(docs, function(doc){ return 'docid: ' + doc.canonicalId(); }).join(' '));
  },

  _openUpload : function() {
    dc.app.uploader.open();
  },

  _panel : function() {
    return this._panelEl = this._panelEl || $(this.el).parents('.panel_content')[0];
  },

  _enableMenuItems : function(menu) {
    $('.menu_item:not(.plus,.always)', menu.content)
      .toggleClass('disabled', !Documents.selectedCount)
      .attr('title', Documents.selectedCount ? '' : 'No documents selected');
    $('.singular', menu.content).toggleClass('disabled', !(Documents.selectedCount == 1));
  },

  _createPublishMenu : function() {
    return new dc.ui.Menu({
      label   : 'Publish',
      onOpen  : this._enableMenuItems,
      items   : [
        {title : 'Embed Document Viewer',    onClick : this.displayEmbedSnippet},
        {title : 'Download Document Viewer', onClick : Documents.downloadSelectedViewers},
        {title : 'Download Original PDF',    onClick : Documents.downloadSelectedPDF},
        {title : 'Download Full Text',       onClick : Documents.downloadSelectedFullText}
      ]
    });
  },

  _createEditMenu : function() {
    return new dc.ui.Menu({
      label   : 'Edit',
      onOpen  : this._enableMenuItems,
      items   : [
        {title : 'New Document',         attrs: {'class' : 'plus'},            onClick : function(){ dc.app.uploader.open(); }},
        {title : 'Edit All Fields',      attrs: {'class' : 'multiple'},        onClick : function(){ dc.ui.DocumentDialog.open(); }},
        {title : 'Edit Title',           attrs: {'class' : 'singular indent'}, onClick : this.editTitle},
        {title : 'Edit Description',     attrs: {'class' : 'singular indent'}, onClick : this.editDescription},
        {title : 'Edit Source',          attrs: {'class' : 'multiple indent'}, onClick : this.editSource},
        {title : 'Edit Related Article', attrs: {'class' : 'multiple indent'}, onClick : this.editRelatedArticle},
        {title : 'Edit Access Level',    attrs: {'class' : 'multiple indent'}, onClick : this.editAccess},
        {title : 'Delete Documents',     attrs: {'class' : 'multiple warn'},   onClick : this._deleteSelectedDocuments}
      ]
    });
  },

  _createAnalyzeMenu : function() {
    return new dc.ui.Menu({
      label   : 'Analyze',
      onOpen  : this._enableMenuItems,
      items   : [
        {title: 'View Entities',          attrs: {'class' : 'always'},   onClick : this._viewEntities},
        {title: 'View Timeline',                                         onClick : this._openTimeline},
        {title: 'Find Related Documents', attrs: {'class' : 'singular'}, onClick : this._openRelatedDocuments}
      ]
    });
  },

  _openViewers : function() {
    if (!Documents.selectedCount) return dc.ui.Dialog.alert('Please select a document to open.');
    _.each(Documents.selected(), function(doc){ doc.openViewer(); });
  }

});<|endoftext|>


<commit_before>'use strict';

var React = require('react-native');
var {
  PixelRatio,
  StyleSheet,
  Text,
  TouchableOpacity,
  View
} = React;

var NavigatorNavigationBarStyles = require('NavigatorNavigationBarStyles');
var StaticContainer = require('StaticContainer.react');
var cssVar = require('cssVar');

var styles = StyleSheet.create({
  navBarContainer: {
    height: NavigatorNavigationBarStyles.General.TotalNavHeight,
    backgroundColor: 'white',
    flexDirection: 'row',
    alignItems: 'flex-end',
    paddingBottom: 5,
    borderBottomColor: 'rgba(0, 0, 0, 0.5)',
    borderBottomWidth: 1 / PixelRatio.get(),
    justifyContent: 'space-between',
  },
  customTitle: {
    position: 'absolute',
    alignItems: 'center',
    bottom: 5,
    left: 0,
    right: 0,
  },
  navBarText: {
    fontSize: 16,
    marginVertical: 10,
    flex: 2,
    textAlign: 'center',
  },
  navBarTitleText: {
    color: cssVar('fbui-bluegray-60'),
    fontWeight: '500',
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 15,
  },
  navBarLeftButton: {
    paddingLeft: 10,
  },
  navBarRightButton: {
    paddingRight: 10,
  },
  navBarButtonText: {
    color: cssVar('fbui-accent-blue'),
  }
});

var NavigationBar = React.createClass({

  propTypes: {
    navigator: React.PropTypes.object.isRequired,
    route: React.PropTypes.object.isRequired,
  },
  /*
   * If there are no routes in the stack, `hidePrev` isn't provided or false,
   * and we haven't received `onPrev` click handler, return true
   */
  prevButtonShouldBeHidden: function() {
    var {
      onPrev,
      hidePrev,
      navigator
    } = this.props;

    var getCurrentRoutes = navigator.getCurrentRoutes;

    return (
      hidePrev ||
      (getCurrentRoutes && getCurrentRoutes().length <= 1 && !onPrev)
    );
  },

  /**
   * Describes how we get a left button in the navbar
   */
  getLeftButtonElement: function() {
    var {
      onPrev,
      prevTitle,
      navigator,
      route,
      buttonsColor,
      customPrev,
    } = this.props;

    /*
     * If we have a `customPrev` component, then return
     * it's clone with additional attributes
     */
    if (customPrev) {
      return React.addons.cloneWithProps(customPrev, { navigator, route });
    }

    /*
     * Check if we need to hide `prev` button
     */
    if (this.prevButtonShouldBeHidden()) {
      return <View style={styles.navBarLeftButton}></View>;
    }

    /*
     * Apply custom background styles to button
     */
    var customStyle = buttonsColor ? { color: buttonsColor } : {};

    return (
      <TouchableOpacity onPress={onPrev || navigator.pop}>
        <View style={styles.navBarLeftButton}>
          <Text style={[styles.navBarText, styles.navBarButtonText, customStyle]}>
            {prevTitle || 'Back'}
          </Text>
        </View>
      </TouchableOpacity>
    );
  },

  /*
   * Describe how we get a title for the navbar
   */
  getTitleElement: function() {
    var {
      title,
      titleColor,
      customTitle,
      navigator,
      route,
    } = this.props;

    /*
     * Return `customTitle` component if we have it
     */
    if (customTitle) {
      return (
        <View style={styles.customTitle}>
          {React.addons.cloneWithProps(customTitle, { navigator, route })}
        </View>
      );
    }

    if (title && !title.length) {
      return true;
    }

    var titleStyle = [
      styles.navBarText,
      styles.navBarTitleText,
      { color: titleColor }
    ];

    return (
      <Text style={titleStyle}>
        {title}
      </Text>
    );
  },

  getRightButtonElement: function() {
    var {
      onNext,
      nextTitle,
      navigator,
      route,
      buttonsColor,
      customNext
    } = this.props;

    /*
     * If we have a `customNext` component, then return
     * it's clone with additional attributes
     */
    if (customNext) {
      return React.addons.cloneWithProps(customNext, { navigator, route });
    }

    /*
     * If we haven't received `onNext` handler, then just return
     * a placeholder for button to keep markup consistant and
     * title aligned to the center
     */
    if (!onNext) {
      return <Text style={styles.navBarRightButton}></Text>;
    }

    /*
     * Apply custom background styles to button
     */
    var customStyle = buttonsColor ? { color: buttonsColor } : {};

    return (
      <TouchableOpacity onPress={onNext}>
        <View style={styles.navBarRightButton}>
          <Text style={[styles.navBarText, styles.navBarButtonText, customStyle]}>
            {nextTitle || 'Next'}
          </Text>
        </View>
      </TouchableOpacity>
    );
  },

  render: function() {
    var backgroundStyle = this.props.backgroundColor ?
      { backgroundColor: this.props.backgroundColor } : {},
        customStyle = this.props.style;

    return (
      <StaticContainer shouldUpdate={false}>
        <View style={[styles.navBarContainer, backgroundStyle, customStyle ]}>
          {this.getTitleElement()}
          {this.getLeftButtonElement()}
          {this.getRightButtonElement()}
        </View>
      </StaticContainer>
    );
  },
});

module.exports = NavigationBar;
<commit_msg>New "statusBar" to change StatusBarIOS color<commit_after>'use strict';

var React = require('react-native');
var {
  PixelRatio,
  StatusBarIOS,
  StyleSheet,
  Text,
  TouchableOpacity,
  View
} = React;

var NavigatorNavigationBarStyles = require('NavigatorNavigationBarStyles');
var StaticContainer = require('StaticContainer.react');
var cssVar = require('cssVar');

var styles = StyleSheet.create({
  navBarContainer: {
    height: NavigatorNavigationBarStyles.General.TotalNavHeight,
    backgroundColor: 'white',
    flexDirection: 'row',
    alignItems: 'flex-end',
    paddingBottom: 5,
    borderBottomColor: 'rgba(0, 0, 0, 0.5)',
    borderBottomWidth: 1 / PixelRatio.get(),
    justifyContent: 'space-between',
  },
  customTitle: {
    position: 'absolute',
    alignItems: 'center',
    bottom: 5,
    left: 0,
    right: 0,
  },
  navBarText: {
    fontSize: 16,
    marginVertical: 10,
    flex: 2,
    textAlign: 'center',
  },
  navBarTitleText: {
    color: cssVar('fbui-bluegray-60'),
    fontWeight: '500',
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 15,
  },
  navBarLeftButton: {
    paddingLeft: 10,
  },
  navBarRightButton: {
    paddingRight: 10,
  },
  navBarButtonText: {
    color: cssVar('fbui-accent-blue'),
  }
});

var NavigationBar = React.createClass({

  propTypes: {
    navigator: React.PropTypes.object.isRequired,
    route: React.PropTypes.object.isRequired,
  },
  /*
   * If there are no routes in the stack, `hidePrev` isn't provided or false,
   * and we haven't received `onPrev` click handler, return true
   */
  prevButtonShouldBeHidden: function() {
    var {
      onPrev,
      hidePrev,
      navigator
    } = this.props;

    var getCurrentRoutes = navigator.getCurrentRoutes;

    return (
      hidePrev ||
      (getCurrentRoutes && getCurrentRoutes().length <= 1 && !onPrev)
    );
  },

  /**
   * Describes how we get a left button in the navbar
   */
  getLeftButtonElement: function() {
    var {
      onPrev,
      prevTitle,
      navigator,
      route,
      buttonsColor,
      customPrev,
    } = this.props;

    /*
     * If we have a `customPrev` component, then return
     * it's clone with additional attributes
     */
    if (customPrev) {
      return React.addons.cloneWithProps(customPrev, { navigator, route });
    }

    /*
     * Check if we need to hide `prev` button
     */
    if (this.prevButtonShouldBeHidden()) {
      return <View style={styles.navBarLeftButton}></View>;
    }

    /*
     * Apply custom background styles to button
     */
    var customStyle = buttonsColor ? { color: buttonsColor } : {};

    return (
      <TouchableOpacity onPress={onPrev || navigator.pop}>
        <View style={styles.navBarLeftButton}>
          <Text style={[styles.navBarText, styles.navBarButtonText, customStyle]}>
            {prevTitle || 'Back'}
          </Text>
        </View>
      </TouchableOpacity>
    );
  },

  /*
   * Describe how we get a title for the navbar
   */
  getTitleElement: function() {
    var {
      title,
      titleColor,
      customTitle,
      navigator,
      route,
    } = this.props;

    /*
     * Return `customTitle` component if we have it
     */
    if (customTitle) {
      return (
        <View style={styles.customTitle}>
          {React.addons.cloneWithProps(customTitle, { navigator, route })}
        </View>
      );
    }

    if (title && !title.length) {
      return true;
    }

    var titleStyle = [
      styles.navBarText,
      styles.navBarTitleText,
      { color: titleColor }
    ];

    return (
      <Text style={titleStyle}>
        {title}
      </Text>
    );
  },

  getRightButtonElement: function() {
    var {
      onNext,
      nextTitle,
      navigator,
      route,
      buttonsColor,
      customNext
    } = this.props;

    /*
     * If we have a `customNext` component, then return
     * it's clone with additional attributes
     */
    if (customNext) {
      return React.addons.cloneWithProps(customNext, { navigator, route });
    }

    /*
     * If we haven't received `onNext` handler, then just return
     * a placeholder for button to keep markup consistant and
     * title aligned to the center
     */
    if (!onNext) {
      return <Text style={styles.navBarRightButton}></Text>;
    }

    /*
     * Apply custom background styles to button
     */
    var customStyle = buttonsColor ? { color: buttonsColor } : {};

    return (
      <TouchableOpacity onPress={onNext}>
        <View style={styles.navBarRightButton}>
          <Text style={[styles.navBarText, styles.navBarButtonText, customStyle]}>
            {nextTitle || 'Next'}
          </Text>
        </View>
      </TouchableOpacity>
    );
  },

  render: function() {

    if (this.props.statusBar == 'lightContent') {
      StatusBarIOS.setStyle(StatusBarIOS.Style['lightContent']);
    }

    if (this.props.statusBar == 'default') {
      StatusBarIOS.setStyle(StatusBarIOS.Style['default']);
    }

    var backgroundStyle = this.props.backgroundColor ?
      { backgroundColor: this.props.backgroundColor } : {},
        customStyle = this.props.style;

    return (
      <StaticContainer shouldUpdate={false}>
        <View style={[styles.navBarContainer, backgroundStyle, customStyle ]}>
          {this.getTitleElement()}
          {this.getLeftButtonElement()}
          {this.getRightButtonElement()}
        </View>
      </StaticContainer>
    );
  },
});

module.exports = NavigationBar;
<|endoftext|>