/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * [description]
 *
 * @name Phaser.Physics.Arcade.Components.Gravity
 * @since 3.0.0
 */
var Gravity = {

    /**
     * [description]
     *
     * @method Phaser.Physics.Arcade.Components.Gravity#setGravity
     * @since 3.0.0
     *
     * @param {number} x - [description]
     * @param {number} [y=x] - [description]
     *
     * @return {this} This Game Object.
     */
    setGravity: function (x, y)
    {
        this.body.gravity.set(x, y);

        return this;
    },

    /**
     * [description]
     *
     * @method Phaser.Physics.Arcade.Components.Gravity#setGravityX
     * @since 3.0.0
     *
     * @param {number} x - [description]
     *
     * @return {this} This Game Object.
     */
    setGravityX: function (x)
    {
        this.body.gravity.x = x;

        return this;
    },

    /**
     * [description]
     *
     * @method Phaser.Physics.Arcade.Components.Gravity#setGravityY
     * @since 3.0.0
     *
     * @param {number} y - [description]
     *
     * @return {this} This Game Object.
     */
    setGravityY: function (y)
    {
        this.body.gravity.y = y;

        return this;
    }

};

module.exports = Gravity;


keep keep keep keep replace keep keep keep keep

/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * Returns a string containing a hex representation of the given color component.
 *
 * @function Phaser.Display.Color.ComponentToHex
 * @since 3.0.0
 *
 * @param {integer} color - The color channel to get the hex value for, must be a value between 0 and 255.
 *
 * @return {string} A string of length 2 characters, i.e. 255 = ff, 100 = 64.
 */
var ComponentToHex = function (color)
{
    var hex = color.toString(16);

    return (hex.length === 1) ? '0' + hex : hex;
};

module.exports = ComponentToHex;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @author       Pavle Goloskokovic <pgoloskokovic@gmail.com> (http://prunegames.com)
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var BaseSoundManager = require('../BaseSoundManager');
var Class = require('../../utils/Class');
var HTML5AudioSound = require('./HTML5AudioSound');

/**
 * HTML5 Audio implementation of the Sound Manager.
 *
 * @class HTML5AudioSoundManager
 * @extends Phaser.Sound.BaseSoundManager
 * @memberOf Phaser.Sound
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Game} game - Reference to the current game instance.
 */
var HTML5AudioSoundManager = new Class({

    Extends: BaseSoundManager,

    initialize:

    function HTML5AudioSoundManager (game)
    {
        /**
         * Flag indicating whether if there are no idle instances of HTML5 Audio tag,
         * for any particular sound, if one of the used tags should be hijacked and used
         * for succeeding playback or if succeeding Phaser.Sound.HTML5AudioSound#play
         * call should be ignored.
         *
         * @name Phaser.Sound.HTML5AudioSoundManager#override
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.override = true;

        /**
         * Value representing time difference, in seconds, between calling
         * play method on an audio tag and when it actually starts playing.
         * It is used to achieve more accurate delayed sound playback.
         *
         * You might need to tweak this value to get the desired results
         * since audio play delay varies depending on the browser/platform.
         *
         * @name Phaser.Sound.HTML5AudioSoundManager#audioPlayDelay
         * @type {number}
         * @default 0.1
         * @since 3.0.0
         */
        this.audioPlayDelay = 0.1;

        /**
         * A value by which we should offset the loop end marker of the
         * looping sound to compensate for lag, caused by changing audio
         * tag playback position, in order to achieve gapless looping.
         *
         * You might need to tweak this value to get the desired results
         * since loop lag varies depending on the browser/platform.
         *
         * @name Phaser.Sound.HTML5AudioSoundManager#loopEndOffset
         * @type {number}
         * @default 0.05
         * @since 3.0.0
         */
        this.loopEndOffset = 0.05;

        /**
         * An array for keeping track of all the sounds
         * that were paused when game lost focus.
         *
         * @name Phaser.Sound.HTML5AudioSoundManager#onBlurPausedSounds
         * @type {Phaser.Sound.HTML5AudioSound[]}
         * @private
         * @default []
         * @since 3.0.0
         */
        this.onBlurPausedSounds = [];

        this.locked = 'ontouchstart' in window;

        /**
         * A queue of all actions performed on sound objects while audio was locked.
         * Once the audio gets unlocked, after an explicit user interaction,
         * all actions will be performed in chronological order.
         * Array of object types: { sound: Phaser.Sound.HTML5AudioSound, name: string, value?: * }
         *
         * @name Phaser.Sound.HTML5AudioSoundManager#lockedActionsQueue
         * @type {array}
         * @private
         * @since 3.0.0
         */
        this.lockedActionsQueue = this.locked ? [] : null;

        /**
         * Property that actually holds the value of global mute
         * for HTML5 Audio sound manager implementation.
         *
         * @name Phaser.Sound.HTML5AudioSoundManager#_mute
         * @type {boolean}
         * @private
         * @default false
         * @since 3.0.0
         */
        this._mute = false;

        /**
         * Property that actually holds the value of global volume
         * for HTML5 Audio sound manager implementation.
         *
         * @name Phaser.Sound.HTML5AudioSoundManager#_volume
         * @type {boolean}
         * @private
         * @default 1
         * @since 3.0.0
         */
        this._volume = 1;

        BaseSoundManager.call(this, game);
    },

    /**
     * Adds a new sound into the sound manager.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#add
     * @since 3.0.0
     *
     * @param {string} key - Asset key for the sound.
     * @param {SoundConfig} [config] - An optional config object containing default sound settings.
     *
     * @return {Phaser.Sound.HTML5AudioSound} The new sound instance.
     */
    add: function (key, config)
    {
        var sound = new HTML5AudioSound(this, key, config);

        this.sounds.push(sound);

        return sound;
    },

    /**
     * Unlocks HTML5 Audio loading and playback on mobile
     * devices on the initial explicit user interaction.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#unlock
     * @since 3.0.0
     */
    unlock: function ()
    {
        this.locked = false;

        var _this = this;

        this.game.cache.audio.entries.each(function (key, tags)
        {
            for (var i = 0; i < tags.length; i++)
            {
                if (tags[i].dataset.locked === 'true')
                {
                    _this.locked = true;

                    return false;
                }
            }

            return true;
        });

        if (!this.locked)
        {
            return;
        }

        var moved = false;

        var detectMove = function ()
        {
            moved = true;
        };

        var unlock = function ()
        {
            if (moved)
            {
                moved = false;
                return;
            }

            document.body.removeEventListener('touchmove', detectMove);
            document.body.removeEventListener('touchend', unlock);

            var lockedTags = [];

            _this.game.cache.audio.entries.each(function (key, tags)
            {
                for (var i = 0; i < tags.length; i++)
                {
                    var tag = tags[i];

                    if (tag.dataset.locked === 'true')
                    {
                        lockedTags.push(tag);
                    }
                }

                return true;
            });

            if (lockedTags.length === 0)
            {
                return;
            }

            var lastTag = lockedTags[lockedTags.length - 1];

            lastTag.oncanplaythrough = function ()
            {
                lastTag.oncanplaythrough = null;

                lockedTags.forEach(function (tag)
                {
                    tag.dataset.locked = 'false';
                });

                _this.unlocked = true;
            };

            lockedTags.forEach(function (tag)
            {
                tag.load();
            });
        };

        this.once('unlocked', function ()
        {
            this.forEachActiveSound(function (sound)
            {
                if (sound.currentMarker === null && sound.duration === 0)
                {
                    sound.duration = sound.tags[0].duration;
                }

                sound.totalDuration = sound.tags[0].duration;
            });

            while (this.lockedActionsQueue.length)
            {
                var lockedAction = this.lockedActionsQueue.shift();

                if (lockedAction.sound[lockedAction.prop].apply)
                {
                    lockedAction.sound[lockedAction.prop].apply(lockedAction.sound, lockedAction.value || []);
                }
                else
                {
                    lockedAction.sound[lockedAction.prop] = lockedAction.value;
                }
            }

        }, this);

        document.body.addEventListener('touchmove', detectMove, false);
        document.body.addEventListener('touchend', unlock, false);
    },

    /**
     * Method used internally for pausing sound manager if
     * Phaser.Sound.HTML5AudioSoundManager#pauseOnBlur is set to true.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#onBlur
     * @protected
     * @since 3.0.0
     */
    onBlur: function ()
    {
        this.forEachActiveSound(function (sound)
        {
            if (sound.isPlaying)
            {
                this.onBlurPausedSounds.push(sound);
                sound.onBlur();
            }
        });
    },

    /**
     * Method used internally for resuming sound manager if
     * Phaser.Sound.HTML5AudioSoundManager#pauseOnBlur is set to true.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#onFocus
     * @protected
     * @since 3.0.0
     */
    onFocus: function ()
    {
        this.onBlurPausedSounds.forEach(function (sound)
        {
            sound.onFocus();
        });

        this.onBlurPausedSounds.length = 0;
    },

    /**
     * Calls Phaser.Sound.BaseSoundManager#destroy method
     * and cleans up all HTML5 Audio related stuff.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#destroy
     * @since 3.0.0
     */
    destroy: function ()
    {
        BaseSoundManager.prototype.destroy.call(this);

        this.onBlurPausedSounds.length = 0;
        this.onBlurPausedSounds = null;
    },

    /**
     * Method used internally by Phaser.Sound.HTML5AudioSound class methods and property setters
     * to check if sound manager is locked and then either perform action immediately or queue it
     * to be performed once the sound manager gets unlocked.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#isLocked
     * @protected
     * @since 3.0.0
     *
     * @param {Phaser.Sound.HTML5AudioSound} sound - Sound object on which to perform queued action.
     * @param {string} prop - Name of the method to be called or property to be assigned a value to.
     * @param {*} [value] - An optional parameter that either holds an array of arguments to be passed to the method call or value to be set to the property.
     *
     * @return {boolean} Whether the sound manager is locked.
     */
    isLocked: function (sound, prop, value)
    {
        if (sound.tags[0].dataset.locked === 'true')
        {
            this.lockedActionsQueue.push({
                sound: sound,
                prop: prop,
                value: value
            });

            return true;
        }

        return false;
    },

    /**
     * @event Phaser.Sound.HTML5AudioSoundManager#muteEvent
     * @param {Phaser.Sound.HTML5AudioSoundManager} soundManager - Reference to the sound manager that emitted event.
     * @param {boolean} value - An updated value of Phaser.Sound.HTML5AudioSoundManager#mute property.
     */

    /**
     * Sets the muted state of all this Sound Manager.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#setMute
     * @fires Phaser.Sound.HTML5AudioSoundManager#muteEvent
     * @since 3.3.0
     *
     * @param {boolean} value - `true` to mute all sounds, `false` to unmute them.
     *
     * @return {Phaser.Sound.HTML5AudioSoundManager} This Sound Manager.
     */
    setMute: function (value)
    {
        this.mute = value;

        return this;
    },

    /**
     * @name Phaser.Sound.HTML5AudioSoundManager#mute
     * @type {boolean}
     * @fires Phaser.Sound.HTML5AudioSoundManager#muteEvent
     * @since 3.0.0
     */
    mute: {

        get: function ()
        {
            return this._mute;
        },

        set: function (value)
        {
            this._mute = value;

            this.forEachActiveSound(function (sound)
            {
                sound.updateMute();
            });

            this.emit('mute', this, value);
        }

    },

    /**
     * @event Phaser.Sound.HTML5AudioSoundManager#volumeEvent
     * @param {Phaser.Sound.HTML5AudioSoundManager} soundManager - Reference to the sound manager that emitted event.
     * @param {number} value - An updated value of Phaser.Sound.HTML5AudioSoundManager#volume property.
     */

    /**
     * Sets the volume of this Sound Manager.
     *
     * @method Phaser.Sound.HTML5AudioSoundManager#setVolume
     * @fires Phaser.Sound.HTML5AudioSoundManager#volumeEvent
     * @since 3.3.0
     *
     * @param {number} value - The global volume of this Sound Manager.
     *
     * @return {Phaser.Sound.HTML5AudioSoundManager} This Sound Manager.
     */
    setVolume: function (value)
    {
        this.volume = value;

        return this;
    },

    /**
     * @name Phaser.Sound.HTML5AudioSoundManager#volume
     * @type {number}
     * @fires Phaser.Sound.HTML5AudioSoundManager#volumeEvent
     * @since 3.0.0
     */
    volume: {

        get: function ()
        {
            return this._volume;
        },

        set: function (value)
        {
            this._volume = value;

            this.forEachActiveSound(function (sound)
            {
                sound.updateVolume();
            });

            this.emit('volume', this, value);
        }

    }

});

module.exports = HTML5AudioSoundManager;


InlineLexer.prototype.mangle = function(text) {

keep keep keep keep add keep keep keep keep

.ds q \N'34'
.TH marked 1 "2014-01-31" "v0.3.1" "marked.js"

.SH NAME
marked \- a javascript markdown parser

.SH SYNOPSIS
.B marked
[\-o \fI<output>\fP] [\-i \fI<input>\fP] [\-\-help]
[\-\-tokens] [\-\-pedantic] [\-\-gfm]
[\-\-breaks] [\-\-tables] [\-\-sanitize]
[\-\-smart\-lists] [\-\-lang\-prefix \fI<prefix>\fP]
[\-\-no\-etc...] [\-\-silent] [\fIfilename\fP]

.SH DESCRIPTION
.B marked
is a full-featured javascript markdown parser, built for speed. It also includes
multiple GFM features.

.SH EXAMPLES
.TP
cat in.md | marked > out.html
.TP
echo "hello *world*" | marked
.TP
marked \-o out.html in.md \-\-gfm
.TP
marked \-\-output="hello world.html" \-i in.md \-\-no-breaks

.SH OPTIONS
.TP
.BI \-o,\ \-\-output\ [\fIoutput\fP]
Specify file output. If none is specified, write to stdout.
.TP
.BI \-i,\ \-\-input\ [\fIinput\fP]
Specify file input, otherwise use last argument as input file. If no input file
is specified, read from stdin.
.TP
.BI \-t,\ \-\-tokens
Output a token stream instead of html.
.TP
.BI \-\-pedantic
Conform to obscure parts of markdown.pl as much as possible. Don't fix original
markdown bugs.
.TP
.BI \-\-gfm
Enable github flavored markdown.
.TP
.BI \-\-breaks
Enable GFM line breaks. Only works with the gfm option.
.TP
.BI \-\-tables
Enable GFM tables. Only works with the gfm option.
.TP
.BI \-\-sanitize
Sanitize output. Ignore any HTML input.
.TP
.BI \-\-smart\-lists
Use smarter list behavior than the original markdown.
.TP
.BI \-\-lang\-prefix\ [\fIprefix\fP]
Set the prefix for code block classes.
.TP
.BI \-\-no\-sanitize,\ \-no-etc...
The inverse of any of the marked options above.
.TP
.BI \-\-silent
Silence error output.
.TP
.BI \-h,\ \-\-help
Display help information.

.SH CONFIGURATION
For configuring and running programmatically.

.B Example

    require('marked')('*foo*', { gfm: true });

.SH BUGS
Please report any bugs to https://github.com/chjj/marked.

.SH LICENSE
Copyright (c) 2011-2014, Christopher Jeffrey (MIT License).

.SH "SEE ALSO"
.BR markdown(1),
.BR node.js(1)


* hello
world
* how
are
* * *
you today?


gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: false,
silent: false,
highlight: null,
langPrefix: 'lang-',
smartypants: false,

keep keep keep keep add keep keep keep keep keep


#!/usr/bin/env node

/**
 * Marked CLI
 * Copyright (c) 2011-2013, Christopher Jeffrey (MIT License)
 */

var fs = require('fs')
  , util = require('util')
  , marked = require('../');

/**
 * Man Page
 */

function help() {
  var spawn = require('child_process').spawn;

  var options = {
    cwd: process.cwd(),
    env: process.env,
    setsid: false,
    customFds: [0, 1, 2]
  };

  spawn('man',
    [__dirname + '/../man/marked.1'],
    options);
}

/**
 * Main
 */

function main(argv, callback) {
  var files = []
    , options = {}
    , input
    , output
    , arg
    , tokens
    , opt;

  function getarg() {
    var arg = argv.shift();

    if (arg.indexOf('--') === 0) {
      // e.g. --opt
      arg = arg.split('=');
      if (arg.length > 1) {
        // e.g. --opt=val
        argv.unshift(arg.slice(1).join('='));
      }
      arg = arg[0];
    } else if (arg[0] === '-') {
      if (arg.length > 2) {
        // e.g. -abc
        argv = arg.substring(1).split('').map(function(ch) {
          return '-' + ch;
        }).concat(argv);
        arg = argv.shift();
      } else {
        // e.g. -a
      }
    } else {
      // e.g. foo
    }

    return arg;
  }

  while (argv.length) {
    arg = getarg();
    switch (arg) {
      case '--test':
        return require('../test').main(process.argv.slice());
      case '-o':
      case '--output':
        output = argv.shift();
        break;
      case '-i':
      case '--input':
        input = argv.shift();
        break;
      case '-t':
      case '--tokens':
        tokens = true;
        break;
      case '-h':
      case '--help':
        return help();
      default:
        if (arg.indexOf('--') === 0) {
          opt = camelize(arg.replace(/^--(no-)?/, ''));
          if (!marked.defaults.hasOwnProperty(opt)) {
            continue;
          }
          if (arg.indexOf('--no-') === 0) {
            options[opt] = typeof marked.defaults[opt] !== 'boolean'
              ? null
              : false;
          } else {
            options[opt] = typeof marked.defaults[opt] !== 'boolean'
              ? argv.shift()
              : true;
          }
        } else {
          files.push(arg);
        }
        break;
    }
  }

  function getData(callback) {
    if (!input) {
      if (files.length <= 2) {
        return getStdin(callback);
      }
      input = files.pop();
    }
    return fs.readFile(input, 'utf8', callback);
  }

  return getData(function(err, data) {
    if (err) return callback(err);

    data = tokens
      ? JSON.stringify(marked.lexer(data, options), null, 2)
      : marked(data, options);

    if (!output) {
      process.stdout.write(data + '\n');
      return callback();
    }

    return fs.writeFile(output, data, callback);
  });
}

/**
 * Helpers
 */

function getStdin(callback) {
  var stdin = process.stdin
    , buff = '';

  stdin.setEncoding('utf8');

  stdin.on('data', function(data) {
    buff += data;
  });

  stdin.on('error', function(err) {
    return callback(err);
  });

  stdin.on('end', function() {
    return callback(null, buff);
  });

  try {
    stdin.resume();
  } catch (e) {
    callback(e);
  }
}

function camelize(text) {
  return text.replace(/(\w)-(\w)/g, function(_, a, b) {
    return a + b.toUpperCase();
  });
}

/**
 * Expose / Entry Point
 */

if (!module.parent) {
  process.title = 'marked';
  main(process.argv.slice(), function(err, code) {
    if (err) throw err;
    return process.exit(code || 0);
  });
} else {
  module.exports = main;
}


The inverse of any of the marked options above.

.BI \-\-mangle
Mangle email addresses.
.TP


#!/usr/bin/env node

/**
 * marked tests
 * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
 * https://github.com/chjj/marked
 */

/**
 * Modules
 */

var fs = require('fs')
  , path = require('path')
  , marked = require('../');

/**
 * Load Tests
 */

function load() {
  var dir = __dirname + '/tests'
    , files = {}
    , list
    , file
    , i
    , l;

  list = fs
    .readdirSync(dir)
    .filter(function(file) {
      return path.extname(file) !== '.html';
    })
    .sort(function(a, b) {
      a = path.basename(a).toLowerCase().charCodeAt(0);
      b = path.basename(b).toLowerCase().charCodeAt(0);
      return a > b ? 1 : (a < b ? -1 : 0);
    });

  i = 0;
  l = list.length;

  for (; i < l; i++) {
    file = path.join(dir, list[i]);
    files[path.basename(file)] = {
      text: fs.readFileSync(file, 'utf8'),
      html: fs.readFileSync(file.replace(/[^.]+$/, 'html'), 'utf8')
    };
  }

  return files;
}

/**
 * Test Runner
 */

function runTests(engine, options) {
  if (typeof engine !== 'function') {
    options = engine;
    engine = null;
  }

  var engine = engine || marked
    , options = options || {}
    , files = options.files || load()
    , complete = 0
    , failed = 0
    , failures = []
    , keys = Object.keys(files)
    , i = 0
    , len = keys.length
    , filename
    , file
    , flags
    , text
    , html
    , j
    , l;

  if (options.marked) {
    marked.setOptions(options.marked);
  }

main:
  for (; i < len; i++) {
    filename = keys[i];
    file = files[filename];

    if (marked._original) {
      marked.defaults = marked._original;
      delete marked._original;
    }

    flags = filename.split('.').slice(1, -1);
    if (flags.length) {
      marked._original = marked.defaults;
      marked.defaults = {};
      Object.keys(marked._original).forEach(function(key) {
        marked.defaults[key] = marked._original[key];
      });
      flags.forEach(function(key) {
        var val = true;
        if (key.indexOf('no') === 0) {
          key = key.substring(2);
          val = false;
        }
        if (marked.defaults.hasOwnProperty(key)) {
          marked.defaults[key] = val;
        }
      });
    }

    try {
      text = engine(file.text).replace(/\s/g, '');
      html = file.html.replace(/\s/g, '');
    } catch(e) {
      console.log('%s failed.', filename);
      throw e;
    }

    j = 0;
    l = html.length;

    for (; j < l; j++) {
      if (text[j] !== html[j]) {
        failed++;
        failures.push(filename);

        text = text.substring(
          Math.max(j - 30, 0),
          Math.min(j + 30, text.length));

        html = html.substring(
          Math.max(j - 30, 0),
          Math.min(j + 30, html.length));

        console.log(
          '\n#%d. %s failed at offset %d. Near: "%s".\n',
          i + 1, filename, j, text);

        console.log('\nGot:\n%s\n', text.trim() || text);
        console.log('\nExpected:\n%s\n', html.trim() || html);

        if (options.stop) {
          break main;
        }

        continue main;
      }
    }

    complete++;
    console.log('#%d. %s completed.', i + 1, filename);
  }

  console.log('%d/%d tests completed successfully.', complete, len);
  if (failed) console.log('%d/%d tests failed.', failed, len);

  // Tests currently failing.
  if (~failures.indexOf('def_blocks.text')) {
    failed -= 1;
  }

  return !failed;
}

/**
 * Benchmark a function
 */

function bench(name, func) {
  var files = bench.files || load();

  if (!bench.files) {
    bench.files = files;

    // Change certain tests to allow
    // comparison to older benchmark times.
    fs.readdirSync(__dirname + '/new').forEach(function(name) {
      if (path.extname(name) === '.html') return;
      if (name === 'main.text') return;
      delete files[name];
    });

    files['backslash_escapes.text'] = {
      text: 'hello world \\[how](are you) today'
    };

    files['main.text'].text = files['main.text'].text.replace('* * *\n\n', '');
  }

  var start = Date.now()
    , times = 1000
    , keys = Object.keys(files)
    , i
    , l = keys.length
    , filename
    , file;

  while (times--) {
    for (i = 0; i < l; i++) {
      filename = keys[i];
      file = files[filename];
      func(file.text);
    }
  }

  console.log('%s completed in %dms.', name, Date.now() - start);
}

/**
 * Benchmark all engines
 */

function runBench(options) {
  var options = options || {};

  // Non-GFM, Non-pedantic
  marked.setOptions({
    gfm: false,
    tables: false,
    breaks: false,
    pedantic: false,
    sanitize: false,
    smartLists: false
  });
  if (options.marked) {
    marked.setOptions(options.marked);
  }
  bench('marked', marked);

  // GFM
  marked.setOptions({
    gfm: true,
    tables: false,
    breaks: false,
    pedantic: false,
    sanitize: false,
    smartLists: false
  });
  if (options.marked) {
    marked.setOptions(options.marked);
  }
  bench('marked (gfm)', marked);

  // Pedantic
  marked.setOptions({
    gfm: false,
    tables: false,
    breaks: false,
    pedantic: true,
    sanitize: false,
    smartLists: false
  });
  if (options.marked) {
    marked.setOptions(options.marked);
  }
  bench('marked (pedantic)', marked);

  // robotskirt
  try {
    bench('robotskirt', (function() {
      var rs = require('robotskirt');
      return function(text) {
        var parser = rs.Markdown.std();
        return parser.render(text);
      };
    })());
  } catch (e) {
    console.log('Could not bench robotskirt.');
  }

  // showdown
  try {
    bench('showdown (reuse converter)', (function() {
      var Showdown = require('showdown');
      var convert = new Showdown.converter();
      return function(text) {
        return convert.makeHtml(text);
      };
    })());
    bench('showdown (new converter)', (function() {
      var Showdown = require('showdown');
      return function(text) {
        var convert = new Showdown.converter();
        return convert.makeHtml(text);
      };
    })());
  } catch (e) {
    console.log('Could not bench showdown.');
  }

  // markdown.js
  try {
    bench('markdown.js', require('markdown').parse);
  } catch (e) {
    console.log('Could not bench markdown.js.');
  }
}

/**
 * A simple one-time benchmark
 */

function time(options) {
  var options = options || {};
  if (options.marked) {
    marked.setOptions(options.marked);
  }
  bench('marked', marked);
}

/**
 * Markdown Test Suite Fixer
 *   This function is responsible for "fixing"
 *   the markdown test suite. There are
 *   certain aspects of the suite that
 *   are strange or might make tests
 *   fail for reasons unrelated to
 *   conformance.
 */

function fix(options) {
  ['tests', 'original', 'new'].forEach(function(dir) {
    try {
      fs.mkdirSync(path.resolve(__dirname, dir), 0755);
    } catch (e) {
      ;
    }
  });

  // rm -rf tests
  fs.readdirSync(path.resolve(__dirname, 'tests')).forEach(function(file) {
    fs.unlinkSync(path.resolve(__dirname, 'tests', file));
  });

  // cp -r original tests
  fs.readdirSync(path.resolve(__dirname, 'original')).forEach(function(file) {
    var nfile = file;
    if (file.indexOf('hard_wrapped_paragraphs_with_list_like_lines.') === 0) {
      nfile = file.replace(/\.(text|html)$/, '.nogfm.$1');
    }
    fs.writeFileSync(path.resolve(__dirname, 'tests', nfile),
      fs.readFileSync(path.resolve(__dirname, 'original', file)));
  });

  // node fix.js
  var dir = __dirname + '/tests';

  fs.readdirSync(dir).filter(function(file) {
    return path.extname(file) === '.html';
  }).forEach(function(file) {
    var file = path.join(dir, file)
      , html = fs.readFileSync(file, 'utf8');

    // fix unencoded quotes
    html = html
      .replace(/='([^\n']*)'(?=[^<>\n]*>)/g, '=&__APOS__;$1&__APOS__;')
      .replace(/="([^\n"]*)"(?=[^<>\n]*>)/g, '=&__QUOT__;$1&__QUOT__;')
      .replace(/"/g, '"')
      .replace(/'/g, ''')
      .replace(/&__QUOT__;/g, '"')
      .replace(/&__APOS__;/g, '\'');

    // add heading id's
    html = html.replace(/<(h[1-6])>([^<]+)<\/\1>/g, function(s, h, text) {
      var id = text
        .replace(/'/g, '\'')
        .replace(/"/g, '"')
        .replace(/>/g, '>')
        .replace(/</g, '<')
        .replace(/&/g, '&');

      id = id.toLowerCase().replace(/[^\w]+/g, '-');

      return '<' + h + ' id="' + id + '">' + text + '</' + h + '>';
    });

    fs.writeFileSync(file, html);
  });

  // turn
 into

  fs.readdirSync(dir).forEach(function(file) {
    var file = path.join(dir, file)
      , text = fs.readFileSync(file, 'utf8');

    text = text.replace(/(<|<)hr\s*\/(>|>)/g, '$1hr$2');

    fs.writeFileSync(file, text);
  });

  // markdown does some strange things.
  // it does not encode naked `>`, marked does.
  (function() {
    var file = dir + '/amps_and_angles_encoding.html';
    var html = fs.readFileSync(file, 'utf8')
      .replace('6 > 5.', '6 > 5.');

    fs.writeFileSync(file, html);
  })();

  // cp new/* tests/
  fs.readdirSync(path.resolve(__dirname, 'new')).forEach(function(file) {
    fs.writeFileSync(path.resolve(__dirname, 'tests', file),
      fs.readFileSync(path.resolve(__dirname, 'new', file)));
  });
}

/**
 * Argument Parsing
 */

function parseArg(argv) {
  var argv = process.argv.slice(2)
    , options = {}
    , orphans = []
    , arg;

  function getarg() {
    var arg = argv.shift();

    if (arg.indexOf('--') === 0) {
      // e.g. --opt
      arg = arg.split('=');
      if (arg.length > 1) {
        // e.g. --opt=val
        argv.unshift(arg.slice(1).join('='));
      }
      arg = arg[0];
    } else if (arg[0] === '-') {
      if (arg.length > 2) {
        // e.g. -abc
        argv = arg.substring(1).split('').map(function(ch) {
          return '-' + ch;
        }).concat(argv);
        arg = argv.shift();
      } else {
        // e.g. -a
      }
    } else {
      // e.g. foo
    }

    return arg;
  }

  while (argv.length) {
    arg = getarg();
    switch (arg) {
      case '-f':
      case '--fix':
      case 'fix':
        options.fix = true;
        break;
      case '-b':
      case '--bench':
        options.bench = true;
        break;
      case '-s':
      case '--stop':
        options.stop = true;
        break;
      case '-t':
      case '--time':
        options.time = true;
        break;
      default:
        if (arg.indexOf('--') === 0) {
          opt = camelize(arg.replace(/^--(no-)?/, ''));
          if (!marked.defaults.hasOwnProperty(opt)) {
            continue;
          }
          options.marked = options.marked || {};
          if (arg.indexOf('--no-') === 0) {
            options.marked[opt] = typeof marked.defaults[opt] !== 'boolean'
              ? null
              : false;
          } else {
            options.marked[opt] = typeof marked.defaults[opt] !== 'boolean'
              ? argv.shift()
              : true;
          }
        } else {
          orphans.push(arg);
        }
        break;
    }
  }

  return options;
}

/**
 * Helpers
 */

function camelize(text) {
  return text.replace(/(\w)-(\w)/g, function(_, a, b) {
    return a + b.toUpperCase();
  });
}

/**
 * Main
 */

function main(argv) {
  var opt = parseArg();

  if (opt.fix) {
    return fix(opt);
  }

  if (opt.bench) {
    return runBench(opt);
  }

  if (opt.time) {
    return time(opt);
  }

  return runTests(opt);
}

/**
 * Execute
 */

if (!module.parent) {
  process.title = 'marked';
  process.exit(main(process.argv.slice()) ? 0 : 1);
} else {
  exports = main;
  exports.main = main;
  exports.runTests = runTests;
  exports.runBench = runBench;
  exports.load = load;
  exports.bench = bench;
  module.exports = exports;
}


These should all get escaped:

Backslash: \\

Backtick: \`

Asterisk: \*

Underscore: \_

Left brace: \{

Right brace: \}

Left bracket: \[

Right bracket: \]

Left paren: \(

Right paren: \)

Greater-than: \>

Hash: \#

Period: \.

Bang: \!

Plus: \+

Minus: \-

These should not, because they occur within a code block:

	Backslash: \\

	Backtick: \`

	Asterisk: \*

	Underscore: \_

	Left brace: \{

	Right brace: \}

	Left bracket: \[

	Right bracket: \]

	Left paren: \(

	Right paren: \)

	Greater-than: \>

	Hash: \#

	Period: \.

	Bang: \!

	Plus: \+

	Minus: \-

Nor should these, which occur in code spans:

Backslash: `\\`

Backtick: `` \` ``

Asterisk: `\*`

Underscore: `\_`

Left brace: `\{`

Right brace: `\}`

Left bracket: `\[`

Right bracket: `\]`

Left paren: `\(`

Right paren: `\)`

Greater-than: `\>`

Hash: `\#`

Period: `\.`

Bang: `\!`

Plus: `\+`

Minus: `\-`

These should get escaped, even though they're matching pairs for
other Markdown constructs:

\*asterisks\*

\_underscores\_

\`backticks\`

This is a code span with a literal backslash-backtick sequence: `` \` ``

This is a tag with unescaped backticks <span attr='`ticks`'>bar.

This is a tag with backslashes bar.


* @param {boolean} refresh true (default) to refresh the editor, false to skip refreshing it

/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, CodeMirror, window */

/**
 * Editor is a 1-to-1 wrapper for a CodeMirror editor instance. It layers on Brackets-specific
 * functionality and provides APIs that cleanly pass through the bits of CodeMirror that the rest
 * of our codebase may want to interact with. An Editor is always backed by a Document, and stays
 * in sync with its content; because Editor keeps the Document alive, it's important to always
 * destroy() an Editor that's going away so it can release its Document ref.
 *
 * For now, there's a distinction between the "master" Editor for a Document - which secretly acts
 * as the Document's internal model of the text state - and the multitude of "slave" secondary Editors
 * which, via Document, sync their changes to and from that master.
 *
 * For now, direct access to the underlying CodeMirror object is still possible via _codeMirror --
 * but this is considered deprecated and may go away.
 *
 * The Editor object dispatches the following events:
 *    - keyEvent -- When any key event happens in the editor (whether it changes the text or not).
 *          Event handlers are passed ({Editor}, {KeyboardEvent}). The 2nd arg is the raw DOM event.
 *          Note: most listeners will only want to respond when event.type === "keypress".
 *    - cursorActivity -- When the user moves the cursor or changes the selection, or an edit occurs.
 *          Note: do not listen to this in order to be generally informed of edits--listen to the
 *          "change" event on Document instead.
 *    - scroll -- When the editor is scrolled, either by user action or programmatically.
 *    - lostContent -- When the backing Document changes in such a way that this Editor is no longer
 *          able to display accurate text. This occurs if the Document's file is deleted, or in certain
 *          Document->editor syncing edge cases that we do not yet support (the latter cause will
 *          eventually go away).
 *
 * The Editor also dispatches "change" events internally, but you should listen for those on
 * Documents, not Editors.
 *
 * These are jQuery events, so to listen for them you do something like this:
 *    $(editorInstance).on("eventname", handler);
 */
define(function (require, exports, module) {
    "use strict";

    var EditorManager      = require("editor/EditorManager"),
        CodeHintManager    = require("editor/CodeHintManager"),
        Commands           = require("command/Commands"),
        CommandManager     = require("command/CommandManager"),
        Menus              = require("command/Menus"),
        PerfUtils          = require("utils/PerfUtils"),
        PreferencesManager = require("preferences/PreferencesManager"),
        Strings            = require("strings"),
        TextRange          = require("document/TextRange").TextRange,
        TokenUtils         = require("utils/TokenUtils"),
        ViewUtils          = require("utils/ViewUtils");

    var PREFERENCES_CLIENT_ID = "com.adobe.brackets.Editor",
        defaultPrefs = { useTabChar: false, tabSize: 4, indentUnit: 4 };

    /** Editor preferences */
    var _prefs = PreferencesManager.getPreferenceStorage(PREFERENCES_CLIENT_ID, defaultPrefs);

    /** @type {boolean}  Global setting: When inserting new text, use tab characters? (instead of spaces) */
    var _useTabChar = _prefs.getValue("useTabChar");

    /** @type {boolean}  Global setting: Tab size */
    var _tabSize = _prefs.getValue("tabSize");

    /** @type {boolean}  Global setting: Indent unit (i.e. number of spaces when indenting) */
    var _indentUnit = _prefs.getValue("indentUnit");

    /**
     * @private
     * Handle Tab key press.
     * @param {!CodeMirror} instance CodeMirror instance.
     */
    function _handleTabKey(instance) {
        // Tab key handling is done as follows:
        // 1. If the selection is before any text and the indentation is to the left of
        //    the proper indentation then indent it to the proper place. Otherwise,
        //    add another tab. In either case, move the insertion point to the
        //    beginning of the text.
        // 2. If the selection is after the first non-space character, and is not an
        //    insertion point, indent the entire line(s).
        // 3. If the selection is after the first non-space character, and is an
        //    insertion point, insert a tab character or the appropriate number
        //    of spaces to pad to the nearest tab boundary.
        var from = instance.getCursor(true),
            to = instance.getCursor(false),
            line = instance.getLine(from.line),
            indentAuto = false,
            insertTab = false;

        if (from.line === to.line) {
            if (line.search(/\S/) > to.ch || to.ch === 0) {
                indentAuto = true;
            }
        }

        if (indentAuto) {
            var currentLength = line.length;
            CodeMirror.commands.indentAuto(instance);
            // If the amount of whitespace didn't change, insert another tab
            if (instance.getLine(from.line).length === currentLength) {
                insertTab = true;
                to.ch = 0;
            }
        } else if (instance.somethingSelected()) {
            CodeMirror.commands.indentMore(instance);
        } else {
            insertTab = true;
        }

        if (insertTab) {
            if (instance.getOption("indentWithTabs")) {
                CodeMirror.commands.insertTab(instance);
            } else {
                var i, ins = "", numSpaces = _indentUnit;
                numSpaces -= to.ch % numSpaces;
                for (i = 0; i < numSpaces; i++) {
                    ins += " ";
                }
                instance.replaceSelection(ins, "end");
            }
        }
    }

    /**
     * @private
     * Handle left arrow, right arrow, backspace and delete keys when soft tabs are used.
     * @param {!CodeMirror} instance CodeMirror instance
     * @param {number} direction Direction of movement: 1 for forward, -1 for backward
     * @param {function} functionName name of the CodeMirror function to call
     * @return {boolean} true if key was handled
     */
    function _handleSoftTabNavigation(instance, direction, functionName) {
        var handled = false;
        if (!instance.getOption("indentWithTabs")) {
            var cursor = instance.getCursor(),
                jump = cursor.ch % _indentUnit,
                line = instance.getLine(cursor.line);

            if (direction === 1) {
                jump = _indentUnit - jump;

                if (cursor.ch + jump > line.length) { // Jump would go beyond current line
                    return false;
                }

                if (line.substr(cursor.ch, jump).search(/\S/) === -1) {
                    instance[functionName](jump, "char");
                    handled = true;
                }
            } else {
                // Quick exit if we are at the beginning of the line
                if (cursor.ch === 0) {
                    return false;
                }

                // If we are on the tab boundary, jump by the full amount,
                // but not beyond the start of the line.
                if (jump === 0) {
                    jump = _indentUnit;
                }

                // Search backwards to the first non-space character
                var offset = line.substr(cursor.ch - jump, jump).search(/\s*$/g);

                if (offset !== -1) { // Adjust to jump to first non-space character
                    jump -= offset;
                }

                if (jump > 0) {
                    instance[functionName](-jump, "char");
                    handled = true;
                }
            }
        }

        return handled;
    }

    /**
     * Checks if the user just typed a closing brace/bracket/paren, and considers automatically
     * back-indenting it if so.
     */
    function _checkElectricChars(jqEvent, editor, event) {
        var instance = editor._codeMirror;
        if (event.type === "keypress") {
            var keyStr = String.fromCharCode(event.which || event.keyCode);
            if (/[\]\{\}\)]/.test(keyStr)) {
                // If all text before the cursor is whitespace, auto-indent it
                var cursor = instance.getCursor();
                var lineStr = instance.getLine(cursor.line);
                var nonWS = lineStr.search(/\S/);

                if (nonWS === -1 || nonWS >= cursor.ch) {
                    // Need to do the auto-indent on a timeout to ensure
                    // the keypress is handled before auto-indenting.
                    // This is the same timeout value used by the
                    // electricChars feature in CodeMirror.
                    window.setTimeout(function () {
                        instance.indentLine(cursor.line);
                    }, 75);
                }
            }
        }
    }

    function _handleKeyEvents(jqEvent, editor, event) {
        _checkElectricChars(jqEvent, editor, event);

        // Pass the key event to the code hint manager. It may call preventDefault() on the event.
        CodeHintManager.handleKeyEvent(editor, event);
    }

    function _handleSelectAll() {
        var result = new $.Deferred(),
            editor = EditorManager.getFocusedEditor();

        if (editor) {
            editor.selectAllNoScroll();
            result.resolve();
        } else {
            result.reject();    // command not handled
        }

        return result.promise();
    }

    /**
     * List of all current (non-destroy()ed) Editor instances. Needed when changing global preferences
     * that affect all editors, e.g. tabbing or color scheme settings.
     * @type {Array.<Editor>}
     */
    var _instances = [];

    /**
     * @constructor
     *
     * Creates a new CodeMirror editor instance bound to the given Document. The Document need not have
     * a "master" Editor realized yet, even if makeMasterEditor is false; in that case, the first time
     * an edit occurs we will automatically ask EditorManager to create a "master" editor to render the
     * Document modifiable.
     *
     * ALWAYS call destroy() when you are done with an Editor - otherwise it will leak a Document ref.
     *
     * @param {!Document} document
     * @param {!boolean} makeMasterEditor  If true, this Editor will set itself as the (secret) "master"
     *          Editor for the Document. If false, this Editor will attach to the Document as a "slave"/
     *          secondary editor.
     * @param {!string} mode  Syntax-highlighting language mode; "" means plain-text mode.
     *          See {@link EditorUtils#getModeFromFileExtension()}.
     * @param {!jQueryObject} container  Container to add the editor to.
     * @param {{startLine: number, endLine: number}=} range If specified, range of lines within the document
     *          to display in this editor. Inclusive.
     */
    function Editor(document, makeMasterEditor, mode, container, range) {
        var self = this;

        _instances.push(this);

        // Attach to document: add ref & handlers
        this.document = document;
        document.addRef();

        if (range) {    // attach this first: want range updated before we process a change
            this._visibleRange = new TextRange(document, range.startLine, range.endLine);
        }

        // store this-bound version of listeners so we can remove them later
        this._handleDocumentChange = this._handleDocumentChange.bind(this);
        this._handleDocumentDeleted = this._handleDocumentDeleted.bind(this);
        $(document).on("change", this._handleDocumentChange);
        $(document).on("deleted", this._handleDocumentDeleted);

        // (if makeMasterEditor, we attach the Doc back to ourselves below once we're fully initialized)

        this._inlineWidgets = [];

        // Editor supplies some standard keyboard behavior extensions of its own
        var codeMirrorKeyMap = {
            "Tab": _handleTabKey,
            "Shift-Tab": "indentLess",

            "Left": function (instance) {
                if (!_handleSoftTabNavigation(instance, -1, "moveH")) {
                    CodeMirror.commands.goCharLeft(instance);
                }
            },
            "Right": function (instance) {
                if (!_handleSoftTabNavigation(instance, 1, "moveH")) {
                    CodeMirror.commands.goCharRight(instance);
                }
            },
            "Backspace": function (instance) {
                if (!_handleSoftTabNavigation(instance, -1, "deleteH")) {
                    CodeMirror.commands.delCharBefore(instance);
                }
            },
            "Delete": function (instance) {
                if (!_handleSoftTabNavigation(instance, 1, "deleteH")) {
                    CodeMirror.commands.delCharAfter(instance);
                }
            },
            "Esc": function (instance) {
                self.removeAllInlineWidgets();
            },
            "Cmd-Left": "goLineStartSmart",
            "'>'": function (cm) { cm.closeTag(cm, '>'); },
            "'/'": function (cm) { cm.closeTag(cm, '/'); }
        };

        // We'd like null/"" to mean plain text mode. CodeMirror defaults to plaintext for any
        // unrecognized mode, but it complains on the console in that fallback case: so, convert
        // here so we're always explicit, avoiding console noise.
        if (!mode) {
            mode = "text/plain";
        }

        // Create the CodeMirror instance
        // (note: CodeMirror doesn't actually require using 'new', but jslint complains without it)
        this._codeMirror = new CodeMirror(container, {
            electricChars: false,   // we use our own impl of this to avoid CodeMirror bugs; see _checkElectricChars()
            indentWithTabs: _useTabChar,
            tabSize: _tabSize,
            indentUnit: _indentUnit,
            lineNumbers: true,
            matchBrackets: true,
            dragDrop: false,    // work around issue #1123
            extraKeys: codeMirrorKeyMap
        });

        // Can't get CodeMirror's focused state without searching for
        // CodeMirror-focused. Instead, track focus via onFocus and onBlur
        // options and track state with this._focused
        this._focused = false;

        this._installEditorListeners();

        $(this)
            .on("keyEvent", _handleKeyEvents)
            .on("change", this._handleEditorChange.bind(this));

        // Set code-coloring mode BEFORE populating with text, to avoid a flash of uncolored text
        this._codeMirror.setOption("mode", mode);

        // Initially populate with text. This will send a spurious change event, so need to make
        // sure this is understood as a 'sync from document' case, not a genuine edit
        this._duringSync = true;
        this._resetText(document.getText());
        this._duringSync = false;

        if (range) {
            // Hide all lines other than those we want to show. We do this rather than trimming the
            // text itself so that the editor still shows accurate line numbers.
            this._codeMirror.operation(function () {
                if (range.startLine > 0) {
                    self._hideLines(0, range.startLine);
                }

                var end = range.endLine + 1;
                if (end < self.lineCount()) {
                    self._hideLines(end, self.lineCount());
                }
            });
            this.setCursorPos(range.startLine, 0);
        }

        // Now that we're fully initialized, we can point the document back at us if needed
        if (makeMasterEditor) {
            document._makeEditable(this);
        }

        // Add scrollTop property to this object for the scroll shadow code to use
        Object.defineProperty(this, "scrollTop", {
            get: function () {
                return this._codeMirror.getScrollInfo().top;
            }
        });
    }

    /**
     * Removes this editor from the DOM and detaches from the Document. If this is the "master"
     * Editor that is secretly providing the Document's backing state, then the Document reverts to
     * a read-only string-backed mode.
     */
    Editor.prototype.destroy = function () {
        // CodeMirror docs for getWrapperElement() say all you have to do is "Remove this from your
        // tree to delete an editor instance."
        $(this.getRootElement()).remove();

        _instances.splice(_instances.indexOf(this), 1);

        // Disconnect from Document
        this.document.releaseRef();
        $(this.document).off("change", this._handleDocumentChange);
        $(this.document).off("deleted", this._handleDocumentDeleted);

        if (this._visibleRange) {   // TextRange also refs the Document
            this._visibleRange.dispose();
        }

        // If we're the Document's master editor, disconnecting from it has special meaning
        if (this.document._masterEditor === this) {
            this.document._makeNonEditable();
        }

        // Destroying us destroys any inline widgets we're hosting. Make sure their closeCallbacks
        // run, at least, since they may also need to release Document refs
        this._inlineWidgets.forEach(function (inlineWidget) {
            inlineWidget.onClosed();
        });
    };

    /**
     * Selects all text and maintains the current scroll position.
     */
    Editor.prototype.selectAllNoScroll = function () {
        var cm = this._codeMirror,
            info = this._codeMirror.getScrollInfo();

        // Note that we do not have to check for the visible range here. This
        // concern is handled internally by code mirror.
        cm.operation(function () {
            cm.scrollTo(info.left, info.top);
            cm.execCommand("selectAll");
        });
    };

    /**
     * Ensures that the lines that are actually hidden in the inline editor correspond to
     * the desired visible range.
     */
    Editor.prototype._updateHiddenLines = function () {
        if (this._visibleRange) {
            var cm = this._codeMirror,
                self = this;
            cm.operation(function () {
                // TODO: could make this more efficient by only iterating across the min-max line
                // range of the union of all changes
                self._hideLines(0, self._visibleRange.startLine);
                self._hideLines(self._visibleRange.endLine + 1, self.lineCount());
            });
        }
    };

    Editor.prototype._applyChanges = function (changeList) {
        // _visibleRange has already updated via its own Document listener. See if this change caused
        // it to lose sync. If so, our whole view is stale - signal our owner to close us.
        if (this._visibleRange) {
            if (this._visibleRange.startLine === null || this._visibleRange.endLine === null) {
                $(this).triggerHandler("lostContent");
                return;
            }
        }

        // Apply text changes to CodeMirror editor
        var cm = this._codeMirror;
        cm.operation(function () {
            var change, newText;
            for (change = changeList; change; change = change.next) {
                newText = change.text.join('\n');
                if (!change.from || !change.to) {
                    if (change.from || change.to) {
                        console.error("Change record received with only one end undefined--replacing entire text");
                    }
                    cm.setValue(newText);
                } else {
                    cm.replaceRange(newText, change.from, change.to);
                }

            }
        });

        // The update above may have inserted new lines - must hide any that fall outside our range
        this._updateHiddenLines();
    };

    /**
     * Responds to changes in the CodeMirror editor's text, syncing the changes to the Document.
     * There are several cases where we want to ignore a CodeMirror change:
     *  - if we're the master editor, editor changes can be ignored because Document is already listening
     *    for our changes
     *  - if we're a secondary editor, editor changes should be ignored if they were caused by us reacting
     *    to a Document change
     */
    Editor.prototype._handleEditorChange = function (event, editor, changeList) {
        // we're currently syncing from the Document, so don't echo back TO the Document
        if (this._duringSync) {
            return;
        }

        // Secondary editor: force creation of "master" editor backing the model, if doesn't exist yet
        this.document._ensureMasterEditor();

        if (this.document._masterEditor !== this) {
            // Secondary editor:
            // we're not the ground truth; if we got here, this was a real editor change (not a
            // sync from the real ground truth), so we need to sync from us into the document
            // (which will directly push the change into the master editor).
            // FUTURE: Technically we should add a replaceRange() method to Document and go through
            // that instead of talking to its master editor directly. It's not clear yet exactly
            // what the right Document API would be, though.
            this._duringSync = true;
            this.document._masterEditor._applyChanges(changeList);
            this._duringSync = false;

            // Update which lines are hidden inside our editor, since we're not going to go through
            // _applyChanges() in our own editor.
            this._updateHiddenLines();
        }
        // Else, Master editor:
        // we're the ground truth; nothing else to do, since Document listens directly to us
        // note: this change might have been a real edit made by the user, OR this might have
        // been a change synced from another editor

        CodeHintManager.handleChange(this);
    };

    /**
     * Responds to changes in the Document's text, syncing the changes into our CodeMirror instance.
     * There are several cases where we want to ignore a Document change:
     *  - if we're the master editor, Document changes should be ignored becuase we already have the right
     *    text (either the change originated with us, or it has already been set into us by Document)
     *  - if we're a secondary editor, Document changes should be ignored if they were caused by us sending
     *    the document an editor change that originated with us
     */
    Editor.prototype._handleDocumentChange = function (event, doc, changeList) {
        var change;

        // we're currently syncing to the Document, so don't echo back FROM the Document
        if (this._duringSync) {
            return;
        }

        if (this.document._masterEditor !== this) {
            // Secondary editor:
            // we're not the ground truth; and if we got here, this was a Document change that
            // didn't come from us (e.g. a sync from another editor, a direct programmatic change
            // to the document, or a sync from external disk changes)... so sync from the Document
            this._duringSync = true;
            this._applyChanges(changeList);
            this._duringSync = false;
        }
        // Else, Master editor:
        // we're the ground truth; nothing to do since Document change is just echoing our
        // editor changes
    };

    /**
     * Responds to the Document's underlying file being deleted. The Document is now basically dead,
     * so we must close.
     */
    Editor.prototype._handleDocumentDeleted = function (event) {
        // Pass the delete event along as the cause (needed in MultiRangeInlineEditor)
        $(this).triggerHandler("lostContent", [event]);
    };

    /**
     * Install event handlers on the CodeMirror instance, translating them into
     * jQuery events on the Editor instance.
     */
    Editor.prototype._installEditorListeners = function () {
        var self = this;

        // onKeyEvent is an option in CodeMirror rather than an event--it's a
        // low-level hook for all keyboard events rather than a specific event. For
        // our purposes, though, it's convenient to treat it as an event internally,
        // so we bridge it to jQuery events the same way we do ordinary CodeMirror
        // events.
        this._codeMirror.setOption("onKeyEvent", function (instance, event) {
            $(self).triggerHandler("keyEvent", [self, event]);
            return event.defaultPrevented;   // false tells CodeMirror we didn't eat the event
        });

        // FUTURE: if this list grows longer, consider making this a more generic mapping
        // NOTE: change is a "private" event--others shouldn't listen to it on Editor, only on
        // Document
        this._codeMirror.on("change", function (instance, changeList) {
            $(self).triggerHandler("change", [self, changeList]);
        });
        this._codeMirror.on("cursorActivity", function (instance) {
            $(self).triggerHandler("cursorActivity", [self]);
        });
        this._codeMirror.on("scroll", function (instance) {
            // If this editor is visible, close all dropdowns on scroll.
            // (We don't want to do this if we're just scrolling in a non-visible editor
            // in response to some document change event.)
            if (self.isFullyVisible()) {
                Menus.closeAll();
            }

            $(self).triggerHandler("scroll", [self]);
        });

        // Convert CodeMirror onFocus events to EditorManager activeEditorChanged
        this._codeMirror.on("focus", function () {
            self._focused = true;
            EditorManager._notifyActiveEditorChanged(self);
        });

        this._codeMirror.on("blur", function () {
            self._focused = false;
            // EditorManager only cares about other Editors gaining focus, so we don't notify it of anything here
        });

        this._codeMirror.on("update", function (instance) {
            $(self).triggerHandler("update", [self]);
        });
    };

    /**
     * Sets the contents of the editor and clears the undo/redo history. Dispatches a change event.
     * Semi-private: only Document should call this.
     * @param {!string} text
     */
    Editor.prototype._resetText = function (text) {
        var perfTimerName = PerfUtils.markStart("Edtitor._resetText()\t" + (!this.document || this.document.file.fullPath));

        var cursorPos = this.getCursorPos(),
            scrollPos = this.getScrollPos();

        // This *will* fire a change event, but we clear the undo immediately afterward
        this._codeMirror.setValue(text);

        // Make sure we can't undo back to the empty state before setValue()
        this._codeMirror.clearHistory();

        // restore cursor and scroll positions
        this.setCursorPos(cursorPos);
        this.setScrollPos(scrollPos.x, scrollPos.y);

        PerfUtils.addMeasurement(perfTimerName);
    };

    /**
     * Gets the current cursor position within the editor. If there is a selection, returns whichever
     * end of the range the cursor lies at.
     * @param {boolean} expandTabs If true, return the actual visual column number instead of the character offset in
     *      the "ch" property.
     * @return !{line:number, ch:number}
     */
    Editor.prototype.getCursorPos = function (expandTabs) {
        var cursor = this._codeMirror.getCursor();

        if (expandTabs) {
            var line    = this._codeMirror.getRange({line: cursor.line, ch: 0}, cursor),
                tabSize = Editor.getTabSize(),
                column  = 0,
                i;

            for (i = 0; i < line.length; i++) {
                if (line[i] === '\t') {
                    column += (tabSize - (column % tabSize));
                } else {
                    column++;
                }
            }

            cursor.ch = column;
        }

        return cursor;
    };

    /**
     * Sets the cursor position within the editor. Removes any selection.
     * @param {number} line The 0 based line number.
     * @param {number} ch  The 0 based character position; treated as 0 if unspecified.
     * @param {boolean} center  true if the view should be centered on the new cursor position
     */
    Editor.prototype.setCursorPos = function (line, ch, center) {
        this._codeMirror.setCursor(line, ch);
        if (center) {
            this.centerOnCursor();
        }
    };

    var CENTERING_MARGIN = 0.15;

    /**
     * Scrolls the editor viewport to vertically center the line with the cursor,
     * but only if the cursor is currently near the edges of the viewport or
     * entirely outside the viewport.
     *
     * This does not alter the horizontal scroll position.
     */
    Editor.prototype.centerOnCursor = function () {
        var $scrollerElement = $(this.getScrollerElement());
        var editorHeight = $scrollerElement.height();

        // we need to make adjustments for the statusbar's padding on the bottom and the menu bar on top.
        var statusBarHeight = $scrollerElement.outerHeight() - editorHeight;
        var menuBarHeight = $scrollerElement.offset().top;

        var documentCursorPosition = this._codeMirror.cursorCoords(null, "local").bottom;
        var screenCursorPosition = this._codeMirror.cursorCoords(null, "page").bottom - menuBarHeight;

        // If the cursor is already reasonably centered, we won't
        // make any change. "Reasonably centered" is defined as
        // not being within CENTERING_MARGIN of the top or bottom
        // of the editor (where CENTERING_MARGIN is a percentage
        // of the editor height).
        if (screenCursorPosition < editorHeight * CENTERING_MARGIN ||
                screenCursorPosition > editorHeight * (1 - CENTERING_MARGIN)) {
            this.setScrollPos(null, documentCursorPosition -
                                editorHeight / 2 + statusBarHeight);
        }
    };

    /**
     * Given a position, returns its index within the text (assuming \n newlines)
     * @param {!{line:number, ch:number}}
     * @return {number}
     */
    Editor.prototype.indexFromPos = function (coords) {
        return this._codeMirror.indexFromPos(coords);
    };

    /**
     * Returns true if pos is between start and end (inclusive at both ends)
     * @param {{line:number, ch:number}} pos
     * @param {{line:number, ch:number}} start
     * @param {{line:number, ch:number}} end
     *
     */
    Editor.prototype.posWithinRange = function (pos, start, end) {
        var startIndex = this.indexFromPos(start),
            endIndex = this.indexFromPos(end),
            posIndex = this.indexFromPos(pos);

        return posIndex >= startIndex && posIndex <= endIndex;
    };

    /**
     * @return {boolean} True if there's a text selection; false if there's just an insertion point
     */
    Editor.prototype.hasSelection = function () {
        return this._codeMirror.somethingSelected();
    };

    /**
     * Gets the current selection. Start is inclusive, end is exclusive. If there is no selection,
     * returns the current cursor position as both the start and end of the range (i.e. a selection
     * of length zero).
     * @return {!{start:{line:number, ch:number}, end:{line:number, ch:number}}}
     */
    Editor.prototype.getSelection = function () {
        var selStart = this._codeMirror.getCursor(true),
            selEnd   = this._codeMirror.getCursor(false);
        return { start: selStart, end: selEnd };
    };

    /**
     * @return {!string} The currently selected text, or "" if no selection. Includes \n if the
     * selection spans multiple lines (does NOT reflect the Document's line-endings style).
     */
    Editor.prototype.getSelectedText = function () {
        return this._codeMirror.getSelection();
    };

    /**
     * Sets the current selection. Start is inclusive, end is exclusive. Places the cursor at the
     * end of the selection range. Optionally centers the around the cursor after
     * making the selection
     *
     * @param {!{line:number, ch:number}} start
     * @param {!{line:number, ch:number}} end
     * @param {boolean} center true to center the viewport
     */
    Editor.prototype.setSelection = function (start, end, center) {
        this._codeMirror.setSelection(start, end);
        if (center) {
            this.centerOnCursor();
        }
    };

    /**
     * Selects word that the given pos lies within or adjacent to. If pos isn't touching a word
     * (e.g. within a token like "//"), moves the cursor to pos without selecting a range.
     * Adapted from selectWordAt() in CodeMirror v2.
     * @param {!{line:number, ch:number}}
     */
    Editor.prototype.selectWordAt = function (pos) {
        var line = this.document.getLine(pos.line),
            start = pos.ch,
            end = pos.ch;

        function isWordChar(ch) {
            return (/\w/).test(ch) || ch.toUpperCase() !== ch.toLowerCase();
        }

        while (start > 0 && isWordChar(line.charAt(start - 1))) {
            --start;
        }
        while (end < line.length && isWordChar(line.charAt(end))) {
            ++end;
        }
        this.setSelection({line: pos.line, ch: start}, {line: pos.line, ch: end});
    };

    /**
     * Gets the total number of lines in the the document (includes lines not visible in the viewport)
     * @returns {!number}
     */
    Editor.prototype.lineCount = function () {
        return this._codeMirror.lineCount();
    };

    /**
     * Gets the number of the first visible line in the editor.
     * @returns {number} The 0-based index of the first visible line.
     */
    Editor.prototype.getFirstVisibleLine = function () {
        return (this._visibleRange ? this._visibleRange.startLine : 0);
    };

    /**
     * Gets the number of the last visible line in the editor.
     * @returns {number} The 0-based index of the last visible line.
     */
    Editor.prototype.getLastVisibleLine = function () {
        return (this._visibleRange ? this._visibleRange.endLine : this.lineCount() - 1);
    };

    /* Hides the specified line number in the editor
     * @param {!from} line to start hiding from (inclusive)
     * @param {!to} line to end hiding at (exclusive)
     */
    Editor.prototype._hideLines = function (from, to) {
        if (to <= from) {
            return;
        }

        var value = this._codeMirror.markText(
            {line: from, ch: 0},
            {line: to - 1, ch: this._codeMirror.getLine(to - 1).length},
            {collapsed: true, inclusiveLeft: true, inclusiveRight: true}
        );

        return value;
    };

    /**
     * Gets the total height of the document in pixels (not the viewport)
     * @returns {!number} height in pixels
     */
    Editor.prototype.totalHeight = function () {
        return this.getScrollerElement().scrollHeight;
    };

    /**
     * Gets the scroller element from the editor.
     * @returns {!HTMLDivElement} scroller
     */
    Editor.prototype.getScrollerElement = function () {
        return this._codeMirror.getScrollerElement();
    };

    /**
     * Gets the root DOM node of the editor.
     * @returns {!HTMLDivElement} The editor's root DOM node.
     */
    Editor.prototype.getRootElement = function () {
        return this._codeMirror.getWrapperElement();
    };

    /**
     * Gets the lineSpace element within the editor (the container around the individual lines of code).
     * FUTURE: This is fairly CodeMirror-specific. Logic that depends on this may break if we switch
     * editors.
     * @returns {!HTMLDivElement} The editor's lineSpace element.
     */
    Editor.prototype._getLineSpaceElement = function () {
        return $(".CodeMirror-lines", this.getScrollerElement()).children().get(0);
    };

    /**
     * Returns the current scroll position of the editor.
     * @returns {{x:number, y:number}} The x,y scroll position in pixels
     */
    Editor.prototype.getScrollPos = function () {
        var scrollInfo = this._codeMirror.getScrollInfo();
        return { x: scrollInfo.left, y: scrollInfo.top };
    };

    /**
     * Sets the current scroll position of the editor.
     * @param {number} x scrollLeft position in pixels
     * @param {number} y scrollTop position in pixels
     */
    Editor.prototype.setScrollPos = function (x, y) {
        this._codeMirror.scrollTo(x, y);
    };

    /**
     * Adds an inline widget below the given line. If any inline widget was already open for that
     * line, it is closed without warning.
     * @param {!{line:number, ch:number}} pos  Position in text to anchor the inline.
     * @param {!InlineWidget} inlineWidget The widget to add.
     * @param {boolean=} scrollLineIntoView Scrolls the associated line into view. Default true.
     */
    Editor.prototype.addInlineWidget = function (pos, inlineWidget, scrollLineIntoView) {
        var self = this;

        this.removeAllInlineWidgetsForLine(pos.line);

        if (scrollLineIntoView === undefined) {
            scrollLineIntoView = true;
        }

        if (scrollLineIntoView) {
            this._codeMirror.scrollIntoView(pos);
        }

        inlineWidget.info = this._codeMirror.addLineWidget(pos.line, inlineWidget.htmlContent,
                                                           { coverGutter: true, noHScroll: true });
        CodeMirror.on(inlineWidget.info.line, "delete", function () {
            self._removeInlineWidgetInternal(inlineWidget);
            inlineWidget.onClosed();
        });
        this._inlineWidgets.push(inlineWidget);

        // Callback to widget once parented to the editor
        inlineWidget.onAdded();
    };

    /**
     * Removes all inline widgets
     */
    Editor.prototype.removeAllInlineWidgets = function () {
        // copy the array because _removeInlineWidgetInternal will modifying the original
        var widgets = [].concat(this.getInlineWidgets());

        widgets.forEach(function (widget) {
            this.removeInlineWidget(widget);
        }, this);
    };

    /**
     * Removes the given inline widget.
     * @param {number} inlineWidget The widget to remove.
     */
    Editor.prototype.removeInlineWidget = function (inlineWidget) {
        var lineNum = this._getInlineWidgetLineNumber(inlineWidget);

        this._codeMirror.removeLineWidget(inlineWidget.info);
        this._removeInlineWidgetInternal(inlineWidget);
        inlineWidget.onClosed();
    };

    /**
     * Removes all inline widgets for a given line
     * @param {number} lineNum The line number to modify
     */
    Editor.prototype.removeAllInlineWidgetsForLine = function (lineNum) {
        var lineInfo = this._codeMirror.lineInfo(lineNum),
            widgetInfos = (lineInfo && lineInfo.widgets) ? [].concat(lineInfo.widgets) : null,
            self = this;

        if (widgetInfos && widgetInfos.length) {
            // Map from CodeMirror LineWidget to Brackets InlineWidget
            var inlineWidget,
                allWidgetInfos = this._inlineWidgets.map(function (w) {
                    return w.info;
                });

            widgetInfos.forEach(function (info) {
                // Lookup the InlineWidget object using the same index
                inlineWidget = self._inlineWidgets[allWidgetInfos.indexOf(info)];
                self.removeInlineWidget(inlineWidget);
            });

        }
    };

    /**
     * Cleans up the given inline widget from our internal list of widgets.
     * @param {number} inlineId  id returned by addInlineWidget().
     */
    Editor.prototype._removeInlineWidgetInternal = function (inlineWidget) {
        var i;
        var l = this._inlineWidgets.length;
        for (i = 0; i < l; i++) {
            if (this._inlineWidgets[i] === inlineWidget) {
                this._inlineWidgets.splice(i, 1);
                break;
            }
        }
    };

    /**
     * Returns a list of all inline widgets currently open in this editor. Each entry contains the
     * inline's id, and the data parameter that was passed to addInlineWidget().
     * @return {!Array.<{id:number, data:Object}>}
     */
    Editor.prototype.getInlineWidgets = function () {
        return this._inlineWidgets;
    };

    /**
     * Returns the offset of the top of the virtual scroll area relative to the browser window (not the editor
     * itself). Mainly useful for calculations related to scrollIntoView(), where you're starting with the
     * offset() of a child widget (relative to the browser window) and need to figure out how far down it is from
     * the top of the virtual scroll area (excluding the top padding).
     * @return {number}
     */
    Editor.prototype.getVirtualScrollAreaTop = function () {
        var topPadding = this._getLineSpaceElement().offsetTop, // padding within mover
            scroller = this.getScrollerElement();
        return $(scroller).offset().top - scroller.scrollTop + topPadding;
    };

    /**
     * Sets the height of an inline widget in this editor.
     * @param {!InlineWidget} inlineWidget The widget whose height should be set.
     * @param {!number} height The height of the widget.
     * @param {boolean} ensureVisible Whether to scroll the entire widget into view.
     */
    Editor.prototype.setInlineWidgetHeight = function (inlineWidget, height, ensureVisible) {
        var self = this,
            node = inlineWidget.htmlContent,
            oldHeight = (node && $(node).height()) || 0,
            changed = (oldHeight !== height),
            isAttached = inlineWidget.info !== undefined;

        if (changed) {
            $(node).height(height);

            if (isAttached) {
                // Notify CodeMirror for the height change
                inlineWidget.info.changed();
            }
        }

        if (ensureVisible && isAttached) {
            var offset = $(node).offset(), // offset relative to document
                position = $(node).position(), // position within parent linespace
                scrollerTop = self.getVirtualScrollAreaTop();

            self._codeMirror.scrollIntoView({
                left: position.left,
                top: offset.top - scrollerTop,
                right: position.left, // don't try to make the right edge visible
                bottom: offset.top + height - scrollerTop
            });
        }
    };

    /**
     * @private
     * Get the starting line number for an inline widget.
     * @param {!InlineWidget} inlineWidget
     * @return {number} The line number of the widget or -1 if not found.
     */
    Editor.prototype._getInlineWidgetLineNumber = function (inlineWidget) {
        return this._codeMirror.getLineNumber(inlineWidget.info.line);
    };

    /** Gives focus to the editor control */
    Editor.prototype.focus = function () {
        this._codeMirror.focus();
    };

    /** Returns true if the editor has focus */
    Editor.prototype.hasFocus = function () {
        return this._focused;
    };

    /**
     * Re-renders the editor UI
     * @param {boolean=} handleResize true if this is in response to resizing the editor. Default false.
     */
    Editor.prototype.refresh = function (handleResize) {
        // If focus is currently in a child of the CodeMirror editor (e.g. in an inline widget), but not in
        // the CodeMirror input field itself, remember the focused item so we can restore focus after the
        // refresh (which might cause the widget to be removed from the display list temporarily).
        var focusedItem = window.document.activeElement,
            restoreFocus = $.contains(this._codeMirror.getScrollerElement(), focusedItem);
        this._codeMirror.refresh();
        if (restoreFocus) {
            focusedItem.focus();
        }
    };

    /**
     * Re-renders the editor, and all children inline editors.
     * @param {boolean=} handleResize true if this is in response to resizing the editor. Default false.
     */
    Editor.prototype.refreshAll = function (handleResize) {
        this.refresh(handleResize);
        this.getInlineWidgets().forEach(function (inlineWidget) {
            inlineWidget.refresh();
        });
    };

    /** Undo the last edit. */
    Editor.prototype.undo = function () {
        this._codeMirror.undo();
    };

    /** Redo the last un-done edit. */
    Editor.prototype.redo = function () {
        this._codeMirror.redo();
    };

    /**
     * Shows or hides the editor within its parent. Does not force its ancestors to
     * become visible.
     * @param {boolean} show true to show the editor, false to hide it
     */
    Editor.prototype.setVisible = function (show) {
        $(this.getRootElement()).css("display", (show ? "" : "none"));
        this.refresh();
        if (show) {
            this._inlineWidgets.forEach(function (inlineWidget) {
                inlineWidget.onParentShown();
            });
        }
    };

    /**
     * Returns true if the editor is fully visible--i.e., is in the DOM, all ancestors are
     * visible, and has a non-zero width/height.
     */
    Editor.prototype.isFullyVisible = function () {
        return $(this.getRootElement()).is(":visible");
    };

    /**
     * Gets the syntax-highlighting mode for the current selection or cursor position. (The mode may
     * vary within one file due to embedded languages, e.g. JS embedded in an HTML script block).
     *
     * Returns null if the mode at the start of the selection differs from the mode at the end -
     * an *approximation* of whether the mode is consistent across the whole range (a pattern like
     * A-B-A would return A as the mode, not null).
     *
     * @return {?(Object|String)} Object or Name of syntax-highlighting mode; see {@link EditorUtils#getModeFromFileExtension()}.
     */
    Editor.prototype.getModeForSelection = function () {
        // Check for mixed mode info
        var sel         = this.getSelection(),
            outerMode   = this._codeMirror.getMode(),
            startMode   = TokenUtils.getModeAt(this._codeMirror, sel.start),
            isMixed     = (outerMode.name !== startMode.name);

        if (isMixed) {
            // If mixed mode, check that mode is the same at start & end of selection
            if (sel.start.line !== sel.end.line || sel.start.ch !== sel.end.ch) {
                var endMode = TokenUtils.getModeAt(this._codeMirror, sel.end);

                if (startMode.name !== endMode.name) {
                    return null;
                }
            }

            return startMode.name;
        } else {
            // Mode does not vary: just use the editor-wide mode
            return this._codeMirror.getOption("mode");
        }
    };

    /**
     * Gets the syntax-highlighting mode for the document.
     *
     * @return {Object|String} Object or Name of syntax-highlighting mode; see {@link EditorUtils#getModeFromFileExtension()}.
     */
    Editor.prototype.getModeForDocument = function () {
        return this._codeMirror.getOption("mode");
    };

    /**
     * Sets the syntax-highlighting mode for the document.
     *
     * @param {string} mode Name of syntax highlighting mode.
     */
    Editor.prototype.setModeForDocument = function (mode) {
        this._codeMirror.setOption("mode", mode);
    };

    /**
     * The Document we're bound to
     * @type {!Document}
     */
    Editor.prototype.document = null;

    /**
     * If true, we're in the middle of syncing to/from the Document. Used to ignore spurious change
     * events caused by us (vs. change events caused by others, which we need to pay attention to).
     * @type {!boolean}
     */
    Editor.prototype._duringSync = false;

    /**
     * @private
     * NOTE: this is actually "semi-private": EditorManager also accesses this field... as well as
     * a few other modules. However, we should try to gradually move most code away from talking to
     * CodeMirror directly.
     * @type {!CodeMirror}
     */
    Editor.prototype._codeMirror = null;

    /**
     * @private
     * @type {!Array.<{id:number, data:Object}>}
     */
    Editor.prototype._inlineWidgets = null;

    /**
     * @private
     * @type {?TextRange}
     */
    Editor.prototype._visibleRange = null;

    // Global settings that affect all Editor instances (both currently open Editors as well as those created
    // in the future)

    /**
     * Sets whether to use tab characters (vs. spaces) when inserting new text. Affects all Editors.
     * @param {boolean} value
     */
    Editor.setUseTabChar = function (value) {
        _useTabChar = value;
        _instances.forEach(function (editor) {
            editor._codeMirror.setOption("indentWithTabs", _useTabChar);
        });

        _prefs.setValue("useTabChar", Boolean(_useTabChar));
    };

    /** @type {boolean} Gets whether all Editors use tab characters (vs. spaces) when inserting new text */
    Editor.getUseTabChar = function (value) {
        return _useTabChar;
    };

    /**
     * Sets tab character width. Affects all Editors.
     * @param {number} value
     */
    Editor.setTabSize = function (value) {
        _tabSize = value;
        _instances.forEach(function (editor) {
            editor._codeMirror.setOption("tabSize", _tabSize);
        });

        _prefs.setValue("tabSize", _tabSize);
    };

    /** @type {number} Get indent unit  */
    Editor.getTabSize = function (value) {
        return _tabSize;
    };

    /**
     * Sets indentation width. Affects all Editors.
     * @param {number} value
     */
    Editor.setIndentUnit = function (value) {
        _indentUnit = value;
        _instances.forEach(function (editor) {
            editor._codeMirror.setOption("indentUnit", _indentUnit);
        });

        _prefs.setValue("indentUnit", _indentUnit);
    };

    /** @type {number} Get indentation width */
    Editor.getIndentUnit = function (value) {
        return _indentUnit;
    };

    // Global commands that affect the currently focused Editor instance, wherever it may be
    CommandManager.register(Strings.CMD_SELECT_ALL,     Commands.EDIT_SELECT_ALL, _handleSelectAll);

    // Define public API
    exports.Editor = Editor;
});


/*!
 * jQuery UI Mouse @VERSION
 *
 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Mouse
 *
 * Depends:
 *	jquery.ui.widget.js
 */
(function( $, undefined ) {

var mouseHandled = false;
$( document ).mouseup( function( e ) {
	mouseHandled = false;
});

$.widget("ui.mouse", {
	version: "@VERSION",
	options: {
		cancel: 'input,textarea,button,select,option',
		distance: 1,
		delay: 0
	},
	_mouseInit: function() {
		var that = this;

		this.element
			.bind('mousedown.'+this.widgetName, function(event) {
				return that._mouseDown(event);
			})
			.bind('click.'+this.widgetName, function(event) {
				if (true === $.data(event.target, that.widgetName + '.preventClickEvent')) {
					$.removeData(event.target, that.widgetName + '.preventClickEvent');
					event.stopImmediatePropagation();
					return false;
				}
			});

		this.started = false;
	},

	// TODO: make sure destroying one instance of mouse doesn't mess with
	// other instances of mouse
	_mouseDestroy: function() {
		this.element.unbind('.'+this.widgetName);
		$(document)
			.unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
			.unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
	},

	_mouseDown: function(event) {
		// don't let more than one widget handle mouseStart
		if( mouseHandled ) { return; }

		// we may have missed mouseup (out of window)
		(this._mouseStarted && this._mouseUp(event));

		this._mouseDownEvent = event;

		var that = this,
			btnIsLeft = (event.which === 1),
			// event.target.nodeName works around a bug in IE 8 with
			// disabled inputs (#7620)
			elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
		if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
			return true;
		}

		this.mouseDelayMet = !this.options.delay;
		if (!this.mouseDelayMet) {
			this._mouseDelayTimer = setTimeout(function() {
				that.mouseDelayMet = true;
			}, this.options.delay);
		}

		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
			this._mouseStarted = (this._mouseStart(event) !== false);
			if (!this._mouseStarted) {
				event.preventDefault();
				return true;
			}
		}

		// Click event may never have fired (Gecko & Opera)
		if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
			$.removeData(event.target, this.widgetName + '.preventClickEvent');
		}

		// these delegates are required to keep context
		this._mouseMoveDelegate = function(event) {
			return that._mouseMove(event);
		};
		this._mouseUpDelegate = function(event) {
			return that._mouseUp(event);
		};
		$(document)
			.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
			.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);

		event.preventDefault();

		mouseHandled = true;
		return true;
	},

	_mouseMove: function(event) {
		// IE mouseup check - mouseup happened when mouse was out of window
		if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
			return this._mouseUp(event);
		}

		if (this._mouseStarted) {
			this._mouseDrag(event);
			return event.preventDefault();
		}

		if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
			this._mouseStarted =
				(this._mouseStart(this._mouseDownEvent, event) !== false);
			(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
		}

		return !this._mouseStarted;
	},

	_mouseUp: function(event) {
		$(document)
			.unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
			.unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);

		if (this._mouseStarted) {
			this._mouseStarted = false;

			if (event.target === this._mouseDownEvent.target) {
				$.data(event.target, this.widgetName + '.preventClickEvent', true);
			}

			this._mouseStop(event);
		}

		return false;
	},

	_mouseDistanceMet: function(event) {
		return (Math.max(
				Math.abs(this._mouseDownEvent.pageX - event.pageX),
				Math.abs(this._mouseDownEvent.pageY - event.pageY)
			) >= this.options.distance
		);
	},

	_mouseDelayMet: function(event) {
		return this.mouseDelayMet;
	},

	// These are placeholder methods, to be overriden by extending plugin
	_mouseStart: function(event) {},
	_mouseDrag: function(event) {},
	_mouseStop: function(event) {},
	_mouseCapture: function(event) { return true; }
});

})(jQuery);


/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, brackets, $ */

define(function (require, exports, module) {
    "use strict";

    // Brackets modules
    var CommandManager      = brackets.getModule("command/CommandManager"),
        EditorManager       = brackets.getModule("editor/EditorManager"),
        DocumentManager     = brackets.getModule("document/DocumentManager"),
        Menus               = brackets.getModule("command/Menus");

    // Define the functions that Commands will execute
    function TestCommand1() {
        var command1 = CommandManager.get("extensionTest.command1");
        if (!command1) {
            return;
        }
        var command2 = CommandManager.get("extensionTest.command2");
        if (!command2) {
            return;
        }

        var checked = command1.getChecked();
        if (checked) {
            alert("Unchecking self. Disabling next.");
            command2.setEnabled(false);
        } else {
            alert("Checking self. Enabling next.");
            command2.setEnabled(true);
        }
        command1.setChecked(!checked);
    }

    function TestCommand2() {
        alert("Executing command 2");
    }

    function TestCommand3() {
        alert("Executing command 3");
    }

    // Register the functions as commands
    var command1 = CommandManager.register("Toggle Checkmark", "extensionTest.command1", TestCommand1);
    var command2 = CommandManager.register("Enabled when previous is Checked", "extensionTest.command2", TestCommand2);
    var command3 = CommandManager.register("Enabled when text selected", "extensionTest.command3", TestCommand3);

    // Set the Command initial state
    command1.setChecked(true);
    command2.setEnabled(true);
    command3.setEnabled(false);

    // Update the MenuItem by changing the underlying command
    var updateEnabledState = function () {
        var editor = EditorManager.getFocusedEditor();
        command3.setEnabled(editor && editor.getSelectedText() !== "");
    };
    var editor_cmenu = Menus.getContextMenu(Menus.ContextMenuIds.EDITOR_MENU);
    $(editor_cmenu).on("beforeContextMenuOpen", updateEnabledState);

    // Add the Commands as MenuItems of the Editor context menu
    if (editor_cmenu) {
        editor_cmenu.addMenuDivider();
        editor_cmenu.addMenuItem("extensionTest.command1");
        editor_cmenu.addMenuItem("extensionTest.command2");
        editor_cmenu.addMenuItem("extensionTest.command3");
    }
});


/* Welsh/UK initialisation for the jQuery UI date picker plugin. */
/* Written by William Griffiths. */
jQuery(function($){
	$.datepicker.regional['cy-GB'] = {
		closeText: 'Done',
		prevText: 'Prev',
		nextText: 'Next',
		currentText: 'Today',
		monthNames: ['Ionawr','Chwefror','Mawrth','Ebrill','Mai','Mehefin',
		'Gorffennaf','Awst','Medi','Hydref','Tachwedd','Rhagfyr'],
		monthNamesShort: ['Ion', 'Chw', 'Maw', 'Ebr', 'Mai', 'Meh',
		'Gor', 'Aws', 'Med', 'Hyd', 'Tac', 'Rha'],
		dayNames: ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher', 'Dydd Iau', 'Dydd Gwener', 'Dydd Sadwrn'],
		dayNamesShort: ['Sul', 'Llu', 'Maw', 'Mer', 'Iau', 'Gwe', 'Sad'],
		dayNamesMin: ['Su','Ll','Ma','Me','Ia','Gw','Sa'],
		weekHeader: 'Wy',
		dateFormat: 'dd/mm/yy',
		firstDay: 1,
		isRTL: false,
		showMonthAfterYear: false,
		yearSuffix: ''};
	$.datepicker.setDefaults($.datepicker.regional['cy-GB']);
});