| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * NOT a dgeni service because we need to be able to use it externally | 
					
						
							|  |  |  |  * as well as from dgeni. | 
					
						
							|  |  |  |  * @description | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2015-10-13 19:44:27 -07:00
										 |  |  | var _ = require('lodash'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  | var nullLine = '###'; | 
					
						
							|  |  |  | var nullLinePattern = new RegExp(nullLine + '\n', 'g'); | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  | module.exports = { | 
					
						
							|  |  |  |   buildRegionDocs: buildRegionDocs, | 
					
						
							|  |  |  |   getRegionDoc: getRegionDoc, | 
					
						
							|  |  |  |   removeDocTags: removeDocTags | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // split out each fragment in {content} into a separate doc
 | 
					
						
							|  |  |  | // a fragment is a section of text surrounded by
 | 
					
						
							|  |  |  | //   1) In front: a comment marker followed by '#docregion' followed by an optional region name. For example:
 | 
					
						
							|  |  |  | //        <-- #docregion foo --> for html
 | 
					
						
							|  |  |  | //     or // #docregion foo      for js/ts
 | 
					
						
							|  |  |  | //   2) In back: a comment marker followed by '#enddocregion'
 | 
					
						
							|  |  |  | // Regions can be nested and any regions not 'closed' are automatically closed at the end of the doc.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // empty enddocregion always closes last region started.
 | 
					
						
							|  |  |  | // enddocregions with names that do no match start region tags get ignored.
 | 
					
						
							|  |  |  | function buildRegionDocs(content, extn) { | 
					
						
							|  |  |  |   var commentInfo = getCommentInfo(extn); | 
					
						
							|  |  |  |   if (!commentInfo) { | 
					
						
							|  |  |  |     return  [ { content: content } ]; | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  |   var lines = result = content.split(/\r?\n/); | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  |   var docStack = []; // items will be both popped and removed from the middle
 | 
					
						
							|  |  |  |   var docMap = {}; | 
					
						
							|  |  |  |   var docPlaster = '. . .'; | 
					
						
							|  |  |  |   var doc; | 
					
						
							|  |  |  |   var regionNames; | 
					
						
							|  |  |  |   lines.forEach(function(line, ix) { | 
					
						
							|  |  |  |     if (isCommentLine(line, commentInfo.prefix)) { | 
					
						
							|  |  |  |       if (hasRegionTag(line)) { | 
					
						
							|  |  |  |         lines[ix] = nullLine; | 
					
						
							|  |  |  |         regionNames = getRegionNames(line); | 
					
						
							|  |  |  |         regionNames.forEach(function(rn) { | 
					
						
							|  |  |  |           doc = docMap[rn]; | 
					
						
							|  |  |  |           if (!doc) { | 
					
						
							|  |  |  |             // regionName may be ''
 | 
					
						
							|  |  |  |             doc = {regionName: rn, ranges: [ { startIx: ix} ] }; | 
					
						
							|  |  |  |             docMap[rn] = doc; | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             // only add a new range if prev range is closed
 | 
					
						
							|  |  |  |             var lastRange = doc.ranges[doc.ranges.length-1]; | 
					
						
							|  |  |  |             if (lastRange.endIx) { | 
					
						
							|  |  |  |               doc.ranges.push({startIx: ix}); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           docStack.push(doc); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       } else if (hasEndRegionTag(line)) { | 
					
						
							|  |  |  |         lines[ix] = nullLine; | 
					
						
							|  |  |  |         regionNames = getEndRegionNames(line); | 
					
						
							|  |  |  |         regionNames.forEach(function(rn) { | 
					
						
							|  |  |  |           // handle endregions with no name specially.
 | 
					
						
							|  |  |  |           // They operate on the last region created.
 | 
					
						
							|  |  |  |           if (rn.length == 0) { | 
					
						
							|  |  |  |             if (docStack.length) { | 
					
						
							|  |  |  |               // update last item on the stack
 | 
					
						
							|  |  |  |               doc = docStack.pop(); | 
					
						
							|  |  |  |               doc.ranges[doc.ranges.length - 1].endIx = ix; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             doc = docMap[rn]; | 
					
						
							|  |  |  |             // ignore endregion if name is specified but not found.
 | 
					
						
							|  |  |  |             if (doc) { | 
					
						
							|  |  |  |               doc.ranges[doc.ranges.length - 1].endIx = ix; | 
					
						
							|  |  |  |               // remove doc from stack
 | 
					
						
							|  |  |  |               _.remove(docStack, function (item) { | 
					
						
							|  |  |  |                 return item.regionName === rn; | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } else if (hasDocPlasterTag(line)) { | 
					
						
							|  |  |  |         line[ix] = nullLine; | 
					
						
							|  |  |  |         docPlaster =  getDocPlaster(line); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-09-11 01:10:48 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  |   var docs = _.values(docMap); | 
					
						
							|  |  |  |   var plasterComment = docPlaster && commentInfo.plasterPattern.replace('{tag}', docPlaster); | 
					
						
							|  |  |  |   docs = reattachDocs(docs, lines, plasterComment); | 
					
						
							|  |  |  |   return docs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getRegionDoc(content, extn, regionName) { | 
					
						
							|  |  |  |   var docs = buildRegionDocs(content, extn); | 
					
						
							|  |  |  |   var doc = _.find(docs, function (doc) { | 
					
						
							|  |  |  |     return doc.regionName === regionName; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return doc && doc.content; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function removeDocTags(content, extn) { | 
					
						
							|  |  |  |   var commentInfo = getCommentInfo(extn); | 
					
						
							|  |  |  |   if (commentInfo == null) { | 
					
						
							|  |  |  |     return content; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   var lines = result = content.split(/\r?\n/); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   lines.forEach(function(line, ix) { | 
					
						
							|  |  |  |     if (isCommentLine(line, commentInfo.prefix)) { | 
					
						
							|  |  |  |       if (hasDocTag(line)) { | 
					
						
							|  |  |  |         lines[ix] = nullLine; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-09-11 01:10:48 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  |   var result = joinLines(lines); | 
					
						
							| 
									
										
										
										
											2015-09-11 01:10:48 -07:00
										 |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  | function reattachDocs(docs, lines, plasterComment) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   docs.forEach(function(doc) { | 
					
						
							|  |  |  |     var content; | 
					
						
							|  |  |  |     var fragLines = []; | 
					
						
							|  |  |  |     doc.ranges.forEach(function (range) { | 
					
						
							|  |  |  |       var subLines; | 
					
						
							|  |  |  |       if (range.endIx) { | 
					
						
							|  |  |  |         subLines = lines.slice(range.startIx + 1, range.endIx); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         subLines = lines.slice(range.startIx + 1); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (plasterComment && fragLines.length) { | 
					
						
							|  |  |  |         // pad is the padding on the previous line
 | 
					
						
							|  |  |  |         var pad = fragLines[fragLines.length - 1].match(/(\s*)/)[0]; | 
					
						
							|  |  |  |         fragLines.push(pad + plasterComment); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       fragLines = fragLines.concat(subLines); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     fragLines = trimLeftIndent(fragLines); | 
					
						
							|  |  |  |     doc.content = joinLines(fragLines); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return docs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getCommentInfo(extension) { | 
					
						
							|  |  |  |   var commentInfo; | 
					
						
							|  |  |  |   switch (extension) { | 
					
						
							|  |  |  |     case 'ts': | 
					
						
							|  |  |  |     case 'js': | 
					
						
							|  |  |  |     case 'dart': | 
					
						
							|  |  |  |       commentInfo = { | 
					
						
							|  |  |  |         prefix: '//', | 
					
						
							|  |  |  |         plasterPattern: '/* {tag} */' | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case 'html': | 
					
						
							|  |  |  |       commentInfo = { | 
					
						
							|  |  |  |         prefix: '<!--', | 
					
						
							|  |  |  |         plasterPattern: '<!-- {tag} -->' | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case 'css': | 
					
						
							|  |  |  |       commentInfo = { | 
					
						
							|  |  |  |         prefix: '/*', | 
					
						
							|  |  |  |         plasterPattern: '/* {tag} */' | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case 'json': | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     case 'yaml': | 
					
						
							|  |  |  |       commentInfo = { | 
					
						
							|  |  |  |         prefix: '#', | 
					
						
							|  |  |  |         plasterPattern: '# {tag} ' | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2016-01-19 02:11:58 -08:00
										 |  |  |     case 'jade': | 
					
						
							|  |  |  |       commentInfo = { | 
					
						
							|  |  |  |         prefix: '//', | 
					
						
							|  |  |  |         plasterPattern: '// {tag} ' | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  |     default: | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return commentInfo; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-14 12:47:22 -07:00
										 |  |  | function isCommentLine(line, commentPrefix) { | 
					
						
							|  |  |  |   return line.trim().indexOf(commentPrefix) == 0; | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  | function hasDocTag(line) { | 
					
						
							|  |  |  |   return hasRegionTag(line) || hasEndRegionTag(line) || hasDocPlasterTag(line); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  | function hasRegionTag(line) { | 
					
						
							|  |  |  |   return line.indexOf("#docregion") >= 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function hasEndRegionTag(line) { | 
					
						
							|  |  |  |   return line.indexOf("#enddocregion") >= 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-14 12:47:22 -07:00
										 |  |  | function hasDocPlasterTag(line) { | 
					
						
							|  |  |  |   return line.indexOf("#docplaster") >= 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-13 19:44:27 -07:00
										 |  |  | function getRegionNames(line) { | 
					
						
							|  |  |  |   return extractRegionNames(line, /#docregion\s*(\S.*)/); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getEndRegionNames(line) { | 
					
						
							|  |  |  |   return extractRegionNames(line, /#enddocregion\s*(\S.*)/); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-14 12:47:22 -07:00
										 |  |  | function getDocPlaster(line) { | 
					
						
							|  |  |  |   var rx =  /#docplaster\s*(\S.*)/; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     var plaster = line.match(rx)[1]; | 
					
						
							| 
									
										
										
										
											2015-10-20 16:51:36 -07:00
										 |  |  |     plaster = plaster.replace("-->","").replace('\*\/',""); | 
					
						
							| 
									
										
										
										
											2015-10-14 12:47:22 -07:00
										 |  |  |     return plaster.trim(); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-13 19:44:27 -07:00
										 |  |  | function extractRegionNames(line, rx) { | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2015-10-13 19:44:27 -07:00
										 |  |  |     var names = line.match(rx)[1]; | 
					
						
							|  |  |  |     names = names.replace(/\s*/g,''); | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  |     // Hack for html regions that look like <!-- #docregion --> or */
 | 
					
						
							| 
									
										
										
										
											2015-10-13 19:44:27 -07:00
										 |  |  |     names = names.replace("-->","").replace('\*\/',""); | 
					
						
							|  |  |  |     names = names.split(','); | 
					
						
							|  |  |  |     return names; | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  |   } catch (e) { | 
					
						
							| 
									
										
										
										
											2015-10-13 19:44:27 -07:00
										 |  |  |     return ['']; | 
					
						
							| 
									
										
										
										
											2015-08-03 17:45:58 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-23 12:47:20 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function trimLeftIndent(lines) { | 
					
						
							|  |  |  |   var minIx  = 100; | 
					
						
							|  |  |  |   var ok = lines.every(function(line) { | 
					
						
							|  |  |  |     // var ix = line.search(/\S/);
 | 
					
						
							|  |  |  |     var ix = line.search(/[^ ]/); | 
					
						
							|  |  |  |     if (ix === 0) return false; | 
					
						
							|  |  |  |     if (ix === -1) return true; | 
					
						
							|  |  |  |     if (ix > 0) { | 
					
						
							|  |  |  |       minIx = Math.min(minIx, ix); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   if ( (!ok) || minIx === 100) return lines; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var result = lines.map(function(line) { | 
					
						
							|  |  |  |     if (line.length > minIx) { | 
					
						
							|  |  |  |       return line.substr(minIx); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // this can happen if line is all blanks and shorter than mixIx
 | 
					
						
							|  |  |  |       return line; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function joinLines(lines) { | 
					
						
							|  |  |  |   var content = lines.join('\n'); | 
					
						
							|  |  |  |   // eliminate all #docregion lines
 | 
					
						
							|  |  |  |   content = content.replace(nullLinePattern, ''); | 
					
						
							|  |  |  |   if (content.substr(-3) === nullLine) { | 
					
						
							|  |  |  |     content = content.substr(0, content.length - 3); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return content; | 
					
						
							|  |  |  | } |