| 1 | /** Handle the creation of HTML links to documented symbols. |
| 2 | @constructor |
| 3 | */ |
| 4 | function Link() { |
| 5 | this.alias = ""; |
| 6 | this.src = ""; |
| 7 | this.file = ""; |
| 8 | this.text = ""; |
| 9 | this.innerName = ""; |
| 10 | this.classLink = false; |
| 11 | this.targetName = ""; |
| 12 | |
| 13 | this.target = function(targetName) { |
| 14 | if (defined(targetName)) this.targetName = targetName; |
| 15 | return this; |
| 16 | } |
| 17 | this.inner = function(inner) { |
| 18 | if (defined(inner)) this.innerName = inner; |
| 19 | return this; |
| 20 | } |
| 21 | this.withText = function(text) { |
| 22 | if (defined(text)) this.text = text; |
| 23 | return this; |
| 24 | } |
| 25 | this.toSrc = function(filename) { |
| 26 | if (defined(filename)) this.src = filename; |
| 27 | return this; |
| 28 | } |
| 29 | this.toSymbol = function(alias) { |
| 30 | if (defined(alias)) this.alias = new String(alias); |
| 31 | return this; |
| 32 | } |
| 33 | this.toClass = function(alias) { |
| 34 | this.classLink = true; |
| 35 | return this.toSymbol(alias); |
| 36 | } |
| 37 | this.toFile = function(file) { |
| 38 | if (defined(file)) this.file = file; |
| 39 | return this; |
| 40 | } |
| 41 | |
| 42 | this.toString = function() { |
| 43 | var linkString; |
| 44 | var thisLink = this; |
| 45 | |
| 46 | if (this.alias) { |
| 47 | linkString = this.alias.replace(/(^|[^a-z$0-9_#.:^-])([|a-z$0-9_#.:^-]+)($|[^a-z$0-9_#.:^-])/i, |
| 48 | function(match, prematch, symbolName, postmatch) { |
| 49 | var symbolNames = symbolName.split("|"); |
| 50 | var links = []; |
| 51 | for (var i = 0, l = symbolNames.length; i < l; i++) { |
| 52 | thisLink.alias = symbolNames[i]; |
| 53 | links.push(thisLink._makeSymbolLink(symbolNames[i])); |
| 54 | } |
| 55 | return prematch+links.join("|")+postmatch; |
| 56 | } |
| 57 | ); |
| 58 | } |
| 59 | else if (this.src) { |
| 60 | linkString = thisLink._makeSrcLink(this.src); |
| 61 | } |
| 62 | else if (this.file) { |
| 63 | linkString = thisLink._makeFileLink(this.file); |
| 64 | } |
| 65 | |
| 66 | return linkString; |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | /** prefixed for hashes */ |
| 71 | Link.hashPrefix = ""; |
| 72 | |
| 73 | /** Appended to the front of relative link paths. */ |
| 74 | Link.base = ""; |
| 75 | |
| 76 | Link.symbolNameToLinkName = function(symbol) { |
| 77 | var linker = "", |
| 78 | ns = ""; |
| 79 | |
| 80 | if (symbol.isStatic) linker = "."; |
| 81 | else if (symbol.isInner) linker = "-"; |
| 82 | |
| 83 | if (symbol.isEvent && !/^event:/.test(symbol.name)) { |
| 84 | ns = "event:"; |
| 85 | } |
| 86 | return Link.hashPrefix+linker+ns+symbol.name; |
| 87 | } |
| 88 | |
| 89 | Link.getSymbol= function(alias) { |
| 90 | var symbol= Link.symbolSet.getSymbol(alias); |
| 91 | |
| 92 | if (symbol) |
| 93 | return symbol; |
| 94 | |
| 95 | if ('#'!==alias.charAt(0) || !Link.currentSymbol) |
| 96 | return null; |
| 97 | |
| 98 | // resolve relative name |
| 99 | var container= Link.currentSymbol; |
| 100 | |
| 101 | while (container) |
| 102 | { |
| 103 | symbol= Link.symbolSet.getSymbol(container.alias + alias); |
| 104 | if (symbol) |
| 105 | return symbol; |
| 106 | |
| 107 | // No superclass |
| 108 | if (!container.augments.length) |
| 109 | return null; |
| 110 | |
| 111 | container= Link.symbolSet.getSymbol(container.augments[0].desc); |
| 112 | } |
| 113 | |
| 114 | return null; |
| 115 | } |
| 116 | |
| 117 | /** Create a link to another symbol. */ |
| 118 | Link.prototype._makeSymbolLink = function(alias) { |
| 119 | var linkBase = Link.base+publish.conf.symbolsDir; |
| 120 | var linkTo = Link.getSymbol(alias); |
| 121 | var linkPath; |
| 122 | var target = (this.targetName)? " target=\""+this.targetName+"\"" : ""; |
| 123 | |
| 124 | // if there is no symbol by that name just return the name unaltered |
| 125 | if (!linkTo) |
| 126 | return this.text || alias; |
| 127 | |
| 128 | // it's a symbol in another file |
| 129 | else { |
| 130 | if (!linkTo.is("CONSTRUCTOR") && !linkTo.isNamespace) { // it's a method or property |
| 131 | linkPath= (Link.filemap) ? Link.filemap[linkTo.memberOf] : |
| 132 | escape(linkTo.memberOf) || "_global_"; |
| 133 | linkPath += publish.conf.ext + "#" + Link.symbolNameToLinkName(linkTo); |
| 134 | } |
| 135 | else { |
| 136 | linkPath = (Link.filemap)? Link.filemap[linkTo.alias] : escape(linkTo.alias); |
| 137 | linkPath += publish.conf.ext;// + (this.classLink? "":"#" + Link.hashPrefix + "constructor"); |
| 138 | } |
| 139 | linkPath = linkBase + linkPath |
| 140 | } |
| 141 | |
| 142 | var linkText= this.text || alias; |
| 143 | |
| 144 | var link = {linkPath: linkPath, linkText: linkText, linkInner: (this.innerName? "#"+this.innerName : "")}; |
| 145 | |
| 146 | if (typeof JSDOC.PluginManager != "undefined") { |
| 147 | JSDOC.PluginManager.run("onSymbolLink", link); |
| 148 | } |
| 149 | |
| 150 | return "<a href=\""+link.linkPath+link.linkInner+"\""+target+">"+link.linkText+"</a>"; |
| 151 | } |
| 152 | |
| 153 | /** Create a link to a source file. */ |
| 154 | Link.prototype._makeSrcLink = function(srcFilePath) { |
| 155 | var target = (this.targetName)? " target=\""+this.targetName+"\"" : ""; |
| 156 | |
| 157 | // transform filepath into a filename |
| 158 | var srcFile = srcFilePath.replace(/\.\.?[\\\/]/g, "").replace(/[:\\\/]/g, "_"); |
| 159 | var outFilePath = Link.base + publish.conf.srcDir + srcFile + publish.conf.ext; |
| 160 | |
| 161 | if (!this.text) this.text = FilePath.fileName(srcFilePath); |
| 162 | return "<a href=\""+outFilePath+"\""+target+">"+this.text+"</a>"; |
| 163 | } |
| 164 | |
| 165 | /** Create a link to a source file. */ |
| 166 | Link.prototype._makeFileLink = function(filePath) { |
| 167 | var target = (this.targetName)? " target=\""+this.targetName+"\"" : ""; |
| 168 | |
| 169 | var outFilePath = Link.base + filePath; |
| 170 | |
| 171 | if (!this.text) this.text = filePath; |
| 172 | return "<a href=\""+outFilePath+"\""+target+">"+this.text+"</a>"; |
| 173 | } |