All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fitnesse.resources.javascript.WikiFormatter.js Maven / Gradle / Ivy

function WikiFormatter()
{
  /*
   * This is the entry point, it takes a chunk of text, splits it into lines, loops
   * through the lines collecting consecutive lines that are part of a table, and returns
   * a chunk of text with those tables it collected formatted.
   */
  this.format = function(wikiText) {
    this.wikificationPrevention = false;

    var formatted = "";
    var currentTable = [];
    var lines = wikiText.split("\n");
    var line = null;

    for(var i = 0, j = lines.length; i < j; i++) {
      line = lines[i];

      if(this.isTableRow(line)) {
        currentTable.push(line);
      }
      else {
        formatted += this.formatTable(currentTable);
        currentTable = [];
        formatted += line + "\n";
      }
    }

    formatted += this.formatTable(currentTable);
    return formatted.slice(0, formatted.length - 1);
  };

  /*
   * This function receives an array of strings(rows), it splits each of those strings
   * into an array of strings(columns), calls off to calculate what the widths
   * of each of those columns should be and then returns a string with each column
   * right/space padded based on the calculated widths.
   */
  this.formatTable = function(table) {
    var formatted = "";
    var splitRowsResult = this.splitRows(table);
    var rows = splitRowsResult.rows;
    var suffixes = splitRowsResult.suffixes;
    var widths = this.calculateColumnWidths(rows);
    var row = null;

    for(var rowIndex = 0, numberOfRows = rows.length; rowIndex < numberOfRows; rowIndex++) {
      row = rows[rowIndex];
      formatted += "|";

      for(var columnIndex = 0, numberOfColumns = row.length; columnIndex < numberOfColumns; columnIndex++) {
        var cellValue = row[columnIndex];
        if (cellValue === '!(') {
            formatted += cellValue + "|";
        } else {
            formatted += this.rightPad(cellValue, widths[rowIndex][columnIndex]) + "|";
        }
      }

      formatted += suffixes[rowIndex] + "\n";
    }

    if(this.wikificationPrevention) {
      formatted = '!|' + formatted.substr(2);
      this.wikificationPrevention = false;
    }

    return formatted;
  };

  /*
   * This is where the nastiness starts due to trying to emulate
   * the html rendering of colspans.
   *   - make a row/column matrix that contains data lengths
   *   - find the max widths of those columns that don't have colspans
   *   - update the matrix to set each non colspan column to those max widths
   *   - find the max widths of the colspan columns
   *   - increase the non colspan columns if the colspan columns lengths are greater
   *   - adjust colspan columns to pad out to the max length of the row
   *
   * Feel free to refactor as necessary for clarity
   */
  this.calculateColumnWidths = function(rows) {
    var widths = this.getRealColumnWidths(rows);
    var totalNumberOfColumns = this.getNumberOfColumns(rows);

    var maxWidths = this.getMaxWidths(widths, totalNumberOfColumns);
    this.setMaxWidthsOnNonColspanColumns(widths, maxWidths);

    var colspanWidths = this.getColspanWidth(widths, totalNumberOfColumns);
    this.adjustWidthsForColspans(widths, maxWidths, colspanWidths);

    this.adjustColspansForWidths(widths, maxWidths);

    return widths;
  };

  this.isTableRow = function(line) {
    return line.match(/^!?\|/);
  };

  this.splitRows = function(rows) {
    var splitRows = [];
    var rowSuffixes = [];

    this.each(rows, function(row) {
      var columns = this.splitRow(row);
      rowSuffixes.push(columns[columns.length - 1]);
      splitRows.push(columns.slice(0, columns.length - 1));
    }, this);

    return {rows: splitRows, suffixes: rowSuffixes};
  };

  this.splitRow = function(row) {
    var replacement = '__TEMP_PIPE_CHARACTER__';
    if (row.match(/!-/)){
      row = this.replacePipesInLiteralsWithPlaceholder(row,replacement );
    }
    var columns = this.trim(row).split('|');

    if(!this.wikificationPrevention && columns[0] == '!') {
      this.wikificationPrevention = true;
      columns[1] = '!' + columns[1]; //leave a placeholder
    }

    columns = columns.slice(1, columns.length);

    this.each(columns, function(column, i) {
      columns[i] = this.trim(column).replace(/__TEMP_PIPE_CHARACTER__/g, '|');
    }, this);

    return columns;
  };

  this.replacePipesInLiteralsWithPlaceholder = function(text, rep){
     var newText = "";

     while(text.match(/!-/)){
         var textParts = this.splitLiteral(text);
         newText = newText  + textParts.left + textParts.literal.replace(/\|/g, rep);
         text = textParts.right;
     }

     return newText + text;
  };


  this.splitLiteral = function(text){
    var leftText = "";
    var rightText = "";
    var literalText = "";

    var matchOpenLiteral = text.match(/(.*?)(!-.*)/);
    leftText = matchOpenLiteral[1];
    if (matchOpenLiteral[2].match(/-!/)){
      var matchCloseLiteral = matchOpenLiteral[2].match(/(.*?-!)(.*)/);
      literalText = matchCloseLiteral[1];
      rightText = matchCloseLiteral[2];
    }
    else {
      literalText = matchOpenLiteral[2];
      rightText = "";
    }

    return {left:leftText, literal:literalText, right:rightText};

  };


  this.getRealColumnWidths = function(rows) {
    var widths = [];

    this.each(rows, function(row, rowIndex) {
      widths.push([]);

      this.each(row, function(column, columnIndex) {
        widths[rowIndex][columnIndex] = column.length;
      }, this);
    }, this);

    return widths;
  };

  this.getMaxWidths = function(widths, totalNumberOfColumns) {
    var maxWidths = [];
    var row = null;

    this.each(widths, function(row, rowIndex) {
      this.each(row, function(columnWidth, columnIndex) {
        if(columnIndex == (row.length - 1) && row.length < totalNumberOfColumns) {
          return false;
        }

        if(columnIndex >= maxWidths.length) {
          maxWidths.push(columnWidth);
        }
        else if(columnWidth > maxWidths[columnIndex]) {
          maxWidths[columnIndex] = columnWidth;
        }
      }, this);
    }, this);

    return maxWidths;
  };

  this.getNumberOfColumns = function(rows) {
    var numberOfColumns = 0;

    this.each(rows, function(row) {
      if(row.length > numberOfColumns) {
        numberOfColumns = row.length;
      }
    });

    return numberOfColumns;
  };

  this.getColspanWidth = function(widths, totalNumberOfColumns) {
    var colspanWidths = [];
    var colspan = null;
    var colspanWidth = null;

    this.each(widths, function(row, rowIndex) {
      if(row.length < totalNumberOfColumns) {
        colspan = totalNumberOfColumns - row.length;
        colspanWidth = row[row.length - 1];

        if(colspan >= colspanWidths.length) {
          colspanWidths[colspan] = colspanWidth;
        }
        else if(!colspanWidths[colspan] || colspanWidth > colspanWidths[colspan]) {
          colspanWidths[colspan] = colspanWidth;
        }
      }
    });

    return colspanWidths;
  };

  this.setMaxWidthsOnNonColspanColumns = function(widths, maxWidths) {
    this.each(widths, function(row, rowIndex) {
      this.each(row, function(columnWidth, columnIndex) {
        if(columnIndex == (row.length - 1) && row.length < maxWidths.length) {
          return false;
        }

        row[columnIndex] = maxWidths[columnIndex];
      }, this);
    }, this);
  };

  this.getWidthOfLastNumberOfColumns = function(maxWidths, numberOfColumns) {
    var width = 0;

    for(var i = 1; i <= numberOfColumns; i++) {
      width += maxWidths[maxWidths.length - i];
    }

    return width + numberOfColumns - 1; //add in length of separators
  };

  this.spreadOutExcessOverLastNumberOfColumns = function(maxWidths, excess, numberOfColumns){
    var columnToApplyExcessTo = maxWidths.length - numberOfColumns;

    for(var i = 0; i < excess; i++) {
      maxWidths[columnToApplyExcessTo++] += 1;

      if(columnToApplyExcessTo == maxWidths.length) {
        columnToApplyExcessTo = maxWidths.length - numberOfColumns;
      }
    }
  };

  this.adjustWidthsForColspans = function(widths, maxWidths, colspanWidths) {
    var lastNumberOfColumnsWidth = null;
    var excess = null;

    this.each(colspanWidths, function(colspanWidth, index) {
      lastNumberOfColumnsWidth = this.getWidthOfLastNumberOfColumns(maxWidths, index + 1);

      if(colspanWidth && colspanWidth > lastNumberOfColumnsWidth){
        excess = colspanWidth - lastNumberOfColumnsWidth;
        this.spreadOutExcessOverLastNumberOfColumns(maxWidths, excess, index + 1);
        this.setMaxWidthsOnNonColspanColumns(widths, maxWidths);
      }
    }, this);
  };

  this.adjustColspansForWidths = function(widths, maxWidths) {
    this.each(widths, function(row, rowIndex) {
      var colspan = maxWidths.length - row.length + 1;

      if(colspan > 1) {
        row[row.length - 1] = this.getWidthOfLastNumberOfColumns(maxWidths, colspan);
      }
    }, this);
  };

  /*
   * Utility functions
   */
  this.trim = function(text) {
    return (text || "").replace( /^\s+|\s+$/g, "" );
  };

  this.each = function(array, callback, context) {
    var index = 0;
    var length = array.length;

    while(index < length && callback.call(context, array[index], index) !== false) {
      index++;
    }
  };

  this.rightPad = function(value, length) {
    var padded = value;

    for(var i = 0, j = length - value.length; i < j; i++) {
      padded += " ";
    }

    return padded;
  };

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy