<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5316466304108610731</id><updated>2012-02-16T07:55:17.356-08:00</updated><category term='&#xA;程式-PHP'/><category term='Fedora-教學'/><category term='養狗日記'/><category term='軟體推薦'/><category term='Eclipse-教學'/><category term='WordPress-教學'/><category term='瀏覽器-教學'/><category term='doctrine'/><category term='Windows系統'/><category term='Testing'/><category term='git'/><category term='網站介紹'/><category term='Mac'/><category term='程式-ShellScript'/><category term='Angile'/><category term='MySQL-教學'/><category term='程式-jQuery'/><category term='工作方法'/><category term='Smarty-教學'/><category term='n23'/><category term='線上工具'/><category term='VMWare'/><category term='SSH'/><category term='Appserv'/><category term='羽球-硬體'/><category term='演算法'/><category term='MailServer'/><category term='投資理財'/><category term='程式-PEAR'/><category term='Xampp'/><category term='網路'/><category term='FED'/><category term='SeverTuning'/><category term='生活'/><category term='Subversion-教學'/><category term='程式-PHP'/><category term='MicrosoftOffice'/><category term='基金-新聞'/><category term='Apache-教學'/><category term='程式-CakePHP'/><category term='RoR'/><category term='HTML'/><category term='CruiseControl'/><category term='正規表示法'/><category term='Propel'/><category term='程式-HTML'/><category term='進修'/><category term='Python'/><category term='育兒'/><category term='手機'/><category term='Linux-教學'/><category term='Deployment'/><category term='羽球-其他'/><category term='IntelliJ IDEA'/><category term='程式-JavaScript'/><category term='MySQL-優化'/><category term='Oracle'/><category term='科技-名詞解釋'/><category term='軟體-推薦'/><category term='C++'/><category term='Dreamweaver-教學'/><category term='TWE-COMMERCE'/><category term='internet'/><category term='其它'/><category term='勵志'/><category term='Blogger-教學'/><category term='PHPFramework'/><category term='XenServer'/><category term='羽球-技巧'/><category term='台灣固網'/><category term='HTML5'/><category term='mootools'/><category term='Mobile'/><category term='PHP-Wiki'/><category term='MySQL-Debug'/><category term='Sqlite'/><category term='程式-手冊'/><category term='生活-勵志'/><category term='SEO-教學'/><category term='CSS-教學'/><category term='ZendFramework'/><category term='程式-安全'/><category term='xslt'/><category term='Java'/><category term='NoSQL'/><category term='API'/><category term='ie'/><category term='FreeBSD-教學'/><category term='PC-病毒'/><category term='程式-名詞解釋'/><category term='Joomla'/><category term='inno'/><category term='Tools'/><category term='PC-硬體'/><category term='其他'/><category term='PC-效能'/><category term='FreeBSD-Debug'/><title type='text'>Linux/Apache/PHP+MySQL/Smarty/JQuery/RubyOnRails</title><subtitle type='html'>分享 Linux/Apache/PHP+MySQL/Smarty/JQuery/RubyOnRails 的點點滴滴!</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default?start-index=101&amp;max-results=100'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>524</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1556312633802175072</id><published>2012-02-08T01:30:00.000-08:00</published><updated>2012-02-08T01:36:45.738-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>[Debug]"FastCGI process exceeded configured activity timeout" error</title><content type='html'>Edit %WINDIR%\system32\inetsrv\fcgiext.ini&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[Types]&lt;br /&gt;&lt;br /&gt;…&lt;br /&gt;&lt;br /&gt;php=PHP&lt;br /&gt;&lt;br /&gt;…&lt;br /&gt;&lt;br /&gt;[PHP]&lt;br /&gt;&lt;br /&gt;ExePath=C:\Program Files (x86)\PHP\php-cgi.exe&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;under ExePath=C:\Program Files (x86)\PHP\php-cgi.exe, &lt;br /&gt;add the following:&lt;br /&gt;&lt;br /&gt;InstanceMaxRequests=10000&lt;br /&gt;ActivityTimeout=600&lt;br /&gt;RequestTimeout=600&lt;br /&gt;&lt;br /&gt;reference : &lt;br /&gt;http://forums.zend.com/viewtopic.php?f=8&amp;t=273&lt;br /&gt;http://forums.kayako.com/threads/support-desk-not-responding-after-sending-attachments.26719/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1556312633802175072?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1556312633802175072/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1556312633802175072' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1556312633802175072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1556312633802175072'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2012/02/debugfastcgi-process-exceeded.html' title='[Debug]&quot;FastCGI process exceeded configured activity timeout&quot; error'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-6974824653896317280</id><published>2012-02-06T21:32:00.000-08:00</published><updated>2012-02-06T21:33:03.975-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internet'/><title type='text'>Crazy Egg Web Analytics</title><content type='html'>The Astonishing Power of Eye&lt;br /&gt;Tracking Technology... Without the High Costs&lt;br /&gt;&lt;br /&gt;website : http://www.crazyegg.com/home2&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-6974824653896317280?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/6974824653896317280/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=6974824653896317280' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6974824653896317280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6974824653896317280'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2012/02/crazy-egg-web-analytics.html' title='Crazy Egg Web Analytics'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1938558958827166634</id><published>2012-01-17T01:24:00.000-08:00</published><updated>2012-01-17T01:26:02.229-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tools'/><title type='text'>Apache Kafka</title><content type='html'>A high-throughput distributed messaging system.&lt;br /&gt;&lt;br /&gt;Apache Kafka is a distributed publish-subscribe messaging system&lt;br /&gt;&lt;br /&gt;Apache Kafka is a distributed publish-subscribe messaging system. It is designed to support the following&lt;br /&gt;&lt;br /&gt;Persistent messaging with O(1) disk structures that provide constant time performance even with many TB of stored messages.&lt;br /&gt;High-throughput: even with very modest hardware Kafka can support hundreds of thousands of messages per second.&lt;br /&gt;Explicit support for partitioning messages over Kafka servers and distributing consumption over a cluster of consumer machines while maintaining per-partition ordering semantics.&lt;br /&gt;Support for parallel data load into Hadoop.&lt;br /&gt;Kafka provides a publish-subscribe solution that can handle all activity stream data and processing on a consumer-scale web site. This kind of activity (page views, searches, and other user actions) are a key ingredient in many of the social feature on the modern web. This data is typically handled by "logging" and ad hoc log aggregation solutions due to the throughput requirements. This kind of ad hoc solution is a viable solution to providing logging data to an offline analysis system like Hadoop, but is very limiting for building real-time processing. Kafka aims to unify offline and online processing by providing a mechanism for parallel load into Hadoop as well as the ability to partition real-time consumption over a cluster of machines.&lt;br /&gt;The use for activity stream processing makes Kafka comparable to Facebook's Scribe or Apache Flume (incubating), though the architecture and primitives are very different for these systems and make Kafka more comparable to a traditional messaging system. See our design page for more details.&lt;br /&gt;&lt;br /&gt;Apache Kafka is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator PMC. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.&lt;br /&gt;&lt;br /&gt;reference : http://incubator.apache.org/kafka/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1938558958827166634?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1938558958827166634/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1938558958827166634' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1938558958827166634'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1938558958827166634'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2012/01/apache-kafka.html' title='Apache Kafka'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-3499055549250856205</id><published>2012-01-04T19:25:00.000-08:00</published><updated>2012-01-04T19:29:35.341-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-JavaScript'/><title type='text'>Highcharts</title><content type='html'>Highcharts&lt;br /&gt;Highcharts is a charting library written in pure JavaScript, offering intuitive, interactive charts to your web site or web application. Highcharts currently supports line, spline, area, areaspline, column, bar, pie and scatter chart types.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-3499055549250856205?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/3499055549250856205/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=3499055549250856205' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3499055549250856205'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3499055549250856205'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2012/01/highcharts.html' title='Highcharts'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-6080870147851917063</id><published>2011-12-28T03:02:00.000-08:00</published><updated>2011-12-28T03:03:17.502-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>Xen VPS Hosting</title><content type='html'>Deploy your Linode cloud servers worldwide, including our newest facility in Tokyo, Japan.&lt;br /&gt;&lt;br /&gt;url : http://www.linode.com/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-6080870147851917063?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/6080870147851917063/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=6080870147851917063' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6080870147851917063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6080870147851917063'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/12/xen-vps-hosting.html' title='Xen VPS Hosting'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-8599290073878859626</id><published>2011-12-28T00:56:00.000-08:00</published><updated>2011-12-28T00:57:11.738-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>結婚習俗 - 結婚紅包行情</title><content type='html'>結婚紅包行情價：&lt;br /&gt;　 &lt;br /&gt;600元---一般訂婚習俗和結婚習俗，紅包是由600元開始起跳，但那是指非常不熟悉的禮金數。&lt;br /&gt;1200元---就是一般辦公室朋友的[價位] ，三分熟交情；只包紅包 祝賀，不出席喜宴。&lt;br /&gt;1600元---是可以一起吃飯逛街的朋友，另外；如果你還可以拿到 一盒新娘的喜餅，那就不好再少於這個數字。&lt;br /&gt;2000~2600元---可以交換一些心事，倒些垃圾的朋友，平日相交已有些推心置腹的感覺。&lt;br /&gt;3600元---親密的姐姐淘，兄弟情，如熟透的親密感。&lt;br /&gt;6000元---親姐妹、親兄弟血濃於水，不容取代的手足情深，自然就從6000元以上起跳。&lt;br /&gt;　 紅包的由來：&lt;br /&gt;　 紅包袋---亦稱為紅封包，是將金錢放置紅色封套內做成的一種小禮物。訂婚結婚包紅包是中國傳統民俗，在各種喜慶場合如過年、小孩滿月及各式送金錢作禮物時都會使用。紅色在中國文化當中象徵著喜慶，紅包當中一般會放入有吉祥數字的金錢，例如有8字或以雙數結尾的數目，而且結婚紅包賀詞寫法也是有重要的象徵意義。&lt;br /&gt;　 　&lt;br /&gt;　 　&lt;br /&gt;　 小叮嚀&lt;br /&gt;　 &lt;br /&gt;如果女方是訂婚收禮金，而男方是結婚收禮金；或是訂結婚一起舉辦，男女雙方都在現場擺禮桌收禮金，所以；要包給她，最好跟她說一聲，如果男方那邊收走了，至少她看過禮金簿時，知道你有包紅包給她，日後；你結婚時她才有包紅包的參考值。&lt;br /&gt;紅包討吉利，新郎倌可不要忘了準備紅包給幫忙的親友；諸如：前來開新郎車門的舅爺，及六名隨新郎前去迎娶新娘的親友，捧洗臉水的女方親友、媒婆禮等。金額多為600、1200及2000元等。&lt;br /&gt;　 　&lt;br /&gt;　 紅包賀詞寫法&lt;br /&gt;　 &lt;br /&gt;紅包上可寫上結婚賀詞，但紅包左下方寫上自己的名字(例如:xxx賀)&lt;br /&gt;百年琴瑟 花好月圓 天緣巧合 金石同心 白頭偕老 情聯碧合 宜爾室家 花開並蒂 情投意合 天作之合 相親相愛 永浴愛河 緣訂三生......更多結婚賀詞&lt;br /&gt;　 　&lt;br /&gt;　 &lt;br /&gt;　  起源 | 訂婚流程 | 迎娶流程 | 歸寧流程 | 南北差異 | 結婚禁忌 | 結婚紅包行情 　&lt;br /&gt;   &lt;br /&gt;&lt;br /&gt;本網站部分相關資料於各大雜誌及網站收集而來，單純為了提供網友方便，不作他用，若原作者不同意引用，請與我們聯絡。&lt;br /&gt;&lt;br /&gt;reference : http://www.myloving.com.tw/WedInfo_MoresPacket.asp&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-8599290073878859626?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/8599290073878859626/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=8599290073878859626' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8599290073878859626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8599290073878859626'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/12/blog-post_28.html' title='結婚習俗 - 結婚紅包行情'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1855870779666947798</id><published>2011-12-28T00:55:00.000-08:00</published><updated>2011-12-28T00:56:28.598-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>結婚紅包行情</title><content type='html'>最近好日子很多，因此收到不少朋友寄來請帖為此我希望得知包多少才得體，同時(1)顧及情感程度(2)顧及對方是否好回禮，台灣習俗:回禮不能比當初收到禮金少(至少等價)，因此包得好是祝福包不好是負擔，可能是自己的負擔也是對方的負擔。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;此圖來自: veryWed討論區&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;這是PTT網友peegoo7 (瓜瓜瓜)整理出來的價目表&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;結論(參考朋友的看法):&lt;br /&gt;(1)有分 人到 和 禮到 兩種金額，由於結婚禮金是要補助新人的做法，因此不難看出，扣除每桌費用後多少補貼的做法，好比一桌一萬元，每桌抓5-7個紅包(假設洽好是5對10人)，包2000是剛好餐費，則2200到2600甚至更多是較佳的包法，這樣的算法因人、地而異&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(2)人到最少得包1600，如果帶的人數太多要酌量多加一點。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(3)由於只用雙數，不用單數！不用「四」和「八」(八有「別」之意) ！因此能用的數字只有600、1000、1200、1600、2000、2200、2600、3600、6000、6600等組合。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(4)你包3600給別人，碰上台灣習俗:回禮不能比當初收到禮金少(至少等價)，雖可回同樣的禮數，但是由於下個數字是6000，會有你要別人包6000的味道在。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;依照景氣不同會有不同的包法，法無定法可由自己的狀態、友好度等做為考量，透過網路資料的整理可提供大家一個參考的方案。&lt;br /&gt;&lt;br /&gt;reference : http://im88.tw/?p=1243&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1855870779666947798?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1855870779666947798/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1855870779666947798' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1855870779666947798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1855870779666947798'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/12/blog-post.html' title='結婚紅包行情'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1803439434042268340</id><published>2011-12-19T23:50:00.000-08:00</published><updated>2011-12-19T23:52:06.205-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tools'/><title type='text'>Gridow - Presenting Yourself in the simplest way</title><content type='html'>Make your own Gridow video,&lt;br /&gt;All you need is a laptop,&lt;br /&gt;with a camera and internet access.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1803439434042268340?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1803439434042268340/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1803439434042268340' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1803439434042268340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1803439434042268340'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/12/gridow-presenting-yourself-in-simplest.html' title='Gridow - Presenting Yourself in the simplest way'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-4663312905080624895</id><published>2011-12-19T00:29:00.000-08:00</published><updated>2011-12-19T00:30:51.991-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><title type='text'>Take a BITE out of Bugs and Redundant Labor</title><content type='html'>In a time when more and more of the web is becoming streamlined, the process of filing bugs for websites remains tedious and manual. Find an issue. Switch to your bug system window. Fill out boilerplate descriptions of the problem. Switch back to the browser, take a screenshot, attach it to the issue. Type some more descriptions.  The whole process is one of context switching; from the tools used to file the bug, to gather information about it, to highlight problematic areas, most of your focus as the tester is pulled away from the very application you’re trying to test.&lt;br /&gt;&lt;br /&gt;The Browser Integrated Testing Environment, or BITE, is an open source Chrome Extension which aims to fix the manual web testing experience. To use the extension, it must be linked to a server providing information about bugs and tests in your system. BITE then provides the ability to file bugs from the context of a website, using relevant templates.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When filing a bug, BITE automatically grabs screenshots, links, and problematic UI elements and attaches them to the bug.  This gives developers charged with investigating and/or fixing the bug a wealth of information to help them determine root causes and factors in the behavior.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When it comes to reproducing a bug, testers will often labor to remember and accurately record the exact steps taken.  With BITE, however, every action the tester takes on the page is recorded in JavaScript, and can be played back later.  This enables engineers to quickly determine if the steps of a bug repro in a specific environment, or whether a code change has resolved the issue.&lt;br /&gt;&lt;br /&gt;Also included in BITE is a Record/Playback console to automate user actions in a manual test.  Like the BITE recording experience, the RPF console will automatically author javascript that can be used to replay your actions at a later date.  And BITE’s record and playback mechanism is fault tolerant; UI automation tests will fail from time to time, and when they do, it tends to be for test issues, rather than product issues.  To that end, when a BITE playback fails, the tester can fix their recording in real-time, just by repeating the action on the page.  There’s no need to touch code, or report a failing test; if your script can’t find a button to click on, just click on it again, and the script will be fixed!  For those times when you do have to touch the code, we’ve used the Ace (http://ace.ajax.org/) as an inline editor, so you can make changes to your javascript in real-time.&lt;br /&gt;&lt;br /&gt;Check out the BITE project page at http://code.google.com/p/bite-project. Feedback is welcome at bite-feedback@google.com.  Posted by Joe Allan Muharsky from the Web Testing Technologies Team (Jason Stredwick, Julie Ralph, Po Hu and Richard Bustamante are the members of the team that delivered the product).&lt;br /&gt;&lt;br /&gt;reference : http://googletesting.blogspot.com/2011/10/take-bite-out-of-bugs-and-redundant.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-4663312905080624895?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/4663312905080624895/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=4663312905080624895' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/4663312905080624895'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/4663312905080624895'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/12/take-bite-out-of-bugs-and-redundant.html' title='Take a BITE out of Bugs and Redundant Labor'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-9059911989458057036</id><published>2011-12-14T18:36:00.000-08:00</published><updated>2011-12-14T18:37:57.905-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-JavaScript'/><title type='text'>What is it? Raphaël is a small JavaScript library that should simplify your work with vector graphics on the web.</title><content type='html'>What is it?&lt;br /&gt;Raphaël is a small JavaScript library that should simplify your work with vector graphics on the web. If you want to create your own specific chart or image crop and rotate widget, for example, you can achieve it simply and easily with this library.&lt;br /&gt;Raphaël ['ræfeɪəl] uses the SVG W3C Recommendation and VML as a base for creating graphics. This means every graphical object you create is also a DOM object, so you can attach JavaScript event handlers or modify them later. Raphaël’s goal is to provide an adapter that will make drawing vector art compatible cross-browser and easy.&lt;br /&gt;Raphaël currently supports Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ and Internet Explorer 6.0+.&lt;br /&gt; How to use it?&lt;br /&gt;Download and include raphael.js into your HTML page, then use it as simple as:&lt;br /&gt;// Creates canvas 320 × 200 at 10, 50&lt;br /&gt;var paper = Raphael(10, 50, 320, 200);&lt;br /&gt;&lt;br /&gt;// Creates circle at x = 50, y = 40, with radius 10&lt;br /&gt;var circle = paper.circle(50, 40, 10);&lt;br /&gt;// Sets the fill attribute of the circle to red (#f00)&lt;br /&gt;circle.attr("fill", "#f00");&lt;br /&gt;&lt;br /&gt;// Sets the stroke attribute of the circle to white&lt;br /&gt;circle.attr("stroke", "#fff");&lt;br /&gt;&lt;br /&gt;reference : http://raphaeljs.com/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-9059911989458057036?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/9059911989458057036/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=9059911989458057036' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/9059911989458057036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/9059911989458057036'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/12/what-is-it-raphael-is-small-javascript.html' title='What is it? Raphaël is a small JavaScript library that should simplify your work with vector graphics on the web.'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-340448780225982653</id><published>2011-11-29T22:24:00.000-08:00</published><updated>2011-11-29T22:25:20.465-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-JavaScript'/><title type='text'>Mar30Speeding up Mootools with createDocumentFragment()</title><content type='html'>A couple of days ago David Walsh posted a blog entry where he recommended a video from Google about JavaScript. I’ve found this very interesting and I have done some research about how to implement this very easy to Mootools. There are a lot of do’s and dont’s  but what is the best practice for us as a Mootools developer? How to avoid the basic Mootools solutions and speed up your plugins? Well, I’ll post my neat workarounds to get your speed up.&lt;br /&gt;&lt;br /&gt;I’ll start with modifying the DOM by injecting elements with the Mootools function inject(). Injecting elements to the DOM is very common with Mootools but it will make it slow down. To speed up you’ll have to do as little DOM modification as possible.&lt;br /&gt;&lt;br /&gt;For example you load a JSON file and add elements depending on the content of the request. In this example we don’t avoid the Mootools each() function. We focus on the point that we don’t call the inject() function every time. This is my solution.Add the following lines to you general JavaScript file to implement a new function. I’ll recommend this if you&lt;br /&gt;&lt;br /&gt;view plaincopy to clipboardprint?&lt;br /&gt;window.addEvent('domready', function() {  &lt;br /&gt;    Element.implement({  &lt;br /&gt;        docFragment: function(){  &lt;br /&gt;            return document.createDocumentFragment();  &lt;br /&gt;        }  &lt;br /&gt;    });  &lt;br /&gt;});  &lt;br /&gt;We’re going to add options to a select element, based on the JSON request.&lt;br /&gt;&lt;br /&gt;view plaincopy to clipboardprint?&lt;br /&gt;var fragment = Element.docFragment();  &lt;br /&gt;  &lt;br /&gt;json.each(function(o){  &lt;br /&gt;    var option = new Element('option',{'value': o.value, 'html': o.html});  &lt;br /&gt;    fragment.appendChild(option); //No reflow  &lt;br /&gt;});  &lt;br /&gt;  &lt;br /&gt;$('select').appendChild(fragment); //Reflow!  &lt;br /&gt;Results&lt;br /&gt;I’ve done some testing and I think it helped me pretty good to speed up my scripts. I’ve modified my cvLinkSelect class and did some testing with it. Here are the results. Not very impressive at this point but you can see it will help the performance.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The test handles a 12 lined JSON request and for each record an option element will be added to the DOM. Not a very big effort for a browser, but you’ll see that there is some difference on the most common browsers and it’s no surprise that IE is the slowest of them all. If you scale this up it has to be a large difference and I’m confident with that.&lt;br /&gt;&lt;br /&gt;Read more about createDocumentFragment on Mozilla Developer Center.&lt;br /&gt;&lt;br /&gt;reference : http://youngdutchdesign.com/speeding-up-mootools-with-createdocumentfragment&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-340448780225982653?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/340448780225982653/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=340448780225982653' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/340448780225982653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/340448780225982653'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/11/mar30speeding-up-mootools-with.html' title='Mar30Speeding up Mootools with createDocumentFragment()'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-9065738847808359605</id><published>2011-11-19T20:31:00.000-08:00</published><updated>2011-11-19T20:32:39.380-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Deployment'/><title type='text'>Flipping Out</title><content type='html'>Flickr is somewhat unique in that it uses a code repository with no branches; everything is checked into head, and head is pushed to production several times a day. This works well for bug fixes that we want to go out immediately, but presents a problem when we’re working on a new feature that takes several months to complete. How do we solve that problem? With flags and flippers!&lt;br /&gt;&lt;br /&gt;Feature Flags&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Flags allow us to restrict features to certain environments, while still using the same code base on all servers. In our code, we’ll write something like this:&lt;br /&gt;&lt;br /&gt;if ($cfg.enable_unicorn_polo) {&lt;br /&gt;    // do something new and amazing here.&lt;br /&gt;}&lt;br /&gt;else {&lt;br /&gt;    // do the current boring stuff.&lt;br /&gt;}&lt;br /&gt;We can then set the value of $cfg.enable_unicorn_polo on each environment (false for production, true for dev, etc.). This has the additional benefit of making new feature launches extremely easy: Instead of having to copy a bunch of new code over to the production servers, we simply change a single false to true, which enables the code that has already been on the servers for months.&lt;br /&gt;&lt;br /&gt;Feature Flippers&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Flags allows us to enable features on a per environment basis, but what if we wanted to be more granular and enable features on a per user basis? For that we use feature flippers. These allow us to turn on features that we are actively developing without being affected by the changes other developers are making. It also lets us turn individual features on and off for testing.&lt;br /&gt;&lt;br /&gt;Here is what the Feature Flip page looks like:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Continuously Integrating&lt;br /&gt;Feature flags and flippers mean we don’t have to do merges, and that all code (no matter how far it is from being released) is integrated as soon as it is committed. Deploys become smaller and more frequent; this leads to bugs that are easier to fix, since we can catch them earlier and the amount of changed code is minimized.&lt;br /&gt;&lt;br /&gt;This style of development isn’t all rainbows and sunshine. We have to restrict it to the development team because occasionally things go horribly wrong; it’s easy to imagine code that’s in development going awry and corrupting all your data. Also, after launching a feature, we have to go back in the code base and remove the old version (maintaining separate versions of all features on Flickr would be a nightmare). But overall, we find it helps us develop new features faster and with fewer bugs.&lt;br /&gt;&lt;br /&gt;reference : http://code.flickr.com/blog/2009/12/02/flipping-out/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-9065738847808359605?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/9065738847808359605/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=9065738847808359605' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/9065738847808359605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/9065738847808359605'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/11/flipping-out.html' title='Flipping Out'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-4355013671695470569</id><published>2011-11-19T16:55:00.000-08:00</published><updated>2011-11-19T17:02:27.332-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Deployment'/><title type='text'>Quantum of Deployment</title><content type='html'>Deployinator is now Open Source!&lt;br /&gt;Grab it on github: https://github.com/etsy/deployinator&lt;br /&gt;&lt;br /&gt;We deploy a lot of code. Deployinator is our creation to make that as easy and painless as possible. Deployinator is a one button web-based deployment app. Hit that button and code goes to our webservers and is serving requests in almost no time. Using Deployinator we’ve brought a typical web push from 3 developers, 1 operations engineer, everyone else on standby and over an hour (when things went smoothly) down to 1 person and under 2 minutes.&lt;br /&gt;&lt;br /&gt;At Etsy, we’re doing what’s come to be called Continuous Deployment. However, what we’ve learned is that having a tool like Deployinator is useful for more than just enabling that. This post is about those benefits – for anyone deploying web code.&lt;br /&gt;&lt;br /&gt;Why&lt;br /&gt;&lt;br /&gt;Our job as engineers (and ops, dev-ops, QA, support, everyone in the company actually) is to enable the business goals. We strongly feel that in order to do that you must have the ability to deploy code quickly and safely. Even if the business goals are to deploy strongly QA’d code once a month at 3am (it’s not for us, we push all the time), having a reliable and easy deployment should be non-negotiable.&lt;br /&gt;&lt;br /&gt;It’s a metric I’ve come to call the “quantum of deployment”: what’s the smallest number of steps, with the smallest number of people and the smallest amount of ceremony required to get new code running on your servers? This isn’t a trivial question. Even if you’re on a slow release cycle, and have a push engineer, what happens if there’s an emergency push needed? Does it go through your normal process, or is there a fast-lane? Do your fast-lane deployments get logged? Are they measured for speed? Is everyone aware that it happened the way they would for a normal deployment, or is it your dirty little secret?&lt;br /&gt;&lt;br /&gt;It’s not hard to get started. If you currently have a bunch of shell scripts that move everything in place, wrap those up with a single shell script. The most important thing is that it’s ONE easy step. This might require changing your process. Try to remove or replace special cases. The less thought it takes to deploy, the more you can focus on getting stuff done.&lt;br /&gt;&lt;br /&gt;Once deploying is No Big Deal, a lot of things can change. Features can go out a piece at a time instead of one all-or-nothing push. Your app configuration options can be in code – and changed quickly. Your hair can grow back. Puppies will lick your face!&lt;br /&gt;&lt;br /&gt;What&lt;br /&gt;&lt;br /&gt;Custom software? There’s a lot of choice out there, and we generally try to not reinvent the wheel whenever possible (and this wheel has been invented again and again). After comparing our requirements with the available software, we decided to roll our own. Here’s what we were looking for:&lt;br /&gt;&lt;br /&gt;Web based&lt;br /&gt;Logged (When, What and Who)&lt;br /&gt;Adaptable to our network&lt;br /&gt;Run from a central location&lt;br /&gt;Announced in our IRC and email&lt;br /&gt;Transparent in regards to its actions&lt;br /&gt;Integrated with our graphing/monitoring tools&lt;br /&gt;A lot of our requirements are inspired by the way Flickr deploys, as documented in Building Scalable Websites (Written by Friend of Etsy, Cal Henderson).&lt;br /&gt;&lt;br /&gt;For anyone deploying code (engineers, designers… anyone really), it’s an easy process. Once your code is ready to go, you go to Deployinator and push the button to get it on QA. From there it visits Princess (see the sidebar). Then, when it’s ready to go live, you hit the “Prod” button and soon your code is live, and everyone in IRC knows who pushed what code, complete with a link to the diff. For anyone not on IRC, there’s the email that everyone gets with the same information.&lt;br /&gt;&lt;br /&gt;What it looks like&lt;br /&gt;This is the main Deployinator screen. Here is how we deploy the “web stack”.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Sidebar: What the heck is a “Princess”?&lt;br /&gt;&lt;br /&gt;Princess is our staging environment. So why didn’t we just call it “staging”? Partly, that’s just how we roll. We used to have an environment called “Staging” that was the last stop before code went live to everyone. However, it was not ideally set up; the first time your code would interact with actual production hardware was when you deployed. Princess uses production data stores, our production network and production hardware. In order to make a clean mental break from the old way, one of our rad engineers, Eric Fixler, came up with “Princess”.&lt;br /&gt;&lt;br /&gt;Here’s our IRC bot telling everyone that something went out. It also includes a link to the commits that went live.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;How&lt;br /&gt;&lt;br /&gt;When we first brought Deployinator online, it was just a web frontend to the shell scripts that moved everything in the right place. What we gained by putting a screen in front of it was the ability to iterate the backend without changing the experience for people deploying. Deployinator currently uses svn to update the code, then rsync to move it between environments.&lt;br /&gt;&lt;br /&gt;Another important part of Deployinator is that the environments are a one way street. Code going from Princess to production is unaffected by any commits that have happened since getting on princess. This creates something of a “mantrap“, so that we know exactly what we’re deploying. No surprises!&lt;br /&gt;&lt;br /&gt;Deployinator isn’t used just for our web stack either. With the simple architecture we’ve built, we can add all kinds of stuff to it easily. It’s currently used for many different things such as the API, Lists service, internal admin-only tools and others. Having a single deployment process has removed a lot of complexity.&lt;br /&gt;&lt;br /&gt;When&lt;br /&gt;&lt;br /&gt;This isn’t a post about continuous deployment. Having a very simple deployment procedure is something you should do even if the thought of deploying your code 20 times a day scares you. Deployment can be a contentious subject with many stakeholders. Getting it simple and repeatable allows everyone to share a common vocabulary.&lt;br /&gt;&lt;br /&gt;For the nerds…&lt;br /&gt;&lt;br /&gt;Transporting the bits and bytes&lt;br /&gt;Here’s a rundown of some of the interesting parts of how Deployinator actually moves bits around. As mentioned above, this has changed and will change again. We analyze our entire process and have some low-hanging performance fruit to pick. As of today, an API push takes about 18 seconds, a Princess push takes about the same, and a production web push is 70-150 seconds. Here are the steps that a web push goes through:&lt;br /&gt;&lt;br /&gt;From the repo of truth&lt;br /&gt;&lt;br /&gt;We’re deploying directly from trunk (that’s a whole other post!). So the first step of deploying is to update the code on the deploy host.&lt;br /&gt;&lt;br /&gt;Builda what now?&lt;br /&gt;&lt;br /&gt;After the code is updated, we run “builda”, an app we wrote to take our assets and bundle them up with lots of magic. We use a combination of javascript async loading, Google’s closure and versioned directories to make things like css, javascript and sprites as fast as possible.&lt;br /&gt;&lt;br /&gt;Rsync&lt;br /&gt;&lt;br /&gt;At this point, we have a bunch of directories on our deploy host that represent our web tree. We then rsync to a staging area on each web box.&lt;br /&gt;&lt;br /&gt;Fanout&lt;br /&gt;&lt;br /&gt;At some number of boxes, rsyncing back to a single push host stops working. We’re employing a strategy called fan out. We rsync in chunks of 10 hosts at a time. This is one area where a lot of speed ups will be happening soon.&lt;br /&gt;&lt;br /&gt;First they came for our assets…&lt;br /&gt;&lt;br /&gt;Pop quiz, hotshot: Someone visits the site during a deployment and box 1 (the one they randomly get) has the new code. The html they’re returned refers to a new image. When they request that image, they end up on box 451.. which doesn’t have that asset yet. What do you do? WHAT DO YOU DO?&lt;br /&gt;&lt;br /&gt;We’ve solved this with two steps. The first (mentioned above) is versioned asset directories. The next is to get those assets on ALL hosts before ANY host has the new code referring to it.&lt;br /&gt;&lt;br /&gt;Graceful&lt;br /&gt;&lt;br /&gt;We’re using APC user caching, and expect it to have fresh data each deployment. Things like some application settings, database fields and routes are all cached for super fast lookups. However, if someone changes something in some code going out, we need to make sure that it’s fresh. We’re currently issuing a graceful restart to our apaches on each deployment.&lt;br /&gt;&lt;br /&gt;Deployinator itself&lt;br /&gt;One of the design goals of Deployinator has been to be as simple as possible. It’s a sinatra web app, but could really be written in anything. The script that does the svn updating (and checking out for new stacks) is in PHP and some of the straight-up simplest code possible.&lt;br /&gt;&lt;br /&gt;The commands that Deployinator runs through for each different stack are listed in ruby methods, and are mostly strings (with servers and such interpolated). It’s easy for anyone to come in and change how something works. Simple, understandable software that gets the job done.&lt;br /&gt;&lt;br /&gt;The one fancy bit of Deployinator is the streaming rack middleware that powers the live updating code window:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Database DDLs aren’t code&lt;br /&gt;An awesome feature of Capistrano is the ability to run schema migrations as part of your deployment. At a certain scale, however, database changes become more time consuming and dangerous. All of our schema changes go through a stringent process with several checks in place. However, not all schema is defined in the database. Whenever we have schema that’s defined in code, or inside the data itself, it’s just a normal code push.&lt;br /&gt;&lt;br /&gt;Conclusion&lt;br /&gt;&lt;br /&gt;Our deployment process is a very important part of how we work at Etsy. We treat it just like our web code, databases or other “serious” things. Deployinator has helped us to get more features out faster with less defects and drama. As we triple our engineering team in 2010 (we’re hiring!), tools like this are what make it possible for us to change the world.&lt;br /&gt;&lt;br /&gt;reference : http://codeascraft.etsy.com/2010/05/20/quantum-of-deployment/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-4355013671695470569?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/4355013671695470569/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=4355013671695470569' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/4355013671695470569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/4355013671695470569'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/11/quantum-of-deployment.html' title='Quantum of Deployment'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-377470441250925594</id><published>2011-11-17T05:24:00.000-08:00</published><updated>2011-11-17T05:25:22.265-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL-教學'/><title type='text'>Mysql忘記root密碼</title><content type='html'>1.先停掉mysql&lt;br /&gt;&lt;br /&gt;# /etc/init.d/mysql stop&lt;br /&gt;&lt;br /&gt;2.以–skip-grant-table&amp; 的參數啟動mysql&lt;br /&gt;/usr/bin/mysqld_safe --skip-grant-table&amp;&lt;br /&gt;&lt;br /&gt;3. 更改root 密碼&lt;br /&gt;&lt;br /&gt;# mysql mysql &lt;br /&gt;mysql&gt; UPDATE user SET password=password('123456') WHERE user='root'; &lt;br /&gt;mysql&gt; exit&lt;br /&gt;&lt;br /&gt;4.停掉mysql再重跑&lt;br /&gt;&lt;br /&gt;# /etc/init.d/mysql stop&lt;br /&gt;# mysql -u root -p&lt;br /&gt;&lt;br /&gt;reference : http://tw.myblog.yahoo.com/cherry-liang/article?mid=579&amp;prev=633&amp;next=-2&amp;page=1&amp;sc=1&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-377470441250925594?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/377470441250925594/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=377470441250925594' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/377470441250925594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/377470441250925594'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/11/mysqlroot.html' title='Mysql忘記root密碼'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5814248377955102661</id><published>2011-11-14T17:54:00.000-08:00</published><updated>2011-11-14T17:55:19.800-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>You could use the DOMDocument class to reformat your xml</title><content type='html'>&lt;code&gt;&lt;br /&gt;$dom = new DOMDocument('1.0');&lt;br /&gt;$dom-&gt;preserveWhiteSpace = false;&lt;br /&gt;$dom-&gt;formatOutput = true;&lt;br /&gt;$dom-&gt;loadXML($simpleXml-&gt;asXML());&lt;br /&gt;echo $dom-&gt;saveXML();&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5814248377955102661?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5814248377955102661/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5814248377955102661' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5814248377955102661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5814248377955102661'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/11/you-could-use-domdocument-class-to.html' title='You could use the DOMDocument class to reformat your xml'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-3770681146141268626</id><published>2011-10-28T08:31:00.000-07:00</published><updated>2011-10-28T08:33:22.563-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>PHP : XML Create-Add-Edit-Modify using DOM , SimpleXML , XPath</title><content type='html'>Over the last few working days, I spent a quite a bit of time playing around with XML. While searching through the net, I found few comprehensive PHP XML guides. There never was a ’1 stop all operations’ guide for learning XML.&lt;br /&gt;As such I decided to club together examples of all kinds of operations I ever did on XML in a single post. I hope it benefits others out there who wish to learn more about XML manipulation.&lt;br /&gt;Note : Since the post got quite large, I decided to only use the Tree Map style parsers – DOM &amp; Simple XML.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Operations Performed:&lt;br /&gt;(1) Create XML OR Array to XML Conversion OR CDATA Element Eg&lt;br /&gt;(2) Edit XML – Edit/Modify Element Data (accessed serially)&lt;br /&gt;(3) Edit XML – Edit specific Elements (accessed conditionally)&lt;br /&gt;(4) Edit XML – Element Addition (to queue end)&lt;br /&gt;(5) Edit XML – Element Addition (to queue start)&lt;br /&gt;(6) Edit XML – Element Addition (before a specific node)&lt;br /&gt;(7) Delete Elements (accessed serially)&lt;br /&gt;(8) Delete Elements (accessed conditionally)&lt;br /&gt;(9) Rearrange / Reorder Elements&lt;br /&gt;(10) Display Required data in XML Form itself OR Remove all children nodes save one OR Copy/Clone Node Eg OR Compare/Search non numeric data (like date or time) to get result.&lt;br /&gt;&lt;br /&gt;library.xml will be used in all operations.&lt;br /&gt;ps : I have added the indention &amp; spaces outside the tags in the below xml for a presentable xml form.&lt;br /&gt;Remove them before saving your xml file else most of the usual XML functions wont work in the desired manner.&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;&lt;?xml version="1.0"?&gt;&lt;br /&gt;02&lt;br /&gt;&lt;library&gt;&lt;br /&gt;03&lt;br /&gt;    &lt;book isbn="1001" pubdate="1943-01-01"&gt;&lt;br /&gt;04&lt;br /&gt;        &lt;title&gt;&lt;![CDATA[The Fountainhead]]&gt;&lt;/title&gt;&lt;br /&gt;05&lt;br /&gt;        &lt;author&gt;Ayn Rand&lt;/author&gt;&lt;br /&gt;06&lt;br /&gt;        &lt;price&gt;300&lt;/price&gt;&lt;br /&gt;07&lt;br /&gt;    &lt;/book&gt;&lt;br /&gt;08&lt;br /&gt;    &lt;book isbn="1002" pubdate="1954-01-01"&gt;&lt;br /&gt;09&lt;br /&gt;        &lt;title&gt;&lt;![CDATA[The Lord of the Rings]]&gt;&lt;/title&gt;&lt;br /&gt;10&lt;br /&gt;        &lt;author&gt;J.R.R.Tolkein&lt;/author&gt;&lt;br /&gt;11&lt;br /&gt;        &lt;price&gt;500&lt;/price&gt;&lt;br /&gt;12&lt;br /&gt;    &lt;/book&gt;&lt;br /&gt;13&lt;br /&gt;    &lt;book isbn="1003" pubdate="1982-01-01"&gt;&lt;br /&gt;14&lt;br /&gt;        &lt;title&gt;&lt;![CDATA[The Dark Tower]]&gt;&lt;/title&gt;&lt;br /&gt;15&lt;br /&gt;        &lt;author&gt;Stephen King&lt;/author&gt;&lt;br /&gt;16&lt;br /&gt;        &lt;price&gt;200&lt;/price&gt;&lt;br /&gt;17&lt;br /&gt;    &lt;/book&gt;&lt;br /&gt;18&lt;br /&gt;&lt;/library&gt;&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (1) Create XML OR&lt;br /&gt;Array to XML Conversion OR&lt;br /&gt;CDATA Element Eg&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Cant create CDATA element for title in SimpleXML.&lt;br /&gt;02&lt;br /&gt;function fnSimpleXMLCreate()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        $arr = array(array('isbn'=&gt;'1001', 'pubdate'=&gt;'1943-01-01', 'title'=&gt;'The Fountainhead',&lt;br /&gt;05&lt;br /&gt;                               'author'=&gt;'Ayn Rand', 'price'=&gt;'300'),&lt;br /&gt;06&lt;br /&gt;                         array('isbn'=&gt;'1002', 'pubdate'=&gt;'1954-01-01',&lt;br /&gt;07&lt;br /&gt;                               'title'=&gt;'The Lord of the Rings', 'author'=&gt;'J.R.R.Tolkein',&lt;br /&gt;08&lt;br /&gt;                               'price'=&gt;'500'),&lt;br /&gt;09&lt;br /&gt;                         array('isbn'=&gt;'1003', 'pubdate'=&gt;'1982-01-01', 'title'=&gt;'The Dark Tower',&lt;br /&gt;10&lt;br /&gt;                               'author'=&gt;'Stephen King', 'price'=&gt;'200'));&lt;br /&gt;11&lt;br /&gt; &lt;br /&gt;12&lt;br /&gt;        $library = new SimpleXMLElement('&lt;library /&gt;');&lt;br /&gt;13&lt;br /&gt; &lt;br /&gt;14&lt;br /&gt;        for($i=0;$i&lt;3;$i++)&lt;br /&gt;15&lt;br /&gt;        {&lt;br /&gt;16&lt;br /&gt;            $book = $library-&gt;addChild('book');&lt;br /&gt;17&lt;br /&gt;            $book-&gt;addAttribute('isbn', $arr[$i]['isbn']);&lt;br /&gt;18&lt;br /&gt;            $book-&gt;addAttribute('pubdate', $arr[$i]['pubdate']);&lt;br /&gt;19&lt;br /&gt;            $book-&gt;addChild('title', $arr[$i]['title']); //cant create CDATA in SimpleXML.&lt;br /&gt;20&lt;br /&gt;            $book-&gt;addChild('author', $arr[$i]['author']);&lt;br /&gt;21&lt;br /&gt;            $book-&gt;addChild('price', $arr[$i]['price']);&lt;br /&gt;22&lt;br /&gt;        }&lt;br /&gt;23&lt;br /&gt; &lt;br /&gt;24&lt;br /&gt;        $library-&gt;asXML('library.xml');&lt;br /&gt;25&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;function fnDomCreate()&lt;br /&gt;02&lt;br /&gt;    {&lt;br /&gt;03&lt;br /&gt;       $arr = array(array('isbn'=&gt;'1001', 'pubdate'=&gt;'1943-01-01', 'title'=&gt;'The Fountainhead',&lt;br /&gt;04&lt;br /&gt;                               'author'=&gt;'Ayn Rand', 'price'=&gt;'300'),&lt;br /&gt;05&lt;br /&gt;                         array('isbn'=&gt;'1002', 'pubdate'=&gt;'1954-01-01',&lt;br /&gt;06&lt;br /&gt;                               'title'=&gt;'The Lord of the Rings', 'author'=&gt;'J.R.R.Tolkein',&lt;br /&gt;07&lt;br /&gt;                               'price'=&gt;'500'),&lt;br /&gt;08&lt;br /&gt;                         array('isbn'=&gt;'1003', 'pubdate'=&gt;'1982-01-01', 'title'=&gt;'The Dark Tower',&lt;br /&gt;09&lt;br /&gt;                               'author'=&gt;'Stephen King', 'price'=&gt;'200'));&lt;br /&gt;10&lt;br /&gt; &lt;br /&gt;11&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;12&lt;br /&gt;        $library = $dom-&gt;createElement('library');&lt;br /&gt;13&lt;br /&gt;        $dom-&gt;appendChild($library);&lt;br /&gt;14&lt;br /&gt; &lt;br /&gt;15&lt;br /&gt;        for($i=0;$i&lt;3;$i++)&lt;br /&gt;16&lt;br /&gt;        {&lt;br /&gt;17&lt;br /&gt;            $book = $dom-&gt;createElement('book');&lt;br /&gt;18&lt;br /&gt;            $book-&gt;setAttribute('isbn',$arr[$i]['isbn']);&lt;br /&gt;19&lt;br /&gt;             $book-&gt;setAttribute('pubdate',$arr[$i]['pubdate']);&lt;br /&gt;20&lt;br /&gt; &lt;br /&gt;21&lt;br /&gt;            //$prop = $dom-&gt;createElement('title', $arr[$i]['title']);&lt;br /&gt;22&lt;br /&gt;            $prop = $dom-&gt;createElement('title');&lt;br /&gt;23&lt;br /&gt;            $text = $dom-&gt;createCDATASection($arr[$i]['title']);&lt;br /&gt;24&lt;br /&gt;            $prop-&gt;appendChild($text);&lt;br /&gt;25&lt;br /&gt;            $book-&gt;appendChild($prop);&lt;br /&gt;26&lt;br /&gt; &lt;br /&gt;27&lt;br /&gt;            $prop = $dom-&gt;createElement('author', $arr[$i]['author']);&lt;br /&gt;28&lt;br /&gt;            $book-&gt;appendChild($prop);&lt;br /&gt;29&lt;br /&gt;            $prop = $dom-&gt;createElement('price', $arr[$i]['price']);&lt;br /&gt;30&lt;br /&gt;            $book-&gt;appendChild($prop);&lt;br /&gt;31&lt;br /&gt;            $library-&gt;appendChild($book);&lt;br /&gt;32&lt;br /&gt;        }&lt;br /&gt;33&lt;br /&gt;        //header("Content-type: text/xml");&lt;br /&gt;34&lt;br /&gt;        $dom-&gt;save('library.xml');&lt;br /&gt;35&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (2) Edit XML – Edit/Modify Element Data (accessed serially)&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;// Edit Last Book Title&lt;br /&gt;2&lt;br /&gt;function fnSimpleXMLEditElementSeq()&lt;br /&gt;3&lt;br /&gt;    {&lt;br /&gt;4&lt;br /&gt;        $library = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;5&lt;br /&gt;        $num = count($library);&lt;br /&gt;6&lt;br /&gt;        $library-&gt;book[$num-1]-&gt;title .= ' - The Gunslinger';&lt;br /&gt;7&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;8&lt;br /&gt;        echo $library-&gt;asXML();&lt;br /&gt;9&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;//Edit Last Book Title&lt;br /&gt;02&lt;br /&gt;    function fnDOMEditElementSeq()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;05&lt;br /&gt;        $dom-&gt;load('library.xml');&lt;br /&gt;06&lt;br /&gt;        $library = $dom-&gt;documentElement;&lt;br /&gt;07&lt;br /&gt;        $cnt = $library-&gt;childNodes-&gt;length;&lt;br /&gt;08&lt;br /&gt; &lt;br /&gt;09&lt;br /&gt;        $library-&gt;childNodes-&gt;item($cnt-1)-&gt;getElementsByTagName('title')-&gt;item(0)-&gt;nodeValue .= ' Series';&lt;br /&gt;10&lt;br /&gt;       // 2nd way #$library-&gt;getElementsByTagName('book')-&gt;item($cnt-1)-&gt;getElementsByTagName('title')-&gt;item(0)-&gt;nodeValue .= ' Series';&lt;br /&gt;11&lt;br /&gt; &lt;br /&gt;12&lt;br /&gt;       //3rd Way&lt;br /&gt;13&lt;br /&gt;       //$library-&gt;childNodes-&gt;item($cnt-1)-&gt;childNodes-&gt;item(0)-&gt;nodeValue .= ' Series';&lt;br /&gt;14&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;15&lt;br /&gt;        echo $dom-&gt;saveXML();&lt;br /&gt;16&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (3) Edit XML – Edit specific Elements (accessed conditionally)&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;//Edit Title of book with author J.R.R.Tolkein&lt;br /&gt;2&lt;br /&gt;    function fnSimpleXMLEditElementCond()&lt;br /&gt;3&lt;br /&gt;    {&lt;br /&gt;4&lt;br /&gt;        $library = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;5&lt;br /&gt;        $book = $library-&gt;xpath('/library/book[author="J.R.R.Tolkein"]');&lt;br /&gt;6&lt;br /&gt;        $book[0]-&gt;title .= ' Series';&lt;br /&gt;7&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;8&lt;br /&gt;        echo $library-&gt;asXML();&lt;br /&gt;9&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM (with XPath):&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;//Edit Title of book with author J.R.R.Tolkein&lt;br /&gt;02&lt;br /&gt;   function fnDOMEditElementCond()&lt;br /&gt;03&lt;br /&gt;   {&lt;br /&gt;04&lt;br /&gt;       $dom = new DOMDocument();&lt;br /&gt;05&lt;br /&gt;       $dom-&gt;load('library.xml');&lt;br /&gt;06&lt;br /&gt;       $library = $dom-&gt;documentElement;&lt;br /&gt;07&lt;br /&gt;       $xpath = new DOMXPath($dom);&lt;br /&gt;08&lt;br /&gt;       $result = $xpath-&gt;query('/library/book[author="J.R.R.Tolkein"]/title');&lt;br /&gt;09&lt;br /&gt;       $result-&gt;item(0)-&gt;nodeValue .= ' Series';&lt;br /&gt;10&lt;br /&gt;       // This will remove the CDATA property of the element.&lt;br /&gt;11&lt;br /&gt;       //To retain it, delete this element (see delete eg) &amp; recreate it with CDATA (see create xml eg).&lt;br /&gt;12&lt;br /&gt; &lt;br /&gt;13&lt;br /&gt;       //2nd Way&lt;br /&gt;14&lt;br /&gt;       //$result = $xpath-&gt;query('/library/book[author="J.R.R.Tolkein"]');&lt;br /&gt;15&lt;br /&gt;      // $result-&gt;item(0)-&gt;getElementsByTagName('title')-&gt;item(0)-&gt;nodeValue .= ' Series';&lt;br /&gt;16&lt;br /&gt;       header("Content-type: text/xml");&lt;br /&gt;17&lt;br /&gt;       echo $dom-&gt;saveXML();&lt;br /&gt;18&lt;br /&gt; &lt;br /&gt;19&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (4) Edit XML – Element Addition (to queue end)&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;//Add another Book to the end&lt;br /&gt;02&lt;br /&gt;    function fnSimpleXMLAddElement2End()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        $library = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;05&lt;br /&gt;        $book = $library-&gt;addChild('book');&lt;br /&gt;06&lt;br /&gt;        $book-&gt;addAttribute('isbn', '1004');&lt;br /&gt;07&lt;br /&gt;        $book-&gt;addAttribute('pubdate', '1960-07-11');&lt;br /&gt;08&lt;br /&gt;        $book-&gt;addChild('title', "To Kill a Mockingbird");&lt;br /&gt;09&lt;br /&gt;        $book-&gt;addChild('author', "Harper Lee");&lt;br /&gt;10&lt;br /&gt;        $book-&gt;addChild('price', "100");&lt;br /&gt;11&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;12&lt;br /&gt;        echo $library-&gt;asXML();&lt;br /&gt;13&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;//Add another Book to the end&lt;br /&gt;02&lt;br /&gt;function fnDOMAddElement2End()&lt;br /&gt;03&lt;br /&gt;{&lt;br /&gt;04&lt;br /&gt;    $dom = new DOMDocument();&lt;br /&gt;05&lt;br /&gt;    $dom-&gt;load('library.xml');&lt;br /&gt;06&lt;br /&gt;    $library = $dom-&gt;documentElement;&lt;br /&gt;07&lt;br /&gt; &lt;br /&gt;08&lt;br /&gt;    $book = $dom-&gt;createElement('book');&lt;br /&gt;09&lt;br /&gt;    $book-&gt;setAttribute('isbn','1000');&lt;br /&gt;10&lt;br /&gt;    $book-&gt;setAttribute('pubdate','1960-07-11');&lt;br /&gt;11&lt;br /&gt; &lt;br /&gt;12&lt;br /&gt;    $prop = $dom-&gt;createElement('title');&lt;br /&gt;13&lt;br /&gt;    $text = $dom-&gt;createTextNode('To Kill a Mockingbird');&lt;br /&gt;14&lt;br /&gt;    $prop-&gt;appendChild($text);&lt;br /&gt;15&lt;br /&gt;    $book-&gt;appendChild($prop);&lt;br /&gt;16&lt;br /&gt; &lt;br /&gt;17&lt;br /&gt;     $prop = $dom-&gt;createElement('author','Harper Lee');&lt;br /&gt;18&lt;br /&gt;    $book-&gt;appendChild($prop);&lt;br /&gt;19&lt;br /&gt;    $prop = $dom-&gt;createElement('price','100');&lt;br /&gt;20&lt;br /&gt;    $book-&gt;appendChild($prop);&lt;br /&gt;21&lt;br /&gt; &lt;br /&gt;22&lt;br /&gt;    $library-&gt;appendChild($book);&lt;br /&gt;23&lt;br /&gt;    header("Content-type: text/xml");&lt;br /&gt;24&lt;br /&gt;    echo $dom-&gt;saveXML();&lt;br /&gt;25&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;//(5) Edit XML – Element Addition (to queue start)&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Add a Book to List Start&lt;br /&gt;02&lt;br /&gt;// Insert Before Functionality not present in SimpleXML&lt;br /&gt;03&lt;br /&gt;// We can integrate DOM with SimpleXML to do it.&lt;br /&gt;04&lt;br /&gt;    function fnSimpleXMLAddElement2Start()&lt;br /&gt;05&lt;br /&gt;    {&lt;br /&gt;06&lt;br /&gt;        $libSimple = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;07&lt;br /&gt;        $libDom = dom_import_simplexml($libSimple);&lt;br /&gt;08&lt;br /&gt; &lt;br /&gt;09&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;10&lt;br /&gt;        //returns a copy of the node to import&lt;br /&gt;11&lt;br /&gt;        $libDom = $dom-&gt;importNode($libDom, true);&lt;br /&gt;12&lt;br /&gt;        //associate it with the current document.&lt;br /&gt;13&lt;br /&gt;        $dom-&gt;appendChild($libDom);&lt;br /&gt;14&lt;br /&gt; &lt;br /&gt;15&lt;br /&gt;        fnDOMAddElement2Start($dom); //see below DOM function&lt;br /&gt;16&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;function fnDOMAddElement2Start($dom='')&lt;br /&gt;02&lt;br /&gt;    {&lt;br /&gt;03&lt;br /&gt;        if(!$dom)&lt;br /&gt;04&lt;br /&gt;        {&lt;br /&gt;05&lt;br /&gt;            $dom = new DOMDocument();&lt;br /&gt;06&lt;br /&gt;            $dom-&gt;load('library.xml');&lt;br /&gt;07&lt;br /&gt;        }&lt;br /&gt;08&lt;br /&gt;        $library = $dom-&gt;documentElement;&lt;br /&gt;09&lt;br /&gt;        #var_dump($library-&gt;childNodes-&gt;item(0)-&gt;parentNode-&gt;nodeName);&lt;br /&gt;10&lt;br /&gt;        $book = $dom-&gt;createElement('book');&lt;br /&gt;11&lt;br /&gt;        $book-&gt;setAttribute('isbn','1000');&lt;br /&gt;12&lt;br /&gt;        $book-&gt;setAttribute('pubdate','1960-07-11');&lt;br /&gt;13&lt;br /&gt; &lt;br /&gt;14&lt;br /&gt;        $prop = $dom-&gt;createElement('title','To Kill a Mockingbird');&lt;br /&gt;15&lt;br /&gt;        $book-&gt;appendChild($prop);&lt;br /&gt;16&lt;br /&gt;         $prop = $dom-&gt;createElement('author','Harper Lee');&lt;br /&gt;17&lt;br /&gt;        $book-&gt;appendChild($prop);&lt;br /&gt;18&lt;br /&gt;         $prop = $dom-&gt;createElement('price','100');&lt;br /&gt;19&lt;br /&gt;        $book-&gt;appendChild($prop);&lt;br /&gt;20&lt;br /&gt; &lt;br /&gt;21&lt;br /&gt;        $library-&gt;childNodes-&gt;item(0)-&gt;parentNode-&gt;insertBefore($book,$library-&gt;childNodes-&gt;item(0));&lt;br /&gt;22&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;23&lt;br /&gt;        echo $dom-&gt;saveXML();&lt;br /&gt;24&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (6) Edit XML – Element Addition (before a specific node)&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Add a Book Before attribute isbn 1002&lt;br /&gt;02&lt;br /&gt;    // Insert Before Functionality not present in SimpleXML&lt;br /&gt;03&lt;br /&gt;    // We can integrate DOM with SimpleXML to do it.&lt;br /&gt;04&lt;br /&gt;    function fnSimpleXMLAddElementCond()&lt;br /&gt;05&lt;br /&gt;    {&lt;br /&gt;06&lt;br /&gt;        $libSimple = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;07&lt;br /&gt;        $libDom = dom_import_simplexml($libSimple);&lt;br /&gt;08&lt;br /&gt; &lt;br /&gt;09&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;10&lt;br /&gt;        //returns a copy of the node to import&lt;br /&gt;11&lt;br /&gt;        $libDom = $dom-&gt;importNode($libDom, true);&lt;br /&gt;12&lt;br /&gt;        //associate it with the current document.&lt;br /&gt;13&lt;br /&gt;        $dom-&gt;appendChild($libDom);&lt;br /&gt;14&lt;br /&gt; &lt;br /&gt;15&lt;br /&gt;        fnDOMAddElementCond($dom); //see below DOM eg.&lt;br /&gt;16&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Add a Book Before isbn 1002&lt;br /&gt;02&lt;br /&gt;    function fnDOMAddElementCond($dom='')&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        if(!$dom)&lt;br /&gt;05&lt;br /&gt;        {&lt;br /&gt;06&lt;br /&gt;            $dom = new DOMDocument();&lt;br /&gt;07&lt;br /&gt;            $dom-&gt;load('library.xml');&lt;br /&gt;08&lt;br /&gt;        }&lt;br /&gt;09&lt;br /&gt;        $library = $dom-&gt;documentElement;&lt;br /&gt;10&lt;br /&gt; &lt;br /&gt;11&lt;br /&gt;        $book = $dom-&gt;createElement('book');&lt;br /&gt;12&lt;br /&gt;        $book-&gt;setAttribute('isbn','1000');&lt;br /&gt;13&lt;br /&gt;        $book-&gt;setAttribute('pubdate', '1960-07-11');&lt;br /&gt;14&lt;br /&gt; &lt;br /&gt;15&lt;br /&gt;        $prop = $dom-&gt;createElement('title','To Kill a Mockingbird');&lt;br /&gt;16&lt;br /&gt;        $book-&gt;appendChild($prop);&lt;br /&gt;17&lt;br /&gt;         $prop = $dom-&gt;createElement('author','Harper Lee');&lt;br /&gt;18&lt;br /&gt;        $book-&gt;appendChild($prop);&lt;br /&gt;19&lt;br /&gt;        $prop = $dom-&gt;createElement('price','100');&lt;br /&gt;20&lt;br /&gt;        $book-&gt;appendChild($prop);&lt;br /&gt;21&lt;br /&gt; &lt;br /&gt;22&lt;br /&gt;        $xpath = new DOMXPath($dom);&lt;br /&gt;23&lt;br /&gt;        $result = $xpath-&gt;query('/library/book[@isbn="1002"]');&lt;br /&gt;24&lt;br /&gt;        $library-&gt;childNodes-&gt;item(0)-&gt;parentNode-&gt;insertBefore($book,$result-&gt;item(0));&lt;br /&gt;25&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;26&lt;br /&gt;        echo $dom-&gt;saveXML();&lt;br /&gt;27&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (7) Delete Elements (accessed serially)&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Delete 2nd book&lt;br /&gt;02&lt;br /&gt;    function fnSimpleXMLDeleteSeq()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        $library = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;05&lt;br /&gt;        //$library-&gt;book[1] = null; // this only empties content&lt;br /&gt;06&lt;br /&gt;        unset($library-&gt;book[1]);&lt;br /&gt;07&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;08&lt;br /&gt;        echo $library-&gt;asXML();&lt;br /&gt;09&lt;br /&gt; &lt;br /&gt;10&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Delete 2nd Book&lt;br /&gt;02&lt;br /&gt;    function fnDOMDeleteSeq()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;05&lt;br /&gt;        $dom-&gt;load('library.xml');&lt;br /&gt;06&lt;br /&gt;        $library = $dom-&gt;documentElement;&lt;br /&gt;07&lt;br /&gt; &lt;br /&gt;08&lt;br /&gt;        $library-&gt;childNodes-&gt;item(0)-&gt;parentNode-&gt;removeChild($library-&gt;childNodes-&gt;item(1));&lt;br /&gt;09&lt;br /&gt; &lt;br /&gt;10&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;11&lt;br /&gt;        echo $dom-&gt;saveXML();&lt;br /&gt;12&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (8) Delete Elements (accessed conditionally)&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Delete a book with  200&lt;price&lt;500&lt;br /&gt;02&lt;br /&gt;    // Not possible to delete node found via XPath in SimpleXML. See below.&lt;br /&gt;03&lt;br /&gt;    function fnSimpleXMLDeleteCond()&lt;br /&gt;04&lt;br /&gt;    {&lt;br /&gt;05&lt;br /&gt;        $library = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;06&lt;br /&gt;        $book = $library-&gt;xpath('/library/book[price&gt;"200" and price&lt;"500"]');&lt;br /&gt;07&lt;br /&gt; &lt;br /&gt;08&lt;br /&gt;        //Problem here....not able to delete parent node using unset($book[0]);&lt;br /&gt;09&lt;br /&gt;        // unset of parent node only works when accessing serially. eg : unset($library-&gt;book[0]);&lt;br /&gt;10&lt;br /&gt; &lt;br /&gt;11&lt;br /&gt;        //header("Content-type: text/xml");&lt;br /&gt;12&lt;br /&gt;        //echo $library-&gt;asXML();&lt;br /&gt;13&lt;br /&gt; &lt;br /&gt;14&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Delete the book with  200&lt;price&lt;500&lt;br /&gt;02&lt;br /&gt;    function fnDOMDeleteCond()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;05&lt;br /&gt;        $dom-&gt;load('library.xml');&lt;br /&gt;06&lt;br /&gt;        $library = $dom-&gt;documentElement;&lt;br /&gt;07&lt;br /&gt;        $xpath = new DOMXPath($dom);&lt;br /&gt;08&lt;br /&gt;        $result = $xpath-&gt;query('/library/book[price&gt;"200" and price&lt;"500"]');&lt;br /&gt;09&lt;br /&gt;        $result-&gt;item(0)-&gt;parentNode-&gt;removeChild($result-&gt;item(0));&lt;br /&gt;10&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;11&lt;br /&gt;        echo $dom-&gt;saveXML();&lt;br /&gt;12&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (9) Rearrange / Reorder Elements&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Exchange Position of 2nd book with 3rd : fnSimpleXMLRearrange(2,3);&lt;br /&gt;02&lt;br /&gt;// Due to absence of an inbuilt function (DOM has it), we have to make our own function in SimpleXML.&lt;br /&gt;03&lt;br /&gt;//Better to use DOM.&lt;br /&gt;04&lt;br /&gt;    function fnSimpleXMLRearrange($num1,$num2)&lt;br /&gt;05&lt;br /&gt;    {&lt;br /&gt;06&lt;br /&gt;         $libSimple= new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;07&lt;br /&gt;         //$library-&gt;book[3] = $library-&gt;book[0]; // this doesnt work&lt;br /&gt;08&lt;br /&gt; &lt;br /&gt;09&lt;br /&gt;        $libDom = dom_import_simplexml($libSimple);&lt;br /&gt;10&lt;br /&gt; &lt;br /&gt;11&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;12&lt;br /&gt;        //returns a copy of the node to import&lt;br /&gt;13&lt;br /&gt;        $libDom = $dom-&gt;importNode($libDom, true);&lt;br /&gt;14&lt;br /&gt;        //associate it with the current document.&lt;br /&gt;15&lt;br /&gt;        $dom-&gt;appendChild($libDom);&lt;br /&gt;16&lt;br /&gt;        fnDOMRearrange($num1,$num2,$dom); // see below DOM function&lt;br /&gt;17&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Exchange Position of 2nd book with 3rd : fnDOMRearrange(2,3);&lt;br /&gt;02&lt;br /&gt;    function fnDOMRearrange($num1,$num2,$dom=0)&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        if(!$dom)&lt;br /&gt;05&lt;br /&gt;        {&lt;br /&gt;06&lt;br /&gt;            $dom = new DOMDocument();&lt;br /&gt;07&lt;br /&gt;            $dom-&gt;load('library.xml');&lt;br /&gt;08&lt;br /&gt;        }&lt;br /&gt;09&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;10&lt;br /&gt;        $dom-&gt;load('library.xml');&lt;br /&gt;11&lt;br /&gt;        $library = $dom-&gt;documentElement;&lt;br /&gt;12&lt;br /&gt; &lt;br /&gt;13&lt;br /&gt;        $book = $dom-&gt;createElement('book');&lt;br /&gt;14&lt;br /&gt;        $book-&gt;setAttribute('isbn','1000');&lt;br /&gt;15&lt;br /&gt;        $book-&gt;setAttribute('pubdate','1960-07-11');&lt;br /&gt;16&lt;br /&gt; &lt;br /&gt;17&lt;br /&gt;        $num1 = fnDOMConvIndex($num1);&lt;br /&gt;18&lt;br /&gt;        $num2 = fnDOMConvIndex($num2);&lt;br /&gt;19&lt;br /&gt;        $library-&gt;childNodes-&gt;item(0)-&gt;parentNode-&gt;insertBefore($library-&gt;childNodes-&gt;item($num2),$library-&gt;childNodes-&gt;item($num1));&lt;br /&gt;20&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;21&lt;br /&gt;        echo $dom-&gt;saveXML();&lt;br /&gt;22&lt;br /&gt;    }&lt;br /&gt;23&lt;br /&gt;    function fnDOMConvIndex($num)&lt;br /&gt;24&lt;br /&gt;    {&lt;br /&gt;25&lt;br /&gt;        $num = ($num==1)?$num:$num+1;//If its 1 then do nothing.&lt;br /&gt;26&lt;br /&gt;        $num = ($num%2)?$num:$num+1; //Always odd index due to nature of DOM Element Index.&lt;br /&gt;27&lt;br /&gt;        return $num;&lt;br /&gt;28&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#######################################&lt;br /&gt;// (10) Display Required data in XML Form itself OR&lt;br /&gt;Remove all children nodes save one OR&lt;br /&gt;Copy/Clone Node Eg OR&lt;br /&gt;Compare/Search non numeric data (like date or time) to get result.&lt;br /&gt;#######################################&lt;br /&gt;&lt;br /&gt;// (i) SimpleXML :&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;// Display Books published after 1980 in XML Form itself.&lt;br /&gt;2&lt;br /&gt;// No function to copy node directly in SimpleXML.&lt;br /&gt;3&lt;br /&gt;// Its simpler for this functionality to be implemented in DOM.&lt;br /&gt;4&lt;br /&gt;    function fnSimpleXMLDisplayElementCond()&lt;br /&gt;5&lt;br /&gt;    {&lt;br /&gt;6&lt;br /&gt;        $library = new SimpleXMLElement('library.xml',null,true);&lt;br /&gt;7&lt;br /&gt;        $book = $library-&gt;xpath('/library/book[translate(@pubdate,"-","")&gt;translate("1980-01-01","-","")]');&lt;br /&gt;8&lt;br /&gt;        // Manually create a new structure then add searched data to it (see create xml eg.)&lt;br /&gt;9&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;// (ii) DOM :&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;// Display Books published after 1980 in XML Form itself.&lt;br /&gt;02&lt;br /&gt;    function fnDOMDisplayElementCond()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        $dom = new DOMDocument();&lt;br /&gt;05&lt;br /&gt;        $dom-&gt;load('library.xml');&lt;br /&gt;06&lt;br /&gt;        $library = $dom-&gt;documentElement;&lt;br /&gt;07&lt;br /&gt;        $xpath = new DOMXPath($dom);&lt;br /&gt;08&lt;br /&gt; &lt;br /&gt;09&lt;br /&gt;        // Comparing non numeric standard data&lt;br /&gt;10&lt;br /&gt;        $result = $xpath-&gt;query('/library/book[translate(@pubdate,"-","")&gt;translate("1980-01-01","-","")]');&lt;br /&gt;11&lt;br /&gt;        // For simpler search paramater use this :&lt;br /&gt;12&lt;br /&gt;        //$result = $xpath-&gt;query('/library/book[author="J.R.R.Tolkein"]');&lt;br /&gt;13&lt;br /&gt; &lt;br /&gt;14&lt;br /&gt;        // Copy only node &amp; its attributes not its contents.&lt;br /&gt;15&lt;br /&gt;        $library = $library-&gt;cloneNode(false);&lt;br /&gt;16&lt;br /&gt;        // Add the 1 element which is search result.&lt;br /&gt;17&lt;br /&gt;        $library-&gt;appendChild($result-&gt;item(0));&lt;br /&gt;18&lt;br /&gt; &lt;br /&gt;19&lt;br /&gt;        header("Content-type: text/xml");&lt;br /&gt;20&lt;br /&gt;        echo $dom-&gt;saveXML($library);&lt;br /&gt;21&lt;br /&gt;    }&lt;br /&gt;Lessons Learn’t :&lt;br /&gt;SimpleXML is fantastic for those who will only briefly flirt with XML (or beginners) &amp; perform simple operations on XML.&lt;br /&gt;DOM is an absolute necessity for performing complex operations on XML data. Its learning curve is higher than SimpleXML off course but once you get the hang of it , you will know its very logical.&lt;br /&gt;Use XPath for conditional access to data. For serial access (like last book) XPath is not needed (but u can use it) since I can use normal DOM / SimpleXML node access.&lt;br /&gt;&lt;br /&gt;reference : http://quest4knowledge.wordpress.com/2010/09/04/php-xml-create-add-edit-modify-using-dom-simplexml-xpath/&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-3770681146141268626?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/3770681146141268626/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=3770681146141268626' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3770681146141268626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3770681146141268626'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/php-xml-create-add-edit-modify-using.html' title='PHP : XML Create-Add-Edit-Modify using DOM , SimpleXML , XPath'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-232099017422070705</id><published>2011-10-27T09:21:00.000-07:00</published><updated>2011-10-27T09:22:42.382-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>PHP中的生成XML文件的4种方法</title><content type='html'>Xml代码&lt;br /&gt; &lt;br /&gt;&lt;?xml version="1.0" encoding="utf-8"?&gt;  &lt;br /&gt;&lt;article&gt;  &lt;br /&gt;    &lt;item&gt;  &lt;br /&gt;        &lt;title size="1"&gt;title1&lt;/title&gt;  &lt;br /&gt;        &lt;content&gt;content1&lt;/content&gt;  &lt;br /&gt;        &lt;pubdate&gt;2009-10-11&lt;/pubdate&gt;  &lt;br /&gt;    &lt;/item&gt;  &lt;br /&gt;    &lt;item&gt;  &lt;br /&gt;        &lt;title size="1"&gt;title2&lt;/title&gt;  &lt;br /&gt;        &lt;content&gt;content2&lt;/content&gt;  &lt;br /&gt;        &lt;pubdate&gt;2009-11-11&lt;/pubdate&gt;  &lt;br /&gt;    &lt;/item&gt;  &lt;br /&gt;&lt;/article&gt;   &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;【直接生成字符串】 &lt;br /&gt;方法1：使用纯粹的PHP代码生成字符串，并把这个字符串写入一个以XML为后缀的文件。这是最原始的生成XML的方法，不过有效！ &lt;br /&gt;PHP代码如下： &lt;br /&gt;Php代码&lt;br /&gt; &lt;br /&gt;&lt;?PHP  &lt;br /&gt;$data_array = array(  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title1',  &lt;br /&gt;    'content' =&gt; 'content1',  &lt;br /&gt;        'pubdate' =&gt; '2009-10-11',  &lt;br /&gt;    ),  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title2',  &lt;br /&gt;    'content' =&gt; 'content2',  &lt;br /&gt;    'pubdate' =&gt; '2009-11-11',  &lt;br /&gt;    )  &lt;br /&gt;);  &lt;br /&gt;$title_size = 1;  &lt;br /&gt;  &lt;br /&gt;$xml = "&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n";  &lt;br /&gt;$xml .= "&lt;article&gt;\n";  &lt;br /&gt;  &lt;br /&gt;foreach ($data_array as $data) {  &lt;br /&gt;$xml .= create_item($data['title'], $title_size, $data['content'], $data['pubdate']);  &lt;br /&gt;}  &lt;br /&gt;  &lt;br /&gt;$xml .= "&lt;/article&gt;\n";  &lt;br /&gt;  &lt;br /&gt;echo $xml;  &lt;br /&gt;  &lt;br /&gt;//  创建XML单项  &lt;br /&gt;function create_item($title_data, $title_size, $content_data, $pubdate_data)  &lt;br /&gt;{  &lt;br /&gt;    $item = "&lt;item&gt;\n";  &lt;br /&gt;    $item .= "&lt;title size=\"" . $title_size . "\"&gt;" . $title_data . "&lt;/title&gt;\n";  &lt;br /&gt;    $item .= "&lt;content&gt;" . $content_data . "&lt;/content&gt;\n";  &lt;br /&gt;    $item .= " &lt;pubdate&gt;" . $pubdate_data . "&lt;/pubdate&gt;\n";  &lt;br /&gt;    $item .= "&lt;/item&gt;\n";  &lt;br /&gt;  &lt;br /&gt;    return $item;  &lt;br /&gt;}  &lt;br /&gt;  &lt;br /&gt;?&gt;   &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;【DomDocument】 &lt;br /&gt;方法2：使用DomDocument生成XML文件 &lt;br /&gt;创建节点使用createElement方法， &lt;br /&gt;创建文本内容使用createTextNode方法， &lt;br /&gt;添加子节点使用appendChild方法， &lt;br /&gt;创建属性使用createAttribute方法 &lt;br /&gt;PHP代码如下： &lt;br /&gt;Php代码&lt;br /&gt; &lt;br /&gt;&lt;?PHP  &lt;br /&gt;$data_array = array(  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title1',  &lt;br /&gt;    'content' =&gt; 'content1',  &lt;br /&gt;        'pubdate' =&gt; '2009-10-11',  &lt;br /&gt;    ),  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title2',  &lt;br /&gt;    'content' =&gt; 'content2',  &lt;br /&gt;    'pubdate' =&gt; '2009-11-11',  &lt;br /&gt;    )  &lt;br /&gt;);  &lt;br /&gt;  &lt;br /&gt;//  属性数组  &lt;br /&gt;$attribute_array = array(  &lt;br /&gt;    'title' =&gt; array(  &lt;br /&gt;    'size' =&gt; 1  &lt;br /&gt;    )  &lt;br /&gt;);  &lt;br /&gt;  &lt;br /&gt;//  创建一个XML文档并设置XML版本和编码。。  &lt;br /&gt;$dom=new DomDocument('1.0', 'utf-8');  &lt;br /&gt;  &lt;br /&gt;//  创建根节点  &lt;br /&gt;$article = $dom-&gt;createElement('article');  &lt;br /&gt;$dom-&gt;appendchild($article);  &lt;br /&gt;  &lt;br /&gt;foreach ($data_array as $data) {  &lt;br /&gt;    $item = $dom-&gt;createElement('item');  &lt;br /&gt;    $article-&gt;appendchild($item);  &lt;br /&gt;  &lt;br /&gt;    create_item($dom, $item, $data, $attribute_array);  &lt;br /&gt;}  &lt;br /&gt;  &lt;br /&gt;echo $dom-&gt;saveXML();  &lt;br /&gt;  &lt;br /&gt;function create_item($dom, $item, $data, $attribute) {  &lt;br /&gt;    if (is_array($data)) {  &lt;br /&gt;        foreach ($data as $key =&gt; $val) {  &lt;br /&gt;            //  创建元素  &lt;br /&gt;            $$key = $dom-&gt;createElement($key);  &lt;br /&gt;            $item-&gt;appendchild($$key);  &lt;br /&gt;  &lt;br /&gt;            //  创建元素值  &lt;br /&gt;            $text = $dom-&gt;createTextNode($val);  &lt;br /&gt;            $$key-&gt;appendchild($text);  &lt;br /&gt;  &lt;br /&gt;            if (isset($attribute[$key])) {  &lt;br /&gt;            //  如果此字段存在相关属性需要设置  &lt;br /&gt;                foreach ($attribute[$key] as $akey =&gt; $row) {  &lt;br /&gt;                    //  创建属性节点  &lt;br /&gt;                    $$akey = $dom-&gt;createAttribute($akey);  &lt;br /&gt;                    $$key-&gt;appendchild($$akey);  &lt;br /&gt;  &lt;br /&gt;                    // 创建属性值节点  &lt;br /&gt;                    $aval = $dom-&gt;createTextNode($row);  &lt;br /&gt;                    $$akey-&gt;appendChild($aval);  &lt;br /&gt;                }  &lt;br /&gt;            }   //  end if  &lt;br /&gt;        }  &lt;br /&gt;    }   //  end if  &lt;br /&gt;}   //  end function  &lt;br /&gt;?&gt;   &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;【XMLWriter】 &lt;br /&gt;方法3：使用XMLWriter类创建XML文件 &lt;br /&gt;此方法在PHP 5.1.2后有效 &lt;br /&gt;另外，它可以输出多种编码的XML，但是输入只能是utf-8 &lt;br /&gt;PHP代码如下： &lt;br /&gt;Php代码&lt;br /&gt; &lt;br /&gt;&lt;?PHP  &lt;br /&gt;$data_array = array(  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title1',  &lt;br /&gt;    'content' =&gt; 'content1',  &lt;br /&gt;        'pubdate' =&gt; '2009-10-11',  &lt;br /&gt;    ),  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title2',  &lt;br /&gt;    'content' =&gt; 'content2',  &lt;br /&gt;    'pubdate' =&gt; '2009-11-11',  &lt;br /&gt;    )  &lt;br /&gt;);  &lt;br /&gt;  &lt;br /&gt;//  属性数组  &lt;br /&gt;$attribute_array = array(  &lt;br /&gt;    'title' =&gt; array(  &lt;br /&gt;    'size' =&gt; 1  &lt;br /&gt;    )  &lt;br /&gt;);  &lt;br /&gt;  &lt;br /&gt;$xml = new XMLWriter();  &lt;br /&gt;$xml-&gt;openUri("php://output");  &lt;br /&gt;//  输出方式，也可以设置为某个xml文件地址，直接输出成文件  &lt;br /&gt;$xml-&gt;setIndentString('  ');  &lt;br /&gt;$xml-&gt;setIndent(true);  &lt;br /&gt;  &lt;br /&gt;$xml-&gt;startDocument('1.0', 'utf-8');  &lt;br /&gt;//  开始创建文件  &lt;br /&gt;//  根结点  &lt;br /&gt;$xml-&gt;startElement('article');  &lt;br /&gt;  &lt;br /&gt;foreach ($data_array as $data) {  &lt;br /&gt;    $xml-&gt;startElement('item');  &lt;br /&gt;  &lt;br /&gt;    if (is_array($data)) {  &lt;br /&gt;        foreach ($data as $key =&gt; $row) {  &lt;br /&gt;          $xml-&gt;startElement($key);  &lt;br /&gt;  &lt;br /&gt;          if (isset($attribute_array[$key]) &amp;&amp; is_array($attribute_array[$key]))  &lt;br /&gt;          {  &lt;br /&gt;              foreach ($attribute_array[$key] as $akey =&gt; $aval) {  &lt;br /&gt;              //  设置属性值  &lt;br /&gt;                    $xml-&gt;writeAttribute($akey, $aval);  &lt;br /&gt;                }  &lt;br /&gt;  &lt;br /&gt;            }  &lt;br /&gt;  &lt;br /&gt;            $xml-&gt;text($row);   //  设置内容  &lt;br /&gt;            $xml-&gt;endElement(); // $key  &lt;br /&gt;        }  &lt;br /&gt;  &lt;br /&gt;    }  &lt;br /&gt;    $xml-&gt;endElement(); //  item  &lt;br /&gt;}  &lt;br /&gt;  &lt;br /&gt;$xml-&gt;endElement(); //  article  &lt;br /&gt;$xml-&gt;endDocument();  &lt;br /&gt;  &lt;br /&gt;$xml-&gt;flush();  &lt;br /&gt;?&gt;  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;【SimpleXML】 &lt;br /&gt;方法4：使用SimpleXML创建XML文档 &lt;br /&gt;Php代码&lt;br /&gt; &lt;br /&gt;   &lt;br /&gt;&lt;?PHP  &lt;br /&gt;$data_array = array(  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title1',  &lt;br /&gt;    'content' =&gt; 'content1',  &lt;br /&gt;        'pubdate' =&gt; '2009-10-11',  &lt;br /&gt;    ),  &lt;br /&gt;    array(  &lt;br /&gt;    'title' =&gt; 'title2',  &lt;br /&gt;    'content' =&gt; 'content2',  &lt;br /&gt;    'pubdate' =&gt; '2009-11-11',  &lt;br /&gt;    )  &lt;br /&gt;);  &lt;br /&gt;  &lt;br /&gt;//  属性数组  &lt;br /&gt;$attribute_array = array(  &lt;br /&gt;    'title' =&gt; array(  &lt;br /&gt;    'size' =&gt; 1  &lt;br /&gt;    )  &lt;br /&gt;);  &lt;br /&gt;  &lt;br /&gt;$string = &lt;&lt;&lt;XML  &lt;br /&gt;&lt;?xml version='1.0' encoding='utf-8'?&gt;  &lt;br /&gt;&lt;article&gt;  &lt;br /&gt;&lt;/article&gt;  &lt;br /&gt;XML;  &lt;br /&gt;  &lt;br /&gt;$xml = simplexml_load_string($string);  &lt;br /&gt;  &lt;br /&gt;foreach ($data_array as $data) {  &lt;br /&gt;    $item = $xml-&gt;addChild('item');  &lt;br /&gt;    if (is_array($data)) {  &lt;br /&gt;        foreach ($data as $key =&gt; $row) {  &lt;br /&gt;          $node = $item-&gt;addChild($key, $row);  &lt;br /&gt;  &lt;br /&gt;          if (isset($attribute_array[$key]) &amp;&amp; is_array($attribute_array[$key]))  &lt;br /&gt;            {  &lt;br /&gt;              foreach ($attribute_array[$key] as $akey =&gt; $aval) {  &lt;br /&gt;             //  设置属性值  &lt;br /&gt;                  $node-&gt;addAttribute($akey, $aval);  &lt;br /&gt;            }  &lt;br /&gt;          }  &lt;br /&gt;        }  &lt;br /&gt;    }  &lt;br /&gt;}  &lt;br /&gt;echo $xml-&gt;asXML();  &lt;br /&gt;?&gt;   &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;reference : http://haroldphp.iteye.com/blog/1090240&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-232099017422070705?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/232099017422070705/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=232099017422070705' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/232099017422070705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/232099017422070705'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/phpxml4.html' title='PHP中的生成XML文件的4种方法'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7000703491566890506</id><published>2011-10-27T09:12:00.000-07:00</published><updated>2011-10-27T09:13:12.351-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='工作方法'/><title type='text'>【教學】縮放式的簡報工具--Prezi</title><content type='html'>Prezi是今年初竄起的簡報工具，由 匈牙利人所開發的，其炫麗的簡報畫面，讓人眼睛為之一亮！&lt;br /&gt;&lt;br /&gt;數週前，由 小弟的啟蒙師父、雙白金資訊教師--鄭文榤老師 推薦 Prezi，&lt;br /&gt;&lt;br /&gt;自己也玩過後，那自由奔放、有條理且人性化的操作介面，覺得超優的，&lt;br /&gt;&lt;br /&gt;但因是英文介面，加上介面的使用方法很創新，故寫下這篇教學文，做個紀錄！&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;其類似 心智圖  般的從中間擴散出去，先觀看全局，再分述細部，打破傳統簡報一頁一頁的單向延伸，更符合人類思考的本質；&lt;br /&gt;&lt;br /&gt;尤其是 Zooming 的 縮放功能 很實用，動一下滑鼠滾輪，即可讓讀者 Focus 目前的重點；&lt;br /&gt;&lt;br /&gt;簡報路徑的呈現功能也是一絕，很像 攝影鏡頭 般，把目光從這一個物件忽然拉過去到另一個物件，時時牽引讀者的心；&lt;br /&gt;&lt;br /&gt;個人覺得Prezi兼具 較 PowerPoint 製作更容易的優點，以及有 心智圖 擴散性思考的優點，其把呆版的簡報，開創了新的局面，原來上台簡報可以這麼有趣，這麼有條理～ &lt;br /&gt;&lt;br /&gt; ※操作簡介：&lt;br /&gt;&lt;br /&gt;操作介面平易近人，就像在一面牆上，貼滿 便利貼 似的，&lt;br /&gt;&lt;br /&gt;藉由十幾個按鈕，把文字、圖片、影片、框框、線條等物件放入，&lt;br /&gt;&lt;br /&gt;每個物件就像一個 便利貼，皆可直覺式的搬移、縮放、旋轉；&lt;br /&gt;&lt;br /&gt;放置定位後，再規劃簡報時的呈現路徑，以便簡報時，Zooming、Focus某一個物件；&lt;br /&gt;&lt;br /&gt;若是第一次使用可能會不甚習慣，但因操作介面簡潔，用個一兩次就順手了； &lt;br /&gt;&lt;br /&gt;加入會員後，可免費在線上編輯簡報，容量限 100MB，&lt;br /&gt;&lt;br /&gt;若付費後，可下載單機版來編輯簡報，也支援多人編輯簡報。&lt;br /&gt;&lt;br /&gt;Prezi 有提供教育版，容量有 500 MB，可以不公開自己的作品，並移除 Prezi 的Logo，&lt;br /&gt;&lt;br /&gt;申請教育版的步驟：&lt;br /&gt;&lt;br /&gt;1. 必須是含有 .edu 的 e-Mail，&lt;br /&gt;&lt;br /&gt;2. 選左邊的[Upgrade]選項，&lt;br /&gt;&lt;br /&gt;3. 接著點選左邊的，填一些資料，即可升級為教育版囉！&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;網站名稱：Prezi&lt;br /&gt;&lt;br /&gt;連結網址：http://prezi.com/&lt;br /&gt;&lt;br /&gt;官網教學：Learn&lt;br /&gt;&lt;br /&gt;互動式教學：四分鐘學會Prezi(英文)、使用Prezi的小技巧(英文)&lt;br /&gt;&lt;br /&gt;中文教學：Prezi中文教學檔&lt;br /&gt;&lt;br /&gt;官網作品：Showcase&lt;br /&gt;&lt;br /&gt;本人拙作：維謙老師的自我介紹(Prezi製)、海報設計原則(Prezi製)&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;※中文教學&lt;br /&gt;&lt;br /&gt;若覺得線上觀看略有模糊，請下載高清版的教學 prezi-full.swf&lt;br /&gt;&lt;br /&gt;※幾項使用的小技巧（感謝　虎尾國中 生生不息的林老師 提供）&lt;br /&gt;&lt;br /&gt;（1）路徑 設在區塊上，FOCUS效果較好。&lt;br /&gt;&lt;br /&gt;（2）小線條無法選取時，滑鼠左鍵要按久一點，再放開才可選取。&lt;br /&gt;&lt;br /&gt;（3）免費版做出的簡報必為publish版，公開發表出來，付費版則可選 隱藏發表。&lt;br /&gt;&lt;br /&gt;（4）登入後方下載自己的簡報可成為 單機播放 使用。&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;※Prezi 目前的缺點(其製作團隊企圖心很強，這些問題應該有望解決)：&lt;br /&gt;&lt;br /&gt;1. 中文支援的問題&lt;br /&gt;&lt;br /&gt;    1-1 雖支援中文輸入，但有的文字還是闕漏。&lt;br /&gt;&lt;br /&gt;    1-2 中文模板只有一種。&lt;br /&gt;&lt;br /&gt;    1-3 只有英文操作介面。&lt;br /&gt;&lt;br /&gt;    1-4 不支援中文檔名的上傳檔案。&lt;br /&gt;&lt;br /&gt;2. 影片、圖片支援的問題&lt;br /&gt;&lt;br /&gt;    2-1 只支援 .FLV 格式，上傳 .SWF 格式亦可，但播放偶而有問題。(上傳 .WMV 格式有時成功有時失敗)。&lt;br /&gt;&lt;br /&gt;    2-2 下載的播放檔，在播放影片時，其下方的影片控制按鈕消失了。&lt;br /&gt;&lt;br /&gt;    2-3 圖片 .GIF 格式上傳後，不支援動畫功能，形成單純的圖片。&lt;br /&gt;&lt;br /&gt;3. 文字編輯的問題&lt;br /&gt;&lt;br /&gt;    3-1 文字顏色太少，只有三種。&lt;br /&gt;&lt;br /&gt;    3-2 無法選擇字體。&lt;br /&gt;&lt;br /&gt;4. 物件的問題&lt;br /&gt;&lt;br /&gt;    4-1 缺少編輯圖層的功能。&lt;br /&gt;&lt;br /&gt;    4-2 缺少物件變形的功能。&lt;br /&gt;&lt;br /&gt;    4-3 編輯物件路徑錯誤時。&lt;br /&gt;&lt;br /&gt;    4-4 缺少自訂框架的功能。&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;延伸閱讀：&lt;br /&gt;&lt;br /&gt;ZuiPrezi～強力建議Google必買的超炫線上簡報器&lt;br /&gt;&lt;br /&gt;Prezi超炫的線上簡報&lt;br /&gt;&lt;br /&gt;reference : http://163.32.219.6/blog/u882061/e-originality/2011/06/10/1294&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-7000703491566890506?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/7000703491566890506/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=7000703491566890506' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7000703491566890506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7000703491566890506'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/prezi.html' title='【教學】縮放式的簡報工具--Prezi'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-953419530255815680</id><published>2011-10-27T04:14:00.001-07:00</published><updated>2011-10-27T04:14:52.558-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>【程式】PHP : Curl with Session</title><content type='html'>今天同事問了一個問題&lt;br /&gt;A.php 用 Curl 去抓 B.php 。&lt;br /&gt;然後 B.php 寫在 Session 的東西，&lt;br /&gt;為什麼 A.php 抓不到 (當然 B.php 也抓不到 A.php 的值)，&lt;br /&gt;這是因為 curl 其實產生了另一個 session，&lt;br /&gt;所以就算存了也抓不到。&lt;br /&gt;當然也有解決的方法，就是使用 session_id()&lt;br /&gt;程式如下：&lt;br /&gt;A.php&lt;br /&gt;?&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;4&lt;br /&gt;5&lt;br /&gt;6&lt;br /&gt;7&lt;br /&gt;8&lt;br /&gt;9&lt;br /&gt;10&lt;br /&gt;11&lt;br /&gt;12&lt;br /&gt;13&lt;br /&gt;14&lt;br /&gt;15&lt;br /&gt;16&lt;br /&gt;17&lt;br /&gt;18&lt;br /&gt;19&lt;br /&gt;20&lt;br /&gt;21&lt;br /&gt;22&lt;br /&gt;23&lt;br /&gt;function doHttpRequest($url, $args='') {&lt;br /&gt;    $ch = curl_init();&lt;br /&gt;    //Post Data&lt;br /&gt;    curl_setopt($ch, CURLOPT_POST, 1);&lt;br /&gt;    curl_setopt($ch, CURLOPT_POSTFIELDS, 'sid='.session_id());&lt;br /&gt;                 &lt;br /&gt;    curl_setopt($ch, CURLOPT_URL, $url);&lt;br /&gt;    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);&lt;br /&gt;    $result = curl_exec($ch);&lt;br /&gt;    curl_close($ch);&lt;br /&gt;    return $result;&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;session_start();&lt;br /&gt;$_SESSION['aa']='[A.php]';&lt;br /&gt;session_write_close();&lt;br /&gt; &lt;br /&gt;$url = 'http://cw.dev.com/B.php';&lt;br /&gt;echo doHttpRequest($url);&lt;br /&gt; &lt;br /&gt;session_id(session_id());&lt;br /&gt;session_start();&lt;br /&gt;var_dump($_SESSION);&lt;br /&gt;B.php&lt;br /&gt;?&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;4&lt;br /&gt;session_id($_POST['sid']);&lt;br /&gt;session_start();&lt;br /&gt;$_SESSION['bb']='[B.php]';&lt;br /&gt;var_dump($_SESSION);&lt;br /&gt;執行結果：&lt;br /&gt;array  'aa' =&gt; string '[A.php]' (length=7)  'bb' =&gt; string '[B.php]' (length=7)&lt;br /&gt;array  'aa' =&gt; string '[A.php]' (length=7)  'bb' =&gt; string '[B.php]' (length=7)&lt;br /&gt; &lt;br /&gt;以上，記錄一下…&lt;br /&gt;&lt;br /&gt;reference : http://ching119.pixnet.net/blog/post/59676045-%E3%80%90%E7%A8%8B%E5%BC%8F%E3%80%91php-%3A-curl-with-session&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-953419530255815680?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/953419530255815680/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=953419530255815680' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/953419530255815680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/953419530255815680'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/php-curl-with-session.html' title='【程式】PHP : Curl with Session'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-386189636831430372</id><published>2011-10-27T01:07:00.000-07:00</published><updated>2011-10-27T01:08:20.112-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Angile'/><title type='text'>Agilefant 軟體</title><content type='html'>在跑 Agile 的人可以試試看&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-386189636831430372?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/386189636831430372/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=386189636831430372' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/386189636831430372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/386189636831430372'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/agilefant.html' title='Agilefant 軟體'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1687022161830746586</id><published>2011-10-26T00:36:00.000-07:00</published><updated>2011-10-26T00:37:19.723-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>Introducing Phake Mocking Framework</title><content type='html'>I have used PHPUnit heavily now for the last 4 years. As anyone that is heavily involved in writing Unit Tests knows, test doubles (commonly referred to as mock objects) are a necessary part of your toolbox. The mocking options that we used to have for PHP unit testing have traditionally been fairly limited and most all of them in some form or another were ports of JMock. The way PHP operates as well as some decisions made to more closely emulate how JMock does things lead to functionality in the existing mock library for PHPUnit that for some are a hassle. This ranges from PHPUnit implicitly calling the “mockee’s” constructor (you have to explicitly specify that you do not want to call the constructor) to the pain of trying to stub or verify multiple invocations of the same method with different parameters.&lt;br /&gt;&lt;br /&gt;Over the last three years, my experience as well as the musing of some of my colleagues has led me to believe that a lot of what I don’t like about mocking in php is the result of the fundamental notions of combining stubbing with verification and setting expectations ahead of method calls instead of verifying that what you expected to happen has indeed happened. This was essentially proven to me over the last year and a half as I have been heavily working with Java code and as a result have been using the Mockito mocking library for Java. The result of this work is the Phake Mocking Framework.&lt;br /&gt;&lt;br /&gt;Now I am fairly certain that at least 5 or 6 people (which may constitute everyone who reads this) are rolling their eyes by now. So instead of going further into why I like this style of mocking I’ll just show you how to use it. Phake was designed with PHPUnit in mind, however I don’t really see any reason why it couldn’t be used in other testing frameworks as well. It is on my roadmap to confirm support for other frameworks. In any case, I will be using PHPUnit for my examples.&lt;br /&gt;&lt;br /&gt;This document assumes you already have a good understanding of the basics of mocking. If the terms ‘Mocking’, ‘Stubbing’, and ‘Test Doubles’ mean nothing to you, I would recommend checking out the following links:&lt;br /&gt;&lt;br /&gt;PHPUnit on Mock Objects&lt;br /&gt;Wikipedia on Mock Objects&lt;br /&gt;The Universe on Mock Objects&lt;br /&gt;Getting Started&lt;br /&gt;&lt;br /&gt;You can get a copy of Phake from Github. If you are familiar and comfortable with with git you can just clone my repository. If you would rather avoid the trappings of Github, you can also download Phake’s latest release tarball. Either way will result in a directory with two subdirectories: src and test. You will want to move the contents of the src directory to somewhere in your include path (such as /usr/share/php). Once you have done this, you can simply include “Phake.php” in any of your tests or in your bootstrap script and you will be off to the races.&lt;br /&gt;&lt;br /&gt;UPDATE!!!&lt;br /&gt;&lt;br /&gt;I just set up a pear channel to distribute phake as well. So if you would like to install Phake using pear, just do the following:&lt;br /&gt;&lt;br /&gt;pear channel-discover pear.digitalsandwich.com&lt;br /&gt;pear install channel://pear.digitalsandwich.com/Phake-1.0.0alpha&lt;br /&gt;To show you some of the basics of how to use this framework, I am going to write various bits of codes to mock, verify, stub, etc the following class:&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;&lt;br /&gt;class MyClass&lt;br /&gt;{&lt;br /&gt;    private $value;&lt;br /&gt;&lt;br /&gt;    public function __construct($value)&lt;br /&gt;    {&lt;br /&gt;        $this-&gt;value = $value;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public function getValue()&lt;br /&gt;    {&lt;br /&gt;        return $this-&gt;value;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public function subtract($int)&lt;br /&gt;    {&lt;br /&gt;        return $this-&gt;value - $int;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;?&gt;&lt;br /&gt;Stubbing&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;&lt;br /&gt;require_once 'Phake.php';&lt;br /&gt;class Test extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    public function testStubbingGetValue()&lt;br /&gt;    {&lt;br /&gt;        // Sets up the mock object.&lt;br /&gt;        // Analogous to $this-&gt;getMock() in PHPUnit&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        // Builds the stub for getValue. &lt;br /&gt;        // Essentially any call to getValue will return 42&lt;br /&gt;        Phake::when($mock)-&gt;getValue()-&gt;thenReturn(42);&lt;br /&gt;&lt;br /&gt;        $this-&gt;assertEquals(42, $mock-&gt;getValue());&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;You can also do conditional stubbing based on passed in parameters.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testStubbingGetValue2()&lt;br /&gt;    {&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        // You can pass parameters into the stubbed method to indicate you&lt;br /&gt;        // only want to stub matching invocations. By default, anything &lt;br /&gt;        // passed in must be loosely matched ('==')&lt;br /&gt;        Phake::when($mock)-&gt;subtract(42)-&gt;thenReturn(30);&lt;br /&gt;&lt;br /&gt;        $this-&gt;assertEquals(30, $mock-&gt;subtract(42));&lt;br /&gt;&lt;br /&gt;        // It is important to note that any unstubbed calls will return null.&lt;br /&gt;        // Since 41 != 42 this call will return null.&lt;br /&gt;        $this-&gt;assertNull($mock-&gt;subtract(41));&lt;br /&gt;    }&lt;br /&gt;If a specific invocation or call of a mock object has not been stubbed, it will return null. This behavior is different than the default behavior of PHPUnit’s mocking framework. If you need the default PHPUnit behavior then you could use something called partial mocks. Partial mocks are setup to call the constructor of the class being mocked and for any call that has not been stubbed, the parent method will be called.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testStubbingGetValue3()&lt;br /&gt;    {&lt;br /&gt;        // Creates a mock object whose constructor will call &lt;br /&gt;        // MyClass::__construct(42);&lt;br /&gt;        $mock = Phake::partMock('MyClass', 42);&lt;br /&gt;&lt;br /&gt;        Phake::when($mock)-&gt;subtract(42)-&gt;thenReturn(0);&lt;br /&gt;&lt;br /&gt;        // Since 18 != 42, the real method gets call &lt;br /&gt;        $this-&gt;assertEquals(24, $mock-&gt;subtract(18));&lt;br /&gt;    }&lt;br /&gt;You can also specify on a per call basis that you want to call the parent, using the thenCallParent() method instead of thenReturn(). The different values you can use for stubbing are referred to as ‘Answers’. Here are a list of them and what they will do when a matching invocation is called:&lt;br /&gt;&lt;br /&gt;thenReturn(mixed $var) – Will return the exact value passed in.&lt;br /&gt;thenCallParent() – Will return the results of calling the mocked parent method.&lt;br /&gt;thenThrow(Exception $e) – Will throw $e.&lt;br /&gt;captureReturnTo(&amp;$variable) – Acts exactly like thenCallParent() however it also captures the value that the parent returned to $variable. This allows you to run assertions. This comes in very handy for testing legacy code with protected or private factory methods whose return values are never returned out of the tested method’s scope.&lt;br /&gt;The other thing to take note of stubbing is that any PHPUnit constraints are supported.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testStubbingGetValue4()&lt;br /&gt;    {&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        //Matches any call to subtract() where the passed in value equals 42&lt;br /&gt;        Phake::when($mock)-&gt;subtract(42)-&gt;thenReturn(30);&lt;br /&gt;        &lt;br /&gt;        //Matches any call to subtract() where the passed in value is less &lt;br /&gt;        // than 42. Notice that this is a phpunit constraint&lt;br /&gt;        Phake::when($mock)-&gt;subtract($this-&gt;lessThan(42))-&gt;thenReturn(29);&lt;br /&gt;&lt;br /&gt;        $this-&gt;assertEquals(30, $mock-&gt;subtract(42));&lt;br /&gt;        $this-&gt;assertEquals(29, $mock-&gt;subtract(41));&lt;br /&gt;    }&lt;br /&gt;This gives you the same kind of stubbing flexibility that you have present in PHPUnit.&lt;br /&gt;&lt;br /&gt;Verification&lt;br /&gt;&lt;br /&gt;Verifying that methods on your stub are called is starkly different then how it is done in PHPUnit. The most apparent symptom of this difference is that you verify calls after the calls to your test methods have been made.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testVerify1()&lt;br /&gt;    {&lt;br /&gt;        // You can apply stubbings and verifications to the same mock objects&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        $mock-&gt;getValue();&lt;br /&gt;&lt;br /&gt;        //Notice, getValue() has already been called&lt;br /&gt;        Phake::verify($mock)-&gt;getValue();&lt;br /&gt;    }&lt;br /&gt;You of course have the same matching functionality at your disposal.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testVerify2()&lt;br /&gt;    {&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        $mock-&gt;subtract(40);&lt;br /&gt;&lt;br /&gt;        //Notice the constraint&lt;br /&gt;        Phake::verify($mock)-&gt;subtract($this-&gt;lessThan(42));&lt;br /&gt;    }&lt;br /&gt;By default, verify only allows a single matching invocation. You can also specify that a specific number of invocations should be allowed.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testVerify3()&lt;br /&gt;    {&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        $mock-&gt;subtract(40);&lt;br /&gt;        $mock-&gt;subtract(39);&lt;br /&gt;&lt;br /&gt;        //The number of times is passed as the second parameter of &lt;br /&gt;        //Phake::verify()&lt;br /&gt;        Phake::verify($mock, Phake::times(2))-&gt;subtract($this-&gt;lessThan(42));&lt;br /&gt;    }&lt;br /&gt;You can also use Phake::atLeast($n) and Phake::atMost($n) instead of Phake::times($n).&lt;br /&gt;&lt;br /&gt;You can also specify that you don’t expect there to be any interactions with a mock.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testVerify4()&lt;br /&gt;    {&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        // This will ensure that UP TO THIS POINT no methods on $mock have &lt;br /&gt;        // been called&lt;br /&gt;        Phake::verifyNoInteraction($mock);&lt;br /&gt;&lt;br /&gt;        // This would not result in an error, this can be prevented with &lt;br /&gt;        // another method explained below&lt;br /&gt;        $mock-&gt;getValue();&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;I am sure you noticed the comment that Phake::verifyNoInteraction() only verifies that no calls were made up to that point. You can essentially freeze a mock with another method&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testVerify5()&lt;br /&gt;    {&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        $mock-&gt;getValue();&lt;br /&gt;&lt;br /&gt;        // This will ensure that no methods on $mock will be called after this &lt;br /&gt;        // point&lt;br /&gt;        Phake::verifyNoFurtherInteraction($mock);&lt;br /&gt;    }&lt;br /&gt;There are a few more advanced things you can do with something called argument captors.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;    public function testVerify6()&lt;br /&gt;    {&lt;br /&gt;        $mock = Phake::mock('MyClass');&lt;br /&gt;&lt;br /&gt;        $mock-&gt;subtract(42);&lt;br /&gt;&lt;br /&gt;        // Phake::capture tells Phake to store the parameter passed to &lt;br /&gt;        // subtract as the variable $val&lt;br /&gt;        Phake::verify($mock)-&gt;subtract(Phake::capture($val));&lt;br /&gt;&lt;br /&gt;        $this-&gt;assertEquals(42, $val);&lt;br /&gt;    }&lt;br /&gt;This is a very pedestrian example, but it is not that uncommon for fairly complicated objects to passed in and out of methods. Argument capturing allows you to much more succinctly assert the state of those types of parameters. It is definitely overkill for asserting scalar types or simple objects.&lt;br /&gt;&lt;br /&gt;More to Come&lt;br /&gt;&lt;br /&gt;This really does cover the very basics of the Phake framework. In the coming days I will be putting out smaller more focused articles discussing some of the specific functionality. In the meantime I would love to get some feedback from anyone who is brave enough to play with this. My future roadmap basically involves shoring up the current code base a little bit more, adding a few pieces of missing or suboptimal functionality (I’m not so sure I have implemented ‘consecutive calls’) but I anticipate releasing an RC version no later than the end of January. Also, I am currently using and monitoring the issue tracker for Phake at github, so if you have some functionality you would like or find any bugs in your exploring, you can also open an issue there. Also, if you would like to help out with contributions, they are certainly welcome.&lt;br /&gt;&lt;br /&gt;reference : http://digitalsandwich.com/archives/84-introducing-phake-mocking-framework.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1687022161830746586?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1687022161830746586/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1687022161830746586' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1687022161830746586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1687022161830746586'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/introducing-phake-mocking-framework.html' title='Introducing Phake Mocking Framework'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-8869830864679092596</id><published>2011-10-23T09:11:00.000-07:00</published><updated>2011-10-23T09:13:14.150-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FED'/><title type='text'>Front-end Code Standards &amp; Best Practices</title><content type='html'>Overview&lt;br /&gt;&lt;br /&gt;This document contains guidelines for web applications built by the Creative Technology (front end engineering) practice of Isobar US. It is to be readily available to anyone who wishes to check the iterative progress of our best practices. If you have any feedback, please leave a comment on the announcement blog post.&lt;br /&gt;&lt;br /&gt;This document's primary motivation is two- fold: 1) code consistency and 2) best practices. By maintaining consistency in coding styles and conventions, we can ease the burden of legacy code maintenance, and mitigate risk of breakage in the future. By adhering to best practices, we ensure optimized page loading, performance and maintainable code.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;General Guidelines◊Back to Top&lt;br /&gt;Pillars of Front-end Development◊&lt;br /&gt;&lt;br /&gt;Separation of presentation, content, and behavior.&lt;br /&gt;Markup should be well-formed, semantically correct and generally valid.&lt;br /&gt;Javascript should progressively enhance the experience.&lt;br /&gt;General Practices◊&lt;br /&gt;&lt;br /&gt;Indentation◊&lt;br /&gt;&lt;br /&gt;For all code languages, we require indentation to be done via soft tabs (using the space character). Hitting Tab in your text editor shall be equivalent to four spaces.&lt;br /&gt;&lt;br /&gt;Readability vs Compression◊&lt;br /&gt;&lt;br /&gt;We prefer readability over file-size savings when it comes to maintaining existing files. Plenty of whitespace is encouraged, along with ASCII art, where appropriate. There is no need for any developer to purposefully compress HTML or CSS, nor obfuscate JavaScript.&lt;br /&gt;&lt;br /&gt;We will use server-side or build processes to automatically minify and gzip all static client-side files, such as CSS and JavaScript.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Markup◊Back to Top&lt;br /&gt;The first component of any web page is the tag-based markup language of HTML. The Hyper Text Markup Language (HTML) has a sordid history but has come into its own in the last few years. After a lengthy experimentation with the XML-based XHTML variant the industry has accepted that HTML is the future of the web.&lt;br /&gt;&lt;br /&gt;Markup defines the structure and outline of a document and offers a structured content. Markup is not intended to define the look and feel of the content on the page beyond rudimentary concepts such as headers, paragraphs, and lists. The presentation attributes of HTML have all been deprecated and style should be contained in style sheets.&lt;br /&gt;&lt;br /&gt;HTML5◊&lt;br /&gt;&lt;br /&gt;HTML5 is a new version of HTML and XHTML. The HTML5 draft specification defines a single language that can be written in HTML and XML. It attempts to solve issues found in previous iterations of HTML and addresses the needs of web Applications, an area previously not adequately covered by HTML. (source).&lt;br /&gt;&lt;br /&gt;We will use the HTML5 Doctype and HTML5 features when appropriate.&lt;br /&gt;&lt;br /&gt;We will test our markup against the W3C validator, to ensure that the markup is well formed. 100% valid code is not a goal, but validation certainly helps to write more maintainable sites as well as debugging code. Isobar does not guarantee code is 100% valid, but instead assures the cross-browser experience is fairly consistent.&lt;br /&gt;&lt;br /&gt;Template◊&lt;br /&gt;&lt;br /&gt;For HTML5 Documents we use a fork of H5BP modified for our own project needs. Fork the Github repository.&lt;br /&gt;&lt;br /&gt;Doctype◊&lt;br /&gt;&lt;br /&gt;A proper Doctype which triggers standards mode in your browser should always be used. Quirks mode should always be avoided.&lt;br /&gt;&lt;br /&gt;A nice aspect of HTML5 is that it streamlines the amount of code that is required. Meaningless attributes have been dropped, and the DOCTYPE declaration has been simplified significantly. Additionally, there is no need to use CDATA to escape inline JavaScript, formerly a requirement to meet XML strictness in XHTML.&lt;br /&gt;&lt;br /&gt;HTML5 Doctype&lt;br /&gt;1.&lt;br /&gt;&lt;!DOCTYPE html&gt;&lt;br /&gt;Character Encoding◊&lt;br /&gt;&lt;br /&gt;All markup should be delivered as UTF-8, as its the most friendly for internationalization. It should be designated in both the HTTP header and the head of the document.&lt;br /&gt;&lt;br /&gt;Setting the character set using &lt;meta&gt; tags.&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;&lt;br /&gt;In HTML5, just do:&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;meta charset="utf-8"&gt;&lt;br /&gt;General Markup Guidelines◊&lt;br /&gt;&lt;br /&gt;The following are general guidelines for structuring your HTML markup. Authors are reminded to always use markup which represents the semantics of the content in the document being created.&lt;br /&gt;&lt;br /&gt;Use actual P elements for paragraph delimiters as opposed to multiple BR tags.&lt;br /&gt;Make use of DL (definition lists) and BLOCKQUOTE, when appropriate.&lt;br /&gt;Items in list form should always be housed in a UL, OL, or DL, never a set of DIVs or Ps.&lt;br /&gt;Use label fields to label each form field, the for attribute should associate itself with the input field, so users can click the labels. cursor:pointer; on the label is wise, as well. note 1 note 2&lt;br /&gt;Do not use the size attribute on your input fields. The size attribute is relative to the font-size of the text inside the input. Instead use css width.&lt;br /&gt;Place an html comment on some closing div tags to indicate what element you're closing. It will help when there is lots of nesting and indentation.&lt;br /&gt;Tables shouldn't be used for page layout.&lt;br /&gt;Use microformats and/or Microdata where appropriate, specifically hCard and adr.&lt;br /&gt;Make use of THEAD, TBODY, and TH tags (and Scope attribute) when appropriate.&lt;br /&gt;Table markup with proper syntax (THEAD,TBODY,TH [scope])&lt;br /&gt;01.&lt;br /&gt;&lt;table summary="This is a chart of year-end returns for 2005."&gt;&lt;br /&gt;02.&lt;br /&gt;&lt;thead&gt;&lt;br /&gt;03.&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;04.&lt;br /&gt;&lt;th scope="col"&gt;Table header 1&lt;/th&gt;&lt;br /&gt;05.&lt;br /&gt;&lt;th scope="col"&gt;Table header 2&lt;/th&gt;&lt;br /&gt;06.&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;07.&lt;br /&gt;&lt;/thead&gt;&lt;br /&gt;08.&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;09.&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;10.&lt;br /&gt;&lt;td&gt;Table data 1&lt;/td&gt;&lt;br /&gt;11.&lt;br /&gt;&lt;td&gt;Table data 2&lt;/td&gt;&lt;br /&gt;12.&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;13.&lt;br /&gt;&lt;/tbody&gt;&lt;br /&gt;14.&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;Always use title-case for headers and titles. Do not use all caps or all lowercase titles in markup, instead apply the CSS property text-transform:uppercase/lowercase.&lt;br /&gt;Quoting Attributes◊&lt;br /&gt;&lt;br /&gt;While the HTML5 specification defines quotes around attributes as optional for consistency with attributes that accept whitespace, all attributes should be quoted.&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;p class="line note" data-attribute="106"&gt;This is my paragraph of special text.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;CSS◊Back to Top&lt;br /&gt;The second component of a web page is the presentation information contained in the Cascading Style Sheet (CSS.) Web browsers successful implementation of CSS has given a whole generation of web authors site-wide control over the look and feel of their web sites.&lt;br /&gt;&lt;br /&gt;Just as the information on a web page is semantically described in the HTML Markup, CSS describes all presentation aspects of the page via a description of it's visual properties. CSS is powerful in that these properties are mixed and matched via identifiers to control the page's layout and visual characteristics through the layering of style rules (the "cascade").&lt;br /&gt;&lt;br /&gt;General Coding Principles◊&lt;br /&gt;&lt;br /&gt;Add CSS through external files, minimizing the # of files, if possible. It should always be in the HEAD of the document.&lt;br /&gt;Use the LINK tag to include, never the @import.&lt;br /&gt;Including a stylesheet&lt;br /&gt;1.&lt;br /&gt;&lt;link rel="stylesheet" type="text/css" href="myStylesheet.css" /&gt;&lt;br /&gt;Don't use inline styling&lt;br /&gt;1.&lt;br /&gt;&lt;p style="font-size: 12px; color: #FFFFFF"&gt;This is poor form, I say&lt;/p&gt;&lt;br /&gt;Don't include styles inline in the document, either in a style tag or on the elements. It's harder to track down style rules.&lt;br /&gt;Use a reset CSS file (like the one present in H5BP or the Eric Meyers reset) to zero our cross-browser weirdness.&lt;br /&gt;Use a font-normalization file like YUI fonts.css&lt;br /&gt;Elements that occur only once inside a document should use IDs, otherwise, use classes.&lt;br /&gt;Understand cascading and selector specificity so you can write very terse and effective code.&lt;br /&gt;Write selectors that are optimized for speed. Where possible, avoid expensive CSS selectors. For example, avoid the * wildcard selector and don't qualify ID selectors (e.g. div#myid) or class selectors (e.g. table.results.) This is especially important with web applications where speed is paramount and there can be thousands or even tens of thousands of DOM elements. More on writing efficient CSS on the MDC.&lt;br /&gt;CSS Box Model◊&lt;br /&gt;&lt;br /&gt;Intimate knowledge and understanding of the CSS and browser-based box model is necessary for conquering the fundamentals of CSS layouts.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3D CSS Box Model diagram by Jon Hicks.&lt;br /&gt;CSS Validation◊&lt;br /&gt;&lt;br /&gt;We typically don't use the W3C validator.&lt;br /&gt;&lt;br /&gt;CSS Formatting◊&lt;br /&gt;&lt;br /&gt;At minimum, format CSS with selectors on one line and each property on its own line. The declarations are indented.&lt;br /&gt;&lt;br /&gt;As an enhancement to that style, related or child styles and additional 2 or 4 spaces. That allows for hierarchical scanning and organization and makes (for some people) an easier-to-read style sheet.&lt;br /&gt;&lt;br /&gt;01.&lt;br /&gt;.post-list li a{&lt;br /&gt;02.&lt;br /&gt;color:#A8A8A8;&lt;br /&gt;03.&lt;br /&gt;}&lt;br /&gt;04.&lt;br /&gt;.post-list li a:hover{&lt;br /&gt;05.&lt;br /&gt;color:#000;&lt;br /&gt;06.&lt;br /&gt;text-decoration:none;&lt;br /&gt;07.&lt;br /&gt;}&lt;br /&gt;08.&lt;br /&gt;.post-list li .author a, .post-list li .author a:hover{&lt;br /&gt;09.&lt;br /&gt;color:#F30;&lt;br /&gt;10.&lt;br /&gt;text-transform:uppercase;&lt;br /&gt;11.&lt;br /&gt;}&lt;br /&gt;For multiple author environments, single line CSS should be avoided because it can cause issues with version control.&lt;br /&gt;&lt;br /&gt;Alphabetize◊&lt;br /&gt;If you're performance obsessed alphabetizing CSS properties increases the odds of larger repeatable patterns being present to aid in GZIP compression.&lt;br /&gt;&lt;br /&gt;Classes vs. IDs◊&lt;br /&gt;&lt;br /&gt;You should only give elements an ID attribute if they are unique. They should be applied to that element only and nothing else. Classes can be applied to multiple elements that share the same style properties. Things that should look and work in the same way can have the same class name.&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;ul id="categories"&gt;&lt;br /&gt;2.&lt;br /&gt;&lt;li class="item"&gt;Category 1&lt;/li&gt;&lt;br /&gt;3.&lt;br /&gt;&lt;li class="item"&gt;Category 2&lt;/li&gt;&lt;br /&gt;4.&lt;br /&gt;&lt;li class="item"&gt;Category 3&lt;/li&gt;&lt;br /&gt;5.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Naming Conventions for Selectors◊&lt;br /&gt;&lt;br /&gt;It is always preferable to name something, be it an ID or a class, by the nature of what it is rather than by what it looks like. For instance, a class name of bigBlueText for a special note on a page is quite meaningless if it has been changed to have a small red text color. Using a more intelligent convention such as noteText is better because when the visual style changes it still makes sense.&lt;br /&gt;&lt;br /&gt;Selectors◊&lt;br /&gt;&lt;br /&gt;The CSS Selectors Level 3 specification introduces a whole new set of CSS Selectors that are extremely useful for better selection of elements.&lt;br /&gt;&lt;br /&gt;Pseudo-classes◊&lt;br /&gt;Pseudo-classes enable you to dynamically style content. Some pseudo-classes have existed since CSS1 (:visited, :hover, etc.) and CSS2 (:first-child, :lang). As of CSS3, 16 new pseudo-classes have been added to the list and are especially useful for styling dynamic content. Learn how to use pseudo-classes in-depth.&lt;br /&gt;&lt;br /&gt;Combinators &amp; Attribute Selectors◊&lt;br /&gt;Combinators provide shortcuts for selecting elements that are a descendant element, a child element, or an element's sibling.&lt;br /&gt;&lt;br /&gt;Attribute Selectors are great for finding elements that have a specific attribute and/or specific value. Knowledge of regular expressions helps with attribute selectors.&lt;br /&gt;&lt;br /&gt;Specificity◊&lt;br /&gt;Browsers calculate a selector's specificity to determine which CSS rule should apply. If two selectors apply to the same element, the one with the higher specificity wins.&lt;br /&gt;&lt;br /&gt;IDs have a higher specificity than attribute selectors do, and class selectors have higher specificity than any number of element selectors. Always try to use IDs to increase the specificity. There are times when we may try to apply a CSS rule to an element and it does not work no matter what we try. This is likely because the specificity of the selector used is lower than another one and the properties of the higher one are taking precedence over those you want to apply. This is more common in working with larger more complex stylesheets. It isn't a big issue with smaller projects usually.&lt;br /&gt;&lt;br /&gt;Calculating specifity◊&lt;br /&gt;&lt;br /&gt;When working with a large and complex stylesheet it helps to know how to calculate the value of a selector's specificity, to save you time and to make your selectors more efficient.&lt;br /&gt;&lt;br /&gt;Specificity is calculated by counting various components of your CSS and expressing them in a form (a,b,c,d).&lt;br /&gt;&lt;br /&gt;Element, Pseudo Element: d = 1 – (0,0,0,1)&lt;br /&gt;Class, Pseudo class, Attribute: c = 1 – (0,0,1,0)&lt;br /&gt;Id: b = 1 – (0,1,0,0)&lt;br /&gt;Inline Style: a = 1 – (1,0,0,0)&lt;br /&gt;However, it may be better to use a specificity calculator.&lt;br /&gt;&lt;br /&gt;Specificity Calculator&lt;br /&gt;Some things you should know about specificity&lt;br /&gt;IE Specificity bugs&lt;br /&gt;Using !important overrides all specificity no matter how high it is. We like to avoid using it for this reason. Most of the time it is not necessary. Even if you need to override a selector in a stylesheet you don't have access to, there are usually ways to override it without using !important. Avoid using it if possible.&lt;br /&gt;&lt;br /&gt;Pixels vs. Ems◊&lt;br /&gt;&lt;br /&gt;We use the px unit of measurement to define font size, because it offers absolute control over text. We realize that using the em unit for font sizing used to be popular, to accommodate for Internet Explorer 6 not resizing pixel based text. However, all major browsers (including IE7 and IE8) now support text resizing of pixel units and/or full-page zooming. Since IE6 is largely considered deprecated, pixels sizing is preferred. Additionally, unit-less line-height is preferred because it does not inherit a percentage value of its parent element, but instead is based on a multiplier of the font-size.&lt;br /&gt;&lt;br /&gt;Correct&lt;br /&gt;1.&lt;br /&gt;#selector {&lt;br /&gt;2.&lt;br /&gt;font-size: 13px;&lt;br /&gt;3.&lt;br /&gt;line-height: 1.5;  /*  13 * 1.5 = 19.5 ~ Rounds to 20px. */&lt;br /&gt;4.&lt;br /&gt;}&lt;br /&gt;Incorrect&lt;br /&gt;1.&lt;br /&gt;/*  Equivalent to 13px font-size and 20px line-height, but only if the browser default text size is 16px. */&lt;br /&gt;2.&lt;br /&gt;#selector {&lt;br /&gt;3.&lt;br /&gt;font-size: 0.813em;&lt;br /&gt;4.&lt;br /&gt;line-height: 1.25em;&lt;br /&gt;5.&lt;br /&gt;}&lt;br /&gt;Internet Explorer Bugs◊&lt;br /&gt;&lt;br /&gt;Inevitably, when all other browsers appear to be working correctly, any and all versions of Internet Explorer will introduce a few nonsensical bugs, delaying time to deployment. While we encourage troubleshooting and building code that will work in all browsers without special modifications, sometimes it is necessary to use conditional if IE comments for CSS hooks we can use in our stylesheets. Read more on paulirish.com&lt;br /&gt;&lt;br /&gt;Fixing IE&lt;br /&gt;1.&lt;br /&gt;&lt;!--[if lt IE 7 ]&gt; &lt;body class="ie6"&gt; &lt;![endif]--&gt;&lt;br /&gt;2.&lt;br /&gt;&lt;!--[if IE 7 ]&gt;    &lt;body class="ie7"&gt; &lt;![endif]--&gt;&lt;br /&gt;3.&lt;br /&gt;&lt;!--[if IE 8 ]&gt;    &lt;body class="ie8"&gt; &lt;![endif]--&gt;&lt;br /&gt;4.&lt;br /&gt;&lt;!--[if IE 9 ]&gt;    &lt;body class="ie9"&gt; &lt;![endif]--&gt;&lt;br /&gt;5.&lt;br /&gt;&lt;!--[if !IE]&gt;&lt;!--&gt; &lt;body&gt; &lt;!--&lt;![endif]--&gt;&lt;br /&gt;1.&lt;br /&gt;.box { float: left; margin-left: 20px; }&lt;br /&gt;2.&lt;br /&gt;.ie6 .box { margin-left: 10px; }&lt;br /&gt;If you're using HTML5 (and the HTML5 Boilerplate) we encourage the use of the Modernizer JavaScript library and this pattern:&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;!--[if lt IE 7]&gt; &lt;html class="no-js ie ie6" lang="en"&gt; &lt;![endif]--&gt;&lt;br /&gt;2.&lt;br /&gt;&lt;!--[if IE 7]&gt;    &lt;html class="no-js ie ie7" lang="en"&gt; &lt;![endif]--&gt;&lt;br /&gt;3.&lt;br /&gt;&lt;!--[if IE 8]&gt;    &lt;html class="no-js ie8" lang="en"&gt; &lt;![endif]--&gt;&lt;br /&gt;4.&lt;br /&gt;&lt;!--[if IE 9]&gt;    &lt;html class="no-js ie9" lang="en"&gt; &lt;![endif]--&gt;&lt;br /&gt;5.&lt;br /&gt;&lt;!--[if gt IE 9]&gt;&lt;!--&gt;&lt;html class="no-js" lang="en"&gt;&lt;!--&lt;![endif]--&gt;&lt;br /&gt;Shorthand◊&lt;br /&gt;&lt;br /&gt;In general, CSS shorthand is preferred because of its terseness, and the ability to later go back and add in values that are already present, such as the case with margin and padding. Developers should be aware of the TRBL acronym, denoting the order in which the sides of an element are defined, in a clock-wise manner: Top, Right, Bottom, Left. If bottom is undefined, it inherits its value from top. Likewise, if left is undefined, it inherits its value from right. If only the top value is defined, all sides inherit from that one declaration.&lt;br /&gt;&lt;br /&gt;For more on reducing stylesheet code redundancy, and using CSS shorthand in general:&lt;br /&gt;http://qrayg.com/journal/news/css-background-shorthand&lt;br /&gt;http://sonspring.com/journal/css-redundancy&lt;br /&gt;http://dustindiaz.com/css-shorthand&lt;br /&gt;Images◊&lt;br /&gt;&lt;br /&gt;For repeating images, use something larger than 1x1 pixels&lt;br /&gt;You should never be using spacer images.&lt;br /&gt;Use CSS sprites generously. They make hover states easy, improve page load time, and reduce carbon dioxide emissions.&lt;br /&gt;Typically, all images should be sliced with a transparent background (PNG8). All should be cropped tightly to the image boundaries.&lt;br /&gt;However, the logo should always have a background matte and have padding before the crop. (so other people can hotlink to the file)&lt;br /&gt;General Text and Font Styling◊&lt;br /&gt;&lt;br /&gt;Headings◊&lt;br /&gt;Define default styling for h1-h6 headings including headings as links. It's helpful to declare these at the top of your CSS document, and modify them with as necessary for consistency across the site.&lt;br /&gt;Headings should show a hierarchy indicating different levels of importance from the top down starting with h1 having the largest font size.&lt;br /&gt;SEO: To get a rough idea of how your page hierarchy is organized and read, use your Developer Toolbar to disable CSS. You'll end up with a text-based view of all your h1-h6 tags, strong, em, etc.&lt;br /&gt;Links◊&lt;br /&gt;Default styles for links should be declared and different from the main text styling, and with differing styles for hover state.&lt;br /&gt;When styling links with underlines use border-bottom and some padding with text-decoration: none;. This just looks better.&lt;br /&gt;Web Typography◊&lt;br /&gt;&lt;br /&gt;The use of custom fonts and typefaces on the web has been growing more popular the past few years. with native browser support on the rise and several supporting services and APIs now available there is real momentum in this space. Each of these approaches have their own pros and cons. Before a project kicks off it's best to do research into technology and licensing limitations to choose the proper approach for the specific project.&lt;br /&gt;&lt;br /&gt;All of these approaches have drawbacks in code overhead, development time and performance (clock and perceived). Familiarizing yourself with these issues and communicating them to the other members of the team and to the client will save significant problems later on in the project.&lt;br /&gt;&lt;br /&gt;Listed here are some popular methods of embed custom fonts, list in the order of our preference for implementation.&lt;br /&gt;&lt;br /&gt;@font-face◊&lt;br /&gt;&lt;br /&gt;The @font-face at-rule allows you to define custom fonts. It was first defined in the CSS2 specification, but was removed from CSS2.1. Currently, it's a draft recommendation for CSS3.&lt;br /&gt;&lt;br /&gt;Our first and most preferred choice for customizing fonts on the web is @font-face, simply because it is part of the CSS Fonts Module working draft which means it will continue to grow in popularity as browser support grows, and ease of use for it improves as it becomes more stable.&lt;br /&gt;&lt;br /&gt;For now, when using @font-face it's recommended to declare the source for each font format. This is important if you want it to work in the most number of browsers, though it is not a requirement for use.&lt;br /&gt;&lt;br /&gt;The font formats included in the specification are:&lt;br /&gt;woff: WOFF (Web Open Font Format)&lt;br /&gt;ttf: TrueType&lt;br /&gt;ttf, otf: OpenType&lt;br /&gt;eot: Embedded OpenType&lt;br /&gt;svg, svgz: SVG Font&lt;br /&gt;Bulletproof @font-face◊&lt;br /&gt;For full cross-browser compatibility use Fontsprings' new bulletproof @font-face syntax (latest version as of 2/21/11).&lt;br /&gt;&lt;br /&gt;01.&lt;br /&gt;@font-face {&lt;br /&gt;02.&lt;br /&gt;font-family: 'MyFontFamily';&lt;br /&gt;03.&lt;br /&gt;src: url('myfont-webfont.eot');                     /* IE9 Compat Modes */&lt;br /&gt;04.&lt;br /&gt;src: url('myfont-webfont.eot?iefix') format('eot'), /* IE6-IE8 */&lt;br /&gt;05.&lt;br /&gt;url('myfont-webfont.woff') format('woff'),     /* Modern Browsers */&lt;br /&gt;06.&lt;br /&gt;url('myfont-webfont.ttf') format('truetype'),      /* Safari, Android, iOS */&lt;br /&gt;07.&lt;br /&gt;url('myfont-webfont.svg#svgFontName') format('svg'); /* Legacy iOS */&lt;br /&gt;08.&lt;br /&gt;font-weight: &lt;font-weight&gt;;&lt;br /&gt;09.&lt;br /&gt;font-style: &lt;font-style&gt;;&lt;br /&gt;10.&lt;br /&gt;// etc.&lt;br /&gt;11.&lt;br /&gt;}&lt;br /&gt;Here's a working demo using this version of implementation.&lt;br /&gt;&lt;br /&gt;Cross-Browser Compatibility◊&lt;br /&gt;Safari, IE 6-9, IE 9 Compatibility Modes, Firefox, Chrome, iOS, Android, Opera&lt;br /&gt;&lt;br /&gt;Prevent Compatibility Mode◊&lt;br /&gt;Sometimes IE can have a mind of its own and will switch to compatibility mode without you knowing. Include the following in the site &lt;head&gt; to prevent your site from defaulting to compatibility mode:&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;&lt;br /&gt;Tips for @font-face◊&lt;br /&gt;IE 6–8 will only accept a TrueType font packaged as an EOT.&lt;br /&gt;font-weight and font-style have different meanings within @font-face. Declarations where font-weight:bold; means this is the bold version of this typeface, rather than apply bold to this text&lt;br /&gt;@font-face gotchas&lt;br /&gt;Pros&lt;br /&gt;Easy to implement&lt;br /&gt;Large variety of APIs&lt;br /&gt;Customizable&lt;br /&gt;Easy to add to elements&lt;br /&gt;Nothing required besides CSS&lt;br /&gt;Is currently part of the working draft of CSS Fonts Module 3&lt;br /&gt;Cons&lt;br /&gt;Limited browser support if used improperly&lt;br /&gt;Some older versions of modern browsers (Chrome, Opera) don't always render well. Text can have rough edges. **I have not been able to confirm whether this is still an issue now or not.&lt;br /&gt;Google WebFonts API &amp; Font Loader◊&lt;br /&gt;&lt;br /&gt;There are two options available with Google Webfonts. Both options have their downsides of course but they can be just as good to use as @font-face, it all depends on a projects needs.&lt;br /&gt;&lt;br /&gt;Webfonts API◊&lt;br /&gt;Google's Webfonts API essentially does the same thing as @font-face, it just does all the hard work for you, providing wider browser support.The major drawback to this method is the very small font library it uses. To make it work all you need to do is include the stylesheet + the font name.&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Font+Name"&gt;&lt;br /&gt;Then define a style for the selector you want to apply the font to:&lt;br /&gt;1.&lt;br /&gt;CSS selector {&lt;br /&gt;2.&lt;br /&gt;font-family: 'Font Name', serif;&lt;br /&gt;3.&lt;br /&gt;}&lt;br /&gt;Webfont Loader◊&lt;br /&gt;&lt;br /&gt;Another option Google offers is the Webfont Loader which is a JavaScript library that allows for more control than the font API does. You can also use multiple webfont providers like Typekit. To use it include the script in your page:&lt;br /&gt;&lt;br /&gt;01.&lt;br /&gt;&lt;script type="text/javascript"&gt;&lt;br /&gt;02.&lt;br /&gt;WebFontConfig = { google: { families: [ 'Tangerine', 'Cantarell' ]} };&lt;br /&gt;03.&lt;br /&gt;(function() {&lt;br /&gt;04.&lt;br /&gt;var wf = document.createElement('script');&lt;br /&gt;05.&lt;br /&gt;wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +&lt;br /&gt;06.&lt;br /&gt;'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';&lt;br /&gt;07.&lt;br /&gt;wf.type = 'text/javascript';&lt;br /&gt;08.&lt;br /&gt;wf.async = 'true';&lt;br /&gt;09.&lt;br /&gt;var s = document.getElementsByTagName('script')[0];&lt;br /&gt;10.&lt;br /&gt;s.parentNode.insertBefore(wf, s);&lt;br /&gt;11.&lt;br /&gt;})();&lt;br /&gt;12.&lt;br /&gt;&lt;/script&gt;&lt;br /&gt;Including the webfont.js file this way is faster if not already using the Ajax APIs. Otherwise you should use this:&lt;br /&gt;1.&lt;br /&gt;&lt;script type="text/javascript" src="https://www.google.com/jsapi"&gt;&lt;/script&gt;&lt;br /&gt;2.&lt;br /&gt;&lt;script type="text/javascript"&gt;&lt;br /&gt;3.&lt;br /&gt;google.load("webfont", "1");&lt;br /&gt;4.&lt;br /&gt;google.setOnLoadCallback(function() {&lt;br /&gt;5.&lt;br /&gt;WebFont.load({ google: { families: [ 'Tangerine', 'Cantarell' ]} });&lt;br /&gt;6.&lt;br /&gt;});&lt;br /&gt;7.&lt;br /&gt;&lt;/script&gt;&lt;br /&gt;By using the Webfont Loader you have more ability to customize things including the use of more fonts, not just those in the Google Webfont library which is not large. However, it then requires you to load JavaScript, sacrificing one thing for another.&lt;br /&gt;&lt;br /&gt;Pros&lt;br /&gt;Very easy to implement&lt;br /&gt;Wide browser support&lt;br /&gt;Can be combined with Typekit&lt;br /&gt;Customizable when using the font loader&lt;br /&gt;API does the same thing as @font-face&lt;br /&gt;Cons&lt;br /&gt;Very small font library if using the font API&lt;br /&gt;Using the Webfont Loader requires the use of JavaScript to work&lt;br /&gt;Most browsers will load the rest of the page first, leaving a blank space where the text would be, or otherwise show the fallback option if one exists, until the page fully loads.&lt;br /&gt;Some fonts in the webfont library render poorly on Windows&lt;br /&gt;Cufon◊&lt;br /&gt;&lt;br /&gt;If you choose to use Cufon, it is highly recommended you use the Cufon compressed version. You will need to convert your font using the generator.&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;&lt;script src="cufon-yui.js" type="text/javascript"&gt;&lt;/script&gt;&lt;br /&gt;2.&lt;br /&gt;&lt;script src="YourFont.font.js" type="text/javascript"&gt;&lt;/script&gt;&lt;br /&gt;3.&lt;br /&gt;&lt;script type="text/javascript"&gt;&lt;br /&gt;4.&lt;br /&gt;Cufon.replace('h1');        // Works without a selector engine&lt;br /&gt;5.&lt;br /&gt;Cufon.replace('#sub1');     // Requires a selector engine for IE 6-7&lt;br /&gt;6.&lt;br /&gt;&lt;/script&gt;&lt;br /&gt;We recommend using Cufon sparingly since it can cause a lot of overhead if applied to a large amount of text. For more info visit the Cufon Wiki.&lt;br /&gt;&lt;br /&gt;Pros&lt;br /&gt;Wide browser support&lt;br /&gt;Renders well in supported browsers&lt;br /&gt;Customizable&lt;br /&gt;Easy to implement&lt;br /&gt;Cons&lt;br /&gt;Requires use of JS to work&lt;br /&gt;Text can't be selected that uses it&lt;br /&gt;Not all characters can be used&lt;br /&gt;Customization can be a pain&lt;br /&gt;Not always easy to apply to multiple elements, especially when adding effects like hovers&lt;br /&gt;Typekit◊&lt;br /&gt;&lt;br /&gt;Using Typekit has its advantages and shouldn't be completely disregarded when choosing which method to use for adding custom fonts to a web site. It has strong platform integration and is a scalable and popular service. It can be used with Google Webfonts and is easily added to WordPress, Posterous, Typepad, and other similar CMS powered sites.&lt;br /&gt;&lt;br /&gt;However, full use of Typekit doesn't come without a cost. If you need to use it on more than 2 sites or on a site that gets a high amount of pageviews you will need to pay an annual cost of $49.99, and for sites with a million+ pageviews it costs twice as much. Though, you probably have the money to cover the cost if you're getting over a million pageviews. If not then you may want to rethink your business model.&lt;br /&gt;&lt;br /&gt;Pros&lt;br /&gt;Large font library, including Adobe fonts&lt;br /&gt;Easy implementation&lt;br /&gt;Google Webfont API and blogging platform integration&lt;br /&gt;Free plan has limits but doesn't expire&lt;br /&gt;Cons&lt;br /&gt;Requires JavaScript to use&lt;br /&gt;Limited font library access without paying&lt;br /&gt;Free and cheapest plans only allow use on 1-2 web sites and 2-5 fonts per site&lt;br /&gt;You have to pay to use it on more than 1 site&lt;br /&gt;Scalable Inman Flash Replacement (sIFR)◊&lt;br /&gt;&lt;br /&gt;We do not recommend that you use this method but because of how widely used it is we felt it was necessary to include so you could make a properly informed decision when choosing which method to go with for customized webfonts.&lt;br /&gt;&lt;br /&gt;Despite its wide popularity among web designers, and its decent support in most browsers, the drawbacks to its use outweigh its convenience. The biggest and most obvious reason to not use sIFR is the fact that it uses Flash. Plus, in order for the Flash to even work, it requires JavaScript and the scripts must be loaded before the text you use it on is visible on the page. Not to mention that it increases page load time, and can cause a slow site to be even slower.&lt;br /&gt;&lt;br /&gt;We'll let you do the math here.&lt;br /&gt;Pros&lt;br /&gt;Text can be selected&lt;br /&gt;Support on most browsers&lt;br /&gt;Renders okay on supported browsers&lt;br /&gt;Cons&lt;br /&gt;It uses Flash&lt;br /&gt;Requires JavaScript for the Flash to work&lt;br /&gt;It's Flash!&lt;br /&gt;Text doesn't appear until the scripts load&lt;br /&gt;...and it's Flash...&lt;br /&gt;Font Licensing◊&lt;br /&gt;&lt;br /&gt;Even though you can transform just about any font into a web font file, you should still make sure it is legally okay for you to do so. Many foundries have updated their conditions to specify how their fonts can be used on the web. View Font Licensing and Protection Details for more information.&lt;br /&gt;&lt;br /&gt;Specifications &amp; Font File Formats◊&lt;br /&gt;&lt;br /&gt;CSS 2 Fonts – May 1998 (Obsolete)&lt;br /&gt;CSS 3 Fonts – Working Draft 2009&lt;br /&gt;CSS Fonts Module – W3C Working Draft March 2011&lt;br /&gt;WOFF Font Format – Working Draft 2010&lt;br /&gt;SVG Font Format&lt;br /&gt;Embedded Open Type (EOT) File Format&lt;br /&gt;Microsoft Open Type Specification&lt;br /&gt;OpenType Feature File Specification&lt;br /&gt;Apple True Type Reference&lt;br /&gt;&lt;br /&gt;JavaScript◊Back to Top&lt;br /&gt;JavaScript is the third major component of a web page. JavaScript code, when properly applied to a web page, enhances the overall user and browser-based experience through attaching to events and controlling the overall behavior layer.&lt;br /&gt;&lt;br /&gt;JavaScript has seen an explosion in popularity in recent years as powerful new browser implementations have finally empowered the creation of full on browser-based web applications. Additionally, careful use of JavaScript allows for full manipulation and control over the other two components of web page authoring, HTML Markup and CSS. Today the structure of pages and the visual styles of pages can be manipulated real time without full web page refreshes.&lt;br /&gt;&lt;br /&gt;JavaScript Libraries◊&lt;br /&gt;&lt;br /&gt;We primarily develop new applications in jQuery, though we have expertise in plain JavaScript as well as all major modern javascript libraries.&lt;br /&gt;General Coding Principles◊&lt;br /&gt;&lt;br /&gt;99% of code should be housed in external javascript files. They should be included at the END of the BODY tag for maximum page performance.&lt;br /&gt;Don't rely on the user-agent string. Do proper feature detection. (More at Dive Into HTML5: Detection &amp; jQuery.support docs)&lt;br /&gt;Don't use document.write().&lt;br /&gt;All Boolean variables should start with "is".&lt;br /&gt;Test for positive conditions&lt;br /&gt;1.&lt;br /&gt;isValid = (test.value &gt;= 4 &amp;&amp; test.success);&lt;br /&gt;Name variables and functions logically: For example: popUpWindowForAd rather than myWindow.&lt;br /&gt;Don't manually minify. With the exception of the traditional i, etc. for for loops, variables should be long enough to be meaningful.&lt;br /&gt;Documentation should follow NaturalDocs structure.&lt;br /&gt;Constants or configuration variables (like animation durations, etc.) should be at the top of the file.&lt;br /&gt;Strive to create functions which can be generalized, take parameters, and return values. This allows for substantial code reuse and, when combined with includes or external scripts, can reduce the overhead when scripts need to change. For example, instead of hard coding a pop-window with window size, options, and url, consider creating a function which takes size, url, and options as variables.&lt;br /&gt;Comment your code! It helps reduce time spent troubleshooting JavaScript functions.&lt;br /&gt;Don't waste your time with &lt;!-- --&gt; comments surrounding your inline javascript, unless you care about Netscape 4. :)&lt;br /&gt;Organize your code as an Object Literal/Singleton, in the Module Pattern, or as an Object with constructors.&lt;br /&gt;Minimize global variables - the less globals you create, the better. Generally one, for your application namespace, is a good number.&lt;br /&gt;When specifying any global variable, clearly identify it&lt;br /&gt;1.&lt;br /&gt;window.globalVar = { ... }&lt;br /&gt;White-space◊&lt;br /&gt;&lt;br /&gt;In general, the use of whitespace should follow longstanding English reading conventions. Such that, there will be one space after each comma and colon (and semi-colon where applicable), but no spaces immediately inside the right and left sides of parenthesis. In short, we advocate readability within reason. Additionally, braces should always appear on the same line as their preceding argument.&lt;br /&gt;&lt;br /&gt;Consider the following examples of a JavaScript for-loop...&lt;br /&gt;&lt;br /&gt;Correct&lt;br /&gt;1.&lt;br /&gt;for (var i = 0, j = arr.length; i &lt; j; i++) {&lt;br /&gt;2.&lt;br /&gt;// Do something.&lt;br /&gt;3.&lt;br /&gt;}&lt;br /&gt;Incorrect&lt;br /&gt;1.&lt;br /&gt;for ( var i = 0, j = arr.length; i &lt; j; i++ )&lt;br /&gt;2.&lt;br /&gt;{&lt;br /&gt;3.&lt;br /&gt;// Do something.&lt;br /&gt;4.&lt;br /&gt;}&lt;br /&gt;Also incorrect&lt;br /&gt;1.&lt;br /&gt;for(var i=0,j=arr.length;i&lt;j;i++){&lt;br /&gt;2.&lt;br /&gt;// Do something.&lt;br /&gt;3.&lt;br /&gt;}&lt;br /&gt;plugins.js and script.js◊&lt;br /&gt;&lt;br /&gt;Starting with H5BP we're presented with two files, plugins.js and script.js. This section outlines basic usage of these two files.&lt;br /&gt;&lt;br /&gt;plugins.js◊&lt;br /&gt;Plugins.js is meant to hold all of a sites plugin code. Instead of linking to many different files, we can improve performance by including plugin code directly in this one file. There can and should be exceptions to this usage. An extremely large plugin only used on one rarely visited page, for example, might be better off in a separate download, only accessed on the target page. Most of the time, however, it's safe to just paste in minified versions of all your plugins here for easy access.&lt;br /&gt;&lt;br /&gt;Here's what an example file might looks like, including a small table of contents. This can serve as a handy guide for what plugins are in use, including URLs for documentation, rationale for use and the like.&lt;br /&gt;&lt;br /&gt;01.&lt;br /&gt;/* PLUGIN DIRECTORY&lt;br /&gt;02.&lt;br /&gt;What you can find in this file [listed in order they appear]&lt;br /&gt;03.&lt;br /&gt; &lt;br /&gt;04.&lt;br /&gt;1.) Animate Background Position - http://plugins.jquery.com/project/backgroundPosition-Effect&lt;br /&gt;05.&lt;br /&gt;2.) jQuery Easing Plugin - http://gsgd.co.uk/sandbox/jquery/easing/&lt;br /&gt;06.&lt;br /&gt;3.) jQuery Ajax Form plugin - http://jquery.malsup.com/form/#download           &lt;br /&gt;07.&lt;br /&gt;4.) jQuery validation plugin (form validation) - http://docs.jquery.com/Plugins/Validation&lt;br /&gt;08.&lt;br /&gt;-password strength&lt;br /&gt;09.&lt;br /&gt;5.) Styled Selects (lightweight) - http://code.google.com/p/lnet/wiki/jQueryStyledSelectOverview&lt;br /&gt;10.&lt;br /&gt;*/&lt;br /&gt;11.&lt;br /&gt; &lt;br /&gt;12.&lt;br /&gt;/**&lt;br /&gt;13.&lt;br /&gt;* 1.) Animate Background Position - http://plugins.jquery.com/project/backgroundPosition-Effect&lt;br /&gt;14.&lt;br /&gt;* @author Alexander Farkas&lt;br /&gt;15.&lt;br /&gt;* v. 1.21&lt;br /&gt;16.&lt;br /&gt;*/&lt;br /&gt;17.&lt;br /&gt;(function($) {&lt;br /&gt;18.&lt;br /&gt;if(!document.defaultView || !document.defaultView.getComputedStyle){ // IE6-IE8&lt;br /&gt;19.&lt;br /&gt;//SNIPPED&lt;br /&gt;20.&lt;br /&gt;};&lt;br /&gt;21.&lt;br /&gt;})(jQuery);&lt;br /&gt;22.&lt;br /&gt; &lt;br /&gt;23.&lt;br /&gt; &lt;br /&gt;24.&lt;br /&gt;/**&lt;br /&gt;25.&lt;br /&gt;* 2.) jQuery Easing Plugin (we're not using jQuery UI as of yet) - http://gsgd.co.uk/sandbox/jquery/easing/&lt;br /&gt;26.&lt;br /&gt;*/&lt;br /&gt;27.&lt;br /&gt; &lt;br /&gt;28.&lt;br /&gt;// t: current time, b: begInnIng value, c: change In value, d: duration&lt;br /&gt;29.&lt;br /&gt;jQuery.easing['jswing'] = jQuery.easing['swing'];&lt;br /&gt;30.&lt;br /&gt; &lt;br /&gt;31.&lt;br /&gt;jQuery.extend( jQuery.easing,&lt;br /&gt;32.&lt;br /&gt;{&lt;br /&gt;33.&lt;br /&gt;//SNIPPED&lt;br /&gt;34.&lt;br /&gt; &lt;br /&gt;35.&lt;br /&gt;});&lt;br /&gt;36.&lt;br /&gt;;(function($) {&lt;br /&gt;37.&lt;br /&gt;$.fn.ajaxSubmit = function(options) {&lt;br /&gt;38.&lt;br /&gt;//SNIPPED&lt;br /&gt;39.&lt;br /&gt;}&lt;br /&gt;40.&lt;br /&gt;})(jQuery);&lt;br /&gt;41.&lt;br /&gt; &lt;br /&gt;42.&lt;br /&gt;/*&lt;br /&gt;43.&lt;br /&gt;* jQuery Styled Select Boxes&lt;br /&gt;44.&lt;br /&gt;* version: 1.1 (2009/03/24)&lt;br /&gt;45.&lt;br /&gt;* @requires jQuery v1.2.6 or later&lt;br /&gt;46.&lt;br /&gt;*&lt;br /&gt;47.&lt;br /&gt;* Examples and documentation at: http://code.google.com/p/lnet/wiki/jQueryStyledSelectOverview&lt;br /&gt;48.&lt;br /&gt;*&lt;br /&gt;49.&lt;br /&gt;* Copyright (c) 2008 Lasar Liepins, liepins.org, liepins@gmail.com&lt;br /&gt;50.&lt;br /&gt;*&lt;br /&gt;51.&lt;br /&gt;* Permission is hereby granted, free of charge, to any person obtaining a copy&lt;br /&gt;52.&lt;br /&gt;* of this software and associated documentation files (the "Software"), to deal&lt;br /&gt;53.&lt;br /&gt;* in the Software without restriction, including without limitation the rights&lt;br /&gt;54.&lt;br /&gt;* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&lt;br /&gt;55.&lt;br /&gt;* copies of the Software, and to permit persons to whom the Software is&lt;br /&gt;56.&lt;br /&gt;* furnished to do so, subject to the following conditions:&lt;br /&gt;57.&lt;br /&gt;*&lt;br /&gt;58.&lt;br /&gt;* The above copyright notice and this permission notice shall be included in&lt;br /&gt;59.&lt;br /&gt;* all copies or substantial portions of the Software.&lt;br /&gt;60.&lt;br /&gt;*&lt;br /&gt;61.&lt;br /&gt;* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&lt;br /&gt;62.&lt;br /&gt;* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&lt;br /&gt;63.&lt;br /&gt;* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&lt;br /&gt;64.&lt;br /&gt;* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&lt;br /&gt;65.&lt;br /&gt;* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&lt;br /&gt;66.&lt;br /&gt;* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&lt;br /&gt;67.&lt;br /&gt;* THE SOFTWARE.&lt;br /&gt;68.&lt;br /&gt;*&lt;br /&gt;69.&lt;br /&gt;*/&lt;br /&gt;70.&lt;br /&gt; &lt;br /&gt;71.&lt;br /&gt;jQuery.fn.styledSelect = function(settings) {&lt;br /&gt;72.&lt;br /&gt;//SNIPPED&lt;br /&gt;73.&lt;br /&gt;return this;&lt;br /&gt;74.&lt;br /&gt;};&lt;br /&gt;Script.js◊&lt;br /&gt;Script.js is meant to hold your site or application code. Again, this isn't always the best solution as larger teams and or larger, more feature rich projects can really benefit from breaking out application code into module or feature specific files. For smaller sites, simpler applications, and initial prototyping, however, dropping your work into scripts.js makes sense.&lt;br /&gt;&lt;br /&gt;A simplified example, using the Markup-based unobtrusive comprehensive DOM-ready execution pattern, might look something like the following:&lt;br /&gt;&lt;br /&gt;01.&lt;br /&gt;/* Name: Demo&lt;br /&gt;02.&lt;br /&gt;Author: Demo King */&lt;br /&gt;03.&lt;br /&gt;/*demo namespace*/&lt;br /&gt;04.&lt;br /&gt;demo = {&lt;br /&gt;05.&lt;br /&gt;common : {&lt;br /&gt;06.&lt;br /&gt;init     : function(){&lt;br /&gt;07.&lt;br /&gt;//initialize&lt;br /&gt;08.&lt;br /&gt;},&lt;br /&gt;09.&lt;br /&gt;finalize : function(){&lt;br /&gt;10.&lt;br /&gt;//finalize&lt;br /&gt;11.&lt;br /&gt;},&lt;br /&gt;12.&lt;br /&gt;config : {&lt;br /&gt;13.&lt;br /&gt;prop : "my value",&lt;br /&gt;14.&lt;br /&gt;constant : "42"&lt;br /&gt;15.&lt;br /&gt;}&lt;br /&gt;16.&lt;br /&gt;},&lt;br /&gt;17.&lt;br /&gt;mapping : {&lt;br /&gt;18.&lt;br /&gt;init     : function(){&lt;br /&gt;19.&lt;br /&gt;//create a map&lt;br /&gt;20.&lt;br /&gt;},&lt;br /&gt;21.&lt;br /&gt;geolocate    : function(){&lt;br /&gt;22.&lt;br /&gt;//geolocation is cool&lt;br /&gt;23.&lt;br /&gt;},&lt;br /&gt;24.&lt;br /&gt;geocode : function(){&lt;br /&gt;25.&lt;br /&gt;//look up an address or landmark&lt;br /&gt;26.&lt;br /&gt;},&lt;br /&gt;27.&lt;br /&gt;drawPolylines : function(){&lt;br /&gt;28.&lt;br /&gt;//draw some lines on a map&lt;br /&gt;29.&lt;br /&gt;},&lt;br /&gt;30.&lt;br /&gt;placeMarker : function(){&lt;br /&gt;31.&lt;br /&gt;//place markers on the map&lt;br /&gt;32.&lt;br /&gt;}&lt;br /&gt;33.&lt;br /&gt;}&lt;br /&gt;34.&lt;br /&gt;}&lt;br /&gt;Variables, ID &amp; Class◊&lt;br /&gt;&lt;br /&gt;All JavaScript variables shall be written in either completely lowercase letter or camelCase. The one exception to this are Constructor functions, which are capitalized by tradition. All id and class declarations in CSS shall be written in only lowercase. Neither dashes nor underscores shall be used.&lt;br /&gt;&lt;br /&gt;Event Delegation◊&lt;br /&gt;&lt;br /&gt;When assigning unobtrusive event listeners, it is typically acceptable to assign the event listener directly to the element(s) which will trigger some resulting action. However, occasionally there may be multiple elements which match the criteria for which you are checking, and attaching event listeners to each one might negatively impact performance. In such cases you should use event delegation instead.&lt;br /&gt;&lt;br /&gt;jQuery's delegate() is preferred over live() for performance reasons.&lt;br /&gt;&lt;br /&gt;Debugging◊&lt;br /&gt;&lt;br /&gt;Even with the best of validators, inevitably browser quirks will cause issues. There are several invaluable tools which will help to refine code integrity and loading speed. It is important that you have all of these tools available to you, despite the browser you primarily use for development. We recommend developing for Firefox and Safari first, then Google Chrome and Opera, with additional tweaks via conditional comments just for Internet Explorer. The following is a list of helpful debuggers and speed analyzers...&lt;br /&gt;&lt;br /&gt;Firefox: Firebug, Page Speed, YSlow&lt;br /&gt;Safari: Web Inspector&lt;br /&gt;Google Chrome: Developer Tools&lt;br /&gt;Opera: Dragonfly&lt;br /&gt;Internet Explorer 6-7: Developer Toolbar&lt;br /&gt;Internet Explorer 8-10: Developer Tools&lt;br /&gt;Patterns for better JavaScript◊&lt;br /&gt;&lt;br /&gt;Writing Maintainable Code&lt;br /&gt;Single var Pattern&lt;br /&gt;Hoisting: A Problem with Scattered vars&lt;br /&gt;(Not) Augmenting Built-in Prototypes&lt;br /&gt;Avoiding Implied Typecasting&lt;br /&gt;Avoiding eval()&lt;br /&gt;Number Conversions with parseInt()&lt;br /&gt;Opening Brace Location&lt;br /&gt;Capitalizing Constructors&lt;br /&gt;Writing Comments&lt;br /&gt;Avoid void&lt;br /&gt;Avoid with Statement&lt;br /&gt;Avoid continue Statement&lt;br /&gt;Avoid Bitwise Operators if possible&lt;br /&gt;Stoyan Stefanov covers these and more in detail here.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Accessibility◊Back to Top&lt;br /&gt;Section 508 Standards for intranet and internet information and applications.&lt;br /&gt;&lt;br /&gt;— Interfaces developed by Isobar should meet Section 508 standards.&lt;br /&gt;W3C checklist of checkpoints for accessibility.&lt;br /&gt;&lt;br /&gt;— Interfaces developed by Isobar should meet Priority 1 guidelines.&lt;br /&gt;— The WCAG 1.0 Guidelines.&lt;br /&gt;&lt;br /&gt;Performance◊Back to Top&lt;br /&gt;As we continue to push the limits of what the web can do, it remains just as important a web page can be used with minimal effort or wait time. The following section explains how web pages can be optimized to keep all audiences happy.&lt;br /&gt;&lt;br /&gt;Optimize Delivery of CSS and JavaScript◊&lt;br /&gt;&lt;br /&gt;There are many optimizations that should be done for serving CSS and javascript in Production:&lt;br /&gt;&lt;br /&gt;Follow the Yahoo Performance Guidelines&lt;br /&gt;Smush images using smush.it. Also using YSlow can autosmush all your images for you.&lt;br /&gt;Set caching headers appropriately.&lt;br /&gt;Consider a cookie-less subdomain for static assets&lt;br /&gt;Avoid inline &lt;script&gt; blocks.&lt;br /&gt;CSS should be located in the &lt;head&gt; of the document, Javascript should be right before the &lt;/body&gt; tag.&lt;br /&gt;Both CSS and JavaScript should be served minified. Most people use the YUI Compressor for this.&lt;br /&gt;Both should be served using gzip on the wire&lt;br /&gt;CSS should be concatenated together. Obviously this can only be done for files that share the same media type (e.g. screen or print). The general goal here is to reduce the number of HTTP connections to dependencies during the loading of the page.&lt;br /&gt;JavaScript should be concatenated. While a ajax script dependency manager would be ideal (similar to the YUI 3 Loader), it's rather complicated to implement. In its place I'd recommend a singular download of most of the script used on the site. (Of course, proper caching should be used to retain the file as long as is reasonable).&lt;br /&gt;Concatenation and minification typically occur during an automated build process, when packaging the code for deployment on stage or production. Many use tools like Ant or Maven for this.&lt;br /&gt;Avoid inline &lt;script&gt; blocks within the HTML. They block rendering and are quite devastating to page load time.&lt;br /&gt;Optimize JavaScript execution◊&lt;br /&gt;&lt;br /&gt;During a page load, there is typically a lot of script waiting to execute, but you can prioritize it. This order prioritizes based on user response:&lt;br /&gt;&lt;br /&gt;Script that changes the visible nature of the page needs to fire absolutely first. This includes any font adjustment, box layout, or IE6 pngfixes.&lt;br /&gt;Page content initialization&lt;br /&gt;Page header, topnav and footer initialization&lt;br /&gt;Attaching event handlers&lt;br /&gt;Omniture, Doubleclick, and other 3rd party scripts&lt;br /&gt;Leverage CSS Sprites◊&lt;br /&gt;&lt;br /&gt;CSS Sprites basically take a number of image assets and merge them together into a singular image file. Each part of it is revealed using CSS background-position. Some typical CSS would look like:&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;a.expandbox { display:block; width: 75px; height: 15px; text-indent: -999px; overflow:hidden;&lt;br /&gt;2.&lt;br /&gt;background: url(../img/sprite.png) no-repeat -50px -5px;  }&lt;br /&gt;It's quite common to leverage sprites for :hover states. In that case you'll see something like:&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;a.expandbox:hover { background-position: -50px -20px; }&lt;br /&gt;Using sprites reduces total page weight and reduces HTTP connections which speeds up page load. More on the general technique and overview at css-tricks.com&lt;br /&gt;&lt;br /&gt;Many developers use a vertically-oriented sprite in addition to the primary sprite. This vertical sprite will be &lt;=100px wide (and tall) and contain icons that are typically placed next to text, such as list item bullets. Yahoo uses a few such as this one.&lt;br /&gt;&lt;br /&gt;The one consideration is to not make sprites too large, something over 1000px in either direction will end up using a sizeable amount of memory. More detail about sprites and memory usage here.&lt;br /&gt;&lt;br /&gt;Some general tips and techniques in sprite creation can be found on the Mozilla Dev Blog.&lt;br /&gt;&lt;br /&gt;Image formats◊&lt;br /&gt;&lt;br /&gt;There are four main image formats that should be used, detailed here:&lt;br /&gt;&lt;br /&gt;JPEG. - This will cover all photography-based images, such as homepage and category page promo images, or anything with a very high color count.&lt;br /&gt;&lt;br /&gt;PNG24. This format, easily accessible in Photoshop, outputs high-color count imagery and fully supports graded opacity by pixel. Relatively, it's quite a heavy format as far as kilobyte weight. It is the only format that IE6 needs to execute a pngfix on. In that case, Isobar recommends the DD_belatedPNG script (A pngfix fixes the issue where PNG24's appear to have a gray or light-blue background in IE6. They are not always compatible with background-position).&lt;br /&gt;&lt;br /&gt;In many cases, you can use a GIF fallback for IE6, in place of a PNG24. This is especially true if any sprites need to be done in PNG24. All pngfixes are very slow and expensive, so it's best to avoid using them.&lt;br /&gt;&lt;br /&gt;PNG8. - A surprising diversity of color can be captured inside 256 colors, so it's worth trying PNG before heading JPG. PNG also is a lot more compressible than GIF (using tools like pngcrush and pngquant). This format allows graded opacity in nearly all browsers, but in IE6, those semi-opaque pixels are just shown 100% transparent. In many cases this is sufficient. It also does not require a pngfix script to be run, so it's optimized for speed.&lt;br /&gt;&lt;br /&gt;Photoshop cannot output these semi-opaque files correctly but Fireworks can. More detail here: http://www.sitepoint.com/blogs/2007/09/18/png8-the-clear-winner/&lt;br /&gt;&lt;br /&gt;Transparent GIF 89a. - GIF 89a offers the flexibility of transparency and wide browser support, but the constraints of no graded opacity nor a color depth above 256. In our experience, color depths of 64 still provide very good quality thumbnails, and keep the file size comparably very small.&lt;br /&gt;&lt;br /&gt;All low-color count imagery such as icons or thematic graphics should be done in PNG8, as it's the most size-efficient of these four. PNG8 is our primary recommendation for most site graphics.&lt;br /&gt;&lt;br /&gt;Detailed information on the PNG format, browser support, and the pros and cons of each can be found in this excellent article.&lt;br /&gt;&lt;br /&gt;For further optimization all of these formats, taking them through Yahoo's Smush.It will reveal how they can be smaller.&lt;br /&gt;&lt;br /&gt;Caching◊&lt;br /&gt;&lt;br /&gt;For static content, the browser should cache the file locally as long as is reasonable. Content that should have far future caching includes:&lt;br /&gt;&lt;br /&gt;CSS and JavaScript&lt;br /&gt;product images&lt;br /&gt;thematic graphics&lt;br /&gt;favicon.ico&lt;br /&gt;flash .swf's&lt;br /&gt;promo images (lighter caching likely)&lt;br /&gt;For the best caching, leverage the Expires http header. This is a far future Expires header, telling the browser that this response won't be stale until April 15, 2015.&lt;br /&gt;&lt;br /&gt;Expires: Thu, 15 Apr 2015 20:00:00 GMT&lt;br /&gt;If your server is Apache, use the ExpiresDefault directive to set an expiration date relative to the current date. This example of the ExpiresDefault directive sets the Expires date 1 year out from the time of the request.&lt;br /&gt;&lt;br /&gt;ExpiresDefault "access plus 1 year"&lt;br /&gt;Expires http header should be set to a value between one month from present to a year (far future) from present. Caching only applies to that exact URL, so a change in the filename of any asset will start a fresh copy. Many developers use a build process to add a version number or timestamp to their assets. Each subsequent build will start a brand new cached version, allowing you to use far future cache dates without worry. Google has a lot more detail on browser caching.&lt;br /&gt;&lt;br /&gt;Shard resources across domains◊&lt;br /&gt;&lt;br /&gt;Static content should certainly be served from a domain different than the one that serves HTML. This is optimal so that there are no extra cookies headers on all static content requests. It's also much easier to write caching rules for the entire domain. (Also any subdomains of the current domain will inherit domain cookies, so it's worth using a completely new domain).&lt;br /&gt;&lt;br /&gt;However with enough assets (especially images) the number of requests grows enough to slow down the load of the page. Many browsers have a low constraint of how many assets they will download simultaneously per domain. (In IE6 and 7, it's only two). In this case, we can serve the assets from multiple subdomains such as:&lt;br /&gt;&lt;br /&gt;static1.otherdomain.com&lt;br /&gt;static2.otherdomain.com&lt;br /&gt;static3.otherdomain.com&lt;br /&gt;More information on domain sharding on Google Speed&lt;br /&gt;&lt;br /&gt;Avoid IFRAMEs◊&lt;br /&gt;&lt;br /&gt;Iframes are the most costly element to add to a given page. They block the page from firing the onload event until they are complete. Sometimes they are useful to let another agency handle tracking scripts. For example the Doubleclick floodlight tag is an iframe, and the admin can add tracking pixels into it from their dashboard. In any case where an iframe is added, it should be appended to the DOM dynamically after window onload has fired. More detail at Yahoo's Performance site.&lt;br /&gt;&lt;br /&gt;3rd Party Integration◊&lt;br /&gt;&lt;br /&gt;Omniture◊&lt;br /&gt;We recommend to add the Omniture JavaScript code to the DOM using JavaScript after the page has loaded (either a DOM ready event or window's load event). Using this technique, there is no external domain script dependency, which can slow down (and potentially hang) a page load.&lt;br /&gt;&lt;br /&gt;Flash◊&lt;br /&gt;Backup HTML content should be in place of the flash in all cases to maximize SEO value. For XML-driven flash, the backup HTML content should be leveraging the exact same XML file, to ensure data consistency.&lt;br /&gt;&lt;br /&gt;All replacements should be done using SWFObject but without inline script tags. SWFObject initialization should fire after the DOM Ready event. Minimum player version should be set to minimum v9, to ensure AS3 compatibility.&lt;br /&gt;&lt;br /&gt;Cross-Browser Performance Strategy◊&lt;br /&gt;&lt;br /&gt;There are two major truths when it comes to in-browser experience:&lt;br /&gt;&lt;br /&gt;Everyone wants the most responsive experience possible.&lt;br /&gt;Everything added to the page slows it down.&lt;br /&gt;So with these two facts of life, what steps do we need to take so everyone is happy?&lt;br /&gt;&lt;br /&gt;Create success metrics with the client◊&lt;br /&gt;&lt;br /&gt;These should be customized to your client and project and done before the wireframing phase. These goals should be reasonable from a technical POV, as well as testable.&lt;br /&gt;&lt;br /&gt;Some goals that would be appropriate:&lt;br /&gt;The slowest browser supported must go from an empty cache to fully loaded and initialized within 5 seconds.&lt;br /&gt;Hover states (and other 'instant' changes) should respond within 100ms.&lt;br /&gt;Animations should appear smooth, with jumpiness occurring &lt; 15% of the time (across all browsers).&lt;br /&gt;For load-time based goals, it's important to define the benchmark system. Something like PageTest is a good option. Additionally, goals may be defined for multiple pages, for example: the two most popular landing pages (e.g. homepage and support).&lt;br /&gt;&lt;br /&gt;If the client has more aggressive goals than are reasonable with the intended design, expectations need to be set across the board, priming the team that performance goals are going to be paramount.&lt;br /&gt;&lt;br /&gt;Communicating the performance impact during design phase◊&lt;br /&gt;&lt;br /&gt;Internally&lt;br /&gt;During IA, IxD, and visual design, it is the front end engineer's role to communicate the performance impact of interactive features or certain visual techniques on the target browsers. Give the designers constraints: "If we're using Cufon, we cannot have more than 10 elements of custom font per page."&lt;br /&gt;&lt;br /&gt;Externally&lt;br /&gt;Expectations need to be set that all browsers will not have the same experience. They won't perform as well as each other, nor may it make sense to have feature parity. It may be sensible to drop a few features from the IE7 experience. Features that could be considered to be dropped are: shadows, transitions, rounded corners, opacity, gradients.&lt;br /&gt;&lt;br /&gt;When communicating the impact of something:&lt;br /&gt;Clarify the impact with as much detail as possible: "it will hurt page load" vs "it will add 2 seconds to page load in IE"&lt;br /&gt;Provide a quick POC (proof of concept) if it's reasonable: "This similar-looking page without siFR loads in 2 seconds, with siFR it loads in 8 and has a delay to show during scrolling"&lt;br /&gt;Develop according to best practices◊&lt;br /&gt;&lt;br /&gt;Choose libraries and plugins that are performance optimized. Make wise architecture decisions based on performance goals. Also minimize DOM manipulation when possible, and write styles to avoid visual changes to the page as it loads and initializes.&lt;br /&gt;&lt;br /&gt;Measure performance during QA◊&lt;br /&gt;&lt;br /&gt;QA teams should also prioritize performance related tickets alongside visual, functional, and usability issues. Developers and QA should determine how that priority will be assigned. During QA, the success metrics should be tested regularly.&lt;br /&gt;&lt;br /&gt;Tools to test with&lt;br /&gt;YSlow, Page Speed, Hammerhead, MSFast, PageTest&lt;br /&gt;When performance goals aren't met, there are three options:&lt;br /&gt;Redevelop the code - profile, discover bottlenecks, refactor code, optimize to target faster execution in the browser&lt;br /&gt;Drop the feature - turn it off for slower browsers only&lt;br /&gt;Redesign approach of the UI - perhaps the design could use a tweak which would bypass the issue entirely&lt;br /&gt;With this approach, we think all parties have a better chance of having aligned expectations heading in as well as a more sensible workflow in dealing with performance challenges.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Browser Testing and Support◊Back to Top&lt;br /&gt;Today's audience can choose from quite a large pool of web browsers, each providing a slightly (or dramatically) different experience. As developers, it is our responsibility to choose just how the web pages we build are displayed to those users. This section describes how we, at Isobar, make some of these key decisions.&lt;br /&gt;&lt;br /&gt;What we support◊&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Isobar supports any browser with an A-Grade in Yahoo's Graded Browser Support, with the exception of Opera. There may be exceptions to this, given regional markets and their particular metrics.&lt;br /&gt;&lt;br /&gt;We will strive to support any other mission critical browsers mandated by the client (IE 5.5, Opera, Konqueror, Safari 3 on PC, etc), though we cannot guarantee all functionality is possible.&lt;br /&gt;&lt;br /&gt;How we test◊&lt;br /&gt;&lt;br /&gt;Comprehensive browser testing is a must on every Isobar web project. Considerable effort must be made to test across browsers and platforms to ensure a quality and consistent user experience. Setting up a testing environment can be a challenge but can be well worth it.&lt;br /&gt;&lt;br /&gt;On Microsoft Windows◊&lt;br /&gt;IE Testing◊&lt;br /&gt;&lt;br /&gt;Since it's impossible to have more than a single copy of Microsoft Internet Explorer installed on a PC, testing for IE is a challenge. Fortunately, Microsoft has finally made available development versions of older Internet Explorer's available for downloading. These Virtual Hard Drives (VHD's) run stripped down versions of Microsoft Windows that expire (time out) over time. Setting them up again after a few months is typically required. Development copies of Microsoft Windows from your MSDN license (if available) may also be an option depending on what you have access to.&lt;br /&gt;&lt;br /&gt;Virtual PC - Virtual PC must be installed on your computer, and if you are on Windows 7, you must use "XP Mode".&lt;br /&gt;Microsoft Windows VPC Images - Several varieties are available of the virtual hard drive images. You may need to install several to have a comprehensive test suite, depending on your project.&lt;br /&gt;Additionally, other less effective IE Testing options (not typically recommended) include IETester, which is still better than Multiple_IE and IE7 standalone.&lt;br /&gt;&lt;br /&gt;Firefox◊&lt;br /&gt;&lt;br /&gt;Firefox 3.6+ should be installed natively as well - with version 3.0 available through a portable apps version.&lt;br /&gt;If you're up to it, Install Firefox 3, 3.5, and/or 3.6 side-by-side with FF4. The Firefox Profile Manager allows you to install to different directories and maintain different profiles for each.&lt;br /&gt;Safari for the PC◊&lt;br /&gt;&lt;br /&gt;Use the newest release of Safari for the PC. It is 98% consistent with Safari on OS X, but not completely, so test there if its a required platform.&lt;br /&gt;Opera◊&lt;br /&gt;&lt;br /&gt;You can download old versions from their archive. Install in different folders to run multiple versions&lt;br /&gt;Google Chrome and Chrome Versions◊&lt;br /&gt;&lt;br /&gt;Google Chrome updates itself and as luck would have it, most all users have the most recent version more often than not. If only every browser were this easy. No worries about old browsers based on Google Chrome.&lt;br /&gt;&lt;br /&gt;On Mac OS X◊&lt;br /&gt;For the core Mac OS X browsers, Mozilla Firefox, Google Chrome and Apple Safari offer virtually identical browser experiences to their Windows counterparts. That said, some OS level differences can be present and web sites should be tested on both platforms. Typical differences are around font rendering and thus spacing issues sometimes arise.&lt;br /&gt;&lt;br /&gt;Testing for Windows on a Mac◊&lt;br /&gt;&lt;br /&gt;There are a number of options for testing Windows-based browsers on the Mac OS. First, Mac computers offer a "boot camp" partition which allow you to boot the Mac into a Microsoft Windows partition. This is a complex yet thorough testing environment. Once you've booted to Windows, the normal Windows options exist.&lt;br /&gt;&lt;br /&gt;Other options include virtualization of Windows inside Mac OS X, which allows you to literally run Windows inside of the Mac OS.&lt;br /&gt;&lt;br /&gt;The Microsoft VHD's can be opened or converted on most of these options, therefore enabling the same degree of testing prowess that Windows users have available to them. Though since you can also test on the Mac simultaneously, some might argue you have more flexibility...&lt;br /&gt;&lt;br /&gt;Parallels - Parallels is available and may already be installed by the Isobar IT department on your Mac.&lt;br /&gt;VMWare Fusion - VMWare Fusion offers the same level of Windows virtualization through their Fusion product.&lt;br /&gt;Mozilla Firefox◊&lt;br /&gt;&lt;br /&gt;Just like on Windows, you can install and run multiple copies of Mozilla Firefox on a Mac, though it is a bit trickier to set up multiple profiles via the Profile Manager. That said, there are some tricks you can use with Automator to make separate profiles and run them nicely.&lt;br /&gt;&lt;br /&gt;Bugs in standalone IE versions◊&lt;br /&gt;&lt;br /&gt;Note: The IE6 standalone has a buggy implementation of opacity in some cases. This will result in any opacity applied with a CSS filter, like alpha opacity or a 24-bit PNG, to fail. In the case that opacity must be tested, you will need a native IE6 installation.&lt;br /&gt;&lt;br /&gt;It has been noticed that IE 7 using the Vista platform does have differences from IE 7 on Windows XP, therefore, you might want to make sure that someone on your team has this configuration as well. IETester is known to fix a number of these issues, as do the Xenocode browsers.&lt;br /&gt;&lt;br /&gt;Browser Resolution◊&lt;br /&gt;&lt;br /&gt;Along with catering to browsers, developers must stay conscious of the screen resolutions of their audience. As monitor screens get larger, the breadth of resolutions grows as well. Below are some guidelines to help you in working with resolutions.&lt;br /&gt;&lt;br /&gt;1024px resolution&lt;br /&gt;&lt;br /&gt;Fold estimated at 570px.&lt;br /&gt;Optimal width: 960px - Has comfortable padding on both sides, is divisible by many numbers, and also plays well with IAB ad standard widths&lt;br /&gt;Larger width: 970px - Still has some padding on both sides in most browsers. The math plays well with the Golden Ratio&lt;br /&gt;Maximum width: 996px - Without incurring any horizontal scrollbars in the major browsers. Based on the research here the maximum is 1003 px wide if you don't want a horizontal scrollbar.&lt;br /&gt;Current stats on window sizes&lt;br /&gt;&lt;br /&gt;Authentic Boredom - Optimal width for 1024px resolution?&lt;br /&gt;Market share for browsers, operating systems and search engines&lt;br /&gt;Global Web Stats&lt;br /&gt;System resolution is not, however, the same as browser size&lt;br /&gt;&lt;br /&gt;Browser size does matter - Actual numbers | mentalized&lt;br /&gt;What size do i need to support | baekdal&lt;br /&gt;Poll results: 50.4% of respondents maximise windows&lt;br /&gt;Screen Resolution and Page Layout&lt;br /&gt;&lt;br /&gt;Search Engine Optimization◊Back to Top&lt;br /&gt;An essential part of good web design and development is SEO. Well-structured code is the key to ensuring that a web page not only gets properly indexed by search engines, but made accessible to those with limited web capabilities as well.&lt;br /&gt;&lt;br /&gt;Be aware of SEO best practices◊&lt;br /&gt;Print CSS best practices.&lt;br /&gt;Site/app will fit according to Browser Resolution guidelines.&lt;br /&gt;Site/app will be compatible with browser requirements described in Browser Testing and Support.&lt;br /&gt;Be aware of Accessibility best practices, such as the 508 and WCAG standards:&lt;br /&gt;http://www.section508.gov&lt;br /&gt;http://www.w3.org/TR/WCAG20/.&lt;br /&gt;Indexability◊&lt;br /&gt;We must use semantic markup that's readable and logical when JavaScript and CSS are off. All page content must be in HTML; we don't want to use iframes or JavaScript for loading initial indexable content.&lt;br /&gt;&lt;br /&gt;All links should be to HTML destinations.&lt;br /&gt;1.&lt;br /&gt;&lt;a href="/shelf/jordan/page/2"&gt;&lt;br /&gt;Instead of&lt;br /&gt;1.&lt;br /&gt;&lt;a href="javascript:loadPage(2);"&gt;&lt;br /&gt;This lets the page get indexed correctly by search engines as well as allows users to open in new tabs and windows.&lt;br /&gt;&lt;br /&gt;Optimization◊&lt;br /&gt;The title tag should feature target keywords for the unique page. The titles should be unique to each page. Headings (h1,h2,etc) should form an outline of the document and represent the most important keywords for that page. URLs should be human-readable with primary target keywords present in them:&lt;br /&gt;&lt;br /&gt;http://domain.com/mens-shoes/basketball/jordan/jordan-mens-ajf-6-basketball-shoe/&lt;br /&gt;vs&lt;br /&gt;&lt;br /&gt;http:// domain.com/ecomm.cfm?view=prod&amp;prodId=23425&lt;br /&gt;Flash and Image Replacement◊&lt;br /&gt;Always use backup HTML content for flash. All promo images should use CSS-based image replacement instead of alt tags.&lt;br /&gt;&lt;br /&gt;&gt;Fallback non-flash version:&lt;br /&gt;1.&lt;br /&gt;&lt;a href="/nike/morethanagame/" id="morethan"&gt;&lt;br /&gt;2.&lt;br /&gt;&lt;h4&gt;Nike: More Than A Game&lt;/h4&gt;&lt;br /&gt;3.&lt;br /&gt;&lt;h5&gt;Experience the movement and view apparel&lt;/h5&gt;&lt;br /&gt;4.&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;1.&lt;br /&gt;a#more than { background:url(/promos/nikegame.jpg) no-repeat; width: 200px; height: 100px;&lt;br /&gt;2.&lt;br /&gt;text-indent: -999px; overflow:hidden; display:block; }&lt;br /&gt;Google's SEO Report Card◊&lt;br /&gt;&lt;br /&gt;Google's SEO Report Card is an effort to provide Google's product teams with ideas. on how they can improve their products' pages using simple and accepted optimizations. These optimizations are intended to not only help search engines understand the content of their pages better, but also to improve their our users' experience.&lt;br /&gt;&lt;br /&gt;when visiting their sites. Simple steps such as fixing 404s and broken links, simplifying URL choice, and providing easier-to-understand titles and snippets for their pages can benefit both users and search engines.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Code Reviews◊Back to Top&lt;br /&gt;A code review is the cornerstone of the formal process for ensuring the quality of user experiences developed by the Creative Technology team. This involves a meeting among authors of markup, reviewers and other stakeholders, with expected input and subsequent code revisions. Simply put, we encourage conducting code reviews to keep our tools sharp and clean.&lt;br /&gt;&lt;br /&gt;Why should I have a code review?&lt;br /&gt;Code reviews are a strategic investment of time to mitigate risk on a project.&lt;br /&gt;&lt;br /&gt;Often times, interface developers are asked to author markup from wire frames or visual compositions. However, its possible that screens that are designed cannot be translated to markup easily, or without compromising quality. Code reviews provide an opportunity for this risk to be addressed and resolved before full production of pages begins.&lt;br /&gt;&lt;br /&gt;Code reviews increase the overall level of knowledge across projects&lt;br /&gt;Since code reviews involve members from within and outside a project team, techniques and best practices are easily shared throughout the entire Creative Technology team.&lt;br /&gt;&lt;br /&gt;Code reviews eliminate bugs before they are reproduced from a few templates into multiple pages&lt;br /&gt;Ideally, code reviews are conducted early in the development process, before full production of pages begins. When templates are reviewed by the team and run through multiple validation tools and browsers, bugs can and will appear. This is the ideal time for bugs to be fixed.&lt;br /&gt;&lt;br /&gt;Code reviews give a set of fresh eyes an opportunity to spot issues with code&lt;br /&gt;Reviewers from outside a project team can spot issues with code more easily than authors of markup, who have been working with code for a longer amount of time.&lt;br /&gt;&lt;br /&gt;Who should participate in a code review?&lt;br /&gt;Ultimately, the front end engineering Lead on a project is required to ensure that proper code review procedures are followed.&lt;br /&gt;&lt;br /&gt;Ideally, a practice lead should act as facilitator for a code review session, unless the practice lead is also the interface developer whose code is under review. In this situation, a project manager can be brought in as a facilitator.&lt;br /&gt;&lt;br /&gt;The review team should include at least two senior members of the Interface Technology team versed in development and best practices.&lt;br /&gt;&lt;br /&gt;What is required for a code review?&lt;br /&gt;Before a code review is conducted, templates to be reviewed must be fully developed, validated, and tested against the browsers and platforms required by the project.&lt;br /&gt;&lt;br /&gt;A practice lead and/or interface developer must distribute the following 48 hours prior to the date of the code review:&lt;br /&gt;&lt;br /&gt;Code for all pages, associated server-side includes, CSS, and JavaScript. These should be fully commented, printed out with line numbers along the left side, and the file/page name in the footer of each printed page.&lt;br /&gt;Screenshots of each template&lt;br /&gt;URLs of the templates, if applicable&lt;br /&gt;A list of browsers and platforms supported by the project&lt;br /&gt;A list of known issues/areas of concern&lt;br /&gt;It is typical for code to be changed constantly until the code review is to take place. Unfortunately, this does not leave enough time for validation and testing. If this is the case, it is recommended that the code review be rescheduled to a date that ensures a proper code review.&lt;br /&gt;&lt;br /&gt;In addition, a practice lead and/or interface developer should book a conference room and call-in number for all attendees, since it is possible that members of either the project or code review team are off-site. An hour should provide enough time for review of two or three templates; however, time will vary depending on the volume and complexity of the templates.&lt;br /&gt;&lt;br /&gt;During the code review, a practice lead and/or interface developer should facilitate the meeting, while the practice lead or project manager takes notes and assigns action items. Reviewers should have reviewed the code and come prepared to ask questions or provide feedback.&lt;br /&gt;&lt;br /&gt;Notes and action items (including owners) should be distributed to all attendees after the code review. If substantial changes come out of a code review, or all code is not reviewed, it may be necessary to schedule a second code review. However, this should be discussed amongst the project team to determine feasibility.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Appendices◊Back to Top&lt;br /&gt;Here are the references, tools and other things that relate to this document.&lt;br /&gt;&lt;br /&gt;Appendix A: Validators◊&lt;br /&gt;&lt;br /&gt;W3C CSS Validation Service&lt;br /&gt;HTML Validation firefox extension&lt;br /&gt;CSS validator&lt;br /&gt;Accessibility - Bobby Validation Service&lt;br /&gt;Tests individual pages for accessibility against either the W3C or Section 508 standards.&lt;br /&gt;Accessibility - Cynthia Says Portal&lt;br /&gt;Similar to Bobby, tests individual pages for accessibility against either the W3C or Section 508 standards.&lt;br /&gt;Appendix B: Tools◊&lt;br /&gt;&lt;br /&gt;BrowserCam - Displays how a web site will appear in most browsers (including versions) and operating systems.&lt;br /&gt;Accessibility - Evaluation, Repair, and Transformation Tools for Web Content Accessibility - lists validation tools for validating, testing and fixing web content to make it more accessible.&lt;br /&gt;Code Editors◊&lt;br /&gt;&lt;br /&gt;A great code editor can spark productivity in exceptional ways. Many developers prefer rudimentary text editors, others prefer powerful integraded development environments (IDEs). What follows is a general listing of some of the more well-known tools, it would be impossible to list them all.&lt;br /&gt;&lt;br /&gt;Aptana&lt;br /&gt;Aptana Studio is a powerful, free and open source Ajax development environment which runs stand-alone or within Eclipse. Aptana Studio offers tooling for Ajax including HTML, CSS, DOM, and JavaScript editing and debugging, plus support via additional free plugins for PHP, Ruby on Rails, Adobe AIR, Apple iPhone development. It also features full SVN repository integration for committing, branching, tagging, merging and repository browsing. Aptana. [Linux, Mac, Windows]&lt;br /&gt;Geany&lt;br /&gt;Geany is a text editor using the GTK2 toolkit with basic features of an integrated development environment. It was developed to provide a small and fast IDE, which has only a few dependencies from other packages. It supports many filetypes and has some nice features. Geany. [Linux, Mac, Windows]&lt;br /&gt;Notepad ++&lt;br /&gt;Notepad++ is a free (free as in "free speech", but also as in "free beer") source code editor and Notepad replacement, which supports several programming languages, running under the MS Windows environment. Notepad ++. [Windows]&lt;br /&gt;e TextEditor&lt;br /&gt;E is a new text editor for Windows, with powerful editing features and quite a few unique abilities. It makes manipulating text fast and easy, and lets you focus on your writing by automating all the manual work. You can extend it in any language, and by supporting TextMate bundles, it allows you to tap into a huge and active community. e TextEditor. [Windows]&lt;br /&gt;Edit Plus&lt;br /&gt;EditPlus is a text editor, HTML editor and programmers editor for Windows. While it can serve as a good Notepad replacement, it also offers many powerful features for Web page authors and programmers. EditPlus. [Windows]&lt;br /&gt;Homesite&lt;br /&gt;HomeSite 5.5 provides a lean, code-only editor for web development. Advanced coding features enable you to instantly create and modify HTML, CFML, JSP, and XHTML tags, while enhanced productivity tools allow you to validate, reuse, navigate, and format code more easily. Configure Adobe (formerly Macromedia) HomeSite to fit your needs by extending its functionality and customizing the interface. Homesite. [Windows]&lt;br /&gt;TextMate&lt;br /&gt;TextMate claims to be the "Missing Editor" for Mac OS X. A general purpose editor with a sparse interface, the real power is in it's extensibility. Features column selections, recordable macros, snippets, auto-pairing of brackets and other characters, clipboard history, code folding, tab-triggers, tabbed placeholders and mirror typing. And that's just for starters. Anything that can be done via scripts through the Mac command line can be done through custom commands, allowing an extremely high degree of customization and expansion of the feature set. TextMate's "bundle" format has been adapted by many other code editors including the aforementioned e TextEditor. TextMate. [Mac]&lt;br /&gt;Espresso&lt;br /&gt;Espresso was created by the same fellow that created the innovative CSSEdit CSS editor. Espresso features syntax highlighting, code folding, code completion, document outliner/navigators, projects, powerful find features, and built-in file transfer publishing capabilities. Finally, it has a powerful "Sugar" feature set which allows the creation of custom commands and plugins. Espresso. [Mac]&lt;br /&gt;BBEdit&lt;br /&gt;BBEdit is the grand-daddy of Mac code editors for web development. Features syntax highlighting, exceptionally powerful text manipulation tools, multi-file searches, a scriptable API, text clippings, and extensive Mac Automator features. BBEdit. [Mac]&lt;br /&gt;TextWrangler&lt;br /&gt;The free "little brother" of BBEdit, it is a powerful raw text editor with a massive text manipulation feature set. Searches, regular expressions, text transformations, syntax highlighting and code navigation tools for a variety of different language environments. TextWrangler. [Mac]&lt;br /&gt;Coda&lt;br /&gt;Coda from Panic software is a powerful IDE with code editing, terminal, remote file management, and help documentation all built into one UI. Aiming to be a one stop shop for your web development workflow, it also features SVN integration and a new plug-in builder with powerful scripting support and TextMate bundle importing. Finally, code clips and live multi-user editing are also supported. Coda. [Mac]&lt;br /&gt;UltraEdit&lt;br /&gt;Another editor that's been around for ages, this is an immensely robust and powerful text editor, able to open files limited only by the amount of memory on your computer. The feature list is virtually too much to list, with a massive list of text manipulation features, project support, powerful search and replace, hex editing, function lists, a massive list of languages supported (600+) remote file ftp, telnet, ssh, file comparison, scriptable macros, tools and compiler support, and much, much more. UltraEdit. [Linux, Mac, Windows]&lt;br /&gt;Sublime Text&lt;br /&gt;A relatively new editor, Sublime Text is a new approach in editors. "Open Anything" searches through file names and file contents, with remarkable efficiency. Incredibly powerful selection controls allow editing text in multiple locations at once and the "Minimap" gives you a bird's eye view of the open file so you can find your place easily. Actively being developed, new features are being added and the community around the editor is rapidly expanding. Macros, auto-complete, snippets, build tools, the list of features goes on and on. Supports Linux and Mac starting with version 2. Sublime Text. [Linux, Mac, Windows]&lt;br /&gt;Vim&lt;br /&gt;If you have to ask, it's probably not for you. Vim [Linux, Mac, Windows]&lt;br /&gt;Google Chrome Extensions◊&lt;br /&gt;&lt;br /&gt;Developer Tools&lt;br /&gt;Not actually an extension for Chrome, but built right in (shares much with Safari's Web Inspector, both being derived from WebKit.) This suite of tools features a DOM inspector, basic JavaScript debugger, profiling tools, network loading inspector and timelines, page resources inspectors, and more. Developer Tools.&lt;br /&gt;code cola&lt;br /&gt;A pop-up panel with CSS editing tools for examining and modifying the styles on a given page. code cola.&lt;br /&gt;Firebug Lite for Google Chrome&lt;br /&gt;You really don't need to install an extension to use Firebug Lite with Chrome, though the extension is nice because it enables one-click application of the Firebug Lite script to any page you are working with. Not the full Firebug feature set, but close. Firebug Lite.&lt;br /&gt;HTML5 Outliner&lt;br /&gt;The HTML5 Outliner adds a pop-up with a generated HTML5 outline of the current page's header hierarchy. Helps for checking your pages' organization against the new HTML5 header outlining algorithms. HTML5 Outliner.&lt;br /&gt;Pendule&lt;br /&gt;Nice set of tools for showing data about and interacting with the current web page. Very similar to the original web developer toolbar for Firefox. Pendule.&lt;br /&gt;Web Developer for Chrome&lt;br /&gt;Chris Pederick, the original developer of the original Web Developer toolbar for Firefox has ported the majority of it over to Chrome. There you have it. Web Developer.&lt;br /&gt;Firefox Plugins◊&lt;br /&gt;&lt;br /&gt;FireFTP&lt;br /&gt;FireFTP is a free, secure, cross-platform FTP client for Mozilla Firefox which provides easy and intuitive access to FTP servers. FireFTP.&lt;br /&gt;Firebug&lt;br /&gt;Firebug integrates with Firefox to put a wealth of development tools at your fingertips while you browse. You can edit, debug, and monitor CSS, HTML, and JavaScript live in any web page. Firebug.&lt;br /&gt;Firequery&lt;br /&gt;FireQuery is a collection of Firebug enhancements for jQuery integrated into Firebug. Firequery.&lt;br /&gt;Firecookie&lt;br /&gt;Firecookie adds cookie viewing, editing, and deletion to Firebug. Firecookie.&lt;br /&gt;CSS Usage&lt;br /&gt;CSS Coverage is an extension for Firebug which allows you to scan multiple pages of your site to see which CSS rules are actually used in your site. CSS Usage.&lt;br /&gt;Greasemonkey&lt;br /&gt;Allows you to customize the way a web page displays using small bits of JavaScript. GreaseMonkey.&lt;br /&gt;Web Developer Toolbar&lt;br /&gt;Adds a menu and a toolbar with various web developer tools. Web Developer Toolbar.&lt;br /&gt;JSView&lt;br /&gt;Adds an item in the status bar that displays all external JavaScript and CSS files loaded on a given page. Allows you to click on and view the files and things like their URLs. Great way to pull file URLs to put into Charles for remote debugging. JSView.&lt;br /&gt;Live HTTP headers&lt;br /&gt;When running, captures all HTTP traffic from the browser, which enables you to see what files are being requested as well as information about the requests and server responses. Live HTTP Headers.&lt;br /&gt;Quick Locale Switcher&lt;br /&gt;A tremendous help when doing internationalization, Quick Locale Switcher allows you to change the locale sent along in the browser's user-agent HTTP header, telling servers to display content for you in other locales. Quick Locale Switcher.&lt;br /&gt;Screengrab&lt;br /&gt;Screengrab sits in the Firefox status bar, allowing you to capture and copy or save screen shots of everything from selections of a web page to the entire page, even parts displayed "below the fold." Screengrab.&lt;br /&gt;Total Validator&lt;br /&gt;Enables one-click access to sending your page through a markup validator. No better way to quickly check for missing or mismatched tags! Also available as a standalone application. Total Validator.&lt;br /&gt;Opera Extensions◊&lt;br /&gt;&lt;br /&gt;Dragonfly is Opera's developer tool similar to Firebug.&lt;br /&gt;&lt;br /&gt;IE Plugins◊&lt;br /&gt;&lt;br /&gt;CompanionJS, DebugBar, IE8 Dev tools.&lt;br /&gt;&lt;br /&gt;Charles Proxy◊&lt;br /&gt;&lt;br /&gt;Charles watches all requests and can tell you a lot of information about them. Also supremely useful is Map Local which lets you use a local file in place of a given URL (good for replacing a minified js with a full one).&lt;br /&gt;&lt;br /&gt;Fiddler◊&lt;br /&gt;&lt;br /&gt;From the site: "Fiddler is a Web Debugging Proxy which logs all HTTP(S) traffic between your computer and the Internet. Fiddler allows you to inspect traffic, set breakpoints, and "fiddle" with incoming or outgoing data. Fiddler includes a powerful event-based scripting subsystem, and can be extended using any .NET language."&lt;br /&gt;&lt;br /&gt;Speedlimit◊&lt;br /&gt;&lt;br /&gt;Speedlimit is a Leopard (works in snow leopard) preference pane for limiting your network bandwidth to one of a couple different speeds—768k DSL, Edge, 3G, and Dialup. Good for testing your lowest supported speeds or when you want to know how your app will function in real world speeds.&lt;br /&gt;&lt;br /&gt;Tutorials &amp; Tools:◊&lt;br /&gt;&lt;br /&gt;CSS Cheatsheet&lt;br /&gt;CSS Links&lt;br /&gt;Clean CSS&lt;br /&gt;Icons◊&lt;br /&gt;&lt;br /&gt;Famfamfam silk icons&lt;br /&gt;Sweetie&lt;br /&gt;Paul's bookmarks for more icons&lt;br /&gt;Fugue Icons - 3,100 icons in PNG format.&lt;br /&gt;Appendix C: Resources◊&lt;br /&gt;&lt;br /&gt;ECMA 262 5th Edition December 2009&lt;br /&gt;Opera's The Web Standards Curriculum - basic articles about building with web standards.&lt;br /&gt;Google Doctype - more advanced tutorials on javascript and CSS.&lt;br /&gt;10 Principles of the CSS Masters - all of this is smart advice.&lt;br /&gt;Google - when in doubt, google it.&lt;br /&gt;Too lazy? - let me google that for you then...&lt;br /&gt;Yahoo's Exceptional Performance team has maintained one of the best summaries of performance advice.&lt;br /&gt;Google now has a Speed site with excellent detail. (Click All best practices).&lt;br /&gt;There are many more excellent resources on page optimization and javascript performance.&lt;br /&gt;Mozilla's coding standards.&lt;br /&gt;Nokia's JavaScript Performance Best Practices.&lt;br /&gt;&lt;br /&gt;reference : http://na.isobar.com/standards/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-8869830864679092596?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/8869830864679092596/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=8869830864679092596' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8869830864679092596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8869830864679092596'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/front-end-code-standards-best-practices.html' title='Front-end Code Standards &amp; Best Practices'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-4183892206646380419</id><published>2011-10-21T11:36:00.000-07:00</published><updated>2011-10-21T11:51:39.112-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IntelliJ IDEA'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>IntelliJ IDea Git repository</title><content type='html'>设置SSH&lt;br /&gt;&lt;br /&gt;github使用SSH链接，需要设置SSH&lt;br /&gt;&lt;br /&gt;1.检查SSH key&lt;br /&gt;&lt;br /&gt;     cd ~/.ssh&lt;br /&gt;&lt;br /&gt;2.备份已有的key，（如果有的话）&lt;br /&gt;&lt;br /&gt;     mkdir key_backup&lt;br /&gt;&lt;br /&gt;     mv id_rsa* key_backup&lt;br /&gt;&lt;br /&gt;3.生成SSH key&lt;br /&gt;&lt;br /&gt;$ ssh-keygen -t rsa -C jiang.bo.hit@gmail.com&lt;br /&gt;&lt;br /&gt;Generating public/private rsa key pair.&lt;br /&gt;&lt;br /&gt;Enter file in which to save the key (/Users/jiangbo/.ssh/id_rsa):&lt;br /&gt;&lt;br /&gt;Enter passphrase (empty for no passphrase):&lt;br /&gt;&lt;br /&gt;Enter same passphrase again:&lt;br /&gt;&lt;br /&gt;Your identification has been saved in yes.&lt;br /&gt;&lt;br /&gt;Your public key has been saved in id_rsa.pub.&lt;br /&gt;&lt;br /&gt;The key fingerprint is:&lt;br /&gt;&lt;br /&gt;fb:c4:b0:e0:47:fd:be:e0:fb:ea:73:ef:a8:29:d5:22 jiang.bo.hit@gmail.com&lt;br /&gt;&lt;br /&gt;The key's randomart image is:&lt;br /&gt;&lt;br /&gt;+--[ RSA 2048]----+&lt;br /&gt;&lt;br /&gt;|                 |&lt;br /&gt;&lt;br /&gt;|                 |&lt;br /&gt;&lt;br /&gt;|                 |&lt;br /&gt;&lt;br /&gt;|         .       |&lt;br /&gt;&lt;br /&gt;|      . S ..     |&lt;br /&gt;&lt;br /&gt;|     . oE=o..    |&lt;br /&gt;&lt;br /&gt;|      . +o+..    |&lt;br /&gt;&lt;br /&gt;|       ..+.+..   |&lt;br /&gt;&lt;br /&gt;|         oOB=+o  |&lt;br /&gt;&lt;br /&gt;+-----------------+&lt;br /&gt;&lt;br /&gt;4.将SSH key添加到GitHub&lt;br /&gt;&lt;br /&gt;登录到GitHub页面，Account Settings-&gt;SSH Public Keys-&gt;Add another key&lt;br /&gt;&lt;br /&gt;将生成的key(id_rsa.pub文件）内容copy到输入框中，save。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;5.测试链接&lt;br /&gt;&lt;br /&gt;$ ssh git@github.com&lt;br /&gt;&lt;br /&gt;The authenticity of host 'github.com (207.97.227.239)' can't be established.&lt;br /&gt;&lt;br /&gt;RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.&lt;br /&gt;&lt;br /&gt;Are you sure you want to continue connecting (yes/no)? yes&lt;br /&gt;&lt;br /&gt;PTY allocation request failed on channel 0&lt;br /&gt;&lt;br /&gt;Hi jiang-bo! You've successfully authenticated, but GitHub does not provide shell access.&lt;br /&gt;&lt;br /&gt;         Connection to github.com closed.&lt;br /&gt;&lt;br /&gt;别担心，这是正常情况。&lt;br /&gt;&lt;br /&gt;三、设置个人信息&lt;br /&gt;&lt;br /&gt;$ git config --global user.name "Bo Jiang"&lt;br /&gt;&lt;br /&gt;$ git config --global user.email "jiang.bo.hit@gmail.com"&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Setting of IDEA&lt;/span&gt;&lt;br /&gt;Step1: IDEA -&gt; Version Control -&gt; Checkout from Version Control -&gt; Git&lt;br /&gt;Step2: Git Repository URL : git@github.com/xxx.git&lt;br /&gt;Step3: Enter passphrase for the private key&lt;br /&gt;Step4: Create a new project -&gt; Create project from Scratch -&gt; next -&gt; ok&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-4183892206646380419?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/4183892206646380419/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=4183892206646380419' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/4183892206646380419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/4183892206646380419'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/intellij-idea-git-repository.html' title='IntelliJ IDea Git repository'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7719286087531302425</id><published>2011-10-21T06:29:00.000-07:00</published><updated>2011-10-21T06:30:15.464-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git flow 開發流程</title><content type='html'>大家都知道 Git 開 branch 很方便，非常鼓勵 topic branch，但有沒有一套模型流程告訴我們應該怎麼管理 branch 呢? 有人便整理出一套最佳實踐慣例 A successful Git branching model，我們團隊就採用了這套流程。簡單來說，他將 branch 分成兩個主要分支，三種支援性分支：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;主要分支&lt;br /&gt;master: 永遠處在 production-ready 狀態&lt;br /&gt;develop: 最新的下次發佈開發狀態&lt;br /&gt;支援性分支&lt;br /&gt;Feature branches: 開發新功能都從 develop 分支出來，完成後 merge 回 develop&lt;br /&gt;Release branches: 準備要 release 的版本，只修 bugs。從 develop 分支出來，完成後 merge 回 master 和 develop&lt;br /&gt;Hotfix branches: 等不及 release 版本就必須馬上修 master 趕上線的情況。會從 master 分支出來，完成後 merge 回 master 和 develop&lt;br /&gt;作者還提供了 git-flow 指令工具幫助我們很容易的實踐，用法如下:&lt;br /&gt;&lt;br /&gt;首先是初始化動作：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    git flow init&lt;br /&gt;&lt;br /&gt;初始化動作會問你一些問題，大抵是命名慣例：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;No branches exist yet. Base branches must be created now.&lt;br /&gt;Branch name for production releases: [master]&lt;br /&gt;Branch name for "next release" development: [develop]&lt;br /&gt;How to name your supporting branch prefixes?&lt;br /&gt;Feature branches? [feature/]&lt;br /&gt;Release branches? [release/]&lt;br /&gt;Hotfix branches? [hotfix/]&lt;br /&gt;Support branches? [support/]&lt;br /&gt;Version tag prefix? []&lt;br /&gt;&lt;br /&gt;設定完之後，預設的 branch 就變成 develop 了。有任何開發，一律都先開 branch：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;git flow feature start some_awesome_feature&lt;br /&gt;(以此類推 git flow release 和 git flow hotfix)&lt;br /&gt;&lt;br /&gt;完成之後輸入&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;git flow feature finish some_awesome_feature&lt;br /&gt;&lt;br /&gt;就會合併回 develop 並幫你刪除這個 (local) branch。&lt;br /&gt;&lt;br /&gt;關於 REMOTE BRANCH&lt;br /&gt;這個 git-flow 工具並沒有幫我們處理 remote branch，所以如果你的 branch 要 push 出去分享給別人，就要自己打 git 指令啦 同事留言說有支援啦：&lt;br /&gt;&lt;br /&gt;push 一個 feature branch 到遠端：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;git flow feature publish some_awesome_feature&lt;br /&gt;或 git push origin feature/some_awesome_feature&lt;br /&gt;&lt;br /&gt;追蹤一個遠端的 branch：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;git flow feature track some_awesome_feature&lt;br /&gt;或 git checkout -b feature/some_awesome_feature -t origin/feature/some_awesome_feature&lt;br /&gt;&lt;br /&gt;刪除遠端的 branch：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;git push origin :feature/some_awesome_feature&lt;br /&gt;&lt;br /&gt;我們還碰到一個問題是輸入 git flow feature finish 時出現以下錯誤：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;warning: not deleting branch 'feature/some_awesome_feature' that is not yet merged to&lt;br /&gt;         'refs/remotes/origin/feature/some_awesome_feature', even though it is merged to HEAD.&lt;br /&gt;error: The branch 'feature/some_awesome_feature' is not fully merged.&lt;br /&gt;If you are sure you want to delete it, run 'git branch -D feature/some_awesome_feature'.&lt;br /&gt;&lt;br /&gt;原因是這個 feature branch 一開始是從遠端 checkout 出來的，以及這個 feature branch 有 commit 沒有 push 回去 ，所以 git flow 不敢幫你刪除 local branch，這時候其實 merge 動作已經完成了，所以你可以手動輸入 git branch -D feature/some_awesome_feature 強制刪除 local branch 即可。(小結論：git-flow 只是個輔助工具，了解 git 還是必要的)&lt;br /&gt;&lt;br /&gt;關於 FEATURE BRANCH 的合併&lt;br /&gt;如果是開發時間比較久的 feature branch，很可能會因為 1. 不定時的 merge develop 與新版同步 2. 實驗性質的修改 3. 需求的變更 等等因素，而讓這個 feature branch 的 commit 記錄變成髒髒的，這時候我們會用以下的方式來做 merge 動作：&lt;br /&gt;&lt;br /&gt;1. 先對 feature branch 做 `git rebase develop`。會很苦，但是弄完會很有成就感，整個 branch commit history 會變成很乾淨。請學 interactive mode，可以讓你拿掉一些 commit、合併或修改，你也可以 rebase 多次直到滿意為止。&lt;br /&gt;2. 在從 develop bracnh 做 `git merge feature/some_awesome_feature –no-ff`，–no-ff 的意思是會強制留一個 merge commit log 記錄，這可以讓 commit tree 看清楚發生了 merge 動作。(因為我們剛做了 rebase，而 git 預設的合併模式是 fast-forward，所以如果不加 –no-ff 是不會有 merge commit 的) 這個 merge commit 的另一個額外方便之處是，如果想要 reset/revert 整個 branch 只要 reset/revert 這個 commit 就可以了。&lt;br /&gt;3. 如果此 feature branch 有 remote branch，要先砍掉 `git push origin :feature/some_awesome_feature` 再 `git push origin develop` (這是因為 rebase 一個已經 push 出去的 repository，然後又把修改的 history push 出去，會造成超級大災難啊~)&lt;br /&gt;&lt;br /&gt;先 rebase 再 merge –no-ff 這樣做的好處到底是什麼? 看圖體會一下吧：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;每一次的 merge 就代表了一個 feature 完成，也可以很清楚看到這個 feature branch 底下包含哪些 commit。&lt;br /&gt;&lt;br /&gt;對了，如果有用 Github 的話，請記得務必用一用它的 pull request 功能，我們會在 branch 完成後發一個 pull request，好讓大家可以對一整個 branch 做 code review 留言。&lt;br /&gt;&lt;br /&gt;註：什麼是 rebase 可以參考舊作: Git 版本控制系統(3) 還沒 push 前可以做的事&lt;br /&gt;&lt;br /&gt;reference : http://ihower.tw/blog/archives/5140&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-7719286087531302425?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/7719286087531302425/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=7719286087531302425' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7719286087531302425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7719286087531302425'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/git-flow.html' title='Git flow 開發流程'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5627917124664808371</id><published>2011-10-20T20:39:00.000-07:00</published><updated>2011-10-20T20:39:54.955-07:00</updated><title type='text'>只有指令碼的BROWSER ﹣ PhantomJS</title><content type='html'>這是一個没有GUI的“Browser”，它是使用Webkit來處理HTML碼，只要利用JS寫好指令，就可以做到很多不同的功能。例如你可以用來測試你的網站、讀取和分析其它的網站內容、又或是擷取網頁的圖象等。由於它是以指令的形式，所以很容易自動化，非常方便。官網：http://www.phantomjs.org/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5627917124664808371?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5627917124664808371/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5627917124664808371' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5627917124664808371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5627917124664808371'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/10/browser-phantomjs.html' title='只有指令碼的BROWSER ﹣ PhantomJS'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1924320245452213076</id><published>2011-09-30T05:42:00.000-07:00</published><updated>2011-09-30T05:43:57.874-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-JavaScript'/><title type='text'>Javascript switch UTC Date to local time</title><content type='html'>&lt;code&gt;&lt;br /&gt;&lt;br /&gt;Date.prototype.formatDate = function(format)&lt;br /&gt;{&lt;br /&gt;    var date = this;&lt;br /&gt;    if (!format)&lt;br /&gt;      format="MM/dd/yyyy";&lt;br /&gt;&lt;br /&gt;    var month = date.getMonth() + 1;&lt;br /&gt;    var year = date.getFullYear();&lt;br /&gt;&lt;br /&gt;    format = format.replace("MM",month.toString().padL(2,"0"));&lt;br /&gt;&lt;br /&gt;    if (format.indexOf("yyyy") &gt; -1)&lt;br /&gt;        format = format.replace("yyyy",year.toString());&lt;br /&gt;    else if (format.indexOf("yy") &gt; -1)&lt;br /&gt;        format = format.replace("yy",year.toString().substr(2,2));&lt;br /&gt;&lt;br /&gt;    format = format.replace("dd",date.getDate().toString().padL(2,"0"));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    var hours = date.getHours();&lt;br /&gt;    var hasAMPM = false;&lt;br /&gt;    if (format.indexOf("t") &gt; -1)&lt;br /&gt;    {&lt;br /&gt;       hasAMPM = true;&lt;br /&gt;       if (hours &gt; 11)&lt;br /&gt;        format = format.replace("t","pm")&lt;br /&gt;       else&lt;br /&gt;        format = format.replace("t","am")&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (format.indexOf("hh") &gt; -1) {&lt;br /&gt;        if (hours &gt; 12) hours = hours - 12;&lt;br /&gt;        if (hours == 0) hours = 12;&lt;br /&gt;        format = format.replace("hh",hours.toString().padL(2,"0"));  &lt;br /&gt;    }&lt;br /&gt;    if (format.indexOf("HH") &gt; -1) {&lt;br /&gt;        if(!hasAMPM) {&lt;br /&gt;            format = format.replace("HH",hours.toString().padL(2,"0")); &lt;br /&gt;        } else {&lt;br /&gt;            if (hours &gt; 12) hours = hours - 12;&lt;br /&gt;            if (hours == 0) hours = 12;&lt;br /&gt;            format = format.replace("HH",hours.toString().padL(2,"0"));  &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    if (format.indexOf("mm") &gt; -1)&lt;br /&gt;       format = format.replace("mm",date.getMinutes().toString().padL(2,"0"));&lt;br /&gt;    if (format.indexOf("ss") &gt; -1)&lt;br /&gt;       format = format.replace("ss",date.getSeconds().toString().padL(2,"0"));&lt;br /&gt;&lt;br /&gt;    if(format.indexOf("O") &gt; -1)&lt;br /&gt;        format=format.replace("O",(date.getTimezoneOffset() &lt; 0 ? '-' : '+') + (date.getTimezoneOffset() / 60 &lt; 10 ? '0' : '') + (date.getTimezoneOffset() / 60) );&lt;br /&gt;&lt;br /&gt;    return format;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;script&gt;&lt;br /&gt;var curdate = "2011-09-09T14:38:08.81+08:00";&lt;br /&gt;var dt = Date.parse(curdate).formatDate('MM/dd/yyyy HH:mm t');&lt;br /&gt;console.log(dt);&lt;br /&gt;&lt;/script&gt;&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1924320245452213076?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1924320245452213076/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1924320245452213076' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1924320245452213076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1924320245452213076'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/javascript-switch-utc-date-to-local.html' title='Javascript switch UTC Date to local time'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5110775905592859982</id><published>2011-09-23T00:36:00.000-07:00</published><updated>2011-09-23T00:39:53.390-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><title type='text'>Mac — Adding Hex Color Picker to Color Picker</title><content type='html'>I’ve actually been launching Photoshop here and there to get my color picker (ridiculous, I know), or if I manage to remember, preview + color picker.&lt;br /&gt;It’s time to set up a real hex color picker.&lt;br /&gt;First, let’s make the color picker an executable app.&lt;br /&gt;Push CMD + Space, and run AppleScript Editor&lt;br /&gt;&lt;br /&gt;In the window that appears, type in “choose color”.&lt;br /&gt;&lt;br /&gt;Save it as an app in the “file format” field, and name it what you’d like.&lt;br /&gt;&lt;br /&gt;Now I’d go ahead and give it the icon of the Digital Color Meter and throw it on the dock:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Install HexColorPicker&lt;br /&gt;Download file at http://wafflesoftware.net/hexpicker/&lt;br /&gt;and move the HexColorPicker.colorPicker to your ~/Library/ColorPickers&lt;br /&gt;&lt;br /&gt;You’re done — start the app and look at your shiny new tab on the right.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;reference : http://yuji.wordpress.com/2010/08/01/mac-adding-hex-color-picker-to-color-picker/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5110775905592859982?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5110775905592859982/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5110775905592859982' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5110775905592859982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5110775905592859982'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/mac-adding-hex-color-picker-to-color.html' title='Mac — Adding Hex Color Picker to Color Picker'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5705804276238360</id><published>2011-09-22T03:05:00.000-07:00</published><updated>2011-09-22T03:06:22.795-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML'/><title type='text'>讓AJAX動態內容支援瀏覽器回上頁功能</title><content type='html'>隨著AJAX動態更新技術的普及，手邊專案有愈來愈多網頁開始實現"無PostBack"的設計風格，透過jQuery $.post(), $.get()與ASP.NET程式溝通，執行查詢、更新作業並取得結果，再動態改變HTML DOM回應使用者。(註: 對ASP.NET開發者來說，UpdatePanel是另一個無痛實現AJAX化的選項，但有些副作用)&lt;br /&gt;&lt;br /&gt;由使用者的回饋來看，減少網頁PostBack與網頁重新導向次數，確實大幅提高操作回應速度，提供更好的操作體驗，不過倒有一個常被垢病之處 --- 使用者明明覺得點選操作後瀏覽器切換到下一個畫面，為什麼按覽器的回上頁卻無法回到上個畫面?&lt;br /&gt;&lt;br /&gt;細究這個問題的根源，在於程式是透過AJAX方式取得下一畫面內容更新在網頁上，或是動態顯示/隱藏不同的DOM元素，給予使用者"由操作網頁A切換到操作網頁B"的認知，但以瀏覽器的角度，卻始終是同一個網頁；由於使用者覺得網頁已被切換過，當需要回前一個介面進行資料修改或重新選擇時，直覺上便會去點選"回上頁"，結果跳過了AJAX切換介面的過程，直接進入這個網頁前的瀏覽網頁，造成認知與執行結果的落差，形成抱怨。&lt;br /&gt;&lt;br /&gt;當然，在吃過幾次悶虧(例如: 回上頁造成結果沒儲存之類的，這個倒有解藥)，碰了一鼻子灰之後，使用者就會學乖，自動避開"在某某網頁不能按【回上頁】，很可怕，不要問"的地雷。或者，我們也可以恐嚇提示或教育使用者避免在這類介面按回上頁，或是在網頁加上自己寫的"AJAX版回上頁"按鈕實現切換回上一個UI效果做為替代，不過，這些治標做法終究無法完全避免使用者誤觸回上頁所造成的困擾。&lt;br /&gt;&lt;br /&gt;用一個實例來展示這個問題:&lt;br /&gt;&lt;br /&gt;排版顯示純文字&lt;br /&gt;&lt;!DOCTYPE html&gt;&lt;br /&gt; &lt;br /&gt;&lt;html&gt;&lt;br /&gt;&lt;head&gt;&lt;br /&gt;    &lt;title&gt;AJAX GoBack&lt;/title&gt;&lt;br /&gt;    &lt;script src="Scripts/jquery-1.6.3.js" type="text/javascript"&gt; &lt;/script&gt;&lt;br /&gt;    &lt;script&gt;&lt;br /&gt;        $(function () {&lt;br /&gt;            $("#s1").show();&lt;br /&gt;            $("input.nav-btn").click(function () {&lt;br /&gt;                var $btn = $(this);&lt;br /&gt;                //隱藏目前按鈕所在的div&lt;br /&gt;                $btn.parent().hide();&lt;br /&gt;                //由按鈕的data-nav屬性決定要顯示div的id&lt;br /&gt;                $("#" + $btn.data("nav")).show();&lt;br /&gt;            });&lt;br /&gt;        });&lt;br /&gt;    &lt;/script&gt;&lt;br /&gt;    &lt;style&gt;&lt;br /&gt;    #main div { width: 300px; height: 200px; display: none; padding: 10px; }&lt;br /&gt;    #s1 { background-color: #ff7777; }&lt;br /&gt;    #s2 { background-color: #77ff77; }&lt;br /&gt;    #s3 { background-color: #7777ff; }&lt;br /&gt;    &lt;/style&gt;&lt;br /&gt;&lt;/head&gt;&lt;br /&gt;&lt;body&gt;&lt;br /&gt;&lt;div id="main"&gt;&lt;br /&gt;    &lt;div id="s1"&gt;&lt;br /&gt;    STEP1&lt;br /&gt;    &lt;input type="button" class="nav-btn" value="Next" data-nav="s2" /&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;    &lt;div id="s2"&gt;&lt;br /&gt;    STEP2&lt;br /&gt;    &lt;input type="button" class="nav-btn" value="Next" data-nav="s3" /&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;    &lt;div id="s3"&gt;&lt;br /&gt;    FINAL&lt;br /&gt;    &lt;input type="button" value="Submit" /&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/body&gt;&lt;br /&gt;&lt;/html&gt;&lt;br /&gt;線上展示&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;以上的網頁包含三個&lt;div&gt;，假裝是輸入流程中的三個步驟，STEP1按Next可以跳到STEP2，STEP2按Next可以跳到STEP3。在操作上，讓使用者感覺在不同的網頁介面間切換，但對瀏覽器來說，自始至終都在同一網頁，於是，如圖所以，操作時連回上頁鈕都沒得按。&lt;br /&gt;&lt;br /&gt;搞HTML的老骨頭可能記得#書籤的用法(參考[請看"書籤"相關說明])，在網頁MyPage.htm中放入&lt;a name="myBookmark"&gt;&lt;/a&gt;，當URL指定MyPage.htm#myBookmark，瀏覽器就會開啟該網頁並捲動到&lt;a name="myBookmark"&gt;&lt;/a&gt;的所在位置。有趣的是，MyPage.htm#a跟MyPage.htm#b可被視為不同網址(這表示在瀏覽歷史中會出現一筆)，卻又不會觸發網頁重新載入，這個特性讓AJAX回上頁問題出現一線曙光。&lt;br /&gt;&lt;br /&gt;在URI規範中，#符號後方所帶的資訊被定義成Fragment Identifier(#是Hash symbol，#後方的內容也被稱為hash)，不只是作為書籤定址，還可實現AJAX的歷史巡覽功能(from Wiki: With the rise of AJAX, some websites use fragment identifiers to emulate the back button behavior of browsers for page changes that do not require a reload, or to emulate subpages.)，硬綁綁的規範讓人頭好痛，這裡只研究怎麼應用就好。&lt;br /&gt;&lt;br /&gt;以下是一個簡單的hash應用示範(請使用IE8/9/10的標準模式、Firefox、Chrome或Safari執行)&lt;br /&gt;&lt;br /&gt;排版顯示純文字&lt;br /&gt;&lt;!DOCTYPE html&gt;&lt;br /&gt; &lt;br /&gt;&lt;html&gt;&lt;br /&gt;&lt;head&gt;&lt;br /&gt;    &lt;title&gt;Hash Demo&lt;/title&gt;&lt;br /&gt;    &lt;script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.3.js"&gt;    &lt;br /&gt;    &lt;/script&gt;&lt;br /&gt;    &lt;script&gt;&lt;br /&gt;        $(function () {&lt;br /&gt; &lt;br /&gt;            function setHash(x) {&lt;br /&gt;                var n = x;&lt;br /&gt;                //在第n秒將loation.href改為後方加#sn&lt;br /&gt;                //注意: 只變動#以後的參數不會導致網頁重新載入&lt;br /&gt;            setTimeout(function () {&lt;br /&gt;                    location.hash = "#s" + n;&lt;br /&gt;                }, n * 1000);&lt;br /&gt;            }&lt;br /&gt;            //在第0,1,...,7秒為location.url加上#s0-#s7&lt;br /&gt;            for (var i = 0; i &lt; 8; i++) setHash(i);&lt;br /&gt;            $("#btnShowHash").click(function () {&lt;br /&gt;                $("#ulDisp").append("&lt;li&gt;hash=" + location.hash + "&lt;/li&gt;");&lt;br /&gt;            });&lt;br /&gt;        });&lt;br /&gt;    &lt;/script&gt;&lt;br /&gt;&lt;/head&gt;&lt;br /&gt;&lt;body&gt;&lt;br /&gt;&lt;input type="button" id="btnShowHash" value="Show location.hash" /&gt;&lt;br /&gt;&lt;ul id="ulDisp"&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/body&gt;&lt;br /&gt;&lt;/html&gt;&lt;br /&gt;線上展示&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;網頁裡有一小段程式，會在第0-7秒時，在URL的最後方加上#s0-#s7，執行完畢後，按瀏覽器回上頁及回下頁可在不同的#sn間切換，按下"Show location.href"則會顯示目前的location.hash，當來回切換上一頁下一頁，下方hash=#sn的&lt;li&gt;顯示並不會被清除，驗證了網頁並未被重新載入，Javascript及DOM的狀態在切換過程中不會遺失。&lt;br /&gt;&lt;br /&gt;但很不幸地，IE7(或IE8/9開相容模式)對location.hash特性支援不夠完整，hash改變時並不會在瀏覽歷史中產生記錄，因此需要透過加入隱藏式iframe等特殊手法來克服，自己處理跨瀏覽器議題太辛苦，有個很好用的jQuery外掛BBQ(不是中秋烤肉巴比Q，是Back Button &amp; Query)可以讓我們輕鬆寫出跨瀏覽器版的AJAX回上頁功能。&lt;br /&gt;&lt;br /&gt;小小改寫前述STEP1-STEP2-FINAL網頁，主要是在切換網頁時透過$.bbq.pushState()將目前的階段設成location.hash(#step=sn)，另外在window.onhashchage事件則加入一小段程式，讀取hash值決定現在是STEP1,2還是FINAL，以及該顯示哪一個&lt;div&gt;。如此，一旦使用者按下回上頁/回下頁，URL中的hash不同就會觸發hashchange事件，程式就能依指定的階段切換不同的&lt;div&gt;。&lt;br /&gt;&lt;br /&gt;排版顯示純文字&lt;br /&gt;&lt;!DOCTYPE html&gt;&lt;br /&gt; &lt;br /&gt;&lt;html&gt;&lt;br /&gt;&lt;head&gt;&lt;br /&gt;    &lt;title&gt;AJAX GoBack&lt;/title&gt;&lt;br /&gt;    &lt;script src="Scripts/jquery-1.6.3.js" type="text/javascript"&gt; &lt;/script&gt;&lt;br /&gt;    &lt;script src="Scripts/jquery.ba-bbq.js" type="text/javascript"&gt; &lt;/script&gt;&lt;br /&gt;    &lt;script&gt;&lt;br /&gt;        $(function () {&lt;br /&gt;            $("#s1").show();&lt;br /&gt;            $("input.nav-btn").click(function () {&lt;br /&gt;                var $btn = $(this);&lt;br /&gt;                $btn.parent().hide();&lt;br /&gt;                var nav = $btn.data("nav");&lt;br /&gt;                $("#" + nav).show();&lt;br /&gt;                //將目前的步驟加註在location.hash&lt;br /&gt;                $.bbq.pushState({ step: nav });&lt;br /&gt;            });&lt;br /&gt;            //hash變化時觸發hashchange事件&lt;br /&gt;            $(window).bind('hashchange', function (e) {&lt;br /&gt;                //由hash取出step參數，決定要顯示哪一個div&lt;br /&gt;                var s = e.getState("step") || "s1";&lt;br /&gt;                if (!$("#" + s + ":visible").length) {&lt;br /&gt;                    $("#main &gt; div").hide();&lt;br /&gt;                    $("#" + s).show();&lt;br /&gt;                }&lt;br /&gt;            });&lt;br /&gt;        });&lt;br /&gt;    &lt;/script&gt;&lt;br /&gt;    &lt;style&gt;&lt;br /&gt;    #main div { width: 300px; height: 200px; display: none; padding: 10px; }&lt;br /&gt;    #s1 { background-color: #ff7777; }&lt;br /&gt;    #s2 { background-color: #77ff77; }&lt;br /&gt;    #s3 { background-color: #7777ff; }&lt;br /&gt;    &lt;/style&gt;&lt;br /&gt;&lt;/head&gt;&lt;br /&gt;&lt;body&gt;&lt;br /&gt;&lt;div id="main"&gt;&lt;br /&gt;    &lt;div id="s1"&gt;&lt;br /&gt;    STEP1&lt;br /&gt;    &lt;input type="button" class="nav-btn" value="Next" data-nav="s2" /&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;    &lt;div id="s2"&gt;&lt;br /&gt;    STEP2&lt;br /&gt;    &lt;input type="button" class="nav-btn" value="Next" data-nav="s3" /&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;    &lt;div id="s3"&gt;&lt;br /&gt;    FINAL&lt;br /&gt;    &lt;input type="button" value="Submit" /&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/body&gt;&lt;br /&gt;&lt;/html&gt;&lt;br /&gt;線上展示&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;經過這番加工，網頁就能跨瀏覽器支援回上頁功能，我們又向高級AJAX網頁開發再前進一步囉!&lt;br /&gt;&lt;br /&gt;reference : http://blog.darkthread.net/post-2011-09-22-ajax-goback.aspx?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+Darkthread+%28Darkthread%29&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5705804276238360?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5705804276238360/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5705804276238360' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5705804276238360'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5705804276238360'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/ajax.html' title='讓AJAX動態內容支援瀏覽器回上頁功能'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7923657520774893468</id><published>2011-09-21T23:23:00.000-07:00</published><updated>2011-09-22T00:07:25.954-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IntelliJ IDEA'/><title type='text'>Mac OSX intelliJ idea 10.5.2 XDebug setting</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Install XDebug&lt;/span&gt;&lt;br /&gt;Download [xdebug.so] from http://code.activestate.com/komodo/remotedebugging/&lt;br /&gt;Click [PHP Remote Debugging Client] &gt; [Mac OS X (universal)]&lt;br /&gt;$ copy xdebug.so /Applications/XAMPP/xamppfiles/lib/php/php-5.3.1/extensions/no-debug-non-zts-20090626/xdebug.so&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;edit php.ini&lt;/span&gt;&lt;br /&gt;$vi /path/to/php.ini&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[xdebug]&lt;br /&gt;zend_extension = /Applications/XAMPP/xamppfiles/lib/php/php-5.3.1/extensions/no-debug-non-zts-20090626/xdebug.so&lt;br /&gt;xdebug.profiler_enable = 1&lt;br /&gt;xdebug.profiler_output_dir = /tmp/xdebug&lt;br /&gt;xdebug.profiler_enable_trigger =1&lt;br /&gt;xdebug.remote_enable=on&lt;br /&gt;xdebug.remote_handler=dbgp&lt;br /&gt;xdebug.remote_host=localhost&lt;br /&gt;xdebug.remote_port=9000&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Set the path of php bin&lt;br /&gt;[Preferences] &gt; PHP&lt;br /&gt;PHP Home : &lt;br /&gt;/Applications/XAMPP/xamppfiles/bin&lt;br /&gt;Debugger : Xdebug&lt;br /&gt;&lt;br /&gt;Set the Server information&lt;br /&gt;Name : MySiteName&lt;br /&gt;Host : 127.0.0.1&lt;br /&gt;Port : 3171&lt;br /&gt;Debugger : Xdebug&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Senerio 1: Javascript Debug Settings&lt;/span&gt;&lt;br /&gt;Step1: [Run] &gt; [Debug Configurations] &gt; Defaults &gt; JavaScript Debug(Mouse right click) &gt; Add New Configuration&lt;br /&gt;Step2: &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Switch to [Remote] tab&lt;/span&gt;&lt;br /&gt;-Name : JS-IndexPHP (Example: If I want to link to index.php)&lt;br /&gt;-URL to open : http://127.0.0.1:3171/index.php (Apache webserver url link to index.php)&lt;br /&gt;-Browser : Chrome&lt;br /&gt;Step3: Set path of Chrome [/Applications/Google Chrome.app]&lt;br /&gt;[Preferences] &gt; Web Browsers &gt; Chrome &lt;br /&gt;check [Active]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Senerio 1: PHPUnit Settings(AllTests.php testsuite)&lt;/span&gt;&lt;br /&gt;Step1: [Run] &gt; [Debug Configurations] &gt; Defaults &gt; PHPUnit &gt; Add New Configuration&lt;br /&gt;Step2: &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Switch to [Configuration] tab&lt;/span&gt;&lt;br /&gt;-Test : All in File&lt;br /&gt;-Test file : /path/to/build/testCase/phpunit/AllTests.php&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Senerio 1: PHPUnit Settings(CustomTest.php testclass)&lt;/span&gt;&lt;br /&gt;Step1: [Run] &gt; [Debug Configurations] &gt; Defaults &gt; PHPUnit &gt; Add New Configuration&lt;br /&gt;Step2: &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Switch to [Configuration] tab&lt;/span&gt;&lt;br /&gt;-Test : Class or Suite&lt;br /&gt;-Test file : /path/to/build/testCase/phpunit/inc/class/widgetPool/class/db/WidgetPoolDB.php&lt;br /&gt;- Test class : WidgetPoolDBTest&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-7923657520774893468?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/7923657520774893468/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=7923657520774893468' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7923657520774893468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7923657520774893468'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/mac-osx-intellij-idea-1052-xdebug.html' title='Mac OSX intelliJ idea 10.5.2 XDebug setting'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7841727973846066751</id><published>2011-09-18T22:33:00.000-07:00</published><updated>2011-09-18T22:34:18.640-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse-教學'/><title type='text'>Eclipse 下右鍵打開文件所在文件夾(Explorer Files) Plugin</title><content type='html'>如果你經常需要在Eclipse裡打開相關資源文件所在的文件夾，比較麻煩，要右鍵，屬性，在Location一欄中把所在的文件夾Copy一下，然後再去資源管理器裡輸入這個路徑，Enter，打開它。&lt;br /&gt;&lt;br /&gt;解決方法： 用EasyExplorer插件，有了這個插件就可以很方便地打開資源文件所在的文件夾了.&lt;br /&gt;&lt;br /&gt;安裝： EasyExplorer 從http://sourceforge.net/projects/easystruts&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-7841727973846066751?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/7841727973846066751/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=7841727973846066751' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7841727973846066751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7841727973846066751'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/eclipse-explorer-files-plugin.html' title='Eclipse 下右鍵打開文件所在文件夾(Explorer Files) Plugin'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1566029913519973233</id><published>2011-09-15T22:49:00.000-07:00</published><updated>2011-09-16T02:33:50.607-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IntelliJ IDEA'/><title type='text'>IntelliJ IDea PHPUnit Support</title><content type='html'>After you set up PHPUnit, Web IDE greatly helps you to run your tests. Namely, to quickly create test run configuration:&lt;br /&gt;&lt;br /&gt;Right-click the desired target: a directory or a PHP file in the Project view, or a test class/method name in the code editor&lt;br /&gt;Choose Run&lt;name&gt; to start or Create&lt;name&gt; to specify additional parameters&lt;br /&gt;That’s it!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;XML configuration file&lt;br /&gt;&lt;br /&gt;Advanced settings can be specified through PHPUnit configuration file. All the options specified in Web IDE take precedence over those set in configuration file.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Test groups&lt;br /&gt;&lt;br /&gt;A test can be tagged as belonging to one or more groups using the @group annotation as shown below.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The test is run if none of the specified groups is excluded and at least one group is included.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Run/Debug Tests&lt;br /&gt;&lt;br /&gt;Before you execute tests, please set up PHP home directory (one that contains PHP executable) in Settings | PHP. Debugging is currently available via XDebug. Specify the same debug port in Settings | PHP as in php.ini file (xdebug.remote_port=&lt;port number&gt;).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Test results window&lt;br /&gt;&lt;br /&gt;You can easily navigate from tests results tree and stack trace to the corresponding source code location.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When debugging your tests you get all the features, such as watches, expressions evaluation, etc. You can have several debug sessions simultaneously.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;All of these features will be available since next EAP.&lt;br /&gt;&lt;br /&gt;Test with pleasure!&lt;br /&gt;&lt;br /&gt;This entry was posted in Feature and tagged PHP, PhpStorm, PHPUnit, testing. Bookmark the permalink.&lt;br /&gt;page: 2&lt;br /&gt;&lt;br /&gt;Web IDE EAP (build 94.5)&lt;br /&gt;Posted on December 9, 2009 by neuro159&lt;br /&gt;&lt;br /&gt;There are numerous updates in this build. As usually, most notable are listed below, with details on all (S)FTP, PHPUnit, formatting, javascript and other fixes available in the issue tracker.&lt;br /&gt;&lt;br /&gt;Code analyzer will recognize classes, functions and constants referenced by their PHP 5.3 use aliases.&lt;br /&gt;Added PHP Unit debugging and XML config support (included/excluded groups etc.)&lt;br /&gt;Rename refactoring will take care of @param annotations&lt;br /&gt;Undefined variable inspection will recognize pass by reference parameters as appropriate declarations, i.e. in preg_match(.., .. &amp;$matches)&lt;br /&gt;Basic “Add declaration” quickfix for unresolved class methods, properties and constants (Alt-Enter)&lt;br /&gt;Tools|Analyze stacktrace&lt;br /&gt;It looks like we’re done with PHPUnit support – no pending feature requests or bug fixes, please check it out.&lt;br /&gt;&lt;br /&gt;Nearest plans include complete support for namespaces to finally close PHP 5.3 support, improved remote synchronization setup and all fixes and neat features that we will be able to stuff-in, just as usual.&lt;br /&gt;&lt;br /&gt;Also since IntelliJ IDEA 9.0 is now released we are expecting third party plugins to be updated for 9.0 platform compatibility – that will bring them to WebIDE plugin repository.&lt;br /&gt;&lt;br /&gt;Download Web IDE EAP build 94.5 for your platform from project EAP page.&lt;br /&gt;&lt;br /&gt;-JetBrains Web IDE Team&lt;br /&gt;&lt;br /&gt;reference : http://blog.jetbrains.com/webide/2009/12/phpunit-support/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1566029913519973233?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1566029913519973233/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1566029913519973233' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1566029913519973233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1566029913519973233'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/intellij-idea-phpunit-support.html' title='IntelliJ IDea PHPUnit Support'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7523960945607398538</id><published>2011-09-09T11:26:00.000-07:00</published><updated>2011-09-09T11:38:39.808-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse-教學'/><title type='text'>Zend Studio 8.0 PHPUnit Debugger setting[use XDebug in Max OSX 10.6]</title><content type='html'>Predefined:&lt;br /&gt;XAMPP version 1.7.3&lt;br /&gt;PHP 5.3.1&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Step1 : Install XDebug&lt;/span&gt;&lt;br /&gt;Download [xdebug.so] from http://code.activestate.com/komodo/remotedebugging/&lt;br /&gt;Click [PHP Remote Debugging Client] &gt; [Mac OS X (universal)]&lt;br /&gt;$ copy xdebug.so /Applications/XAMPP/xamppfiles/lib/php/php-5.3.1/extensions/no-debug-non-zts-20090626/xdebug.so&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Step2 : Edit php.ini&lt;/span&gt;&lt;br /&gt;$ vi /Applications/XAMPP/etc/php.ini &lt;br /&gt;&lt;code&gt;&lt;br /&gt;[XDebug]&lt;br /&gt;zend_extension = /Applications/XAMPP/xamppfiles/lib/php/php-5.3.1/extensions/no-debug-non-zts-20090626/xdebug.so&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Zend Studio 8 setting as below:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-Pn-_hyZ3rak/Tmpcv0dNQwI/AAAAAAAADNI/epoc6oeoPqo/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.35.25.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 390px; height: 176px;" src="http://1.bp.blogspot.com/-Pn-_hyZ3rak/Tmpcv0dNQwI/AAAAAAAADNI/epoc6oeoPqo/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.35.25.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650430659094463234" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-ievBA7zu7w4/TmpcwKlQ11I/AAAAAAAADNQ/_K-ibX5ApAI/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.35.37.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 268px;" src="http://1.bp.blogspot.com/-ievBA7zu7w4/TmpcwKlQ11I/AAAAAAAADNQ/_K-ibX5ApAI/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.35.37.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650430665033832274" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-HhIBS7UQWhk/TmpcwW2OrEI/AAAAAAAADNY/5WJbVG6FSYE/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.35.57.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 359px;" src="http://4.bp.blogspot.com/-HhIBS7UQWhk/TmpcwW2OrEI/AAAAAAAADNY/5WJbVG6FSYE/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.35.57.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650430668326218818" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-1M7ZxVTewZI/TmpcwnHFUdI/AAAAAAAADNg/ubNdbpSbnZ4/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.36.33.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 267px;" src="http://4.bp.blogspot.com/-1M7ZxVTewZI/TmpcwnHFUdI/AAAAAAAADNg/ubNdbpSbnZ4/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.36.33.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650430672691876306" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-icDLyEBveao/TmpcwziaxqI/AAAAAAAADNo/4Q7seIc1RPc/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.36.40.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 267px;" src="http://2.bp.blogspot.com/-icDLyEBveao/TmpcwziaxqI/AAAAAAAADNo/4Q7seIc1RPc/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.36.40.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650430676027754146" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-7523960945607398538?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/7523960945607398538/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=7523960945607398538' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7523960945607398538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7523960945607398538'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/zend-studio-80-phpunit-debugger.html' title='Zend Studio 8.0 PHPUnit Debugger setting[use XDebug in Max OSX 10.6]'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-Pn-_hyZ3rak/Tmpcv0dNQwI/AAAAAAAADNI/epoc6oeoPqo/s72-c/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.35.25.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-2042247060096216485</id><published>2011-09-09T10:35:00.000-07:00</published><updated>2011-09-09T11:39:40.134-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse-教學'/><title type='text'>Zend Studio 8.0.1 PHPUnit Debugger setting[use XDebug in Windows Server2008]</title><content type='html'>Predefined:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-7RjguCTuklk/TmpP6F4jafI/AAAAAAAADMg/OU54DDr8Je4/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.43.25.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 47px;" src="http://4.bp.blogspot.com/-7RjguCTuklk/TmpP6F4jafI/AAAAAAAADMg/OU54DDr8Je4/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.43.25.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650416541920094706" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;php.ini as below:&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[XDebug]&lt;br /&gt;zend_extension = C:\AppServ\php5\ext\php_xdebug-2.1.0-5.3-vc9-nts.dll&lt;br /&gt;&lt;br /&gt;[PHPUnit]&lt;br /&gt;include_path = ".;C:\CruiseControl\projects\Widget_Framework\Int\build\phpunit\PHPUnit"&lt;br /&gt;&lt;br /&gt;[Zend]&lt;br /&gt;;zend_extension="C:\Program Files (x86)\Zend\Zend Studio - 8.0.1\plugins\org.zend.php.debug.debugger.win32.x86_5.3.18.v20110322\resources\php53\ZendDebugger.dll"&lt;br /&gt;zend_debugger.allow_hosts=127.0.0.1&lt;br /&gt;zend_debugger.expose_remotely=always&lt;br /&gt;zend_debugger.connector_port = 10013&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Zend Debugger setting as below:&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-KD85DWhz6_Y/TmpQDVUW2mI/AAAAAAAADMo/5DKLJ9SjQdM/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.40.31.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 316px; height: 140px;" src="http://4.bp.blogspot.com/-KD85DWhz6_Y/TmpQDVUW2mI/AAAAAAAADMo/5DKLJ9SjQdM/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.40.31.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650416700682066530" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-vWC_MGg-YZo/TmpQPwE7_vI/AAAAAAAADMw/bOOHp7D0tQI/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.41.40.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 321px;" src="http://3.bp.blogspot.com/-vWC_MGg-YZo/TmpQPwE7_vI/AAAAAAAADMw/bOOHp7D0tQI/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.41.40.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650416914023579378" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-UKkQsEPhlXE/TmpQWzC54tI/AAAAAAAADM4/uhbe9A2j_h8/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.42.24.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 352px; height: 400px;" src="http://1.bp.blogspot.com/-UKkQsEPhlXE/TmpQWzC54tI/AAAAAAAADM4/uhbe9A2j_h8/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.42.24.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650417035079443154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-R3hL2ThgZ_M/TmpSfrwwSiI/AAAAAAAADNA/7qNJbfALQ34/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.54.45.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 321px;" src="http://3.bp.blogspot.com/-R3hL2ThgZ_M/TmpSfrwwSiI/AAAAAAAADNA/7qNJbfALQ34/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.54.45.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650419386766346786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-q6_Y_sdTV-Q/TmpdYMPuMfI/AAAAAAAADNw/Tv2l25yrUZQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.41.07.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 319px;" src="http://3.bp.blogspot.com/-q6_Y_sdTV-Q/TmpdYMPuMfI/AAAAAAAADNw/Tv2l25yrUZQ/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25882.41.07.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5650431352675119602" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-2042247060096216485?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/2042247060096216485/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=2042247060096216485' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/2042247060096216485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/2042247060096216485'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/09/zend-studio-801-phpunit-debugger.html' title='Zend Studio 8.0.1 PHPUnit Debugger setting[use XDebug in Windows Server2008]'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-7RjguCTuklk/TmpP6F4jafI/AAAAAAAADMg/OU54DDr8Je4/s72-c/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7%2B2011-09-10%2B%25E4%25B8%258A%25E5%258D%25881.43.25.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-8466952832733181692</id><published>2011-08-16T08:18:00.000-07:00</published><updated>2011-08-16T08:19:47.768-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse-教學'/><title type='text'>A project with that name already exists in the wor</title><content type='html'>Keyword : Eclipse The project could not be created. This may occur if a project in by that name already exists&lt;br /&gt;Solution : You need to open up the Project Explorer view (it may already be open) and delete the project from within there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-8466952832733181692?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/8466952832733181692/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=8466952832733181692' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8466952832733181692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8466952832733181692'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/08/project-with-that-name-already-exists.html' title='A project with that name already exists in the wor'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5720883309127457929</id><published>2011-08-16T08:15:00.000-07:00</published><updated>2011-08-16T08:18:05.180-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CruiseControl'/><title type='text'>CruiseControl how to reset build number</title><content type='html'>Step 1 : stop cruisecontrol service&lt;br /&gt;Step 2 : Delete C:\CruiseControl\*.ser&lt;br /&gt;Step 3 : Delete C:\CruiseControl\artifacts\*&lt;br /&gt;Step 4 : Delete C:\CruiseControl\logs\*&lt;br /&gt;Step 5 : start cruisecontrol service&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5720883309127457929?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5720883309127457929/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5720883309127457929' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5720883309127457929'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5720883309127457929'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/08/cruisecontrol-how-to-reset-build-number.html' title='CruiseControl how to reset build number'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7391859995933375657</id><published>2011-08-11T01:34:00.000-07:00</published><updated>2011-08-11T02:07:24.125-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>Use NuSOAP Create your Web Service</title><content type='html'>NuSOAP is a group of PHP classes that allow developers to create and consume SOAP web services. It does not require any special PHP extensions. The current release version (0.6.7) of NuSOAP at the time this was written (03-November-2004), supports much of the SOAP 1.1 specification. It can generate WSDL 1.1 and also consume it for use in serialization. Both rpc/encoded and document/literal services are supported. However, it must be noted that NuSOAP does not provide coverage of the SOAP 1.1 and WSDL 1.1 that is as complete as some other implementations, such as .NET and Apache Axis.&lt;br /&gt;&lt;br /&gt;This document follows up Introduction to NuSOAP, Programming with NuSOAP, and Programming with NuSOAP Part 2 with additional samples that demonstrate how to use NuSOAP to create and consume SOAP web services using WSDL.&lt;br /&gt;&lt;br /&gt;Hello, World Redux&lt;br /&gt;The New Client&lt;br /&gt;Defining New Data Structures&lt;br /&gt;Hello, World Redux&lt;br /&gt;&lt;br /&gt;Showing no imagination whatsoever, I used the ubiquitous "Hello, World" example in Introduction to NuSOAP. In that document, I showed the SOAP request and response exchanged by the client and server. Here, I extend that sample to use WSDL.&lt;br /&gt;&lt;br /&gt;A WSDL document provides metadata for a service. NuSOAP allows a programmer to specify the WSDL to be generated for the service programmatically using additional fields and methods of the soap_server class.&lt;br /&gt;&lt;br /&gt;The service code must do a number of things in order for correct WSDL to be generated. Information about the service is specified by calling the configureWSDL method. Information about each method is specified by supplying additional parameters to the register method. Service code for using WSDL is shown in the following example.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;// Pull in the NuSOAP code&lt;br /&gt;require_once('nusoap.php');&lt;br /&gt;// Create the server instance&lt;br /&gt;$server = new soap_server();&lt;br /&gt;// Initialize WSDL support&lt;br /&gt;$server-&gt;configureWSDL('hellowsdl', 'urn:hellowsdl');&lt;br /&gt;// Register the method to expose&lt;br /&gt;$server-&gt;register('hello',                // method name&lt;br /&gt;    array('name' =&gt; 'xsd:string'),        // input parameters&lt;br /&gt;    array('return' =&gt; 'xsd:string'),      // output parameters&lt;br /&gt;    'urn:hellowsdl',                      // namespace&lt;br /&gt;    'urn:hellowsdl#hello',                // soapaction&lt;br /&gt;    'rpc',                                // style&lt;br /&gt;    'encoded',                            // use&lt;br /&gt;    'Says hello to the caller'            // documentation&lt;br /&gt;);&lt;br /&gt;// Define the method as a PHP function&lt;br /&gt;function hello($name) {&lt;br /&gt;        return 'Hello, ' . $name;&lt;br /&gt;}&lt;br /&gt;// Use the request to (try to) invoke the service&lt;br /&gt;$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';&lt;br /&gt;$server-&gt;service($HTTP_RAW_POST_DATA);&lt;br /&gt;?&gt;&lt;br /&gt;Now for some magic. Point a Web browser at this service, which in my environment is at http://localhost/phphack/hellowsdl.php. The HTML that is returned to your browser gives you links to view the WSDL for the service or view information about each method, in this case the hello method. The screen should look something like this.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;hellowsdl&lt;br /&gt;View the WSDL for the service. Click on an operation name to view it's details.&lt;br /&gt;&lt;br /&gt;hello&lt;br /&gt;Displaying the details for the hello operation looks something like this.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;hellowsdl&lt;br /&gt;View the WSDL for the service. Click on an operation name to view it's details.&lt;br /&gt;&lt;br /&gt;hello&lt;br /&gt;Close&lt;br /&gt;&lt;br /&gt;Name: hello&lt;br /&gt;Binding: hellowsdlBinding&lt;br /&gt;Endpoint: http://localhost/phphack/hellowsdl.php&lt;br /&gt;SoapAction: urn:hellowsdl#hello&lt;br /&gt;Style: rpc&lt;br /&gt;Input:&lt;br /&gt;  use: encoded&lt;br /&gt;  namespace: urn:hellowsdl&lt;br /&gt;  encodingStyle: http://schemas.xmlsoap.org/soap/encoding/&lt;br /&gt;  message: helloRequest&lt;br /&gt;  parts:&lt;br /&gt;    name: xsd:string&lt;br /&gt;Output:&lt;br /&gt;  use: encoded&lt;br /&gt;  namespace: urn:hellowsdl&lt;br /&gt;  encodingStyle: http://schemas.xmlsoap.org/soap/encoding/&lt;br /&gt;  message: helloResponse&lt;br /&gt;  parts:&lt;br /&gt;    return: xsd:string&lt;br /&gt;Namespace: urn:hellowsdl&lt;br /&gt;Transport: http://schemas.xmlsoap.org/soap/http&lt;br /&gt;Documentation: Says hello to the caller&lt;br /&gt;So, with just a little code added to the service, NuSOAP provides browsable documentation of the service. But, that is not all. By either clicking the WSDL link on the documentation page, or by pointing the browser at the service with a query string of ?wsdl (e.g. http://localhost/phphack/hellowsdl.php?wsdl), you get the following WSDL.&lt;br /&gt;&lt;br /&gt;&lt;?xml version="1.0"?&gt;&lt;br /&gt;&lt;definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&lt;br /&gt;             xmlns:xsd="http://www.w3.org/2001/XMLSchema"&lt;br /&gt;             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;             xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;             xmlns:si="http://soapinterop.org/xsd"&lt;br /&gt;             xmlns:tns="urn:hellowsdl"&lt;br /&gt;             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"&lt;br /&gt;             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"&lt;br /&gt;             xmlns="http://schemas.xmlsoap.org/wsdl/"&lt;br /&gt;             targetNamespace="urn:hellowsdl"&gt;&lt;br /&gt;    &lt;types&gt;&lt;br /&gt;        &lt;xsd:schema targetNamespace="urn:hellowsdl"&gt;&lt;br /&gt;            &lt;xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" /&gt;&lt;br /&gt;            &lt;xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" /&gt;&lt;br /&gt;        &lt;/xsd:schema&gt;&lt;br /&gt;    &lt;/types&gt;&lt;br /&gt;    &lt;message name="helloRequest"&gt;&lt;br /&gt;        &lt;part name="name" type="xsd:string" /&gt;&lt;br /&gt;    &lt;/message&gt;&lt;br /&gt;    &lt;message name="helloResponse"&gt;&lt;br /&gt;        &lt;part name="return" type="xsd:string" /&gt;&lt;br /&gt;    &lt;/message&gt;&lt;br /&gt;    &lt;portType name="hellowsdlPortType"&gt;&lt;br /&gt;        &lt;operation name="hello"&gt;&lt;br /&gt;            &lt;documentation&gt;Says hello to the caller&lt;/documentation&gt;&lt;br /&gt;            &lt;input message="tns:helloRequest"/&gt;&lt;br /&gt;            &lt;output message="tns:helloResponse"/&gt;&lt;br /&gt;        &lt;/operation&gt;&lt;br /&gt;    &lt;/portType&gt;&lt;br /&gt;    &lt;binding name="hellowsdlBinding" type="tns:hellowsdlPortType"&gt;&lt;br /&gt;        &lt;soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/&gt;&lt;br /&gt;        &lt;operation name="hello"&gt;&lt;br /&gt;            &lt;soap:operation soapAction="urn:hellowsdl#hello" style="rpc"/&gt;&lt;br /&gt;            &lt;input&gt;&lt;br /&gt;                &lt;soap:body use="encoded" namespace="urn:hellowsdl"&lt;br /&gt;                 encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/&gt;&lt;br /&gt;            &lt;/input&gt;&lt;br /&gt;            &lt;output&gt;&lt;br /&gt;                &lt;soap:body use="encoded" namespace="urn:hellowsdl"&lt;br /&gt;                 encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/&gt;&lt;br /&gt;            &lt;/output&gt;&lt;br /&gt;        &lt;/operation&gt;&lt;br /&gt;    &lt;/binding&gt;&lt;br /&gt;    &lt;service name="hellowsdl"&gt;&lt;br /&gt;        &lt;port name="hellowsdlPort" binding="tns:hellowsdlBinding"&gt;&lt;br /&gt;            &lt;soap:address location="http://localhost/phphack/hellowsdl.php"/&gt;&lt;br /&gt;        &lt;/port&gt;&lt;br /&gt;    &lt;/service&gt;&lt;br /&gt;&lt;/definitions&gt;&lt;br /&gt;Return to top.&lt;br /&gt;The New Client&lt;br /&gt;&lt;br /&gt;Adding a few NuSOAP WSDL calls to the service allows it to generate WSDL and other documentation. By comparison, client support for WSDL is anti-climactic, at least for this simple example. The simple client shown below is not much different than the non-WSDL client. The only difference is that the constructor for the soapclient class is provided the URL of the WSDL, rather than the service endpoint.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;// Pull in the NuSOAP code&lt;br /&gt;require_once('nusoap.php');&lt;br /&gt;// Create the client instance&lt;br /&gt;$client = new nusoap_client('http://localhost/phphack/hellowsdl.php?wsdl', true);&lt;br /&gt;// Check for an error&lt;br /&gt;$err = $client-&gt;getError();&lt;br /&gt;if ($err) {&lt;br /&gt;    // Display the error&lt;br /&gt;    echo '&lt;h2&gt;Constructor error&lt;/h2&gt;&lt;pre&gt;' . $err . '&lt;/pre&gt;';&lt;br /&gt;    // At this point, you know the call that follows will fail&lt;br /&gt;}&lt;br /&gt;// Call the SOAP method&lt;br /&gt;$result = $client-&gt;call('hello', array('name' =&gt; 'Scott'));&lt;br /&gt;// Check for a fault&lt;br /&gt;if ($client-&gt;fault) {&lt;br /&gt;    echo '&lt;h2&gt;Fault&lt;/h2&gt;&lt;pre&gt;';&lt;br /&gt;    print_r($result);&lt;br /&gt;    echo '&lt;/pre&gt;';&lt;br /&gt;} else {&lt;br /&gt;    // Check for errors&lt;br /&gt;    $err = $client-&gt;getError();&lt;br /&gt;    if ($err) {&lt;br /&gt;        // Display the error&lt;br /&gt;        echo '&lt;h2&gt;Error&lt;/h2&gt;&lt;pre&gt;' . $err . '&lt;/pre&gt;';&lt;br /&gt;    } else {&lt;br /&gt;        // Display the result&lt;br /&gt;        echo '&lt;h2&gt;Result&lt;/h2&gt;&lt;pre&gt;';&lt;br /&gt;        print_r($result);&lt;br /&gt;    echo '&lt;/pre&gt;';&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;// Display the request and response&lt;br /&gt;echo '&lt;h2&gt;Request&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($client-&gt;request, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;echo '&lt;h2&gt;Response&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($client-&gt;response, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;// Display the debug messages&lt;br /&gt;echo '&lt;h2&gt;Debug&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($client-&gt;debug_str, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;?&gt;&lt;br /&gt;Here are the request and response for this WSDL implementation.&lt;br /&gt;&lt;br /&gt;POST /phphack/hellowsdl.php HTTP/1.0&lt;br /&gt;Host: localhost&lt;br /&gt;User-Agent: NuSOAP/0.6.8 (1.81)&lt;br /&gt;Content-Type: text/xml; charset=ISO-8859-1&lt;br /&gt;SOAPAction: "urn:hellowsdl#hello"&lt;br /&gt;Content-Length: 550&lt;br /&gt;&lt;br /&gt;&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;&lt;br /&gt;&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&lt;br /&gt;                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"&lt;br /&gt;                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;                   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:si="http://soapinterop.org/xsd"&lt;br /&gt;                   xmlns:tns="urn:hellowsdl"&gt;&lt;br /&gt;    &lt;SOAP-ENV:Body&gt;&lt;br /&gt;        &lt;tns:hello xmlns:tns="urn:hellowsdl"&gt;&lt;br /&gt;            &lt;name xsi:type="xsd:string"&gt;Scott&lt;/name&gt;&lt;br /&gt;        &lt;/tns:hello&gt;&lt;br /&gt;    &lt;/SOAP-ENV:Body&gt;&lt;br /&gt;&lt;/SOAP-ENV:Envelope&gt;&lt;br /&gt;HTTP/1.1 200 OK&lt;br /&gt;Server: Microsoft-IIS/5.0&lt;br /&gt;Date: Wed, 03 Nov 2004 21:05:34 GMT&lt;br /&gt;X-Powered-By: ASP.NET&lt;br /&gt;X-Powered-By: PHP/4.3.4&lt;br /&gt;Server: NuSOAP Server v0.6.8&lt;br /&gt;X-SOAP-Server: NuSOAP/0.6.8 (1.81)&lt;br /&gt;Content-Type: text/xml; charset=ISO-8859-1&lt;br /&gt;Content-Length: 551&lt;br /&gt;&lt;br /&gt;&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;&lt;br /&gt;&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&lt;br /&gt;                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"&lt;br /&gt;                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;                   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:si="http://soapinterop.org/xsd"&gt;&lt;br /&gt;    &lt;SOAP-ENV:Body&gt;&lt;br /&gt;        &lt;ns1:helloResponse xmlns:ns1="urn:hellowsdl"&gt;&lt;br /&gt;            &lt;return xsi:type="xsd:string"&gt;Hello, Scott&lt;/return&gt;&lt;br /&gt;        &lt;/helloResponse&gt;&lt;br /&gt;    &lt;/SOAP-ENV:Body&gt;&lt;br /&gt;&lt;/SOAP-ENV:Envelope&gt;&lt;br /&gt;Return to top.&lt;br /&gt;Defining New Data Structures&lt;br /&gt;&lt;br /&gt;An important aspect of WSDL is that it can encapsulate one or more XML Schema, allowing programmers to describe the data structures used by a service. To illustrate how NuSOAP supports this, I will add WSDL code to the SOAP struct example in Programming with NuSOAP Part 2.&lt;br /&gt;&lt;br /&gt;The service code gains the changes already shown in the Hello, World example, but it also has code to define the Person data structure.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;// Pull in the NuSOAP code&lt;br /&gt;require_once('nusoap.php');&lt;br /&gt;// Create the server instance&lt;br /&gt;$server = new soap_server();&lt;br /&gt;// Initialize WSDL support&lt;br /&gt;$server-&gt;configureWSDL('hellowsdl2', 'urn:hellowsdl2');&lt;br /&gt;// Register the data structures used by the service&lt;br /&gt;$server-&gt;wsdl-&gt;addComplexType(&lt;br /&gt;    'Person',&lt;br /&gt;    'complexType',&lt;br /&gt;    'struct',&lt;br /&gt;    'all',&lt;br /&gt;    '',&lt;br /&gt;    array(&lt;br /&gt;        'firstname' =&gt; array('name' =&gt; 'firstname', 'type' =&gt; 'xsd:string'),&lt;br /&gt;        'age' =&gt; array('name' =&gt; 'age', 'type' =&gt; 'xsd:int'),&lt;br /&gt;        'gender' =&gt; array('name' =&gt; 'gender', 'type' =&gt; 'xsd:string')&lt;br /&gt;    )&lt;br /&gt;);&lt;br /&gt;$server-&gt;wsdl-&gt;addComplexType(&lt;br /&gt;    'SweepstakesGreeting',&lt;br /&gt;    'complexType',&lt;br /&gt;    'struct',&lt;br /&gt;    'all',&lt;br /&gt;    '',&lt;br /&gt;    array(&lt;br /&gt;        'greeting' =&gt; array('name' =&gt; 'greeting', 'type' =&gt; 'xsd:string'),&lt;br /&gt;        'winner' =&gt; array('name' =&gt; 'winner', 'type' =&gt; 'xsd:boolean')&lt;br /&gt;    )&lt;br /&gt;);&lt;br /&gt;// Register the method to expose&lt;br /&gt;$server-&gt;register('hello',                    // method name&lt;br /&gt;    array('person' =&gt; 'tns:Person'),          // input parameters&lt;br /&gt;    array('return' =&gt; 'tns:SweepstakesGreeting'),    // output parameters&lt;br /&gt;    'urn:hellowsdl2',                         // namespace&lt;br /&gt;    'urn:hellowsdl2#hello',                   // soapaction&lt;br /&gt;    'rpc',                                    // style&lt;br /&gt;    'encoded',                                // use&lt;br /&gt;    'Greet a person entering the sweepstakes'        // documentation&lt;br /&gt;);&lt;br /&gt;// Define the method as a PHP function&lt;br /&gt;function hello($person) {&lt;br /&gt;    $greeting = 'Hello, ' . $person['firstname'] .&lt;br /&gt;                '. It is nice to meet a ' . $person['age'] .&lt;br /&gt;                ' year old ' . $person['gender'] . '.';&lt;br /&gt;    &lt;br /&gt;    $winner = $person['firstname'] == 'Scott';&lt;br /&gt;&lt;br /&gt;    return array(&lt;br /&gt;                'greeting' =&gt; $greeting,&lt;br /&gt;                'winner' =&gt; $winner&lt;br /&gt;                );&lt;br /&gt;}&lt;br /&gt;// Use the request to (try to) invoke the service&lt;br /&gt;$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';&lt;br /&gt;$server-&gt;service($HTTP_RAW_POST_DATA);&lt;br /&gt;?&gt;&lt;br /&gt;Besides the additional code to support WSDL, the code for the service method itself is changed slightly. With WSDL, it is no longer necessary to use the soapval object to specify the name and data type for the return value.&lt;br /&gt;&lt;br /&gt;Similarly, the WSDL client does not need to use a soapval to specify the name and data type of the parameter, as shown in the following code.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;// Pull in the NuSOAP code&lt;br /&gt;require_once('nusoap.php');&lt;br /&gt;// Create the client instance&lt;br /&gt;$client = new soapclient('http://localhost/phphack/hellowsdl2.php?wsdl', true);&lt;br /&gt;// Check for an error&lt;br /&gt;$err = $client-&gt;getError();&lt;br /&gt;if ($err) {&lt;br /&gt;    // Display the error&lt;br /&gt;    echo '&lt;h2&gt;Constructor error&lt;/h2&gt;&lt;pre&gt;' . $err . '&lt;/pre&gt;';&lt;br /&gt;    // At this point, you know the call that follows will fail&lt;br /&gt;}&lt;br /&gt;// Call the SOAP method&lt;br /&gt;$person = array('firstname' =&gt; 'Willi', 'age' =&gt; 22, 'gender' =&gt; 'male');&lt;br /&gt;$result = $client-&gt;call('hello', array('person' =&gt; $person));&lt;br /&gt;// Check for a fault&lt;br /&gt;if ($client-&gt;fault) {&lt;br /&gt;    echo '&lt;h2&gt;Fault&lt;/h2&gt;&lt;pre&gt;';&lt;br /&gt;    print_r($result);&lt;br /&gt;    echo '&lt;/pre&gt;';&lt;br /&gt;} else {&lt;br /&gt;    // Check for errors&lt;br /&gt;    $err = $client-&gt;getError();&lt;br /&gt;    if ($err) {&lt;br /&gt;        // Display the error&lt;br /&gt;        echo '&lt;h2&gt;Error&lt;/h2&gt;&lt;pre&gt;' . $err . '&lt;/pre&gt;';&lt;br /&gt;    } else {&lt;br /&gt;        // Display the result&lt;br /&gt;        echo '&lt;h2&gt;Result&lt;/h2&gt;&lt;pre&gt;';&lt;br /&gt;        print_r($result);&lt;br /&gt;    echo '&lt;/pre&gt;';&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;// Display the request and response&lt;br /&gt;echo '&lt;h2&gt;Request&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($client-&gt;request, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;echo '&lt;h2&gt;Response&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($client-&gt;response, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;// Display the debug messages&lt;br /&gt;echo '&lt;h2&gt;Debug&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($client-&gt;debug_str, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;?&gt;&lt;br /&gt;WSDL enables one more capability on the client. Instead of using the call method of the soapclient class, a proxy can be used. The proxy is a class that mirrors the service, in that it has the same methods with the same parameters as the service. Some programmers prefer to use proxies because the code reads as method calls on object instances, rather than invocations through the call method. A client that uses a proxy is shown below.&lt;br /&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;// Pull in the NuSOAP code&lt;br /&gt;require_once('nusoap.php');&lt;br /&gt;// Create the client instance&lt;br /&gt;$client = new soapclient('http://localhost/phphack/hellowsdl2.php?wsdl', true);&lt;br /&gt;// Check for an error&lt;br /&gt;$err = $client-&gt;getError();&lt;br /&gt;if ($err) {&lt;br /&gt;    // Display the error&lt;br /&gt;    echo '&lt;h2&gt;Constructor error&lt;/h2&gt;&lt;pre&gt;' . $err . '&lt;/pre&gt;';&lt;br /&gt;    // At this point, you know the call that follows will fail&lt;br /&gt;}&lt;br /&gt;// Create the proxy&lt;br /&gt;$proxy = $client-&gt;getProxy();&lt;br /&gt;// Call the SOAP method&lt;br /&gt;$person = array('firstname' =&gt; 'Willi', 'age' =&gt; 22, 'gender' =&gt; 'male');&lt;br /&gt;$result = $proxy-&gt;hello($person);&lt;br /&gt;// Check for a fault&lt;br /&gt;if ($proxy-&gt;fault) {&lt;br /&gt;    echo '&lt;h2&gt;Fault&lt;/h2&gt;&lt;pre&gt;';&lt;br /&gt;    print_r($result);&lt;br /&gt;    echo '&lt;/pre&gt;';&lt;br /&gt;} else {&lt;br /&gt;    // Check for errors&lt;br /&gt;    $err = $proxy-&gt;getError();&lt;br /&gt;    if ($err) {&lt;br /&gt;        // Display the error&lt;br /&gt;        echo '&lt;h2&gt;Error&lt;/h2&gt;&lt;pre&gt;' . $err . '&lt;/pre&gt;';&lt;br /&gt;    } else {&lt;br /&gt;        // Display the result&lt;br /&gt;        echo '&lt;h2&gt;Result&lt;/h2&gt;&lt;pre&gt;';&lt;br /&gt;        print_r($result);&lt;br /&gt;    echo '&lt;/pre&gt;';&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;// Display the request and response&lt;br /&gt;echo '&lt;h2&gt;Request&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($proxy-&gt;request, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;echo '&lt;h2&gt;Response&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($proxy-&gt;response, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;// Display the debug messages&lt;br /&gt;echo '&lt;h2&gt;Debug&lt;/h2&gt;';&lt;br /&gt;echo '&lt;pre&gt;' . htmlspecialchars($proxy-&gt;debug_str, ENT_QUOTES) . '&lt;/pre&gt;';&lt;br /&gt;?&gt;&lt;br /&gt;Regardless of whether the "regular" or proxy coding style is used, the request and response messages are the same.&lt;br /&gt;&lt;br /&gt;POST /phphack/hellowsdl2.php HTTP/1.0&lt;br /&gt;Host: localhost&lt;br /&gt;User-Agent: NuSOAP/0.6.8 (1.81)&lt;br /&gt;Content-Type: text/xml; charset=ISO-8859-1&lt;br /&gt;SOAPAction: "urn:hellowsdl2#hello"&lt;br /&gt;Content-Length: 676&lt;br /&gt;&lt;br /&gt;&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;&lt;br /&gt;&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&lt;br /&gt;                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"&lt;br /&gt;                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;                   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:si="http://soapinterop.org/xsd"&lt;br /&gt;                   xmlns:tns="urn:hellowsdl2"&gt;&lt;br /&gt;    &lt;SOAP-ENV:Body&gt;&lt;br /&gt;        &lt;tns:hello xmlns:tns="urn:hellowsdl2"&gt;&lt;br /&gt;            &lt;person xsi:type="tns:Person"&gt;&lt;br /&gt;                &lt;firstname xsi:type="xsd:string"&gt;Willi&lt;/firstname&gt;&lt;br /&gt;                &lt;age xsi:type="xsd:int"&gt;22&lt;/age&gt;&lt;br /&gt;                &lt;gender xsi:type="xsd:string"&gt;male&lt;/gender&gt;&lt;br /&gt;            &lt;/person&gt;&lt;br /&gt;        &lt;/tns:hello&gt;&lt;br /&gt;    &lt;/SOAP-ENV:Body&gt;&lt;br /&gt;&lt;/SOAP-ENV:Envelope&gt;&lt;br /&gt;HTTP/1.1 200 OK&lt;br /&gt;Server: Microsoft-IIS/5.0&lt;br /&gt;Date: Wed, 03 Nov 2004 21:20:44 GMT&lt;br /&gt;X-Powered-By: ASP.NET&lt;br /&gt;X-Powered-By: PHP/4.3.4&lt;br /&gt;Server: NuSOAP Server v0.6.8&lt;br /&gt;X-SOAP-Server: NuSOAP/0.6.8 (1.81)&lt;br /&gt;Content-Type: text/xml; charset=ISO-8859-1&lt;br /&gt;Content-Length: 720&lt;br /&gt;&lt;br /&gt;&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;&lt;br /&gt;&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&lt;br /&gt;                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"&lt;br /&gt;                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;                   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"&lt;br /&gt;                   xmlns:si="http://soapinterop.org/xsd"&lt;br /&gt;                   xmlns:tns="urn:hellowsdl2"&gt;&lt;br /&gt;    &lt;SOAP-ENV:Body&gt;&lt;br /&gt;        &lt;ns1:helloResponse xmlns:ns1="urn:hellowsdl2"&gt;&lt;br /&gt;            &lt;return xsi:type="tns:SweepstakesGreeting"&gt;&lt;br /&gt;                &lt;greeting xsi:type="xsd:string"&gt;&lt;br /&gt;                    Hello, Willi. It is nice to meet a 22 year old male.&lt;br /&gt;                &lt;/greeting&gt;&lt;br /&gt;                &lt;winner xsi:type="xsd:boolean"&gt;0&lt;/winner&gt;&lt;br /&gt;            &lt;/return&gt;&lt;br /&gt;        &lt;/helloResponse&gt;&lt;br /&gt;    &lt;/SOAP-ENV:Body&gt;&lt;br /&gt;&lt;/SOAP-ENV:Envelope&gt;&lt;br /&gt;Return to top.&lt;br /&gt;You can download the source for these examples as well.&lt;br /&gt;&lt;br /&gt;Return to top.&lt;br /&gt;Resources&lt;br /&gt;&lt;br /&gt;Join the NuSOAP mailing list to learn more and ask questions.&lt;br /&gt;The home of the NuSOAP project.&lt;br /&gt;NuSOAP home of Dietrich Ayala, the author of NuSOAP.&lt;br /&gt;&lt;br /&gt;PS : If you use PHP5, Use nusoap_client() instead of soapclient(). PHP5 has some problem with legacy functions on NuSoap. Otherwise you will occurs error [PHP Fatal error:  SoapClient::SoapClient() Invalid parameters]&lt;br /&gt;reference : http://www.scottnichol.com/nusoapprogwsdl.htm&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-7391859995933375657?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/7391859995933375657/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=7391859995933375657' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7391859995933375657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7391859995933375657'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/08/use-nusoap-create-your-web-service.html' title='Use NuSOAP Create your Web Service'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-6904470972574419728</id><published>2011-08-10T02:10:00.000-07:00</published><updated>2011-08-10T02:13:43.454-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VMWare'/><title type='text'>After VMWare converter, Windows failed to start. A recent hardware or software change might be the cause. (0xc0000225)</title><content type='html'>&lt;br /&gt;If you tried to expand a VMware virtual disk on a Windows Vista or Windows Server 2008 VM, or you just messed up your Windows then you might have run into this nasty error when trying to boot:&lt;br /&gt;&lt;br /&gt;Windows failed to start. A recent hardware or software change might be the cause.&lt;br /&gt;&lt;br /&gt;File: \Windows\system23\winload.exe&lt;br /&gt;&lt;br /&gt;Status: 0xc0000225&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-umoG6Yl5wjw/TkJLsT4TaiI/AAAAAAAADMU/rQYUbdsUFUI/s1600/VMWareConverterError0xc0000225.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://1.bp.blogspot.com/-umoG6Yl5wjw/TkJLsT4TaiI/AAAAAAAADMU/rQYUbdsUFUI/s400/VMWareConverterError0xc0000225.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5639152908044036642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Windows failed to start (0xc0000225) – error screen&lt;br /&gt;I got a bit worried when I saw this, since I didnt really want to set up a new VM for my development environment. It was an easy fix though, lucky me. After looking around on the web a bit, it was pretty clear that Windows Vista and Windows Server 2008 really does not like it when you resize its partitions behind its back. It renders the disk unbootable and that needs to be fixed.&lt;br /&gt;&lt;br /&gt;Windows Vista:&lt;br /&gt;&lt;br /&gt;Boot up on your installation DVD. Choose country and keyboard layout and click “Next”. At the next screen and choose “Repair your computer”. It will fix this problem. If you don’t have the installation DVD you might be in trouble &lt;br /&gt;&lt;br /&gt;Windows Server 2008:&lt;br /&gt;&lt;br /&gt;Windows Server 2008 unfortunately is a bit different than Vista when it comes to repairing this partition, but don’t worry it’s quite simple what we need to do.&lt;br /&gt;&lt;br /&gt;Boot up on your Windows DVD.&lt;br /&gt;&lt;br /&gt;Select your keyboard layout and click next and on the next screen click “Repair your computer”&lt;br /&gt;&lt;br /&gt;At the System Recovery Options screen, select the Windows Server 2008 OS from the list, and click “next”&lt;br /&gt;Select the “Command Prompt” option and type:&lt;br /&gt;&lt;br /&gt;cd recovery&lt;br /&gt;startrep&lt;br /&gt;&lt;br /&gt;When it’s done, click “Finish”. It will now reboot. Select “Start Windows Normally” during boot.&lt;br /&gt;&lt;br /&gt;Checkdisk will run automatically before starting windows up, to make sure everything is fine.&lt;br /&gt;&lt;br /&gt;reference : http://code-journey.com/2009/windows-failed-to-start-a-recent-hardware-or-software-change-might-be-the-cause-0xc0000225/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-6904470972574419728?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/6904470972574419728/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=6904470972574419728' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6904470972574419728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6904470972574419728'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/08/after-vmware-converter-windows-failed.html' title='After VMWare converter, Windows failed to start. A recent hardware or software change might be the cause. (0xc0000225)'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-umoG6Yl5wjw/TkJLsT4TaiI/AAAAAAAADMU/rQYUbdsUFUI/s72-c/VMWareConverterError0xc0000225.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5291979795245867240</id><published>2011-07-29T02:23:00.000-07:00</published><updated>2011-07-29T02:24:05.799-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Angile'/><title type='text'>Scrum Project Management Tools</title><content type='html'>http://www.rallydev.com/&lt;br /&gt;&lt;br /&gt;十人以內免費 :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5291979795245867240?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5291979795245867240/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5291979795245867240' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5291979795245867240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5291979795245867240'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/07/scrum-project-management-tools.html' title='Scrum Project Management Tools'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-569197719623166491</id><published>2011-07-13T02:02:00.000-07:00</published><updated>2011-07-13T02:07:52.198-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><title type='text'>Mac OSX Zend Studio 8 newline problem</title><content type='html'>Situation:&lt;br /&gt;I use Zend Studio8 in mac os,&lt;br /&gt;when I use source-&gt;format&lt;br /&gt;&lt;br /&gt;the newline will change to \r\n,&lt;br /&gt;After submit to P4.&lt;br /&gt;&lt;br /&gt;My colleagues open the file in windows platform,&lt;br /&gt;There will appear the extra newline.&lt;br /&gt;&lt;br /&gt;Solution:&lt;br /&gt;Preference -&gt; General -&gt; Workspace -&gt; New text file line delimeter&lt;br /&gt;choose Other -&gt; Unix&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-569197719623166491?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/569197719623166491/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=569197719623166491' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/569197719623166491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/569197719623166491'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/07/mac-osx-zend-studio-8-newline-problem.html' title='Mac OSX Zend Studio 8 newline problem'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-8550526002412474616</id><published>2011-06-27T20:04:00.000-07:00</published><updated>2011-06-27T20:07:22.417-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows系統'/><title type='text'>Why is xcopy returning “invalid number of parameters”?</title><content type='html'>1.The usual solution for this is to be sure that your filenames are enclosed in quotes, as this can be an issue with batch files where you have something like xcopy %1 %2 and you really need xcopy "%1" "%2".&lt;br /&gt;2.xcopy is parsing the forward slashes in the path to its own binary. You can also run into this if you have your PATH defined using forward slashes instead of backslashes.&lt;br /&gt;&lt;br /&gt;reference : http://superuser.com/questions/114178/why-is-xcopy-returning-invalid-number-of-parameters&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-8550526002412474616?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/8550526002412474616/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=8550526002412474616' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8550526002412474616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8550526002412474616'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/06/why-is-xcopy-returning-invalid-number.html' title='Why is xcopy returning “invalid number of parameters”?'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-6314915436508823637</id><published>2011-06-26T03:48:00.000-07:00</published><updated>2011-06-26T05:09:25.392-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows系統'/><title type='text'>利用 XCOPY 和批次檔 .BAT 功能做備份</title><content type='html'>&lt;code&gt;&lt;br /&gt;rem 是註解, 可以依照個人需求 移除 OR 增加 &lt;br /&gt;&lt;br /&gt;代碼:&lt;br /&gt;&lt;br /&gt;rem ----- 檔案名稱 : BACKUP.BAT -------------------------------------------------------&lt;br /&gt;rem ----- 用途 : 利用 XCOPY 和 .BAT 功能做備份 ----------------------------------------&lt;br /&gt;&lt;br /&gt;rem ----- 將 BACKUP.BAT 加入 系統排程 ( 可以手動加入 [開始][程式集][啟動] 中 ) ------  &lt;br /&gt;rem at 23:00 /every:s,m,t,w,th,f,sa "d:\LU\BACKUP.bat" &lt;br /&gt;&lt;br /&gt;rem ----- XCOPY 用法 可用 "XCOPY /?" 查詢 ---------------------------------------------&lt;br /&gt;rem  XCOPY 差異備份常用參數&lt;br /&gt;rem  /D:m-d-y     複製在指定日期當天或之後發生變更的檔案。如果沒有給日期，只複製那些來源檔案日期比目的檔案日期為新的檔案。&lt;br /&gt;rem  /H           時複製隱藏檔和系統檔。&lt;br /&gt;rem  /I           如果目的不存在且複製一個以上的檔案的話，就假?#93;指定的目的一定是目錄。&lt;br /&gt;rem  /S           複製每個目錄及其?#93;含的子目錄，不複製空目錄。&lt;br /&gt;rem  /E           複製每個目錄及其?#93;含的子目錄，?#93;複製空目錄。/S 與 /E 相同，能夠用來修改 /T。&lt;br /&gt;rem  /Y           不要提示您確定是否要覆蓋一個已經存在的檔案。&lt;br /&gt;&lt;br /&gt;rem ----- 增加 route 資料, 廢除 ---------------&lt;br /&gt;rem route add 140.127.177.17 192.168.0.2&lt;br /&gt;&lt;br /&gt;rem ----- 將開始備份時間寫入 log 紀錄中 -----------------------------------------------------------------------&lt;br /&gt;rem %date% 表示日期環境變數, %date:~0,4% 表示  日期環境變數中從第 0 個位置抓出 4 個字&lt;br /&gt;echo %date:~0,4%%date:~5,2%%date:~8,2% 網頁資料備份紀錄檔 &gt; D:\Backup\log\%date:~0,4%%date:~5,2%%date:~8,2%.log &lt;br /&gt;echo 始備份時間 &gt;&gt; D:\Backup\log\%date:~0,4%%date:~5,2%%date:~8,2%.log &lt;br /&gt;TIME /T    &gt;&gt; D:\Backup\log\%date:~0,4%%date:~5,2%%date:~8,2%.log &lt;br /&gt;&lt;br /&gt;rem ------ [完整備份] 將網站資料備份到 以 日期為目錄名稱 的目錄中 --------------------------------------------------------&lt;br /&gt;rem xcopy c:\inetpub D:\Backup\%date:~0,4%%date:~5,2%%date:~8,2% /h /i /s /k /o /y &gt;&gt; D:\Backup\log\%date:~0,4%%date:~5,2%%date:~8,2%.log &lt;br /&gt;&lt;br /&gt;rem ----- [差異備份] 備份 MSN 表情符號, 改名成 .gif 方便?#91;看, 更名失敗表示重複, 可刪除&lt;br /&gt;XCOPY "C:\Documents and Settings\lu\Application Data\Microsoft\MSN Messenger\1061698994\CustomEmoticons" "D:\CustomEmoticons" /D /H /I /S /Y&lt;br /&gt;REN D:\CustomEmoticons\*.dat *.gif&lt;br /&gt;DEL D:\CustomEmoticons\*.dat &lt;br /&gt;&lt;br /&gt;rem ------ [差異備份] 備份 MSN 表情符號 到 File Server 的個人備份區 &lt;br /&gt;XCOPY D:\CustomEmoticons Z:\CustomEmoticons\ /D /H /I /S /Y&lt;br /&gt;&lt;br /&gt;rem ------ [差異備份] 備份 我的最愛 到自己的 D: 磁碟 --------------------------------------------------------------&lt;br /&gt;rem 使用 %USERPROFILE% 環境變數, 它代表 "C:\Documents and Settings\(使用者登入帳號)" &lt;br /&gt;XCOPY %USERPROFILE%\Favorites   "z:\Favorites\"  /D /H /I /S /Y&lt;br /&gt;rem XCOPY "C:\Documents and Settings\lu\Favorites"                                 "D:\Favorites\"  /D /H /I /S /Y&lt;br /&gt;&lt;br /&gt;rem ------ [差異備份] 備份 Outlook Express 6.x 通訊錄 到自己的 D: 磁碟 -------------------------------------------------------------------&lt;br /&gt;XCOPY "%USERPROFILE%\Application Data\Microsoft\Address Book"   "D:\Address Book\"  /D /H /I /S /Y&lt;br /&gt;rem XCOPY "C:\Documents and Settings\lu\Application Data\Microsoft\Address Book"   "D:\Address Book\"  /D /H /I /S /Y&lt;br /&gt;      &lt;br /&gt;rem ------ [差異備份] 備份 我的最愛 到 File Server 的個人備份區 -----------------------------------------------------------&lt;br /&gt;XCOPY %USERPROFILE%\Favorites   "z:\Favorites\"  /D /H /I /S /Y&lt;br /&gt;rem XCOPY "C:\Documents and Settings\lu\Favorites"                                 "z:\Favorites\"  /D /H /I /S /Y&lt;br /&gt;&lt;br /&gt;rem ------ [差異備份] 備份 Outlook Express 6.x 通訊錄 到 File Server 的個人備份區 -----------------------------------------------------------&lt;br /&gt;XCOPY "%USERPROFILE%\Application Data\Microsoft\Address Book"   "z:\Address Book\"  /D /H /I /S /Y&lt;br /&gt;rem XCOPY "C:\Documents and Settings\lu\Application Data\Microsoft\Address Book"   "z:\Address Book\"  /D /H /I /S /Y&lt;br /&gt;&lt;br /&gt;rem ----- [壓縮完整備份] 將 D:\lu 用 WinRAR 壓縮放在 d:\lu.rar 或 d:\日期.rar ----------------------&lt;br /&gt;rem WinRar 是商業軟體, 網?#125; http://www.rarsoft.com/&lt;br /&gt;rem c:\progra~1\winrar\rar a -r d:\lu.rar d:\lu\*&lt;br /&gt;rem c:\progra~1\winrar\rar a -r d:\%date:~0,4%%date:~5,2%%date:~8,2%.rar d:\lu\*&lt;br /&gt;rem "C:\Program Files\WinRar\rar a -r d:\%date:~0,4%%date:~5,2%%date:~8,2%.rar d:\lu\*&lt;br /&gt;&lt;br /&gt;rem ----- [壓縮完整備份] 將 D:\lu 用 7-zip 壓縮 ----------------------&lt;br /&gt;rem 7-Zip 是免費軟體, 網?#125; http://www.7-zip.org/&lt;br /&gt;rem "C:\program Files\7-zip\7z" a -r c:\%date:~0,4%%date:~5,2%%date:~8,2%.7z d:\lu\*.*&lt;br /&gt;rem 壓縮成為 .zip 格式&lt;br /&gt;rem "C:\program Files\7-zip\7z" a -r -tzip c:\%date:~0,4%%date:~5,2%%date:~8,2%.zip d:\lu\*.*&lt;br /&gt;&lt;br /&gt;rem ----- 差異備份, 異動檔案輸出到 1.log -----&lt;br /&gt;rem XCOPY D:\LU Z:\LU\ /D /H /I /S /Y  &gt;&gt; c:\1.log&lt;br /&gt;XCOPY D:\LU Z:\LU\ /D /H /I /S /Y &lt;br /&gt;&lt;br /&gt;rem ------ 備份 Outlook 信件 -------------------------------------------------&lt;br /&gt;rem echo 請先關閉 Outlook , 否則 Outlook 信件無法備份 &lt;br /&gt;rem pause&lt;br /&gt;XCOPY "D:\Outlook"   "z:\Outlook\" /D /H /I /S /Y&lt;br /&gt;&lt;br /&gt;rem ------ 備份 hosts 檔案 ------------------------------------------------- &lt;br /&gt;XCOPY C:\WINDOWS\system32\drivers\etc\hosts  z:\   /D /H /I /S /Y&lt;br /&gt;&lt;br /&gt;rem ------ 輸出備份完成時間 -----------------------------------------------&lt;br /&gt;echo 結束備份時間 &gt;&gt; D:\Backup\log\%date:~0,4%%date:~5,2%%date:~8,2%.log &lt;br /&gt;TIME /T    &gt;&gt; D:\Backup\log\%date:~0,4%%date:~5,2%%date:~8,2%.log &lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;reference : http://www.bestlong.idv.tw/redirect.php?tid=763&amp;goto=lastpost&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-6314915436508823637?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/6314915436508823637/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=6314915436508823637' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6314915436508823637'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6314915436508823637'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/06/xcopy-bat.html' title='利用 XCOPY 和批次檔 .BAT 功能做備份'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5765598839314939831</id><published>2011-06-21T16:32:00.000-07:00</published><updated>2011-06-21T16:33:35.999-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xslt'/><title type='text'>XSLT 筆記</title><content type='html'>&lt;p&gt;&lt;span id="more-1004"&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;PS: 這是筆記，不是詳細的教學手冊&lt;/p&gt; &lt;br /&gt;&lt;p&gt;◎ XSLT 可用於 XML 轉換成其他格式的 XML, HTML 或其他格式檔，所以，將它視為&lt;/p&gt; &lt;br /&gt;&lt;ul&gt; &lt;br /&gt;&lt;li&gt;檔案格式轉換程式&lt;/li&gt; &lt;br /&gt;&lt;li&gt;或，一種 Template Engine&lt;/li&gt; &lt;br /&gt;&lt;/ul&gt; &lt;br /&gt;&lt;p&gt;◎ 和 DOM Tree 的處理一樣，藉由 XPath 的方式來「指定」XML 文件內的特定 element，然後進行各 element 內容的萃取以及處理，一些可以縮寫的 XPath 語法&lt;/p&gt; &lt;br /&gt;&lt;table style="text-align: left; width: 100%" border="1" cellpadding="2" cellspacing="2"&gt; &lt;br /&gt;&lt;tbody&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;非縮寫&lt;/td&gt; &lt;br /&gt;&lt;td&gt;縮寫&lt;/td&gt; &lt;br /&gt;&lt;td&gt;說明&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;/&lt;/td&gt; &lt;br /&gt;&lt;td&gt;/&lt;/td&gt; &lt;br /&gt;&lt;td&gt;root&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;child:para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;目前節點下所有 para 的子點 （只有下一層)&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;/child:para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;/para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;root 下所有 para 的子點&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;descendant::para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;.//para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;目前節點下所有 para 的子點 （不限只有下一層)&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;/descendant::para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;//para&lt;/td&gt; &lt;br /&gt;&lt;td&gt;root 節點下所有 para 的子點 （不限只有下一層)&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;attribute::id&lt;/td&gt; &lt;br /&gt;&lt;td&gt;@id&lt;/td&gt; &lt;br /&gt;&lt;td&gt;具有 id 的屬性的節點&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;attribute::*&lt;/td&gt; &lt;br /&gt;&lt;td&gt;@*&lt;/td&gt; &lt;br /&gt;&lt;td&gt;所有屬性的節點&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;child::chapter[position()=5]&lt;/td&gt; &lt;br /&gt;&lt;td&gt;chapter[5]&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;child::chapter[attribute::id="this-one"]&lt;/td&gt; &lt;br /&gt;&lt;td&gt;chapter[@id="this-one"]&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;child::chapter[not(attribute::include="y")]&lt;/td&gt; &lt;br /&gt;&lt;td&gt;child::chapter[not(@include="y")]&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;/descendant::text()&lt;/td&gt; &lt;br /&gt;&lt;td&gt;//text()&lt;/td&gt; &lt;br /&gt;&lt;td&gt;目前節點下的所有文字節點&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;child::part[attribute::num="1"][attribute::draft="y"]&lt;/td&gt; &lt;br /&gt;&lt;td&gt;part[@num="1"][@draft="y"]&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;parent::node()[attribute::include="y"]&lt;/td&gt; &lt;br /&gt;&lt;td&gt;..[@include="y"]&lt;/td&gt; &lt;br /&gt;&lt;td&gt;選擇 include 屬性內包含 "y" 的母節點&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;tr&gt; &lt;br /&gt;&lt;td&gt;/descendent::chapter[attribute::id and attribute::include]&lt;/td&gt; &lt;br /&gt;&lt;td&gt;//chapter[@id and @include]&lt;/td&gt; &lt;br /&gt;&lt;/tr&gt; &lt;br /&gt;&lt;/tbody&gt; &lt;br /&gt;&lt;/table&gt; &lt;br /&gt;&lt;p&gt;◎ 例子： 資料檔假設 data.xml&lt;/p&gt; &lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;Position&amp;gt;&lt;br /&gt;    &amp;lt;X&amp;gt;9&amp;lt;/X&amp;gt;&lt;br /&gt;    &amp;lt;Y&amp;gt;9&amp;lt;/Y&amp;gt;&lt;br /&gt;&amp;lt;/Position&amp;gt;&lt;br /&gt;&amp;lt;Position&amp;gt;&lt;br /&gt;    &amp;lt;X&amp;gt;10&amp;lt;/X&amp;gt;&lt;br /&gt;    &amp;lt;Y&amp;gt;9&amp;lt;/Y&amp;gt;&lt;br /&gt;&amp;lt;/Position&amp;gt;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;p&gt;我們可以撰寫一個 xsl 檔，假設為 extract.xsl&lt;/p&gt; &lt;br /&gt;&lt;pre class="prettyprint"&gt; &lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&amp;gt;&lt;br /&gt;    &amp;lt;xsl:output method="text"/&amp;gt;&lt;br /&gt; &lt;br /&gt;    &amp;lt;xsl:template match="Position"&amp;gt;&lt;br /&gt;           &amp;lt;xsl:value-of select="./X" /&amp;gt;&lt;br /&gt;           &amp;lt;xsl:text&amp;gt;,&amp;lt;/xsl:text&amp;gt;&lt;br /&gt;           &amp;lt;xsl:value-of select="./Y" /&amp;gt;&lt;br /&gt;    &amp;lt;/xsl:template&amp;gt;&lt;br /&gt; &lt;br /&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;p&gt;然後，我們可以抓個 XSLT Processor，比如 &lt;a target=_blank href="http://saxon.sourceforge.net/"&gt;Saxon&lt;/a&gt;&lt;img src="http://blog.derjohng.com/wp-content/plugins/dj-light-box/external.gif" /&gt;&amp;nbsp;，執行&lt;/p&gt; &lt;br /&gt;&lt;pre class="prettyprint"&gt; &lt;br /&gt;java -jar saxon.jar data.xml convert.xsl&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;p&gt;就會產生&lt;/p&gt; &lt;br /&gt;&lt;pre class="prettyprint"&gt; &lt;br /&gt;9,9&lt;br /&gt;10,9&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;p&gt;的結果&lt;/p&gt; &lt;br /&gt;&lt;p&gt;◎ 當然，有其他更複雜的操作方式，略記一些常用的語法&lt;/p&gt; &lt;br /&gt;&lt;ul&gt; &lt;br /&gt;&lt;li&gt;xsl:apply-templates    整個 XSLT 的核心語法&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:template&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:value-of  select=xxx   將結果輸出&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:import  href=xxx   可以 reuse 其他 xsl 檔&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:include   同前者，唯相同定義出現，這個會中止 compile.&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:strip-space xsl:preserve-space   處理空白&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:output&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:key&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:decimal-format  用來設定數字的格式&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:namespace-alias&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:attribute-set   設定屬性組&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:variable  xsl:param   處理一些變數運算&lt;/li&gt; &lt;br /&gt;&lt;li&gt;條件式判斷&lt;br /&gt;&lt;ul&gt; &lt;br /&gt;&lt;li&gt;xsl: if test="xxx=1"&lt;/li&gt; &lt;br /&gt;&lt;li&gt;xsl:choose   xsl:when&lt;/li&gt; &lt;br /&gt;&lt;/ul&gt; &lt;br /&gt;&lt;/li&gt; &lt;br /&gt;&lt;/ul&gt; &lt;br /&gt;&lt;p&gt;◎ Saxon 是 for Java, 若要 C/C++ 版本，可以考慮&lt;/p&gt; &lt;br /&gt;&lt;ul&gt; &lt;br /&gt;&lt;li&gt;Xalan&lt;/li&gt; &lt;br /&gt;&lt;li&gt;Sablontron  (with expat XML processor)&lt;/li&gt; &lt;br /&gt;&lt;/ul&gt; &lt;br /&gt;&lt;p&gt;◎ MacOSX 下用 MacPort 安裝&lt;/p&gt; &lt;br /&gt;&lt;pre class="prettyprint"&gt; &lt;br /&gt;sudo port install saxon&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;p&gt;安裝在  /opt/local/var/macports/software/saxon/8.9_0 目錄，可以在 .profile 內加下述這行，以簡化指令執行&lt;/p&gt; &lt;br /&gt;&lt;pre class="prettyprint"&gt; &lt;br /&gt;alias saxon='java -jar /opt/local/var/macports/software/saxon/8.9_0/opt/local/share/java/saxon8.jar'&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;p&gt;但要注意 saxon8 支援 XSLT2.0，所以，可能執行時會出現下述訊息&lt;/p&gt; &lt;br /&gt;&lt;pre class="prettyprint"&gt; &lt;br /&gt;Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;p&gt;只要在 xsl:stylesheet version 的部份由原 1.0 改成 2.0 即可 pass 該訊息。&lt;/p&gt; &lt;br /&gt;&lt;p&gt;◎ 參考：&lt;/p&gt; &lt;br /&gt;&lt;ul&gt; &lt;br /&gt;&lt;li&gt;&lt;a target=_blank href="http://www.learn-xslt-tutorial.com/"&gt;XSLT Tutorial&lt;/a&gt;&lt;img src="http://blog.derjohng.com/wp-content/plugins/dj-light-box/external.gif" /&gt;&amp;nbsp;&lt;/li&gt; &lt;br /&gt;&lt;li&gt;&lt;a target=_blank href="http://www.w3schools.com/xsl/xsl_server.asp"&gt;XSLT on the Server&lt;/a&gt;&lt;img src="http://blog.derjohng.com/wp-content/plugins/dj-light-box/external.gif" /&gt;&amp;nbsp;&lt;/li&gt; &lt;br /&gt;&lt;/ul&gt; &lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;reference : http://blog.derjohng.com/2008/10/16/xslt-%E7%AD%86%E8%A8%98/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5765598839314939831?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5765598839314939831/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5765598839314939831' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5765598839314939831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5765598839314939831'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/06/xslt.html' title='XSLT 筆記'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1735121625152262841</id><published>2011-06-06T20:55:00.000-07:00</published><updated>2011-06-07T01:52:29.985-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-JavaScript'/><title type='text'>Essential JavaScript Design Patterns For Beginners</title><content type='html'>Copyright 2011 © Addy Osmani. Last edited: May 23rd 2011.&lt;br /&gt;&lt;br /&gt;Creative Commons Attribution-NonCommercial-ShareAlike 3.0 unported license. You are free to remix, tweak, and build upon this work non-commercially, as long as you credit Addy Osmani (the copyright holder) and license your new creations under the identical terms. Any of the above conditions can be waived if you get permission from the copyright holder. For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to the license.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Foreword&lt;br /&gt;&lt;br /&gt;I would like to thank Rebecca Murphey for inspiring me to open-source this mini-book and release it for free download and distribution - making knowledge both open and easily available is something we should all strive for where possible. I would also like to extend my thanks to the very talented Alex Sexton who was kind enough to be the technical reviewer for this publication. I hope that it helps you learn more about design patterns and the usefulness of their application to JavaScript code.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Introduction&lt;br /&gt;&lt;br /&gt;At the beginning of this book I will be focusing on a discussion about the importance and history of design patterns in any programming language. If you're already sold on or are familiar with this history, feel free to skip to the chapter 'What is a Pattern?' to continue reading.&lt;br /&gt;&lt;br /&gt;One of the most important aspects of writing maintainable code is being able to notice the recurring themes in that code and optimize them. This is an area where knowledge of design patterns can prove invaluable.&lt;br /&gt;&lt;br /&gt;Design patterns can be traced back to the early work of a civil engineer named Christopher Alexander. He would often write publications about his experience in solving design issues and how they related to buildings and towns. One day, it occurred to Alexander that when used time and time again, certain design constructs lead to a desired optimal effect.&lt;br /&gt;&lt;br /&gt;In collaboration with Sarah Ishikawra and Murray Silverstein, Alexander produced a pattern language that would help empower anyone wishing to design and build at any scale. This was published back in 1977 in a paper titled 'A Pattern Language'.&lt;br /&gt;&lt;br /&gt;Some 30 years ago, software engineers began to incorporate the principles Alexander had written about into the first documentation about design patterns, which was to be a guide for novice developers looking to improve their coding skills. It's important to note that the concepts behind design patterns have actually been around in the programming industry more than likely since its inception, albeit in a less formalized form.&lt;br /&gt;&lt;br /&gt;One of the first and arguably most iconic formal works published on design patterns in software engineering was a book in 1995 called 'Design Patterns: Elements Of Reusable Object-Oriented Software'. This was written by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides - a group that became known as the Gang of Four (or GoF for short).&lt;br /&gt;&lt;br /&gt;The GoF's publication is considered quite instrumental to pushing the concept of design patterns further in our field as it describes a number of development techniques and pitfalls as well as providing twenty-three core Object-Oriented design patterns frequently used around the world today. We will be covering these patterns in more detail in the section ‘Categories of Design Patterns’.&lt;br /&gt;&lt;br /&gt;In this book, we will take a look at a number of popular JavaScript design patterns and explore why certain patterns may be more suitable for your projects than others. Remember that patterns can be applied not just to vanilla JavaScript, but also to abstracted libraries such as jQuery or Dojo as well. Before we begin, let’s look at the exact definition of a ‘pattern’ in software design.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Contents&lt;br /&gt;&lt;br /&gt;What is a Pattern?&lt;br /&gt;'Pattern'-ity Testing, Proto-Patterns &amp; The Rule Of Three&lt;br /&gt;The Structure Of A Design Pattern&lt;br /&gt;Writing Design Patterns&lt;br /&gt;Anti-Patterns&lt;br /&gt;Categories Of Design Pattern&lt;br /&gt;Design Patterns in JavaScript&lt;br /&gt;Creational Pattern&lt;br /&gt;Constructor Pattern&lt;br /&gt;Singleton Pattern&lt;br /&gt;Module Pattern&lt;br /&gt;Revealing Module Pattern&lt;br /&gt;Observer Pattern&lt;br /&gt;Prototype Pattern&lt;br /&gt;Command Pattern&lt;br /&gt;DRY Pattern&lt;br /&gt;Facade Pattern&lt;br /&gt;Factory Pattern&lt;br /&gt;Mixin Pattern&lt;br /&gt;Decorator Pattern&lt;br /&gt;A High-level View Of Design Patterns in jQuery&lt;br /&gt;Lazy Initialisation&lt;br /&gt;Composite Pattern&lt;br /&gt;Wrapper Pattern&lt;br /&gt;Facade Pattern&lt;br /&gt;Observer Pattern&lt;br /&gt;Iterator Pattern&lt;br /&gt;Strategy Pattern&lt;br /&gt;Proxy Pattern&lt;br /&gt;Builder Pattern&lt;br /&gt;Prototype Pattern&lt;br /&gt;Flyweight Pattern&lt;br /&gt;Conclusions&lt;br /&gt;References&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;What is a Pattern?&lt;br /&gt;&lt;br /&gt;A pattern is a reusable solution that can be applied to commonly occurring problems in software design - in our case - in writing JavaScript applications. Another way of looking at patterns are as templates for how you solve problems - ones which can be used in quite a few different situations.&lt;br /&gt;&lt;br /&gt;To consider how useful a pattern may be, let us consider that if you were to write a script where you said ‘for each item, sound an alert’, if sounding an alert was complex in nature, it would always result in more maintainable code doing the above over saying ‘do this for item 1’, ‘do this for item 2’, ‘do the same again for item 3’, i.e. If the code performing the bulk of the work exists in fewer places it becomes significantly easier to maintain.&lt;br /&gt;&lt;br /&gt;You may ask why it’s important to understand patterns and be familiar with them. Design patterns have three main benefits:&lt;br /&gt;&lt;br /&gt;Patterns are proven solutions: They provide solid approaches to solving issues in software development using proven solutions that reflect the experience and insights the developers that helped define and improve them bring to the pattern.&lt;br /&gt;Patterns can be easily re-used: A pattern usually reflects an out of the box solution that can be adapted to suit your own needs. This feature makes them quite robust.&lt;br /&gt;Patterns can be expressive: When you look at a pattern there’s generally a set structure and ‘vocabulary’ to the solution presented that can help express rather large solutions quite elegantly.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Patterns are not an exact solution. It’s important that we remember the role of a pattern is merely to provide us with a solution scheme. Patterns don’t solve all design problems nor do they replace good software designers, however, they do support them. Next we’ll take a look at some of the other advantages patterns can offer us.&lt;br /&gt;&lt;br /&gt;Reusing patterns assists in preventing minor issues that can cause major problems in the application development process. What this means is that when your code relies more on proven patterns, you can afford to spend less time worrying about your code architecture and more time focusing on the quality of your overall solution. This is because patterns can encourage you to code in a more structured and organized fashion so the need to refactor it for cleanliness purposes in the future can be significantly decreased.&lt;br /&gt;Patterns can provide generalized solutions which are documented in a fashion that doesn't require them to be tied to a specific problem. This generalized approach means that regardless of the application (and in many cases the programming language) you are working with, design patterns can be applied to improve the structure of your code.&lt;br /&gt;Certain patterns can actually decrease the overall file-size footprint of your code by avoiding repetition. By encouraging developers to look more closely at their solutions for areas where instant reductions in repetition can be made, e.g. reducing the number of functions performing similar processes in favor of a single generalized function, the overall size of your codebase can be decreased.&lt;br /&gt;Patterns that are frequently used can be improved over time by harnessing the collective experiences other developers using those patterns contribute back to the design pattern community. In some cases this leads to the creation of entirely new design patterns whilst in others it can lead to the provision of improved guidelines on how specific patterns can be best used. This can ensure that pattern-based solutions continue to become more robust than ad-hoc solutions may be.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;'Pattern'-ity Testing, Proto-Patterns &amp; The Rule Of Three&lt;br /&gt;&lt;br /&gt;Remember that not every algorithm, best practice or solution represents what might be considered a complete pattern. There may be a few key ingredients here that are missing and the pattern community is generally weary of something claiming to be one unless it has been heavily vetted. Even if something is presented to us which *appears* to meet the criteria for a pattern, it should not be considered one until it has undergone suitable periods of scrutiny and testing by others.&lt;br /&gt;&lt;br /&gt;Looking back upon the work by Alexander once more, he claims that a pattern should both be a process and a ‘thing’. This definition is obtuse on purpose as he follows by saying that the process should create the ‘thing’. This is a reason why patterns generally focus on addressing a visually identifiable structure i.e you should be able to visually depict (or draw) a picture representing the structure that placing the pattern into practice results in. &lt;br /&gt;&lt;br /&gt;In studying design patterns, you may come across the term ‘proto-pattern’ quite frequently. What is this? Well, a pattern that has not yet been known to pass the ‘pattern’-ity tests is usually referred to as a proto-pattern. Proto-patterns may result from the work of someone that has established a particular solution that is worthy of sharing with the community, but may not have yet had the opportunity to have been vetted heavily due to it’s very young age.&lt;br /&gt;&lt;br /&gt;Alternatively, the individual(s) sharing the pattern may not have the time or interest of going through the ‘pattern’-ity process and might release a short description of their proto-pattern instead. Brief descriptions of this type of pattern are known as patlets.&lt;br /&gt;&lt;br /&gt;The work involved in fully documenting a qualified pattern can be quite daunting. Looking back at some of the earliest work in the field of design patterns, a pattern may be considered ‘good’ if it does the following:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Solves a particular problem - patterns are not supposed to just capture principles or strategies. They need to capture solutions. This is one of the most essential ingredients for a good pattern.&lt;br /&gt;The solution to this problem cannot be obvious - you can often find that problem-solving techniques attempt to derive from well-known first principles. The best design patterns usually provide solutions to problems indirectly - this is considered a necessary approach for the most challenging problems related to design.&lt;br /&gt;The concept described must have been proven - design patterns require proof that they function as described and without this proof the design cannot be seriously considered. If a pattern is highly speculative in nature, only the brave may attempt to use it.&lt;br /&gt;It must describe a relationship - in some cases it may appear that a pattern describes a type of module. Although an implementation may appear this way, the official description of the pattern must describe much deeper system structures and mechanisms that explain it’s relationship to code.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;You wouldn’t be blamed for thinking that a proto-pattern that doesn’t meet the guidelines for a complete pattern isn’t worth investigating, but this is far from the truth. Many proto-patterns are actually quite good. I’m not saying that all proto-patterns are worth looking at, but there are quite a few useful ones in the wild that could assist you with future projects. Use best judgment with the above list in mind and you’ll be fine in your selection process. &lt;br /&gt;&lt;br /&gt;One of the additional requirements for a pattern to be valid is that they display some recurring phenomenon. This is often something that can be qualified in at least three key areas, referred to as the rule of three. To show recurrence using this rule, one must demonstrate:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Fitness of purpose - how is the pattern considered successful?&lt;br /&gt;Usefulness - why is the pattern considered successful?&lt;br /&gt;Applicability - is the design worthy of being a pattern because it has wider applicability? If so, this needs to be explained.When reviewing or defining a pattern, it is important to keep the above in mind.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Structure Of A Design Pattern&lt;br /&gt;&lt;br /&gt;When studying design patterns, you may wonder what teams that create them have to put in their design pattern descriptions.  Every pattern has to initially be formulated in a form of a rule that establishes a relationship between a context, a system of forces that arises in that context and a configuration that allows these forces to resolve themselves in context. &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;I find that a lot of the information available out there about the structure of a good pattern can be condensed down to something more easily digestible.With this in mind, lets now take a look at a summary of the component elements for a design pattern.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;A design pattern must have a:&lt;br /&gt;&lt;br /&gt;Pattern Name and a description&lt;br /&gt;Context Outline – the contexts in which the pattern is effective in responding to the users needs.&lt;br /&gt;Problem Statement – a statement of the problem being addressed so we can understand the intent of the pattern.&lt;br /&gt;Solution – a description of how the user’s problem is being solved in an understandable list of steps and perceptions.&lt;br /&gt;Design – a description of the pattern’s design and in particular, the user’s behavior in interacting with it&lt;br /&gt;Implementation – a guide to how the pattern would be implemented&lt;br /&gt;Illustrations – a visual representation of classes in the pattern (eg. a diagram))&lt;br /&gt;Examples – an implementation of the pattern in a minimal form&lt;br /&gt;Co-requisites – what other patterns may be needed to support use of the pattern being described?&lt;br /&gt;Relations – what patterns does this pattern resemble? does it closely mimic any others?&lt;br /&gt;Known usage – is the pattern being used in the ‘wild’?. If so, where and how?&lt;br /&gt;Discussions – the team or author’s thoughts on the exciting benefits of the pattern&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Design patterns are quite a powerful approach to getting all of the developers in an organization or team on the same page when creating or maintaining solutions. If you or your company ever consider working on your own pattern, remember that although they may have a heavy initial cost in the planning and write-up phases, the value returned from that investment can be quite worth it. Always research thoroughly before working on new patterns however, as you may find it more beneficial to use or build on top of existing proven patterns than starting afresh.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Writing Design Patterns&lt;br /&gt;&lt;br /&gt;Although this book is aimed at those new to design patterns, a fundamental understanding of how a design pattern is written can offer you a number of useful benefits. For starters, you can gain a deeper appreciation for the reasoning behind a pattern being needed but can also learn how to tell if a pattern (or proto-pattern) is up to scratch when reviewing it for your own needs.&lt;br /&gt;&lt;br /&gt;Writing good patterns is a challenging task. Patterns not only need to provide a substantial quantity of reference material for end-users (such as the items found in the structure section above), but they also need to be able to almost tell a ‘story’ that describes the experience they are trying to convey. If you’ve already read the previous section on ‘what’ a pattern is, you may think that this in itself should help you identify patterns when you see them in the wild. This is actually quite the opposite - you can’t always tell if a piece of code you’re inspecting follows a pattern.&lt;br /&gt;&lt;br /&gt;When looking at a body of code that you think may be using a pattern, you might write down some of the aspects of the code that you believe falls under a particular existing pattern, but it may not be a one at all. In many cases of pattern-analysis you’ll find that you’re just looking at code that follows good principles and design practices that could happen to overlap with the rules for a pattern by accident. Remember - solutions in which neither interactions nor defined rules appear are not patterns. &lt;br /&gt;&lt;br /&gt;If you’re interested in venturing down the path of writing your own design patterns I recommend learning from others who have already been through the process and done it well. Spend time absorbing the information from a number of different design pattern descriptions and books and take in what’s meaningful to you - this will help you accomplish the goals you’ve got of designing the pattern you want to achieve. You’ll probably also want to examine the structure and semantics of existing patterns - this can be begun by examining the interactions and context of the patterns you are interested in so you can identify the principles that assist in organizing those patterns together in useful configurations.&lt;br /&gt;&lt;br /&gt;Once you’ve exposed yourself to a wealth of information on pattern literature, you may wish to begin your pattern using an existing format and see if you can brainstorm new ideas for improving it or integrating your ideas in there.  An example of someone that did this quite recently is JavaScript developer Christian Heilmann, who took an existing pattern called the module pattern and made some fundamentally useful changes to it to create the revealing module pattern (this is one of the patterns covered later in this book). &lt;br /&gt;&lt;br /&gt;If you would like to try your hand at writing a design pattern (even if just for the learning experience of going through the process), the tips I have for doing so would be as follows:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Bare in mind practicability: Ensure that your pattern describes proven solutions to recurring problems rather than just speculative solutions which haven’t been qualified.&lt;br /&gt;Ensure that you draw upon best practices: The design decisions you make should be based on principles you derive from an understanding of best practices.&lt;br /&gt;Your design patterns should be transparent to the user: Design patterns should be entirely transparent to any type of user-experience. They are primarily there to serve the developers using them and should not force changes to behaviour in the user-experience that would not be incurred without the use of a pattern.&lt;br /&gt;Remember that originality is not key in pattern design: When writing a pattern, you do not need to be the original discoverer of the solutions being documented nor do you have to worry about your design overlapping with minor pieces of other patterns.If your design is strong enough to have broad useful applicability, it has a chance of being recognized as a proper pattern&lt;br /&gt;Know the differences between patterns and design: A design pattern generally draws from proven best practice and serves as a model for a designer to create a solution. The role of the pattern is to give designers guidance to make the best design choices so they can cater to the needs of their users.&lt;br /&gt;Your pattern needs to have a strong set of examples: A good pattern description needs to be followed by an equally strong set of examples demonstrating the successful application of your pattern. To show broad usage, examples that exhibit good design principles are ideal.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Pattern writing is a careful balance between creating a design that is general, specific and above all, useful. Try to ensure that if writing a pattern you cover the widest possible areas of application and you should be fine.  I hope that this brief introduction to writing patterns has given you some insights that will assist your learning process for the next sections of this book.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Anti-Patterns&lt;br /&gt;&lt;br /&gt;If we consider that a pattern represents a best practice, an anti-pattern represents a lesson that has been learned. The term anti-patterns was coined in 1995 by Andrew Koenig in the November C++ Report that year. It was inspired by the Gang of Four's book Design Patterns, that developed the concept of design patterns in the software field. In Koenig’s report, there are two notions of anti-patterns that are presented. Anti-Patterns:&lt;br /&gt;&lt;br /&gt;Describe a bad solution to a particular problem which resulted in a bad situation occurring&lt;br /&gt;Describe how to get out of said situation and how to go from there to a good solution&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;On this topic, Alexander writes about the difficulties in achieving a good balance between good design structure and good context:&lt;br /&gt;&lt;br /&gt;“These notes are about the process of design; the process of inventing physical things which display a new physical order, organization, form, in response to function.…every design problem begins with an effort to achieve fitness between two entities: the form in question and its context. The form is the solution to the problem; the context defines the problem”.&lt;br /&gt;&lt;br /&gt;While it’s quite important to be aware of design patterns, it can be equally important to understand anti-patterns. Let us qualify the reason behind this. When creating an application, a project’s life-cycle begins with construction however once you’ve got the initial release done, it needs to be maintained. The quality of a final solution will either be good or bad, depending on the level of skill and time the team have invested in it. Here good and bad are considered in context - a ‘perfect’ design may qualify as an anti-pattern if applied in the wrong context. &lt;br /&gt;&lt;br /&gt;The bigger challenges happen after an application has hit production and is ready to go into maintenance mode. A developer working on such a system who hasn’t worked on the application before may introduce a bad design into the project by accident. If said bad practices are created as anti-patterns, they allow developers a means to recognize these in advance so that they can avoid common mistakes that can occur - this is parallel to the way in which design patterns provide us with a way to recognize common techniques that are useful. &lt;br /&gt;&lt;br /&gt;To summarize, an anti-pattern is a bad design that is worthy of documenting. Examples of anti-patterns in JavaScript are the following:&lt;br /&gt;&lt;br /&gt;Polluting the namespace by defining a large number of variables in the global context&lt;br /&gt;Passing strings rather than functions to either setTimeout or setInterval as this triggers the use of eval() internally.&lt;br /&gt;Prototyping against the Object object (this is a particularly bad anti-pattern)&lt;br /&gt;Using JavaScript in an inline form as this is inflexible&lt;br /&gt;The use of document.write where native DOM alternatives such as document.createElement are more appropriate. document.write has been grossly misused over the years and has quite a few disadvantages including that if it's executed after the page has been loaded it can actually overwrite the page you're on, whilst document.createElement does not. You can see here for a live example of this in action. It also doesn't work with XHTML which is another reason opting for more DOM-friendly methods such as document.createElement is favorable.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Knowledge of anti-patterns is critical for success. Once you are able to recognize such anti-patterns, you will be able to refactor your code to negate them so that the overall quality of your solutions improves instantly.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Categories Of Design Pattern&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;A glossary from the well-known design book, Domain-Driven Terms, rightly states that:&lt;br /&gt;&lt;br /&gt;“A design pattern names, abstracts, and identifies the key aspects of a common design structure that make it useful for creating a reusable object-oriented design. The design pattern identifies the participating classes and their instances, their roles and collaborations, and the distribution of responsibilities.&lt;br /&gt;&lt;br /&gt;Each design pattern focuses on a particular object-oriented design problem or issue. It describes when it applies, whether or not it can be applied in view of other design constraints, and the consequences and trade-offs of its use. Since we must eventually implement our designs, a design pattern also provides sample ... code to illustrate an implementation.&lt;br /&gt;&lt;br /&gt;Although design patterns describe object-oriented designs, they are based on practical solutions that have been implemented in mainstream object-oriented programming languages ....”&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Design patterns can be broken down into a number of different categories. In this section we’ll review three of these categories and briefly mention a few examples of the patterns that fall into these categories before exploring specific ones in more detail.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Creational Design Patterns&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Creational design patterns focus on handling object creation mechanisms where objects are created in a manner suitable for the situation you are working in. The basic approach to object creation might otherwise lead to added complexity in a project whilst creational patterns aim to solve this problem by controlling the creation of such objects.&lt;br /&gt;&lt;br /&gt;Some of the patterns that fall under this category are: Factory, Abstract, Prototype, Singleton and Builder.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Structural Design Patterns&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Structural patterns focus on the composition of classes and objects. Structural ‘class’ creation patterns use inheritance to compose interfaces whilst ‘object’ patterns define methods to create objects to obtain new functionality. &lt;br /&gt;&lt;br /&gt;Patterns that fall under this category include: Decorator, Facade, Composite, Adapter and Bridge&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Behavioral Design Patterns&lt;br /&gt;&lt;br /&gt;The main focus behind this category of patterns is the communication between a class’s objects. By specifically targeting this problem, these patterns are able to increase the flexibility in carrying out this communication.&lt;br /&gt;&lt;br /&gt;Some behavioral patterns include: Iterator, Mediator, Observer and Visitor.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Summary Table Of Design Pattern Categorization&lt;br /&gt;&lt;br /&gt;In my early experiences of learning about design patterns, I personally found the following table a very useful reminder of what a number of patterns has to offer - it covers the 23 Design Patterns mentioned by the GoF. The original table was summarized by Elyse Nielsen back in 2004 and I've modified it where necessary to suit our discussion in this section of the book.&lt;br /&gt;&lt;br /&gt;I recommending using this table as reference, but do remember that there are a number of additional patterns that are not mentioned here but will be discussed later in the book. Also bare in mind that there will be patterns in this table that reference the concept of 'classes' - something which can be simulated, but which don't come with out-of-the box support in JavaScript.&lt;br /&gt;&lt;br /&gt;That said, it's still a great starting point for learning and I recommend reviewing it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  Creational	  Based on the concept of creating an object.&lt;br /&gt;    Class&lt;br /&gt;      Factory Method	This makes an instance of several derived classes based on interfaced data or events.&lt;br /&gt;    Object&lt;br /&gt;      Abstract Factory	Creates an instance of several families of classes without detailing concrete classes.&lt;br /&gt;      Builder	Separates object construction from its representation, always creates the same type of object.&lt;br /&gt;      Prototype	A fully initialized instance used for copying or cloning.&lt;br /&gt;      Singleton	A class with only a single instance with global access points.&lt;br /&gt; 	 	 	 	 	 	 	 &lt;br /&gt;  Structural	  Based on the idea of building blocks of objects&lt;br /&gt;    Class&lt;br /&gt;      Adapter	 Match interfaces of different classes therefore classes can work together despite incompatible interfaces&lt;br /&gt;    Object&lt;br /&gt;      Adapter	 Match interfaces of different classes therefore classes can work together despite incompatible interfaces&lt;br /&gt;      Bridge	Separates an object's interface from its implementation so the two can vary independently&lt;br /&gt;      Composite	 A structure of simple and composite objects which makes the total object more than just the sum of its parts.&lt;br /&gt;      Decorator	 Dynamically add alternate processing to objects.&lt;br /&gt;      Facade	 A single class that hides the complexity of an entire subsystem.&lt;br /&gt;      Flyweight	 A fine-grained instance used for efficient sharing of information that is contained elsewhere.&lt;br /&gt;      Proxy	 A place holder object representing the true object&lt;br /&gt; &lt;br /&gt;  Behavioral	  Based on the way objects play and work together.&lt;br /&gt;    Class&lt;br /&gt;      Interpreter	 A way to include language elements in an application to match the grammer of the intended language.&lt;br /&gt;      Template &lt;br /&gt;       Method	Creates the shell of an algorithm in a method, then defer the exact steps to a subclass.&lt;br /&gt;    Object&lt;br /&gt;      Chain of &lt;br /&gt;      Responsibility	 A way of passing a request between a chain of objects to find the object that can handle the request.&lt;br /&gt;      Command	 Encapsulate a command request as an object to enable, logging and/or queuing of requests, and provides error-handling for unhandled requests.&lt;br /&gt;      Iterator	 Sequentially access the elements of a collection without knowing the inner workings of the collection.&lt;br /&gt;      Mediator	 Defines simplified communication between classes to prevent a group of classes from referring explicitly to each other.&lt;br /&gt;      Memento	 Capture an object's internal state to be able to restore it later.&lt;br /&gt;      Observer	 A way of notifying change to a number of classes to ensure consistency between the classes.&lt;br /&gt;      State	 Alter an object's behavior when its state changes&lt;br /&gt;      Strategy	 Encapsulates an algorithm inside a class separating the selection from the implementation&lt;br /&gt;      Visitor	 Adds a new operation to a class without changing the class&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Design Patterns In JavaScript&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Next we’re going to take a look at 10 popular design patterns that I’ve personally found very useful to apply in JavaScript applications over the years.&lt;br /&gt;&lt;br /&gt;Note that there is no ‘ideal’ pattern to use from this selection as developers often use best judgment when deciding on the pattern, which is the best ‘fit’ for their needs.&lt;br /&gt;&lt;br /&gt;Each pattern varies in complexity, however I have tried to keep my explanations as simple as possible so that both beginners and intermediate developers can benefit from the material.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The patterns we will be exploring are the:&lt;br /&gt;&lt;br /&gt;Creational Pattern&lt;br /&gt;Constructor Pattern&lt;br /&gt;Singleton Pattern&lt;br /&gt;Module Pattern&lt;br /&gt;Revealing Module Pattern&lt;br /&gt;Observer Pattern&lt;br /&gt;Prototype Pattern&lt;br /&gt;Command Pattern&lt;br /&gt;DRY Pattern&lt;br /&gt;Facade Pattern&lt;br /&gt;Factory Pattern&lt;br /&gt;Mixin Pattern&lt;br /&gt;Decorator Pattern&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Creational Pattern&lt;br /&gt;&lt;br /&gt;This pattern is the basis for a number of other patterns in this section and is actually quite straightforward to understand. As you might guess, a creational pattern deals with the concept of creating objects within an application. In JavaScript, the traditional way of creating objects (collections of name/value pairs) is as follows:&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;var newObject = new Object(); //or&lt;br /&gt;2&lt;br /&gt;var newObject = {};&lt;br /&gt;3&lt;br /&gt; &lt;br /&gt;4&lt;br /&gt;newObject['someValue'] = 'Hello World';&lt;br /&gt;A lot of the time, you'll have reasons for approaching this in a much more object-specific way but the above simplistic approach to creation shows you how easy it is to apply this pattern where non-specific object types need to be created. You simply use a constructor to instantiate an instance of your object for later on when you need it. There are however situations where this is neither an advantage nor a desired feature.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Constructor Pattern&lt;br /&gt;&lt;br /&gt;The phrase ‘constructor’ is familiar to most developers, however if you’re a beginner it can be useful to review what a constructor is before we get into talking about a pattern dedicated to it. Constructors are used to create specific types of objects - they both prepare the object for use and can also accept parameters which the constructor uses to set the values of member variables when the object if first created. The idea that a constructor is a paradigm can be found in the majority of programming languages, including JavaScript. You’re also able to define custom constructors that define properties and methods for your own types of objects. &lt;br /&gt;&lt;br /&gt;Basic Constructors&lt;br /&gt;&lt;br /&gt;In JavaScript, constructor functions are generally considered a reasonable way to implement instances. JavaScript doesn't support the concept of classes but it does support special constructor functions.By simply prefixing a call to a constructor function with the keyword 'new', you can tell JavaScript you would like function to behave like a constructor and instantiate a new object with the members defined by that function.Inside a constructor, the keyword 'this' references the new object that's being created. A very basic constructor may be:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;function Car(model, year, miles){&lt;br /&gt;02&lt;br /&gt;   this.model = model;&lt;br /&gt;03&lt;br /&gt;   this.year    = year;&lt;br /&gt;04&lt;br /&gt;   this.miles  = miles;&lt;br /&gt;05&lt;br /&gt;   this.toString = function(){&lt;br /&gt;06&lt;br /&gt;    return this.model + " has done " + this.miles + " miles";&lt;br /&gt;07&lt;br /&gt;   };&lt;br /&gt;08&lt;br /&gt;}&lt;br /&gt;09&lt;br /&gt;  &lt;br /&gt;10&lt;br /&gt;var civic = new Car("Honda Civic", 2009, 20000);&lt;br /&gt;11&lt;br /&gt;var mondeo = new Car("Ford Mondeo", 2010, 5000);&lt;br /&gt;12&lt;br /&gt; &lt;br /&gt;13&lt;br /&gt;console.log(civic.toString());&lt;br /&gt;14&lt;br /&gt;console.log(mondeo.toString());&lt;br /&gt;The above is a simple version of the constructor pattern but it does suffer from some problems. One is that it makes inheritance difficult and the other is that functions such as toString() are redefined for each of the new objects created using the Car constructor. This isn't very optimal as the function should ideally be shared between all of the instances of the Car type.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Constructors With Prototypes&lt;br /&gt;&lt;br /&gt;Functions in JavaScript have a property called a prototype. When you call a JavaScript constructor to create an object, all the properties of the constructor's prototype are then made available to the new object. In this fashion, multiple Car objects can be created which access the same prototype. We can thus extend the original example as follows:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;function Car(model, year, miles){&lt;br /&gt;02&lt;br /&gt;   this.model = model;&lt;br /&gt;03&lt;br /&gt;   this.year    = year;&lt;br /&gt;04&lt;br /&gt;   this.miles  = miles;&lt;br /&gt;05&lt;br /&gt;}&lt;br /&gt;06&lt;br /&gt; &lt;br /&gt;07&lt;br /&gt;/*&lt;br /&gt;08&lt;br /&gt; Note here that we are using Object.prototype.newMethod rather than&lt;br /&gt;09&lt;br /&gt; Object.prototype so as to avoid redefining the prototype object&lt;br /&gt;10&lt;br /&gt;*/&lt;br /&gt;11&lt;br /&gt;Car.prototype.toString = function(){&lt;br /&gt;12&lt;br /&gt;        return this.model + " has done " + this.miles + " miles";&lt;br /&gt;13&lt;br /&gt;};&lt;br /&gt;14&lt;br /&gt;  &lt;br /&gt;15&lt;br /&gt;var civic = new Car("Honda Civic", 2009, 20000);&lt;br /&gt;16&lt;br /&gt;var mondeo = new Car("Ford Mondeo", 2010, 5000);&lt;br /&gt;17&lt;br /&gt; &lt;br /&gt;18&lt;br /&gt;console.log(civic.toString());&lt;br /&gt;Here, a single instance of toString() will now be shared between all of the Car objects.&lt;br /&gt;&lt;br /&gt;Side-note: Douglas Crockford recommends capitalizing your constructor functions so that it is easier to distinguish between them and normal functions.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Singleton Pattern&lt;br /&gt;&lt;br /&gt;In conventional software engineering, the singleton pattern can be implemented by creating a class with a method that creates a new instance of the class if one doesn't exist. In the event of an instance already existing, it simply returns a reference to that object. The singleton pattern is thus known because traditionally, it restricts instantiation of a class to a single object. With JavaScript, singletons serve as a namespace provider which isolate implementation code from the global namespace so-as to provide a single point of access for functions.&lt;br /&gt;&lt;br /&gt;The singleton doesn't provide a way for code that doesn't know about a previous reference to the singleton to easily retrieve it - it is not the object or 'class' that's returned by a singleton, it's a structure. Think of how closured variables aren't actually closures - the function scope that provides the closure is the closure.&lt;br /&gt;&lt;br /&gt;Singletons in JavaScript can take on a number of different forms and researching this pattern online is likely to result in at least 10 different variations. In its simplest form, a singleton in JS can be an object literal grouped together with its related methods and properties as follows:&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;var mySingleton = {&lt;br /&gt;2&lt;br /&gt;    property1:"something",&lt;br /&gt;3&lt;br /&gt;    property2:"something else",&lt;br /&gt;4&lt;br /&gt;    method1:function(){&lt;br /&gt;5&lt;br /&gt;        console.log('hello world');&lt;br /&gt;6&lt;br /&gt;    }&lt;br /&gt;7&lt;br /&gt;}&lt;br /&gt;If you wished to extend this further, you could add your own private members and methods to the singleton by encapsulating variable and function declarations inside a closure. Exposing only those which you wish to make public is quite straight-forward from that point as demonstrated below:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var mySingleton = function(){&lt;br /&gt;02&lt;br /&gt;  &lt;br /&gt;03&lt;br /&gt;    /* here are my private variables and methods */&lt;br /&gt;04&lt;br /&gt;    var privateVariable = 'something private';&lt;br /&gt;05&lt;br /&gt;    function showPrivate(){&lt;br /&gt;06&lt;br /&gt;        console.log(privateVariable);&lt;br /&gt;07&lt;br /&gt;    }&lt;br /&gt;08&lt;br /&gt;  &lt;br /&gt;09&lt;br /&gt;    /* public variables and methods (which can access private variables and methods ) */&lt;br /&gt;10&lt;br /&gt;    return {&lt;br /&gt;11&lt;br /&gt;        publicMethod:function(){&lt;br /&gt;12&lt;br /&gt;            showPrivate();&lt;br /&gt;13&lt;br /&gt;        },&lt;br /&gt;14&lt;br /&gt;        publicVar:'the public can see this!'&lt;br /&gt;15&lt;br /&gt;    }&lt;br /&gt;16&lt;br /&gt;}&lt;br /&gt;17&lt;br /&gt;  &lt;br /&gt;18&lt;br /&gt;var single = mySingleton();&lt;br /&gt;19&lt;br /&gt;single.publicMethod();  // logs 'something private'&lt;br /&gt;20&lt;br /&gt;console.log(single.publicVar); // logs 'the public can see this!'&lt;br /&gt;The above example is great, but let's next consider a situation where you only want to instantiate the singleton when it's needed. To save on resources, you can place the instantiation code inside another constructor function as follows:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var Singleton =(function(){&lt;br /&gt;02&lt;br /&gt;    var instantiated;&lt;br /&gt;03&lt;br /&gt;    function init (){&lt;br /&gt;04&lt;br /&gt;        /*singleton code here*/&lt;br /&gt;05&lt;br /&gt;        return {&lt;br /&gt;06&lt;br /&gt;            publicMethod:function(){&lt;br /&gt;07&lt;br /&gt;                console.log('hello world')&lt;br /&gt;08&lt;br /&gt;            },&lt;br /&gt;09&lt;br /&gt;            publicProperty:'test'&lt;br /&gt;10&lt;br /&gt;        }&lt;br /&gt;11&lt;br /&gt;    }&lt;br /&gt;12&lt;br /&gt;  &lt;br /&gt;13&lt;br /&gt;    return {&lt;br /&gt;14&lt;br /&gt;        getInstance :function(){&lt;br /&gt;15&lt;br /&gt;            if (!instantiated){&lt;br /&gt;16&lt;br /&gt;                instantiated = init();&lt;br /&gt;17&lt;br /&gt;            }&lt;br /&gt;18&lt;br /&gt;            return instantiated;&lt;br /&gt;19&lt;br /&gt;        }&lt;br /&gt;20&lt;br /&gt;    }&lt;br /&gt;21&lt;br /&gt;})()&lt;br /&gt;22&lt;br /&gt;  &lt;br /&gt;23&lt;br /&gt;/*calling public methods is then as easy as:*/&lt;br /&gt;24&lt;br /&gt;Singleton.getInstance().publicMethod();&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;So, where else is the singleton pattern useful in practice?. Well, it's quite useful when exactly one object is needed to coordinate patterns across the system.  Here's one last example of the singleton pattern being used:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var SingletonTester = (function(){&lt;br /&gt;02&lt;br /&gt; &lt;br /&gt;03&lt;br /&gt;  //args: an object containing arguments for the singleton&lt;br /&gt;04&lt;br /&gt;  function Singleton(args) {&lt;br /&gt;05&lt;br /&gt; &lt;br /&gt;06&lt;br /&gt;   //set args variable to args passed or empty object if none provided.&lt;br /&gt;07&lt;br /&gt;    var args = args || {};&lt;br /&gt;08&lt;br /&gt;    //set the name parameter&lt;br /&gt;09&lt;br /&gt;    this.name = 'SingletonTester';&lt;br /&gt;10&lt;br /&gt;    //set the value of pointX&lt;br /&gt;11&lt;br /&gt;    this.pointX = args.pointX || 6; //get parameter from arguments or set default&lt;br /&gt;12&lt;br /&gt;    //set the value of pointY&lt;br /&gt;13&lt;br /&gt;    this.pointY = args.pointY || 10;  &lt;br /&gt;14&lt;br /&gt; &lt;br /&gt;15&lt;br /&gt;  }&lt;br /&gt;16&lt;br /&gt;   &lt;br /&gt;17&lt;br /&gt; //this is our instance holder&lt;br /&gt;18&lt;br /&gt;  var instance;&lt;br /&gt;19&lt;br /&gt; &lt;br /&gt;20&lt;br /&gt; //this is an emulation of static variables and methods&lt;br /&gt;21&lt;br /&gt;  var _static = {&lt;br /&gt;22&lt;br /&gt;    name: 'SingletonTester',&lt;br /&gt;23&lt;br /&gt;   //This is a method for getting an instance&lt;br /&gt;24&lt;br /&gt; &lt;br /&gt;25&lt;br /&gt;   //It returns a singleton instance of a singleton object&lt;br /&gt;26&lt;br /&gt;    getInstance: function (args){&lt;br /&gt;27&lt;br /&gt;      if (instance === undefined) {&lt;br /&gt;28&lt;br /&gt;        instance = new Singleton(args);&lt;br /&gt;29&lt;br /&gt;      }&lt;br /&gt;30&lt;br /&gt;      return instance;&lt;br /&gt;31&lt;br /&gt;    }&lt;br /&gt;32&lt;br /&gt;  };&lt;br /&gt;33&lt;br /&gt;  return _static;&lt;br /&gt;34&lt;br /&gt;})();&lt;br /&gt;35&lt;br /&gt; &lt;br /&gt;36&lt;br /&gt;var singletonTest = SingletonTester.getInstance({pointX: 5});&lt;br /&gt;37&lt;br /&gt;console.log(singletonTest.pointX); // outputs 5&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Module Pattern&lt;br /&gt;&lt;br /&gt;Let's now look at the popular module pattern. The module pattern was originally defined as a way to provide both private and public encapsulation for classes in conventional software engineering.&lt;br /&gt;&lt;br /&gt;In JavaScript, the module pattern is used to emulate the concept of classes in such a way that we're able to include both public/private methods and variables inside a single object, thus shielding particular parts from the global scope. What this results in is a reduction in the likelihood of your function names conflicting with other functions defined in additional scripts on the page.&lt;br /&gt;&lt;br /&gt;Exploring the concept of public and private methods further, the module pattern pattern allows us to have particular methods and variables which are only accessible from within the module, meaning that you have a level of shielding from external entities accessing this 'hidden' information.&lt;br /&gt;&lt;br /&gt;Let's begin looking at an implementation of the module pattern by creating a module which is self-contained in the global object. Here, other parts of the code are unable to directly read the value of our incrementCounter() or resetCounter(). The counter variable is actually fully shielded from our global scope so it acts just like a private variable would - its existence is limited to within the module's closure so that the only code able to access its scope are our two functions. Our methods are effectively namespaced so in the test section of our code, we need to prefix any calls with the name of the module (eg. 'testModule').&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var testModule = (function(){&lt;br /&gt;02&lt;br /&gt;    var counter = 0;&lt;br /&gt;03&lt;br /&gt;    return {&lt;br /&gt;04&lt;br /&gt;        incrementCounter: function() {&lt;br /&gt;05&lt;br /&gt;            return counter++;&lt;br /&gt;06&lt;br /&gt;        },&lt;br /&gt;07&lt;br /&gt;        resetCounter: function() {&lt;br /&gt;08&lt;br /&gt;            console.log('counter value prior to reset:' + counter);&lt;br /&gt;09&lt;br /&gt;            counter = 0&lt;br /&gt;10&lt;br /&gt;        }&lt;br /&gt;11&lt;br /&gt;    }&lt;br /&gt;12&lt;br /&gt;})();&lt;br /&gt;13&lt;br /&gt; &lt;br /&gt;14&lt;br /&gt;/*test*/&lt;br /&gt;15&lt;br /&gt;testModule.incrementCounter();&lt;br /&gt;16&lt;br /&gt;testModule.resetCounter();&lt;br /&gt;When working with the module pattern, you may find it useful to define a simple template that you use for getting started with it. Here's one that covers namespacing, public and private variables:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var myNamespace = (function(){&lt;br /&gt;02&lt;br /&gt;    var myPrivateVar = 0;&lt;br /&gt;03&lt;br /&gt;        var myPrivateMethod = function(someText){&lt;br /&gt;04&lt;br /&gt;        console.log(someText);&lt;br /&gt;05&lt;br /&gt;    }&lt;br /&gt;06&lt;br /&gt;     &lt;br /&gt;07&lt;br /&gt;    return {&lt;br /&gt;08&lt;br /&gt;        myPublicVar: "foo",&lt;br /&gt;09&lt;br /&gt;        myPublicFunction: function(bar){&lt;br /&gt;10&lt;br /&gt;            myPrivateVar++;&lt;br /&gt;11&lt;br /&gt;            myPrivateMethod(bar);&lt;br /&gt;12&lt;br /&gt;        }&lt;br /&gt;13&lt;br /&gt;    }&lt;br /&gt;14&lt;br /&gt;     &lt;br /&gt;15&lt;br /&gt;})();&lt;br /&gt;A piece of trivia is that the module pattern was originally formally defined by Douglas Crockford (famous for his book 'JavaScript: The Good Parts, and more), although it is likely that variations of this pattern were used long before this. Another piece of trviai is that if you've ever played with Yahoo's YUI library, some of its features may appear quite familiar and the reason for this is that the module pattern was a strong influence for YUI when creating their components.&lt;br /&gt;&lt;br /&gt;So, you've seen why the singleton pattern can be useful, but why is the module pattern a good choice?. For starters, it's a lot cleaner for developers coming from an object-oriented background than the idea of true encapsulation, at least from a JavaScript perspective. Secondly, it supports private data - so, in the module pattern, public parts of your code are able to touch the private parts, however the outside world is unable to touch the class's private parts (no laughing!. oh, and thanks to David Engfer for the joke).&lt;br /&gt;&lt;br /&gt;The disadvantages of the module pattern are that as you access both public and private members differently, when you wish to change visibility, you actually have to make changes to each place the member was used. You also can't access private members in methods that are added to the object at a later point. That said, in many cases the module pattern is still quite useful and when used correctly, certainly has the potential to improve the structure of your application. Here's a final module pattern example:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var someModule = (function(){&lt;br /&gt;02&lt;br /&gt; &lt;br /&gt;03&lt;br /&gt;  //private attributes&lt;br /&gt;04&lt;br /&gt;  var privateVar = 5;&lt;br /&gt;05&lt;br /&gt; &lt;br /&gt;06&lt;br /&gt;  //private methods&lt;br /&gt;07&lt;br /&gt;  var privateMethod = function(){&lt;br /&gt;08&lt;br /&gt;  return 'Private Test';&lt;br /&gt;09&lt;br /&gt;  };&lt;br /&gt;10&lt;br /&gt; &lt;br /&gt;11&lt;br /&gt;  return {&lt;br /&gt;12&lt;br /&gt;        //public attributes&lt;br /&gt;13&lt;br /&gt;        publicVar    : 10,&lt;br /&gt;14&lt;br /&gt;        //public methods&lt;br /&gt;15&lt;br /&gt;        publicMethod : function(){&lt;br /&gt;16&lt;br /&gt;        return ' Followed By Public Test ';&lt;br /&gt;17&lt;br /&gt;         }, &lt;br /&gt;18&lt;br /&gt; &lt;br /&gt;19&lt;br /&gt;         //let's access the private members&lt;br /&gt;20&lt;br /&gt;          getData : function(){&lt;br /&gt;21&lt;br /&gt;          return privateMethod() + this.publicMethod() + privateVar;&lt;br /&gt;22&lt;br /&gt;         }&lt;br /&gt;23&lt;br /&gt;       }&lt;br /&gt;24&lt;br /&gt;    })(); //the parens here cause the anonymous function to execute and return&lt;br /&gt;25&lt;br /&gt;         &lt;br /&gt;26&lt;br /&gt;someModule.getData();&lt;br /&gt;To continue reading more about the module pattern, I strongly recommend Ben Cherry's JavaScript Module Pattern In-Depth article.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Revealing Module Pattern&lt;br /&gt;&lt;br /&gt;Now you’re probably a little more familiar with what the Module pattern is. Let’s take a look at a slightly improved version - Christian Heilmann’s Revealing Module pattern, often described as a neat extension to a rather robust pattern.&lt;br /&gt;&lt;br /&gt;The Revealing Module Pattern came about as Heilmann (now at Mozilla) was frustrated with the fact that in you had to repeat the name of the main object when you wanted to call one public method from another or access public variables.  He also disliked the Module pattern’s requirement for having to switch to object literal notation for the things you wished to make public.&lt;br /&gt;&lt;br /&gt;The result of his efforts were an updated pattern where you would simply define all of your functions and variables in the private scope and return an anonymous object at the end of the module along with pointers to both the private variables and functions you wished to reveal as public.&lt;br /&gt;&lt;br /&gt;Once again, you’re probably wondering what the benefits of this approach are. The RMP allows the syntax of your script to be fairly consistent - it also makes it very clear at the end which of your functions and variables may be accessed publicly, something that is quite useful. In addition, you are also able to reveal private functions with more specific names if you wish.&lt;br /&gt;&lt;br /&gt;An example of how to use the revealing module pattern can be found below:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;/*&lt;br /&gt;02&lt;br /&gt; The idea here is that you have private methods&lt;br /&gt;03&lt;br /&gt; which you want to expose as public methods.&lt;br /&gt;04&lt;br /&gt; &lt;br /&gt;05&lt;br /&gt; What are are doing below is effectively defining&lt;br /&gt;06&lt;br /&gt; a self-executing function and immediately returning&lt;br /&gt;07&lt;br /&gt; the object.&lt;br /&gt;08&lt;br /&gt; */&lt;br /&gt;09&lt;br /&gt; var myRevealingModule = (function(){&lt;br /&gt;10&lt;br /&gt; &lt;br /&gt;11&lt;br /&gt;    var name = 'John Smith';&lt;br /&gt;12&lt;br /&gt;    var age = 40;&lt;br /&gt;13&lt;br /&gt; &lt;br /&gt;14&lt;br /&gt;    function updatePerson(){&lt;br /&gt;15&lt;br /&gt;      name = 'John Smith Updated';&lt;br /&gt;16&lt;br /&gt;    }&lt;br /&gt;17&lt;br /&gt;    function setPerson () {&lt;br /&gt;18&lt;br /&gt;       name = 'John Smith Set';&lt;br /&gt;19&lt;br /&gt;    }&lt;br /&gt;20&lt;br /&gt;    function getPerson () {&lt;br /&gt;21&lt;br /&gt;       return name;&lt;br /&gt;22&lt;br /&gt;    }&lt;br /&gt;23&lt;br /&gt;    return{&lt;br /&gt;24&lt;br /&gt;        set: setPerson,&lt;br /&gt;25&lt;br /&gt;        get: getPerson&lt;br /&gt;26&lt;br /&gt;    }&lt;br /&gt;27&lt;br /&gt;}());&lt;br /&gt;28&lt;br /&gt; &lt;br /&gt;29&lt;br /&gt;// Sample usage:&lt;br /&gt;30&lt;br /&gt;myRevealingModule.get();&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Observer Pattern&lt;br /&gt;&lt;br /&gt;The observer pattern (also known as the publisher/subscriber model) is a design pattern which allows an object (known as an observer) to watch another object (the subject) where the pattern provides a means for the subject and observer to form a publish-subscribe relationship. Observers are able to register so that they can receive events from the subject and when the subject needs to notify observers regarding events, it sends the events to each observer. The pub/sub model is quite popularly used in implementing event handling systems as it is both quite effective and is relatively straight-forward to use.&lt;br /&gt;&lt;br /&gt;The motivation behind using the observer pattern is where you need to maintain consistency between related objects without making classes tightly coupled. For example, when an object needs to be able to notify other objects without making assumptions regarding those objects. Another use case is where abstractions have more than one aspect, where one depends on the other. The encapsulation of these aspects in separate objects allows the variation and re-use of the objects independently.&lt;br /&gt;&lt;br /&gt;Benefits of using the observer pattern include:&lt;br /&gt;&lt;br /&gt;Support for simple broadcast communication. Notifications are broadcast automatically to all objects that have subscribed.&lt;br /&gt;Dynamic relationships may exist between subjects and observers which can be easily established on page load. This provides a great deal of flexibility.&lt;br /&gt;Abstract coupling between subjects and observers where each can be extended and re-used individually.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;A draw-back of the pattern is that observers are ignorant to the existence of each other and are blind to the cost of switching in subject. Due to the dynamic relationship between subjects and observers the update dependency can be difficult to track.&lt;br /&gt;&lt;br /&gt;Although many examples of where the observer pattern can be used correctly exist, a popular one in academic circles is the example of a spreadsheet application. Let us imagine that this application is Google Docs, where a number of UI components may be in use: the spreadsheet's formula bar (ssFormula), a pie-chart (ssPieChart) and a bar graph (ssBarGraph). The data source for all of these components will be referred to as 'ssSpreadsheetData'. ssFormula, ssPieChart and ssBarGraph are all observer objects. ssSpreadsheetData may be considered the subject object. The ssSpreadsheet data object notifies its observers when a data change occurs which could make its state inconsistent with the observers.&lt;br /&gt;&lt;br /&gt;Let us now take a look at an example of the observer pattern implemented in JavaScript. The following demo is a minimalist version of pub/sub I released on GitHub under a project called 'pubsubz'. Sample usage of this implementation can be found lower down the page.&lt;br /&gt;&lt;br /&gt;Observer implementation&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var pubsub = {};&lt;br /&gt;02&lt;br /&gt;(function(q) {&lt;br /&gt;03&lt;br /&gt; &lt;br /&gt;04&lt;br /&gt;    var topics = {},&lt;br /&gt;05&lt;br /&gt;        subUid = -1;&lt;br /&gt;06&lt;br /&gt; &lt;br /&gt;07&lt;br /&gt;    q.publish = function(topic, args) {&lt;br /&gt;08&lt;br /&gt; &lt;br /&gt;09&lt;br /&gt;        if (!topics[topic]) {&lt;br /&gt;10&lt;br /&gt;            return false;&lt;br /&gt;11&lt;br /&gt;        }&lt;br /&gt;12&lt;br /&gt; &lt;br /&gt;13&lt;br /&gt;        setTimeout(function() {&lt;br /&gt;14&lt;br /&gt;            var subscribers = topics[topic],&lt;br /&gt;15&lt;br /&gt;                len = subscribers ? subscribers.length : 0;&lt;br /&gt;16&lt;br /&gt; &lt;br /&gt;17&lt;br /&gt;            while (len--) {&lt;br /&gt;18&lt;br /&gt;                subscribers[len].func(topic, args);&lt;br /&gt;19&lt;br /&gt;            }&lt;br /&gt;20&lt;br /&gt;        }, 0);&lt;br /&gt;21&lt;br /&gt; &lt;br /&gt;22&lt;br /&gt;        return true;&lt;br /&gt;23&lt;br /&gt; &lt;br /&gt;24&lt;br /&gt;    };&lt;br /&gt;25&lt;br /&gt; &lt;br /&gt;26&lt;br /&gt;    q.subscribe = function(topic, func) {&lt;br /&gt;27&lt;br /&gt; &lt;br /&gt;28&lt;br /&gt;        if (!topics[topic]) {&lt;br /&gt;29&lt;br /&gt;            topics[topic] = [];&lt;br /&gt;30&lt;br /&gt;        }&lt;br /&gt;31&lt;br /&gt; &lt;br /&gt;32&lt;br /&gt;        var token = (++subUid).toString();&lt;br /&gt;33&lt;br /&gt;        topics[topic].push({&lt;br /&gt;34&lt;br /&gt;            token: token,&lt;br /&gt;35&lt;br /&gt;            func: func&lt;br /&gt;36&lt;br /&gt;        });&lt;br /&gt;37&lt;br /&gt;        return token;&lt;br /&gt;38&lt;br /&gt;    };&lt;br /&gt;39&lt;br /&gt; &lt;br /&gt;40&lt;br /&gt;    q.unsubscribe = function(token) {&lt;br /&gt;41&lt;br /&gt;        for (var m in topics) {&lt;br /&gt;42&lt;br /&gt;            if (topics[m]) {&lt;br /&gt;43&lt;br /&gt;                for (var i = 0, j = topics[m].length; i &lt; j; i++) {&lt;br /&gt;44&lt;br /&gt;                    if (topics[m][i].token === token) {&lt;br /&gt;45&lt;br /&gt;                        topics[m].splice(i, 1);&lt;br /&gt;46&lt;br /&gt;                        return token;&lt;br /&gt;47&lt;br /&gt;                    }&lt;br /&gt;48&lt;br /&gt;                }&lt;br /&gt;49&lt;br /&gt;            }&lt;br /&gt;50&lt;br /&gt;        }&lt;br /&gt;51&lt;br /&gt;        return false;&lt;br /&gt;52&lt;br /&gt;    };&lt;br /&gt;53&lt;br /&gt;}(pubsub));&lt;br /&gt;Subscribing and publishing&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var testSubscriber = function( topics , data ){&lt;br /&gt;02&lt;br /&gt;    console.log( topics + ": " + data );&lt;br /&gt;03&lt;br /&gt;};&lt;br /&gt;04&lt;br /&gt; &lt;br /&gt;05&lt;br /&gt;/*&lt;br /&gt;06&lt;br /&gt;* Publishers are in charge of "publishing" notifications about events&lt;br /&gt;07&lt;br /&gt;*/&lt;br /&gt;08&lt;br /&gt;pubsub.publish( 'example1', 'hello world!' );&lt;br /&gt;09&lt;br /&gt;pubsub.publish( 'example1', ['test','a','b','c'] );&lt;br /&gt;10&lt;br /&gt;pubsub.publish( 'example1', [{'color':'blue'},{'text':'hello'}] );&lt;br /&gt;11&lt;br /&gt; &lt;br /&gt;12&lt;br /&gt; &lt;br /&gt;13&lt;br /&gt;/*&lt;br /&gt;14&lt;br /&gt;* Subscribers basically "subscribe" (or listen)&lt;br /&gt;15&lt;br /&gt;* And once they've been "notified" their callback functions are invoked&lt;br /&gt;16&lt;br /&gt;*/&lt;br /&gt;17&lt;br /&gt;var testSubscription = pubsub.subscribe( 'example1', testSubscriber );&lt;br /&gt;18&lt;br /&gt; &lt;br /&gt;19&lt;br /&gt; &lt;br /&gt;20&lt;br /&gt;/*&lt;br /&gt;21&lt;br /&gt;* Unsubscribe if you no longer wish to be notified&lt;br /&gt;22&lt;br /&gt;*/&lt;br /&gt;23&lt;br /&gt; &lt;br /&gt;24&lt;br /&gt;setTimeout(function(){&lt;br /&gt;25&lt;br /&gt;    pubsub.unsubscribe( testSubscription );&lt;br /&gt;26&lt;br /&gt;}, 0);&lt;br /&gt;27&lt;br /&gt; &lt;br /&gt;28&lt;br /&gt;pubsub.publish( 'example1', 'hello again! (this will fail)' );&lt;br /&gt;A jsFiddle version of this example can be found at http://jsfiddle.net/LxPrq/&lt;br /&gt;&lt;br /&gt;Note:If you are interested in a pub/sub pattern implementation using jQuery, I recommend Ben Alman's GitHub Gist for an example of how to achieve this.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Prototype Pattern&lt;br /&gt;&lt;br /&gt;The prototype pattern is based on the concept of prototypal inheritance where we create objects which act as prototypes for other objects. The prototype object itself is effectively used as a blueprint for each object the constructor creates.If the prototype of the constructor function used contains a property called 'name' for example (as per the code sample below), then each object created by that same constructor will also have this same property.&lt;br /&gt;&lt;br /&gt;Looking at the definitions for the prototype pattern in existing literature non-specific to JavaScript, you *may* find references to concepts outside the scope of the language such as classes. The reality is that prototypal inheritance avoids using classes altogether. There isn't a 'definition' object nor a core object in theory. We're simply creating copies of existing functional objects.&lt;br /&gt;&lt;br /&gt;One of the core benefits of using the prototype pattern is that we're working with the strengths JavaScript has to offer natively rather than attempting to imitate features of other languages (something a few design pattern implementations do). Not only is this an easy way to implement inheritance, but this also comes with a performance boost as well. When defining a function in an object, they're all created by reference (so all child objects point to the same function) instead of creating their own individual copies.&lt;br /&gt;&lt;br /&gt;For those interested, real prototypal inheritance, as defined in the ECMAScript 5 standard, requires the use of Object.create which is a recent newly native method. Object.create creates an object which has a specified prototype and which optionally contains specified properties (i.e Object.create(prototype, optionalDescriptorObjects)). We can also see this being demonstrated in the example below:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;/*No need for capitalization as it's not a constructor*/&lt;br /&gt;2&lt;br /&gt;var someCar = {&lt;br /&gt;3&lt;br /&gt;  drive: function() {},&lt;br /&gt;4&lt;br /&gt;  name: 'Mazda 3'   &lt;br /&gt;5&lt;br /&gt;};&lt;br /&gt;6&lt;br /&gt; &lt;br /&gt;7&lt;br /&gt;/*Use Object.create to generate a new car*/&lt;br /&gt;8&lt;br /&gt;var anotherCar = Object.create(someCar);&lt;br /&gt;9&lt;br /&gt;anotherCar.name = 'Toyota Camry';&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Object.create allows you to easily implement advanced concepts such as differential inheritance where objects are able to directly inherit from other objects. With Object.create you're also able to initialise object properties using the second supplied argument. For example:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var vehicle = {&lt;br /&gt;02&lt;br /&gt;    getModel : function(){&lt;br /&gt;03&lt;br /&gt;        console.log('The model of this vehicle is..' + this.model);&lt;br /&gt;04&lt;br /&gt;   }&lt;br /&gt;05&lt;br /&gt;};&lt;br /&gt;06&lt;br /&gt; &lt;br /&gt;07&lt;br /&gt;var car = Object.create(vehicle, {&lt;br /&gt;08&lt;br /&gt;    'id' : {&lt;br /&gt;09&lt;br /&gt;        value: MY_GLOBAL.nextId(),&lt;br /&gt;10&lt;br /&gt;        enumerable:true /*writable:false, configurable:false by default*/&lt;br /&gt;11&lt;br /&gt;    },&lt;br /&gt;12&lt;br /&gt;    'model':{&lt;br /&gt;13&lt;br /&gt;        value: 'Ford',&lt;br /&gt;14&lt;br /&gt;        enumerable:true&lt;br /&gt;15&lt;br /&gt;    }&lt;br /&gt;16&lt;br /&gt;});&lt;br /&gt;Here the properties can be initialized on the second argument of Object.create using an object literal using the syntax similar to that used by the Object.defineProperties and Object.defineProperty methods. It allows you to set the property attributes such as enumerable, writable or configurable.&lt;br /&gt;&lt;br /&gt;If you wish to implement the prototype pattern without directly using Object.create, you can simulate the pattern as per the above example as follows:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var vehiclePrototype = {&lt;br /&gt;02&lt;br /&gt;    init: function(carModel) {&lt;br /&gt;03&lt;br /&gt;        this.model = carModel;&lt;br /&gt;04&lt;br /&gt;    },&lt;br /&gt;05&lt;br /&gt;    getModel: function() {&lt;br /&gt;06&lt;br /&gt;        console.log('The model of this vehicle is..' + this.model);&lt;br /&gt;07&lt;br /&gt;    }&lt;br /&gt;08&lt;br /&gt;};&lt;br /&gt;09&lt;br /&gt; &lt;br /&gt;10&lt;br /&gt; &lt;br /&gt;11&lt;br /&gt;function vehicle(model) {&lt;br /&gt;12&lt;br /&gt;    function F() {};&lt;br /&gt;13&lt;br /&gt;    F.prototype = vehiclePrototype;&lt;br /&gt;14&lt;br /&gt;    var f = new F();&lt;br /&gt;15&lt;br /&gt;    f.init(model);&lt;br /&gt;16&lt;br /&gt;    return f;&lt;br /&gt;17&lt;br /&gt;}&lt;br /&gt;18&lt;br /&gt; &lt;br /&gt;19&lt;br /&gt;var car = vehicle('Ford Escort');&lt;br /&gt;20&lt;br /&gt;car.getModel();&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Command Pattern&lt;br /&gt;&lt;br /&gt;The command pattern aims to encapsulate method invocation, requests or operations into a single object and gives you the ability to both parameterize and pass method calls around that can be executed at your discretion. In addition, it enables you to decouple objects invoking the action from the objects which implement them, giving you a greater degree of overall flexibility in swapping out concrete 'classes'.&lt;br /&gt;&lt;br /&gt;If you haven't come across concrete classes before, they are best explained in terms of class-based programming languages and are related to the idea of abstract classes. An abstract class defines an interface, but doesn't necessarily provide implementations for all of its member functions. It acts as a base class from which others are derived. A derived class which implements the missing functionality is called a concrete class (you may find these concepts familiar if you're read about the Decorator or Prototype patterns).&lt;br /&gt;&lt;br /&gt;The main idea behind the command pattern is that it provides you a means to separate the responsibilities of issuing commands from anything executing commands, delegating this responsibility to different objects instead.&lt;br /&gt;&lt;br /&gt;Implementation wise, simple command objects bind together both an action and the object wishing to invoke the action and consistently include an execution operation (such as run() or execute()). All command objects with the same interface can easily be swapped as needed and this is considered one of the larger benefits of the pattern.&lt;br /&gt;&lt;br /&gt;To demonstrate the command pattern we're going to create a simple car purchasing service.&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;$(function(){&lt;br /&gt;02&lt;br /&gt;     &lt;br /&gt;03&lt;br /&gt;    var CarManager = {&lt;br /&gt;04&lt;br /&gt;     &lt;br /&gt;05&lt;br /&gt;          /* request information */&lt;br /&gt;06&lt;br /&gt;          requestInfo: function(model, id){&lt;br /&gt;07&lt;br /&gt;            return 'The information for ' + model + ' with ID ' + id + ' is foobar';&lt;br /&gt;08&lt;br /&gt;          },&lt;br /&gt;09&lt;br /&gt;           &lt;br /&gt;10&lt;br /&gt;          /* purchase the car */&lt;br /&gt;11&lt;br /&gt;          buyVehicle: function(model, id){&lt;br /&gt;12&lt;br /&gt;            return 'You have successfully purchased Item ' + id + ', a ' + model';&lt;br /&gt;13&lt;br /&gt;          },&lt;br /&gt;14&lt;br /&gt;           &lt;br /&gt;15&lt;br /&gt;          /* arrange a viewing */&lt;br /&gt;16&lt;br /&gt;          arrangeViewing: function(model, id){&lt;br /&gt;17&lt;br /&gt;            return 'You have successfully booked a viewing of ' + model + ' ( ' + id + ' ) ';&lt;br /&gt;18&lt;br /&gt;          }&lt;br /&gt;19&lt;br /&gt;       &lt;br /&gt;20&lt;br /&gt;      };&lt;br /&gt;21&lt;br /&gt;       &lt;br /&gt;22&lt;br /&gt;})();&lt;br /&gt;Now taking a look at the above code, we could easily execute our manager commands by directly invoking the methods, however in some situations we don't expect to invoke the inner methods inside the object directly. The reason for this would be, we don't want to increase the dependencies amongst objects i.e if the core login behind the CarManager changes, all our methods that carry out the processing with the manager have to be modified in the mean time. This would effectively go against the OOP methodology of loosely coupling objects as much as possible which we want to avoid.&lt;br /&gt;&lt;br /&gt;Let's now expand on our CarManager so that our application of the command pattern results in the following: accept any process requests from the CarManager object where the contents of the request include the model and car ID.&lt;br /&gt;&lt;br /&gt;Here is what we would like to be able to achieve:&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;CarManager.execute({commandType: "buyVehicle", operand1: 'Ford Escort', operand2: '453543'});&lt;br /&gt;As per this structure we should now add a definition for the "CarManager.execute" method as follows:&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;CarManager.execute = function(command){&lt;br /&gt;2&lt;br /&gt;  return CarManager[command.request](command.model,command.carID);&lt;br /&gt;3&lt;br /&gt;};&lt;br /&gt;Our final sample calls would thus look as follows:&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;CarManager.execute({request: "arrangeViewing", model: 'Ferrari', carID: '145523'}); &lt;br /&gt;2&lt;br /&gt;CarManager.execute({request: "requestInfo", model: 'Ford Mondeo', carID: '543434'});   &lt;br /&gt;3&lt;br /&gt;CarManager.execute({request: "requestInfo", model: 'Ford Escort', carID: '543434'});   &lt;br /&gt;4&lt;br /&gt;CarManager.execute({request: "buyVehicle", model: 'Ford Escort', carID: '543434'});&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The DRY Pattern&lt;br /&gt;&lt;br /&gt;Disclaimer: DRY is essentially a way of thinking and many patterns aim to achieve a level of DRY-ness with their design. In this section we'll be covering what it means for code to be DRY but also covering the DRY design pattern based on these same concepts.&lt;br /&gt;&lt;br /&gt;A challenge that developers writing large applications frequently have is writing similar code multiple times. Sometimes this occurs because your script or application may have multiple similar ways of performing something. Repetitive code writing generally reduces productivity and leaves you open to having to re-write code you’ve already written similar times before, thus leaving you with less time to add in new functionality.&lt;br /&gt;&lt;br /&gt;DRY (don’t repeat yourself) was created to simplify this - it’s based on the idea that each part of your code should ideally only have one representation of each piece of knowledge in it that applies to your system. The key concept to take away here is that if you have code that performs a specific task, you shouldn’t write that code multiple times through your applications or scripts.&lt;br /&gt;&lt;br /&gt;When DRY is applied successfully, the modification of any element in the system doesn’t change other logically-unrelated elements. Elements in your code that are logically related change uniformly and are thus kept in sync.&lt;br /&gt;&lt;br /&gt;As other patterns covered display aspects of DRY-ness with JavaScript, let's take a look at how to write DRY code using jQuery. Note that where jQuery is used, you can easily substitute selections using vanilla JavaScript because jQuery is just JavaScript at an abstracted level.&lt;br /&gt;&lt;br /&gt;Non-DRY&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;/*Let's store some default values in an array*/&lt;br /&gt;02&lt;br /&gt;var defaultSettings = {};&lt;br /&gt;03&lt;br /&gt;defaultSettings['carModel']   = 'Mercedes';&lt;br /&gt;04&lt;br /&gt;defaultSettings['carYear]     = 2010;&lt;br /&gt;05&lt;br /&gt;defaultSettings['carMiles']   = 5000;&lt;br /&gt;06&lt;br /&gt;defaultSettings['carTint']    = 'Metallic Blue';&lt;br /&gt;07&lt;br /&gt; &lt;br /&gt;08&lt;br /&gt;/*Let's do something with this data if a checkbox is clicked*/&lt;br /&gt;09&lt;br /&gt;$('.someCheckbox').click(function(){ &lt;br /&gt;10&lt;br /&gt;       &lt;br /&gt;11&lt;br /&gt; if (this.checked){                &lt;br /&gt;12&lt;br /&gt;        $('#input_carModel').val(activeSettings.carModel);&lt;br /&gt;13&lt;br /&gt;        $('#input_carYear').val(activeSettings.carYear);&lt;br /&gt;14&lt;br /&gt;        $('#input_carMiles').val(activeSettings.carMiles);&lt;br /&gt;15&lt;br /&gt;        $('input_#carTint').val(activeSettings.carTint);&lt;br /&gt;16&lt;br /&gt; &lt;br /&gt;17&lt;br /&gt; } else { &lt;br /&gt;18&lt;br /&gt;                &lt;br /&gt;19&lt;br /&gt;        $('#input_carModel').val('');     &lt;br /&gt;20&lt;br /&gt;        $('#input_carYear').val(''); &lt;br /&gt;21&lt;br /&gt;        $('#input_carMiles').val('');&lt;br /&gt;22&lt;br /&gt;        $('#input_carTint').val('');&lt;br /&gt;23&lt;br /&gt; }&lt;br /&gt;24&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;DRY&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;$('.someCheckbox').click(function(){        &lt;br /&gt;02&lt;br /&gt;    var checked = this.checked;&lt;br /&gt;03&lt;br /&gt;    /*&lt;br /&gt;04&lt;br /&gt;        What are we repeating?&lt;br /&gt;05&lt;br /&gt;        1. input_ precedes each field name&lt;br /&gt;06&lt;br /&gt;        2. accessing the same array for settings&lt;br /&gt;07&lt;br /&gt;        3. repeating value resets&lt;br /&gt;08&lt;br /&gt;  &lt;br /&gt;09&lt;br /&gt;        What can we do?&lt;br /&gt;10&lt;br /&gt;        1. programmatically generate the field names&lt;br /&gt;11&lt;br /&gt;        2. access array by key &lt;br /&gt;12&lt;br /&gt;        3. merge this call using terse coding (ie. if checked, &lt;br /&gt;13&lt;br /&gt;            set a value, otherwise don't)&lt;br /&gt;14&lt;br /&gt;  */  &lt;br /&gt;15&lt;br /&gt;       $.each(['carModel', 'carYear', 'carMiles', 'carTint'], function(i,key){&lt;br /&gt;16&lt;br /&gt;               $('#input_' + v).val(checked ? defaultSettings[key] : '');&lt;br /&gt;17&lt;br /&gt;       });&lt;br /&gt;18&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Facade Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This pattern both simplifies the interface of a class and it also decouples the class from the code that utilizes it. Facades are often considered an essential part of a developer’s pattern toolkit - they can make library utilities significantly easier to understand by creating convenience routines that simplify the use of complex systems. &lt;br /&gt;&lt;br /&gt;An example of where the Facade pattern can be found is in the creation of uniform JavaScript APIs which often seek to provide consistent experiences across all browsers. Facades provide us with an ability to indirectly interact with subsystems in a way that may be less prone to error than accessing the subsystem directly.&lt;br /&gt;&lt;br /&gt;Facade’s advantages include ease of use and often a small size-footprint in implementing the pattern. It does however have some pitfalls - Facade is inefficient when used consecutively and each time it’s called a new check must be made to determine the features available for attaching event listeners. Let’s take a look at the pattern in action:&lt;br /&gt;&lt;br /&gt;This is an unoptimized code example but here we utilize Facade to simplify an interface for attaching events. We do this by creating a common method that can be used in one’s code which does the task of checking for the existence of features so that it can provide a safe and cross-browser compatible solution.&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;var addMyEvent = function(el,ev,fn){&lt;br /&gt;2&lt;br /&gt;   if(el.addEventListener){&lt;br /&gt;3&lt;br /&gt;            el.addEventListener(ev,fn, false);&lt;br /&gt;4&lt;br /&gt;      }else if(el.attachEvent){&lt;br /&gt;5&lt;br /&gt;            el.attachEvent('on'+ev, fn);&lt;br /&gt;6&lt;br /&gt;      } else{&lt;br /&gt;7&lt;br /&gt;           el['on' + ev] = fn;&lt;br /&gt;8&lt;br /&gt;    }&lt;br /&gt;9&lt;br /&gt;};&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Factory Pattern&lt;br /&gt;&lt;br /&gt;Similar to other creational patterns, the Factory Pattern deals with the problem of creating objects (which we can think of as ‘factory products’) without the need to specify the exact class of object being created. &lt;br /&gt;&lt;br /&gt;Specifically, the Factory Pattern suggests defining an interface for creating an object where you allow the subclasses to decide which class to instantiate. This pattern handles the problem by defining a completely separate method for the creation of objects and which sub-classes are able to override so they can specify the ‘type’ of factory product that will be created.&lt;br /&gt;&lt;br /&gt;This can come in quite useful, in particular if the creation process involved is quite complex. eg. if it strongly depends on the settings in configuration files.&lt;br /&gt;&lt;br /&gt;You can often find factory methods in frameworks where the code for a library may need to create objects of particular types which may be subclassed by scripts using the frameworks.&lt;br /&gt;&lt;br /&gt;In our example, let’s take the code used in the original Constructor pattern example and see what this would look like were we to optimize it using the Factory Pattern:&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;var Car = (function() {&lt;br /&gt;02&lt;br /&gt;   var Car = function (model, year, miles){&lt;br /&gt;03&lt;br /&gt;       this.model = model;&lt;br /&gt;04&lt;br /&gt;       this.year   = year;&lt;br /&gt;05&lt;br /&gt;       this.miles = miles;&lt;br /&gt;06&lt;br /&gt;   };&lt;br /&gt;07&lt;br /&gt;   return function (model, year, miles) {&lt;br /&gt;08&lt;br /&gt;       return new Car(model, year, miles);&lt;br /&gt;09&lt;br /&gt;   }&lt;br /&gt;10&lt;br /&gt;})();&lt;br /&gt;11&lt;br /&gt; &lt;br /&gt;12&lt;br /&gt;var civic = new Car("Honda Civic", 2009, 20000);&lt;br /&gt;13&lt;br /&gt;var mondeo = new Car("Ford Mondeo", 2010, 5000);&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;When To Use This Pattern&lt;br /&gt;&lt;br /&gt;The Factory pattern can be especially useful when applied to the falling situations:&lt;br /&gt;&lt;br /&gt;When your object's setup requires a high level of complexity&lt;br /&gt;When you need to generate different instances depending on the environment&lt;br /&gt;When you're working with many small objects that share the same properties&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;When Not To Use This Pattern&lt;br /&gt;&lt;br /&gt;It's generally a good practice to not use the factory pattern in every situation as it can easily add an unnecessarily additional aspect of complexity to your code. It can also make some tests more difficult to run.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Mixin Pattern&lt;br /&gt;&lt;br /&gt;In traditional object-oriented programming languages, mixins are classes which provide the functionality to be inherited by a subclass. Inheriting from mixins are a means of collecting functionality and classes may inherit functionality from multiple mixins through multiple inheritance.&lt;br /&gt;&lt;br /&gt;In the following example, we have a 'Car' class defined without any methods. We also have a constructor called 'Mixin'. What we're going to do is augment the Car 'class' so it has access to the methods within the Mixin.This code demonstrates how with JavaScript you can augment a constructor to have a particular method without using the typical inheritance methods or duplicating code for each constructor function you have.&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;/* Car Class */&lt;br /&gt;02&lt;br /&gt;var Car = function(settings){&lt;br /&gt;03&lt;br /&gt;    this.model = settings.model || 'no model provided';&lt;br /&gt;04&lt;br /&gt;    this.colour = settings.colour || 'no colour provided';&lt;br /&gt;05&lt;br /&gt;};&lt;br /&gt;06&lt;br /&gt; &lt;br /&gt;07&lt;br /&gt;/* Mixin Class */&lt;br /&gt;08&lt;br /&gt;var Mixin = function(){};&lt;br /&gt;09&lt;br /&gt;Mixin.prototype = {&lt;br /&gt;10&lt;br /&gt;    driveForward: function(){&lt;br /&gt;11&lt;br /&gt;        console.log('drive forward');&lt;br /&gt;12&lt;br /&gt;    },&lt;br /&gt;13&lt;br /&gt;    driveBackward: function(){&lt;br /&gt;14&lt;br /&gt;        console.log('drive backward');&lt;br /&gt;15&lt;br /&gt;    }&lt;br /&gt;16&lt;br /&gt;};&lt;br /&gt;17&lt;br /&gt; &lt;br /&gt;18&lt;br /&gt; &lt;br /&gt;19&lt;br /&gt;/* Augment existing class with a method from another class */&lt;br /&gt;20&lt;br /&gt;function augment(receivingClass, givingClass) {&lt;br /&gt;21&lt;br /&gt;    /* only provide certain methods */&lt;br /&gt;22&lt;br /&gt;    if (arguments[2]) {&lt;br /&gt;23&lt;br /&gt;        for (var i=0, len=arguments.length; i&lt;len; i++) {&lt;br /&gt;24&lt;br /&gt;            receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];&lt;br /&gt;25&lt;br /&gt;        }&lt;br /&gt;26&lt;br /&gt;    }&lt;br /&gt;27&lt;br /&gt;    /* provide all methods*/&lt;br /&gt;28&lt;br /&gt;    else {&lt;br /&gt;29&lt;br /&gt;        for (methodName in givingClass.prototype) {&lt;br /&gt;30&lt;br /&gt;            /* check to make sure the receiving class doesn't&lt;br /&gt;31&lt;br /&gt;               have a method of the same name as the one currently&lt;br /&gt;32&lt;br /&gt;               being processed */&lt;br /&gt;33&lt;br /&gt;            if (!receivingClass.prototype[methodName]) {&lt;br /&gt;34&lt;br /&gt;                receivingClass.prototype[methodName] = givingClass.prototype[methodName];&lt;br /&gt;35&lt;br /&gt;            }&lt;br /&gt;36&lt;br /&gt;        }&lt;br /&gt;37&lt;br /&gt;    }&lt;br /&gt;38&lt;br /&gt;}&lt;br /&gt;39&lt;br /&gt; &lt;br /&gt;40&lt;br /&gt; &lt;br /&gt;41&lt;br /&gt;/* Augment the Car class to have the methods 'driveForward' and 'driveBackward'*/&lt;br /&gt;42&lt;br /&gt;augment(Car, Mixin,'driveForward','driveBackward');&lt;br /&gt;43&lt;br /&gt; &lt;br /&gt;44&lt;br /&gt;/* Create a new Car */&lt;br /&gt;45&lt;br /&gt;var vehicle = new Car({model:'Ford Escort', colour:'blue'});&lt;br /&gt;46&lt;br /&gt; &lt;br /&gt;47&lt;br /&gt;/* Test to make sure we now have access to the methods*/&lt;br /&gt;48&lt;br /&gt;vehicle.driveForward();&lt;br /&gt;49&lt;br /&gt;vehicle.driveBackward();&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Decorator Pattern&lt;br /&gt;&lt;br /&gt;Decorator patterns are an alternative to creating subclasses. This pattern can be used to wrap objects within another object of the same interface and allows you to both add behaviour to methods and also pass the method call to the original object (ie the constructor of the decorator).&lt;br /&gt;&lt;br /&gt;The decorator pattern is used when you need to keep adding new functionality to overridden methods. This can be achieved by stacking multiple decorators on top of one another.&lt;br /&gt;&lt;br /&gt;What is the main benefit of using a decorator pattern? Well, if we examine our first definition, I mentioned that decorators are an alternative to subclassing. When a script is being run, subclassing adds behaviour that affects all the instances of the original class, whilst decorating does not. It instead can add new behaviour for individual objects, which can be of benefit depending on the application in question. Let’s take a look at some code that implements the decorator pattern:&lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;//The class we're going to decorate&lt;br /&gt;02&lt;br /&gt;function Macbook(){&lt;br /&gt;03&lt;br /&gt;      this.cost = function(){&lt;br /&gt;04&lt;br /&gt;      return 1000;&lt;br /&gt;05&lt;br /&gt;     };&lt;br /&gt;06&lt;br /&gt;}&lt;br /&gt;07&lt;br /&gt; &lt;br /&gt;08&lt;br /&gt;function Memory(macbook){&lt;br /&gt;09&lt;br /&gt;    this.cost = function(){&lt;br /&gt;10&lt;br /&gt;     return macbook.cost() + 75;&lt;br /&gt;11&lt;br /&gt;  };&lt;br /&gt;12&lt;br /&gt;}&lt;br /&gt;13&lt;br /&gt; &lt;br /&gt;14&lt;br /&gt;function BlurayDrive(macbook){&lt;br /&gt;15&lt;br /&gt;   this.cost = function(){&lt;br /&gt;16&lt;br /&gt;     return macbook.cost() + 300;&lt;br /&gt;17&lt;br /&gt;  };&lt;br /&gt;18&lt;br /&gt;}&lt;br /&gt;19&lt;br /&gt; &lt;br /&gt;20&lt;br /&gt; &lt;br /&gt;21&lt;br /&gt;function Insurance(macbook){&lt;br /&gt;22&lt;br /&gt;   this.cost = function(){&lt;br /&gt;23&lt;br /&gt;     return macbook.cost() + 250;&lt;br /&gt;24&lt;br /&gt;  };&lt;br /&gt;25&lt;br /&gt;}&lt;br /&gt;26&lt;br /&gt; &lt;br /&gt;27&lt;br /&gt;  &lt;br /&gt;28&lt;br /&gt;// Sample usage&lt;br /&gt;29&lt;br /&gt;var myMacbook = new Insurance(new BlurayDrive(new Memory(new Macbook())));&lt;br /&gt;30&lt;br /&gt;console.log( myMacbook.cost() );&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Here's another decorator example where when we invoke performTask on the decorator object, it both performs some behaviour and invokes performTask on the underlying object.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;01&lt;br /&gt;function ConcreteClass(){&lt;br /&gt;02&lt;br /&gt;    this.performTask = function()&lt;br /&gt;03&lt;br /&gt;    {&lt;br /&gt;04&lt;br /&gt;        this.preTask();&lt;br /&gt;05&lt;br /&gt;        console.log('doing something');&lt;br /&gt;06&lt;br /&gt;        this.postTask();&lt;br /&gt;07&lt;br /&gt;    }&lt;br /&gt;08&lt;br /&gt;}&lt;br /&gt;09&lt;br /&gt; &lt;br /&gt;10&lt;br /&gt;function AbstractDecorator(decorated){&lt;br /&gt;11&lt;br /&gt;    this.performTask = function()&lt;br /&gt;12&lt;br /&gt;    {&lt;br /&gt;13&lt;br /&gt;        decorated.performTask();&lt;br /&gt;14&lt;br /&gt;    }&lt;br /&gt;15&lt;br /&gt;}&lt;br /&gt;16&lt;br /&gt; &lt;br /&gt;17&lt;br /&gt;function ConcreteDecoratorClass(decorated){&lt;br /&gt;18&lt;br /&gt;    this.base = AbstractDecorator;&lt;br /&gt;19&lt;br /&gt;    this.base(decorated);&lt;br /&gt;20&lt;br /&gt;     &lt;br /&gt;21&lt;br /&gt;    this.preTask = function()&lt;br /&gt;22&lt;br /&gt;    {&lt;br /&gt;23&lt;br /&gt;        console.log('pre-calling..');&lt;br /&gt;24&lt;br /&gt;    }&lt;br /&gt;25&lt;br /&gt;     &lt;br /&gt;26&lt;br /&gt;    this.postTask = function()&lt;br /&gt;27&lt;br /&gt;    {&lt;br /&gt;28&lt;br /&gt;        console.log('post-calling..');&lt;br /&gt;29&lt;br /&gt;    }&lt;br /&gt;30&lt;br /&gt;     &lt;br /&gt;31&lt;br /&gt;}&lt;br /&gt;32&lt;br /&gt; &lt;br /&gt;33&lt;br /&gt;var concrete = new ConcreteClass();&lt;br /&gt;34&lt;br /&gt;var decorator1 = new ConcreteDecoratorClass(concrete);&lt;br /&gt;35&lt;br /&gt;var decorator2 = new ConcreteDecoratorClass(decorator1);&lt;br /&gt;36&lt;br /&gt;decorator2.performTask();&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Design Patterns in jQuery&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Now that we've taken a look at vanilla-JavaScript implementations of popular design patterns, let's switch gears and find out what of these design patterns might look like when implemented using jQuery. jQuery (as you may know) is currently the most popular JavaScript library and provides a layer of 'sugar' on top of regular JavaScript with a syntax that can be easier to understand at a glance.&lt;br /&gt;&lt;br /&gt;Before we dive into this section, it's important to remember that many vanilla-JavaScript design patterns can be intermixed with jQuery when used correctly because jQuery is still essentially JavaScript itself. &lt;br /&gt;&lt;br /&gt;jQuery is an interesting topic to discuss in the realm of patterns because the library actually uses a number of design patterns itself.  What impresses me is just how cleanly all of the patterns it uses have been implemented so that they exist in harmony.&lt;br /&gt;&lt;br /&gt;Let's take a look at what some of these patterns are and how they are used.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Module Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Module Pattern is based on the idea of having an encapsulated module which doesn't conflict with other modules in your code nor modules created by others which are included through other scripts on the page. This pattern is covered in greater detail in the section on pattern implementations in JavaScript but below is a version showing how you can use it in jQuery. &lt;br /&gt;01&lt;br /&gt;$(function(){&lt;br /&gt;02&lt;br /&gt;    var itemClass = (function(){&lt;br /&gt;03&lt;br /&gt;        var someItem = $('#item');&lt;br /&gt;04&lt;br /&gt;        return{&lt;br /&gt;05&lt;br /&gt;            thisItem: function(){&lt;br /&gt;06&lt;br /&gt;                thisItem = document.createElement("div");&lt;br /&gt;07&lt;br /&gt;                $(thisItem)&lt;br /&gt;08&lt;br /&gt;                    .html("test")&lt;br /&gt;09&lt;br /&gt;                    .appendTo("#container");&lt;br /&gt;10&lt;br /&gt;            }&lt;br /&gt;11&lt;br /&gt;        }&lt;br /&gt;12&lt;br /&gt;    })();&lt;br /&gt;13&lt;br /&gt;});&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;Lazy Initialization&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Lazy Initialization is a design pattern where you employ a tactic of delaying any expensive processes (eg. the creation of objects) until the first instance they are needed. An example of this is the .ready() function in jQuery that only executes a function once the DOM has fully loaded.&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;  $(document).ready(function(){&lt;br /&gt;2&lt;br /&gt;      $('#content').fadeIn();&lt;br /&gt;3&lt;br /&gt;  });&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Composite Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Composite Pattern describes a group of objects that can be treated in the same way a single instance of an object can. Implementing this pattern allows you to treat both individual objects and compositions in a uniform manner. In jQuery, when we're accessing or performing actions on a single DOM element or a group of DOM elements, we can treat both in a uniform manner. This is demonstrated by the code sample below:&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;$('#someDiv').addClass('active');  // a single element&lt;br /&gt;2&lt;br /&gt; $('div').addClass('active');      // a collection of elements&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Wrapper Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Wrapper Pattern is a pattern which translates an interface for a class into a compatible interface. Wrappers basically allow classes to function together which normally couldn't due to their incompatible interfaces. The wrapper translates calls to its interface into calls to the original interface and the code required to achieve this is usually quite minimal.&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;  $('.container').css({&lt;br /&gt;2&lt;br /&gt;        opacity: .5 //apply opacity in modern browsers (eg. Chrome, FireFox) but use filter for IE&lt;br /&gt;3&lt;br /&gt;    });&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Facade Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Facade Pattern is quite commonly used with OOP (Object-oriented programming) where a facade is an object which provides a simpler interface to a larger piece of code (eg. a class library). Facades can be frequently found across the jQuery library and make methods both easier to use and understand, but also more readable. The following are facades for jQuery's $.ajax():&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;  $.get();&lt;br /&gt;2&lt;br /&gt;  $.post();&lt;br /&gt;3&lt;br /&gt;  $.getJSON();&lt;br /&gt;4&lt;br /&gt;  $.getScript();&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Observer Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Observer pattern is where a subject (the object), keeps a list of its dependants, which are known as observers, and notifies them automatically of any changes in state. This is commonly done by calling one of their methods. The Observer pattern can be considered a subset of PubSub (publish/subscribe pattern).&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt; //Here jQuery makes use of its event system on top of DOM events&lt;br /&gt;2&lt;br /&gt;    $('.button').click(function(){})&lt;br /&gt;3&lt;br /&gt;    $('.button').trigger('click', function(){})&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Iterator Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Iterator Pattern is a design pattern where iterators (objects that allow us to traverse through all the elements of a collection) access the elements of an aggregate object sequentially without needing to expose its underlying form. Iterators encapsulate the internal structure of how that particular iteration occurs - in the case of jQuery's .each() iterator, you are actually able to use the underlying code behind .each() to iterate through a collection, without needing to see or understand the code working behind the scenes that's providing this capability. &lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;  $.each(function(){});&lt;br /&gt;2&lt;br /&gt;  $('.items').each(function(){});&lt;br /&gt;An interesting side-note is that jQuery's 'each' method is backwards from the ECMAScript 5 way of doing this but may change at some point in the future.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Strategy Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Strategy Pattern is a pattern where a script may select a particular algorithm at runtime. The purpose of this pattern is that it's able to provide a way to clearly define families of algorithms, encapsulate each as an object and make them easily interchangeable. You could say that the biggest benefit this pattern offers is that it allows algorithms to vary independent of the clients that utilize them. An example of this is where jQuery's toggle() allows you to bind two or more handlers to the matched elements, to be executed on alternate clicks.The strategy pattern allows for alternative algorithms to be used independent of the client internal to the function.&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;  $('#container').toggle(function(){}, function(){});&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Proxy Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Proxy Pattern - a proxy is basically a class that functions as an interface to something else. The proxy can be an interface to almost anything: a file, a resource, an object in memory, something else that is difficult to duplicate etc. jQuery's .proxy() function takes as input a function and returns a new one that will always have a particular context. This is parallel to the idea of providing an interface as per the proxy pattern.&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;  $.proxy(function(){}, obj);&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Builder Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Builder Pattern's main concept is that it abstracts the steps involved in creating objects so that different implementations of these steps have the ability to construct different representations of objects. Below is an example of how jQuery utilizes this pattern to allow an element which you may wish to append to the document body to be constructed using a string definition. &lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;$('&lt;div class= "foo"&gt; bar &lt;/div&gt;');&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;The Prototype Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Prototype Pattern is used when the objects you wish to create are determined by a prototypal instance that is cloned to produce the new objects. Essentially this pattern is used to avoid creating a new object in a standard manner when this process may be expensive or overly complex. In the following code sample which extends the jQuery.fn object for a minimal plugin, underlying prototypal code makes this possible:&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;    $.fn.plugin = function(){}&lt;br /&gt;2&lt;br /&gt;    $('#container').plugin();&lt;br /&gt;&lt;br /&gt;The Flyweight Pattern&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Flyweight Pattern is a design pattern where an object attempts to minimize the amount of memory used by sharing as much information as possible with other objects that are similar in nature. It's a way to utilize objects in large numbers when a simple repeated representation may use an amount of memory deemed unacceptable. There are often aspects of an object state that can be shared and it's commonplace that these be stored in external data-structures that are passed to the flyweight objects temporarily when needed.&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;  // The userConfig is shared here:&lt;br /&gt;2&lt;br /&gt; &lt;br /&gt;3&lt;br /&gt;  $.fn.plugin = function(userConfig){&lt;br /&gt;4&lt;br /&gt;       userConfig = $.extend({&lt;br /&gt;5&lt;br /&gt;           content: 'Hello user!'&lt;br /&gt;6&lt;br /&gt;       }, userConfig);&lt;br /&gt;7&lt;br /&gt;       return this.html(useConfig.content);&lt;br /&gt;8&lt;br /&gt;   });&lt;br /&gt;A side-note here is that prototypal inheritance in JavaScript uses differential inheritance to only define objects once in a prototype chain until they are overridden. This makes it easier to save memory.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Conclusions&lt;br /&gt;&lt;br /&gt;                &lt;br /&gt;That’s it for this introduction to the world of design patterns in JavaScript &amp; jQuery– I hope you’ve found it useful. The contents of this book are in no way an extensive look at the field of patterns, but should give you enough information to get started using the patterns covered in your day-to-day projects. &lt;br /&gt;&lt;br /&gt;Design patterns make it easier to reuse successful designs and architectures. It’s important for every developer to be aware of design patterns but it’s also essential to know how and when to use them. Implementing the right patterns intelligently can be worth the effort but the opposite is also true. A badly implementing pattern can yield little benefit to a project. &lt;br /&gt;&lt;br /&gt;Also bare in mind that it’s not the number of patterns you implement that’s important but how you choose to implement them. For example, don’t choose a pattern just for the sake of using ‘one’ but rather try understanding the pros and cons of what particular patterns have to offer and make a judgement based on it’s fitness for your application.&lt;br /&gt;&lt;br /&gt;If I’ve encouraged your interest in this area further and you would like to learn more about design patterns, there are a number of excellent titles on this area available for generic software development but also those that cover specific languages.&lt;br /&gt;&lt;br /&gt;For JavaScript developers, I recommend checking out two books:&lt;br /&gt;&lt;br /&gt;‘Pro JavaScript Design Patterns’ by Ross Harmes and Dustin Diaz.&lt;br /&gt;'JavaScript Patterns' by Stoyan Stefanov&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;If you’ve managed to absorb most of the information in my mini-book, I think you’ll find reading these the next logical step in your learning process (beyond trying out some pattern examples for yourself of course) : )&lt;br /&gt;&lt;br /&gt;Thanks for reading Essential JavaScript &amp; jQuery Design Patterns. For more free learning material on JavaScript, jQuery and User-Interface Design, check out my official site over at http://addyosmani.com for my latest educational resources.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;References&lt;br /&gt;Design Principles and Design Patterns - Robert C Martinhttp://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf&lt;br /&gt;Ralph Johnson - Special Issue of ACM On Patterns and Pattern Languages - http://www.cs.wustl.edu/~schmidt/CACM-editorial.html&lt;br /&gt;Hillside Engineering Design Patterns Library - http://hillside.net/patterns/&lt;br /&gt;Pro JavaScript Design Patterns - Ross Harmes and Dustin Diaz http://jsdesignpatterns.com/&lt;br /&gt;Design Pattern Definitions - http://en.wikipedia.org/wiki/Design_Patterns&lt;br /&gt;Patterns and Software Terminology http://www.cmcrossroads.com/bradapp/docs/patterns-intro.html&lt;br /&gt;Reap the benefits of Design Patterns - Jeff Juday http://articles.techrepublic.com.com/5100-10878_11-5173591.html&lt;br /&gt;JavaScript Design Patterns - Subramanyan Guhan http://www.slideshare.net/rmsguhan/javascript-design-patterns&lt;br /&gt;What Are Design Patterns and Do I Need Them? - James Moaoriello http://www.developer.com/design/article.php/1474561&lt;br /&gt;Software Design Patterns - Alex Barnett http://alexbarnett.net/blog/archive/2007/07/20/software-design-patterns.aspx&lt;br /&gt;Evaluating Software Design Patterns - Gunni Rode http://www.rode.dk/thesis/&lt;br /&gt;SourceMaking Design Patterns http://sourcemaking.com/design_patterns&lt;br /&gt;The Singleton - Prototyp.ical http://prototyp.ical.ly/index.php/2007/03/01/javascript-design-patterns-1-the-singleton/&lt;br /&gt;JavaScript Patterns - Stoyan Stevanov - http://www.slideshare.net/stoyan/javascript-patterns&lt;br /&gt;Stack Overflow - Design Pattern Implementations in JavaScript (discussion) http://stackoverflow.com/questions/24642/what-are-some-examples-of-design-pattern-implementations-using-javascript&lt;br /&gt;The Elements of a Design Pattern - Jared Spool http://www.uie.com/articles/elements_of_a_design_pattern/&lt;br /&gt;Stack Overflow - Examples of Practical JS Design Patterns (discussion) http://stackoverflow.com/questions/3722820/examples-of-practical-javascript-object-oriented-design-patterns&lt;br /&gt;Design Patterns in JavaScript Part 1 - Nicholas Zakkas http://www.webreference.com/programming/javascript/ncz/column5/&lt;br /&gt;Stack Overflow - Design Patterns in jQuery http://stackoverflow.com/questions/3631039/design-patterns-used-in-the-jquery-library&lt;br /&gt;Classifying Design Patterns By AntiClue - Elyse Neilson http://www.anticlue.net/archives/000198.htm&lt;br /&gt;Design Patterns, Pattern Languages and Frameworks - Douglas Schmidt http://www.cs.wustl.edu/~schmidt/patterns.html&lt;br /&gt;Show Love To The Module Pattern - Christian Heilmann http://www.wait-till-i.com/2007/07/24/show-love-to-the-module-pattern/&lt;br /&gt;JavaScript Design Patterns - Mike G. http://www.lovemikeg.com/2010/09/29/javascript-design-patterns/&lt;br /&gt;Software Designs Made Simple - Anoop Mashudanan http://www.scribd.com/doc/16352479/Software-Design-Patterns-Made-Simple&lt;br /&gt;JavaScript Design Patterns - Klaus Komenda http://www.klauskomenda.com/code/javascript-programming-patterns/&lt;br /&gt;Introduction to the JavaScript Module Pattern https://www.unleashed-technologies.com/blog/2010/12/09/introduction-javascript-module-design-pattern&lt;br /&gt;Design Patterns Explained - http://c2.com/cgi/wiki?DesignPatterns&lt;br /&gt;Mixins explained http://en.wikipedia.org/wiki/Mixin&lt;br /&gt;Working with GoF's Design Patterns In JavaScript http://aspalliance.com/1782_Working_with_GoFs_Design_Patterns_in_JavaScript_Programming.all&lt;br /&gt;Using Object.createhttp://stackoverflow.com/questions/2709612/using-object-create-instead-of-new&lt;br /&gt;t3knomanster's JavaScript Design Patterns http://t3knomanser.livejournal.com/922171.html&lt;br /&gt;Working with GoF Design Patterns In JavaScript Programming - http://aspalliance.com/1782_Working_with_GoFs_Design_Patterns_in_JavaScript_Programming.7&lt;br /&gt;JavaScript Class Patterns - Liam McLennanhttp://geekswithblogs.net/liammclennan/archive/2011/02/06/143842.aspx&lt;br /&gt;Speaking on the Observer pattern - http://www.javaworld.com/javaworld/javaqa/2001-05/04-qa-0525-observer.html&lt;br /&gt;Singleton examples in JavaScript - Hardcode.nlhttp://www.hardcode.nl/subcategory_1/article_526-singleton-examples-in-javascript.htm&lt;br /&gt;Design Patterns by Gamma, Helm supplement http://exciton.cs.rice.edu/javaresources/DesignPatterns/&lt;br /&gt;&lt;br /&gt;reference : http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1735121625152262841?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1735121625152262841/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1735121625152262841' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1735121625152262841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1735121625152262841'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/06/essential-javascript-design-patterns.html' title='Essential JavaScript Design Patterns For Beginners'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-6482811590912036535</id><published>2011-05-24T07:53:00.000-07:00</published><updated>2011-05-24T07:55:21.863-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><title type='text'>OS X 10.6 &amp; PC 共用的檔案格式</title><content type='html'>Mac 和 PC 要共用，最佳解法是使用 extfs.&lt;br /&gt;extfs 是目前多數 linux 使用的檔案系統，完全的公開技術.&lt;br /&gt;在各種平台，都有免費且完整的支援工具..&lt;br /&gt;macos 用的http://sourceforge.net/projects/ext2fsx/&lt;br /&gt;windows 用的http://sourceforge.net/projects/ext2fsd/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-6482811590912036535?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/6482811590912036535/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=6482811590912036535' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6482811590912036535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6482811590912036535'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/05/os-x-106-pc.html' title='OS X 10.6 &amp; PC 共用的檔案格式'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-8663694982160675103</id><published>2011-05-24T07:52:00.000-07:00</published><updated>2011-05-24T07:53:16.159-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><title type='text'>OS X 10.6 雪豹下使用 NTFS 內建或外部 USB 隨身碟</title><content type='html'>剛使用 MacBook 的時候，最不能讓我適應的是外部存檔這件事了。不管是使用網路空間還是外部硬碟，都是困難重重。好像非得要用MobileMe 才可以輕易的把檔案在不同裝置裡傳來傳去。接上原來在 PC 上使用的 NTFS 格式 USB 硬碟，檔案可以輕易的讀出，可是不能寫入。好一陣子，我都是用 FTP 在儲存我的檔案。可是我還是覺得麻煩。經過一陣子的搜尋後，終於找到幾個好用的辦法了。&lt;br /&gt;&lt;br /&gt;在 OS X 下使用NTFS 硬碟讀寫有好幾種方式，可以修該 fstab，使用 MacFUSE + NTFS-3G 或使用付費軟體。這幾個方法我都試過了，都不錯。可是最近 google 到了一個新方法，我個人非常喜歡，因為不用下載新軟體，直接使用雪豹的程式就可以了。不過這個進階設定直接修改雪豹的 NTFS 驅動方法，風險還是有的，修改前請先有心理準備。&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;警告： 本文件說明了如何通過在終端機應用程式中輸入指令來修改權限設定。 不熟悉 Terminal 和 UNIX 式環境的使用者應小心進行作業。 輸入不正確的指令可能會導致資料流失及/或系統軟體無法使用。 不當修改權限可能會降低系統的安全性及/或造成私密資料外洩。 &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;原理：&lt;br /&gt;&lt;br /&gt;雪豹的 NTFS 驅動程式在 /sbin/ 下面，檔案名稱是 mount_ntfs。這個驅動程式原本就可以把 NTFS 硬碟裝在 OS X 的檔案系統下面，可讀可寫。可是雪豹的出廠設定只讓 NTFS 硬碟可以讀，但不能寫。我們只要修改這個設定，把參數加上“讀寫”就好了。&lt;br /&gt;&lt;br /&gt;因為硬碟檢測是自動的，OS X 偵測到 NTFS 硬碟，就會直接調用這個程式。我們只要寫一個小小的銜接程式（mddleware），加入我們要的參數，就大功告成了。&lt;br /&gt;&lt;br /&gt;動工修改：&lt;br /&gt;&lt;br /&gt;因為要直接修改雪豹，我們必須開啓終端機，使用系統管理員的權限做修改。建議先開啓 time machine 備份資料，萬一修改有問題還有機會回復。&lt;br /&gt;&lt;br /&gt;（以下修改都在終端機裡面進行）&lt;br /&gt;&lt;br /&gt; [補充] 如果你是使用系統管理員帳戶登入的話就可以直接從 STEP 1 開始。如果你現在登入的不是系統管理員，先在終端機裡變更身分，就可以順利進行。Stanly 提供另一個啓用 root 帳號的方法也可以。記得全部做完以後再把 root 帳號關起來就是了。參考：啓用和停用 root 帳號&lt;br /&gt;&lt;br /&gt;變更使用者身分：假設管理員帳號名稱為 vincent，在終端機裡打入 su vincent (按 return)，系統會問你密碼。輸入密碼後你的身分就變成管理員 vincent 了。進行 STEP 1－》&lt;br /&gt;&lt;br /&gt;＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝&lt;br /&gt;&lt;br /&gt;[Stanley補充] 開啟root權限 （如果你從Step 1開始做發現有問題不能繼續時，請照這裡步驟先開啟root帳號，再從頭來過）&lt;br /&gt;&lt;br /&gt;在終端機裡輸入 sudo passwd root （然後按下enter）&lt;br /&gt;&lt;br /&gt;會跳出請你輸入password，請先輸入你這個原本帳號的密碼&lt;br /&gt;&lt;br /&gt;然後會請你重新設定root帳號密碼，並在輸入一次確認&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;接著，在終端機裡輸入 su （然後按下enter）&lt;br /&gt;&lt;br /&gt;然後輸入剛剛重新設定的root密碼就可以以root權限使用終端機&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;STEP 1. 保存並更名原有驅動程式&lt;br /&gt;&lt;br /&gt;打入 sudo mv /sbin/mount_ntfs /sbin/mount_ntfs.orig&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;系統會問你管理員密碼，輸入管理員密碼。&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;[重要補充] 檢查 mount_ntfs 是否已更名&lt;br /&gt;&lt;br /&gt;（在終端機裡面）打入 ls /sbin/mount* (按 return)&lt;br /&gt;&lt;br /&gt;系統會列出 /sbin 下面以 mount 開頭的所有檔案。檢查看看 mount_ntfs.orig 在不在，如果不在，檢查一下出了甚麼問題，不要直接往下做。如果沒有順利保存原有程式，你可能連讀取資料的機會都沒了。&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;STEP 2. 製作銜接程式（mddleware）。&lt;br /&gt;&lt;br /&gt;（進階使用者：直接使用你熟悉的編輯軟體製作以下內容的檔案， 檔案名稱為 mount_ntfs （一律小寫），放在 /sbin 目錄下。跳到 STEP 3）&lt;br /&gt;&lt;br /&gt;銜接程式內容只有兩行：&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;/sbin/mount_ntfs.orig -o rw "$@"&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;詳細步驟（在終端機裡面進行）：&lt;br /&gt;&lt;br /&gt;開啓編輯器，打入 nano /sbin/mount_ntfs （然後按下enter）&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;輸入程式碼&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;按 Control -O 儲存檔案，再按 Control -X 離開編輯器&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;STEP 3. 授與銜接程式適當權限&lt;br /&gt;&lt;br /&gt;在終端機中打入&lt;br /&gt;&lt;br /&gt;sudo chown root:wheel /sbin/mount_ntfs （然後按下enter）&lt;br /&gt;&lt;br /&gt;sudo chmod 755 /sbin/mount_ntfs （然後按下enter）&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;大功告成！！&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;****還原步驟*****&lt;br /&gt;&lt;br /&gt;如果有任何理由想要還原上述變更的話，只要打入 sudo mv /sbin/mount_ntfs.orig /sbin/mount_ntfs 把原來的檔還原就好了。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;**注意事項：網路上有相同的方法，而且程式檔案已經做好，可以免費下載。但為了電腦安全，建議不要下載不明軟體，還是自己花點時間，敲敲鍵盤吧！&lt;br /&gt;&lt;br /&gt;reference : http://www.macuknow.com/node/6718&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-8663694982160675103?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/8663694982160675103/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=8663694982160675103' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8663694982160675103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8663694982160675103'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/05/os-x-106-ntfs-usb.html' title='OS X 10.6 雪豹下使用 NTFS 內建或外部 USB 隨身碟'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5383900652488754221</id><published>2011-05-11T16:03:00.000-07:00</published><updated>2011-05-13T13:29:48.417-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='&#xA;程式-PHP'/><title type='text'>Multithreading in php</title><content type='html'>Some time ago, I’ve wrote a small server in PHP. Nothing fancy. It would listen on a socket and when a new client would connect, the server would start a new thread and manage the client’s request. Since threading it’s not available in PHP, I’ve emulated the threads with child processes which are available in php. A thread object simply encapsulates a new process started with pnctl_fork() and emulates – to some extent – the behaviour of the java.lang.Thread class, the main difference being that in my implementation, you don’t extend the Thread class, you simply provide the name of a callback function in the constructor.&lt;br /&gt;&lt;br /&gt;A simple multithreaded application would look like this:&lt;br /&gt;&lt;br /&gt;require_once( 'Thread.php' );&lt;br /&gt; &lt;br /&gt;// test to see if threading is available&lt;br /&gt;if( ! Thread::available() ) {&lt;br /&gt; die( 'Threads not supported' );&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;// function to be ran on separate threads&lt;br /&gt;function paralel( $_limit, $_name ) {&lt;br /&gt; for ( $index = 0; $index &lt; $_limit; $index++ ) {&lt;br /&gt;  echo 'Now running thread ' . $_name . PHP_EOL;&lt;br /&gt;  sleep( 1 );&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;// create 2 thread objects&lt;br /&gt;$t1 = new Thread( 'paralel' );&lt;br /&gt;$t2 = new Thread( 'paralel' );&lt;br /&gt; &lt;br /&gt;// start them&lt;br /&gt;$t1-&gt;start( 10, 't1' );&lt;br /&gt;$t2-&gt;start( 10, 't2' );&lt;br /&gt; &lt;br /&gt;// keep the program running until the threads finish&lt;br /&gt;while( $t1-&gt;isAlive() &amp;&amp; $t2-&gt;isAlive() ) {&lt;br /&gt; &lt;br /&gt;}&lt;br /&gt;This will display Now running thread 1 and Now running thread 2 messages with 1 second delays. I know, not that impressive, but hey, it’s multithreaded.&lt;br /&gt;&lt;br /&gt;PHP threading will only work on Unix and Linux systems, because pnctl_fork is nothing more than a wrapper for the fork() function from unistd.h and it’s not available under Microsoft operating systems.&lt;br /&gt;&lt;br /&gt;I know that the first example was pretty lame, but there are some more interesting things you could do with threads in PHP. For instance, if you need to do some server side processing of all the images in a directory, a multithreaded approach will be much faster.&lt;br /&gt;&lt;br /&gt;require_once( 'Thread.php' );&lt;br /&gt; &lt;br /&gt;// test to see if threading is available&lt;br /&gt;if( ! Thread::available() ) {&lt;br /&gt; die( 'Threads not supported' );&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;// define the function to be run as a separate thread&lt;br /&gt;function processImage( $_image ) {&lt;br /&gt; // expensive image processing logic here&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;$threads = array();&lt;br /&gt;$index = 0;&lt;br /&gt; &lt;br /&gt;foreach( new DirectoryIterator( '/path/to/images' ) as $item ) {&lt;br /&gt; if( $item-&gt;isFile() ) {&lt;br /&gt;  $threads[$index] = new Thread( 'processImage' );&lt;br /&gt;  $threads[$index]-&gt;start( $item-&gt;getPathname() );&lt;br /&gt;  ++$index;&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;// wait for all the threads to finish&lt;br /&gt;while( !empty( $threads ) ) {&lt;br /&gt; foreach( $threads as $index =&gt; $thread ) {&lt;br /&gt;  if( ! $thread-&gt;isAlive() ) {&lt;br /&gt;   unset( $threads[$index] );&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; // let the CPU do its work&lt;br /&gt; sleep( 1 );&lt;br /&gt;}&lt;br /&gt;PS: it’s a bad practice to keep looping in order to wait for a thread to finish. An ongoing empty loop will quickly boost your CPU ’s load to 100%. If you need your processor free (and you need it), simply send the current (looping) thread to sleep and let the others execute.&lt;br /&gt;&lt;br /&gt;// wait for thread - bad approach (overloads the CPU)&lt;br /&gt;while ( $thread-&gt;isAlive() ) {&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;// wait for thread - correct approach&lt;br /&gt;while( $thread-&gt;isAlive() ) {&lt;br /&gt;    sleep( 1 ); &lt;br /&gt;}&lt;br /&gt;Download Thread.php&lt;br /&gt;Here you go: Thread.php. Just click the link to download the class. If you have a better approach to this issue or think that the original class could be improved, don’t be shy and leave a comment below. Credits will be given.&lt;br /&gt;&lt;br /&gt;reference : http://blog.motane.lu/2009/01/02/multithreading-in-php/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5383900652488754221?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5383900652488754221/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5383900652488754221' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5383900652488754221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5383900652488754221'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/05/multithreading-in-php.html' title='Multithreading in php'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-6258075288010730812</id><published>2011-05-10T18:35:00.001-07:00</published><updated>2011-05-10T18:35:45.453-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>PHP CLI slow to exit with CURL + MySQL extensions</title><content type='html'>Description:&lt;br /&gt;------------&lt;br /&gt;OK, this is a strange one.  If you have CURL and either the php_mysql or  php_pdo_mysql extensions active, then PHP (command line) will hang for about 5 seconds before actually exiting the script.&lt;br /&gt;&lt;br /&gt;This is especially problematic in a CGI scenario, because the connection to the browser isn't closed during this delay and it looks like the request isn't complete.&lt;br /&gt;&lt;br /&gt;The problem goes away if I comment out the Mysql extensions in my ini file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-6258075288010730812?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/6258075288010730812/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=6258075288010730812' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6258075288010730812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6258075288010730812'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/05/php-cli-slow-to-exit-with-curl-mysql.html' title='PHP CLI slow to exit with CURL + MySQL extensions'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-3150236494437558780</id><published>2011-04-24T19:10:00.000-07:00</published><updated>2011-04-24T19:11:17.512-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><title type='text'>Failed: There was a problem authoring the DVD</title><content type='html'>solution:&lt;br /&gt;Burn -&gt; Preferences -&gt; Video -&gt; DVD -&gt; NTSC&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-3150236494437558780?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/3150236494437558780/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=3150236494437558780' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3150236494437558780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3150236494437558780'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/failed-there-was-problem-authoring-dvd.html' title='Failed: There was a problem authoring the DVD'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-6673696068977758436</id><published>2011-04-21T00:28:00.000-07:00</published><updated>2011-04-21T00:29:56.024-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>Comparison of file systems</title><content type='html'>The following tables compare general and technical information for a number of file systems.&lt;br /&gt;&lt;br /&gt;reference : http://en.wikipedia.org/wiki/Comparison_of_file_systems&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-6673696068977758436?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/6673696068977758436/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=6673696068977758436' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6673696068977758436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/6673696068977758436'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/comparison-of-file-systems.html' title='Comparison of file systems'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-325800340300603114</id><published>2011-04-17T19:37:00.001-07:00</published><updated>2011-04-17T19:37:39.877-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='軟體推薦'/><title type='text'>mac osx pdf電子書閱讀軟體推薦</title><content type='html'>軟體﹣Skim&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-325800340300603114?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/325800340300603114/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=325800340300603114' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/325800340300603114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/325800340300603114'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/mac-osx-pdf.html' title='mac osx pdf電子書閱讀軟體推薦'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-2308351460754972722</id><published>2011-04-15T03:23:00.000-07:00</published><updated>2011-04-15T03:24:08.084-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows系統'/><title type='text'>NirCmd v2.52 - Freeware Windows command-line tool</title><content type='html'>NirCmd is a small command-line utility that allows you to do some useful tasks without displaying any user interface. By running NirCmd with simple command-line option, you can write and delete values and keys in the Registry, write values into INI file, dial to your internet account or connect to a VPN network, restart windows or shut down the computer, create shortcut to a file, change the created/modified date of a file, change your display settings, turn off your monitor, open the door of your CD-ROM drive, and more...&lt;br /&gt;&lt;br /&gt;Examples of what you can do with NirCmd&lt;br /&gt;&lt;br /&gt;Open the door of J: CD-ROM drive  nircmd.exe cdrom open j:&lt;br /&gt;Close the door of Y: CD-ROM drive  nircmd.exe cdrom close y:&lt;br /&gt;Speaks the text currently in the clipboard (For Windows XP and Vista).  speak text ~$clipboard$&lt;br /&gt;Increase the system volume by 2000 units (out of 65535)  nircmd.exe changesysvolume 2000&lt;br /&gt;Decrease the system volume by 5000 units (out of 65535)  nircmd.exe changesysvolume -5000&lt;br /&gt;Set the volume to the highest value  nircmd.exe setsysvolume 65535&lt;br /&gt;Mute the system volume  nircmd.exe mutesysvolume 1&lt;br /&gt;Unmute the system volume  nircmd.exe mutesysvolume 0&lt;br /&gt;Switch the system volume between the mute and normal state.  nircmd.exe mutesysvolume 2&lt;br /&gt;Create a shortcut on your desktop that switch the system volume between the mute and normal state.  nircmd.exe cmdshortcut "~$folder.desktop$" "Switch Volume" mutesysvolume 2&lt;br /&gt;Turn off the monitor  nircmd.exe monitor off&lt;br /&gt;Start the default screen saver  nircmd.exe screensaver&lt;br /&gt;Put your computer in 'standby' mode  nircmd.exe standby&lt;br /&gt;log off the current user  nircmd.exe exitwin logoff&lt;br /&gt;Ask if you want to reboot, and if you answer 'Yes', reboot the computer.  nircmd.exe qboxcom "Do you want to reboot ?" "question" exitwin reboot&lt;br /&gt;Turn off your computer  nircmd.exe exitwin poweroff&lt;br /&gt;Turn off all computers specified in computers.txt !  multiremote copy "c:\temp\computers.txt" exitwin poweroff force&lt;br /&gt;Dial to "My Internet" connection  nircmd.exe rasdial "My Internet"&lt;br /&gt;Disconnect the "My Internet" connection  nircmd.exe rashangup "My Internet"&lt;br /&gt;Make your Internet Explorer windows 75% transparent ! (192 / 256)  nircmd.exe win trans ititle "internet explorer" 192&lt;br /&gt;Minimize all your Internet Explorer windows  nircmd.exe win min class "IEFrame"&lt;br /&gt;Close all your Internet Explorer windows  nircmd.exe win close class "IEFrame"&lt;br /&gt;Close all your Explorer windows (My Computer, folders, and so on)  nircmd.exe win close class "CabinetWClass"&lt;br /&gt;Hide all your Internet Explorer windows  nircmd.exe win hide class "IEFrame"&lt;br /&gt;Show all your Internet Explorer windows (after you made them hidden with previous example)  nircmd.exe win show class "IEFrame"&lt;br /&gt;Center all top-level windows  nircmd.exe win center alltop&lt;br /&gt;Remove the title bar of My Computer window.  nircmd.exe win -style title "my computer" 0x00C00000&lt;br /&gt;Return the title bar of My Computer window that we removed in the previous example.  nircmd.exe win +style title "my computer" 0x00C00000&lt;br /&gt;Set the My Computer window to right-to-left order (For hebrew and arabic languages)  nircmd win +exstyle title "my computer" 0x00400000&lt;br /&gt;Set all child windows of My Computer window to right-to-left order (For hebrew and arabic languages)  nircmd win child title "my computer" +exstyle all 0x00400000&lt;br /&gt;Create a shortcut on your desktop that closes all your Internet Explorer windows  nircmd.exe cmdshortcut " "~$folder.desktop$ "Close All IE" win close class "IEFrame"&lt;br /&gt;Create a shortcut on your desktop that hides all your Internet Explorer windows  nircmd.exe cmdshortcut " "~$folder.desktop$ "Hide All IE" win hide class "IEFrame"&lt;br /&gt;Create a shortcut on your desktop that shows back all your Internet Explorer windows  nircmd.exe cmdshortcut " "~$folder.desktop$ "Show All IE" win show class "IEFrame"&lt;br /&gt;Set the Windows Calculator as top-most window (above all other windows)  nircmd.exe win settopmost title "Calculator" 1&lt;br /&gt;Set the Windows Calculator back to regular window (non top-most window)  nircmd.exe win settopmost title "Calculator" 0&lt;br /&gt;Create a shortcut to Windows calculator under Start Menu-&gt;Programs-&gt;Calculators  nircmd.exe shortcut "f:\winnt\system32\calc.exe" "~$folder.programs$\Calculators" "Windows Calculator"&lt;br /&gt;Hide the desktop window  nircmd.exe win hide class progman&lt;br /&gt;Show the desktop window (After hiding it in previous example)  nircmd.exe win show class progman&lt;br /&gt;Hide the start button on the system tray  nircmd.exe win child class "Shell_TrayWnd" hide class "button"&lt;br /&gt;Show the start button on the system tray  nircmd.exe win child class "Shell_TrayWnd" show class "button"&lt;br /&gt;Hide the clock on the system tray  nircmd.exe win child class "Shell_TrayWnd" hide class "TrayClockWClass"&lt;br /&gt;Show the clock on the system tray  nircmd.exe win child class "Shell_TrayWnd" show class "TrayClockWClass"&lt;br /&gt;Kill (terminate) all instance of Internet Explorer processes  nircmd.exe killprocess iexplore.exe&lt;br /&gt;Create a shortcut on your desktop that opens the door of K: CDROM drive when you run it.  nircmd.exe cmdshortcut "~$folder.desktop$" "Open CDROM" cdrom open k:&lt;br /&gt;Create a shortcut to NirSoft Web site on your desktop  nircmd.exe urlshortcut "http://www.nirsoft.net" "~$folder.desktop$" "NirSoft"&lt;br /&gt;Add NirSoft Web site to your Favorities under Links folder.  nircmd.exe urlshortcut "http://www.nirsoft.net" "~$folder.favorites$\Links" "NirSoft"&lt;br /&gt;Create a shortcut to NirSoft Web site on the desktop of all computers listed in computers.txt  nircmd.exe multiremote copy "c:\temp\computers.txt" urlshortcut "http://www.nirsoft.net" "~$folder.common_desktop$" "NirSoft"&lt;br /&gt;Set the display mode to 800x600x24bit colors  nircmd.exe setdisplay 800 600 24&lt;br /&gt;Create a shortcut on the desktop that set the display mode to 800x600x24bit colors  nircmd.exe cmdshortcut "~$folder.desktop$" "800x600x24" setdisplay 800 600 24&lt;br /&gt;Copy all shortcuts on your desktop to another folder (f:\temp\desktop).  nircmd.exe execmd copy "~$folder.desktop$\*.lnk" f:\temp\desktop&lt;br /&gt;Restart your Apache server (under Windows NT/2000/XP/2003)  nircmd.exe service restart apache&lt;br /&gt;Create a shortcut on your desktop that restarts the Apache server  nircmd.exe cmdshortcut "~$folder.desktop$" "Restart Apache" service restart apache&lt;br /&gt;Restart your IIS  nircmd.exe service restart w3svc&lt;br /&gt;Restart MySql  nircmd.exe service restart MySql&lt;br /&gt;Open the desired Registry key/value in RegEdit  nircmd.exe regedit "HKLM\Software\Microsoft\Windows\CurrentVersion" "CommonFilesDir"&lt;br /&gt;Open the Registry key that you copied to the clipboard in RegEdit.  nircmd regedit "~$clipboard$"&lt;br /&gt;Disable the screen saver  nircmd.exe regsetval sz "HKCU\control panel\desktop" "ScreenSaveActive" 0&lt;br /&gt;Enable the screen saver  nircmd.exe regsetval sz "HKCU\control panel\desktop" "ScreenSaveActive" 1&lt;br /&gt;Change the date/time of the specified filename (creation time and modified time)  nircmd.exe setfiletime "c:\temp\myfile.txt" "24-06-2003 17:57:11" "22-11-2005 10:21:56"&lt;br /&gt;Copy your desktop folder path to the clipboard  nircmd.exe clipboard set ~$folder.desktop$&lt;br /&gt;Copy your start menu folder path to the clipboard  nircmd.exe clipboard set ~$folder.start_menu$&lt;br /&gt;Copy the content of info1.txt (simple text file) to the clipboard  nircmd.exe clipboard readfile "c:\My Files\info1.txt"&lt;br /&gt;Add the text content of clipboard to info1.txt  nircmd.exe clipboard addfile "c:\My Files\info1.txt"&lt;br /&gt;Clear the clipboard  nircmd.exe clipboard clear&lt;br /&gt;Create all folders specified in "c:\temp\folders.txt". The folder path names are separated by CRLF characters.  nircmd.exe paramsfile "c:\temp\folders.txt" "" "" execmd md ~$fparam.1$&lt;br /&gt;Install the specified .NET assembly in the global assembly cache (like gacutil)  nircmd.exe gac install "C:\temp\MyAssembly\bin\MyAssembly.dll"&lt;br /&gt;Empty the recycle bin in all drives.  nircmd.exe emptybin&lt;br /&gt;Answer 'Yes' to a standard Windows message-box.  nircmd.exe dlg "" "" click yes&lt;br /&gt;Wait 2 seconds, and then save the current screen to shot.png  nircmd.exe cmdwait 2000 savescreenshot "f:\temp\shot.png"&lt;br /&gt;Save 10 screenshots in a loop, and wait 60 seconds between the screenshot save calls. The filenames of the screenshot will contain the time and date of the saved screenshot.  nircmd.exe loop 10 60000 savescreenshot c:\temp\scr~$currdate.MM_dd_yyyy$-~$currtime.HH_mm_ss$.png&lt;br /&gt;Wait until Firefox is closed, and then say "Firefox was closed"  waitprocess firefox.exe speak text "Firefox was closed"&lt;br /&gt;&lt;br /&gt;System Requirements&lt;br /&gt;&lt;br /&gt;This utility can work in all 32-bit Windows operating systems: Windows 9x/ME, Windows NT, Windows 2000, Windows XP, Windows Server 2003, and Windows Vista. However, some of NirCmd commands works only on Windows NT/2000/XP/2003/Vista.&lt;br /&gt;&lt;br /&gt;官網：http://www.nirsoft.net/utils/nircmd.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-2308351460754972722?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/2308351460754972722/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=2308351460754972722' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/2308351460754972722'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/2308351460754972722'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/nircmd-v252-freeware-windows-command.html' title='NirCmd v2.52 - Freeware Windows command-line tool'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-412805343558874884</id><published>2011-04-12T03:35:00.000-07:00</published><updated>2011-04-12T03:36:39.917-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>Portable PHP code: DIRECTORY_SEPARATOR is not necessary</title><content type='html'>In attempting to write cross-platform, portable PHP code, I used PHP’s DIRECTORY_SEPARATOR constant to write path strings, e.g. "..".DIRECTORY_SEPARATOR."foo", because the “proper” way to do it on Windows would be "..\foo" while on everything else (Linux, UNIX, Mac OS X) it would be "../foo".&lt;br /&gt;&lt;br /&gt;Well, as Christian on php.net pointed out and the guys at Web Design Forums confirmed, that’s completely unnecessary. As long as you use the forward slash, “/”, you’ll be OK. Windows doesn’t mind it, and it’s best for *nix operating systems.&lt;br /&gt;&lt;br /&gt;(Note that DIRECTORY_SEPARATOR is still useful for things like explode-ing a path that the system gave you. Thanks to Shadowfiend for pointing this out.)&lt;br /&gt;&lt;br /&gt;reference : http://alanhogan.com/tips/php/directory-separator-not-necessary&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-412805343558874884?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/412805343558874884/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=412805343558874884' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/412805343558874884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/412805343558874884'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/portable-php-code-directoryseparator-is.html' title='Portable PHP code: DIRECTORY_SEPARATOR is not necessary'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1391109602694067331</id><published>2011-04-12T03:34:00.000-07:00</published><updated>2011-04-12T03:35:40.395-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>Write file manipulation cross-platform PHP code</title><content type='html'>As we use Windows computers to develop our PHP scripts and Linux servers in production, we must be careful about what we use in our PHP script, especially when manipulating files.&lt;br /&gt;A lot of PHP function work well in cross-platform, rename(), unlink(), copy(), realpath(), glob() ... all of them will do the job, whether they are called in Linux, or Windows, but when playing with temporary directory (That can be at different places, even on same platform), parsing file paths or include_path, we need some tools.&lt;br /&gt;One thing to avoid is doing specific part of code for Windows, and specific for Linux.&lt;br /&gt;So I will focus in this post on some functions and constants around file manipulation that are helpful when writing cross-platform code.&lt;br /&gt;&lt;br /&gt;Temporary directory with sys_get_temp_dir()&lt;br /&gt;sys_get_temp_dir() function is useful to find the path of the directory PHP stores temporary files by default.&lt;br /&gt;echo sys_get_temp_dir();&lt;br /&gt;Will output on Windows&lt;br /&gt;C:\Windows\Temp\&lt;br /&gt;and on Linux&lt;br /&gt;/tmp&lt;br /&gt;If you want to put some files in temporary directory, and don't want to check what is the temporary directory path, sys_get_temp_dir() is the function made for that. &lt;br /&gt;&lt;br /&gt;DIRECTORY_SEPARATOR&lt;br /&gt;Even if some people tell you that / will do the job in both Windows and Linux, using DIRECTORY_SEPARATOR is the good way to go.&lt;br /&gt;Nothing tell us that next PHP Version or next OS you will deploy your PHP on will not change this, it's not about if it works or not with /, but if you really want to do cross-platform PHP at 100%, not just when you find it useful.&lt;br /&gt;So DIRECTORY_SEPARATOR will produce / on Linux, and \ on Windows.&lt;br /&gt;Where it's again useful, is for exploding or applying regular expression on paths&lt;br /&gt;explode(DIRECTORY_SEPARATOR, $path);&lt;br /&gt;will look and perform better than doing&lt;br /&gt;preg_split('/\\|\//', $path);&lt;br /&gt;&lt;br /&gt;PATH_SEPARATOR&lt;br /&gt;When playing with include_path, one difference between Windows and Linux, is the path separator between each include path, : for Linux and ; for Windows.&lt;br /&gt;PATH_SEPARATOR will contain the right path separator for the actual platform.&lt;br /&gt;# Printing base include_path&lt;br /&gt;echo get_include_path() . PHP_EOL;&lt;br /&gt; &lt;br /&gt;#Adding temporary directory to include_path&lt;br /&gt;set_include_path(get_include_path() . PATH_SEPARATOR . &lt;br /&gt;                         sys_get_temp_var() . DIRECTORY_SEPARATOR . 'somefolder');&lt;br /&gt; &lt;br /&gt;# Printing modified include_path&lt;br /&gt;echo get_include_path();&lt;br /&gt;Will output in Linux&lt;br /&gt;.:/usr/share/pear:/usr/share/php&lt;br /&gt;.:/usr/share/pear:/usr/share/php:/tmp/somefolder&lt;br /&gt;And on Windows&lt;br /&gt;.;C:\Program Files\Zend\ZendServer\bin\pear;&lt;br /&gt;.;C:\Program Files\Zend\ZendServer\bin\pear;C:\Windows\Temp\somefolder&lt;br /&gt;&lt;br /&gt;Creating and managing temporary files with tmpfile()&lt;br /&gt;tmpfile() function is useful as it will create a temporary file with a unique name in read-write mode and will returns a file handle to use with fwrite(), fread(), ...&lt;br /&gt;The file is then removed by PHP when closed (fclose()), or when the script ends, you don't have to worry about cleaning or doing platform dependent code.&lt;br /&gt;# Creating temporary file&lt;br /&gt;$handle = tmpfile();&lt;br /&gt; &lt;br /&gt;# Listing what is under temporary directory&lt;br /&gt;var_dump(glob(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php*'));&lt;br /&gt; &lt;br /&gt;# Closing resource link&lt;br /&gt;echo 'Closing temporary file resource ...' .  PHP_EOL;&lt;br /&gt;fclose($handle);&lt;br /&gt; &lt;br /&gt;# Listing what is under temporary directory&lt;br /&gt;var_dump(glob(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php*'));&lt;br /&gt;Will output on Linux&lt;br /&gt;array(1) {&lt;br /&gt;  [0]=&gt;&lt;br /&gt;  string(14) "/tmp/phpJeGEGm"&lt;br /&gt;}&lt;br /&gt;Closing temporary file resource ...&lt;br /&gt;array(0) {&lt;br /&gt;}&lt;br /&gt;And on Windows&lt;br /&gt;array(1) {&lt;br /&gt;  [0]=&gt;&lt;br /&gt;  string(26) "C:\Windows\Temp\php82.tmp"&lt;br /&gt;}&lt;br /&gt;Closing temporary file resource ...&lt;br /&gt;array(0) {&lt;br /&gt;}&lt;br /&gt;As you see, temporary file is created by PHP, and then removed without any action.&lt;br /&gt;&lt;br /&gt;Conclusion&lt;br /&gt;As you see, writing cross-platform PHP code is quick and simple, and can save you a lot of time, simply check PHP documentation if you have any doubt in a function.&lt;br /&gt;&lt;br /&gt;reference : http://blog.elijaa.org/index.php?post/2010/10/14/Write-file-manipulation-cross-platform-PHP-code&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1391109602694067331?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1391109602694067331/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1391109602694067331' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1391109602694067331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1391109602694067331'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/write-file-manipulation-cross-platform.html' title='Write file manipulation cross-platform PHP code'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-1288150959338861056</id><published>2011-04-12T02:56:00.000-07:00</published><updated>2011-04-12T03:00:37.132-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>PHP_EOL constant</title><content type='html'>This is a PHP constant that represents the correct end-of line string for the particular operating system you are running the code on.&lt;br /&gt;&lt;br /&gt;If you plan to create cross-platform PHP applications this is the correct way to terminate a line in a text file instead of writing code to distinguish between the string "\r\n" and "\n" yourself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-1288150959338861056?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/1288150959338861056/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=1288150959338861056' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1288150959338861056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/1288150959338861056'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/phpeol-constant.html' title='PHP_EOL constant'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-8228374471768779102</id><published>2011-04-12T02:54:00.000-07:00</published><updated>2011-04-12T02:55:20.904-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse-教學'/><title type='text'>See the invisible whitespace characters in Eclipse</title><content type='html'>It is now possible to see the invisible whitespace characters in textual editors:&lt;br /&gt;&lt;br /&gt;This feature can either be controlled via "General &gt; Editors &gt; Text Editors &gt; Show whitespace characters" preference, or via the tool bar button when the Editor Presentation action set is enabled.&lt;br /&gt;&lt;br /&gt;reference : http://stackoverflow.com/questions/971283/view-tabs-in-eclipse&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-8228374471768779102?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/8228374471768779102/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=8228374471768779102' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8228374471768779102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/8228374471768779102'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/see-invisible-whitespace-characters-in.html' title='See the invisible whitespace characters in Eclipse'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5536488873330136189</id><published>2011-04-12T00:29:00.000-07:00</published><updated>2011-04-12T00:30:47.953-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apache-教學'/><title type='text'>Avoiding the use of .htaccess for performance</title><content type='html'>.htaccess files are often used because they allow quick changes to the apache web server configuration and don’t require apache to be restarted. However with this flexibility comes a number of performance implications and as a result they should be avoided unless you have absolutely no other way to put configuration changes into effect e.g: shared hosting where you are unlikely to have access to httpd.conf.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The .htaccess performance hit&lt;/span&gt;&lt;br /&gt;If .htaccess files are allowed (through the AllowOverride directive) then apache will look for .htaccess files on every request. In addition apache has to look up the directory tree to see if there are further .htaccess files in locations above the location the file is requested from so that it can tell which directives have precedence. For example: if you request a file from /foo/bar, apache has to look for /foo/bar/.htaccess, /foo/.htaccess and /.htaccess and that’s even if there are no .htaccess files present in any of those locations! Once a .htaccess file is found it has to be parsed and don’t forget all of this has to happen for every request!!&lt;br /&gt;&lt;br /&gt;As there’s nothing in a .htaccess file that can’t be achieved in your httpd.conf (or files included by httpd.conf) it makes sense to move existing .htaccess rules into your apache conf using a Directory or Location directive as appropriate.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Restarting apache without dropping users&lt;/span&gt;&lt;br /&gt;One other reason why .htaccess are seen as a convenience is that they don’t require apache to be restarted after configuration changes. However if you restart apache with “apachectl graceful” you only force the parent process to re-read the apache configuration files and the children to restart when they’re not doing anything and as a result no end users are suddenly dropped.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Finding all of the .htaccess files on your system&lt;/span&gt;&lt;br /&gt;Simply drop on to your terminal and change directory to where you site files are located and run: find . -name *.htaccess -print. This should provide output that shows the location of all of the .htaccess files so you can go through one by one and move them to into httpd.conf or a file included by httpd.conf.&lt;br /&gt;&lt;br /&gt;reference : http://muffinresearch.co.uk/archives/2008/04/07/avoiding-the-use-of-htaccess-for-performance/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5536488873330136189?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5536488873330136189/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5536488873330136189' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5536488873330136189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5536488873330136189'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/avoiding-use-of-htaccess-for.html' title='Avoiding the use of .htaccess for performance'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7840842857746281584</id><published>2011-04-07T00:40:00.001-07:00</published><updated>2011-04-07T00:44:17.124-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>Multiplatform Principle</title><content type='html'>Always &lt;br /&gt;1)use lower case add underline for filename and foldername(EX: widget_framework, unit_test...)&lt;br /&gt;2)use forward slash(/) for PATH&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-7840842857746281584?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/7840842857746281584/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=7840842857746281584' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7840842857746281584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/7840842857746281584'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/04/multiplatform-principle.html' title='Multiplatform Principle'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-3426408180871353510</id><published>2011-03-30T23:24:00.000-07:00</published><updated>2011-03-30T23:25:36.129-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Source Insight - Trace Code的神兵利器</title><content type='html'>官方網址&lt;br /&gt;http://www.sourceinsight.com/&lt;br /&gt;&lt;br /&gt;也許這套大家不陌生，不過我倒是上班後才開始用到&lt;br /&gt;因為以前寫的code哪有龐大到需要用這種來追 xD&lt;br /&gt;&lt;br /&gt;讓我一步一步來教各位如何使用吧&lt;br /&gt;1) 第一步當然是開新專案囉 [Project] → [New Project]&lt;br /&gt;(在這邊填入你想要的Project名稱以及你source code所在的資料夾)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2) 下一步後出現 New Project Setting，這裡我是覺得直接用default就可以了&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3) 下一步就要開始來做加入專案檔案囉！ 基本上在Directory那邊應該就是你整個程式的資料夾，&lt;br /&gt;在File Name這裡選擇要加入的檔案。&lt;br /&gt;&lt;br /&gt;基本上我都直接按 [Add All]，也就是將整個專案都加入啦！這邊要注意的就是你選Add All會出現&lt;br /&gt;一個小視窗，預設值應該都只有勾第一個，記得把第二個勾起來(這個勾起來就是連同子目錄全選)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;選好之後選擇 [Close]。&lt;br /&gt;&lt;br /&gt;4) 接下來呢就請到[Opetions]→[Document Options]把你要全部有關聯的檔案通通加一下，&lt;br /&gt;因為SI支援的類型實在太多啦&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;5) 嘿！別忘了做[Project]→[Synchronize files]，選項按照你的需求記得勾一勾，&lt;br /&gt;這就是同步檔案啦！這樣就算完成啦！&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;可以開始來追Code啦~~~&lt;br /&gt;&lt;br /&gt;軟體主畫面(右下角可以看relation)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;看看滑鼠右鍵的功能表&lt;br /&gt;幾個Jump to Caller、Jump to Definition、Lookup References就可以用到嚇嚇叫了&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;也可以直接對著某個function或是structure讓游標停留在上面，左下角會出現被定義的地方。&lt;br /&gt;如果要再追進去的話，就在下面左下角那個視窗點兩下，就會跑進去那一隻程式啦！&lt;br /&gt;(記得從[View]→[Relation Window]把右下角的relation叫出來，也可以直接對著那直接點哦)&lt;br /&gt;&lt;br /&gt;這真的好用吧！ 嘿嘿，其實還有更好用的東西，可以去測試你的程式是否有defeat&lt;br /&gt;下次我在介紹，是可以把他與Source Insight結合在一起哦！就像是外掛程式一樣 xDD&lt;br /&gt;&lt;br /&gt;reference : http://www.wretch.cc/blog/hidex/8117748&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-3426408180871353510?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/3426408180871353510/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=3426408180871353510' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3426408180871353510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/3426408180871353510'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/03/source-insight-trace-code.html' title='Source Insight - Trace Code的神兵利器'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-5573546327967579134</id><published>2011-03-27T07:01:00.000-07:00</published><updated>2011-03-27T07:02:10.848-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>log4php - Change log file Name dynamically in log4php.properties</title><content type='html'>$rootlogger = Logger::getRootLogger();&lt;br /&gt;$rootlogger-&gt;setLevel(LoggerLevel::toLevel(LoggerLevel::INFO));&lt;br /&gt;&lt;br /&gt;$appender = new LoggerAppenderRollingFile("MyAppender");&lt;br /&gt;$appender-&gt;setFile("custom_name.log", true);&lt;br /&gt;$appender-&gt;setMaxBackupIndex(10); &lt;br /&gt;$appender-&gt;setMaxFileSize("100MB");&lt;br /&gt;$appenderlayout = new LoggerLayoutPattern();&lt;br /&gt;$pattern = '%d{Y-m-d H:i:s} [%p] %c: %m (at %F line %L)%n';&lt;br /&gt;$appenderlayout-&gt;setConversionPattern($pattern);&lt;br /&gt;$appender-&gt;setLayout($appenderlayout);&lt;br /&gt;$appender-&gt;activateOptions();&lt;br /&gt;&lt;br /&gt;$rootlogger-&gt;removeAllAppenders();&lt;br /&gt;$rootlogger-&gt;addAppender($appender);&lt;br /&gt;&lt;br /&gt;$rootlogger-&gt;info("info");&lt;br /&gt;&lt;br /&gt;reference : http://stackoverflow.com/questions/2658146/log4php-change-log-file-name-dynamically-in-log4php-properties&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5316466304108610731-5573546327967579134?l=jersus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jersus.blogspot.com/feeds/5573546327967579134/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5316466304108610731&amp;postID=5573546327967579134' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5573546327967579134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5316466304108610731/posts/default/5573546327967579134'/><link rel='alternate' type='text/html' href='http://jersus.blogspot.com/2011/03/log4php-change-log-file-name.html' title='log4php - Change log file Name dynamically in log4php.properties'/><author><name>Vege</name><uri>http://www.blogger.com/profile/09538355427316496883</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5316466304108610731.post-7388246174596485932</id><published>2011-03-23T06:50:00.000-07:00</published><updated>2011-03-23T06:51:40.786-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='程式-PHP'/><title type='text'>{PHP} PHP 快速分析器(PHP Quick Profiler)</title><content type='html'>原文：PHP Quick Profiler By Ryan Campbell&lt;br /&gt;&lt;br /&gt;翻译：Vayn a.k.a. VT &lt;vayn at(not spam) vayn dot de&gt;&lt;br /&gt;&lt;br /&gt;在我们公司，代码审计在制作优质软件的开发进程中是一个不可分割的部分。我们开发 Wufoo 的时候选择了 mentor style approach，也就是一个开发人员在一个部分工作一段时间，然后把这个部分传递给一个更有经验的开发者进行审计。我们很喜欢这个方法，因为这意味更多的开发者慢慢熟悉不同代码层服务的基础。更重要地是，他们充当着对抗安全漏洞、内存泄漏、低效查询（poor queries）、复杂文件结构（heavy file structures）等额外保障的角色。不幸地是，这些审计也非常消耗时间，在一个小型团队中有时会对审计者造成不便——他也有他们自己的 todo list 要完成。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;代码审计与同样排在工作表上的开发需求相互冲突，我们发现我们是在一而再再而三的重复同样的任务。我们花了大把时间输出查询、内存统计和对象到浏览器，只是为了查看它们是如何使用在代码中的。为了减少这样的重复，我们花费一些时间创造了一个被称为 PHP 快速分析器(PHP Quick Profiler) 的工具——简称PQP。这是个小工具（想象一下 Firebug for PHP），能根据有关信息为开发人员进行分析和调试，而无需他们在代码里添加一大堆编程开销（programmatic overhead）。现在我们只需切换一个配置设置为 true，我们的审计人员就通过自动化工具来帮助建立一个更快更一致的审查过程（Now，we only need to toggle one config setting to true and our reviewers have access to an automated tool to help create a faster and more consistent review experience）。自从任何人都可以使用PQP，它也给了原始开发人员启示——他们的代码在审查前卡到哪了（Since anyone can use it，PQP also gives the initial developer an idea of where their code stands before the review.）。&lt;br /&gt;&lt;br /&gt;我们已经在 Wufoo 项目上使用了PQP一段时间。它是非常有用，所以我们决定花点时间把所有东西打包，写一些文档，然后把PQP提供给所有人使用。&lt;br /&gt;&lt;br /&gt;看看PQP怎样工作&lt;br /&gt;我们已经做好了一个在线实例展示一个开启了PQP的到达页面。分析器包含了 5 个标签页，分别显示记录消息（logging messages），测量执行时间（measuring execution time），分析查询（analyzing queries），内存使用（revealing memory used）和包含页面（showing the files included）。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;感谢 Kevin 的帮助，他做了件伟大的工作——设计和压缩了界面，这样就不用添加外部的 CSS 和脚本以让 PQP 良好显示。PQP 可以运行在 Internet Explorer 6 及更高的版本，以及 Firefox 和 Safari 上。&lt;br /&gt;&lt;br /&gt;开始使用&lt;br /&gt;我们提供一个包含 PQP 全部安装文件和基本安装说明的 zip 文档。&lt;br /&gt;&lt;br /&gt;Download: pqp.zip&lt;br /&gt;&lt;br /&gt;你下载了 zip 包后将其解压，然后把全部文件上传至一个支持 PHP 的 web 服务器。用浏览器访问文件所在目录，你会看到一个和上文的在线实例一样的例子。你会看到，除了数据库标签之外的其他标签都工作良好，数据库标签需要一些额外的设置。&lt;br /&gt;&lt;br /&gt;当你确定样例能运行，下一步是把它与你自己的项目整合到一起。注意： 把PQP的文件夹直接放入你项目的目录里并不会工作。 这是由于演示的代码有 5 个不同的方面，你可能需要用和我们不一样的做法来处理这些情况。也就是说整合其实很简单，你只要跟着下面每一条指导进行设置就能立刻运行PQP了。&lt;br /&gt;&lt;br /&gt;在你的代码中使用 PQP&lt;br /&gt;让 PQP 工作最简单的方法，是在你想显示 PQP 的代码中包含 PhpQuickProfiler.php 文件。这样单独包含可以让控制台、内存记录和文件记录工作。想要数据库和速度记录工作需要额外设置，我们一会儿讲解。现在，缺省记录可以很好的工作，但是只包含文件不会把信息显示在屏幕上。想把信息显示在屏幕上，需要一些包含文件后会发生什么的知识。&lt;br /&gt;&lt;br /&gt;当代码被执行，运行细节会被 PQP 记录和分析。当代码执行完毕，PQP 也工作完毕，并且把输出显示到屏幕上。棘手的部分是你得知道代码何时结束，在理想状态下这个工具应该是能在开发人员尽可能少修改的情况下工作。在这个例子中，我们判断代码结束工作的方法是查看父对象的析构函数何时运行。因此时间表会是这样：&lt;br /&gt;&lt;br /&gt;目标页构造函数声明一个 PhpOuickProfiler 的实例。&lt;br /&gt;目标页执行全部所需代码。&lt;br /&gt;目标页的析构函数告诉 PhpQuickProfiler 清理并显示输出。&lt;br /&gt;当然，这样安装会让 PQP 一直显示输出，这对产品而言并不理想。为了让它更有用，我们在代码中加入了一个配置标识（debugMode = true），在显示之前检查这个标识有没有被设置为 true。下面的样本类执行了我们刚才所讲的步骤：&lt;br /&gt;&lt;br /&gt;class ExampleLandingPage {&lt;br /&gt;&lt;br /&gt;    private $profiler;&lt;br /&gt;    private $db;&lt;br /&gt;&lt;br /&gt;    public function __construct() {&lt;br /&gt;        $this-&gt;profiler = new PhpQuickProfiler(PhpQuickProfiler::getMicroTime());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public function __destruct() {&lt;br /&gt;        if($debugMode == true) $this-&gt;profiler-&gt;display($this-&gt;db);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;记录到控制台&lt;br /&gt;通过上面的代码，PQP 已经设置完毕静待使用了。我们可以开始引用帮助函数，从记录开始。控制台记录比 echo 声明更进一步，使用 print_r 压缩和格式化输出。这对审计人员很有利，因为它提供了一种显示调试信息的方法，这种显示方法不会把页面的布局撑破。把数据输出到控制台只需静态地调用函数：&lt;br /&gt;&lt;br /&gt;Console::log($data);&lt;br /&gt;静态函数调用接受任何数据类型，只要 PhpQuickProfiler.php 类被包含的时候就有效。我们选择用静态调用的方式实现，以便类不必在使用前实例化。下面的静态调用是我们把记录历史存入一个 PHP 全局变量。如果你希望不用全局变量，可以把 Console.php 类实例化这样它会把记录作为一个成员变量保存。但一如这样，这个类只是充当着全局 PHP 变量的包装器（Wrapper）。&lt;br /&gt;&lt;br /&gt;在记录变量之上，控制台还有四个附加函数。&lt;br /&gt;&lt;br /&gt;Console::logError($exception);&lt;br /&gt;Console::logMemory();&lt;br /&gt;Console::logMemory($variable, $name);&lt;br /&gt;Console::logSpeed();&lt;br /&gt;让我们从 logError() 开始。记录一个错误对显示有关 PHP 异常信息非常有用。在我们的代码中，我们会用一个 catch 块处理错误，这样能让异常沉默。我们这样做是因为我们想忽略错误，让它不会影响到用户做事。现在，在开发中还是了解被抛出的异常比较好，因此在 catch 块里调用 logError() 可以让信息向下面这样显示在控制台：&lt;br /&gt;&lt;br /&gt;try {&lt;br /&gt;    // Execute non critical code&lt;br /&gt;}&lt;br /&gt;catch(Exception $e) {&lt;br /&gt;    Console::logError($e, 'Unable to execute non critical code');&lt;br /&gt;}&lt;br /&gt;另外，控制台可以通过使用 PHP 内置的帮助函数提供更多的值。比如说，记录 debug_backtrace() 将打印脚本指定时间点上的有关信息。PHP 提供一些魔术常量，比如 __LINE__, __FILE__, __FUNCTION__, __METHOD__ 和 __CLASS__ ，也允许打印脚本数据。看看下面的截图，里面有一些这方面的示例用法：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;观察内存使用&lt;br /&gt;Object oriented PHP 是一种看起来很漂亮的东西，但是涉及内存使用的时候有几个明确的问题需要牢记。这些问题会在处理递归输出的时候易于引起一些状况出现（比如：输出到 Excel），如果创造对象的时候有内存泄漏或对象没有被恰当地销毁。所有这些会导致意料之外的资源使用和致命错误，加剧恶化最终用户的使用。&lt;br /&gt;&lt;br /&gt;使用有内存管理辅助的调试器可以显示脚本可用的最大内存容量。memory_get_peak_usage() 在数据起始的地方内置了一个简单的调用。有关内存使用方面的系统设置（通过 ini_get()）也会被显示出来，以便查看还有多少回旋余地。如果综述还不够，你可以利用特定时间点的内存调用研究你的资源使用（ you can drill down into your resource usage with a point in time memory call.）。&lt;br /&gt;&lt;br /&gt;Console::logMemory();&lt;br /&gt;Console::logMemory($variable, $name);&lt;br /&gt;在你的代码中不传任何参数地调用 logMemory() 将输出某个函数被调用时 PHP 的当前内存使用量。这是个完美的观察你代码中循环的方法，这个方法可以观察每次迭代的时候内存的增长。同样，一个变量和一个名字可以传给这个函数。这样做会在控制台输出变量的内存使用情况。知晓脚本正在占用内存真是奇妙，知晓哪一个变量在占用内存就可以修正问题。看一下下面截图，这个例子展示了字符串链接（string concatenation）在慢慢吞掉内存。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;失控的包含&lt;br /&gt;类似内存失控，包含文件（特别是在大型项目）可以非常快速地在接管你的 app 前增殖。更糟地是，太多的包含不会抛出内存使用造成的系统错误（hard errors）。取而代之的是，页面变慢，资源在每次处理请求中被吃掉。避免这个陷阱很简单——只要确保相同的文件不被多次包含，捕捉任何不必要文件的过度包含。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PQP 的文档标签通过调用 get_included_files 显示了全部文件的包含情况和文件大小的总和。单个文件的名称和大小也输出到控制台，以便容易地查看。最大的包含文件被标注在左侧，这样就清晰地指出一个正在使用的大型库文件是否不应该被使用。比如，在 Wufoo 项目上我们发现有一个脚本一直包含它不需要的 htmLawed，一个大小还算过得去的文件（a fairly decent sized file）。&lt;br /&gt;&lt;br /&gt;同样，请牢记，自动加载的类或使用 require_once 通常可以减轻任何由文件包含引起的问题。这就是说，意识到这个问题就不会产生危害（it never hurts to be aware of the situation），特别是当你在使用插件，旧代码，或者借来的代码（borrowed code）。&lt;br /&gt;&lt;br /&gt;页面执行时间&lt;br /&gt;我们总是在谈到性能考虑（理所当然地）的时候强调数据库优化（Emphasis is always placed on database optimization when it comes to performance considerations (rightfully so)），但这不意味着 PHP 执行时间就被忽略了。计算页面加载时间的标准方法是找到脚本开始和结束的时间之间的时间差。这让整合调试器到你自己的项目变得棘手。从父页面的析构调用调试器（Since the debugger is called on the destruction of the parent page），我们了解脚本什么时候结束。但是因为每个项目不同，开始时间可以多种多样。你下载的示例代码用父对象的构造函数调用 PQP 的 getMicroTime() 来设置开始时间。此方法可以在大多数情况下工作，但如果你有一大堆代码在父对象构造前运行，务必在需要的时候
