Convert Indented Text List to HTML List (jQuery)

元气小坏坏 提交于 2019-12-25 09:36:21

问题


I am attempting to create a jQuery script which will convert an indented text list of arbitrary length and depth into a properly formatted HTML list. The lists on which I will be running this script are simple tree structures for directories. Within the tree structures, folders are denoted by a semicolon following the folder name (and files have no ending punctuation). Given this, I would like to attach a <span class="folder"></span> or <span class="file"></span> to the lines as appropriate.

I've found it to be fairly easy to generate most of the structure, but I cannot seem to get the recursion (which I suspect will be necessary) down to ensure that the tags are properly nested. The page on which this will be implemented will include the most recent (i.e., 3.0.3) version of Bootstrap, so feel free to use any of its functionality. I have about two dozen (generally abortive) fragments of code which I've tried or which I'm currently attempting to tweak to produce the desired result. Instead of posting a mass of (likely unhelpful) code, I've created a JSFiddle with the basic form which will be used for input/output, a bit of jQuery, and an example list and some external libraries loaded.

Any help or suggestions will be greatly appreciated.


回答1:


Try this. I copied it to your fiddle and it seems to work.

var indentedToHtmlList = function indentedToHtmlList (text, indentChar, folderChar, listType, showIcons) {
  indentChar = indentChar || '\t';
  folderChar = folderChar || ':';
  listType = listType || 'ul';
  showIcons = !!showIcons;

  var lastDepth,
      lines = text.split(/\r?\n/),
      output = '<' + listType + '>\n',
      depthCounter = new RegExp('^(' + indentChar + '*)(.*)');

  for (var i = 0; i < lines.length; i++) {
    var splitted = lines[i].match(depthCounter),
        indentStr = splitted[1],
        fileName = splitted[2],
        currentDepth = (indentStr === undefined) ? 0 : (indentStr.length / indentChar.length),
        isFolder = (fileName.charAt(fileName.length - 1) === folderChar);

    if (isFolder) {
      fileName = fileName.substring(0, fileName.length -1);
    }

    if (lastDepth === currentDepth) {
      output += '</li>\n';
    } else if (lastDepth > currentDepth) {
      while (lastDepth > currentDepth) {
        output += '</li>\n</' + listType + '>\n</li>\n';
        lastDepth--;
      }
    } else if (lastDepth < currentDepth) {
      output += '\n<' + listType + '>\n';
    }

    output += '<li>';
    if (showIcons) {
      output += '<span class=" glyphicon glyphicon-' +
      (isFolder ? 'folder-open' : 'file') +
      '"></span> ';
    }
    output += fileName;

    lastDepth = currentDepth;
  }

  while (lastDepth >= 0) {
    output += '\n</li>\n</' + listType + '>';
    lastDepth--;
  }

  return output;
};



回答2:


You could use spans and classes to denote files and folders, but you should consider using ul and li elements, they were built for that.

The whole list should be enclosed within an ul element. Each entry on the top level list should create an li element inside of the main element. If the element is a folder, then it should also append another ul. This is where you'll need recursion to allow proper nesting.

However, if you intend to use indentation (no pun indented) the tab and or whitespace parsing is a problem by itself which I'm not solving in this answer. For the sake of this example, I'll just pretend you have a magic function that turns text into a parsed list called MyList, and that files that belong to a folder are whatever lies after the first semicolon of each list element.

var turnTextIntoList=function(AText) {
    //magic stuff;
    return SomeList;
};

var populateList=function(AList) {
    var mainelement=jQuery('<ul></ul>');
    for(element in AList) { 
        var the_li=jQuery('<li></li>');
        if(element.indexOf(';')!=-1) {
             the_li.append('<span class="file">'+element+'</span>');
        } else {
              var thefolder=element.split(';')
              the_li.append('<span class="folder">'+thefolder[0]+'</span>');
              the_li.append(populateList(turnTextIntoList(thefolder[1])));
        } 
        mainelement.append(the_li);
    }
    return mainelement;
};

var MyList=turnTextIntoList(MyText);
jQuery('#targetdiv').append(populateList(MyList));

See, the recursion part is where you do

the_li.append(populateList(turnTextIntoList(thefolder[1])));

which will keep drilling into nesting levels until it reaches a file so it can start its way back.




回答3:


It appears that someone already created a script which does this. Unfortunately, that script is in CoffeeScript, not JavaScript. However, there are a number online converters which will convert from CoffeeScript to JavaScript. Thanks to @charlietfl who provided a link to a working converter, supra.

Here is the converted, working code:

var bind, blank, convert, index, li, lineToMap, linesToMaps, parse, parseTuples, ptAccum, runConvert, tabCount, ul, ulEnd;

convert = function(text) {
  return parse(text.split('\n'));
};

li = function(t) {
  var html;
  html = "<li>" + t['line'] + "</li>";
  ptAccum.push(html);
  return html;
};

ul = function(t) {
  return ptAccum.push("<ul>" + (li(t)));
};

ulEnd = function() {
  return ptAccum.push("</ul>");
};

ptAccum = [];

index = 0;

parse = function(lines) {
  var ts;
  ts = linesToMaps(lines);
  ptAccum = ["<ul>"];
  index = 0;
  parseTuples(ts, 0);
  ulEnd();
  return ptAccum.join("\n");
};

parseTuples = function(tuples, level) {
  var stop, _p, _results;
  stop = false;
  _p = function() {
    var curLevel, t;
    t = tuples[index];
    curLevel = t['level'];
    index++;
    if (curLevel === level) {
      return li(t);
    } else if (curLevel < level) {
      index--;
      return stop = true;
    } else {
      ul(t);
      parseTuples(tuples, level + 1);
      return ulEnd();
    }
  };
  _results = [];
  while (!stop && index < tuples.length) {
    _results.push(_p());
  }
  return _results;
};

tabCount = function(line) {
  var c, count, i, inc, isTab, tc;
  tc = 0;
  c = '\t';
  count = 0;
  if (line) {
    count = line.length;
  }
  i = 0;
  isTab = function() {
    return c === '\t';
  };
  inc = function() {
    c = line.charAt(i);
    if (isTab()) {
      tc++;
    }
    return i++;
  };
  while (isTab() && i < count) {
    inc();
  }
  return tc;
};

lineToMap = function(line) {
  return {
    line: line,
    level: tabCount(line)
  };
};

blank = function(line) {
  return !line || line.length === 0 || line.match(/^ *$/);
};

linesToMaps = function(lines) {
  var line, _i, _len, _results;
  _results = [];
  for (_i = 0, _len = lines.length; _i < _len; _i++) {
    line = lines[_i];
    if (!(blank(line))) {
      _results.push(lineToMap(line));
    }
  }
  return _results;
};

runConvert = function() {
  var result;
  result = convert($('#textarea-plain-text').val());
  $('#textarea-converted-text').val(result);
  return $('#div-converted-text').html(result);
};

bind = function() {
  return $('#list-conversion-button').click(runConvert);
};

$(bind);

JSFiddle



来源:https://stackoverflow.com/questions/20578697/convert-indented-text-list-to-html-list-jquery

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!