<commit_before>var _ = require('lodash'),
  Kramed = require('./kramed'),
  Renderer = new Kramed.Renderer();

// Parser Opts
Kramed.setOptions({
  renderer: Renderer,
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: true,
  smartLists: true,
  smartypants: false
});

;(function() {

/**
 * Markdown to Shower
 * return: string
 *
*/

function Pony(options) {

  var self = this;

  var options = options || {};

  self.debug = options.debug||false;

  self.version = require('../package.json').version;

  self.heading = (options.heading) ? options.heading : [];
  self.sections = (options.sections) ? options.sections : 0;
  self.hideSlideNumbers = (options.hideSlideNumbers) ? options.hideSlideNumbers : "";
  self.hrc = (options.hrc) ? options.hrc : [];

  self.error = (options.error) ? options.error : "

Rendering error

";

  self.header = options.header;
  self.footer = options.footer;

  self._theme = false;
  self.isPonyFile = false;

  Renderer.ponyoptions = {
    format: "",
    slidenumbers: true,
    footer: 'Pony Show',
    timing:false,
    progress:true,
    transition:false,
    shadows:false
  };

  Renderer.list = function(body, ordered) {
    var type = ordered ? 'ol' : 'ul', out="", cls="";

    out = '<' + type + '>\n' + body + '</' + type + '>\n';

    if (self.sections > 0 || !self.isPonyFile) {
      return out;
    } else {
      self.heading.push(out);
      return '';
    }
  };

  Renderer.listitem = function(text) {
    var cls="";
    if (Renderer.ponyoptions.buildLists) {
      cls = "next";
    }

    return '
' + text + '
\n';
  }

  Renderer.hr = function(cls,id) {
    var out = null;
    var timing = false;

    // set format for each section
    if (Renderer.ponyoptions.format) {
      cls = Renderer.ponyoptions.format + " " + cls;
    }

    // Set timing option
    if (Renderer.ponyoptions.timing) {
      timing = 'data-timing="'+Renderer.ponyoptions.timing+'"';
    }

    if (self.sections == 0) {

      // heading
      if (self.heading.length > 0) {
        out = self.heading.join('') + "\n";
      }

      out = out+"\n
\n
\n";
    } else {

      if (self.sections == self.hrc.length) {
        out = "\n
\n
\n";
      } else {
        out = "
\n
\n\n
\n
\n";
      }
    }

    self.sections++;

    return out||"";
  }

  Renderer.heading = function (text, level) {
    var out = false;

    // var str = text.split('[');
    // var style = null;
    //
    // switch(str[0]) {
    //   case 'absolute':
    //     str.shift();
    //     style = str.join('');
    //     break;
    //
    //   default:
    // }

    if (self.sections == 0 && self.heading.length == 0 && self.isPonyFile) {
      self.heading.push('\n');
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else if (self.sections == 0 && self.isPonyFile && (self.heading.length > 1)) {
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else {
      // default
      out = '<h' + level + ' class="fit">' + text + '</h' + level + '>\n';
    }

    return (out) ? out : "";
  };

  Renderer.paragraph = function(text) {
    var out = null;
    var arr = [];

    if (self.sections > 0 || !self.isPonyFile) {

      // add line breaks
      if (text.indexOf("\n") > -1) {
        arr = text.split("\n");
      } else {
        arr.push(text);
      }

      for (x in arr) {
        var img = text.indexOf("![");
        // detect image
        pre = (img) ? "" : "
";
        post = (img) ? "\n" : "
\n";
      }

      out = pre+arr.join('\n\n')+post;

    } else {

      // Check if options exists

        // Add to heading
        if (self.sections == 0 && self.heading.length > 0) {

          self.heading.push("
"+text+"
\n");
        }

    }
    return out||"";
  }

  // images are rendered as paragraph / text
  Renderer.image = function(href, title, text) {

    var out = null;
    var str = text.split(' ');
    var cls = "";
    var style = null;
    var pos = null;

    switch(str[0]) {
      case 'absolute':
        pos = 'absolute';
        str.shift();
        style = str.join('');
        cls = "place";
        break;
      case 'fixed':
        pos = 'fixed';
        str.shift();
        style = str.join('');
        break;

      case 'fit':
        cls = "fit";
        break;

      case 'css':
        str.shift();
        style = str.join('');
        break;

      default:
    }

    if (Renderer.ponyoptions.shadows) {
      cls = cls+" shadow";
    }

    var out = '' : '>';
    return out;

  }

  Renderer.link = function(href, title, text) {
    if (this.options.sanitize) {
      try {
        var prot = decodeURIComponent(unescape(href))
          .replace(/[^\w:]/g, '')
          .toLowerCase();
      } catch (e) {
        return '';
      }
      if (prot.indexOf('javascript:') === 0) {
        return '';
      }
    }
    var out = '' + text + '';
    return out;
  };

  Renderer.code = function(text,lang) {
    return '
'+text+'
';
  }

  Renderer.table = function(header, body) {

    return '
\n'
      + '
\n'
      + header
      + '
\n'
      + '
\n'
      + body
      + '
\n'
      + '
\n';
  }

}

Pony.prototype.setHeader = function(str){
  this.header = str;
}

Pony.prototype.setFooter = function(str){
  this.footer = str;
}

Pony.prototype.setDebug = function(bool) {
  this.debug = bool;
}

Pony.prototype.log = function(args) {
  if (this.debug) {
    console.log(args);
  }
}

Pony.prototype.render = function(dat,opts) {

  var self = this;

  var header, footer, LEXED, htmlClass;

  // Set options (if avail)
  var _globalopts = opts||{};

  // Reset
  this.heading = [];
  this.sections = 0;
  this.hideSlideNumbers = "";
  this.hrc = [];

  var tokens = Kramed.lexer(dat);

  // Check if first token is configuration
  if (tokens[0].type == "paragraph") {
    if (tokens[0].text.indexOf("\n") > -1) {

      self.isPonyFile = true;

      // rebase tokens
      var rebase = tokens.shift();

      var arr = rebase.text.split("\n");

      // Parse options
      for(var x=0;x<arr.length;x++) {

        opts = arr[x].split(":");

        var _key = opts[0].trim().toLowerCase();
        var _prop = opts[1].trim().toLowerCase();

        self.log(_key + " = " + _prop);

        switch(_key) {

          case 'theme':
            Renderer.ponyoptions.theme = _prop;
            break;

          case 'footer':
            Renderer.ponyoptions.footer = _prop;
            break;

          case 'slidenumbers':
            Renderer.ponyoptions.slidenumbers = (_prop == "false") ? false : true;
            break;

          case 'timing':
            Renderer.ponyoptions.timing = _prop;
            break;

          case 'error':
            self.error = opts[1].trim();
            break;

          case 'tableclass':
            Renderer.ponyoptions.tableclass = _prop||"";
            break;

          case 'format':
            Renderer.ponyoptions.format = _prop||"";
            break;

          case 'progress':
            Renderer.ponyoptions.progress = (_prop == "false") ? false : true;
            break;

          case 'build-lists':
            Renderer.ponyoptions.buildLists = (_prop == "false") ? false : true;
            break;

          case 'transition':
            Renderer.ponyoptions.transition = _prop;
            break;

          case 'shadows':
            Renderer.ponyoptions.shadows = (_prop == "false") ? false : true;
            break;

          default:
            //out = "
"+text+"
";
        }

      }

    }
  }

  self.log(tokens);

  // count HR
  for(var x in tokens) {
    if (tokens[x].type == "hr") {
      self.hrc.push(x);
    }
  }

  if (tokens[self.hrc[self.hrc.length-1]]) {
    tokens[self.hrc[self.hrc.length-1]].last = true;
  }

  // Check if ponyfile
  if (!self.isPonyFile) {
    for(var x=0;x<tokens.length;x++) {
      if (tokens[x].type == "paragraph") {
        if ((tokens[x].text.indexOf("\n---\n") > -1)) {
          self.isPonyFile = true;
        }
      }
    }
  }

  LEXED = Kramed.parser(tokens);

  self.log(LEXED);
  self.log('___________');

  if (LEXED.length == 0) {
    LEXED = self.error;
  }

  // Merge file config with global configs giving preference to file config
  Renderer.ponyoptions = _.assign(_globalopts, Renderer.ponyoptions);

  if (!Renderer.ponyoptions.slidenumbers) {
    self.hideSlideNumbers = '';
  }

  // Apply transition
  if (Renderer.ponyoptions.transition) {
    htmlClass = Renderer.ponyoptions.transition;

    // update defined header
    if (self.header) {
      self.header = self.header.replace(/().*?<head/mgi, '$1 class="'+htmlClass+'"');
    }
  }

  header = self.header||'<!DOCTYPE HTML>' +
  '\n' +
  '\n' +
  	'<title>Pony Show IDE</title>\n' +
  	'<meta charset="utf-8">\n' +
  	'<meta name="viewport" content="width=792, user-scalable=no">\n' +
  	'<meta http-equiv="x-ua-compatible" content="ie=edge">\n' +
  	'<link rel="stylesheet" href="css/main.css">\n' +
  	'<link rel="stylesheet" href="themes/'+Renderer.ponyoptions.theme+'/styles/screen.css">\n' +
  	self.hideSlideNumbers +
  '\n' +
  '\n\n';

  footer = self.footer||'\n' +
  '\n' +
  '';

  if (Renderer.ponyoptions.progress) {
    footer = '\n

\n' + footer;
  }

  // Check if last section.
  if (self.sections == self.hrc.length) {
    LEXED = LEXED+"\n
\n
\n";
  }

  return header+LEXED+footer;

};

if (typeof module !== 'undefined' && typeof exports === 'object') {
  module.exports = new Pony;
} else if (typeof define === 'function' && define.amd) {
  define(function() { return Pony; });
} else {
  this.render = new Pony;
}

}).call(function() {
  return this || (typeof window !== 'undefined' ? window : global);
}());<commit_msg>add fit,left,right image props<commit_after>var _ = require('lodash'),
  Kramed = require('./kramed'),
  Renderer = new Kramed.Renderer();

// Parser Opts
Kramed.setOptions({
  renderer: Renderer,
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: true,
  smartLists: true,
  smartypants: false
});

;(function() {

/**
 * Markdown to Shower
 * return: string
 *
*/

function Pony(options) {

  var self = this;

  var options = options || {};

  self.debug = options.debug||false;

  self.version = require('../package.json').version;

  self.heading = (options.heading) ? options.heading : [];
  self.sections = (options.sections) ? options.sections : 0;
  self.hideSlideNumbers = (options.hideSlideNumbers) ? options.hideSlideNumbers : "";
  self.hrc = (options.hrc) ? options.hrc : [];

  self.error = (options.error) ? options.error : "

Rendering error

";

  self.header = options.header;
  self.footer = options.footer;

  self._theme = false;
  self.isPonyFile = false;

  Renderer.ponyoptions = {
    format: "",
    slidenumbers: true,
    footer: 'Pony Show',
    timing:false,
    progress:true,
    transition:false,
    shadows:false
  };

  Renderer.list = function(body, ordered) {
    var type = ordered ? 'ol' : 'ul', out="", cls="";

    out = '<' + type + '>\n' + body + '</' + type + '>\n';

    if (self.sections > 0 || !self.isPonyFile) {
      return out;
    } else {
      self.heading.push(out);
      return '';
    }
  };

  Renderer.listitem = function(text) {
    var cls="";
    if (Renderer.ponyoptions.buildLists) {
      cls = "next";
    }

    return '
' + text + '
\n';
  }

  Renderer.hr = function(cls,id) {
    var out = null;
    var timing = false;

    // set format for each section
    if (Renderer.ponyoptions.format) {
      cls = Renderer.ponyoptions.format + " " + cls;
    }

    // Set timing option
    if (Renderer.ponyoptions.timing) {
      timing = 'data-timing="'+Renderer.ponyoptions.timing+'"';
    }

    if (self.sections == 0) {

      // heading
      if (self.heading.length > 0) {
        out = self.heading.join('') + "\n";
      }

      out = out+"\n
\n
\n";
    } else {

      if (self.sections == self.hrc.length) {
        out = "\n
\n
\n";
      } else {
        out = "
\n
\n\n
\n
\n";
      }
    }

    self.sections++;

    return out||"";
  }

  Renderer.heading = function (text, level) {
    var out = false;

    // var str = text.split('[');
    // var style = null;
    //
    // switch(str[0]) {
    //   case 'absolute':
    //     str.shift();
    //     style = str.join('');
    //     break;
    //
    //   default:
    // }

    if (self.sections == 0 && self.heading.length == 0 && self.isPonyFile) {
      self.heading.push('\n');
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else if (self.sections == 0 && self.isPonyFile && (self.heading.length > 1)) {
      self.heading.push('<h' + level + '>' + text + '</h' + level + '>\n');
    } else {
      // default
      out = '<h' + level + ' class="fit">' + text + '</h' + level + '>\n';
    }

    return (out) ? out : "";
  };

  Renderer.paragraph = function(text) {
    var out = null;
    var arr = [];

    if (self.sections > 0 || !self.isPonyFile) {

      // add line breaks
      if (text.indexOf("\n") > -1) {
        arr = text.split("\n");
      } else {
        arr.push(text);
      }

      for (x in arr) {
        var img = text.indexOf("![");
        // detect image
        pre = (img) ? "" : "
";
        post = (img) ? "\n" : "
\n";
      }

      out = pre+arr.join('\n\n')+post;

    } else {

      // Check if options exists

        // Add to heading
        if (self.sections == 0 && self.heading.length > 0) {

          self.heading.push("
"+text+"
\n");
        }

    }
    return out||"";
  }

  // images are rendered as paragraph / text
  Renderer.image = function(href, title, text) {

    var out = null;
    var str = text.split(' ');
    var cls = "";
    var style = null;
    var pos = null;

    switch(str[0]) {

      case 'fullscreen':
        cls = 'fullscreen';
        break;

      case 'fit':
        cls = "fit";
        break;

      case 'left':
        cls = "left";
        break;

      case 'right':
        cls = "right";
        break;

      case 'css':
        str.shift();
        style = str.join('');
        break;

      default:
        cls = "fit";
    }

    if (Renderer.ponyoptions.shadows) {
      cls = cls+" shadow";
    }

    var out = '' : '>';
    return out;

  }

  Renderer.link = function(href, title, text) {
    if (this.options.sanitize) {
      try {
        var prot = decodeURIComponent(unescape(href))
          .replace(/[^\w:]/g, '')
          .toLowerCase();
      } catch (e) {
        return '';
      }
      if (prot.indexOf('javascript:') === 0) {
        return '';
      }
    }
    var out = '' + text + '';
    return out;
  };

  Renderer.code = function(text,lang) {
    return '
'+text+'
';
  }

  Renderer.table = function(header, body) {

    return '
\n'
      + '
\n'
      + header
      + '
\n'
      + '
\n'
      + body
      + '
\n'
      + '
\n';
  }

}

Pony.prototype.setHeader = function(str){
  this.header = str;
}

Pony.prototype.setFooter = function(str){
  this.footer = str;
}

Pony.prototype.setDebug = function(bool) {
  this.debug = bool;
}

Pony.prototype.log = function(args) {
  if (this.debug) {
    console.log(args);
  }
}

Pony.prototype.render = function(dat,opts) {

  var self = this;

  var header, footer, LEXED, htmlClass;

  // Set options (if avail)
  var _globalopts = opts||{};

  // Reset
  this.heading = [];
  this.sections = 0;
  this.hideSlideNumbers = "";
  this.hrc = [];

  var tokens = Kramed.lexer(dat);

  // Check if first token is configuration
  if (tokens[0].type == "paragraph") {
    if (tokens[0].text.indexOf("\n") > -1) {

      self.isPonyFile = true;

      // rebase tokens
      var rebase = tokens.shift();

      var arr = rebase.text.split("\n");

      // Parse options
      for(var x=0;x<arr.length;x++) {

        opts = arr[x].split(":");

        var _key = opts[0].trim().toLowerCase();
        var _prop = opts[1].trim().toLowerCase();

        self.log(_key + " = " + _prop);

        switch(_key) {

          case 'theme':
            Renderer.ponyoptions.theme = _prop;
            break;

          case 'footer':
            Renderer.ponyoptions.footer = _prop;
            break;

          case 'slidenumbers':
            Renderer.ponyoptions.slidenumbers = (_prop == "false") ? false : true;
            break;

          case 'timing':
            Renderer.ponyoptions.timing = _prop;
            break;

          case 'error':
            self.error = opts[1].trim();
            break;

          case 'tableclass':
            Renderer.ponyoptions.tableclass = _prop||"";
            break;

          case 'format':
            Renderer.ponyoptions.format = _prop||"";
            break;

          case 'progress':
            Renderer.ponyoptions.progress = (_prop == "false") ? false : true;
            break;

          case 'build-lists':
            Renderer.ponyoptions.buildLists = (_prop == "false") ? false : true;
            break;

          case 'transition':
            Renderer.ponyoptions.transition = _prop;
            break;

          case 'shadows':
            Renderer.ponyoptions.shadows = (_prop == "false") ? false : true;
            break;

          default:
            //out = "
"+text+"
";
        }

      }

    }
  }

  self.log(tokens);

  // count HR
  for(var x in tokens) {
    if (tokens[x].type == "hr") {
      self.hrc.push(x);
    }
  }

  if (tokens[self.hrc[self.hrc.length-1]]) {
    tokens[self.hrc[self.hrc.length-1]].last = true;
  }

  // Check if ponyfile
  if (!self.isPonyFile) {
    for(var x=0;x<tokens.length;x++) {
      if (tokens[x].type == "paragraph") {
        if ((tokens[x].text.indexOf("\n---\n") > -1)) {
          self.isPonyFile = true;
        }
      }
    }
  }

  LEXED = Kramed.parser(tokens);

  self.log(LEXED);
  self.log('___________');

  if (LEXED.length == 0) {
    LEXED = self.error;
  }

  // Merge file config with global configs giving preference to file config
  Renderer.ponyoptions = _.assign(_globalopts, Renderer.ponyoptions);

  if (!Renderer.ponyoptions.slidenumbers) {
    self.hideSlideNumbers = '';
  }

  // Apply transition
  if (Renderer.ponyoptions.transition) {
    htmlClass = Renderer.ponyoptions.transition;

    // update defined header
    if (self.header) {
      self.header = self.header.replace(/().*?<head/mgi, '$1 class="'+htmlClass+'"');
    }
  }

  header = self.header||'<!DOCTYPE HTML>' +
  '\n' +
  '\n' +
  	'<title>Pony Show IDE</title>\n' +
  	'<meta charset="utf-8">\n' +
  	'<meta name="viewport" content="width=792, user-scalable=no">\n' +
  	'<meta http-equiv="x-ua-compatible" content="ie=edge">\n' +
  	'<link rel="stylesheet" href="css/main.css">\n' +
  	'<link rel="stylesheet" href="themes/'+Renderer.ponyoptions.theme+'/styles/screen.css">\n' +
  	self.hideSlideNumbers +
  '\n' +
  '\n\n';

  footer = self.footer||'\n' +
  '\n' +
  '';

  if (Renderer.ponyoptions.progress) {
    footer = '\n

\n' + footer;
  }

  // Check if last section.
  if (self.sections == self.hrc.length) {
    LEXED = LEXED+"\n
\n
\n";
  }

  return header+LEXED+footer;

};

if (typeof module !== 'undefined' && typeof exports === 'object') {
  module.exports = new Pony;
} else if (typeof define === 'function' && define.amd) {
  define(function() { return Pony; });
} else {
  this.render = new Pony;
}

}).call(function() {
  return this || (typeof window !== 'undefined' ? window : global);
}());<|endoftext|>


<commit_before>"use babel"

const settings = require("./settings")

let CSON, path, selectList, OperationAbortedError, __plus
const CLASS_REGISTRY = {}

function _plus() {
  return __plus || (__plus = require("underscore-plus"))
}

let VMP_LOADING_FILE
function loadVmpOperationFile(filename) {
  // Call to loadVmpOperationFile can be nested.
  // 1. require("./operator-transform-string")
  // 2. in operator-transform-string.coffee call Base.getClass("Operator") cause operator.coffee required.
  // So we have to save original VMP_LOADING_FILE and restore it after require finished.
  const preserved = VMP_LOADING_FILE
  VMP_LOADING_FILE = filename
  require(filename)
  VMP_LOADING_FILE = preserved
}

class Base {
  static commandTable = null
  static commandPrefix = "vim-mode-plus"
  static commandScope = "atom-text-editor"
  static operationKind = null
  static getEditorState = null // set through init()

  requireTarget = false
  requireInput = false
  recordable = false
  repeated = false
  target = null
  operator = null
  count = null
  defaultCount = 1
  input = null

  get name() {
    return this.constructor.name
  }

  constructor(vimState) {
    this.vimState = vimState
  }

  // NOTE: initialize() must return `this`
  initialize() {
    return this
  }

  // Called both on cancel and success
  resetState() {}

  // Operation processor execute only when isComplete() return true.
  // If false, operation processor postpone its execution.
  isComplete() {
    if (this.requireInput && this.input == null) {
      return false
    } else if (this.requireTarget) {
      // When this function is called in Base::constructor
      // tagert is still string like `MoveToRight`, in this case isComplete
      // is not available.
      return !!this.target && this.target.isComplete()
    } else {
      return true // Set in operator's target( Motion or TextObject )
    }
  }

  isAsTargetExceptSelectInVisualMode() {
    return this.operator && !this.operator.instanceof("SelectInVisualMode")
  }

  abort() {
    if (!OperationAbortedError) OperationAbortedError = require("./errors")
    throw new OperationAbortedError("aborted")
  }

  getCount(offset = 0) {
    if (this.count == null) {
      this.count = this.vimState.hasCount() ? this.vimState.getCount() : this.defaultCount
    }
    return this.count + offset
  }

  resetCount() {
    this.count = null
  }

  countTimes(last, fn) {
    if (last < 1) return

    let stopped = false
    const stop = () => (stopped = true)
    for (let count = 1; count <= last; count++) {
      fn({count, isFinal: count === last, stop})
      if (stopped) break
    }
  }

  activateMode(mode, submode) {
    this.onDidFinishOperation(() => this.vimState.activate(mode, submode))
  }

  activateModeIfNecessary(mode, submode) {
    if (!this.vimState.isMode(mode, submode)) {
      this.activateMode(mode, submode)
    }
  }

  getInstance(name, properties) {
    return this.constructor.getInstance(this.vimState, name, properties)
  }

  cancelOperation() {
    this.vimState.operationStack.cancel(this)
  }

  processOperation() {
    this.vimState.operationStack.process()
  }

  focusSelectList(options = {}) {
    this.onDidCancelSelectList(() => this.cancelOperation())
    if (!selectList) {
      selectList = new (require("./select-list"))()
    }
    selectList.show(this.vimState, options)
  }

  focusInput(options = {}) {
    if (!options.onConfirm) {
      options.onConfirm = input => {
        this.input = input
        this.processOperation()
      }
    }
    if (!options.onCancel) {
      options.onCancel = () => this.cancelOperation()
    }
    if (!options.onChange) {
      options.onChange = input => this.vimState.hover.set(input)
    }
    this.vimState.focusInput(options)
  }

  readChar() {
    this.vimState.readChar({
      onConfirm: input => {
        this.input = input
        this.processOperation()
      },
      onCancel: () => this.cancelOperation(),
    })
  }

  getVimEofBufferPosition() {
    return this.utils.getVimEofBufferPosition(this.editor)
  }

  getVimLastBufferRow() {
    return this.utils.getVimLastBufferRow(this.editor)
  }

  getVimLastScreenRow() {
    return this.utils.getVimLastScreenRow(this.editor)
  }

  getWordBufferRangeAndKindAtBufferPosition(point, options) {
    return this.utils.getWordBufferRangeAndKindAtBufferPosition(this.editor, point, options)
  }

  getFirstCharacterPositionForBufferRow(row) {
    return this.utils.getFirstCharacterPositionForBufferRow(this.editor, row)
  }

  getBufferRangeForRowRange(rowRange) {
    return this.utils.getBufferRangeForRowRange(this.editor, rowRange)
  }

  scanForward(...args) {
    return this.utils.scanEditorInDirection(this.editor, "forward", ...args)
  }

  scanBackward(...args) {
    return this.utils.scanEditorInDirection(this.editor, "backward", ...args)
  }

  getFoldStartRowForRow(...args) {
    return this.utils.getFoldStartRowForRow(this.editor, ...args)
  }

  getFoldEndRowForRow(...args) {
    return this.utils.getFoldEndRowForRow(this.editor, ...args)
  }

  instanceof(klassName) {
    return this instanceof Base.getClass(klassName)
  }

  is(klassName) {
    return this.constructor === Base.getClass(klassName)
  }

  isOperator() {
    // Don't use `instanceof` to postpone require for faster activation.
    return this.constructor.operationKind === "operator"
  }

  isMotion() {
    // Don't use `instanceof` to postpone require for faster activation.
    return this.constructor.operationKind === "motion"
  }

  isTextObject() {
    // Don't use `instanceof` to postpone require for faster activation.
    return this.constructor.operationKind === "text-object"
  }

  getCursorBufferPosition() {
    return this.mode === "visual"
      ? this.getCursorPositionForSelection(this.editor.getLastSelection())
      : this.editor.getCursorBufferPosition()
  }

  getCursorBufferPositions() {
    return this.mode === "visual"
      ? this.editor.getSelections().map(this.getCursorPositionForSelection.bind(this))
      : this.editor.getCursorBufferPositions()
  }

  getBufferPositionForCursor(cursor) {
    return this.mode === "visual" ? this.getCursorPositionForSelection(cursor.selection) : cursor.getBufferPosition()
  }

  getCursorPositionForSelection(selection) {
    return this.swrap(selection).getBufferPositionFor("head", {from: ["property", "selection"]})
  }

  toString() {
    if (this.target) {
      return `${this.name}, target=${this.target.name}, target.wise=${this.target.wise} `
    } else if (this.operator) {
      return `${this.name}, wise=${this.wise} , operator=${this.operator.name}`
    } else {
      return this.name
    }
  }

  getCommandName() {
    return this.constructor.getCommandName()
  }

  getCommandNameWithoutPrefix() {
    return this.constructor.getCommandNameWithoutPrefix()
  }

  static writeCommandTableOnDisk() {
    const commandTable = this.generateCommandTableByEagerLoad()
    const _ = _plus()
    if (_.isEqual(this.commandTable, commandTable)) {
      atom.notifications.addInfo("No changes in commandTable", {dismissable: true})
      return
    }

    if (!CSON) CSON = require("season")
    if (!path) path = require("path")

    let loadableCSONText = "# This file is auto generated by `vim-mode-plus:write-command-table-on-disk` command.\n"
    loadableCSONText += "# DONT edit manually.\n"
    loadableCSONText += CSON.stringify(commandTable) + "\n"

    const commandTablePath = path.join(__dirname, "command-table.coffee")
    atom.workspace.open(commandTablePath).then(editor => {
      editor.setText(loadableCSONText)
      editor.save()
      atom.notifications.addInfo("Updated commandTable", {dismissable: true})
    })
  }

  static generateCommandTableByEagerLoad() {
    // NOTE: changing order affects output of lib/command-table.coffee
    const filesToLoad = [
      "./operator",
      "./operator-insert",
      "./operator-transform-string",
      "./motion",
      "./motion-search",
      "./text-object",
      "./misc-command",
    ]
    filesToLoad.forEach(loadVmpOperationFile)
    const _ = _plus()
    const klassesGroupedByFile = _.groupBy(_.values(CLASS_REGISTRY), klass => klass.file)

    const commandTable = {}
    for (const file of filesToLoad) {
      for (const klass of klassesGroupedByFile[file]) {
        commandTable[klass.name] = klass.isCommand()
          ? {file: klass.file, commandName: klass.getCommandName(), commandScope: klass.getCommandScope()}
          : {file: klass.file}
      }
    }
    return commandTable
  }

  static init(getEditorState) {
    this.getEditorState = getEditorState

    this.commandTable = require("./command-table")
    const subscriptions = []
    for (const name in this.commandTable) {
      const spec = this.commandTable[name]
      if (spec.commandName) {
        subscriptions.push(this.registerCommandFromSpec(name, spec))
      }
    }
    return subscriptions
  }

  static register(command = true) {
    this.command = command
    this.file = VMP_LOADING_FILE
    if (this.name in CLASS_REGISTRY) {
      console.warn(`Duplicate constructor ${this.name}`)
    }
    CLASS_REGISTRY[this.name] = this
  }

  static extend(...args) {
    console.error("calling deprecated Base.extend(), use Base.register instead!")
    this.register(...args)
  }

  static getClass(name) {
    if (name in CLASS_REGISTRY) return CLASS_REGISTRY[name]

    const fileToLoad = this.commandTable[name].file
    if (atom.inDevMode() && settings.get("debug")) {
      console.log(`lazy-require: ${fileToLoad} for ${name}`)
    }
    loadVmpOperationFile(fileToLoad)
    if (name in CLASS_REGISTRY) return CLASS_REGISTRY[name]

    throw new Error(`class '${name}' not found`)
  }

  static getInstance(vimState, klassOrName, properties) {
    const klass = typeof klassOrName === "function" ? klassOrName : Base.getClass(klassOrName)
    const instance = new klass(vimState)
    if (properties) Object.assign(instance, properties)
    return instance.initialize() // initialize must return instance.
  }

  static getClassRegistry() {
    return CLASS_REGISTRY
  }

  static isCommand() {
    return this.command
  }

  static getCommandName() {
    return this.commandPrefix + ":" + _plus().dasherize(this.name)
  }

  static getCommandNameWithoutPrefix() {
    return _plus().dasherize(this.name)
  }

  static getCommandScope() {
    return this.commandScope
  }

  static registerCommand() {
    return this.registerCommandFromSpec(this.name, {
      commandScope: this.getCommandScope(),
      commandName: this.getCommandName(),
      getClass: () => this,
    })
  }

  static registerCommandFromSpec(name, spec) {
    let {commandScope = "atom-text-editor", commandPrefix = "vim-mode-plus", commandName, getClass} = spec
    if (!commandName) commandName = commandPrefix + ":" + _plus().dasherize(name)
    if (!getClass) getClass = name => this.getClass(name)

    const getEditorState = this.getEditorState
    return atom.commands.add(commandScope, commandName, function(event) {
      const vimState = getEditorState(this.getModel()) || getEditorState(atom.workspace.getActiveTextEditor())
      if (vimState) vimState.operationStack.run(getClass(name)) // vimState possibly be undefined See #85
      event.stopPropagation()
    })
  }

  static getKindForCommandName(command) {
    const commandWithoutPrefix = command.replace(/^vim-mode-plus:/, "")
    const {capitalize, camelize} = _plus()
    const commandClassName = capitalize(camelize(commandWithoutPrefix))
    if (commandClassName in CLASS_REGISTRY) {
      return CLASS_REGISTRY[commandClassName].operationKind
    }
  }

  // Proxy propperties and methods
  //===========================================================================
  get mode() { return this.vimState.mode } // prettier-ignore
  get submode() { return this.vimState.submode } // prettier-ignore
  get swrap() { return this.vimState.swrap } // prettier-ignore
  get utils() { return this.vimState.utils } // prettier-ignore
  get editor() { return this.vimState.editor } // prettier-ignore
  get editorElement() { return this.vimState.editorElement } // prettier-ignore
  get globalState() { return this.vimState.globalState } // prettier-ignore
  get mutationManager() { return this.vimState.mutationManager } // prettier-ignore
  get occurrenceManager() { return this.vimState.occurrenceManager } // prettier-ignore
  get persistentSelection() { return this.vimState.persistentSelection } // prettier-ignore

  onDidChangeSearch(...args) { return this.vimState.onDidChangeSearch(...args) } // prettier-ignore
  onDidConfirmSearch(...args) { return this.vimState.onDidConfirmSearch(...args) } // prettier-ignore
  onDidCancelSearch(...args) { return this.vimState.onDidCancelSearch(...args) } // prettier-ignore
  onDidCommandSearch(...args) { return this.vimState.onDidCommandSearch(...args) } // prettier-ignore
  onDidSetTarget(...args) { return this.vimState.onDidSetTarget(...args) } // prettier-ignore
  emitDidSetTarget(...args) { return this.vimState.emitDidSetTarget(...args) } // prettier-ignore
  onWillSelectTarget(...args) { return this.vimState.onWillSelectTarget(...args) } // prettier-ignore
  emitWillSelectTarget(...args) { return this.vimState.emitWillSelectTarget(...args) } // prettier-ignore
  onDidSelectTarget(...args) { return this.vimState.onDidSelectTarget(...args) } // prettier-ignore
  emitDidSelectTarget(...args) { return this.vimState.emitDidSelectTarget(...args) } // prettier-ignore
  onDidFailSelectTarget(...args) { return this.vimState.onDidFailSelectTarget(...args) } // prettier-ignore
  emitDidFailSelectTarget(...args) { return this.vimState.emitDidFailSelectTarget(...args) } // prettier-ignore
  onWillFinishMutation(...args) { return this.vimState.onWillFinishMutation(...args) } // prettier-ignore
  emitWillFinishMutation(...args) { return this.vimState.emitWillFinishMutation(...args) } // prettier-ignore
  onDidFinishMutation(...args) { return this.vimState.onDidFinishMutation(...args) } // prettier-ignore
  emitDidFinishMutation(...args) { return this.vimState.emitDidFinishMutation(...args) } // prettier-ignore
  onDidFinishOperation(...args) { return this.vimState.onDidFinishOperation(...args) } // prettier-ignore
  onDidResetOperationStack(...args) { return this.vimState.onDidResetOperationStack(...args) } // prettier-ignore
  onDidSetOperatorModifier(...args) { return this.vimState.onDidSetOperatorModifier(...args) } // prettier-ignore
  onWillActivateMode(...args) { return this.vimState.onWillActivateMode(...args) } // prettier-ignore
  onDidActivateMode(...args) { return this.vimState.onDidActivateMode(...args) } // prettier-ignore
  preemptWillDeactivateMode(...args) { return this.vimState.preemptWillDeactivateMode(...args) } // prettier-ignore
  onWillDeactivateMode(...args) { return this.vimState.onWillDeactivateMode(...args) } // prettier-ignore
  onDidDeactivateMode(...args) { return this.vimState.onDidDeactivateMode(...args) } // prettier-ignore
  onDidCancelSelectList(...args) { return this.vimState.onDidCancelSelectList(...args) } // prettier-ignore
  subscribe(...args) { return this.vimState.subscribe(...args) } // prettier-ignore
  isMode(...args) { return this.vimState.isMode(...args) } // prettier-ignore
  getBlockwiseSelections(...args) { return this.vimState.getBlockwiseSelections(...args) } // prettier-ignore
  getLastBlockwiseSelection(...args) { return this.vimState.getLastBlockwiseSelection(...args) } // prettier-ignore
  addToClassList(...args) { return this.vimState.addToClassList(...args) } // prettier-ignore
  getConfig(...args) { return this.vimState.getConfig(...args) } // prettier-ignore
}
Base.register(false)

module.exports = Base
<commit_msg>simplify Base.prototype.toString<commit_after>"use babel"

const settings = require("./settings")

let CSON, path, selectList, OperationAbortedError, __plus
const CLASS_REGISTRY = {}

function _plus() {
  return __plus || (__plus = require("underscore-plus"))
}

let VMP_LOADING_FILE
function loadVmpOperationFile(filename) {
  // Call to loadVmpOperationFile can be nested.
  // 1. require("./operator-transform-string")
  // 2. in operator-transform-string.coffee call Base.getClass("Operator") cause operator.coffee required.
  // So we have to save original VMP_LOADING_FILE and restore it after require finished.
  const preserved = VMP_LOADING_FILE
  VMP_LOADING_FILE = filename
  require(filename)
  VMP_LOADING_FILE = preserved
}

class Base {
  static commandTable = null
  static commandPrefix = "vim-mode-plus"
  static commandScope = "atom-text-editor"
  static operationKind = null
  static getEditorState = null // set through init()

  requireTarget = false
  requireInput = false
  recordable = false
  repeated = false
  target = null
  operator = null
  count = null
  defaultCount = 1
  input = null

  get name() {
    return this.constructor.name
  }

  constructor(vimState) {
    this.vimState = vimState
  }

  // NOTE: initialize() must return `this`
  initialize() {
    return this
  }

  // Called both on cancel and success
  resetState() {}

  // Operation processor execute only when isComplete() return true.
  // If false, operation processor postpone its execution.
  isComplete() {
    if (this.requireInput && this.input == null) {
      return false
    } else if (this.requireTarget) {
      // When this function is called in Base::constructor
      // tagert is still string like `MoveToRight`, in this case isComplete
      // is not available.
      return !!this.target && this.target.isComplete()
    } else {
      return true // Set in operator's target( Motion or TextObject )
    }
  }

  isAsTargetExceptSelectInVisualMode() {
    return this.operator && !this.operator.instanceof("SelectInVisualMode")
  }

  abort() {
    if (!OperationAbortedError) OperationAbortedError = require("./errors")
    throw new OperationAbortedError("aborted")
  }

  getCount(offset = 0) {
    if (this.count == null) {
      this.count = this.vimState.hasCount() ? this.vimState.getCount() : this.defaultCount
    }
    return this.count + offset
  }

  resetCount() {
    this.count = null
  }

  countTimes(last, fn) {
    if (last < 1) return

    let stopped = false
    const stop = () => (stopped = true)
    for (let count = 1; count <= last; count++) {
      fn({count, isFinal: count === last, stop})
      if (stopped) break
    }
  }

  activateMode(mode, submode) {
    this.onDidFinishOperation(() => this.vimState.activate(mode, submode))
  }

  activateModeIfNecessary(mode, submode) {
    if (!this.vimState.isMode(mode, submode)) {
      this.activateMode(mode, submode)
    }
  }

  getInstance(name, properties) {
    return this.constructor.getInstance(this.vimState, name, properties)
  }

  cancelOperation() {
    this.vimState.operationStack.cancel(this)
  }

  processOperation() {
    this.vimState.operationStack.process()
  }

  focusSelectList(options = {}) {
    this.onDidCancelSelectList(() => this.cancelOperation())
    if (!selectList) {
      selectList = new (require("./select-list"))()
    }
    selectList.show(this.vimState, options)
  }

  focusInput(options = {}) {
    if (!options.onConfirm) {
      options.onConfirm = input => {
        this.input = input
        this.processOperation()
      }
    }
    if (!options.onCancel) {
      options.onCancel = () => this.cancelOperation()
    }
    if (!options.onChange) {
      options.onChange = input => this.vimState.hover.set(input)
    }
    this.vimState.focusInput(options)
  }

  readChar() {
    this.vimState.readChar({
      onConfirm: input => {
        this.input = input
        this.processOperation()
      },
      onCancel: () => this.cancelOperation(),
    })
  }

  getVimEofBufferPosition() {
    return this.utils.getVimEofBufferPosition(this.editor)
  }

  getVimLastBufferRow() {
    return this.utils.getVimLastBufferRow(this.editor)
  }

  getVimLastScreenRow() {
    return this.utils.getVimLastScreenRow(this.editor)
  }

  getWordBufferRangeAndKindAtBufferPosition(point, options) {
    return this.utils.getWordBufferRangeAndKindAtBufferPosition(this.editor, point, options)
  }

  getFirstCharacterPositionForBufferRow(row) {
    return this.utils.getFirstCharacterPositionForBufferRow(this.editor, row)
  }

  getBufferRangeForRowRange(rowRange) {
    return this.utils.getBufferRangeForRowRange(this.editor, rowRange)
  }

  scanForward(...args) {
    return this.utils.scanEditorInDirection(this.editor, "forward", ...args)
  }

  scanBackward(...args) {
    return this.utils.scanEditorInDirection(this.editor, "backward", ...args)
  }

  getFoldStartRowForRow(...args) {
    return this.utils.getFoldStartRowForRow(this.editor, ...args)
  }

  getFoldEndRowForRow(...args) {
    return this.utils.getFoldEndRowForRow(this.editor, ...args)
  }

  instanceof(klassName) {
    return this instanceof Base.getClass(klassName)
  }

  is(klassName) {
    return this.constructor === Base.getClass(klassName)
  }

  isOperator() {
    // Don't use `instanceof` to postpone require for faster activation.
    return this.constructor.operationKind === "operator"
  }

  isMotion() {
    // Don't use `instanceof` to postpone require for faster activation.
    return this.constructor.operationKind === "motion"
  }

  isTextObject() {
    // Don't use `instanceof` to postpone require for faster activation.
    return this.constructor.operationKind === "text-object"
  }

  getCursorBufferPosition() {
    return this.mode === "visual"
      ? this.getCursorPositionForSelection(this.editor.getLastSelection())
      : this.editor.getCursorBufferPosition()
  }

  getCursorBufferPositions() {
    return this.mode === "visual"
      ? this.editor.getSelections().map(this.getCursorPositionForSelection.bind(this))
      : this.editor.getCursorBufferPositions()
  }

  getBufferPositionForCursor(cursor) {
    return this.mode === "visual" ? this.getCursorPositionForSelection(cursor.selection) : cursor.getBufferPosition()
  }

  getCursorPositionForSelection(selection) {
    return this.swrap(selection).getBufferPositionFor("head", {from: ["property", "selection"]})
  }

  toString() {
    const targetStr = this.target ? `, target: ${this.target.toString()}` : ""
    return `${this.name}{wise: ${this.wise}${targetStr}}`
  }

  getCommandName() {
    return this.constructor.getCommandName()
  }

  getCommandNameWithoutPrefix() {
    return this.constructor.getCommandNameWithoutPrefix()
  }

  static writeCommandTableOnDisk() {
    const commandTable = this.generateCommandTableByEagerLoad()
    const _ = _plus()
    if (_.isEqual(this.commandTable, commandTable)) {
      atom.notifications.addInfo("No changes in commandTable", {dismissable: true})
      return
    }

    if (!CSON) CSON = require("season")
    if (!path) path = require("path")

    let loadableCSONText = "# This file is auto generated by `vim-mode-plus:write-command-table-on-disk` command.\n"
    loadableCSONText += "# DONT edit manually.\n"
    loadableCSONText += CSON.stringify(commandTable) + "\n"

    const commandTablePath = path.join(__dirname, "command-table.coffee")
    atom.workspace.open(commandTablePath).then(editor => {
      editor.setText(loadableCSONText)
      editor.save()
      atom.notifications.addInfo("Updated commandTable", {dismissable: true})
    })
  }

  static generateCommandTableByEagerLoad() {
    // NOTE: changing order affects output of lib/command-table.coffee
    const filesToLoad = [
      "./operator",
      "./operator-insert",
      "./operator-transform-string",
      "./motion",
      "./motion-search",
      "./text-object",
      "./misc-command",
    ]
    filesToLoad.forEach(loadVmpOperationFile)
    const _ = _plus()
    const klassesGroupedByFile = _.groupBy(_.values(CLASS_REGISTRY), klass => klass.file)

    const commandTable = {}
    for (const file of filesToLoad) {
      for (const klass of klassesGroupedByFile[file]) {
        commandTable[klass.name] = klass.isCommand()
          ? {file: klass.file, commandName: klass.getCommandName(), commandScope: klass.getCommandScope()}
          : {file: klass.file}
      }
    }
    return commandTable
  }

  static init(getEditorState) {
    this.getEditorState = getEditorState

    this.commandTable = require("./command-table")
    const subscriptions = []
    for (const name in this.commandTable) {
      const spec = this.commandTable[name]
      if (spec.commandName) {
        subscriptions.push(this.registerCommandFromSpec(name, spec))
      }
    }
    return subscriptions
  }

  static register(command = true) {
    this.command = command
    this.file = VMP_LOADING_FILE
    if (this.name in CLASS_REGISTRY) {
      console.warn(`Duplicate constructor ${this.name}`)
    }
    CLASS_REGISTRY[this.name] = this
  }

  static extend(...args) {
    console.error("calling deprecated Base.extend(), use Base.register instead!")
    this.register(...args)
  }

  static getClass(name) {
    if (name in CLASS_REGISTRY) return CLASS_REGISTRY[name]

    const fileToLoad = this.commandTable[name].file
    if (atom.inDevMode() && settings.get("debug")) {
      console.log(`lazy-require: ${fileToLoad} for ${name}`)
    }
    loadVmpOperationFile(fileToLoad)
    if (name in CLASS_REGISTRY) return CLASS_REGISTRY[name]

    throw new Error(`class '${name}' not found`)
  }

  static getInstance(vimState, klassOrName, properties) {
    const klass = typeof klassOrName === "function" ? klassOrName : Base.getClass(klassOrName)
    const instance = new klass(vimState)
    if (properties) Object.assign(instance, properties)
    return instance.initialize() // initialize must return instance.
  }

  static getClassRegistry() {
    return CLASS_REGISTRY
  }

  static isCommand() {
    return this.command
  }

  static getCommandName() {
    return this.commandPrefix + ":" + _plus().dasherize(this.name)
  }

  static getCommandNameWithoutPrefix() {
    return _plus().dasherize(this.name)
  }

  static getCommandScope() {
    return this.commandScope
  }

  static registerCommand() {
    return this.registerCommandFromSpec(this.name, {
      commandScope: this.getCommandScope(),
      commandName: this.getCommandName(),
      getClass: () => this,
    })
  }

  static registerCommandFromSpec(name, spec) {
    let {commandScope = "atom-text-editor", commandPrefix = "vim-mode-plus", commandName, getClass} = spec
    if (!commandName) commandName = commandPrefix + ":" + _plus().dasherize(name)
    if (!getClass) getClass = name => this.getClass(name)

    const getEditorState = this.getEditorState
    return atom.commands.add(commandScope, commandName, function(event) {
      const vimState = getEditorState(this.getModel()) || getEditorState(atom.workspace.getActiveTextEditor())
      if (vimState) vimState.operationStack.run(getClass(name)) // vimState possibly be undefined See #85
      event.stopPropagation()
    })
  }

  static getKindForCommandName(command) {
    const commandWithoutPrefix = command.replace(/^vim-mode-plus:/, "")
    const {capitalize, camelize} = _plus()
    const commandClassName = capitalize(camelize(commandWithoutPrefix))
    if (commandClassName in CLASS_REGISTRY) {
      return CLASS_REGISTRY[commandClassName].operationKind
    }
  }

  // Proxy propperties and methods
  //===========================================================================
  get mode() { return this.vimState.mode } // prettier-ignore
  get submode() { return this.vimState.submode } // prettier-ignore
  get swrap() { return this.vimState.swrap } // prettier-ignore
  get utils() { return this.vimState.utils } // prettier-ignore
  get editor() { return this.vimState.editor } // prettier-ignore
  get editorElement() { return this.vimState.editorElement } // prettier-ignore
  get globalState() { return this.vimState.globalState } // prettier-ignore
  get mutationManager() { return this.vimState.mutationManager } // prettier-ignore
  get occurrenceManager() { return this.vimState.occurrenceManager } // prettier-ignore
  get persistentSelection() { return this.vimState.persistentSelection } // prettier-ignore

  onDidChangeSearch(...args) { return this.vimState.onDidChangeSearch(...args) } // prettier-ignore
  onDidConfirmSearch(...args) { return this.vimState.onDidConfirmSearch(...args) } // prettier-ignore
  onDidCancelSearch(...args) { return this.vimState.onDidCancelSearch(...args) } // prettier-ignore
  onDidCommandSearch(...args) { return this.vimState.onDidCommandSearch(...args) } // prettier-ignore
  onDidSetTarget(...args) { return this.vimState.onDidSetTarget(...args) } // prettier-ignore
  emitDidSetTarget(...args) { return this.vimState.emitDidSetTarget(...args) } // prettier-ignore
  onWillSelectTarget(...args) { return this.vimState.onWillSelectTarget(...args) } // prettier-ignore
  emitWillSelectTarget(...args) { return this.vimState.emitWillSelectTarget(...args) } // prettier-ignore
  onDidSelectTarget(...args) { return this.vimState.onDidSelectTarget(...args) } // prettier-ignore
  emitDidSelectTarget(...args) { return this.vimState.emitDidSelectTarget(...args) } // prettier-ignore
  onDidFailSelectTarget(...args) { return this.vimState.onDidFailSelectTarget(...args) } // prettier-ignore
  emitDidFailSelectTarget(...args) { return this.vimState.emitDidFailSelectTarget(...args) } // prettier-ignore
  onWillFinishMutation(...args) { return this.vimState.onWillFinishMutation(...args) } // prettier-ignore
  emitWillFinishMutation(...args) { return this.vimState.emitWillFinishMutation(...args) } // prettier-ignore
  onDidFinishMutation(...args) { return this.vimState.onDidFinishMutation(...args) } // prettier-ignore
  emitDidFinishMutation(...args) { return this.vimState.emitDidFinishMutation(...args) } // prettier-ignore
  onDidFinishOperation(...args) { return this.vimState.onDidFinishOperation(...args) } // prettier-ignore
  onDidResetOperationStack(...args) { return this.vimState.onDidResetOperationStack(...args) } // prettier-ignore
  onDidSetOperatorModifier(...args) { return this.vimState.onDidSetOperatorModifier(...args) } // prettier-ignore
  onWillActivateMode(...args) { return this.vimState.onWillActivateMode(...args) } // prettier-ignore
  onDidActivateMode(...args) { return this.vimState.onDidActivateMode(...args) } // prettier-ignore
  preemptWillDeactivateMode(...args) { return this.vimState.preemptWillDeactivateMode(...args) } // prettier-ignore
  onWillDeactivateMode(...args) { return this.vimState.onWillDeactivateMode(...args) } // prettier-ignore
  onDidDeactivateMode(...args) { return this.vimState.onDidDeactivateMode(...args) } // prettier-ignore
  onDidCancelSelectList(...args) { return this.vimState.onDidCancelSelectList(...args) } // prettier-ignore
  subscribe(...args) { return this.vimState.subscribe(...args) } // prettier-ignore
  isMode(...args) { return this.vimState.isMode(...args) } // prettier-ignore
  getBlockwiseSelections(...args) { return this.vimState.getBlockwiseSelections(...args) } // prettier-ignore
  getLastBlockwiseSelection(...args) { return this.vimState.getLastBlockwiseSelection(...args) } // prettier-ignore
  addToClassList(...args) { return this.vimState.addToClassList(...args) } // prettier-ignore
  getConfig(...args) { return this.vimState.getConfig(...args) } // prettier-ignore
}
Base.register(false)

module.exports = Base
<|endoftext|>


<commit_before><commit_msg>Alert on pages that look like ipynb<commit_after>var pageMod = require("sdk/page-mod");

pageMod.PageMod({
  include: /.*github\.com\/.*\.ipynb/, // Match e.g. https://github.com/UCL/ucl_software_carpentry/blob/master/python/01_variables_and_types/exercises.ipynb
  contentScript: 'window.alert("Page matches ruleset");'
});
<|endoftext|>


<commit_before>/**
 * Requires
 *
 */
var prefs = require("sdk/preferences/service");
var ui = require("sdk/ui");
var self = require("sdk/self");

/**
 * Current status
 *
 */
var strictModeDisabled = true;

/**
 * Whitelisted preferences, as a POC I'm using:
 * 1) no 128 bit (except for ECDHE)
 * 2) no RC4
 * 3) no ECDSA
 *
 * This list has been updated for FF 35.0
 *
 */
var whitelist = [
  "security.ssl.require_safe_negotiation",
  "security.ssl.treat_unsafe_negotiation_as_broken",
//  "security.ssl3.ecdhe_ecdsa_aes_128_sha",
  "security.ssl3.rsa_aes_256_sha",
//  "security.ssl3.dhe_rsa_aes_128_sha",
  "security.ssl3.ecdhe_rsa_aes_256_sha",
//  "security.ssl3.rsa_aes_128_sha",
//  "security.ssl3.ecdhe_rsa_rc4_128_sha",
//  "security.ssl3.ecdhe_ecdsa_aes_256_sha",
  "security.ssl3.rsa_des_ede3_sha",
//  "security.ssl3.rsa_rc4_128_sha",
  "security.ssl3.dhe_rsa_aes_256_sha",
//  "security.ssl3.dhe_dss_aes_128_sha",
//  "security.ssl3.ecdhe_rsa_aes_128_sha",
//  "security.ssl3.rsa_rc4_128_md5",
//  "security.ssl3.ecdhe_ecdsa_rc4_128_sha",
  "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
//  "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256",
];

/**
 * These preferences have been defaulted to "false" by Mozilla.
 * Do not modify them.
 *
 * This list has been updated for FF 35.0
 *
 */
var ignorelist = [
  "security.ssl3.dhe_dss_aes_256_sha",
  "security.ssl3.dhe_rsa_camellia_128_sha",
  "security.ssl3.dhe_rsa_camellia_256_sha",
  "security.ssl3.dhe_rsa_des_ede3_sha",
  "security.ssl3.ecdhe_rsa_des_ede3_sha",
  "security.ssl3.rsa_camellia_128_sha",
  "security.ssl3.rsa_camellia_256_sha",
];

/**
 * Toggle all current security.ssl3 preferences.
 *
 */
function toggleAllPreferences() {
  // Get security.ssl3 keys
  var keys = prefs.keys("security.ssl3.");
  // Also concatenate secure renegotiation preferences
  keys = keys.concat(prefs.keys("security.ssl.require_safe_negotiation"));
  keys = keys.concat(prefs.keys("security.ssl.treat_unsafe_negotiation_as_broken"));

  // Flip current status
  strictModeDisabled = !strictModeDisabled;

  // Flip not whitelisted entries
  keys.forEach(flipSinglePreference);
}

/**
 * Toggle a single security.ssl3 preference.
 *
 * Preferences with a default value of "false" will not be modified.
 * In default mode, it will reset the preference to its default value.
 * In strict mode, it will flip whitelisted entries to true and everything else
 * to false.
 *
 * @param prefName {String|Object}
 *        the preference to flip
 */
function flipSinglePreference(prefName) {
  if(ignorelist.indexOf(prefName) === -1) {
    // Preference can be modified
    if(strictModeDisabled) {
      // Default mode
      prefs.reset(prefName);
    } else {
      // Strict mode
      prefs.set(prefName, !(whitelist.indexOf(prefName) === -1));
    }
  }
}

/**
 * Return a path to the current button icon.
 *
 */
function getIcon() {
  if(strictModeDisabled) {
    return self.data.url("default-16.png");
  } else {
    return self.data.url("strict-16.png");
  }
}

/**
 * Start in strict mode.
 *
 */
toggleAllPreferences();

/**
 * Display button.
 *
 */
var button = ui.ActionButton({
  id: "strict-ssl3-config",
  label: "Toggle strict security.ssl3 configuration",
  icon: getIcon(),
  onClick: function() {
    toggleAllPreferences();
    this.icon = getIcon();
  }
});
<commit_msg>Switch to hard white/black-listing logic<commit_after>/**
 * Requires
 *
 */
var prefs = require("sdk/preferences/service");
var ui = require("sdk/ui");
var self = require("sdk/self");

/**
 * Current status
 *
 */
var strictModeDisabled = true;

/**
 * Whitelisted preferences, as a POC I'm using:
 * 1) no 128 bit (except for ECDHE)
 * 2) no RC4
 * 3) no ECDSA
 *
 * This list has been updated for FF 35.0
 *
 */
var whitelist = [
  "security.ssl.require_safe_negotiation",
  "security.ssl.treat_unsafe_negotiation_as_broken",
//  "security.ssl3.ecdhe_ecdsa_aes_128_sha",
  "security.ssl3.rsa_aes_256_sha",
//  "security.ssl3.dhe_rsa_aes_128_sha",
  "security.ssl3.ecdhe_rsa_aes_256_sha",
//  "security.ssl3.rsa_aes_128_sha",
//  "security.ssl3.ecdhe_rsa_rc4_128_sha",
//  "security.ssl3.ecdhe_ecdsa_aes_256_sha",
  "security.ssl3.rsa_des_ede3_sha",
//  "security.ssl3.rsa_rc4_128_sha",
  "security.ssl3.dhe_rsa_aes_256_sha",
//  "security.ssl3.dhe_dss_aes_128_sha",
//  "security.ssl3.ecdhe_rsa_aes_128_sha",
//  "security.ssl3.rsa_rc4_128_md5",
//  "security.ssl3.ecdhe_ecdsa_rc4_128_sha",
  "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
//  "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256",
];

/**
 * These preferences have been defaulted to "false" by Mozilla.
 * Do not modify them. Left here for reference, mostly.
 *
 * This list has been updated for FF 35.0
 *
 */
//var ignorelist = [
//  "security.ssl3.dhe_dss_aes_256_sha",
//  "security.ssl3.dhe_rsa_camellia_128_sha",
//  "security.ssl3.dhe_rsa_camellia_256_sha",
//  "security.ssl3.dhe_rsa_des_ede3_sha",
//  "security.ssl3.ecdhe_rsa_des_ede3_sha",
//  "security.ssl3.rsa_camellia_128_sha",
//  "security.ssl3.rsa_camellia_256_sha",
//];

/**
 * Toggle all current security.ssl3 preferences.
 *
 */
function toggleAllPreferences() {
  // Get security.ssl3 keys
  var keys = prefs.keys("security.ssl3.");
  // Also concatenate secure renegotiation preferences
  keys = keys.concat(prefs.keys("security.ssl.require_safe_negotiation"));
  keys = keys.concat(prefs.keys("security.ssl.treat_unsafe_negotiation_as_broken"));

  // Flip current status
  strictModeDisabled = !strictModeDisabled;

  // Flip not whitelisted entries
  keys.forEach(flipSinglePreference);
}

/**
 * Toggle a single security.ssl3 preference.
 *
 * Preferences with a default value of "false" will not be modified.
 * In default mode, it will reset the preference to its default value.
 * In strict mode, it will flip whitelisted entries to true and everything else
 * to false.
 *
 * @param prefName {String|Object}
 *        the preference to flip
 */
function flipSinglePreference(prefName) {
  // Preference can be modified
  if(strictModeDisabled) {
    // Default mode
    prefs.reset(prefName);
  } else {
    // Strict mode
    prefs.set(prefName, !(whitelist.indexOf(prefName) === -1));
  }
}

/**
 * Return a path to the current button icon.
 *
 */
function getIcon() {
  if(strictModeDisabled) {
    return self.data.url("default-16.png");
  } else {
    return self.data.url("strict-16.png");
  }
}

/**
 * Start in strict mode.
 *
 */
toggleAllPreferences();

/**
 * Display button.
 *
 */
var button = ui.ActionButton({
  id: "strict-ssl3-config",
  label: "Toggle strict security.ssl3 configuration",
  icon: getIcon(),
  onClick: function() {
    toggleAllPreferences();
    this.icon = getIcon();
  }
});
<|endoftext|>


<commit_before>/* jslint node:true, plusplus: true */
/* jshint node: true */
"use strict";

var fs = require("fs"),
	fspath = require("path"),
	aws = require("aws-sdk"),
	glob = require("glob"),
	jsonFile = require("jsonfile"),
	console = require("better-console"),
	prettyjson = require("prettyjson");

function event(message, data, errorOrCallback, callbackOrCallbackData, callbackData) {
	var error, callback;
	if (typeof errorOrCallback === "function") {
		error = null;
		callback = errorOrCallback;
		callbackData = callbackOrCallbackData;
	}
	else {
		error = errorOrCallback;
		callback = callbackOrCallbackData;
	}
	if (error) {
		if (!data) data = {};
		data.error = error;
	}
	if (message || data) message = prettyjson.render({ message: message, data: data });
	if (message) console[error ? "error" : "log"](message);
	if (callback) callback(error, callbackData);
}

function getEC2Client(region) {
	return new aws.EC2( { region: region } );
}

function preProcess(regionName, templatePathPatterns, callback) {
	getEC2Client(regionName).describeRegions(function(error, regionsDescription) {
		if (error) event("Unable to get regions", { region: regionName }, error, callback);
		else {
			var regionNames = regionsDescription.Regions.map(function(o) { return o.RegionName; });
			if (!Array.isArray(templatePathPatterns) || templatePathPatterns.length === 0) templatePathPatterns = [ "./*.cloudformation" ];
			templatePathPatterns.forEach(function(templatePathPattern) {
				glob(templatePathPattern, function (error, templatePaths) {
					if (error) event("Unable to find templates", templatePathPatterns, error, callback);
					else {
						var templatesToDo = templatePaths.length,
							templateCallback = function(message, data, error) {
							event(message, data, error);
							if (--templatesToDo === 0) event("Templates completed", { paths: templatePaths }, callback, templatePaths);
						};
						templatePaths.forEach(function(templatePath) {
							var templateName = fspath.basename(templatePath),
								templateInfo = { template : templateName };
							jsonFile.readFile(templatePath, function (error, template) {
								if (error) templateCallback("Unable to read template", templateInfo, error);
								else {
									var originalTemplateString = JSON.stringify(template),
										tasksToDo = 2, // resources + mappings
										taskCallback = function(message, data, error) {
											event(message, data, error);
											if (error) templateCallback("Template not saved", templateInfo);
											else if (--tasksToDo === 0 ) {
												var templateString = JSON.stringify(template),
													templateChanged = (templateString !== originalTemplateString);
												if (templateChanged) jsonFile.writeFile(templatePath, template, function(error) {
													templateCallback(error ? "Unable to save template" : "Template completed", templateInfo, error);
												});
												else templateCallback("Template unchanged", templateInfo);
											}
										};
									event("Pre-processing template", { name : templateName, tasks: tasksToDo });
									var directoryPath = templatePath + ".d",
										resourcesPath = fspath.join(directoryPath, "resources"),
										filePathsPattern = resourcesPath + "/**";
									glob(filePathsPattern, function(error, filePaths) {
										if (error) taskCallback("Unable to get file paths", { pattern : filePathsPattern }, error);
										else {
											var filesToProcess = filePaths.length,
												fileCallback = function(message, data, error) {
													event(message, data, error);
													if (--filesToProcess === 0) taskCallback("Includes completed", templateInfo);
												};
											filePaths.forEach(function(filePath) {
												var includeInfo = { template: templateName, path: filePath },
													pathParts = fspath.relative(resourcesPath, filePath).split(fspath.sep);
												if (pathParts.length <= 1) fileCallback();
												else if (pathParts.some(function(pathPart) { return pathPart === "."; })) fileCallback("Ignoring include as it is a hidden file", includeInfo);
												else {
													var resourceKey = pathParts[0],
														resource = template.Resources[resourceKey],
														match = pathParts[1].match(/^configs|userdata(.ps1|.cmd|.sh|)$/);
													includeInfo.resource = resourceKey;
													if (!match) fileCallback("File must be config or userdata", includeInfo);
													else {
														var includeType, // config or userdata
															configKey,
															configType, // file or command
															userdataWrapper, // powershell, cmd or empty
															ignore;
														switch (match[0]) {
															case "configs":
																includeType = "config";
																if (pathParts.length <=3) {
																	fileCallback();
																	ignore = true;
																}
																else {
																	includeInfo.config = configKey = pathParts[2];
																	var fileOrCommandMatch = pathParts[3].match(/^files|commands$/);
																	if (!fileOrCommandMatch) {
																		fileCallback("Config file must be file or command", includeInfo);
																		ignore = true;
																	}
																	else {
																		switch (fileOrCommandMatch[0]) {
																			case "files":
																				configType = "file";
																				break;
																			case "commands":
																				configType = "command";
																				break;
																		}
																		includeInfo.configType = configType;
																	}
																}
																break;
															default:
																includeType = "userdata";
																switch (match[1]) {
																	case ".ps1":
																		userdataWrapper = "powershell";
																		break;
																	case ".cmd":
																		userdataWrapper = "script";
																		break;
																}
																includeInfo.userdataType = match[1];
														}
														includeInfo.type = includeType;
														if (!ignore) fs.stat(filePath, function(error, stat) {
															if (error) fileCallback("Unable to get file stats", includeInfo, error);
															else if (!stat.isFile()) fileCallback();
															//need to support ignoring of windows system/hidden files here
															else fs.readFile(filePath, "utf8", function (error, contentString) {
																if (error) fileCallback(error.errno === 34 ? "File not found" : "Unable to read file", includeInfo, error);
																else {
																	var includeObject,
																		includeProperty,
																		objectPath = "Resources/" + resourceKey;
																	switch (includeType) {
																		case "config":
																			var keyParts = pathParts.slice(4);
																			switch (configType) {
																				case "file":
																					for (var i = 0; i < keyParts.length; i++) {
																						var original = keyParts[i],
																							working = original;
																						//if first part is e.g. C$, change to C:
																						if (i === 0) {
																							var match = working.match(/^([A-z])\$$/);
																							if (match) working = match[1] + ":";
																						}
																						//if part starts with $$ or $., remove the first $
																						var dollarMatch = working.match(/^\$(\.\$.*)$/);
																						if (dollarMatch) working = dollarMatch[1];
																						if (working !== original) keyParts[i] = working;
																					}
																					includeProperty = "content";
																					break;
																				case "command":
																					includeProperty = "command";
																					break;
																			}
																			var key = keyParts.join("/"),
																				typeKey = configType + "s";
																			includeInfo.key = key;
																			objectPath += "/Metadata/AWS::CloudFormation::Init/" + configKey + "/" + typeKey + "/\"" + key + "\"";
																			includeObject = resource.Metadata["AWS::CloudFormation::Init"][configKey][typeKey][key];
																			break;
																		case "userdata":
																			if (userdataWrapper && contentString) contentString = "<" + userdataWrapper + ">\n" + contentString + "\n</" + userdataWrapper + ">";
																			objectPath += "/Properties";
																			includeObject = resource.Properties;
																			includeProperty = "UserData";
																			break;
																	}
																	includeInfo.objectPath = (objectPath += "/" + includeProperty);
																	if (!includeObject) fileCallback("Template object not found", includeInfo);
																	else {
																		var originalContentString = JSON.stringify(includeObject[includeProperty]),
																			contentArray = [];
																		for (var i = 0; ; true) {
																			var searchString = contentString.substring(i),
																				attRefMatch = searchString.match(/(?:\{\{((?:b64)?ref) ([\w.:]*)\}\})|(?:\{\{(att) (\w*) ([\w.]*)\}\})/),
																				stringToPush = null,
																				j;
																			if (!attRefMatch) stringToPush = searchString;
																			else {
																				j = attRefMatch.index;
																				if (j > 0) stringToPush = searchString.substring(0, j);
																			}
																			if (stringToPush) {
																				var lastIndex = contentArray.length - 1;
																				if (lastIndex > -1) {
																					var last = contentArray[lastIndex];
																					if (typeof last === "string") {
																						contentArray[lastIndex] = last + stringToPush;
																						stringToPush = null;
																					}
																				}
																				if (stringToPush) contentArray.push(stringToPush);
																			}
																			if (!attRefMatch) break;
																			var variableType = (attRefMatch[1] || attRefMatch[3]);
																			if (variableType) {
																				var variable = null;
																				switch (variableType) {
																					case "ref":
																					case "b64ref":
																						variable = { "Ref": attRefMatch[2] };
																						if (variableType === "b64ref") variable = { "Fn::Base64" : variable };
																						break;
																					case "att":
																						variable = { "Fn::GetAtt": [ attRefMatch[4], attRefMatch[5] ] };
																						break;
																				}
																				contentArray.push(variable);
																				var variableInfo = {
																					template: templateName,
																					type: variableType,
																					string: attRefMatch[0],
																					object: JSON.stringify(variable)
																				};
																				event("Variable found", variableInfo);
																			}
																			i += j + attRefMatch[0].length;
																		}
																		includeInfo.variableCount = contentArray.filter(function(o) {
																			return typeof o === "object";
																		}).length;
																		var contentObject = (contentArray.length === 1) ? contentArray[0] : { "Fn::Join": [ "", contentArray ] };
																		if (contentObject && (includeType === "userdata")) contentObject = { "Fn::Base64": contentObject };
																		contentString = JSON.stringify(contentObject);
																		var contentChanged = (contentString !== originalContentString);
																		if (contentChanged) includeObject[includeProperty] = contentObject;
																		fileCallback(contentChanged ? "Include changed" : "Include unchanged", includeInfo);
																	}
																}
															});
														});
													}
												}
											});
										}
									});
									var mappingsPath = fspath.join(directoryPath, "mappings"),
										mappingPathsPattern = mappingsPath + "/*";
									glob(mappingPathsPattern, function(error, mappingPaths) {
										if (error) taskCallback("Unable to get mappings paths", { pattern : mappingPathsPattern }, error);
										else {
											var mappingsToProcess = mappingPaths.length,
												mappingCallback = function(message, data, error) {
												event(message, data, error);
												if (--mappingsToProcess === 0) taskCallback("Mappings completed", templateInfo);
											};
											mappingPaths.forEach(function(mappingPath) {
												var mappingExtension = fspath.extname(mappingPath),
													mappingName = fspath.basename(mappingPath, mappingExtension),
													mappingInfo = {
														template: templateName,
														path: mappingPath,
														name: mappingName
													};
												jsonFile.readFile(mappingPath, function(error, mapping) {
													if (error) {
														if (error.no === 34) taskCallback("Config not found", mappingInfo);
														else taskCallback("Unable to read mapping", mappingInfo, error);
													}
													else {
														var type = mapping.type,
															templateMapping = template.Mappings[mappingName];
														mappingInfo.type = type;
														if (!templateMapping) mappingCallback("Mapping ignored", mappingInfo);
														else {
															switch (mapping.type) {
																case "ami":
																	var ami = mapping.ami,
																		owner = ami.owner,
																		name = ami.name,
																		amiMappingRegionsToDo = regionNames.length,
																		amiMappingRegionCallback = function(message, data, error) {
																			event(message, data, error);
																			if (--amiMappingRegionsToDo === 0) mappingCallback("Regions' AMIs completed", mappingInfo);
																		};
																	mappingInfo.ami = { owner: owner, name: name };
																	regionNames.forEach(function(regionName) {
																		mappingInfo.region = regionName;
																		var amiMappingRegion = templateMapping[regionName],
																			originalAmiId = amiMappingRegion ? amiMappingRegion.ID : null;
																		event("Searching for AMI", mappingInfo);
																		getEC2Client(regionName).describeImages({
																			Owners: [ owner ],
																			Filters: [ { Name: "name", Values: [ name ] } ]
																		}, function(error, amisDescription) {
																			if (error) amiMappingRegionCallback("AMI not found", mappingInfo, error);
																			else {
																				var amis = amisDescription.Images.sort(function(a, b) {
																					a = a.Name;
																					b = b.Name;
																					return (a > b) ? 1 : ((a < b) ? -1 : 0);
																				});
																				if (amis.length === 0) amiMappingRegionCallback("AMI not found", mappingInfo);
																				else {
																					var ami = amis[0],
																						amiId = ami.ImageId,
																						amiChanged = (amiId !== originalAmiId);
																					mappingInfo.ami = {
																						name: ami.Name,
																						id: amiId,
																						owner: ami.ImageOwnerAlias,
																						platform: ami.Platform,
																						architecture: ami.Architecture
																					};
																					if (amiChanged) {
																						if (originalAmiId) mappingInfo.originalAmiId = originalAmiId;
																						templateMapping[regionName] = { ID: amiId };
																					}
																					amiMappingRegionCallback(amiChanged ? "AMI changed" : "AMI unchanged", mappingInfo);
																					delete mappingInfo.amiId;
																					delete mappingInfo.previousAmiId;
																				}
																			}
																		});
																		delete mappingInfo.region;
																	});
																	break;
																default:
																	mappingCallback("Currently, only AMI mappings supported", mappingInfo);
																	break;
															}
														}
													}
												});
											});
										}
									});
								}
							});
						});
					}
				});
			});
		}
	});
}

module.exports = preProcess;
<commit_msg>Escaped dot in regexes for user data script path<commit_after>/* jslint node:true, plusplus: true */
/* jshint node: true */
"use strict";

var fs = require("fs"),
	fspath = require("path"),
	aws = require("aws-sdk"),
	glob = require("glob"),
	jsonFile = require("jsonfile"),
	console = require("better-console"),
	prettyjson = require("prettyjson");

function event(message, data, errorOrCallback, callbackOrCallbackData, callbackData) {
	var error, callback;
	if (typeof errorOrCallback === "function") {
		error = null;
		callback = errorOrCallback;
		callbackData = callbackOrCallbackData;
	}
	else {
		error = errorOrCallback;
		callback = callbackOrCallbackData;
	}
	if (error) {
		if (!data) data = {};
		data.error = error;
	}
	if (message || data) message = prettyjson.render({ message: message, data: data });
	if (message) console[error ? "error" : "log"](message);
	if (callback) callback(error, callbackData);
}

function getEC2Client(region) {
	return new aws.EC2( { region: region } );
}

function preProcess(regionName, templatePathPatterns, callback) {
	getEC2Client(regionName).describeRegions(function(error, regionsDescription) {
		if (error) event("Unable to get regions", { region: regionName }, error, callback);
		else {
			var regionNames = regionsDescription.Regions.map(function(o) { return o.RegionName; });
			if (!Array.isArray(templatePathPatterns) || templatePathPatterns.length === 0) templatePathPatterns = [ "./*.cloudformation" ];
			templatePathPatterns.forEach(function(templatePathPattern) {
				glob(templatePathPattern, function (error, templatePaths) {
					if (error) event("Unable to find templates", templatePathPatterns, error, callback);
					else {
						var templatesToDo = templatePaths.length,
							templateCallback = function(message, data, error) {
							event(message, data, error);
							if (--templatesToDo === 0) event("Templates completed", { paths: templatePaths }, callback, templatePaths);
						};
						templatePaths.forEach(function(templatePath) {
							var templateName = fspath.basename(templatePath),
								templateInfo = { template : templateName };
							jsonFile.readFile(templatePath, function (error, template) {
								if (error) templateCallback("Unable to read template", templateInfo, error);
								else {
									var originalTemplateString = JSON.stringify(template),
										tasksToDo = 2, // resources + mappings
										taskCallback = function(message, data, error) {
											event(message, data, error);
											if (error) templateCallback("Template not saved", templateInfo);
											else if (--tasksToDo === 0 ) {
												var templateString = JSON.stringify(template),
													templateChanged = (templateString !== originalTemplateString);
												if (templateChanged) jsonFile.writeFile(templatePath, template, function(error) {
													templateCallback(error ? "Unable to save template" : "Template completed", templateInfo, error);
												});
												else templateCallback("Template unchanged", templateInfo);
											}
										};
									event("Pre-processing template", { name : templateName, tasks: tasksToDo });
									var directoryPath = templatePath + ".d",
										resourcesPath = fspath.join(directoryPath, "resources"),
										filePathsPattern = resourcesPath + "/**";
									glob(filePathsPattern, function(error, filePaths) {
										if (error) taskCallback("Unable to get file paths", { pattern : filePathsPattern }, error);
										else {
											var filesToProcess = filePaths.length,
												fileCallback = function(message, data, error) {
													event(message, data, error);
													if (--filesToProcess === 0) taskCallback("Includes completed", templateInfo);
												};
											filePaths.forEach(function(filePath) {
												var includeInfo = { template: templateName, path: filePath },
													pathParts = fspath.relative(resourcesPath, filePath).split(fspath.sep);
												if (pathParts.length <= 1) fileCallback();
												else if (pathParts.some(function(pathPart) { return pathPart === "."; })) fileCallback("Ignoring include as it is a hidden file", includeInfo);
												else {
													var resourceKey = pathParts[0],
														resource = template.Resources[resourceKey],
														match = pathParts[1].match(/^configs|userdata(\.ps1|\.cmd|\.sh|)$/);
													includeInfo.resource = resourceKey;
													if (!match) fileCallback("File must be config or userdata", includeInfo);
													else {
														var includeType, // config or userdata
															configKey,
															configType, // file or command
															userdataWrapper, // powershell, cmd or empty
															ignore;
														switch (match[0]) {
															case "configs":
																includeType = "config";
																if (pathParts.length <=3) {
																	fileCallback();
																	ignore = true;
																}
																else {
																	includeInfo.config = configKey = pathParts[2];
																	var fileOrCommandMatch = pathParts[3].match(/^files|commands$/);
																	if (!fileOrCommandMatch) {
																		fileCallback("Config file must be file or command", includeInfo);
																		ignore = true;
																	}
																	else {
																		switch (fileOrCommandMatch[0]) {
																			case "files":
																				configType = "file";
																				break;
																			case "commands":
																				configType = "command";
																				break;
																		}
																		includeInfo.configType = configType;
																	}
																}
																break;
															default:
																includeType = "userdata";
																switch (match[1]) {
																	case ".ps1":
																		userdataWrapper = "powershell";
																		break;
																	case ".cmd":
																		userdataWrapper = "script";
																		break;
																}
																includeInfo.userdataType = match[1];
														}
														includeInfo.type = includeType;
														if (!ignore) fs.stat(filePath, function(error, stat) {
															if (error) fileCallback("Unable to get file stats", includeInfo, error);
															else if (!stat.isFile()) fileCallback();
															//need to support ignoring of windows system/hidden files here
															else fs.readFile(filePath, "utf8", function (error, contentString) {
																if (error) fileCallback(error.errno === 34 ? "File not found" : "Unable to read file", includeInfo, error);
																else {
																	var includeObject,
																		includeProperty,
																		objectPath = "Resources/" + resourceKey;
																	switch (includeType) {
																		case "config":
																			var keyParts = pathParts.slice(4);
																			switch (configType) {
																				case "file":
																					for (var i = 0; i < keyParts.length; i++) {
																						var original = keyParts[i],
																							working = original;
																						//if first part is e.g. C$, change to C:
																						if (i === 0) {
																							var match = working.match(/^([A-z])\$$/);
																							if (match) working = match[1] + ":";
																						}
																						//if part starts with $$ or $., remove the first $
																						var dollarMatch = working.match(/^\$(\.\$.*)$/);
																						if (dollarMatch) working = dollarMatch[1];
																						if (working !== original) keyParts[i] = working;
																					}
																					includeProperty = "content";
																					break;
																				case "command":
																					includeProperty = "command";
																					break;
																			}
																			var key = keyParts.join("/"),
																				typeKey = configType + "s";
																			includeInfo.key = key;
																			objectPath += "/Metadata/AWS::CloudFormation::Init/" + configKey + "/" + typeKey + "/\"" + key + "\"";
																			includeObject = resource.Metadata["AWS::CloudFormation::Init"][configKey][typeKey][key];
																			break;
																		case "userdata":
																			if (userdataWrapper && contentString) contentString = "<" + userdataWrapper + ">\n" + contentString + "\n</" + userdataWrapper + ">";
																			objectPath += "/Properties";
																			includeObject = resource.Properties;
																			includeProperty = "UserData";
																			break;
																	}
																	includeInfo.objectPath = (objectPath += "/" + includeProperty);
																	if (!includeObject) fileCallback("Template object not found", includeInfo);
																	else {
																		var originalContentString = JSON.stringify(includeObject[includeProperty]),
																			contentArray = [];
																		for (var i = 0; ; true) {
																			var searchString = contentString.substring(i),
																				attRefMatch = searchString.match(/(?:\{\{((?:b64)?ref) ([\w.:]*)\}\})|(?:\{\{(att) (\w*) ([\w.]*)\}\})/),
																				stringToPush = null,
																				j;
																			if (!attRefMatch) stringToPush = searchString;
																			else {
																				j = attRefMatch.index;
																				if (j > 0) stringToPush = searchString.substring(0, j);
																			}
																			if (stringToPush) {
																				var lastIndex = contentArray.length - 1;
																				if (lastIndex > -1) {
																					var last = contentArray[lastIndex];
																					if (typeof last === "string") {
																						contentArray[lastIndex] = last + stringToPush;
																						stringToPush = null;
																					}
																				}
																				if (stringToPush) contentArray.push(stringToPush);
																			}
																			if (!attRefMatch) break;
																			var variableType = (attRefMatch[1] || attRefMatch[3]);
																			if (variableType) {
																				var variable = null;
																				switch (variableType) {
																					case "ref":
																					case "b64ref":
																						variable = { "Ref": attRefMatch[2] };
																						if (variableType === "b64ref") variable = { "Fn::Base64" : variable };
																						break;
																					case "att":
																						variable = { "Fn::GetAtt": [ attRefMatch[4], attRefMatch[5] ] };
																						break;
																				}
																				contentArray.push(variable);
																				var variableInfo = {
																					template: templateName,
																					type: variableType,
																					string: attRefMatch[0],
																					object: JSON.stringify(variable)
																				};
																				event("Variable found", variableInfo);
																			}
																			i += j + attRefMatch[0].length;
																		}
																		includeInfo.variableCount = contentArray.filter(function(o) {
																			return typeof o === "object";
																		}).length;
																		var contentObject = (contentArray.length === 1) ? contentArray[0] : { "Fn::Join": [ "", contentArray ] };
																		if (contentObject && (includeType === "userdata")) contentObject = { "Fn::Base64": contentObject };
																		contentString = JSON.stringify(contentObject);
																		var contentChanged = (contentString !== originalContentString);
																		if (contentChanged) includeObject[includeProperty] = contentObject;
																		fileCallback(contentChanged ? "Include changed" : "Include unchanged", includeInfo);
																	}
																}
															});
														});
													}
												}
											});
										}
									});
									var mappingsPath = fspath.join(directoryPath, "mappings"),
										mappingPathsPattern = mappingsPath + "/*";
									glob(mappingPathsPattern, function(error, mappingPaths) {
										if (error) taskCallback("Unable to get mappings paths", { pattern : mappingPathsPattern }, error);
										else {
											var mappingsToProcess = mappingPaths.length,
												mappingCallback = function(message, data, error) {
												event(message, data, error);
												if (--mappingsToProcess === 0) taskCallback("Mappings completed", templateInfo);
											};
											mappingPaths.forEach(function(mappingPath) {
												var mappingExtension = fspath.extname(mappingPath),
													mappingName = fspath.basename(mappingPath, mappingExtension),
													mappingInfo = {
														template: templateName,
														path: mappingPath,
														name: mappingName
													};
												jsonFile.readFile(mappingPath, function(error, mapping) {
													if (error) {
														if (error.no === 34) taskCallback("Config not found", mappingInfo);
														else taskCallback("Unable to read mapping", mappingInfo, error);
													}
													else {
														var type = mapping.type,
															templateMapping = template.Mappings[mappingName];
														mappingInfo.type = type;
														if (!templateMapping) mappingCallback("Mapping ignored", mappingInfo);
														else {
															switch (mapping.type) {
																case "ami":
																	var ami = mapping.ami,
																		owner = ami.owner,
																		name = ami.name,
																		amiMappingRegionsToDo = regionNames.length,
																		amiMappingRegionCallback = function(message, data, error) {
																			event(message, data, error);
																			if (--amiMappingRegionsToDo === 0) mappingCallback("Regions' AMIs completed", mappingInfo);
																		};
																	mappingInfo.ami = { owner: owner, name: name };
																	regionNames.forEach(function(regionName) {
																		mappingInfo.region = regionName;
																		var amiMappingRegion = templateMapping[regionName],
																			originalAmiId = amiMappingRegion ? amiMappingRegion.ID : null;
																		event("Searching for AMI", mappingInfo);
																		getEC2Client(regionName).describeImages({
																			Owners: [ owner ],
																			Filters: [ { Name: "name", Values: [ name ] } ]
																		}, function(error, amisDescription) {
																			if (error) amiMappingRegionCallback("AMI not found", mappingInfo, error);
																			else {
																				var amis = amisDescription.Images.sort(function(a, b) {
																					a = a.Name;
																					b = b.Name;
																					return (a > b) ? 1 : ((a < b) ? -1 : 0);
																				});
																				if (amis.length === 0) amiMappingRegionCallback("AMI not found", mappingInfo);
																				else {
																					var ami = amis[0],
																						amiId = ami.ImageId,
																						amiChanged = (amiId !== originalAmiId);
																					mappingInfo.ami = {
																						name: ami.Name,
																						id: amiId,
																						owner: ami.ImageOwnerAlias,
																						platform: ami.Platform,
																						architecture: ami.Architecture
																					};
																					if (amiChanged) {
																						if (originalAmiId) mappingInfo.originalAmiId = originalAmiId;
																						templateMapping[regionName] = { ID: amiId };
																					}
																					amiMappingRegionCallback(amiChanged ? "AMI changed" : "AMI unchanged", mappingInfo);
																					delete mappingInfo.amiId;
																					delete mappingInfo.previousAmiId;
																				}
																			}
																		});
																		delete mappingInfo.region;
																	});
																	break;
																default:
																	mappingCallback("Currently, only AMI mappings supported", mappingInfo);
																	break;
															}
														}
													}
												});
											});
										}
									});
								}
							});
						});
					}
				});
			});
		}
	});
}

module.exports = preProcess;
<|endoftext|>