├── .gitignore ├── _static ├── cover.png └── cover_thumb.png ├── bibliography.rst ├── contents.rst.inc ├── cover.tex ├── images ├── arrow.eps ├── global.eps ├── nodejs.eps ├── nodejs.png ├── professional.eps ├── tree.eps └── zh-tw │ ├── node_basic_file_byte.png │ ├── node_basic_file_read.png │ ├── node_basic_rout_test.png │ ├── node_basic_rout_url.png │ ├── node_basic_rout_url_default.png │ ├── node_basic_rout_url_error.png │ ├── node_basic_rout_url_index.png │ ├── node_basic_rout_url_test.png │ ├── node_install_cmd.jpg │ ├── node_install_linux_node_test.jpg │ ├── node_install_node_test.jpg │ ├── node_npm_linux_install.jpg │ ├── node_npm_linux_test.jpg │ ├── node_npm_registry.png │ ├── node_npm_search.png │ └── node_npm_test.png ├── index.rst ├── links.rst ├── readme.rst ├── slides ├── Makefile ├── default.dzslides ├── dzslides │ ├── template.html │ └── template1.html ├── embedder.html ├── onstage.html ├── slide1.html ├── slide1.md ├── slides.html └── watchr.rb ├── social_nature ├── defindex.html ├── domainindex.html ├── genindex-single.html ├── genindex-split.html ├── genindex.html ├── globaltoc.html ├── layout.html ├── localtoc.html ├── page.html ├── relations.html ├── search.html ├── searchbox.html ├── searchresults.html ├── sourcelink.html ├── static │ └── nature.css_t └── theme.conf ├── src ├── hello.js ├── node_basic_file_http_static.js ├── node_basic_file_simple.js ├── node_basic_http_get.js ├── node_basic_http_hello_world.js ├── node_basic_http_rout.js ├── node_basic_http_rout_done.js ├── node_express_ajax_form.js ├── node_express_basic.js ├── node_express_basic_app.js ├── node_express_get_form.js ├── node_express_middleware.js ├── node_express_middleware_simple.js ├── node_express_post_form.js ├── static │ ├── index.html │ ├── static.html │ └── test.html └── view │ ├── express_ajax_example_form.html │ ├── express_get_example_form.html │ └── express_post_example_form.html ├── watchr.rb └── zh-tw ├── Socket.io.rst ├── TODO_list ├── authorization_todo_list.rst ├── convention.rst ├── how-to-setup-a-node-js-development-environment-on-mac-osx-lion.rst ├── how-to-setup-a-node-js-development-environment-on-ubuntu-11-04 ├── js-OO-pattern.rst ├── js-function.rst ├── js-obj-literal.rst ├── node_application_hubot.rst ├── node_basic.rst ├── node_coffeescript.rst ├── node_express.rst ├── node_install.rst ├── node_introduce.rst ├── node_javascript.rst ├── node_npm.rst ├── node_utils.rst ├── outline.rst ├── preface.rst └── todo_list.rst /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | cook 3 | -------------------------------------------------------------------------------- /_static/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/_static/cover.png -------------------------------------------------------------------------------- /_static/cover_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/_static/cover_thumb.png -------------------------------------------------------------------------------- /bibliography.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | 參考資源 3 | ********* 4 | 5 | Node.js 書籍 6 | ============ 7 | 8 | - `Programming 9 | Node.js `_, April 10 | 2012 (est.), Travis Swicegood, Pragmatic Bookshelf 11 | - `The Little Book on 12 | CoffeeScript `_, 13 | January 2012, Alex MacCaw, O'Reilly Media 14 | - `Node for Front-End 15 | Developers `_, 16 | January 2012, Garann Means, O'Reilly Media 17 | - `Building Hypermedia APIs with HTML5 and 18 | Node `_, November 19 | 2011, Mike Amundsen, O'Reilly Media 20 | - `Node Web 21 | Development `_, 22 | August 2011, David Herron, PacktPub 23 | - `CoffeeScript: Accelerated JavaScript 24 | Development `_, July 25 | 2011, Trevor Burnham, Pragmatic Bookshelf 26 | - `Getting Started with GEO, CouchDB, and 27 | Node.js `_, July 28 | 2011, Mick Thompson, O'Reilly Media 29 | - `What Is Node? `_, 30 | July 2011, Brett McLaughlin, O'Reilly Media 31 | - `Node: Up and 32 | Running `_, May 33 | 2011, Tom Hughes-Croucher, Mike Wilson, O'Reilly Media 34 | - `Hands-on Node.js `_, The Node.js introduction and API reference, May 2011, Pedro Teixeira, LeanPub 35 | - `The Node Beginner Book `_, Manuel 36 | Kiessling 37 | - `Mastering Node.js `_, 38 | visionmedia 39 | 40 | Node.js 影音教學 41 | ================ 42 | 43 | - `The Node Sessions: The Best of OSCON 44 | 2011 `_, August 45 | 2011, O'Reilly Media (Video) 46 | - `Tom Hughes-Croucher on 47 | Node `_, March 48 | 2011, O'Reilly Media (Video) 49 | 50 | Node.js 教學網站 51 | ================ 52 | 53 | - `Node Tuts `_ 54 | 55 | Node.js 課程 56 | ============ 57 | 58 | - `Introduction To 59 | Node.js `_, 60 | Van Nguyen, CodeLesson 61 | 62 | -------------------------------------------------------------------------------- /contents.rst.inc: -------------------------------------------------------------------------------- 1 | 2 | .. toctree:: 3 | :maxdepth: 2 4 | 5 | readme 6 | zh-tw/preface 7 | zh-tw/node_introduce 8 | zh-tw/node_install 9 | zh-tw/node_basic 10 | zh-tw/node_npm 11 | zh-tw/node_express 12 | zh-tw/todo_list.rst 13 | zh-tw/node_coffeescript 14 | zh-tw/node_application_hubot 15 | zh-tw/node_utils 16 | zh-tw/node_javascript 17 | links 18 | bibliography 19 | -------------------------------------------------------------------------------- /cover.tex: -------------------------------------------------------------------------------- 1 | \documentclass[b5paper,12pt]{article} 2 | \usepackage[margin=0in]{geometry} 3 | 4 | \usepackage[cm-default]{fontspec} 5 | %% \usepackage{xunicode} 6 | \usepackage{xcolor} 7 | \usepackage{fontspec} 8 | \usepackage[slantfont,boldfont,CJKaddspaces,CJKchecksingle,CJKnumber,fallback]{xeCJK} 9 | \XeTeXlinebreaklocale "zh" 10 | \XeTeXlinebreakskip = 0pt plus 1pt 11 | \defaultfontfeatures{Mapping=tex-text} 12 | \setCJKmainfont[BoldFont={Adobe Heiti Std},ItalicFont={Adobe Fangsong Std},BoldItalicFont={Adobe Kaiti Std}]{Adobe Song Std} 13 | \setCJKsansfont[BoldFont={Adobe Heiti Std},ItalicFont={Adobe Fangsong Std},BoldItalicFont={Adobe Kaiti Std}]{Adobe Song Std} 14 | \setCJKmonofont{Adobe Ming Std} 15 | \setCJKromanfont[BoldFont={Adobe Heiti Std},ItalicFont={Adobe Fangsong Std},BoldItalicFont={Adobe Kaiti Std}]{Adobe Song Std} 16 | \setmainfont{Adobe Garamond Pro} 17 | \setsansfont{Myriad Pro} 18 | \setmonofont{Courier Std} 19 | \setromanfont{Adobe Garamond Pro} 20 | 21 | \usepackage{graphicx} 22 | \usepackage{pstricks} 23 | \definecolor{pink}{rgb}{1,0.7,0.7} 24 | \definecolor{main}{rgb}{0.55,0.78,0.29} 25 | \let\psgrid\relax 26 | 27 | \begin{document} 28 | \thispagestyle{empty} 29 | 30 | \noindent 31 | \begin{pspicture}(\linewidth,\pdfpageheight) 32 | \psgrid 33 | 34 | \psline[linewidth=3mm,linecolor=main](0,0.99\pdfpageheight)(\linewidth,0.99\pdfpageheight) 35 | 36 | \rput(0.5\linewidth,0.96\pdfpageheight){\textsf{\textbf{\fontsize{14pt}{14pt}\selectfont Node.js Taiwan 社群強力推薦}}} 37 | 38 | \rput(\linewidth,\pdfpageheight) 39 | {\pspolygon*(-3.6,0)(-1.4,0)(0,-1.4)(0,-3.6)} 40 | \rput(\linewidth,\pdfpageheight) 41 | {\rput{-45}(-1,-1){\Large\textbf{\white 實戰}}} 42 | \rput(\linewidth,\pdfpageheight) 43 | {\rput{-45}(-1.5,-1.5){\Large\textbf{\white 程式設計}}} 44 | 45 | \psframe[linecolor=main,fillcolor=main,fillstyle=solid](0,13.5)(\linewidth,17.5) 46 | \psline[linewidth=0.3in,linecolor=main](0,12.7)(\linewidth,12.7) 47 | \rput[l](1.5,17.5){\textsl{\fontsize{36pt}{36pt}\selectfont The}} 48 | \rput[l](3.6,15.4){\fontsize{96pt}{96pt}\selectfont Node.js} 49 | \rput[l](3.5,15.5){\fontsize{96pt}{96pt}\selectfont\white Node.js} 50 | \rput[l](12,13.6){\textsl{\fontsize{36pt}{36pt}\selectfont Little Book}} 51 | 52 | \rput[l](0.75\linewidth,11){\textsf{\textbf{\fontsize{36pt}{36pt}\selectfont 中文版}}} 53 | 54 | \rput[b](0.75\linewidth,19) 55 | {\includegraphics[scale=1]{images/nodejs}} 56 | 57 | \rput[b](0.18\linewidth,6.5) 58 | {\includegraphics[scale=2.5]{images/professional}} 59 | 60 | \rput[rb](0.95\linewidth,5){ 61 | \minipage{\linewidth}\raggedleft\huge{\textsf{\textbf{\fontsize{14pt}{28pt}\selectfont 62 | 適用最新版 Node.js 0.6.7\\ 63 | 本書由 Node.js Taiwan 社群程式設計師編寫\\ 64 | 最快樂的 Node.js 入門學習指南 65 | }}}\endminipage} 66 | 67 | \rput[b](3.6,1) 68 | {\includegraphics[scale=0.4]{images/tree}} 69 | \rput[b](3.4,0.3){\textsf{\textbf{\fontsize{28pt}{28pt}\selectfont ContPub Press}}} 70 | 71 | \rput[b](0.85\linewidth,0.2){\textsl{\fontsize{22pt}{22pt}\selectfont Node.js Taiwan}} 72 | 73 | \end{pspicture} 74 | 75 | \end{document} 76 | -------------------------------------------------------------------------------- /images/arrow.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/arrow.eps -------------------------------------------------------------------------------- /images/global.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/global.eps -------------------------------------------------------------------------------- /images/nodejs.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/nodejs.eps -------------------------------------------------------------------------------- /images/nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/nodejs.png -------------------------------------------------------------------------------- /images/professional.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/professional.eps -------------------------------------------------------------------------------- /images/tree.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/tree.eps -------------------------------------------------------------------------------- /images/zh-tw/node_basic_file_byte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_file_byte.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_file_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_file_read.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_rout_test.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_rout_url.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_rout_url_default.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_rout_url_error.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_rout_url_index.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_basic_rout_url_test.png -------------------------------------------------------------------------------- /images/zh-tw/node_install_cmd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_install_cmd.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_install_linux_node_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_install_linux_node_test.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_install_node_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_install_node_test.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_npm_linux_install.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_npm_linux_install.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_npm_linux_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_npm_linux_test.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_npm_registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_npm_registry.png -------------------------------------------------------------------------------- /images/zh-tw/node_npm_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_npm_search.png -------------------------------------------------------------------------------- /images/zh-tw/node_npm_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-wiki-book/834d92debb82af409e598ebd1f0efb72a8c13bf2/images/zh-tw/node_npm_test.png -------------------------------------------------------------------------------- /index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. Settings 4 | @title: Node.js 中文電子書 5 | @authors: Node.js Taiwan 6 | @basename: nodejs-wiki-book 7 | @language: zh_TW 8 | @latex_parindent: 2em 9 | @latex_paper_size: b5 10 | @latex_font_size: 11pt 11 | @latex_logo: images/nodejs.eps 12 | @latex_contentsname: 目錄 13 | @pdf_cover: cover.tex 14 | @epub_cover: _static/cover.png 15 | @html_theme: social_nature 16 | @epub_theme: epub_simple 17 | @mobi_theme: mobi_simple 18 | 19 | .. raw:: latex 20 | 21 | \renewcommand{\chaptername}{} 22 | %% \renewcommand{\chaptermark}[1]{\markboth{~第~\thechapter~章~~~#1~}{}} 23 | %% \renewcommand{\sectionmark}[1]{\markright{~\thesection~~#1~}{}} 24 | \renewcommand\bibname{參考資料} 25 | 26 | ########################## 27 | Node.js 台灣社群協作電子書 28 | ########################## 29 | 30 | .. image:: images/nodejs.png 31 | :width: 50% 32 | :align: right 33 | 34 | 這是一本關於 Node.js 技術的開放源碼電子書,我們使用 GitHub 維護電子書內容,並交由 ContPub(Continuous Publishing)系統自動線上發佈。本書提供 PDF、EPUB、MOBI 及 HTML 等格式,您除了可以在網站檢視本書所有內容,也可以將電子書下載至閱讀器保存。 35 | 36 | .. include:: contents.rst.inc 37 | 38 | -------------------------------------------------------------------------------- /links.rst: -------------------------------------------------------------------------------- 1 | ******** 2 | 精選文章 3 | ******** 4 | 5 | Express 6 | ======= 7 | 8 | * `使用 NodeJS + Express 從 GET/POST Request 取值 `_ [邀稿中] 9 | * link2 [邀稿中] 10 | * link3 [邀稿中] 11 | -------------------------------------------------------------------------------- /readme.rst: -------------------------------------------------------------------------------- 1 | .. raw:: latex 2 | 3 | \setcounter{secnumdepth}{-1} 4 | 5 | ******** 6 | 關於本書 7 | ******** 8 | 9 | 這是一本關於 Node.js 技術的開放源碼電子書,我們使用 GitHub 維護電子書內容,並交由 ContPub(Continuous Publishing)系統自動線上發佈。本書提供 PDF、EPUB、MOBI 及 HTML 等格式,您除了可以在網站檢視本書所有內容,也可以將電子書下載至閱讀器保存。 10 | 11 | 本書的線上閱讀網址,與 GitHub 資料同步更新。 12 | 13 | http://book.nodejs.tw/ 14 | 15 | 如果您想要取得本書的其他格式,可以從 ContPub 的電子書專頁下載最新版本。 16 | 17 | http://contpub.org/read/nodejs-wiki-book 18 | 19 | 本書適合 Node.js 初學者至進階開發者,也歡迎您在學習時一起參與本書內容撰寫。 20 | 21 | 編寫語法與規範 22 | ============ 23 | 24 | 請參考連結, 25 | 26 | :: 27 | 28 | http://sphinx-doc.org/rest.html 29 | 30 | 根目錄結構 31 | ======= 32 | 33 | * index.rst -> 基礎設定檔案,讓 contpub.org 讀取設定 34 | * contents.rst.inc -> 本書編排列表 35 | * conver.tex -> 本書封面 36 | * links.rst -> 外部連結 37 | * readme.rst -> 本書說明 38 | * zh-tw -> 內含各章節詳細資料,編排方式可參考 contents.rst.inc 39 | * src -> 範例程式碼擺放位置 40 | 41 | 授權 42 | ==== 43 | 44 | **Node.js 台灣社群協作電子書**\ 採用創用CC姓名標示-非商業性授權。\ 45 | **您不必為本書付費。** 46 | 47 | **Node.js Wiki Book** book is licensed under the 48 | Attribution-NonCommercial 3.0 Unported license. **You should not have 49 | paid for this book.** 50 | 51 | 您可以複製、散佈及修改本書內容,\ 52 | 但請勿將本書用於商業用途。 53 | 54 | 您可以在以下網址取得授權條款全文。 55 | 56 | http://creativecommons.org/licenses/by-nc/3.0/legalcode 57 | 58 | 作者 59 | ==== 60 | 61 | 本書由 Node.js Taiwan 社群成員協作,以下名單依照字母排序。 62 | 63 | * Caesar Chi (clonn) 64 | * Fillano Feng (fillano) 65 | * Kevin Shu (Kevin) 66 | * lyhcode http://about.me/lyhcode 67 | 68 | Node.js Taiwan 是一個自由開放的技術學習社群,我們歡迎您加入一起學習、研究及分享。 69 | 70 | 下載電子書 71 | ========== 72 | 73 | 線上閱讀本書。 74 | 75 | http://book.nodejs.tw/ 76 | 77 | PDF格式,適合一般電腦及7吋以上平板電腦閱讀 78 | 79 | http://contpub.org/download/nodejs-wiki-book.pdf 80 | 81 | EPUB格式,適合 iPad、iPhone 行動裝置閱讀 82 | 83 | http://contpub.org/download/nodejs-wiki-book.epub 84 | 85 | MOBI格式,適合 Kindle 電子書閱讀器 86 | 87 | http://contpub.org/download/nodejs-wiki-book.mobi 88 | 89 | 原始碼 90 | ====== 91 | 92 | 本書最新的原始碼(中文版)網址如下: 93 | 94 | http://github.com/nodejs-tw/nodejs-wiki-book 95 | 96 | .. raw:: latex 97 | 98 | \setcounter{secnumdepth}{1} 99 | 100 | 精選文章收錄流程 101 | ================ 102 | 103 | 精選文章的用意是鼓勵作者在自已的網誌發表 Node.js 教學,再由 Node.js Taiwan 社群挑選系列文章列入電子書的精選文集。 104 | 105 | 1. 將文章標題及連結貼到「精選文章」分類 106 | 2. 社群工作小組以 E-Mail 通知作者文章列入精選,並邀請將內文授權給 Node.js Taiwan 電子書分享 107 | 3. 作者同意後,由工作小組負責整理圖文,發佈至電子書 108 | 4. 以 E-Mail 寄出感謝函通知原作者文章已收錄,依作者意願調整文章內容 109 | 5. 於 Node.js Taiwan 首頁及粉絲專頁推薦作者的文章 110 | -------------------------------------------------------------------------------- /slides/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | pandoc -t dzslides -s slide1.md -o slide1.html 4 | -------------------------------------------------------------------------------- /slides/default.dzslides: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $for(author-meta)$ 5 | 6 | $endfor$ 7 | $if(date-meta)$ 8 | 9 | $endif$ 10 | $if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$ 11 | $if(highlighting-css)$ 12 | 15 | $endif$ 16 | $if(css)$ 17 | $for(css)$ 18 | 19 | $endfor$ 20 | $else$ 21 | 221 | $endif$ 222 | $if(math)$ 223 | $math$ 224 | $endif$ 225 | $for(header-includes)$ 226 | $header-includes$ 227 | $endfor$ 228 | 229 | 230 | $if(title)$ 231 |
232 |

$title$

233 | $for(author)$ 234 |

$author$

235 | $endfor$ 236 |

$date$

237 |
238 | $endif$ 239 | $for(include-before)$ 240 | $include-before$ 241 | $endfor$ 242 | $body$ 243 | $for(include-after)$ 244 | $include-after$ 245 | $endfor$ 246 | $dzslides-core$ 247 | 248 | 249 | -------------------------------------------------------------------------------- /slides/dzslides/template1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The Title Of Your Presentation 5 | 6 | 7 | 8 | 9 |
10 | 11 |

My Presentation

12 |
by John Doe
13 |
14 | 15 |
16 |

Part one

17 |
18 | 19 |
20 |

An incremental list

21 |
    22 |
  • Item 1 23 |
  • Item 2 24 |
  • Item 3 25 |
26 |
27 | 28 |
29 | 30 | Soothe us with sweet lies. Is that a cooking show? No! I want to 31 | live! There are still too many things I don't own! 32 | 33 |
34 | 35 |
36 |

Part two

37 |
38 | 39 |
40 |

An image

41 | 42 |
43 | 44 |
45 |

A video

46 | 47 |
48 | 49 |
50 |

End!

51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 137 | 138 | 151 | 152 | 153 | 174 | 175 | 470 | 471 | 472 | 514 | 515 | -------------------------------------------------------------------------------- /slides/embedder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ... 5 | 6 |

...

7 | 8 |
9 | 10 | 11 |

12 | /... 13 | 14 |

15 |
16 | 17 | 97 | 98 | 236 | 237 | 238 | 269 | -------------------------------------------------------------------------------- /slides/onstage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DZSlides 5 | 6 |
7 | 8 |
9 | 10 |
11 | 12 |
13 | 14 |
?
15 |
?
16 | 17 |
18 |

19 |
20 | 21 |
?
22 | 23 | 24 |
25 | 00:00:00 26 |
27 | 28 | 152 | 153 | 318 | 319 | 320 | 362 | -------------------------------------------------------------------------------- /slides/slide1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node.js 中文教學手冊 6 | 73 | 74 | 75 |
76 |

Node.js 中文教學手冊

77 |

Node.js Taiwan

78 |

2012

79 |
80 |

In the morning

81 |

Getting up

82 |
    83 |
  • Turn off alarm
  • 84 |
  • Get out of bed
  • 85 |
86 | 90 | 91 | 92 | 93 | 94 |
95 | 108 | 109 | 110 | 131 | 132 | 427 | 428 | 429 | 471 | 472 | 473 | 474 | -------------------------------------------------------------------------------- /slides/slide1.md: -------------------------------------------------------------------------------- 1 | % Node.js 中文教學手冊 2 | % Node.js Taiwan 3 | % 2012 4 | 5 | # In the morning 6 | 7 | ## Getting up 8 | 9 | - Turn off alarm 10 | - Get out of bed 11 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /slides/watchr.rb: -------------------------------------------------------------------------------- 1 | watch( '(.*)\.md' ) {|md| system("make all") } 2 | -------------------------------------------------------------------------------- /social_nature/defindex.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/defindex.html 3 | ~~~~~~~~~~~~~~~~~~~ 4 | 5 | Default template for the "index" page. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% extends "layout.html" %} 11 | {% set title = _('Overview') %} 12 | {% block body %} 13 |

{{ docstitle|e }}

14 |

15 | Welcome! This is 16 | {% block description %}the documentation for {{ project|e }} 17 | {{ release|e }}{% if last_updated %}, last updated {{ last_updated|e }}{% endif %}{% endblock %}. 18 |

19 | {% block tables %} 20 |

{{ _('Indices and tables:') }}

21 | 22 | 33 |
23 | 25 | 27 | 28 | 30 | 32 |
34 | {% endblock %} 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /social_nature/domainindex.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/domainindex.html 3 | ~~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Template for domain indices (module index, ...). 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% extends "layout.html" %} 11 | {% set title = indextitle %} 12 | {% block extrahead %} 13 | {{ super() }} 14 | {% if not embedded and collapse_index %} 15 | 18 | {% endif %} 19 | {% endblock %} 20 | {% block body %} 21 | 22 | {%- set groupid = idgen() %} 23 | 24 |

{{ indextitle }}

25 | 26 |
27 | {%- for (letter, entries) in content %} 28 | {{ letter }} 29 | {%- if not loop.last %} | {% endif %} 30 | {%- endfor %} 31 |
32 | 33 | 34 | {%- for letter, entries in content %} 35 | 36 | 38 | {%- for (name, grouptype, page, anchor, extra, qualifier, description) 39 | in entries %} 40 | 41 | 45 | 52 | {%- endfor %} 53 | {%- endfor %} 54 |
 
37 | {{ letter }}
{% if grouptype == 1 -%} 42 | 44 | {%- endif %}{% if grouptype == 2 %}   {% endif %} 46 | {% if page %}{% endif -%} 47 | {{ name|e }} 48 | {%- if page %}{% endif %} 49 | {%- if extra %} ({{ extra|e }}){% endif -%} 50 | {% if qualifier %}{{ qualifier|e }}:{% endif %} 51 | {{ description|e }}
55 | 56 | {% endblock %} 57 | -------------------------------------------------------------------------------- /social_nature/genindex-single.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/genindex-single.html 3 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Template for a "single" page of a split index. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% macro indexentries(firstname, links) %} 11 |
12 | {%- if links -%} 13 | 14 | {%- if links[0][0] %}{% endif -%} 15 | {{ firstname|e }} 16 | {%- if links[0][0] %}{% endif -%} 17 | 18 | 19 | {%- for ismain, link in links[1:] -%} 20 | , {% if ismain %}{% endif -%} 21 | [{{ loop.index }}] 22 | {%- if ismain %}{% endif -%} 23 | 24 | {%- endfor %} 25 | {%- else %} 26 | {{ firstname|e }} 27 | {%- endif %} 28 |
29 | {% endmacro %} 30 | 31 | {% extends "layout.html" %} 32 | {% set title = _('Index') %} 33 | {% block body %} 34 | 35 |

{% trans key=key %}Index – {{ key }}{% endtrans %}

36 | 37 | 38 | {%- for column in entries|slice(2) if column %} 39 | 51 | {%- endfor %} 52 |
40 | {%- for entryname, (links, subitems) in column %} 41 | {{ indexentries(entryname, links) }} 42 | {%- if subitems %} 43 |
44 | {%- for subentryname, subentrylinks in subitems %} 45 | {{ indexentries(subentryname, subentrylinks) }} 46 | {%- endfor %} 47 |
48 | {%- endif -%} 49 | {%- endfor %} 50 |
53 | 54 | {% endblock %} 55 | 56 | {% block sidebarrel %} 57 |

Index

58 |

{% for key, dummy in genindexentries -%} 59 | {{ key }} 60 | {% if not loop.last %}| {% endif %} 61 | {%- endfor %}

62 | 63 |

{{ _('Full index on one page') }}

64 | {{ super() }} 65 | {% endblock %} 66 | -------------------------------------------------------------------------------- /social_nature/genindex-split.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/genindex-split.html 3 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Template for a "split" index overview page. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% extends "layout.html" %} 11 | {% set title = _('Index') %} 12 | {% block body %} 13 | 14 |

{{ _('Index') }}

15 | 16 |

{{ _('Index pages by letter') }}:

17 | 18 |
19 |

{% for key, dummy in genindexentries -%} 20 | {{ key }} 21 | {% if not loop.last %}| {% endif %} 22 | {%- endfor %}

23 | 24 |

{{ _('Full index on one page') }} 25 | ({{ _('can be huge') }})

26 |
27 | 28 | {% endblock %} 29 | 30 | {% block sidebarrel %} 31 | {% if split_index %} 32 |

Index

33 |

{% for key, dummy in genindexentries -%} 34 | {{ key }} 35 | {% if not loop.last %}| {% endif %} 36 | {%- endfor %}

37 | 38 |

{{ _('Full index on one page') }}

39 | {% endif %} 40 | {{ super() }} 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /social_nature/genindex.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/genindex.html 3 | ~~~~~~~~~~~~~~~~~~~ 4 | 5 | Template for an "all-in-one" index. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% macro indexentries(firstname, links) %} 11 |
12 | {%- if links -%} 13 | 14 | {%- if links[0][0] %}{% endif -%} 15 | {{ firstname|e }} 16 | {%- if links[0][0] %}{% endif -%} 17 | 18 | 19 | {%- for ismain, link in links[1:] -%} 20 | , {% if ismain %}{% endif -%} 21 | [{{ loop.index }}] 22 | {%- if ismain %}{% endif -%} 23 | 24 | {%- endfor %} 25 | {%- else %} 26 | {{ firstname|e }} 27 | {%- endif %} 28 |
29 | {% endmacro %} 30 | 31 | {% extends "layout.html" %} 32 | {% set title = _('Index') %} 33 | {% block body %} 34 | 35 |

{{ _('Index') }}

36 | 37 |
38 | {% for key, dummy in genindexentries -%} 39 | {{ key }} 40 | {% if not loop.last %}| {% endif %} 41 | {%- endfor %} 42 |
43 | 44 | {%- for key, entries in genindexentries %} 45 |

{{ key }}

46 | 47 | {%- for column in entries|slice(2) if column %} 48 | 60 | {%- endfor %} 61 |
49 | {%- for entryname, (links, subitems) in column %} 50 | {{ indexentries(entryname, links) }} 51 | {%- if subitems %} 52 |
53 | {%- for subentryname, subentrylinks in subitems %} 54 | {{ indexentries(subentryname, subentrylinks) }} 55 | {%- endfor %} 56 |
57 | {%- endif -%} 58 | {%- endfor %} 59 |
62 | {% endfor %} 63 | 64 | {% endblock %} 65 | 66 | {% block sidebarrel %} 67 | {% if split_index %} 68 |

{{ _('Index') }}

69 |

{% for key, dummy in genindexentries -%} 70 | {{ key }} 71 | {% if not loop.last %}| {% endif %} 72 | {%- endfor %}

73 | 74 |

{{ _('Full index on one page') }}

75 | {% endif %} 76 | {{ super() }} 77 | {% endblock %} 78 | -------------------------------------------------------------------------------- /social_nature/globaltoc.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/globaltoc.html 3 | ~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx sidebar template: global table of contents. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 |

{{ _('Table Of Contents') }}

11 | {{ toctree() }} 12 | -------------------------------------------------------------------------------- /social_nature/layout.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/layout.html 3 | ~~~~~~~~~~~~~~~~~ 4 | 5 | Master layout template for Sphinx themes. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {%- block doctype -%} 11 | 13 | {%- endblock %} 14 | {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} 15 | {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} 16 | {%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and 17 | (sidebars != []) %} 18 | {%- set url_root = pathto('', 1) %} 19 | {# XXX necessary? #} 20 | {%- if url_root == '#' %}{% set url_root = '' %}{% endif %} 21 | {%- if not embedded and docstitle %} 22 | {%- set titlesuffix = " — "|safe + docstitle|e %} 23 | {%- else %} 24 | {%- set titlesuffix = "" %} 25 | {%- endif %} 26 | 27 | {%- macro relbar() %} 28 | 46 | {%- endmacro %} 47 | 48 | {%- macro sidebar() %} 49 | {%- if render_sidebar %} 50 |
51 |
52 | {%- block sidebarlogo %} 53 | {%- if logo %} 54 | 57 | {%- endif %} 58 | {%- endblock %} 59 | {%- if sidebars != None %} 60 | {#- new style sidebar: explicitly include/exclude templates #} 61 | {%- for sidebartemplate in sidebars %} 62 | {%- include sidebartemplate %} 63 | {%- endfor %} 64 | {%- else %} 65 | {#- old style sidebars: using blocks -- should be deprecated #} 66 | {%- block sidebartoc %} 67 | {%- include "localtoc.html" %} 68 | {%- endblock %} 69 | {%- block sidebarrel %} 70 | {%- include "relations.html" %} 71 | {%- endblock %} 72 | {%- block sidebarsourcelink %} 73 | {%- include "sourcelink.html" %} 74 | {%- endblock %} 75 | {%- if customsidebar %} 76 | {%- include customsidebar %} 77 | {%- endif %} 78 | {%- block sidebarsearch %} 79 | {%- include "searchbox.html" %} 80 | {%- endblock %} 81 | {%- endif %} 82 | 83 |
84 |
85 | {%- endif %} 86 | {%- endmacro %} 87 | 88 | {%- macro script() %} 89 | 98 | {%- for scriptfile in script_files %} 99 | 100 | {%- endfor %} 101 | {%- endmacro %} 102 | 103 | {%- macro css() %} 104 | 105 | 106 | {%- for cssfile in css_files %} 107 | 108 | {%- endfor %} 109 | {%- endmacro %} 110 | 111 | 112 | 113 | 114 | {{ metatags }} 115 | {%- block htmltitle %} 116 | {{ title|striptags|e }}{{ titlesuffix }} 117 | {%- endblock %} 118 | {{ css() }} 119 | {%- if not embedded %} 120 | {{ script() }} 121 | {%- if use_opensearch %} 122 | 125 | {%- endif %} 126 | {%- if favicon %} 127 | 128 | {%- endif %} 129 | {%- endif %} 130 | {%- block linktags %} 131 | {%- if hasdoc('about') %} 132 | 133 | {%- endif %} 134 | {%- if hasdoc('genindex') %} 135 | 136 | {%- endif %} 137 | {%- if hasdoc('search') %} 138 | 139 | {%- endif %} 140 | {%- if hasdoc('copyright') %} 141 | 142 | {%- endif %} 143 | 144 | {%- if parents %} 145 | 146 | {%- endif %} 147 | {%- if next %} 148 | 149 | {%- endif %} 150 | {%- if prev %} 151 | 152 | {%- endif %} 153 | {%- endblock %} 154 | {%- block extrahead %} {% endblock %} 155 | 156 | 157 | {%- block header %}{% endblock %} 158 | 159 | {%- block relbar1 %}{{ relbar() }}{% endblock %} 160 | 161 | {%- block content %} 162 | {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} 163 | 164 |
165 | {%- block document %} 166 |
167 | {%- if render_sidebar %} 168 |
169 | {%- endif %} 170 | 171 |
172 | 173 | 174 |
175 | 176 | 177 | 178 | 179 |
180 | 181 | 182 | 183 | {% block body %} {% endblock %} 184 | 185 | 186 |
187 | 188 | 189 | 190 | 191 |
192 | 193 | 194 | 195 |
196 | 206 | 207 | blog comments powered by Disqus 208 | 209 |
210 | 211 | {%- if render_sidebar %} 212 |
213 | {%- endif %} 214 |
215 | {%- endblock %} 216 | 217 | {%- block sidebar2 %}{{ sidebar() }}{% endblock %} 218 |
219 |
220 | {%- endblock %} 221 | 222 | {%- block relbar2 %}{{ relbar() }}{% endblock %} 223 | 224 | {%- block footer %} 225 | 240 | {%- endblock %} 241 | 242 | 243 | -------------------------------------------------------------------------------- /social_nature/localtoc.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/localtoc.html 3 | ~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx sidebar template: local table of contents. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {%- if display_toc %} 11 |

{{ _('Table Of Contents') }}

12 | {{ toc }} 13 | {%- endif %} 14 | -------------------------------------------------------------------------------- /social_nature/page.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/page.html 3 | ~~~~~~~~~~~~~~~ 4 | 5 | Master template for simple pages. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% extends "layout.html" %} 11 | {% block body %} 12 | {{ body }} 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /social_nature/relations.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/relations.html 3 | ~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx sidebar template: relation links. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {%- if prev %} 11 |

{{ _('Previous topic') }}

12 |

{{ prev.title }}

14 | {%- endif %} 15 | {%- if next %} 16 |

{{ _('Next topic') }}

17 |

{{ next.title }}

19 | {%- endif %} 20 | -------------------------------------------------------------------------------- /social_nature/search.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/search.html 3 | ~~~~~~~~~~~~~~~~~ 4 | 5 | Template for the search page. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {% extends "layout.html" %} 11 | {% set title = _('Search') %} 12 | {% set script_files = script_files + ['_static/searchtools.js'] %} 13 | {% block extrahead %} 14 | 17 | {{ super() }} 18 | {% endblock %} 19 | {% block body %} 20 |

{{ _('Search') }}

21 |
22 | 23 |

24 | {% trans %}Please activate JavaScript to enable the search 25 | functionality.{% endtrans %} 26 |

27 |
28 |

29 | {% trans %}From here you can search these documents. Enter your search 30 | words into the box below and click "search". Note that the search 31 | function will automatically search for all of the words. Pages 32 | containing fewer words won't appear in the result list.{% endtrans %} 33 |

34 |
35 | 36 | 37 | 38 |
39 | {% if search_performed %} 40 |

{{ _('Search Results') }}

41 | {% if not search_results %} 42 |

{{ _('Your search did not match any results.') }}

43 | {% endif %} 44 | {% endif %} 45 |
46 | {% if search_results %} 47 |
    48 | {% for href, caption, context in search_results %} 49 |
  • {{ caption }} 50 |
    {{ context|e }}
    51 |
  • 52 | {% endfor %} 53 |
54 | {% endif %} 55 |
56 | {% endblock %} 57 | -------------------------------------------------------------------------------- /social_nature/searchbox.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/searchbox.html 3 | ~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx sidebar template: quick search box. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {%- if pagename != "search" %} 11 | 23 | 24 | {%- endif %} 25 | -------------------------------------------------------------------------------- /social_nature/searchresults.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/searchresults.html 3 | ~~~~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Template for the body of the search results page. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 |

Search

11 |

12 | From here you can search these documents. Enter your search 13 | words into the box below and click "search". 14 |

15 |
16 | 17 | 18 | 19 |
20 | {%- if search_performed %} 21 |

Search Results

22 | {%- if not search_results %} 23 |

Your search did not match any results.

24 | {%- endif %} 25 | {%- endif %} 26 |
27 | {%- if search_results %} 28 | 35 | {%- endif %} 36 |
37 | -------------------------------------------------------------------------------- /social_nature/sourcelink.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/sourcelink.html 3 | ~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx sidebar template: "show source" link. 6 | 7 | :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {%- if show_source and has_source and sourcename %} 11 |

{{ _('This Page') }}

12 | 16 | {%- endif %} 17 | -------------------------------------------------------------------------------- /social_nature/static/nature.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * nature.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- nature theme. 6 | * 7 | * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: Arial, sans-serif; 18 | font-size: 100%; 19 | background-color: #111; 20 | color: #555; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 0 0 0 {{ theme_sidebarwidth|toint }}px; 32 | } 33 | 34 | hr { 35 | border: 1px solid #B1B4B6; 36 | } 37 | 38 | div.document { 39 | background-color: #eee; 40 | } 41 | 42 | div.body { 43 | background-color: #ffffff; 44 | color: #3E4349; 45 | padding: 0 30px 30px 30px; 46 | font-size: 0.9em; 47 | } 48 | 49 | div.footer { 50 | color: #555; 51 | width: 100%; 52 | padding: 13px 0; 53 | text-align: center; 54 | font-size: 75%; 55 | } 56 | 57 | div.footer a { 58 | color: #444; 59 | text-decoration: underline; 60 | } 61 | 62 | div.related { 63 | background-color: #6BA81E; 64 | line-height: 32px; 65 | color: #fff; 66 | text-shadow: 0px 1px 0 #444; 67 | font-size: 0.9em; 68 | } 69 | 70 | div.related a { 71 | color: #E2F3CC; 72 | } 73 | 74 | div.sphinxsidebar { 75 | font-size: 0.75em; 76 | line-height: 1.5em; 77 | } 78 | 79 | div.sphinxsidebarwrapper{ 80 | padding: 20px 0; 81 | } 82 | 83 | div.sphinxsidebar h3, 84 | div.sphinxsidebar h4 { 85 | font-family: Arial, sans-serif; 86 | color: #222; 87 | font-size: 1.2em; 88 | font-weight: normal; 89 | margin: 0; 90 | padding: 5px 10px; 91 | background-color: #ddd; 92 | text-shadow: 1px 1px 0 white 93 | } 94 | 95 | div.sphinxsidebar h4{ 96 | font-size: 1.1em; 97 | } 98 | 99 | div.sphinxsidebar h3 a { 100 | color: #444; 101 | } 102 | 103 | 104 | div.sphinxsidebar p { 105 | color: #888; 106 | padding: 5px 20px; 107 | } 108 | 109 | div.sphinxsidebar p.topless { 110 | } 111 | 112 | div.sphinxsidebar ul { 113 | margin: 10px 20px; 114 | padding: 0; 115 | color: #000; 116 | } 117 | 118 | div.sphinxsidebar a { 119 | color: #444; 120 | } 121 | 122 | div.sphinxsidebar input { 123 | border: 1px solid #ccc; 124 | font-family: sans-serif; 125 | font-size: 1em; 126 | } 127 | 128 | div.sphinxsidebar input[type=text]{ 129 | margin-left: 20px; 130 | } 131 | 132 | /* -- body styles ----------------------------------------------------------- */ 133 | 134 | a { 135 | color: #005B81; 136 | text-decoration: none; 137 | } 138 | 139 | a:hover { 140 | color: #E32E00; 141 | text-decoration: underline; 142 | } 143 | 144 | div.body h1, 145 | div.body h2, 146 | div.body h3, 147 | div.body h4, 148 | div.body h5, 149 | div.body h6 { 150 | font-family: Arial, sans-serif; 151 | background-color: #BED4EB; 152 | font-weight: normal; 153 | color: #212224; 154 | margin: 30px 0px 10px 0px; 155 | padding: 5px 0 5px 10px; 156 | text-shadow: 0px 1px 0 white 157 | } 158 | 159 | div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } 160 | div.body h2 { font-size: 150%; background-color: #C8D5E3; } 161 | div.body h3 { font-size: 120%; background-color: #D8DEE3; } 162 | div.body h4 { font-size: 110%; background-color: #D8DEE3; } 163 | div.body h5 { font-size: 100%; background-color: #D8DEE3; } 164 | div.body h6 { font-size: 100%; background-color: #D8DEE3; } 165 | 166 | a.headerlink { 167 | color: #c60f0f; 168 | font-size: 0.8em; 169 | padding: 0 4px 0 4px; 170 | text-decoration: none; 171 | } 172 | 173 | a.headerlink:hover { 174 | background-color: #c60f0f; 175 | color: white; 176 | } 177 | 178 | div.body p, div.body dd, div.body li { 179 | line-height: 1.5em; 180 | } 181 | 182 | div.admonition p.admonition-title + p { 183 | display: inline; 184 | } 185 | 186 | div.highlight{ 187 | background-color: white; 188 | } 189 | 190 | div.note { 191 | background-color: #eee; 192 | border: 1px solid #ccc; 193 | } 194 | 195 | div.seealso { 196 | background-color: #ffc; 197 | border: 1px solid #ff6; 198 | } 199 | 200 | div.topic { 201 | background-color: #eee; 202 | } 203 | 204 | div.warning { 205 | background-color: #ffe4e4; 206 | border: 1px solid #f66; 207 | } 208 | 209 | p.admonition-title { 210 | display: inline; 211 | } 212 | 213 | p.admonition-title:after { 214 | content: ":"; 215 | } 216 | 217 | pre { 218 | padding: 10px; 219 | background-color: White; 220 | color: #222; 221 | line-height: 1.2em; 222 | border: 1px solid #C6C9CB; 223 | font-size: 1.1em; 224 | margin: 1.5em 0 1.5em 0; 225 | -webkit-box-shadow: 1px 1px 1px #d8d8d8; 226 | -moz-box-shadow: 1px 1px 1px #d8d8d8; 227 | } 228 | 229 | tt { 230 | background-color: #ecf0f3; 231 | color: #222; 232 | /* padding: 1px 2px; */ 233 | font-size: 1.1em; 234 | font-family: monospace; 235 | } 236 | 237 | .viewcode-back { 238 | font-family: Arial, sans-serif; 239 | } 240 | 241 | div.viewcode-block:target { 242 | background-color: #f4debf; 243 | border-top: 1px solid #ac9; 244 | border-bottom: 1px solid #ac9; 245 | } 246 | 247 | #disqus_thread h3, #disqus_thread h4 { 248 | background: none; 249 | } 250 | 251 | .addthis_toolbox { 252 | padding-top: 10px; 253 | } 254 | -------------------------------------------------------------------------------- /social_nature/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = nature.css 4 | pygments_style = tango 5 | -------------------------------------------------------------------------------- /src/hello.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | http.createServer(function (req, res) { 3 | res.writeHead(200, {'Content-Type': 'text/plain'}); 4 | res.end('Hello World\n'); 5 | }).listen(1337, "127.0.0.1"); 6 | console.log('Server running at http://127.0.0.1:1337/'); 7 | -------------------------------------------------------------------------------- /src/node_basic_file_http_static.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | fs = require("fs"), 6 | folderPath = "static", 7 | url = require('url'), 8 | path, 9 | filePath, 10 | encode = "utf8"; 11 | 12 | server = http.createServer(function (req, res) { 13 | path = url.parse(req.url); 14 | filePath = folderPath + path.pathname; 15 | 16 | fs.readFile(filePath, encode, function(err, file) { 17 | if (err) { 18 | res.writeHead(404, {'Content-Type': 'text/plain'}); 19 | res.end(); 20 | return; 21 | } 22 | 23 | res.writeHead(200, {'Content-Type': 'text/application'}); 24 | res.write(file); 25 | res.end(); 26 | }); 27 | }); 28 | 29 | server.listen(port, ip); 30 | 31 | console.log("Server running at http://" + ip + ":" + port); 32 | 33 | -------------------------------------------------------------------------------- /src/node_basic_file_simple.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), 2 | filename = "static/index.html", 3 | encode = "utf8"; 4 | 5 | fs.readFile(filename, encode, function(err, file) { 6 | console.log(file); 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /src/node_basic_http_get.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | qs = require('querystring'), 6 | url = require('url'); 7 | 8 | server = http.createServer(function (req, res) { 9 | var path = url.parse(req.url), 10 | parameter = qs.parse(path.query); 11 | 12 | console.dir(parameter); 13 | 14 | res.writeHead(200, {'Content-Type': 'text/plain'}); 15 | res.write('Browser test GET parameter\n'); 16 | res.end(); 17 | }); 18 | 19 | server.listen(port, ip); 20 | 21 | console.log("Server running at http://" + ip + ":" + port); 22 | -------------------------------------------------------------------------------- /src/node_basic_http_hello_world.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'); 5 | 6 | server = http.createServer(function (req, res) { 7 | res.writeHead(200, {'Content-Type': 'text/plain'}); 8 | res.end('Hello World\n'); 9 | }); 10 | 11 | server.listen(port, ip); 12 | 13 | console.log("Server running at http://" + ip + ":" + port); 14 | -------------------------------------------------------------------------------- /src/node_basic_http_rout.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | url = require('url'); 6 | 7 | server = http.createServer(function (req, res) { 8 | console.log(req.url); 9 | res.writeHead(200, {'Content-Type': 'text/plain'}); 10 | res.end('hello world\n'); 11 | }); 12 | 13 | server.listen(port, ip); 14 | 15 | console.log("Server running at http://" + ip + ":" + port); 16 | -------------------------------------------------------------------------------- /src/node_basic_http_rout_done.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | url = require('url'), 6 | path; 7 | 8 | server = http.createServer(function (req, res) { 9 | path = url.parse(req.url); 10 | 11 | res.writeHead(200, {'Content-Type': 'text/plain'}); 12 | 13 | switch (path.pathname) { 14 | case "/index": 15 | res.end('I am index.\n'); 16 | break; 17 | case "/test": 18 | res.end('this is test page.\n'); 19 | break; 20 | default: 21 | res.end('default page.\n'); 22 | break; 23 | } 24 | }); 25 | 26 | server.listen(port, ip); 27 | 28 | console.log("Server running at http://" + ip + ":" + port); 29 | -------------------------------------------------------------------------------- /src/node_express_ajax_form.js: -------------------------------------------------------------------------------- 1 | var http = require('http'), 2 | url = require('url'), 3 | fs = require("fs"), 4 | qs = require('querystring'), 5 | server; 6 | 7 | server = http.createServer(function (req,res) { 8 | var urlData, 9 | encode = "utf8", 10 | filePath = "view/express_ajax_example_form.html", 11 | formData, 12 | action; 13 | 14 | urlData = url.parse(req.url,true); 15 | action = urlData.pathname; 16 | 17 | if (action === "/Signup") { 18 | formData = ''; 19 | req.on("data", function (data) { 20 | 21 | formData += data; 22 | 23 | }); 24 | 25 | req.on("end", function () { 26 | var msg; 27 | 28 | user = qs.parse(formData); 29 | user.id = "123456"; 30 | msg = JSON.stringify(user); 31 | res.writeHead(200, {"Content-Type":"application/json;","Content-Length":msg.length}); 32 | res.end(msg); 33 | }); 34 | } 35 | else { 36 | fs.readFile(filePath, encode, function(err, file) { 37 | res.writeHead(200, {"Content-Type":"text/html; charset=utf-8"}); 38 | res.write(file); 39 | res.end(); 40 | }); 41 | } 42 | 43 | }); 44 | 45 | server.listen(3000); 46 | 47 | console.log('Server跑起來了,現在時間是:' + new Date()); 48 | -------------------------------------------------------------------------------- /src/node_express_basic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @overview 3 | * 4 | * @author Caesar Chi 5 | * @blog clonn.blogspot.com 6 | * @version 2012/02/26 7 | */ 8 | 9 | // create server. 10 | var app = require('express').createServer(), 11 | port = 1337; 12 | 13 | app.listen(port); 14 | 15 | // normal style 16 | app.get('/', function(req, res){ 17 | res.send('hello world'); 18 | }); 19 | 20 | app.get('/test', function(req, res){ 21 | res.send('test render'); 22 | }); 23 | 24 | // parameter style 25 | app.get('/user/:id', function(req, res){ 26 | res.send('user: ' + req.params.id); 27 | }); 28 | 29 | app.get('/:number', function(req, res){ 30 | res.send('number: ' + req.params.number); 31 | }); 32 | 33 | // REGX style 34 | app.get(/^\/ip?(?:\/(\d{2,3})(?:\.(\d{2,3}))(?:\.(\d{2,3})))?/, function(req, res){ 35 | res.send(req.params); 36 | }); 37 | 38 | app.get('*', function(req, res){ 39 | res.send('Page not found!', 404); 40 | }); 41 | 42 | console.log('start express server\n'); 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/node_express_basic_app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @overview 3 | * 4 | * @author 5 | * @version 2012/02/26 6 | */ 7 | 8 | // create server. 9 | var app = require('express').createServer(), 10 | port = 1337, 11 | users = [ 12 | {name: 'Clonn'}, 13 | {name: 'Chi'} 14 | ]; 15 | 16 | app.listen(port); 17 | 18 | app.all('/user/:id/:op?', function(req, res, next){ 19 | req.user = users[req.params.id]; 20 | if (req.user) { 21 | next(); 22 | } else { 23 | next(new Error('cannot find user ' + req.params.id)); 24 | } 25 | }); 26 | 27 | app.get('/user/:id', function(req, res){ 28 | res.send('viewing ' + req.user.name); 29 | }); 30 | 31 | app.get('/user/:id/edit', function(req, res){ 32 | res.send('editing ' + req.user.name); 33 | }); 34 | 35 | app.get('/user/:id/delete', function(req, res){ 36 | res.send('deleting ' + req.user.name); 37 | }); 38 | 39 | app.get('*', function(req, res){ 40 | res.send('Page not found!', 404); 41 | }); 42 | 43 | console.log('start express server\n'); 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/node_express_get_form.js: -------------------------------------------------------------------------------- 1 | var http = require('http'), 2 | url = require('url'), 3 | fs = require("fs"), 4 | server; 5 | 6 | server = http.createServer(function (req,res) { 7 | var urlData, 8 | encode = "utf8", 9 | filePath = "view/express_get_example_form.html", 10 | action; 11 | 12 | urlData = url.parse(req.url,true); 13 | action = urlData.pathname; 14 | res.writeHead(200, {"Content-Type":"text/html; charset=utf-8"}); 15 | 16 | if (action === "/Signup") { 17 | user = urlData.query; 18 | res.end("

" + user.username + "歡迎您的加入

我們已經將會員啟用信寄至" + user.email + "

"); 19 | } 20 | else { 21 | fs.readFile(filePath, encode, function(err, file) { 22 | 23 | res.write(file); 24 | res.end(); 25 | }); 26 | } 27 | 28 | }); 29 | 30 | server.listen(3000); 31 | 32 | console.log('Server跑起來了,現在時間是:' + new Date()); 33 | -------------------------------------------------------------------------------- /src/node_express_middleware.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var express = require('express'); 5 | var app = express.createServer(); 6 | var port = 3000; 7 | // Example requests: 8 | // curl http://localhost:3000/user/0 9 | // curl http://localhost:3000/user/0/edit 10 | // curl http://localhost:3000/user/1 11 | // curl http://localhost:3000/user/1/edit (unauthorized since this is not you) 12 | // curl -X DELETE http://localhost:3000/user/0 (unauthorized since you are not an admin) 13 | // Dummy users 14 | var users = [{ 15 | id: 0, 16 | name: 'tj', 17 | email: 'tj@vision-media.ca', 18 | role: 'member' 19 | }, { 20 | id: 1, 21 | name: 'ciaran', 22 | email: 'ciaranj@gmail.com', 23 | role: 'member' 24 | }, { 25 | id: 2, 26 | name: 'aaron', 27 | email: 'aaron.heckmann+github@gmail.com', 28 | role: 'admin' 29 | }]; 30 | 31 | console.log(global); 32 | function loadUser(req, res, next) { 33 | // You would fetch your user from the db 34 | console.log("load user function"); 35 | var user = users[req.params.id]; 36 | if (user) { 37 | req.user = user; 38 | next(); 39 | } else { 40 | next(new Error('Failed to load user ' + req.params.id)); 41 | } 42 | } 43 | 44 | function andRestrictToSelf(req, res, next) { 45 | // If our authenticated user is the user we are viewing 46 | // then everything is fine :) 47 | if (req.authenticatedUser.id == req.user.id) { 48 | next(); 49 | } else { 50 | // You may want to implement specific exceptions 51 | // such as UnauthorizedError or similar so that you 52 | // can handle these can be special-cased in an error handler 53 | // (view ./examples/pages for this) 54 | next(new Error('Unauthorized')); 55 | } 56 | } 57 | 58 | function andRestrictTo(role) { 59 | return function (req, res, next) { 60 | if (req.authenticatedUser.role == role) { 61 | next(); 62 | } else { 63 | next(new Error('Unauthorized')); 64 | } 65 | } 66 | } 67 | // Middleware for faux authentication 68 | // you would of course implement something real, 69 | // but this illustrates how an authenticated user 70 | // may interact with middleware 71 | app.use(function (req, res, next) { 72 | console.log("first middle ware"); 73 | req.authenticatedUser = users[0]; 74 | next(); 75 | }); 76 | app.use(function (req, res, next) { 77 | console.log("second middle ware"); 78 | //req.authenticatedUser = users[0]; 79 | next(); 80 | }); 81 | app.get('/', function (req, res) { 82 | console.log(" \/ function"); 83 | res.redirect('/user/0'); 84 | }); 85 | app.get('/user/:id', loadUser, function (req, res) { 86 | res.send('Viewing user ' + req.user.name); 87 | }); 88 | app.get('/user/:id/edit', loadUser, andRestrictToSelf, function (req, res) { 89 | res.send('Editing user ' + req.user.name); 90 | }); 91 | app.del('/user/:id', loadUser, andRestrictTo('admin'), function (req, res) { 92 | res.send('Deleted user ' + req.user.name); 93 | }); 94 | 95 | 96 | function testfn(req, res, next) { 97 | console.log("test fn"); 98 | next(); 99 | } 100 | 101 | app.get('/test/:id', loadUser, testfn, function (req, res) { 102 | console.log("get function."); 103 | res.send('Viewing user ' + req.user.name); 104 | }); 105 | 106 | 107 | 108 | app.listen(port); 109 | console.log('Express app started on port 3000'); 110 | -------------------------------------------------------------------------------- /src/node_express_middleware_simple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var express = require('express'); 5 | var app = express.createServer(); 6 | var port = 1337; 7 | 8 | function middleHandler(req, res, next) { 9 | console.log("execute middle ware"); 10 | next(); 11 | } 12 | 13 | app.use(function (req, res, next) { 14 | console.log("first middle ware"); 15 | next(); 16 | }); 17 | 18 | app.use(function (req, res, next) { 19 | console.log("second middle ware"); 20 | next(); 21 | }); 22 | 23 | app.get('/', middleHandler, function (req, res) { 24 | console.log("end middleware function"); 25 | res.send("page render finished"); 26 | }); 27 | 28 | app.listen(port); 29 | console.log('start server'); 30 | -------------------------------------------------------------------------------- /src/node_express_post_form.js: -------------------------------------------------------------------------------- 1 | var http = require('http'), 2 | url = require('url'), 3 | fs = require("fs"), 4 | qs = require('querystring'), 5 | server; 6 | 7 | server = http.createServer(function (req,res) { 8 | var urlData, 9 | encode = "utf8", 10 | filePath = "view/express_post_example_form.html", 11 | formData, 12 | action; 13 | 14 | urlData = url.parse(req.url,true); 15 | action = urlData.pathname; 16 | res.writeHead(200, {"Content-Type":"text/html; charset=utf-8"}); 17 | 18 | if (action === "/Signup") { 19 | formData = ''; 20 | req.on("data", function (data) { 21 | 22 | formData += data; 23 | 24 | }); 25 | 26 | req.on("end", function () { 27 | user = qs.parse(formData); 28 | res.end("

" + user.username + "歡迎您的加入

我們已經將會員啟用信寄至" + user.email + "

"); 29 | }); 30 | } 31 | else { 32 | fs.readFile(filePath, encode, function(err, file) { 33 | 34 | res.write(file); 35 | res.end(); 36 | }); 37 | } 38 | 39 | }); 40 | 41 | server.listen(3000); 42 | 43 | console.log('Server跑起來了,現在時間是:' + new Date()); 44 | -------------------------------------------------------------------------------- /src/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | node.js index html file 6 | 7 | 8 |

node.js index html file

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/static/static.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | node.js static html file 6 | 7 | 8 |

node.js static html file

9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/static/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | node.js test html file 6 | 7 | 8 |

node.js test html file

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/view/express_ajax_example_form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Node.js菜鳥筆記(3) 5 | 6 | 7 | 8 | 9 |

Node.js菜鳥筆記-註冊(用Ajax)

10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/view/express_get_example_form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node.js菜鳥筆記(1) 6 | 7 | 8 | 9 |

Node.js菜鳥筆記-註冊

10 |
11 |
12 |
13 |
14 |
15 | 16 | -------------------------------------------------------------------------------- /src/view/express_post_example_form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node.js菜鳥筆記(1) 6 | 7 | 8 | 9 |

Node.js菜鳥筆記-註冊

10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /watchr.rb: -------------------------------------------------------------------------------- 1 | watch( '(.*)\.rst' ) {|md| system("sphinx-cook -f pdf .") } 2 | -------------------------------------------------------------------------------- /zh-tw/Socket.io.rst: -------------------------------------------------------------------------------- 1 | 市面上其實非常多 Socket.io 的文章,所以我寫在這裡其實是筆記居多,不嫌棄的話可以繼續看下去這樣。 2 | 3 | WebSocket API 4 | ============= 5 | 6 | 這一項技術其實在 w3c 上面還是 Draft 的狀態,所以,其實你會聽到大部分的人會說,用 Flash 來作會比較穩定一點。而其實 Socket.io 官方 wiki 上面也有提到 FlashScoket.IO 的東西(笑 7 | 8 | 這個東西是 HTML5 的新的協定,簡單的來說,就是可以讓瀏覽器與後端伺服器之間,經由一個握手(handshake)的動作,來連接一條兩者之間的高速公路。這麼一來,我們就可以瀏覽器與後端伺服器之間,快速的傳遞一些資料。 9 | 10 | `維基百科上的 WebSocket 介紹。` 11 | 12 | 其中有提到了目前的方式,大多是以輪巡(Polling)的方式來達成,還有一種是 Comet(我實在不知道該怎麼用中文來描述他),而因為 Comet 會在後端伺服器上面佔用連線,且若是非 non-blocking 的伺服器,像是 Apache,很容易會讓 IO 爆炸。所以,後來就出現了長輪巡(Long Polling)與 iframe 改良式的 Comet。 13 | 14 | 以上的作法大多都以 AJAX(XHR)來去實做,而WebSocket 就解決了許多的問題,而且他是可以雙向溝通的! 15 | 16 | 17 | 溝通的問題 18 | ========== 19 | 20 | 目前其實最流行的方式,還是以 Long Polling 為主,最重要的原因是沒有瀏覽器相容性的問題。 21 | 22 | BUT! 23 | 24 | 如果你的後端伺服器不支援的話,那他就只是一個單純的 Polling 而已。為什麼? 25 | 26 | .. code-block:: js 27 | 28 | (function polling() { 29 | $.ajax({ 30 | url: "http://server", 31 | type: "post", 32 | dataType: "json", 33 | timeout: 30000, 34 | success: function(data) { 35 | /* Do something */ 36 | }, 37 | complete: function() { 38 | /* Polling here. */ 39 | polling(); 40 | } 41 | }); 42 | })(); 43 | 44 | 上面我做了一件事情,就是等待 30 秒後重複發送一個 ajax 的請求到後端伺服器去。而 Long Polling 的作法是, 45 | 46 | 前端送了一個請求給後端 47 | 後端收到後,回傳資料給前端,並斷開連線 48 | 前端收到後,執行 callback,並再次發送一個請求給後端 49 | 以上的方式就是一個無窮迴圈,所以,如果後端收到後,沒有斷開連線,那麼前端就只會每 30 秒斷線重連,這樣跟一般的 Polling 其實並沒有兩樣。那,為什麼非 non-blocking 的後端伺服器不行?如果我送一個 ajax 給 Apache,那他把事情做完之後,丟一個回應給前端,也會達成 complete 的條件不是嗎? 50 | 51 | 是! 52 | 53 | :: 54 | 55 | 'ok')); 60 | 61 | 但是,當你的後端伺服器沒有放開連線時,你只能等待前端 timeout 的時間到了,並且再次發送一次請求時,才能繼續動作。而,屆時後端的資料到底做完了沒呢?答案是:不知道,所以,使用 non-blocking 的後端伺服器多少能避開這些問題。 62 | 63 | 以上,都是單向的溝通,也是目前流行的方式。 64 | 65 | 這裡有兩篇 Comet 文章可以看一下: 66 | 67 | * `Comet Programming: Using Ajax to Simulate Server Push` 68 | * `Comet Programming: the Hidden IFrame Technique` 69 | 70 | 71 | Socket.IO 72 | ========= 73 | 74 | 他做了一件事情,就是把那些溝通的方式全部整合起來,無論前端還是後端,他都幫你打包好。所以,你只要會用就可以了,這樣是不是很佛心呢! 75 | 76 | :: 77 | 78 | $ npm install socket.io 79 | 80 | 他所支援的傳輸方式有下列幾種, 81 | 82 | * xhr-polling 83 | * xhr-multipart 84 | * htmlfile 85 | * websocket 86 | * flashsocket 87 | * jsonp-polling 88 | 89 | 除了字面上有 socket 的之外,都是 Polling 與其變種方式,其中 xhr-multipart 也是,他只是把資料拆成好幾個部份來傳送而已。而其中 htmlfile 貌似是 IE 底下的東西,我在大神上面問資料的時候,看到了 ActiveXObject 這幾個字,我就不想理他了。 90 | 91 | 簡單的後端應用方式,我們可以這樣寫(以下是官方範例) 92 | 93 | .. code-block:: js 94 | 95 | var io = require('socket.io').listen(8080); 96 | 97 | io.sockets.on('connection', function (socket) { 98 | socket.emit('news', { hello: 'world' }); 99 | socket.on('my other event', function (data) { 100 | console.log(data); 101 | }); 102 | }); 103 | 104 | 而前端是這個樣子, 105 | 106 | :: 107 | 108 | 109 | 116 | 117 | 我們沒有特別去指定 Socket.IO 要用什麼方式來作傳遞,所以他會自己決定,透過目前你的瀏覽器能使用什麼方式,來傳遞我們所需要的資料。這麼說,我們也可以指定傳遞方式, 118 | 119 | .. code-block:: js 120 | 121 | var io = require('socket.io').listen(8080); 122 | 123 | io.configure('development', function() { 124 | io.set('transports', [ 125 | 'xhr-polling' 126 | , 'jsonp-polling' 127 | ]); 128 | }); 129 | 130 | io.sockets.on('connection', function (socket) { 131 | socket.emit('news', { hello: 'world' }); 132 | socket.on('my other event', function (data) { 133 | console.log(data); 134 | }); 135 | }); 136 | 137 | 以上述的例子來說,他就會使用 xhr-polling 與 jsonp-polling 兩種方式的其中一種,來傳遞我們的資料。 138 | 139 | 更多詳細設定,在官方的 wiki 當中有相當詳細的說明, 140 | 141 | * `Configuring Socket.IO` 142 | 143 | 144 | 至於 Socket.IO 在握手(handshake)的處理的部份,在官方 wiki 也有說明, 145 | 146 | * `Authorization and handshaking` 147 | 148 | 為什麼要作上述的動作呢?顧名思義就是為了認證的一些流程而衍生出來的需求。我可以在這個過程中查詢 Session 的相關資料,也可以檢查 Cookie,IP Address 或是其他需要處理的資料等等。當然,處理 Cookie 與 Session 則最為常見。 149 | 150 | 151 | 小插曲 152 | ====== 153 | 154 | 我們在使用 Socket.IO 的時候,當然不可能將 listen 給綁在 port 80 上面,那是給一般伺服器使用的嘛。所以,我們就有可能會像上述的例子一樣,把他綁在 port 8080 或是之類的額外的連接埠上面。 155 | 156 | 問題來了,如果綁在其他的連接埠,那麼前端的呼叫的位址就得加上埠號,否則你的動作是會失效的。怎麼解決呢?網路上有一個很玄妙的解法,利用改寫 Socket.IO 的 xhr-polling 對於 XHRPolling 與 XHRPolling 的處理方式,來讓前端不需要加上埠號就能動作, 157 | 158 | .. code-block:: js 159 | 160 | io.configure(function() { 161 | io.set("transports", ["xhr-polling"]); 162 | io.set("polling duration", 10); 163 | 164 | var path = require('path'); 165 | var HTTPPolling = require(path.join( 166 | path.dirname(require.resolve('socket.io')),'lib', 'transports','http-polling') 167 | ); 168 | var XHRPolling = require(path.join( 169 | path.dirname(require.resolve('socket.io')),'lib','transports','xhr-polling') 170 | ); 171 | 172 | XHRPolling.prototype.doWrite = function(data) { 173 | HTTPPolling.prototype.doWrite.call(this); 174 | 175 | var headers = { 176 | 'Content-Type': 'text/plain; charset=UTF-8', 177 | 'Content-Length': (data && Buffer.byteLength(data)) || 0 178 | }; 179 | 180 | if (this.req.headers.origin) { 181 | headers['Access-Control-Allow-Origin'] = '*'; 182 | if (this.req.headers.cookie) { 183 | headers['Access-Control-Allow-Credentials'] = 'true'; 184 | } 185 | } 186 | 187 | this.response.writeHead(200, headers); 188 | this.response.write(data); 189 | this.log.debug(this.name + ' writing', data); 190 | }; 191 | }); 192 | 193 | 有興趣的人,原文在此,請參閱:How to make Socket.IO work behind nginx (mostly) 194 | 195 | 另外補上 Nginx 的相關設定,其實並不複雜,就依照一般的 Proxy 去設定即可, 196 | 197 | .. code-block:: js 198 | 199 | user www-data; 200 | worker_processes 4; 201 | worker_rlimit_nofile 1024; 202 | 203 | pid /var/run/nginx.pid; 204 | 205 | 206 | events { 207 | worker_connections 1024; 208 | multi_accept on; 209 | use epoll; 210 | } 211 | 212 | http { 213 | sendfile on; 214 | tcp_nopush on; 215 | tcp_nodelay on; 216 | keepalive_timeout 65; 217 | types_hash_max_size 2048; 218 | 219 | server_names_hash_bucket_size 128; 220 | server_name_in_redirect on; 221 | client_header_buffer_size 32k; 222 | large_client_header_buffers 4 32k; 223 | client_max_body_size 8m; 224 | 225 | include /etc/nginx/mime.types; 226 | default_type application/octet-stream; 227 | 228 | access_log /var/log/nginx/access.log; 229 | error_log /var/log/nginx/error.log; 230 | 231 | gzip on; 232 | gzip_vary on; 233 | gzip_proxied any; 234 | gzip_comp_level 6; 235 | gzip_buffers 16 8k; 236 | gzip_http_version 1.1; 237 | gzip_disable "MSIE [1-6].(?!.*SV1)"; 238 | gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 239 | 240 | limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s; 241 | limit_req zone=one burst=100 nodelay; 242 | 243 | upstream nodejs { 244 | ip_hash; 245 | server localhost:3000; 246 | } 247 | 248 | server { 249 | listen 80; 250 | server_name jsdc; 251 | 252 | root /var/www/mynode; 253 | index index.html index.htm; 254 | 255 | location / { 256 | proxy_set_header X-Real-IP $remote_addr; 257 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 258 | proxy_set_header Host $http_host; 259 | proxy_set_header X-NginX-Proxy true; 260 | proxy_pass http://nodejs/; 261 | proxy_redirect off; 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /zh-tw/TODO_list: -------------------------------------------------------------------------------- 1 | ************************************** 2 | 用 Express 和 MongoDB 寫一個 todo list 3 | ************************************** 4 | 5 | 練習一種語言或是 framework 最快的入門方式就是寫一個 todo list 了. 他包含了基本的 C.R.U.D. ( 新增, 讀取, 更新, 刪除 ). 這篇文章將用 node.js 裡最通用的 framework Express 架構 application 和 MongoDB 來儲存資料. 6 | 7 | 原始檔 8 | ====== 9 | 10 | Live Demo 11 | 12 | 功能 13 | ==== 14 | 15 | *無需登入, 用 cookie 來辨別每一問使用者 16 | *可以新增, 讀取, 更新, 刪除待辦事項( todo item ) 17 | 18 | 安裝 19 | ==== 20 | 開發環境 21 | 開始之前請確定你已經安裝了 node.js, Express 和 MongoDB, 如果沒有可以參考下列文章. 22 | *How to setup a node.js development environment on Mac OSX Lion 23 | *How to setup a node.js development environment on Ubuntu 11.04 24 | *How to setup a node.js development environment on Windows 25 | 26 | node.js 套件 27 | ============ 28 | 參考文件 : npm basic commands 29 | *安裝 Express 30 | :: 31 | $ npm install express@2.5.11 -g 32 | 33 | 這個練習裡我們用 Mongoose 這個 ORM. 為何會需要一個必須定義 schema 的 ORM 來操作一個 schema-less 的資料庫呢? 原因是在一般的網站資料結構的關聯, 驗證都是必須處理的問題. Mongoose 在這方面可以幫你省去很多功夫. 我們會在後面才看如何安裝. 34 | -------------------------------------------------------------------------------- /zh-tw/authorization_todo_list.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | nodeJS Code Gen Party 10 - 為 ToDo List 增加第三方認證 3 | ************ 4 | 5 | 這次 nodeJS Taiwan 社群 code gen party 10 活動,要接續之前 code gen party 8 活動 Ben 做的 ToDo List Sample,增加第三方認證 (OAuth) 功能。 6 | 7 | 這次的實作以 everyauth 為主,增加基本的使用者帳號密碼驗證功能,再擴充到使用 Facebook 的 OAuth 認證。 8 | 9 | `程式碼` | `下載` 10 | 11 | 12 | 步驟 13 | ==== 14 | 15 | 自 Github clone express-todo-example 並執行 16 | 17 | 取回程式碼 18 | :: 19 | > git clone https://github.com/dreamerslab/express-todo-example.git 20 | > cd express-todo-example 21 | 22 | 安裝相依模組 (照 package.json 定義) 23 | :: 24 | > npm install 25 | 26 | 啟動 server 27 | :: 28 | > node app.js 29 | Express server listening on port 3001 in development mode 30 | 31 | 開啟瀏覽器,確認 server 順利執行 32 | :: 33 | > open http://localhost:3001 34 | 35 | 安裝認證所需 npm modules 36 | :: 37 | > npm install everyauth 38 | 39 | 記錄安裝的 everyauth 版本號,這次剛好是 0.2.32。 40 | 41 | 修改 package.json,增加 everyauth 模組 42 | :: 43 | > vim package.json 44 | 45 | 依照安裝的 everyauth 版本,修改 package.json 46 | :: 47 | { 48 | "name" : "todo", 49 | "version" : "0.0.2", 50 | "private" : true, 51 | "dependencies" : { 52 | "connect" : "1.8.7", 53 | "express" : "2.5.9", 54 | "ejs" : ">= 0.0.1", 55 | "mongoose" : "2.6.7", 56 | "everyauth" : "0.2.32" 57 | } 58 | } 59 | 60 | 61 | 實作帳號密碼登入 62 | ================ 63 | 64 | 增加 User mongoose data model 65 | 66 | 修改 db.js 增加以下程式碼 67 | .. code-block:: js 68 | var User = new Schema({ 69 | id : { type : String, unique : true }, 70 | password : String 71 | }); 72 | 73 | mongoose.model( 'User', User ); 74 | 75 | 76 | 新增 auth.js,程式碼如下 77 | .. code-block:: js 78 | var everyauth = require('everyauth'); 79 | var mongoose = require( 'mongoose' ); 80 | var User = mongoose.model( 'User' ); 81 | 82 | everyauth.everymodule.findUserById( function (userId, callback) { 83 | User. 84 | findOne({ id : userId }). 85 | run( callback ); 86 | }); 87 | 88 | everyauth.password 89 | .getLoginPath('/login') // Login page url 90 | .postLoginPath('/login') // Url that your login form POSTs to 91 | .loginView('login') 92 | .authenticate( function (login, password) { 93 | var promise = this.Promise(); 94 | User. 95 | findOne({ id : login , password : password }). 96 | run( function ( err, user ){ 97 | if ( !user ) { 98 | err = 'Invalid login'; 99 | } 100 | 101 | if( err ) return promise.fulfill( [ err ] ); 102 | 103 | promise.fulfill( user ); 104 | }); 105 | return promise; 106 | }) 107 | .loginSuccessRedirect('/') // Where to redirect to after login 108 | .getRegisterPath('/signup') // Registration url 109 | .postRegisterPath('/signup') // Url that your registration form POSTs to 110 | .registerView('signup') 111 | .validateRegistration( function (newUser) { 112 | if (!newUser.login || !newUser.password) { 113 | return ['Either ID or Password is missing.']; 114 | } 115 | return null; 116 | }) 117 | .registerUser( function (newUser) { 118 | var promise = this.Promise(); 119 | new User({ 120 | id : newUser.login, 121 | password : newUser.password 122 | }).save( function ( err, user, count ){ 123 | if( err ) return promise.fulfill( [ err ] ); 124 | 125 | promise.fulfill( user ); 126 | }); 127 | return promise; 128 | }) 129 | .registerSuccessRedirect('/') // Url to redirect to after a successful registration 130 | .loginLocals( {title: 'Login'}) 131 | .registerLocals( {title: 'Sign up'}); 132 | 133 | module.exports = { 134 | requireLogin: function( req, res, next ) { 135 | if (!req.loggedIn) { 136 | res.redirect( '/' ); 137 | return; 138 | } 139 | next(); 140 | } 141 | }; 142 | 143 | 144 | 修改 app.js 145 | .. code-block:: js 146 | /** 147 | * Module dependencies. 148 | */ 149 | 150 | var express = require( 'express' ); 151 | var everyauth = require('everyauth'); 152 | 153 | var app = module.exports = express.createServer(); 154 | 155 | // mongoose setup 156 | require( './db' ); 157 | 158 | // autoentication setup 159 | var auth = require( './auth' ); 160 | 161 | // add everyauth view helpers to express 162 | everyauth.helpExpress( app ); 163 | 164 | var routes = require( './routes' ); 165 | 166 | // Configuration 167 | app.configure( 'development', function (){ 168 | app.set( 'views', __dirname + '/views' ); 169 | app.set( 'view engine', 'ejs' ); 170 | app.use( express.favicon()); 171 | app.use( express.static( __dirname + '/public' )); 172 | app.use( express.logger()); 173 | app.use( express.cookieParser()); 174 | app.use( express.bodyParser()); 175 | //app.use( routes.current_user ); 176 | app.use( express.session({secret: 'nodeTWParty'}) ); 177 | app.use( everyauth.middleware() ); 178 | app.use( app.router ); 179 | app.use( express.errorHandler({ dumpExceptions : true, showStack : true })); 180 | }); 181 | 182 | app.configure( 'production', function (){ 183 | app.set( 'views', __dirname + '/views' ); 184 | app.set( 'view engine', 'ejs' ); 185 | app.use( express.cookieParser()); 186 | app.use( express.bodyParser()); 187 | //app.use( routes.current_user ); 188 | app.use( express.session({secret: 'nodeTWParty'}) ); 189 | app.use( everyauth.middleware() ); 190 | app.use( app.router ); 191 | app.use( express.errorHandler()); 192 | }); 193 | 194 | // Routes 195 | app.get( '/', routes.index ); 196 | app.post( '/create', auth.requireLogin, routes.create ); 197 | app.get( '/destroy/:id', auth.requireLogin, routes.destroy ); 198 | app.get( '/edit/:id', auth.requireLogin, routes.edit ); 199 | app.post( '/update/:id', auth.requireLogin, routes.update ); 200 | 201 | app.listen( 3001, '127.0.0.1', function (){ 202 | console.log( 'Express server listening on port %d in %s mode', app.address().port, app.settings.env ); 203 | }); 204 | 205 | 206 | 207 | 修改 views/index.ejs 如下 208 | :: 209 |

<%= title %>

210 | 211 | <% if (everyauth.loggedIn) { %> 212 | 213 |
214 |
215 | Hi, <%= user.id %>. Good to see you! Log out 216 |
217 |
218 | 219 |
220 |
221 |
222 | 223 |
224 |
225 | 226 | <% todos.forEach( function ( todo ){ %> 227 |
228 | <%= todo.content %> 229 | Delete 230 |
231 | <% }); %> 232 | 233 |
234 | 235 | <% } else { %> 236 | 237 |
238 |
239 | You are not logged in.
240 | Please login or sign up. 241 |
242 |
243 | 244 | <% } %> 245 | 246 | 247 | 在 views 目錄下增加 signup.ejs 檔案 248 | :: 249 |

Sign up

250 | 251 | <% if( typeof(errors) !== 'undefined' ) { %> 252 |
253 | <%= errors %> 254 |
255 | <% } %> 256 | 257 |
258 |
259 |
260 | ID 261 |
262 |
263 | Password 264 |
265 |
266 | 267 |
268 |
269 | 270 |
271 | 272 | 273 | 在 views 目錄下增加 login.ejs 檔案 274 | :: 275 |

Login

276 | 277 | <% if( typeof(errors) !== 'undefined' ) { %> 278 |
279 | <%= errors %> 280 |
281 | <% } %> 282 | 283 |
284 |
285 |
286 | ID 287 |
288 |
289 | Password 290 |
291 |
292 | 293 |
294 |
295 | 296 |
297 | 298 | 299 | 修改 routes/index.js 檔案 300 | .. code-block:: js 301 | var mongoose = require( 'mongoose' ); 302 | var Todo = mongoose.model( 'Todo' ); 303 | var utils = require( 'connect' ).utils; 304 | var everyauth= require( 'everyauth' ); 305 | 306 | exports.index = function ( req, res, next ){ 307 | if (!req.loggedIn) { 308 | res.render( 'index', { 309 | title : 'Express Todo Example', 310 | todos : [] 311 | }); 312 | return; 313 | } 314 | ... 315 | 316 | 還要把檔案中的 req.cookies.user_id 置換成 req.user.id 317 | 到這裡就己經完成網站初步的使用者認證機制了。 318 | 319 | 320 | 建立 Facebook Application 321 | ========================= 322 | 323 | 到 https://developers.facebook.com/apps 建立一個應用程式(名稱無所謂) 324 | 325 | App Domains 跟 網站位址(URL)都填:http://local.host:3001/ 326 | 327 | 328 | 設定 local.host 網域 329 | ==================== 330 | 331 | Facebook 不接受 localhost 的網域名稱,所以為了要測試,我們改用另一個網域名稱: local.host 332 | 333 | 要能用這個測試網域名稱,在 Linux / Mac OS X 底下可以 334 | 修改 /etc/hosts 檔案來做 335 | :: 336 | > sudo vim /etc/hosts 337 | 338 | 參考下面內容修改或增加一行 339 | :: 340 | 127.0.0.1 localhost local.host 341 | 342 | 如果是 Windows,則要修改 C:\WINDOWS\system32\drivers\etc\hosts 檔案 343 | :: 344 | 127.0.0.1 local.host 345 | 346 | 347 | 實作 Facebook 登入 348 | ================== 349 | 350 | 修改 db.js 檔案 351 | .. code-block:: js 352 | // 由於 id 是 Facebook 產生的一個代碼, 353 | // 所以我們加一個欄位 name 當做使用者名稱 354 | 355 | var User = new Schema({ 356 | 357 | id : { type : String, unique : true }, 358 | 359 | name : String, 360 | 361 | profile : String, 362 | password : String 363 | 364 | }); 365 | 366 | 修改 auth.js 檔案,增加以下內容 367 | .. code-block:: js 368 | everyauth.facebook 369 | .appId('AppId') 370 | .appSecret('App Secret') 371 | .handleAuthCallbackError( function (req, res) { 372 | res.redirect('/'); 373 | }) 374 | .findOrCreateUser( function (session, accessToken, accessTokExtra, fbUserMetadata) { 375 | var promise = this.Promise(); 376 | User.findOne({ 377 | id : fbUserMetadata.id 378 | }).run( function( err, user ){ 379 | if( err ) return promise.fulfill( [ err ] ); 380 | if( user ) { 381 | promise.fulfill( user ); 382 | } else { 383 | new User({ 384 | id : fbUserMetadata.id, 385 | name : fbUserMetadata.name, 386 | profile : fbUserMetadata 387 | }).save( function ( err, user, count ){ 388 | if( err ) return promise.fulfill( [ err ] ); 389 | 390 | promise.fulfill( user ); 391 | }); 392 | } 393 | }); 394 | return promise; 395 | }) 396 | .redirectPath('/'); 397 | 398 | 修改 views/index.ejs 檔案 399 | :: 400 | <% if (everyauth.loggedIn) { %> 401 |
402 |
403 | Hi, <%= user.name || user.id %>. Good to see you! Log out 404 |
405 |
406 | ... 407 | <% } else { %> 408 | ... 409 | Login with Facebook.... 410 | 411 | <% } %> 412 | 413 | 414 | 測試 Facebook 登入 415 | ================== 416 | 417 | 重新以 http://local.host:3001/ 網址進入網站測試 418 | 419 | 420 | 後記 421 | ==== 422 | 今天 Ben 在活動結束的時候有提到 password.js。Everyauth 雖然很多人用,照著他的說明也很容易上手,但不可否認的 everyauth 與 express.js 的依賴太深,以致程式碼會有點亂。 423 | 424 | 針對這個問題 password.js 提供了一個比較乾淨的做法,可以自由的跟其他 web framework 搭配。而且由於 passport.js 切的比較乾淨,未來在增加新的認證提供者(authentication provider)時,要改的程式碼也比較少(因為大部份的認證程式碼都放在獨立的 passport.js strategy 裡了)。 425 | 426 | 建議對於認證有興趣的朋友可以再研究一下 passport.js。AiNiOOO 原來也是從 everyauth 開始,但後來就改成 passport.js 了。 427 | -------------------------------------------------------------------------------- /zh-tw/convention.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | 書籍寫作規範 3 | ********** 4 | 5 | 專有名詞 6 | ======= 7 | 8 | 技術類專有名詞依循官方常見用法。 9 | 10 | * Node.js 11 | * npm 12 | * JavaScript 13 | 14 | 標點符號 15 | ======= 16 | 17 | 以中文全形標點符號為主。 18 | 19 | * ,、。!「」() 20 | 21 | 遇英文段落(整句)採用一般(半形)標點符號。 22 | 23 | 中文與英文單字之間以一個空白字元隔開,標點符號與英文單字之間不需要空隔。 24 | 25 | 這是為了讓排版顯示的自動斷詞斷句可以正確運作,以及增加中英文混雜段落的閱讀舒適。 26 | 27 | * Node.js 是一種適合用於 Server-side 的開發框架(Framework),相當 Nice! 28 | 29 | 特殊排版說明 30 | ========== 31 | 32 | 中文同一段落為了方便編輯,可以將句子斷行;\ 33 | 但行尾必須加上半形「\」倒斜線符號。\ 34 | 因為在 HTML、EPUB 或 MOBI 格式中,\ 35 | 換行字元會被當做空白字元處理,導致增加過多影響版面美觀的空隔。 36 | 37 | 38 | 程式碼 39 | ===== 40 | 41 | 只有一行、不需要 Highlight,或是一般的 command line 操作指令說明,\ 42 | 使用 reStructuredText 標準 Code 寫法。 43 | 44 | :: 45 | 46 | This is line One 47 | This is line Two 48 | 49 | 片段程式碼(snippets),使用 inline code block,並指定 language type。 50 | 51 | 程式碼與 ``.. code-block`` 之間一定要隔一個空行,且一定要有程式碼(否則文件無法編譯)。 52 | 53 | .. code-block:: javascript 54 | 55 | if (something) { 56 | alert('test'); 57 | } 58 | 59 | .. literalinclude:: src/hello.js 60 | :language: javascript 61 | -------------------------------------------------------------------------------- /zh-tw/how-to-setup-a-node-js-development-environment-on-mac-osx-lion.rst: -------------------------------------------------------------------------------- 1 | ********************************************* 2 | 如何在 Mac OSX Lion 上設定 node.js 的開發環境 3 | ********************************************* 4 | 5 | ================================ 6 | 用 Homebrew 來安裝及更新 node.js 7 | ================================ 8 | 9 | 要在 Mac 上建立一個 node.js 的開發環境有很多方法. 你可以直接下載原始碼自己編譯, 或者是用套件管理系統來幫你解決這些瑣碎的問題. 因為 node.js 還是一個很年輕的專案, 常常會有版本的更新. 手動安裝及更新實在是非常的累人. 若是使用 Homebrew 來幫你處理這些問題可以讓你把時間花在寫程式而不是設定環境上面. 如果你是使用 Ubuntu 的話可以參考這一篇文章: 10 | http://dreamerslab.com/blog/tw/how-to-setup-a-node-js-development-environment-on-ubuntu-11-04 11 | 12 | 13 | ========== 14 | 安裝 Xcode 15 | ========== 16 | 什麼? 我只不過是想寫 server side javascript 而已為什麼要安裝 4.3 GB 的Xcode 4? 嗯… 因為你需要 gcc 來編譯 node.js 和其他的套件. 所以還是乖乖裝吧… 17 | 18 | 19 | ============= 20 | 安裝 Homebrew 21 | ============= 22 | Homebrew 是我在 Mac 上最喜歡的套件管理系統. 他就像是 Ubuntu 上的 apt-get. 我們會需要他來幫我們安裝 node.js 以及 mongoDB. 如果你還沒聽過他的話現在趕快來試試看吧! 23 | :: 24 | $ /usr/bin/ruby -e "$(/usr/bin/curl -fsSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)" 25 | 26 | 但其實用 nvm( node version management ) 來安裝 node 簡單多了, 他是一個像是 ruby rvm 的東西. 可以讓你切換 node 的版本以利在開發時切換版本. 還有 npm 在 node 0.6.3 之後已經直接包在 node 裡面不需另外安裝了. 所以基本上後面 安裝 npm 可以跳過不看. 27 | 28 | 29 | ============ 30 | 安裝 node.js 31 | ============ 32 | 用 Homebrew 來安裝 node.js 非常簡單. 只要下面兩行指令就搞定了. 33 | :: 34 | $ brew update 35 | $ brew install node 36 | 上面是舊的安裝方法, 可以不用理會. 用 nvm 安裝非常的簡單, 方法如下: 37 | :: 38 | # clone repo 39 | $ git clone git://github.com/creationix/nvm.git ~/.nvm 40 | # enable on terminal open 41 | $ echo ". ~/.nvm/nvm.sh" >> ~/.bashrc 42 | # reopen your terminal and do the following 43 | $ nvm install v0.6.6 44 | # set default node 45 | $ nvm alias default v0.6.6 46 | 47 | 48 | ======== 49 | 安裝 npm 50 | ======== 51 | npm 是 node.js 最受歡迎的套件管理系統. 就像是 ruby 的 gem 以及 php 裡的 pear. 現在上面已經有幾千個現成套件了. 包括 ORM, router, 以及第三方 api 的 wrapper 等等. 所以當你在寫新功能之前先上 npm 找找是不是已經有現成的模組可用吧. 52 | :: 53 | $ curl http://npmjs.org/install.sh | sudo sh 54 | 55 | 56 | ============ 57 | 安裝 mongodb 58 | ============ 59 | mongoDB 是我首選的 NoSQL 資料庫. 雖然他不是裡面最快的但卻是最好上手以及使用的一個. 尤其是對習慣關聯式資料庫的人來說更是如此. 但是千萬不要用設計關聯式資料庫資料結構的思維來設計你的 NoSQL 資料結構, 不然你的 node.js 程式跑起來還是快不到哪去的. 記得在安裝之後好好看一下他寫的非常詳盡的文件. 60 | :: 61 | $ brew install mongodb 62 | # create db directory 63 | $ mkdir /usr/local/db 64 | 65 | 66 | ============ 67 | 更新 node.js 68 | ============ 69 | 70 | 一樣用上面的指令就可以安裝新版本的 node 並且可以在版本中切換.. 71 | 72 | 73 | ============ 74 | 更新 mongoDB 75 | ============ 76 | 77 | 用 Homebrew 來更新 mongoDB 非常的容易. 下面兩行指令就幫你搞定了. 78 | :: 79 | $ brew update 80 | $ brew upgrade 81 | 82 | 83 | ===================================== 84 | 在其他作業系統設定 node.js 的開發環境 85 | ===================================== 86 | 如果你想要在 Windows 或是 Ubuntu 11.04 設定 node.js 的開發環境,請參考下面兩篇文章: 87 | * `如何在 Windows 上設定 node.js 的開發環境 ` 88 | * `如何在 Ubuntu 11.04 上設定 node.js 的開發環境 ` 89 | 90 | -------------------------------------------------------------------------------- /zh-tw/how-to-setup-a-node-js-development-environment-on-ubuntu-11-04: -------------------------------------------------------------------------------- 1 | ********************************************* 2 | 如何在 Ubuntu 11.04 上設定 node.js 的開發環境 3 | ********************************************* 4 | 5 | ============================= 6 | 用 nvm 安裝最新的 node.js 7 | ============================= 8 | 9 | 要在 Ubuntu 上建立一個 node.js 的開發環境有很多方法. 你可以直接下載原始碼自己編譯, 或者是用套件管理系統來幫你解決這些瑣碎的問題. 因為 node.js 還是一個很年輕的專案, 常常會有版本的更新. 手動安裝及更新實在是非常的累人. 若是使用 apt-get 來幫你處理這些問題可以讓你把時間花在寫程式而不是設定環境上面。 10 | 如果你是使用 Mac 的話可以參考這篇文章: 11 | http://dreamerslab.com/blog/tw/how-to-setup-a-node-js-development-environment-on-mac-osx-lion 12 | 13 | 另外也可以使用 nvm( node version management ) 來安裝node,會方便得多。 14 | 他是一個像是 ruby rvm 的東西. 可以讓你切換 node 的版本以利在開發時切換版本. 還有 npm 在 node 0.6.3 之後已經直接包在 node 裡面不需另外安裝了,所以基本上後面安裝 npm 可以跳過不看. 15 | 用 nvm 安裝非常的簡單, 方法如下: 16 | :: 17 | # install git 18 | $ sudo apt-get install git 19 | # clone repo 20 | $ git clone git://github.com/creationix/nvm.git ~/.nvm 21 | # enable on terminal open 22 | $ echo ". ~/.nvm/nvm.sh" >> ~/.bashrc 23 | # reopen your terminal and do the following 24 | $ nvm install v0.6.6 25 | # set default node 26 | $ nvm alias default v0.6.6 27 | 28 | (編按:因為目前在apt-get上的版本較舊,使用apt-get來安裝node也較難管理版本,故本篇只節錄nvm的安裝方法) 29 | 30 | 31 | ============ 32 | 安裝 mongodb 33 | ============ 34 | 35 | mongoDB 是我首選的 NoSQL 資料庫. 雖然他不是裡面最快的但卻是最好上手以及使用的一個. 尤其是對習慣關聯式資料庫的人來說更是如此. 但是千萬不要用設計關聯式資料庫資料結構的思維來設計你的 NoSQL 資料結構, 不然你的 node.js 程式跑起來還是快不到哪去的. 記得在安裝之後好好看一下他寫的非常詳盡的文件. 36 | 37 | :: 38 | # switch to super user 39 | $ sudo su 40 | # add 10gen GPG key 41 | $ apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10 42 | # add to source.list for using upstart to manage mongodb 43 | $ echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" >> /etc/apt/sources.list 44 | # install 45 | $ apt-get update 46 | $ apt-get install mongodb-10gen 47 | $ exit 48 | 49 | 50 | ============ 51 | 更新 node.js 52 | ============ 53 | 54 | 一樣用上面的指令就可以安裝新版本的 node 並且可以在版本中切換.. 55 | 56 | 57 | ============ 58 | 更新 mongoDB 59 | ============ 60 | 61 | 因為我們是用 apt 來安裝的, 更新 mongoDB 也是和更新其他套件一樣. 62 | 63 | :: 64 | $ sudo apt-get update 65 | $ sudo apt-get upgrade 66 | 67 | 68 | ===================================== 69 | 在其他作業系統設定 node.js 的開發環境 70 | ===================================== 71 | 72 | 如果你想要在 Windows 或是 Mac OSX Lion 設定 node.js 的開發環境,請參考下面兩篇文章: 73 | * `如何在 Windows 上設定 node.js 的開發環境 ` 74 | * `如何在 Mac OSX Lion 上設定 node.js 的開發環境 ` 75 | 76 | 77 | ============================= 78 | 開始寫你的第一個 node.js 程式 79 | ============================= 80 | 現在你已經設定好 node.js 的開發環境了. 跟著可以來看看如何使用 npm 以及一些基本的 javascript 和 node.js. 81 | -------------------------------------------------------------------------------- /zh-tw/js-OO-pattern.rst: -------------------------------------------------------------------------------- 1 | *********************** 2 | Javascript 物件導向設計 3 | *********************** 4 | 5 | 在javascript中,幾乎一切都是物件,在物件導向設計的模式下,應該會常看到這樣的狀況: 6 | 7 | .. code-block:: js 8 | 9 | var dipsy = { name: "dipsy", color: "green", sayHello: function(){ console.log("Hello!");} }; 10 | var po = { name: "po", color: "red", sayHello: function(){ console.log("Hello!");} }; 11 | var lala = { name: "lala", color: "yellow", sayHello: function(){ console.log("Hello!");} }; 12 | 13 | 這樣的方式很不經濟,這樣的設計也沒有重用性可言,這時候你應該會想到物件導向語言中常有的class。由這個class可以產出一堆長得差不多的「徒子徒孫」,也就是實例。 14 | 15 | 但是javascript中(還)沒有Class,怎麼辦呢? 16 | 這時我們就要自己動手寫一個了... 17 | 18 | 19 | javascript類別實作 20 | ================== 21 | 22 | .. code-block:: js 23 | 24 | var Person = function(name, age) { 25 | this.name = name; 26 | this.age = age; 27 | }; 28 | 29 | var p1 = new Person('Kevin',18); 30 | 31 | 剛接觸js的前端工程師或許沒見過"var p1 = new Person('Kevin',18);"這樣的物件宣告方式, 32 | 他其實就相當於以下這四行程式碼: 33 | 34 | .. code-block:: js 35 | 36 | var p1 = {}; 37 | Person.call(p1, 'Kevin', 18); // 以p1身分執行Person建構函數 38 | p1.__proto__ = Person.prototype; // 先別管,後面會提到 39 | p1.constructer = Person; // 這個物件的建構者是Person 40 | 41 | 一般在建立物件時,最常見的方法是這樣: 42 | 43 | .. code-block:: js 44 | 45 | var obj = {}; 46 | console.log(obj.constructer); // Object(){} 47 | 48 | 從上面這兩行程式碼可以得知,當我們在建立物件時,其實是由一個存在於global的Object函數來產生他的。 49 | 了解到這點以後,你會發現你可以這樣來建立物件: 50 | 51 | .. code-block:: js 52 | 53 | var obj = new Object(); 54 | 55 | 56 | 57 | 實作private attributes 58 | ====================== 59 | 60 | 在物件導向的語言中,有public和private屬性的分別,而在javascript中,我們可以利用「閉包」來製作一個外界無法直接讀取的變數,這樣的特性就如同私有變數了。 61 | 現在我們就來看看要如何實作他: 62 | 63 | .. code-block:: js 64 | 65 | var Person = function(name, age) { 66 | var name = name; // private (這句可以省略) 67 | var age = age; // private (這句可以省略) 68 | this.getName = function() { return name }; // public 69 | this.getAge = function() { return age }; // public 70 | }; 71 | 72 | var p1 = new Person('Kevin',18); 73 | 74 | console.log(p1.name); // undefined 75 | console.log(p1.age); // undefined 76 | console.log(p1.getName()); // 'Keivn' 77 | console.log(p1.getAge()); // 18 78 | 79 | 為什麼會有這樣的情況? 80 | 原來是因為javascript在進行程式的「預編譯」時,會先將靜態定義的函數給建立出來,這時函數的「視野」(scope)是基於「詞法作用域」的原則來定義。也就是說,他的地盤是在這個函數實際存在的地方,而非被呼叫的地方。 81 | 我們再看看下面這個錯誤的例子,或許你就會比較了解: 82 | 83 | .. code-block:: js 84 | 85 | var Person = function(name, age) { 86 | var name = name; // private 87 | var age = age; // private 88 | this.getName = getName; // public 89 | this.getAge = getAge; // public 90 | }; 91 | 92 | var getName = function() { return name }; 93 | var getAge = function() { return age }; 94 | 95 | var p1 = new Person('Kevin',18); 96 | 97 | console.log(p1.name); // undefined 98 | console.log(p1.age); // undefined 99 | console.log(p1.getName()); // undefined 100 | console.log(p1.getAge()); // undefined 101 | 102 | 因為function在參考變數時,只會一層一層往外找, 103 | 所以上面這段程式碼中,getName及getAge是無法往Person這個建構函數中找age、name這兩個變數的,因為以下三個情況都不成立: 104 | 105 | 1. getName及getAge所在的scope找不到age、name 106 | 2. getName及getAge所在的scope的外層中找不到age、name(在這個例子中他們已經在最外層了) 107 | 3. 找不到age、name這兩個全域變數 108 | 109 | 110 | 111 | 類別的繼承(以prototype實作) 112 | =========================== 113 | 114 | javascript是個很活的語言,在實作物件導向的「繼承」機制時,大致可以分為兩種作法,這一節講的是「以protoype來實作繼承模式」 115 | 116 | 117 | 什麼是prototype? 118 | ----------------- 119 | 120 | prototype是函數物件特有的屬性,當利用函數物件來建立一個物件(實例)時(var obj = new F()),實際上是做了以下的事情: 121 | 122 | 1. 新增一個空物件 ( var obj={} ) 123 | 2. 將空物件的__proto__指向建構式的prototype ( obj.__proto__=F.prototype ) 124 | 3. 在新物件的scope中執行建構式 ( F.apply(obj,arguments) ) 125 | 126 | 在第二個步驟中,建構式的prototype這個物件以reference的方式asign給實例的「__proto__」屬性(注意,是雙底線喔) 127 | 之後,__proto__中的所有屬性、方法,就如同這個實例原生擁有的一樣了,舉例來說: 128 | 129 | .. code-block:: js 130 | 131 | function Person(name, age) { this.name = name; this.age = age; } 132 | Person.prototype.nation = "Taiwan"; 133 | 134 | var p1 = new Person("Kevin", "18"); 135 | 136 | console.log(p1); // Person {name: "Kevin", age: "18", nation: "Taiwan"} 137 | 138 | 從上面的code中我們可以看到,雖然我們沒有為p1指定nation,但是因為p1的建構函數的prototype中有這個屬性,所以p1可以藉由__proto__來參考到他的值。 139 | 140 | Note: __proto__並不是正規的物件屬性,只是一個指標,幫助我們了解原形鏈的運作原理, 141 | 在撰寫javascript程式的時候我們並不應該直接使用他。 142 | 143 | 144 | prototype chain 145 | --------------- 146 | 147 | 延續前面的程式碼...如果我們又為p1增加一個屬性"nation"的話會發生什麼事呢? 148 | 149 | .. code-block:: js 150 | 151 | p1.nation = "USA"; 152 | 153 | console.log(p1); // Person {name: "Kevin", age: "18", nation: "USA", nation: "Taiwan"} 154 | console.log(p1.nation); // "USA" 155 | 156 | 這時你會發現p1同時擁有兩個nation的屬性,一個是來自類別(建構函數)的prototype,一個是自身擁有的屬性。 157 | 而在呼叫這個屬性時會先找原生的,如果沒有就會往prototype找,還沒有的話就會再找這個prototype物件的類別的prototype找....直到最上層為止,這個概念就是「prototype chain」。 158 | 下面這個多層繼承的範例應該能讓你更加了解prototype chain的原理: 159 | 160 | .. code-block:: js 161 | 162 | // 哺乳綱 163 | function Mammals() { this.blood = "warm"; } 164 | 165 | // 靈長目 166 | function Primate() { this.tail = true; this.skin = "hairy"; } 167 | Primate.prototype = new Mammals(); 168 | 169 | // 人科 170 | function Homo() { this.skin = "smooth"; } 171 | Homo.prototype = new Primate(); 172 | 173 | var human = new Homo(); 174 | human.name = "Kevin"; 175 | 176 | console.log(human.name); // "Kevin", from self. 177 | console.log(human.skin); // "smooth", from Homo. 178 | console.log(human.tail); // "true", from Primate. 179 | console.log(human.blood); // "warm", from Mammals. 180 | 181 | 182 | prototype設計模式的漏洞 183 | ----------------------- 184 | 185 | 相信以上的範例應該能讓你對prototype實作的繼承模式有一定的認知,但是這樣實作的繼承模式會有如下的風險: 186 | 187 | .. code-block:: js 188 | 189 | function Human() {} 190 | Human.prototype.blood = "red"; 191 | Human.prototype.body = ["foot","hand"]; 192 | 193 | var john = new Human(); 194 | var kevin = new Human(); 195 | 196 | john.blood = "purple"; //john因為不明原因突變,血變成紫色的 197 | john.body.push("wing"); //john因為不明原因突變,長出翅膀來了 198 | 199 | alert(kevin.blood); // "red" 200 | alert(john.blood); // "purple" 201 | alert(kevin.body.toString()); // "foot, hand, wing" 202 | alert(kevin.body.toString()); // "foot, hand, wing" 203 | 204 | 從上面的例子可以看到,john因為不明原因而突變了。但是在john突變之後,kevin的血雖然沒有變色,但是卻莫名其妙長出了翅膀。很明顯的,我們不小心改動到了Human的prototype。 205 | 原來在我們為john的blood指定顏色時,javascript會為john這個物件增加一個屬於自己的"blood"屬性,這種情況就跟為物件增加屬性的方式一樣。於是在後來的呼叫時,會先找到john自己的blood屬性。 206 | 但要john的body屬性執行push函式時,會發生在john中找不到body的狀況,於是就往上找到了Human.prototype的body屬性,並由他來執行push函式,此時改動到的便是Human.prototype.body了,也就連帶的影響到了kevin。 207 | 208 | 209 | 210 | 類別的繼承(借用建構式) 211 | ======================== 212 | 213 | call是函數物件特有的方法,他的用途是在指定的作用域中執行這個函數。 214 | 有些人對apply或許有印象,他們兩個基本上是一樣的東西,只是傳遞變數的方式不同,這邊我們不多做贅述。 215 | 我們直接來看看要如何用它來實作javascript的繼承模式: 216 | 217 | .. code-block:: js 218 | 219 | // 哺乳綱 220 | function Mammals() { 221 | this.blood = "warm"; 222 | } 223 | 224 | // 靈長目 225 | function Primate() { 226 | Mammals.call(this); // 記得放前面,不然會蓋掉重複的屬性 227 | this.tail = true; 228 | this.skin = "hairy"; 229 | } 230 | Primate.prototype = new Mammals(); 231 | 232 | // 人科 233 | function Homo() { 234 | Primate.call(this); // 記得放前面,不然會蓋掉重複的屬性 235 | this.skin = "smooth"; 236 | } 237 | 238 | var human = new Homo(); 239 | human.name = "Kevin"; 240 | 241 | alert(human.name); // "Kevin", from self 242 | alert(human.skin); // "smooth", from Homo 243 | alert(human.tail); // "true", from Primate 244 | alert(human.blood); // "warm", from Mammals 245 | 246 | 247 | 借用建構式的缺點 248 | ---------------- 249 | 250 | 以借用建構式的方式來實作繼承,會發生一個問題,就是父類別的prototype沒有被繼承給子類別。 251 | 這時我們可以用以下的方法來補足: 252 | 253 | function Child() { 254 | Parent.apply(this, arguments); 255 | } 256 | Child.prototype = new Parent(); 257 | 258 | 這樣的作法乍看之下很像是多此一舉,但和單純的prototype繼承比起來,這種方式在自身以及prototype中保留了來自父類別建構式的屬性,當自身的屬性被刪除時,prototype中的同名屬性也會"亮起" 259 | 260 | 261 | 實踐多繼承 262 | ========== 263 | 上面提到了兩種繼承的實作模式,而第二種以call實作的方法可以很輕鬆的達到多繼承的設計,我們來看看以下的例子: 264 | 265 | .. code-block:: js 266 | 267 | // 章魚 268 | function Octopus() { 269 | this.legs = 8; 270 | } 271 | 272 | // 小貓 273 | function Pussy() { 274 | this.speak = function () { console.log( "meow~" ); }; 275 | } 276 | 277 | // 八爪貓 278 | function Octopussy(name) { 279 | Octopus.call(this); 280 | Pussy.call(this); 281 | this.name = name; 282 | } 283 | 284 | 285 | 實踐Mixin機制 286 | ============= 287 | 288 | 在很多情況下,多重繼承的複雜性是被人詬病的(有興趣可以看看Ruby發明者寫的"松本行弘的程式世界",裡面有提到這部份) 289 | 也因為這樣,多種物件導向語言都不支援多繼承,而是改以interface或mixin的概念來實現擴充性。 290 | 這邊我們要來講講mixin。其實mixin就跟jQuery的extend概念一樣: 291 | 292 | .. code-block:: js 293 | 294 | var a = {height: 30, width: 20}, 295 | b = {long: 10}; 296 | $.extend(a,b); 297 | console.log(a);//{height: 30, width: 20, long: 10} 298 | 299 | 接下來我們就來看一下要怎麼實現mixin設計: 300 | 301 | .. code-block:: js 302 | 303 | function mixin(a,b) { 304 | for (key in b) { 305 | a[key]=b[key]; 306 | } 307 | return a; 308 | } 309 | 310 | 311 | 類別的靜態方法與屬性 312 | ==================== 313 | 314 | 在撰寫物件導向的語言時,常常會用到static的機制。 315 | 在javascript物件導向設計中的class本身就一開始就以function的形式存在,其實就是static了。 316 | 接下來我們要在這個class中增加屬於class本身的方法和屬性,即為static method、static attribute。 317 | 318 | .. code-block:: js 319 | 320 | function Human(name,sex) { 321 | this.name = name; 322 | this.sex = sex||"?"; 323 | } 324 | 325 | Human.findByName = function (name) { 326 | this.people[name]; 327 | }; 328 | 329 | Human.people = {}; 330 | 331 | Human.new = function(name, sex){ 332 | var human = new Human(name,sex); 333 | this.people[name]=human; 334 | } 335 | 336 | 337 | 實現多型 338 | ======== 339 | 340 | 在物件導向的繼承關係中,多型(polymorphism)是很常見的設計。 341 | 多型可以讓繼承自同一父類別的類別擁有相同的函數,但是可以依不同的子類別去重新定義這個函數,例如說: 342 | 343 | 哺乳類.getFoot(); // Error:"我沒有腳" 344 | 猩猩.getFoot(); // "我有兩隻腳" 345 | 狗狗.getFoot(); // "我有四隻腳" 346 | 347 | 猩猩和狗狗都是繼承自哺乳類,但是呼叫同名的"getFoot"函數時卻有不同的實作,我們來看看要怎麼實作他: 348 | 349 | .. code-block:: js 350 | 351 | // 哺乳綱 352 | function Mammals() { 353 | // constructor 354 | } 355 | 356 | Mammals.prototype.getFoot = function(){ 357 | throw new Error ("我沒有腳"); 358 | } 359 | 360 | function Chimp() { 361 | // constructor 362 | } 363 | 364 | Chimp.prototype = new Mammals(); 365 | 366 | Chimp.prototype.getFoot = function(){ 367 | console.log("我有兩隻腳"); 368 | } 369 | 370 | function Dog() { 371 | // constructor 372 | } 373 | 374 | Dog.prototype = new Mammals(); 375 | 376 | Dog.prototype.getFoot = function(){ 377 | console.log("我有四隻腳"); 378 | } 379 | 380 | -------------------------------------------------------------------------------- /zh-tw/js-function.rst: -------------------------------------------------------------------------------- 1 | 2 | Bind 3 | ==== 4 | 5 | // p143 6 | -------------------------------------------------------------------------------- /zh-tw/js-obj-literal.rst: -------------------------------------------------------------------------------- 1 | ******************* 2 | JavaScript 物件實字 3 | ******************* 4 | 5 | 在 JavaScript 最常見也最容易產生一個物件的方式就是用物件實字,但他沒有 private、protected,所以就產生了一種撰碼風格,以 _ 代表 protected,__ 代表 private,當沒有 protected 時 _ 代表 private。 6 | 7 | 這是一個很簡單的物件實字 8 | 9 | .. code-block:: js 10 | 11 | var dog = { 12 | walk: function () { 13 | }, 14 | run: function () { 15 | } 16 | }; 17 | 18 | 實作 private 19 | ============ 20 | 21 | 在物件實字的地方如果有需要用 private 屬性、方法,那就要透過 closure、立即函式。 22 | 23 | 寫法可能有許多不同的變化,但下面這個是我習慣的作法,用這種方法有幾點好處 24 | 25 | 1. 方法寫在 return 的地方也可以透過 _this 去操作物件實字 26 | 2. 在定義時可以按照一般的物件實字寫法去寫,之後再來定義哪些是可公開的方法、屬性 27 | 28 | 有好處當然也有壞處 29 | 30 | 1. 在物件實字內不能使用 this,必須用 _this,因為之後我們真正去操作的物件實字並不是 _this,而是後面回傳的物件實字。 31 | 2. 要建立兩個物件實字,好像有點麻煩... 32 | 33 | .. code-block:: js 34 | 35 | var dog = (function () { 36 | var _this = { 37 | _name: 'Dog'; 38 | getName: function () { 39 | return _this.name; 40 | }, 41 | setName: function (name) { 42 | _this.name = name; 43 | } 44 | }; 45 | return { 46 | getName: _this.getName, 47 | setName: _this.setName, 48 | reset: function () { 49 | _this.name = 'Dog'; 50 | } 51 | }; 52 | })(); 53 | 54 | 自動化 private 55 | ============== 56 | 57 | 這裡提供一個方法讓大家方便建立有 private 屬性、方法的物件實字,這裡用之前提過的 _、__ 開頭代表 private 這種程式碼撰寫習慣 58 | 59 | .. code-block:: js 60 | 61 | function objLiteral(_this) { 62 | var i, obj = {}, regex = /^_{1,2}/, tmp; 63 | 64 | function _method(method) { 65 | return function () { 66 | return method.apply(_this, arguments); 67 | }; 68 | } 69 | 70 | for (i in _this) { 71 | if (_this.hasOwnProperty(i)) { 72 | tmp = _this[i]; 73 | if (!regex.test(i)) { 74 | obj[i] = (typeof tmp === 'function' ? _method(tmp) : tmp); 75 | } 76 | } 77 | } 78 | 79 | return obj; 80 | } 81 | 82 | 來建立一個物件實字試試看 83 | 84 | .. code-block:: js 85 | 86 | var dog = objLiteral({ 87 | _name: 'Dog', 88 | getName: function () { 89 | return this._name; 90 | }, 91 | setName: function (name) { 92 | this._name = name; 93 | } 94 | }); 95 | 96 | dog 底下有 getName、setName,沒有 _name,而且 method 裡可以使用 this! 97 | -------------------------------------------------------------------------------- /zh-tw/node_application_hubot.rst: -------------------------------------------------------------------------------- 1 | ******************************* 2 | 製作一個 Hubot 的Plurk Adapter 3 | ******************************* 4 | 5 | 應用事項提醒 6 | ============ 7 | 8 | 此應用範例以 *CoffeeScript* 編寫,主要程式架構採用 Github 釋放的 Hubot 專案,以下有幾個主要注意事項,請讀者注意, 9 | 10 | * Hubot 一開始的架構,除了內建的兩個 Adapter 之外,其餘都要以 Node.js 的 Module 方式才能運作。 11 | * Hubot 的 bin/hubot 裡面寫著 npm install 所以不管你怎麼改原始碼也不能改變第一點的狀況。 12 | 13 | 其實上述都在討論同一件事情: Node.JS 的模組,而且模組不能設定相對路徑之類的來安裝,一定要包裝成 npm 相符和格式才有辦法正確執行。 14 | 15 | 建立 Adapter 16 | ============ 17 | 18 | 首先需要準備兩個檔案, 19 | 20 | * package.json 21 | * plurk.coffee 22 | 23 | 有這兩個就足以變成 Node.JS 的模組了,在開始 Coding 之前,先來設定一下 package.json 相依性, 24 | 25 | :: 26 | 27 | { 28 | "name": "hubot-plurk", 29 | "version": "0.1.1", 30 | "main": "./plurk", 31 | "dependencies": 32 | { 33 | "hubot": ">=2.0.5", 34 | "oauth": "", 35 | "cron":"" 36 | } 37 | } 38 | 39 | 這邊會使用到 Node.JS 的 cron 模組(Module),而登入 Plurk 需要 OAuth 標準授權才行,所以也需要加載 OAuth 模組。 40 | 41 | 注意:*Hubot 在讀取非內建模組時,會自動在前面加上 hubot- 的前置。* 42 | 43 | 建立 Robot 跟 API 44 | ================ 45 | 46 | 本篇應用基本上程式碼大部分參考 Hubot - Twitter Adapter 來製作,主要差異只有在使用 OAuth 這個模組,Twitter 有 Streaming 可用而 Plurk 則得用 Comet 方式來達到即時讀取。 47 | 48 | plurk.coffee 程式碼, 49 | 50 | 51 | .. code-block:: javascript 52 | 53 | Robot = require("hubot").robot() 54 | Adapter = require("hubto").adapter() 55 | 56 | EventEmitter = require("events").EventEmitter 57 | 58 | oauth = require("oauth") 59 | cronJob = require("cron").CronJob 60 | 61 | class Plurk exntends Adapter 62 | 63 | class PlurkStreaming exnteds EventEmitter 64 | 65 | 先弄個基本架構,主要 Class 皆參考 Twitter Adapter 命名方式,接著再將 Plurk 這個 Class 增加幾個 Method,基本上只要有 run, send, reply 就夠了,而 run 用來做初始化的部分。 66 | 67 | .. code-block:: javascript 68 | 69 | class Plurk entends Adapter 70 | send: (plurk_id, strings…) -> 71 | 72 | reply: (plurk_id, strings…) -> 73 | 74 | run: -> 75 | 76 | 看起來有點東西,接著來處理主要的Plurk API 結合部分, 77 | 78 | .. code-block:: javascript 79 | 80 | class PlurkStreaming extends EventEmitter 81 | 82 | constructor: (options) -> 83 | 84 | plurk: (callback) -> 85 | #觀察河道 86 | getChannel: -> 87 | #取得 Comet 網址 88 | reply: (plurk_id, message) -> 89 | #回噗 90 | acceptFriends: -> 91 | #接受好友 92 | get: (path, callback) -> 93 | #GET 請求 94 | post: (path, body, callback)-> 95 | #POST 請求(其實是裝飾) 96 | request: (method, path, body, callback)-> 97 | #主要的 OAuth 請求 98 | comet: (server, callback)-> 99 | #噗浪的 Comet 傳回是 JavaScript Callback 要另外處理後才會變成 JSON 100 | 101 | 然後把注意力集中到 constructor 上,先把建構子弄好。 102 | 103 | .. code-block:: javascript 104 | 105 | constructor: (options) -> 106 | super() 107 | if options.key? and options.secret? and options.token? and options.token_secret? 108 | @key = options.key 109 | @secret = options.secret 110 | @token = options.token 111 | @token_secret = options.token_secret 112 | #建立 OAuth 連接 113 | @consumer = new oauth.OAuth( 114 | "http://www.plurk.com/OAuth/request_token", 115 | "http://www.plurk.com/OAuth/access_token", 116 | @key, 117 | @secret, 118 | "1.0", 119 | "http://www.plurk.com/OAuth/authorize". 120 | "HMAC-SHA1" 121 | ) 122 | @domain = "www.plurk.com" 123 | #初始化取得Comet網址 124 | do @getChannel 125 | else 126 | throw new Error("參數不足,需要 Key, Secret, Token, Token Secret") 127 | 128 | 接著來處理 request 這個 method。 129 | 130 | .. code-block:: javascript 131 | 132 | request: (method, path, body, callback) -> 133 | #記錄一下這次的 Request 134 | console.log("http://#{@domain}#{path}") 135 | 136 | # Callback 這邊先不丟進去,要用另一種方式處理 137 | request = @consumer.get("http://#{@domain}#{path}", @token, @token_secret, null) 138 | 139 | request.on "response", (res) -> 140 | res.on "data", (chunk) -> 141 | parseResponse(chunk+'', callback) 142 | res.on "end", (data) -> 143 | console.log "End Request: #{path}" 144 | res.on "error", (data) -> 145 | console.log "Error: " + data 146 | 147 | request.end() 148 | 149 | #處理資料 150 | parseResponse = (data, callback) -> 151 | if data.length > 0 152 | #用 Try/Catch 避免處理 JSON 出錯導致整個中斷 153 | try 154 | callback null, JSON.parse(data) 155 | catch err 156 | console.log("Error Parse JSON:" + data, err) 157 | #繼續執行 158 | callback null, data || {} 159 | 160 | 161 | 大致上就是這樣,上面程式的架構已經將整個 Hubot Plurk Adapter 完成。因為在測試時竟然因為噗浪 Lag 而沒讀到完整的 Comet 資料,然後造成程式異常,為了避免這個問題發生,因此需要加上為了完美呈現需要再加上 Comet 的處理,所以要使用到 EventEmitter 的功能。 162 | 163 | .. code-block:: javascript 164 | 165 | comet: (server, callback) -> 166 | #在 Callback 裡面會找不到自身,所以設定區域變數 167 | self = @ 168 | 169 | #記錄一下這次的 Request 170 | console.log("[Comet] #{server}") 171 | 172 | Callback 這邊先不丟進去,要用另一種方式處理 173 | request = @consumer.get("http://#{@domain}#{path}", @token, @token_secret, null) 174 | 175 | request.on "response", (res) -> 176 | res.on "data", (chunk) -> 177 | parseResponse(chunk+'', callback) 178 | res.on "end", (data) -> 179 | console.log "End Request: #{path}" 180 | #請求結束,發出事件通知可以進行下一次請求 181 | self.emit "nextPlurk" 182 | res.on "error", (data) -> 183 | console.log "Error: " + data 184 | 185 | request.end() 186 | 187 | #處理資料 188 | parseResponse = (data, callback) -> 189 | if data.length > 0 190 | #用 try/catch 避免失敗中斷 191 | try 192 | #去掉 JavaScript 的 Callback 193 | data = data.match(/CometChannel.scriptCallback\((.+)\);\s*/) 194 | jsonData = "" 195 | 196 | if data? 197 | jsonData = JSON.parse(data[1]) 198 | else 199 | #如果沒有任何 Match 嘗試直接 parse 200 | jsonData = JSON.parse(data) 201 | catch err 202 | console.log("[Comet] Error:", data, err) 203 | 204 | #用 Try/Catch 避免處理 JSON 出錯導致整個中斷 205 | try 206 | #只傳入 json 的 data 部分 207 | callback null, jsonData.data 208 | catch err 209 | console.log("[Comet]Error Parse JSON:" + data, err) 210 | #繼續執行 211 | callback null, data || {} 212 | 213 | 214 | 後面的 get 跟 post 就簡單多了! 215 | 216 | .. code-block:: javascript 217 | 218 | get: (path, callback) -> 219 | @request("GET", path, null, callback) 220 | 221 | post: (path, body, callback) -> 222 | @request("POST", path, body, callback) 223 | 224 | 接著處理取的 Comet 網址的 getChannel 225 | 226 | .. code-block:: javascript 227 | 228 | getChannel: -> 229 | self = @ 230 | 231 | @get "/APP/Realtime/getUserChannel", (error, data) -> 232 | if !error 233 | #檢查是否有 comet server 234 | if data.comet_server? 235 | self.channel = data.comet_server 236 | #如果沒有 Channel Ready 就嘗試連接會失敗 237 | self.emit('channel_ready') 238 | 239 | 那麼,先來處理 Plurk Adaper 好處理的部份 240 | 241 | .. code-block:: javascript 242 | 243 | send: (plruk_id, strings…)-> 244 | #跟 Reply 一樣,直接交給 reply 做 245 | @reply plurk_id, strings… 246 | 247 | reply: (plurk_id, strings…) -> 248 | strings.forEach (message) => 249 | @bot.reply(plruk_id, message) 250 | 251 | 接著把 run 處理好就可以上線運作摟! 252 | 253 | .. code-block:: javascript 254 | 255 | run: -> 256 | self = @ 257 | options = 258 | key: process.env.HUBOT_PLURK_KEY 259 | secret: process.env.HUBOT_PLURK_SECRET 260 | token: process.env.HUBOT_PLURK_TOKEN 261 | token_secret: process.env.HUBOT_PLURK_TOKEN_SECRET 262 | 263 | #創建剛剛的 API 264 | bot = new PlurkStreaming(options) 265 | 266 | #依照 Twitter 的 new Robot.TextMessage 會沒有反應,所以參考 hubot-minecraft 的方式 267 | r = @robot.constructor 268 | 269 | #處理噗浪河道訊息 270 | @doPlurk = (data)-> 271 | #檢查是否為回噗 272 | if data.response? 273 | data.content_raw = data.response.content_raw 274 | data.user_id = data.response.user_id 275 | #確定有噗浪ID跟訊息 276 | if data.plurk_id? and data.content_raw 277 | self.receive new r.TextMessage(data.plurk_id, data.content_raw) 278 | 279 | #取得 Comet Server 完成,開始第一次 Comet 連接 280 | bot.on "channel_ready", () -> 281 | bot.plurk self.doPlurk 282 | 283 | #上一次 Comet 完成,繼續 Polling 284 | bot.on "nextPlurk", ()-> 285 | bot.plurk self.doPlurk 286 | 287 | #定時接受好友邀請 288 | do bot.acceptFriends 289 | 290 | @bot = bot 291 | 292 | 293 | 終於,完成 Adapter!Hubot 專案裡面的 scripts 資料夾內是互動部分,不需要像 Adapter 如此大費周章處理,只新增檔案並且設計好對白,之後就會回噗了,我開發用的機器人在此,大家可以去跟他玩玩,[http://plurk.com/elct9620_bot](http://plurk.com/elct9620_bot) 294 | 295 | 原始資料提供 296 | =========== 297 | 298 | * [製作一個 Hubot 的噗浪 Adapter](http://revo-skill.frost.tw/blog/2012/03/18/create-a-hubot-plurk-adapter/) 299 | 300 | 301 | 302 | 303 | 304 | -------------------------------------------------------------------------------- /zh-tw/node_basic.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Node.js 基礎 3 | ************ 4 | 5 | 前篇文章已經由介紹、安裝至設定都有完整介紹,nodeJS 內部除了javascript 常用的函式(function)、物件(object)之外,也有許多不同的自訂物件,nodeJS 預設建立這些物件為核心物件,是為了要讓開發流程更為,這些資料在官方文件已經具有許多具體說明。接下來將會介紹在開發nodeJS 程式時常見的物件特性與使用方法。 6 | 7 | node.js http 伺服器建立 8 | ======================= 9 | 10 | 在`node.js官方網站 `裡面有舉一個最簡單的HTTP 伺服器建立,一開始初步就是建立一個伺服器平台,讓node.js 可以與瀏覽器互相行為。每種語言一開始的程式建立都是以 Hello world 開始,最初也從Hello world 帶各位進入node.js 的世界。 11 | 12 | 輸入以下程式碼,儲存檔案為 node_basic_http_hello_world.js 13 | 14 | .. literalinclude:: ../src/node_basic_http_hello_world.js 15 | :language: javascript 16 | 17 | 程式碼解講,一開始需要有幾個基本的變數。 18 | * ip: 機器本身的ip 位置,因為使用本地端,因此設定為127.0.0.1 19 | * port: 需要開通的阜號,通常設定為http port 80,因範例不希望與基本port 相衝,隨意設定為1337 20 | 21 | 在node.js 的程式中,有許多預設的模組可以使用,因此需要使用require 方法將模組引入,在這邊我們需要使用http這個模組,因此將http載入。Http 模組裡面內建有許多方法可以使用,這邊採用createServer 創建一個基本的http 伺服器,再將http 伺服器給予一個server 變數。裡面的回呼函式(call back function)可以載入http 伺服器的資料與回應方法(request, response)。在程式裡面就可以看到我們直接回應給瀏覽器端所需的 Header,回應內容。 22 | 23 | .. code-block:: javascript 24 | 25 | res.writeHead(200, {'Content-Type': 'text/plain'}); 26 | res.end('Hello World\n'); 27 | 28 | Http 伺服器需要設定port, ip,在最後需要設定Http 監聽,需要使用到 listen 事件,監聽所有Http 伺服器行為。 29 | 30 | .. code-block:: javascript 31 | 32 | http.listen(port, ip); 33 | 34 | 所有事情都完成之後,需要確認伺服器正確執行因此使用console,在javascript 裡就有這個原生物件,console所印出的資料都會顯示於node.js 伺服器頁面,這邊印出的資料並不會傳送到使用者頁面上,之後許多除壞(debug) 都會用到console 物件。 35 | 36 | .. code-block:: javascript 37 | 38 | console.log("Server running at http://" + ip + ":" + port); 39 | 40 | node.js http 路徑建立 41 | ===================== 42 | 43 | 前面已經介紹如何建立一個簡單的http 伺服器,接下來本章節將會介紹如何處理伺服器路徑(route) 問題。 44 | 在http的協定下所有從瀏覽器發出的要求(request) 都需要經過處理,路徑上的建立也是如此。 45 | 46 | 路徑就是指伺服器 ip 位置,或者是網域名稱之後,對於伺服器給予的要求。修改剛才的hello world 檔案,修改如下。 47 | 48 | .. code-block:: javascript 49 | 50 | server = http.createServer(function (req, res) { 51 | console.log(req.url); 52 | res.writeHead(200, {'Content-Type': 'text/plain'}); 53 | res.end('hello world\n'); 54 | }); 55 | 56 | 重新啟動node.js 程式後,在瀏覽器端測試一下路徑行為,結果如下圖, 57 | 58 | .. image:: ../images/zh-tw/node_basic_rout_test.png 59 | :scale: 100% 60 | :align: center 61 | 62 | 當在瀏覽器輸入http://127.0.0.1:1337/test ,在伺服器端會收到兩個要求,一個是我們輸入的/test 要求,另外一個則是 /favicon.ico。 63 | /test 的路徑要求,http 伺服器本身需要經過程式設定才有辦法回應給瀏覽器端所需要的回應,在伺服器中所有的路徑要求都是需要被解析才有辦法取得資料。 64 | 從上面解說可以了解到在node.js 當中所有的路徑都需要經過設定,未經過設定的路由會讓瀏覽器無法取得任何資料導致錯誤頁面的發生,底下將會解說如何設定路由,同時避免發生錯誤情形。 65 | 先前node.js 程式需要增加一些修改,才能讓使用者透過瀏覽器,在不同路徑時有不同的結果。根據剛才的程式做如下的修改, 66 | 67 | .. literalinclude:: ../src/node_basic_http_rout_done.js 68 | :language: javascript 69 | 70 | 程式做了片段的修改,首先載入url 模組,另外增加一個path 變數。 71 | url 模組就跟如同他的命名一般,專門處理url 字串處理,裡面提供了許多方法來解決路徑上的問題。 72 | 因為從瀏覽器發出的要求路徑可能會帶有多種需求,或者GET 參數組合等。因此我們需要將路徑單純化,取用路徑部分的資料即可,例如使用者可能會送出 http://127.0.0.1:1337/test?send=1 ,如果直接信任**req.url** 就會收到結果為 /test?send=1 ,所以需要透過url 模組的方法將路徑資料過濾。 73 | 74 | 在這邊使用url.parse 的方法,裡面帶入網址格式資料,會回傳路徑資料。為了後需方便使用,將回傳的資料設定到path 變數當中。在回傳的路徑資料,裡面包含資訊,如下圖, 75 | 76 | .. image:: ../images/zh-tw/node_basic_rout_url.png 77 | :scale: 100% 78 | :align: center 79 | 80 | 這邊只需要使用單純的路徑要求,直接取用path.pathname ,就可以達到我們的目的。 81 | 82 | 最後要做路徑的判別,在不同的路徑可以指定不同的輸出,在範例中有三個可能結果,第一個從瀏覽器輸入/index 就會顯示 index 結果, /test 就會呈現出 test 頁面,最後如果都不符合預期的輸入會直接顯示 default 的頁面,最後的預防可以讓瀏覽器不會出現非預期結果,讓程式的可靠性提昇,底下為測試結果。 83 | 84 | .. image:: ../images/zh-tw/node_basic_rout_url_index.png 85 | :scale: 100% 86 | :align: center 87 | 88 | .. image:: ../images/zh-tw/node_basic_rout_url_test.png 89 | :scale: 100% 90 | :align: center 91 | 92 | .. image:: ../images/zh-tw/node_basic_rout_url_default.png 93 | :scale: 100% 94 | :align: center 95 | 96 | .. image:: ../images/zh-tw/node_basic_rout_url_error.png 97 | :scale: 100% 98 | :align: center 99 | 100 | node.js 檔案讀取 101 | ================ 102 | 103 | 前面已經介紹如何使用路由(rount)做出不同的回應,實際應用只有在瀏覽器只有輸出幾個文字資料總是不夠的,在本章節中將介紹如何使用檔案讀取,輸出檔案資料,讓使用者在前端瀏覽器也可以讀取到完整的html, css, javascript 檔案輸出。 104 | 105 | 檔案管理最重要的部分就是`File system ` 這個模組,此模組可以針對檔案做管理、監控、讀取等行為,裡面有許多預設的方法,底下是檔案輸出的基本範例,底下會有兩個檔案,第一個是靜態html 檔案, 106 | 107 | .. literalinclude:: ../src/static/index.html 108 | :language: javascript 109 | 110 | 另一個為node.js 程式, 111 | 112 | .. literalinclude:: ../src/node_basic_file_simple.js 113 | :language: javascript 114 | 115 | 116 | 一開始直接載入**file system 模組**,載入名稱為 **fs** 。讀取檔案主要使用的方法為readFile ,裡面以三個參數 **路徑(file path)** , **編碼方式(encoding)** , **回應函式(callback)** ,路徑必須要設定為靜態html 所在位置,才能指定到正確的檔案。靜態檔案的編碼方式也必須正確,這邊使用靜態檔案的編碼為 **utf8** ,如果編碼設定錯誤,node.js 讀取出來檔案結果會使用 byte raw 格式輸出,如果 **錯誤編碼格式,會導致輸出資料為 byte raw** 117 | 118 | .. image:: ../images/zh-tw/node_basic_file_byte.png 119 | :scale: 100% 120 | :align: center 121 | 122 | **回應函式** 中裡面會使用兩個變數,error 為錯誤資訊,如果讀取的檔案不存在,或者發生錯誤,error 數值會是 true ,如果成功讀取資料 error 將會是 false 。 content 則是檔案內容,資料讀取後將會把資料全數丟到content 這個變數當中。 123 | 124 | 最後程式的輸出結果畫面如下, 125 | 126 | .. image:: ../images/zh-tw/node_basic_file_read.png 127 | :scale: 100% 128 | :align: center 129 | 130 | 131 | node.js http 靜態檔案輸出 132 | ======================== 133 | 134 | 前面已經了解如何讀取本地端檔案,接下來將配合http 伺服器路由,讓每個路由都能夠輸出相對應的靜態 html 檔案。 135 | 首先新增加幾個靜態html 檔案, 136 | 137 | .. literalinclude:: ../src/static/index.html 138 | :language: javascript 139 | 140 | 141 | .. literalinclude:: ../src/static/test.html 142 | :language: javascript 143 | 144 | 145 | .. literalinclude:: ../src/static/static.html 146 | :language: javascript 147 | 148 | 準備一個包含基本路由功能的http 伺服器 149 | 150 | .. code-block:: javascript 151 | 152 | var server, 153 | ip = "127.0.0.1", 154 | port = 1337, 155 | http = require('http'), 156 | url = require('url'); 157 | 158 | server = http.createServer(function (req, res) { 159 | var path = url.parse(req.url); 160 | }); 161 | 162 | server.listen(port, ip); 163 | 164 | console.log("Server running at http://" + ip + ":" + port); 165 | 166 | 加入 **file system** 模組, 使用 **readFile** 的功能,將這一段程式放置於createServer 的回應函式中。 167 | 168 | .. code-block:: javascript 169 | 170 | fs.readFile(filePath, encode, function(err, file) { 171 | }); 172 | 173 | readFile 的回應函式裡面加入頁面輸出,讓瀏覽器可以正確讀到檔案,在這邊我們設定讀取的檔案為 html 靜態檔案,所以 Content-type 設定為 **text/html** 。讀取到檔案的內容,將會正確輸出成 html 靜態檔案。 174 | 175 | .. code-block:: javascript 176 | 177 | fs.readFile(filePath, encode, function(err, file) { 178 | res.writeHead(200, {'Content-Type': 'text/html'}); 179 | res.write(file); 180 | res.end(); 181 | }); 182 | 183 | 到這邊為止基本的程式內容都已經完成,剩下一些細節的調整。首先路徑上必須做調整,目前的靜態檔案全部都放置於 **static 資料夾** 底下,設定一個變數來記住資料夾位置。 184 | 185 | 接著將瀏覽器發出要求路徑與資料夾組合,讀取正確html 靜態檔案。使用者有可能會輸入錯誤路徑,所以在讀取檔案的時候要加入錯誤處理,同時回應 **404** 伺服器無法正確回應的 http header 格式。 186 | 187 | 加入這些細節的修改,一個基本的http 靜態 html 輸出伺服器就完成了,完整程式碼如下, 188 | 189 | .. literalinclude:: ../src/node_basic_file_http_static.js 190 | :language: javascript 191 | 192 | 193 | node.js http GET 資料擷取 194 | ======================== 195 | 196 | http 伺服器中,除了路由之外另一個最常使用的方法就是擷取GET 資料。本單元將會介紹如何透過基本http 伺服器擷取瀏覽器傳來的要求,擷取GET 資料。 197 | 198 | 在http 協定中,GET 參數都是藉由URL 從瀏覽器發出要求送至伺服器端,基本的傳送網址格式可能如下, 199 | 200 | :: 201 | 202 | http://127.0.0.1/test?send=1&test=2 203 | 204 | 上面這段網址,裡面的GET 參數就是 send 而這個變數的數值就為 1,如果想要在http 伺服器取得GET 資料,需要在瀏覽器給予的要求(request)做處理, 205 | 206 | 首先需要載入 **query string** 這個模組,這個模組主要是用來將字串資料過濾後,轉換成 **javascript 物件**。 207 | 208 | 209 | .. code-block:: javascript 210 | 211 | qs = require('querystring'); 212 | 213 | 接著在第一階段,利用url 模組過濾瀏覽器發出的URL 資料後,將回應的物件裡面的 query 這個變數,是一個字串值,資料過濾後如下, 214 | 215 | .. 216 | 217 | send=1&test=2 218 | 219 | 透過 query string ,使用parse 這個方法將資料轉換成javascript 物件,就表示 GET 的資料已經被伺服器端正式擷取下來, 220 | 221 | .. code-block:: javascript 222 | 223 | path = url.parse(req.url); 224 | parameter = qs.parse(path.query); 225 | 226 | 整個node.js http GET 參數完整擷取程式碼如下, 227 | 228 | .. literalinclude:: ../src/node_basic_http_get.js 229 | :language: javascript 230 | 231 | 程式運作之後,由瀏覽器輸入要求網址之後,node.js 伺服器端回應資料為, 232 | 233 | :: 234 | 235 | Server running at http://127.0.0.1:1337 236 | { send: '1', test: '1' } 237 | 238 | 239 | 240 | 本章結語 241 | ======= 242 | 243 | 前面所解說的部份,一大部分主要是處理 http 伺服器基本問題,雖然在某些部分有牽扯到http 伺服器基本運作原理,主要還是希望可以藉由這些基本範例練習node.js ,練習回應函式與語法串接的特點,習慣編寫javascript 風格程式。當然直接這樣開發node.js 是非常辛苦的,接下來在模組實戰開發的部份將會介紹特定的模組,一步一步帶領各位從無到有進行node.js 應用程式開發。 244 | 245 | -------------------------------------------------------------------------------- /zh-tw/node_coffeescript.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | CoffeeScript 3 | ************ 4 | 5 | * Spine 6 | * Hem 7 | * Spine.app 8 | 9 | Let's Have a Cup of CoffeeScript 10 | 11 | * `es5-shim `_ ECMAScript 5 compatibility shims for legacy JavaScript engines 12 | 13 | ES.next 14 | 15 | node-iform - A middleware for node-validator 16 | https://github.com/guileen/node-iform 17 | 18 | 19 | -------------------------------------------------------------------------------- /zh-tw/node_express.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Express 介紹 3 | ************ 4 | 5 | 在前面的node.js 基礎當中介紹許多許多開設http 的使用方法及介紹,以及許多基本的node.js 基本應用。 6 | 7 | 接下來要介紹一個套件稱為express [Express](http://expressjs.com/) ,這個套件主要幫忙解決許多node.js http server 所需要的基本服務,讓開發http service 變得更為容易,不需要像之前需要透過層層模組(module)才有辦法開始編寫自己的程式。 8 | 9 | 這個套件是由TJ Holowaychuk 製作而成的套件,裡面包含基本的路由處理(route),http 資料處理(GET/POST/PUT),另外還與樣板套件(js html template engine)搭配,同時也可以處理許多複雜化的問題。 10 | 11 | Express 安裝 12 | ============ 13 | 14 | 安裝方式十分簡單,只要透過之前介紹的 NPM 就可以使用簡單的指令安裝,指令如下, 15 | 16 | .. code-block:: 17 | 18 | npm install -g express 19 | 20 | 這邊建議需要將此套件安裝成為全域模組,方便日後使用。 21 | 22 | Express 基本操作 23 | ================ 24 | 25 | express 的使用也十分簡單,先來建立一個基本的hello world , 26 | 27 | .. code-block:: javascript 28 | 29 | var app = require('express').createServer(), 30 | port = 1337; 31 | 32 | app.listen(port); 33 | 34 | app.get('/', function(req, res){ 35 | res.send('hello world'); 36 | }); 37 | 38 | console.log('start express server\n'); 39 | 40 | 可以從上面的程式碼發現,基本操作與node.js http 的建立方式沒有太大差異,主要差在當我們設定路由時,可以直接透過 app.get 方式設定回應與接受方式。 41 | 42 | Express 路由處理 43 | ================ 44 | 45 | Express 對於 http 服務上有許多包裝,讓開發者使用及設定上更為方便,例如有幾個路由設定,那我們就統一藉由 app.get 來處理, 46 | 47 | .. code-block:: javascript 48 | 49 | // ... Create http server 50 | 51 | app.get('/', function(req, res){ 52 | res.send('hello world'); 53 | }); 54 | 55 | app.get('/test', function(req, res){ 56 | res.send('test render'); 57 | }); 58 | 59 | app.get('/user/', function(req, res){ 60 | res.send('user page'); 61 | }); 62 | 63 | 如上面的程式碼所表示,app.get 可以帶入兩個參數,第一個是路徑名稱設定,第二個為回應函式(call back function),回應函式裡面就如同之前的 createServer 方法,裡面包含 request, response 兩個物件可供使用。使用者就可以透過瀏覽器,輸入不同的url 切換到不同的頁面,顯示不同的結果。 64 | 65 | 路由設定上也有基本的配對方式,讓使用者從瀏覽器輸入的網址可以是一個變數,只要符合型態就可以有對應的頁面產出,例如, 66 | 67 | .. code-block:: javascript 68 | 69 | // ... Create http server 70 | 71 | app.get('/user/:id', function(req, res){ 72 | res.send('user: ' + req.params.id); 73 | }); 74 | 75 | app.get('/:number', function(req, res){ 76 | res.send('number: ' + req.params.number); 77 | }); 78 | 79 | 80 | 裡面使用到:number ,從網址輸入之後就可以直接使用 req.params.number 取得所輸入的資料,變成url 參數使用,當然前面也是可以加上路徑的設定, /user/:id,在瀏覽器上路徑必須符合 /user/xxx,透過 req.params.id 就可以取到 xxx這個字串值。 81 | 82 | 另外,express 參數處理也提供了路由參數配對處理,也可以透過正規表示法作為參數設定, 83 | 84 | .. code-block:: javascript 85 | 86 | var app = require('express').createServer(), 87 | port = 1337; 88 | 89 | app.listen(port); 90 | 91 | app.get(/^\/ip?(?:\/(\d{2,3})(?:\.(\d{2,3}))(?:\.(\d{2,3}))(?:\.(\d{2,3})))?/, function(req, res){ 92 | res.send(req.params); 93 | }); 94 | 95 | 上面程式碼,可以發現後面路由設定的型態是正規表示法,裡面設定格式為 /ip 之後,必須要加上ip 型態才會符合資料格式,同時取得ip資料已經由正規表示法將資料做分群,因此可以取得ip的四個數字。 96 | 97 | 此程式執行之後,可以透過瀏覽器測試,輸入網址為 localhost:3000/ip/255.255.100.10,可以從頁面獲得資料, 98 | 99 | .. code-block:: 100 | 101 | [ 102 | "255", 103 | "255", 104 | "100", 105 | "10" 106 | ] 107 | 108 | 此章節全部範例程式碼如下, 109 | 110 | .. literalinclude:: ../src/node_express_basic.js 111 | :language: javascript 112 | 113 | 114 | Express middleware 115 | ================== 116 | 117 | Express 裡面有一個十分好用的應用概念稱為middleware,可以透過 middleware 做出複雜的效果,同時上面也有介紹 next 方法參數傳遞,就是靠 middleware 的概念來傳遞參數,讓開發者可以明確的控制程式邏輯。 118 | 119 | .. code-block:: javascript 120 | 121 | // .. create http server 122 | app.use(express.bodyParser()); 123 | app.use(express.methodOverride()); 124 | app.use(express.session()); 125 | 126 | 上面都是一種 middleware 的使用方式,透過 app.use 方式裡面載入函式執行方法,回應函式會包含三個基本參數,response, request, next,其中next 表示下一個 middleware 執行函式,同時會自動將預設三個參數繼續帶往下個函式執行,底下有個實驗, 127 | 128 | .. literalinclude:: ../src/node_express_middle_simple.js 129 | :language: javascript 130 | 131 | 上面的片段程式執行後,開啟瀏覽器,連結上 localhost:1337/,會發現伺服器回應結果順序如下, 132 | 133 | :: 134 | 135 | first middle ware 136 | second middle ware 137 | execute middle ware 138 | end middleware function 139 | 140 | 從上面的結果可以得知,剛才設定的 middleware 都生效了,在 app.use 設定的 middleware 是所有url 皆會執行方法,如果有指定特定方法,就可以使用 app.get 的 middleware 設定,在 app.get 函式的第二個參數,就可以帶入函式,或者是匿名函式,只要函式裡面最後會接受 request, response, next 這三個參數,同時也有正確指定 next 函式的執行時機,最後都會執行到最後一個方法,當然開發者也可以評估程式邏輯要執行到哪一個階段,讓邏輯可以更為分明。 141 | 142 | Express 路由應用 143 | ================ 144 | 145 | 在實際開發上可能會遇到需要使用參數等方式,混和變數一起使用,express 裡面提供了一個很棒的處理方法 app.all 這個方式,可以先採用基本路由配對,再將設定為每個不同的處理方式,開發者可以透過這個方式簡化自己的程式邏輯, 146 | 147 | .. literalinclude:: ../src/node_express_basic_app.js 148 | :language: javascript 149 | 150 | 內部宣告一組預設的使用者分別給予名稱設定,藉由app.all 這個方法,可以先將路由雛形建立,再接下來設定 app.get 的路徑格式,只要符合格式就會分配進入對應的方法中,像上面的程式當中,如果使用者輸入路徑為 /user/0 ,除了執行 app.all 程式之後,執行next 方法就會對應到路徑設定為 /user/:id 的這個方法當中。如果使用者輸入路徑為 /user/0/edit ,就會執行到 /user/:id/edit 的對應方法。 151 | 152 | Express GET 應用範例 153 | ==================== 154 | 155 | 我們準備一個使用GET方法傳送資料的表單。 156 | 157 | .. literalinclude:: ../src/view/express_get_example_form.html 158 | :language: javascript 159 | 160 | 這個表單沒有什麼特別的地方,我們只需要看第9行,form使用的method是GET,然後action是"http://localhost:3000/Signup",等一下我們要來撰寫/Signup這個URL Path的處理程式。 161 | 162 | *處理 Signup 行為* 163 | 164 | 我們知道所謂的GET方法,會透過URL來把表單的值給帶過去,以上面的表單來說,到時候URL會以這樣的形式傳遞 165 | 166 | :: 167 | 168 | http://localhost:3000/Signup?username=xxx&email=xxx 169 | 170 | 所以要能處理這樣的資料,必須有以下功能: 171 | 172 | * 解析URL 173 | * 辨別動作是Signup 174 | * 解析出username和email 175 | 176 | 一旦能取得username和email的值,程式就能加以應用了。 177 | 178 | 處理 Signup 的程式碼雛形, 179 | 180 | .. code-block:: javascript 181 | 182 | // load module 183 | var url = require('url'); 184 | 185 | urlData = url.parse(req.url,true); 186 | action = urlData.pathname; 187 | res.writeHead(200, {"Content-Type":"text/html; charset=utf-8"}); 188 | 189 | if (action === "/Signup") { 190 | user = urlData.query; 191 | res.end("

" + user.username + "歡迎您的加入

我們已經將會員啟用信寄至" + user.email + "

"); 192 | } 193 | 194 | 首先需要加載 url module,它是用來協助我們解析URL的模組,接著使用 url.parse 方法,第一個傳入url 字串變數,也就是req.url。另外第二個參數的用意是,設為ture則引進 querystring模組來協助處理,預設是false。它影響到的是 urlData.query,設為true會傳回物件,不然就只是一般的字串。url.parse 會將字串內容整理成一個物件,我們把它指定給urlData。 195 | 196 | action 變數作為記錄pathname,這是我們稍後要來判斷目前網頁的動作是什麼。接著先將 html 表頭資訊 (Header)準備好,再來判斷路徑邏輯,如果是 */Signup* 這個動作,就把urlData.query裡的資料指定給user,然後輸出user.username和user.email,把使用者從表單註冊的資料顯示於頁面中。 197 | 198 | 最後進行程式測試,啟動 node.js 主程式之後,開啟瀏覽器就會看到表單,填寫完畢按下送出,就可以看到結果了。 199 | 200 | 完整 node.js 程式碼如下, 201 | 202 | .. literalinclude:: ../src/node_express_get_form.js 203 | :language: javascript 204 | 205 | 206 | Express POST 應用範例 207 | ===================== 208 | 209 | 一開始準備基本的 html 表單,傳送內容以 POST 方式, form 的 action 屬性設定為 POST,其餘 html 內容與前一個範例應用相同, 210 | 211 | .. literalinclude:: ../src/view/express_post_example_form.html 212 | :language: javascript 213 | 214 | node.js 的程式處理邏輯與前面 GET 範例類似,部分程式碼如下, 215 | 216 | .. code-block:: javascript 217 | 218 | qs = require('querystring'), 219 | 220 | if (action === "/Signup") { 221 | formData = ''; 222 | req.on("data", function (data) { 223 | 224 | formData += data; 225 | 226 | }); 227 | 228 | req.on("end", function () { 229 | user = qs.parse(formData); 230 | res.end("

" + user.username + "歡迎您的加入

我們已經將會員啟用信寄至" + user.email + "

"); 231 | }); 232 | } 233 | 234 | 主要加入了'querystring' 這個moduel,方便我們等一下解析由表單POST回來的資料,另外加入一個formData的變數,用來搜集待等一下表單回傳的資料。前面的GET 範例,我們只從req 拿出url的資料,這次要在利用 req 身上的事件處理。 235 | 236 | JavaScript在訂閱事件時使用addEventListener,而node.js使用的則是on。這邊加上了監聽 *data* 的事件,會在瀏覽器傳送資料到 Web Server時被執行,參數是它所接收到的資料,型態是字串。 237 | 238 | 接著再增加 *end* 的事件,當瀏覽器的請求事件結束時,它就會動作。 239 | 240 | 由於瀏覽器使用POST在上傳資料時,會將資料一塊塊地上傳,因為我們在監聽data事件時,透過formData 變數將它累加起來< 241 | 不過由於我們上傳的資料很少,一次就結束,不過如果日後需要傳的是資料比較大的檔案,這個累加動作就很重要。 242 | 243 | 當資料傳完,就進到end事件中,會用到 qs.parse來解析formData。formData的內容是字串,內容是: 244 | 245 | :: 246 | 247 | username=wordsmith&email=wordsmith%40some.where 248 | 249 | 而qs.parse可以幫我們把這個querystring轉成物件的格式,也就是: 250 | 251 | :: 252 | 253 | {username=wordsmith&email=wordsmith%40some.where} 254 | 255 | 一旦轉成物件並指定給user之後,其他的事情就和GET方法時操作的一樣,寫response的表頭,將內容回傳,並將user.username和user.email代入到內容中。 256 | 257 | 修改完成後,接著執行 node.js 程式,啟動 web server ,開啟瀏覽器進入表單測試看看,POST 的方式能否順利運作。 258 | 259 | 完整程式碼如下, 260 | 261 | .. literalinclude:: ../src/node_express_post_form.js 262 | :language: javascript 263 | 264 | Express AJAX 應用範例 265 | ===================== 266 | 267 | 在Node.js要使用Ajax傳送資料,並且與之互動,在接受資料的部份沒有太大的差別,client端不是用GET就是用POST來傳資料,重點在處理完後,用JSON格式回傳。當然Ajax不見得只傳JSON格式,有時是回傳一段HTML碼,不過後者對伺服器來說,基本上就和前兩篇沒有差別了。所以我們還是以回傳JSON做為這一回的主題。 268 | 269 | 這一回其實大多數的工作都會落在前端Ajax上面,前端要負責發送與接收資料,並在接收資料後,撤掉原先發送資料的表單,並將取得的資料,改成HTML格式之後,放上頁面。 270 | 271 | 首先先準備 HTML 靜態頁面資料, 272 | 273 | .. literalinclude:: ../src/view/express_ajax_example_form.html 274 | :language: javascript 275 | 276 | HTML 頁面上準備了一個表單,用來傳送註冊資料。接著直接引用了 Google CDN 來載入 jQuery,用來幫我們處理 Ajax 的工作,這次要傳送和接收的工作,很大的變動都在 HTML 頁面上的 JavaScript當中。我們要做的事有(相關 jQuery 處理這邊不多做贅述,指提起主要功能解說): 277 | 278 | * 用jQUery取得submit按鈕,綁定它的click動作 279 | * 取得表單username和email的值,存放在user這個物件中 280 | * 用jQuery的$.post方法,將user的資料傳到Server 281 | * 一旦成功取得資料後,透過greet這個function,組成回報給user的訊息 282 | * 清空原本給使用者填資料的表單 283 | * 將Server回傳的username、email和id這3個資料,組成回應的訊息 284 | * 將訊息放到原本表單的位置 285 | 286 | 經過以上的處理後,一個Ajax的表單的基本功能已經完成。 287 | 288 | 接著進行 node.js 主要程式的編輯,部分程式碼如下, 289 | 290 | .. code-block:: javascript 291 | 292 | var fs = require("fs"), 293 | qs = require('querystring'); 294 | 295 | if (action === "/Signup") { 296 | formData = ''; 297 | req.on("data", function (data) { 298 | 299 | formData += data; 300 | 301 | }); 302 | 303 | req.on("end", function () { 304 | var msg; 305 | 306 | user = qs.parse(formData); 307 | user.id = "123456"; 308 | msg = JSON.stringify(user); 309 | res.writeHead(200, {"Content-Type":"application/json; charset=utf-8","Content-Length":msg.length}); 310 | res.end(msg); 311 | }); 312 | } 313 | 314 | 這裡的程式和前面 POST 範例,基本上大同小異,差別在: 315 | 316 | * 幫user的資料加上id,隨意存放一些文字進去,讓Server回傳的資料多於Client端傳上來的,不然會覺得Server都沒做事。 317 | * 增加了msg這個變數,存放將user物件JSON文字化的結果。JSON.stringify這個轉換函式是V8引擎所提供的,如果你好奇的話。 318 | * 大重點來了,我們要告訴Client端,這次回傳的資料格式是JSON,所在Content-type和Content-Length要提供給Client。 319 | 320 | Server很輕鬆就完成任務了,最後進行程式測試,啟動 node.js 主程式之後,開啟瀏覽器就會看到表單,填寫完畢按下送出,就可以看到結果了。 321 | 322 | 最後 node.js 本篇範例程式碼如下, 323 | 324 | .. literalinclude:: ../src/node_express_ajax_form.js 325 | :language: javascript 326 | 327 | 原始資料提供 328 | ============ 329 | 330 | * [Node.JS初學者筆記(1)-用GET傳送資料] (http://ithelp.ithome.com.tw/question/10087402) 331 | * [Node.JS初學者筆記(2)-用POST傳送資料] (http://ithelp.ithome.com.tw/question/10087489) 332 | * [Node.JS初學者筆記(3)-用Ajax傳送資料] (http://ithelp.ithome.com.tw/question/10087627) 333 | 334 | -------------------------------------------------------------------------------- /zh-tw/node_install.rst: -------------------------------------------------------------------------------- 1 | ****************** 2 | Node.js 安裝與設定 3 | ****************** 4 | 5 | 本篇將講解如何在各個不同OS建立NodeJS 環境,目前NodeJS 0.4.8版本環境架設方式需依賴Linux指令才可編譯完成,當然在不同作業系統中也已經有NodeJS package,可以直接使用指令快速架設。以下各不同作業系統解說如何安裝NodeJS。 6 | 7 | Ubuntu Linux 8 | ============ 9 | 10 | 更新:推薦使用 nvm。 11 | 12 | :: 13 | 14 | git clone git://github.com/creationix/nvm.git ~/.nvm 15 | echo ". ~/.nvm/nvm.sh" >> ~/.bashrc 16 | nvm install v0.6.14 17 | nvm alias default v0.6.14 18 | 19 | 以上可參考: http://dreamerslab.com/blog/tw/how-to-setup-a-node-js-development-environment-on-ubuntu-11-04/ 20 | 21 | 使用 APT 套件管理工具是常見的方法,以下是使用社群提供的 PPA 安裝方式。 22 | 23 | :: 24 | 25 | sudo apt-get install python-software-properties 26 | sudo add-apt-repository ppa:chris-lea/node.js-devel 27 | sudo apt-get update 28 | sudo apt-get install nodejs 29 | 30 | 31 | Other Linux 32 | =========== 33 | 34 | Linux 很適合作為 NodeJS 的伺服器作業系統及開發環境。安裝前,請先確認以下套件已正確安裝。 35 | 36 | * curl (wget) 用來下載檔案的工具 37 | * git 先進的版本控制工具 38 | * g++ GNU C++ 軟體編譯工具 39 | * make GNU 軟體專案建置工具 40 | 41 | 安裝指令如下,如設有權限問題,請在指令前面加上sudo 42 | 43 | .. code-block:: javascript 44 | 45 | git clone https://github.com/joyent/node.git 46 | cd node 47 | git checkout v0.6.7 48 | ./configure 49 | make 50 | sudo make install 51 | 52 | 接著測試nodeJS 是否正常執行 53 | 54 | .. code-block:: javascript 55 | 56 | node --version 57 | 58 | 出現版本訊息即表示安裝成功。 59 | 60 | .. image:: ../images/zh-tw/node_install_linux_node_test.jpg 61 | :scale: 100% 62 | :align: center 63 | 64 | Windows 65 | ======= 66 | 67 | nodeJS 在v0.6.0版本之後開始正式支援windows native,直接使用node.exe 就可以執行程式,支援性完全與linux 相同,更棒的部份就是不需經過編譯,經過下載之後,簡單設定完成,立即開發node 程式。 68 | 69 | `下載node.js 安裝檔案 ` 70 | 71 | 如此完成windows native node.exe 安裝,接著可以進入command line 執行測試。在command line 輸指令如下 72 | 73 | .. code-block:: javascript 74 | 75 | node -v 76 | 77 | 接著出現node.js 版本訊息畫面,表示安裝完成。 78 | 79 | .. image:: ../images/zh-tw/node_install_cmd.jpg 80 | :scale: 100% 81 | :align: center 82 | 83 | .. image:: ../images/zh-tw/node_install_node_test.jpg 84 | :scale: 100% 85 | :align: center 86 | -------------------------------------------------------------------------------- /zh-tw/node_introduce.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Node.js 簡介 3 | ************ 4 | 5 | Node.js 是一個高效能、易擴充的網站應用程式開發框架 (Web Application Framework)。它誕生的原因,是為了讓開發者能夠更容易開發高延展性的網路服務,不需要經過太多複雜的調校、效能調整及程式修改,就能滿足網路服務在不同發展階段對效能的要求。 6 | 7 | Ryan Dahl 是 Node.js 的催生者,目前任職於 Joyent 主機託管服務公司。他開發 Node.js 的目的,就是希望能解決 Apache 在連線數量過高時,緩衝區 (buffer) 和系統資源會很快被耗盡的問題,希望能建立一個新的開發框架以解決這個問題。因此嘗試使用效能十分優秀的 V8 JavaScript Engine,讓網站開發人員使用熟悉的 JavaScript 語言,也能應用於後端服務程式的開發,並且具有出色的執行效能。 8 | 9 | JavaScript 是功能強大的物件導向程式語言,但是在 JavaScript 的官方規格中,主要是定義網頁 (以瀏覽器為基礎) 應用程式需要的應用程式介面 (API),對應用範圍有所侷限。為使 JavaScript 能夠在更多用途發展, CommonJS 規範一組標準函式庫 (standard library),使 JavaScript 的應用範圍能夠和 Ruby 、 Python 及 Java 等語言同樣豐富,並且能在不同的 CommonJS 兼容 (compliant) JavaScript 執行環境中,使程式碼具有可攜性。 10 | 11 | 瀏覽器的 JavaScript 與實現 CommonJS 規範的 Node.js 有何不同呢?瀏覽器的 JavaScript 提供 XMLHttpRequest ,讓程式可以和網頁伺服器建立資料傳輸連線,但這通常只能適用於網站開發的需求,因為我們只能用 XMLHttpRequest 與網頁伺服器通訊,卻無法利用它建立其他類型如 Telnet / FTP / NTP 的伺服器通訊。如果我們想開發網路服務程式,例如 SMTP 電子郵件伺服器,就必須使用 Sockets 建立 TCP (某些服務則用 UDP) 監聽及連線,其他程式語言如 PHP 、 Java 、 Python 、 Perl 及 Ruby 等,在標準開發環境中皆有提供 Sockets API ,而瀏覽器的 JavaScript 基於安全及貼近網站設計需求的考量下,並未將 Sockets 列入標準函式庫之中。而 CommonJS 的規範就填補了這種基礎函式庫功能的空缺,遵循 CommonJS 規範的 Node.js 可以直接使用 Sockets API 建立各種網路服務程式,也能夠讓更多同好基於 JavaScript 開發符合 Node.js 的外掛模組 (Module)。 12 | 13 | 開發人員所編寫出來的 Javascript 腳本程式,怎麼可能會比其他語言寫出來的網路程式還要快上許多呢?以前的網路程式原理是將使用者每次的連線 (connection) 都開啟一個執行緒 (thread),當連線爆增的時候將會快速耗盡系統效能,並且容易產生阻塞 (block)。 14 | 15 | Node.js 對於資源的調配有所不同,當程式接收到一筆連線 (connection),會通知作業系統透過epoll, kqueue, /dev/poll或select將連線保留,並且放入heap中配置,先讓連線進入休眠 (sleep) 狀態,當系統通知時才會觸發連線的 callback。這種處理連線方式只會佔用掉記憶體,並不會使用到CPU資源。另外因為採用 JavaScript 語言的特性,每個 request 都會有一個 callback,如此可以避免發生 block。 16 | 17 | 基於 callback 特性,目前 Node.js 大多應用於 Comet(long pulling) Request Server,或者是高連線數量的網路服務上,目前也有許多公司將 Node.js 設為內部核心網路服務之一。在 Node.js 也提供了外掛管理 (Node package management),讓愛好 Node.js 輕易開發更多有趣的服務、外掛,並且提供到 npm 讓全世界使用者快速安裝使用。 18 | 19 | 本書最後執行測試版本為node.js v0.6.8,相關 API 文件可查詢`http://nodejs.org ` 20 | 本書所有範例均可於 Linux, Windows 上執行,如遇到任何問題歡迎至`http://nodejs.tw `,詢問對於 Node.js 相關問題。 21 | -------------------------------------------------------------------------------- /zh-tw/node_npm.rst: -------------------------------------------------------------------------------- 1 | ************** 2 | NPM 套件管理工具 3 | ************** 4 | 5 | npm 全名為 **N**\ ode **P**\ ackage **M**\ anager,\ 6 | 是 Node.js 的套件(package)管理工具, 7 | 類似 Perl 的 ppm 或 PHP 的 PEAR 等。\ 8 | 安裝 npm 後,\ 9 | 使用 ``npm install module_name`` 指令即可安裝新套件,\ 10 | 維護管理套件的工作會更加輕鬆。 11 | 12 | npm 可以讓 Node.js 的開發者,\ 13 | 直接利用、擴充線上的套件庫(packages registry),\ 14 | 加速軟體專案的開發。\ 15 | npm 提供很友善的搜尋功能,\ 16 | 可以快速找到、安裝需要的套件,\ 17 | 當這些套件發行新版本時,\ 18 | npm 也可以協助開發者自動更新這些套件。 19 | 20 | npm 不僅可用於安裝新的套件,它也支援搜尋、列出已安裝模組及更新的功能。 21 | 22 | 安裝 NPM 23 | ======== 24 | 25 | Node.js 在 0.6.3 版本開始內建 npm,\ 26 | 讀者安裝的版本若是此版本或更新的版本,\ 27 | 就可以略過以下安裝說明。 28 | 29 | 若要檢查 npm 是否正確安裝,可以使用以下的指令: 30 | 31 | :: 32 | 33 | npm -v 34 | 35 | .. topic:: 執行結果說明 36 | 37 | 若 npm 正確安裝,執行 ``npm -v`` 將會看到類似 1.1.0-2 的版本訊息。 38 | 39 | 若讀者安裝的 Node.js 版本比較舊,\ 40 | 或是有興趣嘗試自己動手安裝 npm 工具,\ 41 | 則可以參考以下的說明。 42 | 43 | 安裝於 Windows 系統 44 | ------------------ 45 | 46 | Node.js for Windows 於 0.6.2 版開始內建 npm,\ 47 | 使用 nodejs.org 官方提供的安裝程式,\ 48 | 不需要進一步的設定,\ 49 | 就可以立即使用 npm 指令,\ 50 | 對於 Windows 的開發者來說,\ 51 | 大幅降低環境設定的問題與門檻。 52 | 53 | 除了使用 Node.js 內建的 npm,\ 54 | 讀者也可以從 npm 官方提供的以下網址: 55 | 56 | http://npmjs.org/dist/ 57 | 58 | 這是由 npm 提供的 Fancy Windows Install 版本,\ 59 | 請下載壓縮檔(例如:\ ``npm-1.1.0-3.zip``\ ),\ 60 | 並將壓縮檔內容解壓縮至 Node.js 的安裝路徑(例如:\ ``C:\Program Files\nodejs``\ )。 61 | 62 | 解壓縮後,在 Node.js 的安裝路徑下,應該有以下的檔案及資料夾。 63 | 64 | * npm.cmd (檔案) 65 | * node_modules (資料夾) 66 | 67 | 安裝於 Linux 系統 68 | ---------------- 69 | 70 | Ubuntu Linux 的使用者,\ 71 | 可以加入 `NPM Unoffcial PPA `_ 72 | 這個 repository,\ 73 | 即可使用 apt-get 完成 npm 安裝。 74 | 75 | .. topic:: Ubuntu Linux 使用 apt-get 安裝 npm 76 | 77 | :: 78 | 79 | sudo apt-get install python-software-properties 80 | sudo add-apt-repository ppa:gias-kay-lee/npm 81 | sudo apt-get update 82 | sudo apt-get install npm 83 | 84 | npm 官方提供的安裝程式 ``install.sh``\ ,\ 85 | 可以適用於大多數的 Linux 系統。\ 86 | 使用這個安裝程式,請先確認: 87 | 88 | 1. 系統已安裝 curl 工具(請使用 ``curl --version`` 查看版本訊息) 89 | 2. 已安裝 Node.js 並且 PATH 正確設置 90 | 3. Node.js 的版本必須大於 0.4.x 91 | 92 | 以下為 npm 提供的安裝指令: 93 | 94 | :: 95 | 96 | curl http://npmjs.org/install.sh | sh 97 | 98 | 安裝成功會看到如下訊息: 99 | 100 | .. topic:: install.sh 安裝成功的訊息 101 | 102 | :: 103 | 104 | npm@1.0.105 /home/USERNAME/local/node/lib/node_modules/npm 105 | It worked 106 | 107 | 安裝於 Mac OS X 108 | --------------- 109 | 110 | 建議採用與 Node.js 相同的方式,進行 npm 的安裝。\ 111 | 例如使用 MacPorts 安裝 Node.js,\ 112 | 就同樣使用 MacPorts 安裝 npm,\ 113 | 這樣對日後的維護才會更方便容易。 114 | 115 | 使用 MacPorts 安裝 npm 是本書比較建議的方式,\ 116 | 它可以讓 npm 的安裝、移除及更新工作自動化,\ 117 | 將會幫助開發者節省寶貴時間。 118 | 119 | .. topic:: 安裝 MacPorts 的提示 120 | 121 | 在 MacPorts 網站,可以取得 OS X 系統版本對應的安裝程式(例如 10.6 或 10.7)。 122 | 123 | http://www.macports.org/ 124 | 125 | 安裝過程會詢問系統管理者密碼,使用預設的選項完成安裝即可。\ 126 | 安裝 MacPorts 之後,在終端機執行 ``port -v`` 將會看到 MacPorts 的版本訊息。 127 | 128 | 安裝 npm 之前,先更新 MacPorts 的套件清單,以確保安裝的 npm 是最新版本。 129 | 130 | :: 131 | 132 | sudo port -d selfupdate 133 | 134 | 接著安裝 npm。 135 | 136 | :: 137 | 138 | sudo port install npm 139 | 140 | 若讀者的 Node.js 並非使用 MacPorts 安裝,\ 141 | 則不建議使用 MacPorts 安裝 npm,\ 142 | 因為 MacPorts 會自動檢查並安裝相依套件,\ 143 | 而 npm 相依 nodejs,\ 144 | 所以 MacPorts 也會一併將 nodejs 套件安裝,\ 145 | 造成先前讀者使用其它方式安裝的 nodejs 被覆蓋。 146 | 147 | 讀者可以先使用 MacPorts 安裝 curl(\ ``sudo port install curl``\ ),\ 148 | 再參考 Linux 的 install.sh 安裝方式,\ 149 | 即可使用 npm 官方提供的安裝程式。 150 | 151 | NPM 安裝後測試 152 | ------------- 153 | 154 | npm 是指令列工具(command-line tool),\ 155 | 使用時請先打開系統的文字終端機工具。 156 | 157 | 測試 npm 安裝與設定是否正確,請輸入指令如下: 158 | 159 | :: 160 | 161 | npm -v 162 | 163 | 或是: 164 | 165 | :: 166 | 167 | npm --version 168 | 169 | 如果 npm 已經正確安裝設定,就會顯示版本訊息: 170 | 171 | .. topic:: 執行結果(範例) 172 | 173 | :: 174 | 175 | 1.1.0-2 176 | 177 | 使用 NPM 安裝套件 178 | ================ 179 | 180 | npm 目前擁有超過 6000 種套件(packages),\ 181 | 可以在 `npm registry `_ 使用關鍵字搜尋套件。 182 | 183 | http://search.npmjs.org/ 184 | 185 | 舉例來說,在關鍵字欄位輸入「coffee-script」,\ 186 | 下方的清單就會自動列出包含 coffee-script 關鍵字的套件。 187 | 188 | .. image:: ../images/zh-tw/node_npm_registry.png 189 | 190 | 接著我們回到終端機模式的操作,\ 191 | ``npm`` 的指令工具本身就可以完成套件搜尋的任務。 192 | 193 | 例如,以下的指令同樣可以找出 coffee-script 相關套件。 194 | 195 | :: 196 | 197 | npm search coffee-script 198 | 199 | 以下是搜尋結果的參考畫面: 200 | 201 | .. image:: ../images/zh-tw/node_npm_search.png 202 | 203 | 找到需要的套件後(例如 express),即可使用以下指令安裝: 204 | 205 | :: 206 | 207 | npm install coffee-script 208 | 209 | 值得注意的一點是,使用 ``npm install`` 會將指定的套件,\ 210 | 安裝在工作目錄(Working Directory)的 ``node_modules`` 資料夾下。 211 | 212 | 以 Windows 為例,如果執行 ``npm install`` 的目錄位於: 213 | 214 | ``C:\project1`` 215 | 216 | 那麼 npm 將會自動建立一個 node_modules 的子目錄(如果不存在)。 217 | 218 | ``C:\project1\node_modules`` 219 | 220 | 並且將下載的套件,放置於這個子目錄,例如: 221 | 222 | ``C:\project1\node_modules\coffee-script`` 223 | 224 | 這個設計讓專案可以個別管理相依的套件,\ 225 | 並且可以在專案佈署或發行時,\ 226 | 將這些套件(位於 node_modules)一併打包,\ 227 | 方便其它專案的使用者不必再重新下載套件。 228 | 229 | 這個 ``npm install`` 的預設安裝模式為 **local**\ (本地),\ 230 | 只會變更當前專案的資料夾,\ 231 | 不會影響系統。 232 | 233 | 另一種安裝模式稱為 **global**\ (全域),\ 234 | 這種模式會將套件安裝到系統資料夾,\ 235 | 也就是 npm 安裝路徑的 ``node_modules`` 資料夾,\ 236 | 例如: 237 | 238 | ``C:\Program Files\nodejs\node_modules`` 239 | 240 | 是否要使用全域安裝,\ 241 | 可以依照套件是否提供\ **新指令**\ 來判斷,\ 242 | 舉例來說,\ 243 | express 套件提供 ``express`` 這個指令,\ 244 | 而 coffee-script 則提供 ``coffee`` 指令。 245 | 246 | 在 local 安裝模式中,這些指令的程式檔案,\ 247 | 會被安裝到 ``node_modules`` 的 ``.bin`` 這個隱藏資料夾下。\ 248 | 除非將 .bin 的路徑加入 PATH 環境變數,\ 249 | 否則要執行這些指令將會相當不便。 250 | 251 | 為了方便指令的執行,\ 252 | 我們可以在 ``npm install`` 加上 ``-g`` 或 ``--global`` 參數,\ 253 | 啟用 global 安裝模式。例如: 254 | 255 | :: 256 | 257 | npm install -g coffee-script 258 | npm install -g express 259 | 260 | 使用 global 安裝模式,\ 261 | 需要注意執行權限與搜尋路徑的問題,\ 262 | 若權限不足,可能會出現類似以下的錯誤訊息: 263 | 264 | :: 265 | 266 | npm ERR! Error: EACCES, permission denied '...' 267 | npm ERR! 268 | npm ERR! Please try running this command again as root/Administrator. 269 | 270 | 要獲得足夠得執行權限,請參考以下說明: 271 | 272 | * Windows 7 或 2008 以上,在「命令提示字元」的捷徑按右鍵,\ 273 | 選擇「以系統管理員身分執行」,\ 274 | 執行 npm 指令時就會具有 Administrator 身分。 275 | * Mac OS X 或 Linux 系統,可以使用 ``sudo`` 指令,例如:\ 276 | 277 | ``sudo npm install -g express`` 278 | * Linux 系統可以使用 root 權限登入,或是以「\ ``sudo su -``\ 」切換成 root 身分。\ 279 | (使用 root 權限操作系統相當危險,因此並不建議使用這種方式。) 280 | 281 | 若加上 ``-g`` 參數,使用 ``npm install -g coffee-script`` 完成安裝後,\ 282 | 就可以在終端機執行 ``coffee`` 指令。例如: 283 | 284 | :: 285 | 286 | coffee -v 287 | 288 | .. topic:: 執行結果(範例) 289 | 290 | :: 291 | 292 | CoffeeScript version 1.2.0 293 | 294 | 若未將 Node.js 套件安裝路徑加入環境變數 NODE_PATH,在引入時會回報錯誤。 295 | 296 | .. topic:: 報錯範例 297 | 298 | :: 299 | 300 | module.js:340 301 | throw err; 302 | ^ 303 | Error: Cannot find module 'express' 304 | at Function.Module._resolveFilename (module.js:338:15) 305 | at Function.Module._load (module.js:280:25) 306 | at Module.require (module.js:362:17) 307 | at require (module.js:378:17) 308 | at Object. (/home/clifflu/test/node.js/httpd/express.js:3:15) 309 | at Module._compile (module.js:449:26) 310 | at Object.Module._extensions..js (module.js:467:10) 311 | at Module.load (module.js:356:32) 312 | at Function.Module._load (module.js:312:12) 313 | at Module.runMain (module.js:492:10) 314 | 315 | .. topic:: 使用 ubuntu PPA 安裝 Node.js 的設定範例 316 | 317 | :: 318 | echo 'NODE_PATH="/usr/lib/node_modules"' | sudo tee -a /etc/environment 319 | 320 | 套件的更新及維護 321 | ============== 322 | 323 | 除了前一節說明的 search 及 install 用法,\ 324 | npm 還提供其他許多指令(commands)。 325 | 326 | 使用 ``npm help`` 可以查詢可用的指令。 327 | 328 | :: 329 | 330 | npm help 331 | 332 | .. topic:: 執行結果(部分) 333 | 334 | :: 335 | 336 | where is one of: 337 | adduser, apihelp, author, bin, bugs, c, cache, completion, 338 | config, deprecate, docs, edit, explore, faq, find, get, 339 | help, help-search, home, i, info, init, install, la, link, 340 | list, ll, ln, login, ls, outdated, owner, pack, prefix, 341 | prune, publish, r, rb, rebuild, remove, restart, rm, root, 342 | run-script, s, se, search, set, show, star, start, stop, 343 | submodule, tag, test, un, uninstall, unlink, unpublish, 344 | unstar, up, update, version, view, whoami 345 | 346 | 使用 ``npm help command`` 可以查詢指令的詳細用法。例如: 347 | 348 | :: 349 | 350 | npm help list 351 | 352 | 接下來,本節要介紹開發過程常用的 npm 指令。 353 | 354 | 使用 ``list`` 可以列出已安裝套件: 355 | 356 | :: 357 | 358 | npm list 359 | 360 | .. topic:: 執行結果(範例) 361 | 362 | :: 363 | 364 | ├── coffee-script@1.2.0 365 | └─┬ express@2.5.6 366 | ├─┬ connect@1.8.5 367 | │ └── formidable@1.0.8 368 | ├── mime@1.2.4 369 | ├── mkdirp@0.0.7 370 | └── qs@0.4.1 371 | 372 | 檢視某個套件的詳細資訊,例如: 373 | 374 | :: 375 | 376 | npm show express 377 | 378 | 升級所有套件(如果該套件已發佈更新版本): 379 | 380 | :: 381 | 382 | npm update 383 | 384 | 升級指定的套件: 385 | 386 | :: 387 | 388 | npm update express 389 | 390 | 移除指定的套件: 391 | 392 | :: 393 | 394 | npm uninstall express 395 | 396 | 使用 package.json 397 | ================= 398 | 399 | 對於正式的 Node.js 專案,\ 400 | 可以建立一個命名為 ``package.json`` 的設定檔(純文字格式),\ 401 | 檔案內容參考範例如下: 402 | 403 | .. topic:: package.json(範例) 404 | 405 | :: 406 | 407 | { 408 | "name": "application-name" 409 | , "version": "0.0.1" 410 | , "private": true 411 | , "dependencies": { 412 | "express": "2.5.5" 413 | , "coffee-script": "latest" 414 | , "mongoose": ">= 2.5.3" 415 | } 416 | } 417 | 418 | 其中 ``name`` 與 ``version`` 依照專案的需求設置。 419 | 420 | 需要注意的是 ``dependencies`` 的設定,\ 421 | 它用於指定專案相依的套件名稱及版本: 422 | 423 | * ``"express": "2.5.5"`` 424 | 425 | //代表此專案相依版本 2.5.5 的 express 套件 426 | * ``"coffee-script": "latest"`` 427 | 428 | //使用最新版的 coffee-script 套件(每次更新都會檢查新版) 429 | * ``"mongoose": ">= 2.5.3"`` 430 | 431 | //使用版本大於 2.5.3 的 mongoose 套件 432 | 433 | 假設某個套件的新版可能造成專案無法正常運作,\ 434 | 就必須指定套件的版本,\ 435 | 避免專案的程式碼來不及更新以相容新版套件。\ 436 | 通常在開發初期的專案,\ 437 | 需要盡可能維持新套件的相容性(以取得套件的更新或修正),\ 438 | 可以用「\ ``>=``\ 」設定最低相容的版本,\ 439 | 或是使用「\ ``latest``\ 」設定永遠保持最新套件。 440 | -------------------------------------------------------------------------------- /zh-tw/node_utils.rst: -------------------------------------------------------------------------------- 1 | ******************** 2 | Node.js 好用工具介紹 3 | ******************** 4 | 5 | log.io 6 | ======= 7 | 8 | Real-time log monitoring in your browser 9 | 10 | http://logio.org/ 11 | 12 | node.js (>=0.4.12 <=0.6.11) 13 | 14 | :: 15 | 16 | npm config set unsafe-perm true 17 | npm install -g --prefix=/usr/local log.io 18 | log.io server start 19 | 20 | :: 21 | 22 | http://localhost:8998 23 | -------------------------------------------------------------------------------- /zh-tw/outline.rst: -------------------------------------------------------------------------------- 1 | ****************** 2 | Node.js 安裝與設定 3 | ****************** 4 | 5 | 安裝 6 | ==== 7 | 8 | 設定 9 | ==== 10 | 11 | 12 | ************ 13 | Node.js 基礎 14 | ************ 15 | 16 | 17 | ****************** 18 | 套件管理工具(NPM) 19 | ****************** 20 | 21 | :: 22 | 23 | npm install xxxx 24 | 25 | ************ 26 | 網站開發框架 27 | ************ 28 | 29 | View 30 | 31 | Node.js Layout & Routing 32 | 33 | Template 34 | 35 | 36 | ********** 37 | Socket.IO 38 | ********** 39 | 40 | long pulling 41 | 42 | 即時反饋模組 43 | 44 | 即時網站架構 45 | 46 | websocket 訊息即時傳遞 47 | 48 | 49 | ********* 50 | 存取資料庫 51 | ********* 52 | 53 | Database 54 | 55 | *************** 56 | 實戰一、心智圖 57 | *************** 58 | 59 | ************* 60 | 實戰二、Todo 61 | ************* 62 | 63 | -------------------------------------------------------------------------------- /zh-tw/preface.rst: -------------------------------------------------------------------------------- 1 | .. raw:: latex 2 | 3 | \setcounter{secnumdepth}{-1} 4 | 5 | **** 6 | 前言 7 | **** 8 | 9 | Node.js 是 JavaScript 程式語言的開發框架,由0.4.x至目前v0.6.0版本核心上已經有許多變更,當然目的只有一個就是讓開發變得更簡單,速度能夠更快,這是所有人奮鬥的目標。而nodeJS 華文維基平台,主力由nodeJS.tw 一群喜好javascript 開發者共同主筆,內文以中文為主,希望能夠降低開發者學習門檻,藉由我們拋磚引玉,讓更多華人開發者共同學習、討論。 10 | 11 | 12 | 13 | .. raw:: latex 14 | 15 | \setcounter{secnumdepth}{1} 16 | 17 | --------------------------------------------------------------------------------