Running AngularJS App on IE7

IE7需要額外在body宣告宣告id和ng-app屬性:

1
<body id="ng-app" ng-app="myapp">

盡量使用class、attribute的寫法,例如:

1
2
<div class="ng-view"></div>
<div ng-view></div>

若要在IE7使用angular自訂定義的tag,而不採用上述方式,如下:

1
<ng-view></ng-view>

需要先執行以下script,可參考官方說明

1
2
3
4
5
6
7
8
9
10
11
12
<!--[if lte IE 8]>
<script>
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');

// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
document.createElement('ng:view');
</script>
<![endif]-->

在IE8、IE7使用angular.fromJson,需額外include script,http://bestiejs.github.com/json3/

1
2
3
<!--[if lte IE 8]>
<script src="/static/js/libs/json3.min.js"></script>
<![endif]-->

icon converter

在地圖上要顯示marker,大多數都是使用固定幾個icon,以類別當作區分。如果能更直覺得辨識marker,讓user不用經由點擊,就得知marker的意義,應該會讓user更有觀看地圖意願。所以就寫了一個簡單的功能,讓圖片轉成icon。

Install:

git clone https://github.com/SparrowJang/icon_converter

cd icon_converter

sudo python setup.py install

經由url的image轉換成icon :

from icon_converter import converter

#target image ( http or local )
path = "http://example.com/test.png"

#resize and crop image
size = {"width":40,"height":40}

border = {
    "size":{"width":2,"height":2},
    "color":"#BBBBBB",
    "backgroundColor":"#FFFFFF"
}

blob = converter.convert_by_path( path, size, border )

f = open( "icon.png", "w" )

f.write( blob.data )

f.close()

github

titanium build failed(android)

今天在其他電腦裝titanium遇到以下錯誤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[TRACE] Writing out AndroidManifest.xml
[ERROR] Exception occured while building Android project:
[ERROR] Traceback (most recent call last):
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.0.GA/android/builder.py", line 2480, in <module>
[ERROR] builder.build_and_run(False, avd_id)
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.0.GA/android/builder.py", line 2264, in build_and_run
[ERROR] self.manifest_changed = self.generate_android_manifest(compiler)
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.0.GA/android/builder.py", line 1404, in generate_android_manifest
[ERROR] '-I', self.android_jar], warning_regex=r'skipping')
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.0.GA/android/run.py", line 38, in run
[ERROR] print "[DEBUG] %s" % subprocess.list2cmdline(args_to_log)
[ERROR] File "/usr/lib/python2.7/subprocess.py", line 587, in list2cmdline
[ERROR] needquote = (" " in arg) or ("\t" in arg) or not arg
[ERROR] TypeError: argument of type 'NoneType' is not iterable

查了~/.titanium/mobilesdk/linux/3.1.0.GA/android/run.py這隻程式,發現有些參數是空的。

然後在androidsdk.py找到這段,功能是取得android sdk的aapt路徑:

1
2
3
4
5
6
7
8
9
def get_aapt(self):  
# for aapt (and maybe eventually for others) we
# want to favor platform-tools over android-x/tools
# because of new resource qualifiers for honeycomb
sdk_platform_tools_dir = self.get_sdk_platform_tools_dir()
if not sdk_platform_tools_dir is None and os.path.exists(os.path.join(sdk_platform_tools_dir, 'aapt')):
return os.path.join(sdk_platform_tools_dir, 'aapt')

return self.get_platform_tool('aapt')

比對另一台電腦sdk的platform-tools(舊):

1
2
3
4
5
6
7
8
9
10
11
12
13
|-- platform-tools
| |-- aapt
| |-- adb
| |-- aidl
| |-- api
| |-- dexdump
| |-- dx
| |-- fastboot
| |-- lib
| |-- llvm-rs-cc
| |-- NOTICE.txt
| |-- renderscript
| `-- source.properties

新下載的sdk:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├── platform-tools
│ ├── adb
│ ├── api
│ ├── fastboot
│ ├── NOTICE.txt
│ └── source.properties
├── build-tools
│ └── 17.0.0
│ ├── aapt
│ ├── aidl
│ ├── dexdump
│ ├── dx
│ ├── lib
│ ├── llvm-rs-cc
│ ├── NOTICE.txt
│ ├── renderscript
│ └── source.properties

所以新版的sdk目錄結構有變動,在加上titanium執行檔案有預設路徑,解決方式有下列幾種:

  1. 修改androidsdk.py裡面的get_dx、get_dx_jar、get_aapt…回傳路徑
  2. 搬移檔案build-tools檔案到platform-tools
  3. 直接使用link

我使用直接採用link的方式:

1
2
3
4
5
6
cd ~/tool/android-sdk/platform-tools
ln -s ../build-tools/17.0.0/aapt
ln -s ../build-tools/17.0.0/dx
ln -s ../build-tools/17.0.0/dexdump
ln -s ../build-tools/17.0.0/llvm-rs-cc
ln -s ../build-tools/17.0.0/aidl

最後還要還要記得設定jarsigner、keytool:

1
2
3
4
sudo update-alternatives --install "/usr/bin/jarsigner" "jarsigner" "/usr/lib/jvm/java-6-sun/bin/jarsigner" 1
sudo update-alternatives --install "/usr/bin/keytool" "keytool" "/usr/lib/jvm/java-6-sun/bin/keytool" 1
sudo update-alternatives --config jarsigner
sudo update-alternatives --config keytool

weinre

weinre是一個用於瀏覽器的遠端除錯工具,運作方式與jsconsole相同,都是透過include一隻外部js script,來與server溝通傳遞資料。但是jsconsole只提供一些很基本功能,如果要能像chrome一樣,查看dom element、network等功能,可以使用weinre

使用時機:

使用android、ios等手持裝置的browser,或者對IE除錯的時候,都很適合。

weinre原理:

weinre基本原理是,建立一個console page,然後經由weinre server,與development page(你要除錯的page)互相傳遞資料溝通。( 溝通方式採用long polling)

時序圖如下:

 
透過node.js安裝:
sudo npm install -g weinre
操作流程:

開啟weinre server:

weinre --httpPort 8081 --boundHost localhost

打開瀏覽器輸入http://localhost:8081/

然後在你想要除錯的頁面加上以下script:

<script src="http://localhost:8081/target/target-script-min.js#anonymous"></script>

例如:

<!DOCTYPE HTML>                                                                                  
<html lang="en">                                                                                 
<head>                                                                                           
    <meta charset="UTF-8">                                                                       
    <title></title>                                                                              
    <script src="http://localhost:8081/target/target-script-min.js#anonymous"></script>       
</head>                        
<body>                                                                                           
    <div>weinre test</div>                                                                       
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> 
</body>                                                                                          
</html>                                                                                          

開啟http://localhost:8081/client/#anonymous,就可以開始除錯了。

jquery uploader

使用ajax upload還滿常使用到,但是又不需要一些複雜的功能,所以就寫了一個簡單的jquery uploader

需求如下:
  1. 將form經用ajax post出去。
  2. 選擇要post的欄位包括file。
支援:
  1. firefox
  2. chrome
  3. IE10+

使用ajax submit整個form:

//opts.success(result):上傳成功
//opts.progress:處理進度
//opts.fail:上傳失敗

$("form").uploadForm( opts );

uploadForm會自動抓取form的action連結,目前此plugin只能一次抓取一個form upload。

使用ajax submit部份欄位:

//url:要上傳的目標連結

$( "input[name='file'],input[name='id']" ).upload( url, opts );

如果要支援IE10以下瀏覽器,現在已經另外實作iframe uploader,目前可以支援uploadForm這個功能,但是僅支援success這個參數。

github

X-Frame-Options response header

如果要防止頁面被iframe或frame嵌入,比較常用的方法是,在頁面上加入一串script做判斷,或者加入X-Frame-Options header。

X-Frame-Options有下列3個參數值可用:
deny

拒絕被任何嵌入頁面。

SAMEORIGIN

允許來至於相同domain的嵌入。

ALLOW-FROM uri

允許某個domain嵌入。

 

以下是用node.js寫得example:
var express = require('express');

var html = "from http://localhost:3001/SAMEORIGIN<br>" +
            '<iframe src="http://localhost:3001/SAMEORIGIN"></iframe><br>'+
            'from http://localhost:3001/deny<br>' +
            '<iframe src="http://localhost:3001/deny"></iframe><br>' +
            'from http://localhost:3001/allow_from<br>' +
            '<iframe src="http://localhost:3001/allow_from"></iframe><br>';

(function(){       

  var app = express();

  app.get('/', function(req, res){

    res.send( html );
  });

  app.listen(3000);

})();

(function(){

  var app = express();

  app.get('/', function(req, res){

    res.send( html );
  });

  app.get('/SAMEORIGIN', function(req, res){
    res.set('X-Frame-Options','SAMEORIGIN');
    res.send('hello world');
  });

  app.get('/deny', function(req, res){
    res.set('X-Frame-Options','deny');
    res.send('hello world');
  });

  app.get('/allow_from', function(req, res){
    res.set('X-Frame-Options','Allow\-From http://localhost:3001');
    res.send('hello world');
  });

  app.listen(3001);

})();

需先安裝node.jsexpress.js

#使用npm安裝express
npm install express

#執行server.js
node server.js

分別測試http://localhost:3000/ and http://localhost:3001/,其中allow-from測試時,似乎沒辦法取得正確結果,換成其他domain頁面一樣是可讀取的。

Zend framework page cache

初始projectt為例,可直接至application.ini配置cache的參數。

;關閉預設buffer
resources.frontController.params.disableOutputBuffering = true

resources.cachemanager.page.frontend.name = Page
;cache存活時間      
resources.cachemanager.page.frontend.options.lifetime = 5
;debug模式,會在header最上方顯示提示
resources.cachemanager.page.frontend.options.debug_header = true
;儲存的容器
resources.cachemanager.page.backend.name = File
;設定存放位置
resources.cachemanager.page.backend.options.cache_dir = APPLICATION_PATH "/cache"

;如果不一定要在自身project底下建立cache的目錄
;可直接導向到/tmp即可,以下的權限修改跟folder建立都可以省略
;/tmp底下檔案,當電腦關閉時,將會自動清除
;resources.cachemanager.page.backend.options.cache_dir = "/tmp"

建立cache folder和修改權限:

cd newproject/application

mkdir cache

sudo chown www-data:www-data cache

在controller init的加入要cache的action:

// 註冊 Cache Action Helper
$this->_helper->addHelper(new Zend_Controller_Action_Helper_Cache());

$this->_helper->cache(array(
  'index', // 指定要快取的 Action
));

以IndexController為例如下:

class IndexController extends Zend_Controller_Action      
{

    public function init()      
    {     
        /* Initialize action controller here */   

        // 註冊 Cache Action Helper                 
        $this->_helper->addHelper(new Zend_Controller_Action_Helper_Cache());

        $this->_helper->cache(array(
            'index', // 指定要快取的 Action
        ));

    }

    public function indexAction()
    {              
        // action body
    }
}

當使用debug mode的時候,就會在頁面上出現"DEBUG HEADER : This is a cached page !"的字樣,表示目前是使用cache。

Installation zend framework

官方下載(以1.12.3 version為例),然後解壓縮:

wget https://packages.zendframework.com/releases/ZendFramework-1.12.3/ZendFramework-1.12.3.zip

unzip ZendFramework-1.12.3.zip

建置project和copy library:

./ZendFramework-1.12.3/bin/zf.sh project newproject

cp -r ./ZendFramework-1.12.3/library ./newproject/library

會產生以下檔案及目錄:

.
|-- application
|   |-- Bootstrap.php
|   |-- configs
|   |-- controllers
|   |-- models
|   `-- views
|-- docs
|   `-- README.txt
|-- library
|   `-- Zend
|-- public
|   `-- index.php
`-- tests
    |-- application
    |-- bootstrap.php
    |-- library
    `-- phpunit.xml

最後在配置server(apache、nginx…)即可,以下是用nginx設定:

server {

    listen 80;
    server_name localhost;

    #path:/etc/nginx/sites-enabled/default

    root /var/www/newproject;
    index  index.html index.htm index.php;

    location / {
        if (!-f $request_filename) {
            rewrite "^(.*)$" /public/index.php?q=$1 last;
            break;
        }
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /var/www/newproject$fastcgi_script_name;
        include fastcgi_params;
    }   
}

從上方配置可看到,使用$request_filename,判斷該檔案路徑存不存在,將所有不存在的url導向index.php,在由index.php去分派給controller,如果存在則直接導向該檔案。

最後打開http://localhost就可以看到配置好的project首頁了。

Zend framework core cache

zend framework提供了Zend_Cache::factory,這個factory可以產生出好幾種cache方式,官方範例如下:

// We choose a backend (for example 'File' or 'Sqlite'...)
$backendName = '[...]';

// We choose a frontend (for example 'Core', 'Output', 'Page'...)
$frontendName = '[...]';

// We set an array of options for the chosen frontend
$frontendOptions = array([...]);

// We set an array of options for the chosen backend
$backendOptions = array([...]);

// We create an instance of Zend_Cache
// (of course, the two last arguments are optional)
$cache = Zend_Cache::factory($frontendName,
                             $backendName,
                             $frontendOptions,
                             $backendOptions);

frontendName:

指的是快取對象,例如資料、頁面、檔案。

backendName:

指的是要儲存的容器,例如存入檔案裡、sqlite、或者是連接至Memcache server等…。

frontendOptions和backendOptions:

指的是可選參數值。

可參考官方frontend cachebackend cache

在前台頁面中,有一些資料可能是很久才去更新一次,這時就可以使用cache:
  • 如果是採用page cache,那可能要只cache一部分頁面,透過ajax抓取,或者直接在view上面加判斷式。
  • 透過file cache可直接將抓取到的api資料,直接存入,只要每次去檢查cache時效是否過了,如果時效過則在重新抓取。
以下是一個簡單example:
class FileCache {

    private $cache;

    public function FileCache( $lifetime = 300 ){
        // 設定 Frontend 選項
        $frontendOptions = array(
           'lifetime' => $lifetime, // 快取時間
           'automatic_serialization' => true, // 自動 serialization
        );  
        // 設定 Backend 選項
        $backendOptions = array(
            'cache_dir' => APPLICATION_PATH . '/cache/', // 快取存放路徑
        );  
        // 建立一個快取物件
        $this->cache = Zend_Cache::factory('Core',
                                           'File',
                                           $frontendOptions,
                                           $backendOptions);

    }   

    public function load( $name ){
        return $this->cache->load( $name );
    }   

    public function save( $name, $data ){
        return $this->cache->save( $data, $name );
    }

    public function isAvailable( $name ){
        return $this->cache->test( $name );  
    }   
}

使用方式如下:

$cache = new FileCache( 60 );

$cacheName = "data_cache";

if( $cache->isAvailabel( $cacheName ) ){

    $result = $cache->load( $cacheName );
}else{

    $result = array(1,2,3,4,5);
    $cache->save( $cacheName, $result )
}

print_r( $result );