:::
針對查詢「jquery」依關聯性排序顯示文章。依日期排序 顯示所有文章

輕鬆調整網頁元素!在Chrome的Console使用jQuery / How to include jQuery in Chrome's Console

布丁布丁吃布丁

輕鬆調整網頁元素!在Chrome的Console使用jQuery / How to include jQuery in Chrome's Console

image

Google Chrome (Google瀏覽器)的Console (主控台)不僅可以顯示來自JavaScript的錯誤訊息,其實我們也可以用它直接執行JavaScript程式碼。不過如果我們想要在Console使用JavaScript最流行的函式庫jQuery,那就必須執行一段JavaScript程式碼來引用jQuery囉。我參考「Include jQuery in the JavaScript Console」這篇的作法,跟大家分享如何在Google Chrome使用jQuery。

(more...)

yam天空部落-影音分享下載器(IE版)

布丁布丁吃布丁

9 Comments

yam天空部落-影音分享下載器(IE版)

image

大年初一,終於把yam天空部落影音下載器做調整了。

檔案下載(SkyDrive備份 ),請在本機電腦上用IE瀏覽器直接開啟使用吧。以下是原始碼:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>yam天空部落-影音分享下載器</title>
</head>
 
<body>
<script type="text/javascript" src="http://puddingchen.35.googlepages.com/jquery.js"></script> 
<h1 style="margin:0 auto 0 auto;width:400px;"><a href="http://www.yam.com/" target="yamBlog" style="float:left;"><img src="http://blog.yam.com/i/header/logo.gif" title="yam天空部落" border="0" /></a>影音分享<br />
下載器</h1>
<div id="yam-video-download"> 
<style type="text/css"> 
#yam-video-download div.tip {
    font-size: small;
    color:gray;
}
#yam-video-download div.check-flag {
     color:white;
     background-color:#FF0000; 
     font-size: large;
     padding: 2px 5px;
     margin: 2px auto;
     border: 1px solid gray; 
     width: 200px; 
     text-align:center; 
     display:none; 
} 
#yam-video-download #outputID_yvd div.content {
    font-size: 1em;
} 
#yam-video-download #outputID_yvd div.content span.deleter { 
    color:gray; 
    border: 1px solid gray; 
    /*background-color:gray;*/ 
    font-size: smaller; 
    width: 12px; 
    display:block; 
    float:left; 
    text-align:center; 
    padding: 0 2px; 
    /*font-weight:bold;*/ 
    font-family:Arial, Helvetica, sans-serif; 
    cursor:pointer; 
    margin-right: 0.5em; 
} 
#yam-video-download #outputID_yvd div.content a.downloader,
#yam-video-download #outputID_yvd div.content a.win, 
#yam-video-download #outputID_yvd div.content span.title-copy { 
    /*border: 1px solid gray; padding: 2px; font-size: small; background-color:gray; color:white; */
     text-decoration:underline; 
     color:#0099FF; 
     cursor:pointer;
     margin-right: 0.5em; 
} 
#yam-video-download #outputID_yvd div.content a.downloader {
    /*background-color:#0066FF;*/ 
} 
#yam-video-download #outputID_yvd div.content span.title-copy { 
    /* font-size: smaller; color:gray; border:1px solid gray; text-decoration:none; margin: 5px;*/ 
}
#yam-video-download #outputID_yvd div.content span.waiting { 
    margin-right: 0.5em; cursor:wait; 
} 
#yam-video-download #outputID_yvd div.content .mouseover { 
    color:blue !important; 
    text-decoration:none !important; 
} 
</style> 
<div class="tip">※本程式使用JavaScript跨網域取得資料的低安全性設定運作,<strong>Internet Explorer</strong>之外的瀏覽器可能無法使用。</div>
<form onsubmit="jQuery('#startParsing').click();return false;">
<script type="text/javascript"> 
if (!jQuery.browser.msie) 
    jQuery("#yam-video-download div.tip").css("color", "red"); 
</script> 
<label for="inputID_yvd" style="display:block;">
<p>請輸入<strong style="color:#0066CC">yam天空部落-影音分享</strong>的網址</p>
  </label>
 
<button type="button" style="font-size: 20pt;font-weight:bold;margin: 0 0.5em;height: 1.5em;" onclick="yamVideoParsing('inputID_yvd', 'outputID_yvd', 'check-flag')" id="startParsing">分析</button>
  <input id="inputID_yvd" type="text" style="font-size: 20pt;width: 80%;height: 1.5em;" value="http://mymedia.blog.yam.com/m/" onfocus="this.select()"onchange="jQuery(this).nextAll('button:first').click()" />
<p style="margin:0;padding:0;color:gray;">(以<strong>http://mymedia.blog.yam.com/m/</strong>或<strong>http://mymedia.yam.com/m/</strong>開頭)</p>
<div class="check-flag" style="display:none;">網址有錯!請檢查!</div>
<div id="outputID_yvd"></div>
 
<!-- http://mymedia.yam.com/mp3player2.swf?pID=2195446 -->
<!--
<div class="playerMP3" style="display:none;">
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="450" height="120" id="main2Flash" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="" quality="high" bgcolor="#ffffff" width="450" height="120" name="mymovie" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
</div>
-->
<textarea class="playerMP3" style="display:none"><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="450" height="120" id="main2Flash" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="" quality="high" bgcolor="#ffffff" width="450" height="120" name="mymovie" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object></textarea>
 
<!-- http://mymedia.yam.com/flvplayer.swf?pID=2416365 -->
<textarea class="playerFLV" style="display:none"><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="450" height="368" id="main2Flash" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="" quality="high" bgcolor="#ffffff" width="450" height="368" name="mymovie" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object></textarea>
 
<script type="text/javascript"> 
//jQuery("#yam-video-download button").click(); 
 
//for test
    //jQuery("input#inputID_yvd").val("http://mymedia.yam.com/m/2416365");
    
function yamVideoParsing(inputID, outputID, checkClass) 
{ 
    var url = jQuery("#"+inputID).val(); 
    var checkURL = new Array; 
    checkURL[0] = "http://mymedia.blog.yam.com/m/"; 
    checkURL[1] = "http://mymedia.yam.com/m/"; 
    var checkFlag = false; 
    for (var i = 0 ; i < checkURL.length; i++) { 
        var c = checkURL[i]; 
        if (url.substr(0, c.length) == c) { 
            checkFlag = true; break;
        }
    } 
    if (checkFlag == false) { 
        jQuery("."+checkClass).css("display", "block"); 
        return; 
    } 
    else { 
        jQuery("."+checkClass).css("display", "none"); 
    } 
    //example: http://mymedia.yam.com/m/2425018 
    var mID = url.substring(url.lastIndexOf("/m/")+3,url.length); 
    if (jQuery.trim(mID) == "")
    {
        jQuery("div.check-flag").show();
        return;
    }
    //jQuery("#"+outputID).prepend(mID); 
    if (jQuery("#"+outputID+" div.content[title="+mID+"]").length != 0) 
        return; 
    //建立容器 
    var content = jQuery("<div title='"+mID+"' class='content'></div>"); 
    var deleter = jQuery("<span class='deleter'>×</span>") 
        .click(function() { 
            jQuery(this).parents("div.content:first").remove();
         })
        .hover(function() { 
            this.style.backgroundColor = "gray"; 
            this.style.color = "white"; 
            },
            function() { 
            this.style.backgroundColor = "white"; 
            this.style.color = "gray"; 
            }); 
    var downloader = jQuery("<a class='downloader'>下載</a>") 
        .hover(function() { 
            jQuery(this).addClass("mouseover"); 
        },
        function() { 
            jQuery(this).removeClass("mouseover"); 
        })
        .hide(); 
    var win = jQuery("<a href='"+url+"' target='_blank' class='win'>開啟網頁</a>");
    
    /*var preview = jQuery("div.playerMP3").clone()
        .addClass("preview")
        .show();
        preview.children("param[name=movie]").val("http://mymedia.yam.com/mp3player2.swf?pID=" + mID);
        preview.children("embed").attr("src","http://mymedia.yam.com/mp3player2.swf?pID=" + mID);
    */
    /*
    var previewURL = "http://mymedia.yam.com/mp3player2.swf?pID=" + mID;
    var previewPlayer = jQuery("textarea.playerMP3").val();
        
        //previewPlayer.replace('<param name="movie" value="" />', '<param name="movie" value="'+previewURL+'" />');
        //previewPlayer.replace('<embed src="" quality="high"', '<embed src="'+previewURL+'" quality="high"');
        previewPlayer = strReplace(previewPlayer, '<param name="movie" value="" />', '<param name="movie" value="'+previewURL+'" />');
        previewPlayer = strReplace(previewPlayer, '<embed src="" quality="high"', '<embed src="'+previewURL+'" quality="high"');
        alert(previewPlayer);
    var preview = jQuery("<div class='preview'></div>")
        .html(previewPlayer);
        //preview.children("param[name=movie]").attr("value", previewURL);
        //preview.children("embed").attr("src", previewURL);
    */    
    var preview = jQuery("<div class='preview'></div>");
        
    var titleCopy = jQuery("<span class='title-copy'>複製標題</span>") 
        .click(function () { 
            var t = jQuery(this).nextAll("span.title").html(); 
            window.clipboardData.setData('Text', t); }).hide(); 
            var title = jQuery("<span class='title'>test</span>").hide(); 
            var waiting = jQuery("<span class='waiting'>"+mID+"分析中…<img src='http://puddingchen.35.googlepages.com/ajax-loader.gif' /></span>"); 
            content.append(deleter) 
                .append(waiting) 
                .append(win) 
                .append(downloader) 
                .append(titleCopy) 
                .append(title)
                .append(preview); 
            jQuery("#"+outputID).prepend(content); 
            
            //get title 
            jQuery.get(url, function(data) { 
                //var t = jQuery(data); 
                //.find("h1.heading:first").length; 
                //alert(t.find("div").length);
                var header = '<h1 class="heading">'; 
                var footer = '</h1>';
                var t = data.substring(data.indexOf(header)+header.length, data.indexOf(footer));
                
                if (jQuery.trim(t) == "<html>\n<head>\n<titl")
                {
                    errorFlag();
                    content.hide();
                    return;
                }
                
                jQuery("#"+outputID+" div[title="+mID+"]:first span.title:first").html(t) 
                    .show(); 
                jQuery("#"+outputID+" div[title="+mID+"]:first span.title-copy:first").show(); 
                if (jQuery("#"+outputID+" div[title="+mID+"]:first :hidden").length == 0) 
                {
                    jQuery("#"+outputID+" div[title="+mID+"]:first span.waiting")
                        .hide();
                }
                
             }); 
             
             /*
             //get preview
             jQuery.get(url, function(data) { 
                 var header = '<object classid="'; 
                var footer = '</object>';
                var t = data.substring(data.indexOf(header)+header.length, data.indexOf(footer));
                    t = header + t + footer;
                jQuery("#"+outputID+" div[title="+mID+"]:first div.preview:first").html(t).show();
             });
             */
             
             //get link 
             var FileURL = "http://mymedia.yam.com/api/a/?pID=" + mID; 
             //example: http://mymedia.yam.com/api/a/?pID=2425018 
             jQuery.get(FileURL, function(data) { 
                 //var t = jQuery(data); 
                //.find("h1.heading:first").length; 
                //alert(t.find("div").length); 
                var isFlv = false;
                if (data.indexOf('mp3file=') != -1)
                {
                    var header = 'mp3file='; 
                    var footer = '&totaltime='; 
                    
                    var previewURL = "http://mymedia.yam.com/mp3player2.swf?pID=" + mID;
                    var previewPlayer = jQuery("textarea.playerMP3").val();
                        previewPlayer = strReplace(previewPlayer, '<param name="movie" value="" />', '<param name="movie" value="'+previewURL+'" />');
                        previewPlayer = strReplace(previewPlayer, '<embed src="" quality="high"', '<embed src="'+previewURL+'" quality="high"');
                }
                else if (data.indexOf('&furl=') != -1)
                {
                    isFlv = true;
                    var header = '&furl='; 
                    var footer = '&hidecode=';
                    
                    var previewURL = "http://mymedia.yam.com/flvplayer.swf?pID=" + mID;
                    var previewPlayer = jQuery("textarea.playerMP3").val();
                        previewPlayer = strReplace(previewPlayer, '<param name="movie" value="" />', '<param name="movie" value="'+previewURL+'" />');
                        previewPlayer = strReplace(previewPlayer, '<embed src="" quality="high"', '<embed src="'+previewURL+'" quality="high"');
                }
                
                var t = data.substring(data.indexOf(header)+header.length, data.indexOf(footer)); 
                
                
                jQuery("#"+outputID+" div[title="+mID+"]:first a.downloader:first")
                    .attr("href", t) 
                    .show(); 
                jQuery("#"+outputID+" div[title="+mID+"]:first div.preview:first").html(previewPlayer);
                if (isFlv == true)
                    jQuery("#"+outputID+" div[title="+mID+"]:first div.preview:first object").height("368px");
                if (jQuery("#"+outputID+" div[title="+mID+"]:first :hidden").length == 0) 
                    jQuery("#"+outputID+" div[title="+mID+"]:first span.waiting")
                        .hide();
                 }); 
            } 
            
    function errorFlag()
    {
        jQuery("div.check-flag").show();
    }
    
    function strReplace(str, oldText, newText)
    {
        var temp = str.split(oldText);
        if (temp.length > 1)
        {
            str = temp[0] + newText + temp[1];
        }
        return str;
    }
    </script>    
</div>
</form>
<hr style="margin-top: 1em;" />
<p style="text-align:center;">
版本:<strong style="color:Red">20090126 大年初一</strong> | 程式寫作:<a href="mailto:puddingchen.35@gmail.com">布丁布丁吃布丁</a> | 出處:<a href="http://pulipuli.blogspot.com/2008/11/yam-ie.html">布丁布丁吃?</a> | 資料來源:<a href="http://www.yam.com/">yam天空部落</a><br />
<span style="font-size:small;">本程式僅是利用yam天空部落的程式碼做轉換、分析的動作,並不能確認資料本身是否符合智慧財產權。使用本程式下載任何資料,並不代表本程式立場。</span>
</p>
</body>
</html>


我只是想試試看jQuery的AJAX功能,就改進了以前的yam天空影音分享下載器,取代原本要用PHP去擷取網頁內容的困擾。

壞消息是只有IE在本機端以檔案的方式開啟,才能使用客戶端跨網域取得網頁內容的功能。好消息是這功能其實是比較不安全,不能方便地使用也好。原本我是打算直接寫在Blog裡面,結果發現只要是非本機的情況下,就不能使用跨網域取得網頁內容,這也算是一個收穫吧。

順便測試一下Windows Live Writer插入程式碼的外掛效果如何。事實證明,Insert Code for Windows Live Writer挺不賴的,感謝瓶水相逢.NetInsert Code for Windows Live Writer 1.0.5 Beta - 加入捲軸功能 (20080427 修正),但如果加上一個只有y軸的捲軸就更完美了。

(more...)

任意網頁載入jQuery:書籤小工具產生器 / Load jQuery from Bookmark: Bookmarklet Generator

任意網頁載入jQuery:書籤小工具產生器 / Load jQuery from Bookmark: Bookmarklet Generator

簡報1

書籤小工具(bookmarklet)是一種可以在任意網頁上執行預先寫好的JavaScript程式碼的做法。舉例來說,當你想要用console來撰寫jQuery語法來調整資料顯示結果時,卻發現該網站並沒有載入jQuery函式庫,這時候就可以用書籤小工具來為這個網頁載入jQuery函式庫,讓網頁工程師更方便到處測試。

我把載入jQuery函式的書籤小工具做成了書籤小工具產生器。除了預設的「Load jQuery」書籤小工具之外,你也可以自訂書籤小工具的名稱與JavaScript程式腳本的內容。讓我們來看看怎麼使用這個書籤小工具吧。

(more...)

Spket IDE使用jQuery自動提示

Spket IDE使用jQuery自動提示

image

Spket IDE是網路上知名的JavaScriptXML編輯器,有許採用jQueryExtJS工具庫的開發者都很喜歡使用Spket,而我在找尋理想的JavaScript IDE的時候,也來安裝Spket IDE來使用看看。

自動提示(Code Assist)是IDE中很重要的功能之一,效果就如上圖一樣,讓程式設計師在撰寫程式的時候,自動帶出相關的字詞,不僅減少程式設計師打字的工作量,也能夠避免輸入錯誤語法的人為失誤。然而似乎不少Spket IDE的使用者不清楚如何使用jQuery或ExtJS的自動提示功能,不僅Skpet IDE官方網站上有文字影片(簡體字?)的說明之外,Google中搜尋Spket IDE的介紹中也幾乎都是在教怎麼配置Profile

然而我照著教學上面的方法來做,卻始終帶不出jQuery的自動提示。找尋許多資料之後,終於在Spket IDE的論壇中發現了解決方法,因此想說在此整理一下,給有需要的朋友們一個引路。

環境敘述

image

Spket IDE可以作為plugin安裝在eclipse系列 3.2.x的IDE當中,而我是使用Aptana Studio 2,這是使用eclipse 3.5.2為基礎的IDE,也一樣可以安裝Spket IDE。而作業系統是Windows 7 64位元。

用網站安裝無效

image

原本我是使用eclipse的Install Software,連到Spket IDE的網站 http://www.spket.com/update 去下載1.6.18版本來安裝,但是這個版本似乎有問題,所以安裝之後仍然無法順利使用。

手動下載Plugin並安裝

根據論壇的說法,我將我的Skpet IDE安裝以及jQuery Profile配置的方法整理如下:

  1. 先安裝eclipse系列的IDE編輯器,例如Aptana Studio 2
  2. 下載com.spket.js_1.6.18.jar (SkyDrive備份)。
  3. 將com.spket.js_1.6.18.jar放置到eclipse安裝目錄底下的「plugins」資料夾。以Aptana Studio 2來說,就是「D:\Program Files\Aptana Studio\plugins\com.spket.js_1.6.18.jar」(因為我把Aptana安裝到D磁碟分割去了)
  4. 開啟eclipse,沒有錯誤訊息的話,應該就是安裝完成了。

接著是配置jQuery Profile:

  1. 打開「Window > Preferences」:
    image
  2. 左邊導覽列中,找到「Spket > JavaScript Profiles」,然後點選右上角的「New」以建立新的Profile:
    image
  3. 輸入Profile名稱,你看得懂就好,那就設定為「jQuery」吧。
    image
  4. 現在Profiles裡面多了一個剛剛建立的jQuery Profile,選擇他並點選右邊的「Add Library」:
    image
  5. 在選擇Library的下拉式選單中,選擇「jQuery」,然後按下ok完成:
    image
  6. 接著要加入jQuery的檔案,請點下右方的「Add File」:
    image
  7. jQuery的檔案最好是附加上說明的詳細版本,而不是一般使用的min版本,這樣在自動提示的時候才會把方法的說明也一併帶出來。在此推薦使用阿良翻譯的jQuery 1.3.2文件 (SkyDrive備份),請解壓縮之後選擇裡面的「jquery-1.3.2-jsdoc-Spket-profile.js」即可。
    image
  8. 完成後,選擇左方jQuery的Profile,並按下右邊的「Default」,讓Spket IDE把這個Profile作為預設的環境。
    image

儘管他們的版本號都是1.6.18,可是論壇上面的jar似乎比較正確。安裝正確的版本之後,不僅可以顯示jQuery的自動完成提示,也可以開啟Profile Explorer來查詢可用JavaScript語法。如下圖:

image


結語

儘管總算讓Spket IDE可以讀取jQuery的自動提示了,但是對於自己寫的程式,Spket IDE依然是不會弄出自動提示。接著我要繼續研究怎麼讓我寫的程式加入Spket IDE的自動提示中,如果這點可以做到的話,那麼Spket IDE就幾乎稱得上是理想的JavaScript IDE了。

(more...)

布丁式Blogger自動摘要功能(jQuery版)

布丁布丁吃布丁

布丁式Blogger自動摘要功能(jQuery版)

image

感謝jQuery這個強大的JavaScript程式庫,現在在Blogger上面裝外掛已經不用去版面配置跟HTML的範本(template)奮鬥了(如下圖)。

image

你也知道,程式往往因為少改那幾行而出錯,每次要檢查錯誤都蠻麻煩的。

這次我改寫了之前的布丁式自動摘要功能,並以jQuery的方法來實做他,讓各位Blogger套用在自己Blog的時候能更加地簡單、直接,而且更迅速!


快速安裝

  1. 到Blogger管理介面裡面的「版面配置」中的「網頁元素」,再最下方點選「新增小工具」。(請不要在意為何我的版面配置如此之怪,因為我不小心又把導覽列卡錯位置了orz)
    image
  2. 選擇新增一個「HTML/JavaScript」小工具吧。
    image
  3. 看到設定畫面,標題可以留空,在內容的部份請插入以下程式碼:
    <script src="http://puddingchen.35.googlepages.com/jquery.js" type="text/javascript"></script>
    <script type="text/javascript">
    jQuery.getScript("http://sites.google.com/site/puddingchen35/Home/digest/puliBloggerDigest.js", function() {
    pBD = puliBloggerDigest();
    pBD.delayTime = 0; //執行延遲時間,單位是「毫秒」(1000毫秒=1秒)
    pBD.wordMin = 100; //自動摘要最少字數
    pBD.wordMax = 200; //自動摘要最多字數
    pBD.langBlogURL = "文章網頁"; //連結按鈕
    pBD.langReadAll = "閱讀全文"; //連結按鈕
    pBD.langContiReadAll = "繼續閱讀全文"; //連結按鈕
    pBD.langShowDigest = "僅顯示摘要"; //連結按鈕
    pBD.doDegiest();
    });
    </script>
    最後結果應該會如下圖:
    image
  4. 按「儲存」,完成安裝。

回到首頁的時候,應該就能看到你的文章順利被縮成100字到200字之間的摘要了。


進階設定:自訂你的自動摘要功能

你可以在程式碼當中修改參數,以自訂你的動摘要功能。參數在前面小節安裝程式碼的時候即可設定,例如「pBD.wordMin = 100; //自動摘要最少字數」即是。以下一一說明各個參數的意義:

  • delayTime 延遲處理時間:預設值為「0」毫秒(1000毫秒=1秒)。當你覺得在做自動摘要的時候會讓你網頁讀起來緩慢,你可以增加延遲時間,讓程式分批去處理。
  • wordMin 自動摘要最短字數:預設值為「100」字,此字數並不包含標籤內的文字,而是真的顯示的字數。當超過此字數之後,程式會尋找HTML標籤完結處當中自動摘要的切斷點。
  • wordMax 自動摘要最長字數:預設值為「200」字,此字數並不包含標籤內的文字,而是真的顯示的字數。當程式找尋自動摘要的切斷點超過此字數之後,程式會強迫設定此字數為切斷點,並且自動增加結尾標籤進來,以維持HTML的完整性。
  • 後面的則是各種按鈕的訊息,你可以自訂成其他訊息:
    • langBlogURL:顯示按鈕「文章網頁」;
    • langReadAll:顯示按鈕「閱讀全文」;
    • langContiReadAll:顯示按鈕「繼續閱讀全文」;
    • langShowDigest:顯示按鈕「僅顯示摘要」。

現在來舉一個設定與實際程式碼撰寫的例子:

  • 延遲時間為「500」毫秒,也就是0.5秒;
  • 最小字數為「50」字,最大字數為「100」字,這可以讓你的首頁看起來不會這麼長;
  • 訊息改為「Post Link」、「Read All」、「Read All」(其實跟前面的沒什麼差別嘛)、「Show Digest」,改成英文很有國際化的感覺。

那麼你的程式碼應該如此撰寫:

<script src="http://puddingchen.35.googlepages.com/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
jQuery.getScript("http://sites.google.com/site/puddingchen35/Home/digest/puliBloggerDigest.js", function() {
pBD = puliBloggerDigest();
pBD.delayTime = 500; //執行延遲時間,單位是「毫秒」(1000毫秒=1秒)
pBD.wordMin = 50; //自動摘要最少字數
pBD.wordMax = 100; //自動摘要最多字數
pBD.langBlogURL = "Post Link"; //連結按鈕
pBD.langReadAll = "Read All"; //連結按鈕
pBD.langContiReadAll = "Read All"; //連結按鈕
pBD.langShowDigest = "Show Digest"; //連結按鈕
pBD.doDegiest();
});
</script>

進階設定:手動插入摘要切斷點

如之前的布丁式自動摘要功能一樣,本功能支援你在撰寫文章時手動插入摘要切斷點。

切斷點分成三個種類,個別有特定的格式跟意含,如下說明:

手動摘要功能 需要插入的HTML程式碼
隱藏所有文章
  • <!--Hidden All-->
  • <!-- Hidden All -->
  • <span class="digest-mode hidden-all">&lt;-- HIDDEN ALL --&gt;<span>
顯示所有文章
  • <!--Show All-->
  • <!-- Show All -->
  • <span class="digest-mode show-all">&lt;-- SHOW ALL --&gt;<span>
此處之前設定為摘要
  • <!--Digest-->
  • <!-- Digest -->
  • <!--more-->
  • <span>DIGEST<span>

如果同時插入了這三種切斷點,則效果會由隱藏全部文章優先,其次是顯示所有文章,手動摘要則是最後才會生效的!


進階設定:儲存並修改你自己的自動摘要功能

布丁式自動摘要功能(jQuery版)是由Google Page Creator提供的空間。這空間有流量限制,速度也不快。如果你需要的話,可以自行下載下來並擺置到你的空間,然後修改安裝程式碼當中src的引用位置即可。

  • jquery.js:jQuery 1.2.6版本,也許你可以在jQuery的網站上找到更新的版本。
  • puliBloggerDigest.js:布丁式自動摘要功能(jQuery版),因為要搭配jQuery才能運作,所以安裝程式碼的時候一定要一起引用喔!

在我很久以前推出的布丁式自動摘要功能似乎大受好評,希望這次推出的改良版,能夠幫助更多Blogger來改善你們的Blog頁面。

不過還是要提醒一下,安裝太多JavaScript檔案會拖慢你的網站讀取速度,要在美觀與速度之間取得平衡喔!

(more...)

PHP File Host 檔案上傳專案 / PHP File Host Project

PHP File Host 檔案上傳專案 / PHP File Host Project

圖片1

最近我完成了一個用PHP做的檔案上傳專案「PHP File Host」,順便學習了PHP框架Fat-Free Framework、PHP資料庫函式庫RedBeanPHP、前端檔案上傳工具jQuery File Upload跟前端界面Bootstrap等技術。以下說明這個專案內容。

I wrote a PHP project “PHP File Host” for cross origin file uploading. In this project, I try some new technology include Fat-Free Framework, RedBeanPHP, Bootstrap and jQuery File Upload. Following is introduction of this project.


專案由來 / Project Introduction

原本我的KALS專案並不具備檔案上傳功能(其實一開始規劃時有啦,但是一直沒有實作),但最近開始有了這方面的需求。然而檔案上傳乍聽之下很簡單,但是在跨網域(Cross Origin)的情境中,卻不太容易實作。

另一方面,簡單的檔案上傳應用實作常常會有幾個問題:

  1. 實體檔案管理的問題:檔案存放在哪哩?伺服器空間足夠嗎?
  2. 檔案重複問題:如何有效率地降低檔案的使用空間?
  3. 檔案名稱問題:遇到不支援的檔案名稱編碼,存到伺服器的檔案系統時會造成亂碼的問題。

因此我想要做一個簡單的檔案上傳應用網站。這個只做一件事情:支援跨網域的檔案上傳、然後的到一個下載網址。這樣就夠了。這就是PHP File Host的由來。

專案內容 / Project

圖片2

我把最近對PHP File Host的報告彙整成為一個投影片,裡面有簡單的功能介紹:

特色 / Features

PHP File Host的特色在於:

  • 運作環境:以PHP架設,資料庫預設使用SQLite,但是不需要額外配置資料庫。
  • 跨網域檔案上傳:支援以JSONP上傳jQuery File Upload上傳
  • 避免儲存重複檔案:以MD5特徵碼來辨識檔案內容,避免儲存相同檔案。
  • 完整保留檔案名稱:以資料庫儲存檔案名稱,並由程式負責從header指定下載檔案名稱,因此不會下載到亂碼的檔名。

相關技術 / Technology

裡面主要用到幾種技術,在此我也聊一下使用這些技術的心得。

Fat-Free Framework (F3)

Fat-Free Framework (F3)是一個PHP框架。不過在講F3之前,我想先聊一下CodeIgniter。

在開發KALS時,我主要使用的PHP框架是CodeIgniter (CI)。CI大量參考Ruby on Rails的理念,大量遵守「約定優於配置」(convention over configuration)的準則。特別是對於routing功能來說,要連到指定網址就得在特定的檔案結構中撰寫相對應的PHP類別。

一開始我覺得這也不錯,大家的都能遵守約定的話,開發就能夠維持一致性。但事實上是為了這這個約定,CI限制了大量的靈活性。常常會發現要接手專案的新手要花很多時間來瞭解routing的邏輯,而且無法自由指定routing中的變數與類別也很令人覺得限制很大。最致命的就是不能支援JSONP的呼叫模式,難以跟jQuery.getJSON()搭配運用。

雖然KALS的CI被我大改之後變得可以支援JSONP,但我不覺得這是一種理想的做法。所以當我這次要開發這個專門支援跨網域檔案上傳的PHP File Host時,我就毅然決然換了另一個PHP框架。

我花了一點時間嘗試不同的PHP框架,不過後來找到了F3。這個專案特色是檔案看起來不會太複雜,特別是與龐大的CodeIgniter相比。

image

F3一些零星的功能不多,但是主要功能卻比CI好用很多。

F3的routing是由設定檔控制,寫法跟Node.js的express框架很像。這符合我們一般使用的概念:從URI追溯檔案位置。而不是像CI那樣,得先瞭解約定才能知道檔案的位置。使用配置設定來規範routing這點看起來像是違反了「約定優於配置」,但是從另一個角度來看,這也是讓「使用者」(利用URI使用系統)跟「開發者」(使用伺服器上的檔案配置)脫鉤的一種好方法。CI那種routing規範實在是太過糾結,用起來綁手綁腳的。

而F3的routing也支援分辨GET (查詢)、POST (新增)、PUT (更新)、DELETE (刪除)等REST API會使用的四種方法。不過要注意到,若針對同一URI使用GET跟POST等多種方法,最後變數只會取得使用GET這個而已。這是比較令人困擾的地方,我得再研究看看。

此外,我喜歡F3用擴增HTML標籤的方式來建立樣板,輸出的樣板能夠直接指定MIME Type為JavaScript這點也很不錯,這對JSONP支援良好,也可以輕易使用現在流行的Markdown程式語言。相較之下,CI的樣板只能說是原始人。不過F3預設限制「同源使用」(same-origin),為此得額外宣告以下header才行讓其他網站跨網域開啟F3專案:

header('X-Frame-Options: ');

CI提供了大量零星的函式(helper),讓我們能夠簡單地處理很多小東西。F3並沒有這麼多helper,但是它把很多常用的系統與環境資訊都寫在框架的系統變數裡面。習慣之後也還算好用,但我比較喜歡helper的函式形式。

在資料庫的使用上,F3的資料庫也跟CodeIgniter的Active Record一樣,都是使用ORM (Object-Relation Mapping)的方式操作資料庫。而F3多了一些NoSQL資料庫的支援,像是MongoDBJig

雖然這些ORM用起來不錯,但是我這次更想使用另一種資料庫函式庫更感興趣,那就是RedBeanPHP。

RedBeanPHP

RedBeanPHP是一個PHP資料庫函式庫,使用時只要導入它一個PHP主要檔案即可。

他在使用上跟很多ORM函式庫一樣,可以把資料表當作一個類別,裡面的一列當作是一個物件來使用。但是最大的差別在於,RedBeanPHP是不需要預先設定資料表(schemaless)的架構。

舉例來說,今天我們有一種類別叫做Book,那我們就用RedBeanPHP建立一個類別叫做「Book」的物件,然後設定其中的屬性「title」跟「author」後儲存,這樣子資料庫中就會自動幫我們把相關的table與field都設定好,連field的資料形態都會與物件屬性的形態直接相對應。

未來如果這個Book想要增加第三種屬性「price」,那就在程式中加入「price」,儲存,這樣子資料表就會多一個price的欄位。

這樣子的好處在於,我們不需要在配置程式碼之餘還要煩惱如何配置資料庫。RedBeanPHP預設採用SQLite,但也可以支援主流的關聯式資料庫,如PostgreSQL跟MySQL。

我使用RedBeanPHP在PHP File Host中儲存檔案資料,操作起來非常容易上手,而且令人驚訝地好用。

有人抱怨RedBeanPHP的資料庫查詢速度過慢,這點可能要謹慎評估。但PHP File Host少量應用看來是沒有這個問題。

Bootstrap

這次我也一併改進了前端的界面。跟以往一樣,比起重頭開始設計網頁界面,我比較偏好從既有的網頁範本開始修改。難得這次機會,我也就從知名的客戶端技術Bootstrap的範本Landing Page開始改起。

使用Bootstrap的目的包括:

  • 支援RWD (Responsive Web Design):不論電腦、平板、手機等不同螢幕大小的裝置,網頁都應該自動最佳化調整版面。這點可以靠Bootstrap的Grid system來調整。
  • 一致且美觀的元件:Bootstrap的選單、按鈕、讀取條非常好看。我還蠻喜歡它的Default、Primary、Success、Info、Warning、Danger、Link的通用分類與對應顏色。
    image
  • 豐富的圖示:不光是Bootstrap本身提供了大量的圖示(Glyphicons),Landing Page範本還用了更多元的Font Awesome圖示,基本功能操作真的是不需要額外在準備其他圖片了。

圖片1

最後完成的結果在Am I Responsive?上看起來就是這樣,開頭的圖片會按照視窗畫面去做調整,頂端選單列也會在小螢幕中自動縮成一個按鈕,成果很不錯。

雖然我老是做一些伺服器端的專案,但是作為網頁相關的程式設計師,前端界面技術自然也不能生疏。PHP File Host雖然只是一個小小的網站,但是做起來還是令我挺開心的。

jQuery File Upload

我這次也加入了jQuery File Upload來改善檔案上傳的介面。這個套件可以用簡單的方式來設定檔案上傳的功能,讓我們輕易加入以下功能:

  • 點選按鈕、選擇檔案、馬上上傳
  • 上傳進度條
  • 拖曳至指定區域上傳
  • 剪貼上傳

我很喜歡這些方便的操作,傳統點選檔案按鈕的方式實在是太過麻煩。

另一方面,我本來還蠻煩惱怎麼用jQuery File Upload來進行跨網域傳送檔案,卻意外發現jQuery File Upload使用了HTML5元件postMessage來傳輸檔案的方式,比我以前提出的JSONP跨網域檔案上傳還要好用很多,真讓我驚豔。

不過最後我要在其他專案使用jQuery File Upload設定上傳功能時,卻發現它使用的是jQuery 1.11,與KALS專案的jQuery 1.4有很大的差別。加上jQuery File Upload也要使用jQuery UI等工具,導致它與其他功能互相衝突,最後我還是放棄在KALS專案使用jQuery File Upload而使用原本的JSONP跨網域檔案上傳方式。

向DSpace致敬 / Salute to DSpace

最後也提一下PHP File Host內部檔案儲存的方式,這部分我大量地參考了DSpace保存檔案的方式。

DSpace依照檔案的MD5特徵碼作為檔案名稱與實體位置的設定,因此每個檔案都長得像是「01f7b24e629cc23e369983994d0b8fbe」。檔案的名稱、MIME Type等相關資訊則是寫在資料庫中,連同上傳者、上傳時間等操作記錄也一併分開儲存。

這樣做可以改善檔案重複儲存、避免檔案名稱編碼不支援等問題,但是檔案管理 (特別是刪除)跟下載就成為另一個技術上的重點。目前PHP File Host還沒有做的刪除功能,就只是一直擺放資料,然後以供人下載而已。

利用Base56面碼縮短網址 / Shorten URL by Base56 Encoding

喔對了,為了避免網址過長,我還特別用了Base56將數字ID以「0123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ_-.~」等網址允許的字元進行編碼。即使檔案的編號很大,網址長度仍然不會很誇張地漲大。這種做法我也挺喜歡的。

這些大致上就是PHP File Host使用到的技術。


結語:過時的技術? / Conclusion: Outdated Technology?

剛好最近也看到一篇分析報導:「AngelList 分析:越好的公司越喜歡用 Python,越差的公司越愛用 PHP」。該分析將公司分成Okay、Good、Great三級,然後看公司使用的程式語言。

其中伺服器端PHP排行第三、客戶端Bootstrap排行第五(但是RoR我不認為是客戶端技術)、資料庫SQLite榜上無名。雖然直接看圖表起來也沒啥不好,但我現在才學這些技術,的確是比很多人慢了許多。

之後有機會的話,我想要研究伺服器端的Node.js,然後在單一頁面應用上繼續精進AngularJS (之前寫了一個批次開啟網頁的小應用,蠻好上手的)。久違地學習新的技術,真的很令人開心呢。

(more...)

DSpace Input-type新增taiwanaddress

布丁布丁吃布丁

DSpace Input-type新增taiwanaddress

2009-06-15_000205

為了因應臺灣的地址,我開發了一個TaiwanAddress的input-type,可以在選擇縣市之後,由程式自動帶入郵遞區號前三碼。

這個程式我也寫成jQuery的plugin,只要引入之後以jQuery(selecotr).taiwanAddress()就可以使用。

由於最近修改程式碼的頻率頻繁,故不提供檔案,而是以指示的方式教你安裝這些功能。以下是安裝與使用教學:


檔案下載

請下載以下檔案,並擺到下述的指定位置:

修改遞交作業

本步驟有兩個檔案必需要修改。

修改edit-metadata.jsp

edit-metadata.jsp是遞交作業中填寫metadata使用的JSP檔案。

如果你修改過edit-metadata.jsp的話,你應該要在[dspace-source]/dspace/modules/jspui/src/main/webapp/submit/edit-metadata.jsp找到該檔案,否則請到[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp/submit/edit-metadata.jsp找到該檔案。

請依照以下指示修改程式碼:

插入doTaiwanAddress函式

請找到「do」開頭函式的最後一個,如果你安裝過我之前介紹的DSpace新增input-type,那麼可以找到「}    //void doXMLMetadata()」,請在後面插入以下程式碼:

    void doTaiwanAddress(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary) 
      throws java.io.IOException 
    {

      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;

      if (fieldCount == 0)
         fieldCount = 1;

      for (int i = 0; i < fieldCount; i++) 
      {
	 if (i == 0) 
	    sb.append("<tr><td class=\"submitFormLabel\">")
	      .append(label)
	      .append("</td>");
	 else
	    sb.append("<tr><td>&nbsp;</td>");

         if (i < defaults.length)
           val = defaults[i].value.replaceAll("\"", "&quot;");
         else
           val = "";

         sb.append("<td colspan=\"2\"><input type=\"text\" name=\"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         
         sb.append("\" size=\"50\" value=\"")
           .append(val +"\"")
           .append(hasVocabulary(vocabulary)&&closedVocabulary?" readonly=\"readonly\" ":"")
	   	.append("   />")
	   .append(doControlledVocabulary(fieldName + (repeatable?"_" + i:""), pageContext, vocabulary))
	   .append("<script type=\"text/javascript\">" + "\n")
		.append("jQuery(document).ready(function () {" + "\n")
		.append("jQuery(\"input[name=\'")
		.append(fieldName);
		if (repeatable && i>0)
           sb.append("_").append(i);
		sb.append("\']\").taiwanAddress();" + "\n")
		.append("});" + "\n")
	   .append("</script>")
	   .append("</td>\n");
	   

	 if (repeatable && i < defaults.length) 
	 {
	    // put a remove button next to filled in values
	    sb.append("<td><input type=\"submit\" name=\"submit_")
	      .append(fieldName)
	      .append("_remove_")
	      .append(i)
//	      .append("\" value=\"Remove This Entry\"/> </td></tr>");
	      .append("\" value=\"")
   	      .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
   	      .append("\"/> </td></tr>");
	 } 
	 else if (repeatable && i == fieldCount - 1) 
	 {
	    // put a 'more' button next to the last space
	    sb.append("<td><input type=\"submit\" name=\"submit_")
	      .append(fieldName)
//	      .append("_add\" value=\"Add More\"/> </td></tr>");
	      .append("_add\" value=\"")
	      .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
	      .append("\"/> </td></tr>");
	 } 
	 else 
	 {
	    // put a blank if nothing else
	    sb.append("<td>&nbsp;</td></tr>");
	 }
      }

      out.write(sb.toString());
    }

引用jquery-plugin-taiwan-address.js

找到引用JavaScript與CSS的地方,在「<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.css" type="text/css" media="screen">」之後插入以下程式碼:

<script type="text/javascript" src="<%= request.getContextPath() %>/extension/taiwan-address/jquery-plugin-taiwain-address.js"></script>

插入選擇使用doTaiwanAddress

最後找到以下程式碼:

								else if (inputType.equals("xmlmetadata")) 
								{
									pageContent.append(doXMLMetadata(out, item, fieldName, dcSchema, dcElement, dcQualifier, 
											  repeatable, fieldCountIncr, label, pageContext, vocabulary,
											  closedVocabulary, workspaceItemID, 
											  nonInternalBistreamsID, hasMultipleFiles, defaultValue, 
											  request.getContextPath()));
								} 

在這之後插入以下程式碼:

								else if (inputType.equals("taiwanaddress")) 
								{
									pageContent.append(doTaiwanAddress(out, item, fieldName, dcSchema, dcElement, dcQualifier, 
										repeatable, fieldCountIncr, label, pageContext, vocabulary, 
										closedVocabulary));	
								}
修改DescribeStep.java

DescribeStep.java是遞交作業中儲存資料時使用的檔案。

DescribeStep.java的位置在[dspace-source]/dspace-api/src/main/java/org/dspace/submit/step/DescribeStep.java

請找到以下程式碼:(如果增加過input-type,那這邊應該也會有所改變)

            else if ((inputType.equals("onebox"))
                    || (inputType.equals("twobox"))
                    || (inputType.equals("textarea"))

請多加一個條件式如下:

					|| (inputType.equals("taiwanaddress"))

修改完畢之後,必須將DSpace重新編譯,方能生效。

2009-06-15_010334

修改edit-item-form.jsp

edit-item-form.jsp是修改文件時使用的JSP檔案。

你必須先確認你之前安裝過我寫的DSpace擴增文件編輯功能,你才能夠繼續安裝以下功能。

如果你修改過edit-item-form.jsp的話,你應該要在[dspace-source]/dspace/modules/jspui/src/main/webapp/tools/edit-item-forms.jsp找到該檔案,否則請到[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp/tools/edit-item-form.jsp找到該檔案。

請依照以下步驟一一插入程式碼。

插入doTaiwanAddress函式

請找到「String doXMLMetadata(…」函式的結束位置,我應該有加一個註解:「}//doXMLMetadata End」,再之後加入以下程式碼:

    String doTaiwanAddress(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary) 
      throws java.io.IOException 
	{
		StringBuffer output = new StringBuffer();
      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;

      if (fieldCount == 0)
         fieldCount = 1;

	  int index = 0;
      for (int i = 0; i < fieldCount; i++) 
      {
	 if (i == 0) 
	    sb.append("<tr><td class=\"submitFormLabel\" width=\"35%\">")
	      .append(label)
	      .append("</td>");
	 else
	    sb.append("<tr><td>&nbsp;</td>");

         if (i < defaults.length)
           val = defaults[i].value.replaceAll("\"", "&quot;");
         else
           val = "";

         sb.append("<td colspan=\"2\"><input type=\"text\" name=\"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         

		String editItemFormID = getMetadataID("value_", schema, element, qualifier, index);
		String editItemFormRemoveID = getMetadataID("submit_remove_", schema, element, qualifier, index);
		String editItemFormAddID = getAddID(schema, element, qualifier);
		index++;
		 
         sb.append("\" size=\"80\" value=\"")
           .append(val +"\"")
           //.append(hasVocabulary(vocabulary)&&closedVocabulary?" readonly=\"readonly\" ":"")
	   	.append("  onchange=\"editItemFormSync('"+editItemFormID+"', this.value)\" class=\"doTaiwanAddress\" />")
	   //.append(doControlledVocabulary(fieldName + (repeatable?"_" + i:""), pageContext, vocabulary))
	   .append("<textarea class=\"javascript\" style=\"display:none\">" + "\n")
		.append("jQuery(document).ready(function () {" + "\n")
		.append("jQuery(\"input[name=\'")
		.append(fieldName);
		if (repeatable && i>0)
           sb.append("_").append(i);
		sb.append("\']\").taiwanAddress();" + "\n")
		.append("});" + "\n")
	   .append("</textarea>")
	   .append("</td>\n");
	  
	 String fieldNameFull = fieldName;
	 if (repeatable && i>0)
           fieldNameFull = fieldNameFull + "_" + i;
	 
     
	 if (repeatable && i < defaults.length) 
	 {
	    // put a remove button next to filled in values
	    sb.append("<td><input type=\"button\" onclick=\"editItemRemove('"+editItemFormRemoveID+"')\" name=\"submit_")
	      .append(fieldName)
	      .append("_remove_")
	      .append(i)
//	      .append("\" value=\"Remove This Entry\"/> </td></tr>");
	      .append("\" value=\"")
   	      .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
   	      .append("\"/> </td></tr>");
	 } 
	 else if (repeatable && i == fieldCount - 1) 
	 //else if (true)
	 {
	    // put a 'more' button next to the last space
	    sb.append("<td><input type=\"button\" onclick=\"editItemAdd('"+editItemFormAddID+"')\" name=\"submit_")
	      .append(fieldName)
//	      .append("_add\" value=\"Add More\"/> </td></tr>");
	      .append("_add\" value=\"")
	      .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
	      .append("\"/> </td></tr>");
	 } 
	 else 
	 {
	    // put a blank if nothing else
	    sb.append("<td>&nbsp;</td></tr>");
	 }
      }

     	//out.write(sb.toString());
		output.append(sb.toString());
		return output.toString();
    }//doTaiwanAddress End

引用jquery-plugin-taiwan-address.js

找到引用JavaScript與CSS的地方,在「<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/flora.datepicker.css" type="text/css" media="screen">」之後插入以下程式碼:

<script type="text/javascript" src="<%= request.getContextPath() %>/extension/taiwan-address/jquery-plugin-taiwain-address.js"></script>

插入選擇使用doTaiwanAddress

最後找到以下程式碼:

								else if (inputType.equals("xmlmetadata")) 
								{
									pageContent.append(doXMLMetadata(out, item, fieldName, dcSchema, dcElement, dcQualifier, 
											  repeatable, fieldCountIncr, label, pageContext, vocabulary,
											  closedVocabulary, workspaceItemID, 
											  nonInternalBistreamsID, hasMultipleFiles, defaultValue, 
											  request.getContextPath()));
								} 

在這之後插入以下程式碼:

								else if (inputType.equals("taiwanaddress")) 
								{
									pageContent.append(doTaiwanAddress(out, item, fieldName, dcSchema, dcElement, dcQualifier, 
										repeatable, fieldCountIncr, label, pageContext, vocabulary, 
										closedVocabulary));	
								}

完成!

2009-06-15_000205

使用input-type:taiwanaddress

請修改[dspace]/config/input-forms.xml,把input-type設為taiwanaddress就可以囉。舉例來說如下:

       <field>
         <dc-schema>dc</dc-schema>
         <dc-element>contributor</dc-element>
         <dc-qualifier>authorAddress</dc-qualifier>
         <repeatable>true</repeatable>
         <label>Author Address</label>
         <input-type>taiwanaddress</input-type>
         <hint>Enter the address of the author of this item below.</hint>
         <required></required>
       </field>

結語

之後還會陸續加入一些東西,我想等穩定一點再來發佈edit-metadata.jsp或其他檔案吧。

<-- Post Catalog -->

(more...)

擷取AJAX動態產生的網頁內容:PhantomJS指令列工具 / Crawling AJAX Webpages with PhantomJS Command Line Utility

擷取AJAX動態產生的網頁內容:PhantomJS指令列工具 / Crawling AJAX Webpages with PhantomJS Command Line Utility

image

現在很多網頁內容都是以動態的方式產生,例如Facebook會在開啟網頁之後再來讀取網頁內容,就連「布丁布丁吃什麼?」也是在網頁開啟之後再來慢慢載入旁邊的小工具。這種使用AJAX技巧來調整畫面的網頁,雖然便於一般使用者用瀏覽器查看,但是卻會造成伺服器端用程式抓取網頁的困難。

還好,現在我們可以用Node.js寫成的虛擬瀏覽器PhantomJS來幫我們載入完整的網頁內容。為此我寫了一些搭配PhantomJS使用的命令列腳本,讓我們可以在Linux 32位元環境下以指令端擷取指定網址,並配合jQuery選取器抽取出需要的網頁元素,最後直接回傳顯示在螢幕上。

(more...)

Google分析的行為事件追蹤:使用jQuery加入事件 / Add Google Analytics Event Tracking by jQuery

Google分析的行為事件追蹤:使用jQuery加入事件 / Add Google Analytics Event Tracking by jQuery

image

繼前一篇介紹如何在Google分析中加入使用者ID之後,這篇要講的則是Google分析的基本用法之一:事件記錄(Event Tracking)。網路上已經有許多關於事件記錄的做法,不過大多都是寫在標籤的onclick屬性中。這就篇簡單介紹使用jQuery來加入事件記錄的做法吧。

(more...)

Webpack引用方式對檔案大小的影響 / The Size of Webpack package files are different on Import from Files or Modules

布丁布丁吃布丁

Webpack引用方式對檔案大小的影響 / The Size of Webpack package files are different on Import from Files or Modules

1-Webpack_2.png

在Webpack中引用其他程式碼的模組解析,可以是來自於獨立的JavaScript檔案,也可以是來自於Node.js中已安裝的模組,可以使用require,也可以使用import。我很好奇到底這兩種引用方式對於最後產生的檔案大小有何影響,在這篇中我以引用獨立檔案或是已安裝的模組、require或是import這四種組合來引用 jQueryJSZipFileSaver 這三個模組,發現引用Node.js已安裝模組的方式產生的檔案較大,引用 獨立檔案所產生的檔案較小。而import產生的檔案比較大,require比較小。所以專案要選擇用require來引用獨立檔案會比較好嗎?我也不這樣認為。

(more...)

Blogger側邊欄開關小工具

布丁布丁吃布丁

Blogger側邊欄開關小工具

image

看來的確有人注意到我的Blogger右上角有個「Sidebar」控制的功能,這可以控制右邊側邊欄的大小,搭配jQuery的動畫效果,呈現的效果其實還蠻有趣的。(只是在IE6中動畫效果並不明顯)

由於網友Gooda的詢問,我特別把這功能獨立出來並改寫成較容易操作的方式,分享給各位Blogger的玩家使用。

程式碼下載

下面的快速安裝當中是使用了我架在Google Page Creator的程式,如果你需要的話,歡迎大家自行下載、修改並使用。


安裝方法

Step.1 進入Blogger管理介面的「版面配置」,點選下方的「新增小工具」

image 

Step.2 新增「HTML/JavaScript」

image

按下右上角的「+」就可以進入編輯畫面。

Step.3 在內容新增程式碼

image

程式碼的內文如下:

<script src="http://puddingchen.35.googlepages.com/jquery.js" type="text/javascript"> </script>
<script type="text/javascript">
jQuery.getScript("http://puddingchen.35.googlepages.com/puliBloggerSidebarControl.js", function () {
	pBS = puliBloggerSidebar();
	pBS.styleController = "cursor:default;float:right;font-size: 10pt;font-weight:normal;margin-right:1em;margin-top:15px;";	//定義控制按鈕的格式
	pBS.styleA = "font-weight:blog;";	//定義左右按鈕的格式
	pBS.textLeft = " &lt; Side";	//定義左文字的內容
	pBS.textCenter = "";	//定義中間文字的內容
	pBS.textRight = "bar &gt; ";	//定義右文字的內容
	pBS.setup();
});
</script>

如果你之前已經引用了jQuery,那麼你可以省略掉第一行的「<script src="http://puddingchen.35.googlepages.com/jquery.js" type="text/javascript"> </script> 」部份。

注意到裡面有些屬性設定,包括CSS與內文的部份。如果你對於呈現出來的導覽列控制按鈕的樣式不太滿意,稍後你可以回來修改這邊屬性的內容。

標題留空無所謂,完成之後按下右下角的「儲存」。

Step.4 看看你的網誌,「< Sidebar >」應該已經出來囉!

image

點點看能不能順利運作吧!

(more...)