2010年7月27日 星期二

iOS 開發教學 - 使用 UIToolbar + Image Button

之前學習時很偷懶,一直覺得有了 Navigation controller 和 Tabbar controller 後,總覺得畫面已經沒甚麼好擺的,所以遲遲沒有試試 UIToolbar ,這次就順手筆記一下囉。

參考資料:


程式碼:

@interface UIImage (Extras)

- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize;

@end

@implementation UIImage (Extras)

- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
{

   UIImage *sourceImage = self;
   UIImage *newImage = nil;      
   CGSize imageSize = sourceImage.size;
   CGFloat width = imageSize.width;
   CGFloat height = imageSize.height;
   CGFloat targetWidth = targetSize.width;
   CGFloat targetHeight = targetSize.height;
   CGFloat scaleFactor = 0.0;
   CGFloat scaledWidth = targetWidth;
   CGFloat scaledHeight = targetHeight;
   CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
 
   if (CGSizeEqualToSize(imageSize, targetSize) == NO)
   {
       CGFloat widthFactor = targetWidth / width;
       CGFloat heightFactor = targetHeight / height;
     
       if (widthFactor > heightFactor)
           scaleFactor = widthFactor; // scale to fit height
       else
           scaleFactor = heightFactor; // scale to fit width
       scaledWidth  = width * scaleFactor;
       scaledHeight = height * scaleFactor;
     
       // center the image
       if (widthFactor > heightFactor)
       {
           thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
       }
       else
           if (widthFactor < heightFactor)
           {
               thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
           }
   }     
 
   UIGraphicsBeginImageContext(targetSize); // this will crop
 
   CGRect thumbnailRect = CGRectZero;
   thumbnailRect.origin = thumbnailPoint;
   thumbnailRect.size.width  = scaledWidth;
   thumbnailRect.size.height = scaledHeight;
 
   [sourceImage drawInRect:thumbnailRect];
 
   newImage = UIGraphicsGetImageFromCurrentImageContext();
   if(newImage == nil)
       NSLog(@"could not scale image");
 
   //pop the context to get back to the default
   UIGraphicsEndImageContext();
   return newImage;
}
@end

- (void)aButton:(UIButton *)sender
{
   switch (sender.tag) {
       case 1:
           NSLog(@"Facebook");
           break;
       case 2:
           NSLog(@"Twitter");
           break;
       case 3:
           NSLog(@"Tumblr");
           break;
       case 4:
           NSLog(@"Plurk");
           break;
       default:
           break;
   }
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  

   // Override point for customization after application launch.
   //Initialize the toolbar

   UIToolbar *toolbar = [[UIToolbar alloc] init];
   toolbar.barStyle = UIBarStyleDefault;
   toolbar.alpha = 0.8;
 
   //Set the toolbar to fit the width of the app.
   [toolbar sizeToFit];

   //Caclulate the height of the toolbar
   CGFloat toolbarHeight = [toolbar frame].size.height;

   //Get the bounds of the parent view
   CGRect rootViewBounds = [[UIScreen mainScreen] bounds];
 
   //Get the height of the parent view.
   CGFloat rootViewHeight = CGRectGetHeight(rootViewBounds);
 
   //Get the width of the parent view,
   CGFloat rootViewWidth = CGRectGetWidth(rootViewBounds);
 
   //Create a rectangle for the toolbar
   CGRect rectArea = CGRectMake(0, rootViewHeight - toolbarHeight, rootViewWidth, toolbarHeight);
 
   //Reposition and resize the receiver
   [toolbar setFrame:rectArea];

   NSMutableArray *barButtonItemList = [[NSMutableArray alloc] init];
   CGFloat thumbWidth = 42 , thumbHeight = 42;
   {
       UIImage *image = [[UIImage imageNamed:@"facebook_icon.png"] imageByScalingAndCroppingForSize:CGSizeMake(thumbWidth, thumbHeight)];
       UIButton *button = [[UIButton alloc] init];
       [button setTag:1];
       [button setImage:image forState:UIControlStateNormal];
       [button setFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
       [button addTarget:self action:@selector(aButton:) forControlEvents:UIControlEventTouchDown];
       UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
       [button release];
       [barButtonItemList addObject:barButton];
       [barButton release];
   }
   {
       UIImage *image = [[UIImage imageNamed:@"twitter_icon.png"] imageByScalingAndCroppingForSize:CGSizeMake(thumbWidth, thumbHeight)];
       UIButton *button = [[UIButton alloc] init];
       [button setTag:2];
       [button setImage:image forState:UIControlStateNormal];
       [button setFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
       [button addTarget:self action:@selector(aButton:) forControlEvents:UIControlEventTouchDown];
       UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
       [button release];
       [barButtonItemList addObject:barButton];
       [barButton release];
   }
   {
       UIImage *image = [[UIImage imageNamed:@"tumblr_icon.png"] imageByScalingAndCroppingForSize:CGSizeMake(thumbWidth, thumbHeight)];
       UIButton *button = [[UIButton alloc] init];
       [button setTag:3];
       [button setImage:image forState:UIControlStateNormal];
       [button setFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
       [button addTarget:self action:@selector(aButton:) forControlEvents:UIControlEventTouchDown];
       UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
       [button release];
       [barButtonItemList addObject:barButton];
       [barButton release];
   }
   {
       UIImage *image = [[UIImage imageNamed:@"plurk_icon.png"] imageByScalingAndCroppingForSize:CGSizeMake(thumbWidth, thumbHeight)];
       UIButton *button = [[UIButton alloc] init];
       [button setTag:4];
       [button setImage:image forState:UIControlStateNormal];
       [button setFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
       [button addTarget:self action:@selector(aButton:) forControlEvents:UIControlEventTouchDown];
       UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
       [button release];
       [barButtonItemList addObject:barButton];
       [barButton release];
   }
   [toolbar setItems:barButtonItemList];
 
   [window addSubview:toolbar];
 
   [window makeKeyAndVisible];
 
   return YES;
}

用 wget 下載 iOS SDK

實在是有時網路速度很龜速,所以才想到用其他方式來下載檔案。用到的東西有三個:



  • Firefox + Firebug plugin

  • wget

  • 一個申請過 Apple ID 的帳密


首先先用該帳密登入到 iPhone Dev Center ( https://developer.apple.com/iphone/index.action#downloads ),並開啟 firebug 網路功能,準備偵測封包,接著就去點選下載新版的 iOS SDK,此例為 Xcode 3.2.3 and iOS SDK 4.0.1 ,之後就可以看到相關的網路封包,如 GET http://adcdownload.apple.com/ios/ios_sdk_4.0.1__final/xcode_3.2.3_and_ios_sdk_4.0.1.dmg ,查看其細部的 Cookie 資料,找尋 ADCDownloadAuth=...


最後則可以用其他國外頻寬快的機器,使用 wget 進行下載


wget 'http://adcdownload.apple.com/ios/ios_sdk_4.0.1__final/xcode_3.2.3_and_ios_sdk_4.0.1.dmg' --header 'Cookie: ADCDownloadAuth=your_cookie_info'


收工!


2010年7月26日 星期一

[動畫] 新版鋼之鍊金術師








again - YUI


忘了是多久以前,看了第一版的鋼之鍊金術師,當時就覺得這部動畫很巧妙地描述一些人性層面的事情,有時候也會在想,到底要幾歲才適合看這部動畫呢?或許這都多慮了?生命自會找到出路吧?!


這幾天看了新版的鋼之鍊金術師,原先只是打發時間外加好奇新版到底加了什麼,然而我也忘了舊版少部分的內容跟結局,所幸這就樣當初第一次看吧?不知不覺地就追完整部動畫。說真的我還滿喜歡看這類型的,有點似淺又深的。我喜歡等價交換這個定理,就像以前國高中計算一些物理題目的動量、動位能守恆等等的,另外還有化學的動態平衡等等,或是最大亂度最低能量,都是一種口頭禪啊。


那究竟要追求什麼呢?又要失去什麼呢?


大學的時候,曾羨慕一些功課好的人,覺得他們總是能把課業、社團甚至娛樂與打工取的一種平衡,自己大概就傻傻地顧成績,但又沒有顧好,當自己學有微成,就看不起那些走捷徑的人。快進入碩班時,算是已經取的一些平衡,畢竟,一天只有 24 小時,回想起大一教我們程式的秋媛姐,當時總是提醒大家不要花太多時間在 BBS ,我不覺得她有說錯的地方,我卻也不覺得是對的。當時的 BBS 是另一種家,大家透過個人板、寢室板,用簡單的文字反而得到更多的互動。話說大一時還曾跟室友每天輪流寫寢記呢!當然蠢事也發生不少,像是幫室友認識某系的班花,或是認識學扮的男友,結果到現在還是一樣尷尬,那位從學伴變同系不同班的同學,甚至前陣子發現在同公司上班,哈。


至於現在的我到底要追求什麼?我也很常地問我自己,並還在追求答案。希望可以透過所學的,先拉拔家人,再求幫助更多的人吧!


啊,忘了一說,唱歌的 YUI ,第一次聽到唱歌是太陽之歌,聽學弟說,很多動畫歌也都是他唱的,真厲害。









2010年7月22日 星期四

[Linux] 使用 SSH Tunnel 連線至 Mac OSX @ Ubuntu 10.04

Mac OS X 有提供遠端桌面的服務,雖然可以設定密碼連線,但整個過程中似乎沒有加密?所以我就在 Ubuntu 上試著用 ssh tunnel 連到 Mac OS X。此目的是從 Ubuntu 機器遠端桌面到 Mac OS X 。


參考資料:



指令:


$ ssh -N -f -L 9000:localhost:5900 user@MAC_OS_X_IP


其中 ssh 參數資訊


     -N     Do not execute a remote command.  This is useful for just for‐
             warding ports (protocol version 2 only).

     -f      Requests ssh to go to background just before command execution.
             This is useful if ssh is going to ask for passwords or
             passphrases, but the user wants it in the background.  This
             implies -n.  The recommended way to start X11 programs at a
             remote site is with something like ssh -f host xterm.

     -L [bind_address:]port:host:hostport
             Specifies that the given port on the local (client) host is to be
             forwarded to the given host and port on the remote side.  This
             works by allocating a socket to listen to port on the local side,
             optionally bound to the specified bind_address.  Whenever a con‐
             nection is made to this port, the connection is forwarded over
             the secure channel, and a connection is made to host port
             hostport from the remote machine.  Port forwardings can also be
             specified in the configuration file.  IPv6 addresses can be spec‐
             ified with an alternative syntax:
             [bind_address/]port/host/hostport or by enclosing the address in
             square brackets.  Only the superuser can forward privileged
             ports.  By default, the local port is bound in accordance with
             the GatewayPorts setting.  However, an explicit bind_address may
             be used to bind the connection to a specific address.  The
             bind_address of “localhost” indicates that the listening port be
             bound for local use only, while an empty address or ‘*’ indicates
             that the port should be available from all interfaces.


簡言之,-f 是背景執行,-N 是用在 port forwarding,-L 就是關鍵的東西,依序代表本地端 Port ,以及目的端 IP、Port,而最後接的那個 USER@IP 代表要透過的機器。


以我的情境來說,我將透過本地端 Port 9000 (此乃隨意自訂的 Port)建立一個 ssh tunnel 到 MAC_OS_X_IP 機器上,並在 MAC_OS_X_IP 機器上連到 localhost:5900 (5900是Mac預設的遠端桌面 Port)。所以就等同於在 MAC_OS_X_IP 機器上,連線 localhost (此指的是 MAC_OS_X_IP 機器) 的 Port 5900 。


最後,我在原先的那台機器,也就是 Ubuntu 上,僅需使用 VNC 連到 localhost:9000 即可導到 MAC_OS_X_IP:5900 進行登入的動作。


[OSX] 粗淺地使用 ipfw @ Mac 10.6

距離上一次用 ipfw,大概已經是五六年前啦。主要是想要阻擋公司內部一些"彷彿中毒"電腦去 try 機器,另外,公司已經有強大的防火牆阻擋外部連進來,因此我只需限制公司內部的 IP 就行!此篇是最粗淺的設定筆記,並不適用其他常用的機器。除了 ipfw rules 外,其他的設定只是為了讓他可以開機就執行 ipfw 。

參考文件:



  1. Init

    • $ sudo mkdir /Library/StartupItems/Firewall

    • $ sudo touch /Library/StartupItems/Firewall/Firewall

    • $ sudo chmod ug+x /Library/StartupItems/Firewall/Firewall

    • $ sudo chmod o-rwx /Library/StartupItems/Firewall/Firewall

  2. /Library/StartupItems/Firewall/Firewall

    • !/bin/sh

      ########## file @ /Library/StartupItems/Firewall/Firewall

      # http://www.freebsd.org/doc/en/books/handbook/firewalls-ipfw.html

      # http://www.ibiblio.org/macsupport/ipfw/



      ipfw=`which ipfw`

      $ipfw -q -f flush

      cmd="$ipfw -q add "


      $cmd 00500 check-state


      $cmd 07999 allow all from x.y.My.PC1 to any

      $cmd 07999 allow all from x.y.My.PC2 to any

      $cmd 07999 allow all from x.y.My.PC3 to any

      $cmd 08999 deny all from x.y.0.0/16 to any

      $cmd 09999 allow all from any to any

  3. /Library/StartupItems/Firewall/StartupParameters.plist

    • {
      Description = "Firewall";
      Provides = ("Firewall");
      Requires = ("Network");
      OrderPreference = "None";
      Messages =
         {
         start = "Starting NAT/Firewall";
         stop = "Stopping NAT/Firewall";
         };
      }

其中 x.y.My.PC1 要留意設定,有時要記的加上常用的機器列表,別忘了加公司的 DNS,這樣才能你的機器才能連過去。

2010年7月20日 星期二

[PHP] 使用 Signal Handler

之前用 PHP 寫了一些 tools level 的程式,也就是使用上都是透過 $ php t.php 的模式在進行,最近想替他加上 Signal 的處理,如此一來可以增加一些資料的正確性。


參考資料:



範例程式(小部份的修改 Example #1 pcntl_signal() example):


@ t.php
<?php
// tick use required as of PHP 4.3.0
declare(ticks = 1);

function sig_handler($signo)    // signal handler function
{
        switch ($signo)
        {
                case SIGTERM:   // handle shutdown tasks, kill, kill -15
                        print "SIGTERM\n";
                        break;
                case SIGHUP:    // handle restart tasks, kill -1
                        print "SIGHUP\n";
                        break;
                case SIGUSR1:
                        print "SIGUSR1\n";
                        break;
                case SIGINT:    // ctrl + c
                        print "SIGINT\n";
                        break;
                default:        // handle all other signals
                        print "Other:$signo\n";
        }
}

echo "Installing signal handler...\n";

// setup signal handlers
pcntl_signal( SIGTERM , "sig_handler" );        // kill or kill -15
pcntl_signal( SIGHUP ,  "sig_handler" );        // kill -1
pcntl_signal( SIGUSR1 , "sig_handler" );        // self
pcntl_signal( SIGINT , "sig_handler" );         // ctrl+c

//echo"Generating signal SIGTERM to self...\n";

while( 1 )
{
        echo ".";
        sleep( 1 );
}

// send SIGUSR1 to current process id
//posix_kill(posix_getpid(), SIGUSR1);

echo "Done\n"

?>


執行前需留意,上述程式已經擷取 ctrl+c 的訊號,在 Unix 環境中,需使用 kill -9 的訊號來終止他


$ php t.php


接著在 Unix 上可以用 pa aux | grep 'php t.php' 查看剛剛那隻的 pid,接著可以用 kill 指令去操控他囉!


簡單用 awk 處理,可自行把 system( "kill " $2 ) 更新為 system( "kill -9 " $2 ) 等模式


$ ps aux | grep 'php t.php' | awk 'match( $11,"php" ) && match( $12 , "t.php" ) {system( "kill " $2 )}'


依序用 Ctrl+C , kill -1, kill -15, kill -9 的訊號


$ php t.php
Installing signal handler...
....^CSIGINT
.....SIGHUP
....SIGTERM
...已砍掉


2010年7月19日 星期一

模擬網路連線的延遲 - Simulating network delay

目的:模擬連線到國外的網路品質。


由於有些新穎的服務都嘛是從國外開始蔓延,有的服務連過去使用時都有點頓頓,慶幸的有的是 Open Source,於是我把他架起來用用,但是,還是要好奇一下連到國外頓頓的感覺是不是完全是因為網路品質的關係,因此,我就要來試試將自己網路品質弄糟的方式。


一開始我只用 iptables 跟 limit 、delay、latency 或 ms 等關鍵字,找了找,最後發現這個應該是要跟 iproute 相關才是,有幾篇文章



最後,我是用 How to simulate a slow
network with 'wanem'
的方法,在 Ubuntu 10.04 環境下,用 tc 這個指令,並且將 eth3 網路卡的連線 dely 200ms


$ sudo tc qdisc add dev eth3 root handle 1:0 netem delay 200msec


假設 eth3 綁定的是 192.168.56.101 的位置,那就用 ping 來觀看,果真變慢囉(過程是一開始沒有設定,之後加上 dely 200ms,最後在拿掉該設定)


$ ping 192.168.56.101
PING 192.168.56.101 (192.168.56.101) 56(84) bytes of data.
64 bytes from 192.168.56.101: icmp_seq=1 ttl=64 time=0.305 ms
64 bytes from 192.168.56.101: icmp_seq=2 ttl=64 time=0.267 ms
64 bytes from 192.168.56.101: icmp_seq=3 ttl=64 time=201 ms
64 bytes from 192.168.56.101: icmp_seq=4 ttl=64 time=200 ms
64 bytes from 192.168.56.101: icmp_seq=5 ttl=64 time=200 ms
64 bytes from 192.168.56.101: icmp_seq=6 ttl=64 time=201 ms
64 bytes from 192.168.56.101: icmp_seq=7 ttl=64 time=203 ms
64 bytes from 192.168.56.101: icmp_seq=8 ttl=64 time=202 ms
64 bytes from 192.168.56.101: icmp_seq=9 ttl=64 time=0.344 ms
64 bytes from 192.168.56.101: icmp_seq=10 ttl=64 time=0.334 ms
64 bytes from 192.168.56.101: icmp_seq=11 ttl=64 time=0.697 ms
64 bytes from 192.168.56.101: icmp_seq=12 ttl=64 time=0.341 ms
64 bytes from 192.168.56.101: icmp_seq=13 ttl=64 time=0.631 ms
^C
--- 192.168.56.101 ping statistics ---
13 packets transmitted, 13 received, 0% packet loss, time 11998ms
rtt min/avg/max/mdev = 0.267/93.241/203.547/100.266 ms


測試完可以在用以下指令清除


$ sudo tc qdisc del dev eth3 root handle 1:0 netem delay 200msec


VirtualBox 常用的指令筆記


  1. 複製虛擬硬碟

    • $ VBoxManage clonevdi Source.vdi Target.vdi

      由於 *.vdi 內綁有 UUID 等資訊,若直接用 cp 指令,該 UUID 並不會更新,並且導致掛載時出現問題(重複的UUID)。



  2. 設定 port forwarding

    • 語法 VBoxManage getextradata "GuestOSName" enumerate

      $ VBoxManage getextradata Ubuntu1004i386 enumerate
      觀看 Ubuntu1004i386 的 extradata 資訊

    • 語法 VBoxManage setextradata [GuestOSName] "VBoxInternal/Devices/[ADAPTER]/0/LUN#0/Config/[DescriptiveName]/[GuestPort|HostPort|Protocol]" [TCP|UDP|PortNumber]

      $ VBoxManage setextradata Ubuntu1004i386 "VBoxInternal/Devices/e1000/0/LUN#0/Config/ssh/Protocol" TCP
      $ VBoxManage setextradata Ubuntu1004i386 "VBoxInternal/Devices/e1000/0/LUN#0/Config/ssh/GuestPort" 22
      $ VBoxManage setextradata Ubuntu1004i386 "VBoxInternal/Devices/e1000/0/LUN#0/Config/ssh/HostPort" 2222

      當預設的 Guest OS 使用 NAT 網路模式時,透過上述設定,使用 ssh -p 2222 localhost 即可連進 Guest OS,其中 adapter 為 e1000, 有的人是填 pcnet,關於此部份請查詢 /path_virtualbox/Machine/[GuestOSName]/Logs/VBox.log,此例網路卡使用 "e1000: eth0: e1000_probe: Intel(R) PRO/1000 Network Connection"


    • 若啟動虛擬機器時,產生 Configuration error: Failed to get the "MAC" value (VERR_CFGM_VALUE_NOT_FOUND). 錯誤訊息,可以編輯 /path_virtualbox/Machine/[GuestOSName]/[GuestOSName].xml 檔案,把類似的訊息刪除

          <ExtraData>
            ...
            <ExtraDataItem name="VBoxInternal/Devices/pcnet/0/LUN#0/Config/HTTP/GuestPort" value="22"/>
            <ExtraDataItem name="VBoxInternal/Devices/pcnet/0/LUN#0/Config/HTTP/HostPort" value="2222"/>
            <ExtraDataItem name="VBoxInternal/Devices/pcnet/0/LUN#0/Config/HTTP/Protocol" value="TCP"/>
            ...
          </ExtraData>




  3. 新增網路卡


  4. 共享資料夾

    • Windows 掛載網路硬碟

      • C:\> net use x: \\vboxsvr\[your folder name]






2010年7月14日 星期三

腹有詩書氣自華

記得第一次看到這句,好像是對岸的一篇故事,述說著求學向上的過程。這句話剛剛查了才知道原句出處的情境。


「腹有詩書氣自華」出自蘇軾〈和董傳留別〉:「麤繒大布裹生涯,腹有詩書氣自華。厭伴老儒烹瓠葉,強隨舉子踏槐花。囊空不辦尋春馬,眼亂行看擇婿車。得意猶堪誇世俗,詔黃新濕字如鴉。」(來源:煙霞山林 - http://blog.yam.com/gracecss/article/24906608


碩二上快結束時,曾和吳大閒聊,提到了「技術和健康」,相信只要把持住就不用擔心未來。現在我的確可以慢慢地累積技術跟健康,然而,內心呢?可還需要多多加把勁!期待兩年後。


[PHP] 取得指定目錄下的檔案清單

由於有些 open source 把 Javascript Code 分成很多個目錄,然後我又懶的一個個加入使用,所以,又偷懶寫了個小程式,用來取得指定目錄裡頭的檔案,搭配 Regular Expression 來決定哪些檔名要紀錄,哪些 file path 要捨棄。


程式碼:


<?php
function getFileList( $file_path , $filename_regexp_pattern , $skip_filepath_regexp_pattern )
{
        $_file_list = array();
        if( !file_exists( $file_path ) || !is_dir( $file_path ) )
                return $_file_list;
        if ( $handle = opendir( $file_path ) )
        {
                while (false !== ($file = readdir($handle)))
                {
                        if( $file == '.' || $file == '..' )
                                continue;
                        $target = $file_path . DIRECTORY_SEPARATOR . $file;
                        if( is_dir( $target ) )
                                foreach( getFileList( $target , $filename_regexp_pattern , $skip_filepath_regexp_pattern ) as $k => $v )
                                        $_file_list[ $v ] = $v;
                        if( !empty( $skip_filepath_regexp_pattern ) && preg_match( $skip_filepath_regexp_pattern , $target ) )
                                continue;
                        if( empty( $filename_regexp_pattern ) || preg_match( $filename_regexp_pattern , $file ) )
                                $_file_list[ $target ] = $target;
                }
                closedir($handle);
        }
        return $_file_list;
}

//print_r( getFileList( '.' , '/\.js$/is' , '/test/is' ) );
foreach( getFileList( '.' , '/\.js$/is' , '/test/is' ) as $js_file )
        echo '<script src="'.$js_file.'" type="text/javascript"></script>'."\n";
?>


[PHP] 替已壓縮 Javascript Code 稍稍排版

有時後會需要看別人寫的 Javascript Code,很多情況該程式碼已經被壓縮或最佳化,然後程式碼就被擠成一列而已,然而,那一些展開後可能是數百數千列的程式碼。


為此,就簡單寫隻 PHP 程式碼,把 Javascript Code 稍稍地排版,例如碰到 { 和 } 要縮排等,我記得以前用 Visual C++ 時,可全選程式碼後按 shtif + F8 就可以排版好了,這已經是七年前的印象,不過手邊沒這種環境,目前只愛用 vim 啦,就簡單寫一下 PHP 來處理。在自行更改 input 跟 output 吧。


程式碼(僅適合觀看,不適合轉完又拿來用喔,因為有些像 regular expression 應該因為此排版而出錯):


<?php
        $raw = file_get_contents( 'bundle.js' );

        $level = 0;
        for( $i=0 ; $i<strlen( $raw ) ; ++$i )
        {   
                if( $level > 0 && $raw[$i] == "\n" )
                {   
                        $sp = '';
                        for( $j=0 ; $j<$level; ++$j )
                                $sp .= "\t";

                        $prev_part = substr( $raw , 0 , $i );
                        $next_part = substr( $raw , $i + 1 );
                        $raw = $prev_part . $raw[ $i ] . $sp . $next_part ;

                        $i += $level;
                }   
                else if( $raw[ $i ] == ';' )
                {   
                        $prev_part = substr( $raw , 0 , $i );
                        $next_part = substr( $raw , $i + 1 );
                        $raw = $prev_part . $raw[ $i ] . "\n" . $next_part ;
                }   
                else if( $raw[ $i ] == '{' )
                {   
                        $prev_part = substr( $raw , 0 , $i );
                        $next_part = substr( $raw , $i + 1 );
                        $raw = $prev_part . $raw[ $i ] . "\n" . $next_part ;

                        ++$level;
                }   
                else if( $level > 0 && $raw[ $i ] == '}' )
                {   
                        --$level;
                        $sp = '';
                        for( $j=0 ; $j<$level; ++$j )
                                $sp .= "\t";

                        $prev_part = substr( $raw , 0 , $i );
                        $next_part = substr( $raw , $i + 1 );
                        $raw = $prev_part . "\n" . $sp . $raw[ $i ] . "\n" . $next_part ;

                        $i += $level + 1 ;      // 1 for first newline
                }   
                echo "$level\n";
        }
        file_put_contents( 'bundle.js.new' , $raw );
?>



2010年7月12日 星期一

使用 Django 架設 Bookworm @ Ubuntu 10.04

Bookworm 是一套採用 New BSD License 的 Open Source,細節請參考 http://code.google.com/p/threepress/ ,也可以直接使用線上版:



紀錄一下在從無到有的安裝過程:



  1. 安裝 Virutalbox 

  2. 安裝 Ubuntu 10.04 32-bit

  3. 基本環境安裝

    • $ sudo apt-get install vim subversion



  4. 取得 Django 並安裝

    • $ wget http://www.djangoproject.com/download/1.0.4/tarball/
      $ tar xzvf Django-1.0.4.tar.gz
      $ cd Django-1.0.4
      $ sudo python setup.py install



  5. 取得 bookworm 程式碼

    • svn checkout http://threepress.googlecode.com/svn/trunk/ threepress-read-only



  6. 安裝相關環境

    • $ sudo apt-get install patch mysql-server mysql-query-browser python-mysqldb python-lxml python-cssutils python-beautifulsoup python-twill python-openid

    • $ cd threepress-read-only/bookworm/gdata ; sudo python setup.py install;

    • 讓 mysql 使用 utf8 (此部份是從網路上隨意找到的資料,不保證正確性)

      • $ sudo vim /etc/mysql/my.cnf

        • [client]

          • default-character-set = utf8



        • [mysqld]

          • character-set-server = utf8
            collation-server = utf8_unicode_ci
            init_connect = 'set collation_connection = utf8_unicode_ci;'





      • 重新開機( 我用 $ sudo /etc/init.d/mysql restart 好像沒啥用,最後重開機才解決)





  7. 建立 bookworm 資料庫、threepress 帳號,記得要設定好權限,避免錯誤可以先把 bookworm 的所有權限都打開

  8. 初始化與執行

    • $ cd threepress-read-only/bookworm/ ;
      $ mkdir log ; touch log/bookworm.log ;  chmod -R 777 log/ library/storage
      $ python manage.py syncdb
      $ mysql -u threepress -p
      mysql> use bookworm;
      mysql> create
      fulltext index epubtext on library_htmlfile (words);
      mysql> quit;
      $ python manage.py runserver




啟動後,連上 http://localhost:8000 就可以看到登入介面囉!只是一堆 css 似乎都不存在(請查看 urls.py ),所以版面有點亂。後來我把一本三國演義的 EPUB 電子書丟進去給他處理,看來沒啥問題。


2010年7月8日 星期四

UITableViewController 之 didSelectRowAtIndexPath 無反應的問題

想說寫個簡單的 UITableViewController 來了解一些流程細節,結果每當我按下某個項目時,卻遲遲沒有動作?確認後,發現 didSelectRowAtIndexPath 函式完全沒進入。找了一些文章,也看不出個所以然,畢竟完完全全是最簡單的 UITableViewController ,新增後只修改 numberOfSectionsInTableView 和 numberOfRowsInSection 回傳的數字而已。


經過幾番確認,最後發現是我自己呼叫的流程問題:


錯誤的程式碼:


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    UINavigationController *n = [[UINavigationController alloc] init];
    
    MyUITableViewController *t = [[MyUITableViewController alloc] initWithStyle:UITableViewStylePlain];
    [n pushViewController:t animated:YES];
    t.title = @"MyTable";

    [window addSubview:n.view];
    [t release];
    [n release];
    // Override point for customization after application launch.
    
    [window makeKeyAndVisible];
    
    return YES;
}


錯誤的地方就只是把 UINavigationController 給 release 掉,然後他又會 release 掉 MyUITableViewController,因此關於 MyUITableViewController 物件就等於被回收掉。需留意 [window addSubview:n.view] 也只是會對那個 view 做 retain 而已,所以整個 table 還是看的到,但 controller 的事件卻無反應。


在此僅小小測試用的解法,就是不要去做 [n release] 。若是真正要跑的程式,應該是擺在 dealloc 函式中去處理,而 UINavigationController 要拉到 Interface 裡頭宣告。果真一個禮拜沒寫 iPhone 就會快忘光了!這個簡單的東西花了十來分鐘才發現。


而回到原本的測試是單純想要了解,從一個 UITableViewController 初始化、被切換到另一個 View 時,再被切回來時會呼叫哪些函數。測試的程式就只是在 MyUITableViewController 的 didSelectRowAtIndexPath 裡頭,宣告一個新的 MyUITableViewController 並 push 到 self.navigationController 裡頭,接著再 back 回來。從一開始的執行到結束所印出的訊息:


tabletest[1706:207] viewDidLoad:First
tabletest[1706:207] viewWillAppear:First
tabletest[1706:207] viewDidAppearFirst
tabletest[1706:207] >>>>In didSelectRowAtIndexPath<<<<
tabletest[1706:207] viewDidLoad:Second
tabletest[1706:207] viewWillDisappear:First
tabletest[1706:207] viewWillAppear:Second
tabletest[1706:207] viewDidDisappear:First
tabletest[1706:207] viewDidAppearSecond
tabletest[1706:207] viewWillDisappear:Second
tabletest[1706:207] viewWillAppear:First
tabletest[1706:207] viewDidDisappear:Second
tabletest[1706:207] viewDidAppearFirst


2010年7月3日 星期六

iOS 開發教學 - 使用 UIScrollView 顯示照片清單

UIScrollView

筆記之前透過 UIScorllView 呈現跟 iPhone 內建的照片瀏覽程式的類似效果

關鍵程式碼:

-
(void)viewWillAppear:(BOOL)animated

{
   [super viewWillAppear:animated];
   CGRect currFrame =
[[UIScreen mainScreen] bounds];

   
   [scroll setDelegate:self];
   [scroll
setBackgroundColor:[UIColor blackColor]];

   [scroll
setScrollEnabled:YES];

   [scroll setPagingEnabled:YES];

   CGSize photoView =
CGSizeMake(100, 100);

   
   int photo_cnt = 10;
   
   int col_cnt =
currFrame.size.width / photoView.width;

   int row_cnt =
photo_cnt / col_cnt + ( ( photo_cnt % col_cnt ) ? 1 : 0 );

   float pandding =
(currFrame.size.width - photoView.width * col_cnt ) / ( col_cnt + 1 );

   
   [scroll
setContentOffset:CGPointMake(0, 0)];

   [scroll
setContentSize:CGSizeMake(currFrame.size.width, (photoView.height +
pandding )* row_cnt + pandding )];


   [scroll setFrame:currFrame];
   [[self view]
addSubview:scroll];



   NSLog(@"row,col,pandding:(%d,%d,%f)", row_cnt ,col_cnt,pandding);

   
   for( int i=0 ;
i<photo_cnt ; i++ )

   {
       int x = i % col_cnt;
       int y = i /
col_cnt;


       


       UIImage *thumbnail = [UIImage imageNamed:[NSString
stringWithFormat: ( i+1 < 10 ? @"0%d.jpg" : @"%d.jpg" ), i+1]];


       UIButton *b =
[[UIButton alloc] init];

       [b setBackgroundImage:thumbnail
forState:UIControlStateNormal];

       [thumbnail release];
       
       [b
setShowsTouchWhenHighlighted:YES];

       [b
setUserInteractionEnabled:YES];

       [b setFrame:CGRectMake( x * (
photoView.width + pandding ) + pandding , y * ( photoView.height +
pandding ) + pandding , photoView.width , photoView.height )];

       [scroll
addSubview:b];

       [b release];
   }
}

使用上宣告一個 UIViewController 並且使用 UIScrollViewDelegate protocol,並且修改其 viewWillAppear 函式,除此之外,請自備 10 張圖,依序為 01.jpg, 02.jpg, ..., 10.jpg。程式碼中的 scroll 為 UIScrollView 物件。

程式流程:


  • 取得當前設備的解析度

  • 設定 scroll 物件的控制與回應、背景設成黑色等

  • 假設一張圖將使用 75x75 的大小,計算一列可擺幾張圖,以及圖與圖之間要留多少空間,以及最後有幾列,透過這些資訊決定 scroll 物件的內容大小

  • 接著使用 UIButton 來呈現圖片,並且依照圖片的順序決定顯示的位置

  • 收工!

使用 UIScrollView 算是可以很輕鬆地達到 iPhone 內建軟體的照片呈現效果,但有個很致命的缺點,那就是上述的過程是直接用原圖呈現的,所以圖片看起來會有失真,並且因為是使用原圖大小,將導致記憶體使用吃緊,並隨著圖片很多張時,問題會越來越嚴重!其他細節,可以參考 UITableViewCotrller ,其架構不錯,特別是在 Cell 的重複使用上,巧妙地控制好記憶體的使用。

因此,其實也是可以用 UITableViewController 來這個效果,只需要把 Cell 做一些手腳處理就行,如 [cell setSelectionStyle:UITableViewCellSelectionStyleNone]; 和 [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; 等。在此感謝同事的提醒,不然我還沒留意 UITableViewController 是繼承 UIScrollViewController 的!

此篇主要記錄照片座標等計算的部分,雖然使用 UITableViewController 後座標計算又不一樣並且更加簡單,但還是留念一下囉。至於縮圖方面,可以參考此篇文章:UIImage: Resize, then Crop