ASP.NET MVC - HTML Editor | HTML編輯器 與 DatePicker | 日期選擇器

HTML Editor and DatePicker 總結

在開發網頁的過程式,除了前端 UI 之外,與使用者的互動,不外乎是「表單( Form )」(註1),一個好的表單設計,能讓你的網站加分。前面所談的使用者,包含一般前端網頁瀏覽使用者,與後台管理者,但對於表單的需求,他們兩者可都是一樣,希望更簡單、更方便、更好用。

註1:現在網頁外掛程式很多,可以直接在網頁上與使用者互動,已經不一定是透過表單,但就比例上而言,還是以表單為主,其他方式為輔。

以上六篇,討論在 ASP.NET MVC 中兩個關於表單的重點,HTML Editor ( HTML編輯器) 與 DatePicker (日期選擇器),這兩個算是在表單中很重要,也很常使用的功能。

HTML Editor(HTML編輯器) 介紹:

DatePicker(日期選擇器) 介紹:
每一套都有自己的優點,經過介紹之後,你可以選擇一套合適你自己的來使用。

例如說,我希望HTML Editor可以"簡單"、"小"、"快",那我會推薦使用 CLEditor。希望功能強大,TinyMCE 與 CKEditor 都不錯。希望免費又強大,那就是 TinyMCE 了。DatePicker 也是一樣的道理。

另外偷偷跟你說,實作部分,除非像 TinyMCE 直接跟你說 for MVC,不然,同理可證,你也可以拿去在 ASP.NET 上使用,也就是說,後面的六篇文章,有五篇可以在 ASP.NET 上使用,減一篇 ASP.NET + CKEditor + CKFinder 很早以前就介紹過了,所以有四篇新內容是與 ASP.NET 通用,爽吧!

HTML Editor and DatePicker 實作環境

  • Visual Studio 2010
  • ASP.NET MVC 3
以上是我的實作環境。基本上,就算使用 Visual Studio 11 + ASP.NET MVC 4,或比較舊的 Visual Studio 2008 + ASP.NET MVC 2 也沒關係,因為不管是 HTML Editor(HTML編輯器) 或 DatePicker(日期選擇器) 都與 Script 關係比較大,我是建議最少還是 Visual Studio 2010 + ASP.NET MVC 3會比較好,因為有些必須使用到 NuGet,在 Visual Studio 2008 下就有些問題了。

ASP.NET MVC HTML Editor and DatePicker 範例程式專案




檔案大小 5,218 KB (7z),解壓縮密碼:KKBruce。

HiCloud CaaS 雲端服務與管理介紹

前言

有時,理論是比較簡單,實作反而是困難。HiCloud CaaS這個服務我是一開始推出就使用,我個人是很支持國貨的,雖然有人會拿HiCloud CaaS與國外相似服務比較,但我覺得沒有意義!從國家、文化、市場規模、成本、技術能力、基礎建設…沒有一樣可以是「等號」,不是等號的東西怎麼比,拿 iPhone vs HTC vs ... 沒意義,了不起比比規格,因為規格可以用「大於、小於、等於」來比。

回理論與實作問題上。在HiCloud CaaS剛推出時,它們在Run!PC刊一篇文章,注意日期是2010/6/8日,我馬上寫信去問,什麼時間可以用,他們心虛的回答:應該是年底!過了一年,什麼東西都還沒看到,雖然等了很久,在2011/9月底,通知會員可以登入使用。

HiCloud CaaS 雲端服務與管理介紹

這一個「HiCloud CaaS 雲端服務與管理」是一個線上平台,你能透過它去申請異動查詢整個HiCloud Caas服務

圖一:HiCloud CaaS User Portal 首頁(點擊看大圖)
主畫面主要有功能列、新申請服務,查詢現有申裝情況、操作手冊等,我們按「虛擬主機申請」,

圖二:虛擬主機申請(一) 契約條款
圖三:虛擬主機申請(二) 選擇產品
目前除了日租,還有小時租,以小時為單位,每日最高收取日租費用,一般是超過4小時。臨時、短時間的要測試主機,還真是不錯選擇。申請過後,約15分鐘內就會開好虛擬機,可以使用。

圖四:虛擬主機相關訊息與開關機
這讓我很清楚目前整個虛擬主機的情況,還可以線上直接關機、重開機,很方便。

圖五、防火牆申請與設定
之前,我們要設定防火牆,必須打電話給機房端,由機房端設定,我們在測試是否正確,不過。現在,可以直接線上設定,實在方便許多,而且就算設錯了,趕快登入 User Portal 修改規則即可,不用電話來電話去。

圖六:負載平衡申請與設定
如果你提供的服務流量很大,除了選擇申請更高階虛擬主機外,負載平衡服務也是可以考慮,例如,租用二台以上基礎型雲端伺服器,加上一個負載平衡服務,也是個很不錯的選擇。尤其是HiCloud CaaS的租用方案都不合你胃口時。另一個考慮點,外面一台負載平衡伺服器都幾十萬上下,以日租金來看,成本效益算不錯。

圖七:儲存空間申請與設定
剛開始,反正什麼服務都要寫申請書,不然就是要打電話,現在真的方便很多。而且選擇也多了些,我們之前只能外掛100GB HDD,而且只能一顆,現在可以外掛2顆HDD,而且最大為 100GB * 20 = 2000 GB = 2 TB * 2顆 = 4 TB。一般而言,這樣的空間應該很夠用了。

圖八:HiCloud CaaS 加值服務申請
Cloud監控服務,應該就是Run!PC那篇文章所指的CPU、RAM的即時狀況,可參考http://hicloud.hinet.net/userportal.html網頁。

上網包xxxGB,我看不懂,寫信去問,沒人理我!@_@

HiCloud CaaS使用心得

從一開始的申請使用,到目前已經超過一年的時間,我喜歡HiCloud Caas的彈性,彈性是指,反正租你用,你只要不犯法,他們都不管你。但它們的CPU和RAM分配上,一直是固定方案,然後從一開始3個方案選擇,到現在有8個方案選擇,但未能像其他功能,可以讓我們隨選增減CPU或RAM,我反應過數次,可能是技術上有難處,但希望理論與實作都能看到,這樣就更進於完美。

Tool - Microsoft Exchange Server Best Practices Analyzer 工具

Microsoft Exchange Server Best Practices Analyzer 工具

Exchange Server Best Practices Analyzer 會以程式設計的方式,來從像是 Active Directory、登錄、Metabase 及效能監視器等資料儲存機制收集設定及數值。一旦收集完成,則會將一組全面的「最佳作法」規則套用至拓撲上。 執行此工具的系統管理員將會收到詳細資料報告,其中列出可對環境作出的建議,以達到更高效能、延展性及存留時間。
如果你有在管理 Exchange Server,那一定要使用這一套工具。他會去Scan你的Exchange Server,然找後出有問題的地方,還會直接給"建議"及"參考資料"。讓我們在管理、維護、效能…有很大的幫助。

記錄一下下載位置。

ASP.NET MVC - DatePicker 日期選擇器 | (3) JSCal2

JSCal2 簡介

這一套 JSCal 已經出道很久了,我第一次在 ASP 程式的 Calendar 就是使用 JSCal 版,還不是 JSCal2,不過後來轉為付費版本且免費資源越來越多的情況下,就換其他選擇了。但今天我們還是選擇介紹它,因為我想透過 JSCal2 介紹一個 ASP.NET MVC 裡的功能,這個功能常讓人家誤解。

先把 Model、Controller \ Action、View 建立起來。

Model:BlogPostJSCal2.vb

Imports System.ComponentModel.DataAnnotations

''' <summary>
''' Blog 發佈文章類別
''' </summary>
Public Class BlogPostJSCal2
    ' 標題
    Public Property Title() As String
    ' 發佈日期
    <DataType(DataType.Date)>
    Public Property PostedOn() As DateTime
    ' 標籤
    Public Property Tags() As String
    ' 內容
    Public Property Content() As String
End Class

Controller \ Action:BlogPostJSCal2Controller.vb

Namespace Mvc3HTMLEditorAndDatePicker
    Public Class BlogPostJSCal2Controller
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /BlogPostJSCal2/Create

        Function Create() As ActionResult
            Return View()
        End Function

        <HttpPost(), ActionName("Create")>
        Function Create_Post(model As BlogPostMy97) As ActionResult
            ViewBag.HtmlContent = model.Content
            Return View(model)
        End Function

    End Class
End Namespace

View:Create.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Mvc3HTMLEditorAndDatePicker.BlogPostJSCal2)" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    KKBruce : DatePicker (使用 JSCal2) 日期選擇器測試
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>DatePicker (使用 JSCal2) 日期選擇器測試</h2>

<%-- The following line works around an ASP.NET compiler warning --%>
<%: "" %>

<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>

<% Using Html.BeginForm() %>
    <%: Html.ValidationSummary(True) %>
    <fieldset>
        <legend>JSCal2 DatePicker</legend>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Title) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Title) %>
            <%: Html.ValidationMessageFor(Function(model) model.Title) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.PostedOn) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.PostedOn) %>
            <%: Html.ValidationMessageFor(Function(model) model.PostedOn) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Tags) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Tags) %>
            <%: Html.ValidationMessageFor(Function(model) model.Tags) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Content) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Content) %>
            <%: Html.ValidationMessageFor(Function(model) model.Content) %>
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
<% End Using %>

<div>
    <%: Html.ActionLink("Back to List", "Index") %>
</div>

</asp:Content>

讓我們來使用 JSCal2 吧。

Windows - Windows Server 2008 R2修改DNS記錄更新時間

發現Windows Server 2008 R2裡DNS服務中,預設更新時間設定不是很符合RFC規定,例如:

Windows Server 2008 R2 DNS服務相關預設時間
圖一:Windows Server 2008 R2 DNS服務相關預設時間
原DNS預設時間

Refresh:15 min
Retry:10 min
Expire:1 day
Default TTL:1 hr

以上預設值,可能是以"Intranet"的DNS為目標,我沒有深入查詢。因為我是要放在"Internet"上,所以我修改建議值為:

Refresh:12 hr
Retry:2 hr
Expire:28 days
Default TTL:1 day

換算單位為秒:

Refresh:43200
Retry:7200
Expire:2419200
Default TTL:86400

以上只是個記筆。

ASP.NET MVC - DatePicker 日期選擇器 | (2) My97DatePicker

My97DatePicker


這一套 My97DatePicker 是由對岸所發展,是一套約 60 KB 的 JavaScript,可以說,把 DatePicker 功能發展到非常「高級」的境界,而不是你所想像,跳出一個日曆,讓你選選點點。

首先,一樣,我們先準備我們的 Model、Controller \ Action、View。

Model:BlogPostMy97.vb

Imports System.ComponentModel.DataAnnotations
''' <summary>
''' Blog 發佈文章類別
''' </summary>
Public Class BlogPostMy97
    ' 標題
    Public Property Title() As String
    ' 發佈日期
    <DataType(DataType.Date)>
    Public Property PostedOn() As DateTime
    ' 標籤
    Public Property Tags() As String
    ' 內容
    Public Property Content() As String
End Class

Controller \ Action:BlogPostMy97Controller.vb

Namespace Mvc3HTMLEditorAndDatePicker
    Public Class BlogPostMy97Controller
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /BlogPostMy97/Create

        Function Create() As ActionResult
            Return View()
        End Function

        <HttpPost(), ActionName("Create")>
        Function Create_Post(model As BlogPostMy97) As ActionResult
            ViewBag.HtmlContent = model.Content
            Return View(model)
        End Function

    End Class
End Namespace

View:Create.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Mvc3HTMLEditorAndDatePicker.BlogPostMy97)" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    KKBruce : DatePicker (使用My97 Datepicker) 日期選擇器測試
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>DatePicker (使用My97 Datepicker) 日期選擇器測試</h2>

<%-- The following line works around an ASP.NET compiler warning --%>
<%: "" %>

<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>

<% Using Html.BeginForm() %>
    <%: Html.ValidationSummary(True) %>
    <fieldset>
        <legend>My97 Datepicker</legend>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Title) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Title) %>
            <%: Html.ValidationMessageFor(Function(model) model.Title) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.PostedOn) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.PostedOn) %>
            <%: Html.ValidationMessageFor(Function(model) model.PostedOn) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Tags) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Tags) %>
            <%: Html.ValidationMessageFor(Function(model) model.Tags) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Content) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Content) %>
            <%: Html.ValidationMessageFor(Function(model) model.Content) %>
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
<% End Using %>

<div>
    <%: Html.ActionLink("回首頁", "Index", "Home") %>
</div>

</asp:Content>

準備好,我們就開始來使用 My97 Datepicker 吧。

ASP.NET MVC - 凡走過,必留下痕跡之Log記錄

Log記錄 - 前言

我們之前已經寫過數篇討論檔案上傳下載的文章,如「檔案上傳下載筆記」,不過還有加強的空間,例如今天的主題「Log記錄」。

Log問題,在以前,可大可小,例如,我們寫網頁,可以什麼都不做,IIS還是會幫我們留一些記錄,我們還可以使用超強大Log流量分析軟體 AWStats來分析產出美美的報表。

但今日,個人資料處理法已經快要正式立法完成,我們必須讓走過我們的系統的使用者都留下Log記錄,以防日後舉證之需要。但不經整理的IIS Log記錄,法官應該是不可能接受,一是太大,二是太亂。類似 AWStats 的Log流量分析軟體,整理後的資訊,對於法律實際上效用有限,所以我們必須除了IIS Log記錄外,最好還能提供整個使用者的Log記錄。

Log記錄的方式

要留下Log記錄,基本方向可以有:
  1. System Log記錄
  2. File Log記錄
  3. Database Log記錄
  4. Log Server
System Log記錄指的是像IIS Log記錄就是System Log記錄。或撰寫程式碼,向Windows本身的事件檢視器來發送Log記錄。

File Log記錄指的是透過 System.IO 寫一支程式,將你要的Log記錄到一個純文字檔,例如, FileLogs.txt,因為程式自己寫,你能控制什麼是你要,什麼是你不要,與System Log記錄相比,可以免除太大、太亂的問題。檔案太大還是有可能,這部分可能要寫程式自動處理,或是手動處理。

Database Log記錄,即將Log記錄直接記錄在Database之中,與System Log記錄與File Log記錄相較,Database本來就是儲存與管理資料所在地,而且與撰寫發送Windows Log記錄事件檢視器程式碼和File Log記錄程式碼來看,資料庫 Log記錄程式碼簡單多了。。

Log Server一般而言,是當伺服器群成長到一定數量,會架一台專門處理Log記錄的硬體伺服器,例如,Linux平台的Syslog,Windows下的Kiwi Syslog Server來架設,或是買專門機架硬體式Log Server,來統一收集、分析、管理這些Server Log記錄。

真是可大可小。

以下,我會使用 ASP.NET MVC來實作一個Database Log記錄範例。

ASP.NET MVC - DatePicker 日期選擇器 | (1) jQuery UI : DatePicker

jQuery UI : DatePicker

表單第四篇,我們來終於來到日期選擇器(DataPicker)。

在表單上最容易產生的錯誤就是「輸入錯誤」,例如,我們網站裡的客戶註冊資料,明明就已經設計電子郵件必須輸入兩次,而且兩次必須完全相同才能通過驗證,還是會有客戶發 Mail 來說:「對不起,我 Email 輸入錯誤,請幫我修正資料。」一整個 Orz!

所以能「勾勾、選選」是最好的選擇,最少能減低這類的「輸入錯誤」。其中「日期」的輸入通常都是很重要,註冊日期、購買日期、出貨日期、銷售日期、保固日期、發佈日期、LogOn-LogOut日期…等。

前面介紹的 HTML 編輯器,是美化你輸入的資料。有時"日期"就重要性而言,會比內容更為重要。我們希望減少日期的輸入錯誤,但一個字一個字打,日期的錯誤率就硬是比Email這類資料還高。

所以就出現「用選的」這類需求。就有人開發出 DatePicker,讓使用者可以很方便,用選的,幾年幾月幾日,一整個就是爽。不管是 UI、Usability … DatePicker 與 HTML Editor 都是加分部分。

前面準備一樣,Model、Controller \ Action、View。

Model:BlogPostjQueryUI.vb

Imports System.ComponentModel.DataAnnotations

''' <summary>
''' Blog 發佈文章類別
''' </summary>
Public Class BlogPostjQueryUI
    ' 標題
    Public Property Title() As String
    ' 發佈日期
    <DataType(DataType.Date)>
    Public Property PostedOn() As DateTime
    ' 標籤
    Public Property Tags() As String
    ' 內容
    Public Property Content() As String
End Class

Controller \ Action:BlogPostjQueryUIController.vb

Namespace Mvc3HTMLEditorAndDatePicker
    Public Class BlogPostjQueryUIController
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /BlogPostjQueryUI/Create

        Function Create() As ActionResult
            Return View()
        End Function

        <HttpPost(), ActionName("Create")>
        Function Create_Post(model As BlogPostjQueryUI) As ActionResult
            ViewBag.HtmlContent = model.Content
            Return View(model)
        End Function

    End Class
End Namespace

建置,用強型別新增View。

View:Create.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Mvc3HTMLEditorAndDatePicker.BlogPostjQueryUI)" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    KKBruce : DatePicker (使用jQuery UI) 日期選擇器測試
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>DatePicker (使用jQuery UI) 日期選擇器測試</h2>

<%-- The following line works around an ASP.NET compiler warning --%>
<%: "" %>

<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>

<% Using Html.BeginForm() %>
    <%: Html.ValidationSummary(True) %>
    <fieldset>
        <legend>jQuery UI 日期選擇器測試</legend>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Title) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Title) %>
            <%: Html.ValidationMessageFor(Function(model) model.Title) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.PostedOn) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.PostedOn) %>
            <%: Html.ValidationMessageFor(Function(model) model.PostedOn) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Tags) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Tags) %>
            <%: Html.ValidationMessageFor(Function(model) model.Tags) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Content) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Content) %>
            <%: Html.ValidationMessageFor(Function(model) model.Content) %>
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
<% End Using %>

<div>
    <%: Html.ActionLink("回首頁", "Index", "Home") %>
</div>

</asp:Content>

日期選擇器( DatePicker ) 第一個我們來介紹最簡易入門的 jQuery UI。

ASP.NET MVC - HTML 編輯器 | (3) CKEditor + CKFinder

CKEditor + CKFinder

最後一個 HTML 編輯器,我要介紹王牌 CKEditor

可能是因為版權的關係,我不清楚。目前沒有人把 CKEditor 整合到 NuGet 上。我們必須做一些事,才能在 ASP.NET MVC 上整合 CKEditor。

先下載 CKEditor 3.6.2

這裡請注意,請下載第一個 CKEditor Source 版本即可,不要下載 for xxx 版本,例如,CKEditor for ASP.NET 版本。

解壓縮後,把 ckeditor 目錄整個複製到專案的根目錄之下。

CKEditor 目錄位置
圖一:CKEditor 目錄位置
然後先一樣準備我們的 Model、Controller / Action 、View。

Model:BlogPostCKEditor.vb

''' <summary>
''' Blog 發表文章類別
''' </summary>
Public Class BlogPostCKEditor
    ' 標題
    Public Property Title() As String 
    ' 發佈日期
    Public Property PostedOn() As DateTime 
    ' 標籤
    Public Property Tags() As String 
    ' 內容
    
    Public Property Content() As String 
End Class

Controller / Action :BlogPostCKEditorController.vb

Namespace Mvc3HTMLEditorAndDatePicker
    Public Class BlogPostCKEditorController
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /BlogPostCKEditor/Create

        Function Create() As ActionResult 
            Return View()
        End Function

        <HttpPost(), ActionName("Create")>
        Function Create_Post(model As BlogPostCKEditor) As ActionResult
            ' 在充許 HTML 的欄位,切記必須使用 AntiXSS 4.0 過濾
            ViewBag.HtmlContent = Microsoft.Security.Application.Sanitizer.GetSafeHtmlFragment(model.Content)
            Return View(model)
        End Function

    End Class
End Namespace

先「建置」,用「強型別」產生 View。

View:Create.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Mvc3HTMLEditorAndDatePicker.BlogPostCKEditor)" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    KKBruce : CKEditor HTML編輯器測試
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>CKEditor HTML編輯器測試</h2>

<%-- The following line works around an ASP.NET compiler warning --%>
<%: "" %>

<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>

<% Using Html.BeginForm() %>
    <%: Html.ValidationSummary(True) %>
    <fieldset>
        <legend>CKEditor HTML編輯器測試</legend>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Title) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Title) %>
            <%: Html.ValidationMessageFor(Function(model) model.Title) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.PostedOn) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.PostedOn) %>
            <%: Html.ValidationMessageFor(Function(model) model.PostedOn) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Tags) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Tags) %>
            <%: Html.ValidationMessageFor(Function(model) model.Tags) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Content) %>
        </div>
        <div class="editor-field">
            <%: Html.TextAreaFor(Function(model) model.Content) %>
            <%: Html.ValidationMessageFor(Function(model) model.Content) %>
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
<% End Using %>

<p>
    發表內容(顯示HTML標籤):<%: ViewBag.HtmlContent %>
</p>
<p>
    發表內容(顯示HTML效果):<%= Html.Raw(ViewBag.HtmlContent) %>
</p>
<div>
    <%: Html.ActionLink("回首頁", "Index","Home") %>
</div>

</asp:Content>

注意,要把 Content 欄位的 EditorFor() 修改為 TextAreaFor(),接下來我們就來 ASP.NET MVC 裡使用 CKEditor 與 CKFinder 吧!

ASP.NET MVC - Visual Baisc QRCode擴充方法

以下範例程式修改於阿源哥哥「使用ASP.NET MVC 3產生二維條碼」一文,除將原文之C# Code轉換為Visual Basic Code,還進行
  • alt屬性定義為必填
  • 更完整的註解說明
以產生更符合 SEO 的 HTML img 標籤。相關說明都寫在程式註解中,就不在多做解釋。

QRCodeExtension.vb - Visual Baisc QRCode擴充方法


Imports System.Runtime.CompilerServices
Imports System.ComponentModel

Public Module QRCodeExtension

    ''' <summary>
    ''' 透過 Google 提供的 chart 服務,產生對應的 img QRCode標籤。
    ''' </summary>
    ''' <param name="helper">擴充方法</param>
    ''' <param name="data">要產生QRCode的字串</param>
    ''' <param name="alt">QRCode說明文字</param>
    ''' <param name="size">大小(px),預設 80px*80px</param>
    ''' <param name="margin">留白(px),預設 4px</param>
    ''' <param name="errorCorrectionLevel">回復等級,預設 L</param>
    ''' <param name="htmlAttributes">其他html屬性,預設 Nothing</param>
    ''' <returns>回傳 img 標籤</returns>
    <Extension()>
    Public Function QRCode(helper As HtmlHelper,
                           data As String,
                           alt As String,
                           Optional size As Integer = 80,
                           Optional margin As Integer = 4,
                           Optional errorCorrectionLevel As QRCodeErrorCorrectionLevel = QRCodeErrorCorrectionLevel.Low,
                           Optional htmlAttributes As Object = Nothing) As MvcHtmlString

        ' 錯誤處理
        If String.IsNullOrEmpty(data) Then
            Throw New ArgumentNullException("data", "不得空白.")
        End If

        If size < 1 Then
            Throw New ArgumentOutOfRangeException("size", size, "必須大於零.")
        End If

        If margin < 0 Then
            Throw New ArgumentOutOfRangeException("margin", margin, "必須大於或等於零.")
        End If

        If Not [Enum].IsDefined(GetType(QRCodeErrorCorrectionLevel), errorCorrectionLevel) Then
            ' 需 Imports System.ComponentModel 才會有 InvalidEnumArgumentException
            Throw New InvalidEnumArgumentException("errorCorrectionLevel",
                                                   CType(errorCorrectionLevel, Integer),
                                                   GetType(QRCodeErrorCorrectionLevel)
                                                   )
        End If

        ' 使用 google 的 chart 服務
        ' 參數參考:http://code.google.com/intl/zh-TW/apis/chart/infographics/docs/qr_codes.html
        Dim url As String = String.Format(
                                "http://chart.apis.google.com/chart?cht=qr&chld={2}|{3}&chs={0}x{0}&chl={1}",
                                size,
                                HttpUtility.UrlEncode(data),
                                errorCorrectionLevel.ToString()(0),
                                margin)

        ' 產生img Tag
        Dim tag As New TagBuilder("img")

        tag.MergeAttribute("src", url)
        tag.MergeAttribute("alt", alt)
        tag.MergeAttribute("title", alt)

        ' width, height 兩個屬性,可加可不加,不影響產出 QRCode 結果。
        tag.MergeAttribute("width", size.ToString())
        tag.MergeAttribute("height", size.ToString())

        If htmlAttributes IsNot Nothing Then
            tag.MergeAttributes(New RouteValueDictionary(htmlAttributes))
        End If

        Return New MvcHtmlString(tag.ToString(TagRenderMode.SelfClosing))
    End Function

End Module

''' <summary>
''' 回復等級列舉。
''' 參考:http://code.google.com/intl/zh-TW/apis/chart/infographics/docs/qr_codes.html 說明文件。
''' </summary>
Public Enum QRCodeErrorCorrectionLevel
    ''' <summary>
    ''' 從 7% 的錯誤資料中回復.
    ''' </summary>
    Low
    ''' <summary>
    ''' 從 15% 的錯誤資料中回復.
    ''' </summary>
    Medium
    ''' <summary>
    ''' 從 25% 的錯誤資料中回復.
    ''' </summary>
    QuiteGood
    ''' <summary>
    ''' 從 30% 的錯誤資料中回復.
    ''' </summary>
    High
End Enum

在要使用的 View 之中引用擴充方法 Namespace,即可使用。我們以 Home/Index 為例:

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="Mvc3QRCode.QRCodeExtension" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
    首頁
</asp:Content>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%: ViewData("Message") %></h2>
    <p>
    預設 80*80, margin = 4<br />
    <%: Html.QRCode("http://kkbruce.blogspot.com", "KKBruce部落格網址") %>
    </p>
    <p>
    修改為 150*150, margin = 1<br />
    <%: Html.QRCode("http://kkbruce.blogspot.com", "KKBruce部落格網址", size := 150, margin := 1) %>
    </p>
</asp:Content>

建置,Ctrl+F5,執行網頁。

Visual Basic - QRCode Extension 執行圖
圖一:Visual Basic - QRCode Extension 執行圖
有前人種樹真好。^_^

ASP.NET MVC - HTML 編輯器 | (2) CLEditor

CLEditor HTML 編輯器

第二個我們來介紹 CLEditor HTML 編輯器。

前面準備步驟與前一篇差不多,在同一專案下,先準備 Model 、Controller\Action、View,而且內容 90%以上相同。

Model:BlogPostCLEditor.vb

''' <summary>
''' Blog 發表文章類別
''' </summary>
Public Class BlogPostCLEditor
    ' 標題
    Public Property Title() As String 
    ' 發佈日期
    Public Property PostedOn() As DateTime 
    ' 標籤
    Public Property Tags() As String 
    ' 內容
    <AllowHtml()>
    Public Property Content() As String 
End Class

我們先設定Content() 的屬性 <AllowHtml()>。

Controller \ Action:BlogPostCLEditorController.vb

Namespace Mvc3HTMLEditorAndDatePicker
    Public Class BlogPostCLEditorController
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /BlogPostCLEditor/Create

        Function Create() As ActionResult 
            Return View()
        End Function

        <HttpPost(), ActionName("Create")>
        Function Create_Post(model As BlogPostCLEditor) As ActionResult
            ' 在充許 HTML 的欄位,切記必須使用 AntiXSS 4.0 過濾
            ViewBag.HtmlContent = Microsoft.Security.Application.Sanitizer.GetSafeHtmlFragment(model.Content)
            Return View(model)
        End Function

    End Class
End Namespace

先「建置」,以強型別來新增View。

View:Create.aspx

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Mvc3HTMLEditorAndDatePicker.BlogPostCLEditor)" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    KKBruce : CLEditor HTML編輯器測試
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>CLEditor HTML編輯器測試</h2>

<%-- The following line works around an ASP.NET compiler warning --%>
<%: "" %>

<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>

<% Using Html.BeginForm() %>
    <%: Html.ValidationSummary(True) %>
    <fieldset>
        <legend>CLEditor HTML編輯器測試</legend>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Title) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Title) %>
            <%: Html.ValidationMessageFor(Function(model) model.Title) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.PostedOn) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.PostedOn) %>
            <%: Html.ValidationMessageFor(Function(model) model.PostedOn) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Tags) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Tags) %>
            <%: Html.ValidationMessageFor(Function(model) model.Tags) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Content) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Content) %>
            <%: Html.ValidationMessageFor(Function(model) model.Content) %>
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
<% End Using %>

<p>
    發表內容(顯示HTML效果):<%: ViewBag.HtmlContent %>
</p>
<p>
    發表內容(顯示HTML效果):<%= Html.Raw(ViewBag.HtmlContent) %>
</p>
<div>
    <%: Html.ActionLink("回首頁", "Index", "Home") %>
</div>

</asp:Content>

接下來我們就可以來安裝及設定 CLEditor HTML 編輯器。

ASP.NET MVC - HTML 編輯器 | (1) TinyMCE

TinyMCE HTML 編輯器

第一個 HTML 編輯器,我們介紹 TinyMCE HTML 編輯器。

Step 1. 開一個全新 ASP.NET MVC 3 專案

檔案 → 新增專案 → Visual Basic → Web → ASP.NET MVC 3 Web 應用程式 → 名稱:「Mvc3HTMLEditorAndDatePicker」→ 網際網路應用程式 → 檢視引擎:「ASPX」。

未來六個範例,都會在此專案內增加進來。

Step 2. 準備一個 Model:BlogPostTinyMCE.vb

''' <summary>
''' Blog 發佈文章類別
''' </summary>
Public Class BlogPostTinyMCE
    ' 標題
    Public Property Title() As String 
    ' 發佈日期
    Public Property PostedOn() As DateTime 
    ' 標籤
    Public Property Tags() As String 
    ' 內容
    Public Property Content() As String 
End Class

以上內容很簡單,未來幾個範例,也會使用以上架構 Model 架構來進行。

Step 3. 建立一個 Controller:BlogPostTinyMCEController.vb

在「加入控制器」裡的「Scaffold 選項」→ 「範本」→ 選擇「空白控制器」。然後寫一個 Create() 的 Action 方法。

Namespace Mvc3HTMLEditorAndDatePicker
    Public Class BlogPostTinyMCEController
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /BlogPostTinyMCE/Create

        Function Create() As ActionResult
            Return View()
        End Function

        <HttpPost(), ActionName("Create")>
        Function Create_Post(model As BlogPostTinyMCE) As ActionResult
            Return View(model)
        End Function

    End Class
End Namespace

重點在第二個 <HttpPost(), ActionName("Create")> 方法中,參數要接收 BlogPostTinyMCE類別。然後新增我們 Create 的 View。

Step 4. 新增 Create Action 的 View

先建置專案 → Create → 加入檢視 → 勾選「建立強型別檢視」 → 選擇「 BlogPostTinyMCE」 → Scaffold 樣板:「Create」→ 完成。

完成後,我們簡單修改此 View。

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Mvc3HTMLEditorAndDatePicker.BlogPostTinyMCE)" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    KKBruce : TinyMCE HTML編輯器
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>TinyMCE HTML編輯器</h2>

<%-- The following line works around an ASP.NET compiler warning --%>
<%: "" %>

<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>

<% Using Html.BeginForm() %>
    <%: Html.ValidationSummary(True) %>
    <fieldset>
        <legend>TinyMCE HTML編輯器</legend>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Title) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Title) %>
            <%: Html.ValidationMessageFor(Function(model) model.Title) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.PostedOn) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.PostedOn) %>
            <%: Html.ValidationMessageFor(Function(model) model.PostedOn) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Tags) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Tags) %>
            <%: Html.ValidationMessageFor(Function(model) model.Tags) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(Function(model) model.Content) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(Function(model) model.Content) %>
            <%: Html.ValidationMessageFor(Function(model) model.Content) %>
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
<% End Using %>

<p>
    發表內容(顯示HTML標籤):<%: ViewBag.HtmlContent %>
</p>
<p>
    發表內容(顯示HTML效果):<%= Html.Raw(ViewBag.HtmlContent) %>
</p>
<div>
    <%: Html.ActionLink("回首頁", "Index","Home") %>
</div>

</asp:Content>

我們希望把含編輯後含HTML與實際HTML效果顯示出來,所以我們再最後加上幾行程式。我們把專案按「Ctrl + F5」執行起來,然後輸入「http://localhost:6944/BlogPostTinyMCE/Create」(記得改成你的 Port)的 Controller / Action。

預設表單
圖一:預設表單(點擊看大圖)
我們可以輸入資料,但都是純文字。

文字輸入
圖二:文字輸入
到目前為止,與一般表單沒有什麼不同。現在讓我們為此表單的 Content 加上 TinyMCE HTML編輯器吧。

Visual Studio - 提升Web與CSS開發的流暢度

Visual Studio 2010 與 Usability

老實說,從學習 .NET Framework 2.0 + ASP.NET 2.0 開炲,一直覺得 Visual Studio 2008 是一套很棒的開發工具。到了後來,升級使用了 Visual Studio 2010 之後,更覺得來到了一個不一樣的世界。Microsoft 應該有感覺到我們「程式開發人員」的辛苦,在 Visual Studio 2010 開發工具中有把「Usability」考慮進去。

雖然 Usability 是網頁開發的一個名詞,但其實所有與使用者有互動的內容,都應考慮 Usability,只是考慮多或少。像 Apple 的產品,使用起來只有一個字形容「順」!但,人家背後花了多少的心思在 Usability 之上,才能有今天成功?

回主題,Visual Studio 2010 與 Usability。

Visual Studio 2010 新發現系列文章中,我提到過「擴充組件庫」及介紹了幾套當時使用起來感覺很不錯的擴充組件。在「KKBruce的軟體清單」中,也重新整理過。

今天要來介紹一套讓你在開發Web和CSS更流暢、更省時間、更人性的擴充組件。

ASP.NET MVC - 即時整合與壓縮網站輸出的CSS及JavaScript檔案

讓網頁有高效率

不知道你有沒有為了
  • SEO
  • 安全性
  • 加速網頁載入
種種的理由,去進行過任何的CSS與JavaScript檔案壓縮的行為。在 YSlow 這套測試網頁執行率效軟體的首頁中,提供一篇「Web Performance Best Practices and Rules」文章,此篇文章針對網頁效率提供了 23 個非常棒建議(註1)
  1. 減少發出HTTP請求 (Minimize HTTP Requests)
  2. 使用內容分散式網路 (Use a Content Delivery Network (CDN))
  3. 避免空白的 src 或 href (Avoid empty src or href)
  4. 增加過期或Cache-Control標頭(add an Expires or a Cache-Control Header)
  5. 使用Gzip 組件(Gzip Components)
  6. 樣式表(CSS檔案)放在最頂端(Put StyleSheets at the Top)
  7. 腳本(JavaScript檔案)放在最底端(Put Scripts at the Bottom)
  8. 避免 CSS 表達式(Avoid CSS Expressions)
  9. 將 JavaScript 與 CSS 放在外部檔案(Make JavaScript and CSS External)
  10. 減少 DNS 查詢(Reduce DNS Lookups)
  11. 壓縮 JavaScript 與 CSS(Minify JavaScript and CSS)
  12. 避免重導向(Avoid Redirects)
  13. 刪除重複的腳本(JavaScript檔案)(Remove Duplicate Scripts)
  14. 設置 ETags(Configure ETags)
  15. 讓AJAX能快取(Make AJAX Cacheable)
  16. 在AJAX請求中使用 GET 方法(Use GET for AJAX Requests)
  17. 減少DOM元素數量(Reduce the Number of DOM Elements)
  18. 不要有HTTP 404錯誤(No 404s)
  19. 減少 Cookie 大小(Reduce Cookie Size)
  20. 為組件使用 Cookie-free 網域(Use Cookie-free Domains for Components)
  21. 避免過濾器(Avoid Filters)
  22. 不要在HTML裡使用縮放圖片(Do Not Scale Images in HTML)
  23. 讓favicon.ico圖小且能快取(Make favicon.ico Small and Cacheable)

註1:在查詢相關說明資料時發現,Web Performance Best Practices and Rules 文章內不只有 23 個建議,列在 YSlow 上的 23 個建議,應該重點中的重點了。有些看標題就能了解內容,有些則相反,必須去讀內容能才了解,例如,第 20 點的Cookie-free?非常建議花一些時間全部完整看完。如果你對網頁執行效率或是想改善你的網站/網頁,由此為出發點,絕對是個非常好的開始。另外,這篇 Web Performance Best Practices and Rules 文章和另一本 2007 出版的 High Performance Web Sites: Essential Knowledge for Front-End Engineers (中文:網頁加速的14條黃金法則)重複性很高,內容差不多,原因也很簡單,此書作者是曾任雅虎CPO(Chief Performance Officer),所以 Web Performance Best Practices and Rules 應該是此書的重點整理版本,作者還提供一個測試那 14 條規則的網站http://stevesouders.com/hpws/rules.php,也非常值的一玩。還有,Will保哥也針對 High Performance Web Sites 這本書提供了非常棒的經驗分享「加速前端網頁效能的14條規則」推薦非讀不可。

第2點 CDN 部分,一般網站如果不是成長到一定規模,是不太可能去使用,不過就 Scripts 部分,到是可以使用免費的 CDN 服務。可以參考我的「Microsoft Ajax content delivery network (CDN)」、「Microsoft ajax CDN Domain改名為aspnetcdn」、「jquery-1.4.4-vsdoc.js會讓jQuery無法運作」這幾篇。

借上面的項目,我想要討論的就是第11點「壓縮 JavaScript 與 CSS(Minify JavaScript and CSS)」。其實第9點「將 JavaScript 與 CSS 放在外部檔案(Make JavaScript and CSS External)」可以和第11點一起討論。

當網站開發到一定層度,我們會開始切割CSS檔案及JavaScript檔案以方便管理。

CSS檔案分割範例
例如,原本單一的CSS檔案,我們分割為 reset.css、layout.css、header.css、main.css、footer.css、ie6.css,依每個網站需求規劃而定。


JavaScript檔案分割範例
例如,原本單一的JavaScript檔案,我們分割為ajax.js、cookie.js、dom.js、forms.js、tables.js、validate.js、windows.js、ie6hack.js、ie7.js、ieall.js,依每個網站需求規劃而定。


也就是第9點所討論的內容。我認為,切割最重要的是「縮小問題點。例如,validate.js,是我常新增、修改、使用的一支 JavaScript,當使用者反應說"驗證"方便有問題時,我馬上知道要去找那一支程式查詢及修改,例如有一次使用者反應,為什麼程式在 Firefox 之下都不會有任何提示,而只有 IE 有?查詢之下,我使用到 IE 專用語法,結果所有 Firefox 都可以在不驗證情況下送出表單,嚇死我了。

網站上線之後

好了,通常那是上線開發之時,那上線之後呢?先看一個討論串「抄很大、抄不用錢」,線上之前。建議進行第11點「壓縮 JavaScript 與 CSS(Minify JavaScript and CSS)」,這方面的線上工具很多,然後進行上線動作。

回到討論串,因為那些 JavaScript 沒有經過壓縮,都是明碼,要了解及看懂內容對有功力的人,不是太難。以 JavaScript 而言,壓縮過程會順便重新編碼過,讓人看懂不容易。如果你辛苦寫了 JavaScript 或 jQuery 的程式碼,不想很容易讓人家複製使用,那請執行壓縮這一步驟。

你可以試著去看 jquery-version.js 與 jquery-version.min.js,兩個 JavaScript 檔案的內容。就會了解我說的意思。

但切割及壓縮CSS與JavaScript檔案容易造成一個問題:「管理不易。」

你可以很方便針對「專案」進行版本控制,但針對大量切割的CSS檔案與JavaScript檔案進行版本控制不太容易。另一重點是,我每次只對針對CSS或JavaScript檔案進行了修改,就必須「重新製作壓縮後的CSS與JavaScript檔案」,然後上傳、覆寫原有的CSS與JavaScript檔案。

或許你能學 jQuery,分為未壓縮 jquery-version.js 與壓縮 jquery-version.min.js,也是不錯,不過那一個 min 很好猜,有心人大概只要把 min 去除,就可以得到未壓縮的原始碼。

這方面我有二個想法:
  1. 讓未壓縮與壓縮檔案命名之間無關聯性,但這又造成「管理更加不易」。
  2. 讓未壓縮與壓縮檔案放在不同目錄之中,且那著目錄命名差異大或無關聯性。但還是一樣,易造成「管理更加不易」。

第二點,例如,jsUnZip與jsZip兩個目錄,關聯性及命名差異不大,也是很好猜。但如果改為kkbruceUUUjs(UUU代表unzip)與kkbruceZjs(Z代表zip)目錄,你只要不要在上線網頁引用kkbruceUUUjs目錄裡的 JavaScript 檔案,我想就很難猜得到。

還是麻煩呀,有沒有更好的辦法?

有,不然我前面廢話連篇做什麼?