Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
<<toBalaManager>>
///%
比目魚的天空 - Exchange 也來湊一腳
''flatfish''@2008 powered by TWtBala - TiddlyWiki 2.3.0
<<tagsTree ex "" 2 4 index label>>
<<tagsTree twcms "" 1 4 index label>>
<<tagsTree KMKConfig "" 1 4 index label>>
<<tabs txtMainTab "最近更新" "依更新日期排序" TabTimeline "分類" "所有標籤" TabTags "設定文章" "說所有設定文章" TabMoreShadowed>>
<<search>> | [[首頁]] | [[土芭樂 3.0 - 數位新思路|http://tbala.net/]] | [[MS Exchange|http://www.msexchange.org/]] | <<newTiddler label:"新增文章">> <<saveChanges>>
//%/
<<forEachTiddler
script '
window.fetItemsPerPage = 15;
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)'
>>
}}}
// %/
<<forEachTiddler
where
'tiddler.tags.contains("plugin")'
>>
<<forEachTiddler
where
'tiddler.tags.contains("macro")'
>>
<<forEachTiddler
where
'tiddler.tags.length == 0'
>>
!whatis Exchange Server
*微軟的訊息與協同作業通訊伺服服務 (Microsoft messaging and collaboration server)
*整合訊息群組、協同作業以及電子郵件的軟體系統
*也是一套資料庫型態的系統軟體
*Exchange Server 支援專屬的 MAPI 用戶端軟體:Outlook,也支援 SMTP、POP3、IMAP、Web mail
*Web mail:OWA
*支援行動裝置:ActiveSync、DirectPush 技術
!訊息作業協同服務主流產品
*Microsoft Exchange Server 2000 / 2003 / 2007
*IBM Lotus Domino / Notes
*Novell GroupWise
*Unix sendmail (純粹的電子郵件軟體)
!Exchange 2007 新增與加強功能
*正式版本僅有 64 位元版本,不再支援 32 位元
**32 位元版僅提供測試、教學用途
**32 位元記憶體定址僅能到 4GB,64 位元可到 16 EB
*提供角色為基礎的伺服器部署方式與架構:有五個角色,除第一個邊際傳輸伺服器須獨立安裝,其他可裝在一起
##邊際傳輸
##信箱
##用戶端存取
##集線傳輸
##整合通訊
*新的管理架構:管理工具、新精靈、管理角色與工具、管理命令介面:Windows PowerShell
**進階的管理仍須靠命令列指令
*加強高度可用性的資料庫(避免中斷性)
**
*AD 路由拓樸:
*OWA 的加強:
**WebReady 文件檢視:可用網頁開啟檢視 Word、Excel、PPT、以及 PDF 的附件檔案,轉換為 HTML 直接顯示
**連結存取 LinkAccess:可直接由 OWA 存取共用資料夾
!2007 Standard Vs. Enterprise
|特色|標準版|企業版|
|儲存群組數量|5|50|
|資料庫數量|5|50|
|資料庫大小限制|16TB/資料庫|16TB/資料庫|
|叢集技術 (Cluster)|否|是|
|本機資料庫連續複寫(LCR)|支援|支援|
|單資料中心容錯備援(SCC)|不支援|支援|
|雙資料中心容錯備援(CCR)|不支援|支援|
|價格|$1000|$5000|
*Office Communication Server 2007:企業內使用的 Instant Message
!五大伺服器
#邊際傳輸
**需架設在 DMZ 區
#信箱
#用戶端存取
**
#集線傳輸
#整合通訊
**使用者取得語音信箱、傳真透過整合通訊
!!為何要分為五大伺服器角色
*藉由明確的定義五種角色:
**降低被攻擊的層面
**移除不必要的功能,使五部伺服器分工合作、分層負責
!邊際傳輸伺服器 (Edge Transport Server)
*非必要角色
*必須獨立的實體機器,沒有其他角色存在
*可為工作群組成員或加入網域
**強烈建議獨立伺服器
*使用 Active Directory Application Mode (ADAM) 提供組態及複製資訊
**ADAM 為微軟 AD 的簡化版本,在不需 DNS 與加入網域即可存取 LDAP
*須強制周邊政策 (Perimeter policy enforcement)
*提供訊息保護 (Message hygiene)
**防垃圾
**防釣魚
**Transport anti-virus (需額外購買 Forefront Security for Exchange)
**Anti-Spoofing (Sender ID),需額外付費
!用戶存取伺服器(Client Access Server, CAS)
*提供用戶存取,用戶端直接與 CAS 連接
*支援各種用戶存取通訊協定:
**Outlook Web Access (OWA)
**Echange ActiveSync (EAS)
**Outlook Anywhere (formerly RPC~~over~~HTTPS)
**POP3 與 IMAP4 protocols
**Auto-discover
**Web Service
*內定安裝完僅支援 OWA,其他額外安裝與設定
*取代 Exchange 2000 / 2003 的前端伺服器
*信箱伺服器存再的每個站台與網域至少架設一台 CAS
*與信箱伺服器需穩定 (最好快速) 的連線
*CAS 與信箱伺服器間透過 RPC 聯繫,無法更改
*僅有 MAPI (上層透過 RPC) 可使用戶端直接與信箱伺服器連接,
!集線傳輸伺服器(Hub Transport Server)
*扮演信件橋接功能,在不同的 AD 站台的伺服器傳遞郵件
*負責郵件的接收、傳送、代理傳送
*若邊際傳輸伺服器不存在,則透過集線傳輸伺服器可代替邊際傳輸伺服器提供訊息保護功能 (can handle message hygiene functions)
!信箱伺服器 (Mailbox)
*主要功能為維護信箱存放區 (資料庫),預設沒有公用資料夾,比共用資料夾更多、更安全的存取方式
*利用 MAPI 存取信箱
*可支援 MAPI 加密
*支援三種容錯技術
**本機連續複寫 (Local Coutinuous Replication, LCR)
**雙資料中心容錯 (Cluster Coutinuous
**單資料中心容錯
!整合通訊伺服器 (Unified Messaging)
*非必要角色
*提供語音信箱、傳真、語音自動導引服務
*需 Mailbox 與 Hub Transport
*需額外的 PBX 閘道器
!Ex2007 部署方式
*小型規模
**網際網路 - 防火牆 - [信箱、集線傳輸、用戶存取、(整合通訊)]
*中型規模
**網際網路 - 防火牆 - 邊際傳輸 - 防火牆 - [信箱、集線傳輸、用戶存取、(整合通訊)]
*大型規模
**請自行想像
*伺服器角色為基礎的方式安裝
*利用 Exchange Server 2007 光碟內精靈程式
**安裝精靈會根據所選取的伺服器角色檢查其所需的軟體元件是否安裝
**提供欠缺的軟體元件下載的網址
*支援自動安裝
*無法就地升級,Exchange 2003 無法直接升級為 Exchange 2007
!!環境需求
*樹系需擴充 Schema,安裝精靈正常情況會自動進行
*網域功能層級網域功能層級:至少 Windows 2000 native (不允許有 NT BDC 存在)
*CAS 必須安裝 IIS Web Service
**W3SVC + ASP.Net2.0 + COM + access
**不安裝 SMTP 與 NNTP
{{item1{__用戶存取伺服器 (CAS) 需安裝 IIS__}}} <<toBalaFlashPlayer "flash/03-01-CAS_install_IIS.swf" "有影片有真相" "800" "620">>
!!安裝需求
{{item1{__Exchange Server 2007 安裝前軟體需求檢查__}}} <<toBalaFlashPlayer "flash/03-02-preinstall_requirements.swf" "有影片有真相" "800" "620">>
*Windows server 2003 SP2 + 與最新的更新
*Framework 2.0 SP1 X64 (X86)
{{item1{__安裝 Framework 2.0 SP1__}}} <<toBalaFlashPlayer "flash/03-03-install_framework2.0sp1.swf" "有影片有真相" "800" "620">>
*MMC 3.0
*Windows Powershell 1.0
!!安裝過程
*安裝需 Enterprise admins 成員
*組織名稱不能輸入中文及特殊符號
{{item1{__安裝 Exchange Server 2007 -1__}}} <<toBalaFlashPlayer "flash/03-04-install_exchange_server_2007.swf" "有影片有真相" "800" "620">>
{{item1{__安裝 Exchange Server 2007 -2.swf__}}} <<toBalaFlashPlayer "flash/03-05-install_exchange_server_2007-2.swf" "有影片有真相" "800" "620">>
*透過 setup.com 參數可設定以自動模式安裝
!!安裝後檢查、確認及必要工作
*檢查安裝目錄 <<toBalaFlashPlayer "flash/03-06-verify_install_folders.swf" "有影片有真相" "800" "620">>
**預設安裝在 C:\program files\microsoft\exchanger server
***ClientAccess:CAS
***Mailbox
***TransportRoles
***UnifiedMessaging
*被設定為自動啟動的 Exchange 服務是否已啟動 <<toBalaFlashPlayer "flash/03-07-verify_auto_start_services.swf" "有影片有真相" "800" "620">>
**Microsoft Exchange Active Directory 拓樸服務、Microsoft Exchange Information Sotre、Microsoft Exchange System Attendnt 為檢查重點項目
*檢查應用程式事件記錄,來源為 MSExchange 開頭的事件 <<toBalaFlashPlayer "flash/03-08-verify_applications_event_viewer.swf" "有影片有真相" "800" "620">>
*檢查並確認安裝的 Exchange Server 角色功能是否正常 <<toBalaFlashPlayer "flash/03-09-verify_installed_server_roles.swf" "有影片有真相" "800" "620">>
**get-exchangeserver | format-list
***get-exchangeserver | fl
{{{
ServerRole : Mailbox, ClientAccess, UnifiedMessaging, HubTransport
}}}
**代表本 Exchange Server 已安裝 信箱、用戶存取、整合通訊、集線傳輸等四個伺服器角色,且功能正常
*輸入產品金鑰
**產品金鑰決定產品版本,將不會有不同版本的光碟
*測試 Exchange 組織使用的網域控制站
**可指定要使用的 DC <<toBalaFlashPlayer "flash/03-10-select_domain_controller.swf" "有影片有真相" "800" "620">>
**建議使用一台同站台內較 Powerful 的 DC+DNS+GC 與 Exchange Server 2007 搭配,將會有較高的效能
*連線測試
**安裝完成預設僅支援以下兩種通訊協定
***Outlook MAPI
***IE OWA
****{{{https://srv-cht-ex1/owa}}}
{{item1{__檢查 Outlook - MAPI 連線是否成功__}}} <<toBalaFlashPlayer "flash/03-12-verify_connection-Outlook_MAPI.swf" "有影片有真相" "800" "620">>
{{item1{__檢查 OWA 連線是否成功__}}} <<toBalaFlashPlayer "flash/03-11-verify_connection-OWA.swf" "有影片有真相" "800" "620">>
!常見安裝問題
|問題|原因與處理方案|
|無法安裝某個角色|伺服器角色所需的軟體並未安裝|
|權限不足|準備樹系(foreprep)需schema admins 群組成員; 準備網域需 Domain admins 群組成員; 建議使用 enterprise admins 群組成員|
|空間不足|系統分割區: 用來開機的分割區至少需 200MB|
|AD 網路連線問題|確認與 DNS、GC、操作主機與 DC 的網路連線,使用 support tools 內的 dcdig|
|安裝後關機非常緩慢|若 Exchange 裝在 DC 上|
*系統 (system) 分割區:開機的分割區
*啟動 (boot) 分割區:存放 windows 的分割區
!!安裝在 DC 導致關機非常緩慢
*由於關機時,AD 服務比 Exchange 先停止,導致 Exchange 需等待 AD 查詢至逾時為止
{{{
REM 停止 Exchange 相關服務
start /min net stop MSExchangeADTopology /yes
start /min net stop MSExchangeIS /yes
start /min net stop MSExchangeSA /yes
REM 停止 IISADMIN, W3SVC,
start /min net stop IISADMIN /yes
REM 關機, 若要重新開機開機請將 /s 換成 /r
shutdown /s /m \\localhost /f
}}}
!升級 Exchange Server 2007
*允許 Exchange 2000 / 2003 升級『 Exchange 組織 』
*無法由 Exchange 5.5 升級
*無法支援伺服器就地升級 (no in-place server upgrades
*需搬移所有信箱與服務
*舊的 Exchange 需要『 正常移除 』,才會在 AD 資料庫中移除舊的資料
*新的信箱功能需要將信箱存放在 Exchange 2007 伺服器
*許多新的功能需要使用 Outlook 2007
!一般升級 Exchange Server 2007 的途徑
*準備 AD
*選擇性部署 Edge Transport Servers
*部署 CAS
*部署 Hub Transport
*部署 Mailbox
*搬移 Exchange 2000 / 2003 伺服器資源
*從組織中移除 Exchange 2000 / 2003 伺服器 (需正常移除手續)
*移除路由群組間的連接器
*移除路由群組
!Exchange 2007 管理角色
*Exchange Server 提供角色為基礎的管理模式
*四種基本的管理角色
**Exchange 組織系統管理員角色 (Exchange Organization Administrators)
***組織就是整個樹系,此種角色在整個樹系內的 Exchange 內容及物件有完整個存取權
**Exchange 收件者系統管理員角色 (Exchange Recipient Administrators)
**Exchange 僅檢視管理角色 (Exchange View-Only Administrators)
***樹系內所有相關的 Exchange 物件都有唯讀的權限,不能修改、刪除
**Exchange Server 系統管理員角色 (Exchange Server Administrators)
***管理組織內某一特定 Exchange 伺服器組態
{{item1{__新增 Exchange 系統管理員__}}} <<toBalaFlashPlayer "flash/04-01-new_administrators.swf" "有影片有真相" "800" "620">>
!管理工具
*Exchange 管理主控台 (EMC)
|主控台樹狀目錄<br>Console tree|結果窗格<br>Result pane|動作窗格<br>Action pane|
|~|工作窗格<br>Work pane|~|
|主控台樹狀結構|功能|
|組織組態|管理 Exchange 組織的組態|
|伺服器組態|管理特定角色伺服器設定|
|收件者組態|管理收件者物件設定,可透過『中斷連線的信箱』救援信件|
|工具箱|做分析診斷用途|
*Exchange 管理命令介面 (EMS)
**EMC 為 EMS 的圖形介面,EMS 為 EMC 的後端
**EMC 為 EMS 的子集合,EMS 指令可以做的 EMC 不一定能執行
**cmdlets 為 EMS 的核心,可視為 EMS 的內建命令,由 .Net 寫成,每個 cmdlets 只具單一任務,由 Powershell 解譯器負責執行
*** New-...:新增一個物件,例如新增信箱
***Remove-...:移除一個物件
***Enable-...:開啟某一項設定
***Disable-...:關閉某一項設定
***Set-...:修改某一物件的設定
***Get-...:查詢物件的設定
**Ex2007 SP1 的 EMC 能處理較多的設定,但仍有部分進階設定或是複雜的設定需透過 EMS
*AD 使用者及電腦
**早期 Exchange 2003 必須透過 AD 使用者及電腦設定 Exchange 收件者相關組態
*IIS 管理員
**管理或設定 OWA 組態需用到
*AD 架構
*DNS 管理工具
*ADSI edit
!遠端管理 Exchange 伺服器
*遠端桌面 (Remote Desktop)
*使用 Exchange 管理主控台 (EMC)
**若管理使用的機器為 32 位元作業系統,則需至微軟網站下載 32 位元的 EMC
***需安裝 MMC 3.0
***Powershell 1.0
***.Net framework 2.0
***Microsoft Installer 3.1
!儲存群組 (Storage group)
*儲存群組為一群信箱儲存區 (資料庫) 及公用資料夾儲存區 (也是資料庫) 的集合,也就是說,儲存群組就是一群資料庫的集合,而這些資料庫@@color:red;共用一組交易紀錄檔 (Transaction log)@@
*Exchange 會以獨立的 Server process 來管理每個儲存群組
*一個儲存群組中最多可包含 1 ~ 5 個資料庫
**微軟強烈建議:一個儲存群組只包含一個唯一的資料庫,要維護、備份、還原最容易
!!改良的 Exchange 2007 資料庫
*不再使用 .stm 檔案,只維持一個 .edb 檔
*支援單一個案儲存機制 (SIS, Single Instance Storage)
**SIS:有重複的物件在一個儲存裝置上僅會維護一份
**類似 Linux 的符號連結
*交易紀錄檔縮小 (1MB vs 5MB@Ex2003)
*記錄檔順序代號可超過 100 萬
!預設的儲存群組
*安裝 Mailbox 伺服器角色時,建立預設的 First Storage Group
**First Storage Group 內存單一的 Mailbox Databox
*若安裝時選擇『組織內存在 Outlook 2003 或更舊的用戶端』時,則會自動建立第二個儲存群組:Second storage group 內存單一的 public folder database
!建立儲存群組
*為何要使用多個儲存群組
**若其中一個儲存群組達到五個資料庫,要新增資料庫則勢必建立儲存群組
**使用專用的磁碟機提高伺服器效能
**針對不同資料集使用不同的備份排程,因備份的基本單位為儲存群組,因此要針對不同的備份條件建立不同的儲存群組
*再次建議,一個儲存群組存放單一資料庫
#結果窗格選擇 Mailbox 伺服器名稱,新增儲存群組
#輸入儲存群組名稱,不能為中文
#按 tab 可使用預設合理的儲存群組目錄
#下三項與容錯能力有關
#確認建立儲存群組完成
#點選工作窗格中新增的儲存群組
#設定信箱資料庫名稱
#勾選裝載 (mount) 這個資料庫
#確認資料庫建立完成,到新增的儲存群組資料夾中檢視是否產生該有的檔案
{{item1{__建立儲存群組__}}} <<toBalaFlashPlayer "flash/05-01-create_storage_group.swf" "有影片有真相" "800" "620">>
!搬移交易紀錄、資料庫
*搬移儲存群組代表移動下面兩種類型的檔案:
**交易紀錄檔
**系統檔案 (資料庫+檢查點檔)
*建議將交易紀錄檔與資料庫分別儲存在不同的硬碟以避免磁碟競爭進而提升效率
*搬移資料庫時需卸載資料庫,因此需確定當下並沒有人在使用
*可以針對個別資料庫做搬移
*搬移資料庫的動作在實際上線的機器中可能花費超過一天的時間,且許多設定將經過很長的時間才會生效
{{item1{__移動儲存群組__}}} <<toBalaFlashPlayer "flash/05-02-move_storage_group.swf" "有影片有真相" "800" "620">>
{{item1{__移動單一資料庫__}}} <<toBalaFlashPlayer "flash/05-03-move_single_database.swf" "有影片有真相" "800" "620">>
!關閉或啟用循環紀錄 (circular logging)
*循環紀錄的目的是為了節省空間
*當這個 transaction log 的資料已經被資料庫認可 (commit) 後,在要產生下一個 log 檔的時候將會複寫已被認可的 log 檔
*預設不啟用
*啟用後將限制還原能力
**在沒啟用循環紀錄時,備份的資料庫加上紀錄檔可將資料還原至最新的狀態
**啟用循環紀錄後,還原的資料將可能僅止於該備份的資料庫,而無法將交易紀錄檔內容還原
*若儲存群組啟用了 LCR,將無法使用
{{item1{__啟用循環紀錄__}}} <<toBalaFlashPlayer "flash/05-04-circular_logging.swf" "有影片有真相" "800" "620">>
!刪除資料庫、儲存群組
*要刪除資料庫需先搬移、停用或移除其內所有信箱
*刪除資料庫後需手動刪除資料庫檔案 (.edb)
*要刪除儲存群組必須刪除其中所有資料庫
*刪除儲存群組需先刪除其內所有資料庫
*刪除儲存群組後需手動刪除其目錄及底下紀錄檔
{{item1{__移除資料庫__}}} <<toBalaFlashPlayer "flash/05-05-delete_database.swf" "有影片有真相" "800" "620">>
{{item1{__移除儲存群組__}}} <<toBalaFlashPlayer "flash/05-06-delete_storage_group.swf" "有影片有真相" "800" "620">>
!收件者
*一個具備郵件傳遞的 AD 物件
*在 Microsoft Exchange server 2007 中,收件者包括:
**信箱使用者
**擁有郵件功能的使用者
**郵件連絡人
**通訊群組
**安全性群組
**動態通訊群組
**擁有郵件功能的公用資料夾 (即將自 Exchange server 中移除)
!收件者 (Recipients) 類型
*擁有信箱功能的使用者 (mailbox enable)
**有效的 AD 帳號,可以收信、送信
*擁有郵件功能的使用者 (mail enable)
**有效的 AD 帳號,但無信箱,無法透過 Exchange Server 發信,但可以 maintain 一個外部電子郵件信箱
*聯絡人 (contact)
**只有其電子郵件位址,沒有 AD 有效帳號,沒有信箱,相當於通訊錄中的名單
*通訊群組 (Distribution Group)
**注意:與發佈群組英文名稱相同
**群組中所有的成員都會收到一份轉寄自該群組的郵件
***mail-enabled distribution groups:
***mail-enabled security groups:
**sendmail 透過 alias 別名運作
!信箱類型 (mailbox type)
*使用者信箱 (User mailbox)
**標準的使用者信箱 (mailbox-enable 的使用者信箱)
*資源信箱 (一個停用的 AD 帳號)
**Exchange server 2007
**專門設計用於排程資源的特殊信箱
***會議信箱 (Room mailbox):用來作為會議排程的信箱 (room scheduling)
***設備信箱 (Equipment mailbox):針對設備裝置排程的資源信箱
*連結的信箱
**在多樹系的環境,需透過雙向信任關係使不同的樹系使用者存取
*Legacy mailbox
**用來存放 Exchange 2000 / 2003 搬移至 Ex2007 的環境
*Shared mailbox
**一個停用的帳號,讓多使用者登入到同一個信箱
!Exchange 使用者信箱
*由 AD 使用加上儲存在 Exchange 信箱資料庫信箱資料所組成
*建立 AD 使用者帳戶 + Exchange 信箱資料庫使用者
*Exchange 2000 / 2003 需在 AD 使用者及電腦先建立 AD 使用者帳戶,Ex2007 則可在 Exchange 主控台中一次建立 AD 帳戶與 Exchange 使用者信箱
*建立 Exchange 使用者信箱需具備 Account Operators 及 Exchange 收件者系統管理員角色 以上的權限
{{item1{__替 AD 使用者帳戶建立使用者信箱__}}} <<toBalaFlashPlayer "flash/06-01-create_user_mailbox.swf" "有影片有真相" "800" "620">>
{{item1{__建立新的 AD 使用者帳戶及信箱__}}} <<toBalaFlashPlayer "flash/06-02_create_new_AD_account_with_mailbox.swf" "有影片有真相" "800" "620">>
{{item1{__建立會議室信箱__}}} <<toBalaFlashPlayer "flash/06-03-create_room_mailbox.swf" "有影片有真相" "800" "620">>
!停用及移除使用者信箱
*停用:刪除信箱,但保留 AD 使用者帳戶
*移除使用者信箱:刪除信箱、及 AD 使用者帳戶
*刪除信箱時,僅是在信箱標記成已移除,在 30 天內都可透過中斷連線信箱恢復其信箱
**但刪除前信箱內至少有一封信
{{item1{__停用使用者信箱__}}} <<toBalaFlashPlayer "flash/06-04-diable_user_mailbox.swf" "有影片有真相" "800" "620">>
!管理中斷連線信箱
*選取要連接的使用者信箱
*對應至正確的使用者
*若在中斷連線信箱中沒有看到信箱可能:
**沒有連至正確的伺服器
**信箱自建立後從未收過郵件
**已過了 30 天保留期限
!移動信箱
*將信箱由某一個伺服器的某一個資料庫搬移至另外一個伺服器的另外一個資料庫
*選取信箱資料庫時,將會顯示 AD 樹系內的所有信箱資料庫
*搬移信箱時會檢查信箱中是否有毀損的郵件
**預設上找到毀損的郵件將不移動這個信箱
**設定 0 (不限制),或一定數量內的毀損郵件不移動
*若搬移至不同伺服器則需選擇是否指定通用目錄伺服器,若不選擇詢問 DNS
*建議使用移動排程
**資料庫搬移耗費大量磁碟 I/O、網路頻寬,因此不適合立即搬移
{{item1{__移動使用者信箱至不同的資料庫__}}} <<toBalaFlashPlayer "flash/06-05-move_user_mailbox.swf" "有影片有真相" "800" "620">>
!!設定傳遞選項
*代理:請參閱下方代理傳送者
*轉寄地址:轉寄信件至職務代理人
*收件者人數上限
{{item1{__傳遞選項 - 轉寄、收件者上限__}}} <<toBalaFlashPlayer "flash/06-06-trans_options.swf" "有影片有真相" "800" "620">>
!信箱存取協定限制
*定義該使用者登入使用的協定
**POP3:offline 讀信
**IMAP4:online 讀信
**MAPI:
**整合通訊:預設停用
**Exchange ActiveSync
**Outlook Web Access (OWA)
*提供的功能服務越少越安全越容易管理
**因此建議僅開放 OWA 與 MAPI,或是乾脆只開放 OWA
{{item1{__限制存許信箱的通訊協定__}}} <<toBalaFlashPlayer "flash/06-07-limit_mailbox_access_protocol.swf" "有影片有真相" "800" "620">>
!限制信箱使用配額
*針對信箱資料庫限制 <<toBalaFlashPlayer "flash/06-08-set_mailbox_database_quota.swf" "有影片有真相" "800" "620">>
*針對使用者信箱個別限制 <<toBalaFlashPlayer "flash/06-09-set_mailbox_quota.swf" "有影片有真相" "800" "620">>
*當資料庫限制與使用者信箱限制並存時,使用者信箱個別限制將會覆蓋資料庫限制
*警告:仍可收送
*禁止傳送:仍可收信
*禁止收發
*檢查並不是時時刻刻,內定上一天檢查一次,凌晨一點
!設定刪除保留時間
*保留已刪除郵件的天數:預設 14 天
*保留已刪除信箱的天數:預設 30 天
*對於公司重要信件發送者 (老闆的秘書、業務......) 的信箱建議額外延長保留時間
*加上『除非已經備份資料庫,否則不要永久刪除項目』則必須經過已刪除郵件天數,且已經過備份才會真正刪除郵件
!限制訊息 (message, 即郵件) 大小
*Ex2007 中郵件傳送與接收大小的限制
**組織限制:組織組態 → 集線傳輸 → 通用設定 <<toBalaFlashPlayer "flash/06-10-organizational_limit.swf" "有影片有真相" "800" "620">>
**傳輸限制
***接受大小上限
***傳送大小上限
***收件者人數上限
**通用限制:Exchange server 2003 Global Settings | message Delivery → Properties
***若組織內有 Ex 2000 / 2003 等老舊版本,則有通用限制
**接收連接器限制:伺服器組態 → 集線傳輸 → 選擇伺服器 → 接收連接器 <<toBalaFlashPlayer "flash/06-11-recieve_connector_limit.swf" "有影片有真相" "800" "620">>
***郵件大小限制:接收的郵件大小限制
**傳送連接器限制:預設上尚未建立傳送連接器,組織組態 → 集線傳輸 → 傳送連接器 → 選擇連接器 → 內容 → 一般 <<toBalaFlashPlayer "flash/06-12-send_connector_limit.swf" "有影片有真相" "800" "620">>
**伺服器限制
**使用者限制 <<toBalaFlashPlayer "flash/06-13-user_mailbox_limit.swf" "有影片有真相" "800" "620">>
*當多項限制同時存在時,將會取得最嚴謹的限制
!顯示使用者信箱使用量、信件數量
{{item1{__新增管理介面資訊欄位__}}} <<toBalaFlashPlayer "flash/06-14-add_information_columns.swf" "有影片有真相" "800" "620">>
*無法透過圖形介面顯示 <<toBalaFlashPlayer "flash/06-15-display_user_mailbox_useage_and_sort.swf" "有影片有真相" "800" "620">>
{{{
get-mailboxstatistics |sort-object itemcount -descending | ft displayname, itemcount, totalitemsize, totaldeleteditemsie, dtabasename
}}}
{{item1{__Power Shell Script__}}} <<toBalaFlashPlayer "flash/06-16-power_shell_script.swf" "有影片有真相" "800" "620">>
!設定信箱特殊使用權限
!!代理傳送者權限
*授予代理傳送者權限給其他收件者,將允許那些收件者代表某個信箱使用者傳送電子郵件
**例如秘書可代理老闆傳輸郵件
*EMC → 信箱內容頁面 → 『郵件流程設定』 → 傳遞選項
*Outlook → 工具 → 選項 → 選項 → 代理人
*EMC 僅 Exchange 管理員有權限設定
{{item1{__EMC:老闆 flatfish 指定其代理傳送者__}}} <<toBalaFlashPlayer "flash/06-17-proxy_deliver_EMC.swf" "有影片有真相" "800" "620">>
{{item1{__Outlook:代理傳送者 ff 代替老闆發送信件__}}} <<toBalaFlashPlayer "flash/06-18-proxy_deliver_Outlook.swf" "有影片有真相" "800" "620">>
{{item1{__信箱擁有人自行指定代理傳送者__}}} <<toBalaFlashPlayer "flash/06-19-proxy_deliver_setup_by_mailbox_owner.swf" "有影片有真相" "800" "620">>
!以下列傳送權限 (SendAs Permission)
*與代理傳送者權限類似,但差異在接收者看不出來是代理人所寄出的
*設定後有時間延遲
*除透過 EMC 外,透過 dsa.msc (AD 使用者及電腦) 可以直接針對使用者帳戶的安全性設定中給予『以下列傳送』的權限,也可以達到相同的目的
**dsa.msc 的安全性設定中,還有『以下列接收』,可以授權該使用者接收其郵件
{{item1{__EMC:以下列傳送__}}} <<toBalaFlashPlayer "flash/06-20-sendas_by_EMC.swf" "有影片有真相" "800" "620">>
{{item1{__以下列傳送:收件人將不知道這是由別人送出的信件__}}} <<toBalaFlashPlayer "flash/06-21-sendas_effect.swf" "有影片有真相" "800" "620">>
{{item1{__透過 AD 使用者及電腦設定以下列『接收』的權限__}}} <<toBalaFlashPlayer "flash/06-22-sendas_by_dsa.msc.swf" "有影片有真相" "800" "620">>
!完整存取權限
*針對這個信箱有完整個存取權限,僅能存取,但無法做代理,無法透使用該信箱身份傳送郵件
!郵件連絡人 (contact)
*擁有郵件功能的 AD 連絡人,包含存在 Exchange 組織以外人員或組織的相關資訊
*常用來維護與公司有密切關係的外部人員
*沒有權限登入 AD 網域及存取網域中的資源
{{item1{__建立連絡人__}}} <<toBalaFlashPlayer "flash/06-23-create_contact.swf" "有影片有真相" "800" "620">>
!通訊群組 (Distribution Groups)
*Mail Universal Distribution Groups
**純粹用來作為郵件發送群組,將來不能設定各種資源使用權限
*Mail Universal Security Groups <<toBalaFlashPlayer "flash/06-24-create_mail_universal_security_group.swf" "有影片有真相" "800" "620">>
*Mail Non-Universal Groups
*Dynamic Distribution Groups <<toBalaFlashPlayer "flash/06-25-create_dynamic_distribution_group.swf" "有影片有真相" "800" "620">>
**不需要管理員手動設定,而是透過 AD 查詢自動產生
**Ex2003 中稱為查詢式 (Query-based) 通訊群組
**透過 LDAP 協定向 Global catalog 查詢
**需事先在使用者資料欄位中建立提供查詢的資料,例如部門別
**可自訂屬性 <<toBalaFlashPlayer "flash/06-26-customize_attribution.swf" "有影片有真相" "800" "620">>
!信箱管理常見問題
*OWA 登入後存取選項發生錯誤,Outlook 存取 ok
**在 Exchange 2007 上建立老舊的信箱 (使用 AD 使用者及電腦 建立的)
**Set-Mailbox mary-ApplyMandatoryProberties
*若使用者信箱由 Ex2003 轉移至 Ex2007,使用者使用 OWA 登入失敗,Outlook 登入成功
**DACL 繼承效果會被清除
**啟用帳戶內容的 DACL 繼承效果
*設定代理或以下列傳送權限後未生效
**AD 延遲,最慢可能會超過一天
**等待、重新開機
!電子郵件元件
*三個主要元件
**使用者代理器 (User Agent, MUA, 即用戶端軟體)
**郵件伺服器 (mail server, MTA, 伺服器軟體)
**傳輸協定:SMTP(傳送)、POP3(接收)、IMAP(接收)、http
***若需要安全性則需要 TLS(SSL),POP3S、IMAPS、HTTPS
***網際網路中 mail server 與 mail server 間僅有 SMTP 協定
!!Mail Server
*需替每個使用者維護一個信箱
*需維護一個 FIFO 的佇列 (Queue)
*server-server 間僅有 SMTP 協定,使用 TCP Port:25
|Deliver|user agent|proto|mail server|proto|mail server|proto|user agent|Reciever|
| A | Outlook2007 | MAPI | Ex2007 | SMTP | Ex2007 | MAPI | Outlook2007 | B |
|~| Outlook express | SMTP | ANY | SMTP | Ex2007 | HTTPS | IE7 |~|
|~| IE7 | HTTPS | ANY | SMTP | ANY | POP3 / IMAP | MUA |~|
!!Mail 訊息格式
*RFC 822:定義文字訊息格式
**header lines:
***From: 寄件人
......
*RFC 2045、2056 定義 MIME 多用途郵件延伸
**在訊息 header 中增加一行宣告 MIME content type
!郵件存取協定
*SMTP
*Mail access protocol
**POP3
**IMAP
**HTTP
!!POP3 協定
*身份驗證明碼傳送
*存取程序階段:
**list
**retr
**dele
**quit
!建立網際網路郵件伺服器條件
*一個合法有效的網域名稱
*一個有效的 IP 位址 (public address)
*設定正確的 DNS 伺服器
**MX、A 紀錄
**強烈建議建立反向查詢區域,因愈來愈多的垃圾信件檢查機制會做反向查詢,以確保不是垃圾信件
**A 紀錄對應到的 IP 是最外面的 IP,例如 Edge transport (若無則是 Hub transport),若有架設 NAT 防火牆,則是對應到外部的 Public IP
{{item1{__DNS 建立正向及反向區域,建立正確的 A 紀錄與 MX 紀錄__}}} <<toBalaFlashPlayer "flash/07-01-DNS.swf" "有影片有真相" "800" "620">>
{{item1{__查詢 DNS 是否能正確解析 A 與 MX 紀錄__}}} <<toBalaFlashPlayer "flash/07-02-query_DNS.swf" "有影片有真相" "800" "620">>
!建立基本的 Exchange 郵件伺服器
*啟動 POP3、IMAP 服務 <<toBalaFlashPlayer "flash/07-03-start_pop3_imap.swf" "有影片有真相" "800" "620">>
**透過 netstat 檢查開啟的 port 是否有 110 (POP3)、143(IMAP) <<toBalaFlashPlayer "flash/07-04-check_port_110_143.swf" "有影片有真相" "800" "620">>
*新增公認網域 (Accepted Domain) <<toBalaFlashPlayer "flash/07-05-create_accepted_domains.swf" "有影片有真相" "800" "620">>
**公認網域 Email address @ 後面的命名空間,就是電子郵件的尾碼
***ms1.hinet.net:mail server 的 FQDN
***hinet.net:DNS 中需增加一筆 A 紀錄,名稱為空
***192.168.100.129:ip-based 的公認網域 (提供沒有申請網域名稱的情況下使用) <<toBalaFlashPlayer "flash/07-06-ip_based_accepted_domain.swf" "有影片有真相" "800" "620">>
*新增電子郵件原則 <<toBalaFlashPlayer "flash/07-07-email_address_policies.swf" "有影片有真相" "800" "620">>
**定義收件人使用的郵件地址,就是收件者建立主要及次要的電子郵件地址
**一定要先設定好公認網域,才能被定義在電子郵件原則
*建立傳送連接器 <<toBalaFlashPlayer "flash/07-08-create_deliver_connector.swf" "有影片有真相" "800" "620">>
**SMTP 連接器是支援 SMTP @@color:red;單向@@連線的 Exchange 伺服器元件,用來作為與其他電子郵件伺服器的傳輸
**傳送連接器建立流程
###選擇預定用法:網際網路
###新增 SMTP 位址空間,『 * 』代表允許寄至整個 Internet
###選擇處理傳送郵件的方式
***標準方式:透過查詢 DNS MX 紀錄來傳送郵件
***透過智慧主機:smarthost,大型機構才會遇到
****當各部門都有自己的 mail server,但公司規定禁止各單位的 mail server 能將信件直接送到 Internet,僅公司的 Internet mail server 能送出,因此必須在各部門的 mail server 上設定公司的 Internet mail server 為智慧主機,輸入能解析的 FQDC 或是 IP
###當有多部 Hub transport 主機則需選擇出去的 Hub transport
*建立接收連接器 <<toBalaFlashPlayer "flash/07-09-allow_anonymous_in_reciver_connector.swf" "有影片有真相" "800" "620">>
**預設的 Default Mail1 不允許匿名使用者,因此無法接收來自其他 mail server 寄來的信件
!!SMTP
*典型的 Store & Forward
*NDR:Non-Deliver Report,當 mail server 收到來自網際網路上其他伺服器退回的郵件時,則會發送 NDR 通知原寄件者,但此時郵件仍儲存在佇列中等待傳送,並會持續傳送數次
!訊息傳輸診斷工具
*佇列檢視器 (Queue Viewer) <<toBalaFlashPlayer "flash/07-10-queue_viewer.swf" "有影片有真相" "800" "620">>
**顯示目前等待傳送的郵件佇列中是否有尚未寄出的信件
**無法存取網域中的信件代表出現傳送問題的信件,可透過其內容找出無法傳送的問題所在
**可針對佇列中的信件選擇處理方式:擱置、繼續、移除(回報 NDR)、移除(不傳送 NDR)
*郵件流程疑難排解員 (Mail Flow Troubleshooter) <<toBalaFlashPlayer "flash/07-11-mail_flow_troubleshooter.swf" "有影片有真相" "800" "620">>
**管理員需事先收集徵兆資訊服務
**此工具會分析可能的原因,並提出建議
***紅色錯誤顯示目前的錯誤
***若出現三角形黃色訊息,則代表『可能』的錯誤,但需管理員有經驗的判斷
*郵件追蹤 (Message tracking) <<toBalaFlashPlayer "flash/07-12-message_tracker.swf" "有影片有真相" "800" "620">>
**可讓管理員追蹤 Exchange 環境中路由傳送的特定郵件
**在 Mailbox 與 Hub transport 角色才有郵件追蹤的能力,因其他角色並無郵件追蹤相關資訊
**提供追蹤的參數越詳細,得到的結果將會更準確
**RecipientStatus:狀態經常作為判斷的主要依據
***電子郵件回應代碼 2 開頭表示沒問題,3 需額外判斷,4、5 則是錯誤
!Exchange 通訊清單
*Exchange 的通訊清單是收件者及其他 AD 物件的集合
*根據屬性透過 LDAP 查詢,讓使用者寄信時可以快速的找到收件者,而不需要使用者自行維護連絡人
*提供切割擁有郵件功能物件的機制
*僅提供 Outlook (MAPI) 及 IE - OWA 連線方式使用通訊清單
!預設的通訊清單
*早期區分 Global 與 Non-Global 通訊清單
*全域通訊清單中,不再提供切割子清單的功能
*全域通訊清單
**組織內所有『收件者』的資料都在裏面
**包含使用者、群組、連絡人、資源信箱皆在裏面
!建立自訂的通訊清單 <<toBalaFlashPlayer "flash/08-01-customize_address_list.swf" "有影片有真相" "800" "620">>
*建立自訂的通訊清單後無法馬上生效,若開啟快取功能可能最長需等待一天的時間以更新通訊清單,若無則數分鐘內即可
{{item1{__使用自訂的通訊清單__}}} <<toBalaFlashPlayer "flash/08-02-customize_addres_list-outlook.swf" "有影片有真相" "800" "620">>
!!避免特定使用者出現在通訊清單 <<toBalaFlashPlayer "flash/08-03-hide_specified_user.swf" "有影片有真相" "800" "620">>
*僅管理員能設定
!!通訊清單存取權限
*使用 adsiedit.msc (安裝 support tools)
*限制通訊清單使用正面表列或負面表列
*configuration
**CN=Services
***CN=Microsoft Exchange
****CN=組織名稱
*****CN=Address Lists Container
{{item1{__通訊清單的存取控制__}}} <<toBalaFlashPlayer "flash/08-04-ACL_on_address_list.swf" "有影片有真相" "800" "620">>
!!建立全域清單
*不是非常必要,且只能依賴命令列指令
{{{
New-GlobalAddressList -Name <String> -IncludedRecipients <Nullable> -ConditionalCompany <String>
}}}
!離線通訊清單 (Offline Address Book, OAB) <<toBalaFlashPlayer "flash/08-05-OAB.swf" "有影片有真相" "800" "620">>
*支援行動使用者在離線的狀態下仍可由事先下載的通訊清單中找到要郵寄的收件者
*不同版本 Exchange的 OAB 都透過不同的發佈方式,且都有 Bug
*Ex2007 使用 HTTP(S) (IIS) 和 Background Intelligent Transfer Service (BITS) 取代 Ex2003 之前公用資料夾的發佈方式
*內定上存在一個預設的 OAB,只包含 GAL (Global Address List),可額外增加
!!建立自訂的離線通訊清單 <<toBalaFlashPlayer "flash/08-06-create_OAB.swf" "有影片有真相" "800" "620">>
*需 IIS
*指定要提供離線通訊清單的內含 (全域、或非全域)
*選擇發佈方式,若提供 Outlook 2003 則需選擇啟用公用資料夾發佈,若有更早的 Outlook 版本,則在創造後需在內容對話盒再修改
*OAB 綁死在信箱資料庫,因此預設的 OAB 將會影響資料庫內使用者使用的 OAB
*OAB 建立完後需再綁在指定的信箱資料庫 <<toBalaFlashPlayer "flash/08-07-bind_OAB_to_mailbox_databox.swf" "有影片有真相" "800" "620">>
!!OAB 更新維護時間 <<toBalaFlashPlayer "flash/08-08-OAB_update_schedule.swf" "有影片有真相" "800" "620">>
*OAB 更新時段需配合資料庫,資料庫維護時間必須包含 OAB 更新時間
!!設定下載離線通訊清單網址 <<toBalaFlashPlayer "flash/08-08-set_OAB_url.swf" "有影片有真相" "800" "620">>
*發佈的網頁在 (通常在 CAS) IIS 預設網站中 OAB 目錄
*設定用戶端存取中離限通訊錄發佈
!!第一次手動更新 <<toBalaFlashPlayer "flash/08-09-manual_update_OAB.swf" "有影片有真相" "800" "620">>
*第一次產生 OAB 可能因為更新時間過久而遲遲不產生,因此第一次手動更新
!!用戶端透過 Outlook 下載通訊錄 <<toBalaFlashPlayer "flash/08-10-outlook_download_OAB.swf" "有影片有真相" "800" "620">>
*使用者需在能連線至 Exchange Server 的情況下
*實際測試上有時間的延遲
!公司內部網路存取 Exchange Server
*透過標準的 Internet 電子郵件協定 (POP3、IMAP、SMTP) 則僅能使用電子郵件的功能
*只有透過 Outlook - MAPI 可使用所有 Exchange 協同作業的功能
*目前越來越主流的連線方式:HTTP、HTTPS (OWA)
*手機、PDA 則需透過 ActiveSync
!Internet 使用者存取公司內 Exchange Server
*因公司內防火牆,建議透過 OWA(HTTP、HTTPS),防火牆開放 TCP 80、443 即可透過防火牆
*Outlook Anywhere,舊版設定極其麻煩,主要是將 RPC 封包前再加上一個 HTTPS 表頭,因此也只需要開放 TCP 80、443
**雖然設定上麻煩,但是功能仍然多過 OWA
!!用戶端存取方式
*MAPI 用戶端
**使用 Outlook,但不易通過防火牆
***解決方案:Outlook Anywhere
**可使用完整個郵件及協同作業功能
***伺服器與用戶端軟體需搭配,例如 Exchange Server 2007 與 Outlook 2007
*POP3、IMAP 用戶端
**網際網路標準,容易通過防火牆
**只有電子郵件功能
*OWA 用戶端
**IE 瀏覽器
**提供平台與位置無關的存取方式、容易通過防火牆
*ActiveSync
**提供無線使用者使用行動設配連上 Exchange Server
!佈署用戶端存取伺服器
*決定支援哪些存取協定
**Ex2007 預設僅安裝 OWA、MAPI
!Outlook
!!功能
*電子郵件傳遞
*行事曆
*連絡人管理
*工作
*記事
*Rss Feed
!!Outlook 2007 特性
*採用 MS Office Fluent 新使用者介面
*立即搜尋所有資訊供 (Instant Search)
*@@color:red;快取 Exchange 模式@@
**預設啟動
**開啟後可能會有資訊過時的問題,例如離限通訊清單
*MAPI 壓縮與緩衝打包
**高度壓縮
*預覽附件
**提供使用者在不開啟信件時的預覽,以降低中毒的機率
*新增待辦事項列 (to-do bar)
*採用新的顏色分類、快速旗標方法
*提供信任中心:所有安全性相關功能都在信任中心
**安全性大為提升
!!Outlook 2007 使用者介面 <<toBalaFlashPlayer "flash/09-01-outlook_customize_pane.swf" "有影片有真相" "800" "620">>
|功<br><br>能<br>窗<br>格|檢<br>視<br>工<br>作<br>視<br>窗|讀<br>取<br>窗<br>格|待<br>辦<br>事<br>項<br>列|
*檢視工作視窗跟隨著功能窗格有不同的畫面
*除了檢視工作視窗外,皆可關閉或縮小
!Outlook 設定檔 <<toBalaFlashPlayer "flash/09-02-create_outlook_profile.swf" "有影片有真相" "800" "620">>
*Outlook 設定檔 (Profile)
*預設安裝時建立一個名為『 Outlook 』的設定檔
*使用者可建立多個 Profile
*一個 Profile 只允許一個 Exchange MAPI 的連線,但允許多個電子郵件信箱 (POP3 帳號) <<toBalaFlashPlayer "flash/09-03-create_2nd_email_account.swf" "有影片有真相" "800" "620">>
*設定 Microsoft Exchange 伺服器時,需輸入能解析的名稱或 IP
**輸入 IP 需要能解析主機名稱
**輸入 FQDN 需要能解析為 IP
{{item1{__Outlook Express__}}} <<toBalaFlashPlayer "flash/09-04-outlook_express.swf" "有影片有真相" "800" "620">>
!行事曆
{{item1{__新增行事曆項目__}}} <<toBalaFlashPlayer "flash/09-05-new_calendar.swf" "有影片有真相" "800" "620">>
*製作行事曆
**在行事曆下新增資料夾即為產生第二個行事曆 <<toBalaFlashPlayer "flash/09-09-create_2nd_calendar.swf" "有影片有真相" "800" "620">>
*共用行事曆
**開啟他人分享的行事曆
***需使用 Outlook - MAPI 連線方式
{{item1{__由接收共用行事曆郵件開啟共用行事曆__}}} <<toBalaFlashPlayer "flash/09-06-recieve_calendar_sharing_mail.swf" "有影片有真相" "800" "620">>
{{item1{__直接開啟共用行事曆__}}} <<toBalaFlashPlayer "flash/09-07-open_shared_calendar.swf" "有影片有真相" "800" "620">>
**分享行事曆給他人
***以角色設定不同的權限
****檢閱者:只能讀取
****編輯者:可建立項目、編輯全部
**傳送行事曆
{{item1{__共用行事曆__}}} <<toBalaFlashPlayer "flash/09-08-sharing_calendar.swf" "有影片有真相" "800" "620">>
*發佈行事曆
!連絡人
*儲存個人在 Exchange mailbox 內的連絡人
!工作 <<toBalaFlashPlayer "flash/09-10-task.swf" "有影片有真相" "800" "620">>
*個人重要的工作日誌
{{item1{__工作項目會在待辦事項列中顯示直到完成__}}} <<toBalaFlashPlayer "flash/09-11-task_on_todo_pane.swf" "有影片有真相" "800" "620">>
{{item1{__工作完成__}}} <<toBalaFlashPlayer "flash/09-12-task_completed.swf" "有影片有真相" "800" "620">>
!RSS (Really Simple Syndication)
*以 XML 主動發佈訊息的服務
*透過訂閱 RSS (subscript) 後,該網站則會主動的通知訂閱者
!Outlook 的維護問題
*定期備份
**使用預設的匯出功能
***建議匯出『個人資料夾檔案(*.pst)』,Outlook 自己的匯出檔案,速度最快、檔案最小
**下載 Outlook 2007 / 2003 / 2002 Add-in:Personal Folders Backups
***提供使用者定期
*修復
**不同版本的 作業系統 有不同的資料夾存放 pst 檔,因此建議由備份軟體執行備份
**修復 PST 檔、OST 檔
***舊版 PST 檔有 2G 的容量限制
***scanpst.exe:修復 pst 檔
***scanost.exe:修復 ost 檔
****pst 檔需在使用 POP3 或是 IMAP,POP3 會下載整個郵件,而 IMAP 只會下載表頭,MAPI 則是整個信件存放在 mail 而不會產生 pst 檔
{{item1{__匯出 (備份) Outlook 收件夾__}}} <<toBalaFlashPlayer "flash/09-13-backup_personal_email_by_outlook.swf" "有影片有真相" "800" "620">>
{{item1{__匯入 (還原) Outlook 收件夾__}}} <<toBalaFlashPlayer "flash/09-14-import_personal_email_by_outlook.swf" "有影片有真相" "800" "620">>
{{item1{__安裝個人資料夾備份 (僅管理員能安裝,安裝後又只有管理員能使用,建議重新包裝為 MSI 透過群組原則軟體佈署)__}}} <<toBalaFlashPlayer "flash/09-15-install_personal_folders_backup.swf" "有影片有真相" "800" "620">>
!使用 AutoDiscover 服務
*加入網域的 Outlook 用戶端會全自動完成設定檔
**Outlook 與 Exchange 機器必須處於同一個網域
!!設定 AutoDiscover 服務,針對非網域用戶端電腦或不同網域用戶端電腦
*安裝用戶單存取 (CAS) 伺服器
**會在 IIS 上建立 Autodiscover 虛擬目錄
*在 AD 的 DNS 伺服器建立一個 autodiscover 的主機紀錄對應至 CAS 伺服器
*在 EMS 下輸入指令
{{{
Set-OutlookProvider -id exch -server:srv-cht-ex1
}}}
*非網域內用戶端電腦一定需要輸入 email 與密碼
*測試 Autodiscover:登入後開啟 Outlook,ctrl+左鍵點擊 Outlook 右下角小圖示,選擇『測試電子郵件自動設定』
{{item1{__設定自動完成設定檔 AutoDiscover__}}} <<toBalaFlashPlayer "flash/09-16-autodiscover-1.swf" "有影片有真相" "800" "620">>
{{item1{__設定自動完成設定檔 AutoDiscover -2__}}} <<toBalaFlashPlayer "flash/09-17-autodiscover-2.swf" "有影片有真相" "800" "620">>
{{item1{__非網域使用者自動完成設定檔__}}} <<toBalaFlashPlayer "flash/09-18-non_domain_user_autodiscover.swf" "有影片有真相" "800" "620">>
!!設定 POP3 組態 - 繫結的 IP 位址與連接埠
*需 Exchange 2007 SP1 才加入圖形介面設定
*伺服器組態 → 用戶端存取
*驗證:
*Exchange 2000 / 2003:純文字驗證登入 (POP3+SPA),僅密碼加密,資料及帳號仍然明碼傳送
**用戶端勾選安全密碼驗證登入 (SPA),但不勾選 SSL <<toBalaFlashPlayer "flash/09-20-outlook_express_security_password_authenticate.swf" "有影片有真相" "800" "620">>
*ISP 提供的 POP3:純文字登入,明碼傳送帳號密碼
**用戶端不勾取使用安全密碼驗證登入及 SSL <<toBalaFlashPlayer "flash/09-19-outlook_express_plain_text.swf" "有影片有真相" "800" "620">>
*Exchange 2007:安全登入,POP3+SSL=POP3S
**需提供憑證名稱
**用戶端勾選 POP3 + SSL,不勾選安全密碼驗證登入 <<toBalaFlashPlayer "flash/09-21-outlook_express_security_login.swf" "有影片有真相" "800" "620">>
!!POP3 內容 - 連線標籤頁
!開啟 POP3 與 IMAP 紀錄功能
*預設不啟動
*Exchange server 2007 安裝目錄下 ClientAccess\PopImap\ 內
**Microsoft.Exchange.Pop3.exe.config,記事本開啟後尋找 『protocollog』字串,修改 value="true"
*啟動後會紀錄在應用程式日誌檔中
{{item1{__啟用 POP3 日誌功能__}}} <<toBalaFlashPlayer "flash/09-22-enable_pop3_log.swf" "有影片有真相" "800" "620">>
!Outlook Anywhere
*為了解決 RPC 動態連接埠不易通過防火牆,及在網際網路上其弱點經常被攻擊的問題
*前版稱做 RPC over HTTP(s)
*防火牆僅需開啟 TCP 80 或 443 Port
!!佈署 Outlook Anywhere 步驟
*在 CAS 安裝@@color:red;信任的憑證@@
**安裝 CAS 時會自動附帶一個測試用憑證,但使用 Outlook Anywhere 時會被拒絕
**信任的憑證條件:
***由可信賴的 CA 發行
***沒有過期
***沒有被撤銷
***申請的名稱與連線名稱一樣
**參考匯入憑證
*CAS 負責 HTTPS 封裝與解封裝 (由 RPC over HTTP Proxy 負責)
**安裝 HTTP Proxy 上的 RPC <<toBalaFlashPlayer "flash/09-23-CAS_install_rpc_over_http_proxy.swf" "有影片有真相" "800" "620">>
***安裝後 IIS 預設的網站內會新增兩個虛擬目錄:Rpc、RpcWithCert <<toBalaFlashPlayer "flash/09-24-verify_rpc_over_http_proxy.swf" "有影片有真相" "800" "620">>
****修改 Rpc 與 RpcWithCert 內容,目錄安全設定頁面,編輯驗證,核銷匿名,改用基本驗證
****RPC over HTTP Proxy 基本上支援整合式及基本驗證
****修改後建議重新啟動 IIS
***網頁服務延伸中,應該會出現一個 RPC Proxy Server 延伸,且其狀況為『已允許』
*CAS 伺服器 中點選啟用 Outlook Anywhere
!用戶端存取 Outlook Anywhere <<toBalaFlashPlayer "flash/09-25-enable_outlook_anywhere.swf" "有影片有真相" "800" "620">>
*登入帳號密碼不能空白
*Exchange Proxy 伺服器 URL 必須與憑證相符合
*驗證方式必須符合 (雙方都基本驗證,若伺服器選擇整合式驗證,則用戶端必須選擇 NTLM 驗證
*若有勾選快取模式,則需登出後重新登入
*按ctrl + 滑鼠左鍵點擊畫面右下方 Outlook 小圖示,選擇連線
!何謂 OWA
*OWA 是 Exchange Server 所提供的網頁式存取用戶端
!OWA 優點
*提供非微軟系統用戶端一個簡單存取 Exchange 伺服器的能力
*支援 Exchange 的遠端存取使用者輕易存取 Exchange 伺服器
!OWA 功能特性
*更接近 Outlook 的使用者介面
*變更密碼,可直接變更網域密碼
*設定 out-of-Office message
*拼字檢查 (英文)
*支援鍵盤滑鼠捷徑選單 (快速鍵)
*設定郵件簽名
*存取通訊清單 (Access Address List)
*__存取遠端機器文件__:存取公司內共用資料夾、及 SharePoint
*__WebReady 文件開啟能力__:針對著名的文件,即使沒有相關開啟軟體,也能直接透過 Web 介面開啟
*加強的安全性與私密性
**預設使用表單式驗證 (Form-based)
***目前最安全的驗證方式
**支援 S/MIME:service pack 1 才有
**限制用戶存取附件檔案類型
**支援防垃圾信功能,不強
!OWA 兩種介面:高階 (Premium) vs. 基本 (Basic)
*基本 (basic)、(light)
**提供較快的連線、但功能較少
**只有兩個窗格
**提供非 IE 5.0 以前與其他非 IE 瀏覽器連線
*高階 (Premium)
**提供較多的功能
**需 IE 6.0+
!!OWA - Light 缺乏以下功能:
*拼字檢查、閱讀窗格、通知提醒、每週行事曆檢視、整合 SharePoint 服務和 UNC 檔案分享、外觀選項、語音郵件選項
!!使用者第一次登入需選擇語言與時區
!!OWA 虛擬目錄
*OWA:使用者存取其儲存在 Ex2007 伺服器上的信箱
*Exchange:使用者存取其儲存在 Exchange 2000 / 2003 伺服器上的信箱
*Public:存取 Exchange 2003 / 2000 伺服器上的公用資料夾
*Exchweb:存取其先前版本的 OWA 虛擬目錄
!OWA 設定
*設定 OWA 外部、內部連接的 URL <<toBalaFlashPlayer "flash/10-01-OWA_options.swf" "有影片有真相" "800" "620">>
*設定驗證方式
**表單式驗證
***儲存使用者名稱及密碼於記憶體工作階段的 Cookie 中,而非瀏覽器,且一定需要 SSL 連線
***當使用者關閉瀏覽器,就會將 Cookie 清除,倘若一段時間不使用,也會被清除
***選擇『公用或共用電腦』,則閒置 10 分鐘後清除 Cookie,且中斷連線
***選擇『私人電腦』,則閒置 20 分鐘後清除 Cookie,且中斷連線
***可降低網路流量 (具壓縮功能)
***高安全性:需 SSL、保護驗證,逾時自動登出,IE6sp1 登出時自動清除登入的私密資料
***提供叫彈性的用戶端連線介面
*設定 OWA 功能分割
**一旦某功能需停用,則所有 OWA 使用者都被停用
**要針對個人停用某項功能,則需命令列指令
***停用某人行事曆功能
{{{
Set-CASMailbox {username} OWACalendarEnabld: $False
}}}
*郵件附件處理方式:公用電腦檔案存取 → 直接檔案存取 自訂
**永遠允許:優先權最高,可寫檔案附檔名或是 MIME Type
**永遠封鎖:大部份為具備有執行能力的檔案
**強迫儲存:優先權最低,可下載至用戶端電腦,而不允許在伺服器上執行
**優先權高的會覆蓋低的設定,例如 .exe 同時被設定為永遠允許、及永遠封鎖,則會以永遠允許為主
**不在上面三項處理方式的檔案類型則會落在未知的檔案中,預設為強迫儲存
**直接檔案
*開啟 WebReady 文件檢視:
**開啟的文件檔案格式需設定在永遠允許中
**預設若本機有可開啟文件的應用程式時,使用本機的應用程式開啟
**若勾選『當有可用的轉換器時強制使用 WebReady 文件檢視』,則不管用戶端電腦有無可開啟文件的應用程式,自動以 WebReady 開啟文件
*從遠端檔案伺服器上的下列位置存取檔案
**Windows 檔案共用
**Windows SharePoint Services
!用戶端 OWA 介面
*郵件詳細資料:Ex2007 新增的功能,有助於檢查信件傳送來源
*建立多個行事曆及改變為週曆:Ex2007 新增的功能
*復原已刪除的郵件 (刪除的信件中被刪除的信件可在選項中救回 (選項 → 復原刪除的郵件)
**兩層垃圾桶??
!WebReady 文件檢視 (開啟為網頁)
*即使用戶端電腦沒有安裝能檢視特定文件檔案的應用程式,仍可透過 WebReady 轉換為 HTML 在網頁上顯示
!OWA 直接檔案存取 (Direct Access)
*允許 OWA 直接存取 SharePoint Services 伺服器及檔案伺服器的共用資料夾
*提供更有彈性的檔案共享服務
*只適用於高階 (Premium) 用戶端介面
*遠端檔案伺服器
**封鎖清單:優先權高
**允許清單:優先權低
***設定主機名稱
**尾碼
!變更 OWA 附件大小限制
*Ex2007 預設附件的收發大小為 10MB,OWA 預設為 30MB
*修改預設 OWA 附件大小:
{{{
c:\program files\microsoft\exchange server\clientAccess\owa
編輯 web.config
搜尋關鍵字 maxRequestLength
預設 30000,單位為 KB
}}}
!變更 OWA 網址路徑
*IIS 設定導向 <<toBalaFlashPlayer "flash/10-02-OWA_redirect.swf" "有影片有真相" "800" "620">>
*需在 CAS 上的 IIS 並未提供其他服務時
!改變 公用/私人 電腦 Cookie 驗證逾時
*透過修改登錄資料庫
{{{
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Service\MSExchange OWA
新增 DWORD:PublicTimeout,PrivateTimeout
十進位值:1 ~ 43200
}}}
*透過命令列
{{{
Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\MSExchange OWA' -name {PublicTimeout / PrivateTimeout} -value {逾時時間} -type dword
}}}
*修改後重新啟動 IIS
!使用 OWA 高度壓縮
*預設採用低度壓縮,可設為高度壓縮以提高效能
#Get-OwaVirtualDirectory:顯示目前虛擬目錄的 ID
#Set-OwaVirtualDirectory -identity "owa <預設的網站>" -GzipLevel High
**High、Low、off
!特殊需求不使用 SSL
*取消表單式驗證,改為一般驗證
*IIS 核銷使用 SSL 通道
*要做安全性管理:先要知道遭受哪些威脅
!電子郵件常見的威脅
*由外部郵件夾帶惡意程式
*由外部郵件傳送的垃圾廣告
*DHA (Directory Harvest Attack):竊取公司內部通訊清單
*釣魚詐騙 (phishing):透過誘騙收件者點選信件中的超連結,連上偽造的網站,達到竊取個人資料等目的
*僵屍郵件 (Zombie Messges):假冒某人的 Email 來傳遞訊息
*違反政策 (Policy Violation):將公司內文件透過電子郵件傳送至 Internet
!保護電子郵件
*Anti-Spam
*Anti-Virus:微軟 Forefront
*Anti-Phishing:微軟 ForeFront
*Policy Management:Third Party 軟體
*Zombie Protection
*DHA / Dos Protection
!Exchange Server 安全性層面
#作業系統安全性:作業系統不安全,Exchange 就不用提安全
#Active Directory 安全性:Exchange server 是全世界與 AD 最緊密結合的應用程式,因此 AD 的安全性格外重要
#Exchange 伺服器安全性
#網路傳輸安全
#IIS 安全性 (OWA 使用)
#用戶端安全性 (Outlook)
*安全原則:防禦縱深 (Defense in Depth),每一項威脅都要有兩個以上的防護措施 - Multiple Layer Protection
{{{
例如 Anti-Spam
第一層透過 Anti-Spam 閘道器過濾 95% 垃圾信件
第二層透過 Exchange server
第三層透握 MUA (例如 Outlook)
}}}
!!OS 與 AD 的安全性管理原則
*採用嚴謹的安全性範本
**增加攻擊的困難度
*即時安裝最新的 Service Pack 與安全性修正檔
**避免有心人士利用弱點進行攻擊
*嚴謹的密碼原則
**避免員工使用容易被破解的密碼
!!Exchange server 安全性管理原則
*確保存取 Exchange Server 的安全性
**禁止未經授權的存取
**提供嚴謹的驗證方法
*確保通訊的安全性
**禁止未經授權的通訊
**採用加密傳送
*過濾垃圾郵件 (Anti-Spam)
*攔阻不安全的電子郵件
**惡意程式掃描:病毒、木馬、蠕蟲
**附件攔阻
!!避免不安全的 Exchange 連線驗證方式
*預設禁止 POP3、IMAP、HTTP 不加密連線
**Ex2003 預設使用不加密
!!確保用戶傳遞郵件過程安全性
*Outlook MAPI 用戶傳輸安全
**Outlook 2007 + Exchange 2007 預設 RPC 有加密 <<toBalaFlashPlayer "flash/11-01-Outlook_enable_encyption.swf" "有影片有真相" "800" "620">>
**Outlook 2003 + Exchange 2003 預設 RPC 未加密
***Outlook 2003 安全性選項中需開啟加密處理 Outlook 與 Exchange 加密
*POP3S、IMAPS 用戶傳輸加密
*OWA 傳輸安全
!!電子郵件傳遞加密
*POP3S:僅用戶端與伺服器端有加密
**網際網路中郵件伺服器與伺服器間並未使用加密連線
!電子郵件安全性協定
*學術上電子郵件安全協定
**MTA (TLS、SSL)
**MUA:使用者對使用者的電子郵件加密
***S/MIME:網際網路標準,業界容易接受
***PGP:也是網際網路標準
!!S/MIME
*S/MIME 特性
**確認數位簽署(Digital Signature),確保傳送來源具備法律上與科學上的不可否認性
**具備加密:確保資料傳遞的私密性,不被外洩
**具備完整性 (Integrity):避免郵件傳送過程被竄改
*MIME 是定義電子郵件訊息的業界標準格式
*S/MIME 則是安全性多用途網際網路郵件延伸的縮寫 (Security / Multipurpose Internet Mail Extension)
!!使用 S/MIME
*需先從可信賴 CA 申請並下載電子郵件用途的憑證 (可使用國內的自然人憑證)
*用戶端存取
**Outlook
**Outlook Express
**OWA
*收件者寄件者雙方都需要憑證
{{item1{__Outlook express 匯入憑證__}}} <<toBalaFlashPlayer "flash/11-02-outlook_express_cert_import.swf" "有影片有真相" "800" "620">>
{{item1{__Outlook express 寄出帶有數位簽章的郵件__}}} <<toBalaFlashPlayer "flash/11-03-send_digital_signature_outlook_express.swf" "有影片有真相" "800" "620">>
{{item1{__Outlook express收到帶有數位簽章的郵件__}}} <<toBalaFlashPlayer "flash/11-04-recieve_digital_signature.swf" "有影片有真相" "800" "620">>
{{item1{__Outlook 寄出帶有數位簽章的郵件__}}} <<toBalaFlashPlayer "flash/11-05-outlook_send_digital_signature.swf" "有影片有真相" "800" "620">>
{{item1{__Outlook 收到帶有數位簽章的郵件__}}} <<toBalaFlashPlayer "flash/11-06-outlook_recieve_digital_signature.swf" "有影片有真相" "800" "620">>
!垃圾郵件
*沒有預期會收到、且不想收到的電子郵件
**UCE:不請自來的商業性電子郵件
**UBE:不請自來的大量電子郵件
*垃圾郵件也是主觀定義,同一封郵件對某些人來說可能是垃圾郵件,對另外一群人可能不是
!!SPAM 運作模式
#Spammer 設法取得你的郵件位址
**使用 Web Spider 或 Troller / Crawler (spyware)
**購買 mailing list
**進行字典攻擊取得名單
**Embedded Web-Beacons:透過 sniffer 取得 http 表頭的電子郵件位址
#寄出全球百萬封垃圾信
**使用 Open proxies 或 open relays:不需帳號審核即可寄信的伺服器
**使用錯誤的主旨和身份識別
**從多個位址寄出
#大部份 ISP 和企業嘗試拒絕垃圾信
**利用過濾 SPAM 軟體
**安全清單和拒絕名單
**Challenge 和 Response
**成功減少 80% 以上的 SPAM
#部份垃圾郵件傳送至使用者信箱
**Spammer 低成本、賺大錢
!!垃圾廣告信的問題
*員工花費不少時間處理垃圾信,降低了生產力
*浪費不必要網路資源:Internet 頻寬、Mail Server 計算資源
*可能內含惡意、間諜程式 (釣魚攻擊)
!!防垃圾廣告郵件
*原則:防禦縱深 (多層次保護、多層次抵擋)
**防垃圾閘道器 (Edge Transport Role、硬體)
**郵件伺服器 (Exchange Server 2007)
**郵件用戶端 (Outlook、Outlook Express、OWA)
!Exchange 防垃圾架構
#Connection Filtering
#SMTP Filtering Layer
**收件者、寄件者過濾
**Sender ID (需額外付費)
#Content Filtering 內容過濾 (Ex2003 智慧型篩選器)
**寄件者信譽檢查 Sender Reputation
**Contend Filtering - 內容關鍵字篩選、SCL 分數設定
*Exchange 不支援『灰名單 (greylist)』
*Antivirus scanning:微軟 Forefront for Exchange server
**神推薦 GFI
!安裝防垃圾元件
*Ex2007 預設安裝在 Edge Transport server,如果需要在 Hub Transport Server 上使用垃圾篩選,則需手動安裝
*使用命令 <<toBalaFlashPlayer "flash/11.2-01-install_anti_spam_on_hub_transport.swf" "有影片有真相" "800" "620">>
{{{
cd "\program files\microsoft\exchange server\scripts"
install-Antispamagents.ps1
安裝完後重新啟動 Microsoft Exchange Transport 服務
管理主控台若已經開啟需要關掉重開
檢查 組織組態 → 集線傳輸 多一個 反垃圾郵件 標籤頁
檢查 伺服器組態 → 集線傳輸 多一個反垃圾郵件 標籤頁
}}}
*
!多種 Exchange 防垃圾功能
!!黑名單、白名單運用
*IP Allow List 允許的寄件 IP 設定 (白名單):優先權最高,通過白名單的就不會做其他比對 <<toBalaFlashPlayer "flash/11.2-02-ip_allow_list.swf" "有影片有真相" "800" "620">>
**單一 IP 位址
**整個網段
**IP 範圍
*IP Block List 封鎖的寄件 IP 設定 (黑名單) <<toBalaFlashPlayer "flash/11.2-03-ip_block_list.swf" "有影片有真相" "800" "620">>
**Ex2007 新增封鎖到期時間
**黑名單對阻擋廣告信件用途不大,網路上 Spammer 不是透過假造就是透過代理,因此若要使用黑名單就需要透過網路上機構提供黑白名單
*IP Allow List Provider 封鎖的寄件 IP 提供者設定:神說:目前為止沒看過不要錢的
*IP Block List Provider <<toBalaFlashPlayer "flash/11.2-04-ip_block_list_provider.swf" "有影片有真相" "800" "620">>
**DNS RBL 即時黑名單
***一種以網域名稱為主黑名單 (DNS-based Blackhole List)
***由特定網站公佈一系列與垃圾郵件有關的 IP 位址黑名單,讓網路上的收信服務可以透過 DNS 機制查詢
***需留意 DNS Zone 與 回應代碼 (需到各黑名單提供者網站查詢,這部份並未統一)
***查詢 Wikipedia - DNS bl
***建議增加兩個以上的黑名單提供者,但會拖慢信件的傳遞
***若協力廠商設定不當,造成開啟 Open Relay 以致被列入黑名單,則可在例外狀況中增加例外狀況的 Email address
****若被不當設定而列入黑名單,必須透過電話與黑名單提供者連絡,確定自己關閉 Open Relay,當提供者測試無誤則才會除名於黑名單之外
*寄件者篩選:寄件者的黑名單,可輸入單一電子郵件地址或是整個網域 <<toBalaFlashPlayer "flash/11.2-05-sender_filtering.swf" "有影片有真相" "800" "620">>
**預設採取拒絕郵件:退回,寄件者會收到退信通知
**將封鎖寄件者的郵件加上戳記並繼續進行:加上戳記後交由後方的內容篩選器決定
**封鎖寄件者為空白的郵件:通常寄件者空白的信件是由特殊軟體寫出的信件,強力建議核選 <<toBalaFlashPlayer "flash/11.2-06-block_blank_sender.swf" "有影片有真相" "800" "620">>
*收件者篩選 <<toBalaFlashPlayer "flash/11.2-07-recipient_filtering.swf" "有影片有真相" "800" "620">>
**封鎖收件者不在全域通訊清單的郵件
!!內容篩選 <<toBalaFlashPlayer "flash/11.2-08-contend-filtering.swf" "有影片有真相" "800" "620">>
*新版的智慧型篩選器 (IMF 3)
**每一封垃圾信可能性評等 1 ~ 9 級 (SCL, SPAM Confidence)
***0:不可能為垃圾郵件
***1 ~ 9:數字越大,越有可能為垃圾郵件
**3個層級的 SCL 分數設定
***刪除:直接刪除,原寄件者不會得知
***拒絕:直接退回,原寄件者會收到退信通知
***隔離:做更進一部分析
***越下面設定的等級必須越小
*例外狀況:設定不使用內容篩選的收件者信箱
*自訂文字:增加內容篩選的黑名單、白名單
**設定自訂文字的內容篩選必須小心,以免被誤用
!!寄件者信譽 <<toBalaFlashPlayer "flash/11.2-09-SRL.swf" "有影片有真相" "800" "620">>
*新的反垃圾郵件功能
*寄件者信譽等級 (SRL) 依照下列統計資料計算
**HELO / EHLO 分析
**反向 DNS 查閱:當沒有設定反向區域,則會被拒絕
***透過內容篩選的 SCL 等級分析
***測試寄件者是否為 Open Proxy / Relay:啟動後會拖慢 (拖很大) 郵件傳遞速度,與加重 mail server 負擔,因此需要自行評估要不要開啟
*根據上述評分分為 0 ~ 9 個等級 (SRL 等級),設定不同封鎖 X 值
!Outlook 郵件篩選功能 <<toBalaFlashPlayer "flash/11.2-10-outlook_anti-spam.swf" "有影片有真相" "800" "620">>
*垃圾保護郵件層級:建議設定為『高』
*安全的寄件者
*封鎖的寄件者
*國際
**封鎖的頂層網域清單
**封鎖的編碼清單
*自訂電子郵件規則
{{item1{__Outlook 自訂的內容篩選__}}} <<toBalaFlashPlayer "flash/11.2-11-outlook_content_filtering.swf" "有影片有真相" "800" "620">>
!使用 Ex2007 憑證
*SMTP、OWA、RPC~~over~~HTTP、IMAP4、POP3、ActiveSync
*目前 Internet 使用 X.509 憑證
**早期一個服務需申請一個憑證
*Ex2007 支援主體別名憑證
**SAN (subject alternative name):允許在一個憑證內包含多個主體名稱 (多個 FQDN 或 Common Name)
**貴很多..
*產生憑證請求檔
{{{
New-ExchangeCertificate -generaterequest -subjectname "dc=com,dc=flatfish,o=FF com,cn=mail1.flatfish.com" -domainname srv-cht-ex1,srv-cht-ex1.flatfish.local,mail1.flatfish.com,flatfish.local,flatfish.com -FriendlyName mail1 -PrivateKeyExportable $true -path c:\certrequest.txt
}}}
*向 CA 發行公司申請合法的憑證 <<toBalaFlashPlayer "flash/11.3-01-certification_request.swf" "有影片有真相" "800" "620">>
*匯入憑證
{{{
Import-ExchangeCertificate -Path c:\certnew.cer -friendlyname "FF mail1"
}}}
*利用 Get-ExchangeCertificate 取得憑證指紋 (thumbprint)
*啟用服務的憑證
{{{
Enable-ExchangeCertificate -thumbprint xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -services "IIS,POP,IMAP,UM"
}}}
**Outlook Anywhere 也是透過 IIS 管理 (https)
**UM:Unified Messaging
*移除不使用的憑證
{{{
Remove-ExchangeCertificate -Thumbprint xxxxxxxxxxxxxxxxxxxxxxxxxxxx
}}}
*安全性的身份驗證
**OWA:使用 HTTPS 表單式驗證
**POP3S
**IMAPS
*安全性的傳輸
**S/MIME (端點對端點的加密,重要!)
**TLS (Exchange)
*避免 Openrelay
**禁止未經授權的電腦或使用者轉送郵件
*防垃圾郵件
**有效的垃圾郵件篩選
***黑白名單、即時黑名單、智慧型篩選
*內容過濾
**偵測並攔截惡意程式
!測試特定 mail server 是否接受 openrelay
http://www.abuse.net
!Exchange 資料庫毀損徵兆
*無法掛載資料庫
*管理 Exchange 資料庫的 Microsoft Exchange Information Store 服務無法啟動
!處理原則
*修復資料庫
*還原資料庫
**先決條件要有預先備份
!Exchange 的災難維護工作
*修復資料庫
##使用 Troubleshootig Assistant
##使用命令指令 (Ex2003 僅提供命令列指令)
##使用復原儲存群組 (合併)
***可能毀損的只有少部份信箱、或是有信件刪除超過 30 天要救回
**Exchange 的修復屬於修壞性修復,若修復不當可能造成資料庫更嚴重的毀損
***使用 ntbackup
!Exchange 圖形介面修復資料庫
#卸載有問題的資料庫 <<toBalaFlashPlayer "flash/12-01-unmount_database.swf" "有影片有真相" "800" "620">>
**在卸載大型資料庫時可能需要等待一段時間
#檢查硬碟可用空間
**需要有 110% 的資料庫大小的可用空間
#執行Troubleshooting Assistant <<toBalaFlashPlayer "flash/12-02-troubleshooting_assistant.swf" "有影片有真相" "800" "620">>
#裝載資料庫
{{item1{__顯示與資料庫相關的事件日誌__}}} <<toBalaFlashPlayer "flash/12-03-exchange_event_log.swf" "有影片有真相" "800" "620">>
{{item1{__驗證資料庫及交易紀錄檔__}}} <<toBalaFlashPlayer "flash/12-04-transaction_log.swf" "有影片有真相" "800" "620">>
!備份 Exchange 伺服器
*信箱資料庫
**使用 ntbackup 備份信箱與公用資料夾資料庫
!!線上備份 (online Backup)
*允許所有服務正常執行,不需要中斷服務,但效率降低
*.edb、.log 均會備份並檢查資料庫是否被破壞
*可選擇以儲存群組為單位或是以資料庫為單位備份
**強烈建議採用儲存群組為備份單位,因為交易紀錄檔可以幫助還原為最新的資料,而儲存群組維護一份共用的交易紀錄檔
{{item1{__備份儲存群組__}}} <<toBalaFlashPlayer "flash/12-05-storge_group_backup.swf" "有影片有真相" "800" "620">>
!還原 Exchange 資料庫
*還原不可能在線上還原
#卸載資料庫
#核選資料庫內容對話盒:『還原將複寫這個資料庫』
#開啟 ntbackup:記得選取自動取代現有檔案
{{item1{__還原儲存群組__}}} <<toBalaFlashPlayer "flash/12-06-storage_group_restore.swf" "有影片有真相" "800" "620">>
!復原儲存群組 (Recovery Storage Groups)
*新的還原技術
*可對還原的資料庫比對,針對不同的部份合併以節省時間
*RSG 限制
**只能使用 Exchange Troubleshooting Assistant (ExTRA) 或命令指令透過 MAPI 方式存取
**目標信箱必須與 RSG 中的信箱資料庫在相同的樹系內
**無法在 RSG 中裝載公用資料夾資料庫
#建立復原儲存群組 <<toBalaFlashPlayer "flash/12-07-create_recovery_storage_group.swf" "有影片有真相" "800" "620">>
#@@color:red;不勾選『還原將複寫這個資料庫』、不卸載@@
#使用還原精靈將已備份的儲存群組還原到原始位置 (事實上在不勾選『還原將複寫這個資料庫』及不卸載時,是倒到 RSG 中) <<toBalaFlashPlayer "flash/12-08-restore_backup_storage_group_to_recovery_storage_group.swf" "有影片有真相" "800" "620">>
**在先前建立的復原儲存群組中會出現還原的儲存群組
#檢查復原儲存群組是否裝載 <<toBalaFlashPlayer "flash/12-09-mount_recovery_storage_group.swf" "有影片有真相" "800" "620">>
#合併或複製 <<toBalaFlashPlayer "flash/12-10-recovery_user_mailbox.swf" "有影片有真相" "800" "620">>
**選擇要從復原儲存群組中復原的使用者信箱
**注意要復原的使用者信箱帳號不能被移除
#移除復原儲存群組 <<toBalaFlashPlayer "flash/12-11-remove_recovery_storage_group.swf" "有影片有真相" "800" "620">>
**卸載
**移除
**手動刪除復原儲存群組目錄
!三種高度可用性的設計
*高可用性的設計,資料主機雙容錯能力
**本機連續複寫 (Local Continuous Replication, LCR)
***標準版 Exchange 僅支援此項
***僅限於資料庫的容錯能力
**雙資料中心的容錯 (Cluster Continuous Replication, CCR)
***硬體的 Cluster 提供 failover (錯誤移轉) 與 Load Balance,貴
***微軟的 failover 效果很差
**單資料中心的雙主機 (Single Copy Clustering, SCC)
***Exchange 2007 SP1 後支援
!本機資料庫雙備份 (LCR)
!!啟動儲存群組中的本機連續複寫 <<toBalaFlashPlayer "flash/13-01-enable_local_continuous_replication.swf" "有影片有真相" "800" "620">>
*@@color:red;每個儲存群組只能有一個資料庫@@
#伺服器組態 → 信箱 選擇儲存群組右鍵功能表上的『啟用本機連續複寫』
#選擇複寫系統檔及記錄檔的位置
#檢查目前資料庫是否正常 <<toBalaFlashPlayer "flash/13-02-verify_replication.swf" "有影片有真相" "800" "620">>
*當 active 資料庫毀損,還原儲存群組副本
*特殊原因停用 LCR,停用本機連續複寫
*擱置儲存群組副本,暫時停用同步
*更新儲存群組副本:當有擱置儲存群組時,手動同步
{{item1{__當其中資料庫正本損毀,使用複本資料庫還原__}}} <<toBalaFlashPlayer "flash/13-03-restore_storage_group_replication.swf" "有影片有真相" "800" "620">>
!安裝邊際傳輸伺服器
#Standalone 獨立伺服器
**其 DNS 指向 Internet DNS 或是 ISP DNS 即可,並在 hosts 檔案中加入能解析內部郵件伺服器的名稱即可
**內部郵件伺服器也需要能解析邊際傳輸伺服器
#設定網域尾碼 <<toBalaFlashPlayer "flash/14-01-set_domain_suffix.swf" "有影片有真相" "800" "620">>
#安裝 ADAM SP1 <<toBalaFlashPlayer "flash/14-02-download_and_install_adam_sp1.swf" "有影片有真相" "800" "620">>
**AD 環境的簡易版本,不需加入網域、不需要 DC、不需要 DNS 就能維護 LDAP 規格資料庫
#安裝邊際傳輸伺服器角色 <<toBalaFlashPlayer "flash/14-03-install_edge_transport_role.swf" "有影片有真相" "800" "620">>
#重新開機
!建立 Edge Subscription File 訂閱檔
*{{{New-EdgeSubscription -file "c:\es.xml"}}} <<toBalaFlashPlayer "flash/14-04-edge_subscription_file.swf" "有影片有真相" "800" "620">>
*在組織組態 集線傳輸 新增Edge訂閱 <<toBalaFlashPlayer "flash/14-05-hub_transport_subscription.swf" "有影片有真相" "800" "620">>
**Edge transport role 與 hub transport role 間的防火牆需放行 25(src、dst)、50636(對於 hub transport 的 src) 連接埠
*第一次手動同步 (Hub Transport role) <<toBalaFlashPlayer "flash/14-06-edge_synchronization.swf" "有影片有真相" "800" "620">>
**Start-EdgeSynchronization
*爾後每四個小時透過增量複製同步
*測試同步狀態 <<toBalaFlashPlayer "flash/14-07-test_synchronization.swf" "有影片有真相" "800" "620">>
**Test-EdgeSynchronization
*檢視伺服器資訊 <<toBalaFlashPlayer "flash/14-08-view_server_info.swf" "有影片有真相" "800" "620">>
**Get-ExchangeServer
{{item1{__邊際傳輸伺服器角色上線後,將該給予的工作責任從集線傳輸伺服器角色身上移除__}}} <<toBalaFlashPlayer "flash/14-09-set_hub_transport_role_after_edge_transport_role_online.swf" "有影片有真相" "800" "620">>
Background: #fff
Foreground: #000
PrimaryPale: #fc8
PrimaryLight: #ccf
PrimaryMid: #03c
PrimaryDark: #55f
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>>
<<<
!!!!!Revisions
<<<
2006.06.09 [1.5.0] added configurable txtDisableWikiLinksTag (default value: "excludeWikiWords") to allows selective disabling of automatic WikiWord links for any tiddler tagged with that value.
2006.12.31 [1.4.0] in formatter, test for chkDisableNonExistingWikiLinks
2006.12.09 [1.3.0] in formatter, test for excluded wiki words specified in DisableWikiLinksList
2006.12.09 [1.2.2] fix logic in autoLinkWikiWords() (was allowing links TO shadow tiddlers, even when chkDisableWikiLinks is TRUE).
2006.12.09 [1.2.1] revised logic for handling links in shadow content
2006.12.08 [1.2.0] added hijack of Tiddler.prototype.autoLinkWikiWords so regular (non-bracketed) WikiWords won't be added to the missing list
2006.05.24 [1.1.0] added option to NOT bypass automatic wikiword links when displaying default shadow content (default is to auto-link shadow content)
2006.02.05 [1.0.1] wrapped wikifier hijack in init function to eliminate globals and avoid FireFox 1.5.0.1 crash bug when referencing globals
2005.12.09 [1.0.0] initial release
<<<
!!!!!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:''|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);
//}}}
/***
|''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:''|© 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]].
!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version
!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.
***/
使用此 TiddlyWiki 的空白範本之前,請先修改以下預設文章:
* SiteTitle 及 SiteSubtitle:網站的標題和副標題,顯示於頁面上方<br />(在儲存變更後,將顯示於瀏覽器視窗的標題列)。
* MainMenu:主選單(通常在頁面左側)。
* DefaultTiddlers:內含一些文章的標題,可於載入TiddlyWiki 後的預設開啟。
請輸入您的大名,作為所建立/ 編輯的文章署名:<<option txtUserName>>
<!--{{{-->
<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>
<!--}}}-->
<<importTiddlers inline>>
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|4.3.1|
|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|
|Options|##Configuration|
|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.
!!!!!Documentation
<<<
see [[ImportTiddlersPluginInfo]] for details
<<<
!!!!!interactive control panel:
<<<
<<tiddler ImportTiddlers>>
^^(see [[ImportTiddlers]] shadow tiddler)^^
<<<
!!!!!Installation Notes
<<<
* As of 6/27/2007, "patch" functions that provide backward-compatibility with TW2.1.x and earlier have been split into a separate [[ImportTiddlersPluginPatch]] tiddler to reduce installation overhead for //this// plugin. You only need to install the additional plugin tiddler when using ImportTiddlersPlugin in documents using TW2.1.x or earlier.
* As of 3/21/2007, the interactive {{{<<importTiddlers>>}}} and non-interactive {{{<<loadTiddlers>>}}} macro definitions and related code have been split into separate [[ImportTiddlersPlugin]] and [[LoadTiddlersPlugin]] to permit selective installation of either the interactive and/or non-interactive macro functions.
* Quick Installation Tip: If you are using an unmodified version of TiddlyWiki (core release version <<version>>), you can get a new, empty TiddlyWiki with the Import Tiddlers plugin pre-installed (''[[download from here|TW+ImportExport.html]]''), and then simply import all your content from your old document into this new, empty document.
<<<
!!!!!Revisions
<<<
2008.06.29 [4.3.1] More layout/animation work for simpler sequential interaction. Code reduction/cleanup
|please see [[ImportTiddlersPluginInfo]] for additional revision details|
2005.07.20 [1.0.0] Initial Release
<<<
!!!!!Code
***/
// // ''MACRO DEFINITION''
//{{{
// Version
version.extensions.importTiddlers = {major: 4, minor: 3, revision: 0, date: new Date(2008,6,28)};
// add ImportTiddlerPlugin controls to built-in backstage import task
if (config.tasks) { // TW2.2 or above
config.tasks.importTask.content="Use ~TiddlyWiki built-in importer (below) or, ";
config.tasks.importTask.content+="<<importTiddlers link 'Use ImportTiddlersPlugin control panel...'>>\n";
config.tasks.importTask.content+="<<importTiddlers core>>"
}
// 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="<<importTiddlers inline>>";
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:35em; right:105%; top:3em;\
background-color: #eee; color:#000; font-size: 8pt; 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:8pt; 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:8pt;line-height:110%;}\
#importPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;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:8pt; }\
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }\
#importCollisionPanel { display:none; margin:0.5em 0em 0em 0em; }\
#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);">password\
<input type="checkbox" class="chk" id="importUseProxy"\
onClick="config.macros.importTiddlers.useProxy=this.checked;\
config.macros.importTiddlers.showPanel(\'importSiteProxy\',this.checked);">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>\
<a href="javascript:;" id="importSelectNew"\
onclick="onClickImportButton(this);return false;" title="SELECT tiddlers not already in destination document">\
added</a>\
<a href="javascript:;" id="importSelectChanges"\
onclick="onClickImportButton(this);return false;" title="SELECT tiddlers that have been updated in source document">\
changes</a>\
<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">\
– </a>\
<a href="javascript:;" id="importListLarger"\
onclick="onClickImportButton(this);return false;" title="GROW list size">\
+ </a>\
<a href="javascript:;" id="importListMaximize"\
onclick="onClickImportButton(this);return false;" title="MAXIMIZE/RESTORE list size">\
= </a>\
</td></tr></table>\
<select id="importList" size=8 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 \
<input type=checkbox class="chk" id="chkKeepTags" checked\
onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing \
<input type=checkbox class="chk" id="chkAddTags" \
onClick="config.macros.importTiddlers.addTags=this.checked;\
if (this.checked) document.getElementById(\'txtNewTags\').focus();">add new<br>\
<input type=text id="txtNewTags" style="margin-top:4px;" 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="text-align:left;margin:0 0 0 .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;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
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;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
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;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
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;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
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) {
if (typeof place == "string") var place=document.getElementById(place);
if (!place||!place.style) return;
if(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('importStart').style.visibility="hidden";
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('importStart').style.visibility='visible';
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.fileExists=function(theFile) {
var found=false;
if(window.Components) {
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
catch(e) { return false; } // security access denied
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
try { file.initWithPath(theFile); }
catch(e) { return false; } // invalid directory
found = file.exists();
}
else { // use ActiveX FSO object for MSIE
var fso = new ActiveXObject("Scripting.FileSystemObject");
found = fso.FileExists(theFile)
}
return found;
}
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 src is relative (i.e., not a URL)
if (!this.fileExists(src)) { // if file cannot be found, 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);
}
}
if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not remote URL, read from local filesystem
var txt=loadFile(src);
if ((txt==null)||(txt==false)) // file didn't load
{ 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="…";};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\">© 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;
};
})();
//}}}
<<tagsTree ex "" 2 4 index label>>
<<tagsTree twcms "" 1 4 index label>>
<<tagsTree KMKConfig "" 1 4 index label>>
<<tabs txtMainTab "最近更新" "依更新日期排序" TabTimeline "分類" "所有標籤" TabTags "設定文章" "說所有設定文章" TabMoreShadowed>>
<!--{{{-->
<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>Sky of flatfish</b><br><br><img src="loader.gif"><br>
<span style="font-size: 14px; color:red;">Requires Javascript.</span>
</div>
<!--}}}-->
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryDark]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='toolBar' refresh='content' force='true' tiddler='ToolBar'></div>
<div id='mainMenu' refresh='content' force='true' tiddler='MainMenu'></div>
<!--
<div id='sidebar'>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</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>
<!--}}}-->
//{{{
//============================================================================
// 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>>
<<showUpdates excludeTag:excludeLists write:'(index < 10) ? ""+""+ tiddler.modified.formatString("YYYY年0MM月0DD日")+ "\n [[" +tiddler.title+"]]"+"\n" : ""'>>
''flatfish''@2008 powered by TWtBala - TiddlyWiki 2.3.0
/***
|''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;
}
//}}}
/*{{{*/
/* 主要項目樣式
在 Tiddler 中的使用格式如下 :
{{item1{Item description}}}
*/
.item1{
line-height:28px;
font-weight:bold;
font-size:14px;
border-left: 10px solid blue;
margin: 0px 0px 0px 20px;
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:0.8em;}
h4 {font-size:0.6em;}
h5 {font-size:0.4em;}
.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 3px;}
.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>>
/***
|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|
!Demos
On [[homepage|http://visualtw.ouvaton.org/VisualTW.html]], open several tiddlers to use the tabs bar.
!Installation
#import this tiddler from [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] (tagged as systemConfig)
#save and reload
#''if you're using a custom [[PageTemplate]]'', add {{{<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>}}} before {{{<div id='tiddlerDisplay'></div>}}}
#optionally, adjust StyleSheetTiddlersBar
!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>");
//}}}
<<search>> | [[首頁]] | [[土芭樂 3.0 - 數位新思路|http://tbala.net/]] | [[MS Exchange 資訊網站|http://www.msexchange.org/]] | <<newTiddler label:"新增文章">> <<saveChanges>>
<!--{{{-->
<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>
<!--}}}-->
/***
|''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:''|© 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);
})();
//}}}
''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>>
/% %/
!使用說明
{{{
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>
}}}
/***
|''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+=" ";
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/*}}}*/";
//}}}
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" : ""'
>>
{{{
<<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"'
>>
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'
>>
<<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 \"文章整理\"//"'
>>
//{{{
//============================================================================
// 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 決定文章顯示的樣子"
});
//}}}
/***
|<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 tiddler 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
/***
|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|
|''版本:''|1.0 (2008-03-06)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''|toBalaHTML, toBalaFile|
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0|
|''使用語法''| |
***/
//{{{
config.macros.toBalaAjax = {};
config.macros.toBalaAjax.handler = function(place,macroName,params,wikifier,paramString,tiddler){
if (!params[0]) {
alert("請給檔名");
return;
}
// 檔名
var xfname = params[0];
var slabel = params[1] || "儲存網頁";
var olabel = params[2] || "檢視網頁";
var xwidth = params[3] || 550;
var xheight = params[4] || 400;
createTiddlyButton(place, olabel, olabel,
function(e) { config.macros.toBalaHTML.show(e,"",xfname,xwidth,xheight); },
null, null, null);
if (readOnly) {
return;
}
createTiddlyButton(place, slabel, slabel,
function(e) { config.macros.toBalaSaveFile.show(e, tiddler.title,xfname); },
null, null, null);
}
//}}}
!使用說明
{{{
<<toBalaAjax "ajax\html\testAjax.htm" "儲存網頁 : ajax\html\testAjax.htm">>
}}}
!實作範例
<<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" "儲存網頁 : ajax\AjaxCall.htm" "" "700" "450">>
/*{{{*/
<!--
程式名稱 : 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.
-->
/*}}}*/
config.options.chkAutoSave=false;
config.options.chkSaveBackups=false;
config.options.txtUserName="比目魚";
config.options.txtMaxEditRows=25;
config.options.chkHttpReadOnly=true;
config.options.chkHideTabsBarWhenSingleTab=true;
config.options.chkDisableWikiLinks=true;
config.options.chkAllowLinksFromShadowTiddlers=true;
config.options.chkDisableNonExistingWikiLinks=true;
/***
|''巨集名稱:''|toBalaFile|
|''版本:''|1.0 (2008-03-18)|
|''原始檔:''||
|''作者:''|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);
xtiddler1.text=toptext+xtext+"\n"+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)+"\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);
saveChanges(false);
return;
}
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)+"\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 "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">>
}}}
!實作
<<toBalaFlashPlayer "movie/infotree.swf" "" "820" "610">>
/***
|''巨集名稱:''|toBalaHTML|
|''版本:''|1.0 (2008-03-18)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''|<<toBalaHTML title filename label tooltip width height>> |
***/
//{{{
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。
*/
//}}}
<<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+|
|''使用語法''|<<toBalaJava "程式檔名" "前置編譯參數" "前置執行參數" "後置執行參數" "按鈕提示資訊" "按鈕提示資訊">> |
***/
//{{{
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);
}
//}}}
!程式範例
{{{
使用 toBalaFile 巨集, 編輯及儲存 Java 程式, 命令格式如下 :
<<toBalaFile "java\hello.java">>
}}}
<<toBalaFile "java\hello.java">>
----
{{{
使用 toBalaJava 巨集, 編譯及執行 Java 程式, 命令格式如下 :
<<toBalaJava "java\hello.java" "" "">>
}}}
<<toBalaJava "java\hello.java" "" "">>
!程式範例 : 輸入 [編譯] 及 [執行] 參數
<<toBalaFile "java\assertion.java">>
----
{{{
使用 toBalaJava 巨集, 編譯及執行 Java 程式, 命令格式如下 :
<<toBalaJava "java\assertion.java" "-source 1.4" "-ea">>
"-source 1.4" 是編譯參數
"-ea" 是執行參數
}}}
<<toBalaJava "java\assertion.java" "-source 1.4" "-ea">>
///%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;
}
}
}
//%/
{{{
if (!window.toBala) window.toBala = {};
toBala.showMsg = function(msg,xw,xh) {
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;
var xwin=window.open("","",settings);
xwin.document.open();
xwin.document.write(msg);
xwin.document.close();
return xwin;
};
}}}
/***
|''巨集名稱:''|toBalaManager|
|''版本:''|1.0 (2008-05-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("ToolBar");
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) {
return;
}
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("ToolBar");
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:150px;}\n" +
".xManagerMM {width:680px;height:250px;}\n" +
".toBalaManagerSave {border:1px solid #ccc;font-size:14px;padding:2px;margin-left:4px;margin-bottom:2px;}\n" ,
"toBalaManagerStyles");
//}}}
/***
|''巨集名稱:''|toBalaNotes|
|''版本:''|1.0 (2008-03-18)|
|''原始檔:''||
|''作者:''|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);
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);
// alert(xtitle+":"+xx+":"+yy);
boxtext.setAttribute("rows",15);
boxtext.setAttribute("cols",88);
boxtext.value = xtiddler.text.substring(xx,yy);
},
GUISaveButtonOnclick: function(e,xtiddler1,xbox1,boxtext1,xx1,yy1) {
removeChildren(xbox1);
toptext=xtiddler1.text.substring(0,xx1);
bottomtext=xtiddler1.text.substring(yy1);
xtiddler1.text=toptext+boxtext1.value+bottomtext;
story.refreshTiddler(xtiddler1.title,null,true);
saveChanges(false); // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
},
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);
wikify(xtiddler1.text.substring(xx1,yy1),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);
wikify(xtext.substring(x1,x2),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",
"toBalaNotesStyles");
//}}}
/***
|''巨集名稱:''|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);
}
}
//}}}
<<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>
//%/
<<toBalaRun "tbzoomit.bat" "" "ZoomIt v1.8" "" "no">>
!~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.
!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%\cmdtools;%PATH%
start /B zoomit.exe
exit
}}}
/***
|''巨集名稱:''|toBalaWrite|
|''版本:''|1.0 (2008-04-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>>
How to Cheat at Configuring Exchange Server 2007. including Outlook Web, Mobile, and Voice Access
Microsoft Exchange Server 2007 Administrator's Companion, Second Edition
Microsoft Exchange Server 2007: Tony Redmond's Guide to Successful Implementation
http://www.msexchange.org/
http://forums.microsoft.com/Technet-CHT/
/***
|''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
***/
聯絡我: mailto:bearflatfish@gmail.com
<html><a href="../weblog/weblog.html" target="mainFrame"> 更多的 ''比目魚'' 請看 My Weblog </a></html>
/***
|<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 tiddler 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
!''我的最新文章''
<<showUpdates excludeTag:excludeLists write:'(index < 10) ? "|"+(index+1)+"|"+ tiddler.modified.formatString("YYYY / 0MM / 0DD")+ "|[[" + tiddler.title+"]]|"+tiddler.tags+"|\n" : ""'>>