html5 boiler plate
[scannr.git] / js / flotr2 / examples / lib / codemirror / lib / util / formatting.js
blob:a/js/flotr2/examples/lib/codemirror/lib/util/formatting.js -> blob:b/js/flotr2/examples/lib/codemirror/lib/util/formatting.js
  // ============== Formatting extensions ============================
  // A common storage for all mode-specific formatting features
  if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
   
  // Returns the extension of the editor's current mode
  CodeMirror.defineExtension("getModeExt", function () {
  return CodeMirror.modeExtensions[this.getOption("mode")];
  });
   
  // If the current mode is 'htmlmixed', returns the extension of a mode located at
  // the specified position (can be htmlmixed, css or javascript). Otherwise, simply
  // returns the extension of the editor's current mode.
  CodeMirror.defineExtension("getModeExtAtPos", function (pos) {
  var token = this.getTokenAt(pos);
  if (token && token.state && token.state.mode)
  return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode];
  else
  return this.getModeExt();
  });
   
  // Comment/uncomment the specified range
  CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
  var curMode = this.getModeExtAtPos(this.getCursor());
  if (isComment) { // Comment range
  var commentedText = this.getRange(from, to);
  this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd
  , from, to);
  if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside
  this.setCursor(from.line, from.ch + curMode.commentStart.length);
  }
  }
  else { // Uncomment range
  var selText = this.getRange(from, to);
  var startIndex = selText.indexOf(curMode.commentStart);
  var endIndex = selText.lastIndexOf(curMode.commentEnd);
  if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
  // Take string till comment start
  selText = selText.substr(0, startIndex)
  // From comment start till comment end
  + selText.substring(startIndex + curMode.commentStart.length, endIndex)
  // From comment end till string end
  + selText.substr(endIndex + curMode.commentEnd.length);
  }
  this.replaceRange(selText, from, to);
  }
  });
   
  // Applies automatic mode-aware indentation to the specified range
  CodeMirror.defineExtension("autoIndentRange", function (from, to) {
  var cmInstance = this;
  this.operation(function () {
  for (var i = from.line; i <= to.line; i++) {
  cmInstance.indentLine(i);
  }
  });
  });
   
  // Applies automatic formatting to the specified range
  CodeMirror.defineExtension("autoFormatRange", function (from, to) {
  var absStart = this.indexFromPos(from);
  var absEnd = this.indexFromPos(to);
  // Insert additional line breaks where necessary according to the
  // mode's syntax
  var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd);
  var cmInstance = this;
   
  // Replace and auto-indent the range
  this.operation(function () {
  cmInstance.replaceRange(res, from, to);
  var startLine = cmInstance.posFromIndex(absStart).line;
  var endLine = cmInstance.posFromIndex(absStart + res.length).line;
  for (var i = startLine; i <= endLine; i++) {
  cmInstance.indentLine(i);
  }
  });
  });
   
  // Define extensions for a few modes
   
  CodeMirror.modeExtensions["css"] = {
  commentStart: "/*",
  commentEnd: "*/",
  wordWrapChars: [";", "\\{", "\\}"],
  autoFormatLineBreaks: function (text) {
  return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2");
  }
  };
   
  CodeMirror.modeExtensions["javascript"] = {
  commentStart: "/*",
  commentEnd: "*/",
  wordWrapChars: [";", "\\{", "\\}"],
   
  getNonBreakableBlocks: function (text) {
  var nonBreakableRegexes = [
  new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"),
  new RegExp("'([\\s\\S]*?)('|$)"),
  new RegExp("\"([\\s\\S]*?)(\"|$)"),
  new RegExp("//.*([\r\n]|$)")
  ];
  var nonBreakableBlocks = new Array();
  for (var i = 0; i < nonBreakableRegexes.length; i++) {
  var curPos = 0;
  while (curPos < text.length) {
  var m = text.substr(curPos).match(nonBreakableRegexes[i]);
  if (m != null) {
  nonBreakableBlocks.push({
  start: curPos + m.index,
  end: curPos + m.index + m[0].length
  });
  curPos += m.index + Math.max(1, m[0].length);
  }
  else { // No more matches
  break;
  }
  }
  }
  nonBreakableBlocks.sort(function (a, b) {
  return a.start - b.start;
  });
   
  return nonBreakableBlocks;
  },
   
  autoFormatLineBreaks: function (text) {
  var curPos = 0;
  var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g");
  var nonBreakableBlocks = this.getNonBreakableBlocks(text);
  if (nonBreakableBlocks != null) {
  var res = "";
  for (var i = 0; i < nonBreakableBlocks.length; i++) {
  if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block
  res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2");
  curPos = nonBreakableBlocks[i].start;
  }
  if (nonBreakableBlocks[i].start <= curPos
  && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block
  res += text.substring(curPos, nonBreakableBlocks[i].end);
  curPos = nonBreakableBlocks[i].end;
  }
  }
  if (curPos < text.length - 1) {
  res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2");
  }
  return res;
  }
  else {
  return text.replace(reLinesSplitter, "$1\n$2");
  }
  }
  };
   
  CodeMirror.modeExtensions["xml"] = {
  commentStart: "<!--",
  commentEnd: "-->",
  wordWrapChars: [">"],
   
  autoFormatLineBreaks: function (text) {
  var lines = text.split("\n");
  var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)");
  var reOpenBrackets = new RegExp("<", "g");
  var reCloseBrackets = new RegExp("(>)([^\r\n])", "g");
  for (var i = 0; i < lines.length; i++) {
  var mToProcess = lines[i].match(reProcessedPortion);
  if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces
  lines[i] = mToProcess[1]
  + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2")
  + mToProcess[3];
  continue;
  }
  }
   
  return lines.join("\n");
  }
  };
   
  CodeMirror.modeExtensions["htmlmixed"] = {
  commentStart: "<!--",
  commentEnd: "-->",
  wordWrapChars: [">", ";", "\\{", "\\}"],
   
  getModeInfos: function (text, absPos) {
  var modeInfos = new Array();
  modeInfos[0] =
  {
  pos: 0,
  modeExt: CodeMirror.modeExtensions["xml"],
  modeName: "xml"
  };
   
  var modeMatchers = new Array();
  modeMatchers[0] =
  {
  regex: new RegExp("<style[^>]*>([\\s\\S]*?)(</style[^>]*>|$)", "i"),
  modeExt: CodeMirror.modeExtensions["css"],
  modeName: "css"
  };
  modeMatchers[1] =
  {
  regex: new RegExp("<script[^>]*>([\\s\\S]*?)(</script[^>]*>|$)", "i"),
  modeExt: CodeMirror.modeExtensions["javascript"],
  modeName: "javascript"
  };
   
  var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1);
  // Detect modes for the entire text
  for (var i = 0; i < modeMatchers.length; i++) {
  var curPos = 0;
  while (curPos <= lastCharPos) {
  var m = text.substr(curPos).match(modeMatchers[i].regex);
  if (m != null) {
  if (m.length > 1 && m[1].length > 0) {
  // Push block begin pos
  var blockBegin = curPos + m.index + m[0].indexOf(m[1]);
  modeInfos.push(
  {
  pos: blockBegin,
  modeExt: modeMatchers[i].modeExt,
  modeName: modeMatchers[i].modeName
  });
  // Push block end pos
  modeInfos.push(
  {
  pos: blockBegin + m[1].length,
  modeExt: modeInfos[0].modeExt,
  modeName: modeInfos[0].modeName
  });
  curPos += m.index + m[0].length;
  continue;
  }
  else {
  curPos += m.index + Math.max(m[0].length, 1);
  }
  }
  else { // No more matches
  break;
  }
  }
  }
  // Sort mode infos
  modeInfos.sort(function sortModeInfo(a, b) {
  return a.pos - b.pos;
  });
   
  return modeInfos;
  },
   
  autoFormatLineBreaks: function (text, startPos, endPos) {
  var modeInfos = this.getModeInfos(text);
  var reBlockStartsWithNewline = new RegExp("^\\s*?\n");
  var reBlockEndsWithNewline = new RegExp("\n\\s*?$");
  var res = "";
  // Use modes info to break lines correspondingly
  if (modeInfos.length > 1) { // Deal with multi-mode text
  for (var i = 1; i <= modeInfos.length; i++) {
  var selStart = modeInfos[i - 1].pos;
  var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos);
   
  if (selStart >= endPos) { // The block starts later than the needed fragment
  break;
  }
  if (selStart < startPos) {
  if (selEnd <= startPos) { // The block starts earlier than the needed fragment
  continue;
  }
  selStart = startPos;
  }
  if (selEnd > endPos) {
  selEnd = endPos;
  }
  var textPortion = text.substring(selStart, selEnd);
  if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block
  if (!reBlockStartsWithNewline.test(textPortion)
  && selStart > 0) { // The block does not start with a line break
  textPortion = "\n" + textPortion;
  }
  if (!reBlockEndsWithNewline.test(textPortion)
  && selEnd < text.length - 1) { // The block does not end with a line break
  textPortion += "\n";
  }
  }
  res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion);
  }
  }
  else { // Single-mode text
  res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos));
  }
   
  return res;
  }
  };