自訂標籤大致可以理解為一個HTML代碼塊,也可以指向一個HTML檔案。
在模板頁中,很多地方需要相同的內容,例如頁面的頭部、尾部等,這個時候,可以利用自訂標籤來定義HTML代碼,然後在模板頁中引用就可以了。
自訂標籤的格式如下:
{%@ pageTop%} 頁面頂部的頂部的自訂標籤;
自訂標籤中的HTML代碼,我用了XML來記錄,當然也有可視化的編輯介面來操作,在這裡就不再綴述,只是說明一下原理即可,XML如下:
<Tag Uid="5895643"> <Name>PageTop</Name> <Type>page</Type> <Intro><![CDATA[所有頁面的頂部]]></Intro> <Page>PageTop.html</Page> <Context><![CDATA[<div id="pagetop">logo 資訊化動力核心 </div><div></div>]]></Context> </Tag>
上述代碼,標明了自訂標籤的唯一標識、名稱、類型(指定某個頁面)、介紹、html檔案名稱、html代碼塊;
說到這裡就簡單了,我們只需要把自訂標籤從模板頁中檢索出來,替換成HTML代碼就行了。代碼如下:
/// <summary> /// 將模板內容中的所有自訂標籤,替換為實際的值 /// </summary> /// <param name="html">要處理的模板對象</param> /// <returns></returns> public string Transact(TemplateBuider.PageTransact.Html html) { string patt = @"{%@\s*(\S[^\s%]+)\s*%}"; Regex rex = new Regex(patt, RegexOptions.Singleline); MatchCollection mc = rex.Matches(html.HtmlContext); for (int i = 0; i < mc.Count; i++) { Match ma = mc[i]; string key = ma.Groups[1].Value; //擷取當前標籤對象 Song.TemplateManager.Tags.CustomTag custom = _tags.Find(delegate(TemplateManager.Tags.CustomTag p) { return p.Name.ToLower() == key.ToLower(); }); if (custom == null) continue; //轉換自訂標籤中的路徑,使之與當前模板頁為相對路徑 string context =_replacePath(html, custom); //將自訂標籤合并到的模板 html.HtmlContext = Microsoft.VisualBasic.Strings.Replace(html.HtmlContext, ma.Value, context, 1, -1, Microsoft.VisualBasic.CompareMethod.Text); } return html.HtmlContext; }
上述代碼有一些是我系統中相關方法,大家不必關注,關鍵是Regex。
可這個時候,有個問題。各個模板頁並不在一個檔案夾下,路徑各不相同,自訂標籤中的HTML如果有超連結,在模板頁引用後,如果只是簡單的替換,這些超連結就有可能出錯,找不到指定的內容。所以,我們必須將自訂標籤中的連結化物件轉換成當前模板的路徑名。
思路是這樣的,首先找出自訂標籤的路徑,如果自訂標籤是指向HTML檔案的,則按Html路徑;如果是純HTML代碼,則以當前模板庫的路徑為路徑;自訂標籤中的所有連結,包括超連結、CSS引用、Js引用、iframe等,轉換為相對於自訂標籤路徑的路徑;然後找當前模板頁的路徑;將所有的連結轉換為當前模板頁的路徑。
這中間牽涉到一個演算法,從A檔案到B檔案的相對路徑。舉例說:A檔案在/3/4/5/q/w/a.html,B檔案在/3/4/6/s/b.html,如果A檔案中寫一個超連結引用B檔案,這個超連結怎麼寫?
我這裡寫了一個方法。計算兩個檔案的相對路徑,代碼如下:
/// <summary> /// 計算兩個檔案的相對路徑 /// </summary> /// <param name="baseFile">用於參照的檔案,就從當前檔案開始尋找另一個檔案</param> /// <param name="targetFile">目標檔案,就是求它的相對路徑</param> /// <returns>返回targetFile相對於baseFile的相對路徑</returns> private string _getRelativePath(string baseFile, string targetFile) { baseFile = baseFile.Replace("\\", "/"); baseFile = baseFile.ToLower(); targetFile = targetFile.ToLower(); // while (baseFile.IndexOf("/") > -1 && targetFile.IndexOf("/") > -1) { string b = baseFile.Substring(0, baseFile.IndexOf("/")); string t = targetFile.Substring(0, targetFile.IndexOf("/")); if (b != t) break; baseFile = baseFile.Substring(baseFile.IndexOf("/") + 1); targetFile = targetFile.Substring(targetFile.IndexOf("/") + 1); } string path = ""; while (baseFile.IndexOf("/") > -1) { baseFile = baseFile.Substring(baseFile.IndexOf("/") + 1); path += "../"; } return path + targetFile; }
有了上面的方法,就好處理了,真正的超連結轉換,就是正則匹配處理了。方法如下:
/// <summary> /// 將模板頁中的路徑處理成相對於當前模板頁的路徑 /// </summary> /// <param name="html"></param> /// <param name="tag"></param> /// <returns></returns> private string _replacePath(TemplateBuider.PageTransact.Html html, TemplateManager.Tags.CustomTag tag) { string context = tag.Context; //處理自訂標籤中的超連結,使其相對於當前檔案路徑 string cutomPath = _cutomPath(tag); //將超連結處理為相對於模板頁的路徑 string linkExpr = @"(?<=\s+)(?<key>href|src|action|background[^=""']*)=([""'])?(?<value>[^'"">]*)\1?"; Regex regex = new Regex(linkExpr, RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); MatchCollection mc = regex.Matches(tag.Context); foreach (Match m in mc) { string link = m.Groups["value"].Value.Trim(); if (link == "") continue; //外網連結不處理,如Http://開頭的超連結 if (new Regex(@"[a-zA-z]+://[^\s]*", RegexOptions.Singleline).IsMatch(link)) continue; //根路徑不處理,如/manage/index.aspx,第一個字元是/ if (new Regex(@"^\/\w+.").IsMatch(link)) continue; //如果是參數標籤,則不處理 if (new Regex(@"^{.+").IsMatch(link)) continue; //將超連結轉換為相對於靜態化目錄的路徑 link = _getCutomLinkPath(cutomPath, link); //將超連結轉換為基於當前模板頁的相對路徑 link = _getRelativePath(html.TagetFile, link); link = m.Groups[2].Value + "=\"" + link + "\""; context = context.Replace(m.Value, link); } return context; }
在上述代碼中,根路徑不處理、站外連結不處理、以{開頭的連結不處理。
總結
我的這個自訂標籤功能並不強,雖然自訂標籤中也可以有其它組件,但其本質未變,只是HTML代碼塊的替換。本來想寫帶參數的自訂標籤呢,這樣就更類似於asp.net的使用者控制項,精力有限,暫時先這樣吧。