星期三, 12月 04, 2013

Webkit 要命的修改--支援 DOM4 的 remove()

Webkit 在 2012 年 9 月做了一項變動, 支援 DOM4 的 remove(), 會在執行此方法的元素有父元素時, 刪除掉自己。如果你用 Opera 18 測試以下的 HTML 檔:

<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <script type="text/javascript">
   function remove() {
    alert("remove is called.");
   }
  </script>
 </head>
 <body>
  <button onclick="remove()">刪除</button>
 </body>
</html>
按一下『刪除』鈕, 你可能預期會執行程式中的 remove(), 顯示訊息窗, 但實際的結果卻是『刪除』鈕不見了, 這是因為按下『刪除』後執行的是內建的 remove(), 而不是程式中所撰寫的 remove(), 而內建的 remove() 就把『刪除』鈕的 button 元素刪除了。

要解決這個問題很容易, 就是不要將 function 取名為 "remove", 不過我想應該有許多舊的網頁上都有以 remove 為名的 function, 如果剛好又是用在類似以上案例的情境下, 就會產生悲劇了!

星期二, 12月 03, 2013

Google Play Service 版本更新

今天收到一封信詢問 Google Map API V2 的問題,由於 API 改版頻繁,一個月前寫的程式在另外一台機器上竟然無法執行, 一跑就當掉。經過一番搜尋之後, 發現 Google Map API V2是屬於 Google Play Service,而 Google Play Service 之前改版到 13, 在這一版中必須在專案的 AndroidManifset.xml 中為 application 元素加上以下的 meta 屬性:
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
google-play-services_lib 程式庫才能正常運作, 否則就會當掉, Android 程式設計之路真是步步驚心啊!

星期三, 10月 09, 2013

使用指令連接 WiFi AP

由於工作上的需要, 常會再一個電腦教室內設定連接每一部電腦到同一個 WiFi AP, 手工操作不但要一部一部電腦去點, 而且還要一一輸入金鑰, 其實很麻煩。如果使用 netsh wlan 指令, 就可以簡化操作。

首先, 找一部已經連上指定 WiFi AP 的電腦, 匯出該連線的 profile, profile 即是你在控制台的『管理無線網路』中看到的對應各個 AP 的設定檔:



以上圖為例, 若要匯出 FlagoWiFi 這個 profile, 指令如下:
netsh wlan export profile folder=d:\temp name=FlagoWiFi
即會將 profile 內容匯出儲存在 d:\temp 下, 檔名為『網路介面名稱』加上『profile 名稱』.xml, 例如上圖中的無線網卡的網路介面名稱為預設的『無線網路連線』, 因此檔名即為『無線網路連線-FlagoWiFi.xml』。

接著, 只要將這個 xml 檔複製到別台電腦上, 就可以利用以下指令連接上同一部 AP:
netsh wlan add profile filename=d:\temp\無線網路連線-FlagoWiFi.xml
netsh wlan connect name=FlagoWiFi
第一行先利用匯出的 xml 檔在新的電腦中建立 profile, 接著使用此 profile 連上 AP, 連接的過程中不需要自行輸入 SSID 或是金鑰。你也可以將之製作成批次檔, 就可以快速讓電腦連上指定的 AP 了。

如果電腦上有多個無線網卡時, 上述指令都需要額外加上 interface 參數, 指定無線網卡的介面名稱。

星期六, 9月 14, 2013

Linux 下 Eclipse 的 Java 原始碼編碼設定

由於 Eclipse 的 Java 原始碼預設採用 OS 的編碼, 這在中文 Windows 上是 big-5, 但是在 Linux 上卻是 UTF-8, 所以如果在 Linux 上匯入一個原本是在 Windows 上開發的專案, 就可能會遇到 Java 原始碼中的中文顯示亂碼的情況, 這時可以在 Linux 的 Eclipse 上選取『Window/Preferences』開啟 Preferences 交談窗, 接著切換到『General/Appearance/Content Types』後, 在右側點開『Text/Java Source File』, 並在下方的『Default encoding』填入『BIG5』即可, 詳細畫面如下圖:

星期三, 8月 28, 2013

超簡短的行動裝置瀏覽器 JavaScript 偵測技巧

剛剛看到 stackoverflow 上一篇超讚的回覆, 使用以下的技巧偵測是否行動裝置的瀏覽器:
if(typeof window.orientation !== 'undefined'){...}
由於一般來說, PC 上的瀏覽器都沒有提供 window.orientation 屬性, 因此可以用來快速判別是否為行動裝置的瀏覽器。

當然, 隨著往後 PC 與平版、手機界線的混淆, 也許這個方法會失效, 不過目前應該還堪用, 先學起來!

星期六, 8月 24, 2013

使用 JavaScript 縮放網頁內容

同事開出了一個需求, 希望專為手機製作的網頁若是在桌機上的瀏覽器觀看時, 能夠自動縮小尺寸,不然以現在手機至少 800X400 點數以上的設計, 在手機上看起來大小適中的圖片, 到了桌機上的瀏覽器都會過大。

為了解決這個問題, 第一步是先能判別是手機還是桌機, 這在 detect mobile browser 網站已經提供有現成的 JavaScript 碼可用。接著就是希望能夠縮放網頁內容, 查了各種解法, 看起來要想透過 JavaScript 直接控制瀏覽器的縮放比例是不可行的, 只能透過 CSS 直接縮放網頁內容, 根據 stackoverflow 上這篇問答, 根據瀏覽器的種類, 可分別處理:
  • Firefox 提供有 MozTransform 屬性可以用 scale(1.0) 的格式設定元素的縮放比例, 1,0 就是原尺寸不縮放。
  • Chrome 或是 IE 則可用 zoom 屬性, 以 100% 的格式設定縮放比例。
只要將上述屬性套用在body 元素的 CSS 上, 就可以將整個網頁內容縮放成指定的比例。

不過對於 Firefox, 縮放後網頁的上下左右都會留下大塊的空白區域, 使得縮放後的內容不識顯示在瀏覽器的中央上方處, 要解決這個問題, 必須再使用 MozTransformOrigin 屬性設定為 "center top" 才行。

利用上述方式寫成的 JavaScript 碼如下:

window.onload = function(){
    var a = navigator.userAgent||navigator.vendor||window.opera;
    var level = 60;
    if (!(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))) {
     if (navigator.userAgent.indexOf('Firefox') != -1 && parseFloat(navigator.userAgent.substring(navigator.userAgent.indexOf('Firefox') + 8)) >= 3.6){//Firefox)
         document.body.style.MozTransform = "scale(" + level/100 + ")";
         document.body.style.MozTransformOrigin = "center top";
     }
     else {
      document.body.style.zoom = "" + level + "%";
      document.body.style.msTransformOrigin = "center top";
     }
 }   
};

這個解法在 IE 上也有和 Firefox 類似的問題, 就是網頁內容會偏向瀏覽器視窗的左邊, 而不是在中間上方, 目前還沒有找到解決的方法, 很可惜。

星期四, 8月 15, 2013

CSS 忍者的預載圖檔好威

在 Stackoverflow 上看到 CSS 忍者的這個預載圖檔技巧, 真的是太漂亮了, 只需要 CSS, 不用加上額外隱藏的 div 標籤, 也不需要 JavaScipt,就是利用 CSS generated contnet 在 body 之後產生一些不會顯示的內容, 利用這個功能載入所需的圖檔:
    body:after {
        content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
        display: none;
    }
Even better image preloading with CSS2 | The CSS Ninja - All things CSS, JavaScript & HTML

星期五, 8月 09, 2013

分享至 Facebook 時自訂網頁的標題、摘要、以及圖片

因為工作上的需要, 趕緊上網找了一下, 在以下這個網頁找到了答案:

Facebook 設定分享網頁的標題 + 內文 + 圖片 @ 小雕雕的家 :: 痞客邦 PIXNET ::

只要填入以下的 metatag, 就可以自訂許多分享的項目, 而不是由 facebook 的分享程式自己抓了:





另外, 因為分享時 Facebook 會使用 scraper 剪貼程式到要分享的網頁上搜尋摘要資訊, 並暫存下來, 如果您如上修改之後, 在分享時看到的仍是舊的摘要, 請記得到 Facebook Developer Tools 下的 debug 頁面, 輸入要分享的網址, 就可以清除 Facebook 中暫存的資料了。

StateCounter 網路分析服務的智慧型手機市佔

Source: StatCounter Global Stats - Mobile OS Market Share

星期日, 3月 24, 2013

JavaScript 的 var

今天有同事問我 JavaScript 變數有加 var 宣告跟沒有有什麼差?我聽了也是一愣, 後來去查了文件才發現, 原來 var 不 var 可有差了, 主要大概可以歸納為這兩點:
  1. JavaScript 是以 function 來建立新的變數 scope, 只要是在 function 之外宣告的變數都是全域變數, 也就是全域物件的屬性, 而只有全域變數可以不用加 var 宣告, 就可以直接使用。
  2. 有加 var 宣告的全域變數是全域物件 (在瀏覽器中就是 window) 的 non-configurable 屬性, 比如說你就無法使用 delete 刪除以 var 宣告的全域變數。
舉例來說, 以這個 html 檔為例:
<!DOCTYPE html>
<html>
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
  <div id="da"></div>
  <div id="db"></div>
  <div id="dc"></div>
  <div id="dd"></div>
<script>
i = 10;
var j = 20; 
this.i = 100;
this.j = 200;

$("#da").text(i);
$("#db").text(j);
$("#dc").text(this.i);
$("#dd").text(window.j);
</script>
 
</body>
</html>
實際輸出的結果就是:
100
200
100
200
你可以看到不論是用 i 或是 this.i 都是同一份資料, 這是因為 this 指向全域物件, 所以也可以用 window.j 以存取 j, 因為在瀏覽器中, window 就是全域物件。

如果你在剛剛的 JavaScript 程式中插入兩航程式, 嘗試用 delete 刪除 i 與 j:
i = 10;
var j = 20; 
this.i = 100;
this.j = 200;

delete this.i;
delete this.j;

$("#da").text(i);
$("#db").text(j);
$("#dc").text(this.i);
$("#dd").text(window.j);
在瀏覽器的 JavaScript console 中就會看到以下的錯誤訊息:
Uncaught ReferenceError: i is not defined
這是因為 i 並沒有使用 var 宣告, 是全域物件的 configurable 屬性, 因此可以用 delete 刪除, 所以當執行到底下存取變數 i 的值時, i 變數就已經不存在了。

事情還沒完, 如果你是使用 node.js, 那麼又複雜了一點, 使用 var 宣告的全域變數實際上不是真的全域, 而只能在模組 (可簡單看成是檔案) 內存取, 出了模組, 就無法存取了。以底下的程式為例:
i = 10;
var j =20;

console.log(global.i);
console.log(global.j);
console.log(i);
console.log(j);
執行結果如下:
10
undefined
10
20
透過 node.js 的全域物件 global 可存取到沒有使用 var 宣告的 i 變數, 這是真正全域的變數。可是透過 global 物件嘗試存取 j 變數時, 就會看到 global.j 是未定義的屬性, 這表示 j 並不是真正全域的變數。

如果另外寫個程式把剛剛的測試程式當成模組載入:
var tv = require("./testvar.js");

console.log("global==============");
console.log(i);
//console.log(j);
輸出結果如下:
10
undefined
10
20
global==============
10
可以看到在程式中可存取到在另一個檔案中宣告的變數 i, 的確是全域變數無誤。若是把程式中最後一行的註解拿掉, 嘗試存取 j, 就會看到:
10

D:\mee\My Box Files\practice\jswebapp\testvar2.js:5
console.log(j);
^
ReferenceError: j is not defined
at Object. (D:\mee\My Box Files\practice\jswebapp\testvar2.js:5:13)
可以看到 j 並未定義, 這是因為 j 是用 var 在其他檔案中宣告的變數, 不是全域變數, 所以無法存取。

星期一, 2月 18, 2013

InDesign 的印刷體引號 (typographer’s quotes)

今天同事反應用 InDesign 匯入我給的 Word 檔文稿後, 幾乎所有的半形引號 (就是 ' 與 ") 都自動變成全形的配對引號 (就是 ‘ 與 ’, 以及 “ 與 ”), 查了一下 google, 根據
Get Smart with Smart Quotes 這篇文章, 原來 InDesign 在置入 Word 檔時有個選項是「使用印刷體引號」, 會「很聰明」的自動把引號都轉成配對的引號, 只要在匯入 Word 檔時, 勾選「顯示選項」, 並在「匯入選項」的交談窗中不要勾選「使用印刷體引號」就可以了!

補充一下, 這個功能就跟 Word 中的「智慧引號 (smart quotes)」一樣討厭, Word 預設在「自動校正」的交談窗中, 會在「輸入時自動套用格式」的頁次中勾選「將一般引號取代為智慧引號」, 也會再你輸入時即時自動轉換引號, 每次我安裝 Office 軟體後, 第一次使用 Word 時最先做的事情就是把這個選項取消, 避免看到討厭的配對引號。

星期四, 1月 03, 2013

讓 Android 模擬器顯示虛擬按鈕

Android 的模擬器在小尺寸的設定時, 預設都是假設裝置是有 home、menu、back 這些硬體按鈕, 如果你要測試一些 Android 3.0 之後的功能, 像是在 action bar 顯示功能選單, 就會因為預設的設定而不會出現在 action bar 中, 仍然是從螢幕下方滑出來。如果要設定成沒有這些硬體按鈕的裝置, 就必須手動更改模擬器的設定檔。

首先啟動 Android Virtual Device Manager, 會在上方看到模擬器的資料存放的資料夾:


在該資料夾中, 每個模擬器都會以模擬器的名字建立專屬的資料夾。在個別模擬器的資料夾中, 有個 config.ini 檔, 就是有關該模擬器的基本設定:


開啟 config.ini 檔, 找到以下這行:
hw.mainKeys=yes
把設定值改為 no, 存檔後再重新啟動模擬器, 就可以看到模擬器顯示虛擬的按鈕了: