/** * All parsing/transformation code goes here. All code here should be sync to ease testing. */ var Showdown = require('../../lib/showdown').Showdown; var DOM = require('./dom.js').DOM; var htmlEscape = require('./dom.js').htmlEscape; var Example = require('./example.js').Example; var NEW_LINE = /\n\r?/; var globalID = 0; var fs = require('fs'); var fspath = require('path'); exports.trim = trim; exports.metadata = metadata; exports.scenarios = scenarios; exports.merge = merge; exports.Doc = Doc; var BOOLEAN_ATTR = {}; ['multiple', 'selected', 'checked', 'disabled', 'readOnly', 'required'].forEach(function(value) { BOOLEAN_ATTR[value] = true; }); ////////////////////////////////////////////////////////// function Doc(text, file, line) { if (typeof text == 'object') { for ( var key in text) { this[key] = text[key]; } } else { this.text = text; this.file = file; this.line = line; } this.scenarios = this.scenarios || []; this.requires = this.requires || []; this.param = this.param || []; this.properties = this.properties || []; this.methods = this.methods || []; this.events = this.events || []; this.links = this.links || []; } Doc.METADATA_IGNORE = (function() { var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8'); return words.toString().split(/[,\s\n\r]+/gm); })(); Doc.prototype = { keywords: function keywords() { var keywords = {}; var words = []; Doc.METADATA_IGNORE.forEach(function(ignore){ keywords[ignore] = true; }); function extractWords(text) { var tokens = text.toLowerCase().split(/[\.\s,`'"#]+/mg); tokens.forEach(function(key){ var match = key.match(/^((ng:|[\$_a-z])[\w\-_]+)/); if (match){ key = match[1]; if (!keywords[key]) { keywords[key] = true; words.push(key); } } }); } extractWords(this.text); this.properties.forEach(function(prop) { extractWords(prop.text || prop.description || ''); }); this.methods.forEach(function(method) { extractWords(method.text || method.description || ''); }); words.sort(); return words.join(' '); }, /** * Converts relative urls (without section) into absolute * Absolute url means url with section * * @example * - if the link is inside any api doc: * angular.widget -> api/angular.widget * * - if the link is inside any guid doc: * intro -> guide/intro * * @param {string} url Absolute or relative url * @returns {string} Absolute url */ convertUrlToAbsolute: function(url) { if (url.substr(-1) == '/') return url + 'index'; if (url.match(/\//)) return url; return this.section + '/' + url; }, markdown: function(text) { if (!text) return text; var self = this, IS_URL = /^(https?:\/\/|ftps?:\/\/|mailto:|\.|\/)/, IS_ANGULAR = /^(api\/)?(angular|ng|AUTO)\./, IS_HASH = /^#/, parts = trim(text).split(/(
[\s\S]*?<\/pre>|[\s\S]*?<\/doc:example>|]*>[\s\S]*?<\/example>)/),
      seq = 0,
      placeholderMap = {};

    function placeholder(text) {
      var id = 'REPLACEME' + (seq++);
      placeholderMap[id] = text;
      return id;
    }

    function extractInlineDocCode(text, tag) {
      if(tag == 'all') {
        //use a greedy operator to match the last  tag
        regex = /\/\/([.\s\S]+)\/\/<\/docs>/im;
      }
      else {
        //use a non-greedy operator to match the next  tag
        regex = new RegExp("\/\/([.\\s\\S]+?)\/\/<\/docs>","im");
      }
      var matches = regex.exec(text.toString());
      return matches && matches.length > 1 ? matches[1] : "";
    }

    parts.forEach(function(text, i) {
      parts[i] = (text || '').
        replace(/([\s\S]*?)<\/example>/gmi,
          function(_, module, deps, animations, content) {

          var example = new Example(self.scenarios);
          if(animations) {
            example.enableAnimations();
          }

          example.setModule(module);
          example.addDeps(deps);
          content.replace(/([\s\S]*?)<\/file>/gmi, function(_, name, content) {
            example.addSource(name, content);
          });
          content.replace(//gmi, function(_, file, tag, name) {
            if(fspath.existsSync(file)) {
              var content = fs.readFileSync(file, 'utf8');
              if(content && content.length > 0) {
                if(tag && tag.length > 0) {
                  content = extractInlineDocCode(content, tag);
                }
                name = name && name.length > 0 ? name : fspath.basename(file);
                example.addSource(name, content);
              }
            }
            return '';
          })
          return placeholder(example.toHtml());
        }).
        replace(/(?:\*\s+)?/i, function(_, file, tag) {
          if(fspath.existsSync(file)) {
            var content = fs.readFileSync(file, 'utf8');
            if(tag && tag.length > 0) {
              content = extractInlineDocCode(content, tag);
            }
            return content;
          }
        }).
        replace(/^]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
          var html, script, scenario,
            example = new Example(self.scenarios);

          example.setModule((attrs||'module=""').match(/^\s*module=["'](.*)["']\s*$/)[1]);
          content.
            replace(/]*)?>([\s\S]*)<\/doc:source>/mi, function(_, attrs, content) {
              example.addSource('index.html', content.
                replace(/