顯示具有 分享好物 標籤的文章。 顯示所有文章
顯示具有 分享好物 標籤的文章。 顯示所有文章
2014-04-25 00:36

利用 Google Script 將 Blogger 備份到 Google Drive

在 Google Drive 中建立『指令碼』



然後選擇『空白專案』



貼上以下程式碼
/*
Ref:
https://developers.google.com/apps-script/reference/drive/drive-app
https://developers.google.com/apps-script/reference/drive/file
https://developers.google.com/apps-script/reference/drive/folder
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app
https://developers.google.com/apps-script/reference/url-fetch/o-auth-config
https://developers.google.com/apps-script/reference/base/blob
https://developers.google.com/apps-script/reference/utilities/utilities

*/

var _backupKeepAmount = 10;
var _backupFolderName = 'blogger_backup';
var _backupFolder;



function main(){
  var folders = DriveApp.getFoldersByName(_backupFolderName);

  if(folders.hasNext()){
    _backupFolder = folders.next();
  }else{
    _backupFolder = DriveApp.createFolder(_backupFolderName);
  }


  setAuth();

  backupBlogger('{my_blog_1}', '{blog_id}', false);
  backupBlogger('{my_blog_2}', '{blog_id}', false);

  MailApp.sendEmail(Session.getActiveUser().getEmail(), 'Backup Blogger To Google Drive', Logger.getLog());
}


/**
 * @param {String} prefixName 備份檔名的前綴
 * @param {String} blogId
 * @param {Boolean} isBigSize 如果備份的檔案超過 10M,請設為 true
 */
function backupBlogger(prefixName, blogId, isBigSize){
  logMsg('Backup Start', prefixName);


  /* 取得之前的備份清單 */
  var files = _backupFolder.getFilesByType('application/xml');
  var beforeFiles = [];
  while (files.hasNext()) {
    var file = files.next();
    if(file.getName().indexOf(prefixName) == -1){ continue; }

    beforeFiles.push(file);
  }

  beforeFiles.sort(function (a, b) {
    return b.getName().localeCompare(a.getName());
  });


  /* 下載並儲存檔案 */
  var isChange;
  if(isBigSize){
    isChange = downloadAndSaveBigSizeArchiveXml(prefixName, blogId, beforeFiles[0]);
  }else{
    isChange = downloadAndSaveArchiveXml(prefixName, blogId, beforeFiles[0]);
  }

  /*沒有異動結束處理*/
  if(!isChange){ return; }


  /* 刪除舊檔案 */
  var oleFiles = beforeFiles.slice(_backupKeepAmount);
  for(var i=0; i < oleFiles.length; i++){
    oleFiles[i].setTrashed(true);
  }


  logMsg('Backup Complete', prefixName);
}




function logMsg(status, msg){
  Logger.log('%s | %s', status, msg);
}



function setAuth(){
  var scope = "https://www.blogger.com/feeds/";
  var oAuthConfig = UrlFetchApp.addOAuthService("blogger");
  oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
  oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
  oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");

//  oAuthConfig.setConsumerKey("anonymous");
//  oAuthConfig.setConsumerSecret("anonymous");
}



function getArchiveXmlResponse(blogId){
  var options = {
      "oAuthServiceName" : "blogger",
      "oAuthUseToken" : "always"
  };

  var url = 'https://www.blogger.com/feeds/' + blogId + '/archive';
  var response = UrlFetchApp.fetch(url, options);

  /*下載失敗,錯誤記錄*/
  if(response.getResponseCode() != 200){
    logMsg('Download Failed', response.getAllHeaders().toSource());
    return null;
  }

  return response;
}


function downloadAndSaveArchiveXml(prefixName, blogId, beforeFile){
  var response = getArchiveXmlResponse(blogId);
  if(!response){ return false; }

  var downloadContent = response.getContentText();

  /* 檢查檔案異動 */
  if(beforeFile){
    var content = beforeFile.getBlob().getDataAsString();

    if(beforeFile && Math.abs(content.length -downloadContent.length) < 100){
      logMsg('Not Change', prefixName);
      return false;
    }
  }

  /* 儲存下載 */
  var fileName = Utilities.formatDate(new Date(), "+8", "yyyyMMdd_HHmmss'.xml'");
  _backupFolder.createFile(prefixName + '_' + fileName, downloadContent, 'application/xml');

  return true;
}



function downloadAndSaveBigSizeArchiveXml(prefixName, blogId, beforeFile){
  var response = getArchiveXmlResponse(blogId);
  if(!response){ return false; }


  /* 儲存下載 */
  var fileName = Utilities.formatDate(new Date(), "+8", "yyyyMMdd_HHmmss'.xml'");
  var downloadBlob = response.getBlob();
  downloadBlob.setName(prefixName + '_' + fileName);
  downloadBlob.setContentType('application/xml');
  var downloadFile = _backupFolder.createFile(downloadBlob);


  /* 檢查檔案異動 */
  if(beforeFile && Math.abs(beforeFile.getSize() - downloadFile.getSize() ) < 1000){
    downloadFile.setTrashed(true);
    logMsg('Not Change', prefixName);
    return false;
  }

  return true;
}


修改 {my_blog_1} 及 {blog_id},{my_blog_1} 是備份檔名的前綴,{blog_id} 則可以在文章管理的網址上找到



儲存檔案,並取名為『Backup Blogger To Google Drive』




先來測試一下備份是否能正常執行,請選擇執行的函數為『main』,並按下『執行』,然後先為此程式授權





如果都沒問題,接著來設定排程,選擇『啟動程序』



新增一個觸發程序



選擇執行的函數『main』,然後時間定在晚上 11 點,接著選擇『通知』



這個設定主要是發生錯誤的時候可以寄送通知

2014-02-18 22:26

[jQuery] 製作 $.fn.delayAll

jQuery 有提供 delay 這個方法,可惜只能用在動畫操作上,想要做到下面的事情就只能用 setTimeout 了。
// not working
$('div').css('color','red').delay(200).css('color','blue');


// use setTimeout
$('div').css('color','red');

setTimeout(function(){
    $('div').css('color','blue');
}, 200);


稍微研究了一下,發現 jQuery 本身有製作 queue 的功能,這個用來存放 delay 後要執行動作的紀錄器,最後只要對 jQuery Object 作一個代理器的包裝,就可以達到想要的目的了。
var queueName = 'delayAll';

/*定義 jQuery Delay 代理類別*/
function jQueryDelay(jqObject, duration){
    /*將缺少的成員屬性補上*/
    for(var member in jqObject){
        if(!$.isFunction(jqObject[member]) || !this[member]){
            this[member] = jqObject[member];
        }
    }

    /*新增 delay 時間並啟動 queue*/
    jqObject
        .delay(duration, queueName)
        .dequeue(queueName);

    /*紀錄 jQuery 物件*/
    this._jqObject = jqObject;
};


/*為所有的 jQuery 方法製作 proxy*/
for(var member in $.fn){
    if(!$.isFunction($.fn[member])){ continue; }

    jQueryDelay.prototype[member] = function(){
        var jqObject = this._jqObject;
        var args = Array.prototype.slice.call(arguments);
        var mothed = arguments.callee.methodName;

        /*將需要執行動作加入 queue*/
        jqObject.queue(queueName, function(next) {
            jqObject[mothed].apply(jqObject, args);
            next();
        });

        return this;
    };

    /*紀錄方法名稱,在 proxy 時會需要參考到*/
    jQueryDelay.prototype[member].methodName = member;
}


/*允許多次串接的可能*/
jQueryDelay.prototype.delayAll = function(duration){
    this._jqObject.delay(duration, queueName);
    return this;
};


/*用 jQueryDelay 將原本的 jQuery Object 包裝起來*/
$.fn.delayAll = function(duration){
    return new jQueryDelay(this ,duration);
};


使用範例:
$('div').css('color','#f00')
    .delayAll(2000).css('color','#0f0')
    .delayAll(2000).css('color','#00f');


檔案下載:jquery.delayAll.js
2014-01-21 00:23

[Less] 簡單做到背景漸層

對選顏色不擅長,又想在網頁做出 CSS 漸層效果,Less 提供了兩個方便的函數 lighten(加亮顏色)、darken(加深顏色),透過這兩個函數就可以輕鬆產生漸層所需要的色差,然後調整百比值就可以控制漸層的色階。

.toolbar {
    @color: #914;
    @lighten: lighten(@color, 3%);
    @darken: darken(@color, 3%);

    background-color: @color;
    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@lighten), to(@darken));
    background-image: -webkit-linear-gradient(top, @lighten, @darken);
    background-image: -moz-linear-gradient(top, @lighten, @darken);
    background-image: -ms-linear-gradient(top, @lighten, @darken);
    background-image: -o-linear-gradient(top, @lighten, @darken);
    background-image: linear-gradient(to bottom, @lighten, @darken);
}

上面除了用 linear-gradient 來產生漸層,額外還加上了 background-color 這個保險,讓不支援 CSS3 的 browser 也能有基本的底色。


這樣寫還是有點麻煩,如果包成 mixin 會更方便,如下:
.bg-vertical-gradient(@color, @amount:3%) {
    @lighten: lighten(@color, @amount);
    @darken: darken(@color, @amount);

    background-color: @color;
    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@lighten), to(@darken));
    background-image: -webkit-linear-gradient(top, @lighten, @darken);
    background-image: -moz-linear-gradient(top, @lighten, @darken);
    background-image: -ms-linear-gradient(top, @lighten, @darken);
    background-image: -o-linear-gradient(top, @lighten, @darken);
    background-image: linear-gradient(to bottom, @lighten, @darken);
}

.toolbar {
    .bg-vertical-gradient(#914);
}

.banner {
    .bg-vertical-gradient(#702, 5%);
}
2013-08-25 16:43

[轉載] 字符串匹配的 Boyer-Moore 演算法

轉載自:字符串匹配的Boyer-Moore算法 - 阮一峰的网络日志

上一篇文章,我介绍了KMP算法

但是,它并不是效率最高的算法,实际采用并不多。各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法



Boyer-Moore算法不仅效率高,而且构思巧妙,容易理解。1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了这种算法。

下面,我根据Moore教授自己的例子来解释这种算法。


1.



假定字符串为"HERE IS A SIMPLE EXAMPLE",搜索词为"EXAMPLE"。



2.



首先,"字符串"与"搜索词"头部对齐,从尾部开始比较。

这是一个很聪明的想法,因为如果尾部字符不匹配,那么只要一次比较,就可以知道前7个字符(整体上)肯定不是要找的结果。

我们看到,"S"与"E"不匹配。这时,"S"就被称为"坏字符"(bad character),即不匹配的字符。我们还发现,"S"不包含在搜索词"EXAMPLE"之中,这意味着可以把搜索词直接移到"S"的后一位。



3.



依然从尾部开始比较,发现"P"与"E"不匹配,所以"P"是"坏字符"。但是,"P"包含在搜索词"EXAMPLE"之中。所以,将搜索词后移两位,两个"P"对齐。



4.



我们由此总结出"坏字符规则"

后移位数 = 坏字符的位置 - 搜索词中的上一次出现位置

如果"坏字符"不包含在搜索词之中,则上一次出现位置为 -1。

以"P"为例,它作为"坏字符",出现在搜索词的第6位(从0开始编号),在搜索词中的上一次出现位置为4,所以后移 6 - 4 = 2位。再以前面第二步的"S"为例,它出现在第6位,上一次出现位置是 -1(即未出现),则整个搜索词后移 6 - (-1) = 7位。



5.



依然从尾部开始比较,"E"与"E"匹配。



6.



比较前面一位,"LE"与"LE"匹配。



7.



比较前面一位,"PLE"与"PLE"匹配。



8.



比较前面一位,"MPLE"与"MPLE"匹配。我们把这种情况称为"好后缀"(good suffix),即所有尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好后缀。



9.



比较前一位,发现"I"与"A"不匹配。所以,"I"是"坏字符"。



10.



根据"坏字符规则",此时搜索词应该后移 2 - (-1)= 3 位。问题是,此时有没有更好的移法?



11.



我们知道,此时存在"好后缀"。所以,可以采用"好后缀规则"

后移位数 = 好后缀的位置 - 搜索词中的上一次出现位置

举例来说,如果字符串"ABCDAB"的后一个"AB"是"好后缀"。那么它的位置是5(从0开始计算,取最后的"B"的值),在"搜索词中的上一次出现位置"是1(第一个"B"的位置),所以后移 5 - 1 = 4位,前一个"AB"移到后一个"AB"的位置。

再举一个例子,如果字符串"ABCDEF"的"EF"是好后缀,则"EF"的位置是5 ,上一次出现的位置是 -1(即未出现),所以后移 5 - (-1) = 6位,即整个字符串移到"F"的后一位。

这个规则有三个注意点:

  1. "好后缀"的位置以最后一个字符为准。假定"ABCDEF"的"EF"是好后缀,则它的位置以"F"为准,即5(从0开始计算)。
  2. 如果"好后缀"在搜索词中只出现一次,则它的上一次出现位置为 -1。比如,"EF"在"ABCDEF"之中只出现一次,则它的上一次出现位置为-1(即未出现)。
  3. 如果"好后缀"有多个,则除了最长的那个"好后缀",其他"好后缀"的上一次出现位置必须在头部。比如,假定"BABCDAB"的"好后缀"是"DAB"、"AB"、"B",请问这时"好后缀"的上一次出现位置是什么?回答是,此时采用的好后缀是"B",它的上一次出现位置是头部,即第0位。这个规则也可以这样表达:如果最长的那个"好后缀"只出现一次,则可以把搜索词改写成如下形式进行位置计算"(DA)BABCDAB",即虚拟加入最前面的"DA"。

回到上文的这个例子。此时,所有的"好后缀"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"还出现在头部,所以后移 6 - 0 = 6位。



12.



可以看到,"坏字符规则"只能移3位,"好后缀规则"可以移6位。所以,Boyer-Moore算法的基本思想是,每次后移这两个规则之中的较大值。

更巧妙的是,这两个规则的移动位数,只与搜索词有关,与原字符串无关。因此,可以预先计算生成《坏字符规则表》和《好后缀规则表》。使用时,只要查表比较一下就可以了。



13.



继续从尾部开始比较,"P"与"E"不匹配,因此"P"是"坏字符"。根据"坏字符规则",后移 6 - 4 = 2位。



14.



从尾部开始逐位比较,发现全部匹配,于是搜索结束。如果还要继续查找(即找出全部匹配),则根据"好后缀规则",后移 6 - 0 = 6位,即头部的"E"移到尾部的"E"的位置。

(完)
2013-08-25 16:07

[轉載] 虚数的意义

轉載自:虚数的意义 - 阮一峰的网络日志

有人在Stack Exchange问了一个问题:

"我一直觉得虚数(imaginary number)很难懂。

中学老师说,虚数就是-1的平方根。



可是,什么数的平方等于-1呢?计算器直接显示出错!

直到今天,我也没有搞懂。谁能解释,虚数到底是什么?

它有什么用?"

帖子的下面,很多人给出了自己的解释,还推荐了一篇非常棒的文章《虚数的图解》。我读后恍然大悟,醍醐灌顶,原来虚数这么简单,一点也不奇怪和难懂!

下面,我就用自己的语言,讲述我所理解的虚数。




一、什么是虚数?


首先,假设有一根数轴,上面有两个反向的点:+1和-1。



这根数轴的正向部分,可以绕原点旋转。显然,逆时针旋转180度,+1就会变成-1。



这相当于两次逆时针旋转90度。



因此,我们可以得到下面的关系式:

(+1) * (逆时针旋转90度) * (逆时针旋转90度) = (-1)

如果把+1消去,这个式子就变为:

(逆时针旋转90度)^2 = (-1)

将"逆时针旋转90度"记为 i :

i^2 = (-1)

这个式子很眼熟,它就是虚数的定义公式。

所以,我们可以知道,虚数 i 就是逆时针旋转90度,i 不是一个数,而是一个旋转量。




二、复数的定义


既然 i 表示旋转量,我们就可以用 i ,表示任何实数的旋转状态。



将实数轴看作横轴,虚数轴看作纵轴,就构成了一个二维平面。旋转到某一个角度的任何正实数,必然唯一对应这个平面中的某个点。

只要确定横坐标和纵坐标,比如( 1 , i ),就可以确定某个实数的旋转量(45度)。

数学家用一种特殊的表示方法,表示这个二维坐标:用 + 号把横坐标和纵坐标连接起来。比如,把 ( 1 , i ) 表示成 1 + i 。这种表示方法就叫做复数(complex number),其中 1 称为实数部,i 称为虚数部。

为什么要把二维坐标表示成这样呢,下一节告诉你原因。




三、虚数的作用:加法


虚数的引入,大大方便了涉及到旋转的计算。



比如,物理学需要计算"力的合成"。假定一个力是 3 + i ,另一个力是 1 + 3i ,请问它们的合成力是多少?



根据"平行四边形法则",你马上得到,合成力就是 ( 3 + i ) + ( 1 + 3i ) = ( 4 + 4i )。

这就是虚数加法的物理意义。




四、虚数的作用:乘法


如果涉及到旋转角度的改变,处理起来更方便。



比如,一条船的航向是 3 + 4i 。

如果该船的航向,逆时针增加45度,请问新航向是多少?



45度的航向就是 1 + i 。计算新航向,只要把这两个航向 3 + 4i 与 1 + i 相乘就可以了(原因在下一节解释):

( 3 + 4i ) * ( 1 + i ) = ( -1 + 7i )

所以,该船的新航向是 -1 + 7i 。

如果航向逆时针增加90度,就更简单了。因为90度的航向就是 i ,所以新航向等于:

( 3 + 4i ) * i = ( -4 + 3i )

这就是虚数乘法的物理意义:改变旋转角度。




五、虚数乘法的数学证明


为什么一个复数改变旋转角度,只要做乘法就可以了?

下面就是它的数学证明,实际上很简单。



任何复数 a + bi,都可以改写成旋转半径 r 与横轴夹角 θ 的形式。

假定现有两个复数 a + bi 和 c + di,可以将它们改写如下:

a + bi = r1 * ( cosα + isinα )

c + di = r2 * ( cosβ + isinβ )


这两个复数相乘,( a + bi )( c + di ) 就相当于

r1 * r2 * ( cosα + isinα ) * ( cosβ + isinβ )


展开后面的乘式,得到

cosα * cosβ - sinα * sinβ + i( cosα * sinβ + sinα * cosβ )


根据三角函数公式,上面的式子就等于

cos(α+β) + isin(α+β)


所以,

( a + bi )( c + di ) = r1 * r2 * ( cos(α+β) + isin(α+β) )


这就证明了,两个复数相乘,就等于旋转半径相乘、旋转角度相加。

(完)
2013-08-17 14:55

[PHP] url, base64, sprite 三種格式的 icons.css 產生器

先做一個假設,如果 icon 的檔名就是 css 的 class 樣式名稱,那麼我們只要掃瞄資料夾的 Icon 圖檔,然後產生對應的 CSS 檔案,這樣就可以省去製作 Sprite 圖檔跟維護 CSS 對應的問題。

第一種 url 格式只是取得路徑的問題。

第二種 base64 格式可以透過 base64_encode(file_get_contents($path)); 就簡單的達成。

第三種 sprite 格式則使用 Imagick 去處理,會比較快樂。


接著以下就是如何達成的程式片段:

<?php

/*把目錄改變到當前文件下*/
chdir(dirname(__FILE__));

/*Sprite 圖與圖的間距*/
$spriteGap = 30;


/*=[ 取得圖檔資訊 ]=*/
$maxWidth = 0;
$maxHeight = 0;
$nextTop = 0;
$imageList = array();

foreach ( glob('icons/*.{png,jpg,gif}',GLOB_BRACE) as $path )
{
    $image = new Imagick($path);
    $name = pathinfo($path,PATHINFO_FILENAME);

    if(isset($imageList[$name])){ 
        throw new Exception("圖片名稱重複 [ $name ]"); 
    }

    $info = array(
        '{top}' => $nextTop,
        '{image}' => $image,
        '{width}' => $image->getImageWidth(),
        '{height}' => $image->getImageHeight(),
        '{name}' => $name,
        '{path}' => $path,
        '{isAnimate}' => false
    );

    $header = '';
    switch($image->getImageFormat()){
        case "PNG":
            $header = 'data:image/png;base64,'; break;
        case "JPEG":
            $header = 'data:image/jpeg;base64,'; break;
        case "GIF":
            $header = 'data:image/gif;base64,'; break;
        default: break;
    }

    $info['{uri}'] = $header.base64_encode(file_get_contents($path));

    $maxWidth = max($maxWidth, $info['{width}']);
    $maxHeight = max($maxHeight, $info['{height}']);

    
    /*檢查圖片是否為動畫*/
    $frameNum = 0;
    foreach($image->deconstructImages() as $i) {
        $frameNum++;
        if ($frameNum > 1) {
            $info['{isAnimate}'] = true;
            break;
        }
    }
    
    if(!$info['{isAnimate}']){
        $nextTop += $info['{height}'] + $spriteGap;
    }


    $imageList[$name] = $info;
}


/*=[ 製作 CSS Sprite 圖檔 ]=*/
$spriteImage = new Imagick();
$spriteImage->newImage($maxWidth, $nextTop, new ImagickPixel());
$spriteImage->setImageFormat('png');
$spriteImage->paintTransparentImage(new ImagickPixel(), 0.0, 0);

foreach ($imageList as $name => $info)
{
    if($info['{isAnimate}']){ continue; } /* 忽略 GIF 動畫 */

    /* 複製 Icon 圖檔到 Sprite */
    $spriteImage->compositeImage(
        $info['{image}'],
        $info['{image}']->getImageCompose(),
        0,
        $info['{top}']
    );

    $info['{image}']->destroy();
    unset($imageList[$name]['{image}']);
}

$spriteImage->writeImage('icons.sprite.png');
$spriteImage->destroy();
$spriteImage = null;


下載完整程式: php_make_icons_css.zip
2012-06-04 01:29

[轉載] 自適應網頁設計

轉載自:自适应网页设计(Responsive Web Design) 阮一峰

随着 3G 的普及,越来越多的人使用手机上网。

移动设备正超过桌面设备,成为访问互联网的最常见终端。于是,网页设计师不得不面对一个难题:如何才能在不同大小的设备上呈现同样的网页?



手机的屏幕比较小,宽度通常在 600 像素以下,PC 的屏幕宽度,一般都在 1000 像素以上(目前主流宽度是1366×768),有的还达到了 2000 像素。同样的内容,要在大小迥异的屏幕上,都呈现出满意的效果,并不是一件容易的事。

很多网站的解决方法,是为不同的设备提供不同的网页,比如专门提供一个 mobile 版本,或者 iPhone / iPad 版本。这样做固然保证了效果,但是比较麻烦,同时要维护好几个版本,而且如果一个网站有多个 portal(入口),会大大增加架构设计的复杂度。

于是,很早就有人设想,能不能"一次设计,普遍适用",让同一张网页自动适应不同大小的屏幕,根据屏幕宽度,自动调整布局(layout)?




一、"自适应网页设计"的概念

2010 年,Ethan Marcotte 提出了"自适应网页设计"(Responsive Web Design)这个名词,指可以自动识别屏幕宽度、并做出相应调整的网页设计。

他制作了一个范例,里面是《福尔摩斯历险记》六个主人公的头像。如果屏幕宽度大于 1300 像素,则 6 张图片并排在一行。



如果屏幕宽度在 600 像素到 1300 像素之间,则 6 张图片分成两行。



如果屏幕宽度在 400 像素到 600 像素之间,则导航栏移到网页头部。



如果屏幕宽度在 400 像素以下,则 6 张图片分成三行。



mediaqueri.es 上面有更多这样的例子。

这里还有一个测试小工具,可以在一张网页上,同时显示不同分辨率屏幕的测试效果,我推荐安装。


二、允许网页宽度自动调整

"自适应网页设计"到底是怎么做到的?其实并不难。

首先,在网页代码的头部,加入一行 viewport元标签

<meta name="viewport" content="width=device-width, initial-scale=1" />

viewport 是网页默认的宽度和高度,上面这行代码的意思是,网页宽度默认等于屏幕宽度(width=device-width),原始缩放比例(initial-scale=1)为 1.0,即网页初始大小占屏幕面积的 100%。

所有主流浏览器都支持这个设置,包括 IE9。对于那些老式浏览器(主要是 IE6、7、8),需要使用 css3-mediaqueries.js

<!--[if lt IE 9]>
    <script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
<![endif]-->


三、不使用绝对宽度

由于网页会根据屏幕宽度调整布局,所以不能使用绝对宽度的布局,也不能使用具有绝对宽度的元素。这一条非常重要。

具体说,CSS代码不能指定像素宽度:

width: xxx px;

只能指定百分比宽度:

width: xx%;

或者

width: auto;


四、相对大小的字体

字体也不能使用绝对大小(px),而只能使用相对大小(em)。

body {
    font: normal 100% Helvetica, Arial, sans-serif;
}

上面的代码指定,字体大小是页面默认大小的 100%,即 16 像素。

h1 {
    font-size: 1.5em; 
}

然后,h1 的大小是默认大小的 1.5 倍,即 24 像素(24/16=1.5)。

small {
    font-size: 0.875em;
}

small 元素的大小是默认大小的 0.875 倍,即 14 像素(14/16=0.875)。


五、流动布局(fluid grid)

"流动布局"的含义是,各个区块的位置都是浮动的,不是固定不变的。

.main {
    float: right;
    width: 70%; 
}

.leftBar {
    float: left;
    width: 25%;
}

float 的好处是,如果宽度太小,放不下两个元素,后面的元素会自动滚动到前面元素的下方,不会在水平方向 overflow(溢出),避免了水平滚动条的出现。

另外,绝对定位(position: absolute)的使用,也要非常小心。


六、选择加载CSS

"自适应网页设计"的核心,就是 CSS3 引入的 Media Query 模块。

它的意思就是,自动探测屏幕宽度,然后加载相应的 CSS 文件。

<link rel="stylesheet" type="text/css" media="screen and (max-device-width: 400px)" href="tinyScreen.css" />

上面的代码意思是,如果屏幕宽度小于 400 像素(max-device-width: 400px),就加载 tinyScreen.css 文件。

<link rel="stylesheet" type="text/css" media="screen and (min-width: 400px) and (max-device-width: 600px)" href="smallScreen.css" />

如果屏幕宽度在 400 像素到 600 像素之间,则加载 smallScreen.css 文件。

除了用 html 标签加载 CSS 文件,还可以在现有 CSS 文件中加载。

@import url("tinyScreen.css") screen and (max-device-width: 400px);


七、CSS 的 @media 规则

同一个 CSS 文件中,也可以根据不同的屏幕分辨率,选择应用不同的 CSS 规则。

@media screen and (max-device-width: 400px) {
    .column {
        float: none;
        width: auto;
    }
    #sidebar {
        display: none;
    }
}

上面的代码意思是,如果屏幕宽度小于 400 像素,则 column 块取消浮动(float:none)、宽度自动调节(width:auto),sidebar 块不显示(display:none)。


八、图片的自适应(fluid image)

除了布局和文本,"自适应网页设计"还必须实现图片的自动缩放

这只要一行 CSS 代码:

img { max-width: 100%; }

这行代码对于大多数嵌入网页的视频也有效,所以可以写成:

img, object { max-width: 100%; }

老版本的 IE 不支持 max-width,所以只好写成:

img { width: 100%; }

此外,windows 平台缩放图片时,可能出现图像失真现象。这时,可以尝试使用 IE 的专有命令:

img { -ms-interpolation-mode: bicubic; }

或者,Ethan Marcotte 的 imgSizer.js

addLoadEvent(function() {
    var imgs = document.getElementById("content").getElementsByTagName("img");
    imgSizer.collate(imgs);
});

不过,有条件的话,最好还是根据不同大小的屏幕,加载不同分辨率的图片。有很多方法可以做到这一条,服务器端和客户端都可以实现。

(完)
2012-05-05 01:53

[轉載] 让PHP更快的提供文件下载

作者:Laruence
本文地址:http://www.laruence.com/2012/05/02/2613.html

一般来说, 我们可以通过直接让URL指向一个位于Document Root下面的文件, 来引导用户下载文件.

但是, 这样做, 就没办法做一些统计, 权限检查, 等等的工作. 于是, 很多时候, 我们采用让PHP来做转发, 为用户提供文件下载.

<?php
    $file = "/tmp/dummy.tar.gz";
    header("Content-type: application/octet-stream");
    header('Content-Disposition: attachment; filename="' 
        . basename($file) . '"');
    header("Content-Length: ". filesize($file));
    readfile($file);

但是这个有一个问题, 就是如果文件是中文名的话, 有的用户可能下载后的文件名是乱码.

于是, 我们做一下修改(参考: :

<?php
    $file = "/tmp/中文名.tar.gz";
 
    $filename = basename($file);
 
    header("Content-type: application/octet-stream");
 
    //处理中文文件名
    $ua = $_SERVER["HTTP_USER_AGENT"];
    $encoded_filename = urlencode($filename);
    $encoded_filename = str_replace("+", "%20", $encoded_filename);
    if (preg_match("/MSIE/", $ua)) {
        header('Content-Disposition: attachment; filename="'
            . $encoded_filename . '"');
    } else if (preg_match("/Firefox/", $ua)) {
        header("Content-Disposition: attachment; filename*=\"utf8''"
            . $filename . '"');
    } else {
        header('Content-Disposition: attachment; filename="'
            . $filename . '"');
    }
 
    header('Content-Disposition: attachment; filename="'
        . $filename . '"');
    header("Content-Length: ". filesize($file));
    readfile($file);

恩, 现在看起来好多了, 不过还有一个问题, 那就是readfile, 虽然PHP的readfile尝试实现的尽量高效, 不占用PHP本身的内存, 但是实际上它还是需要采用MMAP(如果支持), 或者是一个固定的buffer去循环读取文件, 直接输出.

输出的时候, 如果是Apache + PHP mod, 那么还需要发送到Apache的输出缓冲区. 最后才发送给用户. 而对于Nginx + fpm如果他们分开部署的话, 那还会带来额外的网络IO.

那么, 能不能不经过PHP这层, 直接让Webserver直接把文件发送给用户呢?

今天, 我看到了一个有意思的文章: How I PHP: X-SendFile.

我们可以使用Apache的module mod_xsendfile, 让Apache直接发送这个文件给用户:

<?php
    $file = "/tmp/中文名.tar.gz";
 
    $filename = basename($file);
 
    header("Content-type: application/octet-stream");
 
    //处理中文文件名
    $ua = $_SERVER["HTTP_USER_AGENT"];
    $encoded_filename = urlencode($filename);
    $encoded_filename = str_replace("+", "%20", $encoded_filename);
    if (preg_match("/MSIE/", $ua)) {
        header('Content-Disposition: attachment; filename="'
            . $encoded_filename . '"');
    } else if (preg_match("/Firefox/", $ua)) {
        header("Content-Disposition: attachment; filename*=\"utf8''"
            . $filename . '"');
    } else {
        header('Content-Disposition: attachment; filename="'
            . $filename . '"');
    }
 
    header('Content-Disposition: attachment; filename="'
            . basename($file) . '"');
 
    //让Xsendfile发送文件
    header("X-Sendfile: $file");

X-Sendfile头将被Apache处理, 并且把响应的文件直接发送给Client.

Lighttpd和Nginx也有类似的模块, 大家有兴趣的可以去找找看


mod-xsendfile 在 Ubuntu 的安裝方法:
sudo apt-get install libapache2-mod-xsendfile
2012-05-05 01:27

[PHP] 使用 php5-ffmpeg 擷取影片圖片

前幾天在玩 FFmpeg 的時後,突然發現 Ubuntu 上多了 php5-ffmpeg 這個擴充套件,就想來玩玩看,看好不好用,有兩個結論:
  1. 讀取影片取決於 FFmpeg 的支援性,如果想要什麼格式都支援的話,建議自己重新編譯 FFmpeg。
  2. 效率並沒有我想像中的快,兩分鐘的影片取十張圖,大約 30 秒。
安裝方法:
sudo apt-get install ffmpeg php5-ffmpeg php5-gd

擷圖測試範例:
<?php
$page = 10;
$prefix = 'screencap';

$mov = new ffmpeg_movie('gt.avi');
$count = $mov->getFrameCount();
$range = (int)round($count/($page+1));

for($i=1; $i<=$page; $i++){
    $frameNum = $range*$i;
    $imgFile = $prefix.'_'.$i.'.png';

    $frame = $mov->getFrame($frameNum);
    if(!$frame){ continue; }

    $gdImage = $frame->toGDImage();
    if(!$gdImage){ continue; }

    imagepng($gdImage, $imgFile);
    imagedestroy($gdImage);

    echo '<img src="'.$imgFile.'" border="1" /><br />';
}

參考文件:
ffmpeg_movie object methods
FFmpeg and PHP
2011-07-11 17:43

為 Blogger 的地點標記加上 Google Maps 呈現

Blogger 增加了一個標記地點的功能,但只有標記也不知道能拿來做什麼,後來想想用JS寫了一個呈現的小程式,讓文章中設定的資訊可以直接呈現。

以下程式不支援 IE,因為我不想在我的 Blog 上用 Framework,所以 IE 就沒辦法呈現,哈!只要將以下其中一個的程式碼加在 </body> 之前就可以了。

在[文章]的最[前面]插入地圖
<script type="text/javascript">
function showGoogleMap(pos){
    var w=300,h=300,z=14;
try {
    var postOuter=document.getElementsByClassName(&#039;post-outer&#039;);
    for(var i=0; i&lt;postOuter.length; i++){
        var postLocation=postOuter[i].getElementsByClassName(&#039;post-location&#039;)[0];
        var point=postLocation.getElementsByTagName(&#039;a&#039;)[0];
        if(!point){continue;}
        var mapIframe=&#039;&lt;iframe width=&quot;&#039;+w+&#039;&quot; height=&quot;&#039;+h+&#039;&quot; src=&quot;&#039;+point.href+&#039;&amp;output=embed&amp;iwloc=z&amp;z=&#039;+z+&#039;&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;&#039;;
        var postBody=postOuter[i].getElementsByClassName(&#039;post-body&#039;)[0];

        switch(pos){
            case &#039;loc-a&#039;:
                postLocation.innerHTML+=mapIframe;
                break;
            case &#039;loc-b&#039;:
                postLocation.innerHTML=mapIframe+postLocation.innerHTML;
                break;
            case &#039;post-a&#039;:
                postBody.innerHTML+=mapIframe;
                break;
            case &#039;post-b&#039;:
            default:
                postBody.innerHTML=mapIframe+postBody.innerHTML;
                break;
        }
    }
}catch(e){}
}
showGoogleMap(&#039;post-b&#039;);
</script>

在[文章]的最[後面]插入地圖
<script type="text/javascript">
function showGoogleMap(pos){
    var w=300,h=300,z=14;
try {
    var postOuter=document.getElementsByClassName(&#039;post-outer&#039;);
    for(var i=0; i&lt;postOuter.length; i++){
        var postLocation=postOuter[i].getElementsByClassName(&#039;post-location&#039;)[0];
        var point=postLocation.getElementsByTagName(&#039;a&#039;)[0];
        if(!point){continue;}
        var mapIframe=&#039;&lt;iframe width=&quot;&#039;+w+&#039;&quot; height=&quot;&#039;+h+&#039;&quot; src=&quot;&#039;+point.href+&#039;&amp;output=embed&amp;iwloc=z&amp;z=&#039;+z+&#039;&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;&#039;;
        var postBody=postOuter[i].getElementsByClassName(&#039;post-body&#039;)[0];

        switch(pos){
            case &#039;loc-a&#039;:
                postLocation.innerHTML+=mapIframe;
                break;
            case &#039;loc-b&#039;:
                postLocation.innerHTML=mapIframe+postLocation.innerHTML;
                break;
            case &#039;post-a&#039;:
                postBody.innerHTML+=mapIframe;
                break;
            case &#039;post-b&#039;:
            default:
                postBody.innerHTML=mapIframe+postBody.innerHTML;
                break;
        }
    }
}catch(e){}
}
showGoogleMap(&#039;post-a&#039;);
</script>

在[地點]的[前面]插入地圖
<script type="text/javascript">
function showGoogleMap(pos){
    var w=300,h=300,z=14;
try {
    var postOuter=document.getElementsByClassName(&#039;post-outer&#039;);
    for(var i=0; i&lt;postOuter.length; i++){
        var postLocation=postOuter[i].getElementsByClassName(&#039;post-location&#039;)[0];
        var point=postLocation.getElementsByTagName(&#039;a&#039;)[0];
        if(!point){continue;}
        var mapIframe=&#039;&lt;iframe width=&quot;&#039;+w+&#039;&quot; height=&quot;&#039;+h+&#039;&quot; src=&quot;&#039;+point.href+&#039;&amp;output=embed&amp;iwloc=z&amp;z=&#039;+z+&#039;&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;&#039;;
        var postBody=postOuter[i].getElementsByClassName(&#039;post-body&#039;)[0];

        switch(pos){
            case &#039;loc-a&#039;:
                postLocation.innerHTML+=mapIframe;
                break;
            case &#039;loc-b&#039;:
                postLocation.innerHTML=mapIframe+postLocation.innerHTML;
                break;
            case &#039;post-a&#039;:
                postBody.innerHTML+=mapIframe;
                break;
            case &#039;post-b&#039;:
            default:
                postBody.innerHTML=mapIframe+postBody.innerHTML;
                break;
        }
    }
}catch(e){}
}
showGoogleMap(&#039;loc-b&#039;);
</script>


實際呈現樣式:


2011-07-11 14:49

[轉載] 屬性(Attribute)與特性(Property)

之前看到jQuery 1.6.1上場救援,不用改寫attr()囉這篇文章時,讓我很疑惑 Attribute 與 Property 到底是有什麼不同,在字典上查出來的結果都是屬性,爬了一些文章後找到一篇不錯的解釋文。


轉載自:屬性與特性

在進入瀏覽器作為客戶端之後,屬性(Attribute)與特性(Property)這兩個名詞就不斷交相出現,到目前還沒正式解釋它們的意義。

其實在正式進入瀏覽器作為客戶端前,對於JavaScript物件本身帶有的名稱,這邊的文件都用特性這個名詞,代表以某個JavaScript物件作為名稱空間的名稱。例如:
var obj = {
    x : 10,
    y : 20
};

以上文件都稱,物件obj擁有特性x與y,特性x的值為10,特性y的值為20。

HTML本身可以擁有屬性。例如:
<input name="user" value="guest">

文件中會稱,<input>標籤擁有屬性name與value,屬性值各為user與guest。

瀏覽器會剖析HTML,為每個標籤建立對應的DOM物件,完成剖析後,對於HTML的所有屬性(無論標籤上是否有撰寫),DOM物件上會建立對應的特性,通常屬性名稱是什麼,特性名稱也會是什麼,如果標籤上有設定某個屬性,則屬性值為何,特性值也就為何,如果標籤上沒有設置屬性,則DOM物件上的特性會有預設值

例如上例中,<input>對應的DOM元素上,name特性與value特性值分別是user與guest。你可以如下分別取得(假設是頁面中第一個<input>標籤):
var input = document.getElementsByName('user')[0];
var name = input.name;
var value = input.value;

像這時,DOM元素上的name、value特性,也可以稱之為name與value屬性。也就是說,屬性這個詞,可用來表示HTML中的屬性,也可用來表示DOM中相對應的特性。對於HTML中沒有設定的標籤屬性,DOM上也會有對應的特性(屬性),不過都是預設值,例如,上面的<input>標籤並沒有設置type屬性,但DOM物件上對應的特性(屬性),其值為"text"。

不過,HTML的屬性名稱未必與DOM物件的特性(屬性)名稱相對應。例如class就是一個例子,因為class在JavaScript中是關鍵字,在DOM上要取得HTML的class屬性對應名稱必須使用className,<label>的for屬性,因為for是關鍵字,而必須使用htmlFor特性來取得。例如:
<img id="logo" src="images/caterpillar.jpg" class="logo" title="Caterpillar's Logo"/>


若要以JavaScript取得HTML的class屬性值,則必須:
var className = document.getElementById('logo').className;

透過JavaScript特性存取方式取得HTML屬性的對應值,也未必是HTML屬性中真正設定的值。例如,透過JavaScript取得<img>的src,結果是絕對URL,即使屬性中設定的是相對URL。

瀏覽器在剖析完HTML後,對於HTML中有設置的屬性,其實會在DOM物件上建立attributes特性。你可以如下顯示attributes的元素值,每個元素的型態是Attr
var attributes = document.getElementById('logo').attributes;
for(var i = 0; i < attributes.length; i++) {
    var attrName = attributes[i].nodeName;
    var attrValue = attributes[i].nodeValue;
    ...
}

以物件結構來表示的話:
{
    attributes : {
        '0' : {nodeName : 'id', nodeValue : 'logo', ...},
        '1' : {nodeName : 'src', nodeValue : 'images/src', ...},
        '2' : {nodeName : 'class', nodeValue : 'logo', ...},
        '3' : {nodeName : 'title', nodeValue : 'Caterpillar\’s logo', ...},
        length : 4
        ...
    },
    id : 'logo',
    src : 'http://caterpillar.onlyfun.net/images/caterpillar.jpg',
    className : 'logo',
    title : 'Caterpillar\'s logo',
    …
}

attributes上的特性值,是HTML上真正設定的屬性與值。在文件剖析完畢後,DOM物件上的屬性(特性)與attributes上的特性是對應的。

注意,上面是以物件結構來示意,並不是指真正的型態就是上面所表示的。attributes的型態會是 NamedNodeMap ,而每個索引元素的型態會是 Attr(如果你手邊有個JavaScript Debugger的話,可以很方便地觀察這些東西)。

你可以使用DOM物件的getAttribute()來取得attributes中的屬性,使用setAttribute()設定attributes中的屬性(同時亦會改變DOM對應的特性),使用removeAttribute()來移除attributes屬性。

移除屬性是指移除attributes上對應的特性值,而非移除DOM物件上對應的特性(屬性)值,DOM物件上對應的特性(屬性)值在使用removeAttribute()後,只是回到預設值,而不是直接將特性移除,沒有任何操作可以將DOM上對應屬性的特性移除。如果HTML上沒有設置該屬性,則使用getAttribute()指定該屬性會取得null,但並不表示DOM上沒有對應屬性的特性,而是該特性值會是預設值。使用setAttribute()可以在attributes中設定屬性,相對應的DOM特性值也會改變。

例如,以下的程式,只會將attributes的中src對應的特性移除,不會移除DOM上src特性(屬性),DOM上src只是回到''的預設值。
var img = document.getElementById('logo');
img.removeAttribute('src');
// img.src 的值是 '',不是undefined
// img.attributes['src'] 是 undefined

如果你直接改變DOM上的特性(屬性),attributes中對應的屬性並不會有變化。例如:
<input id="user" value="guest">

使用以上的程式:
document.getElementById('user').value = 'Justin';
var user1 = document.getElementById('user').value; // 值是'Justin'
var user2 = document.getElementById('user').getAttribute('value'); // 值是'guest'

如果你要同時改變attribues上的屬性與DOM上的特性(屬性),則要使用setAttribute()。例如:
document.getElementById('user').setAttribute('value', 'Justin');
var user1 = document.getElementById('user').value; // 值'Justin'
var user2 = document.getElementById('user').getAttribute('value'); // 值'Justin'


其他參考資訊:
property和attribute的区别
JavaScript property 、 DOM property 、 HTML attribute
翻譯名詞:attribute、property
2011-04-10 23:46

Facebook 推文按鈕 失效了

之前寫過Blogger 的標題加上 Facebook 官方的推文按鈕這篇文章
最近 blogger 加了新功能用 ajax 換頁
結果 Facebook 推文按鈕在第二頁就完全不會動了

Google 一下找到 保留 Blogger Ajax 換頁功能,觸發自訂 JS 功能做法這篇文章

在範本中加入以下程式碼就好了:
<img onload='if(FB){FB.Share.stopScan();}' src='http://www.blogblog.com/1kt/transparent/white80.png' width='0' />
2011-03-28 02:06

在 Blogger 加上各種推文按鈕(修正Plurk 的Bug)

網友提出了 Plurk 的 bug
說實在的找了半天的文件
Blogger 並沒有可以直接使用的參數
看來又只能依靠 JavaScript 來處理這種鳥問題了

最終版程式碼
在 Blogger 加上各種推文按鈕



CSS 程式碼

/*Social Icon*/
.social{
background:transparent url(http://www.xxxx.com/social_icons.png) 0 0 no-repeat;
/*16*16按鈕的圖片網址*/

display: -moz-inline-box;
display: inline-block;
height:16px;
margin:0 4px;
text-indent:-999999px;
vertical-align:middle;
width:16px;
opacity:0.75;
overflow:hidden;
}
.social:hover{
opacity:1;
}

a.toFacebook{ background-position: 0 0; }
a.toPlurk{ background-position: 0 -16px; }
a.toTwitter{ background-position: 0 -32px; }
a.toTechnorati{ background-position: 0 -48px; }
a.toDelicious{ background-position: 0 -64px; }
a.toBuzz{ background-position: 0 -80px; }
a.toDigg{ background-position: 0 -96px; }
a.toStumbleUpon{ background-position: 0 -112px; }
a.toDesignFloat{ background-position: 0 -128px; }
a.getAtomRSS{ background-position: 0 -144px; }
a.toReader{ background-position: 0 -160px; }

首先打開『版面配置』→『修改HTML』
將『展開小裝置範本』打勾
找到 ]]></b:skin> 將 CSS 的程式貼在上一行
小技巧:所有的瀏覽器都有搜尋功能 (Ctrl + F),只要搜尋 "skin" 就可以找到這行了



HTML 程式碼

找尋 <div class='post-footer'> 這一行
然後向下找尋相對應的 </div>
這裡沒有比較清楚的標示
找起來會比較困難一點
將下面的程式貼在 </div> 之前
<div class='post-footer-line post-footer-line-99'>
 <span class='post-social-icons' name='post-social-icons' expr:t='data:post.title' expr:u='data:post.url' expr:h='data:blog.homepageUrl'></span>
</div>


JavaScript 程式碼

找尋 </body> 這一行,將 JavaScript 的程式貼在上一行
<script type="text/javascript">
var data = [
    {&quot;c&quot;:&quot;toFacebook&quot;, &quot;u&quot;:&quot;http://www.facebook.com/sharer.php?u=$url$&amp;t=$title$&quot;,&quot;t&quot;:&quot;Facebook&quot;},
    {&quot;c&quot;:&quot;toPlurk&quot;, &quot;u&quot;:&quot;http://www.plurk.com/?qualifier=shares&amp;status=$url$ ($title$)&quot;,&quot;t&quot;:&quot;Plurk&quot;},
    {&quot;c&quot;:&quot;toTwitter&quot;, &quot;u&quot;:&quot;http://twitter.com/home?status=$title$ $url$&quot;,&quot;t&quot;:&quot;Twitter&quot;},
    {&quot;c&quot;:&quot;toTechnorati&quot;, &quot;u&quot;:&quot;http://technorati.com/faves?add=$url$ $title$&quot;,&quot;t&quot;:&quot;Technorati&quot;},
    {&quot;c&quot;:&quot;toDelicious&quot;, &quot;u&quot;:&quot;http://del.icio.us/post?url=$url$ $title$&quot;,&quot;t&quot;:&quot;Delicious&quot;},
    {&quot;c&quot;:&quot;toDigg&quot;, &quot;u&quot;:&quot;http://digg.com/submit?phase=2&amp;url=$url$&amp;title=$title$&quot;,&quot;t&quot;:&quot;Digg&quot;},  
    {&quot;c&quot;:&quot;toStumbleUpon&quot;, &quot;u&quot;:&quot;http://www.stumbleupon.com/submit?url=$url$&amp;title=$title$&quot;,&quot;t&quot;:&quot;Stumble Upon&quot;},
    {&quot;c&quot;:&quot;toDesignFloat&quot;, &quot;u&quot;:&quot;http://www.designfloat.com/submit.php?url=$url$&amp;title=$title$&quot;,&quot;t&quot;:&quot;Design Float&quot;},
    {&quot;c&quot;:&quot;toReader&quot;, &quot;u&quot;:&quot;http://www.google.com/reader/link?url=$url$&amp;title=$title$&amp;srcURL=$home$&quot;,&quot;t&quot;:&quot;Google Reader&quot;},
    {&quot;c&quot;:&quot;toBuzz&quot;, &quot;u&quot;:&quot;http://www.google.com/buzz/post?url=$url$&quot;,&quot;t&quot;:&quot;Buzz&quot;},
    {&quot;c&quot;:&quot;getAtomRSS&quot;, &quot;u&quot;:&quot;/feeds/posts/default&quot;, &quot;t&quot;:&quot;Atom / RSS&quot;}   
];


var list=document.getElementsByName(&#039;post-social-icons&#039;);

for (var li=0,ll=list.length; li&lt;ll; li++){
    var title=encodeURIComponent(list[li].getAttribute(&#039;t&#039;));    
    var url=encodeURIComponent(list[li].getAttribute(&#039;u&#039;));    
    var home=encodeURIComponent(list[li].getAttribute(&#039;h&#039;));
    var temp=&quot;分享至 &amp;#65306;&quot;;
    for (var di=0,dl=data.length; di&lt;dl; di++){
        var href=data[di].u.replace(&quot;$title$&quot;,title).replace(&quot;$url$&quot;,url).replace(&quot;$home$&quot;,home);
        temp+=&quot;&lt;a class=&#039;social &quot;+data[di].c+&quot;&#039; href=&#039;&quot;+href+&quot;&#039; title=&#039;分享至 &quot;+data[di].t+&quot;&#039;&gt;&quot;+data[di].t+&quot;&lt;/a&gt;&quot;
    };
    list[li].innerHTML=temp;
};
</script>


JavaScript 原始碼

var data = [
    {"c":"toFacebook", "u":"http://www.facebook.com/sharer.php?u=$url$&t=$title$","t":"Facebook"},
    {"c":"toPlurk", "u":"http://www.plurk.com/?qualifier=shares&status=$url$ ($title$)","t":"Plurk"},
    {"c":"toTwitter", "u":"http://twitter.com/home?status=$title$ $url$","t":"Twitter"},
    {"c":"toTechnorati", "u":"http://technorati.com/faves?add=$url$ $title$","t":"Technorati"},
    {"c":"toDelicious", "u":"http://del.icio.us/post?url=$url$ $title$","t":"Delicious"},
    {"c":"toDigg", "u":"http://digg.com/submit?phase=2&url=$url$&title=$title$","t":"Digg"},  
    {"c":"toStumbleUpon", "u":"http://www.stumbleupon.com/submit?url=$url$&title=$title$","t":"Stumble Upon"},
    {"c":"toDesignFloat", "u":"http://www.designfloat.com/submit.php?url=$url$&title=$title$","t":"Design Float"},
    {"c":"toReader", "u":"http://www.google.com/reader/link?url=$url$&title=$title$&srcURL=$home$","t":"Google Reader"},
    {"c":"toBuzz", "u":"http://www.google.com/buzz/post?url=$url$","t":"Buzz"},
    {"c":"getAtomRSS", "u":"/feeds/posts/default", "t":"Atom / RSS"}   
];


var list=document.getElementsByName('post-social-icons');

for (var li=0,ll=list.length; li<ll; li++){
    var title=encodeURIComponent(list[li].getAttribute('t'));    
    var url=encodeURIComponent(list[li].getAttribute('u'));    
    var home=encodeURIComponent(list[li].getAttribute('h'));
    var temp="分享至 :";
    for (var di=0,dl=data.length; di<dl; di++){
        var href=data[di].u.replace("$title$",title).replace("$url$",url).replace("$home$",home);
        temp+="<a class='social "+data[di].c+"' href='"+href+"' title='分享至 "+data[di].t+"'>"+data[di].t+"</a>"
    };
    list[li].innerHTML=temp;
};


圖片來源:

Social Media Network Icons | Komodo Media
2011-01-18 16:15

CSS 常用命名表

版面類

欄目column
容器container
內容content
頁尾footer
頁首header
版型佈局layout
首頁index
頁面主體main
側欄sidebar

導航類

主導航main_nav
全域導航global_nav
導航nav
領行列navbar
左導航left_sidebar
右導航right_sidebar
子導航subnav
頂導航topnav
工具條toolbar

菜單類

菜單menu
子菜單submenu
菜單內容menu_content
菜單容器menu_container

樣式類

箭頭arrow
橫幅廣告banner
分界線boundary
按鈕btn
按鈕button
轉角/圓角corner
文字font
標題title
圖示icon
項目item
列表list
主要的master
頁面page
標示mark
分段section
邊導航圖標sidebar_icon
標籤頁tab
樣式/主題theme
閃爍twinkle
小部件widget
包裝器 wrapper
頁面外圍控制整體佈局寬度
區域zone

功能類

檢舉abuse
點擊這裡click_here
收藏coffin
塌陷collapse
完成的,結束的complete
改變,轉變conversion
當前的current
預設default
下載download
下拉drop
編輯edit
相等equals
例外exception
完成,結束finalize
折疊fold
雜湊hash
局部的localized
管理manager
方法method
即時通訊messenger
提示信息msg
註釋note
通知,告知;報告notify
語法分析parse
語法分析器parser
傳送pass
位置place
投票poll
發表文章post
預覽preview
列印print
發布publish
查詢query
收到,接到receive
重填reset
滾動scroll
搜索search
搜索框search_box
進階搜尋search_further
搜尋結果search_results
統計statistics
狀態status
串流stream
訂閱subscribe
送出submit
查詢訂閱subscriptions
小技巧tips
追蹤清單track
指導tutorial
上傳upload
驗證碼verification_code
觀看view
投票vote

內容類

檔案/文件archive
文章article
所有文章article_all
文章分類article_folder
招呼語blast
部落格blog
部落格資料blog_info
麵包屑bread_crumb
頁面所處位置導航提示
行事曆calendar
徵才careers
社群家族club
評論、評鑑comment
社群家族community
位置導航crumb
娛樂entertainment
電子報epaper
活動event
常見問題faq
回覆意見feedback
論壇forum
友情鏈接friend_link
強力搜尋gd_search_tech
留言板guestbook
指南guide
公會guild
熱門hot
熱門連結hot_link
學習learning
介紹introduce
徵才job
知識knowledge
新聞news
記事本notepad
即時訊息online_news
作品portfolio
活動比賽promo
排行rank
景點scenic
服務service
招呼語set_blast
即時留言板shoutbox
網頁導覽sitemap
技術支援support
旅遊travels
視訊video

網站類

關於about
關於我們about_us
公司company
公司簡介company_profile
聯絡contact
聯絡我們contact_us
版權資訊copyright
資訊info
網站標誌logo
商標label
組織organization
合作夥伴partner
薪資福利remuneration
摘要summary
系統system
網頁快訊web_slices

購物類

atmatm
現折活動allowance
配件appendix
紅利折抵bank_bonus
競標bid
取消訂單cancel
刷卡card
換貨change
推薦commend
優惠卷coupon
顧客customer
顧客服務customer_service
運送deliver
折扣discount
快速到貨express
購物流程flow
贈品gift
集殺group
詢價inquire
服務中心help
訂購單order
訂單查詢order_check
包裝packing
付費payment
集購payshop_flow
價格price
產品名稱product_name
產品products
嚴選保證promise
估價quotes
維修repair
退貨return
交易安全safety
購物shop
商店store
超商取貨付款store
補貨通知supply_info
信用卡線上分期time
統一編號unified business no.

會員類

通訊錄abook
帳務account
地址address
相簿album
申請apply
審核approval
黑名單black_list
信箱email
忘記密碼forgot_password
忘記帳號forgot_username
服務條款legal
登入login
登出logout
登入條login_bar
邀請朋友invite
加入join_us
會員member
會員登入member_login
個人personal
個人資訊personal_information
照片photo
隱私權政策privacy
個人簡介profile
個人相片profile_photo
註冊register
轉寄好友send_friend
註冊sign_up
登入sign_in
登出sign_out


參考來源:
CSS 常用命名参考 - PHP新手博客(phpabc‘s blog)
css常用命名
div+css命名规则 (注重SEO的朋友注意了)
Jane’s Blog: CSS 命名規則
2010-12-30 06:22

美化 Apache autoindex

因為原始的 auto Index 的頁面實在太醜了
也許五年前看起來還很新潮,但現在已經過時了
所以我自己做的一個 Apache 美化套件

檔案下載:fancy.zip

2010-10-07 18:04

讓 Eclipse Task tag 能用在任何文件類型上

之前為了找能夠在 SQL File 中使用 Task tag 套件花了不少時間,最後發現 Mylyn 的套件中有一個針對所有專案下 DTD 跟 XML 的 Task tag 功能,索性利用這個功能讓 SQL 也支援 Task tag。

因為這個功能只支援 XML 格式的註解 <!-- 至 -->,所以只要巧妙的利用這個特性就可以達到我們要的功能。


首先在『內容類型 → DTD』中加入 *.sql 。



再來在 SQL file 的起始處加入 -- <!--



在結尾處加上 -- -->



開啟『專案 → 內容』啟用 Task Tags,並將 『Filters』中的 XML 取消。



我希望可以標出所有資料表的定義,所以在這裡我加入 TABLE 這個關鍵字。



接著就可以看到很快樂的結果了。



當然在 Task View 中也會列出所有的標記。