1 /**
   2  * @class
   3  * LinkクラスはシンボルやファイルへのHTMLリンク(<a>タグ)を作成する機能を提供します。 <br>
   4  * Linkオブジェクトのメソッドのほとんどは自分自身への参照を返すため、以下のようにリンクの設定処理をチェーンした後、
   5  * 最終的に[[toString]]メソッドでタグ文字列を出力するのが標準的な使用方法です。
   6  * <div class="example_in_desc" >
   7  * new Link().toSymbol("MySymbol").withText("マイシンボル").target("_blank").toString();
   8  * </div>
   9  * 上の処理は次のようなHTMLテキストを出力します。(シンボルの出力先設定は標準テンプレートと同一と仮定)
  10  * <pre class="example_in_desc" >
  11  * <a href="./symbols/MySymbol.html" target="_blank" >マイシンボル</a>
  12  * </pre>
  13  * <b>注意:</b>
  14  * Linkオブジェクトを正常に動作させるには、テンプレートのpublish関数にconfプロパティが存在していなくてはなりません。
  15  * <p class="org_comment">
  16  * Handle the creation of HTML links to documented symbols.
  17  * </p>
  18  */
  19 function Link() {
  20     this.alias = "";
  21     this.src = "";
  22     this.file = "";
  23     this.text = "";
  24     this.innerName = "";
  25     this.classLink = false;
  26     this.targetName = "";
  27     
  28     /**
  29      * <a>タグのtarget属性の値を設定します。
  30      * @param {String} targetName target属性値
  31      * @return {Link} このオブジェクトへの参照
  32      */
  33     this.target = function(targetName) {
  34         if (defined(targetName)) this.targetName = targetName;
  35         return this;
  36     }
  37     
  38     /**
  39      * ページ内リンクのアンカー名を設定します。
  40      * @param {String} inner アンカー名
  41      * @return {Link} このオブジェクトへの参照
  42      */
  43     this.inner = function(inner) {
  44         if (defined(inner)) this.innerName = inner;
  45         return this;
  46     }
  47     
  48     /**
  49      * リンクテキストを設定します。
  50      * この指定を行わなかった場合、状況に応じてシンボルエイリアスやファイルパスが出力されます。
  51      * @param {String} text リンクテキスト
  52      * @return {Link} このオブジェクトへの参照
  53      */
  54     this.withText = function(text) {
  55         if (defined(text)) this.text = text;
  56         return this;
  57     }
  58     
  59     /**
  60      * リンク先としてソースファイル名を設定します。これはハイライトされたソースコードファイルへのリンクとなります。
  61      * @param {String} filename ソースファイル名
  62      * @return {Link} このオブジェクトへの参照
  63      */
  64     this.toSrc = function(filename) {
  65         if (defined(filename)) this.src = filename;
  66         return this;
  67     }
  68     
  69     /**
  70      * リンク先としてシンボルのエイリアスを設定します。
  71      * @param {String} alias シンボルのエイリアス
  72      * @return {Link} このオブジェクトへの参照
  73      */
  74     this.toSymbol = function(alias) {
  75         if (defined(alias)) this.alias = new String(alias);
  76         return this;
  77     }
  78     
  79     /**
  80      * リンク先としてクラスシンボルのエイリアスを設定します。<br>
  81      * ※現行バージョンではこのメソッドと[[toSymbol]]メソッドの動作に違いはありません。
  82      * @param {String} alias クラスシンボルのエイリアス
  83      * @return {Link} このオブジェクトへの参照
  84      */
  85     this.toClass = function(alias) {
  86         this.classLink = true;
  87         return this.toSymbol(alias);
  88     }
  89     
  90     /**
  91      * リンク先としてファイルパスを設定します。
  92      * @param {String} file [[Link.base]]プロパティの位置を基準としたファイルの相対パス
  93      * @return {Link} このオブジェクトへの参照
  94      */
  95     this.toFile = function(file) {
  96         if (defined(file)) this.file = file;
  97         return this;
  98     }
  99     
 100     /**
 101      * オブジェクトの文字列表現として、現在の設定に基づいて作成された<a>タグのHTMLテキストを返します。
 102      * @return {String} <a>タグのHTMLテキスト
 103      */
 104     this.toString = function() {
 105         var linkString;
 106         var thisLink = this;
 107 
 108         if (this.alias) {
 109             linkString = this.alias.replace(/(^|[^a-z$0-9_#.:^-])([|a-z$0-9_#.:^-]+)($|[^a-z$0-9_#.:^-])/i,
 110                 function(match, prematch, symbolName, postmatch) {
 111                     var symbolNames = symbolName.split("|");
 112                     var links = [];
 113                     for (var i = 0, l = symbolNames.length; i < l; i++) {
 114                         thisLink.alias = symbolNames[i];
 115                         links.push(thisLink._makeSymbolLink(symbolNames[i]));
 116                     }
 117                     return prematch+links.join("|")+postmatch;
 118                 }
 119             );
 120         }
 121         else if (this.src) {
 122             linkString = thisLink._makeSrcLink(this.src);
 123         }
 124         else if (this.file) {
 125             linkString = thisLink._makeFileLink(this.file);
 126         }
 127 
 128         return linkString;
 129     }
 130 }
 131 
 132 /**
 133  * [[Link.symbolNameToLinkName]]メソッドでシンボル名からページ内リンクのアンカー名を作成する際、
 134  * アンカー名の先頭に付加されるプリフィックス
 135  * <p class="org_comment" >prefixed for hashes</p>
 136  * @type String
 137  */
 138 Link.hashPrefix = "";
 139 
 140 /**
 141  * 作成されるリンクの、ドキュメント出力ディレクトリとの相対位置を示すパス文字列("../"など)
 142  * <p class="org_comment" >Appended to the front of relative link paths.</p>
 143  * @type String
 144  */
 145 Link.base = "";
 146 
 147 /**
 148  * @name Link.symbolSet
 149  * @desc
 150  * シンボルセット。ネームパスからシンボルを取得するために使用します。
 151  * Linkクラスをテンプレートから使用する場合、publish()関数が受け取った[[JSDOC.SymbolSet]]オブジェクトを
 152  * 事前にこのプロパティに設定しておく必要があります。
 153  * @field @type JSDOC.SymbolSet
 154  */
 155 
 156 /**
 157  * クラスシンボルから、そのシンボルを表すリンクアンカー名を作成します。
 158  * @param {JSDOC.Symbol} symbol クラスシンボル
 159  * @return {String} リンクアンカー名
 160  */
 161 Link.symbolNameToLinkName = function(symbol) {
 162     var linker = "";
 163     if (symbol.isStatic) linker = ".";
 164     else if (symbol.isInner) linker = "-";
 165     
 166     return Link.hashPrefix+linker+symbol.name;
 167 }
 168 
 169 /** Create a link to another symbol. */
 170 Link.prototype._makeSymbolLink = function(alias) {
 171     var linkBase = Link.base+publish.conf.symbolsDir;
 172     var linkTo = Link.symbolSet.getSymbol(alias);
 173     var linkPath;
 174     var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
 175 
 176     // is it an internal link?
 177     if (alias.charAt(0) == "#") var linkPath = alias;
 178     
 179     // if there is no symbol by that name just return the name unaltered
 180     else if (!linkTo) return this.text || alias;
 181     
 182     // it's a symbol in another file
 183     else {
 184         if (!linkTo.is("CONSTRUCTOR") && !linkTo.isNamespace) { // it's a method or property
 185             if (linkTo.isEvent) {
 186                 linkPath = 
 187                     (Link.filemap)? Link.filemap[linkTo.memberOf] 
 188                     :
 189                     escape(linkTo.memberOf) || "_global_";
 190                 linkPath += publish.conf.ext + "#event:" + Link.symbolNameToLinkName(linkTo);
 191             }
 192             else {
 193                 linkPath = 
 194                     (Link.filemap)? Link.filemap[linkTo.memberOf] 
 195                     :
 196                     escape(linkTo.memberOf) || "_global_";
 197                 linkPath += publish.conf.ext + "#" + Link.symbolNameToLinkName(linkTo);
 198             }
 199         }
 200         else {
 201             linkPath = (Link.filemap)? Link.filemap[linkTo.alias] : escape(linkTo.alias);
 202             linkPath += publish.conf.ext;// + (this.classLink? "":"#" + Link.hashPrefix + "constructor");
 203         }
 204         linkPath = linkBase + linkPath
 205     }
 206     
 207     var linkText = this.text || alias;
 208     
 209     var link = {linkPath: linkPath, linkText: linkText, linkInner: (this.innerName? "#"+this.innerName : "")};
 210     
 211     if (typeof JSDOC.PluginManager != "undefined") {
 212         JSDOC.PluginManager.run("onSymbolLink", link);
 213     }
 214     
 215     return "<a href=\""+link.linkPath+link.linkInner+"\""+target+">"+link.linkText+"</a>";
 216 }
 217 
 218 /** Create a link to a source file. */
 219 Link.prototype._makeSrcLink = function(srcFilePath) {
 220     var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
 221         
 222     // transform filepath into a filename
 223     var srcFile = srcFilePath.replace(/\.\.?[\\\/]/g, "").replace(/[:\\\/]/g, "_");
 224     var outFilePath = Link.base + publish.conf.srcDir + srcFile + publish.conf.ext;
 225 
 226     if (!this.text) this.text = FilePath.fileName(srcFilePath);
 227     return "<a href=\""+outFilePath+"\""+target+">"+this.text+"</a>";
 228 }
 229 
 230 /** Create a link to a source file. */
 231 Link.prototype._makeFileLink = function(filePath) {
 232     var target = (this.targetName)? " target=\""+this.targetName+"\"" : "";
 233         
 234     var outFilePath =  Link.base + filePath;
 235 
 236     if (!this.text) this.text = filePath;
 237     return "<a href=\""+outFilePath+"\""+target+">"+this.text+"</a>";
 238 }