899 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			899 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/*
 | 
						|
 * @fileoverview Main Doctrine object
 | 
						|
 * @author Yusuke Suzuki <utatane.tea@gmail.com>
 | 
						|
 * @author Dan Tao <daniel.tao@gmail.com>
 | 
						|
 * @author Andrew Eisenberg <andrew@eisenberg.as>
 | 
						|
 */
 | 
						|
 | 
						|
(function () {
 | 
						|
    'use strict';
 | 
						|
 | 
						|
    var typed,
 | 
						|
        utility,
 | 
						|
        jsdoc,
 | 
						|
        esutils,
 | 
						|
        hasOwnProperty;
 | 
						|
 | 
						|
    esutils = require('esutils');
 | 
						|
    typed = require('./typed');
 | 
						|
    utility = require('./utility');
 | 
						|
 | 
						|
    function sliceSource(source, index, last) {
 | 
						|
        return source.slice(index, last);
 | 
						|
    }
 | 
						|
 | 
						|
    hasOwnProperty = (function () {
 | 
						|
        var func = Object.prototype.hasOwnProperty;
 | 
						|
        return function hasOwnProperty(obj, name) {
 | 
						|
            return func.call(obj, name);
 | 
						|
        };
 | 
						|
    }());
 | 
						|
    function shallowCopy(obj) {
 | 
						|
        var ret = {}, key;
 | 
						|
        for (key in obj) {
 | 
						|
            if (obj.hasOwnProperty(key)) {
 | 
						|
                ret[key] = obj[key];
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    function isASCIIAlphanumeric(ch) {
 | 
						|
        return (ch >= 0x61  /* 'a' */ && ch <= 0x7A  /* 'z' */) ||
 | 
						|
            (ch >= 0x41  /* 'A' */ && ch <= 0x5A  /* 'Z' */) ||
 | 
						|
            (ch >= 0x30  /* '0' */ && ch <= 0x39  /* '9' */);
 | 
						|
    }
 | 
						|
 | 
						|
    function isParamTitle(title) {
 | 
						|
        return title === 'param' || title === 'argument' || title === 'arg';
 | 
						|
    }
 | 
						|
 | 
						|
    function isReturnTitle(title) {
 | 
						|
        return title === 'return' || title === 'returns';
 | 
						|
    }
 | 
						|
 | 
						|
    function isProperty(title) {
 | 
						|
        return title === 'property' || title === 'prop';
 | 
						|
    }
 | 
						|
 | 
						|
    function isNameParameterRequired(title) {
 | 
						|
        return isParamTitle(title) || isProperty(title) ||
 | 
						|
            title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires';
 | 
						|
    }
 | 
						|
 | 
						|
    function isAllowedName(title) {
 | 
						|
        return isNameParameterRequired(title) || title === 'const' || title === 'constant';
 | 
						|
    }
 | 
						|
 | 
						|
    function isAllowedNested(title) {
 | 
						|
        return isProperty(title) || isParamTitle(title);
 | 
						|
    }
 | 
						|
 | 
						|
    function isAllowedOptional(title) {
 | 
						|
        return isProperty(title) || isParamTitle(title);
 | 
						|
    }
 | 
						|
 | 
						|
    function isTypeParameterRequired(title) {
 | 
						|
        return isParamTitle(title) || isReturnTitle(title) ||
 | 
						|
            title === 'define' || title === 'enum' ||
 | 
						|
            title === 'implements' || title === 'this' ||
 | 
						|
            title === 'type' || title === 'typedef' || isProperty(title);
 | 
						|
    }
 | 
						|
 | 
						|
    // Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required
 | 
						|
    // This would require changes to 'parseType'
 | 
						|
    function isAllowedType(title) {
 | 
						|
        return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' ||
 | 
						|
            title === 'namespace' || title === 'member' || title === 'var' || title === 'module' ||
 | 
						|
            title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments' ||
 | 
						|
            title === 'public' || title === 'private' || title === 'protected';
 | 
						|
    }
 | 
						|
 | 
						|
    // A regex character class that contains all whitespace except linebreak characters (\r, \n, \u2028, \u2029)
 | 
						|
    var WHITESPACE = '[ \\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]';
 | 
						|
 | 
						|
    var STAR_MATCHER = '(' + WHITESPACE + '*(?:\\*' + WHITESPACE + '?)?)(.+|[\r\n\u2028\u2029])';
 | 
						|
 | 
						|
    function unwrapComment(doc) {
 | 
						|
        // JSDoc comment is following form
 | 
						|
        //   /**
 | 
						|
        //    * .......
 | 
						|
        //    */
 | 
						|
 | 
						|
        return doc.
 | 
						|
            // remove /**
 | 
						|
            replace(/^\/\*\*?/, '').
 | 
						|
            // remove */
 | 
						|
            replace(/\*\/$/, '').
 | 
						|
            // remove ' * ' at the beginning of a line
 | 
						|
            replace(new RegExp(STAR_MATCHER, 'g'), '$2').
 | 
						|
            // remove trailing whitespace
 | 
						|
            replace(/\s*$/, '');
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Converts an index in an "unwrapped" JSDoc comment to the corresponding index in the original "wrapped" version
 | 
						|
     * @param {string} originalSource The original wrapped comment
 | 
						|
     * @param {number} unwrappedIndex The index of a character in the unwrapped string
 | 
						|
     * @returns {number} The index of the corresponding character in the original wrapped string
 | 
						|
     */
 | 
						|
    function convertUnwrappedCommentIndex(originalSource, unwrappedIndex) {
 | 
						|
        var replacedSource = originalSource.replace(/^\/\*\*?/, '');
 | 
						|
        var numSkippedChars = 0;
 | 
						|
        var matcher = new RegExp(STAR_MATCHER, 'g');
 | 
						|
        var match;
 | 
						|
 | 
						|
        while ((match = matcher.exec(replacedSource))) {
 | 
						|
            numSkippedChars += match[1].length;
 | 
						|
 | 
						|
            if (match.index + match[0].length > unwrappedIndex + numSkippedChars) {
 | 
						|
                return unwrappedIndex + numSkippedChars + originalSource.length - replacedSource.length;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return originalSource.replace(/\*\/$/, '').replace(/\s*$/, '').length;
 | 
						|
    }
 | 
						|
 | 
						|
    // JSDoc Tag Parser
 | 
						|
 | 
						|
    (function (exports) {
 | 
						|
        var Rules,
 | 
						|
            index,
 | 
						|
            lineNumber,
 | 
						|
            length,
 | 
						|
            source,
 | 
						|
            originalSource,
 | 
						|
            recoverable,
 | 
						|
            sloppy,
 | 
						|
            strict;
 | 
						|
 | 
						|
        function advance() {
 | 
						|
            var ch = source.charCodeAt(index);
 | 
						|
            index += 1;
 | 
						|
            if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(index) === 0x0A  /* '\n' */)) {
 | 
						|
                lineNumber += 1;
 | 
						|
            }
 | 
						|
            return String.fromCharCode(ch);
 | 
						|
        }
 | 
						|
 | 
						|
        function scanTitle() {
 | 
						|
            var title = '';
 | 
						|
            // waste '@'
 | 
						|
            advance();
 | 
						|
 | 
						|
            while (index < length && isASCIIAlphanumeric(source.charCodeAt(index))) {
 | 
						|
                title += advance();
 | 
						|
            }
 | 
						|
 | 
						|
            return title;
 | 
						|
        }
 | 
						|
 | 
						|
        function seekContent() {
 | 
						|
            var ch, waiting, last = index;
 | 
						|
 | 
						|
            waiting = false;
 | 
						|
            while (last < length) {
 | 
						|
                ch = source.charCodeAt(last);
 | 
						|
                if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(last + 1) === 0x0A  /* '\n' */)) {
 | 
						|
                    waiting = true;
 | 
						|
                } else if (waiting) {
 | 
						|
                    if (ch === 0x40  /* '@' */) {
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                    if (!esutils.code.isWhiteSpace(ch)) {
 | 
						|
                        waiting = false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                last += 1;
 | 
						|
            }
 | 
						|
            return last;
 | 
						|
        }
 | 
						|
 | 
						|
        // type expression may have nest brace, such as,
 | 
						|
        // { { ok: string } }
 | 
						|
        //
 | 
						|
        // therefore, scanning type expression with balancing braces.
 | 
						|
        function parseType(title, last, addRange) {
 | 
						|
            var ch, brace, type, startIndex, direct = false;
 | 
						|
 | 
						|
 | 
						|
            // search '{'
 | 
						|
            while (index < last) {
 | 
						|
                ch = source.charCodeAt(index);
 | 
						|
                if (esutils.code.isWhiteSpace(ch)) {
 | 
						|
                    advance();
 | 
						|
                } else if (ch === 0x7B  /* '{' */) {
 | 
						|
                    advance();
 | 
						|
                    break;
 | 
						|
                } else {
 | 
						|
                    // this is direct pattern
 | 
						|
                    direct = true;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
            if (direct) {
 | 
						|
                return null;
 | 
						|
            }
 | 
						|
 | 
						|
            // type expression { is found
 | 
						|
            brace = 1;
 | 
						|
            type = '';
 | 
						|
            while (index < last) {
 | 
						|
                ch = source.charCodeAt(index);
 | 
						|
                if (esutils.code.isLineTerminator(ch)) {
 | 
						|
                    advance();
 | 
						|
                } else {
 | 
						|
                    if (ch === 0x7D  /* '}' */) {
 | 
						|
                        brace -= 1;
 | 
						|
                        if (brace === 0) {
 | 
						|
                            advance();
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    } else if (ch === 0x7B  /* '{' */) {
 | 
						|
                        brace += 1;
 | 
						|
                    }
 | 
						|
                    if (type === '') {
 | 
						|
                        startIndex = index;
 | 
						|
                    }
 | 
						|
                    type += advance();
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (brace !== 0) {
 | 
						|
                // braces is not balanced
 | 
						|
                return utility.throwError('Braces are not balanced');
 | 
						|
            }
 | 
						|
 | 
						|
            if (isAllowedOptional(title)) {
 | 
						|
                return typed.parseParamType(type, {startIndex: convertIndex(startIndex), range: addRange});
 | 
						|
            }
 | 
						|
 | 
						|
            return typed.parseType(type, {startIndex: convertIndex(startIndex), range: addRange});
 | 
						|
        }
 | 
						|
 | 
						|
        function scanIdentifier(last) {
 | 
						|
            var identifier;
 | 
						|
            if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index)) && !source[index].match(/[0-9]/)) {
 | 
						|
                return null;
 | 
						|
            }
 | 
						|
            identifier = advance();
 | 
						|
            while (index < last && esutils.code.isIdentifierPartES5(source.charCodeAt(index))) {
 | 
						|
                identifier += advance();
 | 
						|
            }
 | 
						|
            return identifier;
 | 
						|
        }
 | 
						|
 | 
						|
        function skipWhiteSpace(last) {
 | 
						|
            while (index < last && (esutils.code.isWhiteSpace(source.charCodeAt(index)) || esutils.code.isLineTerminator(source.charCodeAt(index)))) {
 | 
						|
                advance();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function parseName(last, allowBrackets, allowNestedParams) {
 | 
						|
            var name = '',
 | 
						|
                useBrackets,
 | 
						|
                insideString;
 | 
						|
 | 
						|
 | 
						|
            skipWhiteSpace(last);
 | 
						|
 | 
						|
            if (index >= last) {
 | 
						|
                return null;
 | 
						|
            }
 | 
						|
 | 
						|
            if (source.charCodeAt(index) === 0x5B  /* '[' */) {
 | 
						|
                if (allowBrackets) {
 | 
						|
                    useBrackets = true;
 | 
						|
                    name = advance();
 | 
						|
                } else {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            name += scanIdentifier(last);
 | 
						|
 | 
						|
            if (allowNestedParams) {
 | 
						|
                if (source.charCodeAt(index) === 0x3A /* ':' */ && (
 | 
						|
                        name === 'module' ||
 | 
						|
                        name === 'external' ||
 | 
						|
                        name === 'event')) {
 | 
						|
                    name += advance();
 | 
						|
                    name += scanIdentifier(last);
 | 
						|
 | 
						|
                }
 | 
						|
                if(source.charCodeAt(index) === 0x5B  /* '[' */ && source.charCodeAt(index + 1) === 0x5D  /* ']' */){
 | 
						|
                    name += advance();
 | 
						|
                    name += advance();
 | 
						|
                }
 | 
						|
                while (source.charCodeAt(index) === 0x2E  /* '.' */ ||
 | 
						|
                        source.charCodeAt(index) === 0x2F  /* '/' */ ||
 | 
						|
                        source.charCodeAt(index) === 0x23  /* '#' */ ||
 | 
						|
                        source.charCodeAt(index) === 0x2D  /* '-' */ ||
 | 
						|
                        source.charCodeAt(index) === 0x7E  /* '~' */) {
 | 
						|
                    name += advance();
 | 
						|
                    name += scanIdentifier(last);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (useBrackets) {
 | 
						|
                skipWhiteSpace(last);
 | 
						|
                // do we have a default value for this?
 | 
						|
                if (source.charCodeAt(index) === 0x3D  /* '=' */) {
 | 
						|
                    // consume the '='' symbol
 | 
						|
                    name += advance();
 | 
						|
                    skipWhiteSpace(last);
 | 
						|
 | 
						|
                    var ch;
 | 
						|
                    var bracketDepth = 1;
 | 
						|
 | 
						|
                    // scan in the default value
 | 
						|
                    while (index < last) {
 | 
						|
                        ch = source.charCodeAt(index);
 | 
						|
 | 
						|
                        if (esutils.code.isWhiteSpace(ch)) {
 | 
						|
                            if (!insideString) {
 | 
						|
                                skipWhiteSpace(last);
 | 
						|
                                ch = source.charCodeAt(index);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (ch === 0x27 /* ''' */) {
 | 
						|
                            if (!insideString) {
 | 
						|
                                insideString = '\'';
 | 
						|
                            } else {
 | 
						|
                                if (insideString === '\'') {
 | 
						|
                                    insideString = '';
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (ch === 0x22 /* '"' */) {
 | 
						|
                            if (!insideString) {
 | 
						|
                                insideString = '"';
 | 
						|
                            } else {
 | 
						|
                                if (insideString === '"') {
 | 
						|
                                    insideString = '';
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (ch === 0x5B /* '[' */) {
 | 
						|
                            bracketDepth++;
 | 
						|
                        } else if (ch === 0x5D  /* ']' */ &&
 | 
						|
                            --bracketDepth === 0) {
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
 | 
						|
                        name += advance();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                skipWhiteSpace(last);
 | 
						|
 | 
						|
                if (index >= last || source.charCodeAt(index) !== 0x5D  /* ']' */) {
 | 
						|
                    // we never found a closing ']'
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
 | 
						|
                // collect the last ']'
 | 
						|
                name += advance();
 | 
						|
            }
 | 
						|
 | 
						|
            return name;
 | 
						|
        }
 | 
						|
 | 
						|
        function skipToTag() {
 | 
						|
            while (index < length && source.charCodeAt(index) !== 0x40  /* '@' */) {
 | 
						|
                advance();
 | 
						|
            }
 | 
						|
            if (index >= length) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            utility.assert(source.charCodeAt(index) === 0x40  /* '@' */);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        function convertIndex(rangeIndex) {
 | 
						|
            if (source === originalSource) {
 | 
						|
                return rangeIndex;
 | 
						|
            }
 | 
						|
            return convertUnwrappedCommentIndex(originalSource, rangeIndex);
 | 
						|
        }
 | 
						|
 | 
						|
        function TagParser(options, title) {
 | 
						|
            this._options = options;
 | 
						|
            this._title = title.toLowerCase();
 | 
						|
            this._tag = {
 | 
						|
                title: title,
 | 
						|
                description: null
 | 
						|
            };
 | 
						|
            if (this._options.lineNumbers) {
 | 
						|
                this._tag.lineNumber = lineNumber;
 | 
						|
            }
 | 
						|
            this._first = index - title.length - 1;
 | 
						|
            this._last = 0;
 | 
						|
            // space to save special information for title parsers.
 | 
						|
            this._extra = { };
 | 
						|
        }
 | 
						|
 | 
						|
        // addError(err, ...)
 | 
						|
        TagParser.prototype.addError = function addError(errorText) {
 | 
						|
            var args = Array.prototype.slice.call(arguments, 1),
 | 
						|
                msg = errorText.replace(
 | 
						|
                    /%(\d)/g,
 | 
						|
                    function (whole, index) {
 | 
						|
                        utility.assert(index < args.length, 'Message reference must be in range');
 | 
						|
                        return args[index];
 | 
						|
                    }
 | 
						|
                );
 | 
						|
 | 
						|
            if (!this._tag.errors) {
 | 
						|
                this._tag.errors = [];
 | 
						|
            }
 | 
						|
            if (strict) {
 | 
						|
                utility.throwError(msg);
 | 
						|
            }
 | 
						|
            this._tag.errors.push(msg);
 | 
						|
            return recoverable;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseType = function () {
 | 
						|
            // type required titles
 | 
						|
            if (isTypeParameterRequired(this._title)) {
 | 
						|
                try {
 | 
						|
                    this._tag.type = parseType(this._title, this._last, this._options.range);
 | 
						|
                    if (!this._tag.type) {
 | 
						|
                        if (!isParamTitle(this._title) && !isReturnTitle(this._title)) {
 | 
						|
                            if (!this.addError('Missing or invalid tag type')) {
 | 
						|
                                return false;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                } catch (error) {
 | 
						|
                    this._tag.type = null;
 | 
						|
                    if (!this.addError(error.message)) {
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } else if (isAllowedType(this._title)) {
 | 
						|
                // optional types
 | 
						|
                try {
 | 
						|
                    this._tag.type = parseType(this._title, this._last, this._options.range);
 | 
						|
                } catch (e) {
 | 
						|
                    //For optional types, lets drop the thrown error when we hit the end of the file
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype._parseNamePath = function (optional) {
 | 
						|
            var name;
 | 
						|
            name = parseName(this._last, sloppy && isAllowedOptional(this._title), true);
 | 
						|
            if (!name) {
 | 
						|
                if (!optional) {
 | 
						|
                    if (!this.addError('Missing or invalid tag name')) {
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            this._tag.name = name;
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseNamePath = function () {
 | 
						|
            return this._parseNamePath(false);
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseNamePathOptional = function () {
 | 
						|
            return this._parseNamePath(true);
 | 
						|
        };
 | 
						|
 | 
						|
 | 
						|
        TagParser.prototype.parseName = function () {
 | 
						|
            var assign, name;
 | 
						|
 | 
						|
            // param, property requires name
 | 
						|
            if (isAllowedName(this._title)) {
 | 
						|
                this._tag.name = parseName(this._last, sloppy && isAllowedOptional(this._title), isAllowedNested(this._title));
 | 
						|
                if (!this._tag.name) {
 | 
						|
                    if (!isNameParameterRequired(this._title)) {
 | 
						|
                        return true;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // it's possible the name has already been parsed but interpreted as a type
 | 
						|
                    // it's also possible this is a sloppy declaration, in which case it will be
 | 
						|
                    // fixed at the end
 | 
						|
                    if (isParamTitle(this._title) && this._tag.type && this._tag.type.name) {
 | 
						|
                        this._extra.name = this._tag.type;
 | 
						|
                        this._tag.name = this._tag.type.name;
 | 
						|
                        this._tag.type = null;
 | 
						|
                    } else {
 | 
						|
                        if (!this.addError('Missing or invalid tag name')) {
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    name = this._tag.name;
 | 
						|
                    if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
 | 
						|
                        // extract the default value if there is one
 | 
						|
                        // example: @param {string} [somebody=John Doe] description
 | 
						|
                        assign = name.substring(1, name.length - 1).split('=');
 | 
						|
                        if (assign.length > 1) {
 | 
						|
                            this._tag['default'] = assign.slice(1).join('=');
 | 
						|
                        }
 | 
						|
                        this._tag.name = assign[0];
 | 
						|
 | 
						|
                        // convert to an optional type
 | 
						|
                        if (this._tag.type && this._tag.type.type !== 'OptionalType') {
 | 
						|
                            this._tag.type = {
 | 
						|
                                type: 'OptionalType',
 | 
						|
                                expression: this._tag.type
 | 
						|
                            };
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseDescription = function parseDescription() {
 | 
						|
            var description = sliceSource(source, index, this._last).trim();
 | 
						|
            if (description) {
 | 
						|
                if ((/^-\s+/).test(description)) {
 | 
						|
                    description = description.substring(2);
 | 
						|
                }
 | 
						|
                this._tag.description = description;
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseCaption = function parseDescription() {
 | 
						|
            var description = sliceSource(source, index, this._last).trim();
 | 
						|
            var captionStartTag = '<caption>';
 | 
						|
            var captionEndTag = '</caption>';
 | 
						|
            var captionStart = description.indexOf(captionStartTag);
 | 
						|
            var captionEnd = description.indexOf(captionEndTag);
 | 
						|
            if (captionStart >= 0 && captionEnd >= 0) {
 | 
						|
                this._tag.caption = description.substring(
 | 
						|
                    captionStart + captionStartTag.length, captionEnd).trim();
 | 
						|
                this._tag.description = description.substring(captionEnd + captionEndTag.length).trim();
 | 
						|
            } else {
 | 
						|
                this._tag.description = description;
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseKind = function parseKind() {
 | 
						|
            var kind, kinds;
 | 
						|
            kinds = {
 | 
						|
                'class': true,
 | 
						|
                'constant': true,
 | 
						|
                'event': true,
 | 
						|
                'external': true,
 | 
						|
                'file': true,
 | 
						|
                'function': true,
 | 
						|
                'member': true,
 | 
						|
                'mixin': true,
 | 
						|
                'module': true,
 | 
						|
                'namespace': true,
 | 
						|
                'typedef': true
 | 
						|
            };
 | 
						|
            kind = sliceSource(source, index, this._last).trim();
 | 
						|
            this._tag.kind = kind;
 | 
						|
            if (!hasOwnProperty(kinds, kind)) {
 | 
						|
                if (!this.addError('Invalid kind name \'%0\'', kind)) {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseAccess = function parseAccess() {
 | 
						|
            var access;
 | 
						|
            access = sliceSource(source, index, this._last).trim();
 | 
						|
            this._tag.access = access;
 | 
						|
            if (access !== 'private' && access !== 'protected' && access !== 'public') {
 | 
						|
                if (!this.addError('Invalid access name \'%0\'', access)) {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseThis = function parseThis() {
 | 
						|
            // this name may be a name expression (e.g. {foo.bar}),
 | 
						|
            // an union (e.g. {foo.bar|foo.baz}) or a name path (e.g. foo.bar)
 | 
						|
            var value = sliceSource(source, index, this._last).trim();
 | 
						|
            if (value && value.charAt(0) === '{') {
 | 
						|
                var gotType = this.parseType();
 | 
						|
                if (gotType && this._tag.type.type === 'NameExpression' || this._tag.type.type === 'UnionType') {
 | 
						|
                    this._tag.name = this._tag.type.name;
 | 
						|
                    return true;
 | 
						|
                } else {
 | 
						|
                    return this.addError('Invalid name for this');
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                return this.parseNamePath();
 | 
						|
            }
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parseVariation = function parseVariation() {
 | 
						|
            var variation, text;
 | 
						|
            text = sliceSource(source, index, this._last).trim();
 | 
						|
            variation = parseFloat(text, 10);
 | 
						|
            this._tag.variation = variation;
 | 
						|
            if (isNaN(variation)) {
 | 
						|
                if (!this.addError('Invalid variation \'%0\'', text)) {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.ensureEnd = function () {
 | 
						|
            var shouldBeEmpty = sliceSource(source, index, this._last).trim();
 | 
						|
            if (shouldBeEmpty) {
 | 
						|
                if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.epilogue = function epilogue() {
 | 
						|
            var description;
 | 
						|
 | 
						|
            description = this._tag.description;
 | 
						|
            // un-fix potentially sloppy declaration
 | 
						|
            if (isAllowedOptional(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
 | 
						|
                this._tag.type = this._extra.name;
 | 
						|
                if (!this._tag.name) {
 | 
						|
                    this._tag.name = undefined;
 | 
						|
                }
 | 
						|
 | 
						|
                if (!sloppy) {
 | 
						|
                    if (!this.addError('Missing or invalid tag name')) {
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return true;
 | 
						|
        };
 | 
						|
 | 
						|
        Rules = {
 | 
						|
            // http://usejsdoc.org/tags-access.html
 | 
						|
            'access': ['parseAccess'],
 | 
						|
            // http://usejsdoc.org/tags-alias.html
 | 
						|
            'alias': ['parseNamePath', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-augments.html
 | 
						|
            'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-constructor.html
 | 
						|
            'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // Synonym: http://usejsdoc.org/tags-constructor.html
 | 
						|
            'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // Synonym: http://usejsdoc.org/tags-extends.html
 | 
						|
            'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-example.html
 | 
						|
            'example': ['parseCaption'],
 | 
						|
            // http://usejsdoc.org/tags-deprecated.html
 | 
						|
            'deprecated': ['parseDescription'],
 | 
						|
            // http://usejsdoc.org/tags-global.html
 | 
						|
            'global': ['ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-inner.html
 | 
						|
            'inner': ['ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-instance.html
 | 
						|
            'instance': ['ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-kind.html
 | 
						|
            'kind': ['parseKind'],
 | 
						|
            // http://usejsdoc.org/tags-mixes.html
 | 
						|
            'mixes': ['parseNamePath', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-mixin.html
 | 
						|
            'mixin': ['parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-member.html
 | 
						|
            'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-method.html
 | 
						|
            'method': ['parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-module.html
 | 
						|
            'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // Synonym: http://usejsdoc.org/tags-method.html
 | 
						|
            'func': ['parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // Synonym: http://usejsdoc.org/tags-method.html
 | 
						|
            'function': ['parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // Synonym: http://usejsdoc.org/tags-member.html
 | 
						|
            'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-name.html
 | 
						|
            'name': ['parseNamePath', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-namespace.html
 | 
						|
            'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-private.html
 | 
						|
            'private': ['parseType', 'parseDescription'],
 | 
						|
            // http://usejsdoc.org/tags-protected.html
 | 
						|
            'protected': ['parseType', 'parseDescription'],
 | 
						|
            // http://usejsdoc.org/tags-public.html
 | 
						|
            'public': ['parseType', 'parseDescription'],
 | 
						|
            // http://usejsdoc.org/tags-readonly.html
 | 
						|
            'readonly': ['ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-requires.html
 | 
						|
            'requires': ['parseNamePath', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-since.html
 | 
						|
            'since': ['parseDescription'],
 | 
						|
            // http://usejsdoc.org/tags-static.html
 | 
						|
            'static': ['ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-summary.html
 | 
						|
            'summary': ['parseDescription'],
 | 
						|
            // http://usejsdoc.org/tags-this.html
 | 
						|
            'this': ['parseThis', 'ensureEnd'],
 | 
						|
            // http://usejsdoc.org/tags-todo.html
 | 
						|
            'todo': ['parseDescription'],
 | 
						|
            // http://usejsdoc.org/tags-typedef.html
 | 
						|
            'typedef': ['parseType', 'parseNamePathOptional'],
 | 
						|
            // http://usejsdoc.org/tags-variation.html
 | 
						|
            'variation': ['parseVariation'],
 | 
						|
            // http://usejsdoc.org/tags-version.html
 | 
						|
            'version': ['parseDescription']
 | 
						|
        };
 | 
						|
 | 
						|
        TagParser.prototype.parse = function parse() {
 | 
						|
            var i, iz, sequences, method;
 | 
						|
 | 
						|
 | 
						|
            // empty title
 | 
						|
            if (!this._title) {
 | 
						|
                if (!this.addError('Missing or invalid title')) {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Seek to content last index.
 | 
						|
            this._last = seekContent(this._title);
 | 
						|
 | 
						|
            if (this._options.range) {
 | 
						|
                this._tag.range = [this._first, source.slice(0, this._last).replace(/\s*$/, '').length].map(convertIndex);
 | 
						|
            }
 | 
						|
 | 
						|
            if (hasOwnProperty(Rules, this._title)) {
 | 
						|
                sequences = Rules[this._title];
 | 
						|
            } else {
 | 
						|
                // default sequences
 | 
						|
                sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
 | 
						|
            }
 | 
						|
 | 
						|
            for (i = 0, iz = sequences.length; i < iz; ++i) {
 | 
						|
                method = sequences[i];
 | 
						|
                if (!this[method]()) {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return this._tag;
 | 
						|
        };
 | 
						|
 | 
						|
        function parseTag(options) {
 | 
						|
            var title, parser, tag;
 | 
						|
 | 
						|
            // skip to tag
 | 
						|
            if (!skipToTag()) {
 | 
						|
                return null;
 | 
						|
            }
 | 
						|
 | 
						|
            // scan title
 | 
						|
            title = scanTitle();
 | 
						|
 | 
						|
            // construct tag parser
 | 
						|
            parser = new TagParser(options, title);
 | 
						|
            tag = parser.parse();
 | 
						|
 | 
						|
            // Seek global index to end of this tag.
 | 
						|
            while (index < parser._last) {
 | 
						|
                advance();
 | 
						|
            }
 | 
						|
 | 
						|
            return tag;
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        // Parse JSDoc
 | 
						|
        //
 | 
						|
 | 
						|
        function scanJSDocDescription(preserveWhitespace) {
 | 
						|
            var description = '', ch, atAllowed;
 | 
						|
 | 
						|
            atAllowed = true;
 | 
						|
            while (index < length) {
 | 
						|
                ch = source.charCodeAt(index);
 | 
						|
 | 
						|
                if (atAllowed && ch === 0x40  /* '@' */) {
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                if (esutils.code.isLineTerminator(ch)) {
 | 
						|
                    atAllowed = true;
 | 
						|
                } else if (atAllowed && !esutils.code.isWhiteSpace(ch)) {
 | 
						|
                    atAllowed = false;
 | 
						|
                }
 | 
						|
 | 
						|
                description += advance();
 | 
						|
            }
 | 
						|
 | 
						|
            return preserveWhitespace ? description : description.trim();
 | 
						|
        }
 | 
						|
 | 
						|
        function parse(comment, options) {
 | 
						|
            var tags = [], tag, description, interestingTags, i, iz;
 | 
						|
 | 
						|
            if (options === undefined) {
 | 
						|
                options = {};
 | 
						|
            }
 | 
						|
 | 
						|
            if (typeof options.unwrap === 'boolean' && options.unwrap) {
 | 
						|
                source = unwrapComment(comment);
 | 
						|
            } else {
 | 
						|
                source = comment;
 | 
						|
            }
 | 
						|
 | 
						|
            originalSource = comment;
 | 
						|
 | 
						|
            // array of relevant tags
 | 
						|
            if (options.tags) {
 | 
						|
                if (Array.isArray(options.tags)) {
 | 
						|
                    interestingTags = { };
 | 
						|
                    for (i = 0, iz = options.tags.length; i < iz; i++) {
 | 
						|
                        if (typeof options.tags[i] === 'string') {
 | 
						|
                            interestingTags[options.tags[i]] = true;
 | 
						|
                        } else {
 | 
						|
                            utility.throwError('Invalid "tags" parameter: ' + options.tags);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    utility.throwError('Invalid "tags" parameter: ' + options.tags);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            length = source.length;
 | 
						|
            index = 0;
 | 
						|
            lineNumber = 0;
 | 
						|
            recoverable = options.recoverable;
 | 
						|
            sloppy = options.sloppy;
 | 
						|
            strict = options.strict;
 | 
						|
 | 
						|
            description = scanJSDocDescription(options.preserveWhitespace);
 | 
						|
 | 
						|
            while (true) {
 | 
						|
                tag = parseTag(options);
 | 
						|
                if (!tag) {
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
 | 
						|
                    tags.push(tag);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return {
 | 
						|
                description: description,
 | 
						|
                tags: tags
 | 
						|
            };
 | 
						|
        }
 | 
						|
        exports.parse = parse;
 | 
						|
    }(jsdoc = {}));
 | 
						|
 | 
						|
    exports.version = utility.VERSION;
 | 
						|
    exports.parse = jsdoc.parse;
 | 
						|
    exports.parseType = typed.parseType;
 | 
						|
    exports.parseParamType = typed.parseParamType;
 | 
						|
    exports.unwrapComment = unwrapComment;
 | 
						|
    exports.Syntax = shallowCopy(typed.Syntax);
 | 
						|
    exports.Error = utility.DoctrineError;
 | 
						|
    exports.type = {
 | 
						|
        Syntax: exports.Syntax,
 | 
						|
        parseType: typed.parseType,
 | 
						|
        parseParamType: typed.parseParamType,
 | 
						|
        stringify: typed.stringify
 | 
						|
    };
 | 
						|
}());
 | 
						|
/* vim: set sw=4 ts=4 et tw=80 : */
 |